public inbox for gcc-patches@gcc.gnu.org
 help / color / mirror / Atom feed
* [PATCH v3] c++: Add gnu::diagnose_as attribute
@ 2021-07-15 12:21 Matthias Kretz
  2021-07-23  8:58 ` [PATCH v4] " Matthias Kretz
  0 siblings, 1 reply; 24+ messages in thread
From: Matthias Kretz @ 2021-07-15 12:21 UTC (permalink / raw)
  To: gcc-patches

[-- Attachment #1: Type: text/plain, Size: 7410 bytes --]

Hi Jason,

A new revision of the patch is attached. I think I implemented all your 
suggestions.

Please comment on cp/decl2.c (is_alias_template_p). I find it surprising that 
I had to write this function. Maybe I missed something? In any case, 
DECL_ALIAS_TEMPLATE_P requires a template_decl and the TYPE_DECL apparently 
doesn't have a template_info/decl at this point.

From: Matthias Kretz <m.kretz@gsi.de>

This attribute overrides the diagnostics output string for the entity it
appertains to. The motivation is to improve QoI for library TS
implementations, where diagnostics have a very bad signal-to-noise ratio
due to the long namespaces involved.

With the attribute, it is possible to solve PR89370 and make
std::__cxx11::basic_string<_CharT, _Traits, _Alloc> appear as
std::string in diagnostic output without extra hacks to recognize the
type in the C++ frontend.

Signed-off-by: Matthias Kretz <m.kretz@gsi.de>

gcc/ChangeLog:

	PR c++/89370
	* doc/extend.texi: Document the diagnose_as attribute.
	* doc/invoke.texi: Document -fno-diagnostics-use-aliases.

gcc/c-family/ChangeLog:

	PR c++/89370
	* c.opt (fdiagnostics-use-aliases): New diagnostics flag.

gcc/cp/ChangeLog:

	PR c++/89370
	* cp-tree.h: Add TFF_AS_PRIMARY. Add is_alias_template_p
	declaration.
	* decl2.c (is_alias_template_p): New function. Determines
	whether a given TYPE_DECL is actually an alias template that is
	still missing its template_info.
	(is_late_template_attribute): Decls with diagnose_as attribute
	are early attributes only if they are alias templates.
	* error.c (dump_scope): When printing the name of a namespace,
	look for the diagnose_as attribute. If found, print the
	associated string instead of calling dump_decl.
	(dump_decl_name_or_diagnose_as): New function to replace
	dump_decl (pp, DECL_NAME(t), flags) and inspect the tree for the
	diagnose_as attribute before printing the DECL_NAME.
	(dump_template_scope): New function. Prints the scope of a
	template instance correctly applying diagnose_as attributes and
	adjusting the list of template parms accordingly.
	(dump_aggr_type): If the type has a diagnose_as attribute, print
	the associated string instead of printing the original type
	name. Print template parms only if the attribute was not applied
	to the instantiation / full specialization. Delay call to
	dump_scope until the diagnose_as attribute is found. If the
	attribute has a second argument, use it to override the context
	passed to dump_scope.
	(dump_simple_decl): Call dump_decl_name_or_diagnose_as instead
	of dump_decl.
	(dump_decl): Ditto.
	(lang_decl_name): Ditto.
	(dump_function_decl): Walk the functions context list to
	determine whether a call to dump_template_scope is required.
	Ensure function templates are presented as primary templates.
	(dump_function_name): Replace the function's identifier with the
	diagnose_as attribute value, if set.
	(dump_template_parms): Treat as primary template if flags
	contains TFF_AS_PRIMARY.
	(comparable_template_types_p): Consider the types not a template
	if one carries a diagnose_as attribute.
	(print_template_differences): Replace the identifier with the
	diagnose_as attribute value on the most general template, if it
	is set.
	* name-lookup.c (handle_namespace_attrs): Handle the diagnose_as
	attribute on namespaces. Ensure exactly one string argument.
	Ensure previous diagnose_as attributes used the same name.
	'diagnose_as' on namespace aliases are forwarded to the original
	namespace. Support no-argument 'diagnose_as' on namespace
	aliases.
	(do_namespace_alias): Add attributes parameter and call
	handle_namespace_attrs.
	* name-lookup.h (do_namespace_alias): Add attributes tree
	parameter.
	* parser.c (cp_parser_declaration): If the next token is
	RID_NAMESPACE, tentatively parse a namespace alias definition.
	If this fails expect a namespace definition.
	(cp_parser_namespace_alias_definition): Allow optional
	attributes before and after the identifier. Fast exit if the
	expected CPP_EQ token is missing. Pass attributes to
	do_namespace_alias.
	* tree.c (cxx_attribute_table): Add diagnose_as attribute to the
	table.
	(check_diagnose_as_redeclaration): New function; copied and
	adjusted from check_abi_tag_redeclaration.
	(handle_diagnose_as_attribute): New function; copied and
	adjusted from handle_abi_tag_attribute. If the given *node is a
	TYPE_DECL: allow no argument to the attribute, using DECL_NAME
	instead; apply the attribute to the type on the RHS in place,
	even if the type is complete. Allow 2 arguments when called from
	handle_diagnose_as_attribute. For type aliases, append
	CP_DECL_CONTEXT as second attribute argument when the RHS type
	has a different context. Warn about alias templates without
	matching template arguments. Apply the attribute to the primary
	template type for alias templates.

gcc/testsuite/ChangeLog:

	PR c++/89370
	* g++.dg/diagnostic/diagnose-as1.C: New test.
	* g++.dg/diagnostic/diagnose-as2.C: New test.
	* g++.dg/diagnostic/diagnose-as3.C: New test.
	* g++.dg/diagnostic/diagnose-as4.C: New test.
	* g++.dg/diagnostic/diagnose-as5.C: New test.
	* g++.dg/diagnostic/diagnose-as6.C: New test.
---
 gcc/c-family/c.opt                            |   4 +
 gcc/cp/cp-tree.h                              |   6 +-
 gcc/cp/decl2.c                                |  46 ++++
 gcc/cp/error.c                                | 245 +++++++++++++++++-
 gcc/cp/name-lookup.c                          |  52 +++-
 gcc/cp/name-lookup.h                          |   2 +-
 gcc/cp/parser.c                               |  36 +--
 gcc/cp/tree.c                                 | 151 +++++++++++
 gcc/doc/extend.texi                           |  45 ++++
 gcc/doc/invoke.texi                           |   9 +-
 .../g++.dg/diagnostic/diagnose-as1.C          | 213 +++++++++++++++
 .../g++.dg/diagnostic/diagnose-as2.C          | 144 ++++++++++
 .../g++.dg/diagnostic/diagnose-as3.C          | 152 +++++++++++
 .../g++.dg/diagnostic/diagnose-as4.C          | 158 +++++++++++
 .../g++.dg/diagnostic/diagnose-as5.C          |  21 ++
 .../g++.dg/diagnostic/diagnose-as6.C          |  45 ++++
 16 files changed, 1295 insertions(+), 34 deletions(-)
 create mode 100644 gcc/testsuite/g++.dg/diagnostic/diagnose-as1.C
 create mode 100644 gcc/testsuite/g++.dg/diagnostic/diagnose-as2.C
 create mode 100644 gcc/testsuite/g++.dg/diagnostic/diagnose-as3.C
 create mode 100644 gcc/testsuite/g++.dg/diagnostic/diagnose-as4.C
 create mode 100644 gcc/testsuite/g++.dg/diagnostic/diagnose-as5.C
 create mode 100644 gcc/testsuite/g++.dg/diagnostic/diagnose-as6.C


--
──────────────────────────────────────────────────────────────────────────
 Dr. Matthias Kretz                           https://mattkretz.github.io
 GSI Helmholtz Centre for Heavy Ion Research               https://gsi.de
 std::experimental::simd              https://github.com/VcDevel/std-simd
──────────────────────────────────────────────────────────────────────────

[-- Attachment #2: 0001-c-Add-gnu-diagnose_as-attribute.patch --]
[-- Type: text/x-patch, Size: 63922 bytes --]

diff --git a/gcc/c-family/c.opt b/gcc/c-family/c.opt
index 91929706aff..354590982bd 100644
--- a/gcc/c-family/c.opt
+++ b/gcc/c-family/c.opt
@@ -1606,6 +1606,10 @@ fdiagnostics-show-template-tree
 C++ ObjC++ Var(flag_diagnostics_show_template_tree) Init(0)
 Print hierarchical comparisons when template types are mismatched.
 
+fdiagnostics-use-aliases
+C++ Var(flag_diagnostics_use_aliases) Init(1)
+Replace identifiers or scope names in diagnostics as defined by the diagnose_as attribute.
+
 fdirectives-only
 C ObjC C++ ObjC++
 Preprocess directives only.
diff --git a/gcc/cp/cp-tree.h b/gcc/cp/cp-tree.h
index 6f713719589..427e56e5852 100644
--- a/gcc/cp/cp-tree.h
+++ b/gcc/cp/cp-tree.h
@@ -5892,7 +5892,9 @@ enum auto_deduction_context
        identical to their defaults.
    TFF_NO_TEMPLATE_BINDINGS: do not print information about the template
        arguments for a function template specialization.
-   TFF_POINTER: we are printing a pointer type.  */
+   TFF_POINTER: we are printing a pointer type.
+   TFF_AS_PRIMARY: treat as primary template even if printing a specialized
+       type.  */
 
 #define TFF_PLAIN_IDENTIFIER			(0)
 #define TFF_SCOPE				(1)
@@ -5910,6 +5912,7 @@ enum auto_deduction_context
 #define TFF_NO_OMIT_DEFAULT_TEMPLATE_ARGUMENTS	(1 << 12)
 #define TFF_NO_TEMPLATE_BINDINGS		(1 << 13)
 #define TFF_POINTER		                (1 << 14)
+#define TFF_AS_PRIMARY				(1 << 15)
 
 /* These constants can be used as bit flags to control strip_typedefs.
 
@@ -6768,6 +6771,7 @@ extern tree grokfield (const cp_declarator *, cp_decl_specifier_seq *,
 		       tree, bool, tree, tree);
 extern tree grokbitfield (const cp_declarator *, cp_decl_specifier_seq *,
 			  tree, tree, tree);
+extern bool is_alias_template_p			(tree);
 extern tree splice_template_attributes		(tree *, tree);
 extern bool any_dependent_type_attributes_p	(tree);
 extern tree cp_reconstruct_complex_type		(tree, tree);
diff --git a/gcc/cp/decl2.c b/gcc/cp/decl2.c
index 090a83bd670..7cec8f114c1 100644
--- a/gcc/cp/decl2.c
+++ b/gcc/cp/decl2.c
@@ -1139,6 +1139,48 @@ grokbitfield (const cp_declarator *declarator,
   return value;
 }
 
+/* Return true iff DECL is an alias template of a class template which strictly
+   renames the type.  */
+bool
+is_alias_template_p (tree decl)
+{
+  if (TREE_CODE (decl) != TYPE_DECL)
+    return false;
+
+  tree type = strip_typedefs (TREE_TYPE (decl), nullptr, 0);
+  if (!CLASS_TYPE_P (type) || !CLASSTYPE_TEMPLATE_INFO (type))
+    return false;
+
+  /* Ensure it's a real alias template not just
+       template <class T> struct A {
+	 struct B {};
+	 template <class U> struct C {};
+	 using D [[gnu::diagnose_as]] = B;
+	 using E [[gnu::diagnose_as]] = C<int>;
+       };
+     A<T>::D and A<T>::E are not alias templates.
+     - For A<T>::D, the TREE_TYPE of the innermost template params is A and
+       not B, which would be the case for a real alias template.
+     - For A<T>::E, the innermost template params belong to C but its template
+       args have no wildcard types, which would be the case for a real
+       alias template.  */
+  tree tmpl = CLASSTYPE_TI_TEMPLATE (type);
+  if (tmpl != TREE_TYPE (INNERMOST_TEMPLATE_PARMS (
+			   DECL_TEMPLATE_PARMS (tmpl))))
+    return false;
+
+  tree targs = INNERMOST_TEMPLATE_ARGS (CLASSTYPE_TI_ARGS (type));
+  for (int i = 0; i < NUM_TMPL_ARGS (targs); ++i)
+    {
+      tree arg = TREE_VEC_ELT (targs, i);
+      while (INDIRECT_TYPE_P (arg))
+	arg = TREE_TYPE (arg);
+      if (WILDCARD_TYPE_P (arg))
+	return true;
+    }
+  return false;
+}
+
 \f
 /* Returns true iff ATTR is an attribute which needs to be applied at
    instantiation time rather than template definition time.  */
@@ -1166,6 +1208,10 @@ is_late_template_attribute (tree attr, tree decl)
 	  || is_attribute_p ("used", name)))
     return false;
 
+  /* Allow alias templates to set diagnose_as on the RHS template.  */
+  if (is_attribute_p ("diagnose_as", name))
+    return !is_alias_template_p (decl);
+
   /* Attribute tls_model wants to modify the symtab.  */
   if (is_attribute_p ("tls_model", name))
     return true;
diff --git a/gcc/cp/error.c b/gcc/cp/error.c
index 4a89b348829..a18d758fa43 100644
--- a/gcc/cp/error.c
+++ b/gcc/cp/error.c
@@ -35,6 +35,7 @@ along with GCC; see the file COPYING3.  If not see
 #include "internal-fn.h"
 #include "gcc-rich-location.h"
 #include "cp-name-hint.h"
+#include "attribs.h"
 
 #define pp_separate_with_comma(PP) pp_cxx_separate_with (PP, ',')
 #define pp_separate_with_semicolon(PP) pp_cxx_separate_with (PP, ';')
@@ -66,6 +67,7 @@ static void dump_alias_template_specialization (cxx_pretty_printer *, tree, int)
 static void dump_type (cxx_pretty_printer *, tree, int);
 static void dump_typename (cxx_pretty_printer *, tree, int);
 static void dump_simple_decl (cxx_pretty_printer *, tree, tree, int);
+static void dump_decl_name_or_diagnose_as (cxx_pretty_printer *, tree, int);
 static void dump_decl (cxx_pretty_printer *, tree, int);
 static void dump_template_decl (cxx_pretty_printer *, tree, int);
 static void dump_function_decl (cxx_pretty_printer *, tree, int);
@@ -231,7 +233,15 @@ dump_scope (cxx_pretty_printer *pp, tree scope, int flags)
     {
       if (scope != global_namespace)
 	{
-          dump_decl (pp, scope, f);
+	  tree diagnose_as
+	    = flag_diagnostics_use_aliases
+		? lookup_attribute ("diagnose_as", DECL_ATTRIBUTES (scope))
+		: NULL_TREE;
+	  if (diagnose_as)
+	    pp_cxx_ws_string (
+	      pp, TREE_STRING_POINTER (TREE_VALUE (TREE_VALUE (diagnose_as))));
+	  else
+	    dump_decl (pp, scope, f);
 	  pp_cxx_colon_colon (pp);
 	}
     }
@@ -764,6 +774,45 @@ class_key_or_enum_as_string (tree t)
     return "struct";
 }
 
+/* Print out an enclosing scope of a template instance by inspecting the tree
+   SPECIAL of the template instantiation and the tree GENERAL of the most
+   general template in parallel under the control of FLAGS while adjusting
+   PARMS as needed. This allows the diagnose_as attribute to override the name
+   of full specializations, while hiding its template parameters, and to
+   override the name of partial specializations without falling back to printing
+   the template parameters of the general template decl.  */
+
+static void
+dump_template_scope (cxx_pretty_printer *pp, tree special, tree general,
+		     tree *parms, int flags)
+{
+  gcc_assert (parms);
+  if (special != general && RECORD_OR_UNION_TYPE_P (special) && *parms)
+    {
+      gcc_assert (RECORD_OR_UNION_TYPE_P (general));
+      const bool tmplate
+	= TYPE_LANG_SPECIFIC (special) && CLASSTYPE_TEMPLATE_INFO (special)
+	    && (TREE_CODE (CLASSTYPE_TI_TEMPLATE (special)) != TEMPLATE_DECL
+		  || PRIMARY_TEMPLATE_P (CLASSTYPE_TI_TEMPLATE (special)));
+      dump_template_scope (pp, CP_TYPE_CONTEXT (special),
+			   CP_TYPE_CONTEXT (general),
+			   tmplate ? &TREE_CHAIN(*parms) : parms, flags);
+      tree attr = lookup_attribute ("diagnose_as", TYPE_ATTRIBUTES (special));
+      if (attr)
+	{
+	  pp_cxx_ws_string (pp, TREE_STRING_POINTER (
+				TREE_VALUE (TREE_VALUE (attr))));
+	  if (tmplate)
+	    TREE_VALUE (*parms) = make_tree_vec (0);
+	}
+      else
+	dump_aggr_type (pp, general, flags | TFF_UNQUALIFIED_NAME);
+      pp_cxx_colon_colon (pp);
+    }
+  else
+    dump_scope (pp, general, flags);
+}
+
 /* Print out a class declaration T under the control of FLAGS,
    in the form `class foo'.  */
 
@@ -781,6 +830,7 @@ dump_aggr_type (cxx_pretty_printer *pp, tree t, int flags)
 
   tree decl = TYPE_NAME (t);
 
+  tree diagnose_as = NULL_TREE;
   if (decl)
     {
       typdef = (!DECL_ARTIFICIAL (decl)
@@ -804,11 +854,14 @@ dump_aggr_type (cxx_pretty_printer *pp, tree t, int flags)
 		&& (TREE_CODE (CLASSTYPE_TI_TEMPLATE (t)) != TEMPLATE_DECL
 		    || PRIMARY_TEMPLATE_P (CLASSTYPE_TI_TEMPLATE (t)));
       
-      if (! (flags & TFF_UNQUALIFIED_NAME))
-	dump_scope (pp, CP_DECL_CONTEXT (decl), flags | TFF_SCOPE);
-      flags &= ~TFF_UNQUALIFIED_NAME;
+      tree context = CP_DECL_CONTEXT (decl);
+      tree diagnose_as_specialized = NULL_TREE;
       if (tmplate)
 	{
+	  if (flag_diagnostics_use_aliases)
+	    diagnose_as_specialized
+	      = lookup_attribute ("diagnose_as", TYPE_ATTRIBUTES (t));
+
 	  /* Because the template names are mangled, we have to locate
 	     the most general template, and use that name.  */
 	  tree tpl = TYPE_TI_TEMPLATE (t);
@@ -817,9 +870,61 @@ dump_aggr_type (cxx_pretty_printer *pp, tree t, int flags)
 	    tpl = DECL_TI_TEMPLATE (tpl);
 	  decl = tpl;
 	}
+
+      if (flag_diagnostics_use_aliases)
+	{
+	  diagnose_as = lookup_attribute ("diagnose_as",
+					  TYPE_ATTRIBUTES (TREE_TYPE (decl)));
+	  if (diagnose_as_specialized
+		&& (!diagnose_as || TREE_VALUE (diagnose_as_specialized)
+		      != TREE_VALUE (diagnose_as)))
+	    {
+	      diagnose_as = diagnose_as_specialized;
+	      /* Skip dump_template_parms if diagnose_as applies to a full
+	         specialization.  */
+	      tree args = CLASSTYPE_TI_ARGS (t);
+	      args = INNERMOST_TEMPLATE_ARGS (args);
+	      gcc_assert (args);
+	      tmplate = false;
+	      for (int i = 0; i < NUM_TMPL_ARGS (args); ++i)
+		{
+		  tree arg = TREE_VEC_ELT (args, i);
+		  while (INDIRECT_TYPE_P (arg))
+		    arg = TREE_TYPE (arg);
+		  if (WILDCARD_TYPE_P (arg))
+		    {
+		      tmplate = true;
+		      break;
+		    }
+		}
+	    }
+	  /* Given
+
+	       template <class T> struct [[gnu::diagnose_as("AA")]] A {
+	         struct [[gnu::diagnose_as("BB")]] B {};
+	       };
+
+	     A<int>::B will reach the following branch and find the diagnose_as
+	     attribute for B.  */
+	  else if (!tmplate && !diagnose_as && TYPE_TEMPLATE_INFO (t))
+	    diagnose_as
+	      = lookup_attribute ("diagnose_as",
+				  TYPE_ATTRIBUTES (
+				    TREE_TYPE (TYPE_TI_TEMPLATE (t))));
+
+	  if (diagnose_as && TREE_CHAIN (TREE_VALUE (diagnose_as)))
+	    context = TREE_VALUE (TREE_CHAIN (TREE_VALUE (diagnose_as)));
+	}
+
+      if (! (flags & TFF_UNQUALIFIED_NAME))
+	dump_scope (pp, context, flags | TFF_SCOPE);
+      flags &= ~TFF_UNQUALIFIED_NAME;
     }
 
-  if (LAMBDA_TYPE_P (t))
+  if (diagnose_as)
+    pp_cxx_ws_string (pp, TREE_STRING_POINTER (
+			    TREE_VALUE (TREE_VALUE (diagnose_as))));
+  else if (LAMBDA_TYPE_P (t))
     {
       /* A lambda's "type" is essentially its signature.  */
       pp_string (pp, M_("<lambda"));
@@ -1141,7 +1246,7 @@ dump_simple_decl (cxx_pretty_printer *pp, tree t, tree type, int flags)
 	  pp_string (pp, " capture>");
 	}
       else
-	dump_decl (pp, DECL_NAME (t), flags);
+	dump_decl_name_or_diagnose_as (pp, t, flags);
     }
   else if (DECL_DECOMPOSITION_P (t))
     pp_string (pp, M_("<structured bindings>"));
@@ -1185,6 +1290,25 @@ dump_decl_name (cxx_pretty_printer *pp, tree t, int flags)
   pp_cxx_tree_identifier (pp, t);
 }
 
+/* Print the DECL_NAME of DECL unless a different string was requested by the
+   diagnose_as attribute. */
+
+static void
+dump_decl_name_or_diagnose_as (cxx_pretty_printer *pp, tree decl, int flags)
+{
+  if (flag_diagnostics_use_aliases)
+    {
+      tree attr = lookup_attribute ("diagnose_as", DECL_ATTRIBUTES (decl));
+      if (attr && TREE_VALUE (attr))
+	{
+	  pp_cxx_ws_string (
+	    pp, TREE_STRING_POINTER (TREE_VALUE (TREE_VALUE (attr))));
+	  return;
+	}
+    }
+  dump_decl_name (pp, DECL_NAME (decl), flags);
+}
+
 /* Dump a human readable string for the decl T under control of FLAGS.  */
 
 static void
@@ -1230,7 +1354,7 @@ dump_decl (cxx_pretty_printer *pp, tree t, int flags)
 	      || flags & TFF_CLASS_KEY_OR_ENUM))
 	{
 	  pp_cxx_ws_string (pp, "using");
-	  dump_decl (pp, DECL_NAME (t), flags);
+	  dump_decl_name_or_diagnose_as(pp, t, flags);
 	  pp_cxx_whitespace (pp);
 	  pp_cxx_ws_string (pp, "=");
 	  pp_cxx_whitespace (pp);
@@ -1412,7 +1536,7 @@ dump_decl (cxx_pretty_printer *pp, tree t, int flags)
 	      TREE_CODE (DECL_INITIAL (t)) == TEMPLATE_PARM_INDEX))
 	dump_simple_decl (pp, t, TREE_TYPE (t), flags);
       else if (DECL_NAME (t))
-	dump_decl (pp, DECL_NAME (t), flags);
+	dump_decl_name_or_diagnose_as (pp, t, flags);
       else if (DECL_INITIAL (t))
 	dump_expr (pp, DECL_INITIAL (t), flags | TFF_EXPR_IN_PARENS);
       else
@@ -1431,7 +1555,7 @@ dump_decl (cxx_pretty_printer *pp, tree t, int flags)
 	  }
 	dump_type (pp, scope, flags);
 	pp_cxx_colon_colon (pp);
-	dump_decl (pp, DECL_NAME (t), flags);
+	dump_decl_name_or_diagnose_as (pp, t, flags);
 	if (variadic)
 	  pp_cxx_ws_string (pp, "...");
       }
@@ -1671,7 +1795,8 @@ dump_function_decl (cxx_pretty_printer *pp, tree t, int flags)
   tree template_args = NULL_TREE;
   tree template_parms = NULL_TREE;
   int show_return = flags & TFF_RETURN_TYPE || flags & TFF_DECL_SPECIFIERS;
-  int do_outer_scope = ! (flags & TFF_UNQUALIFIED_NAME);
+  bool do_outer_scope = ! (flags & TFF_UNQUALIFIED_NAME);
+  bool do_template_scope = false;
   tree exceptions;
   bool constexpr_p;
   tree ret = NULL_TREE;
@@ -1688,6 +1813,11 @@ dump_function_decl (cxx_pretty_printer *pp, tree t, int flags)
   /* Likewise for the constexpr specifier, in case t is a specialization.  */
   constexpr_p = DECL_DECLARED_CONSTEXPR_P (t);
 
+  /* Keep t before the following branch makes t point to a more general
+     template. Without the specialized template, the diagnose_as attribute on
+     the function is lost. */
+  tree specialized_t = t;
+
   /* Pretty print template instantiations only.  */
   if (DECL_USE_TEMPLATE (t) && DECL_TEMPLATE_INFO (t)
       && !(flags & TFF_NO_TEMPLATE_BINDINGS)
@@ -1699,8 +1829,46 @@ dump_function_decl (cxx_pretty_printer *pp, tree t, int flags)
       tmpl = most_general_template (t);
       if (tmpl && TREE_CODE (tmpl) == TEMPLATE_DECL)
 	{
+	  /* Inspect whether any record/union type in the context of the
+	     instantiated function template carries a diagnose_as attribute. If
+	     one does, we need to print the scope and template argument list
+	     differently. Consider:
+
+	       template <class T> struct A {
+		 template <class U0, class U1> struct B;
+		 template <class U> struct B<U, int> {
+		   void f();
+		 };
+	       };
+	       using Achar [[gnu::diagnose_as("Achar")]] = A<char>;
+
+	     Now Achar::B<int, int>::f() needs to print as "Achar::B<U, int>::f()
+	     [with U = int]". However, DECL_CONTEXT (t) would print as
+	     "Achar::B<int, int>" and dump_aggr_type has no sensible way to
+	     retrieve A<T>::B<U, int>. tmpl was generalized to A<T>::B<U, int>::f
+	     and thus dump_aggr_type (DECL_CONTEXT (tmpl)) would print
+	     "A<T>::B<U, int> [with T = char, U = int]". I.e. the diagnose_as
+	     attribute on the A<char> instantiation is lost.  */
+	  if (tmpl != t && do_outer_scope && flag_diagnostics_use_aliases)
+	    {
+	      for (tree context = DECL_CONTEXT (t);
+		   context && RECORD_OR_UNION_TYPE_P (context);
+		   context = TYPE_CONTEXT (context))
+		{
+		  if (lookup_attribute ("diagnose_as",
+					TYPE_ATTRIBUTES (context)))
+		    {
+		      do_template_scope = true;
+		      break;
+		    }
+		}
+	    }
+
 	  template_parms = DECL_TEMPLATE_PARMS (tmpl);
 	  t = tmpl;
+	  /* The "[with ...]" clause is printed, thus dump_template_params must
+	     unconditionally present functions as primary templates.  */
+	  dump_function_name_flags |= TFF_AS_PRIMARY;
 	}
     }
 
@@ -1747,6 +1915,25 @@ dump_function_decl (cxx_pretty_printer *pp, tree t, int flags)
   /* Print the function name.  */
   if (!do_outer_scope)
     /* Nothing.  */;
+  else if (do_template_scope)
+    {
+      template_parms = copy_list (template_parms);
+      bool func_template = TREE_TYPE (TREE_VALUE (template_parms)) == t;
+      dump_template_scope(pp, DECL_CONTEXT (specialized_t), DECL_CONTEXT (t),
+			  func_template ? &TREE_CHAIN (template_parms)
+					: &template_parms,
+			  flags);
+      if (TREE_VEC_LENGTH (TREE_VALUE (template_parms)) == 0)
+	{
+	  /* If no template parms are left, make it a NULL_TREE so that no
+	     "[with ]" is printed.  */
+	  tree p = TREE_CHAIN (template_parms);
+	  for (; p && TREE_VEC_LENGTH (TREE_VALUE (p)) == 0; p = TREE_CHAIN (p))
+	    ;
+	  if (!p)
+	    template_parms = template_args = NULL_TREE;
+	}
+    }
   else if (cname)
     {
       dump_type (pp, cname, flags);
@@ -1755,7 +1942,7 @@ dump_function_decl (cxx_pretty_printer *pp, tree t, int flags)
   else
     dump_scope (pp, CP_DECL_CONTEXT (t), flags);
 
-  dump_function_name (pp, t, dump_function_name_flags);
+  dump_function_name (pp, specialized_t, dump_function_name_flags);
 
   if (!(flags & TFF_NO_FUNCTION_ARGUMENTS))
     {
@@ -1935,6 +2122,14 @@ dump_function_name (cxx_pretty_printer *pp, tree t, int flags)
   if (TREE_CODE (t) == TEMPLATE_DECL)
     t = DECL_TEMPLATE_RESULT (t);
 
+  if (flag_diagnostics_use_aliases)
+    {
+      tree attr = lookup_attribute ("diagnose_as", DECL_ATTRIBUTES (t));
+      if (attr)
+	name = get_identifier (
+		 TREE_STRING_POINTER (TREE_VALUE (TREE_VALUE (attr))));
+    }
+
   /* Don't let the user see __comp_ctor et al.  */
   if (DECL_CONSTRUCTOR_P (t)
       || DECL_DESTRUCTOR_P (t))
@@ -1989,6 +2184,8 @@ dump_template_parms (cxx_pretty_printer *pp, tree info,
 {
   tree args = info ? TI_ARGS (info) : NULL_TREE;
 
+  if (flags & TFF_AS_PRIMARY)
+    primary = true;
   if (primary && flags & TFF_TEMPLATE_NAME)
     return;
   flags &= ~(TFF_CLASS_KEY_OR_ENUM | TFF_TEMPLATE_NAME);
@@ -3168,7 +3365,7 @@ lang_decl_name (tree decl, int v, bool translate)
            && TREE_CODE (decl) == NAMESPACE_DECL)
     dump_decl (cxx_pp, decl, TFF_PLAIN_IDENTIFIER | TFF_UNQUALIFIED_NAME);
   else
-    dump_decl (cxx_pp, DECL_NAME (decl), TFF_PLAIN_IDENTIFIER);
+    dump_decl_name_or_diagnose_as (cxx_pp, decl, TFF_PLAIN_IDENTIFIER);
 
   return pp_ggc_formatted_text (cxx_pp);
 }
@@ -3953,6 +4150,13 @@ comparable_template_types_p (tree type_a, tree type_b)
   if (!CLASS_TYPE_P (type_b))
     return false;
 
+  /* If one of the types has a diagnose_as attribute set it is presented as a
+     non-template (even if it's a template specialization). */
+  if (flag_diagnostics_use_aliases
+	&& (lookup_attribute ("diagnose_as", TYPE_ATTRIBUTES (type_a))
+	      || lookup_attribute ("diagnose_as", TYPE_ATTRIBUTES (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)
@@ -4052,8 +4256,21 @@ print_template_differences (pretty_printer *pp, tree type_a, tree type_b,
   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))));
+  const char* identifier_a
+    = IDENTIFIER_POINTER (DECL_NAME (TI_TEMPLATE (tinfo_a)));
+  if (flag_diagnostics_use_aliases)
+    {
+      /* Locate the most general template, and see whether it carries the
+         diagnose_as attribute. If it does, use it as identifier for type_a. */
+      tree tpl = TI_TEMPLATE (tinfo_a);
+      while (DECL_TEMPLATE_INFO (tpl))
+	tpl = DECL_TI_TEMPLATE (tpl);
+      tree attr = lookup_attribute ("diagnose_as",
+				    TYPE_ATTRIBUTES (TREE_TYPE (tpl)));
+      if (attr)
+	identifier_a = TREE_STRING_POINTER (TREE_VALUE (TREE_VALUE (attr)));
+    }
+  pp_printf (pp, "%s<", identifier_a);
 
   tree args_a = TI_ARGS (tinfo_a);
   tree args_b = TI_ARGS (tinfo_b);
diff --git a/gcc/cp/name-lookup.c b/gcc/cp/name-lookup.c
index 1be5f3da6d5..5e8129df429 100644
--- a/gcc/cp/name-lookup.c
+++ b/gcc/cp/name-lookup.c
@@ -6060,6 +6060,53 @@ handle_namespace_attrs (tree ns, tree attributes)
 	    DECL_ATTRIBUTES (ns) = tree_cons (name, args,
 					      DECL_ATTRIBUTES (ns));
 	}
+      else if (is_attribute_p ("diagnose_as", name))
+	{
+	  if (DECL_NAMESPACE_ALIAS (ns))
+	    { // apply attribute to original namespace
+	      if (!args)
+		{ // turn alias identifier into attribute argument
+		  tree ns_name = DECL_NAME (ns);
+		  tree str = fix_string_type (
+			       build_string(IDENTIFIER_LENGTH (ns_name) + 1,
+					    IDENTIFIER_POINTER (ns_name)));
+		  args = build_tree_list (NULL_TREE, str);
+		}
+	      else if (TREE_CHAIN (args)
+			 || TREE_CODE (TREE_VALUE (args)) != STRING_CST)
+		{
+		  warning (OPT_Wattributes,
+			   "%qD attribute requires 0 or 1 NTBS arguments",
+			   name);
+		  continue;
+		}
+	      tree attributes = tree_cons (name, args, NULL_TREE);
+	      handle_namespace_attrs (ORIGINAL_NAMESPACE (ns), attributes);
+	      continue;
+	    }
+	  if (!args || TREE_CHAIN (args)
+		|| TREE_CODE (TREE_VALUE (args)) != STRING_CST)
+	    {
+	      warning (OPT_Wattributes,
+		       "%qD attribute requires a single NTBS argument",
+		       name);
+	      continue;
+	    }
+	  tree existing
+	    = lookup_attribute ("diagnose_as", DECL_ATTRIBUTES (ns));
+	  if (existing
+		&& !cp_tree_equal (TREE_VALUE (args),
+				   TREE_VALUE (TREE_VALUE (existing))))
+	    {
+	      auto_diagnostic_group d;
+	      warning (OPT_Wattributes, "%qD redeclared with different %qD "
+					"attribute value", ns, name);
+	      inform (DECL_SOURCE_LOCATION (ns), "previous declaration here");
+	      continue;
+	    }
+	  DECL_ATTRIBUTES (ns) = tree_cons (name, args,
+					    DECL_ATTRIBUTES (ns));
+	}
       else
 	{
 	  warning (OPT_Wattributes, "%qD attribute directive ignored",
@@ -6092,7 +6139,7 @@ pop_decl_namespace (void)
 /* Process a namespace-alias declaration.  */
 
 void
-do_namespace_alias (tree alias, tree name_space)
+do_namespace_alias (tree alias, tree name_space, tree attributes)
 {
   if (name_space == error_mark_node)
     return;
@@ -6108,6 +6155,9 @@ do_namespace_alias (tree alias, tree name_space)
   DECL_CONTEXT (alias) = FROB_CONTEXT (current_scope ());
   set_originating_module (alias);
 
+  /* Apply attributes.  */
+  handle_namespace_attrs (alias, attributes);
+
   pushdecl (alias);
 
   /* Emit debug info for namespace alias.  */
diff --git a/gcc/cp/name-lookup.h b/gcc/cp/name-lookup.h
index f63c4f5b8bb..cdbb6834220 100644
--- a/gcc/cp/name-lookup.h
+++ b/gcc/cp/name-lookup.h
@@ -435,7 +435,7 @@ extern tree cp_namespace_decls (tree);
 extern void set_decl_namespace (tree, tree, bool);
 extern void push_decl_namespace (tree);
 extern void pop_decl_namespace (void);
-extern void do_namespace_alias (tree, tree);
+extern void do_namespace_alias (tree, tree, tree);
 extern tree do_class_using_decl (tree, tree);
 extern tree lookup_arg_dependent (tree, tree, vec<tree, va_gc> *);
 extern tree search_anon_aggr (tree, tree, bool = false);
diff --git a/gcc/cp/parser.c b/gcc/cp/parser.c
index 096580e7e50..44bd88adb62 100644
--- a/gcc/cp/parser.c
+++ b/gcc/cp/parser.c
@@ -14252,20 +14252,15 @@ cp_parser_declaration (cp_parser* parser, tree prefix_attrs)
 	       || token1->keyword == RID_STATIC
 	       || token1->keyword == RID_INLINE))
     cp_parser_explicit_instantiation (parser);
-  /* If the next token is `namespace', check for a named or unnamed
-     namespace definition.  */
-  else if (token1->keyword == RID_NAMESPACE
-	   && (/* A named namespace definition.  */
-	       (token2->type == CPP_NAME
-		&& (cp_lexer_peek_nth_token (parser->lexer, 3)->type
-		    != CPP_EQ))
-               || (token2->type == CPP_OPEN_SQUARE
-                   && cp_lexer_peek_nth_token (parser->lexer, 3)->type
-                   == CPP_OPEN_SQUARE)
-	       /* An unnamed namespace definition.  */
-	       || token2->type == CPP_OPEN_BRACE
-	       || token2->keyword == RID_ATTRIBUTE))
-    cp_parser_namespace_definition (parser);
+  /* If the next token is `namespace', we have either a namespace alias
+     definition or a namespace definition.  */
+  else if (token1->keyword == RID_NAMESPACE)
+    {
+      cp_parser_parse_tentatively (parser);
+      cp_parser_namespace_alias_definition (parser);
+      if (!cp_parser_parse_definitely (parser))
+	cp_parser_namespace_definition (parser);
+    }
   /* An inline (associated) namespace definition.  */
   else if (token2->keyword == RID_NAMESPACE
 	   && token1->keyword == RID_INLINE)
@@ -20655,10 +20650,18 @@ cp_parser_namespace_alias_definition (cp_parser* parser)
 
   /* Look for the `namespace' keyword.  */
   cp_parser_require_keyword (parser, RID_NAMESPACE, RT_NAMESPACE);
+  /* Look for attributes (GCC extension).  */
+  tree attributes = cp_parser_attributes_opt (parser);
+  if (attributes)
+    pedwarn (input_location, OPT_Wpedantic,
+	     "standard attributes on namespaces aliases must follow "
+	     "the namespace alias name");
   /* Look for the identifier.  */
   identifier = cp_parser_identifier (parser);
   if (identifier == error_mark_node)
     return;
+  /* Look for more attributes (GCC extension).  */
+  attributes = attr_chainon (attributes, cp_parser_attributes_opt (parser));
   /* Look for the `=' token.  */
   if (!cp_parser_uncommitted_to_tentative_parse_p (parser)
       && cp_lexer_next_token_is (parser->lexer, CPP_OPEN_BRACE))
@@ -20670,7 +20673,8 @@ cp_parser_namespace_alias_definition (cp_parser* parser)
 	cp_lexer_consume_token (parser->lexer);
       return;
     }
-  cp_parser_require (parser, CPP_EQ, RT_EQ);
+  if (nullptr == cp_parser_require (parser, CPP_EQ, RT_EQ))
+    return;
   /* Look for the qualified-namespace-specifier.  */
   namespace_specifier
     = cp_parser_qualified_namespace_specifier (parser);
@@ -20679,7 +20683,7 @@ cp_parser_namespace_alias_definition (cp_parser* parser)
   cp_parser_require (parser, CPP_SEMICOLON, RT_SEMICOLON);
 
   /* Register the alias in the symbol table.  */
-  do_namespace_alias (identifier, namespace_specifier);
+  do_namespace_alias (identifier, namespace_specifier, attributes);
 }
 
 /* Parse a qualified-namespace-specifier.
diff --git a/gcc/cp/tree.c b/gcc/cp/tree.c
index 2a14fa92ddb..68317041a0f 100644
--- a/gcc/cp/tree.c
+++ b/gcc/cp/tree.c
@@ -46,6 +46,7 @@ static tree verify_stmt_tree_r (tree *, int *, void *);
 
 static tree handle_init_priority_attribute (tree *, tree, tree, int, bool *);
 static tree handle_abi_tag_attribute (tree *, tree, tree, int, bool *);
+static tree handle_diagnose_as_attribute (tree *, tree, tree, int, bool *);
 
 /* If REF is an lvalue, returns the kind of lvalue that REF is.
    Otherwise, returns clk_none.  */
@@ -4877,6 +4878,8 @@ const struct attribute_spec cxx_attribute_table[] =
     handle_init_priority_attribute, NULL },
   { "abi_tag", 1, -1, false, false, false, true,
     handle_abi_tag_attribute, NULL },
+  { "diagnose_as", 0, -1, false, false, false, false,
+    handle_diagnose_as_attribute, NULL },
   { NULL, 0, 0, false, false, false, false, NULL, NULL }
 };
 
@@ -5145,6 +5148,154 @@ handle_abi_tag_attribute (tree* node, tree name, tree args,
   return NULL_TREE;
 }
 
+static bool
+check_diagnose_as_redeclaration (const_tree decl, const_tree name,
+				 const_tree old, const_tree new_)
+{
+  if (!old)
+    return true;
+  if (TREE_CODE (TREE_VALUE (old)) == TREE_LIST)
+    old = TREE_VALUE (old);
+  if (TREE_CODE (TREE_VALUE (new_)) == TREE_LIST)
+    new_ = TREE_VALUE (new_);
+  tree old_value = TREE_VALUE (old);
+  tree new_value = TREE_VALUE (new_);
+  if (cp_tree_equal (old_value, new_value))
+    return true;
+  warning (OPT_Wattributes, "%qD redeclared with %<%D(%E)%> "
+			    "attribute", decl, name, new_value);
+  inform (DECL_SOURCE_LOCATION (decl), "previous declaration here");
+  return false;
+}
+
+static tree
+handle_diagnose_as_attribute (tree* node, tree name, tree args,
+			      int flags, bool* no_add_attrs)
+{
+  tree decl = NULL_TREE;
+  const bool is_alias = (TREE_CODE (*node) == TYPE_DECL);
+  if (args && list_length (args) == 2
+	&& TREE_PURPOSE (TREE_CHAIN (args)) == integer_zero_node)
+    /* We're called from handle_diagnose_as_attribute with additional context
+       argument.  */;
+  else if (is_alias)
+    {
+      if (args && (TREE_CHAIN (args)
+		     || TREE_CODE (TREE_VALUE (args)) != STRING_CST))
+	{
+	  warning (OPT_Wattributes,
+		   "%qD attribute requires 0 or 1 NTBS arguments", name);
+	  goto fail;
+	}
+    }
+  else if (!args || TREE_CHAIN (args)
+	     || TREE_CODE (TREE_VALUE (args)) != STRING_CST)
+    {
+      warning (OPT_Wattributes,
+	       "%qD attribute requires a single NTBS argument", name);
+      goto fail;
+    }
+
+  if (is_alias && CLASS_TYPE_P (TREE_TYPE (*node)))
+    { // Apply the attribute to the type alias itself.
+      decl = *node;
+
+      /* Reject alias templates without wildcards on the innermost template args
+         of the RHS type. E.g. template <class> using A = B;  */
+      if (DECL_LANG_SPECIFIC (decl)
+	    && DECL_TEMPLATE_INFO (decl)
+	    && DECL_ALIAS_TEMPLATE_P (DECL_TI_TEMPLATE (decl))
+	    && DECL_TI_TEMPLATE (decl)
+		 == TREE_TYPE (INNERMOST_TEMPLATE_PARMS (
+				 DECL_TEMPLATE_PARMS (
+				   DECL_TI_TEMPLATE (decl)))))
+	return error_mark_node;
+
+      if (!args)
+	{ // turn alias identifier into attribute argument
+	  tree alias_name = DECL_NAME (decl);
+	  tree str = fix_string_type (
+		       build_string(IDENTIFIER_LENGTH (alias_name) + 1,
+				    IDENTIFIER_POINTER (alias_name)));
+	  args = build_tree_list (NULL_TREE, str);
+	}
+      // apply the attribute to the specialization on the RHS.
+      tree type = strip_typedefs (TREE_TYPE (*node), nullptr, 0);
+
+      if (is_alias_template_p (decl))
+	{
+	  type = CLASSTYPE_PRIMARY_TEMPLATE_TYPE (type);
+	  // Warn and skip if the template arguments don't match up like in
+	  //   template <class T> using A = B<int, T>;
+	  if (!same_type_p (TREE_TYPE (decl), type))
+	    return error_mark_node;
+	}
+
+      // Add the DECL_CONTEXT of the alias to the attribute if it is different
+      // to the context of the type.
+      if (!TREE_CHAIN (args)
+	    && CP_DECL_CONTEXT (decl) != CP_TYPE_CONTEXT (type))
+	{
+	  TREE_CHAIN (args) = build_tree_list (integer_zero_node,
+					       CP_DECL_CONTEXT (decl));
+	}
+
+      if (COMPLETE_TYPE_P (type))
+	{
+	  tree rhs = handle_diagnose_as_attribute (
+			 &type, name, args, flags, no_add_attrs);
+	  if (rhs == error_mark_node || *no_add_attrs)
+	    return rhs;
+	  else
+	    TYPE_ATTRIBUTES (type)
+	      = tree_cons (name, args, TYPE_ATTRIBUTES (type));
+	}
+      else
+	{
+	  tree attributes = tree_cons (name, args, NULL_TREE);
+	  cplus_decl_attributes (&type, attributes, ATTR_FLAG_TYPE_IN_PLACE);
+	}
+    }
+  else if (TYPE_P (*node))
+    {
+      if (!OVERLOAD_TYPE_P (*node))
+	return error_mark_node;
+      decl = TYPE_NAME (*node);
+    }
+  else
+    {
+      if (!VAR_OR_FUNCTION_DECL_P (*node)
+	    || DECL_LANGUAGE (*node) != lang_cplusplus)
+	return error_mark_node;
+      decl = *node;
+    }
+
+  // Make sure all declarations have the same diagnose_as string.
+  if (DECL_SOURCE_LOCATION (decl) != input_location)
+    {
+      tree attributes = TYPE_P (*node) ? TYPE_ATTRIBUTES (*node)
+				       : DECL_ATTRIBUTES (decl);
+      if (!check_diagnose_as_redeclaration (
+	     decl, name, lookup_attribute ("diagnose_as", attributes), args))
+	goto fail;
+    }
+  else if (CLASS_TYPE_P (*node) && CLASSTYPE_IMPLICIT_INSTANTIATION (*node))
+    {
+      // The above branch (different source location) is taken for declarations
+      // of type aliases that modify an implicit template specialization on the
+      // RHS. This branch is taken when the template is instantiated via
+      // instantiate_class_template_1, in which case the attribute either
+      // already has the value from the general template or from a type alias.
+      goto fail;
+    }
+
+  return NULL_TREE;
+
+ fail:
+  *no_add_attrs = true;
+  return NULL_TREE;
+}
+
 /* Return a new PTRMEM_CST of the indicated TYPE.  The MEMBER is the
    thing pointed to by the constant.  */
 
diff --git a/gcc/doc/extend.texi b/gcc/doc/extend.texi
index 8fc66d626d8..4614800f5eb 100644
--- a/gcc/doc/extend.texi
+++ b/gcc/doc/extend.texi
@@ -2865,6 +2865,51 @@ types (@pxref{Variable Attributes}, @pxref{Type Attributes}.)
 The message attached to the attribute is affected by the setting of
 the @option{-fmessage-length} option.
 
+@item diagnose_as ("@var{string}")
+@itemx diagnose_as
+@cindex @code{diagnose_as} function attribute
+The @code{diagnose_as} attribute modifies how the entity the attribute
+appertains to is diagnosed in compiler messages and @code{__func__},
+@code{__FUNCTION__}, and @code{__PRETTY_FUNCTION__} macros. (The result of
+@code{typeid} is not affected.) If the attribute is applied to a
+@code{namespace}, the specified string replaces the complete enclosing scope.
+The effect of the @code{diagnose_as} attribute can be disabled with the
+@option{-fno-diagnostics-use-aliases} command line option.
+
+The argument @var{string} is optional for type and namespace aliases. If it is
+omitted, the identifier of the type or namespace alias is unconditionally used
+in all places where the type / namespace on the right-hand side is diagnosed.
+
+@smallexample
+namespace Foo @{
+  namespace Bar @{
+    inline namespace v1 [[gnu::diagnose_as("Foobar")]] @{
+      int f() @{
+        // __PRETTY_FUNCTION__ == "void Foobar::f()"
+      @}
+    @}
+  @}
+@}
+// In function 'int Foobar::f()':
+// warning: no return statement in function returning non-void [-Wreturn-type]
+@end smallexample
+
+The @code{diagnose_as} attribute can be used with namespaces, namespace aliases,
+functions, variables, alias declarations (but not alias templates), and
+user-defined types (classes, unions, and enums). If the alias declaration
+aliases a class type (including template specializations), the attribute is
+additionally applied to the class type. Whether the attribute has an effect on
+partial template specializations is unspecified and may depend on several
+different factors.
+
+@smallexample
+template <class T> struct basic_string @{@};
+using string [[gnu::diagnose_as]] = basic_string<char>;
+int f(basic_string<char>) @{@}
+// In function 'int f(string)':
+// warning: no return statement in function returning non-void [-Wreturn-type]
+@end smallexample
+
 @item error ("@var{message}")
 @itemx warning ("@var{message}")
 @cindex @code{error} function attribute
diff --git a/gcc/doc/invoke.texi b/gcc/doc/invoke.texi
index af2ce189fae..ed83462fa7a 100644
--- a/gcc/doc/invoke.texi
+++ b/gcc/doc/invoke.texi
@@ -311,7 +311,8 @@ Objective-C and Objective-C++ Dialects}.
 -fdiagnostics-show-path-depths @gol
 -fno-show-column @gol
 -fdiagnostics-column-unit=@r{[}display@r{|}byte@r{]} @gol
--fdiagnostics-column-origin=@var{origin}}
+-fdiagnostics-column-origin=@var{origin} @gol
+-fno-diagnostics-aliases}
 
 @item Warning Options
 @xref{Warning Options,,Options to Request or Suppress Warnings}.
@@ -5078,6 +5079,12 @@ first column.  The default value of 1 corresponds to traditional GCC
 behavior and to the GNU style guide.  Some utilities may perform better with an
 origin of 0; any non-negative value may be specified.
 
+@item -fno-diagnostics-use-aliases
+@opindex fno-diagnostics-use-aliases
+@opindex fdiagnostics-use-aliases
+Do not replace identifiers or scope names with the aliases defined with the
+@code{[[gnu::diagnose_as("alias")]]} attribute.
+
 @item -fdiagnostics-format=@var{FORMAT}
 @opindex fdiagnostics-format
 Select a different format for printing diagnostics.
diff --git a/gcc/testsuite/g++.dg/diagnostic/diagnose-as1.C b/gcc/testsuite/g++.dg/diagnostic/diagnose-as1.C
new file mode 100644
index 00000000000..e0a2cae32e5
--- /dev/null
+++ b/gcc/testsuite/g++.dg/diagnostic/diagnose-as1.C
@@ -0,0 +1,213 @@
+// { dg-options "-fdiagnostics-use-aliases -fpretty-templates" }
+
+#ifdef __cpp_constexpr
+template <typename T>
+  constexpr bool is_char() { return false; }
+
+template <>
+  constexpr bool is_char<char>() { return true; }
+
+template <typename T>
+  constexpr bool is_int() { return false; }
+
+template <>
+  constexpr bool is_int<int>() { return true; }
+#endif
+
+namespace __attribute__((diagnose_as())) foo0 {} // { dg-warning "'diagnose_as' attribute requires a single NTBS argument" }
+
+namespace __attribute__((diagnose_as("foo2"))) foo1 {} // { dg-line foo1 }
+namespace __attribute__((diagnose_as("XXXX"))) foo1 {} // { dg-warning "'foo1' redeclared with different 'diagnose_as' attribute value" }
+// { dg-message "previous declaration here" "" { target *-*-* } foo1 }
+namespace foo1
+{
+  void f() {
+    f(1); // { dg-error "too many arguments to function 'void foo2::f\\(\\)'" }
+  }
+
+  class __attribute__((diagnose_as("XX"))) X; // { dg-line XX }
+  class __attribute__((diagnose_as("--"))) X { // { dg-warning "'class foo2::XX' redeclared with 'diagnose_as\\(\"--\"\\)' attribute" }
+    // { dg-message "previous declaration here" "" { target *-*-* } XX }
+    __attribute__((diagnose_as("ff"))) void f();
+  };
+  class Y : X
+  {
+    void g() {
+      f(); // { dg-error "'void foo2::XX::ff\\(\\)' is private within this context" }
+    }
+  };
+
+  class __attribute__((diagnose_as())) Z0; // { dg-warning "'diagnose_as' attribute requires a single NTBS argument" }
+  class __attribute__((diagnose_as(1))) Z1; // { dg-warning "'diagnose_as' attribute requires a single NTBS argument" }
+  class __attribute__((diagnose_as("a", "b"))) Z2; // { dg-warning "'diagnose_as' attribute requires a single NTBS argument" }
+
+  template <typename T> class A0 {};
+  typedef A0<char> Achar __attribute__((diagnose_as("Achar")));
+  template class A0<int>;
+  typedef A0<int> Aint __attribute__((diagnose_as("Aint"))); // OK
+  typedef A0<int> Aint2 __attribute__((diagnose_as("Aint2"))); // { dg-warning "'class foo2::Aint' redeclared with 'diagnose_as\\(\"Aint2\"\\)' attribute" }
+  typedef A0<int> Aint3 __attribute__((diagnose_as(1))); // { dg-warning "'diagnose_as' attribute requires 0 or 1 NTBS arguments" }
+
+#if __cplusplus >= 201103L
+  template <typename T> using foo [[gnu::diagnose_as]] = A0<T>;
+  template <typename T> using aRef [[gnu::diagnose_as]] = A0<const T&>; // { dg-warning "'diagnose_as' attribute ignored" "" { target c++11 } }
+#endif
+}
+
+namespace A
+{
+  inline namespace __attribute__((diagnose_as("@1"))) B
+  {
+    template <typename U>
+      struct __attribute__((diagnose_as("@2"))) C
+      {
+	__attribute__((diagnose_as("fun:1")))
+	void f() {} // { dg-line ABC_f }
+
+	template <typename T>
+	  __attribute__((diagnose_as("fun:2")))
+	  void g(T, U) {} // { dg-line ABC_gT }
+
+	template <typename T>
+	  __attribute__((diagnose_as("fun:2.2")))
+	  void g() {} // { dg-line ABC_g }
+
+	__attribute__((diagnose_as("fun:3")))
+	void h()
+	{
+#ifdef __cpp_constexpr
+	  static_assert(__builtin_strcmp(__FUNCTION__, "fun:3") == 0, "");
+	  static_assert(__builtin_strcmp(__func__, "fun:3") == 0, "");
+	  constexpr const char* ref
+	    = is_int<U>() ? "void @1::@3::fun:3()"
+			  : "void @1::@2<U>::fun:3() [with U = char]";
+	  static_assert(__builtin_strcmp(__PRETTY_FUNCTION__, ref) == 0, "");
+#endif
+	}
+
+	template <typename T>
+	  __attribute__((diagnose_as("fun:4")))
+	  void ht()
+	{
+#ifdef __cpp_constexpr
+	  static_assert(__builtin_strcmp(__FUNCTION__, "fun:4") == 0, "");
+	  constexpr const char* ref
+	    = is_int<U>()
+		? "void @1::@3::fun:4<T>() [with T = float]"
+		: "void @1::@2<U>::fun:4<T>() [with T = float; U = char]";
+	  static_assert(__builtin_strcmp(__PRETTY_FUNCTION__, ref) == 0, "");
+#endif
+	}
+
+	template <typename T0, typename T1>
+	  struct __attribute__((diagnose_as("@5"))) E
+	  {
+	    __attribute__((diagnose_as("fun:5"))) static void f() {
+#ifdef __cpp_constexpr
+	      static_assert(__builtin_strcmp(__FUNCTION__, "fun:5") == 0, "");
+	      constexpr const char* ref = is_int<U>()
+		? is_char<T0>()
+		  ? "static void @1::@3::@6::fun:5()"
+		  : "static void @1::@3::@5<T0, T1>::fun:5() [with T0 = float; T1 = short int]"
+		: is_char<T0>()
+		  ? "static void @1::@2<U>::@6::fun:5() [with U = char]"
+		  : "static void @1::@2<U>::@5<T0, T1>::fun:5() [with T0 = float; T1 = short int; U = char]";
+	      static_assert(__builtin_strcmp(__PRETTY_FUNCTION__, ref) == 0,
+			    "");
+#endif
+	    }
+	  };
+	typedef E<char, char> F __attribute__((diagnose_as("@6")));
+
+	template <typename T>
+	  struct __attribute__((diagnose_as("@7"))) E<T, long>
+	  {
+	    __attribute__((diagnose_as("fun:6"))) static void f() {
+#ifdef __cpp_constexpr
+	      static_assert(__builtin_strcmp(__FUNCTION__, "fun:6") == 0, "");
+	      constexpr const char* ref = is_int<U>()
+		  ? "static void @1::@3::@7<T, long int>::fun:6() [with T = float]"
+		  : "static void @1::@2<U>::@7<T, long int>::fun:6() [with T = float; U = char]";
+	      static_assert(__builtin_strcmp(__PRETTY_FUNCTION__, ref) == 0,
+			    "");
+#endif
+	    }
+	  };
+      };
+
+    typedef C<int> D __attribute__((diagnose_as("@3")));
+
+    template <>
+      struct __attribute__((diagnose_as("@4"))) C<float>
+      {
+      };
+
+    typedef C<short> E __attribute__((diagnose_as));
+  }
+}
+
+namespace frob
+{
+  namespace ni
+  {
+    struct kate {};
+    namespace cate
+    {
+      struct me {};
+    }
+  }
+}
+
+namespace frobni __attribute__((diagnose_as("twiggle"))) = frob::ni;
+namespace frobnicate __attribute__((diagnose_as)) = frob::ni::cate;
+namespace wrong __attribute__((diagnose_as(1))) = frob::ni; // { dg-warning "'diagnose_as' attribute requires 0 or 1 NTBS arguments" }
+namespace wrong2 __attribute__((diagnose_as("wrong", 1))) = frob::ni; // { dg-warning "'diagnose_as' attribute requires 0 or 1 NTBS arguments" }
+
+void fn_1(int);
+void fn_2(A::D);
+
+int main ()
+{
+  fn_2 (A::D ());
+  fn_1 (A::D ()); // { dg-error "cannot convert '@1::@3' to 'int'" }
+  fn_1 (A::C<int> ()); // { dg-error "cannot convert '@1::@3' to 'int'" }
+  fn_1 (A::C<char> ()); // { dg-error "cannot convert '@1::@2<char>' to 'int'" }
+  fn_1 (A::C<float> ()); // { dg-error "cannot convert '@1::@4' to 'int'" }
+  fn_1 (A::C<short> ()); // { dg-error "cannot convert '@1::E' to 'int'" }
+  fn_1 (A::E ()); // { dg-error "cannot convert '@1::E' to 'int'" }
+  fn_1 (foo1::A0<int> ()); // { dg-error "cannot convert 'foo2::Aint' to 'int'" }
+  fn_1 (foo1::Aint2 ()); // { dg-error "cannot convert 'foo2::Aint2' {aka 'foo2::Aint'} to 'int'" }
+#if __cplusplus >= 201103L
+  fn_1 (foo1::foo<float> ()); // { dg-error "cannot convert 'foo2::foo<float>' to 'int'" "" { target c++11 } }
+  fn_1 (foo1::aRef<float> ()); // { dg-error "cannot convert 'foo2::aRef<float>' {aka 'foo2::foo<const float&>'} to 'int'" "" { target c++11 } }
+  fn_1 (frob::ni::kate ()); // { dg-error "cannot convert 'twiggle::kate' to 'int'" "" { target c++11 } }
+  fn_1 (frob::ni::cate::me ()); // { dg-error "cannot convert 'frobnicate::me' to 'int'" "" { target c++11 } }
+#endif
+
+  A::C<int>().f(0); // { dg-error "no matching function for call to '@1::@3::f\\(int\\)'" }
+  // { dg-message "candidate: 'void @1::@3::fun:1\\(\\)'" "" { target *-*-* } ABC_f }
+
+  A::C<char>().f(0); // { dg-error "no matching function for call to '@1::@2<char>::f\\(int\\)'" }
+  // { dg-message "candidate: 'void @1::@2<U>::fun:1\\(\\) \\\[with U = char\\\]'" "" { target *-*-* } ABC_f }
+
+  A::C<float>().f(0); // { dg-error "'struct @1::@4' has no member named 'f'" }
+
+  A::C<int>().g(); // { dg-error "no matching function for call to '@1::@3::g\\(\\)'" }
+  // { dg-message "candidate: 'template<class T> void @1::@3::fun:2\\(T, U\\)'" "" { target *-*-* } ABC_gT }
+  // { dg-message "candidate: 'template<class T> void @1::@3::fun:2.2\\(\\)'" "" { target *-*-* } ABC_g }
+
+  A::C<char>().g(); // { dg-error "no matching function for call to '@1::@2<char>::g\\(\\)'" }
+  // { dg-message "candidate: 'template<class T> void @1::@2<U>::fun:2\\(T, U\\) \\\[with U = char\\\]'" "" { target *-*-* } ABC_gT }
+  // { dg-message "candidate: 'template<class T> void @1::@2<U>::fun:2.2\\(\\) \\\[with U = char\\\]'" "" { target *-*-* } ABC_g }
+
+  A::C<int>().h();
+  A::C<char>().h();
+  A::C<int>().ht<float>();
+  A::C<char>().ht<float>();
+  A::C<int>::E<float, short>::f();
+  A::C<char>::E<float, short>::f();
+  A::C<int>::E<float, long>::f();
+  A::C<char>::E<float, long>::f();
+  A::C<int>::F::f();
+  A::C<char>::F::f();
+}
diff --git a/gcc/testsuite/g++.dg/diagnostic/diagnose-as2.C b/gcc/testsuite/g++.dg/diagnostic/diagnose-as2.C
new file mode 100644
index 00000000000..b1d46d12024
--- /dev/null
+++ b/gcc/testsuite/g++.dg/diagnostic/diagnose-as2.C
@@ -0,0 +1,144 @@
+// { dg-options "-fdiagnostics-use-aliases -fno-pretty-templates" }
+
+#ifdef __cpp_constexpr
+template <typename T>
+  constexpr bool is_char() { return false; }
+
+template <>
+  constexpr bool is_char<char>() { return true; }
+
+template <typename T>
+  constexpr bool is_int() { return false; }
+
+template <>
+  constexpr bool is_int<int>() { return true; }
+#endif
+
+namespace A
+{
+  inline namespace B __attribute__((diagnose_as("@1")))
+  {
+    template <typename U>
+      struct __attribute__((diagnose_as("@2"))) C
+      {
+	__attribute__((diagnose_as("fun:1")))
+	void f() {} // { dg-line ABC_f }
+
+	template <typename T>
+	  __attribute__((diagnose_as("fun:2")))
+	  void g(T, U) {} // { dg-line ABC_gT }
+
+	template <typename T>
+	  __attribute__((diagnose_as("fun:2.2")))
+	  void g() {} // { dg-line ABC_g }
+
+	__attribute__((diagnose_as("fun:3")))
+	void h()
+	{
+#ifdef __cpp_constexpr
+	  static_assert(__builtin_strcmp(__FUNCTION__, "fun:3") == 0, "");
+	  constexpr const char* ref
+	    = is_int<U>() ? "void @1::@3::fun:3()"
+			  : "void @1::@2<char>::fun:3()";
+	  static_assert(__builtin_strcmp(__PRETTY_FUNCTION__, ref) == 0, "");
+#endif
+	}
+
+	template <typename T>
+	  __attribute__((diagnose_as("fun:4")))
+	  void ht()
+	{
+#ifdef __cpp_constexpr
+	  static_assert(__builtin_strcmp(__FUNCTION__, "fun:4") == 0, "");
+	  constexpr const char* ref
+	    = is_int<U>() ? "void @1::@3::fun:4<float>()"
+			  : "void @1::@2<char>::fun:4<float>()";
+	  static_assert(__builtin_strcmp(__PRETTY_FUNCTION__, ref) == 0, "");
+#endif
+	}
+
+	template <typename T0, typename T1>
+	  struct __attribute__((diagnose_as("@5"))) E
+	  {
+	    __attribute__((diagnose_as("fun:5"))) static void f() {
+#ifdef __cpp_constexpr
+	      static_assert(__builtin_strcmp(__FUNCTION__, "fun:5") == 0, "");
+	      constexpr const char* ref = is_int<U>()
+		? is_char<T0>()
+		  ? "static void @1::@3::@6::fun:5()"
+		  : "static void @1::@3::@5<float, short int>::fun:5()"
+		: is_char<T0>()
+		  ? "static void @1::@2<char>::@6::fun:5()"
+		  : "static void @1::@2<char>::@5<float, short int>::fun:5()";
+	      static_assert(__builtin_strcmp(__PRETTY_FUNCTION__, ref) == 0,
+			    "");
+#endif
+	    }
+	  };
+	typedef E<char, char> F __attribute__((diagnose_as("@6")));
+
+	template <typename T>
+	  struct __attribute__((diagnose_as("@7"))) E<T, long>
+	  {
+	    __attribute__((diagnose_as("fun:6"))) static void f() {
+#ifdef __cpp_constexpr
+	      static_assert(__builtin_strcmp(__FUNCTION__, "fun:6") == 0, "");
+	      // Note that @7 is ignored with -fno-pretty-templates. After all
+	      // diagnose_as on partial specializations is not supported.
+	      constexpr const char* ref = is_int<U>()
+		  ? "static void @1::@3::@5<float, long int>::fun:6()"
+		  : "static void @1::@2<char>::@5<float, long int>::fun:6()";
+	      static_assert(__builtin_strcmp(__PRETTY_FUNCTION__, ref) == 0,
+			    "");
+#endif
+	    }
+	  };
+      };
+
+    typedef C<int> D __attribute__((diagnose_as("@3")));
+
+    template <>
+      struct __attribute__((diagnose_as("@4"))) C<float>
+      {
+      };
+  }
+}
+
+void fn_1(int);
+void fn_2(A::D);
+
+int main ()
+{
+  fn_2 (A::D ());
+  fn_1 (A::D ()); // { dg-error "cannot convert '@1::@3' to 'int'" }
+  fn_1 (A::C<int> ()); // { dg-error "cannot convert '@1::@3' to 'int'" }
+  fn_1 (A::C<char> ()); // { dg-error "cannot convert '@1::@2<char>' to 'int'" }
+  fn_1 (A::C<float> ()); // { dg-error "cannot convert '@1::@4' to 'int'" }
+
+  A::C<int>().f(0); // { dg-error "no matching function for call to '@1::@3::f\\(int\\)'" }
+  // { dg-message "candidate: 'void @1::@3::fun:1\\(\\)'" "" { target *-*-* } ABC_f }
+
+  A::C<char>().f(0); // { dg-error "no matching function for call to '@1::@2<char>::f\\(int\\)'" }
+  // { dg-message "candidate: 'void @1::@2<char>::fun:1\\(\\)'" "" { target *-*-* } ABC_f }
+
+  A::C<float>().f(0); // { dg-error "'struct @1::@4' has no member named 'f'" }
+
+  A::C<int>().g(); // { dg-error "no matching function for call to '@1::@3::g\\(\\)'" }
+  // { dg-message "candidate: 'template<class T> void @1::@3::fun:2\\(T, int\\)'" "" { target *-*-* } ABC_gT }
+  // { dg-message "candidate: 'template<class T> void @1::@3::fun:2.2\\(\\)'" "" { target *-*-* } ABC_g }
+
+  A::C<char>().g(); // { dg-error "no matching function for call to '@1::@2<char>::g\\(\\)'" }
+  // { dg-message "candidate: 'template<class T> void @1::@2<char>::fun:2\\(T, char\\)'" "" { target *-*-* } ABC_gT }
+  // { dg-message "candidate: 'template<class T> void @1::@2<char>::fun:2.2\\(\\)'" "" { target *-*-* } ABC_g }
+
+  A::C<int>().h();
+  A::C<char>().h();
+  A::C<int>().ht<float>();
+  A::C<char>().ht<float>();
+  A::C<int>::E<float, short>::f();
+  A::C<char>::E<float, short>::f();
+  A::C<int>::E<float, long>::f();
+  A::C<char>::E<float, long>::f();
+  A::C<int>::F::f();
+  A::C<char>::F::f();
+}
diff --git a/gcc/testsuite/g++.dg/diagnostic/diagnose-as3.C b/gcc/testsuite/g++.dg/diagnostic/diagnose-as3.C
new file mode 100644
index 00000000000..beedb9624a8
--- /dev/null
+++ b/gcc/testsuite/g++.dg/diagnostic/diagnose-as3.C
@@ -0,0 +1,152 @@
+// { dg-options "-fdiagnostics-use-aliases -fpretty-templates" }
+// { dg-do compile { target c++11 } }
+
+const char* volatile s;
+
+namespace foo
+{
+  template <class U>
+    struct [[gnu::diagnose_as("Bar'")]] Bar
+    {
+      template <class T0, class T1>
+        struct [[gnu::diagnose_as("A'")]] A
+        { constexpr const char* f() { return __PRETTY_FUNCTION__; } };
+
+      template <class T>
+        struct A<T, float>
+        { constexpr const char* f() { return __PRETTY_FUNCTION__; } };
+
+      template <class T>
+        struct [[gnu::diagnose_as("Adouble")]] A<T, double>
+        { constexpr const char* f() { return __PRETTY_FUNCTION__; } };
+
+      using Special [[gnu::diagnose_as("SpecialA")]] = A<int, int>;
+
+      static constexpr const char* f() { return __PRETTY_FUNCTION__; }
+    };
+
+  template <class P>
+    struct
+    [[gnu::diagnose_as("BarPtr")]]
+    Bar<P*>
+    {
+      template <class U = int>
+      static constexpr const char* f() { return __PRETTY_FUNCTION__; }
+    };
+
+  template <>
+    struct [[gnu::diagnose_as("SpecialBar")]] Bar<void>
+    {
+      static constexpr const char* f() { return __PRETTY_FUNCTION__; }
+    };
+
+  using barchar [[gnu::diagnose_as("barchar")]] = Bar<char>;
+}
+
+namespace A
+{
+  inline namespace B __attribute__((diagnose_as("@1")))
+  {
+    template  <typename U>
+      struct __attribute__((diagnose_as("@2"))) C
+      {
+        template <class T>
+        __attribute__((diagnose_as("fun:2")))
+          constexpr const char* g() { return __PRETTY_FUNCTION__; }
+
+        __attribute__((diagnose_as("fun:3")))
+          constexpr const char* h()
+          { return __PRETTY_FUNCTION__; }
+      };
+
+    typedef C<int> D __attribute__((diagnose_as("@3")));
+  }
+}
+
+template <class T0, class U0>
+struct [[gnu::diagnose_as("X.0")]] X0
+{
+  template <class T1, class U1>
+    struct X1
+    {
+      template <class T2, class U2>
+        struct X2
+        {
+          template <class T3, class U3>
+            [[gnu::diagnose_as("f-1")]]
+            static constexpr const char* f() {
+              return __PRETTY_FUNCTION__;
+            }
+        };
+
+      using XX [[gnu::diagnose_as("X2'")]] = X2<int, long>;
+    };
+
+  template <class T3, class U3>
+    [[gnu::diagnose_as("f-1")]]
+    static constexpr const char* f() {
+      return __PRETTY_FUNCTION__;
+    }
+
+  struct [[gnu::diagnose_as("X.3")]] X3
+  {
+    template <class T3, class U3>
+      [[gnu::diagnose_as("f-1")]]
+      static constexpr const char* f() {
+        return __PRETTY_FUNCTION__;
+      }
+  };
+};
+
+using X0int [[gnu::diagnose_as("[int|int]")]] = X0<int, int>;
+
+#define TEST(expected, fun)                                                    \
+  static_assert(__builtin_strcmp(expected, fun) == 0, "")
+
+
+void h() {
+  TEST("constexpr const char* @1::@3::fun:3()",
+      A::C<int>().h());
+  TEST("constexpr const char* @1::@3::fun:2<T>() [with T = float]",
+      A::C<int>().g<float>());
+  TEST("constexpr const char* @1::@2<U>::fun:3() [with U = char]",
+      A::C<char>().h());
+  TEST("constexpr const char* @1::@2<U>::fun:2<T>() [with T = float; U = char]",
+      A::C<char>().g<float>());
+  TEST("constexpr const char* foo::barchar::A'<T0, T1>::f() [with T0 = char; T1 = char]",
+      (foo::Bar<char>::A<char, char>().f()));
+  TEST("constexpr const char* foo::barchar::A'<T, float>::f() [with T = char]",
+      (foo::Bar<char>::A<char, float>().f()));
+  TEST("constexpr const char* foo::barchar::Adouble<T, double>::f() [with T = int]",
+      (foo::Bar<char>::A<int, double>().f()));
+  TEST("constexpr const char* foo::barchar::SpecialA::f()",
+      (foo::Bar<char>::A<int, int>().f()));
+  TEST("constexpr const char* foo::Bar'<U>::A'<T0, T1>::f() [with T0 = char; T1 = char; U = float]",
+      (foo::Bar<float>::A<char, char>().f()));
+  TEST("constexpr const char* foo::Bar'<U>::A'<T, float>::f() [with T = char; U = float]",
+      (foo::Bar<float>::A<char, float>().f()));
+  TEST("constexpr const char* foo::Bar'<U>::Adouble<T, double>::f() [with T = char; U = float]",
+      (foo::Bar<float>::A<char, double>().f()));
+  TEST("constexpr const char* foo::Bar'<U>::SpecialA::f() [with U = float]",
+      (foo::Bar<float>::A<int, int>().f()));
+  TEST("static constexpr const char* foo::barchar::f()",
+      foo::Bar<char>::f());
+  TEST("static constexpr const char* foo::BarPtr<P*>::f<U>() [with U = int; P = char]",
+      foo::Bar<char*>::f());
+  TEST("static constexpr const char* foo::BarPtr<P*>::f<U>() [with U = int; P = float]",
+      foo::Bar<float*>::f());
+  TEST("static constexpr const char* foo::Bar'<U>::f() [with U = float]",
+      foo::Bar<float>::f());
+  TEST("static constexpr const char* foo::SpecialBar::f()",
+      foo::Bar<void>::f());
+  TEST("static constexpr const char* X.0<T0, U0>::X1<T1, U1>::X2'::f-1<T3, U3>() [with T3 = long int; U3 = long long int; T1 = short int; U1 = int; T0 = char; U0 = short int]",
+      (X0<char, short>::X1<short, int>::X2<int, long>::f<long, long long>()));
+  TEST("static constexpr const char* X.0<T0, U0>::X1<T1, U1>::X2<T2, U2>::f-1<T3, U3>() [with T3 = long int; U3 = long long int; T2 = long int; U2 = int; T1 = short int; U1 = int; T0 = char; U0 = short int]",
+      (X0<char, short>::X1<short, int>::X2<long, int>::f<long, long long>()));
+  TEST("static constexpr const char* X.0<T0, U0>::X.3::f-1<T3, U3>() [with T3 = long int; U3 = long long int; T0 = char; U0 = short int]",
+      (X0<char, short>::X3::f<long, long long>()));
+  TEST("static constexpr const char* [int|int]::f-1<T3, U3>() [with T3 = long int; U3 = long long int]",
+      (X0<int, int>::f<long, long long>()));
+  TEST("static constexpr const char* [int|int]::X.3::f-1<T3, U3>() [with T3 = long int; U3 = long long int]",
+      (X0<int, int>::X3::f<long, long long>()));
+}
diff --git a/gcc/testsuite/g++.dg/diagnostic/diagnose-as4.C b/gcc/testsuite/g++.dg/diagnostic/diagnose-as4.C
new file mode 100644
index 00000000000..89b800c7b9d
--- /dev/null
+++ b/gcc/testsuite/g++.dg/diagnostic/diagnose-as4.C
@@ -0,0 +1,158 @@
+// { dg-options "-fdiagnostics-use-aliases -fno-pretty-templates" }
+// { dg-do compile { target c++11 } }
+
+const char* volatile s;
+
+namespace foo
+{
+  template <class U>
+    struct [[gnu::diagnose_as("Bar'")]] Bar
+    {
+      template <class T0, class T1>
+        struct [[gnu::diagnose_as("A'")]] A
+        { constexpr const char* f() { return __PRETTY_FUNCTION__; } };
+
+      template <class T>
+        struct A<T, float>
+        { constexpr const char* f() { return __PRETTY_FUNCTION__; } };
+
+      template <class T>
+        struct [[gnu::diagnose_as("Adouble")]] A<T, double>
+        { constexpr const char* f() { return __PRETTY_FUNCTION__; } };
+
+      using Special [[gnu::diagnose_as("SpecialA")]] = A<int, int>;
+
+      static constexpr const char* f() { return __PRETTY_FUNCTION__; }
+    };
+
+  template <class P>
+    struct
+    [[gnu::diagnose_as("BarPtr")]]
+    Bar<P*>
+    {
+      template <class U = int>
+      static constexpr const char* f() { return __PRETTY_FUNCTION__; }
+    };
+
+  template <>
+    struct [[gnu::diagnose_as("SpecialBar")]] Bar<void>
+    {
+      static constexpr const char* f() { return __PRETTY_FUNCTION__; }
+    };
+
+  using barchar [[gnu::diagnose_as("barchar")]] = Bar<char>;
+}
+
+namespace A
+{
+  inline namespace B __attribute__((diagnose_as("@1")))
+  {
+    template  <typename U>
+      struct __attribute__((diagnose_as("@2"))) C
+      {
+        template <class T>
+        __attribute__((diagnose_as("fun:2")))
+          constexpr const char* g() { return __PRETTY_FUNCTION__; }
+
+        __attribute__((diagnose_as("fun:3")))
+          constexpr const char* h()
+          { return __PRETTY_FUNCTION__; }
+      };
+
+    typedef C<int> D __attribute__((diagnose_as("@3")));
+  }
+}
+
+template <class T0, class U0>
+struct [[gnu::diagnose_as("XX0")]] X0
+{
+  template <class T1, class U1>
+    struct X1
+    {
+      template <class T2, class U2>
+        struct X2
+        {
+          template <class T3, class U3>
+            [[gnu::diagnose_as("f-1")]]
+            static constexpr const char* f() {
+              return __PRETTY_FUNCTION__;
+            }
+        };
+
+      using XX [[gnu::diagnose_as("X2'")]] = X2<int, long>;
+    };
+
+  template <class T3, class U3>
+    [[gnu::diagnose_as("f-2")]]
+    static constexpr const char* f() {
+      return __PRETTY_FUNCTION__;
+    }
+
+  struct [[gnu::diagnose_as("XX3")]] X3
+  {
+    template <class T3, class U3>
+      [[gnu::diagnose_as("f-3")]]
+      static constexpr const char* f() { // { dg-line X0_X3_f }
+        return __PRETTY_FUNCTION__;
+      }
+  };
+};
+
+using X0int [[gnu::diagnose_as("X0intint")]] = X0<int, int>;
+
+#define TEST(expected, fun)                                                    \
+  static_assert(__builtin_strcmp(expected, fun) == 0, "")
+
+
+void h() {
+  TEST("constexpr const char* @1::@3::fun:3()",
+      A::C<int>().h());
+  TEST("constexpr const char* @1::@3::fun:2<float>()",
+      A::C<int>().g<float>());
+  TEST("constexpr const char* @1::@2<char>::fun:3()",
+      A::C<char>().h());
+  TEST("constexpr const char* @1::@2<char>::fun:2<float>()",
+      A::C<char>().g<float>());
+  TEST("constexpr const char* foo::barchar::A'<char, char>::f()",
+      (foo::Bar<char>::A<char, char>().f()));
+  TEST("constexpr const char* foo::barchar::A'<char, float>::f()",
+      (foo::Bar<char>::A<char, float>().f()));
+  TEST("constexpr const char* foo::barchar::A'<int, double>::f()",
+      (foo::Bar<char>::A<int, double>().f()));
+  TEST("constexpr const char* foo::barchar::SpecialA::f()",
+      (foo::Bar<char>::A<int, int>().f()));
+  TEST("constexpr const char* foo::Bar'<float>::A'<char, char>::f()",
+      (foo::Bar<float>::A<char, char>().f()));
+  TEST("constexpr const char* foo::Bar'<float>::A'<char, float>::f()",
+      (foo::Bar<float>::A<char, float>().f()));
+  TEST("constexpr const char* foo::Bar'<float>::A'<char, double>::f()",
+      (foo::Bar<float>::A<char, double>().f()));
+  TEST("constexpr const char* foo::Bar'<float>::SpecialA::f()",
+      (foo::Bar<float>::A<int, int>().f()));
+  TEST("static constexpr const char* foo::barchar::f()",
+      foo::Bar<char>::f());
+  TEST("static constexpr const char* foo::Bar'<char*>::f<int>()",
+      foo::Bar<char*>::f());
+  TEST("static constexpr const char* foo::Bar'<float*>::f<int>()",
+      foo::Bar<float*>::f());
+  TEST("static constexpr const char* foo::Bar'<float>::f()",
+      foo::Bar<float>::f());
+  TEST("static constexpr const char* foo::SpecialBar::f()",
+      foo::Bar<void>::f());
+  TEST("static constexpr const char* XX0<char, short int>::X1<short int, int>::X2'::f-1<long int, long long int>()",
+      (X0<char, short>::X1<short, int>::X2<int, long>::f<long, long long>()));
+  TEST("static constexpr const char* XX0<char, short int>::X1<short int, int>::X2<long int, int>::f-1<long int, long long int>()",
+      (X0<char, short>::X1<short, int>::X2<long, int>::f<long, long long>()));
+  TEST("static constexpr const char* XX0<char, short int>::XX3::f-3<long int, long long int>()",
+      (X0<char, short>::X3::f<long, long long>()));
+  TEST("static constexpr const char* X0intint::f-2<long int, long long int>()",
+      (X0<int, int>::f<long, long long>()));
+  TEST("static constexpr const char* X0intint::XX3::f-3<long int, long long int>()",
+      (X0<int, int>::X3::f<long, long long>()));
+
+  X0<char, short>::X3::f<long, long long>(1); // { dg-error "no matching function for call to 'XX0<char, short int>::XX3::f<long int, long long int>\\(int\\)" }
+  // { dg-message "candidate: 'static constexpr const char\\* XX0<char, short int>::XX3::f-3<long int, long long int>\\(\\)'" "" { target *-*-* } X0_X3_f }
+
+  X0<int, int>::X3::f<long, long long>(1); // { dg-error "no matching function for call to 'X0intint::XX3::f<long int, long long int>\\(int\\)" }
+  // { dg-message "candidate: 'static constexpr const char\\* X0intint::XX3::f-3<long int, long long int>\\(\\)'" "" { target *-*-* } X0_X3_f }
+}
diff --git a/gcc/testsuite/g++.dg/diagnostic/diagnose-as5.C b/gcc/testsuite/g++.dg/diagnostic/diagnose-as5.C
new file mode 100644
index 00000000000..2a46d64964f
--- /dev/null
+++ b/gcc/testsuite/g++.dg/diagnostic/diagnose-as5.C
@@ -0,0 +1,21 @@
+// { dg-options "-fdiagnostics-use-aliases -fpretty-templates" }
+// { dg-do compile { target c++11 } }
+
+namespace std
+{
+  template <class T, class A>
+    class basic_string {};
+
+  namespace pmr
+  {
+    struct P {};
+    using string [[gnu::diagnose_as]] = basic_string<char, P>;
+  }
+}
+
+void f(int);
+
+int main()
+{
+  f(std::pmr::string()); // { dg-error "cannot convert 'std::pmr::string' to 'int'" }
+}
diff --git a/gcc/testsuite/g++.dg/diagnostic/diagnose-as6.C b/gcc/testsuite/g++.dg/diagnostic/diagnose-as6.C
new file mode 100644
index 00000000000..94747a7aa6e
--- /dev/null
+++ b/gcc/testsuite/g++.dg/diagnostic/diagnose-as6.C
@@ -0,0 +1,45 @@
+// { dg-options "-fdiagnostics-use-aliases -fpretty-templates" }
+// { dg-do compile { target c++11 } }
+
+template <class T, class U>
+  struct A
+  {
+    struct X {};
+    using Y [[gnu::diagnose_as]] = X;
+  };
+
+template <>
+  struct [[gnu::diagnose_as("A!")]] A<int, int>
+  {
+    struct X {};
+    using Y [[gnu::diagnose_as]] = X;
+  };
+
+void f(int);
+
+template <class T, class U>
+  using B [[gnu::diagnose_as]] = A<T, U>;
+
+// this is not a real alias template
+template <class T>
+  using C [[gnu::diagnose_as]] = A<int, int>; // { dg-warning "'diagnose_as' attribute ignored" }
+
+// Would the following request A<float, int> to be diagnosed as D<int>?
+// What about `= A<float, const T>', etc? This is not worth the implementation
+// complexity.
+template <class T>
+  using D [[gnu::diagnose_as]] = A<float, T>; // { dg-warning "'diagnose_as' attribute ignored" }
+
+template <class T, class U>
+  struct E {};
+template <class T, class U>
+  using F [[gnu::diagnose_as("EF")]] = E<U, T>; // { dg-warning "'diagnose_as' attribute ignored" }
+
+int main()
+{
+  f(A<char, int>()); // { dg-error "cannot convert 'B<char, int>' to 'int'" }
+  f(A<char, int>::X()); // { dg-error "cannot convert 'B<char, int>::Y' to 'int'" }
+  f(B<int, int>::X()); // { dg-error "cannot convert 'A!::Y' to 'int'" }
+  f(C<float>::X()); // { dg-error "cannot convert 'A!::Y' to 'int'" }
+  f(F<char, int>()); // { dg-error "cannot convert 'F<char, int>' {aka 'E<int, char>'} to 'int'" }
+}

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

* [PATCH v4] c++: Add gnu::diagnose_as attribute
  2021-07-15 12:21 [PATCH v3] c++: Add gnu::diagnose_as attribute Matthias Kretz
@ 2021-07-23  8:58 ` Matthias Kretz
  2021-08-17 18:31   ` Jason Merrill
  2021-09-08  2:21   ` [PATCH v4] c++: Add gnu::diagnose_as attribute Jason Merrill
  0 siblings, 2 replies; 24+ messages in thread
From: Matthias Kretz @ 2021-07-23  8:58 UTC (permalink / raw)
  To: gcc-patches, Jason Merrill

[-- Attachment #1: Type: text/plain, Size: 9009 bytes --]

Hi Jason,

I found a few regressions from the last patch in the meantime. Version 4 of 
the patch is attached.

Questions:

1. I simplified the condition for calling dump_template_parms in 
dump_function_name. !DECL_FRIEND_PSEUDO_TEMPLATE_INSTANTIATION (t) is 
equivalent to DECL_USE_TEMPLATE (t) in this context; implying that 
dump_template_parms is unconditionally called with `primary = false`. Or am I 
missing something?

2. Given a DECL_TI_ARGS tree, can I query whether an argument was deduced or 
explicitly specified? I'm asking because I still consider diagnostics of 
function templates unfortunate. `template <class T> void f()` is fine, as is 
`void f(T) [with T = float]`, but `void f() [with T = float]` could be better. 
I.e. if the template parameter appears somewhere in the function parameter 
list, dump_template_parms would only produce noise. If, however, the template 
parameter was given explicitly, it would be nice if it could show up 
accordingly in diagnostics.

3. When parsing tentatively and the parse is rejected, input_location is not 
reset, correct? In the attached patch I therefore made 
cp_parser_namespace_alias_definition reset input_location on a failed 
tentative parse. But it feels wrong. Shouldn't input_location be restored on 
cp_parser_parse_definitely?

--

This attribute overrides the diagnostics output string for the entity it
appertains to. The motivation is to improve QoI for library TS
implementations, where diagnostics have a very bad signal-to-noise ratio
due to the long namespaces involved.

With the attribute, it is possible to solve PR89370 and make
std::__cxx11::basic_string<_CharT, _Traits, _Alloc> appear as
std::string in diagnostic output without extra hacks to recognize the
type in the C++ frontend.

Signed-off-by: Matthias Kretz <m.kretz@gsi.de>

gcc/ChangeLog:

        PR c++/89370
        * doc/extend.texi: Document the diagnose_as attribute.
        * doc/invoke.texi: Document -fno-diagnostics-use-aliases.

gcc/c-family/ChangeLog:

        PR c++/89370
        * c.opt (fdiagnostics-use-aliases): New diagnostics flag.

gcc/cp/ChangeLog:

        PR c++/89370
        * cp-tree.h: Add is_alias_template_p declaration.
        * decl2.c (is_alias_template_p): New function. Determines
        whether a given TYPE_DECL is actually an alias template that is
        still missing its template_info.
        (is_late_template_attribute): Decls with diagnose_as attribute
        are early attributes only if they are alias templates.
        * error.c (dump_scope): When printing the name of a namespace,
        look for the diagnose_as attribute. If found, print the
        associated string instead of calling dump_decl.
        (dump_decl_name_or_diagnose_as): New function to replace
        dump_decl (pp, DECL_NAME(t), flags) and inspect the tree for the
        diagnose_as attribute before printing the DECL_NAME.
        (dump_template_scope): New function. Prints the scope of a
        template instance correctly applying diagnose_as attributes and
        adjusting the list of template parms accordingly.
        (dump_aggr_type): If the type has a diagnose_as attribute, print
        the associated string instead of printing the original type
        name. Print template parms only if the attribute was not applied
        to the instantiation / full specialization. Delay call to
        dump_scope until the diagnose_as attribute is found. If the
        attribute has a second argument, use it to override the context
        passed to dump_scope.
        (dump_simple_decl): Call dump_decl_name_or_diagnose_as instead
        of dump_decl.
        (dump_decl): Ditto.
        (lang_decl_name): Ditto.
        (dump_function_decl): Walk the functions context list to
        determine whether a call to dump_template_scope is required.
        Ensure function templates diagnosed with pretty templates set
        TFF_TEMPLATE_NAME to skip dump_template_parms.
        (dump_function_name): Replace the function's identifier with the
        diagnose_as attribute value, if set. Expand
        DECL_FRIEND_PSEUDO_TEMPLATE_INSTANTIATION to DECL_USE_TEMPLATE
        and consequently call dump_template_parms with primary = false.
        (comparable_template_types_p): Consider the types not a template
        if one carries a diagnose_as attribute.
        (print_template_differences): Replace the identifier with the
        diagnose_as attribute value on the most general template, if it
        is set.
        * name-lookup.c (handle_namespace_attrs): Handle the diagnose_as
        attribute on namespaces. Ensure exactly one string argument.
        Ensure previous diagnose_as attributes used the same name.
        'diagnose_as' on namespace aliases are forwarded to the original
        namespace. Support no-argument 'diagnose_as' on namespace
        aliases.
        (do_namespace_alias): Add attributes parameter and call
        handle_namespace_attrs.
        * name-lookup.h (do_namespace_alias): Add attributes tree
        parameter.
        * parser.c (cp_parser_declaration): If the next token is
        RID_NAMESPACE, tentatively parse a namespace alias definition.
        If this fails expect a namespace definition.
        (cp_parser_namespace_alias_definition): Allow optional
        attributes before and after the identifier. Fast exit, restoring
        input_location, if the expected CPP_EQ token is missing. Pass
        attributes to do_namespace_alias.
        * tree.c (cxx_attribute_table): Add diagnose_as attribute to the
        table.
        (check_diagnose_as_redeclaration): New function; copied and
        adjusted from check_abi_tag_redeclaration.
        (handle_diagnose_as_attribute): New function; copied and
        adjusted from handle_abi_tag_attribute. If the given *node is a
        TYPE_DECL: allow no argument to the attribute, using DECL_NAME
        instead; apply the attribute to the type on the RHS in place,
        even if the type is complete. Allow 2 arguments when called from
        handle_diagnose_as_attribute. For type aliases, append
        CP_DECL_CONTEXT as second attribute argument when the RHS type
        has a different context. Warn about alias templates without
        matching template arguments. Apply the attribute to the primary
        template type for alias templates.

gcc/testsuite/ChangeLog:

        PR c++/89370
        * g++.dg/diagnostic/diagnose-as1.C: New test.
        * g++.dg/diagnostic/diagnose-as2.C: New test.
        * g++.dg/diagnostic/diagnose-as3.C: New test.
        * g++.dg/diagnostic/diagnose-as4.C: New test.
        * g++.dg/diagnostic/diagnose-as5.C: New test.
        * g++.dg/diagnostic/diagnose-as6.C: New test.
---
 gcc/c-family/c.opt                            |   4 +
 gcc/cp/cp-tree.h                              |   1 +
 gcc/cp/decl2.c                                |  45 ++++
 gcc/cp/error.c                                | 247 ++++++++++++++++--
 gcc/cp/name-lookup.c                          |  52 +++-
 gcc/cp/name-lookup.h                          |   2 +-
 gcc/cp/parser.c                               |  41 +--
 gcc/cp/tree.c                                 | 148 +++++++++++
 gcc/doc/extend.texi                           |  45 ++++
 gcc/doc/invoke.texi                           |   9 +-
 .../g++.dg/diagnostic/diagnose-as1.C          | 213 +++++++++++++++
 .../g++.dg/diagnostic/diagnose-as2.C          | 144 ++++++++++
 .../g++.dg/diagnostic/diagnose-as3.C          | 152 +++++++++++
 .../g++.dg/diagnostic/diagnose-as4.C          | 158 +++++++++++
 .../g++.dg/diagnostic/diagnose-as5.C          |  21 ++
 .../g++.dg/diagnostic/diagnose-as6.C          |  45 ++++
 16 files changed, 1291 insertions(+), 36 deletions(-)
 create mode 100644 gcc/testsuite/g++.dg/diagnostic/diagnose-as1.C
 create mode 100644 gcc/testsuite/g++.dg/diagnostic/diagnose-as2.C
 create mode 100644 gcc/testsuite/g++.dg/diagnostic/diagnose-as3.C
 create mode 100644 gcc/testsuite/g++.dg/diagnostic/diagnose-as4.C
 create mode 100644 gcc/testsuite/g++.dg/diagnostic/diagnose-as5.C
 create mode 100644 gcc/testsuite/g++.dg/diagnostic/diagnose-as6.C

-- 
──────────────────────────────────────────────────────────────────────────
 Dr. Matthias Kretz                           https://mattkretz.github.io
 GSI Helmholtz Centre for Heavy Ion Research               https://gsi.de
 std::experimental::simd              https://github.com/VcDevel/std-simd
──────────────────────────────────────────────────────────────────────────

[-- Attachment #2: 0001-c-Add-gnu-diagnose_as-attribute.patch --]
[-- Type: text/x-patch, Size: 63428 bytes --]

diff --git a/gcc/c-family/c.opt b/gcc/c-family/c.opt
index 91929706aff..354590982bd 100644
--- a/gcc/c-family/c.opt
+++ b/gcc/c-family/c.opt
@@ -1606,6 +1606,10 @@ fdiagnostics-show-template-tree
 C++ ObjC++ Var(flag_diagnostics_show_template_tree) Init(0)
 Print hierarchical comparisons when template types are mismatched.
 
+fdiagnostics-use-aliases
+C++ Var(flag_diagnostics_use_aliases) Init(1)
+Replace identifiers or scope names in diagnostics as defined by the diagnose_as attribute.
+
 fdirectives-only
 C ObjC C++ ObjC++
 Preprocess directives only.
diff --git a/gcc/cp/cp-tree.h b/gcc/cp/cp-tree.h
index f4bcab5b18d..792849c361c 100644
--- a/gcc/cp/cp-tree.h
+++ b/gcc/cp/cp-tree.h
@@ -6764,6 +6764,7 @@ extern tree grokfield (const cp_declarator *, cp_decl_specifier_seq *,
 		       tree, bool, tree, tree);
 extern tree grokbitfield (const cp_declarator *, cp_decl_specifier_seq *,
 			  tree, tree, tree);
+extern bool is_alias_template_p			(tree);
 extern tree splice_template_attributes		(tree *, tree);
 extern bool any_dependent_type_attributes_p	(tree);
 extern tree cp_reconstruct_complex_type		(tree, tree);
diff --git a/gcc/cp/decl2.c b/gcc/cp/decl2.c
index 9564b0d619f..1ff2ab27813 100644
--- a/gcc/cp/decl2.c
+++ b/gcc/cp/decl2.c
@@ -1139,6 +1139,47 @@ grokbitfield (const cp_declarator *declarator,
   return value;
 }
 
+/* Return true iff DECL is an alias template of a class template which strictly
+   renames the type.  */
+bool
+is_alias_template_p (tree decl)
+{
+  if (TREE_CODE (decl) != TYPE_DECL)
+    return false;
+
+  tree type = strip_typedefs (TREE_TYPE (decl), nullptr, 0);
+  if (!CLASS_TYPE_P (type) || !CLASSTYPE_TEMPLATE_INFO (type))
+    return false;
+
+  /* Ensure it's a real alias template not just
+       template <class T> struct A {
+	 struct B {};
+	 template <class U> struct C {};
+	 using D [[gnu::diagnose_as]] = B;
+	 using E [[gnu::diagnose_as]] = C<int>;
+       };
+     A<T>::D and A<T>::E are not alias templates.
+     - For A<T>::D, the DECL_PRIMARY_TEMPLATE is A and not B, which would be the
+       case for a real alias template.
+     - For A<T>::E, the innermost template params belong to C but its template
+       args have no wildcard types, which would be the case for a real
+       alias template.  */
+  tree tmpl = CLASSTYPE_TI_TEMPLATE (type);
+  if (!PRIMARY_TEMPLATE_P (tmpl))
+    return false;
+
+  tree targs = INNERMOST_TEMPLATE_ARGS (CLASSTYPE_TI_ARGS (type));
+  for (int i = 0; i < NUM_TMPL_ARGS (targs); ++i)
+    {
+      tree arg = TREE_VEC_ELT (targs, i);
+      while (INDIRECT_TYPE_P (arg))
+	arg = TREE_TYPE (arg);
+      if (WILDCARD_TYPE_P (arg))
+	return true;
+    }
+  return false;
+}
+
 \f
 /* Returns true iff ATTR is an attribute which needs to be applied at
    instantiation time rather than template definition time.  */
@@ -1166,6 +1207,10 @@ is_late_template_attribute (tree attr, tree decl)
 	  || is_attribute_p ("used", name)))
     return false;
 
+  /* Allow alias templates to set diagnose_as on the RHS template.  */
+  if (is_attribute_p ("diagnose_as", name))
+    return !is_alias_template_p (decl);
+
   /* Attribute tls_model wants to modify the symtab.  */
   if (is_attribute_p ("tls_model", name))
     return true;
diff --git a/gcc/cp/error.c b/gcc/cp/error.c
index 012a4ecddf4..0452d228b51 100644
--- a/gcc/cp/error.c
+++ b/gcc/cp/error.c
@@ -35,6 +35,7 @@ along with GCC; see the file COPYING3.  If not see
 #include "internal-fn.h"
 #include "gcc-rich-location.h"
 #include "cp-name-hint.h"
+#include "attribs.h"
 
 #define pp_separate_with_comma(PP) pp_cxx_separate_with (PP, ',')
 #define pp_separate_with_semicolon(PP) pp_cxx_separate_with (PP, ';')
@@ -66,6 +67,7 @@ static void dump_alias_template_specialization (cxx_pretty_printer *, tree, int)
 static void dump_type (cxx_pretty_printer *, tree, int);
 static void dump_typename (cxx_pretty_printer *, tree, int);
 static void dump_simple_decl (cxx_pretty_printer *, tree, tree, int);
+static void dump_decl_name_or_diagnose_as (cxx_pretty_printer *, tree, int);
 static void dump_decl (cxx_pretty_printer *, tree, int);
 static void dump_template_decl (cxx_pretty_printer *, tree, int);
 static void dump_function_decl (cxx_pretty_printer *, tree, int);
@@ -231,7 +233,15 @@ dump_scope (cxx_pretty_printer *pp, tree scope, int flags)
     {
       if (scope != global_namespace)
 	{
-          dump_decl (pp, scope, f);
+	  tree diagnose_as
+	    = flag_diagnostics_use_aliases
+		? lookup_attribute ("diagnose_as", DECL_ATTRIBUTES (scope))
+		: NULL_TREE;
+	  if (diagnose_as)
+	    pp_cxx_ws_string (
+	      pp, TREE_STRING_POINTER (TREE_VALUE (TREE_VALUE (diagnose_as))));
+	  else
+	    dump_decl (pp, scope, f);
 	  pp_cxx_colon_colon (pp);
 	}
     }
@@ -764,6 +774,45 @@ class_key_or_enum_as_string (tree t)
     return "struct";
 }
 
+/* Print out an enclosing scope of a template instance by inspecting the tree
+   SPECIAL of the template instantiation and the tree GENERAL of the most
+   general template in parallel under the control of FLAGS while adjusting
+   PARMS as needed. This allows the diagnose_as attribute to override the name
+   of full specializations, while hiding its template parameters, and to
+   override the name of partial specializations without falling back to printing
+   the template parameters of the general template decl.  */
+
+static void
+dump_template_scope (cxx_pretty_printer *pp, tree special, tree general,
+		     tree *parms, int flags)
+{
+  gcc_assert (parms);
+  if (special != general && RECORD_OR_UNION_TYPE_P (special) && *parms)
+    {
+      gcc_assert (RECORD_OR_UNION_TYPE_P (general));
+      const bool tmplate
+	= TYPE_LANG_SPECIFIC (special) && CLASSTYPE_TEMPLATE_INFO (special)
+	    && (TREE_CODE (CLASSTYPE_TI_TEMPLATE (special)) != TEMPLATE_DECL
+		  || PRIMARY_TEMPLATE_P (CLASSTYPE_TI_TEMPLATE (special)));
+      dump_template_scope (pp, CP_TYPE_CONTEXT (special),
+			   CP_TYPE_CONTEXT (general),
+			   tmplate ? &TREE_CHAIN(*parms) : parms, flags);
+      tree attr = lookup_attribute ("diagnose_as", TYPE_ATTRIBUTES (special));
+      if (attr)
+	{
+	  pp_cxx_ws_string (pp, TREE_STRING_POINTER (
+				TREE_VALUE (TREE_VALUE (attr))));
+	  if (tmplate)
+	    TREE_VALUE (*parms) = make_tree_vec (0);
+	}
+      else
+	dump_aggr_type (pp, general, flags | TFF_UNQUALIFIED_NAME);
+      pp_cxx_colon_colon (pp);
+    }
+  else
+    dump_scope (pp, general, flags);
+}
+
 /* Print out a class declaration T under the control of FLAGS,
    in the form `class foo'.  */
 
@@ -781,6 +830,7 @@ dump_aggr_type (cxx_pretty_printer *pp, tree t, int flags)
 
   tree decl = TYPE_NAME (t);
 
+  tree diagnose_as = NULL_TREE;
   if (decl)
     {
       typdef = (!DECL_ARTIFICIAL (decl)
@@ -804,11 +854,14 @@ dump_aggr_type (cxx_pretty_printer *pp, tree t, int flags)
 		&& (TREE_CODE (CLASSTYPE_TI_TEMPLATE (t)) != TEMPLATE_DECL
 		    || PRIMARY_TEMPLATE_P (CLASSTYPE_TI_TEMPLATE (t)));
       
-      if (! (flags & TFF_UNQUALIFIED_NAME))
-	dump_scope (pp, CP_DECL_CONTEXT (decl), flags | TFF_SCOPE);
-      flags &= ~TFF_UNQUALIFIED_NAME;
+      tree context = CP_DECL_CONTEXT (decl);
+      tree diagnose_as_specialized = NULL_TREE;
       if (tmplate)
 	{
+	  if (flag_diagnostics_use_aliases)
+	    diagnose_as_specialized
+	      = lookup_attribute ("diagnose_as", TYPE_ATTRIBUTES (t));
+
 	  /* Because the template names are mangled, we have to locate
 	     the most general template, and use that name.  */
 	  tree tpl = TYPE_TI_TEMPLATE (t);
@@ -817,9 +870,61 @@ dump_aggr_type (cxx_pretty_printer *pp, tree t, int flags)
 	    tpl = DECL_TI_TEMPLATE (tpl);
 	  decl = tpl;
 	}
+
+      if (flag_diagnostics_use_aliases)
+	{
+	  diagnose_as = lookup_attribute ("diagnose_as",
+					  TYPE_ATTRIBUTES (TREE_TYPE (decl)));
+	  if (diagnose_as_specialized
+		&& (!diagnose_as || TREE_VALUE (diagnose_as_specialized)
+		      != TREE_VALUE (diagnose_as)))
+	    {
+	      diagnose_as = diagnose_as_specialized;
+	      /* Skip dump_template_parms if diagnose_as applies to a full
+	         specialization.  */
+	      tree args = CLASSTYPE_TI_ARGS (t);
+	      args = INNERMOST_TEMPLATE_ARGS (args);
+	      gcc_assert (args);
+	      tmplate = false;
+	      for (int i = 0; i < NUM_TMPL_ARGS (args); ++i)
+		{
+		  tree arg = TREE_VEC_ELT (args, i);
+		  while (INDIRECT_TYPE_P (arg))
+		    arg = TREE_TYPE (arg);
+		  if (WILDCARD_TYPE_P (arg))
+		    {
+		      tmplate = true;
+		      break;
+		    }
+		}
+	    }
+	  /* Given
+
+	       template <class T> struct [[gnu::diagnose_as("AA")]] A {
+	         struct [[gnu::diagnose_as("BB")]] B {};
+	       };
+
+	     A<int>::B will reach the following branch and find the diagnose_as
+	     attribute for B.  */
+	  else if (!tmplate && !diagnose_as && TYPE_TEMPLATE_INFO (t))
+	    diagnose_as
+	      = lookup_attribute ("diagnose_as",
+				  TYPE_ATTRIBUTES (
+				    TREE_TYPE (TYPE_TI_TEMPLATE (t))));
+
+	  if (diagnose_as && TREE_CHAIN (TREE_VALUE (diagnose_as)))
+	    context = TREE_VALUE (TREE_CHAIN (TREE_VALUE (diagnose_as)));
+	}
+
+      if (! (flags & TFF_UNQUALIFIED_NAME))
+	dump_scope (pp, context, flags | TFF_SCOPE);
+      flags &= ~TFF_UNQUALIFIED_NAME;
     }
 
-  if (LAMBDA_TYPE_P (t))
+  if (diagnose_as)
+    pp_cxx_ws_string (pp, TREE_STRING_POINTER (
+			    TREE_VALUE (TREE_VALUE (diagnose_as))));
+  else if (LAMBDA_TYPE_P (t))
     {
       /* A lambda's "type" is essentially its signature.  */
       pp_string (pp, M_("<lambda"));
@@ -1141,7 +1246,7 @@ dump_simple_decl (cxx_pretty_printer *pp, tree t, tree type, int flags)
 	  pp_string (pp, " capture>");
 	}
       else
-	dump_decl (pp, DECL_NAME (t), flags);
+	dump_decl_name_or_diagnose_as (pp, t, flags);
     }
   else if (DECL_DECOMPOSITION_P (t))
     pp_string (pp, M_("<structured bindings>"));
@@ -1185,6 +1290,25 @@ dump_decl_name (cxx_pretty_printer *pp, tree t, int flags)
   pp_cxx_tree_identifier (pp, t);
 }
 
+/* Print the DECL_NAME of DECL unless a different string was requested by the
+   diagnose_as attribute. */
+
+static void
+dump_decl_name_or_diagnose_as (cxx_pretty_printer *pp, tree decl, int flags)
+{
+  if (flag_diagnostics_use_aliases)
+    {
+      tree attr = lookup_attribute ("diagnose_as", DECL_ATTRIBUTES (decl));
+      if (attr && TREE_VALUE (attr))
+	{
+	  pp_cxx_ws_string (
+	    pp, TREE_STRING_POINTER (TREE_VALUE (TREE_VALUE (attr))));
+	  return;
+	}
+    }
+  dump_decl_name (pp, DECL_NAME (decl), flags);
+}
+
 /* Dump a human readable string for the decl T under control of FLAGS.  */
 
 static void
@@ -1230,7 +1354,7 @@ dump_decl (cxx_pretty_printer *pp, tree t, int flags)
 	      || flags & TFF_CLASS_KEY_OR_ENUM))
 	{
 	  pp_cxx_ws_string (pp, "using");
-	  dump_decl (pp, DECL_NAME (t), flags);
+	  dump_decl_name_or_diagnose_as(pp, t, flags);
 	  pp_cxx_whitespace (pp);
 	  pp_cxx_ws_string (pp, "=");
 	  pp_cxx_whitespace (pp);
@@ -1412,7 +1536,7 @@ dump_decl (cxx_pretty_printer *pp, tree t, int flags)
 	      TREE_CODE (DECL_INITIAL (t)) == TEMPLATE_PARM_INDEX))
 	dump_simple_decl (pp, t, TREE_TYPE (t), flags);
       else if (DECL_NAME (t))
-	dump_decl (pp, DECL_NAME (t), flags);
+	dump_decl_name_or_diagnose_as (pp, t, flags);
       else if (DECL_INITIAL (t))
 	dump_expr (pp, DECL_INITIAL (t), flags | TFF_EXPR_IN_PARENS);
       else
@@ -1431,7 +1555,7 @@ dump_decl (cxx_pretty_printer *pp, tree t, int flags)
 	  }
 	dump_type (pp, scope, flags);
 	pp_cxx_colon_colon (pp);
-	dump_decl (pp, DECL_NAME (t), flags);
+	dump_decl_name_or_diagnose_as (pp, t, flags);
 	if (variadic)
 	  pp_cxx_ws_string (pp, "...");
       }
@@ -1671,7 +1795,8 @@ dump_function_decl (cxx_pretty_printer *pp, tree t, int flags)
   tree template_args = NULL_TREE;
   tree template_parms = NULL_TREE;
   int show_return = flags & TFF_RETURN_TYPE || flags & TFF_DECL_SPECIFIERS;
-  int do_outer_scope = ! (flags & TFF_UNQUALIFIED_NAME);
+  bool do_outer_scope = ! (flags & TFF_UNQUALIFIED_NAME);
+  bool do_template_scope = false;
   tree exceptions;
   bool constexpr_p;
   tree ret = NULL_TREE;
@@ -1688,6 +1813,11 @@ dump_function_decl (cxx_pretty_printer *pp, tree t, int flags)
   /* Likewise for the constexpr specifier, in case t is a specialization.  */
   constexpr_p = DECL_DECLARED_CONSTEXPR_P (t);
 
+  /* Keep t before the following branch makes t point to a more general
+     template. Without the specialized template, the diagnose_as attribute on
+     the function is lost. */
+  tree specialized_t = t;
+
   /* Pretty print template instantiations only.  */
   if (DECL_USE_TEMPLATE (t) && DECL_TEMPLATE_INFO (t)
       && !(flags & TFF_NO_TEMPLATE_BINDINGS)
@@ -1699,8 +1829,45 @@ dump_function_decl (cxx_pretty_printer *pp, tree t, int flags)
       tmpl = most_general_template (t);
       if (tmpl && TREE_CODE (tmpl) == TEMPLATE_DECL)
 	{
+	  /* Inspect whether any record/union type in the context of the
+	     instantiated function template carries a diagnose_as attribute. If
+	     one does, we need to print the scope and template argument list
+	     differently. Consider:
+
+	       template <class T> struct A {
+		 template <class U0, class U1> struct B;
+		 template <class U> struct B<U, int> {
+		   void f();
+		 };
+	       };
+	       using Achar [[gnu::diagnose_as("Achar")]] = A<char>;
+
+	     Now Achar::B<int, int>::f() needs to print as "Achar::B<U, int>::f()
+	     [with U = int]". However, DECL_CONTEXT (t) would print as
+	     "Achar::B<int, int>" and dump_aggr_type has no sensible way to
+	     retrieve A<T>::B<U, int>. tmpl was generalized to A<T>::B<U, int>::f
+	     and thus dump_aggr_type (DECL_CONTEXT (tmpl)) would print
+	     "A<T>::B<U, int> [with T = char, U = int]". I.e. the diagnose_as
+	     attribute on the A<char> instantiation is lost.  */
+	  if (tmpl != t && do_outer_scope && flag_diagnostics_use_aliases)
+	    {
+	      for (tree context = DECL_CONTEXT (t);
+		   context && RECORD_OR_UNION_TYPE_P (context);
+		   context = TYPE_CONTEXT (context))
+		{
+		  if (lookup_attribute ("diagnose_as",
+					TYPE_ATTRIBUTES (context)))
+		    {
+		      do_template_scope = true;
+		      break;
+		    }
+		}
+	    }
+
 	  template_parms = DECL_TEMPLATE_PARMS (tmpl);
 	  t = tmpl;
+	  /* The "[with ...]" clause is printed, don't print template params. */
+	  dump_function_name_flags |= TFF_TEMPLATE_NAME;
 	}
     }
 
@@ -1747,6 +1914,25 @@ dump_function_decl (cxx_pretty_printer *pp, tree t, int flags)
   /* Print the function name.  */
   if (!do_outer_scope)
     /* Nothing.  */;
+  else if (do_template_scope)
+    {
+      template_parms = copy_list (template_parms);
+      bool func_template = TREE_TYPE (TREE_VALUE (template_parms)) == t;
+      dump_template_scope(pp, DECL_CONTEXT (specialized_t), DECL_CONTEXT (t),
+			  func_template ? &TREE_CHAIN (template_parms)
+					: &template_parms,
+			  flags);
+      if (TREE_VEC_LENGTH (TREE_VALUE (template_parms)) == 0)
+	{
+	  /* If no template parms are left, make it a NULL_TREE so that no
+	     "[with ]" is printed.  */
+	  tree p = TREE_CHAIN (template_parms);
+	  for (; p && TREE_VEC_LENGTH (TREE_VALUE (p)) == 0; p = TREE_CHAIN (p))
+	    ;
+	  if (!p)
+	    template_parms = template_args = NULL_TREE;
+	}
+    }
   else if (cname)
     {
       dump_type (pp, cname, flags);
@@ -1755,7 +1941,7 @@ dump_function_decl (cxx_pretty_printer *pp, tree t, int flags)
   else
     dump_scope (pp, CP_DECL_CONTEXT (t), flags);
 
-  dump_function_name (pp, t, dump_function_name_flags);
+  dump_function_name (pp, specialized_t, dump_function_name_flags);
 
   if (!(flags & TFF_NO_FUNCTION_ARGUMENTS))
     {
@@ -1935,6 +2121,14 @@ dump_function_name (cxx_pretty_printer *pp, tree t, int flags)
   if (TREE_CODE (t) == TEMPLATE_DECL)
     t = DECL_TEMPLATE_RESULT (t);
 
+  if (flag_diagnostics_use_aliases)
+    {
+      tree attr = lookup_attribute ("diagnose_as", DECL_ATTRIBUTES (t));
+      if (attr)
+	name = get_identifier (
+		 TREE_STRING_POINTER (TREE_VALUE (TREE_VALUE (attr))));
+    }
+
   /* Don't let the user see __comp_ctor et al.  */
   if (DECL_CONSTRUCTOR_P (t)
       || DECL_DESTRUCTOR_P (t))
@@ -1970,11 +2164,10 @@ dump_function_name (cxx_pretty_printer *pp, tree t, int flags)
 
   if (DECL_TEMPLATE_INFO (t)
       && !(flags & TFF_TEMPLATE_NAME)
-      && !DECL_FRIEND_PSEUDO_TEMPLATE_INSTANTIATION (t)
+      && DECL_USE_TEMPLATE (t)
       && (TREE_CODE (DECL_TI_TEMPLATE (t)) != TEMPLATE_DECL
 	  || PRIMARY_TEMPLATE_P (DECL_TI_TEMPLATE (t))))
-    dump_template_parms (pp, DECL_TEMPLATE_INFO (t), !DECL_USE_TEMPLATE (t),
-                         flags);
+    dump_template_parms (pp, DECL_TEMPLATE_INFO (t), false, flags);
 }
 
 /* Dump the template parameters from the template info INFO under control of
@@ -3168,7 +3361,7 @@ lang_decl_name (tree decl, int v, bool translate)
            && TREE_CODE (decl) == NAMESPACE_DECL)
     dump_decl (cxx_pp, decl, TFF_PLAIN_IDENTIFIER | TFF_UNQUALIFIED_NAME);
   else
-    dump_decl (cxx_pp, DECL_NAME (decl), TFF_PLAIN_IDENTIFIER);
+    dump_decl_name_or_diagnose_as (cxx_pp, decl, TFF_PLAIN_IDENTIFIER);
 
   return pp_ggc_formatted_text (cxx_pp);
 }
@@ -3953,6 +4146,13 @@ comparable_template_types_p (tree type_a, tree type_b)
   if (!CLASS_TYPE_P (type_b))
     return false;
 
+  /* If one of the types has a diagnose_as attribute set it is presented as a
+     non-template (even if it's a template specialization). */
+  if (flag_diagnostics_use_aliases
+	&& (lookup_attribute ("diagnose_as", TYPE_ATTRIBUTES (type_a))
+	      || lookup_attribute ("diagnose_as", TYPE_ATTRIBUTES (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)
@@ -4052,8 +4252,21 @@ print_template_differences (pretty_printer *pp, tree type_a, tree type_b,
   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))));
+  const char* identifier_a
+    = IDENTIFIER_POINTER (DECL_NAME (TI_TEMPLATE (tinfo_a)));
+  if (flag_diagnostics_use_aliases)
+    {
+      /* Locate the most general template, and see whether it carries the
+         diagnose_as attribute. If it does, use it as identifier for type_a. */
+      tree tpl = TI_TEMPLATE (tinfo_a);
+      while (DECL_TEMPLATE_INFO (tpl))
+	tpl = DECL_TI_TEMPLATE (tpl);
+      tree attr = lookup_attribute ("diagnose_as",
+				    TYPE_ATTRIBUTES (TREE_TYPE (tpl)));
+      if (attr)
+	identifier_a = TREE_STRING_POINTER (TREE_VALUE (TREE_VALUE (attr)));
+    }
+  pp_printf (pp, "%s<", identifier_a);
 
   tree args_a = TI_ARGS (tinfo_a);
   tree args_b = TI_ARGS (tinfo_b);
diff --git a/gcc/cp/name-lookup.c b/gcc/cp/name-lookup.c
index 1be5f3da6d5..5e8129df429 100644
--- a/gcc/cp/name-lookup.c
+++ b/gcc/cp/name-lookup.c
@@ -6060,6 +6060,53 @@ handle_namespace_attrs (tree ns, tree attributes)
 	    DECL_ATTRIBUTES (ns) = tree_cons (name, args,
 					      DECL_ATTRIBUTES (ns));
 	}
+      else if (is_attribute_p ("diagnose_as", name))
+	{
+	  if (DECL_NAMESPACE_ALIAS (ns))
+	    { // apply attribute to original namespace
+	      if (!args)
+		{ // turn alias identifier into attribute argument
+		  tree ns_name = DECL_NAME (ns);
+		  tree str = fix_string_type (
+			       build_string(IDENTIFIER_LENGTH (ns_name) + 1,
+					    IDENTIFIER_POINTER (ns_name)));
+		  args = build_tree_list (NULL_TREE, str);
+		}
+	      else if (TREE_CHAIN (args)
+			 || TREE_CODE (TREE_VALUE (args)) != STRING_CST)
+		{
+		  warning (OPT_Wattributes,
+			   "%qD attribute requires 0 or 1 NTBS arguments",
+			   name);
+		  continue;
+		}
+	      tree attributes = tree_cons (name, args, NULL_TREE);
+	      handle_namespace_attrs (ORIGINAL_NAMESPACE (ns), attributes);
+	      continue;
+	    }
+	  if (!args || TREE_CHAIN (args)
+		|| TREE_CODE (TREE_VALUE (args)) != STRING_CST)
+	    {
+	      warning (OPT_Wattributes,
+		       "%qD attribute requires a single NTBS argument",
+		       name);
+	      continue;
+	    }
+	  tree existing
+	    = lookup_attribute ("diagnose_as", DECL_ATTRIBUTES (ns));
+	  if (existing
+		&& !cp_tree_equal (TREE_VALUE (args),
+				   TREE_VALUE (TREE_VALUE (existing))))
+	    {
+	      auto_diagnostic_group d;
+	      warning (OPT_Wattributes, "%qD redeclared with different %qD "
+					"attribute value", ns, name);
+	      inform (DECL_SOURCE_LOCATION (ns), "previous declaration here");
+	      continue;
+	    }
+	  DECL_ATTRIBUTES (ns) = tree_cons (name, args,
+					    DECL_ATTRIBUTES (ns));
+	}
       else
 	{
 	  warning (OPT_Wattributes, "%qD attribute directive ignored",
@@ -6092,7 +6139,7 @@ pop_decl_namespace (void)
 /* Process a namespace-alias declaration.  */
 
 void
-do_namespace_alias (tree alias, tree name_space)
+do_namespace_alias (tree alias, tree name_space, tree attributes)
 {
   if (name_space == error_mark_node)
     return;
@@ -6108,6 +6155,9 @@ do_namespace_alias (tree alias, tree name_space)
   DECL_CONTEXT (alias) = FROB_CONTEXT (current_scope ());
   set_originating_module (alias);
 
+  /* Apply attributes.  */
+  handle_namespace_attrs (alias, attributes);
+
   pushdecl (alias);
 
   /* Emit debug info for namespace alias.  */
diff --git a/gcc/cp/name-lookup.h b/gcc/cp/name-lookup.h
index f63c4f5b8bb..cdbb6834220 100644
--- a/gcc/cp/name-lookup.h
+++ b/gcc/cp/name-lookup.h
@@ -435,7 +435,7 @@ extern tree cp_namespace_decls (tree);
 extern void set_decl_namespace (tree, tree, bool);
 extern void push_decl_namespace (tree);
 extern void pop_decl_namespace (void);
-extern void do_namespace_alias (tree, tree);
+extern void do_namespace_alias (tree, tree, tree);
 extern tree do_class_using_decl (tree, tree);
 extern tree lookup_arg_dependent (tree, tree, vec<tree, va_gc> *);
 extern tree search_anon_aggr (tree, tree, bool = false);
diff --git a/gcc/cp/parser.c b/gcc/cp/parser.c
index 821ce1771a4..8fcd9840ea5 100644
--- a/gcc/cp/parser.c
+++ b/gcc/cp/parser.c
@@ -14462,20 +14462,15 @@ cp_parser_declaration (cp_parser* parser, tree prefix_attrs)
 	       || token1->keyword == RID_STATIC
 	       || token1->keyword == RID_INLINE))
     cp_parser_explicit_instantiation (parser);
-  /* If the next token is `namespace', check for a named or unnamed
-     namespace definition.  */
-  else if (token1->keyword == RID_NAMESPACE
-	   && (/* A named namespace definition.  */
-	       (token2->type == CPP_NAME
-		&& (cp_lexer_peek_nth_token (parser->lexer, 3)->type
-		    != CPP_EQ))
-               || (token2->type == CPP_OPEN_SQUARE
-                   && cp_lexer_peek_nth_token (parser->lexer, 3)->type
-                   == CPP_OPEN_SQUARE)
-	       /* An unnamed namespace definition.  */
-	       || token2->type == CPP_OPEN_BRACE
-	       || token2->keyword == RID_ATTRIBUTE))
-    cp_parser_namespace_definition (parser);
+  /* If the next token is `namespace', we have either a namespace alias
+     definition or a namespace definition.  */
+  else if (token1->keyword == RID_NAMESPACE)
+    {
+      cp_parser_parse_tentatively (parser);
+      cp_parser_namespace_alias_definition (parser);
+      if (!cp_parser_parse_definitely (parser))
+	cp_parser_namespace_definition (parser);
+    }
   /* An inline (associated) namespace definition.  */
   else if (token2->keyword == RID_NAMESPACE
 	   && token1->keyword == RID_INLINE)
@@ -20860,15 +20855,25 @@ cp_parser_namespace_alias_definition (cp_parser* parser)
 {
   tree identifier;
   tree namespace_specifier;
+  const location_t start = input_location;
 
   cp_token *token = cp_lexer_peek_token (parser->lexer);
 
   /* Look for the `namespace' keyword.  */
   cp_parser_require_keyword (parser, RID_NAMESPACE, RT_NAMESPACE);
+  /* Look for attributes (GCC extension).  */
+  tree attributes = cp_parser_attributes_opt (parser);
+  if (attributes
+	&& !cp_parser_uncommitted_to_tentative_parse_p (parser))
+    pedwarn (input_location, OPT_Wpedantic,
+	     "standard attributes on namespaces aliases must follow "
+	     "the namespace alias name");
   /* Look for the identifier.  */
   identifier = cp_parser_identifier (parser);
   if (identifier == error_mark_node)
     return;
+  /* Look for more attributes (GCC extension).  */
+  attributes = attr_chainon (attributes, cp_parser_attributes_opt (parser));
   /* Look for the `=' token.  */
   if (!cp_parser_uncommitted_to_tentative_parse_p (parser)
       && cp_lexer_next_token_is (parser->lexer, CPP_OPEN_BRACE))
@@ -20880,7 +20885,11 @@ cp_parser_namespace_alias_definition (cp_parser* parser)
 	cp_lexer_consume_token (parser->lexer);
       return;
     }
-  cp_parser_require (parser, CPP_EQ, RT_EQ);
+  if (nullptr == cp_parser_require (parser, CPP_EQ, RT_EQ))
+    {
+      input_location = start;
+      return;
+    }
   /* Look for the qualified-namespace-specifier.  */
   namespace_specifier
     = cp_parser_qualified_namespace_specifier (parser);
@@ -20889,7 +20898,7 @@ cp_parser_namespace_alias_definition (cp_parser* parser)
   cp_parser_require (parser, CPP_SEMICOLON, RT_SEMICOLON);
 
   /* Register the alias in the symbol table.  */
-  do_namespace_alias (identifier, namespace_specifier);
+  do_namespace_alias (identifier, namespace_specifier, attributes);
 }
 
 /* Parse a qualified-namespace-specifier.
diff --git a/gcc/cp/tree.c b/gcc/cp/tree.c
index 2a14fa92ddb..d62e43bff06 100644
--- a/gcc/cp/tree.c
+++ b/gcc/cp/tree.c
@@ -46,6 +46,7 @@ static tree verify_stmt_tree_r (tree *, int *, void *);
 
 static tree handle_init_priority_attribute (tree *, tree, tree, int, bool *);
 static tree handle_abi_tag_attribute (tree *, tree, tree, int, bool *);
+static tree handle_diagnose_as_attribute (tree *, tree, tree, int, bool *);
 
 /* If REF is an lvalue, returns the kind of lvalue that REF is.
    Otherwise, returns clk_none.  */
@@ -4877,6 +4878,8 @@ const struct attribute_spec cxx_attribute_table[] =
     handle_init_priority_attribute, NULL },
   { "abi_tag", 1, -1, false, false, false, true,
     handle_abi_tag_attribute, NULL },
+  { "diagnose_as", 0, -1, false, false, false, false,
+    handle_diagnose_as_attribute, NULL },
   { NULL, 0, 0, false, false, false, false, NULL, NULL }
 };
 
@@ -5145,6 +5148,151 @@ handle_abi_tag_attribute (tree* node, tree name, tree args,
   return NULL_TREE;
 }
 
+static bool
+check_diagnose_as_redeclaration (const_tree decl, const_tree name,
+				 const_tree old, const_tree new_)
+{
+  if (!old)
+    return true;
+  if (TREE_CODE (TREE_VALUE (old)) == TREE_LIST)
+    old = TREE_VALUE (old);
+  if (TREE_CODE (TREE_VALUE (new_)) == TREE_LIST)
+    new_ = TREE_VALUE (new_);
+  tree old_value = TREE_VALUE (old);
+  tree new_value = TREE_VALUE (new_);
+  if (cp_tree_equal (old_value, new_value))
+    return true;
+  warning (OPT_Wattributes, "%qD redeclared with %<%D(%E)%> "
+			    "attribute", decl, name, new_value);
+  inform (DECL_SOURCE_LOCATION (decl), "previous declaration here");
+  return false;
+}
+
+static tree
+handle_diagnose_as_attribute (tree* node, tree name, tree args,
+			      int flags, bool* no_add_attrs)
+{
+  tree decl = NULL_TREE;
+  const bool is_alias = (TREE_CODE (*node) == TYPE_DECL);
+  if (args && list_length (args) == 2
+	&& TREE_PURPOSE (TREE_CHAIN (args)) == integer_zero_node)
+    /* We're called from handle_diagnose_as_attribute with additional context
+       argument.  */;
+  else if (is_alias)
+    {
+      if (args && (TREE_CHAIN (args)
+		     || TREE_CODE (TREE_VALUE (args)) != STRING_CST))
+	{
+	  warning (OPT_Wattributes,
+		   "%qD attribute requires 0 or 1 NTBS arguments", name);
+	  goto fail;
+	}
+    }
+  else if (!args || TREE_CHAIN (args)
+	     || TREE_CODE (TREE_VALUE (args)) != STRING_CST)
+    {
+      warning (OPT_Wattributes,
+	       "%qD attribute requires a single NTBS argument", name);
+      goto fail;
+    }
+
+  if (is_alias && CLASS_TYPE_P (TREE_TYPE (*node)))
+    { // Apply the attribute to the type alias itself.
+      decl = *node;
+
+      /* Reject alias templates without wildcards on the innermost template args
+         of the RHS type. E.g. template <class> using A = B;  */
+      if (DECL_LANG_SPECIFIC (decl)
+	    && DECL_TEMPLATE_INFO (decl)
+	    && DECL_ALIAS_TEMPLATE_P (DECL_TI_TEMPLATE (decl))
+	    && PRIMARY_TEMPLATE_P (DECL_TI_TEMPLATE (decl)))
+	return error_mark_node;
+
+      if (!args)
+	{ // turn alias identifier into attribute argument
+	  tree alias_name = DECL_NAME (decl);
+	  tree str = fix_string_type (
+		       build_string(IDENTIFIER_LENGTH (alias_name) + 1,
+				    IDENTIFIER_POINTER (alias_name)));
+	  args = build_tree_list (NULL_TREE, str);
+	}
+      // apply the attribute to the specialization on the RHS.
+      tree type = strip_typedefs (TREE_TYPE (*node), nullptr, 0);
+
+      if (is_alias_template_p (decl))
+	{
+	  type = CLASSTYPE_PRIMARY_TEMPLATE_TYPE (type);
+	  // Warn and skip if the template arguments don't match up like in
+	  //   template <class T> using A = B<int, T>;
+	  if (!same_type_p (TREE_TYPE (decl), type))
+	    return error_mark_node;
+	}
+
+      // Add the DECL_CONTEXT of the alias to the attribute if it is different
+      // to the context of the type.
+      if (!TREE_CHAIN (args)
+	    && CP_DECL_CONTEXT (decl) != CP_TYPE_CONTEXT (type))
+	{
+	  TREE_CHAIN (args) = build_tree_list (integer_zero_node,
+					       CP_DECL_CONTEXT (decl));
+	}
+
+      if (COMPLETE_TYPE_P (type))
+	{
+	  tree rhs = handle_diagnose_as_attribute (
+			 &type, name, args, flags, no_add_attrs);
+	  if (rhs == error_mark_node || *no_add_attrs)
+	    return rhs;
+	  else
+	    TYPE_ATTRIBUTES (type)
+	      = tree_cons (name, args, TYPE_ATTRIBUTES (type));
+	}
+      else
+	{
+	  tree attributes = tree_cons (name, args, NULL_TREE);
+	  cplus_decl_attributes (&type, attributes, ATTR_FLAG_TYPE_IN_PLACE);
+	}
+    }
+  else if (TYPE_P (*node))
+    {
+      if (!OVERLOAD_TYPE_P (*node))
+	return error_mark_node;
+      decl = TYPE_NAME (*node);
+    }
+  else
+    {
+      if (!VAR_OR_FUNCTION_DECL_P (*node)
+	    || DECL_LANGUAGE (*node) != lang_cplusplus)
+	return error_mark_node;
+      decl = *node;
+    }
+
+  // Make sure all declarations have the same diagnose_as string.
+  if (DECL_SOURCE_LOCATION (decl) != input_location)
+    {
+      tree attributes = TYPE_P (*node) ? TYPE_ATTRIBUTES (*node)
+				       : DECL_ATTRIBUTES (decl);
+      if (!check_diagnose_as_redeclaration (
+	     decl, name, lookup_attribute ("diagnose_as", attributes), args))
+	goto fail;
+    }
+  else if (CLASS_TYPE_P (*node) && CLASSTYPE_IMPLICIT_INSTANTIATION (*node))
+    {
+      // The above branch (different source location) is taken for declarations
+      // of type aliases that modify an implicit template specialization on the
+      // RHS. This branch is taken when the template is instantiated via
+      // instantiate_class_template_1, in which case the attribute either
+      // already has the value from the general template or from a type alias.
+      goto fail;
+    }
+
+  return NULL_TREE;
+
+ fail:
+  *no_add_attrs = true;
+  return NULL_TREE;
+}
+
 /* Return a new PTRMEM_CST of the indicated TYPE.  The MEMBER is the
    thing pointed to by the constant.  */
 
diff --git a/gcc/doc/extend.texi b/gcc/doc/extend.texi
index b83cd4919bb..96640ba156f 100644
--- a/gcc/doc/extend.texi
+++ b/gcc/doc/extend.texi
@@ -2865,6 +2865,51 @@ types (@pxref{Variable Attributes}, @pxref{Type Attributes}.)
 The message attached to the attribute is affected by the setting of
 the @option{-fmessage-length} option.
 
+@item diagnose_as ("@var{string}")
+@itemx diagnose_as
+@cindex @code{diagnose_as} function attribute
+The @code{diagnose_as} attribute modifies how the entity the attribute
+appertains to is diagnosed in compiler messages and @code{__func__},
+@code{__FUNCTION__}, and @code{__PRETTY_FUNCTION__} macros. (The result of
+@code{typeid} is not affected.) If the attribute is applied to a
+@code{namespace}, the specified string replaces the complete enclosing scope.
+The effect of the @code{diagnose_as} attribute can be disabled with the
+@option{-fno-diagnostics-use-aliases} command line option.
+
+The argument @var{string} is optional for type and namespace aliases. If it is
+omitted, the identifier of the type or namespace alias is unconditionally used
+in all places where the type / namespace on the right-hand side is diagnosed.
+
+@smallexample
+namespace Foo @{
+  namespace Bar @{
+    inline namespace v1 [[gnu::diagnose_as("Foobar")]] @{
+      int f() @{
+        // __PRETTY_FUNCTION__ == "void Foobar::f()"
+      @}
+    @}
+  @}
+@}
+// In function 'int Foobar::f()':
+// warning: no return statement in function returning non-void [-Wreturn-type]
+@end smallexample
+
+The @code{diagnose_as} attribute can be used with namespaces, namespace aliases,
+functions, variables, alias declarations (but not alias templates), and
+user-defined types (classes, unions, and enums). If the alias declaration
+aliases a class type (including template specializations), the attribute is
+additionally applied to the class type. Whether the attribute has an effect on
+partial template specializations is unspecified and may depend on several
+different factors.
+
+@smallexample
+template <class T> struct basic_string @{@};
+using string [[gnu::diagnose_as]] = basic_string<char>;
+int f(basic_string<char>) @{@}
+// In function 'int f(string)':
+// warning: no return statement in function returning non-void [-Wreturn-type]
+@end smallexample
+
 @item error ("@var{message}")
 @itemx warning ("@var{message}")
 @cindex @code{error} function attribute
diff --git a/gcc/doc/invoke.texi b/gcc/doc/invoke.texi
index ea8812425e9..c2d49ffab22 100644
--- a/gcc/doc/invoke.texi
+++ b/gcc/doc/invoke.texi
@@ -311,7 +311,8 @@ Objective-C and Objective-C++ Dialects}.
 -fdiagnostics-show-path-depths @gol
 -fno-show-column @gol
 -fdiagnostics-column-unit=@r{[}display@r{|}byte@r{]} @gol
--fdiagnostics-column-origin=@var{origin}}
+-fdiagnostics-column-origin=@var{origin} @gol
+-fno-diagnostics-aliases}
 
 @item Warning Options
 @xref{Warning Options,,Options to Request or Suppress Warnings}.
@@ -5119,6 +5120,12 @@ first column.  The default value of 1 corresponds to traditional GCC
 behavior and to the GNU style guide.  Some utilities may perform better with an
 origin of 0; any non-negative value may be specified.
 
+@item -fno-diagnostics-use-aliases
+@opindex fno-diagnostics-use-aliases
+@opindex fdiagnostics-use-aliases
+Do not replace identifiers or scope names with the aliases defined with the
+@code{[[gnu::diagnose_as("alias")]]} attribute.
+
 @item -fdiagnostics-format=@var{FORMAT}
 @opindex fdiagnostics-format
 Select a different format for printing diagnostics.
diff --git a/gcc/testsuite/g++.dg/diagnostic/diagnose-as1.C b/gcc/testsuite/g++.dg/diagnostic/diagnose-as1.C
new file mode 100644
index 00000000000..44ff3825e95
--- /dev/null
+++ b/gcc/testsuite/g++.dg/diagnostic/diagnose-as1.C
@@ -0,0 +1,213 @@
+// { dg-options "-fdiagnostics-use-aliases -fpretty-templates" }
+
+#ifdef __cpp_constexpr
+template <typename T>
+  constexpr bool is_char() { return false; }
+
+template <>
+  constexpr bool is_char<char>() { return true; }
+
+template <typename T>
+  constexpr bool is_int() { return false; }
+
+template <>
+  constexpr bool is_int<int>() { return true; }
+#endif
+
+namespace __attribute__((diagnose_as())) foo0 {} // { dg-warning "'diagnose_as' attribute requires a single NTBS argument" }
+
+namespace __attribute__((diagnose_as("foo2"))) foo1 {} // { dg-line foo1 }
+namespace __attribute__((diagnose_as("XXXX"))) foo1 {} // { dg-warning "'foo1' redeclared with different 'diagnose_as' attribute value" }
+// { dg-message "previous declaration here" "" { target *-*-* } foo1 }
+namespace foo1
+{
+  void f() {
+    f(1); // { dg-error "too many arguments to function 'void foo2::f\\(\\)'" }
+  }
+
+  class __attribute__((diagnose_as("XX"))) X; // { dg-line XX }
+  class __attribute__((diagnose_as("--"))) X { // { dg-warning "'class foo2::XX' redeclared with 'diagnose_as\\(\"--\"\\)' attribute" }
+    // { dg-message "previous declaration here" "" { target *-*-* } XX }
+    __attribute__((diagnose_as("ff"))) void f();
+  };
+  class Y : X
+  {
+    void g() {
+      f(); // { dg-error "'void foo2::XX::ff\\(\\)' is private within this context" }
+    }
+  };
+
+  class __attribute__((diagnose_as())) Z0; // { dg-warning "'diagnose_as' attribute requires a single NTBS argument" }
+  class __attribute__((diagnose_as(1))) Z1; // { dg-warning "'diagnose_as' attribute requires a single NTBS argument" }
+  class __attribute__((diagnose_as("a", "b"))) Z2; // { dg-warning "'diagnose_as' attribute requires a single NTBS argument" }
+
+  template <typename T> class A0 {};
+  typedef A0<char> Achar __attribute__((diagnose_as("Achar")));
+  template class A0<int>;
+  typedef A0<int> Aint __attribute__((diagnose_as("Aint"))); // OK
+  typedef A0<int> Aint2 __attribute__((diagnose_as("Aint2"))); // { dg-warning "'class foo2::Aint' redeclared with 'diagnose_as\\(\"Aint2\"\\)' attribute" }
+  typedef A0<int> Aint3 __attribute__((diagnose_as(1))); // { dg-warning "'diagnose_as' attribute requires 0 or 1 NTBS arguments" }
+
+#if __cplusplus >= 201103L
+  template <typename T> using foo [[gnu::diagnose_as]] = A0<T>;
+  template <typename T> using aRef [[gnu::diagnose_as]] = A0<const T&>; // { dg-warning "'diagnose_as' attribute ignored" "" { target c++11 } }
+#endif
+}
+
+namespace A
+{
+  inline namespace __attribute__((diagnose_as("@1"))) B
+  {
+    template <typename U>
+      struct __attribute__((diagnose_as("@2"))) C
+      {
+	__attribute__((diagnose_as("fun:1")))
+	void f() {} // { dg-line ABC_f }
+
+	template <typename T>
+	  __attribute__((diagnose_as("fun:2")))
+	  void g(T, U) {} // { dg-line ABC_gT }
+
+	template <typename T>
+	  __attribute__((diagnose_as("fun:2.2")))
+	  void g() {} // { dg-line ABC_g }
+
+	__attribute__((diagnose_as("fun:3")))
+	void h()
+	{
+#ifdef __cpp_constexpr
+	  static_assert(__builtin_strcmp(__FUNCTION__, "fun:3") == 0, "");
+	  static_assert(__builtin_strcmp(__func__, "fun:3") == 0, "");
+	  constexpr const char* ref
+	    = is_int<U>() ? "void @1::@3::fun:3()"
+			  : "void @1::@2<U>::fun:3() [with U = char]";
+	  static_assert(__builtin_strcmp(__PRETTY_FUNCTION__, ref) == 0, "");
+#endif
+	}
+
+	template <typename T>
+	  __attribute__((diagnose_as("fun:4")))
+	  void ht()
+	{
+#ifdef __cpp_constexpr
+	  static_assert(__builtin_strcmp(__FUNCTION__, "fun:4") == 0, "");
+	  constexpr const char* ref
+	    = is_int<U>()
+		? "void @1::@3::fun:4() [with T = float]"
+		: "void @1::@2<U>::fun:4() [with T = float; U = char]";
+	  static_assert(__builtin_strcmp(__PRETTY_FUNCTION__, ref) == 0, "");
+#endif
+	}
+
+	template <typename T0, typename T1>
+	  struct __attribute__((diagnose_as("@5"))) E
+	  {
+	    __attribute__((diagnose_as("fun:5"))) static void f() {
+#ifdef __cpp_constexpr
+	      static_assert(__builtin_strcmp(__FUNCTION__, "fun:5") == 0, "");
+	      constexpr const char* ref = is_int<U>()
+		? is_char<T0>()
+		  ? "static void @1::@3::@6::fun:5()"
+		  : "static void @1::@3::@5<T0, T1>::fun:5() [with T0 = float; T1 = short int]"
+		: is_char<T0>()
+		  ? "static void @1::@2<U>::@6::fun:5() [with U = char]"
+		  : "static void @1::@2<U>::@5<T0, T1>::fun:5() [with T0 = float; T1 = short int; U = char]";
+	      static_assert(__builtin_strcmp(__PRETTY_FUNCTION__, ref) == 0,
+			    "");
+#endif
+	    }
+	  };
+	typedef E<char, char> F __attribute__((diagnose_as("@6")));
+
+	template <typename T>
+	  struct __attribute__((diagnose_as("@7"))) E<T, long>
+	  {
+	    __attribute__((diagnose_as("fun:6"))) static void f() {
+#ifdef __cpp_constexpr
+	      static_assert(__builtin_strcmp(__FUNCTION__, "fun:6") == 0, "");
+	      constexpr const char* ref = is_int<U>()
+		  ? "static void @1::@3::@7<T, long int>::fun:6() [with T = float]"
+		  : "static void @1::@2<U>::@7<T, long int>::fun:6() [with T = float; U = char]";
+	      static_assert(__builtin_strcmp(__PRETTY_FUNCTION__, ref) == 0,
+			    "");
+#endif
+	    }
+	  };
+      };
+
+    typedef C<int> D __attribute__((diagnose_as("@3")));
+
+    template <>
+      struct __attribute__((diagnose_as("@4"))) C<float>
+      {
+      };
+
+    typedef C<short> E __attribute__((diagnose_as));
+  }
+}
+
+namespace frob
+{
+  namespace ni
+  {
+    struct kate {};
+    namespace cate
+    {
+      struct me {};
+    }
+  }
+}
+
+namespace frobni __attribute__((diagnose_as("twiggle"))) = frob::ni;
+namespace frobnicate __attribute__((diagnose_as)) = frob::ni::cate;
+namespace wrong __attribute__((diagnose_as(1))) = frob::ni; // { dg-warning "'diagnose_as' attribute requires 0 or 1 NTBS arguments" }
+namespace wrong2 __attribute__((diagnose_as("wrong", 1))) = frob::ni; // { dg-warning "'diagnose_as' attribute requires 0 or 1 NTBS arguments" }
+
+void fn_1(int);
+void fn_2(A::D);
+
+int main ()
+{
+  fn_2 (A::D ());
+  fn_1 (A::D ()); // { dg-error "cannot convert '@1::@3' to 'int'" }
+  fn_1 (A::C<int> ()); // { dg-error "cannot convert '@1::@3' to 'int'" }
+  fn_1 (A::C<char> ()); // { dg-error "cannot convert '@1::@2<char>' to 'int'" }
+  fn_1 (A::C<float> ()); // { dg-error "cannot convert '@1::@4' to 'int'" }
+  fn_1 (A::C<short> ()); // { dg-error "cannot convert '@1::E' to 'int'" }
+  fn_1 (A::E ()); // { dg-error "cannot convert '@1::E' to 'int'" }
+  fn_1 (foo1::A0<int> ()); // { dg-error "cannot convert 'foo2::Aint' to 'int'" }
+  fn_1 (foo1::Aint2 ()); // { dg-error "cannot convert 'foo2::Aint2' {aka 'foo2::Aint'} to 'int'" }
+#if __cplusplus >= 201103L
+  fn_1 (foo1::foo<float> ()); // { dg-error "cannot convert 'foo2::foo<float>' to 'int'" "" { target c++11 } }
+  fn_1 (foo1::aRef<float> ()); // { dg-error "cannot convert 'foo2::aRef<float>' {aka 'foo2::foo<const float&>'} to 'int'" "" { target c++11 } }
+  fn_1 (frob::ni::kate ()); // { dg-error "cannot convert 'twiggle::kate' to 'int'" "" { target c++11 } }
+  fn_1 (frob::ni::cate::me ()); // { dg-error "cannot convert 'frobnicate::me' to 'int'" "" { target c++11 } }
+#endif
+
+  A::C<int>().f(0); // { dg-error "no matching function for call to '@1::@3::f\\(int\\)'" }
+  // { dg-message "candidate: 'void @1::@3::fun:1\\(\\)'" "" { target *-*-* } ABC_f }
+
+  A::C<char>().f(0); // { dg-error "no matching function for call to '@1::@2<char>::f\\(int\\)'" }
+  // { dg-message "candidate: 'void @1::@2<U>::fun:1\\(\\) \\\[with U = char\\\]'" "" { target *-*-* } ABC_f }
+
+  A::C<float>().f(0); // { dg-error "'struct @1::@4' has no member named 'f'" }
+
+  A::C<int>().g(); // { dg-error "no matching function for call to '@1::@3::g\\(\\)'" }
+  // { dg-message "candidate: 'template<class T> void @1::@3::fun:2\\(T, U\\)'" "" { target *-*-* } ABC_gT }
+  // { dg-message "candidate: 'template<class T> void @1::@3::fun:2.2\\(\\)'" "" { target *-*-* } ABC_g }
+
+  A::C<char>().g(); // { dg-error "no matching function for call to '@1::@2<char>::g\\(\\)'" }
+  // { dg-message "candidate: 'template<class T> void @1::@2<U>::fun:2\\(T, U\\) \\\[with U = char\\\]'" "" { target *-*-* } ABC_gT }
+  // { dg-message "candidate: 'template<class T> void @1::@2<U>::fun:2.2\\(\\) \\\[with U = char\\\]'" "" { target *-*-* } ABC_g }
+
+  A::C<int>().h();
+  A::C<char>().h();
+  A::C<int>().ht<float>();
+  A::C<char>().ht<float>();
+  A::C<int>::E<float, short>::f();
+  A::C<char>::E<float, short>::f();
+  A::C<int>::E<float, long>::f();
+  A::C<char>::E<float, long>::f();
+  A::C<int>::F::f();
+  A::C<char>::F::f();
+}
diff --git a/gcc/testsuite/g++.dg/diagnostic/diagnose-as2.C b/gcc/testsuite/g++.dg/diagnostic/diagnose-as2.C
new file mode 100644
index 00000000000..b1d46d12024
--- /dev/null
+++ b/gcc/testsuite/g++.dg/diagnostic/diagnose-as2.C
@@ -0,0 +1,144 @@
+// { dg-options "-fdiagnostics-use-aliases -fno-pretty-templates" }
+
+#ifdef __cpp_constexpr
+template <typename T>
+  constexpr bool is_char() { return false; }
+
+template <>
+  constexpr bool is_char<char>() { return true; }
+
+template <typename T>
+  constexpr bool is_int() { return false; }
+
+template <>
+  constexpr bool is_int<int>() { return true; }
+#endif
+
+namespace A
+{
+  inline namespace B __attribute__((diagnose_as("@1")))
+  {
+    template <typename U>
+      struct __attribute__((diagnose_as("@2"))) C
+      {
+	__attribute__((diagnose_as("fun:1")))
+	void f() {} // { dg-line ABC_f }
+
+	template <typename T>
+	  __attribute__((diagnose_as("fun:2")))
+	  void g(T, U) {} // { dg-line ABC_gT }
+
+	template <typename T>
+	  __attribute__((diagnose_as("fun:2.2")))
+	  void g() {} // { dg-line ABC_g }
+
+	__attribute__((diagnose_as("fun:3")))
+	void h()
+	{
+#ifdef __cpp_constexpr
+	  static_assert(__builtin_strcmp(__FUNCTION__, "fun:3") == 0, "");
+	  constexpr const char* ref
+	    = is_int<U>() ? "void @1::@3::fun:3()"
+			  : "void @1::@2<char>::fun:3()";
+	  static_assert(__builtin_strcmp(__PRETTY_FUNCTION__, ref) == 0, "");
+#endif
+	}
+
+	template <typename T>
+	  __attribute__((diagnose_as("fun:4")))
+	  void ht()
+	{
+#ifdef __cpp_constexpr
+	  static_assert(__builtin_strcmp(__FUNCTION__, "fun:4") == 0, "");
+	  constexpr const char* ref
+	    = is_int<U>() ? "void @1::@3::fun:4<float>()"
+			  : "void @1::@2<char>::fun:4<float>()";
+	  static_assert(__builtin_strcmp(__PRETTY_FUNCTION__, ref) == 0, "");
+#endif
+	}
+
+	template <typename T0, typename T1>
+	  struct __attribute__((diagnose_as("@5"))) E
+	  {
+	    __attribute__((diagnose_as("fun:5"))) static void f() {
+#ifdef __cpp_constexpr
+	      static_assert(__builtin_strcmp(__FUNCTION__, "fun:5") == 0, "");
+	      constexpr const char* ref = is_int<U>()
+		? is_char<T0>()
+		  ? "static void @1::@3::@6::fun:5()"
+		  : "static void @1::@3::@5<float, short int>::fun:5()"
+		: is_char<T0>()
+		  ? "static void @1::@2<char>::@6::fun:5()"
+		  : "static void @1::@2<char>::@5<float, short int>::fun:5()";
+	      static_assert(__builtin_strcmp(__PRETTY_FUNCTION__, ref) == 0,
+			    "");
+#endif
+	    }
+	  };
+	typedef E<char, char> F __attribute__((diagnose_as("@6")));
+
+	template <typename T>
+	  struct __attribute__((diagnose_as("@7"))) E<T, long>
+	  {
+	    __attribute__((diagnose_as("fun:6"))) static void f() {
+#ifdef __cpp_constexpr
+	      static_assert(__builtin_strcmp(__FUNCTION__, "fun:6") == 0, "");
+	      // Note that @7 is ignored with -fno-pretty-templates. After all
+	      // diagnose_as on partial specializations is not supported.
+	      constexpr const char* ref = is_int<U>()
+		  ? "static void @1::@3::@5<float, long int>::fun:6()"
+		  : "static void @1::@2<char>::@5<float, long int>::fun:6()";
+	      static_assert(__builtin_strcmp(__PRETTY_FUNCTION__, ref) == 0,
+			    "");
+#endif
+	    }
+	  };
+      };
+
+    typedef C<int> D __attribute__((diagnose_as("@3")));
+
+    template <>
+      struct __attribute__((diagnose_as("@4"))) C<float>
+      {
+      };
+  }
+}
+
+void fn_1(int);
+void fn_2(A::D);
+
+int main ()
+{
+  fn_2 (A::D ());
+  fn_1 (A::D ()); // { dg-error "cannot convert '@1::@3' to 'int'" }
+  fn_1 (A::C<int> ()); // { dg-error "cannot convert '@1::@3' to 'int'" }
+  fn_1 (A::C<char> ()); // { dg-error "cannot convert '@1::@2<char>' to 'int'" }
+  fn_1 (A::C<float> ()); // { dg-error "cannot convert '@1::@4' to 'int'" }
+
+  A::C<int>().f(0); // { dg-error "no matching function for call to '@1::@3::f\\(int\\)'" }
+  // { dg-message "candidate: 'void @1::@3::fun:1\\(\\)'" "" { target *-*-* } ABC_f }
+
+  A::C<char>().f(0); // { dg-error "no matching function for call to '@1::@2<char>::f\\(int\\)'" }
+  // { dg-message "candidate: 'void @1::@2<char>::fun:1\\(\\)'" "" { target *-*-* } ABC_f }
+
+  A::C<float>().f(0); // { dg-error "'struct @1::@4' has no member named 'f'" }
+
+  A::C<int>().g(); // { dg-error "no matching function for call to '@1::@3::g\\(\\)'" }
+  // { dg-message "candidate: 'template<class T> void @1::@3::fun:2\\(T, int\\)'" "" { target *-*-* } ABC_gT }
+  // { dg-message "candidate: 'template<class T> void @1::@3::fun:2.2\\(\\)'" "" { target *-*-* } ABC_g }
+
+  A::C<char>().g(); // { dg-error "no matching function for call to '@1::@2<char>::g\\(\\)'" }
+  // { dg-message "candidate: 'template<class T> void @1::@2<char>::fun:2\\(T, char\\)'" "" { target *-*-* } ABC_gT }
+  // { dg-message "candidate: 'template<class T> void @1::@2<char>::fun:2.2\\(\\)'" "" { target *-*-* } ABC_g }
+
+  A::C<int>().h();
+  A::C<char>().h();
+  A::C<int>().ht<float>();
+  A::C<char>().ht<float>();
+  A::C<int>::E<float, short>::f();
+  A::C<char>::E<float, short>::f();
+  A::C<int>::E<float, long>::f();
+  A::C<char>::E<float, long>::f();
+  A::C<int>::F::f();
+  A::C<char>::F::f();
+}
diff --git a/gcc/testsuite/g++.dg/diagnostic/diagnose-as3.C b/gcc/testsuite/g++.dg/diagnostic/diagnose-as3.C
new file mode 100644
index 00000000000..b0e1c064886
--- /dev/null
+++ b/gcc/testsuite/g++.dg/diagnostic/diagnose-as3.C
@@ -0,0 +1,152 @@
+// { dg-options "-fdiagnostics-use-aliases -fpretty-templates" }
+// { dg-do compile { target c++11 } }
+
+const char* volatile s;
+
+namespace foo
+{
+  template <class U>
+    struct [[gnu::diagnose_as("Bar'")]] Bar
+    {
+      template <class T0, class T1>
+        struct [[gnu::diagnose_as("A'")]] A
+        { constexpr const char* f() { return __PRETTY_FUNCTION__; } };
+
+      template <class T>
+        struct A<T, float>
+        { constexpr const char* f() { return __PRETTY_FUNCTION__; } };
+
+      template <class T>
+        struct [[gnu::diagnose_as("Adouble")]] A<T, double>
+        { constexpr const char* f() { return __PRETTY_FUNCTION__; } };
+
+      using Special [[gnu::diagnose_as("SpecialA")]] = A<int, int>;
+
+      static constexpr const char* f() { return __PRETTY_FUNCTION__; }
+    };
+
+  template <class P>
+    struct
+    [[gnu::diagnose_as("BarPtr")]]
+    Bar<P*>
+    {
+      template <class U = int>
+      static constexpr const char* f() { return __PRETTY_FUNCTION__; }
+    };
+
+  template <>
+    struct [[gnu::diagnose_as("SpecialBar")]] Bar<void>
+    {
+      static constexpr const char* f() { return __PRETTY_FUNCTION__; }
+    };
+
+  using barchar [[gnu::diagnose_as("barchar")]] = Bar<char>;
+}
+
+namespace A
+{
+  inline namespace B __attribute__((diagnose_as("@1")))
+  {
+    template  <typename U>
+      struct __attribute__((diagnose_as("@2"))) C
+      {
+        template <class T>
+        __attribute__((diagnose_as("fun:2")))
+          constexpr const char* g() { return __PRETTY_FUNCTION__; }
+
+        __attribute__((diagnose_as("fun:3")))
+          constexpr const char* h()
+          { return __PRETTY_FUNCTION__; }
+      };
+
+    typedef C<int> D __attribute__((diagnose_as("@3")));
+  }
+}
+
+template <class T0, class U0>
+struct [[gnu::diagnose_as("X.0")]] X0
+{
+  template <class T1, class U1>
+    struct X1
+    {
+      template <class T2, class U2>
+        struct X2
+        {
+          template <class T3, class U3>
+            [[gnu::diagnose_as("f-1")]]
+            static constexpr const char* f() {
+              return __PRETTY_FUNCTION__;
+            }
+        };
+
+      using XX [[gnu::diagnose_as("X2'")]] = X2<int, long>;
+    };
+
+  template <class T3, class U3>
+    [[gnu::diagnose_as("f-1")]]
+    static constexpr const char* f() {
+      return __PRETTY_FUNCTION__;
+    }
+
+  struct [[gnu::diagnose_as("X.3")]] X3
+  {
+    template <class T3, class U3>
+      [[gnu::diagnose_as("f-1")]]
+      static constexpr const char* f() {
+        return __PRETTY_FUNCTION__;
+      }
+  };
+};
+
+using X0int [[gnu::diagnose_as("[int|int]")]] = X0<int, int>;
+
+#define TEST(expected, fun)                                                    \
+  static_assert(__builtin_strcmp(expected, fun) == 0, "")
+
+
+void h() {
+  TEST("constexpr const char* @1::@3::fun:3()",
+      A::C<int>().h());
+  TEST("constexpr const char* @1::@3::fun:2() [with T = float]",
+      A::C<int>().g<float>());
+  TEST("constexpr const char* @1::@2<U>::fun:3() [with U = char]",
+      A::C<char>().h());
+  TEST("constexpr const char* @1::@2<U>::fun:2() [with T = float; U = char]",
+      A::C<char>().g<float>());
+  TEST("constexpr const char* foo::barchar::A'<T0, T1>::f() [with T0 = char; T1 = char]",
+      (foo::Bar<char>::A<char, char>().f()));
+  TEST("constexpr const char* foo::barchar::A'<T, float>::f() [with T = char]",
+      (foo::Bar<char>::A<char, float>().f()));
+  TEST("constexpr const char* foo::barchar::Adouble<T, double>::f() [with T = int]",
+      (foo::Bar<char>::A<int, double>().f()));
+  TEST("constexpr const char* foo::barchar::SpecialA::f()",
+      (foo::Bar<char>::A<int, int>().f()));
+  TEST("constexpr const char* foo::Bar'<U>::A'<T0, T1>::f() [with T0 = char; T1 = char; U = float]",
+      (foo::Bar<float>::A<char, char>().f()));
+  TEST("constexpr const char* foo::Bar'<U>::A'<T, float>::f() [with T = char; U = float]",
+      (foo::Bar<float>::A<char, float>().f()));
+  TEST("constexpr const char* foo::Bar'<U>::Adouble<T, double>::f() [with T = char; U = float]",
+      (foo::Bar<float>::A<char, double>().f()));
+  TEST("constexpr const char* foo::Bar'<U>::SpecialA::f() [with U = float]",
+      (foo::Bar<float>::A<int, int>().f()));
+  TEST("static constexpr const char* foo::barchar::f()",
+      foo::Bar<char>::f());
+  TEST("static constexpr const char* foo::BarPtr<P*>::f() [with U = int; P = char]",
+      foo::Bar<char*>::f());
+  TEST("static constexpr const char* foo::BarPtr<P*>::f() [with U = int; P = float]",
+      foo::Bar<float*>::f());
+  TEST("static constexpr const char* foo::Bar'<U>::f() [with U = float]",
+      foo::Bar<float>::f());
+  TEST("static constexpr const char* foo::SpecialBar::f()",
+      foo::Bar<void>::f());
+  TEST("static constexpr const char* X.0<T0, U0>::X1<T1, U1>::X2'::f-1() [with T3 = long int; U3 = long long int; T1 = short int; U1 = int; T0 = char; U0 = short int]",
+      (X0<char, short>::X1<short, int>::X2<int, long>::f<long, long long>()));
+  TEST("static constexpr const char* X.0<T0, U0>::X1<T1, U1>::X2<T2, U2>::f-1() [with T3 = long int; U3 = long long int; T2 = long int; U2 = int; T1 = short int; U1 = int; T0 = char; U0 = short int]",
+      (X0<char, short>::X1<short, int>::X2<long, int>::f<long, long long>()));
+  TEST("static constexpr const char* X.0<T0, U0>::X.3::f-1() [with T3 = long int; U3 = long long int; T0 = char; U0 = short int]",
+      (X0<char, short>::X3::f<long, long long>()));
+  TEST("static constexpr const char* [int|int]::f-1() [with T3 = long int; U3 = long long int]",
+      (X0<int, int>::f<long, long long>()));
+  TEST("static constexpr const char* [int|int]::X.3::f-1() [with T3 = long int; U3 = long long int]",
+      (X0<int, int>::X3::f<long, long long>()));
+}
diff --git a/gcc/testsuite/g++.dg/diagnostic/diagnose-as4.C b/gcc/testsuite/g++.dg/diagnostic/diagnose-as4.C
new file mode 100644
index 00000000000..89b800c7b9d
--- /dev/null
+++ b/gcc/testsuite/g++.dg/diagnostic/diagnose-as4.C
@@ -0,0 +1,158 @@
+// { dg-options "-fdiagnostics-use-aliases -fno-pretty-templates" }
+// { dg-do compile { target c++11 } }
+
+const char* volatile s;
+
+namespace foo
+{
+  template <class U>
+    struct [[gnu::diagnose_as("Bar'")]] Bar
+    {
+      template <class T0, class T1>
+        struct [[gnu::diagnose_as("A'")]] A
+        { constexpr const char* f() { return __PRETTY_FUNCTION__; } };
+
+      template <class T>
+        struct A<T, float>
+        { constexpr const char* f() { return __PRETTY_FUNCTION__; } };
+
+      template <class T>
+        struct [[gnu::diagnose_as("Adouble")]] A<T, double>
+        { constexpr const char* f() { return __PRETTY_FUNCTION__; } };
+
+      using Special [[gnu::diagnose_as("SpecialA")]] = A<int, int>;
+
+      static constexpr const char* f() { return __PRETTY_FUNCTION__; }
+    };
+
+  template <class P>
+    struct
+    [[gnu::diagnose_as("BarPtr")]]
+    Bar<P*>
+    {
+      template <class U = int>
+      static constexpr const char* f() { return __PRETTY_FUNCTION__; }
+    };
+
+  template <>
+    struct [[gnu::diagnose_as("SpecialBar")]] Bar<void>
+    {
+      static constexpr const char* f() { return __PRETTY_FUNCTION__; }
+    };
+
+  using barchar [[gnu::diagnose_as("barchar")]] = Bar<char>;
+}
+
+namespace A
+{
+  inline namespace B __attribute__((diagnose_as("@1")))
+  {
+    template  <typename U>
+      struct __attribute__((diagnose_as("@2"))) C
+      {
+        template <class T>
+        __attribute__((diagnose_as("fun:2")))
+          constexpr const char* g() { return __PRETTY_FUNCTION__; }
+
+        __attribute__((diagnose_as("fun:3")))
+          constexpr const char* h()
+          { return __PRETTY_FUNCTION__; }
+      };
+
+    typedef C<int> D __attribute__((diagnose_as("@3")));
+  }
+}
+
+template <class T0, class U0>
+struct [[gnu::diagnose_as("XX0")]] X0
+{
+  template <class T1, class U1>
+    struct X1
+    {
+      template <class T2, class U2>
+        struct X2
+        {
+          template <class T3, class U3>
+            [[gnu::diagnose_as("f-1")]]
+            static constexpr const char* f() {
+              return __PRETTY_FUNCTION__;
+            }
+        };
+
+      using XX [[gnu::diagnose_as("X2'")]] = X2<int, long>;
+    };
+
+  template <class T3, class U3>
+    [[gnu::diagnose_as("f-2")]]
+    static constexpr const char* f() {
+      return __PRETTY_FUNCTION__;
+    }
+
+  struct [[gnu::diagnose_as("XX3")]] X3
+  {
+    template <class T3, class U3>
+      [[gnu::diagnose_as("f-3")]]
+      static constexpr const char* f() { // { dg-line X0_X3_f }
+        return __PRETTY_FUNCTION__;
+      }
+  };
+};
+
+using X0int [[gnu::diagnose_as("X0intint")]] = X0<int, int>;
+
+#define TEST(expected, fun)                                                    \
+  static_assert(__builtin_strcmp(expected, fun) == 0, "")
+
+
+void h() {
+  TEST("constexpr const char* @1::@3::fun:3()",
+      A::C<int>().h());
+  TEST("constexpr const char* @1::@3::fun:2<float>()",
+      A::C<int>().g<float>());
+  TEST("constexpr const char* @1::@2<char>::fun:3()",
+      A::C<char>().h());
+  TEST("constexpr const char* @1::@2<char>::fun:2<float>()",
+      A::C<char>().g<float>());
+  TEST("constexpr const char* foo::barchar::A'<char, char>::f()",
+      (foo::Bar<char>::A<char, char>().f()));
+  TEST("constexpr const char* foo::barchar::A'<char, float>::f()",
+      (foo::Bar<char>::A<char, float>().f()));
+  TEST("constexpr const char* foo::barchar::A'<int, double>::f()",
+      (foo::Bar<char>::A<int, double>().f()));
+  TEST("constexpr const char* foo::barchar::SpecialA::f()",
+      (foo::Bar<char>::A<int, int>().f()));
+  TEST("constexpr const char* foo::Bar'<float>::A'<char, char>::f()",
+      (foo::Bar<float>::A<char, char>().f()));
+  TEST("constexpr const char* foo::Bar'<float>::A'<char, float>::f()",
+      (foo::Bar<float>::A<char, float>().f()));
+  TEST("constexpr const char* foo::Bar'<float>::A'<char, double>::f()",
+      (foo::Bar<float>::A<char, double>().f()));
+  TEST("constexpr const char* foo::Bar'<float>::SpecialA::f()",
+      (foo::Bar<float>::A<int, int>().f()));
+  TEST("static constexpr const char* foo::barchar::f()",
+      foo::Bar<char>::f());
+  TEST("static constexpr const char* foo::Bar'<char*>::f<int>()",
+      foo::Bar<char*>::f());
+  TEST("static constexpr const char* foo::Bar'<float*>::f<int>()",
+      foo::Bar<float*>::f());
+  TEST("static constexpr const char* foo::Bar'<float>::f()",
+      foo::Bar<float>::f());
+  TEST("static constexpr const char* foo::SpecialBar::f()",
+      foo::Bar<void>::f());
+  TEST("static constexpr const char* XX0<char, short int>::X1<short int, int>::X2'::f-1<long int, long long int>()",
+      (X0<char, short>::X1<short, int>::X2<int, long>::f<long, long long>()));
+  TEST("static constexpr const char* XX0<char, short int>::X1<short int, int>::X2<long int, int>::f-1<long int, long long int>()",
+      (X0<char, short>::X1<short, int>::X2<long, int>::f<long, long long>()));
+  TEST("static constexpr const char* XX0<char, short int>::XX3::f-3<long int, long long int>()",
+      (X0<char, short>::X3::f<long, long long>()));
+  TEST("static constexpr const char* X0intint::f-2<long int, long long int>()",
+      (X0<int, int>::f<long, long long>()));
+  TEST("static constexpr const char* X0intint::XX3::f-3<long int, long long int>()",
+      (X0<int, int>::X3::f<long, long long>()));
+
+  X0<char, short>::X3::f<long, long long>(1); // { dg-error "no matching function for call to 'XX0<char, short int>::XX3::f<long int, long long int>\\(int\\)" }
+  // { dg-message "candidate: 'static constexpr const char\\* XX0<char, short int>::XX3::f-3<long int, long long int>\\(\\)'" "" { target *-*-* } X0_X3_f }
+
+  X0<int, int>::X3::f<long, long long>(1); // { dg-error "no matching function for call to 'X0intint::XX3::f<long int, long long int>\\(int\\)" }
+  // { dg-message "candidate: 'static constexpr const char\\* X0intint::XX3::f-3<long int, long long int>\\(\\)'" "" { target *-*-* } X0_X3_f }
+}
diff --git a/gcc/testsuite/g++.dg/diagnostic/diagnose-as5.C b/gcc/testsuite/g++.dg/diagnostic/diagnose-as5.C
new file mode 100644
index 00000000000..2a46d64964f
--- /dev/null
+++ b/gcc/testsuite/g++.dg/diagnostic/diagnose-as5.C
@@ -0,0 +1,21 @@
+// { dg-options "-fdiagnostics-use-aliases -fpretty-templates" }
+// { dg-do compile { target c++11 } }
+
+namespace std
+{
+  template <class T, class A>
+    class basic_string {};
+
+  namespace pmr
+  {
+    struct P {};
+    using string [[gnu::diagnose_as]] = basic_string<char, P>;
+  }
+}
+
+void f(int);
+
+int main()
+{
+  f(std::pmr::string()); // { dg-error "cannot convert 'std::pmr::string' to 'int'" }
+}
diff --git a/gcc/testsuite/g++.dg/diagnostic/diagnose-as6.C b/gcc/testsuite/g++.dg/diagnostic/diagnose-as6.C
new file mode 100644
index 00000000000..94747a7aa6e
--- /dev/null
+++ b/gcc/testsuite/g++.dg/diagnostic/diagnose-as6.C
@@ -0,0 +1,45 @@
+// { dg-options "-fdiagnostics-use-aliases -fpretty-templates" }
+// { dg-do compile { target c++11 } }
+
+template <class T, class U>
+  struct A
+  {
+    struct X {};
+    using Y [[gnu::diagnose_as]] = X;
+  };
+
+template <>
+  struct [[gnu::diagnose_as("A!")]] A<int, int>
+  {
+    struct X {};
+    using Y [[gnu::diagnose_as]] = X;
+  };
+
+void f(int);
+
+template <class T, class U>
+  using B [[gnu::diagnose_as]] = A<T, U>;
+
+// this is not a real alias template
+template <class T>
+  using C [[gnu::diagnose_as]] = A<int, int>; // { dg-warning "'diagnose_as' attribute ignored" }
+
+// Would the following request A<float, int> to be diagnosed as D<int>?
+// What about `= A<float, const T>', etc? This is not worth the implementation
+// complexity.
+template <class T>
+  using D [[gnu::diagnose_as]] = A<float, T>; // { dg-warning "'diagnose_as' attribute ignored" }
+
+template <class T, class U>
+  struct E {};
+template <class T, class U>
+  using F [[gnu::diagnose_as("EF")]] = E<U, T>; // { dg-warning "'diagnose_as' attribute ignored" }
+
+int main()
+{
+  f(A<char, int>()); // { dg-error "cannot convert 'B<char, int>' to 'int'" }
+  f(A<char, int>::X()); // { dg-error "cannot convert 'B<char, int>::Y' to 'int'" }
+  f(B<int, int>::X()); // { dg-error "cannot convert 'A!::Y' to 'int'" }
+  f(C<float>::X()); // { dg-error "cannot convert 'A!::Y' to 'int'" }
+  f(F<char, int>()); // { dg-error "cannot convert 'F<char, int>' {aka 'E<int, char>'} to 'int'" }
+}

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

* Re: [PATCH v4] c++: Add gnu::diagnose_as attribute
  2021-07-23  8:58 ` [PATCH v4] " Matthias Kretz
@ 2021-08-17 18:31   ` Jason Merrill
  2021-11-08 16:40     ` [RFC] c++: Print function template parms when relevant (was: [PATCH v4] c++: Add gnu::diagnose_as attribute) Matthias Kretz
  2021-09-08  2:21   ` [PATCH v4] c++: Add gnu::diagnose_as attribute Jason Merrill
  1 sibling, 1 reply; 24+ messages in thread
From: Jason Merrill @ 2021-08-17 18:31 UTC (permalink / raw)
  To: Matthias Kretz, gcc-patches

[-- Attachment #1: Type: text/plain, Size: 9585 bytes --]

On 7/23/21 4:58 AM, Matthias Kretz wrote:
> Hi Jason,

Hi, thanks for your patience; I've been out on PTO a lot in the last 
month, and will be again this week.

> I found a few regressions from the last patch in the meantime. Version 4 of
> the patch is attached.
> 
> Questions:
> 
> 1. I simplified the condition for calling dump_template_parms in
> dump_function_name. !DECL_FRIEND_PSEUDO_TEMPLATE_INSTANTIATION (t) is
> equivalent to DECL_USE_TEMPLATE (t) in this context; implying that
> dump_template_parms is unconditionally called with `primary = false`. Or am I
> missing something?

Ah, good catch.  That suggests that 
DECL_FRIEND_PSEUDO_TEMPLATE_INSTANTIATION gives false positives; 
DECL_USE_TEMPLATE is also 0 for template patterns themselves, which 
would be why for

template <class T>
void f() { foo; }

the error refers to 'void f()' rather than 'void f<T>()'

The macro should probably also check DECL_FRIEND_CONTEXT.

And then the use of !DECL_USE_TEMPLATE is intending to check whether t 
is a primary template pattern, and pass true in that case.  This would 
be more correct if it also checked instantiates_primary_template_p.


> 2. Given a DECL_TI_ARGS tree, can I query whether an argument was deduced or
> explicitly specified? I'm asking because I still consider diagnostics of
> function templates unfortunate. `template <class T> void f()` is fine, as is
> `void f(T) [with T = float]`, but `void f() [with T = float]` could be better.
> I.e. if the template parameter appears somewhere in the function parameter
> list, dump_template_parms would only produce noise. If, however, the template
> parameter was given explicitly, it would be nice if it could show up
> accordingly in diagnostics.

NON_DEFAULT_TEMPLATE_ARGS_COUNT has that information, though there are 
some issues with it.  Attached is my WIP from May to improve it 
somewhat, if that's interesting.

> 3. When parsing tentatively and the parse is rejected, input_location is not
> reset, correct? In the attached patch I therefore made
> cp_parser_namespace_alias_definition reset input_location on a failed
> tentative parse. But it feels wrong. Shouldn't input_location be restored on
> cp_parser_parse_definitely?

Makes sense, I guess cp_lexer_rollback_tokens should call 
cp_lexer_set_source_position_from_token.

I'll look at the patch soon.

> --
> 
> This attribute overrides the diagnostics output string for the entity it
> appertains to. The motivation is to improve QoI for library TS
> implementations, where diagnostics have a very bad signal-to-noise ratio
> due to the long namespaces involved.
> 
> With the attribute, it is possible to solve PR89370 and make
> std::__cxx11::basic_string<_CharT, _Traits, _Alloc> appear as
> std::string in diagnostic output without extra hacks to recognize the
> type in the C++ frontend.
> 
> Signed-off-by: Matthias Kretz <m.kretz@gsi.de>
> 
> gcc/ChangeLog:
> 
>          PR c++/89370
>          * doc/extend.texi: Document the diagnose_as attribute.
>          * doc/invoke.texi: Document -fno-diagnostics-use-aliases.
> 
> gcc/c-family/ChangeLog:
> 
>          PR c++/89370
>          * c.opt (fdiagnostics-use-aliases): New diagnostics flag.
> 
> gcc/cp/ChangeLog:
> 
>          PR c++/89370
>          * cp-tree.h: Add is_alias_template_p declaration.
>          * decl2.c (is_alias_template_p): New function. Determines
>          whether a given TYPE_DECL is actually an alias template that is
>          still missing its template_info.
>          (is_late_template_attribute): Decls with diagnose_as attribute
>          are early attributes only if they are alias templates.
>          * error.c (dump_scope): When printing the name of a namespace,
>          look for the diagnose_as attribute. If found, print the
>          associated string instead of calling dump_decl.
>          (dump_decl_name_or_diagnose_as): New function to replace
>          dump_decl (pp, DECL_NAME(t), flags) and inspect the tree for the
>          diagnose_as attribute before printing the DECL_NAME.
>          (dump_template_scope): New function. Prints the scope of a
>          template instance correctly applying diagnose_as attributes and
>          adjusting the list of template parms accordingly.
>          (dump_aggr_type): If the type has a diagnose_as attribute, print
>          the associated string instead of printing the original type
>          name. Print template parms only if the attribute was not applied
>          to the instantiation / full specialization. Delay call to
>          dump_scope until the diagnose_as attribute is found. If the
>          attribute has a second argument, use it to override the context
>          passed to dump_scope.
>          (dump_simple_decl): Call dump_decl_name_or_diagnose_as instead
>          of dump_decl.
>          (dump_decl): Ditto.
>          (lang_decl_name): Ditto.
>          (dump_function_decl): Walk the functions context list to
>          determine whether a call to dump_template_scope is required.
>          Ensure function templates diagnosed with pretty templates set
>          TFF_TEMPLATE_NAME to skip dump_template_parms.
>          (dump_function_name): Replace the function's identifier with the
>          diagnose_as attribute value, if set. Expand
>          DECL_FRIEND_PSEUDO_TEMPLATE_INSTANTIATION to DECL_USE_TEMPLATE
>          and consequently call dump_template_parms with primary = false.
>          (comparable_template_types_p): Consider the types not a template
>          if one carries a diagnose_as attribute.
>          (print_template_differences): Replace the identifier with the
>          diagnose_as attribute value on the most general template, if it
>          is set.
>          * name-lookup.c (handle_namespace_attrs): Handle the diagnose_as
>          attribute on namespaces. Ensure exactly one string argument.
>          Ensure previous diagnose_as attributes used the same name.
>          'diagnose_as' on namespace aliases are forwarded to the original
>          namespace. Support no-argument 'diagnose_as' on namespace
>          aliases.
>          (do_namespace_alias): Add attributes parameter and call
>          handle_namespace_attrs.
>          * name-lookup.h (do_namespace_alias): Add attributes tree
>          parameter.
>          * parser.c (cp_parser_declaration): If the next token is
>          RID_NAMESPACE, tentatively parse a namespace alias definition.
>          If this fails expect a namespace definition.
>          (cp_parser_namespace_alias_definition): Allow optional
>          attributes before and after the identifier. Fast exit, restoring
>          input_location, if the expected CPP_EQ token is missing. Pass
>          attributes to do_namespace_alias.
>          * tree.c (cxx_attribute_table): Add diagnose_as attribute to the
>          table.
>          (check_diagnose_as_redeclaration): New function; copied and
>          adjusted from check_abi_tag_redeclaration.
>          (handle_diagnose_as_attribute): New function; copied and
>          adjusted from handle_abi_tag_attribute. If the given *node is a
>          TYPE_DECL: allow no argument to the attribute, using DECL_NAME
>          instead; apply the attribute to the type on the RHS in place,
>          even if the type is complete. Allow 2 arguments when called from
>          handle_diagnose_as_attribute. For type aliases, append
>          CP_DECL_CONTEXT as second attribute argument when the RHS type
>          has a different context. Warn about alias templates without
>          matching template arguments. Apply the attribute to the primary
>          template type for alias templates.
> 
> gcc/testsuite/ChangeLog:
> 
>          PR c++/89370
>          * g++.dg/diagnostic/diagnose-as1.C: New test.
>          * g++.dg/diagnostic/diagnose-as2.C: New test.
>          * g++.dg/diagnostic/diagnose-as3.C: New test.
>          * g++.dg/diagnostic/diagnose-as4.C: New test.
>          * g++.dg/diagnostic/diagnose-as5.C: New test.
>          * g++.dg/diagnostic/diagnose-as6.C: New test.
> ---
>   gcc/c-family/c.opt                            |   4 +
>   gcc/cp/cp-tree.h                              |   1 +
>   gcc/cp/decl2.c                                |  45 ++++
>   gcc/cp/error.c                                | 247 ++++++++++++++++--
>   gcc/cp/name-lookup.c                          |  52 +++-
>   gcc/cp/name-lookup.h                          |   2 +-
>   gcc/cp/parser.c                               |  41 +--
>   gcc/cp/tree.c                                 | 148 +++++++++++
>   gcc/doc/extend.texi                           |  45 ++++
>   gcc/doc/invoke.texi                           |   9 +-
>   .../g++.dg/diagnostic/diagnose-as1.C          | 213 +++++++++++++++
>   .../g++.dg/diagnostic/diagnose-as2.C          | 144 ++++++++++
>   .../g++.dg/diagnostic/diagnose-as3.C          | 152 +++++++++++
>   .../g++.dg/diagnostic/diagnose-as4.C          | 158 +++++++++++
>   .../g++.dg/diagnostic/diagnose-as5.C          |  21 ++
>   .../g++.dg/diagnostic/diagnose-as6.C          |  45 ++++
>   16 files changed, 1291 insertions(+), 36 deletions(-)
>   create mode 100644 gcc/testsuite/g++.dg/diagnostic/diagnose-as1.C
>   create mode 100644 gcc/testsuite/g++.dg/diagnostic/diagnose-as2.C
>   create mode 100644 gcc/testsuite/g++.dg/diagnostic/diagnose-as3.C
>   create mode 100644 gcc/testsuite/g++.dg/diagnostic/diagnose-as4.C
>   create mode 100644 gcc/testsuite/g++.dg/diagnostic/diagnose-as5.C
>   create mode 100644 gcc/testsuite/g++.dg/diagnostic/diagnose-as6.C
> 


[-- Attachment #2: 0001-default.patch --]
[-- Type: text/x-patch, Size: 5751 bytes --]

From 857e349f43a59c88634664ec8635ae49dd7bd50f Mon Sep 17 00:00:00 2001
From: Jason Merrill <jason@redhat.com>
Date: Thu, 27 May 2021 14:04:45 -0400
Subject: [PATCH] default
To: gcc-patches@gcc.gnu.org

---
 gcc/cp/error.c | 13 +++++++------
 gcc/cp/pt.c    | 44 +++++++++++++++++++++++++++-----------------
 2 files changed, 34 insertions(+), 23 deletions(-)

diff --git a/gcc/cp/error.c b/gcc/cp/error.c
index 9cb8bfc413e..cac4d3e2089 100644
--- a/gcc/cp/error.c
+++ b/gcc/cp/error.c
@@ -87,8 +87,6 @@ static void dump_exception_spec (cxx_pretty_printer *, tree, int);
 static void dump_template_argument (cxx_pretty_printer *, tree, int);
 static void dump_template_argument_list (cxx_pretty_printer *, tree, int);
 static void dump_template_parameter (cxx_pretty_printer *, tree, int);
-static void dump_template_bindings (cxx_pretty_printer *, tree, tree,
-                                    vec<tree, va_gc> *);
 static void dump_scope (cxx_pretty_printer *, tree, int);
 static void dump_template_parms (cxx_pretty_printer *, tree, int, int);
 static int get_non_default_template_args_count (tree, int);
@@ -375,7 +373,7 @@ dump_template_parameter (cxx_pretty_printer *pp, tree parm, int flags)
 
 static void
 dump_template_bindings (cxx_pretty_printer *pp, tree parms, tree args,
-                        vec<tree, va_gc> *typenames)
+			vec<tree, va_gc> *typenames, int flags)
 {
   /* Print "[with" and ']', conditional on whether anything is printed at all.
      This is tied to whether a semicolon is needed to separate multiple template
@@ -425,7 +423,8 @@ dump_template_bindings (cxx_pretty_printer *pp, tree parms, tree args,
       if (TMPL_ARGS_DEPTH (args) >= lvl)
 	lvl_args = TMPL_ARGS_LEVEL (args, lvl);
 
-      for (i = 0; i < TREE_VEC_LENGTH (p); ++i)
+      int len = get_non_default_template_args_count (lvl_args, flags);
+      for (i = 0; i < len; ++i)
 	{
 	  tree arg = NULL_TREE;
 
@@ -1663,7 +1662,8 @@ dump_substitution (cxx_pretty_printer *pp,
       && !(flags & TFF_NO_TEMPLATE_BINDINGS))
     {
       vec<tree, va_gc> *typenames = t ? find_typenames (t) : NULL;
-      dump_template_bindings (pp, template_parms, template_args, typenames);
+      dump_template_bindings (pp, template_parms, template_args, typenames,
+			      flags);
     }
 }
 
@@ -3449,7 +3449,8 @@ subst_to_string (tree p)
     return "";
 
   dump_template_decl (cxx_pp, TREE_PURPOSE (p), flags);
-  dump_substitution (cxx_pp, NULL, tparms, targs, /*flags=*/0);
+  dump_substitution (cxx_pp, NULL, tparms, targs,
+		     /*TFF_NO_OMIT_DEFAULT_TEMPLATE_ARGUMENTS*/0);
   return pp_ggc_formatted_text (cxx_pp);
 }
 
diff --git a/gcc/cp/pt.c b/gcc/cp/pt.c
index 0870ccdc9f6..691f1b82535 100644
--- a/gcc/cp/pt.c
+++ b/gcc/cp/pt.c
@@ -2518,7 +2518,7 @@ determine_specialization (tree template_id,
   if (candidates)
     {
       tree fn = TREE_VALUE (candidates);
-      *targs_out = copy_node (DECL_TI_ARGS (fn));
+      *targs_out = copy_template_args (DECL_TI_ARGS (fn));
 
       /* Propagate the candidate's constraints to the declaration.  */
       if (tsk != tsk_template)
@@ -4816,11 +4816,16 @@ template_parms_level_to_args (tree parms)
 {
   tree a = copy_node (parms);
   TREE_TYPE (a) = NULL_TREE;
+  int nondefault = 0;
   for (int i = TREE_VEC_LENGTH (a) - 1; i >= 0; --i)
-    TREE_VEC_ELT (a, i) = template_parm_to_arg (TREE_VEC_ELT (a, i));
+    {
+      tree elt = TREE_VEC_ELT (a, i);
+      TREE_VEC_ELT (a, i) = template_parm_to_arg (elt);
+      if (!elt || elt == error_mark_node || !TREE_PURPOSE (elt))
+	++nondefault;
+    }
 
-  if (CHECKING_P)
-    SET_NON_DEFAULT_TEMPLATE_ARGS_COUNT (a, TREE_VEC_LENGTH (a));
+  SET_NON_DEFAULT_TEMPLATE_ARGS_COUNT (a, nondefault);
 
   return a;
 }
@@ -13276,8 +13281,9 @@ copy_template_args (tree t)
       TREE_VEC_ELT (new_vec, i) = elt;
     }
 
-  NON_DEFAULT_TEMPLATE_ARGS_COUNT (new_vec)
-    = NON_DEFAULT_TEMPLATE_ARGS_COUNT (t);
+  if (!TMPL_ARGS_HAVE_MULTIPLE_LEVELS (t))
+    NON_DEFAULT_TEMPLATE_ARGS_COUNT (new_vec)
+      = NON_DEFAULT_TEMPLATE_ARGS_COUNT (t);
 
   return new_vec;
 }
@@ -22339,9 +22345,17 @@ type_unification_real (tree tparms,
 	     be NULL_TREE or ERROR_MARK_NODE, so we do not need
 	     to explicitly check cxx_dialect here.  */
 	  if (TREE_PURPOSE (TREE_VEC_ELT (tparms, i)))
-	    /* OK, there is a default argument.  Wait until after the
-	       conversion check to do substitution.  */
-	    continue;
+	    {
+	      /* The position of the first default template argument,
+		 is also the number of non-defaulted arguments in TARGS.
+		 Record that.  */
+	      if (!NON_DEFAULT_TEMPLATE_ARGS_COUNT (targs))
+		SET_NON_DEFAULT_TEMPLATE_ARGS_COUNT (targs, i);
+
+	      /* OK, there is a default argument.  Wait until after the
+		 conversion check to do substitution.  */
+	      continue;
+	    }
 
 	  /* If the type parameter is a parameter pack, then it will
 	     be deduced to an empty parameter pack.  */
@@ -22444,14 +22458,7 @@ type_unification_real (tree tparms,
 	  if (arg == error_mark_node)
 	    return 1;
 	  else if (arg)
-	    {
-	      TREE_VEC_ELT (targs, i) = arg;
-	      /* The position of the first default template argument,
-		 is also the number of non-defaulted arguments in TARGS.
-		 Record that.  */
-	      if (!NON_DEFAULT_TEMPLATE_ARGS_COUNT (targs))
-		SET_NON_DEFAULT_TEMPLATE_ARGS_COUNT (targs, i);
-	    }
+	    TREE_VEC_ELT (targs, i) = arg;
 	}
 
       if (saw_undeduced++ == 1)
@@ -24821,6 +24828,9 @@ get_partial_spec_bindings (tree tmpl, tree spec_tmpl, tree args)
   if (!template_template_parm_bindings_ok_p (tparms, deduced_args))
     return NULL_TREE;
 
+  if (CHECKING_P)
+    SET_NON_DEFAULT_TEMPLATE_ARGS_COUNT (deduced_args, ntparms);
+
   return deduced_args;
 }
 
-- 
2.27.0


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

* Re: [PATCH v4] c++: Add gnu::diagnose_as attribute
  2021-07-23  8:58 ` [PATCH v4] " Matthias Kretz
  2021-08-17 18:31   ` Jason Merrill
@ 2021-09-08  2:21   ` Jason Merrill
  2021-11-15  0:35     ` [PATCH v5] " Matthias Kretz
  1 sibling, 1 reply; 24+ messages in thread
From: Jason Merrill @ 2021-09-08  2:21 UTC (permalink / raw)
  To: Matthias Kretz, gcc-patches

On 7/23/21 4:58 AM, Matthias Kretz wrote:
> Hi Jason,
> 
> I found a few regressions from the last patch in the meantime. Version 4 of
> the patch is attached.
> 
> Questions:
> 
> 1. I simplified the condition for calling dump_template_parms in
> dump_function_name. !DECL_FRIEND_PSEUDO_TEMPLATE_INSTANTIATION (t) is
> equivalent to DECL_USE_TEMPLATE (t) in this context; implying that
> dump_template_parms is unconditionally called with `primary = false`. Or am I
> missing something?
> 
> 2. Given a DECL_TI_ARGS tree, can I query whether an argument was deduced or
> explicitly specified? I'm asking because I still consider diagnostics of
> function templates unfortunate. `template <class T> void f()` is fine, as is
> `void f(T) [with T = float]`, but `void f() [with T = float]` could be better.
> I.e. if the template parameter appears somewhere in the function parameter
> list, dump_template_parms would only produce noise. If, however, the template
> parameter was given explicitly, it would be nice if it could show up
> accordingly in diagnostics.
> 
> 3. When parsing tentatively and the parse is rejected, input_location is not
> reset, correct? In the attached patch I therefore made
> cp_parser_namespace_alias_definition reset input_location on a failed
> tentative parse. But it feels wrong. Shouldn't input_location be restored on
> cp_parser_parse_definitely?
> 
> --
> 
> This attribute overrides the diagnostics output string for the entity it
> appertains to. The motivation is to improve QoI for library TS
> implementations, where diagnostics have a very bad signal-to-noise ratio
> due to the long namespaces involved.
> 
> With the attribute, it is possible to solve PR89370 and make
> std::__cxx11::basic_string<_CharT, _Traits, _Alloc> appear as
> std::string in diagnostic output without extra hacks to recognize the
> type in the C++ frontend.
> 
> Signed-off-by: Matthias Kretz <m.kretz@gsi.de>
> 
> gcc/ChangeLog:
> 
>          PR c++/89370
>          * doc/extend.texi: Document the diagnose_as attribute.
>          * doc/invoke.texi: Document -fno-diagnostics-use-aliases.
> 
> gcc/c-family/ChangeLog:
> 
>          PR c++/89370
>          * c.opt (fdiagnostics-use-aliases): New diagnostics flag.
> 
> gcc/cp/ChangeLog:
> 
>          PR c++/89370
>          * cp-tree.h: Add is_alias_template_p declaration.
>          * decl2.c (is_alias_template_p): New function. Determines
>          whether a given TYPE_DECL is actually an alias template that is
>          still missing its template_info.

I still think you want to share code with get_underlying_template.  For 
the case where the alias doesn't have DECL_TEMPLATE_INFO yet, you can 
compare to current_template_args ().  Or you could do some initial 
processing that doesn't care about templates in the handler, and then do 
more in cp_parser_alias_declaration after the call to grokfield/start_decl.

If you still think you need this function, let's call it 
is_renaming_alias_template or renaming_alias_template_p; using both is_ 
and _p is redundant.  I don't have a strong preference which.

>          (is_late_template_attribute): Decls with diagnose_as attribute
>          are early attributes only if they are alias templates.

Is there a reason not to apply it early to other templates as well?

>          * error.c (dump_scope): When printing the name of a namespace,
>          look for the diagnose_as attribute. If found, print the
>          associated string instead of calling dump_decl.

Did you decide not to handle this in dump_decl, so we use the 
diagnose_as when referring to the namespace in non-scope contexts as well?

>          (dump_decl_name_or_diagnose_as): New function to replace
>          dump_decl (pp, DECL_NAME(t), flags) and inspect the tree for the
>          diagnose_as attribute before printing the DECL_NAME.

> +  if (flag_diagnostics_use_aliases)
> +    {
> +      tree attr = lookup_attribute ("diagnose_as", DECL_ATTRIBUTES (decl));
> +      if (attr && TREE_VALUE (attr))
> +       {
> +         pp_cxx_ws_string (
> +           pp, TREE_STRING_POINTER (TREE_VALUE (TREE_VALUE (attr))));

This pattern is used several places outside this function; can we factor 
it into something like

if (maybe_print_diagnose_as (special))
   /* OK */;

?

>          (dump_template_scope): New function. Prints the scope of a
>          template instance correctly applying diagnose_as attributes and
>          adjusting the list of template parms accordingly.

> +      const bool tmplate
> +       = TYPE_LANG_SPECIFIC (special) && CLASSTYPE_TEMPLATE_INFO (special)
> +           && (TREE_CODE (CLASSTYPE_TI_TEMPLATE (special)) != TEMPLATE_DECL
> +                 || PRIMARY_TEMPLATE_P (CLASSTYPE_TI_TEMPLATE (special)));

CLASSTYPE_SPECIALIZATION_OF_PRIMARY_TEMPLATE_P?

> +                          tmplate ? &TREE_CHAIN(*parms) : parms, flags);

Missing space before (

> +         if (tmplate)
> +           TREE_VALUE (*parms) = make_tree_vec (0);

This could use a comment.

>          (dump_aggr_type): If the type has a diagnose_as attribute, print
>          the associated string instead of printing the original type
>          name. Print template parms only if the attribute was not applied
>          to the instantiation / full specialization. Delay call to
>          dump_scope until the diagnose_as attribute is found. If the
>          attribute has a second argument, use it to override the context
>          passed to dump_scope.

> +             for (int i = 0; i < NUM_TMPL_ARGS (args); ++i)
> +               {
> +                 tree arg = TREE_VEC_ELT (args, i);
> +                 while (INDIRECT_TYPE_P (arg))
> +                   arg = TREE_TYPE (arg);
> +                 if (WILDCARD_TYPE_P (arg))
> +                   {
> +                     tmplate = true;
> +                     break;
> +                   }
> +               }

I think you want any_dependent_template_args_p (args)

Checking WILDCARD_TYPE_P is generally not what you want; plenty of 
dependent types don't show up specifically as wildcards.  T*, for instance.

> +  if (diagnose_as)
> +    pp_cxx_ws_string (pp, TREE_STRING_POINTER (
> +                           TREE_VALUE (TREE_VALUE (diagnose_as))));

( needs to go on the next line.  I'd format this as

if (diagnose_as)
   pp_cxx_ws_string (pp, (TREE_STRING_POINTER
                          (TREE_VALUE (TREE_VALUE (diagnose_as)))));

There's a lot of this formatting pattern in the patch.

>          (dump_simple_decl): Call dump_decl_name_or_diagnose_as instead
>          of dump_decl.
>          (dump_decl): Ditto.

> +         dump_decl_name_or_diagnose_as(pp, t, flags);

Missing space before (

>          (lang_decl_name): Ditto.
>          (dump_function_decl): Walk the functions context list to
>          determine whether a call to dump_template_scope is required.
>          Ensure function templates diagnosed with pretty templates set
>          TFF_TEMPLATE_NAME to skip dump_template_parms.
>          (dump_function_name): Replace the function's identifier with the
>          diagnose_as attribute value, if set. Expand
>          DECL_FRIEND_PSEUDO_TEMPLATE_INSTANTIATION to DECL_USE_TEMPLATE
>          and consequently call dump_template_parms with primary = false.
>          (comparable_template_types_p): Consider the types not a template
>          if one carries a diagnose_as attribute.

I'd think it would be more helpful to suppress diagnose_as if the types 
are comparable.

>          (print_template_differences): Replace the identifier with the
>          diagnose_as attribute value on the most general template, if it
>          is set.
>          * name-lookup.c (handle_namespace_attrs): Handle the diagnose_as
>          attribute on namespaces. Ensure exactly one string argument.
>          Ensure previous diagnose_as attributes used the same name.
>          'diagnose_as' on namespace aliases are forwarded to the original
>          namespace. Support no-argument 'diagnose_as' on namespace
>          aliases.
>          (do_namespace_alias): Add attributes parameter and call
>          handle_namespace_attrs.
>          * name-lookup.h (do_namespace_alias): Add attributes tree
>          parameter.
>          * parser.c (cp_parser_declaration): If the next token is
>          RID_NAMESPACE, tentatively parse a namespace alias definition.
>          If this fails expect a namespace definition.
>          (cp_parser_namespace_alias_definition): Allow optional
>          attributes before and after the identifier. Fast exit, restoring
>          input_location, if the expected CPP_EQ token is missing. Pass
>          attributes to do_namespace_alias.

> +  if (attributes
> +       && !cp_parser_uncommitted_to_tentative_parse_p (parser))
> +    pedwarn (input_location, OPT_Wpedantic,
> +            "standard attributes on namespaces aliases must follow "
> +            "the namespace alias name");

Maybe remember where we saw attributes and warn later, after we've 
committed to parsing as an alias.  Or use cp_parser_skip_attributes_opt 
to avoid tentative parsing in the first place.

> +  if (nullptr == cp_parser_require (parser, CPP_EQ, RT_EQ))

The usual pattern is

  if (!cp_parser_require

>          * tree.c (cxx_attribute_table): Add diagnose_as attribute to the
>          table.
>          (check_diagnose_as_redeclaration): New function; copied and
>          adjusted from check_abi_tag_redeclaration.
>          (handle_diagnose_as_attribute): New function; copied and
>          adjusted from handle_abi_tag_attribute. If the given *node is a
>          TYPE_DECL: allow no argument to the attribute, using DECL_NAME
>          instead; apply the attribute to the type on the RHS in place,
>          even if the type is complete. Allow 2 arguments when called from
>          handle_diagnose_as_attribute. For type aliases, append
>          CP_DECL_CONTEXT as second attribute argument when the RHS type
>          has a different context. Warn about alias templates without
>          matching template arguments. Apply the attribute to the primary
>          template type for alias templates.

All this description of semantics should be in a comment rather than the 
CHangeLog.

> +      /* Reject alias templates without wildcards on the innermost template arg
> s
> +         of the RHS type. E.g. template <class> using A = B;  */
> +      if (DECL_LANG_SPECIFIC (decl)
> +           && DECL_TEMPLATE_INFO (decl)
> +           && DECL_ALIAS_TEMPLATE_P (DECL_TI_TEMPLATE (decl))
> +           && PRIMARY_TEMPLATE_P (DECL_TI_TEMPLATE (decl)))
> +       return error_mark_node;

I don't think this is doing anything useful; if we had already done 
push_template_decl by this point, it would reject all alias templates.

> +      // Add the DECL_CONTEXT of the alias to the attribute if it is different
> +      // to the context of the type.

How about using the alias TYPE_DECL itself as the argument to the 
attribute on the type?  Then we wouldn't need to copy its name into a 
string, either.

> gcc/testsuite/ChangeLog:
> 
>          PR c++/89370
>          * g++.dg/diagnostic/diagnose-as1.C: New test.
>          * g++.dg/diagnostic/diagnose-as2.C: New test.
>          * g++.dg/diagnostic/diagnose-as3.C: New test.
>          * g++.dg/diagnostic/diagnose-as4.C: New test.
>          * g++.dg/diagnostic/diagnose-as5.C: New test.
>          * g++.dg/diagnostic/diagnose-as6.C: New test.
> ---
>   gcc/c-family/c.opt                            |   4 +
>   gcc/cp/cp-tree.h                              |   1 +
>   gcc/cp/decl2.c                                |  45 ++++
>   gcc/cp/error.c                                | 247 ++++++++++++++++--
>   gcc/cp/name-lookup.c                          |  52 +++-
>   gcc/cp/name-lookup.h                          |   2 +-
>   gcc/cp/parser.c                               |  41 +--
>   gcc/cp/tree.c                                 | 148 +++++++++++
>   gcc/doc/extend.texi                           |  45 ++++
>   gcc/doc/invoke.texi                           |   9 +-
>   .../g++.dg/diagnostic/diagnose-as1.C          | 213 +++++++++++++++
>   .../g++.dg/diagnostic/diagnose-as2.C          | 144 ++++++++++
>   .../g++.dg/diagnostic/diagnose-as3.C          | 152 +++++++++++
>   .../g++.dg/diagnostic/diagnose-as4.C          | 158 +++++++++++
>   .../g++.dg/diagnostic/diagnose-as5.C          |  21 ++
>   .../g++.dg/diagnostic/diagnose-as6.C          |  45 ++++
>   16 files changed, 1291 insertions(+), 36 deletions(-)
>   create mode 100644 gcc/testsuite/g++.dg/diagnostic/diagnose-as1.C
>   create mode 100644 gcc/testsuite/g++.dg/diagnostic/diagnose-as2.C
>   create mode 100644 gcc/testsuite/g++.dg/diagnostic/diagnose-as3.C
>   create mode 100644 gcc/testsuite/g++.dg/diagnostic/diagnose-as4.C
>   create mode 100644 gcc/testsuite/g++.dg/diagnostic/diagnose-as5.C
>   create mode 100644 gcc/testsuite/g++.dg/diagnostic/diagnose-as6.C
> 


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

* [RFC] c++: Print function template parms when relevant (was: [PATCH v4] c++: Add gnu::diagnose_as attribute)
  2021-08-17 18:31   ` Jason Merrill
@ 2021-11-08 16:40     ` Matthias Kretz
  2021-11-08 20:00       ` Matthias Kretz
  2021-11-17  6:09       ` Jason Merrill
  0 siblings, 2 replies; 24+ messages in thread
From: Matthias Kretz @ 2021-11-08 16:40 UTC (permalink / raw)
  To: gcc-patches, Jason Merrill

[-- Attachment #1: Type: text/plain, Size: 6057 bytes --]

On Tuesday, 17 August 2021 20:31:54 CET Jason Merrill wrote:
> > 2. Given a DECL_TI_ARGS tree, can I query whether an argument was deduced
> > or explicitly specified? I'm asking because I still consider diagnostics
> > of function templates unfortunate. `template <class T> void f()` is fine,
> > as is `void f(T) [with T = float]`, but `void f() [with T = float]` could
> > be better. I.e. if the template parameter appears somewhere in the
> > function parameter list, dump_template_parms would only produce noise.
> > If, however, the template parameter was given explicitly, it would be
> > nice if it could show up accordingly in diagnostics.
> 
> NON_DEFAULT_TEMPLATE_ARGS_COUNT has that information, though there are
> some issues with it.  Attached is my WIP from May to improve it
> somewhat, if that's interesting.

It is interesting. I used your patch to come up with the attached. Patch. I 
must say, I didn't try to read through all the cp/pt.c code to understand all 
of what you did there (which is why my ChangeLog entry says "Jason?"), but it 
works for me (and all of `make check`).

Anyway, I'd like to propose the following before finishing my diagnose_as 
patch. I believe it's useful to fix this part first. The diagnostic/default-
template-args-[12].C tests show a lot of examples of the intent of this patch. 
And the remaining changes to the testsuite show how it changes diagnostic 
output.

---------------------- 8< --------------------

The choice when to print a function template parameter was still
suboptimal. That's because sometimes the function template parameter
list only adds noise, while in other situations the lack of a function
template parameter list makes diagnostic messages hard to understand.

The general idea of this change is to print template parms wherever they
would appear in the source code as well. Thus, the diagnostics code
needs to know whether any template parameter was given explicitly.

Signed-off-by: Matthias Kretz <m.kretz@gsi.de>

gcc/testsuite/ChangeLog:

        * g++.dg/debug/dwarf2/template-params-12n.C: Optionally, allow
        DW_AT_default_value.
        * g++.dg/diagnostic/default-template-args-1.C: New.
        * g++.dg/diagnostic/default-template-args-2.C: New.
        * g++.dg/diagnostic/param-type-mismatch-2.C: Expect template
        parms in diagnostic.
        * g++.dg/ext/pretty1.C: Expect function template specialization
        to not pretty-print template parms.
        * g++.old-deja/g++.ext/pretty3.C: Ditto.
        * g++.old-deja/g++.pt/memtemp77.C: Ditto.
        * g++.dg/goacc/template.C: Expect function template parms for
        explicit arguments.
        * g++.dg/gomp/declare-variant-7.C: Expect no function template
        parms for deduced arguments.
        * g++.dg/template/error40.C: Expect only non-default template
        arguments in diagnostic.

gcc/cp/ChangeLog:

        * cp-tree.h (GET_NON_DEFAULT_TEMPLATE_ARGS_COUNT): Return
        absolute value of stored constant.
        (EXPLICIT_TEMPLATE_ARGS_P): New.
        (SET_EXPLICIT_TEMPLATE_ARGS_P): New.
        (TFF_AS_PRIMARY): New constant.
        * error.c (get_non_default_template_args_count): Avoid
        GET_NON_DEFAULT_TEMPLATE_ARGS_COUNT if
        NON_DEFAULT_TEMPLATE_ARGS_COUNT is a NULL_TREE. Make independent
        of flag_pretty_templates.
        (dump_template_bindings): Add flags parameter to be passed to
        get_non_default_template_args_count. Print only non-default
        template arguments.
        (dump_function_decl): Call dump_function_name and dump_type of
        the DECL_CONTEXT with specialized template and set
        TFF_AS_PRIMARY for their flags.
        (dump_function_name): Add and document conditions for calling
        dump_template_parms.
        (dump_template_parms): Print only non-default template
        parameters.
        * pt.c (determine_specialization): Jason?
        (template_parms_level_to_args): Jason?
        (copy_template_args): Jason?
        (fn_type_unification): Set EXPLICIT_TEMPLATE_ARGS_P on the
        template arguments tree if any template parameter was explicitly
        given.
        (type_unification_real): Jason?
        (get_partial_spec_bindings): Jason?
        (tsubst_template_args): Determine number of defaulted arguments
        from new argument vector, if possible.
---
 gcc/cp/cp-tree.h                              | 18 +++-
 gcc/cp/error.c                                | 83 ++++++++++++++-----
 gcc/cp/pt.c                                   | 58 +++++++++----
 .../g++.dg/debug/dwarf2/template-params-12n.C |  2 +-
 .../diagnostic/default-template-args-1.C      | 73 ++++++++++++++++
 .../diagnostic/default-template-args-2.C      | 37 +++++++++
 .../g++.dg/diagnostic/param-type-mismatch-2.C |  2 +-
 gcc/testsuite/g++.dg/ext/pretty1.C            |  2 +-
 gcc/testsuite/g++.dg/goacc/template.C         |  8 +-
 gcc/testsuite/g++.dg/gomp/declare-variant-7.C |  4 +-
 gcc/testsuite/g++.dg/template/error40.C       |  6 +-
 gcc/testsuite/g++.old-deja/g++.ext/pretty3.C  |  2 +-
 gcc/testsuite/g++.old-deja/g++.pt/memtemp77.C |  2 +-
 13 files changed, 242 insertions(+), 55 deletions(-)
 create mode 100644 gcc/testsuite/g++.dg/diagnostic/default-template-args-1.C
 create mode 100644 gcc/testsuite/g++.dg/diagnostic/default-template-args-2.C


-- 
──────────────────────────────────────────────────────────────────────────
 Dr. Matthias Kretz                           https://mattkretz.github.io
 GSI Helmholtz Centre for Heavy Ion Research               https://gsi.de
 stdₓ::simd
──────────────────────────────────────────────────────────────────────────

[-- Attachment #2: 0001-c-Print-function-template-parms-when-relevant.patch --]
[-- Type: text/x-patch, Size: 27049 bytes --]

diff --git a/gcc/cp/cp-tree.h b/gcc/cp/cp-tree.h
index f387b5036d2..27f11e12812 100644
--- a/gcc/cp/cp-tree.h
+++ b/gcc/cp/cp-tree.h
@@ -3686,7 +3686,8 @@ struct GTY(()) lang_decl {
   TREE_LANG_FLAG_1 (TEMPLATE_INFO_CHECK (NODE))
 /* For a given TREE_VEC containing a template argument list,
    this property contains the number of arguments that are not
-   defaulted.  */
+   defaulted. The sign of the number is negative for function templates with
+   explicitly given template arguments (i.e. neither deduced nor defaulted).  */
 #define NON_DEFAULT_TEMPLATE_ARGS_COUNT(NODE) \
   TREE_CHAIN (TREE_VEC_CHECK (NODE))
 
@@ -3696,14 +3697,21 @@ struct GTY(()) lang_decl {
   NON_DEFAULT_TEMPLATE_ARGS_COUNT(NODE) = build_int_cst (NULL_TREE, INT_VALUE)
 #if CHECKING_P
 #define GET_NON_DEFAULT_TEMPLATE_ARGS_COUNT(NODE) \
-    int_cst_value (NON_DEFAULT_TEMPLATE_ARGS_COUNT (NODE))
+  abs (int_cst_value (NON_DEFAULT_TEMPLATE_ARGS_COUNT (NODE)))
 #else
 #define GET_NON_DEFAULT_TEMPLATE_ARGS_COUNT(NODE) \
   NON_DEFAULT_TEMPLATE_ARGS_COUNT (NODE) \
-  ? int_cst_value (NON_DEFAULT_TEMPLATE_ARGS_COUNT (NODE)) \
+  ? abs (int_cst_value (NON_DEFAULT_TEMPLATE_ARGS_COUNT (NODE))) \
   : TREE_VEC_LENGTH (INNERMOST_TEMPLATE_ARGS (NODE))
 #endif
 
+#define EXPLICIT_TEMPLATE_ARGS_P(NODE) \
+  (int_cst_value (NON_DEFAULT_TEMPLATE_ARGS_COUNT (NODE)) < 0)
+
+#define SET_EXPLICIT_TEMPLATE_ARGS_P(NODE) \
+  SET_NON_DEFAULT_TEMPLATE_ARGS_COUNT      \
+    (NODE, -GET_NON_DEFAULT_TEMPLATE_ARGS_COUNT (NODE))
+
 /* The list of access checks that were deferred during parsing
    which need to be performed at template instantiation time.
 
@@ -5938,7 +5946,8 @@ enum auto_deduction_context
        identical to their defaults.
    TFF_NO_TEMPLATE_BINDINGS: do not print information about the template
        arguments for a function template specialization.
-   TFF_POINTER: we are printing a pointer type.  */
+   TFF_POINTER: we are printing a pointer type.
+   TFF_AS_PRIMARY: show the template like a primary template.  */
 
 #define TFF_PLAIN_IDENTIFIER			(0)
 #define TFF_SCOPE				(1)
@@ -5956,6 +5965,7 @@ enum auto_deduction_context
 #define TFF_NO_OMIT_DEFAULT_TEMPLATE_ARGUMENTS	(1 << 12)
 #define TFF_NO_TEMPLATE_BINDINGS		(1 << 13)
 #define TFF_POINTER		                (1 << 14)
+#define TFF_AS_PRIMARY		                (1 << 15)
 
 /* These constants can be used as bit flags to control strip_typedefs.
 
diff --git a/gcc/cp/error.c b/gcc/cp/error.c
index 012a4ecddf4..86e9d12103a 100644
--- a/gcc/cp/error.c
+++ b/gcc/cp/error.c
@@ -87,7 +87,7 @@ static void dump_template_argument (cxx_pretty_printer *, tree, int);
 static void dump_template_argument_list (cxx_pretty_printer *, tree, int);
 static void dump_template_parameter (cxx_pretty_printer *, tree, int);
 static void dump_template_bindings (cxx_pretty_printer *, tree, tree,
-                                    vec<tree, va_gc> *);
+                                    vec<tree, va_gc> *, int);
 static void dump_scope (cxx_pretty_printer *, tree, int);
 static void dump_template_parms (cxx_pretty_printer *, tree, int, int);
 static int get_non_default_template_args_count (tree, int);
@@ -278,7 +278,8 @@ dump_template_argument (cxx_pretty_printer *pp, tree arg, int flags)
 static int
 get_non_default_template_args_count (tree args, int flags)
 {
-  int n = TREE_VEC_LENGTH (INNERMOST_TEMPLATE_ARGS (args));
+  args = INNERMOST_TEMPLATE_ARGS (args);
+  int n = TREE_VEC_LENGTH (args);
 
   if (/* We use this flag when generating debug information.  We don't
 	 want to expand templates at this point, for this may generate
@@ -286,10 +287,10 @@ get_non_default_template_args_count (tree args, int flags)
 	 turn cause codegen differences between compilations with and
 	 without -g.  */
       (flags & TFF_NO_OMIT_DEFAULT_TEMPLATE_ARGUMENTS) != 0
-      || !flag_pretty_templates)
+      || !NON_DEFAULT_TEMPLATE_ARGS_COUNT (args))
     return n;
 
-  return GET_NON_DEFAULT_TEMPLATE_ARGS_COUNT (INNERMOST_TEMPLATE_ARGS (args));
+  return GET_NON_DEFAULT_TEMPLATE_ARGS_COUNT (args);
 }
 
 /* Dump a template-argument-list ARGS (always a TREE_VEC) under control
@@ -369,7 +370,7 @@ dump_template_parameter (cxx_pretty_printer *pp, tree parm, int flags)
 
 static void
 dump_template_bindings (cxx_pretty_printer *pp, tree parms, tree args,
-                        vec<tree, va_gc> *typenames)
+                        vec<tree, va_gc> *typenames, int flags)
 {
   /* Print "[with" and ']', conditional on whether anything is printed at all.
      This is tied to whether a semicolon is needed to separate multiple template
@@ -411,11 +412,16 @@ dump_template_bindings (cxx_pretty_printer *pp, tree parms, tree args,
       int i;
       tree lvl_args = NULL_TREE;
 
+      int len = TREE_VEC_LENGTH (p);
       /* Don't crash if we had an invalid argument list.  */
       if (TMPL_ARGS_DEPTH (args) >= lvl)
-	lvl_args = TMPL_ARGS_LEVEL (args, lvl);
+	{
+	  lvl_args = TMPL_ARGS_LEVEL (args, lvl);
+	  len = MIN (len,
+		     get_non_default_template_args_count (lvl_args, flags));
+	}
 
-      for (i = 0; i < TREE_VEC_LENGTH (p); ++i)
+      for (i = 0; i < len; ++i)
 	{
 	  tree arg = NULL_TREE;
 
@@ -1635,7 +1641,8 @@ dump_substitution (cxx_pretty_printer *pp,
       && !(flags & TFF_NO_TEMPLATE_BINDINGS))
     {
       vec<tree, va_gc> *typenames = t ? find_typenames (t) : NULL;
-      dump_template_bindings (pp, template_parms, template_args, typenames);
+      dump_template_bindings (pp, template_parms, template_args, typenames,
+			      flags);
     }
 }
 
@@ -1688,8 +1695,15 @@ dump_function_decl (cxx_pretty_printer *pp, tree t, int flags)
   /* Likewise for the constexpr specifier, in case t is a specialization.  */
   constexpr_p = DECL_DECLARED_CONSTEXPR_P (t);
 
-  /* Pretty print template instantiations only.  */
-  if (DECL_USE_TEMPLATE (t) && DECL_TEMPLATE_INFO (t)
+  /* Keep t before the following branch makes t point to a more general
+     template. Without the specialized template, the information about defaulted
+     template arguments is lost.  */
+  tree specialized_t = t;
+  int specialized_flags = 0;
+
+  /* Pretty print only template instantiations. Don't pretty print explicit
+     specializations like 'template <> void fun<int> (int)'.  */
+  if (DECL_TEMPLATE_INSTANTIATION (t) && DECL_TEMPLATE_INFO (t)
       && !(flags & TFF_NO_TEMPLATE_BINDINGS)
       && flag_pretty_templates)
     {
@@ -1701,6 +1715,9 @@ dump_function_decl (cxx_pretty_printer *pp, tree t, int flags)
 	{
 	  template_parms = DECL_TEMPLATE_PARMS (tmpl);
 	  t = tmpl;
+	  /* The "[with ...]" clause is printed, thus dump functions printing
+	     SPECIALIZED_T need to add TFF_AS_PRIMARY to their flags.  */
+	  specialized_flags = TFF_AS_PRIMARY;
 	}
     }
 
@@ -1710,8 +1727,8 @@ dump_function_decl (cxx_pretty_printer *pp, tree t, int flags)
   fntype = TREE_TYPE (t);
   parmtypes = FUNCTION_FIRST_USER_PARMTYPE (t);
 
-  if (DECL_CLASS_SCOPE_P (t))
-    cname = DECL_CONTEXT (t);
+  if (DECL_CLASS_SCOPE_P (specialized_t))
+    cname = DECL_CONTEXT (specialized_t);
   /* This is for partially instantiated template methods.  */
   else if (TREE_CODE (fntype) == METHOD_TYPE)
     cname = TREE_TYPE (TREE_VALUE (parmtypes));
@@ -1749,13 +1766,14 @@ dump_function_decl (cxx_pretty_printer *pp, tree t, int flags)
     /* Nothing.  */;
   else if (cname)
     {
-      dump_type (pp, cname, flags);
+      dump_type (pp, cname, flags | specialized_flags);
       pp_cxx_colon_colon (pp);
     }
   else
     dump_scope (pp, CP_DECL_CONTEXT (t), flags);
 
-  dump_function_name (pp, t, dump_function_name_flags);
+  dump_function_name (pp, specialized_t,
+		      dump_function_name_flags | specialized_flags);
 
   if (!(flags & TFF_NO_FUNCTION_ARGUMENTS))
     {
@@ -1968,13 +1986,35 @@ dump_function_name (cxx_pretty_printer *pp, tree t, int flags)
 
   dump_module_suffix (pp, t);
 
+/* Print function template parameters if:
+   1. t is template, and
+   2. flags did not request "show only template-name", and
+   3. t is a specialization of a template (Why is this needed? This was present
+      since 1999 via !DECL_FRIEND_PSEUDO_TEMPLATE_INSTANTIATION: "Don't crash if
+      given a friend pseudo-instantiation". The DECL_USE_TEMPLATE should likely
+      inform the PRIMARY parameter of dump_template_parms.), and
+   4. either
+      - flags requests to show no function arguments, in which case deduced
+        types could be hidden, or
+      - at least one function template argument was given explicitly, or
+      - we're printing a DWARF name,
+      and
+   5. either
+      - t is a member friend template of a template class (see DECL_TI_TEMPLATE
+        documentation), or
+      - 
+ */
   if (DECL_TEMPLATE_INFO (t)
       && !(flags & TFF_TEMPLATE_NAME)
-      && !DECL_FRIEND_PSEUDO_TEMPLATE_INSTANTIATION (t)
+      && DECL_USE_TEMPLATE (t)
+      && ((flags & TFF_NO_FUNCTION_ARGUMENTS)
+	    || (DECL_TI_ARGS (t)
+		  && EXPLICIT_TEMPLATE_ARGS_P (INNERMOST_TEMPLATE_ARGS
+						 (DECL_TI_ARGS (t))))
+	    || (pp->flags & pp_c_flag_gnu_v3) != 0)
       && (TREE_CODE (DECL_TI_TEMPLATE (t)) != TEMPLATE_DECL
 	  || PRIMARY_TEMPLATE_P (DECL_TI_TEMPLATE (t))))
-    dump_template_parms (pp, DECL_TEMPLATE_INFO (t), !DECL_USE_TEMPLATE (t),
-                         flags);
+    dump_template_parms (pp, DECL_TEMPLATE_INFO (t), false, flags);
 }
 
 /* Dump the template parameters from the template info INFO under control of
@@ -1989,6 +2029,8 @@ dump_template_parms (cxx_pretty_printer *pp, tree info,
 {
   tree args = info ? TI_ARGS (info) : NULL_TREE;
 
+  if (flags & TFF_AS_PRIMARY)
+    primary = true;
   if (primary && flags & TFF_TEMPLATE_NAME)
     return;
   flags &= ~(TFF_CLASS_KEY_OR_ENUM | TFF_TEMPLATE_NAME);
@@ -1998,10 +2040,11 @@ dump_template_parms (cxx_pretty_printer *pp, tree info,
      to crash producing error messages.  */
   if (args && !primary)
     {
-      int len, ix;
-      len = get_non_default_template_args_count (args, flags);
+      int ix;
 
       args = INNERMOST_TEMPLATE_ARGS (args);
+      const int len = MIN (NUM_TMPL_ARGS (args),
+			   get_non_default_template_args_count (args, flags));
       for (ix = 0; ix != len; ix++)
 	{
 	  tree arg = TREE_VEC_ELT (args, ix);
@@ -2028,6 +2071,8 @@ dump_template_parms (cxx_pretty_printer *pp, tree info,
 
       parms = TREE_CODE (parms) == TREE_LIST ? TREE_VALUE (parms) : NULL_TREE;
       len = parms ? TREE_VEC_LENGTH (parms) : 0;
+      if (args)
+	len = MIN (len, get_non_default_template_args_count (args, flags));
 
       for (ix = 0; ix != len; ix++)
 	{
diff --git a/gcc/cp/pt.c b/gcc/cp/pt.c
index 66040035b2f..800249f0933 100644
--- a/gcc/cp/pt.c
+++ b/gcc/cp/pt.c
@@ -2504,7 +2504,7 @@ determine_specialization (tree template_id,
   if (candidates)
     {
       tree fn = TREE_VALUE (candidates);
-      *targs_out = copy_node (DECL_TI_ARGS (fn));
+      *targs_out = copy_template_args (DECL_TI_ARGS (fn));
 
       /* Propagate the candidate's constraints to the declaration.  */
       if (tsk != tsk_template)
@@ -4828,11 +4828,16 @@ template_parms_level_to_args (tree parms)
 {
   tree a = copy_node (parms);
   TREE_TYPE (a) = NULL_TREE;
+  int nondefault = 0;
   for (int i = TREE_VEC_LENGTH (a) - 1; i >= 0; --i)
-    TREE_VEC_ELT (a, i) = template_parm_to_arg (TREE_VEC_ELT (a, i));
+    {
+      tree elt = TREE_VEC_ELT (a, i);
+      TREE_VEC_ELT (a, i) = template_parm_to_arg (elt);
+      if (!elt || elt == error_mark_node || !TREE_PURPOSE (elt))
+	++nondefault;
+    }
 
-  if (CHECKING_P)
-    SET_NON_DEFAULT_TEMPLATE_ARGS_COUNT (a, TREE_VEC_LENGTH (a));
+  SET_NON_DEFAULT_TEMPLATE_ARGS_COUNT (a, nondefault);
 
   return a;
 }
@@ -13332,8 +13337,9 @@ copy_template_args (tree t)
       TREE_VEC_ELT (new_vec, i) = elt;
     }
 
-  NON_DEFAULT_TEMPLATE_ARGS_COUNT (new_vec)
-    = NON_DEFAULT_TEMPLATE_ARGS_COUNT (t);
+  if (!TMPL_ARGS_HAVE_MULTIPLE_LEVELS (t))
+    NON_DEFAULT_TEMPLATE_ARGS_COUNT (new_vec)
+      = NON_DEFAULT_TEMPLATE_ARGS_COUNT (t);
 
   return new_vec;
 }
@@ -13433,7 +13439,13 @@ tsubst_template_args (tree t, tree args, tsubst_flags_t complain, tree in_decl)
      if it doesn't contain any nested TREE_VEC.  */
   if (NON_DEFAULT_TEMPLATE_ARGS_COUNT (orig_t))
     {
-      int count = GET_NON_DEFAULT_TEMPLATE_ARGS_COUNT (orig_t);
+      /* If ARGS defines a number for the defaulted argument count then that's
+	 the correct number to propagate. Otherwise, assume the number of
+	 defaulted arguments after substitution equals the number of default
+	 arguments before substitution (i.e. ORIG_T).  */
+      int count = args && NON_DEFAULT_TEMPLATE_ARGS_COUNT (args)
+		    ? GET_NON_DEFAULT_TEMPLATE_ARGS_COUNT (args)
+		    : GET_NON_DEFAULT_TEMPLATE_ARGS_COUNT (orig_t);
       count += expanded_len_adjust;
       SET_NON_DEFAULT_TEMPLATE_ARGS_COUNT (t, count);
     }
@@ -21874,6 +21886,12 @@ fn_type_unification (tree fn,
 	excessive_deduction_depth = false;
     }
 
+  /* If all template parameters were explicitly given, treat them like default
+     template arguments for diagnostics. NON_DEFAULT_TEMPLATE_ARGS_COUNT must be
+     present for SET_EXPLICIT_TEMPLATE_ARGS_P.  */
+  if (explicit_targs && NON_DEFAULT_TEMPLATE_ARGS_COUNT (targs))
+    SET_EXPLICIT_TEMPLATE_ARGS_P (targs);
+
   return r;
 }
 
@@ -22516,9 +22534,17 @@ type_unification_real (tree tparms,
 	     be NULL_TREE or ERROR_MARK_NODE, so we do not need
 	     to explicitly check cxx_dialect here.  */
 	  if (TREE_PURPOSE (TREE_VEC_ELT (tparms, i)))
-	    /* OK, there is a default argument.  Wait until after the
-	       conversion check to do substitution.  */
-	    continue;
+	    {
+	      /* The position of the first default template argument,
+		 is also the number of non-defaulted arguments in TARGS.
+		 Record that.  */
+	      if (!NON_DEFAULT_TEMPLATE_ARGS_COUNT (targs))
+		SET_NON_DEFAULT_TEMPLATE_ARGS_COUNT (targs, i);
+
+	      /* OK, there is a default argument.  Wait until after the
+		 conversion check to do substitution.  */
+	      continue;
+	    }
 
 	  /* If the type parameter is a parameter pack, then it will
 	     be deduced to an empty parameter pack.  */
@@ -22621,14 +22647,7 @@ type_unification_real (tree tparms,
 	  if (arg == error_mark_node)
 	    return 1;
 	  else if (arg)
-	    {
-	      TREE_VEC_ELT (targs, i) = arg;
-	      /* The position of the first default template argument,
-		 is also the number of non-defaulted arguments in TARGS.
-		 Record that.  */
-	      if (!NON_DEFAULT_TEMPLATE_ARGS_COUNT (targs))
-		SET_NON_DEFAULT_TEMPLATE_ARGS_COUNT (targs, i);
-	    }
+	    TREE_VEC_ELT (targs, i) = arg;
 	}
 
       if (saw_undeduced++ == 1)
@@ -24998,6 +25017,9 @@ get_partial_spec_bindings (tree tmpl, tree spec_tmpl, tree args)
   if (!template_template_parm_bindings_ok_p (tparms, deduced_args))
     return NULL_TREE;
 
+  if (CHECKING_P)
+    SET_NON_DEFAULT_TEMPLATE_ARGS_COUNT (deduced_args, ntparms);
+
   return deduced_args;
 }
 
diff --git a/gcc/testsuite/g++.dg/debug/dwarf2/template-params-12n.C b/gcc/testsuite/g++.dg/debug/dwarf2/template-params-12n.C
index d3c1f589f87..b88bf7d4b34 100644
--- a/gcc/testsuite/g++.dg/debug/dwarf2/template-params-12n.C
+++ b/gcc/testsuite/g++.dg/debug/dwarf2/template-params-12n.C
@@ -1,6 +1,6 @@
 // { dg-options "-gdwarf-2 -dA" }
 // { dg-final { scan-assembler-times "DIE \\(\[^\n\]*\\) DW_TAG_template_value_param" 1 } }
-// { dg-final { scan-assembler-times "DIE \\(\[^\n\]*\\) DW_TAG_template_value_param\[^\n\]*\n\[^\n\]* DW_AT_name\n\[^\n\]* DW_AT_type\n\[^\n\]* DW_AT_const_value" 1 } }
+// { dg-final { scan-assembler-times "DIE \\(\[^\n\]*\\) DW_TAG_template_value_param\[^\n\]*\n\[^\n\]* DW_AT_name\n\[^\n\]* DW_AT_type\n(?:\[^\n\]* DW_AT_default_value\n)?\[^\n\]* DW_AT_const_value" 1 } }
 #include "template-params-12.H"
 /* We get const_value for NULL pointers to member functions.  */
 #if __cplusplus > 199711L // Ugh, C++98 barfs at both the cast and the overload.
diff --git a/gcc/testsuite/g++.dg/diagnostic/default-template-args-1.C b/gcc/testsuite/g++.dg/diagnostic/default-template-args-1.C
new file mode 100644
index 00000000000..7a535515740
--- /dev/null
+++ b/gcc/testsuite/g++.dg/diagnostic/default-template-args-1.C
@@ -0,0 +1,73 @@
+// { dg-options "-fpretty-templates" }
+// { dg-do compile { target c++11 } }
+
+template <int a = 1>
+  [[deprecated]] void f0(); // { dg-message "'template<int a> void f0\\(\\)'" }
+
+template <int a>
+  [[deprecated]] void f1(); // { dg-message "'template<int a> void f1\\(\\)'" }
+
+template <class a, int b = 1>
+  [[deprecated]] void f2(); // { dg-message "'template<class a, int b> void f2\\(\\)'" }
+
+template <class a, int b = 1>
+  [[deprecated]] void f3(a); // { dg-message "'template<class a, int b> void f3\\(a\\)'" }
+
+template <class a>
+  [[deprecated]] void f4(a); // { dg-message "'template<class a> void f4\\(a\\)'" }
+
+template <>
+  [[deprecated]] void f4<int>(int);
+
+template <>
+  [[deprecated]] void f4(float);
+
+template <class a, class b = int>
+  [[deprecated]] void f5(a);
+
+template void f5<float>(float); // { dg-error "'void f5<a>\\(a\\) .with a = float.'" }
+
+template <class a, class b = int>
+  struct c
+  {
+    template <class d, class e = int>
+      [[deprecated]] static void f(d);
+  };
+
+template <class T>
+struct B { typedef T X; };
+
+template <class U>
+struct D
+{
+  template <class V = typename B<U>::X>
+    [[deprecated]] static void foo (typename B<V>::X);
+};
+
+int main()
+{
+  f0();            // { dg-warning "'void f0\\(\\)'" }
+  f1<1>();         // { dg-warning "'void f1<a>\\(\\) .with int a = 1.'" }
+  f2<int>();       // { dg-warning "'void f2<a>\\(\\) .with a = int.'" }
+  f3(1);           // { dg-warning "'void f3\\(a\\) .with a = int.'" }
+  f3<float>(1);    // { dg-warning "'void f3<a>\\(a\\) .with a = float.'" }
+  f3<float, 2>(1); // { dg-warning "'void f3<a, b>\\(a\\) .with a = float; int b = 2.'" }
+  f4(1.);          // { dg-warning "'void f4\\(a\\) .with a = double.'" }
+  f4(1);           // { dg-warning "'void f4<int>\\(int\\)'" }
+  f4(1.f);         // { dg-warning "'void f4\\(float\\)'" }
+
+  f0(0); // { dg-error "" }
+  f1(0); // { dg-error "" }
+  f2(0); // { dg-error "" }
+  f3();  // { dg-error "" }
+  f4();  // { dg-error "" }
+
+  c<int>::f(1.);    // { dg-warning "'static void c<a>::f\\(d\\) .with d = double; a = int.'" }
+  c<int>::f<int>(1);    // { dg-warning "'static void c<a>::f<d>\\(d\\) .with d = int; a = int.'" }
+  c<int>::f<float, int>(1.f);    // { dg-warning "'static void c<a>::f<d, e>\\(d\\) .with d = float; e = int; a = int.'" }
+  c<float, int>::f(1.);    // { dg-warning "'static void c<a, b>::f\\(d\\) .with d = double; a = float; b = int.'" }
+  c<float, int>::f<int>(1);    // { dg-warning "'static void c<a, b>::f<d>\\(d\\) .with d = int; a = float; b = int.'" }
+  c<float, int>::f<float, int>(1.f);    // { dg-warning "'static void c<a, b>::f<d, e>\\(d\\) .with d = float; e = int; a = float; b = int.'" }
+
+  D<int>::foo(1); // { dg-warning "'static void D<U>::foo\\(typename B<V>::X\\) .with U = int; typename B<V>::X = int.'" }
+}
diff --git a/gcc/testsuite/g++.dg/diagnostic/default-template-args-2.C b/gcc/testsuite/g++.dg/diagnostic/default-template-args-2.C
new file mode 100644
index 00000000000..a82709f5785
--- /dev/null
+++ b/gcc/testsuite/g++.dg/diagnostic/default-template-args-2.C
@@ -0,0 +1,37 @@
+// { dg-options "-fno-pretty-templates" }
+// { dg-do compile { target c++11 } }
+
+template <int a = 1>
+  [[deprecated]] void f0();
+
+template <int a>
+  [[deprecated]] void f1();
+
+template <class a, int b = 1>
+  [[deprecated]] void f2();
+
+template <class a, int b = 1>
+  [[deprecated]] void f3(a);
+
+template <class a>
+  [[deprecated]] void f4(a);
+
+template <>
+  [[deprecated]] void f4<int>(int);
+
+template <class a, class b = int>
+  [[deprecated]] void f5(a);
+
+template void f5<float>(float); // { dg-error "'void f5<float>\\(float\\)'" }
+
+int main()
+{
+  f0();            // { dg-warning "'void f0\\(\\)'" }
+  f1<1>();         // { dg-warning "'void f1<1>\\(\\)'" }
+  f2<int>();       // { dg-warning "'void f2<int>\\(\\)'" }
+  f3(1);           // { dg-warning "'void f3\\(int\\)'" }
+  f3<float>(1);    // { dg-warning "'void f3<float>\\(float\\)'" }
+  f3<float, 2>(1); // { dg-warning "'void f3<float, 2>\\(float\\)'" }
+  f4(1.);          // { dg-warning "'void f4\\(double\\)'" }
+  f4(1);           // { dg-warning "'void f4<int>\\(int\\)'" }
+}
diff --git a/gcc/testsuite/g++.dg/diagnostic/param-type-mismatch-2.C b/gcc/testsuite/g++.dg/diagnostic/param-type-mismatch-2.C
index de7570a6efa..0aa45404283 100644
--- a/gcc/testsuite/g++.dg/diagnostic/param-type-mismatch-2.C
+++ b/gcc/testsuite/g++.dg/diagnostic/param-type-mismatch-2.C
@@ -141,7 +141,7 @@ int test_7 (int first, const char *second, float third)
                                          |
                                          const char*
      { dg-end-multiline-output "" } */
-  // { dg-message "initializing argument 2 of 'int test_7\\(int, T, float\\) .with T = const char\\*\\*.'" "" { target *-*-* } test_7_decl }
+  // { dg-message "initializing argument 2 of 'int test_7<T>\\(int, T, float\\) .with T = const char\\*\\*.'" "" { target *-*-* } test_7_decl }
   /* { dg-begin-multiline-output "" }
  int test_7 (int one, T two, float three);
                       ~~^~~
diff --git a/gcc/testsuite/g++.dg/ext/pretty1.C b/gcc/testsuite/g++.dg/ext/pretty1.C
index 06608ae30eb..c5bfd6082a7 100644
--- a/gcc/testsuite/g++.dg/ext/pretty1.C
+++ b/gcc/testsuite/g++.dg/ext/pretty1.C
@@ -60,7 +60,7 @@ __assert_fail (const char *cond, const char *file, unsigned int line,
   abort ();
 }
 
-// { dg-final { scan-assembler "int bar\\(T\\).*with T = int" } }
+// { dg-final { scan-assembler "int bar<int>\\(int\\)" } }
 // { dg-final { scan-assembler "top level" } }
 // { dg-final { scan-assembler "int main\\(\\)" } }
 // { dg-final { scan-assembler "int bar\\(T\\).*with T = double" } }
diff --git a/gcc/testsuite/g++.dg/goacc/template.C b/gcc/testsuite/g++.dg/goacc/template.C
index 10d3f446da7..4fcd88bfc56 100644
--- a/gcc/testsuite/g++.dg/goacc/template.C
+++ b/gcc/testsuite/g++.dg/goacc/template.C
@@ -157,12 +157,12 @@ main ()
 }
 
 /* { dg-final { scan-tree-dump-times {(?n)^OpenACC routine '[^']+' has 'nohost' clause\.$} 4 oaccloops } }
-   { dg-final { scan-tree-dump-times {(?n)^OpenACC routine 'T accDouble\(int\) \[with T = char\]' has 'nohost' clause\.$} 1 oaccloops { target { ! offloading_enabled } } } }
+   { dg-final { scan-tree-dump-times {(?n)^OpenACC routine 'T accDouble<T>\(int\) \[with T = char\]' has 'nohost' clause\.$} 1 oaccloops { target { ! offloading_enabled } } } }
    { dg-final { scan-tree-dump-times {(?n)^OpenACC routine 'accDouble<char>\(int\)char' has 'nohost' clause\.$} 1 oaccloops { target offloading_enabled } } }
-   { dg-final { scan-tree-dump-times {(?n)^OpenACC routine 'T accDouble\(int\) \[with T = int\]' has 'nohost' clause\.$} 1 oaccloops { target { ! offloading_enabled } } } }
+   { dg-final { scan-tree-dump-times {(?n)^OpenACC routine 'T accDouble<T>\(int\) \[with T = int\]' has 'nohost' clause\.$} 1 oaccloops { target { ! offloading_enabled } } } }
    { dg-final { scan-tree-dump-times {(?n)^OpenACC routine 'accDouble<int>\(int\)int' has 'nohost' clause\.$} 1 oaccloops { target offloading_enabled } } }
-   { dg-final { scan-tree-dump-times {(?n)^OpenACC routine 'T accDouble\(int\) \[with T = float\]' has 'nohost' clause\.$} 1 oaccloops { target { ! offloading_enabled } } } }
+   { dg-final { scan-tree-dump-times {(?n)^OpenACC routine 'T accDouble<T>\(int\) \[with T = float\]' has 'nohost' clause\.$} 1 oaccloops { target { ! offloading_enabled } } } }
    { dg-final { scan-tree-dump-times {(?n)^OpenACC routine 'accDouble<float>\(int\)float' has 'nohost' clause\.$} 1 oaccloops { target offloading_enabled } } }
-   { dg-final { scan-tree-dump-times {(?n)^OpenACC routine 'T accDouble\(int\) \[with T = double\]' has 'nohost' clause\.$} 1 oaccloops { target { ! offloading_enabled } } } }
+   { dg-final { scan-tree-dump-times {(?n)^OpenACC routine 'T accDouble<T>\(int\) \[with T = double\]' has 'nohost' clause\.$} 1 oaccloops { target { ! offloading_enabled } } } }
    { dg-final { scan-tree-dump-times {(?n)^OpenACC routine 'accDouble<double>\(int\)double' has 'nohost' clause\.$} 1 oaccloops { target offloading_enabled } } }
    TODO See PR101551 for 'offloading_enabled' differences.  */
diff --git a/gcc/testsuite/g++.dg/gomp/declare-variant-7.C b/gcc/testsuite/g++.dg/gomp/declare-variant-7.C
index 7dda899578a..9cb654cb5e6 100644
--- a/gcc/testsuite/g++.dg/gomp/declare-variant-7.C
+++ b/gcc/testsuite/g++.dg/gomp/declare-variant-7.C
@@ -70,6 +70,6 @@ test ()
   s.f12 (0.0);		// { dg-final { scan-tree-dump-times "S<1>::f11<double> \\\(&s, 0.0\\\);" 1 "gimple" } }
   s.f14 (0LL);		// { dg-final { scan-tree-dump-times "S<1>::f13<long long int> \\\(&s, 0\\\);" 1 "gimple" } }
   T<0> t;
-  t.f16 (s);		// { dg-final { scan-tree-dump-times "T<0>::f16<S<1> > \\\(&t, s\\\);" 1 "gimple" } }
-  t.f18 (s);		// { dg-final { scan-tree-dump-times "T<0>::f18<S<1> > \\\(&t, s\\\);" 1 "gimple" } }
+  t.f16 (s);		// { dg-final { scan-tree-dump-times "T<0>::f16 \\\(&t, s\\\);" 1 "gimple" } }
+  t.f18 (s);		// { dg-final { scan-tree-dump-times "T<0>::f18 \\\(&t, s\\\);" 1 "gimple" } }
 }
diff --git a/gcc/testsuite/g++.dg/template/error40.C b/gcc/testsuite/g++.dg/template/error40.C
index c5df56fc1be..16a44d1819f 100644
--- a/gcc/testsuite/g++.dg/template/error40.C
+++ b/gcc/testsuite/g++.dg/template/error40.C
@@ -8,11 +8,11 @@ struct A
 
 void foo(void)
 {
-  A<void> a = 0;		// { dg-error "A<void, 0, 1>" }
+  A<void> a = 0;		// { dg-error "A<void>" }
 }
 
-template <class T> T f(T);	    // { dg-message "int f<int>.int." }
-template <class T> T f(T, int = 0); // { dg-message "" }
+template <class T> T f(T);	    // { dg-message "int f.int." }
+template <class T> T f(T, int = 0); // { dg-message "int f.int, int." }
 
 template <class T>
 struct B
diff --git a/gcc/testsuite/g++.old-deja/g++.ext/pretty3.C b/gcc/testsuite/g++.old-deja/g++.ext/pretty3.C
index 6348ae1ee67..30c7ecd5065 100644
--- a/gcc/testsuite/g++.old-deja/g++.ext/pretty3.C
+++ b/gcc/testsuite/g++.old-deja/g++.ext/pretty3.C
@@ -35,7 +35,7 @@ template<> void f1<int> (int)
   
   if (strcmp (function, "f1<int>"))
     bad = true;
-  if (strcmp (pretty, "void f1(T) [with T = int]"))
+  if (strcmp (pretty, "void f1<int>(int)"))
     bad = true;
 }
 
diff --git a/gcc/testsuite/g++.old-deja/g++.pt/memtemp77.C b/gcc/testsuite/g++.old-deja/g++.pt/memtemp77.C
index 6dd4b546c0c..93dbf4b489f 100644
--- a/gcc/testsuite/g++.old-deja/g++.pt/memtemp77.C
+++ b/gcc/testsuite/g++.old-deja/g++.pt/memtemp77.C
@@ -19,7 +19,7 @@ const char* S3<char>::h(int) { return __PRETTY_FUNCTION__; }
 int main()
 {
   if (strcmp (S3<double>::h(7), 
-	      "static const char* S3<T>::h(U) [with U = int; T = double]") == 0)
+	      "static const char* S3<double>::h(int)") == 0)
     return 0;
   else 
     return 1;

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

* Re: [RFC] c++: Print function template parms when relevant (was: [PATCH v4] c++: Add gnu::diagnose_as attribute)
  2021-11-08 16:40     ` [RFC] c++: Print function template parms when relevant (was: [PATCH v4] c++: Add gnu::diagnose_as attribute) Matthias Kretz
@ 2021-11-08 20:00       ` Matthias Kretz
  2021-11-16 20:25         ` Jason Merrill
  2021-11-17  6:09       ` Jason Merrill
  1 sibling, 1 reply; 24+ messages in thread
From: Matthias Kretz @ 2021-11-08 20:00 UTC (permalink / raw)
  To: gcc-patches, Jason Merrill

I forgot to mention why I tagged it [RFC]: I needed one more bit of 
information on the template args TREE_VEC to encode EXPLICIT_TEMPLATE_ARGS_P. 
Its TREE_CHAIN already points to an integer constant denoting the number of 
non-default arguments, so I couldn't trivially replace that. Therefore, I used 
the sign of that integer. I was hoping to find a cleaner solution, though.

-Matthias

On Monday, 8 November 2021 17:40:44 CET Matthias Kretz wrote:
> On Tuesday, 17 August 2021 20:31:54 CET Jason Merrill wrote:
> > > 2. Given a DECL_TI_ARGS tree, can I query whether an argument was
> > > deduced
> > > or explicitly specified? I'm asking because I still consider diagnostics
> > > of function templates unfortunate. `template <class T> void f()` is
> > > fine,
> > > as is `void f(T) [with T = float]`, but `void f() [with T = float]`
> > > could
> > > be better. I.e. if the template parameter appears somewhere in the
> > > function parameter list, dump_template_parms would only produce noise.
> > > If, however, the template parameter was given explicitly, it would be
> > > nice if it could show up accordingly in diagnostics.
> > 
> > NON_DEFAULT_TEMPLATE_ARGS_COUNT has that information, though there are
> > some issues with it.  Attached is my WIP from May to improve it
> > somewhat, if that's interesting.
> 
> It is interesting. I used your patch to come up with the attached. Patch. I
> must say, I didn't try to read through all the cp/pt.c code to understand
> all of what you did there (which is why my ChangeLog entry says "Jason?"),
> but it works for me (and all of `make check`).
> 
> Anyway, I'd like to propose the following before finishing my diagnose_as
> patch. I believe it's useful to fix this part first. The diagnostic/default-
> template-args-[12].C tests show a lot of examples of the intent of this
> patch. And the remaining changes to the testsuite show how it changes
> diagnostic output.
> 
> ---------------------- 8< --------------------
> 
> The choice when to print a function template parameter was still
> suboptimal. That's because sometimes the function template parameter
> list only adds noise, while in other situations the lack of a function
> template parameter list makes diagnostic messages hard to understand.
> 
> The general idea of this change is to print template parms wherever they
> would appear in the source code as well. Thus, the diagnostics code
> needs to know whether any template parameter was given explicitly.
> 
> Signed-off-by: Matthias Kretz <m.kretz@gsi.de>
> 
> gcc/testsuite/ChangeLog:
> 
>         * g++.dg/debug/dwarf2/template-params-12n.C: Optionally, allow
>         DW_AT_default_value.
>         * g++.dg/diagnostic/default-template-args-1.C: New.
>         * g++.dg/diagnostic/default-template-args-2.C: New.
>         * g++.dg/diagnostic/param-type-mismatch-2.C: Expect template
>         parms in diagnostic.
>         * g++.dg/ext/pretty1.C: Expect function template specialization
>         to not pretty-print template parms.
>         * g++.old-deja/g++.ext/pretty3.C: Ditto.
>         * g++.old-deja/g++.pt/memtemp77.C: Ditto.
>         * g++.dg/goacc/template.C: Expect function template parms for
>         explicit arguments.
>         * g++.dg/gomp/declare-variant-7.C: Expect no function template
>         parms for deduced arguments.
>         * g++.dg/template/error40.C: Expect only non-default template
>         arguments in diagnostic.
> 
> gcc/cp/ChangeLog:
> 
>         * cp-tree.h (GET_NON_DEFAULT_TEMPLATE_ARGS_COUNT): Return
>         absolute value of stored constant.
>         (EXPLICIT_TEMPLATE_ARGS_P): New.
>         (SET_EXPLICIT_TEMPLATE_ARGS_P): New.
>         (TFF_AS_PRIMARY): New constant.
>         * error.c (get_non_default_template_args_count): Avoid
>         GET_NON_DEFAULT_TEMPLATE_ARGS_COUNT if
>         NON_DEFAULT_TEMPLATE_ARGS_COUNT is a NULL_TREE. Make independent
>         of flag_pretty_templates.
>         (dump_template_bindings): Add flags parameter to be passed to
>         get_non_default_template_args_count. Print only non-default
>         template arguments.
>         (dump_function_decl): Call dump_function_name and dump_type of
>         the DECL_CONTEXT with specialized template and set
>         TFF_AS_PRIMARY for their flags.
>         (dump_function_name): Add and document conditions for calling
>         dump_template_parms.
>         (dump_template_parms): Print only non-default template
>         parameters.
>         * pt.c (determine_specialization): Jason?
>         (template_parms_level_to_args): Jason?
>         (copy_template_args): Jason?
>         (fn_type_unification): Set EXPLICIT_TEMPLATE_ARGS_P on the
>         template arguments tree if any template parameter was explicitly
>         given.
>         (type_unification_real): Jason?
>         (get_partial_spec_bindings): Jason?
>         (tsubst_template_args): Determine number of defaulted arguments
>         from new argument vector, if possible.
> ---
>  gcc/cp/cp-tree.h                              | 18 +++-
>  gcc/cp/error.c                                | 83 ++++++++++++++-----
>  gcc/cp/pt.c                                   | 58 +++++++++----
>  .../g++.dg/debug/dwarf2/template-params-12n.C |  2 +-
>  .../diagnostic/default-template-args-1.C      | 73 ++++++++++++++++
>  .../diagnostic/default-template-args-2.C      | 37 +++++++++
>  .../g++.dg/diagnostic/param-type-mismatch-2.C |  2 +-
>  gcc/testsuite/g++.dg/ext/pretty1.C            |  2 +-
>  gcc/testsuite/g++.dg/goacc/template.C         |  8 +-
>  gcc/testsuite/g++.dg/gomp/declare-variant-7.C |  4 +-
>  gcc/testsuite/g++.dg/template/error40.C       |  6 +-
>  gcc/testsuite/g++.old-deja/g++.ext/pretty3.C  |  2 +-
>  gcc/testsuite/g++.old-deja/g++.pt/memtemp77.C |  2 +-
>  13 files changed, 242 insertions(+), 55 deletions(-)
>  create mode 100644
> gcc/testsuite/g++.dg/diagnostic/default-template-args-1.C create mode
> 100644 gcc/testsuite/g++.dg/diagnostic/default-template-args-2.C


-- 
──────────────────────────────────────────────────────────────────────────
 Dr. Matthias Kretz                           https://mattkretz.github.io
 GSI Helmholtz Centre for Heavy Ion Research               https://gsi.de
 stdₓ::simd
──────────────────────────────────────────────────────────────────────────




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

* [PATCH v5] c++: Add gnu::diagnose_as attribute
  2021-09-08  2:21   ` [PATCH v4] c++: Add gnu::diagnose_as attribute Jason Merrill
@ 2021-11-15  0:35     ` Matthias Kretz
  0 siblings, 0 replies; 24+ messages in thread
From: Matthias Kretz @ 2021-11-15  0:35 UTC (permalink / raw)
  To: gcc-patches, Jason Merrill

[-- Attachment #1: Type: text/plain, Size: 17825 bytes --]

Sorry for taking so long. I hope we can still get this done for GCC 12.

One open question: If we change std::__cxx11::basic_string<char> to 
std::string with this feature, should DWARF strings change or not? I.e. should 
diagnose_as be conditional on (pp->flags & pp_c_flag_gnu_v3)? If these strings 
are only for user consumption, I think the DWARF strings should be affected by 
the attribute...

Oh, and note that the current patch depends on the "c++: Print function 
template parms when relevant" patch I sent on Nov 8th.

On Wednesday, 8 September 2021 04:21:51 CEST Jason Merrill wrote:
> On 7/23/21 4:58 AM, Matthias Kretz wrote:
> > gcc/cp/ChangeLog:
> >          PR c++/89370
> >          * cp-tree.h: Add is_alias_template_p declaration.
> >          * decl2.c (is_alias_template_p): New function. Determines
> >          whether a given TYPE_DECL is actually an alias template that is
> >          still missing its template_info.
> 
> I still think you want to share code with get_underlying_template.  For
> the case where the alias doesn't have DECL_TEMPLATE_INFO yet, you can
> compare to current_template_args ().  Or you could do some initial
> processing that doesn't care about templates in the handler, and then do
> more in cp_parser_alias_declaration after the call to grokfield/start_decl.

I still don't understand how I could make use of get_underlying_template. I.e. 
I don't even understand how get_underlying_template answers any of the 
questions I need answered. I used way too much time trying to make this 
work...
 
> If you still think you need this function, let's call it
> is_renaming_alias_template or renaming_alias_template_p; using both is_
> and _p is redundant.  I don't have a strong preference which.

OK.
 
> >          (is_late_template_attribute): Decls with diagnose_as attribute
> >          are early attributes only if they are alias templates.
> 
> Is there a reason not to apply it early to other templates as well?

Unconditionally returning false for diagnose_as in is_late_template_attribute 
makes renamed class templates print without template parameter list. E.g.

  template <class T> struct [[diagnose_as("foo")]] A;
  using bar [[diagnose_as]] = A<int>;

  template <class T> struct A {
    template <class U> struct B {};
    using C [[diagnose_as]] = B<int>;
  };

could query for attributes. So IIUC, member types of class templates require 
late attributes.

> >          * error.c (dump_scope): When printing the name of a namespace,
> >          look for the diagnose_as attribute. If found, print the
> >          associated string instead of calling dump_decl.
> 
> Did you decide not to handle this in dump_decl, so we use the
> diagnose_as when referring to the namespace in non-scope contexts as well?

Good question. dump_decl is the more general place for handling the attribute 
and that's where I moved it to.

> > +  if (flag_diagnostics_use_aliases)
> > +    {
> > +      tree attr = lookup_attribute ("diagnose_as", DECL_ATTRIBUTES
> > (decl)); +      if (attr && TREE_VALUE (attr))
> > +       {
> > +         pp_cxx_ws_string (
> > +           pp, TREE_STRING_POINTER (TREE_VALUE (TREE_VALUE (attr))));
> 
> This pattern is used several places outside this function; can we factor
> it into something like
> 
> if (maybe_print_diagnose_as (special))
>    /* OK */;

Yes, I added the functions lookup_diagnose_as_attribute and 
dump_diagnose_as_alias to remove code duplication.

> Missing space before (

OK. I think I found and fixed all of them.

> > +         if (tmplate)
> > +           TREE_VALUE (*parms) = make_tree_vec (0);
> 
> This could use a comment.

Added.

> >          (dump_aggr_type): If the type has a diagnose_as attribute, print
> >          the associated string instead of printing the original type
> >          name. Print template parms only if the attribute was not applied
> >          to the instantiation / full specialization. Delay call to
> >          dump_scope until the diagnose_as attribute is found. If the
> >          attribute has a second argument, use it to override the context
> >          passed to dump_scope.
> > 
> > +             for (int i = 0; i < NUM_TMPL_ARGS (args); ++i)
> > +               {
> > +                 tree arg = TREE_VEC_ELT (args, i);
> > +                 while (INDIRECT_TYPE_P (arg))
> > +                   arg = TREE_TYPE (arg);
> > +                 if (WILDCARD_TYPE_P (arg))
> > +                   {
> > +                     tmplate = true;
> > +                     break;
> > +                   }
> > +               }
> 
> I think you want any_dependent_template_args_p (args)

Yes, except that I need `++processing_template_decl` before calling it (and 
decrement after it, of course). Is that acceptable?

> Checking WILDCARD_TYPE_P is generally not what you want; plenty of
> dependent types don't show up specifically as wildcards.  T*, for instance.

Right, that's why I had `while (INDIRECT_TYPE_P (arg)) arg = TREE_TYPE 
(arg);` before the wildcard test. But I replaced all of those with calls to 
any_dependent_template_arguments_p now.

> > +  if (diagnose_as)
> > +    pp_cxx_ws_string (pp, TREE_STRING_POINTER (
> > +                           TREE_VALUE (TREE_VALUE (diagnose_as))));
> 
> ( needs to go on the next line.  I'd format this as
> 
> if (diagnose_as)
>    pp_cxx_ws_string (pp, (TREE_STRING_POINTER
>                           (TREE_VALUE (TREE_VALUE (diagnose_as)))));
> 
> There's a lot of this formatting pattern in the patch.

All fixed.

> Missing space before (

fixed.
 
> >          (lang_decl_name): Ditto.
> >          (dump_function_decl): Walk the functions context list to
> >          determine whether a call to dump_template_scope is required.
> >          Ensure function templates diagnosed with pretty templates set
> >          TFF_TEMPLATE_NAME to skip dump_template_parms.
> >          (dump_function_name): Replace the function's identifier with the
> >          diagnose_as attribute value, if set. Expand
> >          DECL_FRIEND_PSEUDO_TEMPLATE_INSTANTIATION to DECL_USE_TEMPLATE
> >          and consequently call dump_template_parms with primary = false.
> >          (comparable_template_types_p): Consider the types not a template
> >          if one carries a diagnose_as attribute.
> 
> I'd think it would be more helpful to suppress diagnose_as if the types
> are comparable.

So instead of diagnosing e.g. 'std::string' vs. 'std::wstring' it should print 
'std::basic_string<char, ...>' vs. 'std::basic_string<wchar_t, ...>' here? I 
believe that would be more confusing than helpful. Either switch all aliases 
on or all of them off.

> >          (cp_parser_namespace_alias_definition): Allow optional
> >          attributes before and after the identifier. Fast exit, restoring
> >          input_location, if the expected CPP_EQ token is missing. Pass
> >          attributes to do_namespace_alias.
> > 
> > +  if (attributes
> > +       && !cp_parser_uncommitted_to_tentative_parse_p (parser))
> > +    pedwarn (input_location, OPT_Wpedantic,
> > +            "standard attributes on namespaces aliases must follow "
> > +            "the namespace alias name");
> 
> Maybe remember where we saw attributes and warn later, after we've
> committed to parsing as an alias.

OK.

> Or use cp_parser_skip_attributes_opt
> to avoid tentative parsing in the first place.

I don't see how to do that easily. We need to parse up to the RT_EQ token 
before we know whether it's a namespace alias or namespace definition.

> > +  if (nullptr == cp_parser_require (parser, CPP_EQ, RT_EQ))
> 
> The usual pattern is
> 
>   if (!cp_parser_require

done

> >          (handle_diagnose_as_attribute): New function; copied and
> >          adjusted from handle_abi_tag_attribute. If the given *node is a
> >          TYPE_DECL: allow no argument to the attribute, using DECL_NAME
> >          instead; apply the attribute to the type on the RHS in place,
> >          even if the type is complete. Allow 2 arguments when called from
> >          handle_diagnose_as_attribute. For type aliases, append
> >          CP_DECL_CONTEXT as second attribute argument when the RHS type
> >          has a different context. Warn about alias templates without
> >          matching template arguments. Apply the attribute to the primary
> >          template type for alias templates.
> 
> All this description of semantics should be in a comment rather than the
> CHangeLog.

done
 
> > +      /* Reject alias templates without wildcards on the innermost
> > template arg s
> > +         of the RHS type. E.g. template <class> using A = B;  */
> > +      if (DECL_LANG_SPECIFIC (decl)
> > +           && DECL_TEMPLATE_INFO (decl)
> > +           && DECL_ALIAS_TEMPLATE_P (DECL_TI_TEMPLATE (decl))
> > +           && PRIMARY_TEMPLATE_P (DECL_TI_TEMPLATE (decl)))
> > +       return error_mark_node;
> 
> I don't think this is doing anything useful; if we had already done
> push_template_decl by this point, it would reject all alias templates.

Without this test e.g.

  template <class T>
    using foo [[gnu::diagnose_as]] = typename bar<T>::inner;

is not rejected. I want to reject cases where the RHS is not a primary 
template because I cannot apply the attribute to bar<T>::inner until foo<U> 
instantiates bar<U>. Thus, if bar<U>::inner is used before the alias is used 
the diagnose_as attribute is not present.

> > +      // Add the DECL_CONTEXT of the alias to the attribute if it is
> > different +      // to the context of the type.
> 
> How about using the alias TYPE_DECL itself as the argument to the
> attribute on the type?  Then we wouldn't need to copy its name into a
> string, either.

Done.

New patch:

This attribute overrides the diagnostics output string for the entity it
appertains to. The motivation is to improve QoI for library TS
implementations, where diagnostics have a very bad signal-to-noise ratio
due to the long namespaces involved.

With the attribute, it is possible to solve PR89370 and make
std::__cxx11::basic_string<_CharT, _Traits, _Alloc> appear as
std::string in diagnostic output without extra hacks to recognize the
type in the C++ frontend.

Signed-off-by: Matthias Kretz <m.kretz@gsi.de>

gcc/ChangeLog:

        PR c++/89370
        * doc/extend.texi: Document the diagnose_as attribute.
        * doc/invoke.texi: Document -fno-diagnostics-use-aliases.

gcc/c-family/ChangeLog:

        PR c++/89370
        * c.opt (fdiagnostics-use-aliases): New diagnostics flag.

gcc/cp/ChangeLog:

        PR c++/89370
        * cp-tree.h: Add is_renaming_alias_template declaration.
        * decl2.c (is_renaming_alias_template): New function. Determines
        whether a given TYPE_DECL is actually an alias template that is
        still missing its template_info.
        (is_late_template_attribute): Decls with diagnose_as attribute
        are early attributes only if they are alias templates.
        * error.c (dump_scope): Don't remove TFF_AS_PRIMARY flag.
        (lookup_diagnose_as_attribute): New function.
        (dump_diagnose_as_alias): New function.
        (dump_decl_name_or_diagnose_as): New function to replace
        dump_decl (pp, DECL_NAME(t), flags) and inspect the tree for the
        diagnose_as attribute before printing the DECL_NAME.
        (dump_template_scope): New function. Prints the scope of a
        template instance correctly applying diagnose_as attributes and
        adjusting the list of template parms accordingly.
        (dump_aggr_type): If the type has a diagnose_as attribute, print
        the associated string instead of printing the original type
        name. Print template parms only if the attribute was not applied
        to the instantiation / full specialization. Delay call to
        dump_scope until the diagnose_as attribute is found. If the
        attribute has a second argument, use it to override the context
        passed to dump_scope.
        (dump_simple_decl): Call dump_decl_name_or_diagnose_as instead
        of dump_decl.
        (lang_decl_name): Ditto.
        (dump_decl): Ditto. When printing the name of a namespace, look
        for the diagnose_as attribute. If found, print the associated
        string instead of calling dump_decl.
        (dump_function_decl): Walk the functions context list to
        determine whether a call to dump_template_scope is required.
        (dump_function_name): Replace the function's identifier with the
        diagnose_as attribute value, if set.
        (comparable_template_types_p): Consider the types not a template
        if one carries a diagnose_as attribute.
        (print_template_differences): Replace the identifier with the
        diagnose_as attribute value on the most general template, if it
        is set.
        * name-lookup.c (handle_namespace_attrs): Handle the diagnose_as
        attribute on namespaces. Ensure exactly one string argument.
        Ensure previous diagnose_as attributes used the same name.
        'diagnose_as' on namespace aliases are forwarded to the original
        namespace. Support no-argument 'diagnose_as' on namespace
        aliases.
        (do_namespace_alias): Add attributes parameter and call
        handle_namespace_attrs.
        * name-lookup.h (do_namespace_alias): Add attributes tree
        parameter.
        * parser.c (cp_parser_declaration): If the next token is
        RID_NAMESPACE, tentatively parse a namespace alias definition.
        If this fails expect a namespace definition.
        (cp_parser_namespace_alias_definition): Allow optional
        attributes before and after the identifier. Fast exit, restoring
        input_location, if the expected CPP_EQ token is missing. Pass
        attributes to do_namespace_alias.
        * tree.c (cxx_attribute_table): Add diagnose_as attribute to the
        table.
        (check_diagnose_as_redeclaration): New function; copied and
        adjusted from check_abi_tag_redeclaration.
        (handle_diagnose_as_attribute): New function; copied and
        adjusted from handle_abi_tag_attribute. If the given *node is a
        TYPE_DECL: allow no argument to the attribute, using the
        TYPE_DECL as attribute value instead; apply the attribute to the
        type on the RHS in place, even if the type is complete. Allow 2
        arguments when called from handle_diagnose_as_attribute. For
        type aliases, append CP_DECL_CONTEXT as second attribute
        argument when the RHS type has a different context. Warn about
        alias templates without matching template arguments. Apply the
        attribute to the primary template type for alias templates.

gcc/testsuite/ChangeLog:

        PR c++/89370
        * g++.dg/diagnostic/default-template-args-1.C: Add testcase with
        partial specialization.
        * g++.dg/diagnostic/diagnose-as1.C: New test.
        * g++.dg/diagnostic/diagnose-as2.C: New test.
        * g++.dg/diagnostic/diagnose-as3.C: New test.
        * g++.dg/diagnostic/diagnose-as4.C: New test.
        * g++.dg/diagnostic/diagnose-as5.C: New test.
        * g++.dg/diagnostic/diagnose-as6.C: New test.
        * g++.dg/diagnostic/diagnose-as7.C: New test.
---
 gcc/c-family/c.opt                            |   4 +
 gcc/cp/cp-tree.h                              |   1 +
 gcc/cp/decl2.c                                |  37 +++
 gcc/cp/error.c                                | 289 ++++++++++++++++--
 gcc/cp/name-lookup.c                          |  53 +++-
 gcc/cp/name-lookup.h                          |   2 +-
 gcc/cp/parser.c                               |  40 ++-
 gcc/cp/tree.c                                 | 166 ++++++++++
 gcc/doc/extend.texi                           |  45 +++
 gcc/doc/invoke.texi                           |   9 +-
 .../diagnostic/default-template-args-1.C      |   8 +
 .../g++.dg/diagnostic/diagnose-as1.C          | 228 ++++++++++++++
 .../g++.dg/diagnostic/diagnose-as2.C          | 144 +++++++++
 .../g++.dg/diagnostic/diagnose-as3.C          | 152 +++++++++
 .../g++.dg/diagnostic/diagnose-as4.C          | 158 ++++++++++
 .../g++.dg/diagnostic/diagnose-as5.C          |  21 ++
 .../g++.dg/diagnostic/diagnose-as6.C          |  45 +++
 .../g++.dg/diagnostic/diagnose-as7.C          |  43 +++
 18 files changed, 1400 insertions(+), 45 deletions(-)
 create mode 100644 gcc/testsuite/g++.dg/diagnostic/diagnose-as1.C
 create mode 100644 gcc/testsuite/g++.dg/diagnostic/diagnose-as2.C
 create mode 100644 gcc/testsuite/g++.dg/diagnostic/diagnose-as3.C
 create mode 100644 gcc/testsuite/g++.dg/diagnostic/diagnose-as4.C
 create mode 100644 gcc/testsuite/g++.dg/diagnostic/diagnose-as5.C
 create mode 100644 gcc/testsuite/g++.dg/diagnostic/diagnose-as6.C
 create mode 100644 gcc/testsuite/g++.dg/diagnostic/diagnose-as7.C


--
──────────────────────────────────────────────────────────────────────────
 Dr. Matthias Kretz                           https://mattkretz.github.io
 GSI Helmholtz Centre for Heavy Ion Research               https://gsi.de
 stdₓ::simd
──────────────────────────────────────────────────────────────────────────

[-- Attachment #2: 0001-c-Add-gnu-diagnose_as-attribute.patch --]
[-- Type: text/x-patch, Size: 69964 bytes --]

diff --git a/gcc/c-family/c.opt b/gcc/c-family/c.opt
index 06457ac739e..b1e809cfef9 100644
--- a/gcc/c-family/c.opt
+++ b/gcc/c-family/c.opt
@@ -1619,6 +1619,10 @@ fdiagnostics-show-template-tree
 C++ ObjC++ Var(flag_diagnostics_show_template_tree) Init(0)
 Print hierarchical comparisons when template types are mismatched.
 
+fdiagnostics-use-aliases
+C++ Var(flag_diagnostics_use_aliases) Init(1)
+Replace identifiers or scope names in diagnostics as defined by the diagnose_as attribute.
+
 fdirectives-only
 C ObjC C++ ObjC++
 Preprocess directives only.
diff --git a/gcc/cp/cp-tree.h b/gcc/cp/cp-tree.h
index e1aa95afd05..13b2bd2aaae 100644
--- a/gcc/cp/cp-tree.h
+++ b/gcc/cp/cp-tree.h
@@ -6829,6 +6829,7 @@ extern tree grokfield (const cp_declarator *, cp_decl_specifier_seq *,
 		       tree, bool, tree, tree);
 extern tree grokbitfield (const cp_declarator *, cp_decl_specifier_seq *,
 			  tree, tree, tree);
+extern bool is_renaming_alias_template		(tree);
 extern tree splice_template_attributes		(tree *, tree);
 extern bool any_dependent_type_attributes_p	(tree);
 extern tree cp_reconstruct_complex_type		(tree, tree);
diff --git a/gcc/cp/decl2.c b/gcc/cp/decl2.c
index 32d3fe3636d..6f11850f875 100644
--- a/gcc/cp/decl2.c
+++ b/gcc/cp/decl2.c
@@ -1139,6 +1139,39 @@ grokbitfield (const cp_declarator *declarator,
   return value;
 }
 
+/* Return true iff DECL is an alias template of a class template. This predicate
+   also works before the alias template has its DECL_TEMPLATE_INFO.  */
+bool
+is_renaming_alias_template (tree decl)
+{
+  if (TREE_CODE (decl) != TYPE_DECL)
+    return false;
+
+  tree type = strip_typedefs (TREE_TYPE (decl), nullptr, 0);
+  if (!TYPE_LANG_SPECIFIC (type) || !TYPE_TEMPLATE_INFO (type))
+    return false;
+
+  /* Ensure it's a real alias template not just
+       template <class T> struct A {
+	 struct B {};
+	 template <class U> struct C {};
+	 using D [[gnu::diagnose_as]] = B;
+	 using E [[gnu::diagnose_as]] = C<int>;
+       };
+     A<T>::D and A<T>::E are not alias templates.
+     - For A<T>::D, the DECL_PRIMARY_TEMPLATE is A and not B, which would be the
+       case for a real alias template.
+     - For A<T>::E, the innermost template params belong to C but its template
+       args have no wildcard types, which would be the case for a real
+       alias template.  */
+  tree tmpl = CLASSTYPE_TI_TEMPLATE (type);
+  if (!PRIMARY_TEMPLATE_P (tmpl))
+    return false;
+
+  tree targs = INNERMOST_TEMPLATE_ARGS (CLASSTYPE_TI_ARGS (type));
+  return any_dependent_template_arguments_p (targs);
+}
+
 \f
 /* Returns true iff ATTR is an attribute which needs to be applied at
    instantiation time rather than template definition time.  */
@@ -1166,6 +1199,10 @@ is_late_template_attribute (tree attr, tree decl)
 	  || is_attribute_p ("used", name)))
     return false;
 
+  /* Allow alias templates to set diagnose_as on the RHS template.  */
+  if (is_attribute_p ("diagnose_as", name))
+    return !is_renaming_alias_template (decl);
+
   /* Attribute tls_model wants to modify the symtab.  */
   if (is_attribute_p ("tls_model", name))
     return true;
diff --git a/gcc/cp/error.c b/gcc/cp/error.c
index 86e9d12103a..9c09a3c57fa 100644
--- a/gcc/cp/error.c
+++ b/gcc/cp/error.c
@@ -35,6 +35,7 @@ along with GCC; see the file COPYING3.  If not see
 #include "internal-fn.h"
 #include "gcc-rich-location.h"
 #include "cp-name-hint.h"
+#include "attribs.h"
 
 #define pp_separate_with_comma(PP) pp_cxx_separate_with (PP, ',')
 #define pp_separate_with_semicolon(PP) pp_cxx_separate_with (PP, ';')
@@ -66,6 +67,7 @@ static void dump_alias_template_specialization (cxx_pretty_printer *, tree, int)
 static void dump_type (cxx_pretty_printer *, tree, int);
 static void dump_typename (cxx_pretty_printer *, tree, int);
 static void dump_simple_decl (cxx_pretty_printer *, tree, tree, int);
+static void dump_decl_name_or_diagnose_as (cxx_pretty_printer *, tree, int);
 static void dump_decl (cxx_pretty_printer *, tree, int);
 static void dump_template_decl (cxx_pretty_printer *, tree, int);
 static void dump_function_decl (cxx_pretty_printer *, tree, int);
@@ -104,6 +106,8 @@ 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 **);
+static tree lookup_diagnose_as_attribute (cxx_pretty_printer *, tree);
+static void dump_diagnose_as_alias (cxx_pretty_printer *, tree, tree, int);
 
 /* Struct for handling %H or %I, which require delaying printing the
    type until a postprocessing stage.  */
@@ -216,7 +220,7 @@ dump_module_suffix (cxx_pretty_printer *pp, tree decl)
 static void
 dump_scope (cxx_pretty_printer *pp, tree scope, int flags)
 {
-  int f = flags & (TFF_SCOPE | TFF_CHASE_TYPEDEF);
+  int f = flags & (TFF_SCOPE | TFF_CHASE_TYPEDEF | TFF_AS_PRIMARY);
 
   if (scope == NULL_TREE)
     return;
@@ -231,7 +235,7 @@ dump_scope (cxx_pretty_printer *pp, tree scope, int flags)
     {
       if (scope != global_namespace)
 	{
-          dump_decl (pp, scope, f);
+	  dump_decl (pp, scope, f);
 	  pp_cxx_colon_colon (pp);
 	}
     }
@@ -770,6 +774,60 @@ class_key_or_enum_as_string (tree t)
     return "struct";
 }
 
+/* Print out an enclosing scope of a template instance by inspecting the tree
+   SPECIAL of the template instantiation and the tree GENERAL of the most
+   general template in parallel under the control of FLAGS while adjusting
+   PARMS as needed. This allows the diagnose_as attribute to override the name
+   of full specializations, while hiding its template parameters, and to
+   override the name of partial specializations without falling back to printing
+   the template parameters of the general template decl.  */
+
+static void
+dump_template_scope (cxx_pretty_printer *pp, tree special, tree general,
+		     tree parms, int flags)
+{
+  if (special != general && CLASS_TYPE_P (special) && parms)
+    {
+      gcc_assert (CLASS_TYPE_P (general));
+      const bool tmplate
+	= TYPE_LANG_SPECIFIC (special) && CLASSTYPE_TEMPLATE_INFO (special)
+	    && (TREE_CODE (CLASSTYPE_TI_TEMPLATE (special)) != TEMPLATE_DECL
+		  || PRIMARY_TEMPLATE_P (CLASSTYPE_TI_TEMPLATE (special)));
+      dump_template_scope (pp, CP_TYPE_CONTEXT (special),
+			   CP_TYPE_CONTEXT (general),
+			   tmplate ? TREE_CHAIN (parms) : parms, flags);
+      flags |= TFF_UNQUALIFIED_NAME;
+      tree attr = lookup_diagnose_as_attribute (pp, TYPE_ATTRIBUTES (special));
+      if (attr)
+	{
+	  dump_diagnose_as_alias (pp, attr, NULL_TREE, flags);
+	  if (tmplate)
+	    /* Hide the template parameters of this level from
+	       dump_template_bindings so that the diagnose_as attribute makes
+	       the type appear as a non-template.  */
+	    TREE_VALUE (parms) = make_tree_vec (0);
+	}
+      else if (NON_DEFAULT_TEMPLATE_ARGS_COUNT (CLASSTYPE_TI_ARGS (special))
+	       && NON_DEFAULT_TEMPLATE_ARGS_COUNT (CLASSTYPE_TI_ARGS (general))
+	       && GET_NON_DEFAULT_TEMPLATE_ARGS_COUNT
+		    (CLASSTYPE_TI_ARGS (special))
+		    > GET_NON_DEFAULT_TEMPLATE_ARGS_COUNT
+		      (CLASSTYPE_TI_ARGS (general)))
+	/* If SPECIAL has more non-default template args than GENERAL, print
+	   SPECIAL (with TFF_AS_PRIMARY in FLAGS). Otherwise, template
+	   parameters will be missing in the output.  */
+	dump_aggr_type (pp, special, flags);
+      else
+	/* Printing without TFF_AS_PRIMARY is necessary to print partial
+	   specializations of class templates correctly. If GENERAL is not a
+	   partial specialization, TFF_AS_PRIMARY makes no difference.  */
+	dump_aggr_type (pp, general, flags & ~TFF_AS_PRIMARY);
+      pp_cxx_colon_colon (pp);
+    }
+  else
+    dump_scope (pp, special, flags);
+}
+
 /* Print out a class declaration T under the control of FLAGS,
    in the form `class foo'.  */
 
@@ -787,6 +845,7 @@ dump_aggr_type (cxx_pretty_printer *pp, tree t, int flags)
 
   tree decl = TYPE_NAME (t);
 
+  tree diagnose_as = NULL_TREE;
   if (decl)
     {
       typdef = (!DECL_ARTIFICIAL (decl)
@@ -810,11 +869,13 @@ dump_aggr_type (cxx_pretty_printer *pp, tree t, int flags)
 		&& (TREE_CODE (CLASSTYPE_TI_TEMPLATE (t)) != TEMPLATE_DECL
 		    || PRIMARY_TEMPLATE_P (CLASSTYPE_TI_TEMPLATE (t)));
       
-      if (! (flags & TFF_UNQUALIFIED_NAME))
-	dump_scope (pp, CP_DECL_CONTEXT (decl), flags | TFF_SCOPE);
-      flags &= ~TFF_UNQUALIFIED_NAME;
+      const tree context = CP_DECL_CONTEXT (decl);
+      tree diagnose_as_specialized = NULL_TREE;
       if (tmplate)
 	{
+	  diagnose_as_specialized = lookup_diagnose_as_attribute
+				      (pp, TYPE_ATTRIBUTES (t));
+
 	  /* Because the template names are mangled, we have to locate
 	     the most general template, and use that name.  */
 	  tree tpl = TYPE_TI_TEMPLATE (t);
@@ -823,9 +884,48 @@ dump_aggr_type (cxx_pretty_printer *pp, tree t, int flags)
 	    tpl = DECL_TI_TEMPLATE (tpl);
 	  decl = tpl;
 	}
+
+      if (flag_diagnostics_use_aliases)
+	{
+	  diagnose_as = lookup_diagnose_as_attribute
+			  (pp, TYPE_ATTRIBUTES (TREE_TYPE (decl)));
+	  if (diagnose_as_specialized
+		&& (!diagnose_as || diagnose_as_specialized != diagnose_as))
+	    {
+	      diagnose_as = diagnose_as_specialized;
+	      /* Skip dump_template_parms if diagnose_as applies to a full
+	         specialization.  */
+	      tree args = CLASSTYPE_TI_ARGS (t);
+	      args = INNERMOST_TEMPLATE_ARGS (args);
+	      gcc_assert (args);
+	      ++processing_template_decl;
+	      tmplate = any_dependent_template_arguments_p (args);
+	      --processing_template_decl;
+	    }
+	  /* Given
+
+	       template <class T> struct [[gnu::diagnose_as("AA")]] A {
+	         struct [[gnu::diagnose_as("BB")]] B {};
+	       };
+
+	     A<int>::B will reach the following branch and find the diagnose_as
+	     attribute for B.  */
+	  else if (!tmplate && !diagnose_as && TYPE_TEMPLATE_INFO (t))
+	    diagnose_as
+	      = lookup_diagnose_as_attribute
+		  (pp, TYPE_ATTRIBUTES (TREE_TYPE (TYPE_TI_TEMPLATE (t))));
+	}
+
+      if (diagnose_as)
+	dump_diagnose_as_alias (pp, diagnose_as, context, flags);
+      else if (! (flags & TFF_UNQUALIFIED_NAME))
+	dump_scope (pp, context, flags | TFF_SCOPE);
+      flags &= ~TFF_UNQUALIFIED_NAME;
     }
 
-  if (LAMBDA_TYPE_P (t))
+  if (diagnose_as)
+    /* identifier printed via dump_diagnose_as_alias above */;
+  else if (LAMBDA_TYPE_P (t))
     {
       /* A lambda's "type" is essentially its signature.  */
       pp_string (pp, M_("<lambda"));
@@ -1147,7 +1247,7 @@ dump_simple_decl (cxx_pretty_printer *pp, tree t, tree type, int flags)
 	  pp_string (pp, " capture>");
 	}
       else
-	dump_decl (pp, DECL_NAME (t), flags);
+	dump_decl_name_or_diagnose_as (pp, t, flags);
     }
   else if (DECL_DECOMPOSITION_P (t))
     pp_string (pp, M_("<structured bindings>"));
@@ -1191,6 +1291,64 @@ dump_decl_name (cxx_pretty_printer *pp, tree t, int flags)
   pp_cxx_tree_identifier (pp, t);
 }
 
+/* Returns a TREE_LIST of diagnose_as attribute arguments if set, NULL_TREE
+   otherwise.  */
+
+static tree
+lookup_diagnose_as_attribute (cxx_pretty_printer *, tree attributes)
+{
+  if (!flag_diagnostics_use_aliases)
+    return NULL_TREE;
+  tree attr = lookup_attribute ("diagnose_as", attributes);
+  if (!attr)
+    return NULL_TREE;
+  return TREE_VALUE (attr);
+}
+
+/* Print out the diagnose_as attribute argument list ATTR under the control of
+   FLAGS. If FLAGS does not have TFF_UNQUALIFIED_NAME set, include the CONTEXT
+   in the output. ATTR can override CONTEXT.  */
+
+static void
+dump_diagnose_as_alias (cxx_pretty_printer *pp, tree diagnose_as, tree context,
+			int flags)
+{
+
+  tree arg0 = TREE_VALUE (diagnose_as);
+  if (TREE_CODE (arg0) == STRING_CST)
+    {
+      if (! (flags & TFF_UNQUALIFIED_NAME))
+	{
+	  if (TREE_CHAIN (diagnose_as))
+	    context = TREE_VALUE (TREE_CHAIN (diagnose_as));
+	  if (context)
+	    dump_scope (pp, context, flags | TFF_SCOPE);
+	}
+      pp_cxx_ws_string (pp, TREE_STRING_POINTER (arg0));
+    }
+  else
+    {
+      gcc_assert (TREE_CODE (arg0) == TYPE_DECL);
+      dump_simple_decl (pp, arg0,
+			DECL_ORIGINAL_TYPE (arg0) ? DECL_ORIGINAL_TYPE (arg0)
+						  : TREE_TYPE (arg0),
+			flags & ~TFF_DECL_SPECIFIERS);
+    }
+}
+
+/* Print the DECL_NAME of DECL unless a different string was requested by the
+   diagnose_as attribute. */
+
+static void
+dump_decl_name_or_diagnose_as (cxx_pretty_printer *pp, tree decl, int flags)
+{
+  tree attr = lookup_diagnose_as_attribute (pp, DECL_ATTRIBUTES (decl));
+  if (attr)
+    dump_diagnose_as_alias (pp, attr, NULL_TREE, flags | TFF_UNQUALIFIED_NAME);
+  else
+    dump_decl_name (pp, DECL_NAME (decl), flags);
+}
+
 /* Dump a human readable string for the decl T under control of FLAGS.  */
 
 static void
@@ -1236,7 +1394,7 @@ dump_decl (cxx_pretty_printer *pp, tree t, int flags)
 	      || flags & TFF_CLASS_KEY_OR_ENUM))
 	{
 	  pp_cxx_ws_string (pp, "using");
-	  dump_decl (pp, DECL_NAME (t), flags);
+	  dump_decl_name_or_diagnose_as (pp, t, flags);
 	  pp_cxx_whitespace (pp);
 	  pp_cxx_ws_string (pp, "=");
 	  pp_cxx_whitespace (pp);
@@ -1289,18 +1447,28 @@ dump_decl (cxx_pretty_printer *pp, tree t, int flags)
 	pp->declaration (t);
       else
 	{
-	  if (! (flags & TFF_UNQUALIFIED_NAME))
-	    dump_scope (pp, CP_DECL_CONTEXT (t), flags);
-	  flags &= ~TFF_UNQUALIFIED_NAME;
-	  if (DECL_NAME (t) == NULL_TREE)
-            {
-              if (!(pp->flags & pp_c_flag_gnu_v3))
-                pp_cxx_ws_string (pp, M_("{anonymous}"));
-              else
-                pp_cxx_ws_string (pp, M_("(anonymous namespace)"));
-            }
+	  tree diagnose_as
+	    = lookup_diagnose_as_attribute (pp, DECL_ATTRIBUTES (t));
+	  if (diagnose_as)
+	    /* Do not print DECL_CONTEXT (t), if there's any, since the
+	       diagnose_as attribute on a namespace hides all enclosing scopes.
+	     */
+	    dump_diagnose_as_alias (pp, diagnose_as, NULL_TREE, flags);
 	  else
-	    pp_cxx_tree_identifier (pp, DECL_NAME (t));
+	    {
+	      if (! (flags & TFF_UNQUALIFIED_NAME))
+		dump_scope (pp, CP_DECL_CONTEXT (t), flags);
+	      flags &= ~TFF_UNQUALIFIED_NAME;
+	      if (DECL_NAME (t) == NULL_TREE)
+		{
+		  if (!(pp->flags & pp_c_flag_gnu_v3))
+		    pp_cxx_ws_string (pp, M_("{anonymous}"));
+		  else
+		    pp_cxx_ws_string (pp, M_("(anonymous namespace)"));
+		}
+	      else
+		pp_cxx_tree_identifier (pp, DECL_NAME (t));
+	    }
 	}
       break;
 
@@ -1418,7 +1586,7 @@ dump_decl (cxx_pretty_printer *pp, tree t, int flags)
 	      TREE_CODE (DECL_INITIAL (t)) == TEMPLATE_PARM_INDEX))
 	dump_simple_decl (pp, t, TREE_TYPE (t), flags);
       else if (DECL_NAME (t))
-	dump_decl (pp, DECL_NAME (t), flags);
+	dump_decl_name_or_diagnose_as (pp, t, flags);
       else if (DECL_INITIAL (t))
 	dump_expr (pp, DECL_INITIAL (t), flags | TFF_EXPR_IN_PARENS);
       else
@@ -1437,7 +1605,7 @@ dump_decl (cxx_pretty_printer *pp, tree t, int flags)
 	  }
 	dump_type (pp, scope, flags);
 	pp_cxx_colon_colon (pp);
-	dump_decl (pp, DECL_NAME (t), flags);
+	dump_decl_name_or_diagnose_as (pp, t, flags);
 	if (variadic)
 	  pp_cxx_ws_string (pp, "...");
       }
@@ -1678,7 +1846,8 @@ dump_function_decl (cxx_pretty_printer *pp, tree t, int flags)
   tree template_args = NULL_TREE;
   tree template_parms = NULL_TREE;
   int show_return = flags & TFF_RETURN_TYPE || flags & TFF_DECL_SPECIFIERS;
-  int do_outer_scope = ! (flags & TFF_UNQUALIFIED_NAME);
+  bool do_outer_scope = ! (flags & TFF_UNQUALIFIED_NAME);
+  bool do_template_scope = false;
   tree exceptions;
   bool constexpr_p;
   tree ret = NULL_TREE;
@@ -1713,6 +1882,43 @@ dump_function_decl (cxx_pretty_printer *pp, tree t, int flags)
       tmpl = most_general_template (t);
       if (tmpl && TREE_CODE (tmpl) == TEMPLATE_DECL)
 	{
+	  /* Inspect whether any record/union type in the context of the
+	     instantiated function template carries a diagnose_as attribute or
+	     might be an instantiation of a partially specialized class
+	     template. If one does, we need to print the scope and template
+	     argument list differently. Consider:
+
+	       template <class T> struct A {
+		 template <class U0, class U1> struct B;
+		 template <class U> struct B<U, int> {
+		   void f();
+		 };
+	       };
+	       using Achar [[gnu::diagnose_as("Achar")]] = A<char>;
+
+	     Now Achar::B<int, int>::f() needs to print as "Achar::B<U, int>::f()
+	     [with U = int]". However, DECL_CONTEXT (t) would print as
+	     "Achar::B<int, int>" and dump_aggr_type has no sensible way to
+	     retrieve A<T>::B<U, int>. tmpl was generalized to A<T>::B<U, int>::f
+	     and thus dump_aggr_type (DECL_CONTEXT (tmpl)) would print
+	     "A<T>::B<U, int> [with T = char, U = int]". I.e. the diagnose_as
+	     attribute on the A<char> instantiation is lost.  */
+	  if (tmpl != t && do_outer_scope)
+	    {
+	      for (tree context = DECL_CONTEXT (t);
+		   context && CLASS_TYPE_P (context);
+		   context = TYPE_CONTEXT (context))
+		{
+		  if (lookup_diagnose_as_attribute (pp,
+						    TYPE_ATTRIBUTES (context))
+			|| CLASSTYPE_USE_TEMPLATE (context))
+		    {
+		      do_template_scope = true;
+		      break;
+		    }
+		}
+	    }
+
 	  template_parms = DECL_TEMPLATE_PARMS (tmpl);
 	  t = tmpl;
 	  /* The "[with ...]" clause is printed, thus dump functions printing
@@ -1764,6 +1970,15 @@ dump_function_decl (cxx_pretty_printer *pp, tree t, int flags)
   /* Print the function name.  */
   if (!do_outer_scope)
     /* Nothing.  */;
+  else if (do_template_scope)
+    {
+      template_parms = copy_list (template_parms);
+      bool func_template = TREE_TYPE (TREE_VALUE (template_parms)) == t;
+      dump_template_scope (pp, DECL_CONTEXT (specialized_t), DECL_CONTEXT (t),
+			   func_template ? TREE_CHAIN (template_parms)
+					 : template_parms,
+			   flags | specialized_flags);
+    }
   else if (cname)
     {
       dump_type (pp, cname, flags | specialized_flags);
@@ -1965,7 +2180,10 @@ dump_function_name (cxx_pretty_printer *pp, tree t, int flags)
 	name = constructor_name (DECL_CONTEXT (t));
     }
 
-  if (DECL_DESTRUCTOR_P (t))
+  tree attr = lookup_diagnose_as_attribute (pp, DECL_ATTRIBUTES (t));
+  if (attr)
+    dump_diagnose_as_alias (pp, attr, NULL_TREE, TFF_PLAIN_IDENTIFIER);
+  else if (DECL_DESTRUCTOR_P (t))
     {
       pp_cxx_complement (pp);
       dump_decl (pp, name, TFF_PLAIN_IDENTIFIER);
@@ -3213,7 +3431,7 @@ lang_decl_name (tree decl, int v, bool translate)
            && TREE_CODE (decl) == NAMESPACE_DECL)
     dump_decl (cxx_pp, decl, TFF_PLAIN_IDENTIFIER | TFF_UNQUALIFIED_NAME);
   else
-    dump_decl (cxx_pp, DECL_NAME (decl), TFF_PLAIN_IDENTIFIER);
+    dump_decl_name_or_diagnose_as (cxx_pp, decl, TFF_PLAIN_IDENTIFIER);
 
   return pp_ggc_formatted_text (cxx_pp);
 }
@@ -3998,6 +4216,12 @@ comparable_template_types_p (tree type_a, tree type_b)
   if (!CLASS_TYPE_P (type_b))
     return false;
 
+  /* If one of the types has a diagnose_as attribute set it is presented as a
+     non-template (even if it's a template specialization). */
+  if (lookup_diagnose_as_attribute (cxx_pp, TYPE_ATTRIBUTES (type_a))
+	|| lookup_diagnose_as_attribute (cxx_pp, TYPE_ATTRIBUTES (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)
@@ -4097,8 +4321,21 @@ print_template_differences (pretty_printer *pp, tree type_a, tree type_b,
   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))));
+  const char* identifier_a
+    = IDENTIFIER_POINTER (DECL_NAME (TI_TEMPLATE (tinfo_a)));
+  if (flag_diagnostics_use_aliases)
+    {
+      /* Locate the most general template, and see whether it carries the
+         diagnose_as attribute. If it does, use it as identifier for type_a. */
+      tree tpl = TI_TEMPLATE (tinfo_a);
+      while (DECL_TEMPLATE_INFO (tpl))
+	tpl = DECL_TI_TEMPLATE (tpl);
+      tree attr = lookup_diagnose_as_attribute
+		    (cxx_pp, TYPE_ATTRIBUTES (TREE_TYPE (tpl)));
+      if (attr)
+	identifier_a = TREE_STRING_POINTER (TREE_VALUE (attr));
+    }
+  pp_printf (pp, "%s<", identifier_a);
 
   tree args_a = TI_ARGS (tinfo_a);
   tree args_b = TI_ARGS (tinfo_b);
diff --git a/gcc/cp/name-lookup.c b/gcc/cp/name-lookup.c
index 7f5e41ab610..e1a451aff40 100644
--- a/gcc/cp/name-lookup.c
+++ b/gcc/cp/name-lookup.c
@@ -6090,6 +6090,53 @@ handle_namespace_attrs (tree ns, tree attributes)
 	    DECL_ATTRIBUTES (ns) = tree_cons (name, args,
 					      DECL_ATTRIBUTES (ns));
 	}
+      else if (is_attribute_p ("diagnose_as", name))
+	{
+	  if (DECL_NAMESPACE_ALIAS (ns))
+	    { // apply attribute to original namespace
+	      if (!args)
+		{ // turn alias identifier into attribute argument
+		  tree ns_name = DECL_NAME (ns);
+		  tree str = fix_string_type (
+			       build_string(IDENTIFIER_LENGTH (ns_name) + 1,
+					    IDENTIFIER_POINTER (ns_name)));
+		  args = build_tree_list (NULL_TREE, str);
+		}
+	      else if (TREE_CHAIN (args)
+			 || TREE_CODE (TREE_VALUE (args)) != STRING_CST)
+		{
+		  warning (OPT_Wattributes,
+			   "%qD attribute requires 0 or 1 NTBS arguments",
+			   name);
+		  continue;
+		}
+	      tree attributes = tree_cons (name, args, NULL_TREE);
+	      handle_namespace_attrs (ORIGINAL_NAMESPACE (ns), attributes);
+	      continue;
+	    }
+	  if (!args || TREE_CHAIN (args)
+		|| TREE_CODE (TREE_VALUE (args)) != STRING_CST)
+	    {
+	      warning (OPT_Wattributes,
+		       "%qD attribute requires a single NTBS argument",
+		       name);
+	      continue;
+	    }
+	  tree existing
+	    = lookup_attribute ("diagnose_as", DECL_ATTRIBUTES (ns));
+	  if (existing
+		&& !cp_tree_equal (TREE_VALUE (args),
+				   TREE_VALUE (TREE_VALUE (existing))))
+	    {
+	      auto_diagnostic_group d;
+	      warning (OPT_Wattributes, "%qD redeclared with different %qD "
+					"attribute value", ns, name);
+	      inform (DECL_SOURCE_LOCATION (ns), "previous declaration here");
+	      continue;
+	    }
+	  DECL_ATTRIBUTES (ns) = tree_cons (name, args,
+					    DECL_ATTRIBUTES (ns));
+	}
       else
 	{
 	  warning (OPT_Wattributes, "%qD attribute directive ignored",
@@ -6122,7 +6169,7 @@ pop_decl_namespace (void)
 /* Process a namespace-alias declaration.  */
 
 void
-do_namespace_alias (tree alias, tree name_space)
+do_namespace_alias (tree alias, tree name_space, tree attributes)
 {
   if (name_space == error_mark_node)
     return;
@@ -6138,6 +6185,10 @@ do_namespace_alias (tree alias, tree name_space)
   DECL_CONTEXT (alias) = FROB_CONTEXT (current_scope ());
   set_originating_module (alias);
 
+  /* Apply attributes.  */
+  if (attributes)
+    handle_namespace_attrs (alias, attributes);
+
   pushdecl (alias);
 
   /* Emit debug info for namespace alias.  */
diff --git a/gcc/cp/name-lookup.h b/gcc/cp/name-lookup.h
index f63c4f5b8bb..37f62a81823 100644
--- a/gcc/cp/name-lookup.h
+++ b/gcc/cp/name-lookup.h
@@ -435,7 +435,7 @@ extern tree cp_namespace_decls (tree);
 extern void set_decl_namespace (tree, tree, bool);
 extern void push_decl_namespace (tree);
 extern void pop_decl_namespace (void);
-extern void do_namespace_alias (tree, tree);
+extern void do_namespace_alias (tree, tree, tree = NULL_TREE);
 extern tree do_class_using_decl (tree, tree);
 extern tree lookup_arg_dependent (tree, tree, vec<tree, va_gc> *);
 extern tree search_anon_aggr (tree, tree, bool = false);
diff --git a/gcc/cp/parser.c b/gcc/cp/parser.c
index 12c598798db..fb10574943c 100644
--- a/gcc/cp/parser.c
+++ b/gcc/cp/parser.c
@@ -14819,20 +14819,15 @@ cp_parser_declaration (cp_parser* parser, tree prefix_attrs)
 	       || token1->keyword == RID_STATIC
 	       || token1->keyword == RID_INLINE))
     cp_parser_explicit_instantiation (parser);
-  /* If the next token is `namespace', check for a named or unnamed
-     namespace definition.  */
-  else if (token1->keyword == RID_NAMESPACE
-	   && (/* A named namespace definition.  */
-	       (token2->type == CPP_NAME
-		&& (cp_lexer_peek_nth_token (parser->lexer, 3)->type
-		    != CPP_EQ))
-               || (token2->type == CPP_OPEN_SQUARE
-                   && cp_lexer_peek_nth_token (parser->lexer, 3)->type
-                   == CPP_OPEN_SQUARE)
-	       /* An unnamed namespace definition.  */
-	       || token2->type == CPP_OPEN_BRACE
-	       || token2->keyword == RID_ATTRIBUTE))
-    cp_parser_namespace_definition (parser);
+  /* If the next token is `namespace', we have either a namespace alias
+     definition or a namespace definition.  */
+  else if (token1->keyword == RID_NAMESPACE)
+    {
+      cp_parser_parse_tentatively (parser);
+      cp_parser_namespace_alias_definition (parser);
+      if (!cp_parser_parse_definitely (parser))
+	cp_parser_namespace_definition (parser);
+    }
   /* An inline (associated) namespace definition.  */
   else if (token2->keyword == RID_NAMESPACE
 	   && token1->keyword == RID_INLINE)
@@ -21279,15 +21274,20 @@ cp_parser_namespace_alias_definition (cp_parser* parser)
 {
   tree identifier;
   tree namespace_specifier;
+  const location_t start = input_location;
 
   cp_token *token = cp_lexer_peek_token (parser->lexer);
 
   /* Look for the `namespace' keyword.  */
   cp_parser_require_keyword (parser, RID_NAMESPACE, RT_NAMESPACE);
+  /* Look for attributes (GCC extension).  */
+  tree left_attributes = cp_parser_attributes_opt (parser);
   /* Look for the identifier.  */
   identifier = cp_parser_identifier (parser);
   if (identifier == error_mark_node)
     return;
+  /* Look for more attributes (GCC extension).  */
+  tree attributes = attr_chainon (left_attributes, cp_parser_attributes_opt (parser));
   /* Look for the `=' token.  */
   if (!cp_parser_uncommitted_to_tentative_parse_p (parser)
       && cp_lexer_next_token_is (parser->lexer, CPP_OPEN_BRACE))
@@ -21299,7 +21299,15 @@ cp_parser_namespace_alias_definition (cp_parser* parser)
 	cp_lexer_consume_token (parser->lexer);
       return;
     }
-  cp_parser_require (parser, CPP_EQ, RT_EQ);
+  if (!cp_parser_require (parser, CPP_EQ, RT_EQ))
+    {
+      input_location = start;
+      return;
+    }
+  if (left_attributes)
+    pedwarn (input_location, OPT_Wpedantic,
+	     "standard attributes on namespaces aliases must follow "
+	     "the namespace alias name");
   /* Look for the qualified-namespace-specifier.  */
   namespace_specifier
     = cp_parser_qualified_namespace_specifier (parser);
@@ -21308,7 +21316,7 @@ cp_parser_namespace_alias_definition (cp_parser* parser)
   cp_parser_require (parser, CPP_SEMICOLON, RT_SEMICOLON);
 
   /* Register the alias in the symbol table.  */
-  do_namespace_alias (identifier, namespace_specifier);
+  do_namespace_alias (identifier, namespace_specifier, attributes);
 }
 
 /* Parse a qualified-namespace-specifier.
diff --git a/gcc/cp/tree.c b/gcc/cp/tree.c
index 32ddf835a91..a2ce39925a2 100644
--- a/gcc/cp/tree.c
+++ b/gcc/cp/tree.c
@@ -46,6 +46,7 @@ static tree verify_stmt_tree_r (tree *, int *, void *);
 
 static tree handle_init_priority_attribute (tree *, tree, tree, int, bool *);
 static tree handle_abi_tag_attribute (tree *, tree, tree, int, bool *);
+static tree handle_diagnose_as_attribute (tree *, tree, tree, int, bool *);
 
 /* If REF is an lvalue, returns the kind of lvalue that REF is.
    Otherwise, returns clk_none.  */
@@ -4890,6 +4891,8 @@ const struct attribute_spec cxx_attribute_table[] =
     handle_init_priority_attribute, NULL },
   { "abi_tag", 1, -1, false, false, false, true,
     handle_abi_tag_attribute, NULL },
+  { "diagnose_as", 0, -1, false, false, false, false,
+    handle_diagnose_as_attribute, NULL },
   { NULL, 0, 0, false, false, false, false, NULL, NULL }
 };
 
@@ -5158,6 +5161,169 @@ handle_abi_tag_attribute (tree* node, tree name, tree args,
   return NULL_TREE;
 }
 
+/* DECL is being redeclared; the old declaration had the diagnose_as attribute
+   in OLD, and the new one has the attribute in NEW_.  Give an error if the
+   two attributes are not equal.  */
+
+static bool
+check_diagnose_as_redeclaration (const_tree decl, const_tree name,
+				 const_tree old, const_tree new_)
+{
+  if (!old)
+    return true;
+  if (TREE_CODE (TREE_VALUE (old)) == TREE_LIST)
+    old = TREE_VALUE (old);
+  if (TREE_CODE (TREE_VALUE (new_)) == TREE_LIST)
+    new_ = TREE_VALUE (new_);
+  tree old_value = TREE_VALUE (old);
+  tree new_value = TREE_VALUE (new_);
+  if (cp_tree_equal (old_value, new_value))
+    return true;
+  warning (OPT_Wattributes, "%qD redeclared with %<%D(%E)%> "
+			    "attribute", decl, name, new_value);
+  inform (DECL_SOURCE_LOCATION (decl), "previous declaration here");
+  return false;
+}
+
+/* Handle a "diagnose_as" attribute.
+
+   If the given NODE is a TYPE_DECL: Apply the attribute to the TREE_TYPE in
+   place, even if the type is complete. If no argument was given to the
+   attribute, use NODE as attribute argument when recursing to the TREE_TYPE.
+   Append CP_DECL_CONTEXT as second attribute argument if the TREE_TYPE type has
+   a different context. Alias templates without matching template arguments are
+   not supported. Apply the attribute to the primary template type for alias
+   templates.  */
+
+static tree
+handle_diagnose_as_attribute (tree* node, tree name, tree args,
+			      int flags, bool* no_add_attrs)
+{
+  tree decl = NULL_TREE;
+  const bool is_alias = (TREE_CODE (*node) == TYPE_DECL);
+  if (args && TREE_PURPOSE (args) == integer_zero_node)
+    /* We're recursing from handle_diagnose_as_attribute from a TYPE_DECL.  */;
+  else if (is_alias)
+    {
+      if (args && (TREE_CHAIN (args)
+		     || TREE_CODE (TREE_VALUE (args)) != STRING_CST))
+	{
+	  warning (OPT_Wattributes,
+		   "%qD attribute requires 0 or 1 NTBS arguments", name);
+	  goto fail;
+	}
+    }
+  else if (!args || TREE_CHAIN (args)
+	     || TREE_CODE (TREE_VALUE (args)) != STRING_CST)
+    {
+      warning (OPT_Wattributes,
+	       "%qD attribute requires a single NTBS argument", name);
+      goto fail;
+    }
+
+  if (is_alias && CLASS_TYPE_P (TREE_TYPE (*node)))
+    {
+      decl = *node;
+
+      /* Reject alias templates without primary template on the RHS. E.g.
+         template <class> using A = B;  */
+      if (DECL_LANG_SPECIFIC (decl)
+	    && DECL_TEMPLATE_INFO (decl)
+	    && DECL_ALIAS_TEMPLATE_P (DECL_TI_TEMPLATE (decl))
+	    && PRIMARY_TEMPLATE_P (DECL_TI_TEMPLATE (decl)))
+	return error_mark_node;
+
+      /* Apply the attribute to the specialization on the RHS.  */
+      tree type = strip_typedefs (TREE_TYPE (*node), nullptr, 0);
+
+      if (is_renaming_alias_template (decl))
+	{
+	  type = CLASSTYPE_PRIMARY_TEMPLATE_TYPE (type);
+	  /* Warn and skip if the template arguments don't match up like in
+	       template <class T> using A = B<int, T>;  */
+	  if (!same_type_p (TREE_TYPE (decl), type))
+	    return error_mark_node;
+	}
+
+      if (!args)
+	/* Pass the TYPE_DECL as attribute argument, i.e. tell TYPE to be
+	   diagnosed as DECL. INTEGER_ZERO_NODE is to skip the argument checks
+	   on recursion.  */
+	args = build_tree_list (integer_zero_node, decl);
+      else if (!TREE_CHAIN (args)
+	    && CP_DECL_CONTEXT (decl) != CP_TYPE_CONTEXT (type))
+	{
+	  /* If the context of DECL and TYPE differ, diagnostics of TYPE would
+	     be printed within the wrong scope. Therefore, pass the context of
+	     DECL as second argument to the attribute, so that it can be printed
+	     correctly. INTEGER_ZERO_NODE is used skip the argument checks on
+	     recursion.  */
+	  TREE_PURPOSE (args) = integer_zero_node;
+	  TREE_CHAIN (args) = build_tree_list (NULL_TREE,
+					       CP_DECL_CONTEXT (decl));
+	}
+
+      if (COMPLETE_TYPE_P (type))
+	{
+	  /* If TYPE is already complete, cplus_decl_attributes cannot be used
+	     anymore. Recurse to handle_diagnose_as_attribute with the type
+	     given on the RHS of the alias. On success, append the diagnose_as
+	     attribute to TYPE's attributes.  */
+	  tree rhs = handle_diagnose_as_attribute
+		       (&type, name, args, flags, no_add_attrs);
+	  if (rhs == error_mark_node || *no_add_attrs)
+	    return rhs;
+	  else
+	    TYPE_ATTRIBUTES (type)
+	      = tree_cons (name, args, TYPE_ATTRIBUTES (type));
+	}
+      else
+	{
+	  tree attributes = tree_cons (name, args, NULL_TREE);
+	  cplus_decl_attributes (&type, attributes, ATTR_FLAG_TYPE_IN_PLACE);
+	}
+    }
+  else if (TYPE_P (*node))
+    {
+      if (!OVERLOAD_TYPE_P (*node))
+	return error_mark_node;
+      decl = TYPE_NAME (*node);
+    }
+  else
+    {
+      if (!VAR_OR_FUNCTION_DECL_P (*node)
+	    || DECL_LANGUAGE (*node) != lang_cplusplus)
+	return error_mark_node;
+      decl = *node;
+    }
+
+  // Make sure all declarations have the same diagnose_as string.
+  if (DECL_SOURCE_LOCATION (decl) != input_location)
+    {
+      tree attributes = TYPE_P (*node) ? TYPE_ATTRIBUTES (*node)
+				       : DECL_ATTRIBUTES (decl);
+      if (!check_diagnose_as_redeclaration
+	     (decl, name, lookup_attribute ("diagnose_as", attributes), args))
+	goto fail;
+    }
+  else if (CLASS_TYPE_P (*node) && CLASSTYPE_IMPLICIT_INSTANTIATION (*node))
+    {
+      /* The above branch (different source location) is taken for declarations
+	 of type aliases that modify an implicit template specialization on the
+	 RHS. This branch is taken when the template is instantiated via
+	 instantiate_class_template_1, in which case the attribute either
+	 already has the value from the general template or from a type alias.
+       */
+      goto fail;
+    }
+
+  return NULL_TREE;
+
+ fail:
+  *no_add_attrs = true;
+  return NULL_TREE;
+}
+
 /* Return a new PTRMEM_CST of the indicated TYPE.  The MEMBER is the
    thing pointed to by the constant.  */
 
diff --git a/gcc/doc/extend.texi b/gcc/doc/extend.texi
index ef654d7b878..a97f617f16d 100644
--- a/gcc/doc/extend.texi
+++ b/gcc/doc/extend.texi
@@ -2915,6 +2915,51 @@ interface.  Other than emitting an error rather than a warning, the
 The @code{unavailable} attribute can also be used for variables and
 types (@pxref{Variable Attributes}, @pxref{Type Attributes}.)
 
+@item diagnose_as ("@var{string}")
+@itemx diagnose_as
+@cindex @code{diagnose_as} function attribute
+The @code{diagnose_as} attribute modifies how the entity the attribute
+appertains to is diagnosed in compiler messages and @code{__func__},
+@code{__FUNCTION__}, and @code{__PRETTY_FUNCTION__} macros. (The result of
+@code{typeid} is not affected.) If the attribute is applied to a
+@code{namespace}, the specified string replaces the complete enclosing scope.
+The effect of the @code{diagnose_as} attribute can be disabled with the
+@option{-fno-diagnostics-use-aliases} command line option.
+
+The argument @var{string} is optional for type and namespace aliases. If it is
+omitted, the identifier of the type or namespace alias is unconditionally used
+in all places where the type / namespace on the right-hand side is diagnosed.
+
+@smallexample
+namespace Foo @{
+  namespace Bar @{
+    inline namespace v1 [[gnu::diagnose_as("Foobar")]] @{
+      int f() @{
+        // __PRETTY_FUNCTION__ == "void Foobar::f()"
+      @}
+    @}
+  @}
+@}
+// In function 'int Foobar::f()':
+// warning: no return statement in function returning non-void [-Wreturn-type]
+@end smallexample
+
+The @code{diagnose_as} attribute can be used with namespaces, namespace aliases,
+functions, variables, alias declarations (but not alias templates), and
+user-defined types (classes, unions, and enums). If the alias declaration
+aliases a class type (including template specializations), the attribute is
+additionally applied to the class type. Whether the attribute has an effect on
+partial template specializations is unspecified and may depend on several
+different factors.
+
+@smallexample
+template <class T> struct basic_string @{@};
+using string [[gnu::diagnose_as]] = basic_string<char>;
+int f(basic_string<char>) @{@}
+// In function 'int f(string)':
+// warning: no return statement in function returning non-void [-Wreturn-type]
+@end smallexample
+
 @item error ("@var{message}")
 @itemx warning ("@var{message}")
 @cindex @code{error} function attribute
diff --git a/gcc/doc/invoke.texi b/gcc/doc/invoke.texi
index a7c4d24a762..c3af0c5d4bf 100644
--- a/gcc/doc/invoke.texi
+++ b/gcc/doc/invoke.texi
@@ -313,7 +313,8 @@ Objective-C and Objective-C++ Dialects}.
 -fno-show-column @gol
 -fdiagnostics-column-unit=@r{[}display@r{|}byte@r{]} @gol
 -fdiagnostics-column-origin=@var{origin} @gol
--fdiagnostics-escape-format=@r{[}unicode@r{|}bytes@r{]}}
+-fdiagnostics-escape-format=@r{[}unicode@r{|}bytes@r{]}
+-fno-diagnostics-aliases}
 
 @item Warning Options
 @xref{Warning Options,,Options to Request or Suppress Warnings}.
@@ -5159,6 +5160,12 @@ Unicode characters.  For the example above, the following will be printed:
  before<CF><80><BF>after
 @end smallexample
 
+@item -fno-diagnostics-use-aliases
+@opindex fno-diagnostics-use-aliases
+@opindex fdiagnostics-use-aliases
+Do not replace identifiers or scope names with the aliases defined with the
+@code{[[gnu::diagnose_as("alias")]]} attribute.
+
 @item -fdiagnostics-format=@var{FORMAT}
 @opindex fdiagnostics-format
 Select a different format for printing diagnostics.
diff --git a/gcc/testsuite/g++.dg/diagnostic/default-template-args-1.C b/gcc/testsuite/g++.dg/diagnostic/default-template-args-1.C
index 7a535515740..8086ea16fcd 100644
--- a/gcc/testsuite/g++.dg/diagnostic/default-template-args-1.C
+++ b/gcc/testsuite/g++.dg/diagnostic/default-template-args-1.C
@@ -34,6 +34,13 @@ template <class a, class b = int>
       [[deprecated]] static void f(d);
   };
 
+template <class T>
+  struct c<T, char>
+  {
+    template <class d, class e = int>
+      [[deprecated]] static void g(d);
+  };
+
 template <class T>
 struct B { typedef T X; };
 
@@ -68,6 +75,7 @@ int main()
   c<float, int>::f(1.);    // { dg-warning "'static void c<a, b>::f\\(d\\) .with d = double; a = float; b = int.'" }
   c<float, int>::f<int>(1);    // { dg-warning "'static void c<a, b>::f<d>\\(d\\) .with d = int; a = float; b = int.'" }
   c<float, int>::f<float, int>(1.f);    // { dg-warning "'static void c<a, b>::f<d, e>\\(d\\) .with d = float; e = int; a = float; b = int.'" }
+  c<int, char>::g(1.); // { dg-warning "'static void c<T, char>::g\\(d\\) .with d = double; T = int.'" }
 
   D<int>::foo(1); // { dg-warning "'static void D<U>::foo\\(typename B<V>::X\\) .with U = int; typename B<V>::X = int.'" }
 }
diff --git a/gcc/testsuite/g++.dg/diagnostic/diagnose-as1.C b/gcc/testsuite/g++.dg/diagnostic/diagnose-as1.C
new file mode 100644
index 00000000000..c62e5a7424d
--- /dev/null
+++ b/gcc/testsuite/g++.dg/diagnostic/diagnose-as1.C
@@ -0,0 +1,228 @@
+// { dg-options "-fdiagnostics-use-aliases -fpretty-templates" }
+
+#ifdef __cpp_constexpr
+template <typename T>
+  constexpr bool is_char() { return false; }
+
+template <>
+  constexpr bool is_char<char>() { return true; }
+
+template <typename T>
+  constexpr bool is_int() { return false; }
+
+template <>
+  constexpr bool is_int<int>() { return true; }
+#endif
+
+namespace __attribute__((diagnose_as())) foo0 {} // { dg-warning "'diagnose_as' attribute requires a single NTBS argument" }
+
+namespace __attribute__((diagnose_as("foo2"))) foo1 {} // { dg-line foo1 }
+namespace __attribute__((diagnose_as("XXXX"))) foo1 {} // { dg-warning "'foo2' redeclared with different 'diagnose_as' attribute value" }
+// { dg-message "previous declaration here" "" { target *-*-* } foo1 }
+namespace foo1
+{
+  void f() {
+    f(1); // { dg-error "too many arguments to function 'void foo2::f\\(\\)'" }
+  }
+
+  class __attribute__((diagnose_as("XX"))) X; // { dg-line XX }
+  class __attribute__((diagnose_as("--"))) X { // { dg-warning "'class foo2::XX' redeclared with 'diagnose_as\\(\"--\"\\)' attribute" }
+    // { dg-message "previous declaration here" "" { target *-*-* } XX }
+    __attribute__((diagnose_as("ff"))) void f();
+  };
+  class Y : X
+  {
+    void g() {
+      f(); // { dg-error "'void foo2::XX::ff\\(\\)' is private within this context" }
+    }
+  };
+
+  class __attribute__((diagnose_as())) Z0; // { dg-warning "'diagnose_as' attribute requires a single NTBS argument" }
+  class __attribute__((diagnose_as(1))) Z1; // { dg-warning "'diagnose_as' attribute requires a single NTBS argument" }
+  class __attribute__((diagnose_as("a", "b"))) Z2; // { dg-warning "'diagnose_as' attribute requires a single NTBS argument" }
+
+  template <typename T> class A0 {};
+  typedef A0<char> Achar __attribute__((diagnose_as("Achar")));
+  template class A0<int>;
+  typedef A0<int> Aint __attribute__((diagnose_as("Aint"))); // OK
+  typedef A0<int> Aint2 __attribute__((diagnose_as("Aint2"))); // { dg-warning "'class foo2::Aint' redeclared with 'diagnose_as\\(\"Aint2\"\\)' attribute" }
+  typedef A0<int> Aint3 __attribute__((diagnose_as(1))); // { dg-warning "'diagnose_as' attribute requires 0 or 1 NTBS arguments" }
+
+#if __cplusplus >= 201103L
+  template <typename T> using foo [[gnu::diagnose_as]] = A0<T>;
+  template <typename T> using aRef [[gnu::diagnose_as]] = A0<const T&>; // { dg-warning "'diagnose_as' attribute ignored" "" { target c++11 } }
+#endif
+}
+
+namespace A
+{
+  inline namespace __attribute__((diagnose_as("@1"))) B
+  {
+    template <typename U, typename UU = void>
+      struct __attribute__((diagnose_as("@2"))) C
+      {
+	__attribute__((diagnose_as("fun:1")))
+	void f() {} // { dg-line ABC_f }
+
+	template <typename T>
+	  __attribute__((diagnose_as("fun:2")))
+	  void g(T, U) {} // { dg-line ABC_gT }
+
+	template <typename T>
+	  __attribute__((diagnose_as("fun:2.2")))
+	  void g() {} // { dg-line ABC_g }
+
+	__attribute__((diagnose_as("fun:3")))
+	  void h()
+	{
+#ifdef __cpp_constexpr
+	  static_assert(__builtin_strcmp(__FUNCTION__, "fun:3") == 0, "");
+	  static_assert(__builtin_strcmp(__func__, "fun:3") == 0, "");
+	  constexpr const char* ref
+	    = is_int<U>() ? "void @1::@3::fun:3()"
+			  : "void @1::@2<U>::fun:3() [with U = char]";
+	  static_assert(__builtin_strcmp(__PRETTY_FUNCTION__, ref) == 0, "");
+#endif
+	}
+
+	template <typename T>
+	  __attribute__((diagnose_as("fun:4")))
+	  void ht()
+	  {
+#ifdef __cpp_constexpr
+	    static_assert(__builtin_strcmp(__FUNCTION__, "fun:4") == 0, "");
+	    constexpr const char* ref
+	      = is_int<U>()
+		  ? "void @1::@3::fun:4<T>() [with T = float]"
+		  : "void @1::@2<U>::fun:4<T>() [with T = float; U = char]";
+	    static_assert(__builtin_strcmp(__PRETTY_FUNCTION__, ref) == 0, "");
+#endif
+	  }
+
+	template <typename T0, typename T1>
+	  struct __attribute__((diagnose_as("@5"))) E
+	  {
+	    __attribute__((diagnose_as("fun:5"))) static void f() {
+#ifdef __cpp_constexpr
+	      static_assert(__builtin_strcmp(__FUNCTION__, "fun:5") == 0, "");
+	      constexpr const char* ref = is_int<U>()
+		? is_char<T0>()
+		  ? "static void @1::@3::@6::fun:5()"
+		  : "static void @1::@3::@5<T0, T1>::fun:5() [with T0 = float; T1 = short int]"
+		: is_char<T0>()
+		  ? "static void @1::@2<U>::@6::fun:5() [with U = char]"
+		  : "static void @1::@2<U>::@5<T0, T1>::fun:5() [with T0 = float; T1 = short int; U = char]";
+	      static_assert(__builtin_strcmp(__PRETTY_FUNCTION__, ref) == 0,
+			    "");
+#endif
+	    }
+	  };
+	typedef E<char, char> F __attribute__((diagnose_as("@6")));
+
+	template <typename T>
+	  struct __attribute__((diagnose_as("@7"))) E<T, long>
+	  {
+	    __attribute__((diagnose_as("fun:6"))) static void f() {
+#ifdef __cpp_constexpr
+	      static_assert(__builtin_strcmp(__FUNCTION__, "fun:6") == 0, "");
+	      constexpr const char* ref = is_int<U>()
+		  ? "static void @1::@3::@7<T, long int>::fun:6() [with T = float]"
+		  : "static void @1::@2<U>::@7<T, long int>::fun:6() [with T = float; U = char]";
+	      static_assert(__builtin_strcmp(__PRETTY_FUNCTION__, ref) == 0,
+			    "");
+#endif
+	    }
+	  };
+      };
+
+    typedef C<int> D __attribute__((diagnose_as("@3")));
+
+    template <>
+      struct __attribute__((diagnose_as("@4"))) C<float>
+      {
+      };
+
+    typedef C<short> E __attribute__((diagnose_as));
+  }
+}
+
+namespace frob
+{
+  namespace ni
+  {
+    struct kate {};
+    namespace cate
+    {
+      struct me {};
+    }
+  }
+}
+
+namespace frobni __attribute__((diagnose_as("twiggle"))) = frob::ni;
+namespace frobnicate __attribute__((diagnose_as)) = frob::ni::cate;
+namespace wrong __attribute__((diagnose_as(1))) = frob::ni; // { dg-warning "'diagnose_as' attribute requires 0 or 1 NTBS arguments" }
+namespace wrong2 __attribute__((diagnose_as("wrong", 1))) = frob::ni; // { dg-warning "'diagnose_as' attribute requires 0 or 1 NTBS arguments" }
+
+#if __cplusplus >= 201103L
+template <class T>
+  struct outer
+  {
+    struct inner {};
+  };
+template <class T>
+  using inandout [[gnu::diagnose_as]] = typename outer<T>::inner; // { dg-warning "'diagnose_as' attribute ignored" "" { target c++11 } }
+#endif
+
+
+void fn_1(int);
+void fn_2(A::D);
+
+int main ()
+{
+  fn_2 (A::D ());
+  fn_1 (A::D ()); // { dg-error "cannot convert '@1::@3' to 'int'" }
+  fn_1 (A::C<int> ()); // { dg-error "cannot convert '@1::@3' to 'int'" }
+  fn_1 (A::C<char> ()); // { dg-error "cannot convert '@1::@2<char>' to 'int'" }
+  fn_1 (A::C<float> ()); // { dg-error "cannot convert '@1::@4' to 'int'" }
+  fn_1 (A::C<short> ()); // { dg-error "cannot convert '@1::E' to 'int'" }
+  fn_1 (A::E ()); // { dg-error "cannot convert '@1::E' to 'int'" }
+  fn_1 (foo1::A0<int> ()); // { dg-error "cannot convert 'foo2::Aint' to 'int'" }
+  fn_1 (foo1::Aint2 ()); // { dg-error "cannot convert 'foo2::Aint2' {aka 'foo2::Aint'} to 'int'" }
+#if __cplusplus >= 201103L
+  fn_1 (foo1::foo<float> ()); // { dg-error "cannot convert 'foo2::foo<float>' to 'int'" "" { target c++11 } }
+  fn_1 (foo1::foo<const float&> ()); // { dg-error "cannot convert 'foo2::foo<const float&>' to 'int'" "" { target c++11 } }
+  fn_1 (foo1::foo<float const&> ()); // { dg-error "cannot convert 'foo2::foo<const float&>' to 'int'" "" { target c++11 } }
+  fn_1 (foo1::aRef<float> ()); // { dg-error "cannot convert 'foo2::aRef<float>' {aka 'foo2::foo<const float&>'} to 'int'" "" { target c++11 } }
+  fn_1 (frob::ni::kate ()); // { dg-error "cannot convert 'twiggle::kate' to 'int'" "" { target c++11 } }
+  fn_1 (frob::ni::cate::me ()); // { dg-error "cannot convert 'frobnicate::me' to 'int'" "" { target c++11 } }
+  fn_1 (inandout<double> ()); // { dg-error "cannot convert 'inandout<double>' {aka 'outer<double>::inner'} to 'int'" "" { target c++11 } }
+  fn_1 (outer<char>::inner ()); // { dg-error "cannot convert 'outer<char>::inner' to 'int'" "" { target c++11 } }
+#endif
+
+  A::C<int>().f(0); // { dg-error "no matching function for call to '@1::@3::f\\(int\\)'" }
+  // { dg-message "candidate: 'void @1::@3::fun:1\\(\\)'" "" { target *-*-* } ABC_f }
+
+  A::C<char>().f(0); // { dg-error "no matching function for call to '@1::@2<char>::f\\(int\\)'" }
+  // { dg-message "candidate: 'void @1::@2<U>::fun:1\\(\\) \\\[with U = char\\\]'" "" { target *-*-* } ABC_f }
+
+  A::C<float>().f(0); // { dg-error "'struct @1::@4' has no member named 'f'" }
+
+  A::C<int>().g(); // { dg-error "no matching function for call to '@1::@3::g\\(\\)'" }
+  // { dg-message "candidate: 'template<class T> void @1::@3::fun:2\\(T, U\\)'" "" { target *-*-* } ABC_gT }
+  // { dg-message "candidate: 'template<class T> void @1::@3::fun:2.2\\(\\)'" "" { target *-*-* } ABC_g }
+
+  A::C<char>().g(); // { dg-error "no matching function for call to '@1::@2<char>::g\\(\\)'" }
+  // { dg-message "candidate: 'template<class T> void @1::@2<U>::fun:2\\(T, U\\) \\\[with U = char\\\]'" "" { target *-*-* } ABC_gT }
+  // { dg-message "candidate: 'template<class T> void @1::@2<U>::fun:2.2\\(\\) \\\[with U = char\\\]'" "" { target *-*-* } ABC_g }
+
+  A::C<int>().h();
+  A::C<char>().h();
+  A::C<int>().ht<float>();
+  A::C<char>().ht<float>();
+  A::C<int>::E<float, short>::f();
+  A::C<char>::E<float, short>::f();
+  A::C<int>::E<float, long>::f();
+  A::C<char>::E<float, long>::f();
+  A::C<int>::F::f();
+  A::C<char>::F::f();
+}
diff --git a/gcc/testsuite/g++.dg/diagnostic/diagnose-as2.C b/gcc/testsuite/g++.dg/diagnostic/diagnose-as2.C
new file mode 100644
index 00000000000..b1d46d12024
--- /dev/null
+++ b/gcc/testsuite/g++.dg/diagnostic/diagnose-as2.C
@@ -0,0 +1,144 @@
+// { dg-options "-fdiagnostics-use-aliases -fno-pretty-templates" }
+
+#ifdef __cpp_constexpr
+template <typename T>
+  constexpr bool is_char() { return false; }
+
+template <>
+  constexpr bool is_char<char>() { return true; }
+
+template <typename T>
+  constexpr bool is_int() { return false; }
+
+template <>
+  constexpr bool is_int<int>() { return true; }
+#endif
+
+namespace A
+{
+  inline namespace B __attribute__((diagnose_as("@1")))
+  {
+    template <typename U>
+      struct __attribute__((diagnose_as("@2"))) C
+      {
+	__attribute__((diagnose_as("fun:1")))
+	void f() {} // { dg-line ABC_f }
+
+	template <typename T>
+	  __attribute__((diagnose_as("fun:2")))
+	  void g(T, U) {} // { dg-line ABC_gT }
+
+	template <typename T>
+	  __attribute__((diagnose_as("fun:2.2")))
+	  void g() {} // { dg-line ABC_g }
+
+	__attribute__((diagnose_as("fun:3")))
+	void h()
+	{
+#ifdef __cpp_constexpr
+	  static_assert(__builtin_strcmp(__FUNCTION__, "fun:3") == 0, "");
+	  constexpr const char* ref
+	    = is_int<U>() ? "void @1::@3::fun:3()"
+			  : "void @1::@2<char>::fun:3()";
+	  static_assert(__builtin_strcmp(__PRETTY_FUNCTION__, ref) == 0, "");
+#endif
+	}
+
+	template <typename T>
+	  __attribute__((diagnose_as("fun:4")))
+	  void ht()
+	{
+#ifdef __cpp_constexpr
+	  static_assert(__builtin_strcmp(__FUNCTION__, "fun:4") == 0, "");
+	  constexpr const char* ref
+	    = is_int<U>() ? "void @1::@3::fun:4<float>()"
+			  : "void @1::@2<char>::fun:4<float>()";
+	  static_assert(__builtin_strcmp(__PRETTY_FUNCTION__, ref) == 0, "");
+#endif
+	}
+
+	template <typename T0, typename T1>
+	  struct __attribute__((diagnose_as("@5"))) E
+	  {
+	    __attribute__((diagnose_as("fun:5"))) static void f() {
+#ifdef __cpp_constexpr
+	      static_assert(__builtin_strcmp(__FUNCTION__, "fun:5") == 0, "");
+	      constexpr const char* ref = is_int<U>()
+		? is_char<T0>()
+		  ? "static void @1::@3::@6::fun:5()"
+		  : "static void @1::@3::@5<float, short int>::fun:5()"
+		: is_char<T0>()
+		  ? "static void @1::@2<char>::@6::fun:5()"
+		  : "static void @1::@2<char>::@5<float, short int>::fun:5()";
+	      static_assert(__builtin_strcmp(__PRETTY_FUNCTION__, ref) == 0,
+			    "");
+#endif
+	    }
+	  };
+	typedef E<char, char> F __attribute__((diagnose_as("@6")));
+
+	template <typename T>
+	  struct __attribute__((diagnose_as("@7"))) E<T, long>
+	  {
+	    __attribute__((diagnose_as("fun:6"))) static void f() {
+#ifdef __cpp_constexpr
+	      static_assert(__builtin_strcmp(__FUNCTION__, "fun:6") == 0, "");
+	      // Note that @7 is ignored with -fno-pretty-templates. After all
+	      // diagnose_as on partial specializations is not supported.
+	      constexpr const char* ref = is_int<U>()
+		  ? "static void @1::@3::@5<float, long int>::fun:6()"
+		  : "static void @1::@2<char>::@5<float, long int>::fun:6()";
+	      static_assert(__builtin_strcmp(__PRETTY_FUNCTION__, ref) == 0,
+			    "");
+#endif
+	    }
+	  };
+      };
+
+    typedef C<int> D __attribute__((diagnose_as("@3")));
+
+    template <>
+      struct __attribute__((diagnose_as("@4"))) C<float>
+      {
+      };
+  }
+}
+
+void fn_1(int);
+void fn_2(A::D);
+
+int main ()
+{
+  fn_2 (A::D ());
+  fn_1 (A::D ()); // { dg-error "cannot convert '@1::@3' to 'int'" }
+  fn_1 (A::C<int> ()); // { dg-error "cannot convert '@1::@3' to 'int'" }
+  fn_1 (A::C<char> ()); // { dg-error "cannot convert '@1::@2<char>' to 'int'" }
+  fn_1 (A::C<float> ()); // { dg-error "cannot convert '@1::@4' to 'int'" }
+
+  A::C<int>().f(0); // { dg-error "no matching function for call to '@1::@3::f\\(int\\)'" }
+  // { dg-message "candidate: 'void @1::@3::fun:1\\(\\)'" "" { target *-*-* } ABC_f }
+
+  A::C<char>().f(0); // { dg-error "no matching function for call to '@1::@2<char>::f\\(int\\)'" }
+  // { dg-message "candidate: 'void @1::@2<char>::fun:1\\(\\)'" "" { target *-*-* } ABC_f }
+
+  A::C<float>().f(0); // { dg-error "'struct @1::@4' has no member named 'f'" }
+
+  A::C<int>().g(); // { dg-error "no matching function for call to '@1::@3::g\\(\\)'" }
+  // { dg-message "candidate: 'template<class T> void @1::@3::fun:2\\(T, int\\)'" "" { target *-*-* } ABC_gT }
+  // { dg-message "candidate: 'template<class T> void @1::@3::fun:2.2\\(\\)'" "" { target *-*-* } ABC_g }
+
+  A::C<char>().g(); // { dg-error "no matching function for call to '@1::@2<char>::g\\(\\)'" }
+  // { dg-message "candidate: 'template<class T> void @1::@2<char>::fun:2\\(T, char\\)'" "" { target *-*-* } ABC_gT }
+  // { dg-message "candidate: 'template<class T> void @1::@2<char>::fun:2.2\\(\\)'" "" { target *-*-* } ABC_g }
+
+  A::C<int>().h();
+  A::C<char>().h();
+  A::C<int>().ht<float>();
+  A::C<char>().ht<float>();
+  A::C<int>::E<float, short>::f();
+  A::C<char>::E<float, short>::f();
+  A::C<int>::E<float, long>::f();
+  A::C<char>::E<float, long>::f();
+  A::C<int>::F::f();
+  A::C<char>::F::f();
+}
diff --git a/gcc/testsuite/g++.dg/diagnostic/diagnose-as3.C b/gcc/testsuite/g++.dg/diagnostic/diagnose-as3.C
new file mode 100644
index 00000000000..2020443bcef
--- /dev/null
+++ b/gcc/testsuite/g++.dg/diagnostic/diagnose-as3.C
@@ -0,0 +1,152 @@
+// { dg-options "-fdiagnostics-use-aliases -fpretty-templates" }
+// { dg-do compile { target c++11 } }
+
+const char* volatile s;
+
+namespace foo
+{
+  template <class U>
+    struct [[gnu::diagnose_as("Bar'")]] Bar
+    {
+      template <class T0, class T1>
+        struct [[gnu::diagnose_as("A'")]] A
+        { constexpr const char* f() { return __PRETTY_FUNCTION__; } };
+
+      template <class T>
+        struct A<T, float>
+        { constexpr const char* f() { return __PRETTY_FUNCTION__; } };
+
+      template <class T>
+        struct [[gnu::diagnose_as("Adouble")]] A<T, double>
+        { constexpr const char* f() { return __PRETTY_FUNCTION__; } };
+
+      using Special [[gnu::diagnose_as("SpecialA")]] = A<int, int>;
+
+      static constexpr const char* f() { return __PRETTY_FUNCTION__; }
+    };
+
+  template <class P>
+    struct
+    [[gnu::diagnose_as("BarPtr")]]
+    Bar<P*>
+    {
+      template <class U = int>
+      static constexpr const char* f() { return __PRETTY_FUNCTION__; }
+    };
+
+  template <>
+    struct [[gnu::diagnose_as("SpecialBar")]] Bar<void>
+    {
+      static constexpr const char* f() { return __PRETTY_FUNCTION__; }
+    };
+
+  using barchar [[gnu::diagnose_as("barchar")]] = Bar<char>;
+}
+
+namespace A
+{
+  inline namespace B __attribute__((diagnose_as("@1")))
+  {
+    template  <typename U>
+      struct __attribute__((diagnose_as("@2"))) C
+      {
+        template <class T>
+        __attribute__((diagnose_as("fun:2")))
+          constexpr const char* g() { return __PRETTY_FUNCTION__; }
+
+        __attribute__((diagnose_as("fun:3")))
+          constexpr const char* h()
+          { return __PRETTY_FUNCTION__; }
+      };
+
+    typedef C<int> D __attribute__((diagnose_as("@3")));
+  }
+}
+
+template <class T0, class U0>
+struct [[gnu::diagnose_as("X.0")]] X0
+{
+  template <class T1, class U1>
+    struct X1
+    {
+      template <class T2, class U2>
+        struct X2
+        {
+          template <class T3, class U3>
+            [[gnu::diagnose_as("f-1")]]
+            static constexpr const char* f() {
+              return __PRETTY_FUNCTION__;
+            }
+        };
+
+      using XX [[gnu::diagnose_as("X2'")]] = X2<int, long>;
+    };
+
+  template <class T3, class U3>
+    [[gnu::diagnose_as("f-1")]]
+    static constexpr const char* f() {
+      return __PRETTY_FUNCTION__;
+    }
+
+  struct [[gnu::diagnose_as("X.3")]] X3
+  {
+    template <class T3, class U3>
+      [[gnu::diagnose_as("f-1")]]
+      static constexpr const char* f() {
+        return __PRETTY_FUNCTION__;
+      }
+  };
+};
+
+using X0int [[gnu::diagnose_as("[int|int]")]] = X0<int, int>;
+
+#define TEST(expected, fun)                                                    \
+  static_assert(__builtin_strcmp(expected, fun) == 0, "")
+
+
+void h() {
+  TEST("constexpr const char* @1::@3::fun:3()",
+      A::C<int>().h());
+  TEST("constexpr const char* @1::@3::fun:2<T>() [with T = float]",
+      A::C<int>().g<float>());
+  TEST("constexpr const char* @1::@2<U>::fun:3() [with U = char]",
+      A::C<char>().h());
+  TEST("constexpr const char* @1::@2<U>::fun:2<T>() [with T = float; U = char]",
+      A::C<char>().g<float>());
+  TEST("constexpr const char* foo::barchar::A'<T0, T1>::f() [with T0 = char; T1 = char]",
+      (foo::Bar<char>::A<char, char>().f()));
+  TEST("constexpr const char* foo::barchar::A'<T, float>::f() [with T = char]",
+      (foo::Bar<char>::A<char, float>().f()));
+  TEST("constexpr const char* foo::barchar::Adouble<T, double>::f() [with T = int]",
+      (foo::Bar<char>::A<int, double>().f()));
+  TEST("constexpr const char* foo::barchar::SpecialA::f()",
+      (foo::Bar<char>::A<int, int>().f()));
+  TEST("constexpr const char* foo::Bar'<U>::A'<T0, T1>::f() [with T0 = char; T1 = char; U = float]",
+      (foo::Bar<float>::A<char, char>().f()));
+  TEST("constexpr const char* foo::Bar'<U>::A'<T, float>::f() [with T = char; U = float]",
+      (foo::Bar<float>::A<char, float>().f()));
+  TEST("constexpr const char* foo::Bar'<U>::Adouble<T, double>::f() [with T = char; U = float]",
+      (foo::Bar<float>::A<char, double>().f()));
+  TEST("constexpr const char* foo::Bar'<U>::SpecialA::f() [with U = float]",
+      (foo::Bar<float>::A<int, int>().f()));
+  TEST("static constexpr const char* foo::barchar::f()",
+      foo::Bar<char>::f());
+  TEST("static constexpr const char* foo::BarPtr<P*>::f() [with P = char]",
+      foo::Bar<char*>::f());
+  TEST("static constexpr const char* foo::BarPtr<P*>::f() [with P = float]",
+      foo::Bar<float*>::f());
+  TEST("static constexpr const char* foo::Bar'<U>::f() [with U = float]",
+      foo::Bar<float>::f());
+  TEST("static constexpr const char* foo::SpecialBar::f()",
+      foo::Bar<void>::f());
+  TEST("static constexpr const char* X.0<T0, U0>::X1<T1, U1>::X2'::f-1<T3, U3>() [with T3 = long int; U3 = long long int; T1 = short int; U1 = int; T0 = char; U0 = short int]",
+      (X0<char, short>::X1<short, int>::X2<int, long>::f<long, long long>()));
+  TEST("static constexpr const char* X.0<T0, U0>::X1<T1, U1>::X2<T2, U2>::f-1<T3, U3>() [with T3 = long int; U3 = long long int; T2 = long int; U2 = int; T1 = short int; U1 = int; T0 = char; U0 = short int]",
+      (X0<char, short>::X1<short, int>::X2<long, int>::f<long, long long>()));
+  TEST("static constexpr const char* X.0<T0, U0>::X.3::f-1<T3, U3>() [with T3 = long int; U3 = long long int; T0 = char; U0 = short int]",
+      (X0<char, short>::X3::f<long, long long>()));
+  TEST("static constexpr const char* [int|int]::f-1<T3, U3>() [with T3 = long int; U3 = long long int]",
+      (X0<int, int>::f<long, long long>()));
+  TEST("static constexpr const char* [int|int]::X.3::f-1<T3, U3>() [with T3 = long int; U3 = long long int]",
+      (X0<int, int>::X3::f<long, long long>()));
+}
diff --git a/gcc/testsuite/g++.dg/diagnostic/diagnose-as4.C b/gcc/testsuite/g++.dg/diagnostic/diagnose-as4.C
new file mode 100644
index 00000000000..a36f38aeac0
--- /dev/null
+++ b/gcc/testsuite/g++.dg/diagnostic/diagnose-as4.C
@@ -0,0 +1,158 @@
+// { dg-options "-fdiagnostics-use-aliases -fno-pretty-templates" }
+// { dg-do compile { target c++11 } }
+
+const char* volatile s;
+
+namespace foo
+{
+  template <class U>
+    struct [[gnu::diagnose_as("Bar'")]] Bar
+    {
+      template <class T0, class T1>
+        struct [[gnu::diagnose_as("A'")]] A
+        { constexpr const char* f() { return __PRETTY_FUNCTION__; } };
+
+      template <class T>
+        struct A<T, float>
+        { constexpr const char* f() { return __PRETTY_FUNCTION__; } };
+
+      template <class T>
+        struct [[gnu::diagnose_as("Adouble")]] A<T, double>
+        { constexpr const char* f() { return __PRETTY_FUNCTION__; } };
+
+      using Special [[gnu::diagnose_as("SpecialA")]] = A<int, int>;
+
+      static constexpr const char* f() { return __PRETTY_FUNCTION__; }
+    };
+
+  template <class P>
+    struct
+    [[gnu::diagnose_as("BarPtr")]]
+    Bar<P*>
+    {
+      template <class U = int>
+      static constexpr const char* f() { return __PRETTY_FUNCTION__; }
+    };
+
+  template <>
+    struct [[gnu::diagnose_as("SpecialBar")]] Bar<void>
+    {
+      static constexpr const char* f() { return __PRETTY_FUNCTION__; }
+    };
+
+  using barchar [[gnu::diagnose_as("barchar")]] = Bar<char>;
+}
+
+namespace A
+{
+  inline namespace B __attribute__((diagnose_as("@1")))
+  {
+    template  <typename U>
+      struct __attribute__((diagnose_as("@2"))) C
+      {
+        template <class T>
+        __attribute__((diagnose_as("fun:2")))
+          constexpr const char* g() { return __PRETTY_FUNCTION__; }
+
+        __attribute__((diagnose_as("fun:3")))
+          constexpr const char* h()
+          { return __PRETTY_FUNCTION__; }
+      };
+
+    typedef C<int> D __attribute__((diagnose_as("@3")));
+  }
+}
+
+template <class T0, class U0>
+struct [[gnu::diagnose_as("XX0")]] X0
+{
+  template <class T1, class U1>
+    struct X1
+    {
+      template <class T2, class U2>
+        struct X2
+        {
+          template <class T3, class U3>
+            [[gnu::diagnose_as("f-1")]]
+            static constexpr const char* f() {
+              return __PRETTY_FUNCTION__;
+            }
+        };
+
+      using XX [[gnu::diagnose_as("X2'")]] = X2<int, long>;
+    };
+
+  template <class T3, class U3>
+    [[gnu::diagnose_as("f-2")]]
+    static constexpr const char* f() {
+      return __PRETTY_FUNCTION__;
+    }
+
+  struct [[gnu::diagnose_as("XX3")]] X3
+  {
+    template <class T3, class U3>
+      [[gnu::diagnose_as("f-3")]]
+      static constexpr const char* f() { // { dg-line X0_X3_f }
+        return __PRETTY_FUNCTION__;
+      }
+  };
+};
+
+using X0int [[gnu::diagnose_as("X0intint")]] = X0<int, int>;
+
+#define TEST(expected, fun)                                                    \
+  static_assert(__builtin_strcmp(expected, fun) == 0, "")
+
+
+void h() {
+  TEST("constexpr const char* @1::@3::fun:3()",
+      A::C<int>().h());
+  TEST("constexpr const char* @1::@3::fun:2<float>()",
+      A::C<int>().g<float>());
+  TEST("constexpr const char* @1::@2<char>::fun:3()",
+      A::C<char>().h());
+  TEST("constexpr const char* @1::@2<char>::fun:2<float>()",
+      A::C<char>().g<float>());
+  TEST("constexpr const char* foo::barchar::A'<char, char>::f()",
+      (foo::Bar<char>::A<char, char>().f()));
+  TEST("constexpr const char* foo::barchar::A'<char, float>::f()",
+      (foo::Bar<char>::A<char, float>().f()));
+  TEST("constexpr const char* foo::barchar::A'<int, double>::f()",
+      (foo::Bar<char>::A<int, double>().f()));
+  TEST("constexpr const char* foo::barchar::SpecialA::f()",
+      (foo::Bar<char>::A<int, int>().f()));
+  TEST("constexpr const char* foo::Bar'<float>::A'<char, char>::f()",
+      (foo::Bar<float>::A<char, char>().f()));
+  TEST("constexpr const char* foo::Bar'<float>::A'<char, float>::f()",
+      (foo::Bar<float>::A<char, float>().f()));
+  TEST("constexpr const char* foo::Bar'<float>::A'<char, double>::f()",
+      (foo::Bar<float>::A<char, double>().f()));
+  TEST("constexpr const char* foo::Bar'<float>::SpecialA::f()",
+      (foo::Bar<float>::A<int, int>().f()));
+  TEST("static constexpr const char* foo::barchar::f()",
+      foo::Bar<char>::f());
+  TEST("static constexpr const char* foo::Bar'<char*>::f()",
+      foo::Bar<char*>::f());
+  TEST("static constexpr const char* foo::Bar'<float*>::f()",
+      foo::Bar<float*>::f());
+  TEST("static constexpr const char* foo::Bar'<float>::f()",
+      foo::Bar<float>::f());
+  TEST("static constexpr const char* foo::SpecialBar::f()",
+      foo::Bar<void>::f());
+  TEST("static constexpr const char* XX0<char, short int>::X1<short int, int>::X2'::f-1<long int, long long int>()",
+      (X0<char, short>::X1<short, int>::X2<int, long>::f<long, long long>()));
+  TEST("static constexpr const char* XX0<char, short int>::X1<short int, int>::X2<long int, int>::f-1<long int, long long int>()",
+      (X0<char, short>::X1<short, int>::X2<long, int>::f<long, long long>()));
+  TEST("static constexpr const char* XX0<char, short int>::XX3::f-3<long int, long long int>()",
+      (X0<char, short>::X3::f<long, long long>()));
+  TEST("static constexpr const char* X0intint::f-2<long int, long long int>()",
+      (X0<int, int>::f<long, long long>()));
+  TEST("static constexpr const char* X0intint::XX3::f-3<long int, long long int>()",
+      (X0<int, int>::X3::f<long, long long>()));
+
+  X0<char, short>::X3::f<long, long long>(1); // { dg-error "no matching function for call to 'XX0<char, short int>::XX3::f<long int, long long int>\\(int\\)" }
+  // { dg-message "candidate: 'template<class T3, class U3> static constexpr const char\\* XX0<char, short int>::XX3::f-3\\(\\)'" "" { target *-*-* } X0_X3_f }
+
+  X0<int, int>::X3::f<long, long long>(1); // { dg-error "no matching function for call to 'X0intint::XX3::f<long int, long long int>\\(int\\)" }
+  // { dg-message "candidate: 'template<class T3, class U3> static constexpr const char\\* X0intint::XX3::f-3\\(\\)'" "" { target *-*-* } X0_X3_f }
+}
diff --git a/gcc/testsuite/g++.dg/diagnostic/diagnose-as5.C b/gcc/testsuite/g++.dg/diagnostic/diagnose-as5.C
new file mode 100644
index 00000000000..2a46d64964f
--- /dev/null
+++ b/gcc/testsuite/g++.dg/diagnostic/diagnose-as5.C
@@ -0,0 +1,21 @@
+// { dg-options "-fdiagnostics-use-aliases -fpretty-templates" }
+// { dg-do compile { target c++11 } }
+
+namespace std
+{
+  template <class T, class A>
+    class basic_string {};
+
+  namespace pmr
+  {
+    struct P {};
+    using string [[gnu::diagnose_as]] = basic_string<char, P>;
+  }
+}
+
+void f(int);
+
+int main()
+{
+  f(std::pmr::string()); // { dg-error "cannot convert 'std::pmr::string' to 'int'" }
+}
diff --git a/gcc/testsuite/g++.dg/diagnostic/diagnose-as6.C b/gcc/testsuite/g++.dg/diagnostic/diagnose-as6.C
new file mode 100644
index 00000000000..94747a7aa6e
--- /dev/null
+++ b/gcc/testsuite/g++.dg/diagnostic/diagnose-as6.C
@@ -0,0 +1,45 @@
+// { dg-options "-fdiagnostics-use-aliases -fpretty-templates" }
+// { dg-do compile { target c++11 } }
+
+template <class T, class U>
+  struct A
+  {
+    struct X {};
+    using Y [[gnu::diagnose_as]] = X;
+  };
+
+template <>
+  struct [[gnu::diagnose_as("A!")]] A<int, int>
+  {
+    struct X {};
+    using Y [[gnu::diagnose_as]] = X;
+  };
+
+void f(int);
+
+template <class T, class U>
+  using B [[gnu::diagnose_as]] = A<T, U>;
+
+// this is not a real alias template
+template <class T>
+  using C [[gnu::diagnose_as]] = A<int, int>; // { dg-warning "'diagnose_as' attribute ignored" }
+
+// Would the following request A<float, int> to be diagnosed as D<int>?
+// What about `= A<float, const T>', etc? This is not worth the implementation
+// complexity.
+template <class T>
+  using D [[gnu::diagnose_as]] = A<float, T>; // { dg-warning "'diagnose_as' attribute ignored" }
+
+template <class T, class U>
+  struct E {};
+template <class T, class U>
+  using F [[gnu::diagnose_as("EF")]] = E<U, T>; // { dg-warning "'diagnose_as' attribute ignored" }
+
+int main()
+{
+  f(A<char, int>()); // { dg-error "cannot convert 'B<char, int>' to 'int'" }
+  f(A<char, int>::X()); // { dg-error "cannot convert 'B<char, int>::Y' to 'int'" }
+  f(B<int, int>::X()); // { dg-error "cannot convert 'A!::Y' to 'int'" }
+  f(C<float>::X()); // { dg-error "cannot convert 'A!::Y' to 'int'" }
+  f(F<char, int>()); // { dg-error "cannot convert 'F<char, int>' {aka 'E<int, char>'} to 'int'" }
+}
diff --git a/gcc/testsuite/g++.dg/diagnostic/diagnose-as7.C b/gcc/testsuite/g++.dg/diagnostic/diagnose-as7.C
new file mode 100644
index 00000000000..3005c787615
--- /dev/null
+++ b/gcc/testsuite/g++.dg/diagnostic/diagnose-as7.C
@@ -0,0 +1,43 @@
+// { dg-options "-fdiagnostics-use-aliases -fpretty-templates" }
+// { dg-do compile { target c++11 } }
+
+namespace A
+{
+  inline namespace B
+  {
+    template <typename U>
+      struct __attribute__((diagnose_as("@2"))) C
+      {
+	template <typename T>
+	  [[gnu::diagnose_as("fun:1")]] void f() {} // { dg-line f }
+
+	template <typename T>
+	  [[gnu::diagnose_as("fun:2")]] void g(T) {} // { dg-line g }
+
+	template <typename T>
+	  [[deprecated, gnu::diagnose_as("fun:3")]] void h(T) {}
+
+	template <typename T, typename R = void>
+	  [[deprecated, gnu::diagnose_as("fun:4")]] R f0(T) { return R(); }
+      };
+
+    using D [[gnu::diagnose_as]] = C<int>;
+  }
+}
+
+namespace AB [[gnu::diagnose_as]] = A::B;
+
+int main()
+{
+  A::C<int>().f<float>(1); // { dg-error "no matching function for call to 'AB::D::f<float>\\(int\\)'" }
+  // { dg-message "candidate: 'template<class T> void AB::D::fun:1\\(\\)'" "" { target *-*-* } f }
+
+  A::C<int>().g<int>(1, 1); // { dg-error "no matching function for call to 'AB::D::g<int>\\(int, int\\)'" }
+  // { dg-message "candidate: 'template<class T> void AB::D::fun:2\\(T\\)'" "" { target *-*-* } g }
+
+  A::C<int>().h(1); // { dg-warning "'void AB::D::fun:3\\(T\\) \\\[with T = int\\\]' is deprecated" }
+
+  A::C<int>().f0(1); // { dg-warning "'R AB::D::fun:4\\(T\\) \\\[with T = int\\\]' is deprecated" }
+  A::C<int>().f0<float>(1); // { dg-warning "'R AB::D::fun:4<T>\\(T\\) \\\[with T = float\\\]' is deprecated" }
+  A::C<int>().f0<float, int>(1); // { dg-warning "'R AB::D::fun:4<T, R>\\(T\\) \\\[with T = float; R = int\\\]' is deprecated" }
+}

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

* Re: [RFC] c++: Print function template parms when relevant (was: [PATCH v4] c++: Add gnu::diagnose_as attribute)
  2021-11-08 20:00       ` Matthias Kretz
@ 2021-11-16 20:25         ` Jason Merrill
  2021-11-16 20:42           ` Matthias Kretz
  0 siblings, 1 reply; 24+ messages in thread
From: Jason Merrill @ 2021-11-16 20:25 UTC (permalink / raw)
  To: Matthias Kretz, gcc-patches

On 11/8/21 15:00, Matthias Kretz wrote:
> I forgot to mention why I tagged it [RFC]: I needed one more bit of
> information on the template args TREE_VEC to encode EXPLICIT_TEMPLATE_ARGS_P.
> Its TREE_CHAIN already points to an integer constant denoting the number of
> non-default arguments, so I couldn't trivially replace that. Therefore, I used
> the sign of that integer. I was hoping to find a cleaner solution, though.

It seems that we aren't using any TREE_LANG_FLAG_n on TREE_VEC, so that 
would be a cleaner solution.

> On Monday, 8 November 2021 17:40:44 CET Matthias Kretz wrote:
>> On Tuesday, 17 August 2021 20:31:54 CET Jason Merrill wrote:
>>>> 2. Given a DECL_TI_ARGS tree, can I query whether an argument was
>>>> deduced
>>>> or explicitly specified? I'm asking because I still consider diagnostics
>>>> of function templates unfortunate. `template <class T> void f()` is
>>>> fine,
>>>> as is `void f(T) [with T = float]`, but `void f() [with T = float]`
>>>> could
>>>> be better. I.e. if the template parameter appears somewhere in the
>>>> function parameter list, dump_template_parms would only produce noise.
>>>> If, however, the template parameter was given explicitly, it would be
>>>> nice if it could show up accordingly in diagnostics.
>>>
>>> NON_DEFAULT_TEMPLATE_ARGS_COUNT has that information, though there are
>>> some issues with it.  Attached is my WIP from May to improve it
>>> somewhat, if that's interesting.
>>
>> It is interesting. I used your patch to come up with the attached. Patch. I
>> must say, I didn't try to read through all the cp/pt.c code to understand
>> all of what you did there (which is why my ChangeLog entry says "Jason?"),
>> but it works for me (and all of `make check`).
>>
>> Anyway, I'd like to propose the following before finishing my diagnose_as
>> patch. I believe it's useful to fix this part first. The diagnostic/default-
>> template-args-[12].C tests show a lot of examples of the intent of this
>> patch. And the remaining changes to the testsuite show how it changes
>> diagnostic output.
>>
>> ---------------------- 8< --------------------
>>
>> The choice when to print a function template parameter was still
>> suboptimal. That's because sometimes the function template parameter
>> list only adds noise, while in other situations the lack of a function
>> template parameter list makes diagnostic messages hard to understand.
>>
>> The general idea of this change is to print template parms wherever they
>> would appear in the source code as well. Thus, the diagnostics code
>> needs to know whether any template parameter was given explicitly.
>>
>> Signed-off-by: Matthias Kretz <m.kretz@gsi.de>
>>
>> gcc/testsuite/ChangeLog:
>>
>>          * g++.dg/debug/dwarf2/template-params-12n.C: Optionally, allow
>>          DW_AT_default_value.
>>          * g++.dg/diagnostic/default-template-args-1.C: New.
>>          * g++.dg/diagnostic/default-template-args-2.C: New.
>>          * g++.dg/diagnostic/param-type-mismatch-2.C: Expect template
>>          parms in diagnostic.
>>          * g++.dg/ext/pretty1.C: Expect function template specialization
>>          to not pretty-print template parms.
>>          * g++.old-deja/g++.ext/pretty3.C: Ditto.
>>          * g++.old-deja/g++.pt/memtemp77.C: Ditto.
>>          * g++.dg/goacc/template.C: Expect function template parms for
>>          explicit arguments.
>>          * g++.dg/gomp/declare-variant-7.C: Expect no function template
>>          parms for deduced arguments.
>>          * g++.dg/template/error40.C: Expect only non-default template
>>          arguments in diagnostic.
>>
>> gcc/cp/ChangeLog:
>>
>>          * cp-tree.h (GET_NON_DEFAULT_TEMPLATE_ARGS_COUNT): Return
>>          absolute value of stored constant.
>>          (EXPLICIT_TEMPLATE_ARGS_P): New.
>>          (SET_EXPLICIT_TEMPLATE_ARGS_P): New.
>>          (TFF_AS_PRIMARY): New constant.
>>          * error.c (get_non_default_template_args_count): Avoid
>>          GET_NON_DEFAULT_TEMPLATE_ARGS_COUNT if
>>          NON_DEFAULT_TEMPLATE_ARGS_COUNT is a NULL_TREE. Make independent
>>          of flag_pretty_templates.
>>          (dump_template_bindings): Add flags parameter to be passed to
>>          get_non_default_template_args_count. Print only non-default
>>          template arguments.
>>          (dump_function_decl): Call dump_function_name and dump_type of
>>          the DECL_CONTEXT with specialized template and set
>>          TFF_AS_PRIMARY for their flags.
>>          (dump_function_name): Add and document conditions for calling
>>          dump_template_parms.
>>          (dump_template_parms): Print only non-default template
>>          parameters.
>>          * pt.c (determine_specialization): Jason?
>>          (template_parms_level_to_args): Jason?
>>          (copy_template_args): Jason?
>>          (fn_type_unification): Set EXPLICIT_TEMPLATE_ARGS_P on the
>>          template arguments tree if any template parameter was explicitly
>>          given.
>>          (type_unification_real): Jason?
>>          (get_partial_spec_bindings): Jason?
>>          (tsubst_template_args): Determine number of defaulted arguments
>>          from new argument vector, if possible.
>> ---
>>   gcc/cp/cp-tree.h                              | 18 +++-
>>   gcc/cp/error.c                                | 83 ++++++++++++++-----
>>   gcc/cp/pt.c                                   | 58 +++++++++----
>>   .../g++.dg/debug/dwarf2/template-params-12n.C |  2 +-
>>   .../diagnostic/default-template-args-1.C      | 73 ++++++++++++++++
>>   .../diagnostic/default-template-args-2.C      | 37 +++++++++
>>   .../g++.dg/diagnostic/param-type-mismatch-2.C |  2 +-
>>   gcc/testsuite/g++.dg/ext/pretty1.C            |  2 +-
>>   gcc/testsuite/g++.dg/goacc/template.C         |  8 +-
>>   gcc/testsuite/g++.dg/gomp/declare-variant-7.C |  4 +-
>>   gcc/testsuite/g++.dg/template/error40.C       |  6 +-
>>   gcc/testsuite/g++.old-deja/g++.ext/pretty3.C  |  2 +-
>>   gcc/testsuite/g++.old-deja/g++.pt/memtemp77.C |  2 +-
>>   13 files changed, 242 insertions(+), 55 deletions(-)
>>   create mode 100644
>> gcc/testsuite/g++.dg/diagnostic/default-template-args-1.C create mode
>> 100644 gcc/testsuite/g++.dg/diagnostic/default-template-args-2.C
> 
> 


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

* Re: [RFC] c++: Print function template parms when relevant (was: [PATCH v4] c++: Add gnu::diagnose_as attribute)
  2021-11-16 20:25         ` Jason Merrill
@ 2021-11-16 20:42           ` Matthias Kretz
  2021-11-16 20:49             ` Jason Merrill
  0 siblings, 1 reply; 24+ messages in thread
From: Matthias Kretz @ 2021-11-16 20:42 UTC (permalink / raw)
  To: gcc-patches, Jason Merrill

On Tuesday, 16 November 2021 21:25:33 CET Jason Merrill wrote:
> On 11/8/21 15:00, Matthias Kretz wrote:
> > I forgot to mention why I tagged it [RFC]: I needed one more bit of
> > information on the template args TREE_VEC to encode
> > EXPLICIT_TEMPLATE_ARGS_P. Its TREE_CHAIN already points to an integer
> > constant denoting the number of non-default arguments, so I couldn't
> > trivially replace that. Therefore, I used the sign of that integer. I was
> > hoping to find a cleaner solution, though.
> It seems that we aren't using any TREE_LANG_FLAG_n on TREE_VEC, so that
> would be a cleaner solution.

I tried that first but realized that TREE_VEC doesn't allow any 
TREE_LANG_FLAGs (it uses those bits for the length IIRC). And setting the 
TREE_LANG_FLAGs on the TREE_CHAIN of the TREE_VEC can't work either (since the 
int constants are shared between many trees).

Should I maybe turn the TREE_CHAIN into a TREE_LIST using TREE_PURPOSE and 
TREE_VALUE for EXPLICIT_TEMPLATE_ARGS_P and non-default arguments, 
respectively? (And where would I document this?)

-- 
──────────────────────────────────────────────────────────────────────────
 Dr. Matthias Kretz                           https://mattkretz.github.io
 GSI Helmholtz Centre for Heavy Ion Research               https://gsi.de
 stdₓ::simd
──────────────────────────────────────────────────────────────────────────

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

* Re: [RFC] c++: Print function template parms when relevant (was: [PATCH v4] c++: Add gnu::diagnose_as attribute)
  2021-11-16 20:42           ` Matthias Kretz
@ 2021-11-16 20:49             ` Jason Merrill
  2021-11-16 20:51               ` Matthias Kretz
  0 siblings, 1 reply; 24+ messages in thread
From: Jason Merrill @ 2021-11-16 20:49 UTC (permalink / raw)
  To: Matthias Kretz, gcc-patches

On 11/16/21 15:42, Matthias Kretz wrote:
> On Tuesday, 16 November 2021 21:25:33 CET Jason Merrill wrote:
>> On 11/8/21 15:00, Matthias Kretz wrote:
>>> I forgot to mention why I tagged it [RFC]: I needed one more bit of
>>> information on the template args TREE_VEC to encode
>>> EXPLICIT_TEMPLATE_ARGS_P. Its TREE_CHAIN already points to an integer
>>> constant denoting the number of non-default arguments, so I couldn't
>>> trivially replace that. Therefore, I used the sign of that integer. I was
>>> hoping to find a cleaner solution, though.
>> It seems that we aren't using any TREE_LANG_FLAG_n on TREE_VEC, so that
>> would be a cleaner solution.
> 
> I tried that first but realized that TREE_VEC doesn't allow any
> TREE_LANG_FLAGs (it uses those bits for the length IIRC). And setting the
> TREE_LANG_FLAGs on the TREE_CHAIN of the TREE_VEC can't work either (since the
> int constants are shared between many trees).
> 
> Should I maybe turn the TREE_CHAIN into a TREE_LIST using TREE_PURPOSE and
> TREE_VALUE for EXPLICIT_TEMPLATE_ARGS_P and non-default arguments,
> respectively? (And where would I document this?)

Maybe a TREE_LIST if there are explicit template arguments to a function 
template, where TREE_PURPOSE is the number of explicit arguments and 
TREE_VALUE is the number of non-default arguments.

I'd document it at the definition of NON_DEFAULT_TEMPLATE_ARGS_COUNT. 
The SET/GET macros should become functions.

Jason


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

* Re: [RFC] c++: Print function template parms when relevant (was: [PATCH v4] c++: Add gnu::diagnose_as attribute)
  2021-11-16 20:49             ` Jason Merrill
@ 2021-11-16 20:51               ` Matthias Kretz
  0 siblings, 0 replies; 24+ messages in thread
From: Matthias Kretz @ 2021-11-16 20:51 UTC (permalink / raw)
  To: gcc-patches, Jason Merrill

On Tuesday, 16 November 2021 21:49:31 CET Jason Merrill wrote:
> On 11/16/21 15:42, Matthias Kretz wrote:
> > On Tuesday, 16 November 2021 21:25:33 CET Jason Merrill wrote:
> >> On 11/8/21 15:00, Matthias Kretz wrote:
> >>> I forgot to mention why I tagged it [RFC]: I needed one more bit of
> >>> information on the template args TREE_VEC to encode
> >>> EXPLICIT_TEMPLATE_ARGS_P. Its TREE_CHAIN already points to an integer
> >>> constant denoting the number of non-default arguments, so I couldn't
> >>> trivially replace that. Therefore, I used the sign of that integer. I
> >>> was
> >>> hoping to find a cleaner solution, though.
> >> 
> >> It seems that we aren't using any TREE_LANG_FLAG_n on TREE_VEC, so that
> >> would be a cleaner solution.
> > 
> > I tried that first but realized that TREE_VEC doesn't allow any
> > TREE_LANG_FLAGs (it uses those bits for the length IIRC). And setting the
> > TREE_LANG_FLAGs on the TREE_CHAIN of the TREE_VEC can't work either (since
> > the int constants are shared between many trees).
> > 
> > Should I maybe turn the TREE_CHAIN into a TREE_LIST using TREE_PURPOSE and
> > TREE_VALUE for EXPLICIT_TEMPLATE_ARGS_P and non-default arguments,
> > respectively? (And where would I document this?)
> 
> Maybe a TREE_LIST if there are explicit template arguments to a function
> template, where TREE_PURPOSE is the number of explicit arguments and
> TREE_VALUE is the number of non-default arguments.
> 
> I'd document it at the definition of NON_DEFAULT_TEMPLATE_ARGS_COUNT.
> The SET/GET macros should become functions.

Sounds good. I'll come up with a new patch ASAP.

-- 
──────────────────────────────────────────────────────────────────────────
 Dr. Matthias Kretz                           https://mattkretz.github.io
 GSI Helmholtz Centre for Heavy Ion Research               https://gsi.de
 stdₓ::simd
──────────────────────────────────────────────────────────────────────────

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

* Re: [RFC] c++: Print function template parms when relevant (was: [PATCH v4] c++: Add gnu::diagnose_as attribute)
  2021-11-08 16:40     ` [RFC] c++: Print function template parms when relevant (was: [PATCH v4] c++: Add gnu::diagnose_as attribute) Matthias Kretz
  2021-11-08 20:00       ` Matthias Kretz
@ 2021-11-17  6:09       ` Jason Merrill
  2021-11-17  9:04         ` Matthias Kretz
  1 sibling, 1 reply; 24+ messages in thread
From: Jason Merrill @ 2021-11-17  6:09 UTC (permalink / raw)
  To: Matthias Kretz, gcc-patches

On 11/8/21 11:40, Matthias Kretz wrote:
> On Tuesday, 17 August 2021 20:31:54 CET Jason Merrill wrote:
>>> 2. Given a DECL_TI_ARGS tree, can I query whether an argument was deduced
>>> or explicitly specified? I'm asking because I still consider diagnostics
>>> of function templates unfortunate. `template <class T> void f()` is fine,
>>> as is `void f(T) [with T = float]`, but `void f() [with T = float]` could
>>> be better. I.e. if the template parameter appears somewhere in the
>>> function parameter list, dump_template_parms would only produce noise.
>>> If, however, the template parameter was given explicitly, it would be
>>> nice if it could show up accordingly in diagnostics.
>>
>> NON_DEFAULT_TEMPLATE_ARGS_COUNT has that information, though there are
>> some issues with it.  Attached is my WIP from May to improve it
>> somewhat, if that's interesting.
> 
> It is interesting. I used your patch to come up with the attached. Patch. I
> must say, I didn't try to read through all the cp/pt.c code to understand all
> of what you did there (which is why my ChangeLog entry says "Jason?"), but it
> works for me (and all of `make check`).
> 
> Anyway, I'd like to propose the following before finishing my diagnose_as
> patch. I believe it's useful to fix this part first. The diagnostic/default-
> template-args-[12].C tests show a lot of examples of the intent of this patch.
> And the remaining changes to the testsuite show how it changes diagnostic
> output.
> 
> ---------------------- 8< --------------------
> 
> The choice when to print a function template parameter was still
> suboptimal. That's because sometimes the function template parameter
> list only adds noise, while in other situations the lack of a function
> template parameter list makes diagnostic messages hard to understand.
> 
> The general idea of this change is to print template parms wherever they
> would appear in the source code as well. Thus, the diagnostics code
> needs to know whether any template parameter was given explicitly.
> 
> Signed-off-by: Matthias Kretz <m.kretz@gsi.de>
> 
> gcc/testsuite/ChangeLog:
> 
>          * g++.dg/debug/dwarf2/template-params-12n.C: Optionally, allow
>          DW_AT_default_value.
>          * g++.dg/diagnostic/default-template-args-1.C: New.
>          * g++.dg/diagnostic/default-template-args-2.C: New.
>          * g++.dg/diagnostic/param-type-mismatch-2.C: Expect template
>          parms in diagnostic.
>          * g++.dg/ext/pretty1.C: Expect function template specialization
>          to not pretty-print template parms.
>          * g++.old-deja/g++.ext/pretty3.C: Ditto.
>          * g++.old-deja/g++.pt/memtemp77.C: Ditto.
>          * g++.dg/goacc/template.C: Expect function template parms for
>          explicit arguments.
>          * g++.dg/gomp/declare-variant-7.C: Expect no function template
>          parms for deduced arguments.
>          * g++.dg/template/error40.C: Expect only non-default template
>          arguments in diagnostic.
> 
> gcc/cp/ChangeLog:
> 
>          * cp-tree.h (GET_NON_DEFAULT_TEMPLATE_ARGS_COUNT): Return
>          absolute value of stored constant.
>          (EXPLICIT_TEMPLATE_ARGS_P): New.
>          (SET_EXPLICIT_TEMPLATE_ARGS_P): New.
>          (TFF_AS_PRIMARY): New constant.
>          * error.c (get_non_default_template_args_count): Avoid
>          GET_NON_DEFAULT_TEMPLATE_ARGS_COUNT if
>          NON_DEFAULT_TEMPLATE_ARGS_COUNT is a NULL_TREE. Make independent
>          of flag_pretty_templates.
>          (dump_template_bindings): Add flags parameter to be passed to
>          get_non_default_template_args_count. Print only non-default
>          template arguments.
>          (dump_function_decl): Call dump_function_name and dump_type of
>          the DECL_CONTEXT with specialized template and set
>          TFF_AS_PRIMARY for their flags.
>          (dump_function_name): Add and document conditions for calling
>          dump_template_parms.
>          (dump_template_parms): Print only non-default template
>          parameters.
>          * pt.c (determine_specialization): Jason?

Also copy the inner TREE_VECs.

>          (template_parms_level_to_args): Jason?

Always count non-default args.  Also, I think

> -  if (CHECKING_P)
> -    SET_NON_DEFAULT_TEMPLATE_ARGS_COUNT (a, TREE_VEC_LENGTH (a));
> +  SET_NON_DEFAULT_TEMPLATE_ARGS_COUNT (a, nondefault);

should have been

if (CHECKING_P || nondefault != TREE_VEC_LENGTH (a))
   SET_NON_DEFAULT_TEMPLATE_ARGS_COUNT (a, nondefault);

>          (copy_template_args): Jason?

Only copy the non-default template args count on TREE_VECs that should 
have it.

>          (fn_type_unification): Set EXPLICIT_TEMPLATE_ARGS_P on the
>          template arguments tree if any template parameter was explicitly
>          given.
>          (type_unification_real): Jason?

Count non-default args sooner.

>          (get_partial_spec_bindings): Jason?

Set non-default args count.

>          (tsubst_template_args): Determine number of defaulted arguments
>          from new argument vector, if possible.
> ---
>   gcc/cp/cp-tree.h                              | 18 +++-
>   gcc/cp/error.c                                | 83 ++++++++++++++-----
>   gcc/cp/pt.c                                   | 58 +++++++++----
>   .../g++.dg/debug/dwarf2/template-params-12n.C |  2 +-
>   .../diagnostic/default-template-args-1.C      | 73 ++++++++++++++++
>   .../diagnostic/default-template-args-2.C      | 37 +++++++++
>   .../g++.dg/diagnostic/param-type-mismatch-2.C |  2 +-
>   gcc/testsuite/g++.dg/ext/pretty1.C            |  2 +-
>   gcc/testsuite/g++.dg/goacc/template.C         |  8 +-
>   gcc/testsuite/g++.dg/gomp/declare-variant-7.C |  4 +-
>   gcc/testsuite/g++.dg/template/error40.C       |  6 +-
>   gcc/testsuite/g++.old-deja/g++.ext/pretty3.C  |  2 +-
>   gcc/testsuite/g++.old-deja/g++.pt/memtemp77.C |  2 +-
>   13 files changed, 242 insertions(+), 55 deletions(-)
>   create mode 100644 gcc/testsuite/g++.dg/diagnostic/default-template-args-1.C
>   create mode 100644 gcc/testsuite/g++.dg/diagnostic/default-template-args-2.C
> 

> +  /* Pretty print only template instantiations. Don't pretty print explicit
> +     specializations like 'template <> void fun<int> (int)'.  */

This seems like a significant change of behavior unrelated to printing 
default template arguments.  What's the rationale for handling 
specializations differently from instantiations?

I also don't understand the purpose of TFF_AS_PRIMARY.

> +/* Print function template parameters if:
> +   1. t is template, and
> +   2. flags did not request "show only template-name", and
> +   3. t is a specialization of a template (Why is this needed? This was present
> +      since 1999 via !DECL_FRIEND_PSEUDO_TEMPLATE_INSTANTIATION: "Don't crash if
> +      given a friend pseudo-instantiation". The DECL_USE_TEMPLATE should likely
> +      inform the PRIMARY parameter of dump_template_parms.), and

Good question.  It seems that the existing 
!DECL_FRIEND_PSEUDO_TEMPLATE_INSTANTIATION has mostly been excluding the 
most general template; removing that line changes things like

int bar(T) [with T = int]

to

int bar<T>(T) [with T = int]

> +   4. either
> +      - flags requests to show no function arguments, in which case deduced
> +        types could be hidden, or
> +      - at least one function template argument was given explicitly, or
> +      - we're printing a DWARF name,

...but perhaps this can replace the above.  Though, why do we want 
different behavior here when printing a DWARF name?

> +   5. either
> +      - t is a member friend template of a template class (see DECL_TI_TEMPLATE
> +        documentation), or
> +      - 

Missing the last item.  :)

> + */

*/ doesn't get its own line.

>    if (DECL_TEMPLATE_INFO (t)
>        && !(flags & TFF_TEMPLATE_NAME)
> -      && !DECL_FRIEND_PSEUDO_TEMPLATE_INSTANTIATION (t)
> +      && DECL_USE_TEMPLATE (t)
> +      && ((flags & TFF_NO_FUNCTION_ARGUMENTS)
> +	    || (DECL_TI_ARGS (t)
> +		  && EXPLICIT_TEMPLATE_ARGS_P (INNERMOST_TEMPLATE_ARGS
> +						 (DECL_TI_ARGS (t))))
> +	    || (pp->flags & pp_c_flag_gnu_v3) != 0)
>        && (TREE_CODE (DECL_TI_TEMPLATE (t)) != TEMPLATE_DECL
>  	  || PRIMARY_TEMPLATE_P (DECL_TI_TEMPLATE (t))))
> -    dump_template_parms (pp, DECL_TEMPLATE_INFO (t), !DECL_USE_TEMPLATE (t),
> -                         flags);
> +    dump_template_parms (pp, DECL_TEMPLATE_INFO (t), false, flags);


Jason


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

* Re: [RFC] c++: Print function template parms when relevant (was: [PATCH v4] c++: Add gnu::diagnose_as attribute)
  2021-11-17  6:09       ` Jason Merrill
@ 2021-11-17  9:04         ` Matthias Kretz
  2021-11-17 18:25           ` Jason Merrill
  0 siblings, 1 reply; 24+ messages in thread
From: Matthias Kretz @ 2021-11-17  9:04 UTC (permalink / raw)
  To: gcc-patches, Jason Merrill

On Wednesday, 17 November 2021 07:09:18 CET Jason Merrill wrote:
> > -  if (CHECKING_P)
> > -    SET_NON_DEFAULT_TEMPLATE_ARGS_COUNT (a, TREE_VEC_LENGTH (a));
> > +  SET_NON_DEFAULT_TEMPLATE_ARGS_COUNT (a, nondefault);
> 
> should have been
> 
> if (CHECKING_P || nondefault != TREE_VEC_LENGTH (a))
>    SET_NON_DEFAULT_TEMPLATE_ARGS_COUNT (a, nondefault);

TBH, I don't understand the purpose of CHECKING_P here, or rather it makes me 
nervous because AFAIU I'm only testing with CHECKING_P enabled. Why make 
behavior dependent on CHECKING_P? I expected CHECKING_P to basically only add 
more assertions.

> > (copy_template_args): Jason?
> 
> Only copy the non-default template args count on TREE_VECs that should
> have it.

Why not simply set the count on all args? Is it a performance concern? The 
INTEGER_CST the TREE_CHAIN has to point to exists anyway, so it's not wasting 
any memory, right?

> 
> > +  /* Pretty print only template instantiations. Don't pretty print
> > explicit
> > +     specializations like 'template <> void fun<int> (int)'. 
> 
> This seems like a significant change of behavior unrelated to printing
> default template arguments.  What's the rationale for handling
> specializations differently from instantiations?

Right, this is about "The general idea of this change is to print template 
parms wherever they would appear in the source code as well".

Initially, the change to print function template arguments/parameters only if 
the args were explicitly specified lead to printing 'void fun (T) [with T = 
...]' or 'template <> void fun (int)'. Both are not telling the full story, 
even if the former is how the function would be called. But if the reader 
should quickly recognize what code is getting called, it is helpful to see 
right away that a function template specialization is called. (It might also 
reveal an implementation detail of a library, so it's not 100% obvious how to 
choose here.) Also, saying 'T = int' is kind of wrong. Yes, 'int' was deduced. 
But there's no T in fun<int>:

template <class T> void fun (T);
template <> void fun<int> (int);

__FUNCTION__ was 'fun<int>' all the time, but __PRETTY_FUNCTION__ was 'void 
fun(T) [with T = int]'. It's more consistent that __PRETTY_FUNCTION__ contains 
__FUNCTION__, IMHO, so it would have to be at least 'void fun<int>(T) [with T 
= int]'. But that's strange: How it uses T and int for the same type. So I 
settled on 'void fun<int>(int)'.

> I also don't understand the purpose of TFF_AS_PRIMARY.

dump_function_decl generalizes the TEMPLATE_DECL (if flag_pretty_templates is 
true) and, before this change, passes the generalized TEMPLATE_DECL to 
dump_type (... DECL_CONTEXT (t) ...) and dump_function_name (... t ...). 
That's why the whole template is printed as primary template (i.e. with 
template parms instead of template args, as is needed for 
flag_pretty_templates). But this drops the count of non-default template args. 
To retain the count, dump_type and dump_function_name need to be called with 
the original TEMPLATE_DECL. But if I do this, pretty-templates is broken.
'template <class T> struct A { template <class U> void f(T, U); };' would 
print as 'A<int>::f<float>(T, U) [with U = float, T = int]'. To get back to 
'A<T>::f<U>(T, U) [with U = float, T = int]' I needed to tell 
dump_template_parms that even though the template args are there, it should 
print only the template parms. The most obvious way to do that was to carry it 
through via flags.

Note that this creates another problem. Given

template <class T0, class T1 = int> struct Outer {
  template <class T, class U> struct A;
  template <class X> struct A<X, int> {
    void f();
  };
};

we want to print e.g. 'void Outer<T0>::A<X, int>::f() [with X = int, T0 = 
int]', but certainly not 'void Outer<T0>::A<T, U>::f() [with X = int, T0 = 
int]'. However, specialized_t holds A<int, int> which is printed as A<T, U> 
with TFF_AS_PRIMARY. Only most_general_template of the function's 
TEMPLATE_DECL can give us A<X, int> as DECL_CONTEXT.

I have a solution in the diagnose_as patch, where I had to solve a similar 
problem because for the diagnose_as attribute (dump_template_scope).

> > +/* Print function template parameters if:
> > +   1. t is template, and
> > +   2. flags did not request "show only template-name", and
> > +   3. t is a specialization of a template (Why is this needed? This was
> > present +      since 1999 via !DECL_FRIEND_PSEUDO_TEMPLATE_INSTANTIATION:
> > "Don't crash if +      given a friend pseudo-instantiation". The
> > DECL_USE_TEMPLATE should likely +      inform the PRIMARY parameter of
> > dump_template_parms.), and
> 
> Good question.  It seems that the existing
> !DECL_FRIEND_PSEUDO_TEMPLATE_INSTANTIATION has mostly been excluding the
> most general template; removing that line changes things like
> 
> int bar(T) [with T = int]
> 
> to
> 
> int bar<T>(T) [with T = int]
> 
> 
> 
> > +   4. either
> > +      - flags requests to show no function arguments, in which case
> > deduced +        types could be hidden, or
> > +      - at least one function template argument was given explicitly, or
> > +      - we're printing a DWARF name,
> 
> ...but perhaps this can replace the above.

I'll rerun the tests with DECL_USE_TEMPLATE moved to the PRIMARY parameter of 
dump_template_parms.

> Though, why do we want
> different behavior here when printing a DWARF name?

Sorry, I should have asked ... I also added the same issue as an open question 
on the diagnose_as patch. When I ran the whole testsuite I had failures in the 
DWARF tests. This change resolved them. I don't know enough about how those 
strings are used and whether they may change between GCC versions. Anyway, the 
DWARF strings in that test had only the function name and template argument 
list (i.e. no function arguments). If the template argument list was removed 
(as was the case without the condition here), the DWARF strings lost important 
information, giving several different functions the same name. That seemed 
like a regression. So either the DWARF strings need to include the function 
arguments, or we need this condition to keep showing the template arguments 
independent of whether they were explicitly given or not.

> > +   5. either
> > +      - t is a member friend template of a template class (see
> > DECL_TI_TEMPLATE +        documentation), or
> > +      -
> 
> Missing the last item.  :)

Oh, I got distracted while trying to figure this out. :) I'll try to 
understand why PRIMARY_TEMPLATE_P (DECL_TI_TEMPLATE (t)) is needed and 
document it.

-- 
──────────────────────────────────────────────────────────────────────────
 Dr. Matthias Kretz                           https://mattkretz.github.io
 GSI Helmholtz Centre for Heavy Ion Research               https://gsi.de
 stdₓ::simd
──────────────────────────────────────────────────────────────────────────




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

* Re: [RFC] c++: Print function template parms when relevant (was: [PATCH v4] c++: Add gnu::diagnose_as attribute)
  2021-11-17  9:04         ` Matthias Kretz
@ 2021-11-17 18:25           ` Jason Merrill
  2021-11-17 22:51             ` Matthias Kretz
  0 siblings, 1 reply; 24+ messages in thread
From: Jason Merrill @ 2021-11-17 18:25 UTC (permalink / raw)
  To: Matthias Kretz, gcc-patches

On 11/17/21 04:04, Matthias Kretz wrote:
> On Wednesday, 17 November 2021 07:09:18 CET Jason Merrill wrote:
>>> -  if (CHECKING_P)
>>> -    SET_NON_DEFAULT_TEMPLATE_ARGS_COUNT (a, TREE_VEC_LENGTH (a));
>>> +  SET_NON_DEFAULT_TEMPLATE_ARGS_COUNT (a, nondefault);
>>
>> should have been
>>
>> if (CHECKING_P || nondefault != TREE_VEC_LENGTH (a))
>>     SET_NON_DEFAULT_TEMPLATE_ARGS_COUNT (a, nondefault);
> 
> TBH, I don't understand the purpose of CHECKING_P here, or rather it makes me
> nervous because AFAIU I'm only testing with CHECKING_P enabled. Why make
> behavior dependent on CHECKING_P? I expected CHECKING_P to basically only add
> more assertions.

The idea when NON_DEFAULT_TEMPLATE_ARGS_COUNT was added years back was 
to leave the TREE_CHAIN null when !CHECKING_P and treat that as 
equivalent to TREE_VEC_LENGTH (args).  But perhaps you're right that 
it's not a savings worth the complexity.

>>> (copy_template_args): Jason?
>>
>> Only copy the non-default template args count on TREE_VECs that should
>> have it.
> 
> Why not simply set the count on all args? Is it a performance concern? The
> INTEGER_CST the TREE_CHAIN has to point to exists anyway, so it's not wasting
> any memory, right?

In this case the TREE_VEC we're excluding is the one wrapping multiple 
levels of template args; it doesn't contain args directly, so setting 
NON_DEFAULT_ARGS_COUNT on it doesn't make sense.

>>> +  /* Pretty print only template instantiations. Don't pretty print
>>> explicit
>>> +     specializations like 'template <> void fun<int> (int)'.
>>
>> This seems like a significant change of behavior unrelated to printing
>> default template arguments.  What's the rationale for handling
>> specializations differently from instantiations?
> 
> Right, this is about "The general idea of this change is to print template
> parms wherever they would appear in the source code as well".
> 
> Initially, the change to print function template arguments/parameters only if
> the args were explicitly specified lead to printing 'void fun (T) [with T =
> ...]' or 'template <> void fun (int)'. Both are not telling the full story,
> even if the former is how the function would be called.

and the latter is how I expect the specialization to be declared, not 
with the deducible template argument made explicit.

> But if the reader
> should quickly recognize what code is getting called, it is helpful to see
> right away that a function template specialization is called. (It might also
> reveal an implementation detail of a library, so it's not 100% obvious how to
> choose here.) Also, saying 'T = int' is kind of wrong. Yes, 'int' was deduced.
> But there's no T in fun<int>:
> 
> template <class T> void fun (T);
> template <> void fun<int> (int);

There's a T in the template, and as you said above, that's how it's 
called (and mangled).

> __FUNCTION__ was 'fun<int>' all the time, but __PRETTY_FUNCTION__ was 'void
> fun(T) [with T = int]'.

Isn't that true for instantiations, as well?

> It's more consistent that __PRETTY_FUNCTION__ contains __FUNCTION__, IMHO

I suppose, but I don't see that as a strong enough motivation to mix 
this up.

> so it would have to be at least 'void fun<int>(T) [with T
> = int]'. But that's strange: How it uses T and int for the same type. So I
> settled on 'void fun<int>(int)'.
> 
>> I also don't understand the purpose of TFF_AS_PRIMARY.
> 
> dump_function_decl generalizes the TEMPLATE_DECL (if flag_pretty_templates is
> true) and, before this change, passes the generalized TEMPLATE_DECL to
> dump_type (... DECL_CONTEXT (t) ...) and dump_function_name (... t ...).
> That's why the whole template is printed as primary template (i.e. with
> template parms instead of template args, as is needed for
> flag_pretty_templates). But this drops the count of non-default template args.

Ah, you're trying to omit defaulted parms from the <list>?  I'm not sure 
that's necessary, leaving them out of the [with ...] list should be 
sufficient.

> To retain the count, dump_type and dump_function_name need to be called with
> the original TEMPLATE_DECL. But if I do this, pretty-templates is broken.
> 'template <class T> struct A { template <class U> void f(T, U); };' would
> print as 'A<int>::f<float>(T, U) [with U = float, T = int]'. To get back to
> 'A<T>::f<U>(T, U) [with U = float, T = int]' I needed to tell
> dump_template_parms that even though the template args are there, it should
> print only the template parms. The most obvious way to do that was to carry it
> through via flags.
> 
> Note that this creates another problem. Given
> 
> template <class T0, class T1 = int> struct Outer {
>    template <class T, class U> struct A;
>    template <class X> struct A<X, int> {
>      void f();
>    };
> };
> 
> we want to print e.g. 'void Outer<T0>::A<X, int>::f() [with X = int, T0 =
> int]', but certainly not 'void Outer<T0>::A<T, U>::f() [with X = int, T0 =
> int]'. However, specialized_t holds A<int, int> which is printed as A<T, U>
> with TFF_AS_PRIMARY. Only most_general_template of the function's
> TEMPLATE_DECL can give us A<X, int> as DECL_CONTEXT.
> 
> I have a solution in the diagnose_as patch, where I had to solve a similar
> problem because for the diagnose_as attribute (dump_template_scope).
> 
>>> +/* Print function template parameters if:
>>> +   1. t is template, and
>>> +   2. flags did not request "show only template-name", and
>>> +   3. t is a specialization of a template (Why is this needed? This was
>>> present +      since 1999 via !DECL_FRIEND_PSEUDO_TEMPLATE_INSTANTIATION:
>>> "Don't crash if +      given a friend pseudo-instantiation". The
>>> DECL_USE_TEMPLATE should likely +      inform the PRIMARY parameter of
>>> dump_template_parms.), and
>>
>> Good question.  It seems that the existing
>> !DECL_FRIEND_PSEUDO_TEMPLATE_INSTANTIATION has mostly been excluding the
>> most general template; removing that line changes things like
>>
>> int bar(T) [with T = int]
>>
>> to
>>
>> int bar<T>(T) [with T = int]
>>
>>
>>
>>> +   4. either
>>> +      - flags requests to show no function arguments, in which case
>>> deduced +        types could be hidden, or
>>> +      - at least one function template argument was given explicitly, or
>>> +      - we're printing a DWARF name,
>>
>> ...but perhaps this can replace the above.
> 
> I'll rerun the tests with DECL_USE_TEMPLATE moved to the PRIMARY parameter of
> dump_template_parms.
> 
>> Though, why do we want
>> different behavior here when printing a DWARF name?
> 
> Sorry, I should have asked ... I also added the same issue as an open question
> on the diagnose_as patch. When I ran the whole testsuite I had failures in the
> DWARF tests. This change resolved them. I don't know enough about how those
> strings are used and whether they may change between GCC versions. Anyway, the
> DWARF strings in that test had only the function name and template argument
> list (i.e. no function arguments).

If they only have arguments, isn't TFF_NO_FUNCTION_ARGUMENTS set?

> If the template argument list was removed
> (as was the case without the condition here), the DWARF strings lost important
> information, giving several different functions the same name. That seemed
> like a regression. So either the DWARF strings need to include the function
> arguments, or we need this condition to keep showing the template arguments
> independent of whether they were explicitly given or not.
> 
>>> +   5. either
>>> +      - t is a member friend template of a template class (see
>>> DECL_TI_TEMPLATE +        documentation), or
>>> +      -
>>
>> Missing the last item.  :)
> 
> Oh, I got distracted while trying to figure this out. :) I'll try to
> understand why PRIMARY_TEMPLATE_P (DECL_TI_TEMPLATE (t)) is needed and
> document it.
> 


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

* Re: [RFC] c++: Print function template parms when relevant (was: [PATCH v4] c++: Add gnu::diagnose_as attribute)
  2021-11-17 18:25           ` Jason Merrill
@ 2021-11-17 22:51             ` Matthias Kretz
  2021-11-18 19:24               ` Jason Merrill
  0 siblings, 1 reply; 24+ messages in thread
From: Matthias Kretz @ 2021-11-17 22:51 UTC (permalink / raw)
  To: gcc-patches, Jason Merrill

On Wednesday, 17 November 2021 19:25:46 CET Jason Merrill wrote:
> On 11/17/21 04:04, Matthias Kretz wrote:
> > On Wednesday, 17 November 2021 07:09:18 CET Jason Merrill wrote:
> >>> -  if (CHECKING_P)
> >>> -    SET_NON_DEFAULT_TEMPLATE_ARGS_COUNT (a, TREE_VEC_LENGTH (a));
> >>> +  SET_NON_DEFAULT_TEMPLATE_ARGS_COUNT (a, nondefault);
> >> 
> >> should have been
> >> 
> >> if (CHECKING_P || nondefault != TREE_VEC_LENGTH (a))
> >> 
> >>     SET_NON_DEFAULT_TEMPLATE_ARGS_COUNT (a, nondefault);
> > 
> > TBH, I don't understand the purpose of CHECKING_P here, or rather it makes
> > me nervous because AFAIU I'm only testing with CHECKING_P enabled. Why
> > make behavior dependent on CHECKING_P? I expected CHECKING_P to basically
> > only add more assertions.
> 
> The idea when NON_DEFAULT_TEMPLATE_ARGS_COUNT was added years back was
> to leave the TREE_CHAIN null when !CHECKING_P and treat that as
> equivalent to TREE_VEC_LENGTH (args).  But perhaps you're right that
> it's not a savings worth the complexity.

Thanks, now I understand.

> >>> (copy_template_args): Jason?
> >> 
> >> Only copy the non-default template args count on TREE_VECs that should
> >> have it.
> > 
> > Why not simply set the count on all args? Is it a performance concern? The
> > INTEGER_CST the TREE_CHAIN has to point to exists anyway, so it's not
> > wasting any memory, right?
> 
> In this case the TREE_VEC we're excluding is the one wrapping multiple
> levels of template args; it doesn't contain args directly, so setting
> NON_DEFAULT_ARGS_COUNT on it doesn't make sense.

Right, I had already added a `gcc_assert (!TMPL_ARGS_HAVE_MULTIPLE_LEVELS 
(args))` to my new set_non_default_template_args_count function and found cp/
constraint.cc:2896 (get_mapped_args), which calls 
SET_NON_DEFAULT_TEMPLATE_ARGS_COUNT on the outer TREE_VEC. Was this supposed 
to apply to all inner TREE_VECs? Or is deleting the line the correct fix?

> >>> +  /* Pretty print only template instantiations. Don't pretty print
> >>> explicit
> >>> +     specializations like 'template <> void fun<int> (int)'.
> >> 
> >> This seems like a significant change of behavior unrelated to printing
> >> default template arguments.  What's the rationale for handling
> >> specializations differently from instantiations?
> > 
> > Right, this is about "The general idea of this change is to print template
> > parms wherever they would appear in the source code as well".
> > 
> > Initially, the change to print function template arguments/parameters only
> > if the args were explicitly specified lead to printing 'void fun (T)
> > [with T = ...]' or 'template <> void fun (int)'. Both are not telling the
> > full story, even if the former is how the function would be called.
> 
> and the latter is how I expect the specialization to be declared, not
> with the deducible template argument made explicit.

You're right. From my tests:

template <class a>
  [[deprecated]] void f4(a);

template <>
  [[deprecated]] void f4<int>(int);

template <>
  [[deprecated]] void f4(float);

  f4(1.);          // { dg-warning "'void f4\\(a\\) .with a = double.'" }
  f4(1);           // { dg-warning "'void f4<int>\\(int\\)'" }
  f4(1.f);         // { dg-warning "'void f4\\(float\\)'" }

So how it's printed depends on how the specialization is declared. It just 
falls out that way and it didn't seem awfully wrong... ;)

> > But if the reader
> > should quickly recognize what code is getting called, it is helpful to see
> > right away that a function template specialization is called. (It might
> > also reveal an implementation detail of a library, so it's not 100%
> > obvious how to choose here.) Also, saying 'T = int' is kind of wrong.
> > Yes, 'int' was deduced. But there's no T in fun<int>:
> > 
> > template <class T> void fun (T);
> > template <> void fun<int> (int);
> 
> There's a T in the template, and as you said above, that's how it's
> called (and mangled).
> 
> > __FUNCTION__ was 'fun<int>' all the time, but __PRETTY_FUNCTION__ was
> > 'void
> > fun(T) [with T = int]'.
> 
> Isn't that true for instantiations, as well?

No, instantiations don't have template args/parms in __FUNCTION__.

> > It's more consistent that __PRETTY_FUNCTION__ contains __FUNCTION__, IMHO
> 
> I suppose, but I don't see that as a strong enough motivation to mix
> this up.

What about

template <class T> void f();
template <> void f<int>();

With -fpretty-templates shouldn't it print as 'void f<T>() [with T = float]' 
and 'void f<int>()'? Yes, it's probably too subtle for most users to notice 
the difference. But I find it's more consistent this way.

> > so it would have to be at least 'void fun<int>(T) [with T
> > = int]'. But that's strange: How it uses T and int for the same type. So I
> > settled on 'void fun<int>(int)'.
> > 
> >> I also don't understand the purpose of TFF_AS_PRIMARY.
> > 
> > dump_function_decl generalizes the TEMPLATE_DECL (if flag_pretty_templates
> > is true) and, before this change, passes the generalized TEMPLATE_DECL to
> > dump_type (... DECL_CONTEXT (t) ...) and dump_function_name (... t ...).
> > That's why the whole template is printed as primary template (i.e. with
> > template parms instead of template args, as is needed for
> > flag_pretty_templates). But this drops the count of non-default template
> > args.
> Ah, you're trying to omit defaulted parms from the <list>?  I'm not sure
> that's necessary, leaving them out of the [with ...] list should be
> sufficient.

I was thinking about all the std::allocator defaults in the standard library. 
I don't want to see them. E.g. vector<int>::clear() on const object:

error: passing 'const std::vector<int>' as 'this' argument discards qualifiers
[...]/stl_vector.h:1498:7: note:   in call to 'void std::vector<_Tp, 
_Alloc>::clear() [with _Tp = int; _Alloc = std::allocator<int>]'

With my patch the last line becomes
[...]/stl_vector.h:1498:7: note:   in call to 'void std::vector<_Tp>::clear() 
[with _Tp = int]'


Another case I didn't consider before:

template <class T, class U = int> struct A {
  [[deprecated]] void f(U);
};

A<float> a; a.f(1);

With my patch it prints 'void A<T>::f(U) [with T = float]', with your 
suggestion 'void A<T, U>::f(U) [with T = float]'. Both are missing important 
information in the substitution list, IMHO. Would 'void A<T, U = int>::f(U) 
[with T = float]' be an improvement? Or should find_typenames (in cp/error.c) 
find defaulted template parms and add them to its list? IIUC find_typenames 
would find all template parms and couldn't know whether they're defaulted.

> >>> +   4. either
> >>> +      - flags requests to show no function arguments, in which case
> >>> deduced +        types could be hidden, or
> >>> +      - at least one function template argument was given explicitly,
> >>> or
> >>> +      - we're printing a DWARF name,
> >> 
> >> Though, why do we want
> >> different behavior here when printing a DWARF name?
> > 
> > Sorry, I should have asked ... I also added the same issue as an open
> > question on the diagnose_as patch. When I ran the whole testsuite I had
> > failures in the DWARF tests. This change resolved them. I don't know
> > enough about how those strings are used and whether they may change
> > between GCC versions. Anyway, the DWARF strings in that test had only the
> > function name and template argument list (i.e. no function arguments).
> 
> If they only have arguments, isn't TFF_NO_FUNCTION_ARGUMENTS set?

No, it isn't set. I could try to set it for DWARF names. It sounds like the 
right solution here.

-- 
──────────────────────────────────────────────────────────────────────────
 Dr. Matthias Kretz                           https://mattkretz.github.io
 GSI Helmholtz Centre for Heavy Ion Research               https://gsi.de
 stdₓ::simd
──────────────────────────────────────────────────────────────────────────




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

* Re: [RFC] c++: Print function template parms when relevant (was: [PATCH v4] c++: Add gnu::diagnose_as attribute)
  2021-11-17 22:51             ` Matthias Kretz
@ 2021-11-18 19:24               ` Jason Merrill
  2021-11-19  9:53                 ` Matthias Kretz
  0 siblings, 1 reply; 24+ messages in thread
From: Jason Merrill @ 2021-11-18 19:24 UTC (permalink / raw)
  To: Matthias Kretz, gcc-patches

On 11/17/21 17:51, Matthias Kretz wrote:
> On Wednesday, 17 November 2021 19:25:46 CET Jason Merrill wrote:
>> On 11/17/21 04:04, Matthias Kretz wrote:
>>> On Wednesday, 17 November 2021 07:09:18 CET Jason Merrill wrote:
>>>>> -  if (CHECKING_P)
>>>>> -    SET_NON_DEFAULT_TEMPLATE_ARGS_COUNT (a, TREE_VEC_LENGTH (a));
>>>>> +  SET_NON_DEFAULT_TEMPLATE_ARGS_COUNT (a, nondefault);
>>>>
>>>> should have been
>>>>
>>>> if (CHECKING_P || nondefault != TREE_VEC_LENGTH (a))
>>>>
>>>>      SET_NON_DEFAULT_TEMPLATE_ARGS_COUNT (a, nondefault);
>>>
>>> TBH, I don't understand the purpose of CHECKING_P here, or rather it makes
>>> me nervous because AFAIU I'm only testing with CHECKING_P enabled. Why
>>> make behavior dependent on CHECKING_P? I expected CHECKING_P to basically
>>> only add more assertions.
>>
>> The idea when NON_DEFAULT_TEMPLATE_ARGS_COUNT was added years back was
>> to leave the TREE_CHAIN null when !CHECKING_P and treat that as
>> equivalent to TREE_VEC_LENGTH (args).  But perhaps you're right that
>> it's not a savings worth the complexity.
> 
> Thanks, now I understand.
> 
>>>>> (copy_template_args): Jason?
>>>>
>>>> Only copy the non-default template args count on TREE_VECs that should
>>>> have it.
>>>
>>> Why not simply set the count on all args? Is it a performance concern? The
>>> INTEGER_CST the TREE_CHAIN has to point to exists anyway, so it's not
>>> wasting any memory, right?
>>
>> In this case the TREE_VEC we're excluding is the one wrapping multiple
>> levels of template args; it doesn't contain args directly, so setting
>> NON_DEFAULT_ARGS_COUNT on it doesn't make sense.
> 
> Right, I had already added a `gcc_assert (!TMPL_ARGS_HAVE_MULTIPLE_LEVELS
> (args))` to my new set_non_default_template_args_count function and found cp/
> constraint.cc:2896 (get_mapped_args), which calls
> SET_NON_DEFAULT_TEMPLATE_ARGS_COUNT on the outer TREE_VEC. Was this supposed
> to apply to all inner TREE_VECs? Or is deleting the line the correct fix?

That should apply to the inner TREE_VECs (and probably use list.length)

>>>>> +  /* Pretty print only template instantiations. Don't pretty print
>>>>> explicit
>>>>> +     specializations like 'template <> void fun<int> (int)'.
>>>>
>>>> This seems like a significant change of behavior unrelated to printing
>>>> default template arguments.  What's the rationale for handling
>>>> specializations differently from instantiations?
>>>
>>> Right, this is about "The general idea of this change is to print template
>>> parms wherever they would appear in the source code as well".
>>>
>>> Initially, the change to print function template arguments/parameters only
>>> if the args were explicitly specified lead to printing 'void fun (T)
>>> [with T = ...]' or 'template <> void fun (int)'. Both are not telling the
>>> full story, even if the former is how the function would be called.
>>
>> and the latter is how I expect the specialization to be declared, not
>> with the deducible template argument made explicit.
> 
> You're right. From my tests:
> 
> template <class a>
>    [[deprecated]] void f4(a);
> 
> template <>
>    [[deprecated]] void f4<int>(int);
> 
> template <>
>    [[deprecated]] void f4(float);
> 
>    f4(1.);          // { dg-warning "'void f4\\(a\\) .with a = double.'" }
>    f4(1);           // { dg-warning "'void f4<int>\\(int\\)'" }
>    f4(1.f);         // { dg-warning "'void f4\\(float\\)'" }
> 
> So how it's printed depends on how the specialization is declared. It just
> falls out that way and it didn't seem awfully wrong... ;)
> 
>>> But if the reader
>>> should quickly recognize what code is getting called, it is helpful to see
>>> right away that a function template specialization is called. (It might
>>> also reveal an implementation detail of a library, so it's not 100%
>>> obvious how to choose here.) Also, saying 'T = int' is kind of wrong.
>>> Yes, 'int' was deduced. But there's no T in fun<int>:
>>>
>>> template <class T> void fun (T);
>>> template <> void fun<int> (int);
>>
>> There's a T in the template, and as you said above, that's how it's
>> called (and mangled).
>>
>>> __FUNCTION__ was 'fun<int>' all the time, but __PRETTY_FUNCTION__ was
>>> 'void
>>> fun(T) [with T = int]'.
>>
>> Isn't that true for instantiations, as well?
> 
> No, instantiations don't have template args/parms in __FUNCTION__.

Hmm, that inconsistency seems like a bug, though I'm not sure whether it 
should have the template args or not; I lean toward not.  The standard 
says that the value of __func__ is implementation-defined, the GCC 
manual says it's "the unadorned name of the function".

>>> It's more consistent that __PRETTY_FUNCTION__ contains __FUNCTION__, IMHO
>>
>> I suppose, but I don't see that as a strong enough motivation to mix
>> this up.
> 
> What about
> 
> template <class T> void f();
> template <> void f<int>();
> 
> With -fpretty-templates shouldn't it print as 'void f<T>() [with T = float]'
> and 'void f<int>()'? Yes, it's probably too subtle for most users to notice
> the difference. But I find it's more consistent this way.

I disagree; the function signature is the same whether a particular 
function is an explicit specialization or an instantiation.

>>> so it would have to be at least 'void fun<int>(T) [with T
>>> = int]'. But that's strange: How it uses T and int for the same type. So I
>>> settled on 'void fun<int>(int)'.
>>>
>>>> I also don't understand the purpose of TFF_AS_PRIMARY.
>>>
>>> dump_function_decl generalizes the TEMPLATE_DECL (if flag_pretty_templates
>>> is true) and, before this change, passes the generalized TEMPLATE_DECL to
>>> dump_type (... DECL_CONTEXT (t) ...) and dump_function_name (... t ...).
>>> That's why the whole template is printed as primary template (i.e. with
>>> template parms instead of template args, as is needed for
>>> flag_pretty_templates). But this drops the count of non-default template
>>> args.
>> Ah, you're trying to omit defaulted parms from the <list>?  I'm not sure
>> that's necessary, leaving them out of the [with ...] list should be
>> sufficient.
> 
> I was thinking about all the std::allocator defaults in the standard library.
> I don't want to see them. E.g. vector<int>::clear() on const object:
> 
> error: passing 'const std::vector<int>' as 'this' argument discards qualifiers
> [...]/stl_vector.h:1498:7: note:   in call to 'void std::vector<_Tp,
> _Alloc>::clear() [with _Tp = int; _Alloc = std::allocator<int>]'
> 
> With my patch the last line becomes
> [...]/stl_vector.h:1498:7: note:   in call to 'void std::vector<_Tp>::clear()
> [with _Tp = int]'
> 
> 
> Another case I didn't consider before:
> 
> template <class T, class U = int> struct A {
>    [[deprecated]] void f(U);
> };
> 
> A<float> a; a.f(1);
> 
> With my patch it prints 'void A<T>::f(U) [with T = float]', with your
> suggestion 'void A<T, U>::f(U) [with T = float]'. Both are missing important
> information in the substitution list, IMHO. Would 'void A<T, U = int>::f(U)
> [with T = float]' be an improvement? Or should find_typenames (in cp/error.c)
> find defaulted template parms and add them to its list? IIUC find_typenames
> would find all template parms and couldn't know whether they're defaulted.

That sounds good: omit defaulted parms only if they don't appear in the 
signature (other than as another default template argument).

>>>>> +   4. either
>>>>> +      - flags requests to show no function arguments, in which case
>>>>> deduced +        types could be hidden, or
>>>>> +      - at least one function template argument was given explicitly,
>>>>> or
>>>>> +      - we're printing a DWARF name,
>>>>
>>>> Though, why do we want
>>>> different behavior here when printing a DWARF name?
>>>
>>> Sorry, I should have asked ... I also added the same issue as an open
>>> question on the diagnose_as patch. When I ran the whole testsuite I had
>>> failures in the DWARF tests. This change resolved them. I don't know
>>> enough about how those strings are used and whether they may change
>>> between GCC versions. Anyway, the DWARF strings in that test had only the
>>> function name and template argument list (i.e. no function arguments).
>>
>> If they only have arguments, isn't TFF_NO_FUNCTION_ARGUMENTS set?
> 
> No, it isn't set. I could try to set it for DWARF names. It sounds like the
> right solution here.

I agree.

Jason


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

* Re: [RFC] c++: Print function template parms when relevant (was: [PATCH v4] c++: Add gnu::diagnose_as attribute)
  2021-11-18 19:24               ` Jason Merrill
@ 2021-11-19  9:53                 ` Matthias Kretz
  2021-11-19 12:02                   ` Matthias Kretz
  2021-11-19 22:26                   ` Jason Merrill
  0 siblings, 2 replies; 24+ messages in thread
From: Matthias Kretz @ 2021-11-19  9:53 UTC (permalink / raw)
  To: gcc-patches, Jason Merrill

On Thursday, 18 November 2021 20:24:36 CET Jason Merrill wrote:
> On 11/17/21 17:51, Matthias Kretz wrote:
> > Right, I had already added a `gcc_assert (!TMPL_ARGS_HAVE_MULTIPLE_LEVELS
> > (args))` to my new set_non_default_template_args_count function and found
> > cp/ constraint.cc:2896 (get_mapped_args), which calls
> > SET_NON_DEFAULT_TEMPLATE_ARGS_COUNT on the outer TREE_VEC. Was this
> > supposed to apply to all inner TREE_VECs? Or is deleting the line the
> > correct fix?
>
> That should apply to the inner TREE_VECs (and probably use list.length)

Like this?

@@ -2890,10 +2890,11 @@ get_mapped_args (tree map)
       tree level = make_tree_vec (list.length ());
       for (unsigned j = 0; j < list.length(); ++j)
         TREE_VEC_ELT (level, j) = list[j];
+      /* None of the args at any level are defaulted.  */
+      SET_NON_DEFAULT_TEMPLATE_ARGS_COUNT (level, list.length());
       SET_TMPL_ARGS_LEVEL (args, i + 1, level);
       list.release ();
     }
-  SET_NON_DEFAULT_TEMPLATE_ARGS_COUNT (args, 0);

   return args;
 }

> >>> __FUNCTION__ was 'fun<int>' all the time, but __PRETTY_FUNCTION__ was
> >>> 'void fun(T) [with T = int]'.
> >> 
> >> Isn't that true for instantiations, as well?
> > 
> > No, instantiations don't have template args/parms in __FUNCTION__.
> 
> Hmm, that inconsistency seems like a bug, though I'm not sure whether it
> should have the template args or not; I lean toward not.  The standard
> says that the value of __func__ is implementation-defined, the GCC
> manual says it's "the unadorned name of the function".

So you think f1<int> in testsuite/g++.old-deja/g++.ext/pretty3.C needs to test 
for

  if (strcmp (function, "f1"))
    bad = true;
  if (strcmp (pretty, "void f1(T) [with T = int]"))
    bad = true;

Or should the latter be "void f1(int)"?

> >>> It's more consistent that __PRETTY_FUNCTION__ contains __FUNCTION__,
> >>> IMHO
> >> 
> >> I suppose, but I don't see that as a strong enough motivation to mix
> >> this up.
> > 
> > What about
> > 
> > template <class T> void f();
> > template <> void f<int>();
> > 
> > With -fpretty-templates shouldn't it print as 'void f<T>() [with T =
> > float]' and 'void f<int>()'? Yes, it's probably too subtle for most users
> > to notice the difference. But I find it's more consistent this way.
> 
> I disagree; the function signature is the same whether a particular
> function is an explicit specialization or an instantiation.

Yes, the call signature is the same. But what it calls is different. There's 
no obvious answer for my stated goal "print template parms wherever they
would appear in the source code as well", since it depends on whether the user 
is interested in recognizing the exact function body that was called.

My motivation for printing a function template specialization differently is:

1. It's a different function definition that's being called. The user (caller) 
might be surprised to realize this is the case as he forgot about the 
specialization and was expecting his change to the general template to make a 
difference.

2. There's no T in

template <> void f<int>() {
  // no T here, but of course I can define one:
  using T = int;
}

so presenting the function that was called as 'void f<T>() [with T = int]' is 
not exactly correct. In this case it wasn't even the case that T was deduced 
to be 'int', which we could argue to be useful information that might get 
lost.

For

template <class T> void f(T);
template <> void f(int);

the whole story is "'void f(int)' was called for 'template <class T> void f(T) 
[with T = int]'". What the user wants to see depends on what is more important 
to fix the bug: that T was deduced to be int, or that the specialization of f 
was called instead of the general template. I'd still go with 'void f(int)', 
though I'd be happier if I had some indication that a template was involved.

> >> Ah, you're trying to omit defaulted parms from the <list>?  I'm not sure
> >> that's necessary, leaving them out of the [with ...] list should be
> >> sufficient.
> > 
> > I was thinking about all the std::allocator defaults in the standard
> > library. I don't want to see them. E.g. vector<int>::clear() on const
> > object:
> > 
> > error: passing 'const std::vector<int>' as 'this' argument discards
> > qualifiers [...]/stl_vector.h:1498:7: note:   in call to 'void
> > std::vector<_Tp, _Alloc>::clear() [with _Tp = int; _Alloc =
> > std::allocator<int>]'
> > 
> > With my patch the last line becomes
> > [...]/stl_vector.h:1498:7: note:   in call to 'void
> > std::vector<_Tp>::clear() [with _Tp = int]'
> > 
> > 
> > Another case I didn't consider before:
> > 
> > template <class T, class U = int> struct A {
> > 
> >    [[deprecated]] void f(U);
> > 
> > };
> > 
> > A<float> a; a.f(1);
> > 
> > With my patch it prints 'void A<T>::f(U) [with T = float]', with your
> > suggestion 'void A<T, U>::f(U) [with T = float]'. Both are missing
> > important information in the substitution list, IMHO. Would 'void A<T, U
> > = int>::f(U) [with T = float]' be an improvement? Or should
> > find_typenames (in cp/error.c) find defaulted template parms and add them
> > to its list? IIUC find_typenames would find all template parms and
> > couldn't know whether they're defaulted.
>
> That sounds good: omit defaulted parms only if they don't appear in the
> signature (other than as another default template argument).

Let me check whether I have the right idea:

I could extend find_typenames (which walks the complete) tree to look for 
TEMPLATE_TYPE_PARM (and the 3 others I don't recall right now). But since that 
walks the *complete* tree, it'll simply find all parms with no indication 
whether they appear in the signature. Ideas:

1. Count occurrences: with 2 occurrences, one of them must be a use in the 
signature.

2. Walk only over TYPE_ARG_TYPES (TREE_TYPE (DECL_TEMPLATE_RESULT (fn))) to 
collect TEMPLATE_TYPE_PARMs.


-- 
──────────────────────────────────────────────────────────────────────────
 Dr. Matthias Kretz                           https://mattkretz.github.io
 GSI Helmholtz Centre for Heavy Ion Research               https://gsi.de
 stdₓ::simd
──────────────────────────────────────────────────────────────────────────




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

* Re: [RFC] c++: Print function template parms when relevant (was: [PATCH v4] c++: Add gnu::diagnose_as attribute)
  2021-11-19  9:53                 ` Matthias Kretz
@ 2021-11-19 12:02                   ` Matthias Kretz
  2021-11-19 22:26                   ` Jason Merrill
  1 sibling, 0 replies; 24+ messages in thread
From: Matthias Kretz @ 2021-11-19 12:02 UTC (permalink / raw)
  To: gcc-patches, Jason Merrill

On Friday, 19 November 2021 10:53:27 CET Matthias Kretz wrote:
> > >> Ah, you're trying to omit defaulted parms from the <list>?  I'm not
> > >> sure
> > >> that's necessary, leaving them out of the [with ...] list should be
> > >> sufficient.
> > >
> > > I was thinking about all the std::allocator defaults in the standard
> > > library. I don't want to see them. E.g. vector<int>::clear() on const
> > > object:
> > > 
> > > error: passing 'const std::vector<int>' as 'this' argument discards
> > > qualifiers [...]/stl_vector.h:1498:7: note:   in call to 'void
> > > std::vector<_Tp, _Alloc>::clear() [with _Tp = int; _Alloc =
> > > std::allocator<int>]'
> > > 
> > > With my patch the last line becomes
> > > [...]/stl_vector.h:1498:7: note:   in call to 'void
> > > std::vector<_Tp>::clear() [with _Tp = int]'
> > > 
> > > 
> > > Another case I didn't consider before:
> > > 
> > > template <class T, class U = int> struct A {
> > > 
> > > [[deprecated]] void f(U);
> > > 
> > > };
> > > 
> > > A<float> a; a.f(1);
> > > 
> > > With my patch it prints 'void A<T>::f(U) [with T = float]', with your
> > > suggestion 'void A<T, U>::f(U) [with T = float]'. Both are missing
> > > important information in the substitution list, IMHO. Would 'void A<T, U
> > > = int>::f(U) [with T = float]' be an improvement? Or should
> > > find_typenames (in cp/error.c) find defaulted template parms and add
> > > them
> > > to its list? IIUC find_typenames would find all template parms and
> > > couldn't know whether they're defaulted.
> > 
> > That sounds good: omit defaulted parms only if they don't appear in the
> > signature (other than as another default template argument).
> 
> Let me check whether I have the right idea:
> 
> I could extend find_typenames (which walks the complete) tree to look for
> TEMPLATE_TYPE_PARM (and the 3 others I don't recall right now). But since
> that walks the *complete* tree, it'll simply find all parms with no
> indication whether they appear in the signature. Ideas:
> 
> 1. Count occurrences: with 2 occurrences, one of them must be a use in the
> signature.
> 
> 2. Walk only over TYPE_ARG_TYPES (TREE_TYPE (DECL_TEMPLATE_RESULT (fn))) to
> collect TEMPLATE_TYPE_PARMs.

I tried the latter:

@@ -1641,8 +1652,11 @@ dump_substitution (cxx_pretty_printer *pp,                              
       && !(flags & TFF_NO_TEMPLATE_BINDINGS))                                                 
     {
       vec<tree, va_gc> *typenames = t ? find_typenames (t) : NULL;                            
-      dump_template_bindings (pp, template_parms, template_args, typenames,                   
-			      flags);                                                          
+      tree fn_arguments = TYPE_ARG_TYPES (TREE_TYPE (DECL_TEMPLATE_RESULT 
(t)));              
+      tree used_template_parms = find_template_parameters (fn_arguments,                      
+							   template_parms);                    
+      dump_template_bindings (pp, template_parms, template_args,                              
+			      used_template_parms, typenames, flags);                          
     }
 }

Now in dump_template_bindings it skips all defaulted template_parms that are 
not in used_template_parms. Makes this test pass:

template <class T>                                                              
  struct id                                                                     
  { using type = T; };                                                          
                                                                                
template <class T0, class T1 = int>                                             
  struct A                                                                      
  {                                                                             
    template <class U0 = const T1&>                                             
      [[deprecated]] static void                                                
      f(typename id<U0>::type);                                                 
  };                                                                            
                                                                                
int main()                                                                      
{                                                                               
  A<int>::f(0); // { dg-warning "'static void A<T0>::f\\(typename 
id<U0>::type\\) .with U0 = const int&; T0 = int; typename id<U0>::type = const 
int&.'" }
}

-- 
──────────────────────────────────────────────────────────────────────────
 Dr. Matthias Kretz                           https://mattkretz.github.io
 GSI Helmholtz Centre for Heavy Ion Research               https://gsi.de
 stdₓ::simd
──────────────────────────────────────────────────────────────────────────




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

* Re: [RFC] c++: Print function template parms when relevant (was: [PATCH v4] c++: Add gnu::diagnose_as attribute)
  2021-11-19  9:53                 ` Matthias Kretz
  2021-11-19 12:02                   ` Matthias Kretz
@ 2021-11-19 22:26                   ` Jason Merrill
  2021-11-19 23:11                     ` Matthias Kretz
  2021-11-26 15:23                     ` [PATCH 0/2] " Matthias Kretz
  1 sibling, 2 replies; 24+ messages in thread
From: Jason Merrill @ 2021-11-19 22:26 UTC (permalink / raw)
  To: Matthias Kretz, gcc-patches

On 11/19/21 04:53, Matthias Kretz wrote:
> On Thursday, 18 November 2021 20:24:36 CET Jason Merrill wrote:
>> On 11/17/21 17:51, Matthias Kretz wrote:
>>> Right, I had already added a `gcc_assert (!TMPL_ARGS_HAVE_MULTIPLE_LEVELS
>>> (args))` to my new set_non_default_template_args_count function and found
>>> cp/ constraint.cc:2896 (get_mapped_args), which calls
>>> SET_NON_DEFAULT_TEMPLATE_ARGS_COUNT on the outer TREE_VEC. Was this
>>> supposed to apply to all inner TREE_VECs? Or is deleting the line the
>>> correct fix?
>>
>> That should apply to the inner TREE_VECs (and probably use list.length)
> 
> Like this?

Yes.

> @@ -2890,10 +2890,11 @@ get_mapped_args (tree map)
>         tree level = make_tree_vec (list.length ());
>         for (unsigned j = 0; j < list.length(); ++j)
>           TREE_VEC_ELT (level, j) = list[j];
> +      /* None of the args at any level are defaulted.  */
> +      SET_NON_DEFAULT_TEMPLATE_ARGS_COUNT (level, list.length());
>         SET_TMPL_ARGS_LEVEL (args, i + 1, level);
>         list.release ();
>       }
> -  SET_NON_DEFAULT_TEMPLATE_ARGS_COUNT (args, 0);
> 
>     return args;
>   }
> 
>>>>> __FUNCTION__ was 'fun<int>' all the time, but __PRETTY_FUNCTION__ was
>>>>> 'void fun(T) [with T = int]'.
>>>>
>>>> Isn't that true for instantiations, as well?
>>>
>>> No, instantiations don't have template args/parms in __FUNCTION__.
>>
>> Hmm, that inconsistency seems like a bug, though I'm not sure whether it
>> should have the template args or not; I lean toward not.  The standard
>> says that the value of __func__ is implementation-defined, the GCC
>> manual says it's "the unadorned name of the function".
> 
> So you think f1<int> in testsuite/g++.old-deja/g++.ext/pretty3.C needs to test
> for
> 
>    if (strcmp (function, "f1"))
>      bad = true;
>    if (strcmp (pretty, "void f1(T) [with T = int]"))
>      bad = true;

I think so.

>>>>> It's more consistent that __PRETTY_FUNCTION__ contains __FUNCTION__,
>>>>> IMHO
>>>>
>>>> I suppose, but I don't see that as a strong enough motivation to mix
>>>> this up.
>>>
>>> What about
>>>
>>> template <class T> void f();
>>> template <> void f<int>();
>>>
>>> With -fpretty-templates shouldn't it print as 'void f<T>() [with T =
>>> float]' and 'void f<int>()'? Yes, it's probably too subtle for most users
>>> to notice the difference. But I find it's more consistent this way.
>>
>> I disagree; the function signature is the same whether a particular
>> function is an explicit specialization or an instantiation.
> 
> Yes, the call signature is the same. But what it calls is different. There's
> no obvious answer for my stated goal "print template parms wherever they
> would appear in the source code as well", since it depends on whether the user
> is interested in recognizing the exact function body that was called.
> 
> My motivation for printing a function template specialization differently is:
> 
> 1. It's a different function definition that's being called. The user (caller)
> might be surprised to realize this is the case as he forgot about the
> specialization and was expecting his change to the general template to make a
> difference.
>
> 2. There's no T in
> 
> template <> void f<int>() {
>    // no T here, but of course I can define one:
>    using T = int;
> }
> 
> so presenting the function that was called as 'void f<T>() [with T = int]' is
> not exactly correct. In this case it wasn't even the case that T was deduced
> to be 'int', which we could argue to be useful information that might get
> lost.

On the other hand, this tells us what template this specialization is 
specializing, which could be unclear if there are multiple overloaded 
function templates.

There's always -fno-pretty-templates if you want the form without 
template args.

Incidentally, the contents of __PRETTY_FUNCTION__ probably shouldn't 
vary with that flag...

> For
> 
> template <class T> void f(T);
> template <> void f(int);
> 
> the whole story is "'void f(int)' was called for 'template <class T> void f(T)
> [with T = int]'". What the user wants to see depends on what is more important
> to fix the bug: that T was deduced to be int, or that the specialization of f
> was called instead of the general template. I'd still go with 'void f(int)',
> though I'd be happier if I had some indication that a template was involved.

The current form tells you about the template, and the line number 
points you at the declaration.

>>>> Ah, you're trying to omit defaulted parms from the <list>?  I'm not sure
>>>> that's necessary, leaving them out of the [with ...] list should be
>>>> sufficient.
>>>
>>> I was thinking about all the std::allocator defaults in the standard
>>> library. I don't want to see them. E.g. vector<int>::clear() on const
>>> object:
>>>
>>> error: passing 'const std::vector<int>' as 'this' argument discards
>>> qualifiers [...]/stl_vector.h:1498:7: note:   in call to 'void
>>> std::vector<_Tp, _Alloc>::clear() [with _Tp = int; _Alloc =
>>> std::allocator<int>]'
>>>
>>> With my patch the last line becomes
>>> [...]/stl_vector.h:1498:7: note:   in call to 'void
>>> std::vector<_Tp>::clear() [with _Tp = int]'
>>>
>>>
>>> Another case I didn't consider before:
>>>
>>> template <class T, class U = int> struct A {
>>>
>>>     [[deprecated]] void f(U);
>>>
>>> };
>>>
>>> A<float> a; a.f(1);
>>>
>>> With my patch it prints 'void A<T>::f(U) [with T = float]', with your
>>> suggestion 'void A<T, U>::f(U) [with T = float]'. Both are missing
>>> important information in the substitution list, IMHO. Would 'void A<T, U
>>> = int>::f(U) [with T = float]' be an improvement? Or should
>>> find_typenames (in cp/error.c) find defaulted template parms and add them
>>> to its list? IIUC find_typenames would find all template parms and
>>> couldn't know whether they're defaulted.
>>
>> That sounds good: omit defaulted parms only if they don't appear in the
>> signature (other than as another default template argument).
> 
> Let me check whether I have the right idea:
> 
> I could extend find_typenames (which walks the complete) tree to look for
> TEMPLATE_TYPE_PARM (and the 3 others I don't recall right now). But since that
> walks the *complete* tree, it'll simply find all parms with no indication
> whether they appear in the signature. Ideas:

Hmm, since it walks DECL_TEMPLATE_RESULT, I wouldn't expect it to find 
template parms that aren't in the function signature.

> 1. Count occurrences: with 2 occurrences, one of them must be a use in the
> signature.
> 
> 2. Walk only over TYPE_ARG_TYPES (TREE_TYPE (DECL_TEMPLATE_RESULT (fn))) to
> collect TEMPLATE_TYPE_PARMs.

Jason


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

* Re: [RFC] c++: Print function template parms when relevant (was: [PATCH v4] c++: Add gnu::diagnose_as attribute)
  2021-11-19 22:26                   ` Jason Merrill
@ 2021-11-19 23:11                     ` Matthias Kretz
  2021-11-26 15:23                     ` [PATCH 0/2] " Matthias Kretz
  1 sibling, 0 replies; 24+ messages in thread
From: Matthias Kretz @ 2021-11-19 23:11 UTC (permalink / raw)
  To: gcc-patches, Jason Merrill; +Cc: Ville Voutilainen

On Friday, 19 November 2021 23:26:57 CET Jason Merrill wrote:
> On 11/19/21 04:53, Matthias Kretz wrote:
> > My motivation for printing a function template specialization differently
> > is:
> > 
> > 1. It's a different function definition that's being called. The user
> > (caller) might be surprised to realize this is the case as he forgot
> > about the specialization and was expecting his change to the general
> > template to make a difference.
> > 
> > 2. There's no T in
> > 
> > template <> void f<int>() {
> > // no T here, but of course I can define one:
> > using T = int;
> > }
> > 
> > so presenting the function that was called as 'void f<T>() [with T = int]'
> > is not exactly correct. In this case it wasn't even the case that T was
> > deduced to be 'int', which we could argue to be useful information that
> > might get lost.
> 
> On the other hand, this tells us what template this specialization is
> specializing, which could be unclear if there are multiple overloaded
> function templates.

I don't disagree that printing the primary template + substitution has value. 
I just believe that being more explicit about a function specialization 
getting called has more value. But I guess we'll just have to live with our 
disagreement.

I'll re-add the condition for "pretty-printing" specialized functions for the 
next patch revision, unless someone else wants to & can convince you. :)

> There's always -fno-pretty-templates if you want the form without
> template args.
> 
> Incidentally, the contents of __PRETTY_FUNCTION__ probably shouldn't
> vary with that flag...

That ship is sailing since -fpretty-templates was implemented AFAICS. Do you 
want it to come back? ;)

> >> That sounds good: omit defaulted parms only if they don't appear in the
> >> signature (other than as another default template argument).
> >
> > Let me check whether I have the right idea:
> > 
> > I could extend find_typenames (which walks the complete) tree to look for
> > TEMPLATE_TYPE_PARM (and the 3 others I don't recall right now). But since
> > that walks the *complete* tree, it'll simply find all parms with no
> > indication
> > whether they appear in the signature. Ideas:
>
> Hmm, since it walks DECL_TEMPLATE_RESULT, I wouldn't expect it to find
> template parms that aren't in the function signature.

Maybe I made an error elsewhere, but that's what I saw when I tried it. I'll 
try again (next week, I think).

-- 
──────────────────────────────────────────────────────────────────────────
 Dr. Matthias Kretz                           https://mattkretz.github.io
 GSI Helmholtz Centre for Heavy Ion Research               https://gsi.de
 stdₓ::simd
──────────────────────────────────────────────────────────────────────────

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

* [PATCH 0/2] c++: Print function template parms when relevant (was: [PATCH v4] c++: Add gnu::diagnose_as attribute)
  2021-11-19 22:26                   ` Jason Merrill
  2021-11-19 23:11                     ` Matthias Kretz
@ 2021-11-26 15:23                     ` Matthias Kretz
  2021-11-26 15:24                       ` [PATCH 1/2] c++: Print function template parms when relevant Matthias Kretz
  2021-11-26 15:24                       ` [PATCH 2/2] c++: Print function template parms when relevant [part 2] Matthias Kretz
  1 sibling, 2 replies; 24+ messages in thread
From: Matthias Kretz @ 2021-11-26 15:23 UTC (permalink / raw)
  To: gcc-patches, Jason Merrill

On Friday, 19 November 2021 23:26:57 CET Jason Merrill wrote:
> On 11/19/21 04:53, Matthias Kretz wrote:
> > On Thursday, 18 November 2021 20:24:36 CET Jason Merrill wrote:
> >> On 11/17/21 17:51, Matthias Kretz wrote:
> >>>>> __FUNCTION__ was 'fun<int>' all the time, but __PRETTY_FUNCTION__ was
> >>>>> 'void fun(T) [with T = int]'.
> >>>> 
> >>>> Isn't that true for instantiations, as well?
> >>> 
> >>> No, instantiations don't have template args/parms in __FUNCTION__.
> >> 
> >> Hmm, that inconsistency seems like a bug, though I'm not sure whether it
> >> should have the template args or not; I lean toward not.  The standard
> >> says that the value of __func__ is implementation-defined, the GCC
> >> manual says it's "the unadorned name of the function".
> > 
> > So you think f1<int> in testsuite/g++.old-deja/g++.ext/pretty3.C needs to
> > test for
> > 
> >    if (strcmp (function, "f1"))
> >    
> >      bad = true;
> >    
> >    if (strcmp (pretty, "void f1(T) [with T = int]"))
> >    
> >      bad = true;
> 
> I think so.

I just found out that the behavior of __FUNCTION__ and DWARF names is related 
because both go through lang_decl_name in cp/error.c. I.e by removing the test 
for pp_c_flag_gnu_v3 in dump_function_name and requesting 
TFF_NO_FUNCTION_ARGUMENTS from lang_decl_name I turned the __FUNCTION__ string 
into "f1<T>" / "f1<int>". I can filter the former by rejecting the most 
general template (the DECL_USE_TEMPLATE in dump_function_name we were 
wondering about). But I can't turn "f1<int>" into "f1" without adding the test 
for pp_c_flag_gnu_v3 back in dump_function_name.

So far __FUNCTION__ and DWARF names want to be the same string. If you want to 
keep it like this, let me know how the patch should go: "f1" or "f1<int>" (the 
latter is the status quo and disambiguates different DWARF strings)

The __PRETTY_FUNCTION__ string wants to be "void f1<T>(T) [with T = int]", 
i.e. with the function tparms, because the template args are marked as 
explicitly specified. This depends on how the function specialization is 
declared, i.e. 'template<> void f1(int)' vs 'template<> void f1<int>(int)'. I 
don't know if I can/want to do anything about that. Is that an acceptable 
result?

I'll send two patches: The first patch is what I'd push. The second restores 
the diagnostics of specialized function templates.

> >> That sounds good: omit defaulted parms only if they don't appear in the
> >> signature (other than as another default template argument).
> > 
> > Let me check whether I have the right idea:
> > 
> > I could extend find_typenames (which walks the complete) tree to look for
> > TEMPLATE_TYPE_PARM (and the 3 others I don't recall right now). But since
> > that walks the *complete* tree, it'll simply find all parms with no
> > indication
> > whether they appear in the signature. Ideas:
> Hmm, since it walks DECL_TEMPLATE_RESULT, I wouldn't expect it to find
> template parms that aren't in the function signature.

You were right, walking TREE_TYPE (DECL_TEMPLATE_RESULT (t)) does what I need.

-- 
──────────────────────────────────────────────────────────────────────────
 Dr. Matthias Kretz                           https://mattkretz.github.io
 GSI Helmholtz Centre for Heavy Ion Research               https://gsi.de
 stdₓ::simd
──────────────────────────────────────────────────────────────────────────




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

* [PATCH 1/2] c++: Print function template parms when relevant
  2021-11-26 15:23                     ` [PATCH 0/2] " Matthias Kretz
@ 2021-11-26 15:24                       ` Matthias Kretz
  2021-12-02  8:35                         ` [PATCH v2 " Matthias Kretz
  2021-11-26 15:24                       ` [PATCH 2/2] c++: Print function template parms when relevant [part 2] Matthias Kretz
  1 sibling, 1 reply; 24+ messages in thread
From: Matthias Kretz @ 2021-11-26 15:24 UTC (permalink / raw)
  To: gcc-patches, jason

[-- Attachment #1: Type: text/plain, Size: 6917 bytes --]


The choice when to print a function template parameter was still
suboptimal. That's because sometimes the function template parameter
list only adds noise, while in other situations the lack of a function
template parameter list makes diagnostic messages hard to understand.

The general idea of this change is to print template parms wherever they
would appear in the source code as well. Thus, the diagnostics code
needs to know whether any template parameter was given explicitly.

DWARF names of functions use dump_function_name to produce a name
without function arguments (function arguments are printed from
dump_function_decl). However, FLAGS should still state the intent of
printing a name without function arguments (TFF_NO_FUNCTION_ARGUMENTS so
that dump_function_name can decide correctly whether to call
dump_template_parms.

Based on an initial patch from Jason Merrill <jason@redhat.com>.

Signed-off-by: Matthias Kretz <m.kretz@gsi.de>

gcc/testsuite/ChangeLog:

	* g++.dg/debug/dwarf2/template-params-12n.C: Optionally, allow
	DW_AT_default_value.
	* g++.dg/diagnostic/default-template-args-1.C: New test.
	* g++.dg/diagnostic/default-template-args-2.C: New test.
	* g++.dg/diagnostic/default-template-args-3.C: New test.
	* g++.dg/diagnostic/default-template-args-4.C: New test.
	* g++.dg/diagnostic/default-template-args-5.C: New test.
	* g++.dg/diagnostic/param-type-mismatch-2.C: Expect template
	parms in diagnostic.
	* g++.dg/ext/pretty1.C: Expect function template specialization
	to not pretty-print template parms.
	* g++.old-deja/g++.ext/pretty3.C: Ditto.
	* g++.old-deja/g++.pt/memtemp77.C: Ditto.
	* g++.dg/goacc/template.C: Expect function template parms for
	explicit arguments.
	* g++.dg/gomp/declare-variant-7.C: Expect no function template
	parms for deduced arguments.
	* g++.dg/template/error40.C: Expect only non-default template
	arguments in diagnostic.

gcc/cp/ChangeLog:

	* constraint.cc (get_mapped_args): Remove incorrect non-default
	args count on multi-level template args; instead set the
	non-default args count on each inner TREE_VEC.
	* cp-tree.h: Rewrite NON_DEFAULT_TEMPLATE_ARGS_COUNT
	implementation to store the number explicitly specified
	arguments in a TREE_LIST.
	(EXPLICIT_TEMPLATE_ARGS_P): New.
	(SET_EXPLICIT_TEMPLATE_ARGS_P): New.
	(set_non_default_template_args_count): New declaration.
	(get_non_default_template_args_count): New declaration.
	(set_explicit_template_args_count): New declaration.
	(get_explicit_template_args_count): New declaration.
	(TFF_AS_PRIMARY): New constant.
	* decl.c (grokfndecl): Mark all template arguments in a friend
	declaration as explicitly specified.
	* error.c (args_or_non_default_template_args_count): Renamed
	from get_non_default_template_args_count (name-clash). Make
	independent of flag_pretty_templates.
	(dump_template_bindings): Add flags parameter to be passed to
	get_non_default_template_args_count. Print only non-default
	template arguments. Add used_parms parameter: print defaulted
	template bindings if the parameter name is part of used_parms.
	(dump_substitution): Walk the function_decl to find all used
	template parameters.
	(dump_function_decl): Call dump_function_name and dump_type of
	the DECL_CONTEXT with specialized template and set
	TFF_AS_PRIMARY for their flags. Don't print template arguments.
	dump_function_name already does so.
	(dump_function_name): Add and document conditions for calling
	dump_template_parms. Move DECL_USE_TEMPLATE to PRIMARY parameter
	of dump_template_parms.
	(dump_template_parms): Print only non-default template
	parameters.
	(lang_decl_name): Add TFF_NO_FUNCTION_ARGUMENTS to
	dump_function_name flags.
	* pt.c (set_non_default_template_args_count): New function.
	(get_non_default_template_args_count): New function.
	(set_explicit_template_args_count): New function.
	(get_explicit_template_args_count): New function.
	(expand_template_argument_pack): Always adjust and set
	the adjusted non-default args count.
	(template_parm_to_arg): SET_NON_DEFAULT_TEMPLATE_ARGS_COUNT
	independent of CHECKING_P.
	(coerce_template_parameter_pack): Ditto.
	(coerce_template_parms): Ditto.
	(fn_type_unification): Ditto.
	(type_unification_real): Ditto.
	(get_partial_spec_bindings): Ditto.
	(determine_specialization): Also copy the inner TREE_VECs.
	(template_parms_level_to_args): Always count non-default args.
	(copy_template_args): Only copy the non-default template args
	count on TREE_VECs that should have it.
	(fn_type_unification): Set EXPLICIT_TEMPLATE_ARGS_P on the
	template arguments tree if any template parameter was explicitly
	given.
	(type_unification_real): Count non-default args sooner.
	(get_partial_spec_bindings): Set non-default args count.
	(tsubst_template_args): Take a shortcut for multi-level args to
	avoid a lot of unnecessary checks and simplify non-default args
	counting. Fix the count of non-default template arguments.
 gcc/cp/constraint.cc                          |   3 +-
 gcc/cp/cp-tree.h                              |  36 ++-
 gcc/cp/decl.c                                 |   4 +
 gcc/cp/error.c                                | 200 +++++++++------
 gcc/cp/pt.c                                   | 232 ++++++++++++++----
 .../g++.dg/debug/dwarf2/template-params-12n.C |   2 +-
 .../diagnostic/default-template-args-1.C      |  61 +++++
 .../diagnostic/default-template-args-2.C      |  37 +++
 .../diagnostic/default-template-args-3.C      |  29 +++
 .../diagnostic/default-template-args-4.C      |  19 ++
 .../diagnostic/default-template-args-5.C      |  12 +
 .../g++.dg/diagnostic/param-type-mismatch-2.C |   2 +-
 gcc/testsuite/g++.dg/ext/pretty1.C            |   2 +-
 gcc/testsuite/g++.dg/goacc/template.C         |   8 +-
 gcc/testsuite/g++.dg/gomp/declare-variant-7.C |   4 +-
 gcc/testsuite/g++.dg/template/error40.C       |   6 +-
 gcc/testsuite/g++.old-deja/g++.ext/pretty3.C  |   2 +-
 gcc/testsuite/g++.old-deja/g++.pt/memtemp77.C |   2 +-
 18 files changed, 508 insertions(+), 153 deletions(-)
 create mode 100644 gcc/testsuite/g++.dg/diagnostic/default-template-args-1.C
 create mode 100644 gcc/testsuite/g++.dg/diagnostic/default-template-args-2.C
 create mode 100644 gcc/testsuite/g++.dg/diagnostic/default-template-args-3.C
 create mode 100644 gcc/testsuite/g++.dg/diagnostic/default-template-args-4.C
 create mode 100644 gcc/testsuite/g++.dg/diagnostic/default-template-args-5.C

 Dr. Matthias Kretz                           https://mattkretz.github.io
 GSI Helmholtz Centre for Heavy Ion Research               https://gsi.de
 stdₓ::simd
──────────────────────────────────────────────────────────────────────────

[-- Attachment #2: 0001-c-Print-function-template-parms-when-relevant.patch --]
[-- Type: text/x-patch, Size: 47690 bytes --]

diff --git a/gcc/cp/constraint.cc b/gcc/cp/constraint.cc
index 2896efdd7f2..17dce107491 100644
--- a/gcc/cp/constraint.cc
+++ b/gcc/cp/constraint.cc
@@ -2890,10 +2890,11 @@ get_mapped_args (tree map)
       tree level = make_tree_vec (list.length ());
       for (unsigned j = 0; j < list.length(); ++j)
 	TREE_VEC_ELT (level, j) = list[j];
+      /* None of the args at any level are defaulted.  */
+      SET_NON_DEFAULT_TEMPLATE_ARGS_COUNT (level, list.length());
       SET_TMPL_ARGS_LEVEL (args, i + 1, level);
       list.release ();
     }
-  SET_NON_DEFAULT_TEMPLATE_ARGS_COUNT (args, 0);
 
   return args;
 }
diff --git a/gcc/cp/cp-tree.h b/gcc/cp/cp-tree.h
index f387b5036d2..b5350bb8409 100644
--- a/gcc/cp/cp-tree.h
+++ b/gcc/cp/cp-tree.h
@@ -3684,25 +3684,29 @@ struct GTY(()) lang_decl {
   ((struct tree_template_info*)TEMPLATE_INFO_CHECK (NODE))->args
 #define TI_PENDING_TEMPLATE_FLAG(NODE) \
   TREE_LANG_FLAG_1 (TEMPLATE_INFO_CHECK (NODE))
-/* For a given TREE_VEC containing a template argument list,
-   this property contains the number of arguments that are not
-   defaulted.  */
+/* For a given TREE_VEC containing a template argument list (but not multiple
+   levels of arguments), this property contains the number of arguments that are
+   not defaulted and optionally the number of explicitly specified template
+   arguments. It is either a INT_CST denoting the number of non-default
+   arguments, or a TREE_LIST with TREE_PURPOSE denoting the number of explicitly
+   given template arguments of a function template, and TREE_VALUE denoting the
+   number of non-default arguments.  */
 #define NON_DEFAULT_TEMPLATE_ARGS_COUNT(NODE) \
   TREE_CHAIN (TREE_VEC_CHECK (NODE))
 
 /* Below are the setter and getter of the NON_DEFAULT_TEMPLATE_ARGS_COUNT
    property.  */
 #define SET_NON_DEFAULT_TEMPLATE_ARGS_COUNT(NODE, INT_VALUE) \
-  NON_DEFAULT_TEMPLATE_ARGS_COUNT(NODE) = build_int_cst (NULL_TREE, INT_VALUE)
-#if CHECKING_P
-#define GET_NON_DEFAULT_TEMPLATE_ARGS_COUNT(NODE) \
-    int_cst_value (NON_DEFAULT_TEMPLATE_ARGS_COUNT (NODE))
-#else
+  set_non_default_template_args_count (TREE_VEC_CHECK (NODE), INT_VALUE)
+
 #define GET_NON_DEFAULT_TEMPLATE_ARGS_COUNT(NODE) \
-  NON_DEFAULT_TEMPLATE_ARGS_COUNT (NODE) \
-  ? int_cst_value (NON_DEFAULT_TEMPLATE_ARGS_COUNT (NODE)) \
-  : TREE_VEC_LENGTH (INNERMOST_TEMPLATE_ARGS (NODE))
-#endif
+  get_non_default_template_args_count (TREE_VEC_CHECK (NODE))
+
+#define EXPLICIT_TEMPLATE_ARGS_P(NODE) \
+  (get_explicit_template_args_count (TREE_VEC_CHECK (NODE)) > 0)
+
+#define SET_EXPLICIT_TEMPLATE_ARGS_COUNT(NODE, INT_VALUE) \
+  set_explicit_template_args_count (TREE_VEC_CHECK (NODE), INT_VALUE)
 
 /* The list of access checks that were deferred during parsing
    which need to be performed at template instantiation time.
@@ -5938,7 +5942,8 @@ enum auto_deduction_context
        identical to their defaults.
    TFF_NO_TEMPLATE_BINDINGS: do not print information about the template
        arguments for a function template specialization.
-   TFF_POINTER: we are printing a pointer type.  */
+   TFF_POINTER: we are printing a pointer type.
+   TFF_AS_PRIMARY: show the template like a primary template.  */
 
 #define TFF_PLAIN_IDENTIFIER			(0)
 #define TFF_SCOPE				(1)
@@ -5956,6 +5961,7 @@ enum auto_deduction_context
 #define TFF_NO_OMIT_DEFAULT_TEMPLATE_ARGUMENTS	(1 << 12)
 #define TFF_NO_TEMPLATE_BINDINGS		(1 << 13)
 #define TFF_POINTER		                (1 << 14)
+#define TFF_AS_PRIMARY		                (1 << 15)
 
 /* These constants can be used as bit flags to control strip_typedefs.
 
@@ -7153,6 +7159,10 @@ extern void pop_access_scope			(tree);
 extern bool check_template_shadow		(tree);
 extern bool check_auto_in_tmpl_args             (tree, tree);
 extern tree get_innermost_template_args		(tree, int);
+extern void set_non_default_template_args_count (tree args, int count);
+extern int  get_non_default_template_args_count (tree args);
+extern void set_explicit_template_args_count (tree args, int count);
+extern bool get_explicit_template_args_count (tree args);
 extern void maybe_begin_member_template_processing (tree);
 extern void maybe_end_member_template_processing (void);
 extern tree finish_member_template_decl		(tree);
diff --git a/gcc/cp/decl.c b/gcc/cp/decl.c
index 7c2a134e406..74b0cac0f2f 100644
--- a/gcc/cp/decl.c
+++ b/gcc/cp/decl.c
@@ -9985,6 +9985,10 @@ grokfndecl (tree ctype,
 	  /* A friend declaration of the form friend void f<>().  Record
 	     the information in the TEMPLATE_ID_EXPR.  */
 	  SET_DECL_IMPLICIT_INSTANTIATION (decl);
+	  /* Set the template args as explicitly specified - they were certainly
+	     not deduced from function arguments.  */
+	  if (args)
+	    SET_EXPLICIT_TEMPLATE_ARGS_COUNT (args, NUM_TMPL_ARGS (args));
 
 	  gcc_assert (identifier_p (fns) || OVL_P (fns));
 	  DECL_TEMPLATE_INFO (decl) = build_template_info (fns, args);
diff --git a/gcc/cp/error.c b/gcc/cp/error.c
index 012a4ecddf4..fb62adbeb0b 100644
--- a/gcc/cp/error.c
+++ b/gcc/cp/error.c
@@ -86,11 +86,11 @@ static void dump_exception_spec (cxx_pretty_printer *, tree, int);
 static void dump_template_argument (cxx_pretty_printer *, tree, int);
 static void dump_template_argument_list (cxx_pretty_printer *, tree, int);
 static void dump_template_parameter (cxx_pretty_printer *, tree, int);
-static void dump_template_bindings (cxx_pretty_printer *, tree, tree,
-                                    vec<tree, va_gc> *);
+static void dump_template_bindings (cxx_pretty_printer *, tree, tree, tree,
+                                    vec<tree, va_gc> *, int);
 static void dump_scope (cxx_pretty_printer *, tree, int);
 static void dump_template_parms (cxx_pretty_printer *, tree, int, int);
-static int get_non_default_template_args_count (tree, int);
+static int args_or_non_default_template_args_count (tree, int);
 static const char *function_category (tree);
 static void maybe_print_constexpr_context (diagnostic_context *);
 static void maybe_print_instantiation_context (diagnostic_context *);
@@ -272,24 +272,23 @@ dump_template_argument (cxx_pretty_printer *pp, tree arg, int flags)
     }
 }
 
-/* Count the number of template arguments ARGS whose value does not
-   match the (optional) default template parameter in PARAMS  */
+/* Returns GET_NON_DEFAULT_TEMPLATE_ARGS_COUNT (ARGS) unless FLAGS requests the
+   full count of template args.  */
 
 static int
-get_non_default_template_args_count (tree args, int flags)
+args_or_non_default_template_args_count (tree args, int flags)
 {
-  int n = TREE_VEC_LENGTH (INNERMOST_TEMPLATE_ARGS (args));
+  gcc_assert (!TMPL_ARGS_HAVE_MULTIPLE_LEVELS (args));
 
   if (/* We use this flag when generating debug information.  We don't
 	 want to expand templates at this point, for this may generate
 	 new decls, which gets decl counts out of sync, which may in
 	 turn cause codegen differences between compilations with and
 	 without -g.  */
-      (flags & TFF_NO_OMIT_DEFAULT_TEMPLATE_ARGUMENTS) != 0
-      || !flag_pretty_templates)
-    return n;
+      (flags & TFF_NO_OMIT_DEFAULT_TEMPLATE_ARGUMENTS) != 0)
+    return NUM_TMPL_ARGS (args);
 
-  return GET_NON_DEFAULT_TEMPLATE_ARGS_COUNT (INNERMOST_TEMPLATE_ARGS (args));
+  return GET_NON_DEFAULT_TEMPLATE_ARGS_COUNT (args);
 }
 
 /* Dump a template-argument-list ARGS (always a TREE_VEC) under control
@@ -298,7 +297,7 @@ get_non_default_template_args_count (tree args, int flags)
 static void
 dump_template_argument_list (cxx_pretty_printer *pp, tree args, int flags)
 {
-  int n = get_non_default_template_args_count (args, flags);
+  int n = args_or_non_default_template_args_count (args, flags);
   int need_comma = 0;
   int i;
 
@@ -369,7 +368,7 @@ dump_template_parameter (cxx_pretty_printer *pp, tree parm, int flags)
 
 static void
 dump_template_bindings (cxx_pretty_printer *pp, tree parms, tree args,
-                        vec<tree, va_gc> *typenames)
+			tree used_parms, vec<tree, va_gc> *typenames, int flags)
 {
   /* Print "[with" and ']', conditional on whether anything is printed at all.
      This is tied to whether a semicolon is needed to separate multiple template
@@ -414,23 +413,47 @@ dump_template_bindings (cxx_pretty_printer *pp, tree parms, tree args,
       /* Don't crash if we had an invalid argument list.  */
       if (TMPL_ARGS_DEPTH (args) >= lvl)
 	lvl_args = TMPL_ARGS_LEVEL (args, lvl);
+      const int len = TREE_VEC_LENGTH (p);
+      const int non_default_len
+	= lvl_args ? args_or_non_default_template_args_count (lvl_args, flags)
+		   : len;
 
-      for (i = 0; i < TREE_VEC_LENGTH (p); ++i)
+      for (i = 0; i < len; ++i, ++arg_idx)
 	{
+	  const tree parm_i = TREE_VEC_ELT (p, i);
+	  gcc_assert (TREE_CODE (parm_i) == TREE_LIST);
+	  const tree parm_val = TREE_VALUE (parm_i);
+	  const tree parm_type = TREE_CODE (parm_val) == TYPE_DECL
+				   || TREE_CODE (parm_val) == TEMPLATE_DECL
+				   ? TREE_TYPE (parm_val)
+				   : TREE_CODE (parm_val) == PARM_DECL
+				       ? DECL_ARG_TYPE (parm_val)
+				       : NULL_TREE;
+
+	  /* If the template parameter is defaulted and does not appear in
+	     used_parms (function arguments, return type, or exception
+	     specifier), skip the parameter.  */
+	  if (i >= non_default_len)
+	    {
+	      tree it;
+	      for (it = used_parms; it && TREE_VALUE (it) != parm_type;
+		   it = TREE_CHAIN (it))
+		;
+	      if (!it)
+		continue;
+	    }
+
 	  tree arg = NULL_TREE;
 
 	  /* Don't crash if we had an invalid argument list.  */
 	  if (lvl_args && NUM_TMPL_ARGS (lvl_args) > arg_idx)
 	    arg = TREE_VEC_ELT (lvl_args, arg_idx);
 
-	  tree parm_i = TREE_VEC_ELT (p, i);
 	  /* If the template argument repeats the template parameter (T = T),
 	     skip the parameter.*/
 	  if (arg && TREE_CODE (arg) == TEMPLATE_TYPE_PARM
-		&& TREE_CODE (parm_i) == TREE_LIST
-		&& TREE_CODE (TREE_VALUE (parm_i)) == TYPE_DECL
-		&& TREE_CODE (TREE_TYPE (TREE_VALUE (parm_i)))
-		     == TEMPLATE_TYPE_PARM
+		&& TREE_CODE (parm_val) == TYPE_DECL
+		&& TREE_CODE (TREE_TYPE (parm_val)) == TEMPLATE_TYPE_PARM
 		&& DECL_NAME (TREE_VALUE (parm_i))
 		     == DECL_NAME (TREE_CHAIN (arg)))
 	    continue;
@@ -450,8 +473,6 @@ dump_template_bindings (cxx_pretty_printer *pp, tree parms, tree args,
 	    }
 	  else
 	    pp_string (pp, M_("<missing>"));
-
-	  ++arg_idx;
 	}
 
       parms = TREE_CHAIN (parms);
@@ -1634,8 +1655,16 @@ dump_substitution (cxx_pretty_printer *pp,
   if (template_parms != NULL_TREE && template_args != NULL_TREE
       && !(flags & TFF_NO_TEMPLATE_BINDINGS))
     {
-      vec<tree, va_gc> *typenames = t ? find_typenames (t) : NULL;
-      dump_template_bindings (pp, template_parms, template_args, typenames);
+      vec<tree, va_gc> *typenames = nullptr;
+      tree used_parms = NULL_TREE;
+      if (t)
+	{
+	  typenames = find_typenames (t);
+	  const tree fn = TREE_TYPE (DECL_TEMPLATE_RESULT (t));
+	  used_parms = find_template_parameters (fn, template_parms);
+	}
+      dump_template_bindings (pp, template_parms, template_args, used_parms,
+			      typenames, flags);
     }
 }
 
@@ -1688,8 +1717,15 @@ dump_function_decl (cxx_pretty_printer *pp, tree t, int flags)
   /* Likewise for the constexpr specifier, in case t is a specialization.  */
   constexpr_p = DECL_DECLARED_CONSTEXPR_P (t);
 
-  /* Pretty print template instantiations only.  */
-  if (DECL_USE_TEMPLATE (t) && DECL_TEMPLATE_INFO (t)
+  /* Keep t before the following branch makes t point to a more general
+     template. Without the specialized template, the information about defaulted
+     template arguments is lost.  */
+  tree specialized_t = t;
+  int specialized_flags = 0;
+
+  /* Pretty print only template instantiations. Don't pretty print explicit
+     specializations like 'template <> void fun<int> (int)'.  */
+  if (DECL_TEMPLATE_INSTANTIATION (t) && DECL_TEMPLATE_INFO (t)
       && !(flags & TFF_NO_TEMPLATE_BINDINGS)
       && flag_pretty_templates)
     {
@@ -1701,6 +1737,9 @@ dump_function_decl (cxx_pretty_printer *pp, tree t, int flags)
 	{
 	  template_parms = DECL_TEMPLATE_PARMS (tmpl);
 	  t = tmpl;
+	  /* The "[with ...]" clause is printed, thus dump functions printing
+	     SPECIALIZED_T need to add TFF_AS_PRIMARY to their flags.  */
+	  specialized_flags = TFF_AS_PRIMARY;
 	}
     }
 
@@ -1710,8 +1749,8 @@ dump_function_decl (cxx_pretty_printer *pp, tree t, int flags)
   fntype = TREE_TYPE (t);
   parmtypes = FUNCTION_FIRST_USER_PARMTYPE (t);
 
-  if (DECL_CLASS_SCOPE_P (t))
-    cname = DECL_CONTEXT (t);
+  if (DECL_CLASS_SCOPE_P (specialized_t))
+    cname = DECL_CONTEXT (specialized_t);
   /* This is for partially instantiated template methods.  */
   else if (TREE_CODE (fntype) == METHOD_TYPE)
     cname = TREE_TYPE (TREE_VALUE (parmtypes));
@@ -1749,13 +1788,14 @@ dump_function_decl (cxx_pretty_printer *pp, tree t, int flags)
     /* Nothing.  */;
   else if (cname)
     {
-      dump_type (pp, cname, flags);
+      dump_type (pp, cname, flags | specialized_flags);
       pp_cxx_colon_colon (pp);
     }
   else
     dump_scope (pp, CP_DECL_CONTEXT (t), flags);
 
-  dump_function_name (pp, t, dump_function_name_flags);
+  dump_function_name (pp, specialized_t,
+		      dump_function_name_flags | specialized_flags);
 
   if (!(flags & TFF_NO_FUNCTION_ARGUMENTS))
     {
@@ -1802,26 +1842,6 @@ dump_function_decl (cxx_pretty_printer *pp, tree t, int flags)
 	  pp_character (pp, ']');
 	}
     }
-  else if (template_args)
-    {
-      bool need_comma = false;
-      int i;
-      pp_cxx_begin_template_argument_list (pp);
-      template_args = INNERMOST_TEMPLATE_ARGS (template_args);
-      for (i = 0; i < TREE_VEC_LENGTH (template_args); ++i)
-	{
-	  tree arg = TREE_VEC_ELT (template_args, i);
-	  if (need_comma)
-	    pp_separate_with_comma (pp);
-	  if (ARGUMENT_PACK_P (arg))
-	    pp_cxx_left_brace (pp);
-	  dump_template_argument (pp, arg, TFF_PLAIN_IDENTIFIER);
-	  if (ARGUMENT_PACK_P (arg))
-	    pp_cxx_right_brace (pp);
-	  need_comma = true;
-	}
-      pp_cxx_end_template_argument_list (pp);
-    }
 }
 
 /* Print a parameter list. If this is for a member function, the
@@ -1968,13 +1988,35 @@ dump_function_name (cxx_pretty_printer *pp, tree t, int flags)
 
   dump_module_suffix (pp, t);
 
+/* Print function template parameters if:
+   1. t is template, and
+   2. the caller didn't request to only print the template-name, and
+   3. t actually has template parameters that are not indirect parameters from
+      enclosing scopes, i.e. either
+      - t is a friend template specialization
+	(eg. template<class T> struct X { friend void foo<T>(int); }; since
+	PRIMARY_TEMPLATE_P requires a TEMPLATE_DECL, this case must be checked
+	before PRIMARY_TEMPLATE_P is safe to call), or
+      - t is a primary template (own template header),
+      and
+   4. either
+      - flags requests to show no function arguments, in which case deduced
+	types could be hidden and thus need to be printed, or
+      - at least one function template argument was given explicitly and the
+	diagnostics output should reflect the code as it was written,
+
+  Whether t is a specialization of a template informs the PRIMARY parameter of
+  dump_template_parms.
+ */
   if (DECL_TEMPLATE_INFO (t)
       && !(flags & TFF_TEMPLATE_NAME)
-      && !DECL_FRIEND_PSEUDO_TEMPLATE_INSTANTIATION (t)
       && (TREE_CODE (DECL_TI_TEMPLATE (t)) != TEMPLATE_DECL
-	  || PRIMARY_TEMPLATE_P (DECL_TI_TEMPLATE (t))))
-    dump_template_parms (pp, DECL_TEMPLATE_INFO (t), !DECL_USE_TEMPLATE (t),
-                         flags);
+	    || PRIMARY_TEMPLATE_P (DECL_TI_TEMPLATE (t)))
+      && ((flags & TFF_NO_FUNCTION_ARGUMENTS)
+	    || (DECL_TI_ARGS (t)
+		  && EXPLICIT_TEMPLATE_ARGS_P (INNERMOST_TEMPLATE_ARGS
+						 (DECL_TI_ARGS (t))))))
+    dump_template_parms (pp, DECL_TEMPLATE_INFO (t), !DECL_USE_TEMPLATE (t), flags);
 }
 
 /* Dump the template parameters from the template info INFO under control of
@@ -1989,6 +2031,8 @@ dump_template_parms (cxx_pretty_printer *pp, tree info,
 {
   tree args = info ? TI_ARGS (info) : NULL_TREE;
 
+  if (flags & TFF_AS_PRIMARY)
+    primary = true;
   if (primary && flags & TFF_TEMPLATE_NAME)
     return;
   flags &= ~(TFF_CLASS_KEY_OR_ENUM | TFF_TEMPLATE_NAME);
@@ -1998,10 +2042,11 @@ dump_template_parms (cxx_pretty_printer *pp, tree info,
      to crash producing error messages.  */
   if (args && !primary)
     {
-      int len, ix;
-      len = get_non_default_template_args_count (args, flags);
+      int ix;
 
       args = INNERMOST_TEMPLATE_ARGS (args);
+      int len = args_or_non_default_template_args_count (args, flags);
+      gcc_assert (len <= NUM_TMPL_ARGS (args));
       for (ix = 0; ix != len; ix++)
 	{
 	  tree arg = TREE_VEC_ELT (args, ix);
@@ -2026,25 +2071,35 @@ dump_template_parms (cxx_pretty_printer *pp, tree info,
       tree parms = DECL_TEMPLATE_PARMS (tpl);
       int len, ix;
 
-      parms = TREE_CODE (parms) == TREE_LIST ? TREE_VALUE (parms) : NULL_TREE;
-      len = parms ? TREE_VEC_LENGTH (parms) : 0;
-
-      for (ix = 0; ix != len; ix++)
+      if (TREE_CODE (parms) == TREE_LIST)
 	{
-	  tree parm;
+	  parms = INNERMOST_TEMPLATE_PARMS (parms);
+	  if (args)
+	    {
+	      len = args_or_non_default_template_args_count
+		      (INNERMOST_TEMPLATE_ARGS (args), flags);
+	      gcc_assert (len <= TREE_VEC_LENGTH (parms));
+	    }
+	  else
+	    len = TREE_VEC_LENGTH (parms);
 
-          if (TREE_VEC_ELT (parms, ix) == error_mark_node)
-            {
-              pp_string (pp, M_("<template parameter error>"));
-              continue;
-            }
+	  for (ix = 0; ix != len; ix++)
+	    {
+	      tree parm;
 
-          parm = TREE_VALUE (TREE_VEC_ELT (parms, ix));
+	      if (TREE_VEC_ELT (parms, ix) == error_mark_node)
+		{
+		  pp_string (pp, M_("<template parameter error>"));
+		  continue;
+		}
 
-	  if (ix)
-	    pp_separate_with_comma (pp);
+	      parm = TREE_VALUE (TREE_VEC_ELT (parms, ix));
 
-	  dump_decl (pp, parm, flags & ~TFF_DECL_SPECIFIERS);
+	      if (ix)
+		pp_separate_with_comma (pp);
+
+	      dump_decl (pp, parm, flags & ~TFF_DECL_SPECIFIERS);
+	    }
 	}
     }
   pp_cxx_end_template_argument_list (pp);
@@ -3163,7 +3218,8 @@ lang_decl_name (tree decl, int v, bool translate)
     }
 
   if (TREE_CODE (decl) == FUNCTION_DECL)
-    dump_function_name (cxx_pp, decl, TFF_PLAIN_IDENTIFIER);
+    dump_function_name (cxx_pp, decl,
+			TFF_PLAIN_IDENTIFIER | TFF_NO_FUNCTION_ARGUMENTS);
   else if ((DECL_NAME (decl) == NULL_TREE)
            && TREE_CODE (decl) == NAMESPACE_DECL)
     dump_decl (cxx_pp, decl, TFF_PLAIN_IDENTIFIER | TFF_UNQUALIFIED_NAME);
@@ -4060,10 +4116,10 @@ print_template_differences (pretty_printer *pp, tree type_a, tree type_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);
+  int len_a = args_or_non_default_template_args_count (args_a, flags);
   args_b = INNERMOST_TEMPLATE_ARGS (args_b);
+  int len_b = args_or_non_default_template_args_count (args_b, flags);
   /* 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.
diff --git a/gcc/cp/pt.c b/gcc/cp/pt.c
index 66040035b2f..2460bdc0fe4 100644
--- a/gcc/cp/pt.c
+++ b/gcc/cp/pt.c
@@ -689,6 +689,63 @@ strip_innermost_template_args (tree args, int extra_levels)
   return new_args;
 }
 
+/* Let ARGS know that there are COUNT entries which are not defaulted. ARGS must
+ * be a single level of arguments.  */
+
+void
+set_non_default_template_args_count (tree args, int count)
+{
+  gcc_assert (!TMPL_ARGS_HAVE_MULTIPLE_LEVELS (args));
+  gcc_assert (count <= NUM_TMPL_ARGS (args));
+  tree cst = build_int_cst (integer_type_node, count);
+  if (TREE_CHAIN (args) && TREE_CODE (TREE_CHAIN (args)) == TREE_LIST)
+    TREE_VALUE (TREE_CHAIN (args)) = cst;
+  else
+    TREE_CHAIN (args) = cst;
+}
+
+/* Query the number of non-defaulted template arguments in a single level of
+   template ARGS.  */
+
+int
+get_non_default_template_args_count (tree args)
+{
+  gcc_assert (!TMPL_ARGS_HAVE_MULTIPLE_LEVELS (args));
+  tree node = TREE_CHAIN (args);
+  if (!node)
+    return NUM_TMPL_ARGS (args);
+  if (TREE_CODE (node) == TREE_LIST)
+    node = TREE_VALUE (node);
+  const int count = int_cst_value (node);
+  gcc_assert (count <= NUM_TMPL_ARGS (args));
+  return count;
+}
+
+/* Set the COUNT of explicitly specified function template ARGS.  */
+
+void
+set_explicit_template_args_count (tree args, int count)
+{
+  args = INNERMOST_TEMPLATE_ARGS (args);
+  tree non_default
+    = TREE_CHAIN (args)
+	? INTEGER_CST_CHECK (TREE_CHAIN (args))
+	: build_int_cst (integer_type_node, TREE_VEC_LENGTH (args));
+  TREE_CHAIN (args) = build_tree_list (build_int_cst (integer_type_node, count),
+				       non_default);
+}
+
+/* Query the number of explicity specified function template ARGS.  */
+
+bool
+get_explicit_template_args_count (tree args)
+{
+  tree node = TREE_CHAIN (INNERMOST_TEMPLATE_ARGS (args));
+  return node && TREE_CODE (node) == TREE_LIST
+	   ? int_cst_value (TREE_PURPOSE (node))
+	   : 0;
+}
+
 /* We've got a template header coming up; push to a new level for storing
    the parms.  */
 
@@ -2504,7 +2561,7 @@ determine_specialization (tree template_id,
   if (candidates)
     {
       tree fn = TREE_VALUE (candidates);
-      *targs_out = copy_node (DECL_TI_ARGS (fn));
+      *targs_out = copy_template_args (DECL_TI_ARGS (fn));
 
       /* Propagate the candidate's constraints to the declaration.  */
       if (tsk != tsk_template)
@@ -4334,7 +4391,6 @@ expand_template_argument_pack (tree args)
   tree result_args = NULL_TREE;
   int in_arg, out_arg = 0, nargs = args ? TREE_VEC_LENGTH (args) : 0;
   int num_result_args = -1;
-  int non_default_args_count = -1;
 
   /* First, determine if we need to expand anything, and the number of
      slots we'll need.  */
@@ -4364,9 +4420,7 @@ expand_template_argument_pack (tree args)
 
   /* Expand arguments.  */
   result_args = make_tree_vec (num_result_args);
-  if (NON_DEFAULT_TEMPLATE_ARGS_COUNT (args))
-    non_default_args_count =
-      GET_NON_DEFAULT_TEMPLATE_ARGS_COUNT (args);
+  int non_default_args_count = GET_NON_DEFAULT_TEMPLATE_ARGS_COUNT (args);
   for (in_arg = 0; in_arg < nargs; ++in_arg)
     {
       tree arg = TREE_VEC_ELT (args, in_arg);
@@ -4376,8 +4430,7 @@ expand_template_argument_pack (tree args)
           int i, num_packed = TREE_VEC_LENGTH (packed);
           for (i = 0; i < num_packed; ++i, ++out_arg)
             TREE_VEC_ELT (result_args, out_arg) = TREE_VEC_ELT(packed, i);
-	  if (non_default_args_count > 0)
-	    non_default_args_count += num_packed - 1;
+	  non_default_args_count += num_packed - 1;
         }
       else
         {
@@ -4385,8 +4438,7 @@ expand_template_argument_pack (tree args)
           ++out_arg;
         }
     }
-  if (non_default_args_count >= 0)
-    SET_NON_DEFAULT_TEMPLATE_ARGS_COUNT (result_args, non_default_args_count);
+  SET_NON_DEFAULT_TEMPLATE_ARGS_COUNT (result_args, non_default_args_count);
   return result_args;
 }
 
@@ -4789,8 +4841,7 @@ template_parm_to_arg (tree t)
 	  /* Turn this argument into a TYPE_ARGUMENT_PACK
 	     with a single element, which expands T.  */
 	  tree vec = make_tree_vec (1);
-	  if (CHECKING_P)
-	    SET_NON_DEFAULT_TEMPLATE_ARGS_COUNT (vec, TREE_VEC_LENGTH (vec));
+	  SET_NON_DEFAULT_TEMPLATE_ARGS_COUNT (vec, TREE_VEC_LENGTH (vec));
 
 	  TREE_VEC_ELT (vec, 0) = make_pack_expansion (t);
 
@@ -4805,8 +4856,7 @@ template_parm_to_arg (tree t)
 	  /* Turn this argument into a NONTYPE_ARGUMENT_PACK
 	     with a single element, which expands T.  */
 	  tree vec = make_tree_vec (1);
-	  if (CHECKING_P)
-	    SET_NON_DEFAULT_TEMPLATE_ARGS_COUNT (vec, TREE_VEC_LENGTH (vec));
+	  SET_NON_DEFAULT_TEMPLATE_ARGS_COUNT (vec, TREE_VEC_LENGTH (vec));
 
 	  t = convert_from_reference (t);
 	  TREE_VEC_ELT (vec, 0) = make_pack_expansion (t);
@@ -4828,11 +4878,16 @@ template_parms_level_to_args (tree parms)
 {
   tree a = copy_node (parms);
   TREE_TYPE (a) = NULL_TREE;
+  int nondefault = 0;
   for (int i = TREE_VEC_LENGTH (a) - 1; i >= 0; --i)
-    TREE_VEC_ELT (a, i) = template_parm_to_arg (TREE_VEC_ELT (a, i));
+    {
+      tree elt = TREE_VEC_ELT (a, i);
+      TREE_VEC_ELT (a, i) = template_parm_to_arg (elt);
+      if (!elt || elt == error_mark_node || !TREE_PURPOSE (elt))
+	++nondefault;
+    }
 
-  if (CHECKING_P)
-    SET_NON_DEFAULT_TEMPLATE_ARGS_COUNT (a, TREE_VEC_LENGTH (a));
+  SET_NON_DEFAULT_TEMPLATE_ARGS_COUNT (a, nondefault);
 
   return a;
 }
@@ -8721,9 +8776,8 @@ coerce_template_parameter_pack (tree parms,
     }
 
   SET_ARGUMENT_PACK_ARGS (argument_pack, packed_args);
-  if (CHECKING_P)
-    SET_NON_DEFAULT_TEMPLATE_ARGS_COUNT (packed_args,
-					 TREE_VEC_LENGTH (packed_args));
+  SET_NON_DEFAULT_TEMPLATE_ARGS_COUNT (packed_args,
+				       TREE_VEC_LENGTH (packed_args));
   return argument_pack;
 }
 
@@ -9087,7 +9141,7 @@ coerce_template_parms (tree parms,
       return error_mark_node;
     }
 
-  if (CHECKING_P && !NON_DEFAULT_TEMPLATE_ARGS_COUNT (new_inner_args))
+  if (!NON_DEFAULT_TEMPLATE_ARGS_COUNT (new_inner_args))
     SET_NON_DEFAULT_TEMPLATE_ARGS_COUNT (new_inner_args,
 					 TREE_VEC_LENGTH (new_inner_args));
 
@@ -13332,8 +13386,9 @@ copy_template_args (tree t)
       TREE_VEC_ELT (new_vec, i) = elt;
     }
 
-  NON_DEFAULT_TEMPLATE_ARGS_COUNT (new_vec)
-    = NON_DEFAULT_TEMPLATE_ARGS_COUNT (t);
+  if (!TMPL_ARGS_HAVE_MULTIPLE_LEVELS (t))
+    NON_DEFAULT_TEMPLATE_ARGS_COUNT (new_vec)
+      = NON_DEFAULT_TEMPLATE_ARGS_COUNT (t);
 
   return new_vec;
 }
@@ -13373,13 +13428,33 @@ tree
 tsubst_template_args (tree t, tree args, tsubst_flags_t complain, tree in_decl)
 {
   tree orig_t = t;
-  int len, need_new = 0, i, expanded_len_adjust = 0, out;
+  int need_new = 0, i, expanded_len_adjust = 0, out;
   tree *elts;
 
   if (t == error_mark_node)
     return error_mark_node;
 
-  len = TREE_VEC_LENGTH (t);
+  const int len = TREE_VEC_LENGTH (t);
+  if (len == 0)
+    return t;
+
+  /* Shortcut if T has multiple levels. All elts must be TREE_VECs and we simply
+     recurse. The remainder of this function doesn't apply to multi-level
+     args.  */
+  if (TMPL_ARGS_HAVE_MULTIPLE_LEVELS (t))
+    {
+      tree r = make_tree_vec (len);
+      for (i = 0; i < len; i++)
+	{
+	  tree new_arg = tsubst_template_args (TREE_VEC_ELT (t, i), args,
+					       complain, in_decl);
+	  if (new_arg == error_mark_node)
+	    return error_mark_node;
+	  TREE_VEC_ELT (r, i) = new_arg;
+	}
+      return r;
+    }
+
   elts = XALLOCAVEC (tree, len);
 
   for (i = 0; i < len; i++)
@@ -13389,8 +13464,6 @@ tsubst_template_args (tree t, tree args, tsubst_flags_t complain, tree in_decl)
 
       if (!orig_arg)
 	new_arg = NULL_TREE;
-      else if (TREE_CODE (orig_arg) == TREE_VEC)
-	new_arg = tsubst_template_args (orig_arg, args, complain, in_decl);
       else if (PACK_EXPANSION_P (orig_arg))
         {
           /* Substitute into an expansion expression.  */
@@ -13424,19 +13497,6 @@ tsubst_template_args (tree t, tree args, tsubst_flags_t complain, tree in_decl)
   /* Make space for the expanded arguments coming from template
      argument packs.  */
   t = make_tree_vec (len + expanded_len_adjust);
-  /* ORIG_T can contain TREE_VECs. That happens if ORIG_T contains the
-     arguments for a member template.
-     In that case each TREE_VEC in ORIG_T represents a level of template
-     arguments, and ORIG_T won't carry any non defaulted argument count.
-     It will rather be the nested TREE_VECs that will carry one.
-     In other words, ORIG_T carries a non defaulted argument count only
-     if it doesn't contain any nested TREE_VEC.  */
-  if (NON_DEFAULT_TEMPLATE_ARGS_COUNT (orig_t))
-    {
-      int count = GET_NON_DEFAULT_TEMPLATE_ARGS_COUNT (orig_t);
-      count += expanded_len_adjust;
-      SET_NON_DEFAULT_TEMPLATE_ARGS_COUNT (t, count);
-    }
   for (i = 0, out = 0; i < len; i++)
     {
       tree orig_arg = TREE_VEC_ELT (orig_t, i);
@@ -13457,6 +13517,65 @@ tsubst_template_args (tree t, tree args, tsubst_flags_t complain, tree in_decl)
         }
     }
 
+  /* The non-default template count can only be copied from ARGS if ORIG_T
+     requests a substitution of a complete level from ARGS. If the resulting T
+     contains the same entries as ARGS, then simply return the relevant level
+     from ARGS, which already has the correct non-default count.  */
+  int non_default_count = GET_NON_DEFAULT_TEMPLATE_ARGS_COUNT (orig_t)
+			    + expanded_len_adjust;
+  if (expanded_len_adjust == 0)
+    {
+      auto level_of_parm = [](tree vec, int i) -> int {
+	tree parm = TREE_VEC_ELT (vec, i);
+	while (ARGUMENT_PACK_P (parm) || PACK_EXPANSION_P (parm))
+	  {
+	    if (ARGUMENT_PACK_P (parm))
+	      parm = ARGUMENT_PACK_ARGS (parm);
+	    if (PACK_EXPANSION_P (parm))
+	      parm = PACK_EXPANSION_PATTERN (parm);
+	    if (TREE_CODE (parm) == TREE_VEC && TREE_VEC_LENGTH (parm) == 1)
+	      parm = TREE_VEC_ELT (parm, 0);
+	  }
+	if ((TREE_CODE (parm) == TEMPLATE_TYPE_PARM
+	       || TREE_CODE (parm) == TEMPLATE_TEMPLATE_PARM
+	       || TREE_CODE (parm) == BOUND_TEMPLATE_TEMPLATE_PARM)
+	      && i == TEMPLATE_TYPE_IDX (parm))
+	  return TEMPLATE_TYPE_LEVEL (parm);
+	else if (TREE_CODE (parm) == TEMPLATE_PARM_INDEX
+		   && i == TEMPLATE_PARM_IDX (parm))
+	  return TEMPLATE_PARM_LEVEL (parm);
+	return -1;
+      };
+
+      const int level = level_of_parm (orig_t, 0);
+      if (level > 0 && level <= TMPL_ARGS_DEPTH (args))
+	{
+	  args = TMPL_ARGS_LEVEL (args, level);
+	  if (len == NUM_TMPL_ARGS (args))
+	    {
+	      bool t_equals_args
+		= TREE_VEC_ELT (args, 0) == TREE_VEC_ELT (t, 0);
+	      int i;
+	      for (i = 1; i < len; ++i)
+		{
+		  if (level_of_parm (orig_t, i) == level)
+		    t_equals_args = t_equals_args && TREE_VEC_ELT (args, i)
+						       == TREE_VEC_ELT (t, i);
+		  else
+		    break;
+		}
+	      if (i == len && t_equals_args)
+		{
+		  ggc_free (t);
+		  return args;
+		}
+	      if (i == len && NON_DEFAULT_TEMPLATE_ARGS_COUNT (args))
+		non_default_count = GET_NON_DEFAULT_TEMPLATE_ARGS_COUNT (args);
+	    }
+	}
+    }
+  SET_NON_DEFAULT_TEMPLATE_ARGS_COUNT (t, non_default_count);
+
   return t;
 }
 
@@ -21687,8 +21806,7 @@ fn_type_unification (tree fn,
       explicit_targs = INNERMOST_TEMPLATE_ARGS (explicit_targs);
       for (i = NUM_TMPL_ARGS (explicit_targs); i--;)
 	TREE_VEC_ELT (targs, i) = TREE_VEC_ELT (explicit_targs, i);
-      if (!incomplete && CHECKING_P
-	  && !NON_DEFAULT_TEMPLATE_ARGS_COUNT (targs))
+      if (!incomplete && !NON_DEFAULT_TEMPLATE_ARGS_COUNT (targs))
 	SET_NON_DEFAULT_TEMPLATE_ARGS_COUNT
 	  (targs, NUM_TMPL_ARGS (explicit_targs));
     }
@@ -21874,6 +21992,11 @@ fn_type_unification (tree fn,
 	excessive_deduction_depth = false;
     }
 
+  /* If all template parameters were explicitly given, treat them like default
+     template arguments for diagnostics.  */
+  if (explicit_targs && explicit_targs != error_mark_node)
+    SET_EXPLICIT_TEMPLATE_ARGS_COUNT (targs, NUM_TMPL_ARGS (explicit_targs));
+
   return r;
 }
 
@@ -22516,9 +22639,17 @@ type_unification_real (tree tparms,
 	     be NULL_TREE or ERROR_MARK_NODE, so we do not need
 	     to explicitly check cxx_dialect here.  */
 	  if (TREE_PURPOSE (TREE_VEC_ELT (tparms, i)))
-	    /* OK, there is a default argument.  Wait until after the
-	       conversion check to do substitution.  */
-	    continue;
+	    {
+	      /* The position of the first default template argument,
+		 is also the number of non-defaulted arguments in TARGS.
+		 Record that.  */
+	      if (!NON_DEFAULT_TEMPLATE_ARGS_COUNT (targs))
+		SET_NON_DEFAULT_TEMPLATE_ARGS_COUNT (targs, i);
+
+	      /* OK, there is a default argument.  Wait until after the
+		 conversion check to do substitution.  */
+	      continue;
+	    }
 
 	  /* If the type parameter is a parameter pack, then it will
 	     be deduced to an empty parameter pack.  */
@@ -22621,21 +22752,14 @@ type_unification_real (tree tparms,
 	  if (arg == error_mark_node)
 	    return 1;
 	  else if (arg)
-	    {
-	      TREE_VEC_ELT (targs, i) = arg;
-	      /* The position of the first default template argument,
-		 is also the number of non-defaulted arguments in TARGS.
-		 Record that.  */
-	      if (!NON_DEFAULT_TEMPLATE_ARGS_COUNT (targs))
-		SET_NON_DEFAULT_TEMPLATE_ARGS_COUNT (targs, i);
-	    }
+	    TREE_VEC_ELT (targs, i) = arg;
 	}
 
       if (saw_undeduced++ == 1)
 	goto again;
     }
 
-  if (CHECKING_P && !NON_DEFAULT_TEMPLATE_ARGS_COUNT (targs))
+  if (!NON_DEFAULT_TEMPLATE_ARGS_COUNT (targs))
     SET_NON_DEFAULT_TEMPLATE_ARGS_COUNT (targs, TREE_VEC_LENGTH (targs));
 
   return unify_success (explain_p);
@@ -24998,6 +25122,8 @@ get_partial_spec_bindings (tree tmpl, tree spec_tmpl, tree args)
   if (!template_template_parm_bindings_ok_p (tparms, deduced_args))
     return NULL_TREE;
 
+  SET_NON_DEFAULT_TEMPLATE_ARGS_COUNT (deduced_args, ntparms);
+
   return deduced_args;
 }
 
diff --git a/gcc/testsuite/g++.dg/debug/dwarf2/template-params-12n.C b/gcc/testsuite/g++.dg/debug/dwarf2/template-params-12n.C
index d3c1f589f87..b88bf7d4b34 100644
--- a/gcc/testsuite/g++.dg/debug/dwarf2/template-params-12n.C
+++ b/gcc/testsuite/g++.dg/debug/dwarf2/template-params-12n.C
@@ -1,6 +1,6 @@
 // { dg-options "-gdwarf-2 -dA" }
 // { dg-final { scan-assembler-times "DIE \\(\[^\n\]*\\) DW_TAG_template_value_param" 1 } }
-// { dg-final { scan-assembler-times "DIE \\(\[^\n\]*\\) DW_TAG_template_value_param\[^\n\]*\n\[^\n\]* DW_AT_name\n\[^\n\]* DW_AT_type\n\[^\n\]* DW_AT_const_value" 1 } }
+// { dg-final { scan-assembler-times "DIE \\(\[^\n\]*\\) DW_TAG_template_value_param\[^\n\]*\n\[^\n\]* DW_AT_name\n\[^\n\]* DW_AT_type\n(?:\[^\n\]* DW_AT_default_value\n)?\[^\n\]* DW_AT_const_value" 1 } }
 #include "template-params-12.H"
 /* We get const_value for NULL pointers to member functions.  */
 #if __cplusplus > 199711L // Ugh, C++98 barfs at both the cast and the overload.
diff --git a/gcc/testsuite/g++.dg/diagnostic/default-template-args-1.C b/gcc/testsuite/g++.dg/diagnostic/default-template-args-1.C
new file mode 100644
index 00000000000..6481798a69b
--- /dev/null
+++ b/gcc/testsuite/g++.dg/diagnostic/default-template-args-1.C
@@ -0,0 +1,61 @@
+// { dg-options "-fpretty-templates" }
+// { dg-do compile { target c++11 } }
+
+template <int a = 1>
+  [[deprecated]] void f0(); // { dg-message "'template<int a> void f0\\(\\)'" }
+
+template <int a>
+  [[deprecated]] void f1(); // { dg-message "'template<int a> void f1\\(\\)'" }
+
+template <class a, int b = 1>
+  [[deprecated]] void f2(); // { dg-message "'template<class a, int b> void f2\\(\\)'" }
+
+template <class a, int b = 1>
+  [[deprecated]] void f3(a); // { dg-message "'template<class a, int b> void f3\\(a\\)'" }
+
+template <class a>
+  [[deprecated]] void f4(a); // { dg-message "'template<class a> void f4\\(a\\)'" }
+
+template <>
+  [[deprecated]] void f4<int>(int);
+
+template <>
+  [[deprecated]] void f4(float);
+
+template <class a, class b = int>
+  [[deprecated]] void f5(a);
+
+template void f5<float>(float); // { dg-error "'void f5<a>\\(a\\) .with a = float.'" }
+
+template <class a, class b = int>
+  struct c
+  {
+    template <class d, class e = int>
+      [[deprecated]] static void f(d);
+  };
+
+int main()
+{
+  f0();            // { dg-warning "'void f0\\(\\)'" }
+  f1<1>();         // { dg-warning "'void f1<a>\\(\\) .with int a = 1.'" }
+  f2<int>();       // { dg-warning "'void f2<a>\\(\\) .with a = int.'" }
+  f3(1);           // { dg-warning "'void f3\\(a\\) .with a = int.'" }
+  f3<float>(1);    // { dg-warning "'void f3<a>\\(a\\) .with a = float.'" }
+  f3<float, 2>(1); // { dg-warning "'void f3<a, b>\\(a\\) .with a = float; int b = 2.'" }
+  f4(1.);          // { dg-warning "'void f4\\(a\\) .with a = double.'" }
+  f4(1);           // { dg-warning "'void f4<int>\\(int\\)'" }
+  f4(1.f);         // { dg-warning "'void f4\\(float\\)'" }
+
+  f0(0); // { dg-error "" }
+  f1(0); // { dg-error "" }
+  f2(0); // { dg-error "" }
+  f3();  // { dg-error "" }
+  f4();  // { dg-error "" }
+
+  c<int>::f(1.);    // { dg-warning "'static void c<a>::f\\(d\\) .with d = double; a = int.'" }
+  c<int>::f<int>(1);    // { dg-warning "'static void c<a>::f<d>\\(d\\) .with d = int; a = int.'" }
+  c<int>::f<float, int>(1.f);    // { dg-warning "'static void c<a>::f<d, e>\\(d\\) .with d = float; e = int; a = int.'" }
+  c<float, int>::f(1.);    // { dg-warning "'static void c<a, b>::f\\(d\\) .with d = double; a = float; b = int.'" }
+  c<float, int>::f<int>(1);    // { dg-warning "'static void c<a, b>::f<d>\\(d\\) .with d = int; a = float; b = int.'" }
+  c<float, int>::f<float, int>(1.f);    // { dg-warning "'static void c<a, b>::f<d, e>\\(d\\) .with d = float; e = int; a = float; b = int.'" }
+}
diff --git a/gcc/testsuite/g++.dg/diagnostic/default-template-args-2.C b/gcc/testsuite/g++.dg/diagnostic/default-template-args-2.C
new file mode 100644
index 00000000000..a82709f5785
--- /dev/null
+++ b/gcc/testsuite/g++.dg/diagnostic/default-template-args-2.C
@@ -0,0 +1,37 @@
+// { dg-options "-fno-pretty-templates" }
+// { dg-do compile { target c++11 } }
+
+template <int a = 1>
+  [[deprecated]] void f0();
+
+template <int a>
+  [[deprecated]] void f1();
+
+template <class a, int b = 1>
+  [[deprecated]] void f2();
+
+template <class a, int b = 1>
+  [[deprecated]] void f3(a);
+
+template <class a>
+  [[deprecated]] void f4(a);
+
+template <>
+  [[deprecated]] void f4<int>(int);
+
+template <class a, class b = int>
+  [[deprecated]] void f5(a);
+
+template void f5<float>(float); // { dg-error "'void f5<float>\\(float\\)'" }
+
+int main()
+{
+  f0();            // { dg-warning "'void f0\\(\\)'" }
+  f1<1>();         // { dg-warning "'void f1<1>\\(\\)'" }
+  f2<int>();       // { dg-warning "'void f2<int>\\(\\)'" }
+  f3(1);           // { dg-warning "'void f3\\(int\\)'" }
+  f3<float>(1);    // { dg-warning "'void f3<float>\\(float\\)'" }
+  f3<float, 2>(1); // { dg-warning "'void f3<float, 2>\\(float\\)'" }
+  f4(1.);          // { dg-warning "'void f4\\(double\\)'" }
+  f4(1);           // { dg-warning "'void f4<int>\\(int\\)'" }
+}
diff --git a/gcc/testsuite/g++.dg/diagnostic/default-template-args-3.C b/gcc/testsuite/g++.dg/diagnostic/default-template-args-3.C
new file mode 100644
index 00000000000..c5dceafca29
--- /dev/null
+++ b/gcc/testsuite/g++.dg/diagnostic/default-template-args-3.C
@@ -0,0 +1,29 @@
+// { dg-options "-fpretty-templates" }
+// { dg-do compile { target c++11 } }
+
+template <class T>
+  struct id
+  { using type = T; };
+
+template <class T0, class T1 = int, template <class> class foo = id>
+  struct A
+  {
+    template <class U0 = const T1&>
+      [[deprecated]] static void
+      f(typename foo<U0>::type);
+  };
+
+template <int> struct B;
+
+template <> struct B<1> 
+  { using type = int; };
+
+template <class T, int x = 1>
+  [[deprecated]] typename B<x>::type
+  f0(T);
+
+int main()
+{
+  A<int>::f(0); // { dg-warning "'static void A<T0>::f\\(typename foo<U0>::type\\) .with U0 = const int&; T0 = int; foo = id; typename foo<U0>::type = const int&.'" }
+  f0(0); // { dg-warning "'typename B<x>::type f0\\(T\\) .with T = int; int x = 1; typename B<x>::type = int.'" }
+}
diff --git a/gcc/testsuite/g++.dg/diagnostic/default-template-args-4.C b/gcc/testsuite/g++.dg/diagnostic/default-template-args-4.C
new file mode 100644
index 00000000000..14704b6a29e
--- /dev/null
+++ b/gcc/testsuite/g++.dg/diagnostic/default-template-args-4.C
@@ -0,0 +1,19 @@
+// { dg-options "-fpretty-templates" }
+// { dg-do compile { target c++11 } }
+
+template <class T> struct X {};
+template <class... T> struct Y {};
+
+template <template <class> class Tpl, template <class...> class VTpl,
+	  class T0 = float, class U0 = VTpl<Tpl<T0>>, int x0 = 2>
+  struct S
+  {
+    template <class T, class... Ps, class U = int, int x = 1>
+      [[deprecated]] static U0
+      f(T, Ps&&...);
+  };
+
+void g()
+{
+  S<X, Y>::f(1, '2', 3, 4.); // { dg-warning "'static U0 S<Tpl, VTpl>::f\\(T, Ps&& ...\\) .with T = int; Ps = {char, int, double}; Tpl = X; VTpl = Y; U0 = Y<X<float> >.'" }
+}
diff --git a/gcc/testsuite/g++.dg/diagnostic/default-template-args-5.C b/gcc/testsuite/g++.dg/diagnostic/default-template-args-5.C
new file mode 100644
index 00000000000..0f13466fb27
--- /dev/null
+++ b/gcc/testsuite/g++.dg/diagnostic/default-template-args-5.C
@@ -0,0 +1,12 @@
+// { dg-options "-fpretty-templates" }
+// { dg-do compile { target c++11 } }
+//
+// Tests the case where DECL_TI_TEMPLATE doesn't return a TEMPLATE_DECL for the
+// FUNCTION_DECL of foo<U>(int).
+
+template <class T0, class T1 = int, int x = 1> void foo (int);
+
+template <class U, int y = 2>
+class Q {
+  friend void foo<U> (int = 3); // { dg-error "'void foo<U>\\(int\\)'" }
+};
diff --git a/gcc/testsuite/g++.dg/diagnostic/param-type-mismatch-2.C b/gcc/testsuite/g++.dg/diagnostic/param-type-mismatch-2.C
index de7570a6efa..0aa45404283 100644
--- a/gcc/testsuite/g++.dg/diagnostic/param-type-mismatch-2.C
+++ b/gcc/testsuite/g++.dg/diagnostic/param-type-mismatch-2.C
@@ -141,7 +141,7 @@ int test_7 (int first, const char *second, float third)
                                          |
                                          const char*
      { dg-end-multiline-output "" } */
-  // { dg-message "initializing argument 2 of 'int test_7\\(int, T, float\\) .with T = const char\\*\\*.'" "" { target *-*-* } test_7_decl }
+  // { dg-message "initializing argument 2 of 'int test_7<T>\\(int, T, float\\) .with T = const char\\*\\*.'" "" { target *-*-* } test_7_decl }
   /* { dg-begin-multiline-output "" }
  int test_7 (int one, T two, float three);
                       ~~^~~
diff --git a/gcc/testsuite/g++.dg/ext/pretty1.C b/gcc/testsuite/g++.dg/ext/pretty1.C
index 06608ae30eb..c5bfd6082a7 100644
--- a/gcc/testsuite/g++.dg/ext/pretty1.C
+++ b/gcc/testsuite/g++.dg/ext/pretty1.C
@@ -60,7 +60,7 @@ __assert_fail (const char *cond, const char *file, unsigned int line,
   abort ();
 }
 
-// { dg-final { scan-assembler "int bar\\(T\\).*with T = int" } }
+// { dg-final { scan-assembler "int bar<int>\\(int\\)" } }
 // { dg-final { scan-assembler "top level" } }
 // { dg-final { scan-assembler "int main\\(\\)" } }
 // { dg-final { scan-assembler "int bar\\(T\\).*with T = double" } }
diff --git a/gcc/testsuite/g++.dg/goacc/template.C b/gcc/testsuite/g++.dg/goacc/template.C
index 10d3f446da7..4fcd88bfc56 100644
--- a/gcc/testsuite/g++.dg/goacc/template.C
+++ b/gcc/testsuite/g++.dg/goacc/template.C
@@ -157,12 +157,12 @@ main ()
 }
 
 /* { dg-final { scan-tree-dump-times {(?n)^OpenACC routine '[^']+' has 'nohost' clause\.$} 4 oaccloops } }
-   { dg-final { scan-tree-dump-times {(?n)^OpenACC routine 'T accDouble\(int\) \[with T = char\]' has 'nohost' clause\.$} 1 oaccloops { target { ! offloading_enabled } } } }
+   { dg-final { scan-tree-dump-times {(?n)^OpenACC routine 'T accDouble<T>\(int\) \[with T = char\]' has 'nohost' clause\.$} 1 oaccloops { target { ! offloading_enabled } } } }
    { dg-final { scan-tree-dump-times {(?n)^OpenACC routine 'accDouble<char>\(int\)char' has 'nohost' clause\.$} 1 oaccloops { target offloading_enabled } } }
-   { dg-final { scan-tree-dump-times {(?n)^OpenACC routine 'T accDouble\(int\) \[with T = int\]' has 'nohost' clause\.$} 1 oaccloops { target { ! offloading_enabled } } } }
+   { dg-final { scan-tree-dump-times {(?n)^OpenACC routine 'T accDouble<T>\(int\) \[with T = int\]' has 'nohost' clause\.$} 1 oaccloops { target { ! offloading_enabled } } } }
    { dg-final { scan-tree-dump-times {(?n)^OpenACC routine 'accDouble<int>\(int\)int' has 'nohost' clause\.$} 1 oaccloops { target offloading_enabled } } }
-   { dg-final { scan-tree-dump-times {(?n)^OpenACC routine 'T accDouble\(int\) \[with T = float\]' has 'nohost' clause\.$} 1 oaccloops { target { ! offloading_enabled } } } }
+   { dg-final { scan-tree-dump-times {(?n)^OpenACC routine 'T accDouble<T>\(int\) \[with T = float\]' has 'nohost' clause\.$} 1 oaccloops { target { ! offloading_enabled } } } }
    { dg-final { scan-tree-dump-times {(?n)^OpenACC routine 'accDouble<float>\(int\)float' has 'nohost' clause\.$} 1 oaccloops { target offloading_enabled } } }
-   { dg-final { scan-tree-dump-times {(?n)^OpenACC routine 'T accDouble\(int\) \[with T = double\]' has 'nohost' clause\.$} 1 oaccloops { target { ! offloading_enabled } } } }
+   { dg-final { scan-tree-dump-times {(?n)^OpenACC routine 'T accDouble<T>\(int\) \[with T = double\]' has 'nohost' clause\.$} 1 oaccloops { target { ! offloading_enabled } } } }
    { dg-final { scan-tree-dump-times {(?n)^OpenACC routine 'accDouble<double>\(int\)double' has 'nohost' clause\.$} 1 oaccloops { target offloading_enabled } } }
    TODO See PR101551 for 'offloading_enabled' differences.  */
diff --git a/gcc/testsuite/g++.dg/gomp/declare-variant-7.C b/gcc/testsuite/g++.dg/gomp/declare-variant-7.C
index 7dda899578a..9cb654cb5e6 100644
--- a/gcc/testsuite/g++.dg/gomp/declare-variant-7.C
+++ b/gcc/testsuite/g++.dg/gomp/declare-variant-7.C
@@ -70,6 +70,6 @@ test ()
   s.f12 (0.0);		// { dg-final { scan-tree-dump-times "S<1>::f11<double> \\\(&s, 0.0\\\);" 1 "gimple" } }
   s.f14 (0LL);		// { dg-final { scan-tree-dump-times "S<1>::f13<long long int> \\\(&s, 0\\\);" 1 "gimple" } }
   T<0> t;
-  t.f16 (s);		// { dg-final { scan-tree-dump-times "T<0>::f16<S<1> > \\\(&t, s\\\);" 1 "gimple" } }
-  t.f18 (s);		// { dg-final { scan-tree-dump-times "T<0>::f18<S<1> > \\\(&t, s\\\);" 1 "gimple" } }
+  t.f16 (s);		// { dg-final { scan-tree-dump-times "T<0>::f16 \\\(&t, s\\\);" 1 "gimple" } }
+  t.f18 (s);		// { dg-final { scan-tree-dump-times "T<0>::f18 \\\(&t, s\\\);" 1 "gimple" } }
 }
diff --git a/gcc/testsuite/g++.dg/template/error40.C b/gcc/testsuite/g++.dg/template/error40.C
index c5df56fc1be..16a44d1819f 100644
--- a/gcc/testsuite/g++.dg/template/error40.C
+++ b/gcc/testsuite/g++.dg/template/error40.C
@@ -8,11 +8,11 @@ struct A
 
 void foo(void)
 {
-  A<void> a = 0;		// { dg-error "A<void, 0, 1>" }
+  A<void> a = 0;		// { dg-error "A<void>" }
 }
 
-template <class T> T f(T);	    // { dg-message "int f<int>.int." }
-template <class T> T f(T, int = 0); // { dg-message "" }
+template <class T> T f(T);	    // { dg-message "int f.int." }
+template <class T> T f(T, int = 0); // { dg-message "int f.int, int." }
 
 template <class T>
 struct B
diff --git a/gcc/testsuite/g++.old-deja/g++.ext/pretty3.C b/gcc/testsuite/g++.old-deja/g++.ext/pretty3.C
index 6348ae1ee67..30c7ecd5065 100644
--- a/gcc/testsuite/g++.old-deja/g++.ext/pretty3.C
+++ b/gcc/testsuite/g++.old-deja/g++.ext/pretty3.C
@@ -35,7 +35,7 @@ template<> void f1<int> (int)
   
   if (strcmp (function, "f1<int>"))
     bad = true;
-  if (strcmp (pretty, "void f1(T) [with T = int]"))
+  if (strcmp (pretty, "void f1<int>(int)"))
     bad = true;
 }
 
diff --git a/gcc/testsuite/g++.old-deja/g++.pt/memtemp77.C b/gcc/testsuite/g++.old-deja/g++.pt/memtemp77.C
index 6dd4b546c0c..93dbf4b489f 100644
--- a/gcc/testsuite/g++.old-deja/g++.pt/memtemp77.C
+++ b/gcc/testsuite/g++.old-deja/g++.pt/memtemp77.C
@@ -19,7 +19,7 @@ const char* S3<char>::h(int) { return __PRETTY_FUNCTION__; }
 int main()
 {
   if (strcmp (S3<double>::h(7), 
-	      "static const char* S3<T>::h(U) [with U = int; T = double]") == 0)
+	      "static const char* S3<double>::h(int)") == 0)
     return 0;
   else 
     return 1;

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

* [PATCH 2/2] c++: Print function template parms when relevant [part 2]
  2021-11-26 15:23                     ` [PATCH 0/2] " Matthias Kretz
  2021-11-26 15:24                       ` [PATCH 1/2] c++: Print function template parms when relevant Matthias Kretz
@ 2021-11-26 15:24                       ` Matthias Kretz
  1 sibling, 0 replies; 24+ messages in thread
From: Matthias Kretz @ 2021-11-26 15:24 UTC (permalink / raw)
  To: gcc-patches, jason

[-- Attachment #1: Type: text/plain, Size: 1546 bytes --]



Restore status-quo how function template specializations were diagnosed.

Signed-off-by: Matthias Kretz <m.kretz@gsi.de>

gcc/testsuite/ChangeLog:

	* g++.dg/diagnostic/default-template-args-1.C: Adjust for
	different presentation of function template specializations.
	* g++.dg/ext/pretty1.C: Ditto.
	* g++.old-deja/g++.ext/pretty3.C: Ditto.
	* g++.old-deja/g++.pt/memtemp77.C: Ditto.

gcc/cp/ChangeLog:

	* error.c (dump_function_decl): Pretty print all template
	specializations.
	(dump_function_name): Only print template parms if given a
	function template specialization.
---
 gcc/cp/error.c                                 | 18 ++++++++++--------
 .../diagnostic/default-template-args-1.C       |  4 ++--
 gcc/testsuite/g++.dg/ext/pretty1.C             |  2 +-
 gcc/testsuite/g++.old-deja/g++.ext/pretty3.C   |  2 +-
 gcc/testsuite/g++.old-deja/g++.pt/memtemp77.C  |  2 +-
 5 files changed, 15 insertions(+), 13 deletions(-)


--
──────────────────────────────────────────────────────────────────────────
 Dr. Matthias Kretz                           https://mattkretz.github.io
 GSI Helmholtz Centre for Heavy Ion Research               https://gsi.de
 stdₓ::simd
──────────────────────────────────────────────────────────────────────────

[-- Attachment #2: 0002-c-Print-function-template-parms-when-relevant-part-2.patch --]
[-- Type: text/x-patch, Size: 5217 bytes --]

diff --git a/gcc/cp/error.c b/gcc/cp/error.c
index fb62adbeb0b..9365e42dd76 100644
--- a/gcc/cp/error.c
+++ b/gcc/cp/error.c
@@ -1723,9 +1723,8 @@ dump_function_decl (cxx_pretty_printer *pp, tree t, int flags)
   tree specialized_t = t;
   int specialized_flags = 0;
 
-  /* Pretty print only template instantiations. Don't pretty print explicit
-     specializations like 'template <> void fun<int> (int)'.  */
-  if (DECL_TEMPLATE_INSTANTIATION (t) && DECL_TEMPLATE_INFO (t)
+  /* Pretty print template instantiations only.  */
+  if (DECL_USE_TEMPLATE (t) && DECL_TEMPLATE_INFO (t)
       && !(flags & TFF_NO_TEMPLATE_BINDINGS)
       && flag_pretty_templates)
     {
@@ -1989,9 +1988,11 @@ dump_function_name (cxx_pretty_printer *pp, tree t, int flags)
   dump_module_suffix (pp, t);
 
 /* Print function template parameters if:
-   1. t is template, and
-   2. the caller didn't request to only print the template-name, and
-   3. t actually has template parameters that are not indirect parameters from
+   1. t is a template, and
+   2. t is a specialization of a template (so that __FUNCTION__ of
+      'template <class T> void f(T)' is 'f' and not 'f<T>'), and
+   3. the caller didn't request to only print the template-name, and
+   4. t actually has template parameters that are not indirect parameters from
       enclosing scopes, i.e. either
       - t is a friend template specialization
 	(eg. template<class T> struct X { friend void foo<T>(int); }; since
@@ -1999,7 +2000,7 @@ dump_function_name (cxx_pretty_printer *pp, tree t, int flags)
 	before PRIMARY_TEMPLATE_P is safe to call), or
       - t is a primary template (own template header),
       and
-   4. either
+   5. either
       - flags requests to show no function arguments, in which case deduced
 	types could be hidden and thus need to be printed, or
       - at least one function template argument was given explicitly and the
@@ -2009,6 +2010,7 @@ dump_function_name (cxx_pretty_printer *pp, tree t, int flags)
   dump_template_parms.
  */
   if (DECL_TEMPLATE_INFO (t)
+      && DECL_USE_TEMPLATE (t)
       && !(flags & TFF_TEMPLATE_NAME)
       && (TREE_CODE (DECL_TI_TEMPLATE (t)) != TEMPLATE_DECL
 	    || PRIMARY_TEMPLATE_P (DECL_TI_TEMPLATE (t)))
@@ -2016,7 +2018,7 @@ dump_function_name (cxx_pretty_printer *pp, tree t, int flags)
 	    || (DECL_TI_ARGS (t)
 		  && EXPLICIT_TEMPLATE_ARGS_P (INNERMOST_TEMPLATE_ARGS
 						 (DECL_TI_ARGS (t))))))
-    dump_template_parms (pp, DECL_TEMPLATE_INFO (t), !DECL_USE_TEMPLATE (t), flags);
+    dump_template_parms (pp, DECL_TEMPLATE_INFO (t), false, flags);
 }
 
 /* Dump the template parameters from the template info INFO under control of
diff --git a/gcc/testsuite/g++.dg/diagnostic/default-template-args-1.C b/gcc/testsuite/g++.dg/diagnostic/default-template-args-1.C
index 6481798a69b..1b82ea46bb5 100644
--- a/gcc/testsuite/g++.dg/diagnostic/default-template-args-1.C
+++ b/gcc/testsuite/g++.dg/diagnostic/default-template-args-1.C
@@ -43,8 +43,8 @@ int main()
   f3<float>(1);    // { dg-warning "'void f3<a>\\(a\\) .with a = float.'" }
   f3<float, 2>(1); // { dg-warning "'void f3<a, b>\\(a\\) .with a = float; int b = 2.'" }
   f4(1.);          // { dg-warning "'void f4\\(a\\) .with a = double.'" }
-  f4(1);           // { dg-warning "'void f4<int>\\(int\\)'" }
-  f4(1.f);         // { dg-warning "'void f4\\(float\\)'" }
+  f4(1);           // { dg-warning "'void f4<a>\\(a\\) .with a = int.'" }
+  f4(1.f);         // { dg-warning "'void f4\\(a\\) .with a = float.'" }
 
   f0(0); // { dg-error "" }
   f1(0); // { dg-error "" }
diff --git a/gcc/testsuite/g++.dg/ext/pretty1.C b/gcc/testsuite/g++.dg/ext/pretty1.C
index c5bfd6082a7..07cc9a6f2cd 100644
--- a/gcc/testsuite/g++.dg/ext/pretty1.C
+++ b/gcc/testsuite/g++.dg/ext/pretty1.C
@@ -60,7 +60,7 @@ __assert_fail (const char *cond, const char *file, unsigned int line,
   abort ();
 }
 
-// { dg-final { scan-assembler "int bar<int>\\(int\\)" } }
+// { dg-final { scan-assembler "int bar<T>\\(T\\).*with T = int" } }
 // { dg-final { scan-assembler "top level" } }
 // { dg-final { scan-assembler "int main\\(\\)" } }
 // { dg-final { scan-assembler "int bar\\(T\\).*with T = double" } }
diff --git a/gcc/testsuite/g++.old-deja/g++.ext/pretty3.C b/gcc/testsuite/g++.old-deja/g++.ext/pretty3.C
index 30c7ecd5065..6c82f5b6061 100644
--- a/gcc/testsuite/g++.old-deja/g++.ext/pretty3.C
+++ b/gcc/testsuite/g++.old-deja/g++.ext/pretty3.C
@@ -35,7 +35,7 @@ template<> void f1<int> (int)
   
   if (strcmp (function, "f1<int>"))
     bad = true;
-  if (strcmp (pretty, "void f1<int>(int)"))
+  if (strcmp (pretty, "void f1<T>(T) [with T = int]"))
     bad = true;
 }
 
diff --git a/gcc/testsuite/g++.old-deja/g++.pt/memtemp77.C b/gcc/testsuite/g++.old-deja/g++.pt/memtemp77.C
index 93dbf4b489f..6dd4b546c0c 100644
--- a/gcc/testsuite/g++.old-deja/g++.pt/memtemp77.C
+++ b/gcc/testsuite/g++.old-deja/g++.pt/memtemp77.C
@@ -19,7 +19,7 @@ const char* S3<char>::h(int) { return __PRETTY_FUNCTION__; }
 int main()
 {
   if (strcmp (S3<double>::h(7), 
-	      "static const char* S3<double>::h(int)") == 0)
+	      "static const char* S3<T>::h(U) [with U = int; T = double]") == 0)
     return 0;
   else 
     return 1;

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

* Re: [PATCH v2 1/2] c++: Print function template parms when relevant
  2021-11-26 15:24                       ` [PATCH 1/2] c++: Print function template parms when relevant Matthias Kretz
@ 2021-12-02  8:35                         ` Matthias Kretz
  0 siblings, 0 replies; 24+ messages in thread
From: Matthias Kretz @ 2021-12-02  8:35 UTC (permalink / raw)
  To: gcc-patches, jason

[-- Attachment #1: Type: text/plain, Size: 8351 bytes --]

On Friday, 26 November 2021 16:23:43 CET Matthias Kretz wrote:
> > Hmm, since it walks DECL_TEMPLATE_RESULT, I wouldn't expect it to find
> > template parms that aren't in the function signature.
> 
> You were right, walking TREE_TYPE (DECL_TEMPLATE_RESULT (t)) does what I
> need.

... for FUNCTION_TYPE, but not for METHOD_TYPE. Also, even though I did 
regression testing, I found a few minor issues while looking for new 
regressions in the diagnose_as patch. Therefore, the revised patch (part 1):

--------------------------

The choice when to print a function template parameter was still
suboptimal. That's because sometimes the function template parameter
list only adds noise, while in other situations the lack of a function
template parameter list makes diagnostic messages hard to understand.

The general idea of this change is to print template parms wherever they
would appear in the source code as well. Thus, the diagnostics code
needs to know whether any template parameter was given explicitly.

DWARF names of functions use dump_function_name to produce a name
without function arguments (function arguments are printed from
dump_function_decl). However, FLAGS should still state the intent of
printing a name without function arguments (TFF_NO_FUNCTION_ARGUMENTS so
that dump_function_name can decide correctly whether to call
dump_template_parms.

Based on an initial patch from Jason Merrill <jason@redhat.com>.

Signed-off-by: Matthias Kretz <m.kretz@gsi.de>

gcc/testsuite/ChangeLog:

        * g++.dg/debug/dwarf2/template-params-12n.C: Optionally, allow
        DW_AT_default_value.
        * g++.dg/diagnostic/default-template-args-1.C: New test.
        * g++.dg/diagnostic/default-template-args-2.C: New test.
        * g++.dg/diagnostic/default-template-args-3.C: New test.
        * g++.dg/diagnostic/default-template-args-4.C: New test.
        * g++.dg/diagnostic/default-template-args-5.C: New test.
        * g++.dg/diagnostic/param-type-mismatch-2.C: Expect template
        parms in diagnostic.
        * g++.dg/ext/pretty1.C: Expect function template specialization
        to not pretty-print template parms.
        * g++.old-deja/g++.ext/pretty3.C: Ditto.
        * g++.old-deja/g++.pt/memtemp77.C: Ditto.
        * g++.dg/goacc/template.C: Expect function template parms for
        explicit arguments.
        * g++.dg/template/error40.C: Expect only non-default template
        arguments in diagnostic.

gcc/cp/ChangeLog:

        * constraint.cc (get_mapped_args): Remove incorrect non-default
        args count on multi-level template args; instead set the
        non-default args count on each inner TREE_VEC.
        * cp-tree.h: Rewrite NON_DEFAULT_TEMPLATE_ARGS_COUNT
        implementation to store the number explicitly specified
        arguments in a TREE_LIST.
        (EXPLICIT_TEMPLATE_ARGS_P): New.
        (SET_EXPLICIT_TEMPLATE_ARGS_P): New.
        (set_non_default_template_args_count): New declaration.
        (get_non_default_template_args_count): New declaration.
        (set_explicit_template_args_count): New declaration.
        (get_explicit_template_args_count): New declaration.
        (TFF_AS_PRIMARY): New constant.
        * decl.c (grokfndecl): Mark all template arguments in a friend
        declaration as explicitly specified.
        * error.c (args_or_non_default_template_args_count): Renamed
        from get_non_default_template_args_count (name-clash). Make
        independent of flag_pretty_templates.
        (dump_template_bindings): Add flags parameter to be passed to
        get_non_default_template_args_count. Print only non-default
        template arguments. Add used_parms parameter: print defaulted
        template bindings if the parameter name is part of used_parms.
        (dump_substitution): Walk the function_decl to find all used
        template parameters. Non-static member functions need a special
        case to not find all template parameters of enclosing scopes.
        (dump_function_decl): Call dump_function_name and dump_type of
        the DECL_CONTEXT with specialized template and set
        TFF_AS_PRIMARY for their flags. Don't print template arguments.
        dump_function_name already does so.
        (dump_function_name): Add and document conditions for calling
        dump_template_parms. Move DECL_USE_TEMPLATE to PRIMARY parameter
        of dump_template_parms.
        (dump_template_parms): Print only non-default template
        parameters.
        (lang_decl_name): Add TFF_NO_FUNCTION_ARGUMENTS to
        dump_function_name flags.
        * module.cc (trees_out::core_vals): Accept TREE_LISTs in
        TREE_CHAIN of TREE_VEC.
        * pt.c (set_non_default_template_args_count): New function.
        (get_non_default_template_args_count): New function.
        (set_explicit_template_args_count): New function.
        (get_explicit_template_args_count): New function.
        (expand_template_argument_pack): Always adjust and set
        the adjusted non-default args count.
        (template_parm_to_arg): SET_NON_DEFAULT_TEMPLATE_ARGS_COUNT
        independent of CHECKING_P.
        (coerce_template_parameter_pack): Ditto.
        (coerce_template_parms): Ditto.
        (fn_type_unification): Ditto.
        (type_unification_real): Ditto.
        (get_partial_spec_bindings): Ditto. And, set non-default
        template args count on inner args.
        (determine_specialization): Also copy the inner TREE_VECs.
        (template_parms_level_to_args): Always count non-default args.
        (copy_template_args): Only copy the non-default template args
        count on TREE_VECs that should have it.
        (fn_type_unification): Set EXPLICIT_TEMPLATE_ARGS_P on the
        template arguments tree if any template parameter was explicitly
        given.
        (type_unification_real): Count non-default args sooner.
        (tsubst_template_args): Take a shortcut for multi-level args to
        avoid a lot of unnecessary checks and simplify non-default args
        counting. Fix the count of non-default template arguments.
---
 gcc/cp/constraint.cc                          |   3 +-
 gcc/cp/cp-tree.h                              |  36 ++-
 gcc/cp/decl.c                                 |   4 +
 gcc/cp/error.c                                | 214 ++++++++++------
 gcc/cp/module.cc                              |   4 +-
 gcc/cp/pt.c                                   | 231 ++++++++++++++----
 .../g++.dg/debug/dwarf2/template-params-12n.C |   2 +-
 .../diagnostic/default-template-args-1.C      |  61 +++++
 .../diagnostic/default-template-args-2.C      |  37 +++
 .../diagnostic/default-template-args-3.C      |  29 +++
 .../diagnostic/default-template-args-4.C      |  24 ++
 .../diagnostic/default-template-args-5.C      |  12 +
 .../g++.dg/diagnostic/param-type-mismatch-2.C |   2 +-
 gcc/testsuite/g++.dg/ext/pretty1.C            |   2 +-
 gcc/testsuite/g++.dg/goacc/template.C         |   8 +-
 gcc/testsuite/g++.dg/template/error40.C       |   6 +-
 gcc/testsuite/g++.old-deja/g++.ext/pretty3.C  |   2 +-
 gcc/testsuite/g++.old-deja/g++.pt/memtemp77.C |   2 +-
 18 files changed, 527 insertions(+), 152 deletions(-)
 create mode 100644 gcc/testsuite/g++.dg/diagnostic/default-template-args-1.C
 create mode 100644 gcc/testsuite/g++.dg/diagnostic/default-template-args-2.C
 create mode 100644 gcc/testsuite/g++.dg/diagnostic/default-template-args-3.C
 create mode 100644 gcc/testsuite/g++.dg/diagnostic/default-template-args-4.C
 create mode 100644 gcc/testsuite/g++.dg/diagnostic/default-template-args-5.C


--
──────────────────────────────────────────────────────────────────────────
 Dr. Matthias Kretz                           https://mattkretz.github.io
 GSI Helmholtz Centre for Heavy Ion Research               https://gsi.de
 stdₓ::simd
──────────────────────────────────────────────────────────────────────────

[-- Attachment #2: 0001-c-Print-function-template-parms-when-relevant.patch --]
[-- Type: text/x-patch, Size: 48225 bytes --]

diff --git a/gcc/cp/constraint.cc b/gcc/cp/constraint.cc
index 2896efdd7f2..17dce107491 100644
--- a/gcc/cp/constraint.cc
+++ b/gcc/cp/constraint.cc
@@ -2890,10 +2890,11 @@ get_mapped_args (tree map)
       tree level = make_tree_vec (list.length ());
       for (unsigned j = 0; j < list.length(); ++j)
 	TREE_VEC_ELT (level, j) = list[j];
+      /* None of the args at any level are defaulted.  */
+      SET_NON_DEFAULT_TEMPLATE_ARGS_COUNT (level, list.length());
       SET_TMPL_ARGS_LEVEL (args, i + 1, level);
       list.release ();
     }
-  SET_NON_DEFAULT_TEMPLATE_ARGS_COUNT (args, 0);
 
   return args;
 }
diff --git a/gcc/cp/cp-tree.h b/gcc/cp/cp-tree.h
index 1ee2c57e83c..97d5efec2e1 100644
--- a/gcc/cp/cp-tree.h
+++ b/gcc/cp/cp-tree.h
@@ -3689,25 +3689,29 @@ struct GTY(()) lang_decl {
   ((struct tree_template_info*)TEMPLATE_INFO_CHECK (NODE))->args
 #define TI_PENDING_TEMPLATE_FLAG(NODE) \
   TREE_LANG_FLAG_1 (TEMPLATE_INFO_CHECK (NODE))
-/* For a given TREE_VEC containing a template argument list,
-   this property contains the number of arguments that are not
-   defaulted.  */
+/* For a given TREE_VEC containing a template argument list (but not multiple
+   levels of arguments), this property contains the number of arguments that are
+   not defaulted and optionally the number of explicitly specified template
+   arguments. It is either a INT_CST denoting the number of non-default
+   arguments, or a TREE_LIST with TREE_PURPOSE denoting the number of explicitly
+   given template arguments of a function template, and TREE_VALUE denoting the
+   number of non-default arguments.  */
 #define NON_DEFAULT_TEMPLATE_ARGS_COUNT(NODE) \
   TREE_CHAIN (TREE_VEC_CHECK (NODE))
 
 /* Below are the setter and getter of the NON_DEFAULT_TEMPLATE_ARGS_COUNT
    property.  */
 #define SET_NON_DEFAULT_TEMPLATE_ARGS_COUNT(NODE, INT_VALUE) \
-  NON_DEFAULT_TEMPLATE_ARGS_COUNT(NODE) = build_int_cst (NULL_TREE, INT_VALUE)
-#if CHECKING_P
-#define GET_NON_DEFAULT_TEMPLATE_ARGS_COUNT(NODE) \
-    int_cst_value (NON_DEFAULT_TEMPLATE_ARGS_COUNT (NODE))
-#else
+  set_non_default_template_args_count (TREE_VEC_CHECK (NODE), INT_VALUE)
+
 #define GET_NON_DEFAULT_TEMPLATE_ARGS_COUNT(NODE) \
-  NON_DEFAULT_TEMPLATE_ARGS_COUNT (NODE) \
-  ? int_cst_value (NON_DEFAULT_TEMPLATE_ARGS_COUNT (NODE)) \
-  : TREE_VEC_LENGTH (INNERMOST_TEMPLATE_ARGS (NODE))
-#endif
+  get_non_default_template_args_count (TREE_VEC_CHECK (NODE))
+
+#define EXPLICIT_TEMPLATE_ARGS_P(NODE) \
+  (get_explicit_template_args_count (TREE_VEC_CHECK (NODE)) > 0)
+
+#define SET_EXPLICIT_TEMPLATE_ARGS_COUNT(NODE, INT_VALUE) \
+  set_explicit_template_args_count (TREE_VEC_CHECK (NODE), INT_VALUE)
 
 /* The list of access checks that were deferred during parsing
    which need to be performed at template instantiation time.
@@ -5949,7 +5953,8 @@ enum auto_deduction_context
        identical to their defaults.
    TFF_NO_TEMPLATE_BINDINGS: do not print information about the template
        arguments for a function template specialization.
-   TFF_POINTER: we are printing a pointer type.  */
+   TFF_POINTER: we are printing a pointer type.
+   TFF_AS_PRIMARY: show the template like a primary template.  */
 
 #define TFF_PLAIN_IDENTIFIER			(0)
 #define TFF_SCOPE				(1)
@@ -5967,6 +5972,7 @@ enum auto_deduction_context
 #define TFF_NO_OMIT_DEFAULT_TEMPLATE_ARGUMENTS	(1 << 12)
 #define TFF_NO_TEMPLATE_BINDINGS		(1 << 13)
 #define TFF_POINTER		                (1 << 14)
+#define TFF_AS_PRIMARY		                (1 << 15)
 
 /* These constants can be used as bit flags to control strip_typedefs.
 
@@ -7171,6 +7177,10 @@ extern void pop_access_scope			(tree);
 extern bool check_template_shadow		(tree);
 extern bool check_auto_in_tmpl_args             (tree, tree);
 extern tree get_innermost_template_args		(tree, int);
+extern void set_non_default_template_args_count (tree args, int count);
+extern int  get_non_default_template_args_count (tree args);
+extern void set_explicit_template_args_count (tree args, int count);
+extern bool get_explicit_template_args_count (tree args);
 extern void maybe_begin_member_template_processing (tree);
 extern void maybe_end_member_template_processing (void);
 extern tree finish_member_template_decl		(tree);
diff --git a/gcc/cp/decl.c b/gcc/cp/decl.c
index 56f80775ca0..24d6ee51db7 100644
--- a/gcc/cp/decl.c
+++ b/gcc/cp/decl.c
@@ -9968,6 +9968,10 @@ grokfndecl (tree ctype,
 	  /* A friend declaration of the form friend void f<>().  Record
 	     the information in the TEMPLATE_ID_EXPR.  */
 	  SET_DECL_IMPLICIT_INSTANTIATION (decl);
+	  /* Set the template args as explicitly specified - they were certainly
+	     not deduced from function arguments.  */
+	  if (args)
+	    SET_EXPLICIT_TEMPLATE_ARGS_COUNT (args, NUM_TMPL_ARGS (args));
 
 	  gcc_assert (identifier_p (fns) || OVL_P (fns));
 	  DECL_TEMPLATE_INFO (decl) = build_template_info (fns, args);
diff --git a/gcc/cp/error.c b/gcc/cp/error.c
index 98c1f0e4bdf..5b5e67e6963 100644
--- a/gcc/cp/error.c
+++ b/gcc/cp/error.c
@@ -86,11 +86,11 @@ static void dump_exception_spec (cxx_pretty_printer *, tree, int);
 static void dump_template_argument (cxx_pretty_printer *, tree, int);
 static void dump_template_argument_list (cxx_pretty_printer *, tree, int);
 static void dump_template_parameter (cxx_pretty_printer *, tree, int);
-static void dump_template_bindings (cxx_pretty_printer *, tree, tree,
-                                    vec<tree, va_gc> *);
+static void dump_template_bindings (cxx_pretty_printer *, tree, tree, tree,
+                                    vec<tree, va_gc> *, int);
 static void dump_scope (cxx_pretty_printer *, tree, int);
 static void dump_template_parms (cxx_pretty_printer *, tree, int, int);
-static int get_non_default_template_args_count (tree, int);
+static int args_or_non_default_template_args_count (tree, int);
 static const char *function_category (tree);
 static void maybe_print_constexpr_context (diagnostic_context *);
 static void maybe_print_instantiation_context (diagnostic_context *);
@@ -272,24 +272,23 @@ dump_template_argument (cxx_pretty_printer *pp, tree arg, int flags)
     }
 }
 
-/* Count the number of template arguments ARGS whose value does not
-   match the (optional) default template parameter in PARAMS  */
+/* Returns GET_NON_DEFAULT_TEMPLATE_ARGS_COUNT (ARGS) unless FLAGS requests the
+   full count of template args.  */
 
 static int
-get_non_default_template_args_count (tree args, int flags)
+args_or_non_default_template_args_count (tree args, int flags)
 {
-  int n = TREE_VEC_LENGTH (INNERMOST_TEMPLATE_ARGS (args));
+  gcc_assert (!TMPL_ARGS_HAVE_MULTIPLE_LEVELS (args));
 
   if (/* We use this flag when generating debug information.  We don't
 	 want to expand templates at this point, for this may generate
 	 new decls, which gets decl counts out of sync, which may in
 	 turn cause codegen differences between compilations with and
 	 without -g.  */
-      (flags & TFF_NO_OMIT_DEFAULT_TEMPLATE_ARGUMENTS) != 0
-      || !flag_pretty_templates)
-    return n;
+      (flags & TFF_NO_OMIT_DEFAULT_TEMPLATE_ARGUMENTS) != 0)
+    return NUM_TMPL_ARGS (args);
 
-  return GET_NON_DEFAULT_TEMPLATE_ARGS_COUNT (INNERMOST_TEMPLATE_ARGS (args));
+  return GET_NON_DEFAULT_TEMPLATE_ARGS_COUNT (args);
 }
 
 /* Dump a template-argument-list ARGS (always a TREE_VEC) under control
@@ -298,7 +297,7 @@ get_non_default_template_args_count (tree args, int flags)
 static void
 dump_template_argument_list (cxx_pretty_printer *pp, tree args, int flags)
 {
-  int n = get_non_default_template_args_count (args, flags);
+  int n = args_or_non_default_template_args_count (args, flags);
   int need_comma = 0;
   int i;
 
@@ -369,7 +368,7 @@ dump_template_parameter (cxx_pretty_printer *pp, tree parm, int flags)
 
 static void
 dump_template_bindings (cxx_pretty_printer *pp, tree parms, tree args,
-                        vec<tree, va_gc> *typenames)
+			tree used_parms, vec<tree, va_gc> *typenames, int flags)
 {
   /* Print "[with" and ']', conditional on whether anything is printed at all.
      This is tied to whether a semicolon is needed to separate multiple template
@@ -414,23 +413,47 @@ dump_template_bindings (cxx_pretty_printer *pp, tree parms, tree args,
       /* Don't crash if we had an invalid argument list.  */
       if (TMPL_ARGS_DEPTH (args) >= lvl)
 	lvl_args = TMPL_ARGS_LEVEL (args, lvl);
+      const int len = TREE_VEC_LENGTH (p);
+      const int non_default_len
+	= lvl_args ? args_or_non_default_template_args_count (lvl_args, flags)
+		   : len;
 
-      for (i = 0; i < TREE_VEC_LENGTH (p); ++i)
+      for (i = 0; i < len; ++i, ++arg_idx)
 	{
+	  const tree parm_i = TREE_VEC_ELT (p, i);
+	  gcc_assert (TREE_CODE (parm_i) == TREE_LIST);
+	  const tree parm_val = TREE_VALUE (parm_i);
+	  const tree parm_type = TREE_CODE (parm_val) == TYPE_DECL
+				   || TREE_CODE (parm_val) == TEMPLATE_DECL
+				   ? TREE_TYPE (parm_val)
+				   : TREE_CODE (parm_val) == PARM_DECL
+				       ? DECL_ARG_TYPE (parm_val)
+				       : NULL_TREE;
+
+	  /* If the template parameter is defaulted and does not appear in
+	     used_parms (function arguments, return type, or exception
+	     specifier), skip the parameter.  */
+	  if (i >= non_default_len)
+	    {
+	      tree it;
+	      for (it = used_parms; it && TREE_VALUE (it) != parm_type;
+		   it = TREE_CHAIN (it))
+		;
+	      if (!it)
+		continue;
+	    }
+
 	  tree arg = NULL_TREE;
 
 	  /* Don't crash if we had an invalid argument list.  */
 	  if (lvl_args && NUM_TMPL_ARGS (lvl_args) > arg_idx)
 	    arg = TREE_VEC_ELT (lvl_args, arg_idx);
 
-	  tree parm_i = TREE_VEC_ELT (p, i);
 	  /* If the template argument repeats the template parameter (T = T),
 	     skip the parameter.*/
 	  if (arg && TREE_CODE (arg) == TEMPLATE_TYPE_PARM
-		&& TREE_CODE (parm_i) == TREE_LIST
-		&& TREE_CODE (TREE_VALUE (parm_i)) == TYPE_DECL
-		&& TREE_CODE (TREE_TYPE (TREE_VALUE (parm_i)))
-		     == TEMPLATE_TYPE_PARM
+		&& TREE_CODE (parm_val) == TYPE_DECL
+		&& TREE_CODE (TREE_TYPE (parm_val)) == TEMPLATE_TYPE_PARM
 		&& DECL_NAME (TREE_VALUE (parm_i))
 		     == DECL_NAME (TREE_CHAIN (arg)))
 	    continue;
@@ -450,8 +473,6 @@ dump_template_bindings (cxx_pretty_printer *pp, tree parms, tree args,
 	    }
 	  else
 	    pp_string (pp, M_("<missing>"));
-
-	  ++arg_idx;
 	}
 
       parms = TREE_CHAIN (parms);
@@ -1634,8 +1655,30 @@ dump_substitution (cxx_pretty_printer *pp,
   if (template_parms != NULL_TREE && template_args != NULL_TREE
       && !(flags & TFF_NO_TEMPLATE_BINDINGS))
     {
-      vec<tree, va_gc> *typenames = t ? find_typenames (t) : NULL;
-      dump_template_bindings (pp, template_parms, template_args, typenames);
+      vec<tree, va_gc> *typenames = nullptr;
+      tree used_parms = NULL_TREE;
+      if (t)
+	{
+	  typenames = find_typenames (t);
+	  const tree fn = TREE_TYPE (DECL_TEMPLATE_RESULT (t));
+	  if (TREE_CODE (fn) == METHOD_TYPE)
+	    {
+	      /* For methods, we have to skip walking TYPE_METHOD_BASETYPE and
+		 the hidden "self" function parameter. Otherwise, we'll discover
+		 all template parameters of class templates in the function's
+		 context.  */
+	      used_parms = find_template_parameters (TREE_TYPE (fn),
+						     template_parms);
+	      for (tree arg = TREE_CHAIN (TYPE_ARG_TYPES (fn)); arg;
+		   arg = TREE_CHAIN (arg))
+		used_parms = chainon (used_parms, find_template_parameters
+						    (arg, template_parms));
+	    }
+	  else
+	    used_parms = find_template_parameters (fn, template_parms);
+	}
+      dump_template_bindings (pp, template_parms, template_args, used_parms,
+			      typenames, flags);
     }
 }
 
@@ -1689,8 +1732,15 @@ dump_function_decl (cxx_pretty_printer *pp, tree t, int flags)
   constexpr_p = (DECL_DECLARED_CONSTEXPR_P (t)
 		 && !decl_implicit_constexpr_p (t));
 
-  /* Pretty print template instantiations only.  */
-  if (DECL_USE_TEMPLATE (t) && DECL_TEMPLATE_INFO (t)
+  /* Keep t before the following branch makes t point to a more general
+     template. Without the specialized template, the information about defaulted
+     template arguments is lost.  */
+  tree specialized_t = t;
+  int specialized_flags = 0;
+
+  /* Pretty print only template instantiations. Don't pretty print explicit
+     specializations like 'template <> void fun<int> (int)'.  */
+  if (DECL_TEMPLATE_INSTANTIATION (t) && DECL_TEMPLATE_INFO (t)
       && !(flags & TFF_NO_TEMPLATE_BINDINGS)
       && flag_pretty_templates)
     {
@@ -1702,6 +1752,9 @@ dump_function_decl (cxx_pretty_printer *pp, tree t, int flags)
 	{
 	  template_parms = DECL_TEMPLATE_PARMS (tmpl);
 	  t = tmpl;
+	  /* The "[with ...]" clause is printed, thus dump functions printing
+	     SPECIALIZED_T need to add TFF_AS_PRIMARY to their flags.  */
+	  specialized_flags = TFF_AS_PRIMARY;
 	}
     }
 
@@ -1711,8 +1764,8 @@ dump_function_decl (cxx_pretty_printer *pp, tree t, int flags)
   fntype = TREE_TYPE (t);
   parmtypes = FUNCTION_FIRST_USER_PARMTYPE (t);
 
-  if (DECL_CLASS_SCOPE_P (t))
-    cname = DECL_CONTEXT (t);
+  if (DECL_CLASS_SCOPE_P (specialized_t))
+    cname = DECL_CONTEXT (specialized_t);
   /* This is for partially instantiated template methods.  */
   else if (TREE_CODE (fntype) == METHOD_TYPE)
     cname = TREE_TYPE (TREE_VALUE (parmtypes));
@@ -1750,13 +1803,14 @@ dump_function_decl (cxx_pretty_printer *pp, tree t, int flags)
     /* Nothing.  */;
   else if (cname)
     {
-      dump_type (pp, cname, flags);
+      dump_type (pp, cname, flags | specialized_flags);
       pp_cxx_colon_colon (pp);
     }
   else
     dump_scope (pp, CP_DECL_CONTEXT (t), flags);
 
-  dump_function_name (pp, t, dump_function_name_flags);
+  dump_function_name (pp, specialized_t,
+		      dump_function_name_flags | specialized_flags);
 
   if (!(flags & TFF_NO_FUNCTION_ARGUMENTS))
     {
@@ -1803,26 +1857,6 @@ dump_function_decl (cxx_pretty_printer *pp, tree t, int flags)
 	  pp_character (pp, ']');
 	}
     }
-  else if (template_args)
-    {
-      bool need_comma = false;
-      int i;
-      pp_cxx_begin_template_argument_list (pp);
-      template_args = INNERMOST_TEMPLATE_ARGS (template_args);
-      for (i = 0; i < TREE_VEC_LENGTH (template_args); ++i)
-	{
-	  tree arg = TREE_VEC_ELT (template_args, i);
-	  if (need_comma)
-	    pp_separate_with_comma (pp);
-	  if (ARGUMENT_PACK_P (arg))
-	    pp_cxx_left_brace (pp);
-	  dump_template_argument (pp, arg, TFF_PLAIN_IDENTIFIER);
-	  if (ARGUMENT_PACK_P (arg))
-	    pp_cxx_right_brace (pp);
-	  need_comma = true;
-	}
-      pp_cxx_end_template_argument_list (pp);
-    }
 }
 
 /* Print a parameter list. If this is for a member function, the
@@ -1969,13 +2003,35 @@ dump_function_name (cxx_pretty_printer *pp, tree t, int flags)
 
   dump_module_suffix (pp, t);
 
+/* Print function template parameters if:
+   1. t is template, and
+   2. the caller didn't request to only print the template-name, and
+   3. t actually has template parameters that are not indirect parameters from
+      enclosing scopes, i.e. either
+      - t is a friend template specialization
+	(eg. template<class T> struct X { friend void foo<T>(int); }; since
+	PRIMARY_TEMPLATE_P requires a TEMPLATE_DECL, this case must be checked
+	before PRIMARY_TEMPLATE_P is safe to call), or
+      - t is a primary template (own template header),
+      and
+   4. either
+      - flags requests to show no function arguments, in which case deduced
+	types could be hidden and thus need to be printed, or
+      - at least one function template argument was given explicitly and the
+	diagnostics output should reflect the code as it was written,
+
+  Whether t is a specialization of a template informs the PRIMARY parameter of
+  dump_template_parms.
+ */
   if (DECL_TEMPLATE_INFO (t)
       && !(flags & TFF_TEMPLATE_NAME)
-      && !DECL_FRIEND_PSEUDO_TEMPLATE_INSTANTIATION (t)
       && (TREE_CODE (DECL_TI_TEMPLATE (t)) != TEMPLATE_DECL
-	  || PRIMARY_TEMPLATE_P (DECL_TI_TEMPLATE (t))))
-    dump_template_parms (pp, DECL_TEMPLATE_INFO (t), !DECL_USE_TEMPLATE (t),
-                         flags);
+	    || PRIMARY_TEMPLATE_P (DECL_TI_TEMPLATE (t)))
+      && ((flags & TFF_NO_FUNCTION_ARGUMENTS)
+	    || (DECL_TI_ARGS (t)
+		  && EXPLICIT_TEMPLATE_ARGS_P (INNERMOST_TEMPLATE_ARGS
+						 (DECL_TI_ARGS (t))))))
+    dump_template_parms (pp, DECL_TEMPLATE_INFO (t), !DECL_USE_TEMPLATE (t), flags);
 }
 
 /* Dump the template parameters from the template info INFO under control of
@@ -1990,6 +2046,8 @@ dump_template_parms (cxx_pretty_printer *pp, tree info,
 {
   tree args = info ? TI_ARGS (info) : NULL_TREE;
 
+  if (flags & TFF_AS_PRIMARY)
+    primary = true;
   if (primary && flags & TFF_TEMPLATE_NAME)
     return;
   flags &= ~(TFF_CLASS_KEY_OR_ENUM | TFF_TEMPLATE_NAME);
@@ -1999,10 +2057,11 @@ dump_template_parms (cxx_pretty_printer *pp, tree info,
      to crash producing error messages.  */
   if (args && !primary)
     {
-      int len, ix;
-      len = get_non_default_template_args_count (args, flags);
+      int ix;
 
       args = INNERMOST_TEMPLATE_ARGS (args);
+      int len = args_or_non_default_template_args_count (args, flags);
+      gcc_assert (len <= NUM_TMPL_ARGS (args));
       for (ix = 0; ix != len; ix++)
 	{
 	  tree arg = TREE_VEC_ELT (args, ix);
@@ -2027,25 +2086,35 @@ dump_template_parms (cxx_pretty_printer *pp, tree info,
       tree parms = DECL_TEMPLATE_PARMS (tpl);
       int len, ix;
 
-      parms = TREE_CODE (parms) == TREE_LIST ? TREE_VALUE (parms) : NULL_TREE;
-      len = parms ? TREE_VEC_LENGTH (parms) : 0;
-
-      for (ix = 0; ix != len; ix++)
+      if (TREE_CODE (parms) == TREE_LIST)
 	{
-	  tree parm;
+	  parms = INNERMOST_TEMPLATE_PARMS (parms);
+	  if (args)
+	    {
+	      len = args_or_non_default_template_args_count
+		      (INNERMOST_TEMPLATE_ARGS (args), flags);
+	      gcc_assert (len <= TREE_VEC_LENGTH (parms));
+	    }
+	  else
+	    len = TREE_VEC_LENGTH (parms);
 
-          if (TREE_VEC_ELT (parms, ix) == error_mark_node)
-            {
-              pp_string (pp, M_("<template parameter error>"));
-              continue;
-            }
+	  for (ix = 0; ix != len; ix++)
+	    {
+	      tree parm;
+
+	      if (TREE_VEC_ELT (parms, ix) == error_mark_node)
+		{
+		  pp_string (pp, M_("<template parameter error>"));
+		  continue;
+		}
 
-          parm = TREE_VALUE (TREE_VEC_ELT (parms, ix));
+	      parm = TREE_VALUE (TREE_VEC_ELT (parms, ix));
 
-	  if (ix)
-	    pp_separate_with_comma (pp);
+	      if (ix)
+		pp_separate_with_comma (pp);
 
-	  dump_decl (pp, parm, flags & ~TFF_DECL_SPECIFIERS);
+	      dump_decl (pp, parm, flags & ~TFF_DECL_SPECIFIERS);
+	    }
 	}
     }
   pp_cxx_end_template_argument_list (pp);
@@ -3164,7 +3233,8 @@ lang_decl_name (tree decl, int v, bool translate)
     }
 
   if (TREE_CODE (decl) == FUNCTION_DECL)
-    dump_function_name (cxx_pp, decl, TFF_PLAIN_IDENTIFIER);
+    dump_function_name (cxx_pp, decl,
+			TFF_PLAIN_IDENTIFIER | TFF_NO_FUNCTION_ARGUMENTS);
   else if ((DECL_NAME (decl) == NULL_TREE)
            && TREE_CODE (decl) == NAMESPACE_DECL)
     dump_decl (cxx_pp, decl, TFF_PLAIN_IDENTIFIER | TFF_UNQUALIFIED_NAME);
@@ -4061,10 +4131,10 @@ print_template_differences (pretty_printer *pp, tree type_a, tree type_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);
+  int len_a = args_or_non_default_template_args_count (args_a, flags);
   args_b = INNERMOST_TEMPLATE_ARGS (args_b);
+  int len_b = args_or_non_default_template_args_count (args_b, flags);
   /* 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.
diff --git a/gcc/cp/module.cc b/gcc/cp/module.cc
index 71d0fab411f..3b1b5ca0ac0 100644
--- a/gcc/cp/module.cc
+++ b/gcc/cp/module.cc
@@ -6154,7 +6154,9 @@ trees_out::core_vals (tree t)
       /* We stash NON_DEFAULT_TEMPLATE_ARGS_COUNT on TREE_CHAIN!  */
       gcc_checking_assert (!t->type_common.common.chain
 			   || (TREE_CODE (t->type_common.common.chain)
-			       == INTEGER_CST));
+			       == INTEGER_CST)
+			   || (TREE_CODE (t->type_common.common.chain)
+			       == TREE_LIST));
       WT (t->type_common.common.chain);
       break;
 
diff --git a/gcc/cp/pt.c b/gcc/cp/pt.c
index 74323701a7d..2ba7622a669 100644
--- a/gcc/cp/pt.c
+++ b/gcc/cp/pt.c
@@ -689,6 +689,63 @@ strip_innermost_template_args (tree args, int extra_levels)
   return new_args;
 }
 
+/* Let ARGS know that there are COUNT entries which are not defaulted. ARGS must
+ * be a single level of arguments.  */
+
+void
+set_non_default_template_args_count (tree args, int count)
+{
+  gcc_assert (!TMPL_ARGS_HAVE_MULTIPLE_LEVELS (args));
+  gcc_assert (count <= NUM_TMPL_ARGS (args));
+  tree cst = build_int_cst (integer_type_node, count);
+  if (TREE_CHAIN (args) && TREE_CODE (TREE_CHAIN (args)) == TREE_LIST)
+    TREE_VALUE (TREE_CHAIN (args)) = cst;
+  else
+    TREE_CHAIN (args) = cst;
+}
+
+/* Query the number of non-defaulted template arguments in a single level of
+   template ARGS.  */
+
+int
+get_non_default_template_args_count (tree args)
+{
+  gcc_assert (!TMPL_ARGS_HAVE_MULTIPLE_LEVELS (args));
+  tree node = TREE_CHAIN (args);
+  if (!node)
+    return NUM_TMPL_ARGS (args);
+  if (TREE_CODE (node) == TREE_LIST)
+    node = TREE_VALUE (node);
+  const int count = int_cst_value (node);
+  gcc_assert (count <= NUM_TMPL_ARGS (args));
+  return count;
+}
+
+/* Set the COUNT of explicitly specified function template ARGS.  */
+
+void
+set_explicit_template_args_count (tree args, int count)
+{
+  args = INNERMOST_TEMPLATE_ARGS (args);
+  tree non_default
+    = TREE_CHAIN (args)
+	? INTEGER_CST_CHECK (TREE_CHAIN (args))
+	: build_int_cst (integer_type_node, TREE_VEC_LENGTH (args));
+  TREE_CHAIN (args) = build_tree_list (build_int_cst (integer_type_node, count),
+				       non_default);
+}
+
+/* Query the number of explicity specified function template ARGS.  */
+
+bool
+get_explicit_template_args_count (tree args)
+{
+  tree node = TREE_CHAIN (INNERMOST_TEMPLATE_ARGS (args));
+  return node && TREE_CODE (node) == TREE_LIST
+	   ? int_cst_value (TREE_PURPOSE (node))
+	   : 0;
+}
+
 /* We've got a template header coming up; push to a new level for storing
    the parms.  */
 
@@ -2502,7 +2559,7 @@ determine_specialization (tree template_id,
   if (candidates)
     {
       tree fn = TREE_VALUE (candidates);
-      *targs_out = copy_node (DECL_TI_ARGS (fn));
+      *targs_out = copy_template_args (DECL_TI_ARGS (fn));
 
       /* Propagate the candidate's constraints to the declaration.  */
       if (tsk != tsk_template)
@@ -4332,7 +4389,6 @@ expand_template_argument_pack (tree args)
   tree result_args = NULL_TREE;
   int in_arg, out_arg = 0, nargs = args ? TREE_VEC_LENGTH (args) : 0;
   int num_result_args = -1;
-  int non_default_args_count = -1;
 
   /* First, determine if we need to expand anything, and the number of
      slots we'll need.  */
@@ -4362,9 +4418,7 @@ expand_template_argument_pack (tree args)
 
   /* Expand arguments.  */
   result_args = make_tree_vec (num_result_args);
-  if (NON_DEFAULT_TEMPLATE_ARGS_COUNT (args))
-    non_default_args_count =
-      GET_NON_DEFAULT_TEMPLATE_ARGS_COUNT (args);
+  int non_default_args_count = GET_NON_DEFAULT_TEMPLATE_ARGS_COUNT (args);
   for (in_arg = 0; in_arg < nargs; ++in_arg)
     {
       tree arg = TREE_VEC_ELT (args, in_arg);
@@ -4374,8 +4428,7 @@ expand_template_argument_pack (tree args)
           int i, num_packed = TREE_VEC_LENGTH (packed);
           for (i = 0; i < num_packed; ++i, ++out_arg)
             TREE_VEC_ELT (result_args, out_arg) = TREE_VEC_ELT(packed, i);
-	  if (non_default_args_count > 0)
-	    non_default_args_count += num_packed - 1;
+	  non_default_args_count += num_packed - 1;
         }
       else
         {
@@ -4383,8 +4436,7 @@ expand_template_argument_pack (tree args)
           ++out_arg;
         }
     }
-  if (non_default_args_count >= 0)
-    SET_NON_DEFAULT_TEMPLATE_ARGS_COUNT (result_args, non_default_args_count);
+  SET_NON_DEFAULT_TEMPLATE_ARGS_COUNT (result_args, non_default_args_count);
   return result_args;
 }
 
@@ -4787,8 +4839,7 @@ template_parm_to_arg (tree t)
 	  /* Turn this argument into a TYPE_ARGUMENT_PACK
 	     with a single element, which expands T.  */
 	  tree vec = make_tree_vec (1);
-	  if (CHECKING_P)
-	    SET_NON_DEFAULT_TEMPLATE_ARGS_COUNT (vec, TREE_VEC_LENGTH (vec));
+	  SET_NON_DEFAULT_TEMPLATE_ARGS_COUNT (vec, TREE_VEC_LENGTH (vec));
 
 	  TREE_VEC_ELT (vec, 0) = make_pack_expansion (t);
 
@@ -4803,8 +4854,7 @@ template_parm_to_arg (tree t)
 	  /* Turn this argument into a NONTYPE_ARGUMENT_PACK
 	     with a single element, which expands T.  */
 	  tree vec = make_tree_vec (1);
-	  if (CHECKING_P)
-	    SET_NON_DEFAULT_TEMPLATE_ARGS_COUNT (vec, TREE_VEC_LENGTH (vec));
+	  SET_NON_DEFAULT_TEMPLATE_ARGS_COUNT (vec, TREE_VEC_LENGTH (vec));
 
 	  t = convert_from_reference (t);
 	  TREE_VEC_ELT (vec, 0) = make_pack_expansion (t);
@@ -4826,11 +4876,16 @@ template_parms_level_to_args (tree parms)
 {
   tree a = copy_node (parms);
   TREE_TYPE (a) = NULL_TREE;
+  int nondefault = 0;
   for (int i = TREE_VEC_LENGTH (a) - 1; i >= 0; --i)
-    TREE_VEC_ELT (a, i) = template_parm_to_arg (TREE_VEC_ELT (a, i));
+    {
+      tree elt = TREE_VEC_ELT (a, i);
+      TREE_VEC_ELT (a, i) = template_parm_to_arg (elt);
+      if (!elt || elt == error_mark_node || !TREE_PURPOSE (elt))
+	++nondefault;
+    }
 
-  if (CHECKING_P)
-    SET_NON_DEFAULT_TEMPLATE_ARGS_COUNT (a, TREE_VEC_LENGTH (a));
+  SET_NON_DEFAULT_TEMPLATE_ARGS_COUNT (a, nondefault);
 
   return a;
 }
@@ -8727,9 +8782,8 @@ coerce_template_parameter_pack (tree parms,
     }
 
   SET_ARGUMENT_PACK_ARGS (argument_pack, packed_args);
-  if (CHECKING_P)
-    SET_NON_DEFAULT_TEMPLATE_ARGS_COUNT (packed_args,
-					 TREE_VEC_LENGTH (packed_args));
+  SET_NON_DEFAULT_TEMPLATE_ARGS_COUNT (packed_args,
+				       TREE_VEC_LENGTH (packed_args));
   return argument_pack;
 }
 
@@ -9093,7 +9147,7 @@ coerce_template_parms (tree parms,
       return error_mark_node;
     }
 
-  if (CHECKING_P && !NON_DEFAULT_TEMPLATE_ARGS_COUNT (new_inner_args))
+  if (!NON_DEFAULT_TEMPLATE_ARGS_COUNT (new_inner_args))
     SET_NON_DEFAULT_TEMPLATE_ARGS_COUNT (new_inner_args,
 					 TREE_VEC_LENGTH (new_inner_args));
 
@@ -13357,8 +13411,9 @@ copy_template_args (tree t)
       TREE_VEC_ELT (new_vec, i) = elt;
     }
 
-  NON_DEFAULT_TEMPLATE_ARGS_COUNT (new_vec)
-    = NON_DEFAULT_TEMPLATE_ARGS_COUNT (t);
+  if (!TMPL_ARGS_HAVE_MULTIPLE_LEVELS (t))
+    NON_DEFAULT_TEMPLATE_ARGS_COUNT (new_vec)
+      = NON_DEFAULT_TEMPLATE_ARGS_COUNT (t);
 
   return new_vec;
 }
@@ -13398,13 +13453,33 @@ tree
 tsubst_template_args (tree t, tree args, tsubst_flags_t complain, tree in_decl)
 {
   tree orig_t = t;
-  int len, need_new = 0, i, expanded_len_adjust = 0, out;
+  int need_new = 0, i, expanded_len_adjust = 0, out;
   tree *elts;
 
   if (t == error_mark_node)
     return error_mark_node;
 
-  len = TREE_VEC_LENGTH (t);
+  const int len = TREE_VEC_LENGTH (t);
+  if (len == 0)
+    return t;
+
+  /* Shortcut if T has multiple levels. All elts must be TREE_VECs and we simply
+     recurse. The remainder of this function doesn't apply to multi-level
+     args.  */
+  if (TMPL_ARGS_HAVE_MULTIPLE_LEVELS (t))
+    {
+      tree r = make_tree_vec (len);
+      for (i = 0; i < len; i++)
+	{
+	  tree new_arg = tsubst_template_args (TREE_VEC_ELT (t, i), args,
+					       complain, in_decl);
+	  if (new_arg == error_mark_node)
+	    return error_mark_node;
+	  TREE_VEC_ELT (r, i) = new_arg;
+	}
+      return r;
+    }
+
   elts = XALLOCAVEC (tree, len);
 
   for (i = 0; i < len; i++)
@@ -13414,8 +13489,6 @@ tsubst_template_args (tree t, tree args, tsubst_flags_t complain, tree in_decl)
 
       if (!orig_arg)
 	new_arg = NULL_TREE;
-      else if (TREE_CODE (orig_arg) == TREE_VEC)
-	new_arg = tsubst_template_args (orig_arg, args, complain, in_decl);
       else if (PACK_EXPANSION_P (orig_arg))
         {
           /* Substitute into an expansion expression.  */
@@ -13449,19 +13522,6 @@ tsubst_template_args (tree t, tree args, tsubst_flags_t complain, tree in_decl)
   /* Make space for the expanded arguments coming from template
      argument packs.  */
   t = make_tree_vec (len + expanded_len_adjust);
-  /* ORIG_T can contain TREE_VECs. That happens if ORIG_T contains the
-     arguments for a member template.
-     In that case each TREE_VEC in ORIG_T represents a level of template
-     arguments, and ORIG_T won't carry any non defaulted argument count.
-     It will rather be the nested TREE_VECs that will carry one.
-     In other words, ORIG_T carries a non defaulted argument count only
-     if it doesn't contain any nested TREE_VEC.  */
-  if (NON_DEFAULT_TEMPLATE_ARGS_COUNT (orig_t))
-    {
-      int count = GET_NON_DEFAULT_TEMPLATE_ARGS_COUNT (orig_t);
-      count += expanded_len_adjust;
-      SET_NON_DEFAULT_TEMPLATE_ARGS_COUNT (t, count);
-    }
   for (i = 0, out = 0; i < len; i++)
     {
       tree orig_arg = TREE_VEC_ELT (orig_t, i);
@@ -13482,6 +13542,65 @@ tsubst_template_args (tree t, tree args, tsubst_flags_t complain, tree in_decl)
         }
     }
 
+  /* The non-default template count can only be copied from ARGS if ORIG_T
+     requests a substitution of a complete level from ARGS. If the resulting T
+     contains the same entries as ARGS, then simply return the relevant level
+     from ARGS, which already has the correct non-default count.  */
+  int non_default_count = GET_NON_DEFAULT_TEMPLATE_ARGS_COUNT (orig_t)
+			    + expanded_len_adjust;
+  if (expanded_len_adjust == 0)
+    {
+      auto level_of_parm = [](tree vec, int i) -> int {
+	tree parm = TREE_VEC_ELT (vec, i);
+	while (ARGUMENT_PACK_P (parm) || PACK_EXPANSION_P (parm))
+	  {
+	    if (ARGUMENT_PACK_P (parm))
+	      parm = ARGUMENT_PACK_ARGS (parm);
+	    if (PACK_EXPANSION_P (parm))
+	      parm = PACK_EXPANSION_PATTERN (parm);
+	    if (TREE_CODE (parm) == TREE_VEC && TREE_VEC_LENGTH (parm) == 1)
+	      parm = TREE_VEC_ELT (parm, 0);
+	  }
+	if ((TREE_CODE (parm) == TEMPLATE_TYPE_PARM
+	       || TREE_CODE (parm) == TEMPLATE_TEMPLATE_PARM
+	       || TREE_CODE (parm) == BOUND_TEMPLATE_TEMPLATE_PARM)
+	      && i == TEMPLATE_TYPE_IDX (parm))
+	  return TEMPLATE_TYPE_LEVEL (parm);
+	else if (TREE_CODE (parm) == TEMPLATE_PARM_INDEX
+		   && i == TEMPLATE_PARM_IDX (parm))
+	  return TEMPLATE_PARM_LEVEL (parm);
+	return -1;
+      };
+
+      const int level = level_of_parm (orig_t, 0);
+      if (level > 0 && level <= TMPL_ARGS_DEPTH (args))
+	{
+	  args = TMPL_ARGS_LEVEL (args, level);
+	  if (len == NUM_TMPL_ARGS (args))
+	    {
+	      bool t_equals_args
+		= TREE_VEC_ELT (args, 0) == TREE_VEC_ELT (t, 0);
+	      int i;
+	      for (i = 1; i < len; ++i)
+		{
+		  if (level_of_parm (orig_t, i) == level)
+		    t_equals_args = t_equals_args && TREE_VEC_ELT (args, i)
+						       == TREE_VEC_ELT (t, i);
+		  else
+		    break;
+		}
+	      if (i == len && t_equals_args)
+		{
+		  ggc_free (t);
+		  return args;
+		}
+	      if (i == len && NON_DEFAULT_TEMPLATE_ARGS_COUNT (args))
+		non_default_count = GET_NON_DEFAULT_TEMPLATE_ARGS_COUNT (args);
+	    }
+	}
+    }
+  SET_NON_DEFAULT_TEMPLATE_ARGS_COUNT (t, non_default_count);
+
   return t;
 }
 
@@ -21785,8 +21904,7 @@ fn_type_unification (tree fn,
       explicit_targs = INNERMOST_TEMPLATE_ARGS (explicit_targs);
       for (i = NUM_TMPL_ARGS (explicit_targs); i--;)
 	TREE_VEC_ELT (targs, i) = TREE_VEC_ELT (explicit_targs, i);
-      if (!incomplete && CHECKING_P
-	  && !NON_DEFAULT_TEMPLATE_ARGS_COUNT (targs))
+      if (!incomplete && !NON_DEFAULT_TEMPLATE_ARGS_COUNT (targs))
 	SET_NON_DEFAULT_TEMPLATE_ARGS_COUNT
 	  (targs, NUM_TMPL_ARGS (explicit_targs));
     }
@@ -21972,6 +22090,11 @@ fn_type_unification (tree fn,
 	excessive_deduction_depth = false;
     }
 
+  /* If all template parameters were explicitly given, treat them like default
+     template arguments for diagnostics.  */
+  if (explicit_targs && explicit_targs != error_mark_node)
+    SET_EXPLICIT_TEMPLATE_ARGS_COUNT (targs, NUM_TMPL_ARGS (explicit_targs));
+
   return r;
 }
 
@@ -22614,9 +22737,17 @@ type_unification_real (tree tparms,
 	     be NULL_TREE or ERROR_MARK_NODE, so we do not need
 	     to explicitly check cxx_dialect here.  */
 	  if (TREE_PURPOSE (TREE_VEC_ELT (tparms, i)))
-	    /* OK, there is a default argument.  Wait until after the
-	       conversion check to do substitution.  */
-	    continue;
+	    {
+	      /* The position of the first default template argument,
+		 is also the number of non-defaulted arguments in TARGS.
+		 Record that.  */
+	      if (!NON_DEFAULT_TEMPLATE_ARGS_COUNT (targs))
+		SET_NON_DEFAULT_TEMPLATE_ARGS_COUNT (targs, i);
+
+	      /* OK, there is a default argument.  Wait until after the
+		 conversion check to do substitution.  */
+	      continue;
+	    }
 
 	  /* If the type parameter is a parameter pack, then it will
 	     be deduced to an empty parameter pack.  */
@@ -22719,21 +22850,14 @@ type_unification_real (tree tparms,
 	  if (arg == error_mark_node)
 	    return 1;
 	  else if (arg)
-	    {
-	      TREE_VEC_ELT (targs, i) = arg;
-	      /* The position of the first default template argument,
-		 is also the number of non-defaulted arguments in TARGS.
-		 Record that.  */
-	      if (!NON_DEFAULT_TEMPLATE_ARGS_COUNT (targs))
-		SET_NON_DEFAULT_TEMPLATE_ARGS_COUNT (targs, i);
-	    }
+	    TREE_VEC_ELT (targs, i) = arg;
 	}
 
       if (saw_undeduced++ == 1)
 	goto again;
     }
 
-  if (CHECKING_P && !NON_DEFAULT_TEMPLATE_ARGS_COUNT (targs))
+  if (!NON_DEFAULT_TEMPLATE_ARGS_COUNT (targs))
     SET_NON_DEFAULT_TEMPLATE_ARGS_COUNT (targs, TREE_VEC_LENGTH (targs));
 
   return unify_success (explain_p);
@@ -25022,6 +25146,7 @@ get_partial_spec_bindings (tree tmpl, tree spec_tmpl, tree args)
   tree innermost_deduced_args;
 
   innermost_deduced_args = make_tree_vec (ntparms);
+  SET_NON_DEFAULT_TEMPLATE_ARGS_COUNT (innermost_deduced_args, ntparms);
   if (TMPL_ARGS_HAVE_MULTIPLE_LEVELS (args))
     {
       deduced_args = copy_node (args);
diff --git a/gcc/testsuite/g++.dg/debug/dwarf2/template-params-12n.C b/gcc/testsuite/g++.dg/debug/dwarf2/template-params-12n.C
index d3c1f589f87..b88bf7d4b34 100644
--- a/gcc/testsuite/g++.dg/debug/dwarf2/template-params-12n.C
+++ b/gcc/testsuite/g++.dg/debug/dwarf2/template-params-12n.C
@@ -1,6 +1,6 @@
 // { dg-options "-gdwarf-2 -dA" }
 // { dg-final { scan-assembler-times "DIE \\(\[^\n\]*\\) DW_TAG_template_value_param" 1 } }
-// { dg-final { scan-assembler-times "DIE \\(\[^\n\]*\\) DW_TAG_template_value_param\[^\n\]*\n\[^\n\]* DW_AT_name\n\[^\n\]* DW_AT_type\n\[^\n\]* DW_AT_const_value" 1 } }
+// { dg-final { scan-assembler-times "DIE \\(\[^\n\]*\\) DW_TAG_template_value_param\[^\n\]*\n\[^\n\]* DW_AT_name\n\[^\n\]* DW_AT_type\n(?:\[^\n\]* DW_AT_default_value\n)?\[^\n\]* DW_AT_const_value" 1 } }
 #include "template-params-12.H"
 /* We get const_value for NULL pointers to member functions.  */
 #if __cplusplus > 199711L // Ugh, C++98 barfs at both the cast and the overload.
diff --git a/gcc/testsuite/g++.dg/diagnostic/default-template-args-1.C b/gcc/testsuite/g++.dg/diagnostic/default-template-args-1.C
new file mode 100644
index 00000000000..6481798a69b
--- /dev/null
+++ b/gcc/testsuite/g++.dg/diagnostic/default-template-args-1.C
@@ -0,0 +1,61 @@
+// { dg-options "-fpretty-templates" }
+// { dg-do compile { target c++11 } }
+
+template <int a = 1>
+  [[deprecated]] void f0(); // { dg-message "'template<int a> void f0\\(\\)'" }
+
+template <int a>
+  [[deprecated]] void f1(); // { dg-message "'template<int a> void f1\\(\\)'" }
+
+template <class a, int b = 1>
+  [[deprecated]] void f2(); // { dg-message "'template<class a, int b> void f2\\(\\)'" }
+
+template <class a, int b = 1>
+  [[deprecated]] void f3(a); // { dg-message "'template<class a, int b> void f3\\(a\\)'" }
+
+template <class a>
+  [[deprecated]] void f4(a); // { dg-message "'template<class a> void f4\\(a\\)'" }
+
+template <>
+  [[deprecated]] void f4<int>(int);
+
+template <>
+  [[deprecated]] void f4(float);
+
+template <class a, class b = int>
+  [[deprecated]] void f5(a);
+
+template void f5<float>(float); // { dg-error "'void f5<a>\\(a\\) .with a = float.'" }
+
+template <class a, class b = int>
+  struct c
+  {
+    template <class d, class e = int>
+      [[deprecated]] static void f(d);
+  };
+
+int main()
+{
+  f0();            // { dg-warning "'void f0\\(\\)'" }
+  f1<1>();         // { dg-warning "'void f1<a>\\(\\) .with int a = 1.'" }
+  f2<int>();       // { dg-warning "'void f2<a>\\(\\) .with a = int.'" }
+  f3(1);           // { dg-warning "'void f3\\(a\\) .with a = int.'" }
+  f3<float>(1);    // { dg-warning "'void f3<a>\\(a\\) .with a = float.'" }
+  f3<float, 2>(1); // { dg-warning "'void f3<a, b>\\(a\\) .with a = float; int b = 2.'" }
+  f4(1.);          // { dg-warning "'void f4\\(a\\) .with a = double.'" }
+  f4(1);           // { dg-warning "'void f4<int>\\(int\\)'" }
+  f4(1.f);         // { dg-warning "'void f4\\(float\\)'" }
+
+  f0(0); // { dg-error "" }
+  f1(0); // { dg-error "" }
+  f2(0); // { dg-error "" }
+  f3();  // { dg-error "" }
+  f4();  // { dg-error "" }
+
+  c<int>::f(1.);    // { dg-warning "'static void c<a>::f\\(d\\) .with d = double; a = int.'" }
+  c<int>::f<int>(1);    // { dg-warning "'static void c<a>::f<d>\\(d\\) .with d = int; a = int.'" }
+  c<int>::f<float, int>(1.f);    // { dg-warning "'static void c<a>::f<d, e>\\(d\\) .with d = float; e = int; a = int.'" }
+  c<float, int>::f(1.);    // { dg-warning "'static void c<a, b>::f\\(d\\) .with d = double; a = float; b = int.'" }
+  c<float, int>::f<int>(1);    // { dg-warning "'static void c<a, b>::f<d>\\(d\\) .with d = int; a = float; b = int.'" }
+  c<float, int>::f<float, int>(1.f);    // { dg-warning "'static void c<a, b>::f<d, e>\\(d\\) .with d = float; e = int; a = float; b = int.'" }
+}
diff --git a/gcc/testsuite/g++.dg/diagnostic/default-template-args-2.C b/gcc/testsuite/g++.dg/diagnostic/default-template-args-2.C
new file mode 100644
index 00000000000..a82709f5785
--- /dev/null
+++ b/gcc/testsuite/g++.dg/diagnostic/default-template-args-2.C
@@ -0,0 +1,37 @@
+// { dg-options "-fno-pretty-templates" }
+// { dg-do compile { target c++11 } }
+
+template <int a = 1>
+  [[deprecated]] void f0();
+
+template <int a>
+  [[deprecated]] void f1();
+
+template <class a, int b = 1>
+  [[deprecated]] void f2();
+
+template <class a, int b = 1>
+  [[deprecated]] void f3(a);
+
+template <class a>
+  [[deprecated]] void f4(a);
+
+template <>
+  [[deprecated]] void f4<int>(int);
+
+template <class a, class b = int>
+  [[deprecated]] void f5(a);
+
+template void f5<float>(float); // { dg-error "'void f5<float>\\(float\\)'" }
+
+int main()
+{
+  f0();            // { dg-warning "'void f0\\(\\)'" }
+  f1<1>();         // { dg-warning "'void f1<1>\\(\\)'" }
+  f2<int>();       // { dg-warning "'void f2<int>\\(\\)'" }
+  f3(1);           // { dg-warning "'void f3\\(int\\)'" }
+  f3<float>(1);    // { dg-warning "'void f3<float>\\(float\\)'" }
+  f3<float, 2>(1); // { dg-warning "'void f3<float, 2>\\(float\\)'" }
+  f4(1.);          // { dg-warning "'void f4\\(double\\)'" }
+  f4(1);           // { dg-warning "'void f4<int>\\(int\\)'" }
+}
diff --git a/gcc/testsuite/g++.dg/diagnostic/default-template-args-3.C b/gcc/testsuite/g++.dg/diagnostic/default-template-args-3.C
new file mode 100644
index 00000000000..c5dceafca29
--- /dev/null
+++ b/gcc/testsuite/g++.dg/diagnostic/default-template-args-3.C
@@ -0,0 +1,29 @@
+// { dg-options "-fpretty-templates" }
+// { dg-do compile { target c++11 } }
+
+template <class T>
+  struct id
+  { using type = T; };
+
+template <class T0, class T1 = int, template <class> class foo = id>
+  struct A
+  {
+    template <class U0 = const T1&>
+      [[deprecated]] static void
+      f(typename foo<U0>::type);
+  };
+
+template <int> struct B;
+
+template <> struct B<1> 
+  { using type = int; };
+
+template <class T, int x = 1>
+  [[deprecated]] typename B<x>::type
+  f0(T);
+
+int main()
+{
+  A<int>::f(0); // { dg-warning "'static void A<T0>::f\\(typename foo<U0>::type\\) .with U0 = const int&; T0 = int; foo = id; typename foo<U0>::type = const int&.'" }
+  f0(0); // { dg-warning "'typename B<x>::type f0\\(T\\) .with T = int; int x = 1; typename B<x>::type = int.'" }
+}
diff --git a/gcc/testsuite/g++.dg/diagnostic/default-template-args-4.C b/gcc/testsuite/g++.dg/diagnostic/default-template-args-4.C
new file mode 100644
index 00000000000..3cb604a28ea
--- /dev/null
+++ b/gcc/testsuite/g++.dg/diagnostic/default-template-args-4.C
@@ -0,0 +1,24 @@
+// { dg-options "-fpretty-templates" }
+// { dg-do compile { target c++11 } }
+
+template <class T> struct X {};
+template <class... T> struct Y {};
+
+template <template <class> class Tpl, template <class...> class VTpl,
+	  class T0 = float, class U0 = VTpl<Tpl<T0>>, int x0 = 2>
+  struct S
+  {
+    template <class T, class... Ps, class U = int, int x = 1>
+      [[deprecated]] static U0
+      f(T, Ps&&...);
+
+    template <class T, class... Ps, class U = int, int x = 1>
+      [[deprecated]] U0
+      g(T, Ps&&...);
+  };
+
+void g()
+{
+  S<X, Y>::f(1, '2', 3, 4.); // { dg-warning "'static U0 S<Tpl, VTpl>::f\\(T, Ps&& ...\\) .with T = int; Ps = {char, int, double}; Tpl = X; VTpl = Y; U0 = Y<X<float> >.'" }
+  S<X, Y>().g(1, '2', 3, 4.); // { dg-warning "'U0 S<Tpl, VTpl>::g\\(T, Ps&& ...\\) .with T = int; Ps = {char, int, double}; Tpl = X; VTpl = Y; U0 = Y<X<float> >.'" }
+}
diff --git a/gcc/testsuite/g++.dg/diagnostic/default-template-args-5.C b/gcc/testsuite/g++.dg/diagnostic/default-template-args-5.C
new file mode 100644
index 00000000000..0f13466fb27
--- /dev/null
+++ b/gcc/testsuite/g++.dg/diagnostic/default-template-args-5.C
@@ -0,0 +1,12 @@
+// { dg-options "-fpretty-templates" }
+// { dg-do compile { target c++11 } }
+//
+// Tests the case where DECL_TI_TEMPLATE doesn't return a TEMPLATE_DECL for the
+// FUNCTION_DECL of foo<U>(int).
+
+template <class T0, class T1 = int, int x = 1> void foo (int);
+
+template <class U, int y = 2>
+class Q {
+  friend void foo<U> (int = 3); // { dg-error "'void foo<U>\\(int\\)'" }
+};
diff --git a/gcc/testsuite/g++.dg/diagnostic/param-type-mismatch-2.C b/gcc/testsuite/g++.dg/diagnostic/param-type-mismatch-2.C
index de7570a6efa..0aa45404283 100644
--- a/gcc/testsuite/g++.dg/diagnostic/param-type-mismatch-2.C
+++ b/gcc/testsuite/g++.dg/diagnostic/param-type-mismatch-2.C
@@ -141,7 +141,7 @@ int test_7 (int first, const char *second, float third)
                                          |
                                          const char*
      { dg-end-multiline-output "" } */
-  // { dg-message "initializing argument 2 of 'int test_7\\(int, T, float\\) .with T = const char\\*\\*.'" "" { target *-*-* } test_7_decl }
+  // { dg-message "initializing argument 2 of 'int test_7<T>\\(int, T, float\\) .with T = const char\\*\\*.'" "" { target *-*-* } test_7_decl }
   /* { dg-begin-multiline-output "" }
  int test_7 (int one, T two, float three);
                       ~~^~~
diff --git a/gcc/testsuite/g++.dg/ext/pretty1.C b/gcc/testsuite/g++.dg/ext/pretty1.C
index 06608ae30eb..c5bfd6082a7 100644
--- a/gcc/testsuite/g++.dg/ext/pretty1.C
+++ b/gcc/testsuite/g++.dg/ext/pretty1.C
@@ -60,7 +60,7 @@ __assert_fail (const char *cond, const char *file, unsigned int line,
   abort ();
 }
 
-// { dg-final { scan-assembler "int bar\\(T\\).*with T = int" } }
+// { dg-final { scan-assembler "int bar<int>\\(int\\)" } }
 // { dg-final { scan-assembler "top level" } }
 // { dg-final { scan-assembler "int main\\(\\)" } }
 // { dg-final { scan-assembler "int bar\\(T\\).*with T = double" } }
diff --git a/gcc/testsuite/g++.dg/goacc/template.C b/gcc/testsuite/g++.dg/goacc/template.C
index 10d3f446da7..4fcd88bfc56 100644
--- a/gcc/testsuite/g++.dg/goacc/template.C
+++ b/gcc/testsuite/g++.dg/goacc/template.C
@@ -157,12 +157,12 @@ main ()
 }
 
 /* { dg-final { scan-tree-dump-times {(?n)^OpenACC routine '[^']+' has 'nohost' clause\.$} 4 oaccloops } }
-   { dg-final { scan-tree-dump-times {(?n)^OpenACC routine 'T accDouble\(int\) \[with T = char\]' has 'nohost' clause\.$} 1 oaccloops { target { ! offloading_enabled } } } }
+   { dg-final { scan-tree-dump-times {(?n)^OpenACC routine 'T accDouble<T>\(int\) \[with T = char\]' has 'nohost' clause\.$} 1 oaccloops { target { ! offloading_enabled } } } }
    { dg-final { scan-tree-dump-times {(?n)^OpenACC routine 'accDouble<char>\(int\)char' has 'nohost' clause\.$} 1 oaccloops { target offloading_enabled } } }
-   { dg-final { scan-tree-dump-times {(?n)^OpenACC routine 'T accDouble\(int\) \[with T = int\]' has 'nohost' clause\.$} 1 oaccloops { target { ! offloading_enabled } } } }
+   { dg-final { scan-tree-dump-times {(?n)^OpenACC routine 'T accDouble<T>\(int\) \[with T = int\]' has 'nohost' clause\.$} 1 oaccloops { target { ! offloading_enabled } } } }
    { dg-final { scan-tree-dump-times {(?n)^OpenACC routine 'accDouble<int>\(int\)int' has 'nohost' clause\.$} 1 oaccloops { target offloading_enabled } } }
-   { dg-final { scan-tree-dump-times {(?n)^OpenACC routine 'T accDouble\(int\) \[with T = float\]' has 'nohost' clause\.$} 1 oaccloops { target { ! offloading_enabled } } } }
+   { dg-final { scan-tree-dump-times {(?n)^OpenACC routine 'T accDouble<T>\(int\) \[with T = float\]' has 'nohost' clause\.$} 1 oaccloops { target { ! offloading_enabled } } } }
    { dg-final { scan-tree-dump-times {(?n)^OpenACC routine 'accDouble<float>\(int\)float' has 'nohost' clause\.$} 1 oaccloops { target offloading_enabled } } }
-   { dg-final { scan-tree-dump-times {(?n)^OpenACC routine 'T accDouble\(int\) \[with T = double\]' has 'nohost' clause\.$} 1 oaccloops { target { ! offloading_enabled } } } }
+   { dg-final { scan-tree-dump-times {(?n)^OpenACC routine 'T accDouble<T>\(int\) \[with T = double\]' has 'nohost' clause\.$} 1 oaccloops { target { ! offloading_enabled } } } }
    { dg-final { scan-tree-dump-times {(?n)^OpenACC routine 'accDouble<double>\(int\)double' has 'nohost' clause\.$} 1 oaccloops { target offloading_enabled } } }
    TODO See PR101551 for 'offloading_enabled' differences.  */
diff --git a/gcc/testsuite/g++.dg/template/error40.C b/gcc/testsuite/g++.dg/template/error40.C
index c5df56fc1be..16a44d1819f 100644
--- a/gcc/testsuite/g++.dg/template/error40.C
+++ b/gcc/testsuite/g++.dg/template/error40.C
@@ -8,11 +8,11 @@ struct A
 
 void foo(void)
 {
-  A<void> a = 0;		// { dg-error "A<void, 0, 1>" }
+  A<void> a = 0;		// { dg-error "A<void>" }
 }
 
-template <class T> T f(T);	    // { dg-message "int f<int>.int." }
-template <class T> T f(T, int = 0); // { dg-message "" }
+template <class T> T f(T);	    // { dg-message "int f.int." }
+template <class T> T f(T, int = 0); // { dg-message "int f.int, int." }
 
 template <class T>
 struct B
diff --git a/gcc/testsuite/g++.old-deja/g++.ext/pretty3.C b/gcc/testsuite/g++.old-deja/g++.ext/pretty3.C
index 6348ae1ee67..30c7ecd5065 100644
--- a/gcc/testsuite/g++.old-deja/g++.ext/pretty3.C
+++ b/gcc/testsuite/g++.old-deja/g++.ext/pretty3.C
@@ -35,7 +35,7 @@ template<> void f1<int> (int)
   
   if (strcmp (function, "f1<int>"))
     bad = true;
-  if (strcmp (pretty, "void f1(T) [with T = int]"))
+  if (strcmp (pretty, "void f1<int>(int)"))
     bad = true;
 }
 
diff --git a/gcc/testsuite/g++.old-deja/g++.pt/memtemp77.C b/gcc/testsuite/g++.old-deja/g++.pt/memtemp77.C
index 6dd4b546c0c..93dbf4b489f 100644
--- a/gcc/testsuite/g++.old-deja/g++.pt/memtemp77.C
+++ b/gcc/testsuite/g++.old-deja/g++.pt/memtemp77.C
@@ -19,7 +19,7 @@ const char* S3<char>::h(int) { return __PRETTY_FUNCTION__; }
 int main()
 {
   if (strcmp (S3<double>::h(7), 
-	      "static const char* S3<T>::h(U) [with U = int; T = double]") == 0)
+	      "static const char* S3<double>::h(int)") == 0)
     return 0;
   else 
     return 1;

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

end of thread, other threads:[~2021-12-02  8:35 UTC | newest]

Thread overview: 24+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2021-07-15 12:21 [PATCH v3] c++: Add gnu::diagnose_as attribute Matthias Kretz
2021-07-23  8:58 ` [PATCH v4] " Matthias Kretz
2021-08-17 18:31   ` Jason Merrill
2021-11-08 16:40     ` [RFC] c++: Print function template parms when relevant (was: [PATCH v4] c++: Add gnu::diagnose_as attribute) Matthias Kretz
2021-11-08 20:00       ` Matthias Kretz
2021-11-16 20:25         ` Jason Merrill
2021-11-16 20:42           ` Matthias Kretz
2021-11-16 20:49             ` Jason Merrill
2021-11-16 20:51               ` Matthias Kretz
2021-11-17  6:09       ` Jason Merrill
2021-11-17  9:04         ` Matthias Kretz
2021-11-17 18:25           ` Jason Merrill
2021-11-17 22:51             ` Matthias Kretz
2021-11-18 19:24               ` Jason Merrill
2021-11-19  9:53                 ` Matthias Kretz
2021-11-19 12:02                   ` Matthias Kretz
2021-11-19 22:26                   ` Jason Merrill
2021-11-19 23:11                     ` Matthias Kretz
2021-11-26 15:23                     ` [PATCH 0/2] " Matthias Kretz
2021-11-26 15:24                       ` [PATCH 1/2] c++: Print function template parms when relevant Matthias Kretz
2021-12-02  8:35                         ` [PATCH v2 " Matthias Kretz
2021-11-26 15:24                       ` [PATCH 2/2] c++: Print function template parms when relevant [part 2] Matthias Kretz
2021-09-08  2:21   ` [PATCH v4] c++: Add gnu::diagnose_as attribute Jason Merrill
2021-11-15  0:35     ` [PATCH v5] " Matthias Kretz

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