diff mbox

[1/2] C++ template type diff printing

Message ID 1493941499-33647-1-git-send-email-dmalcolm@redhat.com
State New
Headers show

Commit Message

David Malcolm May 4, 2017, 11:44 p.m. UTC
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

Comments

Nathan Sidwell May 8, 2017, 11:36 a.m. UTC | #1
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
Li, Pan2 via Gcc-patches May 8, 2017, 1:06 p.m. UTC | #2
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
David Malcolm May 8, 2017, 4:51 p.m. UTC | #3
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
Nathan Sidwell May 8, 2017, 5:40 p.m. UTC | #4
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
Jakub Jelinek May 9, 2017, 3:51 p.m. UTC | #5
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
Manuel López-Ibáñez May 9, 2017, 9:03 p.m. UTC | #6
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.
diff mbox

Patch

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)
     }
 }
 
+
+/* 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 'vector<double>()' from 'vector<double>' to 'vector<int>'
+  vector<
+    [double != int]>
+     { dg-end-multiline-output "" } */
+
+  fn_2 (map<int, double>());
+  /* { dg-begin-multiline-output "" }
+could not convert 'map<int, double>()' from 'map<int,double>' to 'map<int,int>'
+  map<
+    int,
+    [double != int]>
+     { 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 'vector<double>()' from 'vector<double>' to 'vector<int>'
+  vector<
+    [double != int]>
+     { dg-end-multiline-output "" } */
+
+  fn_2 (map<int, double>());
+  /* { dg-begin-multiline-output "" }
+could not convert 'map<int, double>()' from 'map<[...],double>' to 'map<[...],int>'
+  map<
+    [...],
+    [double != int]>
+     { 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 */