From patchwork Thu Jul 15 12:21:04 2021 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Patchwork-Submitter: Matthias Kretz X-Patchwork-Id: 1505662 Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Authentication-Results: ozlabs.org; spf=pass (sender SPF authorized) smtp.mailfrom=gcc.gnu.org (client-ip=8.43.85.97; helo=sourceware.org; envelope-from=gcc-patches-bounces+incoming=patchwork.ozlabs.org@gcc.gnu.org; receiver=) Received: from sourceware.org (ip-8-43-85-97.sourceware.org [8.43.85.97]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (4096 bits) server-digest SHA256) (No client certificate requested) by ozlabs.org (Postfix) with ESMTPS id 4GQYRD3M6Jz9sWk for ; Thu, 15 Jul 2021 22:22:32 +1000 (AEST) Received: from server2.sourceware.org (localhost [IPv6:::1]) by sourceware.org (Postfix) with ESMTP id 83E11398200A for ; Thu, 15 Jul 2021 12:22:29 +0000 (GMT) X-Original-To: gcc-patches@gcc.gnu.org Delivered-To: gcc-patches@gcc.gnu.org Received: from lxmtout1.gsi.de (lxmtout1.gsi.de [140.181.3.111]) by sourceware.org (Postfix) with ESMTPS id 4D535385481D for ; Thu, 15 Jul 2021 12:21:08 +0000 (GMT) DMARC-Filter: OpenDMARC Filter v1.4.1 sourceware.org 4D535385481D Authentication-Results: sourceware.org; dmarc=none (p=none dis=none) header.from=gsi.de Authentication-Results: sourceware.org; spf=pass smtp.mailfrom=gsi.de Received: from localhost (localhost [127.0.0.1]) by lxmtout1.gsi.de (Postfix) with ESMTP id 705BA2050D1A; Thu, 15 Jul 2021 14:21:06 +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 7Q-Gd-ex3m9N; Thu, 15 Jul 2021 14:21:06 +0200 (CEST) Received: from srvex1.campus.gsi.de (unknown [10.10.4.11]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-SHA256 (128/128 bits)) (No client certificate requested) by lxmtout1.gsi.de (Postfix) with ESMTPS id 46FCA2050D12; Thu, 15 Jul 2021 14:21:06 +0200 (CEST) Received: from minbar.localnet (140.181.3.12) by srvex1.campus.gsi.de (10.10.4.11) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256_P256) id 15.1.2242.10; Thu, 15 Jul 2021 14:21:05 +0200 From: Matthias Kretz To: Subject: [PATCH v3] c++: Add gnu::diagnose_as attribute Date: Thu, 15 Jul 2021 14:21:04 +0200 Message-ID: <4361366.VLH7GnMWUR@minbar> Organization: GSI Helmholtz Centre for Heavy Ion Research MIME-Version: 1.0 X-Originating-IP: [140.181.3.12] X-ClientProxiedBy: SRVEX2.campus.gsi.de (10.10.4.15) To srvex1.campus.gsi.de (10.10.4.11) X-Spam-Status: No, score=-10.8 required=5.0 tests=BAYES_00, BODY_8BITS, GIT_PATCH_0, KAM_DMARC_STATUS, SPF_HELO_PASS, SPF_PASS, TXREP autolearn=ham autolearn_force=no version=3.4.4 X-Spam-Checker-Version: SpamAssassin 3.4.4 (2020-01-24) on server2.sourceware.org X-BeenThere: gcc-patches@gcc.gnu.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: Gcc-patches mailing list List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: gcc-patches-bounces+incoming=patchwork.ozlabs.org@gcc.gnu.org Sender: "Gcc-patches" Hi Jason, A new revision of the patch is attached. I think I implemented all your suggestions. Please comment on cp/decl2.c (is_alias_template_p). I find it surprising that I had to write this function. Maybe I missed something? In any case, DECL_ALIAS_TEMPLATE_P requires a template_decl and the TYPE_DECL apparently doesn't have a template_info/decl at this point. From: Matthias Kretz This attribute overrides the diagnostics output string for the entity it appertains to. The motivation is to improve QoI for library TS implementations, where diagnostics have a very bad signal-to-noise ratio due to the long namespaces involved. With the attribute, it is possible to solve PR89370 and make std::__cxx11::basic_string<_CharT, _Traits, _Alloc> appear as std::string in diagnostic output without extra hacks to recognize the type in the C++ frontend. Signed-off-by: Matthias Kretz gcc/ChangeLog: PR c++/89370 * doc/extend.texi: Document the diagnose_as attribute. * doc/invoke.texi: Document -fno-diagnostics-use-aliases. gcc/c-family/ChangeLog: PR c++/89370 * c.opt (fdiagnostics-use-aliases): New diagnostics flag. gcc/cp/ChangeLog: PR c++/89370 * cp-tree.h: Add TFF_AS_PRIMARY. Add is_alias_template_p declaration. * decl2.c (is_alias_template_p): New function. Determines whether a given TYPE_DECL is actually an alias template that is still missing its template_info. (is_late_template_attribute): Decls with diagnose_as attribute are early attributes only if they are alias templates. * error.c (dump_scope): When printing the name of a namespace, look for the diagnose_as attribute. If found, print the associated string instead of calling dump_decl. (dump_decl_name_or_diagnose_as): New function to replace dump_decl (pp, DECL_NAME(t), flags) and inspect the tree for the diagnose_as attribute before printing the DECL_NAME. (dump_template_scope): New function. Prints the scope of a template instance correctly applying diagnose_as attributes and adjusting the list of template parms accordingly. (dump_aggr_type): If the type has a diagnose_as attribute, print the associated string instead of printing the original type name. Print template parms only if the attribute was not applied to the instantiation / full specialization. Delay call to dump_scope until the diagnose_as attribute is found. If the attribute has a second argument, use it to override the context passed to dump_scope. (dump_simple_decl): Call dump_decl_name_or_diagnose_as instead of dump_decl. (dump_decl): Ditto. (lang_decl_name): Ditto. (dump_function_decl): Walk the functions context list to determine whether a call to dump_template_scope is required. Ensure function templates are presented as primary templates. (dump_function_name): Replace the function's identifier with the diagnose_as attribute value, if set. (dump_template_parms): Treat as primary template if flags contains TFF_AS_PRIMARY. (comparable_template_types_p): Consider the types not a template if one carries a diagnose_as attribute. (print_template_differences): Replace the identifier with the diagnose_as attribute value on the most general template, if it is set. * name-lookup.c (handle_namespace_attrs): Handle the diagnose_as attribute on namespaces. Ensure exactly one string argument. Ensure previous diagnose_as attributes used the same name. 'diagnose_as' on namespace aliases are forwarded to the original namespace. Support no-argument 'diagnose_as' on namespace aliases. (do_namespace_alias): Add attributes parameter and call handle_namespace_attrs. * name-lookup.h (do_namespace_alias): Add attributes tree parameter. * parser.c (cp_parser_declaration): If the next token is RID_NAMESPACE, tentatively parse a namespace alias definition. If this fails expect a namespace definition. (cp_parser_namespace_alias_definition): Allow optional attributes before and after the identifier. Fast exit if the expected CPP_EQ token is missing. Pass attributes to do_namespace_alias. * tree.c (cxx_attribute_table): Add diagnose_as attribute to the table. (check_diagnose_as_redeclaration): New function; copied and adjusted from check_abi_tag_redeclaration. (handle_diagnose_as_attribute): New function; copied and adjusted from handle_abi_tag_attribute. If the given *node is a TYPE_DECL: allow no argument to the attribute, using DECL_NAME instead; apply the attribute to the type on the RHS in place, even if the type is complete. Allow 2 arguments when called from handle_diagnose_as_attribute. For type aliases, append CP_DECL_CONTEXT as second attribute argument when the RHS type has a different context. Warn about alias templates without matching template arguments. Apply the attribute to the primary template type for alias templates. gcc/testsuite/ChangeLog: PR c++/89370 * g++.dg/diagnostic/diagnose-as1.C: New test. * g++.dg/diagnostic/diagnose-as2.C: New test. * g++.dg/diagnostic/diagnose-as3.C: New test. * g++.dg/diagnostic/diagnose-as4.C: New test. * g++.dg/diagnostic/diagnose-as5.C: New test. * g++.dg/diagnostic/diagnose-as6.C: New test. --- gcc/c-family/c.opt | 4 + gcc/cp/cp-tree.h | 6 +- gcc/cp/decl2.c | 46 ++++ gcc/cp/error.c | 245 +++++++++++++++++- gcc/cp/name-lookup.c | 52 +++- gcc/cp/name-lookup.h | 2 +- gcc/cp/parser.c | 36 +-- gcc/cp/tree.c | 151 +++++++++++ gcc/doc/extend.texi | 45 ++++ gcc/doc/invoke.texi | 9 +- .../g++.dg/diagnostic/diagnose-as1.C | 213 +++++++++++++++ .../g++.dg/diagnostic/diagnose-as2.C | 144 ++++++++++ .../g++.dg/diagnostic/diagnose-as3.C | 152 +++++++++++ .../g++.dg/diagnostic/diagnose-as4.C | 158 +++++++++++ .../g++.dg/diagnostic/diagnose-as5.C | 21 ++ .../g++.dg/diagnostic/diagnose-as6.C | 45 ++++ 16 files changed, 1295 insertions(+), 34 deletions(-) create mode 100644 gcc/testsuite/g++.dg/diagnostic/diagnose-as1.C create mode 100644 gcc/testsuite/g++.dg/diagnostic/diagnose-as2.C create mode 100644 gcc/testsuite/g++.dg/diagnostic/diagnose-as3.C create mode 100644 gcc/testsuite/g++.dg/diagnostic/diagnose-as4.C create mode 100644 gcc/testsuite/g++.dg/diagnostic/diagnose-as5.C create mode 100644 gcc/testsuite/g++.dg/diagnostic/diagnose-as6.C -- ────────────────────────────────────────────────────────────────────────── Dr. Matthias Kretz https://mattkretz.github.io GSI Helmholtz Centre for Heavy Ion Research https://gsi.de std::experimental::simd https://github.com/VcDevel/std-simd ────────────────────────────────────────────────────────────────────────── diff --git a/gcc/c-family/c.opt b/gcc/c-family/c.opt index 91929706aff..354590982bd 100644 --- a/gcc/c-family/c.opt +++ b/gcc/c-family/c.opt @@ -1606,6 +1606,10 @@ fdiagnostics-show-template-tree C++ ObjC++ Var(flag_diagnostics_show_template_tree) Init(0) Print hierarchical comparisons when template types are mismatched. +fdiagnostics-use-aliases +C++ Var(flag_diagnostics_use_aliases) Init(1) +Replace identifiers or scope names in diagnostics as defined by the diagnose_as attribute. + fdirectives-only C ObjC C++ ObjC++ Preprocess directives only. diff --git a/gcc/cp/cp-tree.h b/gcc/cp/cp-tree.h index 6f713719589..427e56e5852 100644 --- a/gcc/cp/cp-tree.h +++ b/gcc/cp/cp-tree.h @@ -5892,7 +5892,9 @@ enum auto_deduction_context identical to their defaults. TFF_NO_TEMPLATE_BINDINGS: do not print information about the template arguments for a function template specialization. - TFF_POINTER: we are printing a pointer type. */ + TFF_POINTER: we are printing a pointer type. + TFF_AS_PRIMARY: treat as primary template even if printing a specialized + type. */ #define TFF_PLAIN_IDENTIFIER (0) #define TFF_SCOPE (1) @@ -5910,6 +5912,7 @@ enum auto_deduction_context #define TFF_NO_OMIT_DEFAULT_TEMPLATE_ARGUMENTS (1 << 12) #define TFF_NO_TEMPLATE_BINDINGS (1 << 13) #define TFF_POINTER (1 << 14) +#define TFF_AS_PRIMARY (1 << 15) /* These constants can be used as bit flags to control strip_typedefs. @@ -6768,6 +6771,7 @@ extern tree grokfield (const cp_declarator *, cp_decl_specifier_seq *, tree, bool, tree, tree); extern tree grokbitfield (const cp_declarator *, cp_decl_specifier_seq *, tree, tree, tree); +extern bool is_alias_template_p (tree); extern tree splice_template_attributes (tree *, tree); extern bool any_dependent_type_attributes_p (tree); extern tree cp_reconstruct_complex_type (tree, tree); diff --git a/gcc/cp/decl2.c b/gcc/cp/decl2.c index 090a83bd670..7cec8f114c1 100644 --- a/gcc/cp/decl2.c +++ b/gcc/cp/decl2.c @@ -1139,6 +1139,48 @@ grokbitfield (const cp_declarator *declarator, return value; } +/* Return true iff DECL is an alias template of a class template which strictly + renames the type. */ +bool +is_alias_template_p (tree decl) +{ + if (TREE_CODE (decl) != TYPE_DECL) + return false; + + tree type = strip_typedefs (TREE_TYPE (decl), nullptr, 0); + if (!CLASS_TYPE_P (type) || !CLASSTYPE_TEMPLATE_INFO (type)) + return false; + + /* Ensure it's a real alias template not just + template struct A { + struct B {}; + template struct C {}; + using D [[gnu::diagnose_as]] = B; + using E [[gnu::diagnose_as]] = C; + }; + A::D and A::E are not alias templates. + - For A::D, the TREE_TYPE of the innermost template params is A and + not B, which would be the case for a real alias template. + - For A::E, the innermost template params belong to C but its template + args have no wildcard types, which would be the case for a real + alias template. */ + tree tmpl = CLASSTYPE_TI_TEMPLATE (type); + if (tmpl != TREE_TYPE (INNERMOST_TEMPLATE_PARMS ( + DECL_TEMPLATE_PARMS (tmpl)))) + return false; + + tree targs = INNERMOST_TEMPLATE_ARGS (CLASSTYPE_TI_ARGS (type)); + for (int i = 0; i < NUM_TMPL_ARGS (targs); ++i) + { + tree arg = TREE_VEC_ELT (targs, i); + while (INDIRECT_TYPE_P (arg)) + arg = TREE_TYPE (arg); + if (WILDCARD_TYPE_P (arg)) + return true; + } + return false; +} + /* Returns true iff ATTR is an attribute which needs to be applied at instantiation time rather than template definition time. */ @@ -1166,6 +1208,10 @@ is_late_template_attribute (tree attr, tree decl) || is_attribute_p ("used", name))) return false; + /* Allow alias templates to set diagnose_as on the RHS template. */ + if (is_attribute_p ("diagnose_as", name)) + return !is_alias_template_p (decl); + /* Attribute tls_model wants to modify the symtab. */ if (is_attribute_p ("tls_model", name)) return true; diff --git a/gcc/cp/error.c b/gcc/cp/error.c index 4a89b348829..a18d758fa43 100644 --- a/gcc/cp/error.c +++ b/gcc/cp/error.c @@ -35,6 +35,7 @@ along with GCC; see the file COPYING3. If not see #include "internal-fn.h" #include "gcc-rich-location.h" #include "cp-name-hint.h" +#include "attribs.h" #define pp_separate_with_comma(PP) pp_cxx_separate_with (PP, ',') #define pp_separate_with_semicolon(PP) pp_cxx_separate_with (PP, ';') @@ -66,6 +67,7 @@ static void dump_alias_template_specialization (cxx_pretty_printer *, tree, int) static void dump_type (cxx_pretty_printer *, tree, int); static void dump_typename (cxx_pretty_printer *, tree, int); static void dump_simple_decl (cxx_pretty_printer *, tree, tree, int); +static void dump_decl_name_or_diagnose_as (cxx_pretty_printer *, tree, int); static void dump_decl (cxx_pretty_printer *, tree, int); static void dump_template_decl (cxx_pretty_printer *, tree, int); static void dump_function_decl (cxx_pretty_printer *, tree, int); @@ -231,7 +233,15 @@ dump_scope (cxx_pretty_printer *pp, tree scope, int flags) { if (scope != global_namespace) { - dump_decl (pp, scope, f); + tree diagnose_as + = flag_diagnostics_use_aliases + ? lookup_attribute ("diagnose_as", DECL_ATTRIBUTES (scope)) + : NULL_TREE; + if (diagnose_as) + pp_cxx_ws_string ( + pp, TREE_STRING_POINTER (TREE_VALUE (TREE_VALUE (diagnose_as)))); + else + dump_decl (pp, scope, f); pp_cxx_colon_colon (pp); } } @@ -764,6 +774,45 @@ class_key_or_enum_as_string (tree t) return "struct"; } +/* Print out an enclosing scope of a template instance by inspecting the tree + SPECIAL of the template instantiation and the tree GENERAL of the most + general template in parallel under the control of FLAGS while adjusting + PARMS as needed. This allows the diagnose_as attribute to override the name + of full specializations, while hiding its template parameters, and to + override the name of partial specializations without falling back to printing + the template parameters of the general template decl. */ + +static void +dump_template_scope (cxx_pretty_printer *pp, tree special, tree general, + tree *parms, int flags) +{ + gcc_assert (parms); + if (special != general && RECORD_OR_UNION_TYPE_P (special) && *parms) + { + gcc_assert (RECORD_OR_UNION_TYPE_P (general)); + const bool tmplate + = TYPE_LANG_SPECIFIC (special) && CLASSTYPE_TEMPLATE_INFO (special) + && (TREE_CODE (CLASSTYPE_TI_TEMPLATE (special)) != TEMPLATE_DECL + || PRIMARY_TEMPLATE_P (CLASSTYPE_TI_TEMPLATE (special))); + dump_template_scope (pp, CP_TYPE_CONTEXT (special), + CP_TYPE_CONTEXT (general), + tmplate ? &TREE_CHAIN(*parms) : parms, flags); + tree attr = lookup_attribute ("diagnose_as", TYPE_ATTRIBUTES (special)); + if (attr) + { + pp_cxx_ws_string (pp, TREE_STRING_POINTER ( + TREE_VALUE (TREE_VALUE (attr)))); + if (tmplate) + TREE_VALUE (*parms) = make_tree_vec (0); + } + else + dump_aggr_type (pp, general, flags | TFF_UNQUALIFIED_NAME); + pp_cxx_colon_colon (pp); + } + else + dump_scope (pp, general, flags); +} + /* Print out a class declaration T under the control of FLAGS, in the form `class foo'. */ @@ -781,6 +830,7 @@ dump_aggr_type (cxx_pretty_printer *pp, tree t, int flags) tree decl = TYPE_NAME (t); + tree diagnose_as = NULL_TREE; if (decl) { typdef = (!DECL_ARTIFICIAL (decl) @@ -804,11 +854,14 @@ dump_aggr_type (cxx_pretty_printer *pp, tree t, int flags) && (TREE_CODE (CLASSTYPE_TI_TEMPLATE (t)) != TEMPLATE_DECL || PRIMARY_TEMPLATE_P (CLASSTYPE_TI_TEMPLATE (t))); - if (! (flags & TFF_UNQUALIFIED_NAME)) - dump_scope (pp, CP_DECL_CONTEXT (decl), flags | TFF_SCOPE); - flags &= ~TFF_UNQUALIFIED_NAME; + tree context = CP_DECL_CONTEXT (decl); + tree diagnose_as_specialized = NULL_TREE; if (tmplate) { + if (flag_diagnostics_use_aliases) + diagnose_as_specialized + = lookup_attribute ("diagnose_as", TYPE_ATTRIBUTES (t)); + /* Because the template names are mangled, we have to locate the most general template, and use that name. */ tree tpl = TYPE_TI_TEMPLATE (t); @@ -817,9 +870,61 @@ dump_aggr_type (cxx_pretty_printer *pp, tree t, int flags) tpl = DECL_TI_TEMPLATE (tpl); decl = tpl; } + + if (flag_diagnostics_use_aliases) + { + diagnose_as = lookup_attribute ("diagnose_as", + TYPE_ATTRIBUTES (TREE_TYPE (decl))); + if (diagnose_as_specialized + && (!diagnose_as || TREE_VALUE (diagnose_as_specialized) + != TREE_VALUE (diagnose_as))) + { + diagnose_as = diagnose_as_specialized; + /* Skip dump_template_parms if diagnose_as applies to a full + specialization. */ + tree args = CLASSTYPE_TI_ARGS (t); + args = INNERMOST_TEMPLATE_ARGS (args); + gcc_assert (args); + tmplate = false; + for (int i = 0; i < NUM_TMPL_ARGS (args); ++i) + { + tree arg = TREE_VEC_ELT (args, i); + while (INDIRECT_TYPE_P (arg)) + arg = TREE_TYPE (arg); + if (WILDCARD_TYPE_P (arg)) + { + tmplate = true; + break; + } + } + } + /* Given + + template 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 (diagnose_as && TREE_CHAIN (TREE_VALUE (diagnose_as))) + context = TREE_VALUE (TREE_CHAIN (TREE_VALUE (diagnose_as))); + } + + if (! (flags & TFF_UNQUALIFIED_NAME)) + dump_scope (pp, context, flags | TFF_SCOPE); + flags &= ~TFF_UNQUALIFIED_NAME; } - if (LAMBDA_TYPE_P (t)) + if (diagnose_as) + pp_cxx_ws_string (pp, TREE_STRING_POINTER ( + TREE_VALUE (TREE_VALUE (diagnose_as)))); + else if (LAMBDA_TYPE_P (t)) { /* A lambda's "type" is essentially its signature. */ pp_string (pp, M_(""); } 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_("")); @@ -1185,6 +1290,25 @@ dump_decl_name (cxx_pretty_printer *pp, tree t, int flags) pp_cxx_tree_identifier (pp, t); } +/* Print the DECL_NAME of DECL unless a different string was requested by the + diagnose_as attribute. */ + +static void +dump_decl_name_or_diagnose_as (cxx_pretty_printer *pp, tree decl, int flags) +{ + if (flag_diagnostics_use_aliases) + { + tree attr = lookup_attribute ("diagnose_as", DECL_ATTRIBUTES (decl)); + if (attr && TREE_VALUE (attr)) + { + pp_cxx_ws_string ( + pp, TREE_STRING_POINTER (TREE_VALUE (TREE_VALUE (attr)))); + return; + } + } + dump_decl_name (pp, DECL_NAME (decl), flags); +} + /* Dump a human readable string for the decl T under control of FLAGS. */ static void @@ -1230,7 +1354,7 @@ dump_decl (cxx_pretty_printer *pp, tree t, int flags) || flags & TFF_CLASS_KEY_OR_ENUM)) { pp_cxx_ws_string (pp, "using"); - dump_decl (pp, DECL_NAME (t), flags); + dump_decl_name_or_diagnose_as(pp, t, flags); pp_cxx_whitespace (pp); pp_cxx_ws_string (pp, "="); pp_cxx_whitespace (pp); @@ -1412,7 +1536,7 @@ dump_decl (cxx_pretty_printer *pp, tree t, int flags) TREE_CODE (DECL_INITIAL (t)) == TEMPLATE_PARM_INDEX)) dump_simple_decl (pp, t, TREE_TYPE (t), flags); else if (DECL_NAME (t)) - dump_decl (pp, DECL_NAME (t), flags); + dump_decl_name_or_diagnose_as (pp, t, flags); else if (DECL_INITIAL (t)) dump_expr (pp, DECL_INITIAL (t), flags | TFF_EXPR_IN_PARENS); else @@ -1431,7 +1555,7 @@ dump_decl (cxx_pretty_printer *pp, tree t, int flags) } dump_type (pp, scope, flags); pp_cxx_colon_colon (pp); - dump_decl (pp, DECL_NAME (t), flags); + dump_decl_name_or_diagnose_as (pp, t, flags); if (variadic) pp_cxx_ws_string (pp, "..."); } @@ -1671,7 +1795,8 @@ dump_function_decl (cxx_pretty_printer *pp, tree t, int flags) tree template_args = NULL_TREE; tree template_parms = NULL_TREE; int show_return = flags & TFF_RETURN_TYPE || flags & TFF_DECL_SPECIFIERS; - int do_outer_scope = ! (flags & TFF_UNQUALIFIED_NAME); + bool do_outer_scope = ! (flags & TFF_UNQUALIFIED_NAME); + bool do_template_scope = false; tree exceptions; bool constexpr_p; tree ret = NULL_TREE; @@ -1688,6 +1813,11 @@ dump_function_decl (cxx_pretty_printer *pp, tree t, int flags) /* Likewise for the constexpr specifier, in case t is a specialization. */ constexpr_p = DECL_DECLARED_CONSTEXPR_P (t); + /* Keep t before the following branch makes t point to a more general + template. Without the specialized template, the diagnose_as attribute on + the function is lost. */ + tree specialized_t = t; + /* Pretty print template instantiations only. */ if (DECL_USE_TEMPLATE (t) && DECL_TEMPLATE_INFO (t) && !(flags & TFF_NO_TEMPLATE_BINDINGS) @@ -1699,8 +1829,46 @@ dump_function_decl (cxx_pretty_printer *pp, tree t, int flags) tmpl = most_general_template (t); if (tmpl && TREE_CODE (tmpl) == TEMPLATE_DECL) { + /* Inspect whether any record/union type in the context of the + instantiated function template carries a diagnose_as attribute. If + one does, we need to print the scope and template argument list + differently. Consider: + + template 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; } } @@ -1747,6 +1915,25 @@ dump_function_decl (cxx_pretty_printer *pp, tree t, int flags) /* Print the function name. */ if (!do_outer_scope) /* Nothing. */; + else if (do_template_scope) + { + template_parms = copy_list (template_parms); + bool func_template = TREE_TYPE (TREE_VALUE (template_parms)) == t; + dump_template_scope(pp, DECL_CONTEXT (specialized_t), DECL_CONTEXT (t), + func_template ? &TREE_CHAIN (template_parms) + : &template_parms, + flags); + if (TREE_VEC_LENGTH (TREE_VALUE (template_parms)) == 0) + { + /* If no template parms are left, make it a NULL_TREE so that no + "[with ]" is printed. */ + tree p = TREE_CHAIN (template_parms); + for (; p && TREE_VEC_LENGTH (TREE_VALUE (p)) == 0; p = TREE_CHAIN (p)) + ; + if (!p) + template_parms = template_args = NULL_TREE; + } + } else if (cname) { dump_type (pp, cname, flags); @@ -1755,7 +1942,7 @@ dump_function_decl (cxx_pretty_printer *pp, tree t, int flags) else dump_scope (pp, CP_DECL_CONTEXT (t), flags); - dump_function_name (pp, t, dump_function_name_flags); + dump_function_name (pp, specialized_t, dump_function_name_flags); if (!(flags & TFF_NO_FUNCTION_ARGUMENTS)) { @@ -1935,6 +2122,14 @@ dump_function_name (cxx_pretty_printer *pp, tree t, int flags) if (TREE_CODE (t) == TEMPLATE_DECL) t = DECL_TEMPLATE_RESULT (t); + if (flag_diagnostics_use_aliases) + { + tree attr = lookup_attribute ("diagnose_as", DECL_ATTRIBUTES (t)); + if (attr) + name = get_identifier ( + TREE_STRING_POINTER (TREE_VALUE (TREE_VALUE (attr)))); + } + /* Don't let the user see __comp_ctor et al. */ if (DECL_CONSTRUCTOR_P (t) || DECL_DESTRUCTOR_P (t)) @@ -1989,6 +2184,8 @@ dump_template_parms (cxx_pretty_printer *pp, tree info, { tree args = info ? TI_ARGS (info) : NULL_TREE; + if (flags & TFF_AS_PRIMARY) + primary = true; if (primary && flags & TFF_TEMPLATE_NAME) return; flags &= ~(TFF_CLASS_KEY_OR_ENUM | TFF_TEMPLATE_NAME); @@ -3168,7 +3365,7 @@ lang_decl_name (tree decl, int v, bool translate) && TREE_CODE (decl) == NAMESPACE_DECL) dump_decl (cxx_pp, decl, TFF_PLAIN_IDENTIFIER | TFF_UNQUALIFIED_NAME); else - dump_decl (cxx_pp, DECL_NAME (decl), TFF_PLAIN_IDENTIFIER); + dump_decl_name_or_diagnose_as (cxx_pp, decl, TFF_PLAIN_IDENTIFIER); return pp_ggc_formatted_text (cxx_pp); } @@ -3953,6 +4150,13 @@ comparable_template_types_p (tree type_a, tree type_b) if (!CLASS_TYPE_P (type_b)) return false; + /* If one of the types has a diagnose_as attribute set it is presented as a + non-template (even if it's a template specialization). */ + if (flag_diagnostics_use_aliases + && (lookup_attribute ("diagnose_as", TYPE_ATTRIBUTES (type_a)) + || lookup_attribute ("diagnose_as", TYPE_ATTRIBUTES (type_b)))) + return false; + tree tinfo_a = TYPE_TEMPLATE_INFO (type_a); tree tinfo_b = TYPE_TEMPLATE_INFO (type_b); if (!tinfo_a || !tinfo_b) @@ -4052,8 +4256,21 @@ print_template_differences (pretty_printer *pp, tree type_a, tree type_b, tree tinfo_a = TYPE_TEMPLATE_INFO (type_a); tree tinfo_b = TYPE_TEMPLATE_INFO (type_b); - pp_printf (pp, "%s<", - IDENTIFIER_POINTER (DECL_NAME (TI_TEMPLATE (tinfo_a)))); + const char* identifier_a + = IDENTIFIER_POINTER (DECL_NAME (TI_TEMPLATE (tinfo_a))); + if (flag_diagnostics_use_aliases) + { + /* Locate the most general template, and see whether it carries the + diagnose_as attribute. If it does, use it as identifier for type_a. */ + tree tpl = TI_TEMPLATE (tinfo_a); + while (DECL_TEMPLATE_INFO (tpl)) + tpl = DECL_TI_TEMPLATE (tpl); + tree attr = lookup_attribute ("diagnose_as", + TYPE_ATTRIBUTES (TREE_TYPE (tpl))); + if (attr) + identifier_a = TREE_STRING_POINTER (TREE_VALUE (TREE_VALUE (attr))); + } + pp_printf (pp, "%s<", identifier_a); tree args_a = TI_ARGS (tinfo_a); tree args_b = TI_ARGS (tinfo_b); diff --git a/gcc/cp/name-lookup.c b/gcc/cp/name-lookup.c index 1be5f3da6d5..5e8129df429 100644 --- a/gcc/cp/name-lookup.c +++ b/gcc/cp/name-lookup.c @@ -6060,6 +6060,53 @@ handle_namespace_attrs (tree ns, tree attributes) DECL_ATTRIBUTES (ns) = tree_cons (name, args, DECL_ATTRIBUTES (ns)); } + else if (is_attribute_p ("diagnose_as", name)) + { + if (DECL_NAMESPACE_ALIAS (ns)) + { // apply attribute to original namespace + if (!args) + { // turn alias identifier into attribute argument + tree ns_name = DECL_NAME (ns); + tree str = fix_string_type ( + build_string(IDENTIFIER_LENGTH (ns_name) + 1, + IDENTIFIER_POINTER (ns_name))); + args = build_tree_list (NULL_TREE, str); + } + else if (TREE_CHAIN (args) + || TREE_CODE (TREE_VALUE (args)) != STRING_CST) + { + warning (OPT_Wattributes, + "%qD attribute requires 0 or 1 NTBS arguments", + name); + continue; + } + tree attributes = tree_cons (name, args, NULL_TREE); + handle_namespace_attrs (ORIGINAL_NAMESPACE (ns), attributes); + continue; + } + if (!args || TREE_CHAIN (args) + || TREE_CODE (TREE_VALUE (args)) != STRING_CST) + { + warning (OPT_Wattributes, + "%qD attribute requires a single NTBS argument", + name); + continue; + } + tree existing + = lookup_attribute ("diagnose_as", DECL_ATTRIBUTES (ns)); + if (existing + && !cp_tree_equal (TREE_VALUE (args), + TREE_VALUE (TREE_VALUE (existing)))) + { + auto_diagnostic_group d; + warning (OPT_Wattributes, "%qD redeclared with different %qD " + "attribute value", ns, name); + inform (DECL_SOURCE_LOCATION (ns), "previous declaration here"); + continue; + } + DECL_ATTRIBUTES (ns) = tree_cons (name, args, + DECL_ATTRIBUTES (ns)); + } else { warning (OPT_Wattributes, "%qD attribute directive ignored", @@ -6092,7 +6139,7 @@ pop_decl_namespace (void) /* Process a namespace-alias declaration. */ void -do_namespace_alias (tree alias, tree name_space) +do_namespace_alias (tree alias, tree name_space, tree attributes) { if (name_space == error_mark_node) return; @@ -6108,6 +6155,9 @@ do_namespace_alias (tree alias, tree name_space) DECL_CONTEXT (alias) = FROB_CONTEXT (current_scope ()); set_originating_module (alias); + /* Apply attributes. */ + handle_namespace_attrs (alias, attributes); + pushdecl (alias); /* Emit debug info for namespace alias. */ diff --git a/gcc/cp/name-lookup.h b/gcc/cp/name-lookup.h index f63c4f5b8bb..cdbb6834220 100644 --- a/gcc/cp/name-lookup.h +++ b/gcc/cp/name-lookup.h @@ -435,7 +435,7 @@ extern tree cp_namespace_decls (tree); extern void set_decl_namespace (tree, tree, bool); extern void push_decl_namespace (tree); extern void pop_decl_namespace (void); -extern void do_namespace_alias (tree, tree); +extern void do_namespace_alias (tree, tree, tree); extern tree do_class_using_decl (tree, tree); extern tree lookup_arg_dependent (tree, tree, vec *); extern tree search_anon_aggr (tree, tree, bool = false); diff --git a/gcc/cp/parser.c b/gcc/cp/parser.c index 096580e7e50..44bd88adb62 100644 --- a/gcc/cp/parser.c +++ b/gcc/cp/parser.c @@ -14252,20 +14252,15 @@ cp_parser_declaration (cp_parser* parser, tree prefix_attrs) || token1->keyword == RID_STATIC || token1->keyword == RID_INLINE)) cp_parser_explicit_instantiation (parser); - /* If the next token is `namespace', check for a named or unnamed - namespace definition. */ - else if (token1->keyword == RID_NAMESPACE - && (/* A named namespace definition. */ - (token2->type == CPP_NAME - && (cp_lexer_peek_nth_token (parser->lexer, 3)->type - != CPP_EQ)) - || (token2->type == CPP_OPEN_SQUARE - && cp_lexer_peek_nth_token (parser->lexer, 3)->type - == CPP_OPEN_SQUARE) - /* An unnamed namespace definition. */ - || token2->type == CPP_OPEN_BRACE - || token2->keyword == RID_ATTRIBUTE)) - cp_parser_namespace_definition (parser); + /* If the next token is `namespace', we have either a namespace alias + definition or a namespace definition. */ + else if (token1->keyword == RID_NAMESPACE) + { + cp_parser_parse_tentatively (parser); + cp_parser_namespace_alias_definition (parser); + if (!cp_parser_parse_definitely (parser)) + cp_parser_namespace_definition (parser); + } /* An inline (associated) namespace definition. */ else if (token2->keyword == RID_NAMESPACE && token1->keyword == RID_INLINE) @@ -20655,10 +20650,18 @@ cp_parser_namespace_alias_definition (cp_parser* parser) /* Look for the `namespace' keyword. */ cp_parser_require_keyword (parser, RID_NAMESPACE, RT_NAMESPACE); + /* Look for attributes (GCC extension). */ + tree attributes = cp_parser_attributes_opt (parser); + if (attributes) + pedwarn (input_location, OPT_Wpedantic, + "standard attributes on namespaces aliases must follow " + "the namespace alias name"); /* Look for the identifier. */ identifier = cp_parser_identifier (parser); if (identifier == error_mark_node) return; + /* Look for more attributes (GCC extension). */ + attributes = attr_chainon (attributes, cp_parser_attributes_opt (parser)); /* Look for the `=' token. */ if (!cp_parser_uncommitted_to_tentative_parse_p (parser) && cp_lexer_next_token_is (parser->lexer, CPP_OPEN_BRACE)) @@ -20670,7 +20673,8 @@ cp_parser_namespace_alias_definition (cp_parser* parser) cp_lexer_consume_token (parser->lexer); return; } - cp_parser_require (parser, CPP_EQ, RT_EQ); + if (nullptr == cp_parser_require (parser, CPP_EQ, RT_EQ)) + return; /* Look for the qualified-namespace-specifier. */ namespace_specifier = cp_parser_qualified_namespace_specifier (parser); @@ -20679,7 +20683,7 @@ cp_parser_namespace_alias_definition (cp_parser* parser) cp_parser_require (parser, CPP_SEMICOLON, RT_SEMICOLON); /* Register the alias in the symbol table. */ - do_namespace_alias (identifier, namespace_specifier); + do_namespace_alias (identifier, namespace_specifier, attributes); } /* Parse a qualified-namespace-specifier. diff --git a/gcc/cp/tree.c b/gcc/cp/tree.c index 2a14fa92ddb..68317041a0f 100644 --- a/gcc/cp/tree.c +++ b/gcc/cp/tree.c @@ -46,6 +46,7 @@ static tree verify_stmt_tree_r (tree *, int *, void *); static tree handle_init_priority_attribute (tree *, tree, tree, int, bool *); static tree handle_abi_tag_attribute (tree *, tree, tree, int, bool *); +static tree handle_diagnose_as_attribute (tree *, tree, tree, int, bool *); /* If REF is an lvalue, returns the kind of lvalue that REF is. Otherwise, returns clk_none. */ @@ -4877,6 +4878,8 @@ const struct attribute_spec cxx_attribute_table[] = handle_init_priority_attribute, NULL }, { "abi_tag", 1, -1, false, false, false, true, handle_abi_tag_attribute, NULL }, + { "diagnose_as", 0, -1, false, false, false, false, + handle_diagnose_as_attribute, NULL }, { NULL, 0, 0, false, false, false, false, NULL, NULL } }; @@ -5145,6 +5148,154 @@ handle_abi_tag_attribute (tree* node, tree name, tree args, return NULL_TREE; } +static bool +check_diagnose_as_redeclaration (const_tree decl, const_tree name, + const_tree old, const_tree new_) +{ + if (!old) + return true; + if (TREE_CODE (TREE_VALUE (old)) == TREE_LIST) + old = TREE_VALUE (old); + if (TREE_CODE (TREE_VALUE (new_)) == TREE_LIST) + new_ = TREE_VALUE (new_); + tree old_value = TREE_VALUE (old); + tree new_value = TREE_VALUE (new_); + if (cp_tree_equal (old_value, new_value)) + return true; + warning (OPT_Wattributes, "%qD redeclared with %<%D(%E)%> " + "attribute", decl, name, new_value); + inform (DECL_SOURCE_LOCATION (decl), "previous declaration here"); + return false; +} + +static tree +handle_diagnose_as_attribute (tree* node, tree name, tree args, + int flags, bool* no_add_attrs) +{ + tree decl = NULL_TREE; + const bool is_alias = (TREE_CODE (*node) == TYPE_DECL); + if (args && list_length (args) == 2 + && TREE_PURPOSE (TREE_CHAIN (args)) == integer_zero_node) + /* We're called from handle_diagnose_as_attribute with additional context + argument. */; + else if (is_alias) + { + if (args && (TREE_CHAIN (args) + || TREE_CODE (TREE_VALUE (args)) != STRING_CST)) + { + warning (OPT_Wattributes, + "%qD attribute requires 0 or 1 NTBS arguments", name); + goto fail; + } + } + else if (!args || TREE_CHAIN (args) + || TREE_CODE (TREE_VALUE (args)) != STRING_CST) + { + warning (OPT_Wattributes, + "%qD attribute requires a single NTBS argument", name); + goto fail; + } + + if (is_alias && CLASS_TYPE_P (TREE_TYPE (*node))) + { // Apply the attribute to the type alias itself. + decl = *node; + + /* Reject alias templates without wildcards on the innermost template args + of the RHS type. E.g. template using A = B; */ + if (DECL_LANG_SPECIFIC (decl) + && DECL_TEMPLATE_INFO (decl) + && DECL_ALIAS_TEMPLATE_P (DECL_TI_TEMPLATE (decl)) + && DECL_TI_TEMPLATE (decl) + == TREE_TYPE (INNERMOST_TEMPLATE_PARMS ( + DECL_TEMPLATE_PARMS ( + DECL_TI_TEMPLATE (decl))))) + return error_mark_node; + + if (!args) + { // turn alias identifier into attribute argument + tree alias_name = DECL_NAME (decl); + tree str = fix_string_type ( + build_string(IDENTIFIER_LENGTH (alias_name) + 1, + IDENTIFIER_POINTER (alias_name))); + args = build_tree_list (NULL_TREE, str); + } + // apply the attribute to the specialization on the RHS. + tree type = strip_typedefs (TREE_TYPE (*node), nullptr, 0); + + if (is_alias_template_p (decl)) + { + type = CLASSTYPE_PRIMARY_TEMPLATE_TYPE (type); + // Warn and skip if the template arguments don't match up like in + // template using A = B; + if (!same_type_p (TREE_TYPE (decl), type)) + return error_mark_node; + } + + // Add the DECL_CONTEXT of the alias to the attribute if it is different + // to the context of the type. + if (!TREE_CHAIN (args) + && CP_DECL_CONTEXT (decl) != CP_TYPE_CONTEXT (type)) + { + TREE_CHAIN (args) = build_tree_list (integer_zero_node, + CP_DECL_CONTEXT (decl)); + } + + if (COMPLETE_TYPE_P (type)) + { + tree rhs = handle_diagnose_as_attribute ( + &type, name, args, flags, no_add_attrs); + if (rhs == error_mark_node || *no_add_attrs) + return rhs; + else + TYPE_ATTRIBUTES (type) + = tree_cons (name, args, TYPE_ATTRIBUTES (type)); + } + else + { + tree attributes = tree_cons (name, args, NULL_TREE); + cplus_decl_attributes (&type, attributes, ATTR_FLAG_TYPE_IN_PLACE); + } + } + else if (TYPE_P (*node)) + { + if (!OVERLOAD_TYPE_P (*node)) + return error_mark_node; + decl = TYPE_NAME (*node); + } + else + { + if (!VAR_OR_FUNCTION_DECL_P (*node) + || DECL_LANGUAGE (*node) != lang_cplusplus) + return error_mark_node; + decl = *node; + } + + // Make sure all declarations have the same diagnose_as string. + if (DECL_SOURCE_LOCATION (decl) != input_location) + { + tree attributes = TYPE_P (*node) ? TYPE_ATTRIBUTES (*node) + : DECL_ATTRIBUTES (decl); + if (!check_diagnose_as_redeclaration ( + decl, name, lookup_attribute ("diagnose_as", attributes), args)) + goto fail; + } + else if (CLASS_TYPE_P (*node) && CLASSTYPE_IMPLICIT_INSTANTIATION (*node)) + { + // The above branch (different source location) is taken for declarations + // of type aliases that modify an implicit template specialization on the + // RHS. This branch is taken when the template is instantiated via + // instantiate_class_template_1, in which case the attribute either + // already has the value from the general template or from a type alias. + goto fail; + } + + return NULL_TREE; + + fail: + *no_add_attrs = true; + return NULL_TREE; +} + /* Return a new PTRMEM_CST of the indicated TYPE. The MEMBER is the thing pointed to by the constant. */ diff --git a/gcc/doc/extend.texi b/gcc/doc/extend.texi index 8fc66d626d8..4614800f5eb 100644 --- a/gcc/doc/extend.texi +++ b/gcc/doc/extend.texi @@ -2865,6 +2865,51 @@ types (@pxref{Variable Attributes}, @pxref{Type Attributes}.) The message attached to the attribute is affected by the setting of the @option{-fmessage-length} option. +@item diagnose_as ("@var{string}") +@itemx diagnose_as +@cindex @code{diagnose_as} function attribute +The @code{diagnose_as} attribute modifies how the entity the attribute +appertains to is diagnosed in compiler messages and @code{__func__}, +@code{__FUNCTION__}, and @code{__PRETTY_FUNCTION__} macros. (The result of +@code{typeid} is not affected.) If the attribute is applied to a +@code{namespace}, the specified string replaces the complete enclosing scope. +The effect of the @code{diagnose_as} attribute can be disabled with the +@option{-fno-diagnostics-use-aliases} command line option. + +The argument @var{string} is optional for type and namespace aliases. If it is +omitted, the identifier of the type or namespace alias is unconditionally used +in all places where the type / namespace on the right-hand side is diagnosed. + +@smallexample +namespace Foo @{ + namespace Bar @{ + inline namespace v1 [[gnu::diagnose_as("Foobar")]] @{ + int f() @{ + // __PRETTY_FUNCTION__ == "void Foobar::f()" + @} + @} + @} +@} +// In function 'int Foobar::f()': +// warning: no return statement in function returning non-void [-Wreturn-type] +@end smallexample + +The @code{diagnose_as} attribute can be used with namespaces, namespace aliases, +functions, variables, alias declarations (but not alias templates), and +user-defined types (classes, unions, and enums). If the alias declaration +aliases a class type (including template specializations), the attribute is +additionally applied to the class type. Whether the attribute has an effect on +partial template specializations is unspecified and may depend on several +different factors. + +@smallexample +template struct basic_string @{@}; +using string [[gnu::diagnose_as]] = 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 af2ce189fae..ed83462fa7a 100644 --- a/gcc/doc/invoke.texi +++ b/gcc/doc/invoke.texi @@ -311,7 +311,8 @@ Objective-C and Objective-C++ Dialects}. -fdiagnostics-show-path-depths @gol -fno-show-column @gol -fdiagnostics-column-unit=@r{[}display@r{|}byte@r{]} @gol --fdiagnostics-column-origin=@var{origin}} +-fdiagnostics-column-origin=@var{origin} @gol +-fno-diagnostics-aliases} @item Warning Options @xref{Warning Options,,Options to Request or Suppress Warnings}. @@ -5078,6 +5079,12 @@ first column. The default value of 1 corresponds to traditional GCC behavior and to the GNU style guide. Some utilities may perform better with an origin of 0; any non-negative value may be specified. +@item -fno-diagnostics-use-aliases +@opindex fno-diagnostics-use-aliases +@opindex fdiagnostics-use-aliases +Do not replace identifiers or scope names with the aliases defined with the +@code{[[gnu::diagnose_as("alias")]]} attribute. + @item -fdiagnostics-format=@var{FORMAT} @opindex fdiagnostics-format Select a different format for printing diagnostics. diff --git a/gcc/testsuite/g++.dg/diagnostic/diagnose-as1.C b/gcc/testsuite/g++.dg/diagnostic/diagnose-as1.C new file mode 100644 index 00000000000..e0a2cae32e5 --- /dev/null +++ b/gcc/testsuite/g++.dg/diagnostic/diagnose-as1.C @@ -0,0 +1,213 @@ +// { dg-options "-fdiagnostics-use-aliases -fpretty-templates" } + +#ifdef __cpp_constexpr +template + 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 __attribute__((diagnose_as())) foo0 {} // { dg-warning "'diagnose_as' attribute requires a single NTBS argument" } + +namespace __attribute__((diagnose_as("foo2"))) foo1 {} // { dg-line foo1 } +namespace __attribute__((diagnose_as("XXXX"))) foo1 {} // { dg-warning "'foo1' redeclared with different 'diagnose_as' attribute value" } +// { dg-message "previous declaration here" "" { target *-*-* } foo1 } +namespace foo1 +{ + void f() { + f(1); // { dg-error "too many arguments to function 'void foo2::f\\(\\)'" } + } + + class __attribute__((diagnose_as("XX"))) X; // { dg-line XX } + class __attribute__((diagnose_as("--"))) X { // { dg-warning "'class foo2::XX' redeclared with 'diagnose_as\\(\"--\"\\)' attribute" } + // { dg-message "previous declaration here" "" { target *-*-* } XX } + __attribute__((diagnose_as("ff"))) void f(); + }; + class Y : X + { + void g() { + f(); // { dg-error "'void foo2::XX::ff\\(\\)' is private within this context" } + } + }; + + class __attribute__((diagnose_as())) Z0; // { dg-warning "'diagnose_as' attribute requires a single NTBS argument" } + class __attribute__((diagnose_as(1))) Z1; // { dg-warning "'diagnose_as' attribute requires a single NTBS argument" } + class __attribute__((diagnose_as("a", "b"))) Z2; // { dg-warning "'diagnose_as' attribute requires a single NTBS argument" } + + template class A0 {}; + typedef A0 Achar __attribute__((diagnose_as("Achar"))); + template class A0; + typedef A0 Aint __attribute__((diagnose_as("Aint"))); // OK + typedef A0 Aint2 __attribute__((diagnose_as("Aint2"))); // { dg-warning "'class foo2::Aint' redeclared with 'diagnose_as\\(\"Aint2\"\\)' attribute" } + typedef A0 Aint3 __attribute__((diagnose_as(1))); // { dg-warning "'diagnose_as' attribute requires 0 or 1 NTBS arguments" } + +#if __cplusplus >= 201103L + template using foo [[gnu::diagnose_as]] = A0; + template using aRef [[gnu::diagnose_as]] = A0; // { dg-warning "'diagnose_as' attribute ignored" "" { target c++11 } } +#endif +} + +namespace A +{ + inline namespace __attribute__((diagnose_as("@1"))) B + { + 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 + { + }; + + typedef C E __attribute__((diagnose_as)); + } +} + +namespace frob +{ + namespace ni + { + struct kate {}; + namespace cate + { + struct me {}; + } + } +} + +namespace frobni __attribute__((diagnose_as("twiggle"))) = frob::ni; +namespace frobnicate __attribute__((diagnose_as)) = frob::ni::cate; +namespace wrong __attribute__((diagnose_as(1))) = frob::ni; // { dg-warning "'diagnose_as' attribute requires 0 or 1 NTBS arguments" } +namespace wrong2 __attribute__((diagnose_as("wrong", 1))) = frob::ni; // { dg-warning "'diagnose_as' attribute requires 0 or 1 NTBS arguments" } + +void fn_1(int); +void fn_2(A::D); + +int main () +{ + fn_2 (A::D ()); + fn_1 (A::D ()); // { dg-error "cannot convert '@1::@3' to 'int'" } + fn_1 (A::C ()); // { 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'" } + fn_1 (A::C ()); // { dg-error "cannot convert '@1::E' to 'int'" } + fn_1 (A::E ()); // { dg-error "cannot convert '@1::E' to 'int'" } + fn_1 (foo1::A0 ()); // { dg-error "cannot convert 'foo2::Aint' to 'int'" } + fn_1 (foo1::Aint2 ()); // { dg-error "cannot convert 'foo2::Aint2' {aka 'foo2::Aint'} to 'int'" } +#if __cplusplus >= 201103L + fn_1 (foo1::foo ()); // { dg-error "cannot convert 'foo2::foo' to 'int'" "" { target c++11 } } + fn_1 (foo1::aRef ()); // { dg-error "cannot convert 'foo2::aRef' {aka 'foo2::foo'} to 'int'" "" { target c++11 } } + fn_1 (frob::ni::kate ()); // { dg-error "cannot convert 'twiggle::kate' to 'int'" "" { target c++11 } } + fn_1 (frob::ni::cate::me ()); // { dg-error "cannot convert 'frobnicate::me' to 'int'" "" { target c++11 } } +#endif + + A::C().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 } +} diff --git a/gcc/testsuite/g++.dg/diagnostic/diagnose-as5.C b/gcc/testsuite/g++.dg/diagnostic/diagnose-as5.C new file mode 100644 index 00000000000..2a46d64964f --- /dev/null +++ b/gcc/testsuite/g++.dg/diagnostic/diagnose-as5.C @@ -0,0 +1,21 @@ +// { dg-options "-fdiagnostics-use-aliases -fpretty-templates" } +// { dg-do compile { target c++11 } } + +namespace std +{ + template + class basic_string {}; + + namespace pmr + { + struct P {}; + using string [[gnu::diagnose_as]] = basic_string; + } +} + +void f(int); + +int main() +{ + f(std::pmr::string()); // { dg-error "cannot convert 'std::pmr::string' to 'int'" } +} diff --git a/gcc/testsuite/g++.dg/diagnostic/diagnose-as6.C b/gcc/testsuite/g++.dg/diagnostic/diagnose-as6.C new file mode 100644 index 00000000000..94747a7aa6e --- /dev/null +++ b/gcc/testsuite/g++.dg/diagnostic/diagnose-as6.C @@ -0,0 +1,45 @@ +// { dg-options "-fdiagnostics-use-aliases -fpretty-templates" } +// { dg-do compile { target c++11 } } + +template + struct A + { + struct X {}; + using Y [[gnu::diagnose_as]] = X; + }; + +template <> + struct [[gnu::diagnose_as("A!")]] A + { + struct X {}; + using Y [[gnu::diagnose_as]] = X; + }; + +void f(int); + +template + using B [[gnu::diagnose_as]] = A; + +// this is not a real alias template +template + using C [[gnu::diagnose_as]] = A; // { dg-warning "'diagnose_as' attribute ignored" } + +// Would the following request A to be diagnosed as D? +// What about `= A', etc? This is not worth the implementation +// complexity. +template + using D [[gnu::diagnose_as]] = A; // { dg-warning "'diagnose_as' attribute ignored" } + +template + struct E {}; +template + using F [[gnu::diagnose_as("EF")]] = E; // { dg-warning "'diagnose_as' attribute ignored" } + +int main() +{ + f(A()); // { dg-error "cannot convert 'B' to 'int'" } + f(A::X()); // { dg-error "cannot convert 'B::Y' to 'int'" } + f(B::X()); // { dg-error "cannot convert 'A!::Y' to 'int'" } + f(C::X()); // { dg-error "cannot convert 'A!::Y' to 'int'" } + f(F()); // { dg-error "cannot convert 'F' {aka 'E'} to 'int'" } +}