public inbox for gcc-patches@gcc.gnu.org
 help / color / mirror / Atom feed
* [PATCH 2/2] Use %H and %I throughout C++ frontend.
  2017-05-05  0:45 [PATCH 1/2] C++ template type diff printing David Malcolm
@ 2017-05-04 23:13 ` David Malcolm
  2017-05-08 11:48 ` [PATCH 1/2] C++ template type diff printing Nathan Sidwell
  2017-05-09 15:55 ` Jakub Jelinek
  2 siblings, 0 replies; 17+ messages in thread
From: David Malcolm @ 2017-05-04 23:13 UTC (permalink / raw)
  To: gcc-patches; +Cc: David Malcolm

This is the second half of the kit, which uses %H and %I throughout
the C++ frontend whenever describing type mismatches between a pair
of %qT.

gcc/cp/ChangeLog:
	* call.c (print_conversion_rejection): Replace pairs of %qT with
	%H and %I in various places.
	(build_user_type_conversion_1): Likewise.
	(build_integral_nontype_arg_conv): Likewise.
	(build_conditional_expr_1): Likewise.
	(convert_like_real): Likewise.
	(convert_arg_to_ellipsis): Likewise.
	(joust): Likewise.
	(initialize_reference): Likewise.
	* cvt.c (cp_convert_to_pointer): Likewise.
	(cp_convert_to_pointer): Likewise.
	(convert_to_reference): Likewise.
	(ocp_convert): Likewise.
	* typeck.c (cp_build_binary_op): Likewise.
	(convert_member_func_to_ptr): Likewise.
	(build_reinterpret_cast_1): Likewise.
	(convert_for_assignment): Likewise.
	* typeck2.c (check_narrowing): Likewise.
---
 gcc/cp/call.c    | 38 +++++++++++++++++++-------------------
 gcc/cp/cvt.c     | 18 +++++++++---------
 gcc/cp/typeck.c  | 22 +++++++++++-----------
 gcc/cp/typeck2.c |  6 +++---
 4 files changed, 42 insertions(+), 42 deletions(-)

diff --git a/gcc/cp/call.c b/gcc/cp/call.c
index 737f312..3b7d3e3 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.  */
 
@@ -4833,14 +4833,14 @@ build_conditional_expr_1 (location_t loc, tree arg1, tree arg2, tree arg3,
 	  if (unsafe_conversion_p (loc, stype, arg2, false))
 	    {
 	      if (complain & tf_error)
-		error_at (loc, "conversion of scalar %qT to vector %qT "
+		error_at (loc, "conversion of scalar %H to vector %I "
 			       "involves truncation", arg2_type, vtype);
 	      return error_mark_node;
 	    }
 	  if (unsafe_conversion_p (loc, stype, arg3, false))
 	    {
 	      if (complain & tf_error)
-		error_at (loc, "conversion of scalar %qT to vector %qT "
+		error_at (loc, "conversion of scalar %H to vector %I "
 			       "involves truncation", arg3_type, vtype);
 	      return error_mark_node;
 	    }
@@ -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);
 
@@ -6603,7 +6603,7 @@ convert_like_real (conversion *convs, tree expr, tree fn, int argnum,
 	 from std::nullptr_t requires direct-initialization.  */
       if (NULLPTR_TYPE_P (TREE_TYPE (expr))
 	  && TREE_CODE (totype) == BOOLEAN_TYPE)
-	complained = permerror (loc, "converting to %qT from %qT requires "
+	complained = permerror (loc, "converting to %H from %I requires "
 				"direct-initialization",
 				totype, TREE_TYPE (expr));
 
@@ -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),
@@ -6924,14 +6924,14 @@ convert_like_real (conversion *convs, tree expr, tree fn, int argnum,
 	    tree extype = TREE_TYPE (expr);
 	    if (TYPE_REF_IS_RVALUE (ref_type)
 		&& lvalue_p (expr))
-	      error_at (loc, "cannot bind rvalue reference of type %qT to "
-                        "lvalue of type %qT", totype, extype);
+	      error_at (loc, "cannot bind rvalue reference of type %H to "
+                        "lvalue of type %I", totype, extype);
 	    else if (!TYPE_REF_IS_RVALUE (ref_type) && !lvalue_p (expr)
 		     && !CP_TYPE_CONST_NON_VOLATILE_P (TREE_TYPE (ref_type)))
 	      error_at (loc, "cannot bind non-const lvalue reference of "
-			"type %qT to an rvalue of type %qT", totype, extype);
+			"type %H to an rvalue of type %I", totype, extype);
 	    else if (!reference_compatible_p (TREE_TYPE (totype), extype))
-	      error_at (loc, "binding reference of type %qT to %qT "
+	      error_at (loc, "binding reference of type %H to %I "
 			"discards qualifiers", totype, extype);
 	    else
 	      gcc_unreachable ();
@@ -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);
@@ -9643,7 +9643,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");
@@ -10417,11 +10417,11 @@ initialize_reference (tree type, tree expr,
 		   && !TYPE_REF_IS_RVALUE (type)
 		   && !lvalue_p (expr))
 	    error_at (loc, "invalid initialization of non-const reference of "
-		      "type %qT from an rvalue of type %qT",
+		      "type %H from an rvalue of type %I",
 		      type, TREE_TYPE (expr));
 	  else
 	    error_at (loc, "invalid initialization of reference of type "
-		      "%qT from expression of type %qT", type,
+		      "%H from expression of type %I", type,
 		      TREE_TYPE (expr));
 	}
       return error_mark_node;
diff --git a/gcc/cp/cvt.c b/gcc/cp/cvt.c
index 5f4b5e3..1064e63 100644
--- a/gcc/cp/cvt.c
+++ b/gcc/cp/cvt.c
@@ -86,7 +86,7 @@ cp_convert_to_pointer (tree type, tree expr, bool dofold,
       if (!COMPLETE_TYPE_P (intype))
 	{
 	  if (complain & tf_error)
-	    error_at (loc, "can%'t convert from incomplete type %qT to %qT",
+	    error_at (loc, "can%'t convert from incomplete type %H to %I",
 		      intype, type);
 	  return error_mark_node;
 	}
@@ -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;
 	}
@@ -168,7 +168,7 @@ cp_convert_to_pointer (tree type, tree expr, bool dofold,
       if (TYPE_PTRMEMFUNC_P (type))
 	{
 	  if (complain & tf_error)
-	    error_at (loc, "cannot convert %qE from type %qT to type %qT",
+	    error_at (loc, "cannot convert %qE from type %H to type %I",
 		      expr, intype, type);
 	  return error_mark_node;
 	}
@@ -195,7 +195,7 @@ cp_convert_to_pointer (tree type, tree expr, bool dofold,
 	    }
 	}
       if (complain & tf_error)
-	error_at (loc, "cannot convert %qE from type %qT to type %qT",
+	error_at (loc, "cannot convert %qE from type %H to type %I",
 		  expr, intype, type);
       return error_mark_node;
     }
@@ -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;
     }
 
@@ -244,7 +244,7 @@ cp_convert_to_pointer (tree type, tree expr, bool dofold,
     return instantiate_type (type, expr, complain);
 
   if (complain & tf_error)
-    error_at (loc, "cannot convert %qE from type %qT to type %qT",
+    error_at (loc, "cannot convert %qE from type %H to type %I",
 	      expr, 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;
@@ -514,7 +514,7 @@ convert_to_reference (tree reftype, tree expr, int convtype,
     }
 
   if (complain & tf_error)
-    error_at (loc, "cannot convert type %qT to type %qT", intype, reftype);
+    error_at (loc, "cannot convert type %H to type %I", intype, reftype);
 
   return error_mark_node;
 }
@@ -907,7 +907,7 @@ ocp_convert (tree type, tree expr, int convtype, int flags,
       if (invalid_nonstatic_memfn_p (loc, expr, complain))
 	/* We displayed the error message.  */;
       else
-	error_at (loc, "conversion from %qT to non-scalar type %qT requested",
+	error_at (loc, "conversion from %H to non-scalar type %I requested",
 		  TREE_TYPE (expr), type);
     }
   return error_mark_node;
diff --git a/gcc/cp/typeck.c b/gcc/cp/typeck.c
index 7aee0d6..6f3f327 100644
--- a/gcc/cp/typeck.c
+++ b/gcc/cp/typeck.c
@@ -5011,7 +5011,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);
@@ -7028,7 +7028,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);
@@ -7150,7 +7150,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;
@@ -7190,7 +7190,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
@@ -8504,33 +8504,33 @@ convert_for_assignment (tree type, tree rhs,
 		    return r;
 		}
 	      else if (fndecl)
-		error ("cannot convert %qT to %qT for argument %qP to %qD",
+		error ("cannot convert %H to %I for argument %qP to %qD",
 		       rhstype, type, parmnum, fndecl);
 	      else
 		switch (errtype)
 		  {
 		    case ICR_DEFAULT_ARGUMENT:
-		      error ("cannot convert %qT to %qT in default argument",
+		      error ("cannot convert %H to %I in default argument",
 			     rhstype, type);
 		      break;
 		    case ICR_ARGPASS:
-		      error ("cannot convert %qT to %qT in argument passing",
+		      error ("cannot convert %H to %I in argument passing",
 			     rhstype, type);
 		      break;
 		    case ICR_CONVERTING:
-		      error ("cannot convert %qT to %qT",
+		      error ("cannot convert %H to %I",
 			     rhstype, type);
 		      break;
 		    case ICR_INIT:
-		      error ("cannot convert %qT to %qT in initialization",
+		      error ("cannot convert %H to %I in initialization",
 			     rhstype, type);
 		      break;
 		    case ICR_RETURN:
-		      error ("cannot convert %qT to %qT in return",
+		      error ("cannot convert %H to %I in return",
 			     rhstype, type);
 		      break;
 		    case ICR_ASSIGN:
-		      error ("cannot convert %qT to %qT in assignment",
+		      error ("cannot convert %H to %I in assignment",
 			     rhstype, type);
 		      break;
 		    default:
diff --git a/gcc/cp/typeck2.c b/gcc/cp/typeck2.c
index 1f0eb45..cf14480 100644
--- a/gcc/cp/typeck2.c
+++ b/gcc/cp/typeck2.c
@@ -954,7 +954,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;
 	}
@@ -965,7 +965,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 "
@@ -978,7 +978,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;
-- 
1.8.5.3

^ permalink raw reply	[flat|nested] 17+ messages in thread

* [PATCH 1/2] C++ template type diff printing
@ 2017-05-05  0:45 David Malcolm
  2017-05-04 23:13 ` [PATCH 2/2] Use %H and %I throughout C++ frontend David Malcolm
                   ` (2 more replies)
  0 siblings, 3 replies; 17+ messages in thread
From: David Malcolm @ 2017-05-05  0:45 UTC (permalink / raw)
  To: gcc-patches; +Cc: David Malcolm

This patch kit implements two new options to make it easier
to read diagnostics involving mismatched template types:
  -fdiagnostics-show-template-tree and
  -fno-elide-type.

It adds two new formatting codes: %H and %I which are
equivalent to %qT, but are to be used together for type
comparisons e.g.
  "can't convert from %H to %I".

The formatters work together, and if they're used with template
types, they highlight the differences between the types via color.

For example in:

 #include <map>
 #include <vector>
 using std::vector;
 using std::map;

 void takes_mivf (map<int, vector<float> > v);

 int test ()
 {
    takes_mivf (map<int, vector<double> > ());
 }

rather than printing:

  could not convert 'std::map<int, std::vector<double> >()'
    from 'std::map<int, std::vector<double> >' to 'std::map<int, std::vector<float> >'

with -felide-type (the default), it prints:

  could not convert 'std::map<int, std::vector<double> >()'
    from 'map<[...],vector<double>>' to 'map<[...],vector<float>>

where "[...]"  is used to elide matching parts of the template,
and the different parts ("double" and "float") are colorized so
they catch the reader's eye.

With -fdiagnostics-show-template-tree a tree-like structure of the
template is printed, showing the differences; in this case:

  map<
    [...],
    vector<
      [double != float]>>

again with colorization of the different parts.

You can see a colorized verison of this here:
  https://dmalcolm.fedorapeople.org/gcc/2017-05-04/template-tree.html

With -fno-elide-type, the output is as before the patch, but colorized;
an example can be seen here:
  https://dmalcolm.fedorapeople.org/gcc/2017-05-04/template-tree-fno-elide-type.html

Implementing %H and %I is slightly fiddly: given that they affect
each other, printing the types in pp_format has to be delayed until
both are seen, so the patch adds an optional hook to pretty-printers
to allow for a post-processing stage after stage 2 of pp_format, which
the C++ frontend implements by doing the comparison printing.
In an earlier version of this patch I had a single format code which
was printed as "from %qT to %qT", but I realized that that wasn't going
to support i18n, so I went with the two format codes.

To keep the patch simpler to review, I've only converted one
diagnostic to using them (which is the one used by the testcases);
the rest are converted in the followup patch.

The option names are chosen to be the same as the equivalent
options in clang.

The combination of the two patches  was successfully
bootstrapped&regrtested on x86_64-pc-linux-gnu.

OK for trunk?

gcc/c-family/ChangeLog:
	* c-format.c (gcc_cxxdiag_char_table): Add 'H' and 'I' to
	format_chars.
	* c.opt (fdiagnostics-show-template-tree): New option.
	(felide-type): New option.

gcc/c/ChangeLog:
	* c-objc-common.c (c_tree_printer): Gain a const char **
	parameter.

gcc/cp/ChangeLog:
	* call.c (perform_implicit_conversion_flags): Convert
	"from %qT to %qT" to "from %H to %I" in diagnostic.
	* error.c (struct deferred_printed_type): New struct.
	(class cxx_format_postprocessor): New class.
	(cxx_initialize_diagnostics): Wire up a cxx_format_postprocessor
	to pp->m_format_postprocessor.
	(comparable_template_types_p): New function.
	(newline_and_indent): New function.
	(arg_to_string): New function.
	(print_nonequal_arg): New function.
	(type_to_string_with_compare): New function.
	(print_template_tree_comparison): New function.
	(append_formatted_chunk): New function.
	(add_quotes): New function.
	(cxx_format_postprocessor::handle): New function.
	(defer_half_of_type_diff): New function.
	(cp_printer): Add "buffer_ptr" param.  Implement %H and %I.

gcc/ChangeLog:
	* diagnostic-color.c (color_dict): Add "type-diff".
	(parse_gcc_colors): Update comment.
	* doc/invoke.texi (Diagnostic Message Formatting Options): Add
	-fdiagnostics-show-template-tree and -fno-elide-type.
	(GCC_COLORS): Add type-diff to example.
	(type-diff=): New.
	(-fdiagnostics-show-template-tree): New.
	(-fno-elide-type): New.
	* tree-diagnostic.c (default_tree_printer): Update for new
	const char ** param.
	* tree-diagnostic.h (default_tree_printer): Likewise.
	* pretty-print.c (pp_format): Pass formatters[argno] to the
	pp_format_decoder callback.  Call any m_format_postprocessor's
	"handle" method.
	(pretty_printer::pretty_printer): Initialize
	m_format_postprocessor.
	(pretty_printer::~pretty_printer): Delete any
	m_format_postprocessor.
	* pretty-print.h (printer_fn): Add a const char ** parameter.
	(class format_postprocessor): New class.
	(struct pretty_printer::format_decoder): Document the new
	parameter.
	(struct pretty_printer::m_format_postprocessor): New field.

gcc/fortran/ChangeLog:
	* error.c (gfc_format_decoder): Update for new const char ** param.

gcc/testsuite/ChangeLog:
	* g++.dg/plugin/plugin.exp (plugin_test_list): Add...
	* g++.dg/plugin/show-template-tree-color-no-elide-type.C: New
	test case.
	* g++.dg/plugin/show-template-tree-color.C: New test case.
	* g++.dg/plugin/show_template_tree_color_plugin.c: New plugin.
	* g++.dg/template/show-template-tree-2.C: New test case.
	* g++.dg/template/show-template-tree-3.C: New test case.
	* g++.dg/template/show-template-tree-no-elide-type.C: New test case.
	* g++.dg/template/show-template-tree.C: New test case.
---
 gcc/c-family/c-format.c                            |   2 +-
 gcc/c-family/c.opt                                 |   8 +
 gcc/c/c-objc-common.c                              |   5 +-
 gcc/cp/call.c                                      |   2 +-
 gcc/cp/error.c                                     | 421 ++++++++++++++++++++-
 gcc/diagnostic-color.c                             |   6 +-
 gcc/doc/invoke.texi                                |  50 ++-
 gcc/fortran/error.c                                |   5 +-
 gcc/pretty-print.c                                 |  11 +-
 gcc/pretty-print.h                                 |  20 +-
 gcc/testsuite/g++.dg/plugin/plugin.exp             |   3 +
 .../show-template-tree-color-no-elide-type.C       |  30 ++
 .../g++.dg/plugin/show-template-tree-color.C       |  30 ++
 .../plugin/show_template_tree_color_plugin.c       |  38 ++
 .../g++.dg/template/show-template-tree-2.C         | 118 ++++++
 .../g++.dg/template/show-template-tree-3.C         |  37 ++
 .../template/show-template-tree-no-elide-type.C    |  24 ++
 gcc/testsuite/g++.dg/template/show-template-tree.C |  43 +++
 gcc/tree-diagnostic.c                              |   3 +-
 gcc/tree-diagnostic.h                              |   2 +-
 20 files changed, 841 insertions(+), 17 deletions(-)
 create mode 100644 gcc/testsuite/g++.dg/plugin/show-template-tree-color-no-elide-type.C
 create mode 100644 gcc/testsuite/g++.dg/plugin/show-template-tree-color.C
 create mode 100644 gcc/testsuite/g++.dg/plugin/show_template_tree_color_plugin.c
 create mode 100644 gcc/testsuite/g++.dg/template/show-template-tree-2.C
 create mode 100644 gcc/testsuite/g++.dg/template/show-template-tree-3.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-format.c b/gcc/c-family/c-format.c
index 400eb66..fd40359 100644
--- a/gcc/c-family/c-format.c
+++ b/gcc/c-family/c-format.c
@@ -754,7 +754,7 @@ static const format_char_info gcc_cxxdiag_char_table[] =
   /* Custom conversion specifiers.  */
 
   /* These will require a "tree" at runtime.  */
-  { "ADEFKSTVX",0,STD_C89,{ T89_V,   BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN  }, "q+#",   "",   NULL },
+  { "ADEFHIKSTVX",0,STD_C89,{ T89_V,   BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN  }, "q+#",   "",   NULL },
 
   { "v", 0,STD_C89, { T89_I,   BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN  }, "q#",  "",   NULL },
 
diff --git a/gcc/c-family/c.opt b/gcc/c-family/c.opt
index 9ad2f6e..4648e60 100644
--- a/gcc/c-family/c.opt
+++ b/gcc/c-family/c.opt
@@ -1342,6 +1342,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)
+Print hierarchical comparisons when template types are mismatched.
+
 fdirectives-only
 C ObjC C++ ObjC++
 Preprocess directives only.
@@ -1361,6 +1365,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 c15b8e4..737f312 100644
--- a/gcc/cp/call.c
+++ b/gcc/cp/call.c
@@ -10099,7 +10099,7 @@ 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,
+	    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/error.c b/gcc/cp/error.c
index a83ecb2..be9c78c 100644
--- a/gcc/cp/error.c
+++ b/gcc/cp/error.c
@@ -99,7 +99,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 +163,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.  */
@@ -3565,6 +3606,370 @@ maybe_print_constexpr_context (diagnostic_context *context)
     }
 }
 \f
+
+/* 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);
+}
+
+/* Generate a GC-allocated string for ARG, an expression or type.  */
+
+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);
+}
+
+/* Subroutine to type_to_string_with_compare and
+   print_template_tree_comparison.
+
+   Print a representation of ARG (an expression or type) to PP,
+   colorizing it as "type-diff" if PP->show_color.  */
+
+static void
+print_nonequal_arg (pretty_printer *pp, tree arg, bool verbose)
+{
+  pp_printf (pp, "%r%s%R",
+	     "type-diff",
+	     (arg
+	      ? arg_to_string (arg, verbose)
+	      : G_("(no argument)")));
+}
+
+/* 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".
+
+   Only non-default template arguments are printed.
+
+   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 flags = 0;
+  int len_a = get_non_default_template_args_count (args_a, flags);
+  args_a = INNERMOST_TEMPLATE_ARGS (args_a);
+  int len_b = get_non_default_template_args_count (args_b, flags);
+  args_b = INNERMOST_TEMPLATE_ARGS (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).
+
+   Only non-default template arguments are printed.  */
+
+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);
+  int flags = 0;
+  int len_a = get_non_default_template_args_count (args_a, flags);
+  args_a = INNERMOST_TEMPLATE_ARGS (args_a);
+  int len_b = get_non_default_template_args_count (args_b, flags);
+  args_b = INNERMOST_TEMPLATE_ARGS (args_b);
+  int len_max = MAX (len_a, len_b);
+  gcc_assert (TREE_CODE (args_a) == TREE_VEC);
+  gcc_assert (TREE_CODE (args_b) == TREE_VEC);
+  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, storing the result into the chunk_info
+   for pp_format.
+   Also print the difference in tree form, adding it as
+   an additional chunk.  */
+
+void
+cxx_format_postprocessor::handle (pretty_printer *pp)
+{
+  /* If we have one of %H and %I, the other should have
+     been present.  */
+  if (m_type_a.m_tree || m_type_b.m_tree)
+    {
+      gcc_assert (m_type_a.m_tree);
+      gcc_assert (m_type_b.m_tree);
+    }
+
+  if (m_type_a.m_tree && m_type_b.m_tree)
+    {
+      /* 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;
+
+      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.
+
+   This is called in phase 2 of pp_format, when it is accumulating
+   a series of formatted chunks.  We stash the location of the chunk
+   we're meant to have written to, so that we can write to it in the
+   m_format_postprocessor hook.  */
+
+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.
@@ -3579,10 +3984,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;
@@ -3629,6 +4037,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/diagnostic-color.c b/gcc/diagnostic-color.c
index 8353fe0..6adb872 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 }
 };
 
@@ -204,8 +205,9 @@ colorize_stop (bool show_color)
 /* Parse GCC_COLORS.  The default would look like:
    GCC_COLORS='error=01;31:warning=01;35:note=01;36:\
    range1=32:range2=34:locus=01:quote=01:\
-   fixit-insert=32:fixit-delete=31'\
-   diff-filename=01:diff-hunk=32:diff-delete=31:diff-insert=32'
+   fixit-insert=32:fixit-delete=31:'\
+   diff-filename=01:diff-hunk=32:diff-delete=31:diff-insert=32:\
+   type-diff=01;32'
    No character escaping is needed or supported.  */
 static bool
 parse_gcc_colors (void)
diff --git a/gcc/doc/invoke.texi b/gcc/doc/invoke.texi
index 65308c9..9966fa0 100644
--- a/gcc/doc/invoke.texi
+++ b/gcc/doc/invoke.texi
@@ -251,6 +251,7 @@ Objective-C and Objective-C++ Dialects}.
 -fdiagnostics-color=@r{[}auto@r{|}never@r{|}always@r{]}  @gol
 -fno-diagnostics-show-option  -fno-diagnostics-show-caret @gol
 -fdiagnostics-parseable-fixits  -fdiagnostics-generate-patch @gol
+-fdiagnostics-show-template-tree -fno-elide-type @gol
 -fno-show-column}
 
 @item Warning Options
@@ -3442,7 +3443,8 @@ The default @env{GCC_COLORS} is
 @smallexample
 error=01;31:warning=01;35:note=01;36:range1=32:range2=34:locus=01:\
 quote=01:fixit-insert=32:fixit-delete=31:\
-diff-filename=01:diff-hunk=32:diff-delete=31:diff-insert=32
+diff-filename=01:diff-hunk=32:diff-delete=31:diff-insert=32:\
+type-diff=01;32
 @end smallexample
 @noindent
 where @samp{01;31} is bold red, @samp{01;35} is bold magenta,
@@ -3506,6 +3508,11 @@ SGR substring for deleted lines within generated patches.
 @item diff-insert=
 @vindex diff-insert GCC_COLORS @r{capability}
 SGR substring for inserted lines within generated patches.
+
+@item type-diff=
+@vindex type-diff GCC_COLORS @r{capability}
+SGR substring for highlighting mismatching types within template
+arguments in the C++ frontend.
 @end table
 
 @item -fno-diagnostics-show-option
@@ -3578,6 +3585,47 @@ are printed.  For example:
 The diff may or may not be colorized, following the same rules
 as for diagnostics (see @option{-fdiagnostics-color}).
 
+@item -fdiagnostics-show-template-tree
+@opindex fdiagnostics-show-template-tree
+
+In the C++ frontend, when printing diagnostics showing mismatching
+template types, such as:
+
+@smallexample
+  could not convert 'std::map<int, std::vector<double> >()'
+    from 'map<[...],vector<double>>' to 'map<[...],vector<float>>
+@end smallexample
+
+the @option{-fdiagnostics-show-template-tree} flag enables printing a
+tree-like structure showing the common and differing parts of the types,
+such as:
+
+@smallexample
+  map<
+    [...],
+    vector<
+      [double != float]>>
+@end smallexample
+
+The parts that differ are highlighted with color (``double'' and
+``float'' in this case).
+
+@item -fno-elide-type
+@opindex fno-elide-type
+@opindex felide-type
+By default when the C++ frontend prints diagnostics showing mismatching
+template types, common parts of the types are printed as ``[...]'' to
+simplify the error message.  For example:
+
+@smallexample
+  could not convert 'std::map<int, std::vector<double> >()'
+    from 'map<[...],vector<double>>' to 'map<[...],vector<float>>
+@end smallexample
+
+Specifying the @option{-fno-elide-type} flag suppresses that behavior.
+This flag also affects the output of the
+@option{-fdiagnostics-show-template-tree} flag.
+
 @item -fno-show-column
 @opindex fno-show-column
 Do not print column numbers in diagnostics.  This may be necessary if
diff --git a/gcc/fortran/error.c b/gcc/fortran/error.c
index 0312499..a5a324c 100644
--- a/gcc/fortran/error.c
+++ b/gcc/fortran/error.c
@@ -917,7 +917,8 @@ gfc_notify_std (int std, const char *gmsgid, ...)
 */
 static bool
 gfc_format_decoder (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 **buffer_ptr)
 {
   switch (*spec)
     {
@@ -948,7 +949,7 @@ gfc_format_decoder (pretty_printer *pp, text_info *text, const char *spec,
 	 etc. diagnostics can use the FE printer while the FE is still
 	 active.  */
       return default_tree_printer (pp, text, spec, precision, wide,
-				   set_locus, hash);
+				   set_locus, hash, buffer_ptr);
     }
 }
 
diff --git a/gcc/pretty-print.c b/gcc/pretty-print.c
index bcb1a70..e70a0ed 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);
+
   /* 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 (NULL),
     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/plugin/plugin.exp b/gcc/testsuite/g++.dg/plugin/plugin.exp
index 6a0c1aa..94ebe93 100644
--- a/gcc/testsuite/g++.dg/plugin/plugin.exp
+++ b/gcc/testsuite/g++.dg/plugin/plugin.exp
@@ -65,6 +65,9 @@ set plugin_test_list [list \
     { def_plugin.c def-plugin-test.C } \
     { ../../gcc.dg/plugin/diagnostic_plugin_test_tree_expression_range.c \
 	  diagnostic-test-expressions-1.C } \
+    { show_template_tree_color_plugin.c \
+    	  show-template-tree-color.C \
+    	  show-template-tree-color-no-elide-type.C } \
 ]
 
 foreach plugin_test $plugin_test_list {
diff --git a/gcc/testsuite/g++.dg/plugin/show-template-tree-color-no-elide-type.C b/gcc/testsuite/g++.dg/plugin/show-template-tree-color-no-elide-type.C
new file mode 100644
index 0000000..cab0359
--- /dev/null
+++ b/gcc/testsuite/g++.dg/plugin/show-template-tree-color-no-elide-type.C
@@ -0,0 +1,30 @@
+/* Verify colorization of the output of -fdiagnostics-show-template-tree,
+   and within the %H and %I format codes.
+   Doing so requires a plugin; see the comments in the plugin for the
+   rationale.  */
+
+// { dg-options "-fdiagnostics-show-template-tree -fdiagnostics-color=always -fno-elide-type" }
+
+template<typename> struct vector {};
+template<typename, typename> struct map {};
+
+void fn_1(vector<int>);
+void fn_2(map<int, int>);
+
+void test ()
+{
+  fn_1 (vector<double> ());
+  /* { dg-begin-multiline-output "" }
+could not convert '^[[01m^[[Kvector<double>()^[[m^[[K' from '^[[01m^[[Kvector<^[[01;32m^[[Kdouble^[[m^[[K>^[[m^[[K' to '^[[01m^[[Kvector<^[[01;32m^[[Kint^[[m^[[K>^[[m^[[K'
+  vector<
+    [^[[01;32m^[[Kdouble^[[m^[[K != ^[[01;32m^[[Kint^[[m^[[K]>
+     { dg-end-multiline-output "" } */
+
+  fn_2 (map<int, double>());
+  /* { dg-begin-multiline-output "" }
+could not convert '^[[01m^[[Kmap<int, double>()^[[m^[[K' from '^[[01m^[[Kmap<int,^[[01;32m^[[Kdouble^[[m^[[K>^[[m^[[K' to '^[[01m^[[Kmap<int,^[[01;32m^[[Kint^[[m^[[K>^[[m^[[K'
+  map<
+    int,
+    [^[[01;32m^[[Kdouble^[[m^[[K != ^[[01;32m^[[Kint^[[m^[[K]>
+     { dg-end-multiline-output "" } */
+}
diff --git a/gcc/testsuite/g++.dg/plugin/show-template-tree-color.C b/gcc/testsuite/g++.dg/plugin/show-template-tree-color.C
new file mode 100644
index 0000000..eb99a3e
--- /dev/null
+++ b/gcc/testsuite/g++.dg/plugin/show-template-tree-color.C
@@ -0,0 +1,30 @@
+/* Verify colorization of the output of -fdiagnostics-show-template-tree,
+   and within the %H and %I format codes.
+   Doing so requires a plugin; see the comments in the plugin for the
+   rationale.  */
+
+// { 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 test ()
+{
+  fn_1 (vector<double> ());
+  /* { dg-begin-multiline-output "" }
+could not convert '^[[01m^[[Kvector<double>()^[[m^[[K' from '^[[01m^[[Kvector<^[[01;32m^[[Kdouble^[[m^[[K>^[[m^[[K' to '^[[01m^[[Kvector<^[[01;32m^[[Kint^[[m^[[K>^[[m^[[K'
+  vector<
+    [^[[01;32m^[[Kdouble^[[m^[[K != ^[[01;32m^[[Kint^[[m^[[K]>
+     { dg-end-multiline-output "" } */
+
+  fn_2 (map<int, double>());
+  /* { dg-begin-multiline-output "" }
+could not convert '^[[01m^[[Kmap<int, double>()^[[m^[[K' from '^[[01m^[[Kmap<[...],^[[01;32m^[[Kdouble^[[m^[[K>^[[m^[[K' to '^[[01m^[[Kmap<[...],^[[01;32m^[[Kint^[[m^[[K>^[[m^[[K'
+  map<
+    [...],
+    [^[[01;32m^[[Kdouble^[[m^[[K != ^[[01;32m^[[Kint^[[m^[[K]>
+     { dg-end-multiline-output "" } */
+}
diff --git a/gcc/testsuite/g++.dg/plugin/show_template_tree_color_plugin.c b/gcc/testsuite/g++.dg/plugin/show_template_tree_color_plugin.c
new file mode 100644
index 0000000..af568bf
--- /dev/null
+++ b/gcc/testsuite/g++.dg/plugin/show_template_tree_color_plugin.c
@@ -0,0 +1,38 @@
+/* We want to verify the colorized output of cxx_format_postprocessor,
+   but turning on colorization for everything confuses "dg-error" etc.
+   The color codes in the generated messages would also need escaping
+   for use within dg-error.
+
+   Hence the simplest approach is to provide a custom diagnostic_starter_fn,
+   which does nothing.
+
+   The resulting messages lack the "FILENAME:LINE:COL: error: " prefix
+   and can thus be tested using dg-begin/end-multiline-output.  */
+
+/* { dg-options "-O" } */
+
+#include "gcc-plugin.h"
+#include "config.h"
+#include "system.h"
+#include "coretypes.h"
+#include "plugin-version.h"
+#include "diagnostic.h"
+
+int plugin_is_GPL_compatible;
+
+void
+noop_starter_fn (diagnostic_context *, diagnostic_info *)
+{
+}
+
+int
+plugin_init (struct plugin_name_args *plugin_info,
+	     struct plugin_gcc_version *version)
+{
+  if (!plugin_default_version_check (version, &gcc_version))
+    return 1;
+
+  diagnostic_starter (global_dc) = noop_starter_fn;
+
+  return 0;
+}
diff --git a/gcc/testsuite/g++.dg/template/show-template-tree-2.C b/gcc/testsuite/g++.dg/template/show-template-tree-2.C
new file mode 100644
index 0000000..1cd3a06
--- /dev/null
+++ b/gcc/testsuite/g++.dg/template/show-template-tree-2.C
@@ -0,0 +1,118 @@
+// Tests of -fdiagnostics-show-template-tree with variadic templates
+// { dg-options "-fdiagnostics-show-template-tree -std=c++11" }
+
+template<typename> struct vector {};
+template<typename, typename> struct map {};
+template<typename ... Types> struct var {};
+
+void fn_0(var<>);
+void fn_1(var<int>);
+void fn_2(var<int, int>);
+void fn_3(vector<var<> >);
+void fn_4(vector<var<int> >);
+void fn_5(vector<var<int, int> >);
+
+void test_fn_0 ()
+{
+  fn_0 (var<> ());
+  fn_0 (var<int> ()); // { dg-error "could not convert .* from 'var<int>' to 'var<>'" }
+  /* { dg-begin-multiline-output "" }
+  var<
+    [int != ]>
+     { dg-end-multiline-output "" } */
+  fn_0 (var<int, int> ()); // { dg-error "could not convert .* from 'var<int, int>' to 'var<>'" }
+  /* { dg-begin-multiline-output "" }
+  var<
+    [int, int != ]>
+     { dg-end-multiline-output "" } */
+  fn_0 (vector<var<int> >()); // { dg-error "could not convert .* from 'vector<var<int> >' to 'var<>'" }
+  fn_0 (vector<var<int, int> >());  // { dg-error "could not convert .* from 'vector<var<int, int> >' to 'var<>'" }
+}
+
+void test_fn_1 ()
+{
+  fn_1 (var<> ()); // { dg-error "could not convert .* from 'var<>' to 'var<int>'" }
+  /* { dg-begin-multiline-output "" }
+  var<
+    [ != int]>
+     { dg-end-multiline-output "" } */
+  fn_1 (var<int> ());
+  fn_1 (var<int, int> ()); // { dg-error "could not convert .* from 'var<int, int>' to 'var<int>'" }
+  /* { dg-begin-multiline-output "" }
+  var<
+    [int, int != int]>
+     { dg-end-multiline-output "" } */
+  fn_1 (vector<var<int> >()); // { dg-error "could not convert .* from 'vector<var<int> >' to 'var<int>'" }
+  fn_1 (vector<var<int, int> >()); // { dg-error "could not convert .* from 'vector<var<int, int> >' to 'var<int>'" }
+}
+
+void test_fn_2 ()
+{
+  fn_2 (var<> ()); // { dg-error "could not convert .* from 'var<>' to 'var<int, int>'" }
+  /* { dg-begin-multiline-output "" }
+  var<
+    [ != int, int]>
+     { dg-end-multiline-output "" } */
+  fn_2 (var<int> ()); // { dg-error "could not convert .* from 'var<int>' to 'var<int, int>'" }
+  /* { dg-begin-multiline-output "" }
+  var<
+    [int != int, int]>
+     { dg-end-multiline-output "" } */
+  fn_2 (var<int, int> ());
+  fn_2 (vector<var<int> >()); // { dg-error "could not convert .* from 'vector<var<int> >' to 'var<int, int>'" }
+  fn_2 (vector<var<int, int> >()); // { dg-error "could not convert .* from 'vector<var<int, int> >' to 'var<int, int>'" }
+}
+
+void test_fn_3 ()
+{
+  fn_3 (var<> ()); // { dg-error "could not convert .* from 'var<>' to 'vector<var<> >'" }
+  fn_3 (var<int> ()); // { dg-error "could not convert .* from 'var<int>' to 'vector<var<> >'" }
+  fn_3 (var<int, int> ()); // { dg-error "could not convert .* from 'var<int, int>' to 'vector<var<> >'" }
+  fn_3 (vector<var<> >());
+  fn_3 (vector<var<int> >());  // { dg-error "could not convert .* from 'vector<var<int>>' to 'vector<var<>>'" }
+  /* { dg-begin-multiline-output "" }
+  vector<
+    var<
+      [int != ]>>
+     { dg-end-multiline-output "" } */
+  fn_3 (vector<var<int, int> >()); // { dg-error "could not convert .* from 'vector<var<int, int>>' to 'vector<var<>>'" }
+  /* { dg-begin-multiline-output "" }
+  vector<
+    var<
+      [int, int != ]>>
+     { dg-end-multiline-output "" } */
+}
+
+void test_fn_4 ()
+{
+  fn_4 (var<> ()); // { dg-error "could not convert .* from 'var<>' to 'vector<var<int> >'" }
+  fn_4 (var<int> ()); // { dg-error "could not convert .* from 'var<int>' to 'vector<var<int> >'" }
+  fn_4 (var<int, int> ()); // { dg-error "could not convert .* from 'var<int, int>' to 'vector<var<int> >'" }
+  fn_4 (vector<var<> >()); // { dg-error "could not convert .* from 'vector<var<>>' to 'vector<var<int>>'" }
+  /* { dg-begin-multiline-output "" }
+  vector<
+    var<
+      [ != int]>>
+     { dg-end-multiline-output "" } */
+  fn_4 (vector<var<int> >()); 
+  fn_4 (vector<var<int, int> >()); // { dg-error "could not convert .* from 'vector<var<int, int>>' to 'vector<var<int>>'" }
+  /* { dg-begin-multiline-output "" }
+  vector<
+    var<
+      [int, int != int]>>
+     { dg-end-multiline-output "" } */
+}
+
+void test_fn_5 ()
+{
+  fn_5 (var<> ()); // { dg-error "could not convert .* from 'var<>' to 'vector<var<int, int> >'" }
+  fn_5 (var<int> ());  // { dg-error "could not convert .* from 'var<int>' to 'vector<var<int, int> >'" }
+  fn_5 (var<int, int> ());  // { dg-error "could not convert .* from 'var<int, int>' to 'vector<var<int, int> >'" }
+  fn_5 (vector<var<int> >());  // { dg-error "could not convert .* from 'vector<var<int>>' to 'vector<var<int, int>>'" }
+  /* { dg-begin-multiline-output "" }
+  vector<
+    var<
+      [int != int, int]>>
+     { dg-end-multiline-output "" } */
+  fn_5 (vector<var<int, int> >());
+}
diff --git a/gcc/testsuite/g++.dg/template/show-template-tree-3.C b/gcc/testsuite/g++.dg/template/show-template-tree-3.C
new file mode 100644
index 0000000..0eda40b
--- /dev/null
+++ b/gcc/testsuite/g++.dg/template/show-template-tree-3.C
@@ -0,0 +1,37 @@
+/* End-to-end test of -fdiagnostics-show-template-tree and -felide-type
+   using the STL.
+   In particular, ensure that we don't print the default arguments e.g.
+   rather than printing
+     from 'vector<double,allocator<double>>' to 'vector<float,allocator<float>>'
+   (albeit with differences nicely color-coded), we want to print:
+     from 'vector<double>' to 'vector<float>'
+   (again, with the "double" and "float" highlighted, though we can't test
+   for that in this case).  */
+
+// { dg-options "-fdiagnostics-show-template-tree" }
+
+#include <map>
+#include <vector>
+
+using std::vector;
+using std::map;
+
+void takes_vf (vector<float> v);
+void takes_mivf (map<int, vector<float> > v);
+
+int test ()
+{
+  takes_vf (vector<double> ()); // { dg-error "could not convert '.*' from 'vector<double>' to 'vector<float>'" }
+  /* { dg-begin-multiline-output "" }
+  vector<
+    [double != float]>
+     { dg-end-multiline-output "" } */
+
+  takes_mivf (map<int, vector<double> > ()); // { dg-error "could not convert '.*' from 'map<.\\.\\.\\..,vector<double>>' to 'map<.\\.\\.\\..,vector<float>>'" }
+  /* { dg-begin-multiline-output "" }
+  map<
+    [...],
+    vector<
+      [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..d4bfa81
--- /dev/null
+++ b/gcc/testsuite/g++.dg/template/show-template-tree-no-elide-type.C
@@ -0,0 +1,24 @@
+// { 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<double> ()); // { dg-error "could not convert .* from 'vector<double>' to 'vector<int>'" }
+  /* { dg-begin-multiline-output "" }
+  vector<
+    [double != int]>
+     { dg-end-multiline-output "" } */
+
+  fn_2 (map<int, double>());  // { dg-error "could not convert .* from 'map<int,double>' to 'map<int,int>'" }
+  /* { dg-begin-multiline-output "" }
+  map<
+    int,
+    [double != int]>
+     { 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..2567dbf
--- /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 .* 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 .* from 'map<int, int>' to 'vector<int>'" }
+
+  fn_2 (map<int, int>());
+  fn_2 (map<int, double>());  // { dg-error "could not convert .* 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 .* 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 .* 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 4f211ed..bc225fb 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.  */
 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;
 
diff --git a/gcc/tree-diagnostic.h b/gcc/tree-diagnostic.h
index e95183f..03a1e5b 100644
--- a/gcc/tree-diagnostic.h
+++ b/gcc/tree-diagnostic.h
@@ -55,6 +55,6 @@ void virt_loc_aware_diagnostic_finalizer (diagnostic_context *,
 
 void tree_diagnostics_defaults (diagnostic_context *context);
 bool default_tree_printer (pretty_printer *, text_info *, const char *,
-			   int, bool, bool, bool);
+			   int, bool, bool, bool, const char **);
 
 #endif /* ! GCC_TREE_DIAGNOSTIC_H */
-- 
1.8.5.3

^ permalink raw reply	[flat|nested] 17+ messages in thread

* Re: [PATCH 1/2] C++ template type diff printing
  2017-05-05  0:45 [PATCH 1/2] C++ template type diff printing David Malcolm
  2017-05-04 23:13 ` [PATCH 2/2] Use %H and %I throughout C++ frontend David Malcolm
@ 2017-05-08 11:48 ` Nathan Sidwell
  2017-05-08 13:07   ` Jason Merrill via gcc-patches
  2017-05-08 16:58   ` David Malcolm
  2017-05-09 15:55 ` Jakub Jelinek
  2 siblings, 2 replies; 17+ messages in thread
From: Nathan Sidwell @ 2017-05-08 11:48 UTC (permalink / raw)
  To: David Malcolm, gcc-patches

On 05/04/2017 07:44 PM, David Malcolm wrote:
> This patch kit implements two new options to make it easier
> to read diagnostics involving mismatched template types:
>    -fdiagnostics-show-template-tree and
>    -fno-elide-type.
> 
> It adds two new formatting codes: %H and %I which are
> equivalent to %qT, but are to be used together for type
> comparisons e.g.
>    "can't convert from %H to %I".

While I can see why one might want %H and %I to imply quoting, it seems 
confusingly inconsistent with the other formatters, which require an 
explicit 'q'.


> rather than printing:
> 
>    could not convert 'std::map<int, std::vector<double> >()'
>      from 'std::map<int, std::vector<double> >' to 'std::map<int, std::vector<float> >'
> 
> with -felide-type (the default), it prints:
> 
>    could not convert 'std::map<int, std::vector<double> >()'
>      from 'map<[...],vector<double>>' to 'map<[...],vector<float>>

Neat.

Is '-felide-type' a good name?  Wouldn't something like 
'-fdiagnostic-elide-template-args' be better?


> With -fdiagnostics-show-template-tree a tree-like structure of the
> template is printed, showing the differences; in this case:

'tree' sounds implementor-speaky,  Perhaps 
'-fdiagnostic-expand-template-args' or something?

Right, bikeshedding out of the way ...

> --- a/gcc/cp/error.c
> +++ b/gcc/cp/error.c
> @@ -99,7 +99,47 @@ static void cp_diagnostic_starter (diagnostic_context *, diagnostic_info *);
>   static void cp_print_error_function (diagnostic_context *, diagnostic_info *);
>   

> +
> +/* 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;

CLASS_TYPE_P (type_a) etc?


> +  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)

Should this use same_type_p for types?  If direct comparison is correct, 
a comment would help.


> +   Only non-default template arguments are printed.  */
> +
> +static void
> +print_template_tree_comparison (pretty_printer *pp, tree type_a, tree type_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)

Same question.  If these have to be comparable template type, when can 
len_a and len_b be different?



> +   This is called in phase 2 of pp_format, when it is accumulating
> +   a series of formatted chunks.  We stash the location of the chunk
> +   we're meant to have written to, so that we can write to it in the
> +   m_format_postprocessor hook.  */
> +
> +static void
> +defer_half_of_type_diff (pretty_printer *pp, char spec, tree type,

If this is called 'phase 2'  why not name the function for that, rather 
than 'half'?

> +    case 'H':
> +    case 'I':
> +      {
> +	defer_half_of_type_diff (pp, *spec, next_tree, buffer_ptr, verbose);

Why not tell defer_half_of_type_diff whether it's doing the %H bit or 
the %I bit directly, rather than have it also check for H and I?

I've not looked more than cursorily at the non C++ bits.

nathan

-- 
Nathan Sidwell

^ permalink raw reply	[flat|nested] 17+ messages in thread

* Re: [PATCH 1/2] C++ template type diff printing
  2017-05-08 11:48 ` [PATCH 1/2] C++ template type diff printing Nathan Sidwell
@ 2017-05-08 13:07   ` Jason Merrill via gcc-patches
  2017-05-08 16:58   ` David Malcolm
  1 sibling, 0 replies; 17+ messages in thread
From: Jason Merrill via gcc-patches @ 2017-05-08 13:07 UTC (permalink / raw)
  To: Nathan Sidwell; +Cc: David Malcolm, gcc-patches List

On Mon, May 8, 2017 at 7:36 AM, Nathan Sidwell <nathan@acm.org> wrote:

I agree with most of Nathan's comments, but

> 'tree' sounds implementor-speaky,  Perhaps
> '-fdiagnostic-expand-template-args' or something?

I like "tree" here; I read it as referring to the formatting of the
abstract tree data structure, not the GCC internal type.

Jason

^ permalink raw reply	[flat|nested] 17+ messages in thread

* Re: [PATCH 1/2] C++ template type diff printing
  2017-05-08 11:48 ` [PATCH 1/2] C++ template type diff printing Nathan Sidwell
  2017-05-08 13:07   ` Jason Merrill via gcc-patches
@ 2017-05-08 16:58   ` David Malcolm
  2017-05-08 17:44     ` Nathan Sidwell
                       ` (2 more replies)
  1 sibling, 3 replies; 17+ messages in thread
From: David Malcolm @ 2017-05-08 16:58 UTC (permalink / raw)
  To: Nathan Sidwell, gcc-patches

On Mon, 2017-05-08 at 07:36 -0400, Nathan Sidwell wrote:
> On 05/04/2017 07:44 PM, David Malcolm wrote:
> > This patch kit implements two new options to make it easier
> > to read diagnostics involving mismatched template types:
> >    -fdiagnostics-show-template-tree and
> >    -fno-elide-type.
> > 
> > It adds two new formatting codes: %H and %I which are
> > equivalent to %qT, but are to be used together for type
> > comparisons e.g.
> >    "can't convert from %H to %I".
> 
> While I can see why one might want %H and %I to imply quoting, it
> seems 
> confusingly inconsistent with the other formatters, which require an 
> explicit 'q'.

This an implementation detail leaking through, but I *think* it's
fixable.

The implementation has to jump through some hoops to interact with
pp_format, because the two codes interact with each other: in order to
elide commonality and colorize differences we can't start printing the
%H until we've seen the %I (vice versa should work also).
 
The 'q' for quoting the next format code happens in the 2nd phase of
 pp_format: if 'q' is present, then a quote (and potentially
colorization) is emitted to the chunk for the item before and after the
item's main content is emitted to the chunk.

To make the interaction of %H and %I work, we have to delay the writing
to the chunk to a new phase between phases 2 and 3 (and stash a pointer
to the chunk we're going to write to).

As written, if one uses '%qH' and '%qI', then phase 2 writes the open
and close quotes to the chunks, and then the delayed handler blows that
away and overwrites the chunk content with (forcibly) quoted content
for the types.

So I think it can work if we add a "needs quoting" flag to the
postprocessing phase, if we need to handle the case where %H and %I
ever appear without 'q' (and have the delayed handling stash that flag,
and do the quoting there).

I'll look at implementing that.

> > rather than printing:
> > 
> >    could not convert 'std::map<int, std::vector<double> >()'
> >      from 'std::map<int, std::vector<double> >' to 'std::map<int,
> > std::vector<float> >'
> > 
> > with -felide-type (the default), it prints:
> > 
> >    could not convert 'std::map<int, std::vector<double> >()'
> >      from 'map<[...],vector<double>>' to 'map<[...],vector<float>>
> 
> Neat.
> 
> Is '-felide-type' a good name?  Wouldn't something like 
> '-fdiagnostic-elide-template-args' be better?

Here I'm merely copying clang's option name.
  http://clang.llvm.org/docs/UsersManual.html#cmdoption-fno-elide-type


> > With -fdiagnostics-show-template-tree a tree-like structure of the
> > template is printed, showing the differences; in this case:
> 
> 'tree' sounds implementor-speaky,  Perhaps 
> '-fdiagnostic-expand-template-args' or something?

As Jason noted, this is a user-visible thing that's tree-like, rather
than our "tree" type.

Also, I'm (shamelessly) copying clang's name for this.

> Right, bikeshedding out of the way ...
> 
> > --- a/gcc/cp/error.c
> > +++ b/gcc/cp/error.c
> > @@ -99,7 +99,47 @@ static void cp_diagnostic_starter
> > (diagnostic_context *, diagnostic_info *);
> >   static void cp_print_error_function (diagnostic_context *,
> > diagnostic_info *);
> >   
> 
> > +
> > +/* 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;
> 
> CLASS_TYPE_P (type_a) etc?

I'll use this in the next version.

> > +  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)
> 
> Should this use same_type_p for types?  If direct comparison is
> correct, 
> a comment would help.

What about non-type template arguments?  That said, I just tried one,
and it's handled poorly by my patch.  I'll try to address for the next
version.

> > +   Only non-default template arguments are printed.  */
> > +
> > +static void
> > +print_template_tree_comparison (pretty_printer *pp, tree type_a,
> > tree type_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)
> 
> Same question.  If these have to be comparable template type, when
> can 
> len_a and len_b be different?

I guess I was thinking about variadic templates.  I'll have another
look at how these are handled.

> > +   This is called in phase 2 of pp_format, when it is accumulating
> > +   a series of formatted chunks.  We stash the location of the
> > chunk
> > +   we're meant to have written to, so that we can write to it in
> > the
> > +   m_format_postprocessor hook.  */
> > +
> > +static void
> > +defer_half_of_type_diff (pretty_printer *pp, char spec, tree type,
> 
> If this is called 'phase 2'  why not name the function for that,
> rather 
> than 'half'?

Maybe I should have used "peer" rather than "half": I'm attempting to
refer to one of the two peer types being compared - half of the
comparison.

> > +    case 'H':
> > +    case 'I':
> > +      {
> > +	defer_half_of_type_diff (pp, *spec, next_tree, buffer_ptr,
> > verbose);
> 
> Why not tell defer_half_of_type_diff whether it's doing the %H bit or
> the %I bit directly, rather than have it also check for H and I?

OK.

> I've not looked more than cursorily at the non C++ bits.
> 
> nathan

Thanks for the input.  I'm working on an updated version.

Dave

^ permalink raw reply	[flat|nested] 17+ messages in thread

* Re: [PATCH 1/2] C++ template type diff printing
  2017-05-08 16:58   ` David Malcolm
@ 2017-05-08 17:44     ` Nathan Sidwell
  2017-05-09 13:39     ` [PATCH 1/2] (v2) " David Malcolm
  2017-05-09 21:14     ` [PATCH 1/2] C++ template type diff printing Manuel López-Ibáñez
  2 siblings, 0 replies; 17+ messages in thread
From: Nathan Sidwell @ 2017-05-08 17:44 UTC (permalink / raw)
  To: David Malcolm, gcc-patches

On 05/08/2017 12:51 PM, David Malcolm wrote:
> On Mon, 2017-05-08 at 07:36 -0400, Nathan Sidwell wrote:
>> On 05/04/2017 07:44 PM, David Malcolm wrote:

>> Is '-felide-type' a good name?  Wouldn't something like
>> '-fdiagnostic-elide-template-args' be better?
> 
> Here I'm merely copying clang's option name.
>    http://clang.llvm.org/docs/UsersManual.html#cmdoption-fno-elide-type

Whelp, that's ... unfortunate.

nathan
-- 
Nathan Sidwell

^ permalink raw reply	[flat|nested] 17+ messages in thread

* [PATCH 2/2] (v2) Use %qH and %qI throughout C++ frontend
  2017-05-09 13:39     ` [PATCH 1/2] (v2) " David Malcolm
@ 2017-05-09 13:35       ` David Malcolm
  2017-05-09 14:59       ` [PATCH 1/2] (v2) C++ template type diff printing Nathan Sidwell
  1 sibling, 0 replies; 17+ messages in thread
From: David Malcolm @ 2017-05-09 13:35 UTC (permalink / raw)
  To: Nathan Sidwell, gcc-patches; +Cc: David Malcolm

Changed in v2: use %qH and %qI rather than %H and %I.

This is the second half of the kit, which uses %qH and %qI throughout
the C++ frontend whenever describing type mismatches between a pair
of %qT.

gcc/cp/ChangeLog:
	* call.c (print_conversion_rejection): Replace pairs of %qT with
	%qH and %qI in various places.
	(build_user_type_conversion_1): Likewise.
	(build_integral_nontype_arg_conv): Likewise.
	(build_conditional_expr_1): Likewise.
	(convert_like_real): Likewise.
	(convert_arg_to_ellipsis): Likewise.
	(joust): Likewise.
	(initialize_reference): Likewise.
	* cvt.c (cp_convert_to_pointer): Likewise.
	(cp_convert_to_pointer): Likewise.
	(convert_to_reference): Likewise.
	(ocp_convert): Likewise.
	* typeck.c (cp_build_binary_op): Likewise.
	(convert_member_func_to_ptr): Likewise.
	(build_reinterpret_cast_1): Likewise.
	(convert_for_assignment): Likewise.
	* typeck2.c (check_narrowing): Likewise.
---
 gcc/cp/call.c    | 38 +++++++++++++++++++-------------------
 gcc/cp/cvt.c     | 18 +++++++++---------
 gcc/cp/typeck.c  | 22 +++++++++++-----------
 gcc/cp/typeck2.c |  6 +++---
 4 files changed, 42 insertions(+), 42 deletions(-)

diff --git a/gcc/cp/call.c b/gcc/cp/call.c
index f1b6bf4..a7882e6 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 %qH to %qI",
 		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 %qH to %qI",
 	    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 %qH to %qI",
 	    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 %qH to %qI 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 %qH to %qI not considered for "
 		    "non-type template argument", t, type);
 	/* fall through.  */
 
@@ -4833,14 +4833,14 @@ build_conditional_expr_1 (location_t loc, tree arg1, tree arg2, tree arg3,
 	  if (unsafe_conversion_p (loc, stype, arg2, false))
 	    {
 	      if (complain & tf_error)
-		error_at (loc, "conversion of scalar %qT to vector %qT "
+		error_at (loc, "conversion of scalar %qH to vector %qI "
 			       "involves truncation", arg2_type, vtype);
 	      return error_mark_node;
 	    }
 	  if (unsafe_conversion_p (loc, stype, arg3, false))
 	    {
 	      if (complain & tf_error)
-		error_at (loc, "conversion of scalar %qT to vector %qT "
+		error_at (loc, "conversion of scalar %qH to vector %qI "
 			       "involves truncation", arg3_type, vtype);
 	      return error_mark_node;
 	    }
@@ -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 %qH to %qI to "
 				  "match other result of conditional",
 				  loc);
 
@@ -6603,7 +6603,7 @@ convert_like_real (conversion *convs, tree expr, tree fn, int argnum,
 	 from std::nullptr_t requires direct-initialization.  */
       if (NULLPTR_TYPE_P (TREE_TYPE (expr))
 	  && TREE_CODE (totype) == BOOLEAN_TYPE)
-	complained = permerror (loc, "converting to %qT from %qT requires "
+	complained = permerror (loc, "converting to %qH from %qI requires "
 				"direct-initialization",
 				totype, TREE_TYPE (expr));
 
@@ -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 %qH to %qI", 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 %qH to %qI",
 				TREE_TYPE (expr), totype);
       if (complained && fn)
 	inform (DECL_SOURCE_LOCATION (fn),
@@ -6924,14 +6924,14 @@ convert_like_real (conversion *convs, tree expr, tree fn, int argnum,
 	    tree extype = TREE_TYPE (expr);
 	    if (TYPE_REF_IS_RVALUE (ref_type)
 		&& lvalue_p (expr))
-	      error_at (loc, "cannot bind rvalue reference of type %qT to "
-                        "lvalue of type %qT", totype, extype);
+	      error_at (loc, "cannot bind rvalue reference of type %qH to "
+                        "lvalue of type %qI", totype, extype);
 	    else if (!TYPE_REF_IS_RVALUE (ref_type) && !lvalue_p (expr)
 		     && !CP_TYPE_CONST_NON_VOLATILE_P (TREE_TYPE (ref_type)))
 	      error_at (loc, "cannot bind non-const lvalue reference of "
-			"type %qT to an rvalue of type %qT", totype, extype);
+			"type %qH to an rvalue of type %qI", totype, extype);
 	    else if (!reference_compatible_p (TREE_TYPE (totype), extype))
-	      error_at (loc, "binding reference of type %qT to %qT "
+	      error_at (loc, "binding reference of type %qH to %qI "
 			"discards qualifiers", totype, extype);
 	    else
 	      gcc_unreachable ();
@@ -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 %qH to %qI when passing "
 		    "argument to function",
 		    arg_type, double_type_node);
       arg = convert_to_real_nofold (double_type_node, arg);
@@ -9643,7 +9643,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 %qH to %qI",
 			  source, w->second_conv->type)) 
 	    {
 	      inform (input_location, "  because conversion sequence for the argument is better");
@@ -10417,11 +10417,11 @@ initialize_reference (tree type, tree expr,
 		   && !TYPE_REF_IS_RVALUE (type)
 		   && !lvalue_p (expr))
 	    error_at (loc, "invalid initialization of non-const reference of "
-		      "type %qT from an rvalue of type %qT",
+		      "type %qH from an rvalue of type %qI",
 		      type, TREE_TYPE (expr));
 	  else
 	    error_at (loc, "invalid initialization of reference of type "
-		      "%qT from expression of type %qT", type,
+		      "%qH from expression of type %qI", type,
 		      TREE_TYPE (expr));
 	}
       return error_mark_node;
diff --git a/gcc/cp/cvt.c b/gcc/cp/cvt.c
index 5f4b5e3..9ea8084 100644
--- a/gcc/cp/cvt.c
+++ b/gcc/cp/cvt.c
@@ -86,7 +86,7 @@ cp_convert_to_pointer (tree type, tree expr, bool dofold,
       if (!COMPLETE_TYPE_P (intype))
 	{
 	  if (complain & tf_error)
-	    error_at (loc, "can%'t convert from incomplete type %qT to %qT",
+	    error_at (loc, "can%'t convert from incomplete type %qH to %qI",
 		      intype, type);
 	  return error_mark_node;
 	}
@@ -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 %qH to %qI is ambiguous",
 		      expr, intype, type);
 	  return rval;
 	}
@@ -168,7 +168,7 @@ cp_convert_to_pointer (tree type, tree expr, bool dofold,
       if (TYPE_PTRMEMFUNC_P (type))
 	{
 	  if (complain & tf_error)
-	    error_at (loc, "cannot convert %qE from type %qT to type %qT",
+	    error_at (loc, "cannot convert %qE from type %qH to type %qI",
 		      expr, intype, type);
 	  return error_mark_node;
 	}
@@ -195,7 +195,7 @@ cp_convert_to_pointer (tree type, tree expr, bool dofold,
 	    }
 	}
       if (complain & tf_error)
-	error_at (loc, "cannot convert %qE from type %qT to type %qT",
+	error_at (loc, "cannot convert %qE from type %qH to type %qI",
 		  expr, intype, type);
       return error_mark_node;
     }
@@ -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 %qH to %qI", intype, type);
       return error_mark_node;
     }
 
@@ -244,7 +244,7 @@ cp_convert_to_pointer (tree type, tree expr, bool dofold,
     return instantiate_type (type, expr, complain);
 
   if (complain & tf_error)
-    error_at (loc, "cannot convert %qE from type %qT to type %qT",
+    error_at (loc, "cannot convert %qE from type %qH to type %qI",
 	      expr, 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 %qH to %qI discards qualifiers",
 			 ttr, reftype);
 	    else
 	      return error_mark_node;
@@ -514,7 +514,7 @@ convert_to_reference (tree reftype, tree expr, int convtype,
     }
 
   if (complain & tf_error)
-    error_at (loc, "cannot convert type %qT to type %qT", intype, reftype);
+    error_at (loc, "cannot convert type %qH to type %qI", intype, reftype);
 
   return error_mark_node;
 }
@@ -907,7 +907,7 @@ ocp_convert (tree type, tree expr, int convtype, int flags,
       if (invalid_nonstatic_memfn_p (loc, expr, complain))
 	/* We displayed the error message.  */;
       else
-	error_at (loc, "conversion from %qT to non-scalar type %qT requested",
+	error_at (loc, "conversion from %qH to non-scalar type %qI requested",
 		  TREE_TYPE (expr), type);
     }
   return error_mark_node;
diff --git a/gcc/cp/typeck.c b/gcc/cp/typeck.c
index 7aee0d6..ccb5495 100644
--- a/gcc/cp/typeck.c
+++ b/gcc/cp/typeck.c
@@ -5011,7 +5011,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 %qH to %qI "
 				  "to match other operand of binary "
 				  "expression",
 				  location);
@@ -7028,7 +7028,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 %qH to %qI", intype, type);
 
   if (TREE_CODE (intype) == METHOD_TYPE)
     expr = build_addr_func (expr, complain);
@@ -7150,7 +7150,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 %qH to %qI loses precision",
                        intype, type);
           else
             return error_mark_node;
@@ -7190,7 +7190,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 %qH to %qI "
                  "increases required alignment of target type", intype, type);
 
       /* We need to strip nops here, because the front end likes to
@@ -8504,33 +8504,33 @@ convert_for_assignment (tree type, tree rhs,
 		    return r;
 		}
 	      else if (fndecl)
-		error ("cannot convert %qT to %qT for argument %qP to %qD",
+		error ("cannot convert %qH to %qI for argument %qP to %qD",
 		       rhstype, type, parmnum, fndecl);
 	      else
 		switch (errtype)
 		  {
 		    case ICR_DEFAULT_ARGUMENT:
-		      error ("cannot convert %qT to %qT in default argument",
+		      error ("cannot convert %qH to %qI in default argument",
 			     rhstype, type);
 		      break;
 		    case ICR_ARGPASS:
-		      error ("cannot convert %qT to %qT in argument passing",
+		      error ("cannot convert %qH to %qI in argument passing",
 			     rhstype, type);
 		      break;
 		    case ICR_CONVERTING:
-		      error ("cannot convert %qT to %qT",
+		      error ("cannot convert %qH to %qI",
 			     rhstype, type);
 		      break;
 		    case ICR_INIT:
-		      error ("cannot convert %qT to %qT in initialization",
+		      error ("cannot convert %qH to %qI in initialization",
 			     rhstype, type);
 		      break;
 		    case ICR_RETURN:
-		      error ("cannot convert %qT to %qT in return",
+		      error ("cannot convert %qH to %qI in return",
 			     rhstype, type);
 		      break;
 		    case ICR_ASSIGN:
-		      error ("cannot convert %qT to %qT in assignment",
+		      error ("cannot convert %qH to %qI in assignment",
 			     rhstype, type);
 		      break;
 		    default:
diff --git a/gcc/cp/typeck2.c b/gcc/cp/typeck2.c
index 1f0eb45..40328cd 100644
--- a/gcc/cp/typeck2.c
+++ b/gcc/cp/typeck2.c
@@ -954,7 +954,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 %qH to %qI inside { } is ill-formed in C++11",
 			init, ftype, type);
 	  ok = true;
 	}
@@ -965,7 +965,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 %qH to %qI inside { }",
 			      init, ftype, type)
 		  && almost_ok)
 		inform (loc, " the expression has a constant value but is not "
@@ -978,7 +978,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 %qH to %qI "
 		   "inside { }", init, ftype, type);
 	  if (errorcount == savederrorcount)
 	    ok = true;
-- 
1.8.5.3

^ permalink raw reply	[flat|nested] 17+ messages in thread

* [PATCH 1/2] (v2) C++ template type diff printing
  2017-05-08 16:58   ` David Malcolm
  2017-05-08 17:44     ` Nathan Sidwell
@ 2017-05-09 13:39     ` David Malcolm
  2017-05-09 13:35       ` [PATCH 2/2] (v2) Use %qH and %qI throughout C++ frontend David Malcolm
  2017-05-09 14:59       ` [PATCH 1/2] (v2) C++ template type diff printing Nathan Sidwell
  2017-05-09 21:14     ` [PATCH 1/2] C++ template type diff printing Manuel López-Ibáñez
  2 siblings, 2 replies; 17+ messages in thread
From: David Malcolm @ 2017-05-09 13:39 UTC (permalink / raw)
  To: Nathan Sidwell, gcc-patches; +Cc: David Malcolm

Changes in v2:
- pass "quote" as an extra bool argument to the pp_format_decoder
callback, so that we can optionally quote the delayed chunks (with
%qH and %qI)
- use %qH and %qI rather than %H and %I.
- use CLASS_TYPE_P.
- use TYPE_P rather than EXPR_P in arg_to_string to handle non-type template
arguments; added test cases for this
- in type_to_string_with_compare, drop the "foo_a" and "foo_b" naming
convention in favor of "foo" vs "foo_peer", since here we could be dealing
with either %H or %I, either way around.   Remove logic for returning NULL
types, and clairy behavior for handling of mixtures of default and
non-default args, adding test coverage for this.

For example, given:

  template <int = 0, int = 1, int = 2> struct s {};
  void takes_s (s<> );

then:
  takes_s (s<0, 2>());
is reported as:
  can't convert from 's<[...],2>' to 's<[...], 1>'
highlighting the "2" and "1", rather than
  can't convert from 's<0,2>' to 's<>'
since these are the arguments of interest.

The template tree comparison for this case is printed as:
 s<
   [...]
   [2 != 1]>

- renamed "defer_half_of_type_diff" to "defer_phase_2_of_type_diff",
and rewrite to avoid a switch statement

Would it make sense to rename "m_type_a"/"m_type_b" to "m_type_H"/m_type_I"?
(we normally don't go for uppercase in fieldnames, but given the codes are
case-sensitive, does it make sense here?)

Successfully bootstrapped&regrtested the combination of the two patches
on x86_64-pc-linux-gnu.

gcc/c-family/ChangeLog:
	* c-format.c (gcc_cxxdiag_char_table): Add 'H' and 'I' to
	format_chars.
	* c.opt (fdiagnostics-show-template-tree): New option.
	(felide-type): New option.
	* c-format.c (static): Likewise.

gcc/c/ChangeLog:
	* c-objc-common.c (c_tree_printer): Gain bool and const char **
	parameters.

gcc/cp/ChangeLog:
	* call.c (perform_implicit_conversion_flags): Convert
	"from %qT to %qT" to "from %qH to %qI" in diagnostic.
	* error.c (struct deferred_printed_type): New struct.
	(class cxx_format_postprocessor): New class.
	(cxx_initialize_diagnostics): Wire up a cxx_format_postprocessor
	to pp->m_format_postprocessor.
	(comparable_template_types_p): New function.
	(newline_and_indent): New function.
	(arg_to_string): New function.
	(print_nonequal_arg): New function.
	(type_to_string_with_compare): New function.
	(print_template_tree_comparison): New function.
	(append_formatted_chunk): New function.
	(add_quotes): New function.
	(cxx_format_postprocessor::handle): New function.
	(defer_phase_2_of_type_diff): New function.
	(cp_printer): Add "quoted" and "buffer_ptr" params.  Implement
	%H and %I.

gcc/ChangeLog:
	* diagnostic-color.c (color_dict): Add "type-diff".
	(parse_gcc_colors): Update comment.
	* doc/invoke.texi (Diagnostic Message Formatting Options): Add
	-fdiagnostics-show-template-tree and -fno-elide-type.
	(GCC_COLORS): Add type-diff to example.
	(type-diff=): New.
	(-fdiagnostics-show-template-tree): New.
	(-fno-elide-type): New.
	* pretty-print.c (pp_format): Pass formatters[argno] to the
	pp_format_decoder callback.  Call any m_format_postprocessor's
	"handle" method.
	(pretty_printer::pretty_printer): Initialize
	m_format_postprocessor.
	(pretty_printer::~pretty_printer): Delete any
	m_format_postprocessor.
	* pretty-print.h (printer_fn): Add bool and const char **
	parameters.
	(class format_postprocessor): New class.
	(struct pretty_printer::format_decoder): Document the new
	parameters.
	(struct pretty_printer::m_format_postprocessor): New field.
	* tree-diagnostic.c (default_tree_printer): Update for new
	bool and const char ** params.
	* tree-diagnostic.h (default_tree_printer): Likewise.

gcc/fortran/ChangeLog:
	* error.c (gfc_format_decoder): Update for new bool and
	const char ** params.

gcc/testsuite/ChangeLog:
	* g++.dg/plugin/plugin.exp (plugin_test_list): Add...
	* g++.dg/plugin/show-template-tree-color-no-elide-type.C: New
	test case.
	* g++.dg/plugin/show-template-tree-color.C: New test case.
	* g++.dg/plugin/show_template_tree_color_plugin.c: New plugin.
	* g++.dg/template/show-template-tree-2.C: New test case.
	* g++.dg/template/show-template-tree-3.C: New test case.
	* g++.dg/template/show-template-tree-4.C: New test case.
	* g++.dg/template/show-template-tree-no-elide-type.C: New test case.
	* g++.dg/template/show-template-tree.C: New test case.
---
 gcc/c-family/c-format.c                            |   2 +-
 gcc/c-family/c.opt                                 |   8 +
 gcc/c/c-objc-common.c                              |   5 +-
 gcc/cp/call.c                                      |   2 +-
 gcc/cp/error.c                                     | 438 ++++++++++++++++++++-
 gcc/diagnostic-color.c                             |   6 +-
 gcc/doc/invoke.texi                                |  50 ++-
 gcc/fortran/error.c                                |   5 +-
 gcc/pretty-print.c                                 |  11 +-
 gcc/pretty-print.h                                 |  20 +-
 gcc/testsuite/g++.dg/plugin/plugin.exp             |   3 +
 .../show-template-tree-color-no-elide-type.C       |  30 ++
 .../g++.dg/plugin/show-template-tree-color.C       |  30 ++
 .../plugin/show_template_tree_color_plugin.c       |  38 ++
 .../g++.dg/template/show-template-tree-2.C         | 118 ++++++
 .../g++.dg/template/show-template-tree-3.C         |  37 ++
 .../g++.dg/template/show-template-tree-4.C         |  95 +++++
 .../template/show-template-tree-no-elide-type.C    |  24 ++
 gcc/testsuite/g++.dg/template/show-template-tree.C |  51 +++
 gcc/tree-diagnostic.c                              |   3 +-
 gcc/tree-diagnostic.h                              |   2 +-
 21 files changed, 961 insertions(+), 17 deletions(-)
 create mode 100644 gcc/testsuite/g++.dg/plugin/show-template-tree-color-no-elide-type.C
 create mode 100644 gcc/testsuite/g++.dg/plugin/show-template-tree-color.C
 create mode 100644 gcc/testsuite/g++.dg/plugin/show_template_tree_color_plugin.c
 create mode 100644 gcc/testsuite/g++.dg/template/show-template-tree-2.C
 create mode 100644 gcc/testsuite/g++.dg/template/show-template-tree-3.C
 create mode 100644 gcc/testsuite/g++.dg/template/show-template-tree-4.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-format.c b/gcc/c-family/c-format.c
index 400eb66..fd40359 100644
--- a/gcc/c-family/c-format.c
+++ b/gcc/c-family/c-format.c
@@ -754,7 +754,7 @@ static const format_char_info gcc_cxxdiag_char_table[] =
   /* Custom conversion specifiers.  */
 
   /* These will require a "tree" at runtime.  */
-  { "ADEFKSTVX",0,STD_C89,{ T89_V,   BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN  }, "q+#",   "",   NULL },
+  { "ADEFHIKSTVX",0,STD_C89,{ T89_V,   BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN  }, "q+#",   "",   NULL },
 
   { "v", 0,STD_C89, { T89_I,   BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN  }, "q#",  "",   NULL },
 
diff --git a/gcc/c-family/c.opt b/gcc/c-family/c.opt
index 9ad2f6e..4648e60 100644
--- a/gcc/c-family/c.opt
+++ b/gcc/c-family/c.opt
@@ -1342,6 +1342,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)
+Print hierarchical comparisons when template types are mismatched.
+
 fdirectives-only
 C ObjC C++ ObjC++
 Preprocess directives only.
@@ -1361,6 +1365,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..05212b2 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, 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,
+		bool, const char **)
 {
   tree t = NULL_TREE;
   tree name;
diff --git a/gcc/cp/call.c b/gcc/cp/call.c
index c15b8e4..f1b6bf4 100644
--- a/gcc/cp/call.c
+++ b/gcc/cp/call.c
@@ -10099,7 +10099,7 @@ 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,
+	    error_at (loc, "could not convert %qE from %qH to %qI", expr,
 		      TREE_TYPE (expr), type);
 	}
       expr = error_mark_node;
diff --git a/gcc/cp/error.c b/gcc/cp/error.c
index a83ecb2..ccd4bdc 100644
--- a/gcc/cp/error.c
+++ b/gcc/cp/error.c
@@ -99,7 +99,50 @@ 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, 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), m_quote (false)
+  {}
+
+  deferred_printed_type (tree type, const char **buffer_ptr, bool verbose,
+			 bool quote)
+  : m_tree (type), m_buffer_ptr (buffer_ptr), m_verbose (verbose),
+    m_quote (quote)
+  {
+    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;
+  bool m_quote;
+};
+
+/* 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 +166,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.  */
@@ -3565,6 +3609,373 @@ maybe_print_constexpr_context (diagnostic_context *context)
     }
 }
 \f
+
+/* 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 (!CLASS_TYPE_P (type_a))
+    return false;
+  if (!CLASS_TYPE_P (type_b))
+    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);
+}
+
+/* Generate a GC-allocated string for ARG, an expression or type.  */
+
+static const char *
+arg_to_string (tree arg, bool verbose)
+{
+  if (TYPE_P (arg))
+    return type_to_string (arg, verbose);
+  else
+    return expr_to_string (arg);
+}
+
+/* Subroutine to type_to_string_with_compare and
+   print_template_tree_comparison.
+
+   Print a representation of ARG (an expression or type) to PP,
+   colorizing it as "type-diff" if PP->show_color.  */
+
+static void
+print_nonequal_arg (pretty_printer *pp, tree arg, bool verbose)
+{
+  pp_printf (pp, "%r%s%R",
+	     "type-diff",
+	     (arg
+	      ? arg_to_string (arg, verbose)
+	      : G_("(no argument)")));
+}
+
+/* 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".
+
+   Template arguments in which both types are using the default arguments
+   are not printed; if at least one of the two types is using a non-default
+   argument, then both arguments are printed.
+
+   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 = TI_ARGS (tinfo);
+  tree args_peer = TI_ARGS (tinfo_peer);
+  gcc_assert (TREE_CODE (args) == TREE_VEC);
+  gcc_assert (TREE_CODE (args_peer) == TREE_VEC);
+  int flags = 0;
+  int len = get_non_default_template_args_count (args, flags);
+  args = INNERMOST_TEMPLATE_ARGS (args);
+  int len_peer = get_non_default_template_args_count (args_peer, flags);
+  args_peer = INNERMOST_TEMPLATE_ARGS (args_peer);
+  /* Determine the maximum range of args for which non-default template args
+     were used; beyond this, only default args (if any) were used, and so
+     they will be equal from this point onwards.
+     One of the two peers might have used default arguments within this
+     range, but the other will be using non-default arguments, and so
+     it's more readable to print both within this range, to highlight
+     the differences.  */
+  int len_max = MAX (len, len_peer);
+  for (int idx = 0; idx < len_max; idx++)
+    {
+      tree arg = TREE_VEC_ELT (args, idx);
+      tree arg_peer = TREE_VEC_ELT (args_peer, idx);
+      if (arg == arg_peer)
+	{
+	  if (flag_elide_type)
+	    pp_string (pp, G_("[...]"));
+	  else
+	    pp_string (pp, arg_to_string (arg, verbose));
+	}
+      else
+	{
+	  if (comparable_template_types_p (arg, arg_peer))
+	    pp_string (pp,
+		       type_to_string_with_compare (arg, arg_peer, verbose,
+						    show_color));
+	  else
+	    print_nonequal_arg (pp, arg, 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).
+
+   Template arguments in which both types are using the default arguments
+   are not printed; if at least one of the two types is using a non-default
+   argument, then both arguments are printed.  */
+
+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);
+  int flags = 0;
+  int len_a = get_non_default_template_args_count (args_a, flags);
+  args_a = INNERMOST_TEMPLATE_ARGS (args_a);
+  int len_b = get_non_default_template_args_count (args_b, flags);
+  args_b = INNERMOST_TEMPLATE_ARGS (args_b);
+  int len_max = MAX (len_a, len_b);
+  gcc_assert (TREE_CODE (args_a) == TREE_VEC);
+  gcc_assert (TREE_CODE (args_b) == TREE_VEC);
+  for (int idx = 0; idx < len_max; idx++)
+    {
+      tree arg_a = TREE_VEC_ELT (args_a, idx);
+      tree arg_b = TREE_VEC_ELT (args_b, idx);
+      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, storing the result into the chunk_info
+   for pp_format.  Quote them if 'q' was provided.
+   Also print the difference in tree form, adding it as
+   an additional chunk.  */
+
+void
+cxx_format_postprocessor::handle (pretty_printer *pp)
+{
+  /* If we have one of %H and %I, the other should have
+     been present.  */
+  if (m_type_a.m_tree || m_type_b.m_tree)
+    {
+      gcc_assert (m_type_a.m_tree);
+      gcc_assert (m_type_b.m_tree);
+    }
+
+  if (m_type_a.m_tree && m_type_b.m_tree)
+    {
+      /* 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;
+
+      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);
+	}
+
+      if (type_a.m_quote)
+	type_a_text = add_quotes (type_a_text, show_color);
+      *type_a.m_buffer_ptr = type_a_text;
+
+       if (type_b.m_quote)
+	type_b_text = add_quotes (type_b_text, show_color);
+      *type_b.m_buffer_ptr = type_b_text;
+   }
+}
+
+/* Subroutine for handling %H and %I, to support i18n of messages like:
+
+    error_at (loc, "could not convert %qE from %qH to %qI",
+	       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.
+
+   This is called in phase 2 of pp_format, when it is accumulating
+   a series of formatted chunks.  We stash the location of the chunk
+   we're meant to have written to, so that we can write to it in the
+   m_format_postprocessor hook.
+
+   We also need to stash whether a 'q' prefix was provided (the QUOTE
+   param)  so that we can add the quotes when writing out the delayed
+   chunk.  */
+
+static void
+defer_phase_2_of_type_diff (deferred_printed_type *deferred,
+			    tree type, const char **buffer_ptr,
+			    bool verbose, bool quote)
+{
+  gcc_assert (deferred->m_tree == NULL_TREE);
+  gcc_assert (deferred->m_buffer_ptr == NULL);
+  *deferred = deferred_printed_type (type, buffer_ptr, verbose, quote);
+}
+
+
 /* Called from output_format -- during diagnostic message processing --
    to handle C++ specific format specifier with the following meanings:
    %A   function argument-list.
@@ -3579,11 +3990,18 @@ 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,
+	    bool quoted, const char **buffer_ptr)
 {
+  gcc_assert (pp->m_format_postprocessor);
+  cxx_format_postprocessor *postprocessor
+    = static_cast <cxx_format_postprocessor *> (pp->m_format_postprocessor);
+
   const char *result;
   tree t = NULL;
 #define next_tree    (t = va_arg (*text->args_ptr, tree))
@@ -3629,6 +4047,20 @@ cp_printer (pretty_printer *pp, text_info *text, const char *spec,
       percent_K_format (text);
       return true;
 
+    case 'H':
+      {
+	defer_phase_2_of_type_diff (&postprocessor->m_type_a, next_tree,
+				    buffer_ptr, verbose, quoted);
+	return true;
+      }
+
+    case 'I':
+      {
+	defer_phase_2_of_type_diff (&postprocessor->m_type_b, next_tree,
+				    buffer_ptr, verbose, quoted);
+	return true;
+      }
+
     default:
       return false;
     }
diff --git a/gcc/diagnostic-color.c b/gcc/diagnostic-color.c
index 8353fe0..6adb872 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 }
 };
 
@@ -204,8 +205,9 @@ colorize_stop (bool show_color)
 /* Parse GCC_COLORS.  The default would look like:
    GCC_COLORS='error=01;31:warning=01;35:note=01;36:\
    range1=32:range2=34:locus=01:quote=01:\
-   fixit-insert=32:fixit-delete=31'\
-   diff-filename=01:diff-hunk=32:diff-delete=31:diff-insert=32'
+   fixit-insert=32:fixit-delete=31:'\
+   diff-filename=01:diff-hunk=32:diff-delete=31:diff-insert=32:\
+   type-diff=01;32'
    No character escaping is needed or supported.  */
 static bool
 parse_gcc_colors (void)
diff --git a/gcc/doc/invoke.texi b/gcc/doc/invoke.texi
index 65308c9..9966fa0 100644
--- a/gcc/doc/invoke.texi
+++ b/gcc/doc/invoke.texi
@@ -251,6 +251,7 @@ Objective-C and Objective-C++ Dialects}.
 -fdiagnostics-color=@r{[}auto@r{|}never@r{|}always@r{]}  @gol
 -fno-diagnostics-show-option  -fno-diagnostics-show-caret @gol
 -fdiagnostics-parseable-fixits  -fdiagnostics-generate-patch @gol
+-fdiagnostics-show-template-tree -fno-elide-type @gol
 -fno-show-column}
 
 @item Warning Options
@@ -3442,7 +3443,8 @@ The default @env{GCC_COLORS} is
 @smallexample
 error=01;31:warning=01;35:note=01;36:range1=32:range2=34:locus=01:\
 quote=01:fixit-insert=32:fixit-delete=31:\
-diff-filename=01:diff-hunk=32:diff-delete=31:diff-insert=32
+diff-filename=01:diff-hunk=32:diff-delete=31:diff-insert=32:\
+type-diff=01;32
 @end smallexample
 @noindent
 where @samp{01;31} is bold red, @samp{01;35} is bold magenta,
@@ -3506,6 +3508,11 @@ SGR substring for deleted lines within generated patches.
 @item diff-insert=
 @vindex diff-insert GCC_COLORS @r{capability}
 SGR substring for inserted lines within generated patches.
+
+@item type-diff=
+@vindex type-diff GCC_COLORS @r{capability}
+SGR substring for highlighting mismatching types within template
+arguments in the C++ frontend.
 @end table
 
 @item -fno-diagnostics-show-option
@@ -3578,6 +3585,47 @@ are printed.  For example:
 The diff may or may not be colorized, following the same rules
 as for diagnostics (see @option{-fdiagnostics-color}).
 
+@item -fdiagnostics-show-template-tree
+@opindex fdiagnostics-show-template-tree
+
+In the C++ frontend, when printing diagnostics showing mismatching
+template types, such as:
+
+@smallexample
+  could not convert 'std::map<int, std::vector<double> >()'
+    from 'map<[...],vector<double>>' to 'map<[...],vector<float>>
+@end smallexample
+
+the @option{-fdiagnostics-show-template-tree} flag enables printing a
+tree-like structure showing the common and differing parts of the types,
+such as:
+
+@smallexample
+  map<
+    [...],
+    vector<
+      [double != float]>>
+@end smallexample
+
+The parts that differ are highlighted with color (``double'' and
+``float'' in this case).
+
+@item -fno-elide-type
+@opindex fno-elide-type
+@opindex felide-type
+By default when the C++ frontend prints diagnostics showing mismatching
+template types, common parts of the types are printed as ``[...]'' to
+simplify the error message.  For example:
+
+@smallexample
+  could not convert 'std::map<int, std::vector<double> >()'
+    from 'map<[...],vector<double>>' to 'map<[...],vector<float>>
+@end smallexample
+
+Specifying the @option{-fno-elide-type} flag suppresses that behavior.
+This flag also affects the output of the
+@option{-fdiagnostics-show-template-tree} flag.
+
 @item -fno-show-column
 @opindex fno-show-column
 Do not print column numbers in diagnostics.  This may be necessary if
diff --git a/gcc/fortran/error.c b/gcc/fortran/error.c
index 0312499..af72581 100644
--- a/gcc/fortran/error.c
+++ b/gcc/fortran/error.c
@@ -917,7 +917,8 @@ gfc_notify_std (int std, const char *gmsgid, ...)
 */
 static bool
 gfc_format_decoder (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,
+		    bool quoted, const char **buffer_ptr)
 {
   switch (*spec)
     {
@@ -948,7 +949,7 @@ gfc_format_decoder (pretty_printer *pp, text_info *text, const char *spec,
 	 etc. diagnostics can use the FE printer while the FE is still
 	 active.  */
       return default_tree_printer (pp, text, spec, precision, wide,
-				   set_locus, hash);
+				   set_locus, hash, quoted, buffer_ptr);
     }
 }
 
diff --git a/gcc/pretty-print.c b/gcc/pretty-print.c
index bcb1a70..570dec7 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, quote,
+					 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);
+
   /* 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 (NULL),
     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..40e56a3 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, 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 QUOTE and BUFFER_PTR are 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/plugin/plugin.exp b/gcc/testsuite/g++.dg/plugin/plugin.exp
index 6a0c1aa..94ebe93 100644
--- a/gcc/testsuite/g++.dg/plugin/plugin.exp
+++ b/gcc/testsuite/g++.dg/plugin/plugin.exp
@@ -65,6 +65,9 @@ set plugin_test_list [list \
     { def_plugin.c def-plugin-test.C } \
     { ../../gcc.dg/plugin/diagnostic_plugin_test_tree_expression_range.c \
 	  diagnostic-test-expressions-1.C } \
+    { show_template_tree_color_plugin.c \
+    	  show-template-tree-color.C \
+    	  show-template-tree-color-no-elide-type.C } \
 ]
 
 foreach plugin_test $plugin_test_list {
diff --git a/gcc/testsuite/g++.dg/plugin/show-template-tree-color-no-elide-type.C b/gcc/testsuite/g++.dg/plugin/show-template-tree-color-no-elide-type.C
new file mode 100644
index 0000000..cab0359
--- /dev/null
+++ b/gcc/testsuite/g++.dg/plugin/show-template-tree-color-no-elide-type.C
@@ -0,0 +1,30 @@
+/* Verify colorization of the output of -fdiagnostics-show-template-tree,
+   and within the %H and %I format codes.
+   Doing so requires a plugin; see the comments in the plugin for the
+   rationale.  */
+
+// { dg-options "-fdiagnostics-show-template-tree -fdiagnostics-color=always -fno-elide-type" }
+
+template<typename> struct vector {};
+template<typename, typename> struct map {};
+
+void fn_1(vector<int>);
+void fn_2(map<int, int>);
+
+void test ()
+{
+  fn_1 (vector<double> ());
+  /* { dg-begin-multiline-output "" }
+could not convert '^[[01m^[[Kvector<double>()^[[m^[[K' from '^[[01m^[[Kvector<^[[01;32m^[[Kdouble^[[m^[[K>^[[m^[[K' to '^[[01m^[[Kvector<^[[01;32m^[[Kint^[[m^[[K>^[[m^[[K'
+  vector<
+    [^[[01;32m^[[Kdouble^[[m^[[K != ^[[01;32m^[[Kint^[[m^[[K]>
+     { dg-end-multiline-output "" } */
+
+  fn_2 (map<int, double>());
+  /* { dg-begin-multiline-output "" }
+could not convert '^[[01m^[[Kmap<int, double>()^[[m^[[K' from '^[[01m^[[Kmap<int,^[[01;32m^[[Kdouble^[[m^[[K>^[[m^[[K' to '^[[01m^[[Kmap<int,^[[01;32m^[[Kint^[[m^[[K>^[[m^[[K'
+  map<
+    int,
+    [^[[01;32m^[[Kdouble^[[m^[[K != ^[[01;32m^[[Kint^[[m^[[K]>
+     { dg-end-multiline-output "" } */
+}
diff --git a/gcc/testsuite/g++.dg/plugin/show-template-tree-color.C b/gcc/testsuite/g++.dg/plugin/show-template-tree-color.C
new file mode 100644
index 0000000..eb99a3e
--- /dev/null
+++ b/gcc/testsuite/g++.dg/plugin/show-template-tree-color.C
@@ -0,0 +1,30 @@
+/* Verify colorization of the output of -fdiagnostics-show-template-tree,
+   and within the %H and %I format codes.
+   Doing so requires a plugin; see the comments in the plugin for the
+   rationale.  */
+
+// { 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 test ()
+{
+  fn_1 (vector<double> ());
+  /* { dg-begin-multiline-output "" }
+could not convert '^[[01m^[[Kvector<double>()^[[m^[[K' from '^[[01m^[[Kvector<^[[01;32m^[[Kdouble^[[m^[[K>^[[m^[[K' to '^[[01m^[[Kvector<^[[01;32m^[[Kint^[[m^[[K>^[[m^[[K'
+  vector<
+    [^[[01;32m^[[Kdouble^[[m^[[K != ^[[01;32m^[[Kint^[[m^[[K]>
+     { dg-end-multiline-output "" } */
+
+  fn_2 (map<int, double>());
+  /* { dg-begin-multiline-output "" }
+could not convert '^[[01m^[[Kmap<int, double>()^[[m^[[K' from '^[[01m^[[Kmap<[...],^[[01;32m^[[Kdouble^[[m^[[K>^[[m^[[K' to '^[[01m^[[Kmap<[...],^[[01;32m^[[Kint^[[m^[[K>^[[m^[[K'
+  map<
+    [...],
+    [^[[01;32m^[[Kdouble^[[m^[[K != ^[[01;32m^[[Kint^[[m^[[K]>
+     { dg-end-multiline-output "" } */
+}
diff --git a/gcc/testsuite/g++.dg/plugin/show_template_tree_color_plugin.c b/gcc/testsuite/g++.dg/plugin/show_template_tree_color_plugin.c
new file mode 100644
index 0000000..af568bf
--- /dev/null
+++ b/gcc/testsuite/g++.dg/plugin/show_template_tree_color_plugin.c
@@ -0,0 +1,38 @@
+/* We want to verify the colorized output of cxx_format_postprocessor,
+   but turning on colorization for everything confuses "dg-error" etc.
+   The color codes in the generated messages would also need escaping
+   for use within dg-error.
+
+   Hence the simplest approach is to provide a custom diagnostic_starter_fn,
+   which does nothing.
+
+   The resulting messages lack the "FILENAME:LINE:COL: error: " prefix
+   and can thus be tested using dg-begin/end-multiline-output.  */
+
+/* { dg-options "-O" } */
+
+#include "gcc-plugin.h"
+#include "config.h"
+#include "system.h"
+#include "coretypes.h"
+#include "plugin-version.h"
+#include "diagnostic.h"
+
+int plugin_is_GPL_compatible;
+
+void
+noop_starter_fn (diagnostic_context *, diagnostic_info *)
+{
+}
+
+int
+plugin_init (struct plugin_name_args *plugin_info,
+	     struct plugin_gcc_version *version)
+{
+  if (!plugin_default_version_check (version, &gcc_version))
+    return 1;
+
+  diagnostic_starter (global_dc) = noop_starter_fn;
+
+  return 0;
+}
diff --git a/gcc/testsuite/g++.dg/template/show-template-tree-2.C b/gcc/testsuite/g++.dg/template/show-template-tree-2.C
new file mode 100644
index 0000000..1cd3a06
--- /dev/null
+++ b/gcc/testsuite/g++.dg/template/show-template-tree-2.C
@@ -0,0 +1,118 @@
+// Tests of -fdiagnostics-show-template-tree with variadic templates
+// { dg-options "-fdiagnostics-show-template-tree -std=c++11" }
+
+template<typename> struct vector {};
+template<typename, typename> struct map {};
+template<typename ... Types> struct var {};
+
+void fn_0(var<>);
+void fn_1(var<int>);
+void fn_2(var<int, int>);
+void fn_3(vector<var<> >);
+void fn_4(vector<var<int> >);
+void fn_5(vector<var<int, int> >);
+
+void test_fn_0 ()
+{
+  fn_0 (var<> ());
+  fn_0 (var<int> ()); // { dg-error "could not convert .* from 'var<int>' to 'var<>'" }
+  /* { dg-begin-multiline-output "" }
+  var<
+    [int != ]>
+     { dg-end-multiline-output "" } */
+  fn_0 (var<int, int> ()); // { dg-error "could not convert .* from 'var<int, int>' to 'var<>'" }
+  /* { dg-begin-multiline-output "" }
+  var<
+    [int, int != ]>
+     { dg-end-multiline-output "" } */
+  fn_0 (vector<var<int> >()); // { dg-error "could not convert .* from 'vector<var<int> >' to 'var<>'" }
+  fn_0 (vector<var<int, int> >());  // { dg-error "could not convert .* from 'vector<var<int, int> >' to 'var<>'" }
+}
+
+void test_fn_1 ()
+{
+  fn_1 (var<> ()); // { dg-error "could not convert .* from 'var<>' to 'var<int>'" }
+  /* { dg-begin-multiline-output "" }
+  var<
+    [ != int]>
+     { dg-end-multiline-output "" } */
+  fn_1 (var<int> ());
+  fn_1 (var<int, int> ()); // { dg-error "could not convert .* from 'var<int, int>' to 'var<int>'" }
+  /* { dg-begin-multiline-output "" }
+  var<
+    [int, int != int]>
+     { dg-end-multiline-output "" } */
+  fn_1 (vector<var<int> >()); // { dg-error "could not convert .* from 'vector<var<int> >' to 'var<int>'" }
+  fn_1 (vector<var<int, int> >()); // { dg-error "could not convert .* from 'vector<var<int, int> >' to 'var<int>'" }
+}
+
+void test_fn_2 ()
+{
+  fn_2 (var<> ()); // { dg-error "could not convert .* from 'var<>' to 'var<int, int>'" }
+  /* { dg-begin-multiline-output "" }
+  var<
+    [ != int, int]>
+     { dg-end-multiline-output "" } */
+  fn_2 (var<int> ()); // { dg-error "could not convert .* from 'var<int>' to 'var<int, int>'" }
+  /* { dg-begin-multiline-output "" }
+  var<
+    [int != int, int]>
+     { dg-end-multiline-output "" } */
+  fn_2 (var<int, int> ());
+  fn_2 (vector<var<int> >()); // { dg-error "could not convert .* from 'vector<var<int> >' to 'var<int, int>'" }
+  fn_2 (vector<var<int, int> >()); // { dg-error "could not convert .* from 'vector<var<int, int> >' to 'var<int, int>'" }
+}
+
+void test_fn_3 ()
+{
+  fn_3 (var<> ()); // { dg-error "could not convert .* from 'var<>' to 'vector<var<> >'" }
+  fn_3 (var<int> ()); // { dg-error "could not convert .* from 'var<int>' to 'vector<var<> >'" }
+  fn_3 (var<int, int> ()); // { dg-error "could not convert .* from 'var<int, int>' to 'vector<var<> >'" }
+  fn_3 (vector<var<> >());
+  fn_3 (vector<var<int> >());  // { dg-error "could not convert .* from 'vector<var<int>>' to 'vector<var<>>'" }
+  /* { dg-begin-multiline-output "" }
+  vector<
+    var<
+      [int != ]>>
+     { dg-end-multiline-output "" } */
+  fn_3 (vector<var<int, int> >()); // { dg-error "could not convert .* from 'vector<var<int, int>>' to 'vector<var<>>'" }
+  /* { dg-begin-multiline-output "" }
+  vector<
+    var<
+      [int, int != ]>>
+     { dg-end-multiline-output "" } */
+}
+
+void test_fn_4 ()
+{
+  fn_4 (var<> ()); // { dg-error "could not convert .* from 'var<>' to 'vector<var<int> >'" }
+  fn_4 (var<int> ()); // { dg-error "could not convert .* from 'var<int>' to 'vector<var<int> >'" }
+  fn_4 (var<int, int> ()); // { dg-error "could not convert .* from 'var<int, int>' to 'vector<var<int> >'" }
+  fn_4 (vector<var<> >()); // { dg-error "could not convert .* from 'vector<var<>>' to 'vector<var<int>>'" }
+  /* { dg-begin-multiline-output "" }
+  vector<
+    var<
+      [ != int]>>
+     { dg-end-multiline-output "" } */
+  fn_4 (vector<var<int> >()); 
+  fn_4 (vector<var<int, int> >()); // { dg-error "could not convert .* from 'vector<var<int, int>>' to 'vector<var<int>>'" }
+  /* { dg-begin-multiline-output "" }
+  vector<
+    var<
+      [int, int != int]>>
+     { dg-end-multiline-output "" } */
+}
+
+void test_fn_5 ()
+{
+  fn_5 (var<> ()); // { dg-error "could not convert .* from 'var<>' to 'vector<var<int, int> >'" }
+  fn_5 (var<int> ());  // { dg-error "could not convert .* from 'var<int>' to 'vector<var<int, int> >'" }
+  fn_5 (var<int, int> ());  // { dg-error "could not convert .* from 'var<int, int>' to 'vector<var<int, int> >'" }
+  fn_5 (vector<var<int> >());  // { dg-error "could not convert .* from 'vector<var<int>>' to 'vector<var<int, int>>'" }
+  /* { dg-begin-multiline-output "" }
+  vector<
+    var<
+      [int != int, int]>>
+     { dg-end-multiline-output "" } */
+  fn_5 (vector<var<int, int> >());
+}
diff --git a/gcc/testsuite/g++.dg/template/show-template-tree-3.C b/gcc/testsuite/g++.dg/template/show-template-tree-3.C
new file mode 100644
index 0000000..0eda40b
--- /dev/null
+++ b/gcc/testsuite/g++.dg/template/show-template-tree-3.C
@@ -0,0 +1,37 @@
+/* End-to-end test of -fdiagnostics-show-template-tree and -felide-type
+   using the STL.
+   In particular, ensure that we don't print the default arguments e.g.
+   rather than printing
+     from 'vector<double,allocator<double>>' to 'vector<float,allocator<float>>'
+   (albeit with differences nicely color-coded), we want to print:
+     from 'vector<double>' to 'vector<float>'
+   (again, with the "double" and "float" highlighted, though we can't test
+   for that in this case).  */
+
+// { dg-options "-fdiagnostics-show-template-tree" }
+
+#include <map>
+#include <vector>
+
+using std::vector;
+using std::map;
+
+void takes_vf (vector<float> v);
+void takes_mivf (map<int, vector<float> > v);
+
+int test ()
+{
+  takes_vf (vector<double> ()); // { dg-error "could not convert '.*' from 'vector<double>' to 'vector<float>'" }
+  /* { dg-begin-multiline-output "" }
+  vector<
+    [double != float]>
+     { dg-end-multiline-output "" } */
+
+  takes_mivf (map<int, vector<double> > ()); // { dg-error "could not convert '.*' from 'map<.\\.\\.\\..,vector<double>>' to 'map<.\\.\\.\\..,vector<float>>'" }
+  /* { dg-begin-multiline-output "" }
+  map<
+    [...],
+    vector<
+      [double != float]>>
+     { dg-end-multiline-output "" } */
+}
diff --git a/gcc/testsuite/g++.dg/template/show-template-tree-4.C b/gcc/testsuite/g++.dg/template/show-template-tree-4.C
new file mode 100644
index 0000000..953733b
--- /dev/null
+++ b/gcc/testsuite/g++.dg/template/show-template-tree-4.C
@@ -0,0 +1,95 @@
+// { dg-options "-fdiagnostics-show-template-tree" }
+
+/* Example of default template args, and various kinds of mismatch.  */
+
+template <int = 0, int = 1, int = 2>
+struct s {};
+
+void takes_s (s<> );
+void takes_s013 (s<0, 1, 3> );
+void takes_s321 (s<3, 2, 1> );
+
+void test ()
+{
+  takes_s (s<>());
+  takes_s (s<0, 1>());
+  takes_s (s<0, 1, 2>());
+  takes_s (s<0, 2>()); // { dg-error "could not convert '.*' from 's<.\\.\\.\\..,2>' to 's<.\\.\\.\\..,1>'" }
+  /* { dg-begin-multiline-output "" }
+  s<
+    [...],
+    [2 != 1]>
+     { dg-end-multiline-output "" } */
+
+  takes_s (s<1>()); // { dg-error "could not convert '.*' from 's<1>' to 's<0>'" }
+  /* { dg-begin-multiline-output "" }
+  s<
+    [1 != 0]>
+     { dg-end-multiline-output "" } */
+
+  takes_s (s<0, 1, 3>()); // { dg-error "could not convert '.*' from 's<.\\.\\.\\..,.\\.\\.\\..,3>' to 's<.\\.\\.\\..,.\\.\\.\\..,2>'" }
+  /* { dg-begin-multiline-output "" }
+  s<
+    [...],
+    [...],
+    [3 != 2]>
+     { dg-end-multiline-output "" } */
+
+  takes_s (s<3, 2, 0>()); // { dg-error "could not convert '.*' from 's<3,2,0>' to 's<0,1,2>'" }
+  /* { dg-begin-multiline-output "" }
+  s<
+    [3 != 0],
+    [2 != 1],
+    [0 != 2]>
+     { dg-end-multiline-output "" } */
+
+  takes_s (s<3, 2, 1>()); // { dg-error "could not convert '.*' from 's<3,2,1>' to 's<0,1,2>'" }
+  /* { dg-begin-multiline-output "" }
+  s<
+    [3 != 0],
+    [2 != 1],
+    [1 != 2]>
+     { dg-end-multiline-output "" } */
+
+  takes_s013 (s<0, 1, 2>()); // { dg-error "could not convert '.*' from 's<.\\.\\.\\..,.\\.\\.\\..,2>' to 's<.\\.\\.\\..,.\\.\\.\\..,3>'" }
+  /* { dg-begin-multiline-output "" }
+  s<
+    [...],
+    [...],
+    [2 != 3]>
+     { dg-end-multiline-output "" } */
+
+  takes_s321 (s<>());        // { dg-error "could not convert '.*' from 's<0,1,2>' to 's<3,2,1>'" }
+  /* { dg-begin-multiline-output "" }
+  s<
+    [0 != 3],
+    [1 != 2],
+    [2 != 1]>
+     { dg-end-multiline-output "" } */
+
+  takes_s321 (s<0, 1, 3>()); // { dg-error "could not convert '.*' from 's<0,1,3>' to 's<3,2,1>'" }
+  /* { dg-begin-multiline-output "" }
+  s<
+    [0 != 3],
+    [1 != 2],
+    [3 != 1]>
+     { dg-end-multiline-output "" } */
+
+  takes_s321 (s<3, 2, 0>()); // { dg-error "could not convert '.*' from 's<.\\.\\.\\..,.\\.\\.\\..,0>' to 's<.\\.\\.\\..,.\\.\\.\\..,1>'" }
+  /* { dg-begin-multiline-output "" }
+  s<
+    [...],
+    [...],
+    [0 != 1]>
+     { dg-end-multiline-output "" } */
+
+  takes_s321 (s<3, 2, 1>());
+
+  takes_s321 (s<1, 2, 3>()); // { dg-error "could not convert '.*' from 's<1,.\\.\\.\\..,3>' to 's<3,.\\.\\.\\..,1>'" }
+  /* { dg-begin-multiline-output "" }
+  s<
+    [1 != 3],
+    [...],
+    [3 != 1]>
+     { 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..d4bfa81
--- /dev/null
+++ b/gcc/testsuite/g++.dg/template/show-template-tree-no-elide-type.C
@@ -0,0 +1,24 @@
+// { 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<double> ()); // { dg-error "could not convert .* from 'vector<double>' to 'vector<int>'" }
+  /* { dg-begin-multiline-output "" }
+  vector<
+    [double != int]>
+     { dg-end-multiline-output "" } */
+
+  fn_2 (map<int, double>());  // { dg-error "could not convert .* from 'map<int,double>' to 'map<int,int>'" }
+  /* { dg-begin-multiline-output "" }
+  map<
+    int,
+    [double != int]>
+     { 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..0b75098
--- /dev/null
+++ b/gcc/testsuite/g++.dg/template/show-template-tree.C
@@ -0,0 +1,51 @@
+// { dg-options "-fdiagnostics-show-template-tree" }
+
+template<typename> struct vector {};
+template<typename, typename> struct map {};
+template<int> struct arr {};
+
+void fn_1(vector<int>);
+void fn_2(map<int, int>);
+void fn_3(vector<map<int, float> >);
+void takes_arr_10 (arr<10>);
+
+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 .* 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 .* from 'map<int, int>' to 'vector<int>'" }
+
+  fn_2 (map<int, int>());
+  fn_2 (map<int, double>());  // { dg-error "could not convert .* 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 .* 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 .* from 'vector<map<.\\.\\.\\..,double>>' to 'vector<map<.\\.\\.\\..,float>>'" }
+  /* { dg-begin-multiline-output "" }
+  vector<
+    map<
+      [...],
+      [double != float]>>
+     { dg-end-multiline-output "" } */
+
+  takes_arr_10 (arr<5>()); // { dg-error "could not convert '.*' from 'arr<5>' to 'arr<10>'" }
+  /* { dg-begin-multiline-output "" }
+  arr<
+    [5 != 10]>
+     { dg-end-multiline-output "" } */
+}
diff --git a/gcc/tree-diagnostic.c b/gcc/tree-diagnostic.c
index 4f211ed..ebce1a4 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.  */
 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,
+		      bool, const char **)
 {
   tree t;
 
diff --git a/gcc/tree-diagnostic.h b/gcc/tree-diagnostic.h
index e95183f..85aa980 100644
--- a/gcc/tree-diagnostic.h
+++ b/gcc/tree-diagnostic.h
@@ -55,6 +55,6 @@ void virt_loc_aware_diagnostic_finalizer (diagnostic_context *,
 
 void tree_diagnostics_defaults (diagnostic_context *context);
 bool default_tree_printer (pretty_printer *, text_info *, const char *,
-			   int, bool, bool, bool);
+			   int, bool, bool, bool, bool, const char **);
 
 #endif /* ! GCC_TREE_DIAGNOSTIC_H */
-- 
1.8.5.3

^ permalink raw reply	[flat|nested] 17+ messages in thread

* Re: [PATCH 1/2] (v2) C++ template type diff printing
  2017-05-09 13:39     ` [PATCH 1/2] (v2) " David Malcolm
  2017-05-09 13:35       ` [PATCH 2/2] (v2) Use %qH and %qI throughout C++ frontend David Malcolm
@ 2017-05-09 14:59       ` Nathan Sidwell
  2017-05-10  1:09         ` [PATCH 0/3] v3 of C++ template type diffing David Malcolm
  1 sibling, 1 reply; 17+ messages in thread
From: Nathan Sidwell @ 2017-05-09 14:59 UTC (permalink / raw)
  To: David Malcolm, gcc-patches

On 05/09/2017 10:07 AM, David Malcolm wrote:


> +static const char *
> +type_to_string_with_compare (tree type, tree peer, bool verbose,
> +			     bool show_color)
> +{

> +  for (int idx = 0; idx < len_max; idx++)
> +    {
...
> +      if (idx + 1 < len_max)
> +	pp_character (pp, ',');
> +    }

My favorite idiom for this is to place the test at the beginning of the 
loop and avoid an extra loop value check.  (perhaps optimizers are smart 
enough to tell that 'idx + 1 < max' and 'idx++, idx < max' are the same 
these days)

   if (idx)
     pp_character (pp, ',');

> +static void
> +print_template_tree_comparison (pretty_printer *pp, tree type_a, tree type_b,
> +				bool verbose, int indent)

It looks to me that type_to_string_with_compare and 
print_template_tree_comparison are doing very nearly the same thing, 
with a little formatting difference.  How hard would it be to have them 
forward to a worker function?


> +  unsigned int chunk_idx;
> +  for (chunk_idx = 0; args[chunk_idx]; chunk_idx++)
> +    ;

I have a fondness for putting 'continue;' as the body of such empty 
loops.  Dunno if that's style-compliant though.


> +void
> +cxx_format_postprocessor::handle (pretty_printer *pp)
> +{
> +  /* If we have one of %H and %I, the other should have
> +     been present.  */
> +  if (m_type_a.m_tree || m_type_b.m_tree)
> +    {
> +      gcc_assert (m_type_a.m_tree);
> +      gcc_assert (m_type_b.m_tree);
> +    }

> +  if (m_type_a.m_tree && m_type_b.m_tree)
  As you  fall into this.  Why not simply
    if (m_type_a.m_tree || m_type_b.m_tree)
      { do stuff that will seg fault if one's null }


> +      gcc_assert (type_a.m_tree);

And these asserts are confusing, because some, at least, seem to be 
checking the if condition.

> +      gcc_assert (type_a.m_buffer_ptr);
> +      gcc_assert (type_b.m_tree);
> +      gcc_assert (type_b.m_buffer_ptr);


Generally the C++ bits look good, and my style comments are FWIW not 
obligatory.
1) is it possible to commonize the two functions I mention
2) cleanup the unnecessary asserts in cxx_format_postprocessor::handle

non-c++ bits not reviewed.

nathan
-- 
Nathan Sidwell

^ permalink raw reply	[flat|nested] 17+ messages in thread

* Re: [PATCH 1/2] C++ template type diff printing
  2017-05-05  0:45 [PATCH 1/2] C++ template type diff printing David Malcolm
  2017-05-04 23:13 ` [PATCH 2/2] Use %H and %I throughout C++ frontend David Malcolm
  2017-05-08 11:48 ` [PATCH 1/2] C++ template type diff printing Nathan Sidwell
@ 2017-05-09 15:55 ` Jakub Jelinek
  2 siblings, 0 replies; 17+ messages in thread
From: Jakub Jelinek @ 2017-05-09 15:55 UTC (permalink / raw)
  To: David Malcolm, Joseph S. Myers; +Cc: gcc-patches

On Thu, May 04, 2017 at 07:44:58PM -0400, David Malcolm wrote:
> This patch kit implements two new options to make it easier
> to read diagnostics involving mismatched template types:
>   -fdiagnostics-show-template-tree and
>   -fno-elide-type.
> 
> It adds two new formatting codes: %H and %I which are
> equivalent to %qT, but are to be used together for type
> comparisons e.g.
>   "can't convert from %H to %I".

Just a note, I believe we need to teach gettext about all the extensions
we add, so that xgettext doesn't give up on those.
Dunno if it has been adjusted for the additions done in the last few years.
$ grep gcc-internal-format /usr/share/doc/gettext/NEWS
* Updated the meaning of 'gcc-internal-format' to match GCC 4.3.
* Updated the meaning of 'gcc-internal-format' to match GCC 4.1.
* Updated the meaning of 'gcc-internal-format' to match GCC 4.0.
    as 'gcc-internal-format'.
doesn't look good.
The source file is format-gcc-internal.c.

	Jakub

^ permalink raw reply	[flat|nested] 17+ messages in thread

* Re: [PATCH 1/2] C++ template type diff printing
  2017-05-08 16:58   ` David Malcolm
  2017-05-08 17:44     ` Nathan Sidwell
  2017-05-09 13:39     ` [PATCH 1/2] (v2) " David Malcolm
@ 2017-05-09 21:14     ` Manuel López-Ibáñez
  2 siblings, 0 replies; 17+ messages in thread
From: Manuel López-Ibáñez @ 2017-05-09 21:14 UTC (permalink / raw)
  To: gcc-patches

On 08/05/17 17:51, David Malcolm wrote:
> So I think it can work if we add a "needs quoting" flag to the
> postprocessing phase, if we need to handle the case where %H and %I
> ever appear without 'q' (and have the delayed handling stash that flag,
> and do the quoting there).
>
> I'll look at implementing that.

Perhaps this may also help to fix the quoting (and coloring) of typedefs?

https://gcc.gnu.org/bugzilla/show_bug.cgi?id=62170

>> Is '-felide-type' a good name?  Wouldn't something like
>> '-fdiagnostic-elide-template-args' be better?
>
> Here I'm merely copying clang's option name.
>   http://clang.llvm.org/docs/UsersManual.html#cmdoption-fno-elide-type
>

We don't need to copy things from Clang when they are worse and they are surely 
stuck now with some bad choices, like we are. Let's just copy the good bits :)

-fdiagnostics-show-template-tree is ok, but -felide-type sounds like an 
optimization. Moreover, with modern command-line completion, it is easier to 
partially complete -fdiagnostics- and see what options are there then press 
'e', than partially complete '-f' and try to find the name of that option that 
summarises the template arguments in diagnostics.

Cheers,

Manuel.

^ permalink raw reply	[flat|nested] 17+ messages in thread

* [PATCH 3/3] (v3) Use %qH and %qI throughout C++ frontend
  2017-05-10  1:09         ` [PATCH 0/3] v3 of C++ template type diffing David Malcolm
@ 2017-05-10  1:09           ` David Malcolm
  2017-05-10  1:10           ` [PATCH 1/3] (v3) Non-C++ parts of template type diff printing David Malcolm
                             ` (2 subsequent siblings)
  3 siblings, 0 replies; 17+ messages in thread
From: David Malcolm @ 2017-05-10  1:09 UTC (permalink / raw)
  To: Nathan Sidwell, gcc-patches; +Cc: David Malcolm

(unchanged in v3)

Changed in v2: use %qH and %qI rather than %H and %I.

This is the second half of the kit, which uses %qH and %qI throughout
the C++ frontend whenever describing type mismatches between a pair
of %qT.

gcc/cp/ChangeLog:
	* call.c (print_conversion_rejection): Replace pairs of %qT with
	%qH and %qI in various places.
	(build_user_type_conversion_1): Likewise.
	(build_integral_nontype_arg_conv): Likewise.
	(build_conditional_expr_1): Likewise.
	(convert_like_real): Likewise.
	(convert_arg_to_ellipsis): Likewise.
	(joust): Likewise.
	(initialize_reference): Likewise.
	* cvt.c (cp_convert_to_pointer): Likewise.
	(cp_convert_to_pointer): Likewise.
	(convert_to_reference): Likewise.
	(ocp_convert): Likewise.
	* typeck.c (cp_build_binary_op): Likewise.
	(convert_member_func_to_ptr): Likewise.
	(build_reinterpret_cast_1): Likewise.
	(convert_for_assignment): Likewise.
	* typeck2.c (check_narrowing): Likewise.
---
 gcc/cp/call.c    | 38 +++++++++++++++++++-------------------
 gcc/cp/cvt.c     | 18 +++++++++---------
 gcc/cp/typeck.c  | 22 +++++++++++-----------
 gcc/cp/typeck2.c |  6 +++---
 4 files changed, 42 insertions(+), 42 deletions(-)

diff --git a/gcc/cp/call.c b/gcc/cp/call.c
index f1b6bf4..a7882e6 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 %qH to %qI",
 		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 %qH to %qI",
 	    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 %qH to %qI",
 	    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 %qH to %qI 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 %qH to %qI not considered for "
 		    "non-type template argument", t, type);
 	/* fall through.  */
 
@@ -4833,14 +4833,14 @@ build_conditional_expr_1 (location_t loc, tree arg1, tree arg2, tree arg3,
 	  if (unsafe_conversion_p (loc, stype, arg2, false))
 	    {
 	      if (complain & tf_error)
-		error_at (loc, "conversion of scalar %qT to vector %qT "
+		error_at (loc, "conversion of scalar %qH to vector %qI "
 			       "involves truncation", arg2_type, vtype);
 	      return error_mark_node;
 	    }
 	  if (unsafe_conversion_p (loc, stype, arg3, false))
 	    {
 	      if (complain & tf_error)
-		error_at (loc, "conversion of scalar %qT to vector %qT "
+		error_at (loc, "conversion of scalar %qH to vector %qI "
 			       "involves truncation", arg3_type, vtype);
 	      return error_mark_node;
 	    }
@@ -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 %qH to %qI to "
 				  "match other result of conditional",
 				  loc);
 
@@ -6603,7 +6603,7 @@ convert_like_real (conversion *convs, tree expr, tree fn, int argnum,
 	 from std::nullptr_t requires direct-initialization.  */
       if (NULLPTR_TYPE_P (TREE_TYPE (expr))
 	  && TREE_CODE (totype) == BOOLEAN_TYPE)
-	complained = permerror (loc, "converting to %qT from %qT requires "
+	complained = permerror (loc, "converting to %qH from %qI requires "
 				"direct-initialization",
 				totype, TREE_TYPE (expr));
 
@@ -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 %qH to %qI", 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 %qH to %qI",
 				TREE_TYPE (expr), totype);
       if (complained && fn)
 	inform (DECL_SOURCE_LOCATION (fn),
@@ -6924,14 +6924,14 @@ convert_like_real (conversion *convs, tree expr, tree fn, int argnum,
 	    tree extype = TREE_TYPE (expr);
 	    if (TYPE_REF_IS_RVALUE (ref_type)
 		&& lvalue_p (expr))
-	      error_at (loc, "cannot bind rvalue reference of type %qT to "
-                        "lvalue of type %qT", totype, extype);
+	      error_at (loc, "cannot bind rvalue reference of type %qH to "
+                        "lvalue of type %qI", totype, extype);
 	    else if (!TYPE_REF_IS_RVALUE (ref_type) && !lvalue_p (expr)
 		     && !CP_TYPE_CONST_NON_VOLATILE_P (TREE_TYPE (ref_type)))
 	      error_at (loc, "cannot bind non-const lvalue reference of "
-			"type %qT to an rvalue of type %qT", totype, extype);
+			"type %qH to an rvalue of type %qI", totype, extype);
 	    else if (!reference_compatible_p (TREE_TYPE (totype), extype))
-	      error_at (loc, "binding reference of type %qT to %qT "
+	      error_at (loc, "binding reference of type %qH to %qI "
 			"discards qualifiers", totype, extype);
 	    else
 	      gcc_unreachable ();
@@ -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 %qH to %qI when passing "
 		    "argument to function",
 		    arg_type, double_type_node);
       arg = convert_to_real_nofold (double_type_node, arg);
@@ -9643,7 +9643,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 %qH to %qI",
 			  source, w->second_conv->type)) 
 	    {
 	      inform (input_location, "  because conversion sequence for the argument is better");
@@ -10417,11 +10417,11 @@ initialize_reference (tree type, tree expr,
 		   && !TYPE_REF_IS_RVALUE (type)
 		   && !lvalue_p (expr))
 	    error_at (loc, "invalid initialization of non-const reference of "
-		      "type %qT from an rvalue of type %qT",
+		      "type %qH from an rvalue of type %qI",
 		      type, TREE_TYPE (expr));
 	  else
 	    error_at (loc, "invalid initialization of reference of type "
-		      "%qT from expression of type %qT", type,
+		      "%qH from expression of type %qI", type,
 		      TREE_TYPE (expr));
 	}
       return error_mark_node;
diff --git a/gcc/cp/cvt.c b/gcc/cp/cvt.c
index 5f4b5e3..9ea8084 100644
--- a/gcc/cp/cvt.c
+++ b/gcc/cp/cvt.c
@@ -86,7 +86,7 @@ cp_convert_to_pointer (tree type, tree expr, bool dofold,
       if (!COMPLETE_TYPE_P (intype))
 	{
 	  if (complain & tf_error)
-	    error_at (loc, "can%'t convert from incomplete type %qT to %qT",
+	    error_at (loc, "can%'t convert from incomplete type %qH to %qI",
 		      intype, type);
 	  return error_mark_node;
 	}
@@ -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 %qH to %qI is ambiguous",
 		      expr, intype, type);
 	  return rval;
 	}
@@ -168,7 +168,7 @@ cp_convert_to_pointer (tree type, tree expr, bool dofold,
       if (TYPE_PTRMEMFUNC_P (type))
 	{
 	  if (complain & tf_error)
-	    error_at (loc, "cannot convert %qE from type %qT to type %qT",
+	    error_at (loc, "cannot convert %qE from type %qH to type %qI",
 		      expr, intype, type);
 	  return error_mark_node;
 	}
@@ -195,7 +195,7 @@ cp_convert_to_pointer (tree type, tree expr, bool dofold,
 	    }
 	}
       if (complain & tf_error)
-	error_at (loc, "cannot convert %qE from type %qT to type %qT",
+	error_at (loc, "cannot convert %qE from type %qH to type %qI",
 		  expr, intype, type);
       return error_mark_node;
     }
@@ -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 %qH to %qI", intype, type);
       return error_mark_node;
     }
 
@@ -244,7 +244,7 @@ cp_convert_to_pointer (tree type, tree expr, bool dofold,
     return instantiate_type (type, expr, complain);
 
   if (complain & tf_error)
-    error_at (loc, "cannot convert %qE from type %qT to type %qT",
+    error_at (loc, "cannot convert %qE from type %qH to type %qI",
 	      expr, 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 %qH to %qI discards qualifiers",
 			 ttr, reftype);
 	    else
 	      return error_mark_node;
@@ -514,7 +514,7 @@ convert_to_reference (tree reftype, tree expr, int convtype,
     }
 
   if (complain & tf_error)
-    error_at (loc, "cannot convert type %qT to type %qT", intype, reftype);
+    error_at (loc, "cannot convert type %qH to type %qI", intype, reftype);
 
   return error_mark_node;
 }
@@ -907,7 +907,7 @@ ocp_convert (tree type, tree expr, int convtype, int flags,
       if (invalid_nonstatic_memfn_p (loc, expr, complain))
 	/* We displayed the error message.  */;
       else
-	error_at (loc, "conversion from %qT to non-scalar type %qT requested",
+	error_at (loc, "conversion from %qH to non-scalar type %qI requested",
 		  TREE_TYPE (expr), type);
     }
   return error_mark_node;
diff --git a/gcc/cp/typeck.c b/gcc/cp/typeck.c
index 7aee0d6..ccb5495 100644
--- a/gcc/cp/typeck.c
+++ b/gcc/cp/typeck.c
@@ -5011,7 +5011,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 %qH to %qI "
 				  "to match other operand of binary "
 				  "expression",
 				  location);
@@ -7028,7 +7028,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 %qH to %qI", intype, type);
 
   if (TREE_CODE (intype) == METHOD_TYPE)
     expr = build_addr_func (expr, complain);
@@ -7150,7 +7150,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 %qH to %qI loses precision",
                        intype, type);
           else
             return error_mark_node;
@@ -7190,7 +7190,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 %qH to %qI "
                  "increases required alignment of target type", intype, type);
 
       /* We need to strip nops here, because the front end likes to
@@ -8504,33 +8504,33 @@ convert_for_assignment (tree type, tree rhs,
 		    return r;
 		}
 	      else if (fndecl)
-		error ("cannot convert %qT to %qT for argument %qP to %qD",
+		error ("cannot convert %qH to %qI for argument %qP to %qD",
 		       rhstype, type, parmnum, fndecl);
 	      else
 		switch (errtype)
 		  {
 		    case ICR_DEFAULT_ARGUMENT:
-		      error ("cannot convert %qT to %qT in default argument",
+		      error ("cannot convert %qH to %qI in default argument",
 			     rhstype, type);
 		      break;
 		    case ICR_ARGPASS:
-		      error ("cannot convert %qT to %qT in argument passing",
+		      error ("cannot convert %qH to %qI in argument passing",
 			     rhstype, type);
 		      break;
 		    case ICR_CONVERTING:
-		      error ("cannot convert %qT to %qT",
+		      error ("cannot convert %qH to %qI",
 			     rhstype, type);
 		      break;
 		    case ICR_INIT:
-		      error ("cannot convert %qT to %qT in initialization",
+		      error ("cannot convert %qH to %qI in initialization",
 			     rhstype, type);
 		      break;
 		    case ICR_RETURN:
-		      error ("cannot convert %qT to %qT in return",
+		      error ("cannot convert %qH to %qI in return",
 			     rhstype, type);
 		      break;
 		    case ICR_ASSIGN:
-		      error ("cannot convert %qT to %qT in assignment",
+		      error ("cannot convert %qH to %qI in assignment",
 			     rhstype, type);
 		      break;
 		    default:
diff --git a/gcc/cp/typeck2.c b/gcc/cp/typeck2.c
index 1f0eb45..40328cd 100644
--- a/gcc/cp/typeck2.c
+++ b/gcc/cp/typeck2.c
@@ -954,7 +954,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 %qH to %qI inside { } is ill-formed in C++11",
 			init, ftype, type);
 	  ok = true;
 	}
@@ -965,7 +965,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 %qH to %qI inside { }",
 			      init, ftype, type)
 		  && almost_ok)
 		inform (loc, " the expression has a constant value but is not "
@@ -978,7 +978,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 %qH to %qI "
 		   "inside { }", init, ftype, type);
 	  if (errorcount == savederrorcount)
 	    ok = true;
-- 
1.8.5.3

^ permalink raw reply	[flat|nested] 17+ messages in thread

* [PATCH 0/3] v3 of C++ template type diffing
  2017-05-09 14:59       ` [PATCH 1/2] (v2) C++ template type diff printing Nathan Sidwell
@ 2017-05-10  1:09         ` David Malcolm
  2017-05-10  1:09           ` [PATCH 3/3] (v3) Use %qH and %qI throughout C++ frontend David Malcolm
                             ` (3 more replies)
  0 siblings, 4 replies; 17+ messages in thread
From: David Malcolm @ 2017-05-10  1:09 UTC (permalink / raw)
  To: Nathan Sidwell, gcc-patches; +Cc: David Malcolm

On Tue, 2017-05-09 at 10:52 -0400, Nathan Sidwell wrote:
> On 05/09/2017 10:07 AM, David Malcolm wrote:
> 
> 
> > +static const char *
> > +type_to_string_with_compare (tree type, tree peer, bool verbose,
> > +			     bool show_color)
> > +{
> 
> > +  for (int idx = 0; idx < len_max; idx++)
> > +    {
> ...
> > +      if (idx + 1 < len_max)
> > +	pp_character (pp, ',');
> > +    }
> 
> My favorite idiom for this is to place the test at the beginning of
> the
> loop and avoid an extra loop value check.  (perhaps optimizers are
> smart
> enough to tell that 'idx + 1 < max' and 'idx++, idx < max' are the
> same
> these days)
> 
>    if (idx)
>      pp_character (pp, ',');

Done.

> > +static void
> > +print_template_tree_comparison (pretty_printer *pp, tree type_a,
> > tree type_b,
> > +				bool verbose, int indent)
> 
> It looks to me that type_to_string_with_compare and
> print_template_tree_comparison are doing very nearly the same thing,
> with a little formatting difference.  How hard would it be to have
> them
> forward to a worker function?

Done: I merged their bodies into a new function -
print_template_differences.


> > +  unsigned int chunk_idx;
> > +  for (chunk_idx = 0; args[chunk_idx]; chunk_idx++)
> > +    ;
> 
> I have a fondness for putting 'continue;' as the body of such empty
> loops.  Dunno if that's style-compliant though.

Dunno either.  I left this untouched. 

> > +void
> > +cxx_format_postprocessor::handle (pretty_printer *pp)
> > +{
> > +  /* If we have one of %H and %I, the other should have
> > +     been present.  */
> > +  if (m_type_a.m_tree || m_type_b.m_tree)
> > +    {
> > +      gcc_assert (m_type_a.m_tree);
> > +      gcc_assert (m_type_b.m_tree);
> > +    }
> 
> > +  if (m_type_a.m_tree && m_type_b.m_tree)
>   As you  fall into this.  Why not simply
>     if (m_type_a.m_tree || m_type_b.m_tree)
>       { do stuff that will seg fault if one's null }

Done. 

> > +      gcc_assert (type_a.m_tree);
> 
> And these asserts are confusing, because some, at least, seem to be
> checking the if condition.
> 
> > +      gcc_assert (type_a.m_buffer_ptr);
> > +      gcc_assert (type_b.m_tree);
> > +      gcc_assert (type_b.m_buffer_ptr);
> 
> 
> Generally the C++ bits look good, and my style comments are FWIW not
> obligatory.
> 1) is it possible to commonize the two functions I mention
> 2) cleanup the unnecessary asserts in
> cxx_format_postprocessor::handle

Done.

> non-c++ bits not reviewed.
> 
> nathan

I split out the non-c++ bits into a separate patch.

v3 of the patch kit is thus three parts:

[1/3] Non-C++ parts of template type diff printing
  I believe I can self-approve this part

[2/3] (v3) C++ template type diff printing
  Updated as above.
  
[3/3] (v3) Use %qH and %qI throughout C++ frontend
  Mostly mechanical

I've successfully bootstrapped&regrtested the combination of the
three patches on x86_64-pc-linux-gnu.

Are patches 2 and 3 OK for trunk?

Thanks
Dave

 gcc/c-family/c-format.c                            |   2 +-
 gcc/c-family/c.opt                                 |   8 +
 gcc/c/c-objc-common.c                              |   5 +-
 gcc/cp/call.c                                      |  40 +-
 gcc/cp/cvt.c                                       |  18 +-
 gcc/cp/error.c                                     | 453 ++++++++++++++++++++-
 gcc/cp/typeck.c                                    |  22 +-
 gcc/cp/typeck2.c                                   |   6 +-
 gcc/diagnostic-color.c                             |   6 +-
 gcc/doc/invoke.texi                                |  50 ++-
 gcc/fortran/error.c                                |   5 +-
 gcc/pretty-print.c                                 |  11 +-
 gcc/pretty-print.h                                 |  20 +-
 gcc/testsuite/g++.dg/plugin/plugin.exp             |   3 +
 .../show-template-tree-color-no-elide-type.C       |  30 ++
 .../g++.dg/plugin/show-template-tree-color.C       |  30 ++
 .../plugin/show_template_tree_color_plugin.c       |  38 ++
 .../g++.dg/template/show-template-tree-2.C         | 118 ++++++
 .../g++.dg/template/show-template-tree-3.C         |  37 ++
 .../g++.dg/template/show-template-tree-4.C         |  95 +++++
 .../template/show-template-tree-no-elide-type.C    |  24 ++
 gcc/testsuite/g++.dg/template/show-template-tree.C |  51 +++
 gcc/tree-diagnostic.c                              |   3 +-
 gcc/tree-diagnostic.h                              |   2 +-
 24 files changed, 1018 insertions(+), 59 deletions(-)
 create mode 100644 gcc/testsuite/g++.dg/plugin/show-template-tree-color-no-elide-type.C
 create mode 100644 gcc/testsuite/g++.dg/plugin/show-template-tree-color.C
 create mode 100644 gcc/testsuite/g++.dg/plugin/show_template_tree_color_plugin.c
 create mode 100644 gcc/testsuite/g++.dg/template/show-template-tree-2.C
 create mode 100644 gcc/testsuite/g++.dg/template/show-template-tree-3.C
 create mode 100644 gcc/testsuite/g++.dg/template/show-template-tree-4.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

-- 
1.8.5.3

^ permalink raw reply	[flat|nested] 17+ messages in thread

* [PATCH 1/3] (v3) Non-C++ parts of template type diff printing
  2017-05-10  1:09         ` [PATCH 0/3] v3 of C++ template type diffing David Malcolm
  2017-05-10  1:09           ` [PATCH 3/3] (v3) Use %qH and %qI throughout C++ frontend David Malcolm
@ 2017-05-10  1:10           ` David Malcolm
  2017-05-10  2:32           ` [PATCH 2/3] (v3) C++ " David Malcolm
  2017-05-10 13:46           ` [PATCH 0/3] v3 of C++ template type diffing Nathan Sidwell
  3 siblings, 0 replies; 17+ messages in thread
From: David Malcolm @ 2017-05-10  1:10 UTC (permalink / raw)
  To: Nathan Sidwell, gcc-patches; +Cc: David Malcolm

This patch is broken out from the C++ patch, for ease of review.

I believe I can self-approve this one, assuming the other patches
in the kit are OK.

The patch:
- adds the bool and const char ** params to the
pp_format_decoder callback
- adds the format_postprocessor class
- adds the "type-diff" colorization code.

All are unused until patch 2 in the kit.

gcc/c/ChangeLog:
	* c-objc-common.c (c_tree_printer): Gain bool and const char **
	parameters.

gcc/cp/ChangeLog:
	* error.c (cp_printer): Gain bool and const char ** parameters.

gcc/ChangeLog:
	* diagnostic-color.c (color_dict): Add "type-diff".
	(parse_gcc_colors): Update comment.
	* doc/invoke.texi (Diagnostic Message Formatting Options): Add
	-fdiagnostics-show-template-tree and -fno-elide-type.
	(GCC_COLORS): Add type-diff to example.
	(type-diff=): New.

gcc/fortran/ChangeLog:
	* error.c (gfc_format_decoder): Update for new bool and
	const char ** params.

gcc/ChangeLog:
	* pretty-print.c (pp_format): Pass quote and formatters[argno] to
	the pp_format_decoder callback.  Call any m_format_postprocessor's
	"handle" method.
	(pretty_printer::pretty_printer): Initialize
	m_format_postprocessor.
	(pretty_printer::~pretty_printer): Delete any
	m_format_postprocessor.
	* pretty-print.h (printer_fn): Add bool and const char **
	parameters.
	(class format_postprocessor): New class.
	(struct pretty_printer::format_decoder): Document the new
	parameters.
	(struct pretty_printer::m_format_postprocessor): New field.
	* tree-diagnostic.c (default_tree_printer): Update for new
	bool and const char ** params.
	* tree-diagnostic.h (default_tree_printer): Likewise.
---
 gcc/c/c-objc-common.c  |  5 +++--
 gcc/cp/error.c         |  5 +++--
 gcc/diagnostic-color.c |  6 ++++--
 gcc/doc/invoke.texi    |  8 +++++++-
 gcc/fortran/error.c    |  5 +++--
 gcc/pretty-print.c     | 11 ++++++++++-
 gcc/pretty-print.h     | 20 ++++++++++++++++++--
 gcc/tree-diagnostic.c  |  3 ++-
 gcc/tree-diagnostic.h  |  2 +-
 9 files changed, 51 insertions(+), 14 deletions(-)

diff --git a/gcc/c/c-objc-common.c b/gcc/c/c-objc-common.c
index 5e69488..05212b2 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, 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,
+		bool, const char **)
 {
   tree t = NULL_TREE;
   tree name;
diff --git a/gcc/cp/error.c b/gcc/cp/error.c
index a83ecb2..4934c51 100644
--- a/gcc/cp/error.c
+++ b/gcc/cp/error.c
@@ -99,7 +99,7 @@ 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, bool, const char **);
 
 /* CONTEXT->printer is a basic pretty printer that was constructed
    presumably by diagnostic_initialize(), called early in the
@@ -3582,7 +3582,8 @@ maybe_print_constexpr_context (diagnostic_context *context)
    %X   exception-specification.  */
 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,
+	    bool, const char **)
 {
   const char *result;
   tree t = NULL;
diff --git a/gcc/diagnostic-color.c b/gcc/diagnostic-color.c
index 8353fe0..6adb872 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 }
 };
 
@@ -204,8 +205,9 @@ colorize_stop (bool show_color)
 /* Parse GCC_COLORS.  The default would look like:
    GCC_COLORS='error=01;31:warning=01;35:note=01;36:\
    range1=32:range2=34:locus=01:quote=01:\
-   fixit-insert=32:fixit-delete=31'\
-   diff-filename=01:diff-hunk=32:diff-delete=31:diff-insert=32'
+   fixit-insert=32:fixit-delete=31:'\
+   diff-filename=01:diff-hunk=32:diff-delete=31:diff-insert=32:\
+   type-diff=01;32'
    No character escaping is needed or supported.  */
 static bool
 parse_gcc_colors (void)
diff --git a/gcc/doc/invoke.texi b/gcc/doc/invoke.texi
index 65308c9..1ba45cf 100644
--- a/gcc/doc/invoke.texi
+++ b/gcc/doc/invoke.texi
@@ -3442,7 +3442,8 @@ The default @env{GCC_COLORS} is
 @smallexample
 error=01;31:warning=01;35:note=01;36:range1=32:range2=34:locus=01:\
 quote=01:fixit-insert=32:fixit-delete=31:\
-diff-filename=01:diff-hunk=32:diff-delete=31:diff-insert=32
+diff-filename=01:diff-hunk=32:diff-delete=31:diff-insert=32:\
+type-diff=01;32
 @end smallexample
 @noindent
 where @samp{01;31} is bold red, @samp{01;35} is bold magenta,
@@ -3506,6 +3507,11 @@ SGR substring for deleted lines within generated patches.
 @item diff-insert=
 @vindex diff-insert GCC_COLORS @r{capability}
 SGR substring for inserted lines within generated patches.
+
+@item type-diff=
+@vindex type-diff GCC_COLORS @r{capability}
+SGR substring for highlighting mismatching types within template
+arguments in the C++ frontend.
 @end table
 
 @item -fno-diagnostics-show-option
diff --git a/gcc/fortran/error.c b/gcc/fortran/error.c
index 0312499..af72581 100644
--- a/gcc/fortran/error.c
+++ b/gcc/fortran/error.c
@@ -917,7 +917,8 @@ gfc_notify_std (int std, const char *gmsgid, ...)
 */
 static bool
 gfc_format_decoder (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,
+		    bool quoted, const char **buffer_ptr)
 {
   switch (*spec)
     {
@@ -948,7 +949,7 @@ gfc_format_decoder (pretty_printer *pp, text_info *text, const char *spec,
 	 etc. diagnostics can use the FE printer while the FE is still
 	 active.  */
       return default_tree_printer (pp, text, spec, precision, wide,
-				   set_locus, hash);
+				   set_locus, hash, quoted, buffer_ptr);
     }
 }
 
diff --git a/gcc/pretty-print.c b/gcc/pretty-print.c
index bcb1a70..570dec7 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, quote,
+					 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);
+
   /* 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 (NULL),
     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..40e56a3 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, 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 QUOTE and BUFFER_PTR are 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/tree-diagnostic.c b/gcc/tree-diagnostic.c
index 4f211ed..ebce1a4 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.  */
 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,
+		      bool, const char **)
 {
   tree t;
 
diff --git a/gcc/tree-diagnostic.h b/gcc/tree-diagnostic.h
index e95183f..85aa980 100644
--- a/gcc/tree-diagnostic.h
+++ b/gcc/tree-diagnostic.h
@@ -55,6 +55,6 @@ void virt_loc_aware_diagnostic_finalizer (diagnostic_context *,
 
 void tree_diagnostics_defaults (diagnostic_context *context);
 bool default_tree_printer (pretty_printer *, text_info *, const char *,
-			   int, bool, bool, bool);
+			   int, bool, bool, bool, bool, const char **);
 
 #endif /* ! GCC_TREE_DIAGNOSTIC_H */
-- 
1.8.5.3

^ permalink raw reply	[flat|nested] 17+ messages in thread

* [PATCH 2/3] (v3) C++ template type diff printing
  2017-05-10  1:09         ` [PATCH 0/3] v3 of C++ template type diffing David Malcolm
  2017-05-10  1:09           ` [PATCH 3/3] (v3) Use %qH and %qI throughout C++ frontend David Malcolm
  2017-05-10  1:10           ` [PATCH 1/3] (v3) Non-C++ parts of template type diff printing David Malcolm
@ 2017-05-10  2:32           ` David Malcolm
  2017-05-10 13:46           ` [PATCH 0/3] v3 of C++ template type diffing Nathan Sidwell
  3 siblings, 0 replies; 17+ messages in thread
From: David Malcolm @ 2017-05-10  2:32 UTC (permalink / raw)
  To: Nathan Sidwell, gcc-patches; +Cc: David Malcolm

Changes in v3:
- Merge bodies of type_to_string_with_compare and
print_template_tree_comparison into a new function,
print_template_differences.  Use simpler comma-insertion criterion.
- Cleanup assertions in cxx_format_postprocessor::handle.

Would it make sense to rename "m_type_a"/"m_type_b" to "m_type_H"/m_type_I"?
(we normally don't go for uppercase in fieldnames, but given the codes are
case-sensitive, does it make sense here?)

gcc/c-family/ChangeLog:
	* c-format.c (gcc_cxxdiag_char_table): Add 'H' and 'I' to
	format_chars.
	* c.opt (fdiagnostics-show-template-tree): New option.
	(felide-type): New option.

gcc/cp/ChangeLog:
	* call.c (perform_implicit_conversion_flags): Convert
	"from %qT to %qT" to "from %qH to %qI" in diagnostic.
	* error.c (struct deferred_printed_type): New struct.
	(class cxx_format_postprocessor): New class.
	(cxx_initialize_diagnostics): Wire up a cxx_format_postprocessor
	to pp->m_format_postprocessor.
	(comparable_template_types_p): New function.
	(newline_and_indent): New function.
	(arg_to_string): New function.
	(print_nonequal_arg): New function.
	(print_template_differences): New function.
	(type_to_string_with_compare): New function.
	(print_template_tree_comparison): New function.
	(append_formatted_chunk): New function.
	(add_quotes): New function.
	(cxx_format_postprocessor::handle): New function.
	(defer_phase_2_of_type_diff): New function.
	(cp_printer): Add "quoted" and "buffer_ptr" params.  Implement
	%H and %I.

gcc/ChangeLog:
	* doc/invoke.texi (-fdiagnostics-show-template-tree): New.
	(-fno-elide-type): New.

gcc/testsuite/ChangeLog:
	* g++.dg/plugin/plugin.exp (plugin_test_list): Add...
	* g++.dg/plugin/show-template-tree-color-no-elide-type.C: New
	test case.
	* g++.dg/plugin/show-template-tree-color.C: New test case.
	* g++.dg/plugin/show_template_tree_color_plugin.c: New plugin.
	* g++.dg/template/show-template-tree-2.C: New test case.
	* g++.dg/template/show-template-tree-3.C: New test case.
	* g++.dg/template/show-template-tree-4.C: New test case.
	* g++.dg/template/show-template-tree-no-elide-type.C: New test case.
	* g++.dg/template/show-template-tree.C: New test case.
---
 gcc/c-family/c-format.c                            |   2 +-
 gcc/c-family/c.opt                                 |   8 +
 gcc/cp/call.c                                      |   2 +-
 gcc/cp/error.c                                     | 450 ++++++++++++++++++++-
 gcc/doc/invoke.texi                                |  42 ++
 gcc/testsuite/g++.dg/plugin/plugin.exp             |   3 +
 .../show-template-tree-color-no-elide-type.C       |  30 ++
 .../g++.dg/plugin/show-template-tree-color.C       |  30 ++
 .../plugin/show_template_tree_color_plugin.c       |  38 ++
 .../g++.dg/template/show-template-tree-2.C         | 118 ++++++
 .../g++.dg/template/show-template-tree-3.C         |  37 ++
 .../g++.dg/template/show-template-tree-4.C         |  95 +++++
 .../template/show-template-tree-no-elide-type.C    |  24 ++
 gcc/testsuite/g++.dg/template/show-template-tree.C |  51 +++
 14 files changed, 926 insertions(+), 4 deletions(-)
 create mode 100644 gcc/testsuite/g++.dg/plugin/show-template-tree-color-no-elide-type.C
 create mode 100644 gcc/testsuite/g++.dg/plugin/show-template-tree-color.C
 create mode 100644 gcc/testsuite/g++.dg/plugin/show_template_tree_color_plugin.c
 create mode 100644 gcc/testsuite/g++.dg/template/show-template-tree-2.C
 create mode 100644 gcc/testsuite/g++.dg/template/show-template-tree-3.C
 create mode 100644 gcc/testsuite/g++.dg/template/show-template-tree-4.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-format.c b/gcc/c-family/c-format.c
index 400eb66..fd40359 100644
--- a/gcc/c-family/c-format.c
+++ b/gcc/c-family/c-format.c
@@ -754,7 +754,7 @@ static const format_char_info gcc_cxxdiag_char_table[] =
   /* Custom conversion specifiers.  */
 
   /* These will require a "tree" at runtime.  */
-  { "ADEFKSTVX",0,STD_C89,{ T89_V,   BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN  }, "q+#",   "",   NULL },
+  { "ADEFHIKSTVX",0,STD_C89,{ T89_V,   BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN  }, "q+#",   "",   NULL },
 
   { "v", 0,STD_C89, { T89_I,   BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN  }, "q#",  "",   NULL },
 
diff --git a/gcc/c-family/c.opt b/gcc/c-family/c.opt
index 9ad2f6e..4648e60 100644
--- a/gcc/c-family/c.opt
+++ b/gcc/c-family/c.opt
@@ -1342,6 +1342,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)
+Print hierarchical comparisons when template types are mismatched.
+
 fdirectives-only
 C ObjC C++ ObjC++
 Preprocess directives only.
@@ -1361,6 +1365,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/cp/call.c b/gcc/cp/call.c
index c15b8e4..f1b6bf4 100644
--- a/gcc/cp/call.c
+++ b/gcc/cp/call.c
@@ -10099,7 +10099,7 @@ 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,
+	    error_at (loc, "could not convert %qE from %qH to %qI", expr,
 		      TREE_TYPE (expr), type);
 	}
       expr = error_mark_node;
diff --git a/gcc/cp/error.c b/gcc/cp/error.c
index 4934c51..996998c 100644
--- a/gcc/cp/error.c
+++ b/gcc/cp/error.c
@@ -101,6 +101,49 @@ static void cp_print_error_function (diagnostic_context *, diagnostic_info *);
 static bool cp_printer (pretty_printer *, text_info *, const char *,
 			int, bool, 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), m_quote (false)
+  {}
+
+  deferred_printed_type (tree type, const char **buffer_ptr, bool verbose,
+			 bool quote)
+  : m_tree (type), m_buffer_ptr (buffer_ptr), m_verbose (verbose),
+    m_quote (quote)
+  {
+    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;
+  bool m_quote;
+};
+
+/* 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
    compiler's initialization process (in general_init) Before the FE
@@ -123,6 +166,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.  */
@@ -3565,6 +3609,388 @@ maybe_print_constexpr_context (diagnostic_context *context)
     }
 }
 \f
+
+/* 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 (!CLASS_TYPE_P (type_a))
+    return false;
+  if (!CLASS_TYPE_P (type_b))
+    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);
+}
+
+/* Generate a GC-allocated string for ARG, an expression or type.  */
+
+static const char *
+arg_to_string (tree arg, bool verbose)
+{
+  if (TYPE_P (arg))
+    return type_to_string (arg, verbose);
+  else
+    return expr_to_string (arg);
+}
+
+/* Subroutine to type_to_string_with_compare and
+   print_template_tree_comparison.
+
+   Print a representation of ARG (an expression or type) to PP,
+   colorizing it as "type-diff" if PP->show_color.  */
+
+static void
+print_nonequal_arg (pretty_printer *pp, tree arg, bool verbose)
+{
+  pp_printf (pp, "%r%s%R",
+	     "type-diff",
+	     (arg
+	      ? arg_to_string (arg, verbose)
+	      : G_("(no argument)")));
+}
+
+/* Recursively print template TYPE_A to PP, as compared to template TYPE_B.
+
+   The types must satisfy comparable_template_types_p.
+
+   If INDENT is 0, then this is equivalent to type_to_string (TYPE_A), but
+   potentially colorizing/eliding in comparison with TYPE_B.
+
+   For example given types:
+     vector<map<int,double>>
+   and
+     vector<map<int,float>>
+   then the result on PP would be:
+     vector<map<[...],double>>
+   with type elision, and:
+     vector<map<int,double>>
+   without type elision.
+
+   In both cases the parts of TYPE that differ from PEER will be colorized
+   if pp_show_color (pp) is true.  In the above example, this would be
+   "double".
+
+   If INDENT is non-zero, then the types are printed in a tree-like form
+   which shows both types.  In the above example, the result on PP would be:
+
+     vector<
+       map<
+         [...],
+         [double != float]>>
+
+   and without type-elision would be:
+
+     vector<
+       map<
+         int,
+         [double != float]>>
+
+   As before, the differing parts of the types are colorized if
+   pp_show_color (pp) is true ("double" and "float" in this example).
+
+   Template arguments in which both types are using the default arguments
+   are not printed; if at least one of the two types is using a non-default
+   argument, then that argument is printed (or both arguments for the
+   tree-like print format).  */
+
+static void
+print_template_differences (pretty_printer *pp, tree type_a, tree type_b,
+			    bool verbose, int indent)
+{
+  if (indent)
+    newline_and_indent (pp, indent);
+
+  tree tinfo_a = TYPE_TEMPLATE_INFO (type_a);
+  tree tinfo_b = TYPE_TEMPLATE_INFO (type_b);
+
+  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 flags = 0;
+  int len_a = get_non_default_template_args_count (args_a, flags);
+  args_a = INNERMOST_TEMPLATE_ARGS (args_a);
+  int len_b = get_non_default_template_args_count (args_b, flags);
+  args_b = INNERMOST_TEMPLATE_ARGS (args_b);
+  /* Determine the maximum range of args for which non-default template args
+     were used; beyond this, only default args (if any) were used, and so
+     they will be equal from this point onwards.
+     One of the two peers might have used default arguments within this
+     range, but the other will be using non-default arguments, and so
+     it's more readable to print both within this range, to highlight
+     the differences.  */
+  int len_max = MAX (len_a, len_b);
+  gcc_assert (TREE_CODE (args_a) == TREE_VEC);
+  gcc_assert (TREE_CODE (args_b) == TREE_VEC);
+  for (int idx = 0; idx < len_max; idx++)
+    {
+      if (idx)
+	pp_character (pp, ',');
+
+      tree arg_a = TREE_VEC_ELT (args_a, idx);
+      tree arg_b = TREE_VEC_ELT (args_b, idx);
+      if (arg_a == arg_b)
+	{
+	  if (indent)
+	    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
+	{
+	  int new_indent = indent ? indent + 2 : 0;
+	  if (comparable_template_types_p (arg_a, arg_b))
+	    print_template_differences (pp, arg_a, arg_b, verbose, new_indent);
+	  else
+	    if (indent)
+	      {
+		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, ']');
+	      }
+	    else
+	      print_nonequal_arg (pp, arg_a, verbose);
+	}
+    }
+  pp_printf (pp, ">");
+}
+
+/* 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".
+
+   Template arguments in which both types are using the default arguments
+   are not printed; if at least one of the two types is using a non-default
+   argument, then both arguments are printed.
+
+   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;
+
+  print_template_differences (pp, type, peer, verbose, 0);
+  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).
+
+   Template arguments in which both types are using the default arguments
+   are not printed; if at least one of the two types is using a non-default
+   argument, then both arguments are printed.  */
+
+static void
+print_template_tree_comparison (pretty_printer *pp, tree type_a, tree type_b,
+				bool verbose, int indent)
+{
+  print_template_differences (pp, type_a, type_b, verbose, indent);
+}
+
+/* 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, storing the result into the chunk_info
+   for pp_format.  Quote them if 'q' was provided.
+   Also print the difference in tree form, adding it as
+   an additional chunk.  */
+
+void
+cxx_format_postprocessor::handle (pretty_printer *pp)
+{
+  /* If we have one of %H and %I, the other should have
+     been present.  */
+  if (m_type_a.m_tree || m_type_b.m_tree)
+    {
+      /* 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_buffer_ptr);
+      gcc_assert (type_b.m_buffer_ptr);
+
+      bool show_color = pp_show_color (pp);
+
+      const char *type_a_text;
+      const char *type_b_text;
+
+      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);
+	}
+
+      if (type_a.m_quote)
+	type_a_text = add_quotes (type_a_text, show_color);
+      *type_a.m_buffer_ptr = type_a_text;
+
+       if (type_b.m_quote)
+	type_b_text = add_quotes (type_b_text, show_color);
+      *type_b.m_buffer_ptr = type_b_text;
+   }
+}
+
+/* Subroutine for handling %H and %I, to support i18n of messages like:
+
+    error_at (loc, "could not convert %qE from %qH to %qI",
+	       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.
+
+   This is called in phase 2 of pp_format, when it is accumulating
+   a series of formatted chunks.  We stash the location of the chunk
+   we're meant to have written to, so that we can write to it in the
+   m_format_postprocessor hook.
+
+   We also need to stash whether a 'q' prefix was provided (the QUOTE
+   param)  so that we can add the quotes when writing out the delayed
+   chunk.  */
+
+static void
+defer_phase_2_of_type_diff (deferred_printed_type *deferred,
+			    tree type, const char **buffer_ptr,
+			    bool verbose, bool quote)
+{
+  gcc_assert (deferred->m_tree == NULL_TREE);
+  gcc_assert (deferred->m_buffer_ptr == NULL);
+  *deferred = deferred_printed_type (type, buffer_ptr, verbose, quote);
+}
+
+
 /* Called from output_format -- during diagnostic message processing --
    to handle C++ specific format specifier with the following meanings:
    %A   function argument-list.
@@ -3579,12 +4005,18 @@ 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,
-	    bool, const char **)
+	    bool quoted, const char **buffer_ptr)
 {
+  gcc_assert (pp->m_format_postprocessor);
+  cxx_format_postprocessor *postprocessor
+    = static_cast <cxx_format_postprocessor *> (pp->m_format_postprocessor);
+
   const char *result;
   tree t = NULL;
 #define next_tree    (t = va_arg (*text->args_ptr, tree))
@@ -3630,6 +4062,20 @@ cp_printer (pretty_printer *pp, text_info *text, const char *spec,
       percent_K_format (text);
       return true;
 
+    case 'H':
+      {
+	defer_phase_2_of_type_diff (&postprocessor->m_type_a, next_tree,
+				    buffer_ptr, verbose, quoted);
+	return true;
+      }
+
+    case 'I':
+      {
+	defer_phase_2_of_type_diff (&postprocessor->m_type_b, next_tree,
+				    buffer_ptr, verbose, quoted);
+	return true;
+      }
+
     default:
       return false;
     }
diff --git a/gcc/doc/invoke.texi b/gcc/doc/invoke.texi
index 1ba45cf..9966fa0 100644
--- a/gcc/doc/invoke.texi
+++ b/gcc/doc/invoke.texi
@@ -251,6 +251,7 @@ Objective-C and Objective-C++ Dialects}.
 -fdiagnostics-color=@r{[}auto@r{|}never@r{|}always@r{]}  @gol
 -fno-diagnostics-show-option  -fno-diagnostics-show-caret @gol
 -fdiagnostics-parseable-fixits  -fdiagnostics-generate-patch @gol
+-fdiagnostics-show-template-tree -fno-elide-type @gol
 -fno-show-column}
 
 @item Warning Options
@@ -3584,6 +3585,47 @@ are printed.  For example:
 The diff may or may not be colorized, following the same rules
 as for diagnostics (see @option{-fdiagnostics-color}).
 
+@item -fdiagnostics-show-template-tree
+@opindex fdiagnostics-show-template-tree
+
+In the C++ frontend, when printing diagnostics showing mismatching
+template types, such as:
+
+@smallexample
+  could not convert 'std::map<int, std::vector<double> >()'
+    from 'map<[...],vector<double>>' to 'map<[...],vector<float>>
+@end smallexample
+
+the @option{-fdiagnostics-show-template-tree} flag enables printing a
+tree-like structure showing the common and differing parts of the types,
+such as:
+
+@smallexample
+  map<
+    [...],
+    vector<
+      [double != float]>>
+@end smallexample
+
+The parts that differ are highlighted with color (``double'' and
+``float'' in this case).
+
+@item -fno-elide-type
+@opindex fno-elide-type
+@opindex felide-type
+By default when the C++ frontend prints diagnostics showing mismatching
+template types, common parts of the types are printed as ``[...]'' to
+simplify the error message.  For example:
+
+@smallexample
+  could not convert 'std::map<int, std::vector<double> >()'
+    from 'map<[...],vector<double>>' to 'map<[...],vector<float>>
+@end smallexample
+
+Specifying the @option{-fno-elide-type} flag suppresses that behavior.
+This flag also affects the output of the
+@option{-fdiagnostics-show-template-tree} flag.
+
 @item -fno-show-column
 @opindex fno-show-column
 Do not print column numbers in diagnostics.  This may be necessary if
diff --git a/gcc/testsuite/g++.dg/plugin/plugin.exp b/gcc/testsuite/g++.dg/plugin/plugin.exp
index 6a0c1aa..94ebe93 100644
--- a/gcc/testsuite/g++.dg/plugin/plugin.exp
+++ b/gcc/testsuite/g++.dg/plugin/plugin.exp
@@ -65,6 +65,9 @@ set plugin_test_list [list \
     { def_plugin.c def-plugin-test.C } \
     { ../../gcc.dg/plugin/diagnostic_plugin_test_tree_expression_range.c \
 	  diagnostic-test-expressions-1.C } \
+    { show_template_tree_color_plugin.c \
+    	  show-template-tree-color.C \
+    	  show-template-tree-color-no-elide-type.C } \
 ]
 
 foreach plugin_test $plugin_test_list {
diff --git a/gcc/testsuite/g++.dg/plugin/show-template-tree-color-no-elide-type.C b/gcc/testsuite/g++.dg/plugin/show-template-tree-color-no-elide-type.C
new file mode 100644
index 0000000..cab0359
--- /dev/null
+++ b/gcc/testsuite/g++.dg/plugin/show-template-tree-color-no-elide-type.C
@@ -0,0 +1,30 @@
+/* Verify colorization of the output of -fdiagnostics-show-template-tree,
+   and within the %H and %I format codes.
+   Doing so requires a plugin; see the comments in the plugin for the
+   rationale.  */
+
+// { dg-options "-fdiagnostics-show-template-tree -fdiagnostics-color=always -fno-elide-type" }
+
+template<typename> struct vector {};
+template<typename, typename> struct map {};
+
+void fn_1(vector<int>);
+void fn_2(map<int, int>);
+
+void test ()
+{
+  fn_1 (vector<double> ());
+  /* { dg-begin-multiline-output "" }
+could not convert '^[[01m^[[Kvector<double>()^[[m^[[K' from '^[[01m^[[Kvector<^[[01;32m^[[Kdouble^[[m^[[K>^[[m^[[K' to '^[[01m^[[Kvector<^[[01;32m^[[Kint^[[m^[[K>^[[m^[[K'
+  vector<
+    [^[[01;32m^[[Kdouble^[[m^[[K != ^[[01;32m^[[Kint^[[m^[[K]>
+     { dg-end-multiline-output "" } */
+
+  fn_2 (map<int, double>());
+  /* { dg-begin-multiline-output "" }
+could not convert '^[[01m^[[Kmap<int, double>()^[[m^[[K' from '^[[01m^[[Kmap<int,^[[01;32m^[[Kdouble^[[m^[[K>^[[m^[[K' to '^[[01m^[[Kmap<int,^[[01;32m^[[Kint^[[m^[[K>^[[m^[[K'
+  map<
+    int,
+    [^[[01;32m^[[Kdouble^[[m^[[K != ^[[01;32m^[[Kint^[[m^[[K]>
+     { dg-end-multiline-output "" } */
+}
diff --git a/gcc/testsuite/g++.dg/plugin/show-template-tree-color.C b/gcc/testsuite/g++.dg/plugin/show-template-tree-color.C
new file mode 100644
index 0000000..eb99a3e
--- /dev/null
+++ b/gcc/testsuite/g++.dg/plugin/show-template-tree-color.C
@@ -0,0 +1,30 @@
+/* Verify colorization of the output of -fdiagnostics-show-template-tree,
+   and within the %H and %I format codes.
+   Doing so requires a plugin; see the comments in the plugin for the
+   rationale.  */
+
+// { 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 test ()
+{
+  fn_1 (vector<double> ());
+  /* { dg-begin-multiline-output "" }
+could not convert '^[[01m^[[Kvector<double>()^[[m^[[K' from '^[[01m^[[Kvector<^[[01;32m^[[Kdouble^[[m^[[K>^[[m^[[K' to '^[[01m^[[Kvector<^[[01;32m^[[Kint^[[m^[[K>^[[m^[[K'
+  vector<
+    [^[[01;32m^[[Kdouble^[[m^[[K != ^[[01;32m^[[Kint^[[m^[[K]>
+     { dg-end-multiline-output "" } */
+
+  fn_2 (map<int, double>());
+  /* { dg-begin-multiline-output "" }
+could not convert '^[[01m^[[Kmap<int, double>()^[[m^[[K' from '^[[01m^[[Kmap<[...],^[[01;32m^[[Kdouble^[[m^[[K>^[[m^[[K' to '^[[01m^[[Kmap<[...],^[[01;32m^[[Kint^[[m^[[K>^[[m^[[K'
+  map<
+    [...],
+    [^[[01;32m^[[Kdouble^[[m^[[K != ^[[01;32m^[[Kint^[[m^[[K]>
+     { dg-end-multiline-output "" } */
+}
diff --git a/gcc/testsuite/g++.dg/plugin/show_template_tree_color_plugin.c b/gcc/testsuite/g++.dg/plugin/show_template_tree_color_plugin.c
new file mode 100644
index 0000000..af568bf
--- /dev/null
+++ b/gcc/testsuite/g++.dg/plugin/show_template_tree_color_plugin.c
@@ -0,0 +1,38 @@
+/* We want to verify the colorized output of cxx_format_postprocessor,
+   but turning on colorization for everything confuses "dg-error" etc.
+   The color codes in the generated messages would also need escaping
+   for use within dg-error.
+
+   Hence the simplest approach is to provide a custom diagnostic_starter_fn,
+   which does nothing.
+
+   The resulting messages lack the "FILENAME:LINE:COL: error: " prefix
+   and can thus be tested using dg-begin/end-multiline-output.  */
+
+/* { dg-options "-O" } */
+
+#include "gcc-plugin.h"
+#include "config.h"
+#include "system.h"
+#include "coretypes.h"
+#include "plugin-version.h"
+#include "diagnostic.h"
+
+int plugin_is_GPL_compatible;
+
+void
+noop_starter_fn (diagnostic_context *, diagnostic_info *)
+{
+}
+
+int
+plugin_init (struct plugin_name_args *plugin_info,
+	     struct plugin_gcc_version *version)
+{
+  if (!plugin_default_version_check (version, &gcc_version))
+    return 1;
+
+  diagnostic_starter (global_dc) = noop_starter_fn;
+
+  return 0;
+}
diff --git a/gcc/testsuite/g++.dg/template/show-template-tree-2.C b/gcc/testsuite/g++.dg/template/show-template-tree-2.C
new file mode 100644
index 0000000..1cd3a06
--- /dev/null
+++ b/gcc/testsuite/g++.dg/template/show-template-tree-2.C
@@ -0,0 +1,118 @@
+// Tests of -fdiagnostics-show-template-tree with variadic templates
+// { dg-options "-fdiagnostics-show-template-tree -std=c++11" }
+
+template<typename> struct vector {};
+template<typename, typename> struct map {};
+template<typename ... Types> struct var {};
+
+void fn_0(var<>);
+void fn_1(var<int>);
+void fn_2(var<int, int>);
+void fn_3(vector<var<> >);
+void fn_4(vector<var<int> >);
+void fn_5(vector<var<int, int> >);
+
+void test_fn_0 ()
+{
+  fn_0 (var<> ());
+  fn_0 (var<int> ()); // { dg-error "could not convert .* from 'var<int>' to 'var<>'" }
+  /* { dg-begin-multiline-output "" }
+  var<
+    [int != ]>
+     { dg-end-multiline-output "" } */
+  fn_0 (var<int, int> ()); // { dg-error "could not convert .* from 'var<int, int>' to 'var<>'" }
+  /* { dg-begin-multiline-output "" }
+  var<
+    [int, int != ]>
+     { dg-end-multiline-output "" } */
+  fn_0 (vector<var<int> >()); // { dg-error "could not convert .* from 'vector<var<int> >' to 'var<>'" }
+  fn_0 (vector<var<int, int> >());  // { dg-error "could not convert .* from 'vector<var<int, int> >' to 'var<>'" }
+}
+
+void test_fn_1 ()
+{
+  fn_1 (var<> ()); // { dg-error "could not convert .* from 'var<>' to 'var<int>'" }
+  /* { dg-begin-multiline-output "" }
+  var<
+    [ != int]>
+     { dg-end-multiline-output "" } */
+  fn_1 (var<int> ());
+  fn_1 (var<int, int> ()); // { dg-error "could not convert .* from 'var<int, int>' to 'var<int>'" }
+  /* { dg-begin-multiline-output "" }
+  var<
+    [int, int != int]>
+     { dg-end-multiline-output "" } */
+  fn_1 (vector<var<int> >()); // { dg-error "could not convert .* from 'vector<var<int> >' to 'var<int>'" }
+  fn_1 (vector<var<int, int> >()); // { dg-error "could not convert .* from 'vector<var<int, int> >' to 'var<int>'" }
+}
+
+void test_fn_2 ()
+{
+  fn_2 (var<> ()); // { dg-error "could not convert .* from 'var<>' to 'var<int, int>'" }
+  /* { dg-begin-multiline-output "" }
+  var<
+    [ != int, int]>
+     { dg-end-multiline-output "" } */
+  fn_2 (var<int> ()); // { dg-error "could not convert .* from 'var<int>' to 'var<int, int>'" }
+  /* { dg-begin-multiline-output "" }
+  var<
+    [int != int, int]>
+     { dg-end-multiline-output "" } */
+  fn_2 (var<int, int> ());
+  fn_2 (vector<var<int> >()); // { dg-error "could not convert .* from 'vector<var<int> >' to 'var<int, int>'" }
+  fn_2 (vector<var<int, int> >()); // { dg-error "could not convert .* from 'vector<var<int, int> >' to 'var<int, int>'" }
+}
+
+void test_fn_3 ()
+{
+  fn_3 (var<> ()); // { dg-error "could not convert .* from 'var<>' to 'vector<var<> >'" }
+  fn_3 (var<int> ()); // { dg-error "could not convert .* from 'var<int>' to 'vector<var<> >'" }
+  fn_3 (var<int, int> ()); // { dg-error "could not convert .* from 'var<int, int>' to 'vector<var<> >'" }
+  fn_3 (vector<var<> >());
+  fn_3 (vector<var<int> >());  // { dg-error "could not convert .* from 'vector<var<int>>' to 'vector<var<>>'" }
+  /* { dg-begin-multiline-output "" }
+  vector<
+    var<
+      [int != ]>>
+     { dg-end-multiline-output "" } */
+  fn_3 (vector<var<int, int> >()); // { dg-error "could not convert .* from 'vector<var<int, int>>' to 'vector<var<>>'" }
+  /* { dg-begin-multiline-output "" }
+  vector<
+    var<
+      [int, int != ]>>
+     { dg-end-multiline-output "" } */
+}
+
+void test_fn_4 ()
+{
+  fn_4 (var<> ()); // { dg-error "could not convert .* from 'var<>' to 'vector<var<int> >'" }
+  fn_4 (var<int> ()); // { dg-error "could not convert .* from 'var<int>' to 'vector<var<int> >'" }
+  fn_4 (var<int, int> ()); // { dg-error "could not convert .* from 'var<int, int>' to 'vector<var<int> >'" }
+  fn_4 (vector<var<> >()); // { dg-error "could not convert .* from 'vector<var<>>' to 'vector<var<int>>'" }
+  /* { dg-begin-multiline-output "" }
+  vector<
+    var<
+      [ != int]>>
+     { dg-end-multiline-output "" } */
+  fn_4 (vector<var<int> >()); 
+  fn_4 (vector<var<int, int> >()); // { dg-error "could not convert .* from 'vector<var<int, int>>' to 'vector<var<int>>'" }
+  /* { dg-begin-multiline-output "" }
+  vector<
+    var<
+      [int, int != int]>>
+     { dg-end-multiline-output "" } */
+}
+
+void test_fn_5 ()
+{
+  fn_5 (var<> ()); // { dg-error "could not convert .* from 'var<>' to 'vector<var<int, int> >'" }
+  fn_5 (var<int> ());  // { dg-error "could not convert .* from 'var<int>' to 'vector<var<int, int> >'" }
+  fn_5 (var<int, int> ());  // { dg-error "could not convert .* from 'var<int, int>' to 'vector<var<int, int> >'" }
+  fn_5 (vector<var<int> >());  // { dg-error "could not convert .* from 'vector<var<int>>' to 'vector<var<int, int>>'" }
+  /* { dg-begin-multiline-output "" }
+  vector<
+    var<
+      [int != int, int]>>
+     { dg-end-multiline-output "" } */
+  fn_5 (vector<var<int, int> >());
+}
diff --git a/gcc/testsuite/g++.dg/template/show-template-tree-3.C b/gcc/testsuite/g++.dg/template/show-template-tree-3.C
new file mode 100644
index 0000000..0eda40b
--- /dev/null
+++ b/gcc/testsuite/g++.dg/template/show-template-tree-3.C
@@ -0,0 +1,37 @@
+/* End-to-end test of -fdiagnostics-show-template-tree and -felide-type
+   using the STL.
+   In particular, ensure that we don't print the default arguments e.g.
+   rather than printing
+     from 'vector<double,allocator<double>>' to 'vector<float,allocator<float>>'
+   (albeit with differences nicely color-coded), we want to print:
+     from 'vector<double>' to 'vector<float>'
+   (again, with the "double" and "float" highlighted, though we can't test
+   for that in this case).  */
+
+// { dg-options "-fdiagnostics-show-template-tree" }
+
+#include <map>
+#include <vector>
+
+using std::vector;
+using std::map;
+
+void takes_vf (vector<float> v);
+void takes_mivf (map<int, vector<float> > v);
+
+int test ()
+{
+  takes_vf (vector<double> ()); // { dg-error "could not convert '.*' from 'vector<double>' to 'vector<float>'" }
+  /* { dg-begin-multiline-output "" }
+  vector<
+    [double != float]>
+     { dg-end-multiline-output "" } */
+
+  takes_mivf (map<int, vector<double> > ()); // { dg-error "could not convert '.*' from 'map<.\\.\\.\\..,vector<double>>' to 'map<.\\.\\.\\..,vector<float>>'" }
+  /* { dg-begin-multiline-output "" }
+  map<
+    [...],
+    vector<
+      [double != float]>>
+     { dg-end-multiline-output "" } */
+}
diff --git a/gcc/testsuite/g++.dg/template/show-template-tree-4.C b/gcc/testsuite/g++.dg/template/show-template-tree-4.C
new file mode 100644
index 0000000..953733b
--- /dev/null
+++ b/gcc/testsuite/g++.dg/template/show-template-tree-4.C
@@ -0,0 +1,95 @@
+// { dg-options "-fdiagnostics-show-template-tree" }
+
+/* Example of default template args, and various kinds of mismatch.  */
+
+template <int = 0, int = 1, int = 2>
+struct s {};
+
+void takes_s (s<> );
+void takes_s013 (s<0, 1, 3> );
+void takes_s321 (s<3, 2, 1> );
+
+void test ()
+{
+  takes_s (s<>());
+  takes_s (s<0, 1>());
+  takes_s (s<0, 1, 2>());
+  takes_s (s<0, 2>()); // { dg-error "could not convert '.*' from 's<.\\.\\.\\..,2>' to 's<.\\.\\.\\..,1>'" }
+  /* { dg-begin-multiline-output "" }
+  s<
+    [...],
+    [2 != 1]>
+     { dg-end-multiline-output "" } */
+
+  takes_s (s<1>()); // { dg-error "could not convert '.*' from 's<1>' to 's<0>'" }
+  /* { dg-begin-multiline-output "" }
+  s<
+    [1 != 0]>
+     { dg-end-multiline-output "" } */
+
+  takes_s (s<0, 1, 3>()); // { dg-error "could not convert '.*' from 's<.\\.\\.\\..,.\\.\\.\\..,3>' to 's<.\\.\\.\\..,.\\.\\.\\..,2>'" }
+  /* { dg-begin-multiline-output "" }
+  s<
+    [...],
+    [...],
+    [3 != 2]>
+     { dg-end-multiline-output "" } */
+
+  takes_s (s<3, 2, 0>()); // { dg-error "could not convert '.*' from 's<3,2,0>' to 's<0,1,2>'" }
+  /* { dg-begin-multiline-output "" }
+  s<
+    [3 != 0],
+    [2 != 1],
+    [0 != 2]>
+     { dg-end-multiline-output "" } */
+
+  takes_s (s<3, 2, 1>()); // { dg-error "could not convert '.*' from 's<3,2,1>' to 's<0,1,2>'" }
+  /* { dg-begin-multiline-output "" }
+  s<
+    [3 != 0],
+    [2 != 1],
+    [1 != 2]>
+     { dg-end-multiline-output "" } */
+
+  takes_s013 (s<0, 1, 2>()); // { dg-error "could not convert '.*' from 's<.\\.\\.\\..,.\\.\\.\\..,2>' to 's<.\\.\\.\\..,.\\.\\.\\..,3>'" }
+  /* { dg-begin-multiline-output "" }
+  s<
+    [...],
+    [...],
+    [2 != 3]>
+     { dg-end-multiline-output "" } */
+
+  takes_s321 (s<>());        // { dg-error "could not convert '.*' from 's<0,1,2>' to 's<3,2,1>'" }
+  /* { dg-begin-multiline-output "" }
+  s<
+    [0 != 3],
+    [1 != 2],
+    [2 != 1]>
+     { dg-end-multiline-output "" } */
+
+  takes_s321 (s<0, 1, 3>()); // { dg-error "could not convert '.*' from 's<0,1,3>' to 's<3,2,1>'" }
+  /* { dg-begin-multiline-output "" }
+  s<
+    [0 != 3],
+    [1 != 2],
+    [3 != 1]>
+     { dg-end-multiline-output "" } */
+
+  takes_s321 (s<3, 2, 0>()); // { dg-error "could not convert '.*' from 's<.\\.\\.\\..,.\\.\\.\\..,0>' to 's<.\\.\\.\\..,.\\.\\.\\..,1>'" }
+  /* { dg-begin-multiline-output "" }
+  s<
+    [...],
+    [...],
+    [0 != 1]>
+     { dg-end-multiline-output "" } */
+
+  takes_s321 (s<3, 2, 1>());
+
+  takes_s321 (s<1, 2, 3>()); // { dg-error "could not convert '.*' from 's<1,.\\.\\.\\..,3>' to 's<3,.\\.\\.\\..,1>'" }
+  /* { dg-begin-multiline-output "" }
+  s<
+    [1 != 3],
+    [...],
+    [3 != 1]>
+     { 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..d4bfa81
--- /dev/null
+++ b/gcc/testsuite/g++.dg/template/show-template-tree-no-elide-type.C
@@ -0,0 +1,24 @@
+// { 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<double> ()); // { dg-error "could not convert .* from 'vector<double>' to 'vector<int>'" }
+  /* { dg-begin-multiline-output "" }
+  vector<
+    [double != int]>
+     { dg-end-multiline-output "" } */
+
+  fn_2 (map<int, double>());  // { dg-error "could not convert .* from 'map<int,double>' to 'map<int,int>'" }
+  /* { dg-begin-multiline-output "" }
+  map<
+    int,
+    [double != int]>
+     { 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..0b75098
--- /dev/null
+++ b/gcc/testsuite/g++.dg/template/show-template-tree.C
@@ -0,0 +1,51 @@
+// { dg-options "-fdiagnostics-show-template-tree" }
+
+template<typename> struct vector {};
+template<typename, typename> struct map {};
+template<int> struct arr {};
+
+void fn_1(vector<int>);
+void fn_2(map<int, int>);
+void fn_3(vector<map<int, float> >);
+void takes_arr_10 (arr<10>);
+
+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 .* 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 .* from 'map<int, int>' to 'vector<int>'" }
+
+  fn_2 (map<int, int>());
+  fn_2 (map<int, double>());  // { dg-error "could not convert .* 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 .* 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 .* from 'vector<map<.\\.\\.\\..,double>>' to 'vector<map<.\\.\\.\\..,float>>'" }
+  /* { dg-begin-multiline-output "" }
+  vector<
+    map<
+      [...],
+      [double != float]>>
+     { dg-end-multiline-output "" } */
+
+  takes_arr_10 (arr<5>()); // { dg-error "could not convert '.*' from 'arr<5>' to 'arr<10>'" }
+  /* { dg-begin-multiline-output "" }
+  arr<
+    [5 != 10]>
+     { dg-end-multiline-output "" } */
+}
-- 
1.8.5.3

^ permalink raw reply	[flat|nested] 17+ messages in thread

* Re: [PATCH 0/3] v3 of C++ template type diffing
  2017-05-10  1:09         ` [PATCH 0/3] v3 of C++ template type diffing David Malcolm
                             ` (2 preceding siblings ...)
  2017-05-10  2:32           ` [PATCH 2/3] (v3) C++ " David Malcolm
@ 2017-05-10 13:46           ` Nathan Sidwell
  2017-05-30 21:12             ` David Malcolm
  3 siblings, 1 reply; 17+ messages in thread
From: Nathan Sidwell @ 2017-05-10 13:46 UTC (permalink / raw)
  To: David Malcolm, gcc-patches

On 05/09/2017 09:41 PM, David Malcolm wrote:
> On Tue, 2017-05-09 at 10:52 -0400, Nathan Sidwell wrote:

> I split out the non-c++ bits into a separate patch.
> 
> v3 of the patch kit is thus three parts:
> 
> [1/3] Non-C++ parts of template type diff printing
>    I believe I can self-approve this part
> 
> [2/3] (v3) C++ template type diff printing
>    Updated as above.

C++ bits ok -- thanks for addressing my points.  Give Jason a day to 
respond if he wants too.  The gcc/c-family/ and gcc/ changes will need 
someone other than me.

> [3/3] (v3) Use %qH and %qI throughout C++ frontend
>    Mostly mechanical

ok.

nathan

-- 
Nathan Sidwell

^ permalink raw reply	[flat|nested] 17+ messages in thread

* Re: [PATCH 0/3] v3 of C++ template type diffing
  2017-05-10 13:46           ` [PATCH 0/3] v3 of C++ template type diffing Nathan Sidwell
@ 2017-05-30 21:12             ` David Malcolm
  0 siblings, 0 replies; 17+ messages in thread
From: David Malcolm @ 2017-05-30 21:12 UTC (permalink / raw)
  To: Nathan Sidwell, gcc-patches

On Wed, 2017-05-10 at 09:34 -0400, Nathan Sidwell wrote:
> On 05/09/2017 09:41 PM, David Malcolm wrote:
> > On Tue, 2017-05-09 at 10:52 -0400, Nathan Sidwell wrote:
> 
> > I split out the non-c++ bits into a separate patch.
> > 
> > v3 of the patch kit is thus three parts:
> > 
> > [1/3] Non-C++ parts of template type diff printing
> >    I believe I can self-approve this part
> > 
> > [2/3] (v3) C++ template type diff printing
> >    Updated as above.
> 
> C++ bits ok -- thanks for addressing my points.  Give Jason a day to 
> respond if he wants too.  The gcc/c-family/ and gcc/ changes will
> need 
> someone other than me.
> 
> > [3/3] (v3) Use %qH and %qI throughout C++ frontend
> >    Mostly mechanical
> 
> ok.
> 
> nathan

Thanks.

Jason didn't respond, and I believe I can self-approve the other
changes (all part of the diagnostics subsystem).

I've committed the combination of the patches to trunk (as r248698)
after rebasing and retesting.

^ permalink raw reply	[flat|nested] 17+ messages in thread

end of thread, other threads:[~2017-05-30 20:39 UTC | newest]

Thread overview: 17+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2017-05-05  0:45 [PATCH 1/2] C++ template type diff printing David Malcolm
2017-05-04 23:13 ` [PATCH 2/2] Use %H and %I throughout C++ frontend David Malcolm
2017-05-08 11:48 ` [PATCH 1/2] C++ template type diff printing Nathan Sidwell
2017-05-08 13:07   ` Jason Merrill via gcc-patches
2017-05-08 16:58   ` David Malcolm
2017-05-08 17:44     ` Nathan Sidwell
2017-05-09 13:39     ` [PATCH 1/2] (v2) " David Malcolm
2017-05-09 13:35       ` [PATCH 2/2] (v2) Use %qH and %qI throughout C++ frontend David Malcolm
2017-05-09 14:59       ` [PATCH 1/2] (v2) C++ template type diff printing Nathan Sidwell
2017-05-10  1:09         ` [PATCH 0/3] v3 of C++ template type diffing David Malcolm
2017-05-10  1:09           ` [PATCH 3/3] (v3) Use %qH and %qI throughout C++ frontend David Malcolm
2017-05-10  1:10           ` [PATCH 1/3] (v3) Non-C++ parts of template type diff printing David Malcolm
2017-05-10  2:32           ` [PATCH 2/3] (v3) C++ " David Malcolm
2017-05-10 13:46           ` [PATCH 0/3] v3 of C++ template type diffing Nathan Sidwell
2017-05-30 21:12             ` David Malcolm
2017-05-09 21:14     ` [PATCH 1/2] C++ template type diff printing Manuel López-Ibáñez
2017-05-09 15:55 ` Jakub Jelinek

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).