From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: from lxmtout1.gsi.de (lxmtout1.gsi.de [140.181.3.111]) by sourceware.org (Postfix) with ESMTPS id F1132385782E; Wed, 26 May 2021 21:33:17 +0000 (GMT) DMARC-Filter: OpenDMARC Filter v1.3.2 sourceware.org F1132385782E Authentication-Results: sourceware.org; dmarc=none (p=none dis=none) header.from=gsi.de Authentication-Results: sourceware.org; spf=pass smtp.mailfrom=M.Kretz@gsi.de Received: from localhost (localhost [127.0.0.1]) by lxmtout1.gsi.de (Postfix) with ESMTP id A4AEE2050D02; Wed, 26 May 2021 23:33:16 +0200 (CEST) X-Virus-Scanned: Debian amavisd-new at lxmtout1.gsi.de Received: from lxmtout1.gsi.de ([127.0.0.1]) by localhost (lxmtout1.gsi.de [127.0.0.1]) (amavisd-new, port 10024) with LMTP id hI5l6AcqgJbH; Wed, 26 May 2021 23:33:16 +0200 (CEST) Received: from srvex3.campus.gsi.de (unknown [10.10.4.16]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-SHA256 (128/128 bits)) (No client certificate requested) by lxmtout1.gsi.de (Postfix) with ESMTPS id 827EA2050D00; Wed, 26 May 2021 23:33:16 +0200 (CEST) Received: from excalibur.localnet (140.181.3.12) by srvex3.campus.gsi.de (10.10.4.16) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256_P256) id 15.1.2242.10; Wed, 26 May 2021 23:33:15 +0200 From: Matthias Kretz To: , Martin Sebor CC: , David Malcolm Subject: Re: [PATCH] Add gnu::diagnose_as attribute Date: Wed, 26 May 2021 23:33:15 +0200 Message-ID: <4182615.gU163fZyj2@excalibur> Organization: GSI Helmholtzzentrum =?UTF-8?B?ZsO8cg==?= Schwerionenforschung In-Reply-To: References: <14205410.xuKvIAzr1H@excalibur> <91863212.B8guWdUDZo@excalibur> MIME-Version: 1.0 Content-Type: multipart/mixed; boundary="nextPart24578775.GzJpooMjnH" Content-Transfer-Encoding: 7Bit X-Originating-IP: [140.181.3.12] X-ClientProxiedBy: srvex1.Campus.gsi.de (10.10.4.11) To srvex3.campus.gsi.de (10.10.4.16) X-Spam-Status: No, score=-10.9 required=5.0 tests=BAYES_00, BODY_8BITS, GIT_PATCH_0, KAM_DMARC_STATUS, SPF_HELO_PASS, SPF_PASS, TXREP, URIBL_SBL, URIBL_SBL_A autolearn=ham autolearn_force=no version=3.4.2 X-Spam-Checker-Version: SpamAssassin 3.4.2 (2018-09-13) on server2.sourceware.org X-BeenThere: libstdc++@gcc.gnu.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: Libstdc++ mailing list List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , X-List-Received-Date: Wed, 26 May 2021 21:33:24 -0000 --nextPart24578775.GzJpooMjnH Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="UTF-8" On Friday, 14 May 2021 18:05:03 CEST Martin Sebor wrote: > [...] >=20 > 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. I agree with your concern. This attribute is easy to use for obfuscation an= d=20 slightly harder to use in a significantly helpful way. If you've ever had t= o=20 parse diagnostic output involving=20 std::experimental::parallelism_v2::simd (when your source says ``` namespace stdx =3D std::experimental; stdx::simd ``` you know the potential of this attribute. > 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__. I've been thinking about it. It would not be helpful to always display the= =20 original names when diagnose_as overrides something. But maybe there could = be=20 like a glossary at the bottom of the diagnostic output? Or simply a "note:= =20 Some names above do not reflect their real names in the source. Use -fno- diagnostics-use-aliases to disable the replacement." > Other than that, I have a couple of questions: >=20 > 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.) There exists nothing like it AFAIK. > 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? Good question. From all my tests (and my understanding how typeinfo strings= =20 are construted) the attribute has no effect on it. From one of my tests: `X0::X3` is diagnosed as "[int|int]::X.3", and `typeid(X0::X3).name()` is "N2X0IiiE2X3E" which is "X0::X3" again. I=20 experienced the difference between compile-time and runtime diagnostics mys= elf=20 (i.e. objdump and gdb disagree with diagnostic output) when working on=20 stdx::simd (I've been using the attribute since March with stdx::simd). > (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.) Right, if you make __FUNCTION__ part of your ABI or communication protocol= =20 then the attribute can create incompatibilities. I'm not sure we should car= e=20 about this part too much. Note that -fpretty-templates also changes the=20 __PRETTY_FUNCTION__ string. While I plan to submit a patch for std::__cxx11::basic_string to e.g. turn 'template std::__cxx11::basic_string<_CharT, _Traits,=20 _Alloc>::_If_sv<_Tp, std::__cxx11::basic_string<_CharT, _Traits, _Alloc>&>= =20 std::__cxx11::basic_string<_CharT, _Traits,=20 _Alloc>::insert(std::__cxx11::basic_string<_CharT, _Traits,=20 _Alloc>::size_type, const _Tp&, std::__cxx11::basic_string<_CharT, _Traits,= =20 _Alloc>::size_type, std::__cxx11::basic_string<_CharT, _Traits,=20 _Alloc>::size_type) [with _Tp =3D _Tp; _CharT =3D char; _Traits =3D=20 std::char_traits; _Alloc =3D std::allocator]' into 'template std::string::_If_sv<_Tp, std::string&>=20 std::string::insert<_Tp>(std::string::size_type, const _Tp&,=20 std::string::size_type, std::string::size_type)' , i.e. affecting close to all C++ users, I don't believe the accumulated=20 headache will increase. ;-) > > [...] > > 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). > [...] Will do. Thanks for your detailed comments on this topic. Very helpful =F0= =9F=91=8D. > > + continue; > > + } > > + if (TREE_CODE (TREE_VALUE (args)) !=3D STRING_CST) > > + { > > + error ("the argument to the %qE attribute must be a string " > > + "literal", name); >=20 > Similarly here, recommend to follow one of the existing styles (see > c-family/c-attribs.c) rather than adding another variation to the mix. The visibility attribute on namespaces says: "%qD attribute requires a sing= le=20 NTBS argument". So I copied that (and its logic) for now. However, I believ= e=20 the use of "NTBS" is not very user friendly. > > + 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= =20 " > > + "after its instantiation", name, type); >=20 > Ditto here: > msgid "ignoring %qE attribute applied to template instantiation %qT" Ah, here I want to be more precise. Because the attribute can be applied to= a=20 template instantiation. But only before its instantiation. Example: template struct X {}; using [[gnu::diagnose_as("XX")]] XX =3D X; // OK template struct X; using [[gnu::diagnose_as("XY")]] XY =3D X; // not OK msgid "ignoring %qE attribute applied to template %qT after instantiation" OK? > > + error ("%qE attribute applied to extern \"C\" declaration %qD", >=20 > Please quote extern "C" (as "%). OK. However the msgid was copied from handle_abi_tag_attribute above. New patch (and ChangeLog) below: =46rom: Matthias Kretz 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. 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. * 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. (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. 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/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. =2D-- gcc/c-family/c.opt | 4 + gcc/cp/cp-tree.h | 5 +- gcc/cp/error.c | 235 +++++++++++++++++- gcc/cp/name-lookup.c | 25 ++ gcc/cp/tree.c | 116 +++++++++ gcc/doc/extend.texi | 40 +++ gcc/doc/invoke.texi | 9 +- .../g++.dg/diagnostic/diagnose-as1.C | 177 +++++++++++++ .../g++.dg/diagnostic/diagnose-as2.C | 144 +++++++++++ .../g++.dg/diagnostic/diagnose-as3.C | 152 +++++++++++ .../g++.dg/diagnostic/diagnose-as4.C | 158 ++++++++++++ 11 files changed, 1052 insertions(+), 13 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 =2D- =E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2= =94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94= =80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80= =E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2= =94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94= =80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80= =E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2= =94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94= =80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80 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 =E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2= =94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94= =80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80= =E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2= =94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94= =80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80= =E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2= =94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94= =80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80 --nextPart24578775.GzJpooMjnH Content-Disposition: inline; filename="0004-Add-gnu-diagnose_as-attribute.patch" Content-Transfer-Encoding: 7Bit Content-Type: text/x-patch; charset="utf-8"; name="0004-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/cp-tree.h b/gcc/cp/cp-tree.h index cb254e0adea..139ef8f1c0d 100644 --- a/gcc/cp/cp-tree.h +++ b/gcc/cp/cp-tree.h @@ -5858,7 +5858,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) @@ -5876,6 +5878,7 @@ enum auto_deduction_context #define TFF_NO_OMIT_DEFAULT_TEMPLATE_ARGUMENTS (1 << 12) #define TFF_NO_TEMPLATE_BINDINGS (1 << 13) #define TFF_POINTER (1 << 14) +#define TFF_AS_PRIMARY (1 << 15) /* These constants can be used as bit flags to control strip_typedefs. diff --git a/gcc/cp/error.c b/gcc/cp/error.c index b0836d83888..1950ffb8ab6 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); } } @@ -760,6 +770,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'. */ @@ -777,6 +826,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) @@ -803,8 +853,14 @@ 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 (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); @@ -813,9 +869,54 @@ 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 struct [[gnu::diagnose_as("AA")]] A { + struct [[gnu::diagnose_as("BB")]] B {}; + }; + + A::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 (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_(""); } 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_("")); @@ -1181,6 +1282,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 @@ -1226,7 +1346,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); @@ -1408,7 +1528,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 @@ -1427,7 +1547,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, "..."); } @@ -1667,7 +1787,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; @@ -1684,6 +1805,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) @@ -1695,8 +1821,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 struct A { + template struct B; + template struct B { + void f(); + }; + }; + using Achar [[gnu::diagnose_as("Achar")]] = A; + + Now Achar::B::f() needs to print as "Achar::B::f() + [with U = int]". However, DECL_CONTEXT (t) would print as + "Achar::B" and dump_aggr_type has no sensible way to + retrieve A::B. tmpl was generalized to A::B::f + and thus dump_aggr_type (DECL_CONTEXT (tmpl)) would print + "A::B [with T = char, U = int]". I.e. the diagnose_as + attribute on the A 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; } } @@ -1743,6 +1907,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); @@ -1751,7 +1934,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)) { @@ -1931,6 +2114,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)) @@ -1985,6 +2176,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); @@ -3164,7 +3357,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); } @@ -3949,6 +4142,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) @@ -4048,8 +4248,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 4e84e2f9987..7782fd09ead 100644 --- a/gcc/cp/name-lookup.c +++ b/gcc/cp/name-lookup.c @@ -6082,6 +6082,31 @@ 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) + || 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", diff --git a/gcc/cp/tree.c b/gcc/cp/tree.c index a8bfd5fc053..251f6db4055 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,119 @@ 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; + 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 (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_TEMPLATE_INSTANTIATION (type)) + { + if (COMPLETE_OR_OPEN_TYPE_P (type)) + warning (OPT_Wattributes, + "ignoring %qE attribute applied to template %qT after " + "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 % 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, 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 c8caf36f293..da8928fc667 100644 --- a/gcc/doc/extend.texi +++ b/gcc/doc/extend.texi @@ -2865,6 +2865,46 @@ 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 @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. + +@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 (before instantiation), the attribute is additionally +applied to the class template specialization. Whether the attribute has an +effect on partial template specializations is unspecified and may depend on +several different factors. + +@smallexample +template struct basic_string @{@}; +using string [[gnu::diagnose_as("string")]] = basic_string; +int f(basic_string) @{@} +// 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. 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..ca9db2b3a5c --- /dev/null +++ b/gcc/testsuite/g++.dg/diagnostic/diagnose-as1.C @@ -0,0 +1,177 @@ +// { dg-options "-fdiagnostics-use-aliases -fpretty-templates" } + +#ifdef __cpp_constexpr +template + constexpr bool is_char() { return false; } + +template <> + constexpr bool is_char() { return true; } + +template + constexpr bool is_int() { return false; } + +template <> + constexpr bool is_int() { return true; } +#endif + +namespace foo0 __attribute__((diagnose_as())) {} // { dg-warning "'diagnose_as' attribute requires a single NTBS argument" } + +namespace foo1 __attribute__((diagnose_as("foo2"))) {} // { dg-line foo1 } +namespace foo1 __attribute__((diagnose_as("XXXX"))) {} // { 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-error "wrong number of arguments specified for 'diagnose_as' attribute" } + class __attribute__((diagnose_as(1))) Z1; // { dg-warning "'diagnose_as' attribute requires a single NTBS argument" } + class __attribute__((diagnose_as("a", "b"))) Z2; // { dg-error "wrong number of arguments specified for 'diagnose_as' attribute" } + + template class A0 {}; + typedef A0 Achar __attribute__((diagnose_as("Achar"))); + template class A0; + typedef A0 Aint __attribute__((diagnose_as("Aint"))); // { dg-warning "ignoring 'diagnose_as' attribute applied to template 'foo2::A0' after instantiation" } +} + +namespace A +{ + inline namespace B __attribute__((diagnose_as("@1"))) + { + template + struct __attribute__((diagnose_as("@2"))) C + { + __attribute__((diagnose_as("fun:1"))) + void f() {} // { dg-line ABC_f } + + template + __attribute__((diagnose_as("fun:2"))) + void g(T, U) {} // { dg-line ABC_gT } + + template + __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() ? "void @1::@3::fun:3()" + : "void @1::@2::fun:3() [with U = char]"; + static_assert(__builtin_strcmp(__PRETTY_FUNCTION__, ref) == 0, ""); +#endif + } + + template + __attribute__((diagnose_as("fun:4"))) + void ht() + { +#ifdef __cpp_constexpr + static_assert(__builtin_strcmp(__FUNCTION__, "fun:4") == 0, ""); + constexpr const char* ref + = is_int() + ? "void @1::@3::fun:4() [with T = float]" + : "void @1::@2::fun:4() [with T = float; U = char]"; + static_assert(__builtin_strcmp(__PRETTY_FUNCTION__, ref) == 0, ""); +#endif + } + + template + 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() + ? is_char() + ? "static void @1::@3::@6::fun:5()" + : "static void @1::@3::@5::fun:5() [with T0 = float; T1 = short int]" + : is_char() + ? "static void @1::@2::@6::fun:5() [with U = char]" + : "static void @1::@2::@5::fun:5() [with T0 = float; T1 = short int; U = char]"; + static_assert(__builtin_strcmp(__PRETTY_FUNCTION__, ref) == 0, + ""); +#endif + } + }; + typedef E F __attribute__((diagnose_as("@6"))); + + template + struct __attribute__((diagnose_as("@7"))) E + { + __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() + ? "static void @1::@3::@7::fun:6() [with T = float]" + : "static void @1::@2::@7::fun:6() [with T = float; U = char]"; + static_assert(__builtin_strcmp(__PRETTY_FUNCTION__, ref) == 0, + ""); +#endif + } + }; + }; + + typedef C D __attribute__((diagnose_as("@3"))); + + template <> + struct __attribute__((diagnose_as("@4"))) C + { + }; + } +} + +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 ()); // { dg-error "cannot convert '@1::@3' to 'int'" } + fn_1 (A::C ()); // { dg-error "cannot convert '@1::@2' to 'int'" } + fn_1 (A::C ()); // { dg-error "cannot convert '@1::@4' to 'int'" } + + A::C().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().f(0); // { dg-error "no matching function for call to '@1::@2::f\\(int\\)'" } + // { dg-message "candidate: 'void @1::@2::fun:1\\(\\) \\\[with U = char\\\]'" "" { target *-*-* } ABC_f } + + A::C().f(0); // { dg-error "'struct @1::@4' has no member named 'f'" } + + A::C().g(); // { dg-error "no matching function for call to '@1::@3::g\\(\\)'" } + // { dg-message "candidate: 'template void @1::@3::fun:2\\(T, U\\)'" "" { target *-*-* } ABC_gT } + // { dg-message "candidate: 'template void @1::@3::fun:2.2\\(\\)'" "" { target *-*-* } ABC_g } + + A::C().g(); // { dg-error "no matching function for call to '@1::@2::g\\(\\)'" } + // { dg-message "candidate: 'template void @1::@2::fun:2\\(T, U\\) \\\[with U = char\\\]'" "" { target *-*-* } ABC_gT } + // { dg-message "candidate: 'template void @1::@2::fun:2.2\\(\\) \\\[with U = char\\\]'" "" { target *-*-* } ABC_g } + + A::C().h(); + A::C().h(); + A::C().ht(); + A::C().ht(); + A::C::E::f(); + A::C::E::f(); + A::C::E::f(); + A::C::E::f(); + A::C::F::f(); + A::C::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 + constexpr bool is_char() { return false; } + +template <> + constexpr bool is_char() { return true; } + +template + constexpr bool is_int() { return false; } + +template <> + constexpr bool is_int() { return true; } +#endif + +namespace A +{ + inline namespace B __attribute__((diagnose_as("@1"))) + { + template + struct __attribute__((diagnose_as("@2"))) C + { + __attribute__((diagnose_as("fun:1"))) + void f() {} // { dg-line ABC_f } + + template + __attribute__((diagnose_as("fun:2"))) + void g(T, U) {} // { dg-line ABC_gT } + + template + __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() ? "void @1::@3::fun:3()" + : "void @1::@2::fun:3()"; + static_assert(__builtin_strcmp(__PRETTY_FUNCTION__, ref) == 0, ""); +#endif + } + + template + __attribute__((diagnose_as("fun:4"))) + void ht() + { +#ifdef __cpp_constexpr + static_assert(__builtin_strcmp(__FUNCTION__, "fun:4") == 0, ""); + constexpr const char* ref + = is_int() ? "void @1::@3::fun:4()" + : "void @1::@2::fun:4()"; + static_assert(__builtin_strcmp(__PRETTY_FUNCTION__, ref) == 0, ""); +#endif + } + + template + 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() + ? is_char() + ? "static void @1::@3::@6::fun:5()" + : "static void @1::@3::@5::fun:5()" + : is_char() + ? "static void @1::@2::@6::fun:5()" + : "static void @1::@2::@5::fun:5()"; + static_assert(__builtin_strcmp(__PRETTY_FUNCTION__, ref) == 0, + ""); +#endif + } + }; + typedef E F __attribute__((diagnose_as("@6"))); + + template + struct __attribute__((diagnose_as("@7"))) E + { + __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() + ? "static void @1::@3::@5::fun:6()" + : "static void @1::@2::@5::fun:6()"; + static_assert(__builtin_strcmp(__PRETTY_FUNCTION__, ref) == 0, + ""); +#endif + } + }; + }; + + typedef C D __attribute__((diagnose_as("@3"))); + + template <> + struct __attribute__((diagnose_as("@4"))) C + { + }; + } +} + +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 ()); // { dg-error "cannot convert '@1::@3' to 'int'" } + fn_1 (A::C ()); // { dg-error "cannot convert '@1::@2' to 'int'" } + fn_1 (A::C ()); // { dg-error "cannot convert '@1::@4' to 'int'" } + + A::C().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().f(0); // { dg-error "no matching function for call to '@1::@2::f\\(int\\)'" } + // { dg-message "candidate: 'void @1::@2::fun:1\\(\\)'" "" { target *-*-* } ABC_f } + + A::C().f(0); // { dg-error "'struct @1::@4' has no member named 'f'" } + + A::C().g(); // { dg-error "no matching function for call to '@1::@3::g\\(\\)'" } + // { dg-message "candidate: 'template void @1::@3::fun:2\\(T, int\\)'" "" { target *-*-* } ABC_gT } + // { dg-message "candidate: 'template void @1::@3::fun:2.2\\(\\)'" "" { target *-*-* } ABC_g } + + A::C().g(); // { dg-error "no matching function for call to '@1::@2::g\\(\\)'" } + // { dg-message "candidate: 'template void @1::@2::fun:2\\(T, char\\)'" "" { target *-*-* } ABC_gT } + // { dg-message "candidate: 'template void @1::@2::fun:2.2\\(\\)'" "" { target *-*-* } ABC_g } + + A::C().h(); + A::C().h(); + A::C().ht(); + A::C().ht(); + A::C::E::f(); + A::C::E::f(); + A::C::E::f(); + A::C::E::f(); + A::C::F::f(); + A::C::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 + struct [[gnu::diagnose_as("Bar'")]] Bar + { + template + struct [[gnu::diagnose_as("A'")]] A + { constexpr const char* f() { return __PRETTY_FUNCTION__; } }; + + template + struct A + { constexpr const char* f() { return __PRETTY_FUNCTION__; } }; + + template + struct [[gnu::diagnose_as("Adouble")]] A + { constexpr const char* f() { return __PRETTY_FUNCTION__; } }; + + using Special [[gnu::diagnose_as("SpecialA")]] = A; + + static constexpr const char* f() { return __PRETTY_FUNCTION__; } + }; + + template + struct + [[gnu::diagnose_as("BarPtr")]] + Bar + { + template + static constexpr const char* f() { return __PRETTY_FUNCTION__; } + }; + + template <> + struct [[gnu::diagnose_as("SpecialBar")]] Bar + { + static constexpr const char* f() { return __PRETTY_FUNCTION__; } + }; + + using barchar [[gnu::diagnose_as("barchar")]] = Bar; +} + +namespace A +{ + inline namespace B __attribute__((diagnose_as("@1"))) + { + template + struct __attribute__((diagnose_as("@2"))) C + { + template + __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 D __attribute__((diagnose_as("@3"))); + } +} + +template +struct [[gnu::diagnose_as("X.0")]] X0 +{ + template + struct X1 + { + template + struct X2 + { + template + [[gnu::diagnose_as("f-1")]] + static constexpr const char* f() { + return __PRETTY_FUNCTION__; + } + }; + + using XX [[gnu::diagnose_as("X2'")]] = X2; + }; + + template + [[gnu::diagnose_as("f-1")]] + static constexpr const char* f() { + return __PRETTY_FUNCTION__; + } + + struct [[gnu::diagnose_as("X.3")]] X3 + { + template + [[gnu::diagnose_as("f-1")]] + static constexpr const char* f() { + return __PRETTY_FUNCTION__; + } + }; +}; + +using X0int [[gnu::diagnose_as("[int|int]")]] = X0; + +#define TEST(expected, fun) \ + static_assert(__builtin_strcmp(expected, fun) == 0, "") + + +void h() { + TEST("constexpr const char* @1::@3::fun:3()", + A::C().h()); + TEST("constexpr const char* @1::@3::fun:2() [with T = float]", + A::C().g()); + TEST("constexpr const char* @1::@2::fun:3() [with U = char]", + A::C().h()); + TEST("constexpr const char* @1::@2::fun:2() [with T = float; U = char]", + A::C().g()); + TEST("constexpr const char* foo::barchar::A'::f() [with T0 = char; T1 = char]", + (foo::Bar::A().f())); + TEST("constexpr const char* foo::barchar::A'::f() [with T = char]", + (foo::Bar::A().f())); + TEST("constexpr const char* foo::barchar::Adouble::f() [with T = int]", + (foo::Bar::A().f())); + TEST("constexpr const char* foo::barchar::SpecialA::f()", + (foo::Bar::A().f())); + TEST("constexpr const char* foo::Bar'::A'::f() [with T0 = char; T1 = char; U = float]", + (foo::Bar::A().f())); + TEST("constexpr const char* foo::Bar'::A'::f() [with T = char; U = float]", + (foo::Bar::A().f())); + TEST("constexpr const char* foo::Bar'::Adouble::f() [with T = char; U = float]", + (foo::Bar::A().f())); + TEST("constexpr const char* foo::Bar'::SpecialA::f() [with U = float]", + (foo::Bar::A().f())); + TEST("static constexpr const char* foo::barchar::f()", + foo::Bar::f()); + TEST("static constexpr const char* foo::BarPtr::f() [with U = int; P = char]", + foo::Bar::f()); + TEST("static constexpr const char* foo::BarPtr::f() [with U = int; P = float]", + foo::Bar::f()); + TEST("static constexpr const char* foo::Bar'::f() [with U = float]", + foo::Bar::f()); + TEST("static constexpr const char* foo::SpecialBar::f()", + foo::Bar::f()); + TEST("static constexpr const char* X.0::X1::X2'::f-1() [with T3 = long int; U3 = long long int; T1 = short int; U1 = int; T0 = char; U0 = short int]", + (X0::X1::X2::f())); + TEST("static constexpr const char* X.0::X1::X2::f-1() [with T3 = long int; U3 = long long int; T2 = long int; U2 = int; T1 = short int; U1 = int; T0 = char; U0 = short int]", + (X0::X1::X2::f())); + TEST("static constexpr const char* X.0::X.3::f-1() [with T3 = long int; U3 = long long int; T0 = char; U0 = short int]", + (X0::X3::f())); + TEST("static constexpr const char* [int|int]::f-1() [with T3 = long int; U3 = long long int]", + (X0::f())); + TEST("static constexpr const char* [int|int]::X.3::f-1() [with T3 = long int; U3 = long long int]", + (X0::X3::f())); +} 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 + struct [[gnu::diagnose_as("Bar'")]] Bar + { + template + struct [[gnu::diagnose_as("A'")]] A + { constexpr const char* f() { return __PRETTY_FUNCTION__; } }; + + template + struct A + { constexpr const char* f() { return __PRETTY_FUNCTION__; } }; + + template + struct [[gnu::diagnose_as("Adouble")]] A + { constexpr const char* f() { return __PRETTY_FUNCTION__; } }; + + using Special [[gnu::diagnose_as("SpecialA")]] = A; + + static constexpr const char* f() { return __PRETTY_FUNCTION__; } + }; + + template + struct + [[gnu::diagnose_as("BarPtr")]] + Bar + { + template + static constexpr const char* f() { return __PRETTY_FUNCTION__; } + }; + + template <> + struct [[gnu::diagnose_as("SpecialBar")]] Bar + { + static constexpr const char* f() { return __PRETTY_FUNCTION__; } + }; + + using barchar [[gnu::diagnose_as("barchar")]] = Bar; +} + +namespace A +{ + inline namespace B __attribute__((diagnose_as("@1"))) + { + template + struct __attribute__((diagnose_as("@2"))) C + { + template + __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 D __attribute__((diagnose_as("@3"))); + } +} + +template +struct [[gnu::diagnose_as("XX0")]] X0 +{ + template + struct X1 + { + template + struct X2 + { + template + [[gnu::diagnose_as("f-1")]] + static constexpr const char* f() { + return __PRETTY_FUNCTION__; + } + }; + + using XX [[gnu::diagnose_as("X2'")]] = X2; + }; + + template + [[gnu::diagnose_as("f-2")]] + static constexpr const char* f() { + return __PRETTY_FUNCTION__; + } + + struct [[gnu::diagnose_as("XX3")]] X3 + { + template + [[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; + +#define TEST(expected, fun) \ + static_assert(__builtin_strcmp(expected, fun) == 0, "") + + +void h() { + TEST("constexpr const char* @1::@3::fun:3()", + A::C().h()); + TEST("constexpr const char* @1::@3::fun:2()", + A::C().g()); + TEST("constexpr const char* @1::@2::fun:3()", + A::C().h()); + TEST("constexpr const char* @1::@2::fun:2()", + A::C().g()); + TEST("constexpr const char* foo::barchar::A'::f()", + (foo::Bar::A().f())); + TEST("constexpr const char* foo::barchar::A'::f()", + (foo::Bar::A().f())); + TEST("constexpr const char* foo::barchar::A'::f()", + (foo::Bar::A().f())); + TEST("constexpr const char* foo::barchar::SpecialA::f()", + (foo::Bar::A().f())); + TEST("constexpr const char* foo::Bar'::A'::f()", + (foo::Bar::A().f())); + TEST("constexpr const char* foo::Bar'::A'::f()", + (foo::Bar::A().f())); + TEST("constexpr const char* foo::Bar'::A'::f()", + (foo::Bar::A().f())); + TEST("constexpr const char* foo::Bar'::SpecialA::f()", + (foo::Bar::A().f())); + TEST("static constexpr const char* foo::barchar::f()", + foo::Bar::f()); + TEST("static constexpr const char* foo::Bar'::f()", + foo::Bar::f()); + TEST("static constexpr const char* foo::Bar'::f()", + foo::Bar::f()); + TEST("static constexpr const char* foo::Bar'::f()", + foo::Bar::f()); + TEST("static constexpr const char* foo::SpecialBar::f()", + foo::Bar::f()); + TEST("static constexpr const char* XX0::X1::X2'::f-1()", + (X0::X1::X2::f())); + TEST("static constexpr const char* XX0::X1::X2::f-1()", + (X0::X1::X2::f())); + TEST("static constexpr const char* XX0::XX3::f-3()", + (X0::X3::f())); + TEST("static constexpr const char* X0intint::f-2()", + (X0::f())); + TEST("static constexpr const char* X0intint::XX3::f-3()", + (X0::X3::f())); + + X0::X3::f(1); // { dg-error "no matching function for call to 'XX0::XX3::f\\(int\\)" } + // { dg-message "candidate: 'static constexpr const char\\* XX0::XX3::f-3\\(\\)'" "" { target *-*-* } X0_X3_f } + + X0::X3::f(1); // { dg-error "no matching function for call to 'X0intint::XX3::f\\(int\\)" } + // { dg-message "candidate: 'static constexpr const char\\* X0intint::XX3::f-3\\(\\)'" "" { target *-*-* } X0_X3_f } +} --nextPart24578775.GzJpooMjnH--