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.kr...@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
──────────────────────────────────────────────────────────────────────────
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;
+}
+
 
 /* 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'" }
+}

Reply via email to