diff mbox series

[v3] c++: Add gnu::diagnose_as attribute

Message ID 4361366.VLH7GnMWUR@minbar
State New
Headers show
Series [v3] c++: Add gnu::diagnose_as attribute | expand

Commit Message

Matthias Kretz July 15, 2021, 12:21 p.m. UTC
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
──────────────────────────────────────────────────────────────────────────
diff mbox series

Patch

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