@@ -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.
@@ -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);
@@ -1139,6 +1139,48 @@ grokbitfield (const cp_declarator *declarator,
return value;
}
+/* Return true iff DECL is an alias template of a class template which strictly
+ renames the type. */
+bool
+is_alias_template_p (tree decl)
+{
+ if (TREE_CODE (decl) != TYPE_DECL)
+ return false;
+
+ tree type = strip_typedefs (TREE_TYPE (decl), nullptr, 0);
+ if (!CLASS_TYPE_P (type) || !CLASSTYPE_TEMPLATE_INFO (type))
+ return false;
+
+ /* Ensure it's a real alias template not just
+ template <class T> struct A {
+ struct B {};
+ template <class U> struct C {};
+ using D [[gnu::diagnose_as]] = B;
+ using E [[gnu::diagnose_as]] = C<int>;
+ };
+ A<T>::D and A<T>::E are not alias templates.
+ - For A<T>::D, the TREE_TYPE of the innermost template params is A and
+ not B, which would be the case for a real alias template.
+ - For A<T>::E, the innermost template params belong to C but its template
+ args have no wildcard types, which would be the case for a real
+ alias template. */
+ tree tmpl = CLASSTYPE_TI_TEMPLATE (type);
+ if (tmpl != TREE_TYPE (INNERMOST_TEMPLATE_PARMS (
+ DECL_TEMPLATE_PARMS (tmpl))))
+ return false;
+
+ tree targs = INNERMOST_TEMPLATE_ARGS (CLASSTYPE_TI_ARGS (type));
+ for (int i = 0; i < NUM_TMPL_ARGS (targs); ++i)
+ {
+ tree arg = TREE_VEC_ELT (targs, i);
+ while (INDIRECT_TYPE_P (arg))
+ arg = TREE_TYPE (arg);
+ if (WILDCARD_TYPE_P (arg))
+ return true;
+ }
+ return false;
+}
+
/* Returns true iff ATTR is an attribute which needs to be applied at
instantiation time rather than template definition time. */
@@ -1166,6 +1208,10 @@ is_late_template_attribute (tree attr, tree decl)
|| is_attribute_p ("used", name)))
return false;
+ /* Allow alias templates to set diagnose_as on the RHS template. */
+ if (is_attribute_p ("diagnose_as", name))
+ return !is_alias_template_p (decl);
+
/* Attribute tls_model wants to modify the symtab. */
if (is_attribute_p ("tls_model", name))
return true;
@@ -35,6 +35,7 @@ along with GCC; see the file COPYING3. If not see
#include "internal-fn.h"
#include "gcc-rich-location.h"
#include "cp-name-hint.h"
+#include "attribs.h"
#define pp_separate_with_comma(PP) pp_cxx_separate_with (PP, ',')
#define pp_separate_with_semicolon(PP) pp_cxx_separate_with (PP, ';')
@@ -66,6 +67,7 @@ static void dump_alias_template_specialization (cxx_pretty_printer *, tree, int)
static void dump_type (cxx_pretty_printer *, tree, int);
static void dump_typename (cxx_pretty_printer *, tree, int);
static void dump_simple_decl (cxx_pretty_printer *, tree, tree, int);
+static void dump_decl_name_or_diagnose_as (cxx_pretty_printer *, tree, int);
static void dump_decl (cxx_pretty_printer *, tree, int);
static void dump_template_decl (cxx_pretty_printer *, tree, int);
static void dump_function_decl (cxx_pretty_printer *, tree, int);
@@ -231,7 +233,15 @@ dump_scope (cxx_pretty_printer *pp, tree scope, int flags)
{
if (scope != global_namespace)
{
- dump_decl (pp, scope, f);
+ tree diagnose_as
+ = flag_diagnostics_use_aliases
+ ? lookup_attribute ("diagnose_as", DECL_ATTRIBUTES (scope))
+ : NULL_TREE;
+ if (diagnose_as)
+ pp_cxx_ws_string (
+ pp, TREE_STRING_POINTER (TREE_VALUE (TREE_VALUE (diagnose_as))));
+ else
+ dump_decl (pp, scope, f);
pp_cxx_colon_colon (pp);
}
}
@@ -764,6 +774,45 @@ class_key_or_enum_as_string (tree t)
return "struct";
}
+/* Print out an enclosing scope of a template instance by inspecting the tree
+ SPECIAL of the template instantiation and the tree GENERAL of the most
+ general template in parallel under the control of FLAGS while adjusting
+ PARMS as needed. This allows the diagnose_as attribute to override the name
+ of full specializations, while hiding its template parameters, and to
+ override the name of partial specializations without falling back to printing
+ the template parameters of the general template decl. */
+
+static void
+dump_template_scope (cxx_pretty_printer *pp, tree special, tree general,
+ tree *parms, int flags)
+{
+ gcc_assert (parms);
+ if (special != general && RECORD_OR_UNION_TYPE_P (special) && *parms)
+ {
+ gcc_assert (RECORD_OR_UNION_TYPE_P (general));
+ const bool tmplate
+ = TYPE_LANG_SPECIFIC (special) && CLASSTYPE_TEMPLATE_INFO (special)
+ && (TREE_CODE (CLASSTYPE_TI_TEMPLATE (special)) != TEMPLATE_DECL
+ || PRIMARY_TEMPLATE_P (CLASSTYPE_TI_TEMPLATE (special)));
+ dump_template_scope (pp, CP_TYPE_CONTEXT (special),
+ CP_TYPE_CONTEXT (general),
+ tmplate ? &TREE_CHAIN(*parms) : parms, flags);
+ tree attr = lookup_attribute ("diagnose_as", TYPE_ATTRIBUTES (special));
+ if (attr)
+ {
+ pp_cxx_ws_string (pp, TREE_STRING_POINTER (
+ TREE_VALUE (TREE_VALUE (attr))));
+ if (tmplate)
+ TREE_VALUE (*parms) = make_tree_vec (0);
+ }
+ else
+ dump_aggr_type (pp, general, flags | TFF_UNQUALIFIED_NAME);
+ pp_cxx_colon_colon (pp);
+ }
+ else
+ dump_scope (pp, general, flags);
+}
+
/* Print out a class declaration T under the control of FLAGS,
in the form `class foo'. */
@@ -781,6 +830,7 @@ dump_aggr_type (cxx_pretty_printer *pp, tree t, int flags)
tree decl = TYPE_NAME (t);
+ tree diagnose_as = NULL_TREE;
if (decl)
{
typdef = (!DECL_ARTIFICIAL (decl)
@@ -804,11 +854,14 @@ dump_aggr_type (cxx_pretty_printer *pp, tree t, int flags)
&& (TREE_CODE (CLASSTYPE_TI_TEMPLATE (t)) != TEMPLATE_DECL
|| PRIMARY_TEMPLATE_P (CLASSTYPE_TI_TEMPLATE (t)));
- if (! (flags & TFF_UNQUALIFIED_NAME))
- dump_scope (pp, CP_DECL_CONTEXT (decl), flags | TFF_SCOPE);
- flags &= ~TFF_UNQUALIFIED_NAME;
+ tree context = CP_DECL_CONTEXT (decl);
+ tree diagnose_as_specialized = NULL_TREE;
if (tmplate)
{
+ if (flag_diagnostics_use_aliases)
+ diagnose_as_specialized
+ = lookup_attribute ("diagnose_as", TYPE_ATTRIBUTES (t));
+
/* Because the template names are mangled, we have to locate
the most general template, and use that name. */
tree tpl = TYPE_TI_TEMPLATE (t);
@@ -817,9 +870,61 @@ dump_aggr_type (cxx_pretty_printer *pp, tree t, int flags)
tpl = DECL_TI_TEMPLATE (tpl);
decl = tpl;
}
+
+ if (flag_diagnostics_use_aliases)
+ {
+ diagnose_as = lookup_attribute ("diagnose_as",
+ TYPE_ATTRIBUTES (TREE_TYPE (decl)));
+ if (diagnose_as_specialized
+ && (!diagnose_as || TREE_VALUE (diagnose_as_specialized)
+ != TREE_VALUE (diagnose_as)))
+ {
+ diagnose_as = diagnose_as_specialized;
+ /* Skip dump_template_parms if diagnose_as applies to a full
+ specialization. */
+ tree args = CLASSTYPE_TI_ARGS (t);
+ args = INNERMOST_TEMPLATE_ARGS (args);
+ gcc_assert (args);
+ tmplate = false;
+ for (int i = 0; i < NUM_TMPL_ARGS (args); ++i)
+ {
+ tree arg = TREE_VEC_ELT (args, i);
+ while (INDIRECT_TYPE_P (arg))
+ arg = TREE_TYPE (arg);
+ if (WILDCARD_TYPE_P (arg))
+ {
+ tmplate = true;
+ break;
+ }
+ }
+ }
+ /* Given
+
+ template <class T> struct [[gnu::diagnose_as("AA")]] A {
+ struct [[gnu::diagnose_as("BB")]] B {};
+ };
+
+ A<int>::B will reach the following branch and find the diagnose_as
+ attribute for B. */
+ else if (!tmplate && !diagnose_as && TYPE_TEMPLATE_INFO (t))
+ diagnose_as
+ = lookup_attribute ("diagnose_as",
+ TYPE_ATTRIBUTES (
+ TREE_TYPE (TYPE_TI_TEMPLATE (t))));
+
+ if (diagnose_as && TREE_CHAIN (TREE_VALUE (diagnose_as)))
+ context = TREE_VALUE (TREE_CHAIN (TREE_VALUE (diagnose_as)));
+ }
+
+ if (! (flags & TFF_UNQUALIFIED_NAME))
+ dump_scope (pp, context, flags | TFF_SCOPE);
+ flags &= ~TFF_UNQUALIFIED_NAME;
}
- if (LAMBDA_TYPE_P (t))
+ if (diagnose_as)
+ pp_cxx_ws_string (pp, TREE_STRING_POINTER (
+ TREE_VALUE (TREE_VALUE (diagnose_as))));
+ else if (LAMBDA_TYPE_P (t))
{
/* A lambda's "type" is essentially its signature. */
pp_string (pp, M_("<lambda"));
@@ -1141,7 +1246,7 @@ dump_simple_decl (cxx_pretty_printer *pp, tree t, tree type, int flags)
pp_string (pp, " capture>");
}
else
- dump_decl (pp, DECL_NAME (t), flags);
+ dump_decl_name_or_diagnose_as (pp, t, flags);
}
else if (DECL_DECOMPOSITION_P (t))
pp_string (pp, M_("<structured bindings>"));
@@ -1185,6 +1290,25 @@ dump_decl_name (cxx_pretty_printer *pp, tree t, int flags)
pp_cxx_tree_identifier (pp, t);
}
+/* Print the DECL_NAME of DECL unless a different string was requested by the
+ diagnose_as attribute. */
+
+static void
+dump_decl_name_or_diagnose_as (cxx_pretty_printer *pp, tree decl, int flags)
+{
+ if (flag_diagnostics_use_aliases)
+ {
+ tree attr = lookup_attribute ("diagnose_as", DECL_ATTRIBUTES (decl));
+ if (attr && TREE_VALUE (attr))
+ {
+ pp_cxx_ws_string (
+ pp, TREE_STRING_POINTER (TREE_VALUE (TREE_VALUE (attr))));
+ return;
+ }
+ }
+ dump_decl_name (pp, DECL_NAME (decl), flags);
+}
+
/* Dump a human readable string for the decl T under control of FLAGS. */
static void
@@ -1230,7 +1354,7 @@ dump_decl (cxx_pretty_printer *pp, tree t, int flags)
|| flags & TFF_CLASS_KEY_OR_ENUM))
{
pp_cxx_ws_string (pp, "using");
- dump_decl (pp, DECL_NAME (t), flags);
+ dump_decl_name_or_diagnose_as(pp, t, flags);
pp_cxx_whitespace (pp);
pp_cxx_ws_string (pp, "=");
pp_cxx_whitespace (pp);
@@ -1412,7 +1536,7 @@ dump_decl (cxx_pretty_printer *pp, tree t, int flags)
TREE_CODE (DECL_INITIAL (t)) == TEMPLATE_PARM_INDEX))
dump_simple_decl (pp, t, TREE_TYPE (t), flags);
else if (DECL_NAME (t))
- dump_decl (pp, DECL_NAME (t), flags);
+ dump_decl_name_or_diagnose_as (pp, t, flags);
else if (DECL_INITIAL (t))
dump_expr (pp, DECL_INITIAL (t), flags | TFF_EXPR_IN_PARENS);
else
@@ -1431,7 +1555,7 @@ dump_decl (cxx_pretty_printer *pp, tree t, int flags)
}
dump_type (pp, scope, flags);
pp_cxx_colon_colon (pp);
- dump_decl (pp, DECL_NAME (t), flags);
+ dump_decl_name_or_diagnose_as (pp, t, flags);
if (variadic)
pp_cxx_ws_string (pp, "...");
}
@@ -1671,7 +1795,8 @@ dump_function_decl (cxx_pretty_printer *pp, tree t, int flags)
tree template_args = NULL_TREE;
tree template_parms = NULL_TREE;
int show_return = flags & TFF_RETURN_TYPE || flags & TFF_DECL_SPECIFIERS;
- int do_outer_scope = ! (flags & TFF_UNQUALIFIED_NAME);
+ bool do_outer_scope = ! (flags & TFF_UNQUALIFIED_NAME);
+ bool do_template_scope = false;
tree exceptions;
bool constexpr_p;
tree ret = NULL_TREE;
@@ -1688,6 +1813,11 @@ dump_function_decl (cxx_pretty_printer *pp, tree t, int flags)
/* Likewise for the constexpr specifier, in case t is a specialization. */
constexpr_p = DECL_DECLARED_CONSTEXPR_P (t);
+ /* Keep t before the following branch makes t point to a more general
+ template. Without the specialized template, the diagnose_as attribute on
+ the function is lost. */
+ tree specialized_t = t;
+
/* Pretty print template instantiations only. */
if (DECL_USE_TEMPLATE (t) && DECL_TEMPLATE_INFO (t)
&& !(flags & TFF_NO_TEMPLATE_BINDINGS)
@@ -1699,8 +1829,46 @@ dump_function_decl (cxx_pretty_printer *pp, tree t, int flags)
tmpl = most_general_template (t);
if (tmpl && TREE_CODE (tmpl) == TEMPLATE_DECL)
{
+ /* Inspect whether any record/union type in the context of the
+ instantiated function template carries a diagnose_as attribute. If
+ one does, we need to print the scope and template argument list
+ differently. Consider:
+
+ template <class T> struct A {
+ template <class U0, class U1> struct B;
+ template <class U> struct B<U, int> {
+ void f();
+ };
+ };
+ using Achar [[gnu::diagnose_as("Achar")]] = A<char>;
+
+ Now Achar::B<int, int>::f() needs to print as "Achar::B<U, int>::f()
+ [with U = int]". However, DECL_CONTEXT (t) would print as
+ "Achar::B<int, int>" and dump_aggr_type has no sensible way to
+ retrieve A<T>::B<U, int>. tmpl was generalized to A<T>::B<U, int>::f
+ and thus dump_aggr_type (DECL_CONTEXT (tmpl)) would print
+ "A<T>::B<U, int> [with T = char, U = int]". I.e. the diagnose_as
+ attribute on the A<char> instantiation is lost. */
+ if (tmpl != t && do_outer_scope && flag_diagnostics_use_aliases)
+ {
+ for (tree context = DECL_CONTEXT (t);
+ context && RECORD_OR_UNION_TYPE_P (context);
+ context = TYPE_CONTEXT (context))
+ {
+ if (lookup_attribute ("diagnose_as",
+ TYPE_ATTRIBUTES (context)))
+ {
+ do_template_scope = true;
+ break;
+ }
+ }
+ }
+
template_parms = DECL_TEMPLATE_PARMS (tmpl);
t = tmpl;
+ /* The "[with ...]" clause is printed, thus dump_template_params must
+ unconditionally present functions as primary templates. */
+ dump_function_name_flags |= TFF_AS_PRIMARY;
}
}
@@ -1747,6 +1915,25 @@ dump_function_decl (cxx_pretty_printer *pp, tree t, int flags)
/* Print the function name. */
if (!do_outer_scope)
/* Nothing. */;
+ else if (do_template_scope)
+ {
+ template_parms = copy_list (template_parms);
+ bool func_template = TREE_TYPE (TREE_VALUE (template_parms)) == t;
+ dump_template_scope(pp, DECL_CONTEXT (specialized_t), DECL_CONTEXT (t),
+ func_template ? &TREE_CHAIN (template_parms)
+ : &template_parms,
+ flags);
+ if (TREE_VEC_LENGTH (TREE_VALUE (template_parms)) == 0)
+ {
+ /* If no template parms are left, make it a NULL_TREE so that no
+ "[with ]" is printed. */
+ tree p = TREE_CHAIN (template_parms);
+ for (; p && TREE_VEC_LENGTH (TREE_VALUE (p)) == 0; p = TREE_CHAIN (p))
+ ;
+ if (!p)
+ template_parms = template_args = NULL_TREE;
+ }
+ }
else if (cname)
{
dump_type (pp, cname, flags);
@@ -1755,7 +1942,7 @@ dump_function_decl (cxx_pretty_printer *pp, tree t, int flags)
else
dump_scope (pp, CP_DECL_CONTEXT (t), flags);
- dump_function_name (pp, t, dump_function_name_flags);
+ dump_function_name (pp, specialized_t, dump_function_name_flags);
if (!(flags & TFF_NO_FUNCTION_ARGUMENTS))
{
@@ -1935,6 +2122,14 @@ dump_function_name (cxx_pretty_printer *pp, tree t, int flags)
if (TREE_CODE (t) == TEMPLATE_DECL)
t = DECL_TEMPLATE_RESULT (t);
+ if (flag_diagnostics_use_aliases)
+ {
+ tree attr = lookup_attribute ("diagnose_as", DECL_ATTRIBUTES (t));
+ if (attr)
+ name = get_identifier (
+ TREE_STRING_POINTER (TREE_VALUE (TREE_VALUE (attr))));
+ }
+
/* Don't let the user see __comp_ctor et al. */
if (DECL_CONSTRUCTOR_P (t)
|| DECL_DESTRUCTOR_P (t))
@@ -1989,6 +2184,8 @@ dump_template_parms (cxx_pretty_printer *pp, tree info,
{
tree args = info ? TI_ARGS (info) : NULL_TREE;
+ if (flags & TFF_AS_PRIMARY)
+ primary = true;
if (primary && flags & TFF_TEMPLATE_NAME)
return;
flags &= ~(TFF_CLASS_KEY_OR_ENUM | TFF_TEMPLATE_NAME);
@@ -3168,7 +3365,7 @@ lang_decl_name (tree decl, int v, bool translate)
&& TREE_CODE (decl) == NAMESPACE_DECL)
dump_decl (cxx_pp, decl, TFF_PLAIN_IDENTIFIER | TFF_UNQUALIFIED_NAME);
else
- dump_decl (cxx_pp, DECL_NAME (decl), TFF_PLAIN_IDENTIFIER);
+ dump_decl_name_or_diagnose_as (cxx_pp, decl, TFF_PLAIN_IDENTIFIER);
return pp_ggc_formatted_text (cxx_pp);
}
@@ -3953,6 +4150,13 @@ comparable_template_types_p (tree type_a, tree type_b)
if (!CLASS_TYPE_P (type_b))
return false;
+ /* If one of the types has a diagnose_as attribute set it is presented as a
+ non-template (even if it's a template specialization). */
+ if (flag_diagnostics_use_aliases
+ && (lookup_attribute ("diagnose_as", TYPE_ATTRIBUTES (type_a))
+ || lookup_attribute ("diagnose_as", TYPE_ATTRIBUTES (type_b))))
+ return false;
+
tree tinfo_a = TYPE_TEMPLATE_INFO (type_a);
tree tinfo_b = TYPE_TEMPLATE_INFO (type_b);
if (!tinfo_a || !tinfo_b)
@@ -4052,8 +4256,21 @@ print_template_differences (pretty_printer *pp, tree type_a, tree type_b,
tree tinfo_a = TYPE_TEMPLATE_INFO (type_a);
tree tinfo_b = TYPE_TEMPLATE_INFO (type_b);
- pp_printf (pp, "%s<",
- IDENTIFIER_POINTER (DECL_NAME (TI_TEMPLATE (tinfo_a))));
+ const char* identifier_a
+ = IDENTIFIER_POINTER (DECL_NAME (TI_TEMPLATE (tinfo_a)));
+ if (flag_diagnostics_use_aliases)
+ {
+ /* Locate the most general template, and see whether it carries the
+ diagnose_as attribute. If it does, use it as identifier for type_a. */
+ tree tpl = TI_TEMPLATE (tinfo_a);
+ while (DECL_TEMPLATE_INFO (tpl))
+ tpl = DECL_TI_TEMPLATE (tpl);
+ tree attr = lookup_attribute ("diagnose_as",
+ TYPE_ATTRIBUTES (TREE_TYPE (tpl)));
+ if (attr)
+ identifier_a = TREE_STRING_POINTER (TREE_VALUE (TREE_VALUE (attr)));
+ }
+ pp_printf (pp, "%s<", identifier_a);
tree args_a = TI_ARGS (tinfo_a);
tree args_b = TI_ARGS (tinfo_b);
@@ -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. */
@@ -435,7 +435,7 @@ extern tree cp_namespace_decls (tree);
extern void set_decl_namespace (tree, tree, bool);
extern void push_decl_namespace (tree);
extern void pop_decl_namespace (void);
-extern void do_namespace_alias (tree, tree);
+extern void do_namespace_alias (tree, tree, tree);
extern tree do_class_using_decl (tree, tree);
extern tree lookup_arg_dependent (tree, tree, vec<tree, va_gc> *);
extern tree search_anon_aggr (tree, tree, bool = false);
@@ -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.
@@ -46,6 +46,7 @@ static tree verify_stmt_tree_r (tree *, int *, void *);
static tree handle_init_priority_attribute (tree *, tree, tree, int, bool *);
static tree handle_abi_tag_attribute (tree *, tree, tree, int, bool *);
+static tree handle_diagnose_as_attribute (tree *, tree, tree, int, bool *);
/* If REF is an lvalue, returns the kind of lvalue that REF is.
Otherwise, returns clk_none. */
@@ -4877,6 +4878,8 @@ const struct attribute_spec cxx_attribute_table[] =
handle_init_priority_attribute, NULL },
{ "abi_tag", 1, -1, false, false, false, true,
handle_abi_tag_attribute, NULL },
+ { "diagnose_as", 0, -1, false, false, false, false,
+ handle_diagnose_as_attribute, NULL },
{ NULL, 0, 0, false, false, false, false, NULL, NULL }
};
@@ -5145,6 +5148,154 @@ handle_abi_tag_attribute (tree* node, tree name, tree args,
return NULL_TREE;
}
+static bool
+check_diagnose_as_redeclaration (const_tree decl, const_tree name,
+ const_tree old, const_tree new_)
+{
+ if (!old)
+ return true;
+ if (TREE_CODE (TREE_VALUE (old)) == TREE_LIST)
+ old = TREE_VALUE (old);
+ if (TREE_CODE (TREE_VALUE (new_)) == TREE_LIST)
+ new_ = TREE_VALUE (new_);
+ tree old_value = TREE_VALUE (old);
+ tree new_value = TREE_VALUE (new_);
+ if (cp_tree_equal (old_value, new_value))
+ return true;
+ warning (OPT_Wattributes, "%qD redeclared with %<%D(%E)%> "
+ "attribute", decl, name, new_value);
+ inform (DECL_SOURCE_LOCATION (decl), "previous declaration here");
+ return false;
+}
+
+static tree
+handle_diagnose_as_attribute (tree* node, tree name, tree args,
+ int flags, bool* no_add_attrs)
+{
+ tree decl = NULL_TREE;
+ const bool is_alias = (TREE_CODE (*node) == TYPE_DECL);
+ if (args && list_length (args) == 2
+ && TREE_PURPOSE (TREE_CHAIN (args)) == integer_zero_node)
+ /* We're called from handle_diagnose_as_attribute with additional context
+ argument. */;
+ else if (is_alias)
+ {
+ if (args && (TREE_CHAIN (args)
+ || TREE_CODE (TREE_VALUE (args)) != STRING_CST))
+ {
+ warning (OPT_Wattributes,
+ "%qD attribute requires 0 or 1 NTBS arguments", name);
+ goto fail;
+ }
+ }
+ else if (!args || TREE_CHAIN (args)
+ || TREE_CODE (TREE_VALUE (args)) != STRING_CST)
+ {
+ warning (OPT_Wattributes,
+ "%qD attribute requires a single NTBS argument", name);
+ goto fail;
+ }
+
+ if (is_alias && CLASS_TYPE_P (TREE_TYPE (*node)))
+ { // Apply the attribute to the type alias itself.
+ decl = *node;
+
+ /* Reject alias templates without wildcards on the innermost template args
+ of the RHS type. E.g. template <class> using A = B; */
+ if (DECL_LANG_SPECIFIC (decl)
+ && DECL_TEMPLATE_INFO (decl)
+ && DECL_ALIAS_TEMPLATE_P (DECL_TI_TEMPLATE (decl))
+ && DECL_TI_TEMPLATE (decl)
+ == TREE_TYPE (INNERMOST_TEMPLATE_PARMS (
+ DECL_TEMPLATE_PARMS (
+ DECL_TI_TEMPLATE (decl)))))
+ return error_mark_node;
+
+ if (!args)
+ { // turn alias identifier into attribute argument
+ tree alias_name = DECL_NAME (decl);
+ tree str = fix_string_type (
+ build_string(IDENTIFIER_LENGTH (alias_name) + 1,
+ IDENTIFIER_POINTER (alias_name)));
+ args = build_tree_list (NULL_TREE, str);
+ }
+ // apply the attribute to the specialization on the RHS.
+ tree type = strip_typedefs (TREE_TYPE (*node), nullptr, 0);
+
+ if (is_alias_template_p (decl))
+ {
+ type = CLASSTYPE_PRIMARY_TEMPLATE_TYPE (type);
+ // Warn and skip if the template arguments don't match up like in
+ // template <class T> using A = B<int, T>;
+ if (!same_type_p (TREE_TYPE (decl), type))
+ return error_mark_node;
+ }
+
+ // Add the DECL_CONTEXT of the alias to the attribute if it is different
+ // to the context of the type.
+ if (!TREE_CHAIN (args)
+ && CP_DECL_CONTEXT (decl) != CP_TYPE_CONTEXT (type))
+ {
+ TREE_CHAIN (args) = build_tree_list (integer_zero_node,
+ CP_DECL_CONTEXT (decl));
+ }
+
+ if (COMPLETE_TYPE_P (type))
+ {
+ tree rhs = handle_diagnose_as_attribute (
+ &type, name, args, flags, no_add_attrs);
+ if (rhs == error_mark_node || *no_add_attrs)
+ return rhs;
+ else
+ TYPE_ATTRIBUTES (type)
+ = tree_cons (name, args, TYPE_ATTRIBUTES (type));
+ }
+ else
+ {
+ tree attributes = tree_cons (name, args, NULL_TREE);
+ cplus_decl_attributes (&type, attributes, ATTR_FLAG_TYPE_IN_PLACE);
+ }
+ }
+ else if (TYPE_P (*node))
+ {
+ if (!OVERLOAD_TYPE_P (*node))
+ return error_mark_node;
+ decl = TYPE_NAME (*node);
+ }
+ else
+ {
+ if (!VAR_OR_FUNCTION_DECL_P (*node)
+ || DECL_LANGUAGE (*node) != lang_cplusplus)
+ return error_mark_node;
+ decl = *node;
+ }
+
+ // Make sure all declarations have the same diagnose_as string.
+ if (DECL_SOURCE_LOCATION (decl) != input_location)
+ {
+ tree attributes = TYPE_P (*node) ? TYPE_ATTRIBUTES (*node)
+ : DECL_ATTRIBUTES (decl);
+ if (!check_diagnose_as_redeclaration (
+ decl, name, lookup_attribute ("diagnose_as", attributes), args))
+ goto fail;
+ }
+ else if (CLASS_TYPE_P (*node) && CLASSTYPE_IMPLICIT_INSTANTIATION (*node))
+ {
+ // The above branch (different source location) is taken for declarations
+ // of type aliases that modify an implicit template specialization on the
+ // RHS. This branch is taken when the template is instantiated via
+ // instantiate_class_template_1, in which case the attribute either
+ // already has the value from the general template or from a type alias.
+ goto fail;
+ }
+
+ return NULL_TREE;
+
+ fail:
+ *no_add_attrs = true;
+ return NULL_TREE;
+}
+
/* Return a new PTRMEM_CST of the indicated TYPE. The MEMBER is the
thing pointed to by the constant. */
@@ -2865,6 +2865,51 @@ types (@pxref{Variable Attributes}, @pxref{Type Attributes}.)
The message attached to the attribute is affected by the setting of
the @option{-fmessage-length} option.
+@item diagnose_as ("@var{string}")
+@itemx diagnose_as
+@cindex @code{diagnose_as} function attribute
+The @code{diagnose_as} attribute modifies how the entity the attribute
+appertains to is diagnosed in compiler messages and @code{__func__},
+@code{__FUNCTION__}, and @code{__PRETTY_FUNCTION__} macros. (The result of
+@code{typeid} is not affected.) If the attribute is applied to a
+@code{namespace}, the specified string replaces the complete enclosing scope.
+The effect of the @code{diagnose_as} attribute can be disabled with the
+@option{-fno-diagnostics-use-aliases} command line option.
+
+The argument @var{string} is optional for type and namespace aliases. If it is
+omitted, the identifier of the type or namespace alias is unconditionally used
+in all places where the type / namespace on the right-hand side is diagnosed.
+
+@smallexample
+namespace Foo @{
+ namespace Bar @{
+ inline namespace v1 [[gnu::diagnose_as("Foobar")]] @{
+ int f() @{
+ // __PRETTY_FUNCTION__ == "void Foobar::f()"
+ @}
+ @}
+ @}
+@}
+// In function 'int Foobar::f()':
+// warning: no return statement in function returning non-void [-Wreturn-type]
+@end smallexample
+
+The @code{diagnose_as} attribute can be used with namespaces, namespace aliases,
+functions, variables, alias declarations (but not alias templates), and
+user-defined types (classes, unions, and enums). If the alias declaration
+aliases a class type (including template specializations), the attribute is
+additionally applied to the class type. Whether the attribute has an effect on
+partial template specializations is unspecified and may depend on several
+different factors.
+
+@smallexample
+template <class T> struct basic_string @{@};
+using string [[gnu::diagnose_as]] = basic_string<char>;
+int f(basic_string<char>) @{@}
+// In function 'int f(string)':
+// warning: no return statement in function returning non-void [-Wreturn-type]
+@end smallexample
+
@item error ("@var{message}")
@itemx warning ("@var{message}")
@cindex @code{error} function attribute
@@ -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.
new file mode 100644
@@ -0,0 +1,213 @@
+// { dg-options "-fdiagnostics-use-aliases -fpretty-templates" }
+
+#ifdef __cpp_constexpr
+template <typename T>
+ constexpr bool is_char() { return false; }
+
+template <>
+ constexpr bool is_char<char>() { return true; }
+
+template <typename T>
+ constexpr bool is_int() { return false; }
+
+template <>
+ constexpr bool is_int<int>() { return true; }
+#endif
+
+namespace __attribute__((diagnose_as())) foo0 {} // { dg-warning "'diagnose_as' attribute requires a single NTBS argument" }
+
+namespace __attribute__((diagnose_as("foo2"))) foo1 {} // { dg-line foo1 }
+namespace __attribute__((diagnose_as("XXXX"))) foo1 {} // { dg-warning "'foo1' redeclared with different 'diagnose_as' attribute value" }
+// { dg-message "previous declaration here" "" { target *-*-* } foo1 }
+namespace foo1
+{
+ void f() {
+ f(1); // { dg-error "too many arguments to function 'void foo2::f\\(\\)'" }
+ }
+
+ class __attribute__((diagnose_as("XX"))) X; // { dg-line XX }
+ class __attribute__((diagnose_as("--"))) X { // { dg-warning "'class foo2::XX' redeclared with 'diagnose_as\\(\"--\"\\)' attribute" }
+ // { dg-message "previous declaration here" "" { target *-*-* } XX }
+ __attribute__((diagnose_as("ff"))) void f();
+ };
+ class Y : X
+ {
+ void g() {
+ f(); // { dg-error "'void foo2::XX::ff\\(\\)' is private within this context" }
+ }
+ };
+
+ class __attribute__((diagnose_as())) Z0; // { dg-warning "'diagnose_as' attribute requires a single NTBS argument" }
+ class __attribute__((diagnose_as(1))) Z1; // { dg-warning "'diagnose_as' attribute requires a single NTBS argument" }
+ class __attribute__((diagnose_as("a", "b"))) Z2; // { dg-warning "'diagnose_as' attribute requires a single NTBS argument" }
+
+ template <typename T> class A0 {};
+ typedef A0<char> Achar __attribute__((diagnose_as("Achar")));
+ template class A0<int>;
+ typedef A0<int> Aint __attribute__((diagnose_as("Aint"))); // OK
+ typedef A0<int> Aint2 __attribute__((diagnose_as("Aint2"))); // { dg-warning "'class foo2::Aint' redeclared with 'diagnose_as\\(\"Aint2\"\\)' attribute" }
+ typedef A0<int> Aint3 __attribute__((diagnose_as(1))); // { dg-warning "'diagnose_as' attribute requires 0 or 1 NTBS arguments" }
+
+#if __cplusplus >= 201103L
+ template <typename T> using foo [[gnu::diagnose_as]] = A0<T>;
+ template <typename T> using aRef [[gnu::diagnose_as]] = A0<const T&>; // { dg-warning "'diagnose_as' attribute ignored" "" { target c++11 } }
+#endif
+}
+
+namespace A
+{
+ inline namespace __attribute__((diagnose_as("@1"))) B
+ {
+ template <typename U>
+ struct __attribute__((diagnose_as("@2"))) C
+ {
+ __attribute__((diagnose_as("fun:1")))
+ void f() {} // { dg-line ABC_f }
+
+ template <typename T>
+ __attribute__((diagnose_as("fun:2")))
+ void g(T, U) {} // { dg-line ABC_gT }
+
+ template <typename T>
+ __attribute__((diagnose_as("fun:2.2")))
+ void g() {} // { dg-line ABC_g }
+
+ __attribute__((diagnose_as("fun:3")))
+ void h()
+ {
+#ifdef __cpp_constexpr
+ static_assert(__builtin_strcmp(__FUNCTION__, "fun:3") == 0, "");
+ static_assert(__builtin_strcmp(__func__, "fun:3") == 0, "");
+ constexpr const char* ref
+ = is_int<U>() ? "void @1::@3::fun:3()"
+ : "void @1::@2<U>::fun:3() [with U = char]";
+ static_assert(__builtin_strcmp(__PRETTY_FUNCTION__, ref) == 0, "");
+#endif
+ }
+
+ template <typename T>
+ __attribute__((diagnose_as("fun:4")))
+ void ht()
+ {
+#ifdef __cpp_constexpr
+ static_assert(__builtin_strcmp(__FUNCTION__, "fun:4") == 0, "");
+ constexpr const char* ref
+ = is_int<U>()
+ ? "void @1::@3::fun:4<T>() [with T = float]"
+ : "void @1::@2<U>::fun:4<T>() [with T = float; U = char]";
+ static_assert(__builtin_strcmp(__PRETTY_FUNCTION__, ref) == 0, "");
+#endif
+ }
+
+ template <typename T0, typename T1>
+ struct __attribute__((diagnose_as("@5"))) E
+ {
+ __attribute__((diagnose_as("fun:5"))) static void f() {
+#ifdef __cpp_constexpr
+ static_assert(__builtin_strcmp(__FUNCTION__, "fun:5") == 0, "");
+ constexpr const char* ref = is_int<U>()
+ ? is_char<T0>()
+ ? "static void @1::@3::@6::fun:5()"
+ : "static void @1::@3::@5<T0, T1>::fun:5() [with T0 = float; T1 = short int]"
+ : is_char<T0>()
+ ? "static void @1::@2<U>::@6::fun:5() [with U = char]"
+ : "static void @1::@2<U>::@5<T0, T1>::fun:5() [with T0 = float; T1 = short int; U = char]";
+ static_assert(__builtin_strcmp(__PRETTY_FUNCTION__, ref) == 0,
+ "");
+#endif
+ }
+ };
+ typedef E<char, char> F __attribute__((diagnose_as("@6")));
+
+ template <typename T>
+ struct __attribute__((diagnose_as("@7"))) E<T, long>
+ {
+ __attribute__((diagnose_as("fun:6"))) static void f() {
+#ifdef __cpp_constexpr
+ static_assert(__builtin_strcmp(__FUNCTION__, "fun:6") == 0, "");
+ constexpr const char* ref = is_int<U>()
+ ? "static void @1::@3::@7<T, long int>::fun:6() [with T = float]"
+ : "static void @1::@2<U>::@7<T, long int>::fun:6() [with T = float; U = char]";
+ static_assert(__builtin_strcmp(__PRETTY_FUNCTION__, ref) == 0,
+ "");
+#endif
+ }
+ };
+ };
+
+ typedef C<int> D __attribute__((diagnose_as("@3")));
+
+ template <>
+ struct __attribute__((diagnose_as("@4"))) C<float>
+ {
+ };
+
+ typedef C<short> E __attribute__((diagnose_as));
+ }
+}
+
+namespace frob
+{
+ namespace ni
+ {
+ struct kate {};
+ namespace cate
+ {
+ struct me {};
+ }
+ }
+}
+
+namespace frobni __attribute__((diagnose_as("twiggle"))) = frob::ni;
+namespace frobnicate __attribute__((diagnose_as)) = frob::ni::cate;
+namespace wrong __attribute__((diagnose_as(1))) = frob::ni; // { dg-warning "'diagnose_as' attribute requires 0 or 1 NTBS arguments" }
+namespace wrong2 __attribute__((diagnose_as("wrong", 1))) = frob::ni; // { dg-warning "'diagnose_as' attribute requires 0 or 1 NTBS arguments" }
+
+void fn_1(int);
+void fn_2(A::D);
+
+int main ()
+{
+ fn_2 (A::D ());
+ fn_1 (A::D ()); // { dg-error "cannot convert '@1::@3' to 'int'" }
+ fn_1 (A::C<int> ()); // { dg-error "cannot convert '@1::@3' to 'int'" }
+ fn_1 (A::C<char> ()); // { dg-error "cannot convert '@1::@2<char>' to 'int'" }
+ fn_1 (A::C<float> ()); // { dg-error "cannot convert '@1::@4' to 'int'" }
+ fn_1 (A::C<short> ()); // { dg-error "cannot convert '@1::E' to 'int'" }
+ fn_1 (A::E ()); // { dg-error "cannot convert '@1::E' to 'int'" }
+ fn_1 (foo1::A0<int> ()); // { dg-error "cannot convert 'foo2::Aint' to 'int'" }
+ fn_1 (foo1::Aint2 ()); // { dg-error "cannot convert 'foo2::Aint2' {aka 'foo2::Aint'} to 'int'" }
+#if __cplusplus >= 201103L
+ fn_1 (foo1::foo<float> ()); // { dg-error "cannot convert 'foo2::foo<float>' to 'int'" "" { target c++11 } }
+ fn_1 (foo1::aRef<float> ()); // { dg-error "cannot convert 'foo2::aRef<float>' {aka 'foo2::foo<const float&>'} to 'int'" "" { target c++11 } }
+ fn_1 (frob::ni::kate ()); // { dg-error "cannot convert 'twiggle::kate' to 'int'" "" { target c++11 } }
+ fn_1 (frob::ni::cate::me ()); // { dg-error "cannot convert 'frobnicate::me' to 'int'" "" { target c++11 } }
+#endif
+
+ A::C<int>().f(0); // { dg-error "no matching function for call to '@1::@3::f\\(int\\)'" }
+ // { dg-message "candidate: 'void @1::@3::fun:1\\(\\)'" "" { target *-*-* } ABC_f }
+
+ A::C<char>().f(0); // { dg-error "no matching function for call to '@1::@2<char>::f\\(int\\)'" }
+ // { dg-message "candidate: 'void @1::@2<U>::fun:1\\(\\) \\\[with U = char\\\]'" "" { target *-*-* } ABC_f }
+
+ A::C<float>().f(0); // { dg-error "'struct @1::@4' has no member named 'f'" }
+
+ A::C<int>().g(); // { dg-error "no matching function for call to '@1::@3::g\\(\\)'" }
+ // { dg-message "candidate: 'template<class T> void @1::@3::fun:2\\(T, U\\)'" "" { target *-*-* } ABC_gT }
+ // { dg-message "candidate: 'template<class T> void @1::@3::fun:2.2\\(\\)'" "" { target *-*-* } ABC_g }
+
+ A::C<char>().g(); // { dg-error "no matching function for call to '@1::@2<char>::g\\(\\)'" }
+ // { dg-message "candidate: 'template<class T> void @1::@2<U>::fun:2\\(T, U\\) \\\[with U = char\\\]'" "" { target *-*-* } ABC_gT }
+ // { dg-message "candidate: 'template<class T> void @1::@2<U>::fun:2.2\\(\\) \\\[with U = char\\\]'" "" { target *-*-* } ABC_g }
+
+ A::C<int>().h();
+ A::C<char>().h();
+ A::C<int>().ht<float>();
+ A::C<char>().ht<float>();
+ A::C<int>::E<float, short>::f();
+ A::C<char>::E<float, short>::f();
+ A::C<int>::E<float, long>::f();
+ A::C<char>::E<float, long>::f();
+ A::C<int>::F::f();
+ A::C<char>::F::f();
+}
new file mode 100644
@@ -0,0 +1,144 @@
+// { dg-options "-fdiagnostics-use-aliases -fno-pretty-templates" }
+
+#ifdef __cpp_constexpr
+template <typename T>
+ constexpr bool is_char() { return false; }
+
+template <>
+ constexpr bool is_char<char>() { return true; }
+
+template <typename T>
+ constexpr bool is_int() { return false; }
+
+template <>
+ constexpr bool is_int<int>() { return true; }
+#endif
+
+namespace A
+{
+ inline namespace B __attribute__((diagnose_as("@1")))
+ {
+ template <typename U>
+ struct __attribute__((diagnose_as("@2"))) C
+ {
+ __attribute__((diagnose_as("fun:1")))
+ void f() {} // { dg-line ABC_f }
+
+ template <typename T>
+ __attribute__((diagnose_as("fun:2")))
+ void g(T, U) {} // { dg-line ABC_gT }
+
+ template <typename T>
+ __attribute__((diagnose_as("fun:2.2")))
+ void g() {} // { dg-line ABC_g }
+
+ __attribute__((diagnose_as("fun:3")))
+ void h()
+ {
+#ifdef __cpp_constexpr
+ static_assert(__builtin_strcmp(__FUNCTION__, "fun:3") == 0, "");
+ constexpr const char* ref
+ = is_int<U>() ? "void @1::@3::fun:3()"
+ : "void @1::@2<char>::fun:3()";
+ static_assert(__builtin_strcmp(__PRETTY_FUNCTION__, ref) == 0, "");
+#endif
+ }
+
+ template <typename T>
+ __attribute__((diagnose_as("fun:4")))
+ void ht()
+ {
+#ifdef __cpp_constexpr
+ static_assert(__builtin_strcmp(__FUNCTION__, "fun:4") == 0, "");
+ constexpr const char* ref
+ = is_int<U>() ? "void @1::@3::fun:4<float>()"
+ : "void @1::@2<char>::fun:4<float>()";
+ static_assert(__builtin_strcmp(__PRETTY_FUNCTION__, ref) == 0, "");
+#endif
+ }
+
+ template <typename T0, typename T1>
+ struct __attribute__((diagnose_as("@5"))) E
+ {
+ __attribute__((diagnose_as("fun:5"))) static void f() {
+#ifdef __cpp_constexpr
+ static_assert(__builtin_strcmp(__FUNCTION__, "fun:5") == 0, "");
+ constexpr const char* ref = is_int<U>()
+ ? is_char<T0>()
+ ? "static void @1::@3::@6::fun:5()"
+ : "static void @1::@3::@5<float, short int>::fun:5()"
+ : is_char<T0>()
+ ? "static void @1::@2<char>::@6::fun:5()"
+ : "static void @1::@2<char>::@5<float, short int>::fun:5()";
+ static_assert(__builtin_strcmp(__PRETTY_FUNCTION__, ref) == 0,
+ "");
+#endif
+ }
+ };
+ typedef E<char, char> F __attribute__((diagnose_as("@6")));
+
+ template <typename T>
+ struct __attribute__((diagnose_as("@7"))) E<T, long>
+ {
+ __attribute__((diagnose_as("fun:6"))) static void f() {
+#ifdef __cpp_constexpr
+ static_assert(__builtin_strcmp(__FUNCTION__, "fun:6") == 0, "");
+ // Note that @7 is ignored with -fno-pretty-templates. After all
+ // diagnose_as on partial specializations is not supported.
+ constexpr const char* ref = is_int<U>()
+ ? "static void @1::@3::@5<float, long int>::fun:6()"
+ : "static void @1::@2<char>::@5<float, long int>::fun:6()";
+ static_assert(__builtin_strcmp(__PRETTY_FUNCTION__, ref) == 0,
+ "");
+#endif
+ }
+ };
+ };
+
+ typedef C<int> D __attribute__((diagnose_as("@3")));
+
+ template <>
+ struct __attribute__((diagnose_as("@4"))) C<float>
+ {
+ };
+ }
+}
+
+void fn_1(int);
+void fn_2(A::D);
+
+int main ()
+{
+ fn_2 (A::D ());
+ fn_1 (A::D ()); // { dg-error "cannot convert '@1::@3' to 'int'" }
+ fn_1 (A::C<int> ()); // { dg-error "cannot convert '@1::@3' to 'int'" }
+ fn_1 (A::C<char> ()); // { dg-error "cannot convert '@1::@2<char>' to 'int'" }
+ fn_1 (A::C<float> ()); // { dg-error "cannot convert '@1::@4' to 'int'" }
+
+ A::C<int>().f(0); // { dg-error "no matching function for call to '@1::@3::f\\(int\\)'" }
+ // { dg-message "candidate: 'void @1::@3::fun:1\\(\\)'" "" { target *-*-* } ABC_f }
+
+ A::C<char>().f(0); // { dg-error "no matching function for call to '@1::@2<char>::f\\(int\\)'" }
+ // { dg-message "candidate: 'void @1::@2<char>::fun:1\\(\\)'" "" { target *-*-* } ABC_f }
+
+ A::C<float>().f(0); // { dg-error "'struct @1::@4' has no member named 'f'" }
+
+ A::C<int>().g(); // { dg-error "no matching function for call to '@1::@3::g\\(\\)'" }
+ // { dg-message "candidate: 'template<class T> void @1::@3::fun:2\\(T, int\\)'" "" { target *-*-* } ABC_gT }
+ // { dg-message "candidate: 'template<class T> void @1::@3::fun:2.2\\(\\)'" "" { target *-*-* } ABC_g }
+
+ A::C<char>().g(); // { dg-error "no matching function for call to '@1::@2<char>::g\\(\\)'" }
+ // { dg-message "candidate: 'template<class T> void @1::@2<char>::fun:2\\(T, char\\)'" "" { target *-*-* } ABC_gT }
+ // { dg-message "candidate: 'template<class T> void @1::@2<char>::fun:2.2\\(\\)'" "" { target *-*-* } ABC_g }
+
+ A::C<int>().h();
+ A::C<char>().h();
+ A::C<int>().ht<float>();
+ A::C<char>().ht<float>();
+ A::C<int>::E<float, short>::f();
+ A::C<char>::E<float, short>::f();
+ A::C<int>::E<float, long>::f();
+ A::C<char>::E<float, long>::f();
+ A::C<int>::F::f();
+ A::C<char>::F::f();
+}
new file mode 100644
@@ -0,0 +1,152 @@
+// { dg-options "-fdiagnostics-use-aliases -fpretty-templates" }
+// { dg-do compile { target c++11 } }
+
+const char* volatile s;
+
+namespace foo
+{
+ template <class U>
+ struct [[gnu::diagnose_as("Bar'")]] Bar
+ {
+ template <class T0, class T1>
+ struct [[gnu::diagnose_as("A'")]] A
+ { constexpr const char* f() { return __PRETTY_FUNCTION__; } };
+
+ template <class T>
+ struct A<T, float>
+ { constexpr const char* f() { return __PRETTY_FUNCTION__; } };
+
+ template <class T>
+ struct [[gnu::diagnose_as("Adouble")]] A<T, double>
+ { constexpr const char* f() { return __PRETTY_FUNCTION__; } };
+
+ using Special [[gnu::diagnose_as("SpecialA")]] = A<int, int>;
+
+ static constexpr const char* f() { return __PRETTY_FUNCTION__; }
+ };
+
+ template <class P>
+ struct
+ [[gnu::diagnose_as("BarPtr")]]
+ Bar<P*>
+ {
+ template <class U = int>
+ static constexpr const char* f() { return __PRETTY_FUNCTION__; }
+ };
+
+ template <>
+ struct [[gnu::diagnose_as("SpecialBar")]] Bar<void>
+ {
+ static constexpr const char* f() { return __PRETTY_FUNCTION__; }
+ };
+
+ using barchar [[gnu::diagnose_as("barchar")]] = Bar<char>;
+}
+
+namespace A
+{
+ inline namespace B __attribute__((diagnose_as("@1")))
+ {
+ template <typename U>
+ struct __attribute__((diagnose_as("@2"))) C
+ {
+ template <class T>
+ __attribute__((diagnose_as("fun:2")))
+ constexpr const char* g() { return __PRETTY_FUNCTION__; }
+
+ __attribute__((diagnose_as("fun:3")))
+ constexpr const char* h()
+ { return __PRETTY_FUNCTION__; }
+ };
+
+ typedef C<int> D __attribute__((diagnose_as("@3")));
+ }
+}
+
+template <class T0, class U0>
+struct [[gnu::diagnose_as("X.0")]] X0
+{
+ template <class T1, class U1>
+ struct X1
+ {
+ template <class T2, class U2>
+ struct X2
+ {
+ template <class T3, class U3>
+ [[gnu::diagnose_as("f-1")]]
+ static constexpr const char* f() {
+ return __PRETTY_FUNCTION__;
+ }
+ };
+
+ using XX [[gnu::diagnose_as("X2'")]] = X2<int, long>;
+ };
+
+ template <class T3, class U3>
+ [[gnu::diagnose_as("f-1")]]
+ static constexpr const char* f() {
+ return __PRETTY_FUNCTION__;
+ }
+
+ struct [[gnu::diagnose_as("X.3")]] X3
+ {
+ template <class T3, class U3>
+ [[gnu::diagnose_as("f-1")]]
+ static constexpr const char* f() {
+ return __PRETTY_FUNCTION__;
+ }
+ };
+};
+
+using X0int [[gnu::diagnose_as("[int|int]")]] = X0<int, int>;
+
+#define TEST(expected, fun) \
+ static_assert(__builtin_strcmp(expected, fun) == 0, "")
+
+
+void h() {
+ TEST("constexpr const char* @1::@3::fun:3()",
+ A::C<int>().h());
+ TEST("constexpr const char* @1::@3::fun:2<T>() [with T = float]",
+ A::C<int>().g<float>());
+ TEST("constexpr const char* @1::@2<U>::fun:3() [with U = char]",
+ A::C<char>().h());
+ TEST("constexpr const char* @1::@2<U>::fun:2<T>() [with T = float; U = char]",
+ A::C<char>().g<float>());
+ TEST("constexpr const char* foo::barchar::A'<T0, T1>::f() [with T0 = char; T1 = char]",
+ (foo::Bar<char>::A<char, char>().f()));
+ TEST("constexpr const char* foo::barchar::A'<T, float>::f() [with T = char]",
+ (foo::Bar<char>::A<char, float>().f()));
+ TEST("constexpr const char* foo::barchar::Adouble<T, double>::f() [with T = int]",
+ (foo::Bar<char>::A<int, double>().f()));
+ TEST("constexpr const char* foo::barchar::SpecialA::f()",
+ (foo::Bar<char>::A<int, int>().f()));
+ TEST("constexpr const char* foo::Bar'<U>::A'<T0, T1>::f() [with T0 = char; T1 = char; U = float]",
+ (foo::Bar<float>::A<char, char>().f()));
+ TEST("constexpr const char* foo::Bar'<U>::A'<T, float>::f() [with T = char; U = float]",
+ (foo::Bar<float>::A<char, float>().f()));
+ TEST("constexpr const char* foo::Bar'<U>::Adouble<T, double>::f() [with T = char; U = float]",
+ (foo::Bar<float>::A<char, double>().f()));
+ TEST("constexpr const char* foo::Bar'<U>::SpecialA::f() [with U = float]",
+ (foo::Bar<float>::A<int, int>().f()));
+ TEST("static constexpr const char* foo::barchar::f()",
+ foo::Bar<char>::f());
+ TEST("static constexpr const char* foo::BarPtr<P*>::f<U>() [with U = int; P = char]",
+ foo::Bar<char*>::f());
+ TEST("static constexpr const char* foo::BarPtr<P*>::f<U>() [with U = int; P = float]",
+ foo::Bar<float*>::f());
+ TEST("static constexpr const char* foo::Bar'<U>::f() [with U = float]",
+ foo::Bar<float>::f());
+ TEST("static constexpr const char* foo::SpecialBar::f()",
+ foo::Bar<void>::f());
+ TEST("static constexpr const char* X.0<T0, U0>::X1<T1, U1>::X2'::f-1<T3, U3>() [with T3 = long int; U3 = long long int; T1 = short int; U1 = int; T0 = char; U0 = short int]",
+ (X0<char, short>::X1<short, int>::X2<int, long>::f<long, long long>()));
+ TEST("static constexpr const char* X.0<T0, U0>::X1<T1, U1>::X2<T2, U2>::f-1<T3, U3>() [with T3 = long int; U3 = long long int; T2 = long int; U2 = int; T1 = short int; U1 = int; T0 = char; U0 = short int]",
+ (X0<char, short>::X1<short, int>::X2<long, int>::f<long, long long>()));
+ TEST("static constexpr const char* X.0<T0, U0>::X.3::f-1<T3, U3>() [with T3 = long int; U3 = long long int; T0 = char; U0 = short int]",
+ (X0<char, short>::X3::f<long, long long>()));
+ TEST("static constexpr const char* [int|int]::f-1<T3, U3>() [with T3 = long int; U3 = long long int]",
+ (X0<int, int>::f<long, long long>()));
+ TEST("static constexpr const char* [int|int]::X.3::f-1<T3, U3>() [with T3 = long int; U3 = long long int]",
+ (X0<int, int>::X3::f<long, long long>()));
+}
new file mode 100644
@@ -0,0 +1,158 @@
+// { dg-options "-fdiagnostics-use-aliases -fno-pretty-templates" }
+// { dg-do compile { target c++11 } }
+
+const char* volatile s;
+
+namespace foo
+{
+ template <class U>
+ struct [[gnu::diagnose_as("Bar'")]] Bar
+ {
+ template <class T0, class T1>
+ struct [[gnu::diagnose_as("A'")]] A
+ { constexpr const char* f() { return __PRETTY_FUNCTION__; } };
+
+ template <class T>
+ struct A<T, float>
+ { constexpr const char* f() { return __PRETTY_FUNCTION__; } };
+
+ template <class T>
+ struct [[gnu::diagnose_as("Adouble")]] A<T, double>
+ { constexpr const char* f() { return __PRETTY_FUNCTION__; } };
+
+ using Special [[gnu::diagnose_as("SpecialA")]] = A<int, int>;
+
+ static constexpr const char* f() { return __PRETTY_FUNCTION__; }
+ };
+
+ template <class P>
+ struct
+ [[gnu::diagnose_as("BarPtr")]]
+ Bar<P*>
+ {
+ template <class U = int>
+ static constexpr const char* f() { return __PRETTY_FUNCTION__; }
+ };
+
+ template <>
+ struct [[gnu::diagnose_as("SpecialBar")]] Bar<void>
+ {
+ static constexpr const char* f() { return __PRETTY_FUNCTION__; }
+ };
+
+ using barchar [[gnu::diagnose_as("barchar")]] = Bar<char>;
+}
+
+namespace A
+{
+ inline namespace B __attribute__((diagnose_as("@1")))
+ {
+ template <typename U>
+ struct __attribute__((diagnose_as("@2"))) C
+ {
+ template <class T>
+ __attribute__((diagnose_as("fun:2")))
+ constexpr const char* g() { return __PRETTY_FUNCTION__; }
+
+ __attribute__((diagnose_as("fun:3")))
+ constexpr const char* h()
+ { return __PRETTY_FUNCTION__; }
+ };
+
+ typedef C<int> D __attribute__((diagnose_as("@3")));
+ }
+}
+
+template <class T0, class U0>
+struct [[gnu::diagnose_as("XX0")]] X0
+{
+ template <class T1, class U1>
+ struct X1
+ {
+ template <class T2, class U2>
+ struct X2
+ {
+ template <class T3, class U3>
+ [[gnu::diagnose_as("f-1")]]
+ static constexpr const char* f() {
+ return __PRETTY_FUNCTION__;
+ }
+ };
+
+ using XX [[gnu::diagnose_as("X2'")]] = X2<int, long>;
+ };
+
+ template <class T3, class U3>
+ [[gnu::diagnose_as("f-2")]]
+ static constexpr const char* f() {
+ return __PRETTY_FUNCTION__;
+ }
+
+ struct [[gnu::diagnose_as("XX3")]] X3
+ {
+ template <class T3, class U3>
+ [[gnu::diagnose_as("f-3")]]
+ static constexpr const char* f() { // { dg-line X0_X3_f }
+ return __PRETTY_FUNCTION__;
+ }
+ };
+};
+
+using X0int [[gnu::diagnose_as("X0intint")]] = X0<int, int>;
+
+#define TEST(expected, fun) \
+ static_assert(__builtin_strcmp(expected, fun) == 0, "")
+
+
+void h() {
+ TEST("constexpr const char* @1::@3::fun:3()",
+ A::C<int>().h());
+ TEST("constexpr const char* @1::@3::fun:2<float>()",
+ A::C<int>().g<float>());
+ TEST("constexpr const char* @1::@2<char>::fun:3()",
+ A::C<char>().h());
+ TEST("constexpr const char* @1::@2<char>::fun:2<float>()",
+ A::C<char>().g<float>());
+ TEST("constexpr const char* foo::barchar::A'<char, char>::f()",
+ (foo::Bar<char>::A<char, char>().f()));
+ TEST("constexpr const char* foo::barchar::A'<char, float>::f()",
+ (foo::Bar<char>::A<char, float>().f()));
+ TEST("constexpr const char* foo::barchar::A'<int, double>::f()",
+ (foo::Bar<char>::A<int, double>().f()));
+ TEST("constexpr const char* foo::barchar::SpecialA::f()",
+ (foo::Bar<char>::A<int, int>().f()));
+ TEST("constexpr const char* foo::Bar'<float>::A'<char, char>::f()",
+ (foo::Bar<float>::A<char, char>().f()));
+ TEST("constexpr const char* foo::Bar'<float>::A'<char, float>::f()",
+ (foo::Bar<float>::A<char, float>().f()));
+ TEST("constexpr const char* foo::Bar'<float>::A'<char, double>::f()",
+ (foo::Bar<float>::A<char, double>().f()));
+ TEST("constexpr const char* foo::Bar'<float>::SpecialA::f()",
+ (foo::Bar<float>::A<int, int>().f()));
+ TEST("static constexpr const char* foo::barchar::f()",
+ foo::Bar<char>::f());
+ TEST("static constexpr const char* foo::Bar'<char*>::f<int>()",
+ foo::Bar<char*>::f());
+ TEST("static constexpr const char* foo::Bar'<float*>::f<int>()",
+ foo::Bar<float*>::f());
+ TEST("static constexpr const char* foo::Bar'<float>::f()",
+ foo::Bar<float>::f());
+ TEST("static constexpr const char* foo::SpecialBar::f()",
+ foo::Bar<void>::f());
+ TEST("static constexpr const char* XX0<char, short int>::X1<short int, int>::X2'::f-1<long int, long long int>()",
+ (X0<char, short>::X1<short, int>::X2<int, long>::f<long, long long>()));
+ TEST("static constexpr const char* XX0<char, short int>::X1<short int, int>::X2<long int, int>::f-1<long int, long long int>()",
+ (X0<char, short>::X1<short, int>::X2<long, int>::f<long, long long>()));
+ TEST("static constexpr const char* XX0<char, short int>::XX3::f-3<long int, long long int>()",
+ (X0<char, short>::X3::f<long, long long>()));
+ TEST("static constexpr const char* X0intint::f-2<long int, long long int>()",
+ (X0<int, int>::f<long, long long>()));
+ TEST("static constexpr const char* X0intint::XX3::f-3<long int, long long int>()",
+ (X0<int, int>::X3::f<long, long long>()));
+
+ X0<char, short>::X3::f<long, long long>(1); // { dg-error "no matching function for call to 'XX0<char, short int>::XX3::f<long int, long long int>\\(int\\)" }
+ // { dg-message "candidate: 'static constexpr const char\\* XX0<char, short int>::XX3::f-3<long int, long long int>\\(\\)'" "" { target *-*-* } X0_X3_f }
+
+ X0<int, int>::X3::f<long, long long>(1); // { dg-error "no matching function for call to 'X0intint::XX3::f<long int, long long int>\\(int\\)" }
+ // { dg-message "candidate: 'static constexpr const char\\* X0intint::XX3::f-3<long int, long long int>\\(\\)'" "" { target *-*-* } X0_X3_f }
+}
new file mode 100644
@@ -0,0 +1,21 @@
+// { dg-options "-fdiagnostics-use-aliases -fpretty-templates" }
+// { dg-do compile { target c++11 } }
+
+namespace std
+{
+ template <class T, class A>
+ class basic_string {};
+
+ namespace pmr
+ {
+ struct P {};
+ using string [[gnu::diagnose_as]] = basic_string<char, P>;
+ }
+}
+
+void f(int);
+
+int main()
+{
+ f(std::pmr::string()); // { dg-error "cannot convert 'std::pmr::string' to 'int'" }
+}
new file mode 100644
@@ -0,0 +1,45 @@
+// { dg-options "-fdiagnostics-use-aliases -fpretty-templates" }
+// { dg-do compile { target c++11 } }
+
+template <class T, class U>
+ struct A
+ {
+ struct X {};
+ using Y [[gnu::diagnose_as]] = X;
+ };
+
+template <>
+ struct [[gnu::diagnose_as("A!")]] A<int, int>
+ {
+ struct X {};
+ using Y [[gnu::diagnose_as]] = X;
+ };
+
+void f(int);
+
+template <class T, class U>
+ using B [[gnu::diagnose_as]] = A<T, U>;
+
+// this is not a real alias template
+template <class T>
+ using C [[gnu::diagnose_as]] = A<int, int>; // { dg-warning "'diagnose_as' attribute ignored" }
+
+// Would the following request A<float, int> to be diagnosed as D<int>?
+// What about `= A<float, const T>', etc? This is not worth the implementation
+// complexity.
+template <class T>
+ using D [[gnu::diagnose_as]] = A<float, T>; // { dg-warning "'diagnose_as' attribute ignored" }
+
+template <class T, class U>
+ struct E {};
+template <class T, class U>
+ using F [[gnu::diagnose_as("EF")]] = E<U, T>; // { dg-warning "'diagnose_as' attribute ignored" }
+
+int main()
+{
+ f(A<char, int>()); // { dg-error "cannot convert 'B<char, int>' to 'int'" }
+ f(A<char, int>::X()); // { dg-error "cannot convert 'B<char, int>::Y' to 'int'" }
+ f(B<int, int>::X()); // { dg-error "cannot convert 'A!::Y' to 'int'" }
+ f(C<float>::X()); // { dg-error "cannot convert 'A!::Y' to 'int'" }
+ f(F<char, int>()); // { dg-error "cannot convert 'F<char, int>' {aka 'E<int, char>'} to 'int'" }
+}