Add gnu::diagnose_as attribute

Message ID 91863212.B8guWdUDZo@excalibur
State New
Headers show
Series
  • Add gnu::diagnose_as attribute
Related show

Commit Message

Matthias Kretz May 4, 2021, 11:13 a.m.
From: Matthias Kretz <kretz@kde.org>


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.

On Tuesday, 27 April 2021 11:46:48 CEST Jonathan Wakely wrote:
> I think it's a great idea and would like to use it for all the TS

> implementations where there is some inline namespace that the user

> doesn't care about. std::experimental::fundamentals_v1:: would be much

> better as just std::experimental::, or something like std::[LFTS]::.


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.

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
	* 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_aggr_type): If the type has a diagnose_as attribute, print
	the associated string instead of printing the original type
	name.
	(dump_simple_decl): Call dump_decl_name_or_diagnose_as instead
	of dump_decl.
	(dump_decl): Ditto.
	(lang_decl_name): Ditto.
	(dump_function_decl): Ensure complete replacement of the class
	template diagnostics if a diagnose_as attribute is present.
	(dump_function_name): Replace the function diagnostic output if
	the diagnose_as attribute is set.
	* name-lookup.c (handle_namespace_attrs): Handle the diagnose_as
	attribute. Ensure exactly one string argument. Ensure previous
	diagnose_as attributes used the same name.
	* 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 and the TREE_TYPE is an implicit class template
	instantiation, call decl_attributes to add the diagnose_as
	attribute to the TREE_TYPE.
---
 gcc/c-family/c.opt   |   4 ++
 gcc/cp/error.c       |  85 ++++++++++++++++++++++++++++---
 gcc/cp/name-lookup.c |  27 ++++++++++
 gcc/cp/tree.c        | 117 +++++++++++++++++++++++++++++++++++++++++++
 gcc/doc/extend.texi  |  37 ++++++++++++++
 gcc/doc/invoke.texi  |   9 +++-
 6 files changed, 270 insertions(+), 9 deletions(-)


--
──────────────────────────────────────────────────────────────────────────
 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
──────────────────────────────────────────────────────────────────────────

Comments

Jakub Jelinek via Gcc-patches May 4, 2021, 1:34 p.m. | #1
On Tue, 2021-05-04 at 13:13 +0200, Matthias Kretz wrote:
> From: Matthias Kretz <kretz@kde.org>

> 

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

> 

> On Tuesday, 27 April 2021 11:46:48 CEST Jonathan Wakely wrote:

> > I think it's a great idea and would like to use it for all the TS

> > implementations where there is some inline namespace that the user

> > doesn't care about. std::experimental::fundamentals_v1:: would be

> > much

> > better as just std::experimental::, or something like std::[LFTS]::.

> 

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


Thanks for the patch, it looks very promising.

The C++ frontend maintainers will need to review the C++ frontend parts
in detail, so I'll defer to them for the bulk of the review.

Various thoughts:

The patch has no testcases; it should probably add test coverage for:
- the various places and ways in which diagnose_as can affect the
output,
- disabling it with the option
- the various ways in which the user can get diagnose_as wrong
- etc

Does the patch affect the output of labels when underlining ranges of
source code in diagnostics?

Does the patch interact correctly with the %H and %I codes that try to
show the differences between two template types?

I have some minor nits from a diagnostics point of view:

[...snip...]

> diff --git a/gcc/cp/name-lookup.c b/gcc/cp/name-lookup.c

> index 4e84e2f9987..80637503310 100644

> --- a/gcc/cp/name-lookup.c

> +++ b/gcc/cp/name-lookup.c


[...]

> +	  tree existing

> +	    = lookup_attribute ("diagnose_as", DECL_ATTRIBUTES (ns));

> +	  if (existing

> +		&& !cp_tree_equal(TREE_VALUE (args),

> +				  TREE_VALUE (TREE_VALUE (existing))))

> +	    {


Please add an auto_diagnostic_group here so that the "inform" is
associated with the "error".

> +	      error ("the namespace %qE already uses a different diagnose_as "

> +		     "attribute value", ns);


diagnose_as should be in quotes here (%< and %>).


> +	      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",

> diff --git a/gcc/cp/tree.c b/gcc/cp/tree.c

> index a8bfd5fc053..f7b93dc89d7 100644

> --- a/gcc/cp/tree.c

> +++ b/gcc/cp/tree.c


[...snip...]

> +      else if (DECL_LANGUAGE (*node) == lang_c)

> +	{

> +	  error ("%qE attribute applied to extern \"C\" declaration %qD",


Please quote extern "C":
                                           %<extern \"C\"%>

> +		 name, *node);


[...snip...]

Thanks again for the patch; hope this is constructive
Dave
Matthias Kretz May 4, 2021, 2:23 p.m. | #2
On Tuesday, 4 May 2021 15:34:13 CEST David Malcolm wrote:
> On Tue, 2021-05-04 at 13:13 +0200, Matthias Kretz wrote:
> > 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.
> > [...]
> 
> Thanks for the patch, it looks very promising.

Thanks. I'm new to modifying the compiler like this, so please be extra 
careful with my patch. I believe I understand most of what I did, but I might 
have misunderstood. :)

> The patch has no testcases; it should probably add test coverage for:
> - the various places and ways in which diagnose_as can affect the
> output,
> - disabling it with the option
> - the various ways in which the user can get diagnose_as wrong
> - etc

Right. If you know of an existing similar testcase, that'd help me a lot to 
get started.

> Does the patch affect the output of labels when underlining ranges of
> source code in diagnostics?

AFAIU (and tested), it doesn't affect source code output. So, no?

> Does the patch interact correctly with the %H and %I codes that try to
> show the differences between two template types?

I don't know. I'll try to find out. If you have a good idea (or pointer) for a 
testcase, let me know.

> I have some minor nits from a diagnostics point of view:
> [...]
> Please add an auto_diagnostic_group here so that the "inform" is
> associated with the "error".
> [...]
> diagnose_as should be in quotes here (%< and %>).
> [...]
> Please quote extern "C":

Thanks. All done in my tree. I'll work on testcases before sending an updated 
patch.

> Thanks again for the patch; hope this is constructive

👍
Matthias Kretz May 4, 2021, 2:32 p.m. | #3
On Tuesday, 4 May 2021 16:23:23 CEST Matthias Kretz wrote:
> On Tuesday, 4 May 2021 15:34:13 CEST David Malcolm wrote:
> > Does the patch interact correctly with the %H and %I codes that try to
> > show the differences between two template types?
> 
> I don't know. I'll try to find out. If you have a good idea (or pointer) for
> a testcase, let me know.

I see it now. It currently does not interact with %H and %I (at least in my 
tests). I'll investigate what it should do.
Jakub Jelinek via Gcc-patches May 4, 2021, 7 p.m. | #4
On Tue, 2021-05-04 at 16:23 +0200, Matthias Kretz wrote:
> On Tuesday, 4 May 2021 15:34:13 CEST David Malcolm wrote:

> > On Tue, 2021-05-04 at 13:13 +0200, Matthias Kretz wrote:

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

> > > [...]

> > 

> > Thanks for the patch, it looks very promising.

> 

> Thanks. I'm new to modifying the compiler like this, so please be

> extra 

> careful with my patch. I believe I understand most of what I did, but

> I might 

> have misunderstood. :)


That's tends to be how I feel when working on the C++ FE :)

> 

> > The patch has no testcases; it should probably add test coverage

> > for:

> > - the various places and ways in which diagnose_as can affect the

> > output,

> > - disabling it with the option

> > - the various ways in which the user can get diagnose_as wrong

> > - etc

> 

> Right. If you know of an existing similar testcase, that'd help me a

> lot to 

> get started.


FWIW I implemented the %H and %I codes in
f012c8ef4b35dcee9b5a3807868d050812d5b3b9 and that commit adds various
DejaGnu testcases that exercise C++ diagnostics with and without
various options, verifying the precise wording of errors.  So that
might be a good place to look.


> 

> > Does the patch affect the output of labels when underlining ranges of

> > source code in diagnostics?

> 

> AFAIU (and tested), it doesn't affect source code output. So, no?


Sorry, I was unclear.  I was referring to this kind of thing:

$ cat t.C
#include <string>

std::string test (std::string s)
{
    return &s;
}

$ g++ t.C
t.C: In function ‘std::string test(std::string)’:
t.C:5:12: error: could not convert ‘& s’ from ‘std::string*’ {aka
‘std::__cxx11::basic_string<char>*’} to ‘std::string’ {aka
‘std::__cxx11::basic_string<char>’}
    5 |     return &s;
      |            ^~
      |            |
      |            std::string* {aka std::__cxx11::basic_string<char>*}

i.e. the final line in the output above, where the underlined range of
source code in line 5 is labelled, showing the type of the expression.

Hopefully it ought to automatically just drop out of the work you've
already done.

FWIW an example of implementation this can be seen in
a14feb3c783fba6af8d66b8138214a3a313be5c5 (which added labels for type
errors in C++ binary operators).


> Does the patch interact correctly with the %H and %I codes that try to

> show the differences between two template types?


I don't know. I'll try to find out. If you have a good idea (or
pointer) for a 
testcase, let me know.

See above.


Dave
Matthias Kretz May 4, 2021, 7:22 p.m. | #5
> On Tuesday, 4 May 2021 15:34:13 CEST David Malcolm wrote:
> > Does the patch interact correctly with the %H and %I codes that try to
> > show the differences between two template types?

While looking into this, I noticed that given

namespace std {
  struct A {};
  typedef A B;
}

const std::B would print as "'const B' {aka 'const std::A'}", i.e. without 
printing the scope of the typedef. I traced it to cp/error.c (dump_type). In 
the `if (TYPE_P (t) && typedef_variant_p (t))` branch, in the final else 
branch only cv-qualifiers and identifier are printed:

  pp_cxx_cv_qualifier_seq (pp, t);
  pp_cxx_tree_identifier (pp, TYPE_IDENTIFIER (t));

I believe the following should go in between, correct?

  pp_cxx_cv_qualifier_seq (pp, t);
  if (! (flags & TFF_UNQUALIFIED_NAME))
    dump_scope (pp, CP_DECL_CONTEXT (TYPE_NAME (t)), flags);
  pp_cxx_tree_identifier (pp, TYPE_IDENTIFIER (t));

This is important for my diagnose_as patch because otherwise the output is:

  'const string' {aka 'const std::string'}

which is confusing and unnecessarily verbose. Patch below.


From: Matthias Kretz <kretz@kde.org>

dump_type on 'const std::string' should not print 'const string' unless
TFF_UNQUALIFIED_NAME is requested.

gcc/cp/ChangeLog:
	* error.c: Call dump_scope when printing a typedef.
---
 gcc/cp/error.c | 2 ++
 1 file changed, 2 insertions(+)
Jakub Jelinek via Gcc-patches May 14, 2021, 4:05 p.m. | #6
On 5/4/21 5:13 AM, Matthias Kretz wrote:
> From: Matthias Kretz<kretz@kde.org>

> 

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


There are other mechanisms to change symbol names such as macros,
typedefs or alias or using declarations, and at the object level,
ELF aliases, and even the C++ ABI with its substitutions for types
like std::string and with the abi_tag attribute.

They all have a potential to cause some confusion when a symbol with
one name is referred to by another.  For the syntactic mechanisms
compilers usually deal with the problem by mentioning both the macro
or alias name and its expansion or target in the diagnostics they
issue.  The renaming prescribed by the C++ is reflected in the mangled
symbols that demanglers know how to decode and restore the their
originals.  For ELF aliases no such solution exists but they are
used only rarely by well-tested system code and so the different
names don't typically come up in compiler (or even linker)
diagnostics.  When they do, the names often share some common
prefix or suffix so the confusion is minimal.

I like the idea of printing names in diagnostics that are familiar
to users while leaving out implementation details that's in most
cases irrelevant to them.

At the same time, my concern with adding another syntactic renaming
mechanism that's specifically intended to change symbol names in
diagnostics (and the two macros) but nowhere else is that it would
considerably raise the risk of confusion between the name in
the source and the name in the diagnostic.  (E.g., one source
file renames symbol Foo to Bar but another one doesn't; messages
from one file will refer to Foo and other to Bar with no way of
knowing they're the same.  This could be solved by printing both
the renamed symbol and what it stands for (like for typedefs or
template instantiations) but that would then increase the S/R
ratio this solution is aimed at improving.  Providing an option
to disable the renaming is great but suffers from the same problem:
to be sure what symbol is being referred to, users would have to
disable the renaming.

It doesn't seem like we can have it both ways.  But maybe indicating
in some way when a symbol mentioned in a diagnostic is the result of
renaming by the attribute, without spelling out the original name,
would be a good enough compromise.  Though that won't help with
the same problem in the expansion of macros like __FUNCTION__.

Other than that, I have a couple of questions:

Are there any implementations that provide this attribute?  (If
so does the syntax and effects match?  It would be nice to be
compatible if possible.)

Does the attribute change the typeinfo strings?  If not, did you
consider the interplay between compile-time and runtime diagnostics
involving names decorated with the attribute?  (A related concern
is the runtime output of messages involving macros like
__FUNCTION__ issued from object files compiled by different
compilers or versions of the same compiler.)

Below are a few comments on the changes below, mostly focused on
the phrasing and style of diagnostic messages.  As others already
mentioned, the patch should include tests (including them early
in the review process helps get an idea of how the whole feature
works).

> 

> On Tuesday, 27 April 2021 11:46:48 CEST Jonathan Wakely wrote:

>> I think it's a great idea and would like to use it for all the TS

>> implementations where there is some inline namespace that the user

>> doesn't care about. std::experimental::fundamentals_v1:: would be much

>> better as just std::experimental::, or something like std::[LFTS]::.

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

> 

> 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

> 	* 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_aggr_type): If the type has a diagnose_as attribute, print

> 	the associated string instead of printing the original type

> 	name.

> 	(dump_simple_decl): Call dump_decl_name_or_diagnose_as instead

> 	of dump_decl.

> 	(dump_decl): Ditto.

> 	(lang_decl_name): Ditto.

> 	(dump_function_decl): Ensure complete replacement of the class

> 	template diagnostics if a diagnose_as attribute is present.

> 	(dump_function_name): Replace the function diagnostic output if

> 	the diagnose_as attribute is set.

> 	* name-lookup.c (handle_namespace_attrs): Handle the diagnose_as

> 	attribute. Ensure exactly one string argument. Ensure previous

> 	diagnose_as attributes used the same name.

> 	* 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 and the TREE_TYPE is an implicit class template

> 	instantiation, call decl_attributes to add the diagnose_as

> 	attribute to the TREE_TYPE.

> ---

>   gcc/c-family/c.opt   |   4 ++

>   gcc/cp/error.c       |  85 ++++++++++++++++++++++++++++---

>   gcc/cp/name-lookup.c |  27 ++++++++++

>   gcc/cp/tree.c        | 117 +++++++++++++++++++++++++++++++++++++++++++

>   gcc/doc/extend.texi  |  37 ++++++++++++++

>   gcc/doc/invoke.texi  |   9 +++-

>   6 files changed, 270 insertions(+), 9 deletions(-)

> 

> 

> --

> ──────────────────────────────────────────────────────────────────────────

>   Dr. Matthias Kretzhttps://mattkretz.github.io

>   GSI Helmholtz Centre for Heavy Ion Researchhttps://gsi.de

>   std::experimental::simdhttps://github.com/VcDevel/std-simd

> ──────────────────────────────────────────────────────────────────────────

> 

> 

> 0001-Add-gnu-diagnose_as-attribute.patch

> 

> diff --git a/gcc/c-family/c.opt b/gcc/c-family/c.opt

> index 3f8b72cdc00..0cf01c6dba4 100644

> --- a/gcc/c-family/c.opt

> +++ b/gcc/c-family/c.opt

> @@ -1582,6 +1582,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/error.c b/gcc/cp/error.c

> index c88d1749a0f..10b547afaa7 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);

>   	}

>       }

> @@ -743,6 +753,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)

> @@ -769,8 +780,15 @@ dump_aggr_type (cxx_pretty_printer *pp, tree t, int flags)

>         if (! (flags & TFF_UNQUALIFIED_NAME))

>   	dump_scope (pp, CP_DECL_CONTEXT (decl), flags | TFF_SCOPE);

>         flags &= ~TFF_UNQUALIFIED_NAME;

> +

> +      tree diagnose_as_specialized = NULL_TREE;

>         if (tmplate)

>   	{

> +	  if (flag_diagnostics_use_aliases)

> +	    diagnose_as_specialized

> +	      = lookup_attribute ("diagnose_as",

> +				  TYPE_ATTRIBUTES (TREE_TYPE (decl)));

> +

>   	  /* Because the template names are mangled, we have to locate

>   	     the most general template, and use that name.  */

>   	  tree tpl = TYPE_TI_TEMPLATE (t);

> @@ -779,9 +797,25 @@ 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)))

> +	 {

> +	   pp_cxx_ws_string (

> +	     pp, TREE_STRING_POINTER (

> +		   TREE_VALUE (TREE_VALUE (diagnose_as_specialized))));

> +	   return;

> +	 }

>       }

>   

> -  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"));

> @@ -1103,7 +1137,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>"));

> @@ -1147,6 +1181,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)

> +	{

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

> @@ -1192,7 +1245,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);

> @@ -1374,7 +1427,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

> @@ -1393,7 +1446,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, "...");

>         }

> @@ -1657,7 +1710,13 @@ dump_function_decl (cxx_pretty_printer *pp, tree t, int flags)

>     /* Pretty print template instantiations only.  */

>     if (DECL_USE_TEMPLATE (t) && DECL_TEMPLATE_INFO (t)

>         && !(flags & TFF_NO_TEMPLATE_BINDINGS)

> -      && flag_pretty_templates)

> +      && flag_pretty_templates

> +      // skip the output of template parameters if the function is a member of a

> +      // class with diagnose_as attribute:

> +      && !(flag_diagnostics_use_aliases

> +	     && DECL_CLASS_SCOPE_P (t)

> +	     && lookup_attribute ("diagnose_as",

> +				  TYPE_ATTRIBUTES (DECL_CONTEXT (t)))))

>       {

>         tree tmpl;

>   

> @@ -1885,6 +1944,16 @@ dump_exception_spec (cxx_pretty_printer *pp, tree t, int flags)

>   static void

>   dump_function_name (cxx_pretty_printer *pp, tree t, int flags)

>   {

> +  if (flag_diagnostics_use_aliases)

> +    {

> +      tree attr = lookup_attribute ("diagnose_as", DECL_ATTRIBUTES (t));

> +      if (attr)

> +	{

> +	  pp_cxx_ws_string (

> +	    pp, TREE_STRING_POINTER (TREE_VALUE (TREE_VALUE (attr))));

> +	  return;

> +	}

> +    }

>     tree name = DECL_NAME (t);

>   

>     /* We can get here with a decl that was synthesized by language-

> @@ -3133,7 +3202,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);

>   }

> diff --git a/gcc/cp/name-lookup.c b/gcc/cp/name-lookup.c

> index 4e84e2f9987..80637503310 100644

> --- a/gcc/cp/name-lookup.c

> +++ b/gcc/cp/name-lookup.c

> @@ -6082,6 +6082,33 @@ 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 (!args || TREE_CHAIN(args))

> +	    {

> +	      error ("the %qE attribute requires exactly one argument", name);


Other diagnostics involving attributes do not start with an article.
Can you please drop the "the"?  (In general, I would suggest to either
reuse or follow the style of existing diagnostics issued from the same
file, and/or look at those in gcc/po/gcc.pot.  Not nearly all of then
are consistent with one another but it's best to avoid introducing
yet another style; it will make converging on a single style easier,
and reduces the effort involved in translating messages).

> +	      continue;

> +	    }

> +	  if (TREE_CODE (TREE_VALUE (args)) != STRING_CST)

> +	    {

> +	      error ("the argument to the %qE attribute must be a string "

> +		    "literal", name);


Similarly here, recommend to follow one of the existing styles (see
c-family/c-attribs.c) rather than adding another variation to the mix.

> +	      continue;

> +	    }

> +	  tree existing

> +	    = lookup_attribute ("diagnose_as", DECL_ATTRIBUTES (ns));

> +	  if (existing

> +		&& !cp_tree_equal(TREE_VALUE (args),

> +				  TREE_VALUE (TREE_VALUE (existing))))

> +	    {

> +	      error ("the namespace %qE already uses a different diagnose_as "

> +		     "attribute value", ns);


Here too, please drop the article.  Also, please quote the attribute
name (using %qs or %<diagnose_as%>).


> +	      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",

> diff --git a/gcc/cp/tree.c b/gcc/cp/tree.c

> index a8bfd5fc053..f7b93dc89d7 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.  */

> @@ -4860,6 +4861,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", 1, 1, false, false, false, false,

> +    handle_diagnose_as_attribute, NULL },

>     { NULL, 0, 0, false, false, false, false, NULL, NULL }

>   };

>   

> @@ -5128,6 +5131,120 @@ handle_abi_tag_attribute (tree* node, tree name, tree args,

>     return NULL_TREE;

>   }

>   

> +static bool

> +check_diagnose_as_redeclaration (const_tree decl, 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;

> +  error ("conflicting declaration of %<diagnose_as%> attribute on %qD would "

> +	 "overwrite %qE with %qE", decl, old_value, new_value);


There are a few messages GCC issues to diagnose attribute conflicts.
Conflicts between different attributes are usually diagnosed by
warnings:
msgid "ignoring attribute %qE because it conflicts with attribute %qs"
but some conflicts cause errors:
msgid "section of %q+D conflicts with previous declaration"

Either way, I would again recommend adopting one of the existing styles
for consistency rather than introducing a new one.

> +  return false;

> +}

> +

> +static tree

> +handle_diagnose_as_attribute (tree* node, tree name, tree args,

> +			      int flags, bool* no_add_attrs)

> +{

> +  tree decl = NULL_TREE;

> +  if (!args || TREE_CHAIN (args))

> +    {

> +      error ("the %qE attribute requires exactly one argument", name);

> +      goto fail;

> +    }

> +  if (TREE_CODE (TREE_VALUE (args)) != STRING_CST)

> +    {

> +      error ("the argument to the %qE attribute must be a string literal",

> +	     name);


Same comment here.  E.g.,
msgid "%qE attribute argument must be a string constant"
in the same file.

> +      goto fail;

> +    }

> +  if (TREE_CODE (*node) == TYPE_DECL)

> +    {

> +      // Apply the attribute to the type alias itself.

> +      decl = *node;

> +      tree type = TREE_TYPE (*node);

> +      if (CLASS_TYPE_P (type) && CLASSTYPE_IMPLICIT_INSTANTIATION (type))

> +	{

> +	  if (COMPLETE_OR_OPEN_TYPE_P (type))

> +	    warning (OPT_Wattributes, "%qE attribute cannot be applied to %qT "

> +				      "after its instantiation", name, type);


Ditto here:
msgid "ignoring %qE attribute applied to template instantiation %qT"


> +	  else

> +	    {

> +	      type = strip_typedefs(type, nullptr, 0);

> +	      // And apply the attribute to the specialization on the RHS.

> +	      tree attributes = tree_cons (name, args, NULL_TREE);

> +	      decl_attributes (&type, attributes,

> +			       ATTR_FLAG_TYPE_IN_PLACE, NULL_TREE);

> +	    }

> +	}

> +    }

> +  else if (TYPE_P (*node))

> +    {

> +      if (!OVERLOAD_TYPE_P (*node))

> +	{

> +	  error ("%qE attribute applied to non-class, non-enum type %qT",

> +		 name, *node);

> +	  goto fail;

> +	}

> +      else if (!(flags & (int)ATTR_FLAG_TYPE_IN_PLACE))

> +	{

> +	  error ("%qE attribute applied to %qT after its definition",

> +		 name, *node);

> +	  goto fail;

> +	}

> +      decl = TYPE_NAME (*node);

> +    }

> +  else

> +    {

> +      if (!VAR_OR_FUNCTION_DECL_P (*node))

> +	{

> +	  error ("%qE attribute applied to non-function, non-variable %qD",

> +		 name, *node);

> +	  goto fail;

> +	}

> +      else if (DECL_LANGUAGE (*node) == lang_c)

> +	{

> +	  error ("%qE attribute applied to extern \"C\" declaration %qD",


Please quote extern "C" (as "%<extern \"C\"%>).

> +		 name, *node);

> +	  goto fail;

> +	}

> +      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, 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 c8caf36f293..7976e3a72d4 100644

> --- a/gcc/doc/extend.texi

> +++ b/gcc/doc/extend.texi

> @@ -2865,6 +2865,43 @@ 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}")

> +@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 __FUNCTION__ /

> +__PRETTY_FUNCTION__ macros.


Presumably also __func__.  Symbol names should be quoted in @code{}.


  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.


If the attribute doesn't change typeinfo it might be worth mentioning
in the documentation.

Martin

> +

> +@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, functions,

> +variables, alias declarations (but not alias templates), and user-defined types

> +(classes, unions, and enums). If the alias declaration aliases an implicit class

> +template specialization, the attribute is additionally applied to the class

> +template specialization.

> +

> +@smallexample

> +template <class T> struct basic_string @{@};

> +using string [[gnu::diagnose_as("string")]] = 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 f5a9dc33819..84c19344c89 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}.

> @@ -5077,6 +5078,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.

>

Patch

diff --git a/gcc/c-family/c.opt b/gcc/c-family/c.opt
index 3f8b72cdc00..0cf01c6dba4 100644
--- a/gcc/c-family/c.opt
+++ b/gcc/c-family/c.opt
@@ -1582,6 +1582,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/error.c b/gcc/cp/error.c
index c88d1749a0f..10b547afaa7 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);
 	}
     }
@@ -743,6 +753,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)
@@ -769,8 +780,15 @@  dump_aggr_type (cxx_pretty_printer *pp, tree t, int flags)
       if (! (flags & TFF_UNQUALIFIED_NAME))
 	dump_scope (pp, CP_DECL_CONTEXT (decl), flags | TFF_SCOPE);
       flags &= ~TFF_UNQUALIFIED_NAME;
+
+      tree diagnose_as_specialized = NULL_TREE;
       if (tmplate)
 	{
+	  if (flag_diagnostics_use_aliases)
+	    diagnose_as_specialized
+	      = lookup_attribute ("diagnose_as",
+				  TYPE_ATTRIBUTES (TREE_TYPE (decl)));
+
 	  /* Because the template names are mangled, we have to locate
 	     the most general template, and use that name.  */
 	  tree tpl = TYPE_TI_TEMPLATE (t);
@@ -779,9 +797,25 @@  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)))
+	 {
+	   pp_cxx_ws_string (
+	     pp, TREE_STRING_POINTER (
+		   TREE_VALUE (TREE_VALUE (diagnose_as_specialized))));
+	   return;
+	 }
     }
 
-  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"));
@@ -1103,7 +1137,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>"));
@@ -1147,6 +1181,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)
+	{
+	  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
@@ -1192,7 +1245,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);
@@ -1374,7 +1427,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
@@ -1393,7 +1446,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, "...");
       }
@@ -1657,7 +1710,13 @@  dump_function_decl (cxx_pretty_printer *pp, tree t, int flags)
   /* Pretty print template instantiations only.  */
   if (DECL_USE_TEMPLATE (t) && DECL_TEMPLATE_INFO (t)
       && !(flags & TFF_NO_TEMPLATE_BINDINGS)
-      && flag_pretty_templates)
+      && flag_pretty_templates
+      // skip the output of template parameters if the function is a member of a
+      // class with diagnose_as attribute:
+      && !(flag_diagnostics_use_aliases
+	     && DECL_CLASS_SCOPE_P (t)
+	     && lookup_attribute ("diagnose_as",
+				  TYPE_ATTRIBUTES (DECL_CONTEXT (t)))))
     {
       tree tmpl;
 
@@ -1885,6 +1944,16 @@  dump_exception_spec (cxx_pretty_printer *pp, tree t, int flags)
 static void
 dump_function_name (cxx_pretty_printer *pp, tree t, int flags)
 {
+  if (flag_diagnostics_use_aliases)
+    {
+      tree attr = lookup_attribute ("diagnose_as", DECL_ATTRIBUTES (t));
+      if (attr)
+	{
+	  pp_cxx_ws_string (
+	    pp, TREE_STRING_POINTER (TREE_VALUE (TREE_VALUE (attr))));
+	  return;
+	}
+    }
   tree name = DECL_NAME (t);
 
   /* We can get here with a decl that was synthesized by language-
@@ -3133,7 +3202,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);
 }
diff --git a/gcc/cp/name-lookup.c b/gcc/cp/name-lookup.c
index 4e84e2f9987..80637503310 100644
--- a/gcc/cp/name-lookup.c
+++ b/gcc/cp/name-lookup.c
@@ -6082,6 +6082,33 @@  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 (!args || TREE_CHAIN(args))
+	    {
+	      error ("the %qE attribute requires exactly one argument", name);
+	      continue;
+	    }
+	  if (TREE_CODE (TREE_VALUE (args)) != STRING_CST)
+	    {
+	      error ("the argument to the %qE attribute must be a string "
+		    "literal", name);
+	      continue;
+	    }
+	  tree existing
+	    = lookup_attribute ("diagnose_as", DECL_ATTRIBUTES (ns));
+	  if (existing
+		&& !cp_tree_equal(TREE_VALUE (args),
+				  TREE_VALUE (TREE_VALUE (existing))))
+	    {
+	      error ("the namespace %qE already uses a different diagnose_as "
+		     "attribute value", ns);
+	      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",
diff --git a/gcc/cp/tree.c b/gcc/cp/tree.c
index a8bfd5fc053..f7b93dc89d7 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.  */
@@ -4860,6 +4861,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", 1, 1, false, false, false, false,
+    handle_diagnose_as_attribute, NULL },
   { NULL, 0, 0, false, false, false, false, NULL, NULL }
 };
 
@@ -5128,6 +5131,120 @@  handle_abi_tag_attribute (tree* node, tree name, tree args,
   return NULL_TREE;
 }
 
+static bool
+check_diagnose_as_redeclaration (const_tree decl, 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;
+  error ("conflicting declaration of %<diagnose_as%> attribute on %qD would "
+	 "overwrite %qE with %qE", decl, old_value, new_value);
+  return false;
+}
+
+static tree
+handle_diagnose_as_attribute (tree* node, tree name, tree args,
+			      int flags, bool* no_add_attrs)
+{
+  tree decl = NULL_TREE;
+  if (!args || TREE_CHAIN (args))
+    {
+      error ("the %qE attribute requires exactly one argument", name);
+      goto fail;
+    }
+  if (TREE_CODE (TREE_VALUE (args)) != STRING_CST)
+    {
+      error ("the argument to the %qE attribute must be a string literal",
+	     name);
+      goto fail;
+    }
+  if (TREE_CODE (*node) == TYPE_DECL)
+    {
+      // Apply the attribute to the type alias itself.
+      decl = *node;
+      tree type = TREE_TYPE (*node);
+      if (CLASS_TYPE_P (type) && CLASSTYPE_IMPLICIT_INSTANTIATION (type))
+	{
+	  if (COMPLETE_OR_OPEN_TYPE_P (type))
+	    warning (OPT_Wattributes, "%qE attribute cannot be applied to %qT "
+				      "after its instantiation", name, type);
+	  else
+	    {
+	      type = strip_typedefs(type, nullptr, 0);
+	      // And apply the attribute to the specialization on the RHS.
+	      tree attributes = tree_cons (name, args, NULL_TREE);
+	      decl_attributes (&type, attributes,
+			       ATTR_FLAG_TYPE_IN_PLACE, NULL_TREE);
+	    }
+	}
+    }
+  else if (TYPE_P (*node))
+    {
+      if (!OVERLOAD_TYPE_P (*node))
+	{
+	  error ("%qE attribute applied to non-class, non-enum type %qT",
+		 name, *node);
+	  goto fail;
+	}
+      else if (!(flags & (int)ATTR_FLAG_TYPE_IN_PLACE))
+	{
+	  error ("%qE attribute applied to %qT after its definition",
+		 name, *node);
+	  goto fail;
+	}
+      decl = TYPE_NAME (*node);
+    }
+  else
+    {
+      if (!VAR_OR_FUNCTION_DECL_P (*node))
+	{
+	  error ("%qE attribute applied to non-function, non-variable %qD",
+		 name, *node);
+	  goto fail;
+	}
+      else if (DECL_LANGUAGE (*node) == lang_c)
+	{
+	  error ("%qE attribute applied to extern \"C\" declaration %qD",
+		 name, *node);
+	  goto fail;
+	}
+      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, 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 c8caf36f293..7976e3a72d4 100644
--- a/gcc/doc/extend.texi
+++ b/gcc/doc/extend.texi
@@ -2865,6 +2865,43 @@  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}")
+@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 __FUNCTION__ /
+__PRETTY_FUNCTION__ macros. 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.
+
+@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, functions,
+variables, alias declarations (but not alias templates), and user-defined types
+(classes, unions, and enums). If the alias declaration aliases an implicit class
+template specialization, the attribute is additionally applied to the class
+template specialization.
+
+@smallexample
+template <class T> struct basic_string @{@};
+using string [[gnu::diagnose_as("string")]] = 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 f5a9dc33819..84c19344c89 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}.
@@ -5077,6 +5078,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.