From 1ecbdf0fb2c2f2edea9086bbbe74768241f1593f Mon Sep 17 00:00:00 2001
From: David Malcolm <dmalcolm@redhat.com>
Date: Fri, 16 Sep 2016 11:16:40 -0400
Subject: [PATCH 1/3] FIXME: WIP on template type diff printing

---
 gcc/c-family/c.opt                                 |   8 +
 gcc/c/c-objc-common.c                              |   5 +-
 gcc/cp/call.c                                      |  23 +-
 gcc/cp/cvt.c                                       |   6 +-
 gcc/cp/error.c                                     | 412 ++++++++++++++++++++-
 gcc/cp/typeck.c                                    |   8 +-
 gcc/cp/typeck2.c                                   |   6 +-
 gcc/diagnostic-color.c                             |   1 +
 gcc/fortran/error.c                                |   3 +-
 gcc/pretty-print.c                                 |  11 +-
 gcc/pretty-print.h                                 |  20 +-
 .../g++.dg/template/show-template-tree-color.C     |  42 +++
 .../template/show-template-tree-no-elide-type.C    |  42 +++
 gcc/testsuite/g++.dg/template/show-template-tree.C |  43 +++
 gcc/tree-diagnostic.c                              |   3 +-
 15 files changed, 602 insertions(+), 31 deletions(-)
 create mode 100644 gcc/testsuite/g++.dg/template/show-template-tree-color.C
 create mode 100644 gcc/testsuite/g++.dg/template/show-template-tree-no-elide-type.C
 create mode 100644 gcc/testsuite/g++.dg/template/show-template-tree.C

diff --git a/gcc/c-family/c.opt b/gcc/c-family/c.opt
index 78fea61..5eb330e 100644
--- a/gcc/c-family/c.opt
+++ b/gcc/c-family/c.opt
@@ -1338,6 +1338,10 @@ fdefault-inline
 C++ ObjC++ Ignore
 Does nothing.  Preserved for backward compatibility.
 
+fdiagnostics-show-template-tree
+C++ ObjC++ Var(flag_diagnostics_show_template_tree) Init(0)
+FIXME
+
 fdirectives-only
 C ObjC C++ ObjC++
 Preprocess directives only.
@@ -1357,6 +1361,10 @@ Write all declarations as Ada code for the given file only.
 felide-constructors
 C++ ObjC++ Var(flag_elide_constructors) Init(1)
 
+felide-type
+C++ ObjC++ Var(flag_elide_type) Init(1)
+-fno-elide-type Do not elide common elements in template comparisons
+
 fenforce-eh-specs
 C++ ObjC++ Var(flag_enforce_eh_specs) Init(1)
 Generate code to check exception specifications.
diff --git a/gcc/c/c-objc-common.c b/gcc/c/c-objc-common.c
index 5e69488..ee0fa69 100644
--- a/gcc/c/c-objc-common.c
+++ b/gcc/c/c-objc-common.c
@@ -28,7 +28,7 @@ along with GCC; see the file COPYING3.  If not see
 #include "c-objc-common.h"
 
 static bool c_tree_printer (pretty_printer *, text_info *, const char *,
-			    int, bool, bool, bool);
+			    int, bool, bool, bool, const char **);
 
 bool
 c_missing_noreturn_ok_p (tree decl)
@@ -75,7 +75,8 @@ c_objc_common_init (void)
    diagnostic machinery.  */
 static bool
 c_tree_printer (pretty_printer *pp, text_info *text, const char *spec,
-		int precision, bool wide, bool set_locus, bool hash)
+		int precision, bool wide, bool set_locus, bool hash,
+		const char **)
 {
   tree t = NULL_TREE;
   tree name;
diff --git a/gcc/cp/call.c b/gcc/cp/call.c
index 86c7647..4ac060d 100644
--- a/gcc/cp/call.c
+++ b/gcc/cp/call.c
@@ -3402,7 +3402,7 @@ print_conversion_rejection (location_t loc, struct conversion_info *info)
 		from);
       else
 	inform (loc, "  no known conversion for implicit "
-		"%<this%> parameter from %qT to %qT",
+		"%<this%> parameter from %H to %I",
 		from, info->to_type);
     }
   else if (!TYPE_P (info->from))
@@ -3415,10 +3415,10 @@ print_conversion_rejection (location_t loc, struct conversion_info *info)
     }
   else if (info->n_arg == -2)
     /* Conversion of conversion function return value failed.  */
-    inform (loc, "  no known conversion from %qT to %qT",
+    inform (loc, "  no known conversion from %H to %I",
 	    from, info->to_type);
   else
-    inform (loc, "  no known conversion for argument %d from %qT to %qT",
+    inform (loc, "  no known conversion for argument %d from %H to %I",
 	    info->n_arg + 1, from, info->to_type);
 }
 
@@ -3925,7 +3925,7 @@ build_user_type_conversion_1 (tree totype, tree expr, int flags,
     {
       if (complain & tf_error)
 	{
-	  error ("conversion from %qT to %qT is ambiguous",
+	  error ("conversion from %H to %I is ambiguous",
 		 fromtype, totype);
 	  print_z_candidates (location_of (expr), candidates);
 	}
@@ -4052,7 +4052,7 @@ build_integral_nontype_arg_conv (tree type, tree expr, tsubst_flags_t complain)
 	  break;
 
 	if (complain & tf_error)
-	  error_at (loc, "conversion from %qT to %qT not considered for "
+	  error_at (loc, "conversion from %H to %I not considered for "
 		    "non-type template argument", t, type);
 	/* fall through.  */
 
@@ -5229,7 +5229,7 @@ build_conditional_expr_1 (location_t loc, tree arg1, tree arg2, tree arg3,
 							     arg3_type);
       if (complain & tf_warning)
 	do_warn_double_promotion (result_type, arg2_type, arg3_type,
-				  "implicit conversion from %qT to %qT to "
+				  "implicit conversion from %H to %I to "
 				  "match other result of conditional",
 				  loc);
 
@@ -6612,7 +6612,7 @@ convert_like_real (conversion *convs, tree expr, tree fn, int argnum,
 	  if (t->kind == ck_user && t->cand->reason)
 	    {
 	      complained = permerror (loc, "invalid user-defined conversion "
-				      "from %qT to %qT", TREE_TYPE (expr),
+				      "from %H to %I", TREE_TYPE (expr),
 				      totype);
 	      if (complained)
 		print_z_candidate (loc, "candidate is:", t->cand);
@@ -6648,7 +6648,7 @@ convert_like_real (conversion *convs, tree expr, tree fn, int argnum,
 	    break;
 	}
       if (!complained)
-	complained = permerror (loc, "invalid conversion from %qT to %qT",
+	complained = permerror (loc, "invalid conversion from %H to %I",
 				TREE_TYPE (expr), totype);
       if (complained && fn)
 	inform (DECL_SOURCE_LOCATION (fn),
@@ -7083,7 +7083,7 @@ convert_arg_to_ellipsis (tree arg, tsubst_flags_t complain)
       if ((complain & tf_warning)
 	  && warn_double_promotion && !c_inhibit_evaluation_warnings)
 	warning_at (loc, OPT_Wdouble_promotion,
-		    "implicit conversion from %qT to %qT when passing "
+		    "implicit conversion from %H to %I when passing "
 		    "argument to function",
 		    arg_type, double_type_node);
       arg = convert_to_real_nofold (double_type_node, arg);
@@ -9644,7 +9644,7 @@ joust (struct z_candidate *cand1, struct z_candidate *cand2, bool warn,
 	  if (! DECL_CONSTRUCTOR_P (w->fn))
 	    source = TREE_TYPE (source);
 	  if (warning (OPT_Wconversion, "choosing %qD over %qD", w->fn, l->fn)
-	      && warning (OPT_Wconversion, "  for conversion from %qT to %qT",
+	      && warning (OPT_Wconversion, "  for conversion from %H to %I",
 			  source, w->second_conv->type)) 
 	    {
 	      inform (input_location, "  because conversion sequence for the argument is better");
@@ -10100,7 +10100,8 @@ perform_implicit_conversion_flags (tree type, tree expr,
 	  else if (invalid_nonstatic_memfn_p (loc, expr, complain))
 	    /* We gave an error.  */;
 	  else
-	    error_at (loc, "could not convert %qE from %qT to %qT", expr,
+            // FIXME: here's the one I'm testing...
+	    error_at (loc, "could not convert %qE from %H to %I", expr,
 		      TREE_TYPE (expr), type);
 	}
       expr = error_mark_node;
diff --git a/gcc/cp/cvt.c b/gcc/cp/cvt.c
index 5f4b5e3..c3d3fc1 100644
--- a/gcc/cp/cvt.c
+++ b/gcc/cp/cvt.c
@@ -96,7 +96,7 @@ cp_convert_to_pointer (tree type, tree expr, bool dofold,
 	{
 	  if ((complain & tf_error)
 	      && rval == error_mark_node)
-	    error_at (loc, "conversion of %qE from %qT to %qT is ambiguous",
+	    error_at (loc, "conversion of %qE from %H to %I is ambiguous",
 		      expr, intype, type);
 	  return rval;
 	}
@@ -221,7 +221,7 @@ cp_convert_to_pointer (tree type, tree expr, bool dofold,
   else if (TYPE_PTRMEM_P (type) && INTEGRAL_CODE_P (form))
     {
       if (complain & tf_error)
-	error_at (loc, "invalid conversion from %qT to %qT", intype, type);
+	error_at (loc, "invalid conversion from %H to %I", intype, type);
       return error_mark_node;
     }
 
@@ -464,7 +464,7 @@ convert_to_reference (tree reftype, tree expr, int convtype,
 	    && !at_least_as_qualified_p (ttl, ttr))
 	  {
 	    if (complain & tf_error)
-	      permerror (loc, "conversion from %qT to %qT discards qualifiers",
+	      permerror (loc, "conversion from %H to %I discards qualifiers",
 			 ttr, reftype);
 	    else
 	      return error_mark_node;
diff --git a/gcc/cp/error.c b/gcc/cp/error.c
index d8c5d5e..8b13fad 100644
--- a/gcc/cp/error.c
+++ b/gcc/cp/error.c
@@ -30,6 +30,7 @@ along with GCC; see the file COPYING3.  If not see
 #include "c-family/c-objc.h"
 #include "ubsan.h"
 #include "internal-fn.h"
+//#include "diagnostic-color.h"
 
 #define pp_separate_with_comma(PP) pp_cxx_separate_with (PP, ',')
 #define pp_separate_with_semicolon(PP) pp_cxx_separate_with (PP, ';')
@@ -99,7 +100,47 @@ static void cp_diagnostic_starter (diagnostic_context *, diagnostic_info *);
 static void cp_print_error_function (diagnostic_context *, diagnostic_info *);
 
 static bool cp_printer (pretty_printer *, text_info *, const char *,
-			int, bool, bool, bool);
+			int, bool, bool, bool, const char **);
+
+/* Struct for handling %H or %I, which require delaying printing the
+   type until a postprocessing stage.  */
+
+struct deferred_printed_type
+{
+  deferred_printed_type ()
+  : m_tree (NULL_TREE), m_buffer_ptr (NULL), m_verbose (false)
+  {}
+
+  deferred_printed_type (tree type, const char **buffer_ptr, bool verbose)
+  : m_tree (type), m_buffer_ptr (buffer_ptr), m_verbose (verbose)
+  {
+    gcc_assert (type);
+    gcc_assert (buffer_ptr);
+  }
+
+  /* The tree is not GTY-marked: they are only non-NULL within a
+     call to pp_format.  */
+  tree m_tree;
+  const char **m_buffer_ptr;
+  bool m_verbose;
+};
+
+/* Subclass of format_postprocessor for the C++ frontend.
+   This handles the %H and %I formatting codes, printing them
+   in a postprocessing phase (since they affect each other).  */
+
+class cxx_format_postprocessor : public format_postprocessor
+{
+ public:
+  cxx_format_postprocessor ()
+  : m_type_a (), m_type_b ()
+  {}
+
+  void handle (pretty_printer *pp) FINAL OVERRIDE;
+
+  deferred_printed_type m_type_a;
+  deferred_printed_type m_type_b;
+};
 
 /* CONTEXT->printer is a basic pretty printer that was constructed
    presumably by diagnostic_initialize(), called early in the
@@ -123,6 +164,7 @@ cxx_initialize_diagnostics (diagnostic_context *context)
   diagnostic_starter (context) = cp_diagnostic_starter;
   /* diagnostic_finalizer is already c_diagnostic_finalizer.  */
   diagnostic_format_decoder (context) = cp_printer;
+  pp->m_format_postprocessor = new cxx_format_postprocessor ();
 }
 
 /* Dump a scope, if deemed necessary.  */
@@ -3556,6 +3598,360 @@ maybe_print_constexpr_context (diagnostic_context *context)
     }
 }
 
+
+/* Return true iff TYPE_A and TYPE_B are template types that are
+   meaningful to compare.  */
+
+static bool
+comparable_template_types_p (tree type_a, tree type_b)
+{
+  if (TREE_CODE (type_a) != RECORD_TYPE)
+    return false;
+  if (TREE_CODE (type_b) != RECORD_TYPE)
+    return false;
+
+  tree tinfo_a = TYPE_TEMPLATE_INFO (type_a);
+  tree tinfo_b = TYPE_TEMPLATE_INFO (type_b);
+  if (!tinfo_a || !tinfo_b)
+    return false;
+
+  return TI_TEMPLATE (tinfo_a) == TI_TEMPLATE (tinfo_b);
+}
+
+/* Start a new line indented by SPC spaces on PP.  */
+
+static void
+newline_and_indent (pretty_printer *pp, int spc)
+{
+  pp_newline (pp);
+  for (int i = 0; i < spc; i++)
+    pp_space (pp);
+}
+
+/* FIXME.  */
+static const char *
+arg_to_string (tree arg, bool verbose)
+{
+  if (EXPR_P (arg))
+    return expr_to_string (arg);
+  return type_to_string (arg, verbose);
+}
+
+/* FIXME.  */
+
+static void
+print_nonequal_arg (pretty_printer *pp, tree arg, bool verbose)
+{
+#if 1
+  pp_printf (pp, "%r%s%R",
+	     "type-diff",
+	     (arg
+	      ? arg_to_string (arg, verbose)
+	      : G_("(no argument)")));
+#else
+  /* Probably need to turn off quoting here: use verbatim?  */
+  pp_string (pp, colorize_stop (pp_show_color (pp)));
+  pp_string (pp, colorize_start (pp_show_color (pp), "type-diff"));
+
+  pp_printf (pp, arg
+	     ? arg_to_string (arg, verbose)
+	     : G_("(no argument)"));
+
+  /* Resume "quote".  */
+  pp_string (pp, colorize_stop (pp_show_color (pp)));
+  pp_string (pp, colorize_start (pp_show_color (pp), "quote"));
+#endif
+}
+
+/* As type_to_string, but for a template, potentially colorizing/eliding
+   in comparison with PEER.
+   For example, if TYPE is map<int,double> and PEER is map<int,int>,
+   then the resulting string would be:
+     map<[...],double>
+   with type elision, and:
+     map<int,double>
+   without type elision.
+
+   In both cases the parts of TYPE that differ from PEER will be colorized
+   if SHOW_COLOR is true.  In the above example, this would be "double".
+
+   The resulting string is in a GC-allocated buffer.  */
+
+static const char *
+type_to_string_with_compare (tree type, tree peer, bool verbose,
+			     bool show_color)
+{
+  pretty_printer inner_pp;
+  pretty_printer *pp = &inner_pp;
+  pp_show_color (pp) = show_color;
+
+  tree tinfo = TYPE_TEMPLATE_INFO (type);
+  tree tinfo_peer = TYPE_TEMPLATE_INFO (peer);
+
+  pp_printf (pp, "%s<",
+	     IDENTIFIER_POINTER (DECL_NAME (TI_TEMPLATE (tinfo))));
+  tree args_a = TI_ARGS (tinfo);
+  tree args_b = TI_ARGS (tinfo_peer);
+  gcc_assert (TREE_CODE (args_a) == TREE_VEC);
+  gcc_assert (TREE_CODE (args_b) == TREE_VEC);
+  int len_a = TREE_VEC_LENGTH (args_a);
+  int len_b = TREE_VEC_LENGTH (args_b);
+  int len_max = MAX (len_a, len_b);
+  for (int idx = 0; idx < len_max; idx++)
+    {
+      tree arg_a = idx < len_a ? TREE_VEC_ELT (args_a, idx) : NULL;
+      tree arg_b = idx < len_b ? TREE_VEC_ELT (args_b, idx) : NULL;
+      if (arg_a == arg_b)
+	{
+	  if (flag_elide_type)
+	    pp_string (pp, G_("[...]"));
+	  else
+	    pp_string (pp, arg_to_string (arg_a, verbose));
+	}
+      else
+	{
+	  if (comparable_template_types_p (arg_a, arg_b))
+	    pp_string (pp, type_to_string_with_compare (arg_a, arg_b, verbose,
+							show_color));
+	  else
+	    print_nonequal_arg (pp, arg_a, verbose);
+	}
+      if (idx + 1 < len_max)
+	pp_character (pp, ',');
+    }
+  pp_printf (pp, ">");
+  return pp_ggc_formatted_text (pp);
+}
+
+/* Recursively print a tree-like comparison of TYPE_A and TYPE_B to PP,
+   indented by INDENT spaces.
+
+   For example given types:
+
+     vector<map<int,double>>
+
+   and
+
+     vector<map<double,float>>
+
+   the output with type elision would be:
+
+     vector<
+       map<
+         [...],
+         [double != float]>>
+
+   and without type-elision would be:
+
+     vector<
+       map<
+         int,
+         [double != float]>>
+
+   TYPE_A and TYPE_B must both be comparable template types
+   (as per comparable_template_types_p).  */
+
+static void
+print_template_tree_comparison (pretty_printer *pp, tree type_a, tree type_b,
+				bool verbose, int indent)
+{
+  tree tinfo_a = TYPE_TEMPLATE_INFO (type_a);
+  tree tinfo_b = TYPE_TEMPLATE_INFO (type_b);
+
+  newline_and_indent (pp, indent);
+  pp_printf (pp, "%s<",
+	     IDENTIFIER_POINTER (DECL_NAME (TI_TEMPLATE (tinfo_a))));
+  tree args_a = TI_ARGS (tinfo_a);
+  tree args_b = TI_ARGS (tinfo_b);
+  gcc_assert (TREE_CODE (args_a) == TREE_VEC);
+  gcc_assert (TREE_CODE (args_b) == TREE_VEC);
+  int len_a = TREE_VEC_LENGTH (args_a);
+  int len_b = TREE_VEC_LENGTH (args_b);
+  int len_max = MAX (len_a, len_b);
+  for (int idx = 0; idx < len_max; idx++)
+    {
+      tree arg_a = idx < len_a ? TREE_VEC_ELT (args_a, idx) : NULL;
+      tree arg_b = idx < len_b ? TREE_VEC_ELT (args_b, idx) : NULL;
+      if (arg_a == arg_b)
+	{
+	  newline_and_indent (pp, indent + 2);
+	  /* Can do elision here, printing "[...]".  */
+	  if (flag_elide_type)
+	    pp_string (pp, G_("[...]"));
+	  else
+	    pp_string (pp, arg_to_string (arg_a, verbose));
+	}
+      else
+	{
+	  if (comparable_template_types_p (arg_a, arg_b))
+	    print_template_tree_comparison (pp, arg_a, arg_b, verbose,
+					    indent + 2);
+	  else
+	    {
+	      newline_and_indent (pp, indent + 2);
+	      pp_character (pp, '[');
+	      print_nonequal_arg (pp, arg_a, verbose);
+	      pp_string (pp, " != ");
+	      print_nonequal_arg (pp, arg_b, verbose);
+	      pp_character (pp, ']');
+	    }
+	}
+      if (idx + 1 < len_max)
+	pp_character (pp, ',');
+    }
+  pp_printf (pp, ">");
+}
+
+/* Subroutine for use in a format_postprocessor::handle
+   implementation.  Adds a chunk to the end of
+   formatted output, so that it will be printed
+   by pp_output_formatted_text.  */
+
+static void
+append_formatted_chunk (pretty_printer *pp, const char *content)
+{
+  output_buffer *buffer = pp_buffer (pp);
+  struct chunk_info *chunk_array = buffer->cur_chunk_array;
+  const char **args = chunk_array->args;
+
+  unsigned int chunk_idx;
+  for (chunk_idx = 0; args[chunk_idx]; chunk_idx++)
+    ;
+  args[chunk_idx++] = content;
+  args[chunk_idx] = NULL;
+}
+
+/* Create a copy of CONTENT, with quotes added, and,
+   potentially, with colorization.
+   No escaped is performed on CONTENT.
+   The result is in a GC-allocated buffer. */
+
+static const char *
+add_quotes (const char *content, bool show_color)
+{
+  pretty_printer tmp_pp;
+  pp_show_color (&tmp_pp) = show_color;
+
+  /* We have to use "%<%s%>" rather than "%qs" here in order to avoid
+     quoting colorization bytes within the results.  */
+  pp_printf (&tmp_pp, "%<%s%>", content);
+
+  return pp_ggc_formatted_text (&tmp_pp);
+}
+
+/* If we had %H and %I, and hence deferred printing them,
+   print them now.  Also print the difference in tree form.  */
+
+void
+cxx_format_postprocessor::handle (pretty_printer *pp)
+{
+  if (m_type_a.m_tree || m_type_b.m_tree)
+    {
+      // FIXME: convert to internal error to better handle this;
+      // FIXME: add test cases via a plugin
+
+      /* Avoid reentrancy issues by working with a copy of
+	 m_type_a and m_type_b, resetting them now.  */
+      deferred_printed_type type_a = m_type_a;
+      deferred_printed_type type_b = m_type_b;
+      m_type_a = deferred_printed_type ();
+      m_type_b = deferred_printed_type ();
+
+      gcc_assert (type_a.m_tree);
+      gcc_assert (type_a.m_buffer_ptr);
+      gcc_assert (type_b.m_tree);
+      gcc_assert (type_b.m_buffer_ptr);
+
+      bool show_color = pp_show_color (pp);
+
+      const char *type_a_text;
+      const char *type_b_text;
+
+      /* FIXME: fix them up.  */
+      if (comparable_template_types_p (type_a.m_tree, type_b.m_tree))
+	{
+	  type_a_text
+	    = type_to_string_with_compare (type_a.m_tree, type_b.m_tree,
+					   type_a.m_verbose, show_color);
+	  type_b_text
+	    = type_to_string_with_compare (type_b.m_tree, type_a.m_tree,
+					   type_b.m_verbose, show_color);
+
+	  if (flag_diagnostics_show_template_tree)
+	    {
+	      pretty_printer inner_pp;
+	      pp_show_color (&inner_pp) = pp_show_color (pp);
+	      print_template_tree_comparison
+		(&inner_pp, type_a.m_tree, type_b.m_tree, type_a.m_verbose, 2);
+	      append_formatted_chunk (pp, pp_ggc_formatted_text (&inner_pp));
+	    }
+	}
+      else
+	{
+	  /* If the types were not comparable, they are printed normally,
+	     and no difference tree is printed.  */
+	  type_a_text = type_to_string (type_a.m_tree, type_a.m_verbose);
+	  type_b_text = type_to_string (type_b.m_tree, type_b.m_verbose);
+	}
+
+      *type_a.m_buffer_ptr = add_quotes (type_a_text, show_color);
+      *type_b.m_buffer_ptr = add_quotes (type_b_text, show_color);
+   }
+}
+
+/* Subroutine for handling %H and %I, to support i18n of messages like:
+
+    error_at (loc, "could not convert %qE from %H to %I",
+	       expr, type_a, type_b);
+
+   so that we can print things like:
+
+     could not convert `foo' from `map<int,double>' to `map<int,int>'
+
+   and, with type-elision:
+
+     could not convert `foo' from `map<[...],double>' to `map<[...],int>'
+
+   (with color-coding of the differences between the types).
+
+   The %H and %I format codes are peers: both must be present,
+   and they affect each other.  Hence to handle them, we must
+   delay printing until we have both, deferring the printing to
+   pretty_printer's m_format_postprocessor hook.
+
+   FIXME: can they appear in either order
+   FIXME: add a testcase for reversed order via a plugin.  */
+
+static void
+defer_half_of_type_diff (pretty_printer *pp, char spec, tree type,
+			 const char **buffer_ptr, bool verbose)
+{
+  gcc_assert (pp->m_format_postprocessor);
+  cxx_format_postprocessor *postprocessor
+    = static_cast <cxx_format_postprocessor *> (pp->m_format_postprocessor);
+
+  switch (spec)
+    {
+    case 'H':
+      gcc_assert (postprocessor->m_type_a.m_tree == NULL_TREE);
+      gcc_assert (postprocessor->m_type_a.m_buffer_ptr == NULL);
+      postprocessor->m_type_a
+	= deferred_printed_type (type, buffer_ptr, verbose);
+      break;
+
+    case 'I':
+      gcc_assert (postprocessor->m_type_b.m_tree == NULL_TREE);
+      gcc_assert (postprocessor->m_type_b.m_buffer_ptr == NULL);
+      postprocessor->m_type_b
+	= deferred_printed_type (type, buffer_ptr, verbose);
+      break;
+
+    default:
+      gcc_unreachable ();
+    }
+}
+
+
 /* Called from output_format -- during diagnostic message processing --
    to handle C++ specific format specifier with the following meanings:
    %A   function argument-list.
@@ -3570,10 +3966,13 @@ maybe_print_constexpr_context (diagnostic_context *context)
    %S   substitution (template + args)
    %T   type.
    %V   cv-qualifier.
-   %X   exception-specification.  */
+   %X   exception-specification.
+   %H   type difference (from)
+   %I   type difference (to).  */
 static bool
 cp_printer (pretty_printer *pp, text_info *text, const char *spec,
-	    int precision, bool wide, bool set_locus, bool verbose)
+	    int precision, bool wide, bool set_locus, bool verbose,
+	    const char **buffer_ptr)
 {
   const char *result;
   tree t = NULL;
@@ -3620,6 +4019,13 @@ cp_printer (pretty_printer *pp, text_info *text, const char *spec,
       percent_K_format (text);
       return true;
 
+    case 'H':
+    case 'I':
+      {
+	defer_half_of_type_diff (pp, *spec, next_tree, buffer_ptr, verbose);
+	return true;
+      }
+
     default:
       return false;
     }
diff --git a/gcc/cp/typeck.c b/gcc/cp/typeck.c
index d111124..e09efba 100644
--- a/gcc/cp/typeck.c
+++ b/gcc/cp/typeck.c
@@ -5012,7 +5012,7 @@ cp_build_binary_op (location_t location,
       result_type = cp_common_type (type0, type1);
       if (complain & tf_warning)
 	do_warn_double_promotion (result_type, type0, type1,
-				  "implicit conversion from %qT to %qT "
+				  "implicit conversion from %H to %I "
 				  "to match other operand of binary "
 				  "expression",
 				  location);
@@ -7017,7 +7017,7 @@ convert_member_func_to_ptr (tree type, tree expr, tsubst_flags_t complain)
 
   if (pedantic || warn_pmf2ptr)
     pedwarn (input_location, pedantic ? OPT_Wpedantic : OPT_Wpmf_conversions,
-	     "converting from %qT to %qT", intype, type);
+	     "converting from %H to %I", intype, type);
 
   if (TREE_CODE (intype) == METHOD_TYPE)
     expr = build_addr_func (expr, complain);
@@ -7139,7 +7139,7 @@ build_reinterpret_cast_1 (tree type, tree expr, bool c_cast_p,
       if (TYPE_PRECISION (type) < TYPE_PRECISION (intype))
         {
           if (complain & tf_error)
-            permerror (input_location, "cast from %qT to %qT loses precision",
+            permerror (input_location, "cast from %H to %I loses precision",
                        intype, type);
           else
             return error_mark_node;
@@ -7179,7 +7179,7 @@ build_reinterpret_cast_1 (tree type, tree expr, bool c_cast_p,
 	  && COMPLETE_TYPE_P (TREE_TYPE (type))
 	  && COMPLETE_TYPE_P (TREE_TYPE (intype))
 	  && TYPE_ALIGN (TREE_TYPE (type)) > TYPE_ALIGN (TREE_TYPE (intype)))
-	warning (OPT_Wcast_align, "cast from %qT to %qT "
+	warning (OPT_Wcast_align, "cast from %H to %I "
                  "increases required alignment of target type", intype, type);
 
       /* We need to strip nops here, because the front end likes to
diff --git a/gcc/cp/typeck2.c b/gcc/cp/typeck2.c
index 58a01c9..a00c3b2 100644
--- a/gcc/cp/typeck2.c
+++ b/gcc/cp/typeck2.c
@@ -951,7 +951,7 @@ check_narrowing (tree type, tree init, tsubst_flags_t complain)
 	{
 	  if (complain & tf_warning)
 	    warning_at (loc, OPT_Wnarrowing, "narrowing conversion of %qE "
-			"from %qT to %qT inside { } is ill-formed in C++11",
+			"from %H to %I inside { } is ill-formed in C++11",
 			init, ftype, type);
 	  ok = true;
 	}
@@ -962,7 +962,7 @@ check_narrowing (tree type, tree init, tsubst_flags_t complain)
 	      if ((!almost_ok || pedantic)
 		  && pedwarn (loc, OPT_Wnarrowing,
 			      "narrowing conversion of %qE "
-			      "from %qT to %qT inside { }",
+			      "from %H to %I inside { }",
 			      init, ftype, type)
 		  && almost_ok)
 		inform (loc, " the expression has a constant value but is not "
@@ -975,7 +975,7 @@ check_narrowing (tree type, tree init, tsubst_flags_t complain)
 	  int savederrorcount = errorcount;
 	  global_dc->pedantic_errors = 1;
 	  pedwarn (loc, OPT_Wnarrowing,
-		   "narrowing conversion of %qE from %qT to %qT "
+		   "narrowing conversion of %qE from %H to %I "
 		   "inside { }", init, ftype, type);
 	  if (errorcount == savederrorcount)
 	    ok = true;
diff --git a/gcc/diagnostic-color.c b/gcc/diagnostic-color.c
index 8353fe0..4943008 100644
--- a/gcc/diagnostic-color.c
+++ b/gcc/diagnostic-color.c
@@ -174,6 +174,7 @@ static struct color_cap color_dict[] =
   { "diff-hunk", SGR_SEQ (COLOR_FG_CYAN), 9, false },
   { "diff-delete", SGR_SEQ (COLOR_FG_RED), 11, false },
   { "diff-insert", SGR_SEQ (COLOR_FG_GREEN), 11, false },
+  { "type-diff", SGR_SEQ (COLOR_BOLD COLOR_SEPARATOR COLOR_FG_GREEN), 9, false },
   { NULL, NULL, 0, false }
 };
 
diff --git a/gcc/fortran/error.c b/gcc/fortran/error.c
index ccf0be0..9ec54d6 100644
--- a/gcc/fortran/error.c
+++ b/gcc/fortran/error.c
@@ -919,7 +919,8 @@ static bool
 gfc_format_decoder (pretty_printer *pp,
 		    text_info *text, const char *spec,
 		    int precision ATTRIBUTE_UNUSED, bool wide ATTRIBUTE_UNUSED,
-		    bool plus ATTRIBUTE_UNUSED, bool hash ATTRIBUTE_UNUSED)
+		    bool plus ATTRIBUTE_UNUSED, bool hash ATTRIBUTE_UNUSED,
+		    const char **buffer_ptr ATTRIBUTE_UNUSED)
 {
   switch (*spec)
     {
diff --git a/gcc/pretty-print.c b/gcc/pretty-print.c
index bcb1a70..d625cdb 100644
--- a/gcc/pretty-print.c
+++ b/gcc/pretty-print.c
@@ -677,7 +677,8 @@ pp_format (pretty_printer *pp, text_info *text)
 
 	    gcc_assert (pp_format_decoder (pp));
 	    ok = pp_format_decoder (pp) (pp, text, p,
-					 precision, wide, plus, hash);
+					 precision, wide, plus, hash,
+					 formatters[argno]);
 	    gcc_assert (ok);
 	  }
 	}
@@ -696,6 +697,11 @@ pp_format (pretty_printer *pp, text_info *text)
     for (; argno < PP_NL_ARGMAX; argno++)
       gcc_assert (!formatters[argno]);
 
+  /* If the client supplied a postprocessing object, call its "handle"
+     hook here.  */
+  if (pp->m_format_postprocessor)
+    pp->m_format_postprocessor->handle (pp); // FIXME
+
   /* Revert to normal obstack and wrapping mode.  */
   buffer->obstack = &buffer->formatted_obstack;
   buffer->line_length = 0;
@@ -847,6 +853,7 @@ pretty_printer::pretty_printer (const char *p, int l)
     indent_skip (),
     wrapping (),
     format_decoder (),
+    m_format_postprocessor (),
     emitted_prefix (),
     need_newline (),
     translate_identifiers (true),
@@ -860,6 +867,8 @@ pretty_printer::pretty_printer (const char *p, int l)
 
 pretty_printer::~pretty_printer ()
 {
+  if (m_format_postprocessor)
+    delete m_format_postprocessor;
   buffer->~output_buffer ();
   XDELETE (buffer);
 }
diff --git a/gcc/pretty-print.h b/gcc/pretty-print.h
index 2596678..5635bf8 100644
--- a/gcc/pretty-print.h
+++ b/gcc/pretty-print.h
@@ -180,11 +180,20 @@ struct pp_wrapping_mode_t
    A client-supplied formatter returns true if everything goes well,
    otherwise it returns false.  */
 typedef bool (*printer_fn) (pretty_printer *, text_info *, const char *,
-			    int, bool, bool, bool);
+			    int, bool, bool, bool, const char **);
 
 /* Client supplied function used to decode formats.  */
 #define pp_format_decoder(PP) (PP)->format_decoder
 
+/* Base class for an optional client-supplied object for doing additional
+   processing between stages 2 and 3 of formatted printing.  */
+class format_postprocessor
+{
+ public:
+  virtual ~format_postprocessor () {}
+  virtual void handle (pretty_printer *) = 0;
+};
+
 /* TRUE if a newline character needs to be added before further
    formatting.  */
 #define pp_needs_newline(PP)  (PP)->need_newline
@@ -239,9 +248,16 @@ struct pretty_printer
      If the BUFFER needs additional characters from the format string, it
      should advance the TEXT->format_spec as it goes.  When FORMAT_DECODER
      returns, TEXT->format_spec should point to the last character processed.
-  */
+     The BUFFER_PTR is passed in, to allow for deferring-handling of format
+     codes (e.g. %H and %I in the C++ frontend).  */
   printer_fn format_decoder;
 
+  /* If non-NULL, this is called by pp_format once after all format codes
+     have been processed, to allow for client-specific postprocessing.
+     This is used by the C++ frontend for handling the %H and %I
+     format codes (which interract with each other).  */
+  format_postprocessor *m_format_postprocessor;
+
   /* Nonzero if current PREFIX was emitted at least once.  */
   bool emitted_prefix;
 
diff --git a/gcc/testsuite/g++.dg/template/show-template-tree-color.C b/gcc/testsuite/g++.dg/template/show-template-tree-color.C
new file mode 100644
index 0000000..7859e53
--- /dev/null
+++ b/gcc/testsuite/g++.dg/template/show-template-tree-color.C
@@ -0,0 +1,42 @@
+// { dg-options "-fdiagnostics-show-template-tree -fdiagnostics-color=always" }
+
+template<typename> struct vector {};
+template<typename, typename> struct map {};
+
+void fn_1(vector<int>);
+void fn_2(map<int, int>);
+void fn_3(vector<map<int, float> >);
+
+void test ()
+{
+  fn_1 (vector<int> ());
+  fn_1 (vector<double> ()); // { dg-error "could not convert 'vector<double>\\(\\)' from 'vector<double>' to 'vector<int>'" }
+  /* { dg-begin-multiline-output "" }
+  vector<
+    [double != int]>
+     { dg-end-multiline-output "" } */
+  fn_1 (map<int, int> ()); // { dg-error "could not convert 'map<int, int>\\(\\)' from 'map<int, int>' to 'vector<int>'" }
+
+  fn_2 (map<int, int>());
+  fn_2 (map<int, double>());  // { dg-error "could not convert 'map<int, double>\\(\\)' from 'map<int, double>' to 'map<int, int>'" }
+  /* { dg-begin-multiline-output "" }
+  map<
+    [...],
+    [double != int]>
+     { dg-end-multiline-output "" } */
+  fn_2 (map<double, double>());  // { dg-error "could not convert 'map<double, double>\\(\\)' from 'map<double, double>' to 'map<int, int>'" }
+  /* { dg-begin-multiline-output "" }
+  map<
+    [double != int],
+    [double != int]>
+     { dg-end-multiline-output "" } */
+
+  fn_3 (vector<map<int, float> >());
+  fn_3 (vector<map<int, double> >());  // { dg-error "could not convert 'vector<map<int, double> >\\(\\)' from 'vector<map<int, double> >' to 'vector<map<int, float> >'" }
+  /* { dg-begin-multiline-output "" }
+  vector<
+    map<
+      [...],
+      [double != float]>>
+     { dg-end-multiline-output "" } */
+}
diff --git a/gcc/testsuite/g++.dg/template/show-template-tree-no-elide-type.C b/gcc/testsuite/g++.dg/template/show-template-tree-no-elide-type.C
new file mode 100644
index 0000000..fe2b549
--- /dev/null
+++ b/gcc/testsuite/g++.dg/template/show-template-tree-no-elide-type.C
@@ -0,0 +1,42 @@
+// { dg-options "-fdiagnostics-show-template-tree -fno-elide-type" }
+
+template<typename> struct vector {};
+template<typename, typename> struct map {};
+
+void fn_1(vector<int>);
+void fn_2(map<int, int>);
+void fn_3(vector<map<int, float> >);
+
+void test ()
+{
+  fn_1 (vector<int> ());
+  fn_1 (vector<double> ()); // { dg-error "could not convert 'vector<double>\\(\\)' from 'vector<double>' to 'vector<int>'" }
+  /* { dg-begin-multiline-output "" }
+  vector<
+    [double != int]>
+     { dg-end-multiline-output "" } */
+  fn_1 (map<int, int> ()); // { dg-error "could not convert 'map<int, int>\\(\\)' from 'map<int, int>' to 'vector<int>'" }
+
+  fn_2 (map<int, int>());
+  fn_2 (map<int, double>());  // { dg-error "could not convert 'map<int, double>\\(\\)' from 'map<int, double>' to 'map<int, int>'" }
+  /* { dg-begin-multiline-output "" }
+  map<
+    int,
+    [double != int]>
+     { dg-end-multiline-output "" } */
+  fn_2 (map<double, double>());  // { dg-error "could not convert 'map<double, double>\\(\\)' from 'map<double, double>' to 'map<int, int>'" }
+  /* { dg-begin-multiline-output "" }
+  map<
+    [double != int],
+    [double != int]>
+     { dg-end-multiline-output "" } */
+
+  fn_3 (vector<map<int, float> >());
+  fn_3 (vector<map<int, double> >());  // { dg-error "could not convert 'vector<map<int, double> >\\(\\)' from 'vector<map<int, double> >' to 'vector<map<int, float> >'" }
+  /* { dg-begin-multiline-output "" }
+  vector<
+    map<
+      int,
+      [double != float]>>
+     { dg-end-multiline-output "" } */
+}
diff --git a/gcc/testsuite/g++.dg/template/show-template-tree.C b/gcc/testsuite/g++.dg/template/show-template-tree.C
new file mode 100644
index 0000000..9737db6
--- /dev/null
+++ b/gcc/testsuite/g++.dg/template/show-template-tree.C
@@ -0,0 +1,43 @@
+// { dg-options "-fdiagnostics-show-template-tree" }
+
+template<typename> struct vector {};
+template<typename, typename> struct map {};
+
+void fn_1(vector<int>);
+void fn_2(map<int, int>);
+void fn_3(vector<map<int, float> >);
+
+void test ()
+{
+  fn_1 (vector<int> ());
+  fn_1 (42); // { dg-error "could not convert '42' from 'int' to 'vector<int>'" }
+  fn_1 (vector<double> ()); // { dg-error "could not convert 'vector<double>\\(\\)' from 'vector<double>' to 'vector<int>'" }
+  /* { dg-begin-multiline-output "" }
+  vector<
+    [double != int]>
+     { dg-end-multiline-output "" } */
+  fn_1 (map<int, int> ()); // { dg-error "could not convert 'map<int, int>\\(\\)' from 'map<int, int>' to 'vector<int>'" }
+
+  fn_2 (map<int, int>());
+  fn_2 (map<int, double>());  // { dg-error "could not convert .map<int, double>\\(\\). from .map<....., double>. to .map<....., int>." }
+  /* { dg-begin-multiline-output "" }
+  map<
+    [...],
+    [double != int]>
+     { dg-end-multiline-output "" } */
+  fn_2 (map<double, double>());  // { dg-error "could not convert .map<double, double>\\(\\). from .map<double, double>. to .map<int, int>." }
+  /* { dg-begin-multiline-output "" }
+  map<
+    [double != int],
+    [double != int]>
+     { dg-end-multiline-output "" } */
+
+  fn_3 (vector<map<int, float> >());
+  fn_3 (vector<map<int, double> >());  // { dg-error "could not convert 'vector<map<int, double> >\\(\\)' from 'vector<map<....., double>>' to 'vector<map<....., float>>'" }
+  /* { dg-begin-multiline-output "" }
+  vector<
+    map<
+      [...],
+      [double != float]>>
+     { dg-end-multiline-output "" } */
+}
diff --git a/gcc/tree-diagnostic.c b/gcc/tree-diagnostic.c
index 7edea0e..b004f24 100644
--- a/gcc/tree-diagnostic.c
+++ b/gcc/tree-diagnostic.c
@@ -245,7 +245,8 @@ virt_loc_aware_diagnostic_finalizer (diagnostic_context *context,
 /* Default tree printer.   Handles declarations only.  */
 static bool
 default_tree_printer (pretty_printer *pp, text_info *text, const char *spec,
-		      int precision, bool wide, bool set_locus, bool hash)
+		      int precision, bool wide, bool set_locus, bool hash,
+		      const char **)
 {
   tree t;
 
-- 
1.8.5.3

