@@ -1619,6 +1619,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.
@@ -6829,6 +6829,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_renaming_alias_template (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,39 @@ grokbitfield (const cp_declarator *declarator,
return value;
}
+/* Return true iff DECL is an alias template of a class template. This predicate
+ also works before the alias template has its DECL_TEMPLATE_INFO. */
+bool
+is_renaming_alias_template (tree decl)
+{
+ if (TREE_CODE (decl) != TYPE_DECL)
+ return false;
+
+ tree type = strip_typedefs (TREE_TYPE (decl), nullptr, 0);
+ if (!TYPE_LANG_SPECIFIC (type) || !TYPE_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 DECL_PRIMARY_TEMPLATE 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 (!PRIMARY_TEMPLATE_P (tmpl))
+ return false;
+
+ tree targs = INNERMOST_TEMPLATE_ARGS (CLASSTYPE_TI_ARGS (type));
+ return any_dependent_template_arguments_p (targs);
+}
+
/* Returns true iff ATTR is an attribute which needs to be applied at
instantiation time rather than template definition time. */
@@ -1166,6 +1199,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_renaming_alias_template (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);
@@ -104,6 +106,8 @@ static void cp_print_error_function (diagnostic_context *, diagnostic_info *);
static bool cp_printer (pretty_printer *, text_info *, const char *,
int, bool, bool, bool, bool *, const char **);
+static tree lookup_diagnose_as_attribute (cxx_pretty_printer *, tree);
+static void dump_diagnose_as_alias (cxx_pretty_printer *, tree, tree, int);
/* Struct for handling %H or %I, which require delaying printing the
type until a postprocessing stage. */
@@ -216,7 +220,7 @@ dump_module_suffix (cxx_pretty_printer *pp, tree decl)
static void
dump_scope (cxx_pretty_printer *pp, tree scope, int flags)
{
- int f = flags & (TFF_SCOPE | TFF_CHASE_TYPEDEF);
+ int f = flags & (TFF_SCOPE | TFF_CHASE_TYPEDEF | TFF_AS_PRIMARY);
if (scope == NULL_TREE)
return;
@@ -231,7 +235,7 @@ dump_scope (cxx_pretty_printer *pp, tree scope, int flags)
{
if (scope != global_namespace)
{
- dump_decl (pp, scope, f);
+ dump_decl (pp, scope, f);
pp_cxx_colon_colon (pp);
}
}
@@ -770,6 +774,60 @@ 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)
+{
+ if (special != general && CLASS_TYPE_P (special) && parms)
+ {
+ gcc_assert (CLASS_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);
+ flags |= TFF_UNQUALIFIED_NAME;
+ tree attr = lookup_diagnose_as_attribute (pp, TYPE_ATTRIBUTES (special));
+ if (attr)
+ {
+ dump_diagnose_as_alias (pp, attr, NULL_TREE, flags);
+ if (tmplate)
+ /* Hide the template parameters of this level from
+ dump_template_bindings so that the diagnose_as attribute makes
+ the type appear as a non-template. */
+ TREE_VALUE (parms) = make_tree_vec (0);
+ }
+ else if (NON_DEFAULT_TEMPLATE_ARGS_COUNT (CLASSTYPE_TI_ARGS (special))
+ && NON_DEFAULT_TEMPLATE_ARGS_COUNT (CLASSTYPE_TI_ARGS (general))
+ && GET_NON_DEFAULT_TEMPLATE_ARGS_COUNT
+ (CLASSTYPE_TI_ARGS (special))
+ > GET_NON_DEFAULT_TEMPLATE_ARGS_COUNT
+ (CLASSTYPE_TI_ARGS (general)))
+ /* If SPECIAL has more non-default template args than GENERAL, print
+ SPECIAL (with TFF_AS_PRIMARY in FLAGS). Otherwise, template
+ parameters will be missing in the output. */
+ dump_aggr_type (pp, special, flags);
+ else
+ /* Printing without TFF_AS_PRIMARY is necessary to print partial
+ specializations of class templates correctly. If GENERAL is not a
+ partial specialization, TFF_AS_PRIMARY makes no difference. */
+ dump_aggr_type (pp, general, flags & ~TFF_AS_PRIMARY);
+ pp_cxx_colon_colon (pp);
+ }
+ else
+ dump_scope (pp, special, flags);
+}
+
/* Print out a class declaration T under the control of FLAGS,
in the form `class foo'. */
@@ -787,6 +845,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)
@@ -810,11 +869,13 @@ 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;
+ const tree context = CP_DECL_CONTEXT (decl);
+ tree diagnose_as_specialized = NULL_TREE;
if (tmplate)
{
+ diagnose_as_specialized = lookup_diagnose_as_attribute
+ (pp, 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);
@@ -823,9 +884,48 @@ 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_diagnose_as_attribute
+ (pp, TYPE_ATTRIBUTES (TREE_TYPE (decl)));
+ if (diagnose_as_specialized
+ && (!diagnose_as || diagnose_as_specialized != 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);
+ ++processing_template_decl;
+ tmplate = any_dependent_template_arguments_p (args);
+ --processing_template_decl;
+ }
+ /* 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_diagnose_as_attribute
+ (pp, TYPE_ATTRIBUTES (TREE_TYPE (TYPE_TI_TEMPLATE (t))));
+ }
+
+ if (diagnose_as)
+ dump_diagnose_as_alias (pp, diagnose_as, context, flags);
+ else if (! (flags & TFF_UNQUALIFIED_NAME))
+ dump_scope (pp, context, flags | TFF_SCOPE);
+ flags &= ~TFF_UNQUALIFIED_NAME;
}
- if (LAMBDA_TYPE_P (t))
+ if (diagnose_as)
+ /* identifier printed via dump_diagnose_as_alias above */;
+ else if (LAMBDA_TYPE_P (t))
{
/* A lambda's "type" is essentially its signature. */
pp_string (pp, M_("<lambda"));
@@ -1147,7 +1247,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>"));
@@ -1191,6 +1291,64 @@ dump_decl_name (cxx_pretty_printer *pp, tree t, int flags)
pp_cxx_tree_identifier (pp, t);
}
+/* Returns a TREE_LIST of diagnose_as attribute arguments if set, NULL_TREE
+ otherwise. */
+
+static tree
+lookup_diagnose_as_attribute (cxx_pretty_printer *, tree attributes)
+{
+ if (!flag_diagnostics_use_aliases)
+ return NULL_TREE;
+ tree attr = lookup_attribute ("diagnose_as", attributes);
+ if (!attr)
+ return NULL_TREE;
+ return TREE_VALUE (attr);
+}
+
+/* Print out the diagnose_as attribute argument list ATTR under the control of
+ FLAGS. If FLAGS does not have TFF_UNQUALIFIED_NAME set, include the CONTEXT
+ in the output. ATTR can override CONTEXT. */
+
+static void
+dump_diagnose_as_alias (cxx_pretty_printer *pp, tree diagnose_as, tree context,
+ int flags)
+{
+
+ tree arg0 = TREE_VALUE (diagnose_as);
+ if (TREE_CODE (arg0) == STRING_CST)
+ {
+ if (! (flags & TFF_UNQUALIFIED_NAME))
+ {
+ if (TREE_CHAIN (diagnose_as))
+ context = TREE_VALUE (TREE_CHAIN (diagnose_as));
+ if (context)
+ dump_scope (pp, context, flags | TFF_SCOPE);
+ }
+ pp_cxx_ws_string (pp, TREE_STRING_POINTER (arg0));
+ }
+ else
+ {
+ gcc_assert (TREE_CODE (arg0) == TYPE_DECL);
+ dump_simple_decl (pp, arg0,
+ DECL_ORIGINAL_TYPE (arg0) ? DECL_ORIGINAL_TYPE (arg0)
+ : TREE_TYPE (arg0),
+ flags & ~TFF_DECL_SPECIFIERS);
+ }
+}
+
+/* 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)
+{
+ tree attr = lookup_diagnose_as_attribute (pp, DECL_ATTRIBUTES (decl));
+ if (attr)
+ dump_diagnose_as_alias (pp, attr, NULL_TREE, flags | TFF_UNQUALIFIED_NAME);
+ else
+ dump_decl_name (pp, DECL_NAME (decl), flags);
+}
+
/* Dump a human readable string for the decl T under control of FLAGS. */
static void
@@ -1236,7 +1394,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);
@@ -1289,18 +1447,28 @@ dump_decl (cxx_pretty_printer *pp, tree t, int flags)
pp->declaration (t);
else
{
- if (! (flags & TFF_UNQUALIFIED_NAME))
- dump_scope (pp, CP_DECL_CONTEXT (t), flags);
- flags &= ~TFF_UNQUALIFIED_NAME;
- if (DECL_NAME (t) == NULL_TREE)
- {
- if (!(pp->flags & pp_c_flag_gnu_v3))
- pp_cxx_ws_string (pp, M_("{anonymous}"));
- else
- pp_cxx_ws_string (pp, M_("(anonymous namespace)"));
- }
+ tree diagnose_as
+ = lookup_diagnose_as_attribute (pp, DECL_ATTRIBUTES (t));
+ if (diagnose_as)
+ /* Do not print DECL_CONTEXT (t), if there's any, since the
+ diagnose_as attribute on a namespace hides all enclosing scopes.
+ */
+ dump_diagnose_as_alias (pp, diagnose_as, NULL_TREE, flags);
else
- pp_cxx_tree_identifier (pp, DECL_NAME (t));
+ {
+ if (! (flags & TFF_UNQUALIFIED_NAME))
+ dump_scope (pp, CP_DECL_CONTEXT (t), flags);
+ flags &= ~TFF_UNQUALIFIED_NAME;
+ if (DECL_NAME (t) == NULL_TREE)
+ {
+ if (!(pp->flags & pp_c_flag_gnu_v3))
+ pp_cxx_ws_string (pp, M_("{anonymous}"));
+ else
+ pp_cxx_ws_string (pp, M_("(anonymous namespace)"));
+ }
+ else
+ pp_cxx_tree_identifier (pp, DECL_NAME (t));
+ }
}
break;
@@ -1418,7 +1586,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
@@ -1437,7 +1605,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, "...");
}
@@ -1678,7 +1846,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;
@@ -1713,6 +1882,43 @@ 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 or
+ might be an instantiation of a partially specialized class
+ template. 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)
+ {
+ for (tree context = DECL_CONTEXT (t);
+ context && CLASS_TYPE_P (context);
+ context = TYPE_CONTEXT (context))
+ {
+ if (lookup_diagnose_as_attribute (pp,
+ TYPE_ATTRIBUTES (context))
+ || CLASSTYPE_USE_TEMPLATE (context))
+ {
+ do_template_scope = true;
+ break;
+ }
+ }
+ }
+
template_parms = DECL_TEMPLATE_PARMS (tmpl);
t = tmpl;
/* The "[with ...]" clause is printed, thus dump functions printing
@@ -1764,6 +1970,15 @@ 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 | specialized_flags);
+ }
else if (cname)
{
dump_type (pp, cname, flags | specialized_flags);
@@ -1965,7 +2180,10 @@ dump_function_name (cxx_pretty_printer *pp, tree t, int flags)
name = constructor_name (DECL_CONTEXT (t));
}
- if (DECL_DESTRUCTOR_P (t))
+ tree attr = lookup_diagnose_as_attribute (pp, DECL_ATTRIBUTES (t));
+ if (attr)
+ dump_diagnose_as_alias (pp, attr, NULL_TREE, TFF_PLAIN_IDENTIFIER);
+ else if (DECL_DESTRUCTOR_P (t))
{
pp_cxx_complement (pp);
dump_decl (pp, name, TFF_PLAIN_IDENTIFIER);
@@ -3213,7 +3431,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);
}
@@ -3998,6 +4216,12 @@ 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 (lookup_diagnose_as_attribute (cxx_pp, TYPE_ATTRIBUTES (type_a))
+ || lookup_diagnose_as_attribute (cxx_pp, 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)
@@ -4097,8 +4321,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_diagnose_as_attribute
+ (cxx_pp, TYPE_ATTRIBUTES (TREE_TYPE (tpl)));
+ if (attr)
+ identifier_a = TREE_STRING_POINTER (TREE_VALUE (attr));
+ }
+ pp_printf (pp, "%s<", identifier_a);
tree args_a = TI_ARGS (tinfo_a);
tree args_b = TI_ARGS (tinfo_b);
@@ -6090,6 +6090,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",
@@ -6122,7 +6169,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;
@@ -6138,6 +6185,10 @@ do_namespace_alias (tree alias, tree name_space)
DECL_CONTEXT (alias) = FROB_CONTEXT (current_scope ());
set_originating_module (alias);
+ /* Apply attributes. */
+ if (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 = NULL_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);
@@ -14819,20 +14819,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)
@@ -21279,15 +21274,20 @@ cp_parser_namespace_alias_definition (cp_parser* parser)
{
tree identifier;
tree namespace_specifier;
+ const location_t start = input_location;
cp_token *token = cp_lexer_peek_token (parser->lexer);
/* Look for the `namespace' keyword. */
cp_parser_require_keyword (parser, RID_NAMESPACE, RT_NAMESPACE);
+ /* Look for attributes (GCC extension). */
+ tree left_attributes = cp_parser_attributes_opt (parser);
/* Look for the identifier. */
identifier = cp_parser_identifier (parser);
if (identifier == error_mark_node)
return;
+ /* Look for more attributes (GCC extension). */
+ tree attributes = attr_chainon (left_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))
@@ -21299,7 +21299,15 @@ cp_parser_namespace_alias_definition (cp_parser* parser)
cp_lexer_consume_token (parser->lexer);
return;
}
- cp_parser_require (parser, CPP_EQ, RT_EQ);
+ if (!cp_parser_require (parser, CPP_EQ, RT_EQ))
+ {
+ input_location = start;
+ return;
+ }
+ if (left_attributes)
+ pedwarn (input_location, OPT_Wpedantic,
+ "standard attributes on namespaces aliases must follow "
+ "the namespace alias name");
/* Look for the qualified-namespace-specifier. */
namespace_specifier
= cp_parser_qualified_namespace_specifier (parser);
@@ -21308,7 +21316,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. */
@@ -4890,6 +4891,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 }
};
@@ -5158,6 +5161,169 @@ handle_abi_tag_attribute (tree* node, tree name, tree args,
return NULL_TREE;
}
+/* DECL is being redeclared; the old declaration had the diagnose_as attribute
+ in OLD, and the new one has the attribute in NEW_. Give an error if the
+ two attributes are not equal. */
+
+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;
+}
+
+/* Handle a "diagnose_as" attribute.
+
+ If the given NODE is a TYPE_DECL: Apply the attribute to the TREE_TYPE in
+ place, even if the type is complete. If no argument was given to the
+ attribute, use NODE as attribute argument when recursing to the TREE_TYPE.
+ Append CP_DECL_CONTEXT as second attribute argument if the TREE_TYPE type has
+ a different context. Alias templates without matching template arguments are
+ not supported. Apply the attribute to the primary template type for alias
+ templates. */
+
+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 && TREE_PURPOSE (args) == integer_zero_node)
+ /* We're recursing from handle_diagnose_as_attribute from a TYPE_DECL. */;
+ 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)))
+ {
+ decl = *node;
+
+ /* Reject alias templates without primary template on the RHS. E.g.
+ template <class> using A = B; */
+ if (DECL_LANG_SPECIFIC (decl)
+ && DECL_TEMPLATE_INFO (decl)
+ && DECL_ALIAS_TEMPLATE_P (DECL_TI_TEMPLATE (decl))
+ && PRIMARY_TEMPLATE_P (DECL_TI_TEMPLATE (decl)))
+ return error_mark_node;
+
+ /* Apply the attribute to the specialization on the RHS. */
+ tree type = strip_typedefs (TREE_TYPE (*node), nullptr, 0);
+
+ if (is_renaming_alias_template (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;
+ }
+
+ if (!args)
+ /* Pass the TYPE_DECL as attribute argument, i.e. tell TYPE to be
+ diagnosed as DECL. INTEGER_ZERO_NODE is to skip the argument checks
+ on recursion. */
+ args = build_tree_list (integer_zero_node, decl);
+ else if (!TREE_CHAIN (args)
+ && CP_DECL_CONTEXT (decl) != CP_TYPE_CONTEXT (type))
+ {
+ /* If the context of DECL and TYPE differ, diagnostics of TYPE would
+ be printed within the wrong scope. Therefore, pass the context of
+ DECL as second argument to the attribute, so that it can be printed
+ correctly. INTEGER_ZERO_NODE is used skip the argument checks on
+ recursion. */
+ TREE_PURPOSE (args) = integer_zero_node;
+ TREE_CHAIN (args) = build_tree_list (NULL_TREE,
+ CP_DECL_CONTEXT (decl));
+ }
+
+ if (COMPLETE_TYPE_P (type))
+ {
+ /* If TYPE is already complete, cplus_decl_attributes cannot be used
+ anymore. Recurse to handle_diagnose_as_attribute with the type
+ given on the RHS of the alias. On success, append the diagnose_as
+ attribute to TYPE's attributes. */
+ 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. */
@@ -2915,6 +2915,51 @@ interface. Other than emitting an error rather than a warning, the
The @code{unavailable} attribute can also be used for variables and
types (@pxref{Variable Attributes}, @pxref{Type Attributes}.)
+@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
@@ -313,7 +313,8 @@ Objective-C and Objective-C++ Dialects}.
-fno-show-column @gol
-fdiagnostics-column-unit=@r{[}display@r{|}byte@r{]} @gol
-fdiagnostics-column-origin=@var{origin} @gol
--fdiagnostics-escape-format=@r{[}unicode@r{|}bytes@r{]}}
+-fdiagnostics-escape-format=@r{[}unicode@r{|}bytes@r{]}
+-fno-diagnostics-aliases}
@item Warning Options
@xref{Warning Options,,Options to Request or Suppress Warnings}.
@@ -5159,6 +5160,12 @@ Unicode characters. For the example above, the following will be printed:
before<CF><80><BF>after
@end smallexample
+@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.
@@ -34,6 +34,13 @@ template <class a, class b = int>
[[deprecated]] static void f(d);
};
+template <class T>
+ struct c<T, char>
+ {
+ template <class d, class e = int>
+ [[deprecated]] static void g(d);
+ };
+
template <class T>
struct B { typedef T X; };
@@ -68,6 +75,7 @@ int main()
c<float, int>::f(1.); // { dg-warning "'static void c<a, b>::f\\(d\\) .with d = double; a = float; b = int.'" }
c<float, int>::f<int>(1); // { dg-warning "'static void c<a, b>::f<d>\\(d\\) .with d = int; a = float; b = int.'" }
c<float, int>::f<float, int>(1.f); // { dg-warning "'static void c<a, b>::f<d, e>\\(d\\) .with d = float; e = int; a = float; b = int.'" }
+ c<int, char>::g(1.); // { dg-warning "'static void c<T, char>::g\\(d\\) .with d = double; T = int.'" }
D<int>::foo(1); // { dg-warning "'static void D<U>::foo\\(typename B<V>::X\\) .with U = int; typename B<V>::X = int.'" }
}
new file mode 100644
@@ -0,0 +1,228 @@
+// { 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 "'foo2' 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, typename UU = void>
+ 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" }
+
+#if __cplusplus >= 201103L
+template <class T>
+ struct outer
+ {
+ struct inner {};
+ };
+template <class T>
+ using inandout [[gnu::diagnose_as]] = typename outer<T>::inner; // { dg-warning "'diagnose_as' attribute ignored" "" { target c++11 } }
+#endif
+
+
+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::foo<const float&> ()); // { dg-error "cannot convert 'foo2::foo<const float&>' to 'int'" "" { target c++11 } }
+ fn_1 (foo1::foo<float const&> ()); // { dg-error "cannot convert 'foo2::foo<const 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 } }
+ fn_1 (inandout<double> ()); // { dg-error "cannot convert 'inandout<double>' {aka 'outer<double>::inner'} to 'int'" "" { target c++11 } }
+ fn_1 (outer<char>::inner ()); // { dg-error "cannot convert 'outer<char>::inner' 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() [with P = char]",
+ foo::Bar<char*>::f());
+ TEST("static constexpr const char* foo::BarPtr<P*>::f() [with 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()",
+ foo::Bar<char*>::f());
+ TEST("static constexpr const char* foo::Bar'<float*>::f()",
+ 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: 'template<class T3, class U3> static constexpr const char\\* XX0<char, short int>::XX3::f-3\\(\\)'" "" { 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: 'template<class T3, class U3> static constexpr const char\\* X0intint::XX3::f-3\\(\\)'" "" { 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'" }
+}
new file mode 100644
@@ -0,0 +1,43 @@
+// { dg-options "-fdiagnostics-use-aliases -fpretty-templates" }
+// { dg-do compile { target c++11 } }
+
+namespace A
+{
+ inline namespace B
+ {
+ template <typename U>
+ struct __attribute__((diagnose_as("@2"))) C
+ {
+ template <typename T>
+ [[gnu::diagnose_as("fun:1")]] void f() {} // { dg-line f }
+
+ template <typename T>
+ [[gnu::diagnose_as("fun:2")]] void g(T) {} // { dg-line g }
+
+ template <typename T>
+ [[deprecated, gnu::diagnose_as("fun:3")]] void h(T) {}
+
+ template <typename T, typename R = void>
+ [[deprecated, gnu::diagnose_as("fun:4")]] R f0(T) { return R(); }
+ };
+
+ using D [[gnu::diagnose_as]] = C<int>;
+ }
+}
+
+namespace AB [[gnu::diagnose_as]] = A::B;
+
+int main()
+{
+ A::C<int>().f<float>(1); // { dg-error "no matching function for call to 'AB::D::f<float>\\(int\\)'" }
+ // { dg-message "candidate: 'template<class T> void AB::D::fun:1\\(\\)'" "" { target *-*-* } f }
+
+ A::C<int>().g<int>(1, 1); // { dg-error "no matching function for call to 'AB::D::g<int>\\(int, int\\)'" }
+ // { dg-message "candidate: 'template<class T> void AB::D::fun:2\\(T\\)'" "" { target *-*-* } g }
+
+ A::C<int>().h(1); // { dg-warning "'void AB::D::fun:3\\(T\\) \\\[with T = int\\\]' is deprecated" }
+
+ A::C<int>().f0(1); // { dg-warning "'R AB::D::fun:4\\(T\\) \\\[with T = int\\\]' is deprecated" }
+ A::C<int>().f0<float>(1); // { dg-warning "'R AB::D::fun:4<T>\\(T\\) \\\[with T = float\\\]' is deprecated" }
+ A::C<int>().f0<float, int>(1); // { dg-warning "'R AB::D::fun:4<T, R>\\(T\\) \\\[with T = float; R = int\\\]' is deprecated" }
+}