* [PATCH v3] c++: Add gnu::diagnose_as attribute
@ 2021-07-15 12:21 Matthias Kretz
2021-07-23 8:58 ` [PATCH v4] " Matthias Kretz
0 siblings, 1 reply; 24+ messages in thread
From: Matthias Kretz @ 2021-07-15 12:21 UTC (permalink / raw)
To: gcc-patches
[-- Attachment #1: Type: text/plain, Size: 7410 bytes --]
Hi Jason,
A new revision of the patch is attached. I think I implemented all your
suggestions.
Please comment on cp/decl2.c (is_alias_template_p). I find it surprising that
I had to write this function. Maybe I missed something? In any case,
DECL_ALIAS_TEMPLATE_P requires a template_decl and the TYPE_DECL apparently
doesn't have a template_info/decl at this point.
From: Matthias Kretz <m.kretz@gsi.de>
This attribute overrides the diagnostics output string for the entity it
appertains to. The motivation is to improve QoI for library TS
implementations, where diagnostics have a very bad signal-to-noise ratio
due to the long namespaces involved.
With the attribute, it is possible to solve PR89370 and make
std::__cxx11::basic_string<_CharT, _Traits, _Alloc> appear as
std::string in diagnostic output without extra hacks to recognize the
type in the C++ frontend.
Signed-off-by: Matthias Kretz <m.kretz@gsi.de>
gcc/ChangeLog:
PR c++/89370
* doc/extend.texi: Document the diagnose_as attribute.
* doc/invoke.texi: Document -fno-diagnostics-use-aliases.
gcc/c-family/ChangeLog:
PR c++/89370
* c.opt (fdiagnostics-use-aliases): New diagnostics flag.
gcc/cp/ChangeLog:
PR c++/89370
* cp-tree.h: Add TFF_AS_PRIMARY. Add is_alias_template_p
declaration.
* decl2.c (is_alias_template_p): New function. Determines
whether a given TYPE_DECL is actually an alias template that is
still missing its template_info.
(is_late_template_attribute): Decls with diagnose_as attribute
are early attributes only if they are alias templates.
* error.c (dump_scope): When printing the name of a namespace,
look for the diagnose_as attribute. If found, print the
associated string instead of calling dump_decl.
(dump_decl_name_or_diagnose_as): New function to replace
dump_decl (pp, DECL_NAME(t), flags) and inspect the tree for the
diagnose_as attribute before printing the DECL_NAME.
(dump_template_scope): New function. Prints the scope of a
template instance correctly applying diagnose_as attributes and
adjusting the list of template parms accordingly.
(dump_aggr_type): If the type has a diagnose_as attribute, print
the associated string instead of printing the original type
name. Print template parms only if the attribute was not applied
to the instantiation / full specialization. Delay call to
dump_scope until the diagnose_as attribute is found. If the
attribute has a second argument, use it to override the context
passed to dump_scope.
(dump_simple_decl): Call dump_decl_name_or_diagnose_as instead
of dump_decl.
(dump_decl): Ditto.
(lang_decl_name): Ditto.
(dump_function_decl): Walk the functions context list to
determine whether a call to dump_template_scope is required.
Ensure function templates are presented as primary templates.
(dump_function_name): Replace the function's identifier with the
diagnose_as attribute value, if set.
(dump_template_parms): Treat as primary template if flags
contains TFF_AS_PRIMARY.
(comparable_template_types_p): Consider the types not a template
if one carries a diagnose_as attribute.
(print_template_differences): Replace the identifier with the
diagnose_as attribute value on the most general template, if it
is set.
* name-lookup.c (handle_namespace_attrs): Handle the diagnose_as
attribute on namespaces. Ensure exactly one string argument.
Ensure previous diagnose_as attributes used the same name.
'diagnose_as' on namespace aliases are forwarded to the original
namespace. Support no-argument 'diagnose_as' on namespace
aliases.
(do_namespace_alias): Add attributes parameter and call
handle_namespace_attrs.
* name-lookup.h (do_namespace_alias): Add attributes tree
parameter.
* parser.c (cp_parser_declaration): If the next token is
RID_NAMESPACE, tentatively parse a namespace alias definition.
If this fails expect a namespace definition.
(cp_parser_namespace_alias_definition): Allow optional
attributes before and after the identifier. Fast exit if the
expected CPP_EQ token is missing. Pass attributes to
do_namespace_alias.
* tree.c (cxx_attribute_table): Add diagnose_as attribute to the
table.
(check_diagnose_as_redeclaration): New function; copied and
adjusted from check_abi_tag_redeclaration.
(handle_diagnose_as_attribute): New function; copied and
adjusted from handle_abi_tag_attribute. If the given *node is a
TYPE_DECL: allow no argument to the attribute, using DECL_NAME
instead; apply the attribute to the type on the RHS in place,
even if the type is complete. Allow 2 arguments when called from
handle_diagnose_as_attribute. For type aliases, append
CP_DECL_CONTEXT as second attribute argument when the RHS type
has a different context. Warn about alias templates without
matching template arguments. Apply the attribute to the primary
template type for alias templates.
gcc/testsuite/ChangeLog:
PR c++/89370
* g++.dg/diagnostic/diagnose-as1.C: New test.
* g++.dg/diagnostic/diagnose-as2.C: New test.
* g++.dg/diagnostic/diagnose-as3.C: New test.
* g++.dg/diagnostic/diagnose-as4.C: New test.
* g++.dg/diagnostic/diagnose-as5.C: New test.
* g++.dg/diagnostic/diagnose-as6.C: New test.
---
gcc/c-family/c.opt | 4 +
gcc/cp/cp-tree.h | 6 +-
gcc/cp/decl2.c | 46 ++++
gcc/cp/error.c | 245 +++++++++++++++++-
gcc/cp/name-lookup.c | 52 +++-
gcc/cp/name-lookup.h | 2 +-
gcc/cp/parser.c | 36 +--
gcc/cp/tree.c | 151 +++++++++++
gcc/doc/extend.texi | 45 ++++
gcc/doc/invoke.texi | 9 +-
.../g++.dg/diagnostic/diagnose-as1.C | 213 +++++++++++++++
.../g++.dg/diagnostic/diagnose-as2.C | 144 ++++++++++
.../g++.dg/diagnostic/diagnose-as3.C | 152 +++++++++++
.../g++.dg/diagnostic/diagnose-as4.C | 158 +++++++++++
.../g++.dg/diagnostic/diagnose-as5.C | 21 ++
.../g++.dg/diagnostic/diagnose-as6.C | 45 ++++
16 files changed, 1295 insertions(+), 34 deletions(-)
create mode 100644 gcc/testsuite/g++.dg/diagnostic/diagnose-as1.C
create mode 100644 gcc/testsuite/g++.dg/diagnostic/diagnose-as2.C
create mode 100644 gcc/testsuite/g++.dg/diagnostic/diagnose-as3.C
create mode 100644 gcc/testsuite/g++.dg/diagnostic/diagnose-as4.C
create mode 100644 gcc/testsuite/g++.dg/diagnostic/diagnose-as5.C
create mode 100644 gcc/testsuite/g++.dg/diagnostic/diagnose-as6.C
--
──────────────────────────────────────────────────────────────────────────
Dr. Matthias Kretz https://mattkretz.github.io
GSI Helmholtz Centre for Heavy Ion Research https://gsi.de
std::experimental::simd https://github.com/VcDevel/std-simd
──────────────────────────────────────────────────────────────────────────
[-- Attachment #2: 0001-c-Add-gnu-diagnose_as-attribute.patch --]
[-- Type: text/x-patch, Size: 63922 bytes --]
diff --git a/gcc/c-family/c.opt b/gcc/c-family/c.opt
index 91929706aff..354590982bd 100644
--- a/gcc/c-family/c.opt
+++ b/gcc/c-family/c.opt
@@ -1606,6 +1606,10 @@ fdiagnostics-show-template-tree
C++ ObjC++ Var(flag_diagnostics_show_template_tree) Init(0)
Print hierarchical comparisons when template types are mismatched.
+fdiagnostics-use-aliases
+C++ Var(flag_diagnostics_use_aliases) Init(1)
+Replace identifiers or scope names in diagnostics as defined by the diagnose_as attribute.
+
fdirectives-only
C ObjC C++ ObjC++
Preprocess directives only.
diff --git a/gcc/cp/cp-tree.h b/gcc/cp/cp-tree.h
index 6f713719589..427e56e5852 100644
--- a/gcc/cp/cp-tree.h
+++ b/gcc/cp/cp-tree.h
@@ -5892,7 +5892,9 @@ enum auto_deduction_context
identical to their defaults.
TFF_NO_TEMPLATE_BINDINGS: do not print information about the template
arguments for a function template specialization.
- TFF_POINTER: we are printing a pointer type. */
+ TFF_POINTER: we are printing a pointer type.
+ TFF_AS_PRIMARY: treat as primary template even if printing a specialized
+ type. */
#define TFF_PLAIN_IDENTIFIER (0)
#define TFF_SCOPE (1)
@@ -5910,6 +5912,7 @@ enum auto_deduction_context
#define TFF_NO_OMIT_DEFAULT_TEMPLATE_ARGUMENTS (1 << 12)
#define TFF_NO_TEMPLATE_BINDINGS (1 << 13)
#define TFF_POINTER (1 << 14)
+#define TFF_AS_PRIMARY (1 << 15)
/* These constants can be used as bit flags to control strip_typedefs.
@@ -6768,6 +6771,7 @@ extern tree grokfield (const cp_declarator *, cp_decl_specifier_seq *,
tree, bool, tree, tree);
extern tree grokbitfield (const cp_declarator *, cp_decl_specifier_seq *,
tree, tree, tree);
+extern bool is_alias_template_p (tree);
extern tree splice_template_attributes (tree *, tree);
extern bool any_dependent_type_attributes_p (tree);
extern tree cp_reconstruct_complex_type (tree, tree);
diff --git a/gcc/cp/decl2.c b/gcc/cp/decl2.c
index 090a83bd670..7cec8f114c1 100644
--- a/gcc/cp/decl2.c
+++ b/gcc/cp/decl2.c
@@ -1139,6 +1139,48 @@ grokbitfield (const cp_declarator *declarator,
return value;
}
+/* Return true iff DECL is an alias template of a class template which strictly
+ renames the type. */
+bool
+is_alias_template_p (tree decl)
+{
+ if (TREE_CODE (decl) != TYPE_DECL)
+ return false;
+
+ tree type = strip_typedefs (TREE_TYPE (decl), nullptr, 0);
+ if (!CLASS_TYPE_P (type) || !CLASSTYPE_TEMPLATE_INFO (type))
+ return false;
+
+ /* Ensure it's a real alias template not just
+ template <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;
+}
+
\f
/* Returns true iff ATTR is an attribute which needs to be applied at
instantiation time rather than template definition time. */
@@ -1166,6 +1208,10 @@ is_late_template_attribute (tree attr, tree decl)
|| is_attribute_p ("used", name)))
return false;
+ /* Allow alias templates to set diagnose_as on the RHS template. */
+ if (is_attribute_p ("diagnose_as", name))
+ return !is_alias_template_p (decl);
+
/* Attribute tls_model wants to modify the symtab. */
if (is_attribute_p ("tls_model", name))
return true;
diff --git a/gcc/cp/error.c b/gcc/cp/error.c
index 4a89b348829..a18d758fa43 100644
--- a/gcc/cp/error.c
+++ b/gcc/cp/error.c
@@ -35,6 +35,7 @@ along with GCC; see the file COPYING3. If not see
#include "internal-fn.h"
#include "gcc-rich-location.h"
#include "cp-name-hint.h"
+#include "attribs.h"
#define pp_separate_with_comma(PP) pp_cxx_separate_with (PP, ',')
#define pp_separate_with_semicolon(PP) pp_cxx_separate_with (PP, ';')
@@ -66,6 +67,7 @@ static void dump_alias_template_specialization (cxx_pretty_printer *, tree, int)
static void dump_type (cxx_pretty_printer *, tree, int);
static void dump_typename (cxx_pretty_printer *, tree, int);
static void dump_simple_decl (cxx_pretty_printer *, tree, tree, int);
+static void dump_decl_name_or_diagnose_as (cxx_pretty_printer *, tree, int);
static void dump_decl (cxx_pretty_printer *, tree, int);
static void dump_template_decl (cxx_pretty_printer *, tree, int);
static void dump_function_decl (cxx_pretty_printer *, tree, int);
@@ -231,7 +233,15 @@ dump_scope (cxx_pretty_printer *pp, tree scope, int flags)
{
if (scope != global_namespace)
{
- dump_decl (pp, scope, f);
+ tree diagnose_as
+ = flag_diagnostics_use_aliases
+ ? lookup_attribute ("diagnose_as", DECL_ATTRIBUTES (scope))
+ : NULL_TREE;
+ if (diagnose_as)
+ pp_cxx_ws_string (
+ pp, TREE_STRING_POINTER (TREE_VALUE (TREE_VALUE (diagnose_as))));
+ else
+ dump_decl (pp, scope, f);
pp_cxx_colon_colon (pp);
}
}
@@ -764,6 +774,45 @@ class_key_or_enum_as_string (tree t)
return "struct";
}
+/* Print out an enclosing scope of a template instance by inspecting the tree
+ SPECIAL of the template instantiation and the tree GENERAL of the most
+ general template in parallel under the control of FLAGS while adjusting
+ PARMS as needed. This allows the diagnose_as attribute to override the name
+ of full specializations, while hiding its template parameters, and to
+ override the name of partial specializations without falling back to printing
+ the template parameters of the general template decl. */
+
+static void
+dump_template_scope (cxx_pretty_printer *pp, tree special, tree general,
+ tree *parms, int flags)
+{
+ gcc_assert (parms);
+ if (special != general && RECORD_OR_UNION_TYPE_P (special) && *parms)
+ {
+ gcc_assert (RECORD_OR_UNION_TYPE_P (general));
+ const bool tmplate
+ = TYPE_LANG_SPECIFIC (special) && CLASSTYPE_TEMPLATE_INFO (special)
+ && (TREE_CODE (CLASSTYPE_TI_TEMPLATE (special)) != TEMPLATE_DECL
+ || PRIMARY_TEMPLATE_P (CLASSTYPE_TI_TEMPLATE (special)));
+ dump_template_scope (pp, CP_TYPE_CONTEXT (special),
+ CP_TYPE_CONTEXT (general),
+ tmplate ? &TREE_CHAIN(*parms) : parms, flags);
+ tree attr = lookup_attribute ("diagnose_as", TYPE_ATTRIBUTES (special));
+ if (attr)
+ {
+ pp_cxx_ws_string (pp, TREE_STRING_POINTER (
+ TREE_VALUE (TREE_VALUE (attr))));
+ if (tmplate)
+ TREE_VALUE (*parms) = make_tree_vec (0);
+ }
+ else
+ dump_aggr_type (pp, general, flags | TFF_UNQUALIFIED_NAME);
+ pp_cxx_colon_colon (pp);
+ }
+ else
+ dump_scope (pp, general, flags);
+}
+
/* Print out a class declaration T under the control of FLAGS,
in the form `class foo'. */
@@ -781,6 +830,7 @@ dump_aggr_type (cxx_pretty_printer *pp, tree t, int flags)
tree decl = TYPE_NAME (t);
+ tree diagnose_as = NULL_TREE;
if (decl)
{
typdef = (!DECL_ARTIFICIAL (decl)
@@ -804,11 +854,14 @@ dump_aggr_type (cxx_pretty_printer *pp, tree t, int flags)
&& (TREE_CODE (CLASSTYPE_TI_TEMPLATE (t)) != TEMPLATE_DECL
|| PRIMARY_TEMPLATE_P (CLASSTYPE_TI_TEMPLATE (t)));
- if (! (flags & TFF_UNQUALIFIED_NAME))
- dump_scope (pp, CP_DECL_CONTEXT (decl), flags | TFF_SCOPE);
- flags &= ~TFF_UNQUALIFIED_NAME;
+ tree context = CP_DECL_CONTEXT (decl);
+ tree diagnose_as_specialized = NULL_TREE;
if (tmplate)
{
+ if (flag_diagnostics_use_aliases)
+ diagnose_as_specialized
+ = lookup_attribute ("diagnose_as", TYPE_ATTRIBUTES (t));
+
/* Because the template names are mangled, we have to locate
the most general template, and use that name. */
tree tpl = TYPE_TI_TEMPLATE (t);
@@ -817,9 +870,61 @@ dump_aggr_type (cxx_pretty_printer *pp, tree t, int flags)
tpl = DECL_TI_TEMPLATE (tpl);
decl = tpl;
}
+
+ if (flag_diagnostics_use_aliases)
+ {
+ diagnose_as = lookup_attribute ("diagnose_as",
+ TYPE_ATTRIBUTES (TREE_TYPE (decl)));
+ if (diagnose_as_specialized
+ && (!diagnose_as || TREE_VALUE (diagnose_as_specialized)
+ != TREE_VALUE (diagnose_as)))
+ {
+ diagnose_as = diagnose_as_specialized;
+ /* Skip dump_template_parms if diagnose_as applies to a full
+ specialization. */
+ tree args = CLASSTYPE_TI_ARGS (t);
+ args = INNERMOST_TEMPLATE_ARGS (args);
+ gcc_assert (args);
+ tmplate = false;
+ for (int i = 0; i < NUM_TMPL_ARGS (args); ++i)
+ {
+ tree arg = TREE_VEC_ELT (args, i);
+ while (INDIRECT_TYPE_P (arg))
+ arg = TREE_TYPE (arg);
+ if (WILDCARD_TYPE_P (arg))
+ {
+ tmplate = true;
+ break;
+ }
+ }
+ }
+ /* Given
+
+ template <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);
diff --git a/gcc/cp/name-lookup.c b/gcc/cp/name-lookup.c
index 1be5f3da6d5..5e8129df429 100644
--- a/gcc/cp/name-lookup.c
+++ b/gcc/cp/name-lookup.c
@@ -6060,6 +6060,53 @@ handle_namespace_attrs (tree ns, tree attributes)
DECL_ATTRIBUTES (ns) = tree_cons (name, args,
DECL_ATTRIBUTES (ns));
}
+ else if (is_attribute_p ("diagnose_as", name))
+ {
+ if (DECL_NAMESPACE_ALIAS (ns))
+ { // apply attribute to original namespace
+ if (!args)
+ { // turn alias identifier into attribute argument
+ tree ns_name = DECL_NAME (ns);
+ tree str = fix_string_type (
+ build_string(IDENTIFIER_LENGTH (ns_name) + 1,
+ IDENTIFIER_POINTER (ns_name)));
+ args = build_tree_list (NULL_TREE, str);
+ }
+ else if (TREE_CHAIN (args)
+ || TREE_CODE (TREE_VALUE (args)) != STRING_CST)
+ {
+ warning (OPT_Wattributes,
+ "%qD attribute requires 0 or 1 NTBS arguments",
+ name);
+ continue;
+ }
+ tree attributes = tree_cons (name, args, NULL_TREE);
+ handle_namespace_attrs (ORIGINAL_NAMESPACE (ns), attributes);
+ continue;
+ }
+ if (!args || TREE_CHAIN (args)
+ || TREE_CODE (TREE_VALUE (args)) != STRING_CST)
+ {
+ warning (OPT_Wattributes,
+ "%qD attribute requires a single NTBS argument",
+ name);
+ continue;
+ }
+ tree existing
+ = lookup_attribute ("diagnose_as", DECL_ATTRIBUTES (ns));
+ if (existing
+ && !cp_tree_equal (TREE_VALUE (args),
+ TREE_VALUE (TREE_VALUE (existing))))
+ {
+ auto_diagnostic_group d;
+ warning (OPT_Wattributes, "%qD redeclared with different %qD "
+ "attribute value", ns, name);
+ inform (DECL_SOURCE_LOCATION (ns), "previous declaration here");
+ continue;
+ }
+ DECL_ATTRIBUTES (ns) = tree_cons (name, args,
+ DECL_ATTRIBUTES (ns));
+ }
else
{
warning (OPT_Wattributes, "%qD attribute directive ignored",
@@ -6092,7 +6139,7 @@ pop_decl_namespace (void)
/* Process a namespace-alias declaration. */
void
-do_namespace_alias (tree alias, tree name_space)
+do_namespace_alias (tree alias, tree name_space, tree attributes)
{
if (name_space == error_mark_node)
return;
@@ -6108,6 +6155,9 @@ do_namespace_alias (tree alias, tree name_space)
DECL_CONTEXT (alias) = FROB_CONTEXT (current_scope ());
set_originating_module (alias);
+ /* Apply attributes. */
+ handle_namespace_attrs (alias, attributes);
+
pushdecl (alias);
/* Emit debug info for namespace alias. */
diff --git a/gcc/cp/name-lookup.h b/gcc/cp/name-lookup.h
index f63c4f5b8bb..cdbb6834220 100644
--- a/gcc/cp/name-lookup.h
+++ b/gcc/cp/name-lookup.h
@@ -435,7 +435,7 @@ extern tree cp_namespace_decls (tree);
extern void set_decl_namespace (tree, tree, bool);
extern void push_decl_namespace (tree);
extern void pop_decl_namespace (void);
-extern void do_namespace_alias (tree, tree);
+extern void do_namespace_alias (tree, tree, tree);
extern tree do_class_using_decl (tree, tree);
extern tree lookup_arg_dependent (tree, tree, vec<tree, va_gc> *);
extern tree search_anon_aggr (tree, tree, bool = false);
diff --git a/gcc/cp/parser.c b/gcc/cp/parser.c
index 096580e7e50..44bd88adb62 100644
--- a/gcc/cp/parser.c
+++ b/gcc/cp/parser.c
@@ -14252,20 +14252,15 @@ cp_parser_declaration (cp_parser* parser, tree prefix_attrs)
|| token1->keyword == RID_STATIC
|| token1->keyword == RID_INLINE))
cp_parser_explicit_instantiation (parser);
- /* If the next token is `namespace', check for a named or unnamed
- namespace definition. */
- else if (token1->keyword == RID_NAMESPACE
- && (/* A named namespace definition. */
- (token2->type == CPP_NAME
- && (cp_lexer_peek_nth_token (parser->lexer, 3)->type
- != CPP_EQ))
- || (token2->type == CPP_OPEN_SQUARE
- && cp_lexer_peek_nth_token (parser->lexer, 3)->type
- == CPP_OPEN_SQUARE)
- /* An unnamed namespace definition. */
- || token2->type == CPP_OPEN_BRACE
- || token2->keyword == RID_ATTRIBUTE))
- cp_parser_namespace_definition (parser);
+ /* If the next token is `namespace', we have either a namespace alias
+ definition or a namespace definition. */
+ else if (token1->keyword == RID_NAMESPACE)
+ {
+ cp_parser_parse_tentatively (parser);
+ cp_parser_namespace_alias_definition (parser);
+ if (!cp_parser_parse_definitely (parser))
+ cp_parser_namespace_definition (parser);
+ }
/* An inline (associated) namespace definition. */
else if (token2->keyword == RID_NAMESPACE
&& token1->keyword == RID_INLINE)
@@ -20655,10 +20650,18 @@ cp_parser_namespace_alias_definition (cp_parser* parser)
/* Look for the `namespace' keyword. */
cp_parser_require_keyword (parser, RID_NAMESPACE, RT_NAMESPACE);
+ /* Look for attributes (GCC extension). */
+ tree attributes = cp_parser_attributes_opt (parser);
+ if (attributes)
+ pedwarn (input_location, OPT_Wpedantic,
+ "standard attributes on namespaces aliases must follow "
+ "the namespace alias name");
/* Look for the identifier. */
identifier = cp_parser_identifier (parser);
if (identifier == error_mark_node)
return;
+ /* Look for more attributes (GCC extension). */
+ attributes = attr_chainon (attributes, cp_parser_attributes_opt (parser));
/* Look for the `=' token. */
if (!cp_parser_uncommitted_to_tentative_parse_p (parser)
&& cp_lexer_next_token_is (parser->lexer, CPP_OPEN_BRACE))
@@ -20670,7 +20673,8 @@ cp_parser_namespace_alias_definition (cp_parser* parser)
cp_lexer_consume_token (parser->lexer);
return;
}
- cp_parser_require (parser, CPP_EQ, RT_EQ);
+ if (nullptr == cp_parser_require (parser, CPP_EQ, RT_EQ))
+ return;
/* Look for the qualified-namespace-specifier. */
namespace_specifier
= cp_parser_qualified_namespace_specifier (parser);
@@ -20679,7 +20683,7 @@ cp_parser_namespace_alias_definition (cp_parser* parser)
cp_parser_require (parser, CPP_SEMICOLON, RT_SEMICOLON);
/* Register the alias in the symbol table. */
- do_namespace_alias (identifier, namespace_specifier);
+ do_namespace_alias (identifier, namespace_specifier, attributes);
}
/* Parse a qualified-namespace-specifier.
diff --git a/gcc/cp/tree.c b/gcc/cp/tree.c
index 2a14fa92ddb..68317041a0f 100644
--- a/gcc/cp/tree.c
+++ b/gcc/cp/tree.c
@@ -46,6 +46,7 @@ static tree verify_stmt_tree_r (tree *, int *, void *);
static tree handle_init_priority_attribute (tree *, tree, tree, int, bool *);
static tree handle_abi_tag_attribute (tree *, tree, tree, int, bool *);
+static tree handle_diagnose_as_attribute (tree *, tree, tree, int, bool *);
/* If REF is an lvalue, returns the kind of lvalue that REF is.
Otherwise, returns clk_none. */
@@ -4877,6 +4878,8 @@ const struct attribute_spec cxx_attribute_table[] =
handle_init_priority_attribute, NULL },
{ "abi_tag", 1, -1, false, false, false, true,
handle_abi_tag_attribute, NULL },
+ { "diagnose_as", 0, -1, false, false, false, false,
+ handle_diagnose_as_attribute, NULL },
{ NULL, 0, 0, false, false, false, false, NULL, NULL }
};
@@ -5145,6 +5148,154 @@ handle_abi_tag_attribute (tree* node, tree name, tree args,
return NULL_TREE;
}
+static bool
+check_diagnose_as_redeclaration (const_tree decl, const_tree name,
+ const_tree old, const_tree new_)
+{
+ if (!old)
+ return true;
+ if (TREE_CODE (TREE_VALUE (old)) == TREE_LIST)
+ old = TREE_VALUE (old);
+ if (TREE_CODE (TREE_VALUE (new_)) == TREE_LIST)
+ new_ = TREE_VALUE (new_);
+ tree old_value = TREE_VALUE (old);
+ tree new_value = TREE_VALUE (new_);
+ if (cp_tree_equal (old_value, new_value))
+ return true;
+ warning (OPT_Wattributes, "%qD redeclared with %<%D(%E)%> "
+ "attribute", decl, name, new_value);
+ inform (DECL_SOURCE_LOCATION (decl), "previous declaration here");
+ return false;
+}
+
+static tree
+handle_diagnose_as_attribute (tree* node, tree name, tree args,
+ int flags, bool* no_add_attrs)
+{
+ tree decl = NULL_TREE;
+ const bool is_alias = (TREE_CODE (*node) == TYPE_DECL);
+ if (args && list_length (args) == 2
+ && TREE_PURPOSE (TREE_CHAIN (args)) == integer_zero_node)
+ /* We're called from handle_diagnose_as_attribute with additional context
+ argument. */;
+ else if (is_alias)
+ {
+ if (args && (TREE_CHAIN (args)
+ || TREE_CODE (TREE_VALUE (args)) != STRING_CST))
+ {
+ warning (OPT_Wattributes,
+ "%qD attribute requires 0 or 1 NTBS arguments", name);
+ goto fail;
+ }
+ }
+ else if (!args || TREE_CHAIN (args)
+ || TREE_CODE (TREE_VALUE (args)) != STRING_CST)
+ {
+ warning (OPT_Wattributes,
+ "%qD attribute requires a single NTBS argument", name);
+ goto fail;
+ }
+
+ if (is_alias && CLASS_TYPE_P (TREE_TYPE (*node)))
+ { // Apply the attribute to the type alias itself.
+ decl = *node;
+
+ /* Reject alias templates without wildcards on the innermost template args
+ of the RHS type. E.g. template <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. */
diff --git a/gcc/doc/extend.texi b/gcc/doc/extend.texi
index 8fc66d626d8..4614800f5eb 100644
--- a/gcc/doc/extend.texi
+++ b/gcc/doc/extend.texi
@@ -2865,6 +2865,51 @@ types (@pxref{Variable Attributes}, @pxref{Type Attributes}.)
The message attached to the attribute is affected by the setting of
the @option{-fmessage-length} option.
+@item diagnose_as ("@var{string}")
+@itemx diagnose_as
+@cindex @code{diagnose_as} function attribute
+The @code{diagnose_as} attribute modifies how the entity the attribute
+appertains to is diagnosed in compiler messages and @code{__func__},
+@code{__FUNCTION__}, and @code{__PRETTY_FUNCTION__} macros. (The result of
+@code{typeid} is not affected.) If the attribute is applied to a
+@code{namespace}, the specified string replaces the complete enclosing scope.
+The effect of the @code{diagnose_as} attribute can be disabled with the
+@option{-fno-diagnostics-use-aliases} command line option.
+
+The argument @var{string} is optional for type and namespace aliases. If it is
+omitted, the identifier of the type or namespace alias is unconditionally used
+in all places where the type / namespace on the right-hand side is diagnosed.
+
+@smallexample
+namespace Foo @{
+ namespace Bar @{
+ inline namespace v1 [[gnu::diagnose_as("Foobar")]] @{
+ int f() @{
+ // __PRETTY_FUNCTION__ == "void Foobar::f()"
+ @}
+ @}
+ @}
+@}
+// In function 'int Foobar::f()':
+// warning: no return statement in function returning non-void [-Wreturn-type]
+@end smallexample
+
+The @code{diagnose_as} attribute can be used with namespaces, namespace aliases,
+functions, variables, alias declarations (but not alias templates), and
+user-defined types (classes, unions, and enums). If the alias declaration
+aliases a class type (including template specializations), the attribute is
+additionally applied to the class type. Whether the attribute has an effect on
+partial template specializations is unspecified and may depend on several
+different factors.
+
+@smallexample
+template <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
diff --git a/gcc/doc/invoke.texi b/gcc/doc/invoke.texi
index af2ce189fae..ed83462fa7a 100644
--- a/gcc/doc/invoke.texi
+++ b/gcc/doc/invoke.texi
@@ -311,7 +311,8 @@ Objective-C and Objective-C++ Dialects}.
-fdiagnostics-show-path-depths @gol
-fno-show-column @gol
-fdiagnostics-column-unit=@r{[}display@r{|}byte@r{]} @gol
--fdiagnostics-column-origin=@var{origin}}
+-fdiagnostics-column-origin=@var{origin} @gol
+-fno-diagnostics-aliases}
@item Warning Options
@xref{Warning Options,,Options to Request or Suppress Warnings}.
@@ -5078,6 +5079,12 @@ first column. The default value of 1 corresponds to traditional GCC
behavior and to the GNU style guide. Some utilities may perform better with an
origin of 0; any non-negative value may be specified.
+@item -fno-diagnostics-use-aliases
+@opindex fno-diagnostics-use-aliases
+@opindex fdiagnostics-use-aliases
+Do not replace identifiers or scope names with the aliases defined with the
+@code{[[gnu::diagnose_as("alias")]]} attribute.
+
@item -fdiagnostics-format=@var{FORMAT}
@opindex fdiagnostics-format
Select a different format for printing diagnostics.
diff --git a/gcc/testsuite/g++.dg/diagnostic/diagnose-as1.C b/gcc/testsuite/g++.dg/diagnostic/diagnose-as1.C
new file mode 100644
index 00000000000..e0a2cae32e5
--- /dev/null
+++ b/gcc/testsuite/g++.dg/diagnostic/diagnose-as1.C
@@ -0,0 +1,213 @@
+// { dg-options "-fdiagnostics-use-aliases -fpretty-templates" }
+
+#ifdef __cpp_constexpr
+template <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();
+}
diff --git a/gcc/testsuite/g++.dg/diagnostic/diagnose-as2.C b/gcc/testsuite/g++.dg/diagnostic/diagnose-as2.C
new file mode 100644
index 00000000000..b1d46d12024
--- /dev/null
+++ b/gcc/testsuite/g++.dg/diagnostic/diagnose-as2.C
@@ -0,0 +1,144 @@
+// { dg-options "-fdiagnostics-use-aliases -fno-pretty-templates" }
+
+#ifdef __cpp_constexpr
+template <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();
+}
diff --git a/gcc/testsuite/g++.dg/diagnostic/diagnose-as3.C b/gcc/testsuite/g++.dg/diagnostic/diagnose-as3.C
new file mode 100644
index 00000000000..beedb9624a8
--- /dev/null
+++ b/gcc/testsuite/g++.dg/diagnostic/diagnose-as3.C
@@ -0,0 +1,152 @@
+// { dg-options "-fdiagnostics-use-aliases -fpretty-templates" }
+// { dg-do compile { target c++11 } }
+
+const char* volatile s;
+
+namespace foo
+{
+ template <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>()));
+}
diff --git a/gcc/testsuite/g++.dg/diagnostic/diagnose-as4.C b/gcc/testsuite/g++.dg/diagnostic/diagnose-as4.C
new file mode 100644
index 00000000000..89b800c7b9d
--- /dev/null
+++ b/gcc/testsuite/g++.dg/diagnostic/diagnose-as4.C
@@ -0,0 +1,158 @@
+// { dg-options "-fdiagnostics-use-aliases -fno-pretty-templates" }
+// { dg-do compile { target c++11 } }
+
+const char* volatile s;
+
+namespace foo
+{
+ template <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 }
+}
diff --git a/gcc/testsuite/g++.dg/diagnostic/diagnose-as5.C b/gcc/testsuite/g++.dg/diagnostic/diagnose-as5.C
new file mode 100644
index 00000000000..2a46d64964f
--- /dev/null
+++ b/gcc/testsuite/g++.dg/diagnostic/diagnose-as5.C
@@ -0,0 +1,21 @@
+// { dg-options "-fdiagnostics-use-aliases -fpretty-templates" }
+// { dg-do compile { target c++11 } }
+
+namespace std
+{
+ template <class 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'" }
+}
diff --git a/gcc/testsuite/g++.dg/diagnostic/diagnose-as6.C b/gcc/testsuite/g++.dg/diagnostic/diagnose-as6.C
new file mode 100644
index 00000000000..94747a7aa6e
--- /dev/null
+++ b/gcc/testsuite/g++.dg/diagnostic/diagnose-as6.C
@@ -0,0 +1,45 @@
+// { dg-options "-fdiagnostics-use-aliases -fpretty-templates" }
+// { dg-do compile { target c++11 } }
+
+template <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'" }
+}
^ permalink raw reply [flat|nested] 24+ messages in thread
* [PATCH v4] c++: Add gnu::diagnose_as attribute
2021-07-15 12:21 [PATCH v3] c++: Add gnu::diagnose_as attribute Matthias Kretz
@ 2021-07-23 8:58 ` Matthias Kretz
2021-08-17 18:31 ` Jason Merrill
2021-09-08 2:21 ` [PATCH v4] c++: Add gnu::diagnose_as attribute Jason Merrill
0 siblings, 2 replies; 24+ messages in thread
From: Matthias Kretz @ 2021-07-23 8:58 UTC (permalink / raw)
To: gcc-patches, Jason Merrill
[-- Attachment #1: Type: text/plain, Size: 9009 bytes --]
Hi Jason,
I found a few regressions from the last patch in the meantime. Version 4 of
the patch is attached.
Questions:
1. I simplified the condition for calling dump_template_parms in
dump_function_name. !DECL_FRIEND_PSEUDO_TEMPLATE_INSTANTIATION (t) is
equivalent to DECL_USE_TEMPLATE (t) in this context; implying that
dump_template_parms is unconditionally called with `primary = false`. Or am I
missing something?
2. Given a DECL_TI_ARGS tree, can I query whether an argument was deduced or
explicitly specified? I'm asking because I still consider diagnostics of
function templates unfortunate. `template <class T> void f()` is fine, as is
`void f(T) [with T = float]`, but `void f() [with T = float]` could be better.
I.e. if the template parameter appears somewhere in the function parameter
list, dump_template_parms would only produce noise. If, however, the template
parameter was given explicitly, it would be nice if it could show up
accordingly in diagnostics.
3. When parsing tentatively and the parse is rejected, input_location is not
reset, correct? In the attached patch I therefore made
cp_parser_namespace_alias_definition reset input_location on a failed
tentative parse. But it feels wrong. Shouldn't input_location be restored on
cp_parser_parse_definitely?
--
This attribute overrides the diagnostics output string for the entity it
appertains to. The motivation is to improve QoI for library TS
implementations, where diagnostics have a very bad signal-to-noise ratio
due to the long namespaces involved.
With the attribute, it is possible to solve PR89370 and make
std::__cxx11::basic_string<_CharT, _Traits, _Alloc> appear as
std::string in diagnostic output without extra hacks to recognize the
type in the C++ frontend.
Signed-off-by: Matthias Kretz <m.kretz@gsi.de>
gcc/ChangeLog:
PR c++/89370
* doc/extend.texi: Document the diagnose_as attribute.
* doc/invoke.texi: Document -fno-diagnostics-use-aliases.
gcc/c-family/ChangeLog:
PR c++/89370
* c.opt (fdiagnostics-use-aliases): New diagnostics flag.
gcc/cp/ChangeLog:
PR c++/89370
* cp-tree.h: Add is_alias_template_p declaration.
* decl2.c (is_alias_template_p): New function. Determines
whether a given TYPE_DECL is actually an alias template that is
still missing its template_info.
(is_late_template_attribute): Decls with diagnose_as attribute
are early attributes only if they are alias templates.
* error.c (dump_scope): When printing the name of a namespace,
look for the diagnose_as attribute. If found, print the
associated string instead of calling dump_decl.
(dump_decl_name_or_diagnose_as): New function to replace
dump_decl (pp, DECL_NAME(t), flags) and inspect the tree for the
diagnose_as attribute before printing the DECL_NAME.
(dump_template_scope): New function. Prints the scope of a
template instance correctly applying diagnose_as attributes and
adjusting the list of template parms accordingly.
(dump_aggr_type): If the type has a diagnose_as attribute, print
the associated string instead of printing the original type
name. Print template parms only if the attribute was not applied
to the instantiation / full specialization. Delay call to
dump_scope until the diagnose_as attribute is found. If the
attribute has a second argument, use it to override the context
passed to dump_scope.
(dump_simple_decl): Call dump_decl_name_or_diagnose_as instead
of dump_decl.
(dump_decl): Ditto.
(lang_decl_name): Ditto.
(dump_function_decl): Walk the functions context list to
determine whether a call to dump_template_scope is required.
Ensure function templates diagnosed with pretty templates set
TFF_TEMPLATE_NAME to skip dump_template_parms.
(dump_function_name): Replace the function's identifier with the
diagnose_as attribute value, if set. Expand
DECL_FRIEND_PSEUDO_TEMPLATE_INSTANTIATION to DECL_USE_TEMPLATE
and consequently call dump_template_parms with primary = false.
(comparable_template_types_p): Consider the types not a template
if one carries a diagnose_as attribute.
(print_template_differences): Replace the identifier with the
diagnose_as attribute value on the most general template, if it
is set.
* name-lookup.c (handle_namespace_attrs): Handle the diagnose_as
attribute on namespaces. Ensure exactly one string argument.
Ensure previous diagnose_as attributes used the same name.
'diagnose_as' on namespace aliases are forwarded to the original
namespace. Support no-argument 'diagnose_as' on namespace
aliases.
(do_namespace_alias): Add attributes parameter and call
handle_namespace_attrs.
* name-lookup.h (do_namespace_alias): Add attributes tree
parameter.
* parser.c (cp_parser_declaration): If the next token is
RID_NAMESPACE, tentatively parse a namespace alias definition.
If this fails expect a namespace definition.
(cp_parser_namespace_alias_definition): Allow optional
attributes before and after the identifier. Fast exit, restoring
input_location, if the expected CPP_EQ token is missing. Pass
attributes to do_namespace_alias.
* tree.c (cxx_attribute_table): Add diagnose_as attribute to the
table.
(check_diagnose_as_redeclaration): New function; copied and
adjusted from check_abi_tag_redeclaration.
(handle_diagnose_as_attribute): New function; copied and
adjusted from handle_abi_tag_attribute. If the given *node is a
TYPE_DECL: allow no argument to the attribute, using DECL_NAME
instead; apply the attribute to the type on the RHS in place,
even if the type is complete. Allow 2 arguments when called from
handle_diagnose_as_attribute. For type aliases, append
CP_DECL_CONTEXT as second attribute argument when the RHS type
has a different context. Warn about alias templates without
matching template arguments. Apply the attribute to the primary
template type for alias templates.
gcc/testsuite/ChangeLog:
PR c++/89370
* g++.dg/diagnostic/diagnose-as1.C: New test.
* g++.dg/diagnostic/diagnose-as2.C: New test.
* g++.dg/diagnostic/diagnose-as3.C: New test.
* g++.dg/diagnostic/diagnose-as4.C: New test.
* g++.dg/diagnostic/diagnose-as5.C: New test.
* g++.dg/diagnostic/diagnose-as6.C: New test.
---
gcc/c-family/c.opt | 4 +
gcc/cp/cp-tree.h | 1 +
gcc/cp/decl2.c | 45 ++++
gcc/cp/error.c | 247 ++++++++++++++++--
gcc/cp/name-lookup.c | 52 +++-
gcc/cp/name-lookup.h | 2 +-
gcc/cp/parser.c | 41 +--
gcc/cp/tree.c | 148 +++++++++++
gcc/doc/extend.texi | 45 ++++
gcc/doc/invoke.texi | 9 +-
.../g++.dg/diagnostic/diagnose-as1.C | 213 +++++++++++++++
.../g++.dg/diagnostic/diagnose-as2.C | 144 ++++++++++
.../g++.dg/diagnostic/diagnose-as3.C | 152 +++++++++++
.../g++.dg/diagnostic/diagnose-as4.C | 158 +++++++++++
.../g++.dg/diagnostic/diagnose-as5.C | 21 ++
.../g++.dg/diagnostic/diagnose-as6.C | 45 ++++
16 files changed, 1291 insertions(+), 36 deletions(-)
create mode 100644 gcc/testsuite/g++.dg/diagnostic/diagnose-as1.C
create mode 100644 gcc/testsuite/g++.dg/diagnostic/diagnose-as2.C
create mode 100644 gcc/testsuite/g++.dg/diagnostic/diagnose-as3.C
create mode 100644 gcc/testsuite/g++.dg/diagnostic/diagnose-as4.C
create mode 100644 gcc/testsuite/g++.dg/diagnostic/diagnose-as5.C
create mode 100644 gcc/testsuite/g++.dg/diagnostic/diagnose-as6.C
--
──────────────────────────────────────────────────────────────────────────
Dr. Matthias Kretz https://mattkretz.github.io
GSI Helmholtz Centre for Heavy Ion Research https://gsi.de
std::experimental::simd https://github.com/VcDevel/std-simd
──────────────────────────────────────────────────────────────────────────
[-- Attachment #2: 0001-c-Add-gnu-diagnose_as-attribute.patch --]
[-- Type: text/x-patch, Size: 63428 bytes --]
diff --git a/gcc/c-family/c.opt b/gcc/c-family/c.opt
index 91929706aff..354590982bd 100644
--- a/gcc/c-family/c.opt
+++ b/gcc/c-family/c.opt
@@ -1606,6 +1606,10 @@ fdiagnostics-show-template-tree
C++ ObjC++ Var(flag_diagnostics_show_template_tree) Init(0)
Print hierarchical comparisons when template types are mismatched.
+fdiagnostics-use-aliases
+C++ Var(flag_diagnostics_use_aliases) Init(1)
+Replace identifiers or scope names in diagnostics as defined by the diagnose_as attribute.
+
fdirectives-only
C ObjC C++ ObjC++
Preprocess directives only.
diff --git a/gcc/cp/cp-tree.h b/gcc/cp/cp-tree.h
index f4bcab5b18d..792849c361c 100644
--- a/gcc/cp/cp-tree.h
+++ b/gcc/cp/cp-tree.h
@@ -6764,6 +6764,7 @@ extern tree grokfield (const cp_declarator *, cp_decl_specifier_seq *,
tree, bool, tree, tree);
extern tree grokbitfield (const cp_declarator *, cp_decl_specifier_seq *,
tree, tree, tree);
+extern bool is_alias_template_p (tree);
extern tree splice_template_attributes (tree *, tree);
extern bool any_dependent_type_attributes_p (tree);
extern tree cp_reconstruct_complex_type (tree, tree);
diff --git a/gcc/cp/decl2.c b/gcc/cp/decl2.c
index 9564b0d619f..1ff2ab27813 100644
--- a/gcc/cp/decl2.c
+++ b/gcc/cp/decl2.c
@@ -1139,6 +1139,47 @@ 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 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));
+ 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;
+}
+
\f
/* Returns true iff ATTR is an attribute which needs to be applied at
instantiation time rather than template definition time. */
@@ -1166,6 +1207,10 @@ is_late_template_attribute (tree attr, tree decl)
|| is_attribute_p ("used", name)))
return false;
+ /* Allow alias templates to set diagnose_as on the RHS template. */
+ if (is_attribute_p ("diagnose_as", name))
+ return !is_alias_template_p (decl);
+
/* Attribute tls_model wants to modify the symtab. */
if (is_attribute_p ("tls_model", name))
return true;
diff --git a/gcc/cp/error.c b/gcc/cp/error.c
index 012a4ecddf4..0452d228b51 100644
--- a/gcc/cp/error.c
+++ b/gcc/cp/error.c
@@ -35,6 +35,7 @@ along with GCC; see the file COPYING3. If not see
#include "internal-fn.h"
#include "gcc-rich-location.h"
#include "cp-name-hint.h"
+#include "attribs.h"
#define pp_separate_with_comma(PP) pp_cxx_separate_with (PP, ',')
#define pp_separate_with_semicolon(PP) pp_cxx_separate_with (PP, ';')
@@ -66,6 +67,7 @@ static void dump_alias_template_specialization (cxx_pretty_printer *, tree, int)
static void dump_type (cxx_pretty_printer *, tree, int);
static void dump_typename (cxx_pretty_printer *, tree, int);
static void dump_simple_decl (cxx_pretty_printer *, tree, tree, int);
+static void dump_decl_name_or_diagnose_as (cxx_pretty_printer *, tree, int);
static void dump_decl (cxx_pretty_printer *, tree, int);
static void dump_template_decl (cxx_pretty_printer *, tree, int);
static void dump_function_decl (cxx_pretty_printer *, tree, int);
@@ -231,7 +233,15 @@ dump_scope (cxx_pretty_printer *pp, tree scope, int flags)
{
if (scope != global_namespace)
{
- dump_decl (pp, scope, f);
+ tree diagnose_as
+ = flag_diagnostics_use_aliases
+ ? lookup_attribute ("diagnose_as", DECL_ATTRIBUTES (scope))
+ : NULL_TREE;
+ if (diagnose_as)
+ pp_cxx_ws_string (
+ pp, TREE_STRING_POINTER (TREE_VALUE (TREE_VALUE (diagnose_as))));
+ else
+ dump_decl (pp, scope, f);
pp_cxx_colon_colon (pp);
}
}
@@ -764,6 +774,45 @@ class_key_or_enum_as_string (tree t)
return "struct";
}
+/* Print out an enclosing scope of a template instance by inspecting the tree
+ SPECIAL of the template instantiation and the tree GENERAL of the most
+ general template in parallel under the control of FLAGS while adjusting
+ PARMS as needed. This allows the diagnose_as attribute to override the name
+ of full specializations, while hiding its template parameters, and to
+ override the name of partial specializations without falling back to printing
+ the template parameters of the general template decl. */
+
+static void
+dump_template_scope (cxx_pretty_printer *pp, tree special, tree general,
+ tree *parms, int flags)
+{
+ gcc_assert (parms);
+ if (special != general && RECORD_OR_UNION_TYPE_P (special) && *parms)
+ {
+ gcc_assert (RECORD_OR_UNION_TYPE_P (general));
+ const bool tmplate
+ = TYPE_LANG_SPECIFIC (special) && CLASSTYPE_TEMPLATE_INFO (special)
+ && (TREE_CODE (CLASSTYPE_TI_TEMPLATE (special)) != TEMPLATE_DECL
+ || PRIMARY_TEMPLATE_P (CLASSTYPE_TI_TEMPLATE (special)));
+ dump_template_scope (pp, CP_TYPE_CONTEXT (special),
+ CP_TYPE_CONTEXT (general),
+ tmplate ? &TREE_CHAIN(*parms) : parms, flags);
+ tree attr = lookup_attribute ("diagnose_as", TYPE_ATTRIBUTES (special));
+ if (attr)
+ {
+ pp_cxx_ws_string (pp, TREE_STRING_POINTER (
+ TREE_VALUE (TREE_VALUE (attr))));
+ if (tmplate)
+ TREE_VALUE (*parms) = make_tree_vec (0);
+ }
+ else
+ dump_aggr_type (pp, general, flags | TFF_UNQUALIFIED_NAME);
+ pp_cxx_colon_colon (pp);
+ }
+ else
+ dump_scope (pp, general, flags);
+}
+
/* Print out a class declaration T under the control of FLAGS,
in the form `class foo'. */
@@ -781,6 +830,7 @@ dump_aggr_type (cxx_pretty_printer *pp, tree t, int flags)
tree decl = TYPE_NAME (t);
+ tree diagnose_as = NULL_TREE;
if (decl)
{
typdef = (!DECL_ARTIFICIAL (decl)
@@ -804,11 +854,14 @@ dump_aggr_type (cxx_pretty_printer *pp, tree t, int flags)
&& (TREE_CODE (CLASSTYPE_TI_TEMPLATE (t)) != TEMPLATE_DECL
|| PRIMARY_TEMPLATE_P (CLASSTYPE_TI_TEMPLATE (t)));
- if (! (flags & TFF_UNQUALIFIED_NAME))
- dump_scope (pp, CP_DECL_CONTEXT (decl), flags | TFF_SCOPE);
- flags &= ~TFF_UNQUALIFIED_NAME;
+ tree context = CP_DECL_CONTEXT (decl);
+ tree diagnose_as_specialized = NULL_TREE;
if (tmplate)
{
+ if (flag_diagnostics_use_aliases)
+ diagnose_as_specialized
+ = lookup_attribute ("diagnose_as", TYPE_ATTRIBUTES (t));
+
/* Because the template names are mangled, we have to locate
the most general template, and use that name. */
tree tpl = TYPE_TI_TEMPLATE (t);
@@ -817,9 +870,61 @@ dump_aggr_type (cxx_pretty_printer *pp, tree t, int flags)
tpl = DECL_TI_TEMPLATE (tpl);
decl = tpl;
}
+
+ if (flag_diagnostics_use_aliases)
+ {
+ diagnose_as = lookup_attribute ("diagnose_as",
+ TYPE_ATTRIBUTES (TREE_TYPE (decl)));
+ if (diagnose_as_specialized
+ && (!diagnose_as || TREE_VALUE (diagnose_as_specialized)
+ != TREE_VALUE (diagnose_as)))
+ {
+ diagnose_as = diagnose_as_specialized;
+ /* Skip dump_template_parms if diagnose_as applies to a full
+ specialization. */
+ tree args = CLASSTYPE_TI_ARGS (t);
+ args = INNERMOST_TEMPLATE_ARGS (args);
+ gcc_assert (args);
+ tmplate = false;
+ for (int i = 0; i < NUM_TMPL_ARGS (args); ++i)
+ {
+ tree arg = TREE_VEC_ELT (args, i);
+ while (INDIRECT_TYPE_P (arg))
+ arg = TREE_TYPE (arg);
+ if (WILDCARD_TYPE_P (arg))
+ {
+ tmplate = true;
+ break;
+ }
+ }
+ }
+ /* Given
+
+ template <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,45 @@ 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, don't print template params. */
+ dump_function_name_flags |= TFF_TEMPLATE_NAME;
}
}
@@ -1747,6 +1914,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 +1941,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 +2121,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))
@@ -1970,11 +2164,10 @@ dump_function_name (cxx_pretty_printer *pp, tree t, int flags)
if (DECL_TEMPLATE_INFO (t)
&& !(flags & TFF_TEMPLATE_NAME)
- && !DECL_FRIEND_PSEUDO_TEMPLATE_INSTANTIATION (t)
+ && DECL_USE_TEMPLATE (t)
&& (TREE_CODE (DECL_TI_TEMPLATE (t)) != TEMPLATE_DECL
|| PRIMARY_TEMPLATE_P (DECL_TI_TEMPLATE (t))))
- dump_template_parms (pp, DECL_TEMPLATE_INFO (t), !DECL_USE_TEMPLATE (t),
- flags);
+ dump_template_parms (pp, DECL_TEMPLATE_INFO (t), false, flags);
}
/* Dump the template parameters from the template info INFO under control of
@@ -3168,7 +3361,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 +4146,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 +4252,21 @@ print_template_differences (pretty_printer *pp, tree type_a, tree type_b,
tree tinfo_a = TYPE_TEMPLATE_INFO (type_a);
tree tinfo_b = TYPE_TEMPLATE_INFO (type_b);
- pp_printf (pp, "%s<",
- IDENTIFIER_POINTER (DECL_NAME (TI_TEMPLATE (tinfo_a))));
+ const char* identifier_a
+ = IDENTIFIER_POINTER (DECL_NAME (TI_TEMPLATE (tinfo_a)));
+ if (flag_diagnostics_use_aliases)
+ {
+ /* Locate the most general template, and see whether it carries the
+ diagnose_as attribute. If it does, use it as identifier for type_a. */
+ tree tpl = TI_TEMPLATE (tinfo_a);
+ while (DECL_TEMPLATE_INFO (tpl))
+ tpl = DECL_TI_TEMPLATE (tpl);
+ tree attr = lookup_attribute ("diagnose_as",
+ TYPE_ATTRIBUTES (TREE_TYPE (tpl)));
+ if (attr)
+ identifier_a = TREE_STRING_POINTER (TREE_VALUE (TREE_VALUE (attr)));
+ }
+ pp_printf (pp, "%s<", identifier_a);
tree args_a = TI_ARGS (tinfo_a);
tree args_b = TI_ARGS (tinfo_b);
diff --git a/gcc/cp/name-lookup.c b/gcc/cp/name-lookup.c
index 1be5f3da6d5..5e8129df429 100644
--- a/gcc/cp/name-lookup.c
+++ b/gcc/cp/name-lookup.c
@@ -6060,6 +6060,53 @@ handle_namespace_attrs (tree ns, tree attributes)
DECL_ATTRIBUTES (ns) = tree_cons (name, args,
DECL_ATTRIBUTES (ns));
}
+ else if (is_attribute_p ("diagnose_as", name))
+ {
+ if (DECL_NAMESPACE_ALIAS (ns))
+ { // apply attribute to original namespace
+ if (!args)
+ { // turn alias identifier into attribute argument
+ tree ns_name = DECL_NAME (ns);
+ tree str = fix_string_type (
+ build_string(IDENTIFIER_LENGTH (ns_name) + 1,
+ IDENTIFIER_POINTER (ns_name)));
+ args = build_tree_list (NULL_TREE, str);
+ }
+ else if (TREE_CHAIN (args)
+ || TREE_CODE (TREE_VALUE (args)) != STRING_CST)
+ {
+ warning (OPT_Wattributes,
+ "%qD attribute requires 0 or 1 NTBS arguments",
+ name);
+ continue;
+ }
+ tree attributes = tree_cons (name, args, NULL_TREE);
+ handle_namespace_attrs (ORIGINAL_NAMESPACE (ns), attributes);
+ continue;
+ }
+ if (!args || TREE_CHAIN (args)
+ || TREE_CODE (TREE_VALUE (args)) != STRING_CST)
+ {
+ warning (OPT_Wattributes,
+ "%qD attribute requires a single NTBS argument",
+ name);
+ continue;
+ }
+ tree existing
+ = lookup_attribute ("diagnose_as", DECL_ATTRIBUTES (ns));
+ if (existing
+ && !cp_tree_equal (TREE_VALUE (args),
+ TREE_VALUE (TREE_VALUE (existing))))
+ {
+ auto_diagnostic_group d;
+ warning (OPT_Wattributes, "%qD redeclared with different %qD "
+ "attribute value", ns, name);
+ inform (DECL_SOURCE_LOCATION (ns), "previous declaration here");
+ continue;
+ }
+ DECL_ATTRIBUTES (ns) = tree_cons (name, args,
+ DECL_ATTRIBUTES (ns));
+ }
else
{
warning (OPT_Wattributes, "%qD attribute directive ignored",
@@ -6092,7 +6139,7 @@ pop_decl_namespace (void)
/* Process a namespace-alias declaration. */
void
-do_namespace_alias (tree alias, tree name_space)
+do_namespace_alias (tree alias, tree name_space, tree attributes)
{
if (name_space == error_mark_node)
return;
@@ -6108,6 +6155,9 @@ do_namespace_alias (tree alias, tree name_space)
DECL_CONTEXT (alias) = FROB_CONTEXT (current_scope ());
set_originating_module (alias);
+ /* Apply attributes. */
+ handle_namespace_attrs (alias, attributes);
+
pushdecl (alias);
/* Emit debug info for namespace alias. */
diff --git a/gcc/cp/name-lookup.h b/gcc/cp/name-lookup.h
index f63c4f5b8bb..cdbb6834220 100644
--- a/gcc/cp/name-lookup.h
+++ b/gcc/cp/name-lookup.h
@@ -435,7 +435,7 @@ extern tree cp_namespace_decls (tree);
extern void set_decl_namespace (tree, tree, bool);
extern void push_decl_namespace (tree);
extern void pop_decl_namespace (void);
-extern void do_namespace_alias (tree, tree);
+extern void do_namespace_alias (tree, tree, tree);
extern tree do_class_using_decl (tree, tree);
extern tree lookup_arg_dependent (tree, tree, vec<tree, va_gc> *);
extern tree search_anon_aggr (tree, tree, bool = false);
diff --git a/gcc/cp/parser.c b/gcc/cp/parser.c
index 821ce1771a4..8fcd9840ea5 100644
--- a/gcc/cp/parser.c
+++ b/gcc/cp/parser.c
@@ -14462,20 +14462,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)
@@ -20860,15 +20855,25 @@ 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 attributes = cp_parser_attributes_opt (parser);
+ if (attributes
+ && !cp_parser_uncommitted_to_tentative_parse_p (parser))
+ 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))
@@ -20880,7 +20885,11 @@ 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))
+ {
+ input_location = start;
+ return;
+ }
/* Look for the qualified-namespace-specifier. */
namespace_specifier
= cp_parser_qualified_namespace_specifier (parser);
@@ -20889,7 +20898,7 @@ cp_parser_namespace_alias_definition (cp_parser* parser)
cp_parser_require (parser, CPP_SEMICOLON, RT_SEMICOLON);
/* Register the alias in the symbol table. */
- do_namespace_alias (identifier, namespace_specifier);
+ do_namespace_alias (identifier, namespace_specifier, attributes);
}
/* Parse a qualified-namespace-specifier.
diff --git a/gcc/cp/tree.c b/gcc/cp/tree.c
index 2a14fa92ddb..d62e43bff06 100644
--- a/gcc/cp/tree.c
+++ b/gcc/cp/tree.c
@@ -46,6 +46,7 @@ static tree verify_stmt_tree_r (tree *, int *, void *);
static tree handle_init_priority_attribute (tree *, tree, tree, int, bool *);
static tree handle_abi_tag_attribute (tree *, tree, tree, int, bool *);
+static tree handle_diagnose_as_attribute (tree *, tree, tree, int, bool *);
/* If REF is an lvalue, returns the kind of lvalue that REF is.
Otherwise, returns clk_none. */
@@ -4877,6 +4878,8 @@ const struct attribute_spec cxx_attribute_table[] =
handle_init_priority_attribute, NULL },
{ "abi_tag", 1, -1, false, false, false, true,
handle_abi_tag_attribute, NULL },
+ { "diagnose_as", 0, -1, false, false, false, false,
+ handle_diagnose_as_attribute, NULL },
{ NULL, 0, 0, false, false, false, false, NULL, NULL }
};
@@ -5145,6 +5148,151 @@ 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))
+ && PRIMARY_TEMPLATE_P (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. */
diff --git a/gcc/doc/extend.texi b/gcc/doc/extend.texi
index b83cd4919bb..96640ba156f 100644
--- a/gcc/doc/extend.texi
+++ b/gcc/doc/extend.texi
@@ -2865,6 +2865,51 @@ types (@pxref{Variable Attributes}, @pxref{Type Attributes}.)
The message attached to the attribute is affected by the setting of
the @option{-fmessage-length} option.
+@item diagnose_as ("@var{string}")
+@itemx diagnose_as
+@cindex @code{diagnose_as} function attribute
+The @code{diagnose_as} attribute modifies how the entity the attribute
+appertains to is diagnosed in compiler messages and @code{__func__},
+@code{__FUNCTION__}, and @code{__PRETTY_FUNCTION__} macros. (The result of
+@code{typeid} is not affected.) If the attribute is applied to a
+@code{namespace}, the specified string replaces the complete enclosing scope.
+The effect of the @code{diagnose_as} attribute can be disabled with the
+@option{-fno-diagnostics-use-aliases} command line option.
+
+The argument @var{string} is optional for type and namespace aliases. If it is
+omitted, the identifier of the type or namespace alias is unconditionally used
+in all places where the type / namespace on the right-hand side is diagnosed.
+
+@smallexample
+namespace Foo @{
+ namespace Bar @{
+ inline namespace v1 [[gnu::diagnose_as("Foobar")]] @{
+ int f() @{
+ // __PRETTY_FUNCTION__ == "void Foobar::f()"
+ @}
+ @}
+ @}
+@}
+// In function 'int Foobar::f()':
+// warning: no return statement in function returning non-void [-Wreturn-type]
+@end smallexample
+
+The @code{diagnose_as} attribute can be used with namespaces, namespace aliases,
+functions, variables, alias declarations (but not alias templates), and
+user-defined types (classes, unions, and enums). If the alias declaration
+aliases a class type (including template specializations), the attribute is
+additionally applied to the class type. Whether the attribute has an effect on
+partial template specializations is unspecified and may depend on several
+different factors.
+
+@smallexample
+template <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
diff --git a/gcc/doc/invoke.texi b/gcc/doc/invoke.texi
index ea8812425e9..c2d49ffab22 100644
--- a/gcc/doc/invoke.texi
+++ b/gcc/doc/invoke.texi
@@ -311,7 +311,8 @@ Objective-C and Objective-C++ Dialects}.
-fdiagnostics-show-path-depths @gol
-fno-show-column @gol
-fdiagnostics-column-unit=@r{[}display@r{|}byte@r{]} @gol
--fdiagnostics-column-origin=@var{origin}}
+-fdiagnostics-column-origin=@var{origin} @gol
+-fno-diagnostics-aliases}
@item Warning Options
@xref{Warning Options,,Options to Request or Suppress Warnings}.
@@ -5119,6 +5120,12 @@ first column. The default value of 1 corresponds to traditional GCC
behavior and to the GNU style guide. Some utilities may perform better with an
origin of 0; any non-negative value may be specified.
+@item -fno-diagnostics-use-aliases
+@opindex fno-diagnostics-use-aliases
+@opindex fdiagnostics-use-aliases
+Do not replace identifiers or scope names with the aliases defined with the
+@code{[[gnu::diagnose_as("alias")]]} attribute.
+
@item -fdiagnostics-format=@var{FORMAT}
@opindex fdiagnostics-format
Select a different format for printing diagnostics.
diff --git a/gcc/testsuite/g++.dg/diagnostic/diagnose-as1.C b/gcc/testsuite/g++.dg/diagnostic/diagnose-as1.C
new file mode 100644
index 00000000000..44ff3825e95
--- /dev/null
+++ b/gcc/testsuite/g++.dg/diagnostic/diagnose-as1.C
@@ -0,0 +1,213 @@
+// { dg-options "-fdiagnostics-use-aliases -fpretty-templates" }
+
+#ifdef __cpp_constexpr
+template <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() [with T = float]"
+ : "void @1::@2<U>::fun:4() [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();
+}
diff --git a/gcc/testsuite/g++.dg/diagnostic/diagnose-as2.C b/gcc/testsuite/g++.dg/diagnostic/diagnose-as2.C
new file mode 100644
index 00000000000..b1d46d12024
--- /dev/null
+++ b/gcc/testsuite/g++.dg/diagnostic/diagnose-as2.C
@@ -0,0 +1,144 @@
+// { dg-options "-fdiagnostics-use-aliases -fno-pretty-templates" }
+
+#ifdef __cpp_constexpr
+template <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();
+}
diff --git a/gcc/testsuite/g++.dg/diagnostic/diagnose-as3.C b/gcc/testsuite/g++.dg/diagnostic/diagnose-as3.C
new file mode 100644
index 00000000000..b0e1c064886
--- /dev/null
+++ b/gcc/testsuite/g++.dg/diagnostic/diagnose-as3.C
@@ -0,0 +1,152 @@
+// { dg-options "-fdiagnostics-use-aliases -fpretty-templates" }
+// { dg-do compile { target c++11 } }
+
+const char* volatile s;
+
+namespace foo
+{
+ template <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() [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() [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 U = int; P = char]",
+ foo::Bar<char*>::f());
+ TEST("static constexpr const char* foo::BarPtr<P*>::f() [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() [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() [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() [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() [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() [with T3 = long int; U3 = long long int]",
+ (X0<int, int>::X3::f<long, long long>()));
+}
diff --git a/gcc/testsuite/g++.dg/diagnostic/diagnose-as4.C b/gcc/testsuite/g++.dg/diagnostic/diagnose-as4.C
new file mode 100644
index 00000000000..89b800c7b9d
--- /dev/null
+++ b/gcc/testsuite/g++.dg/diagnostic/diagnose-as4.C
@@ -0,0 +1,158 @@
+// { dg-options "-fdiagnostics-use-aliases -fno-pretty-templates" }
+// { dg-do compile { target c++11 } }
+
+const char* volatile s;
+
+namespace foo
+{
+ template <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 }
+}
diff --git a/gcc/testsuite/g++.dg/diagnostic/diagnose-as5.C b/gcc/testsuite/g++.dg/diagnostic/diagnose-as5.C
new file mode 100644
index 00000000000..2a46d64964f
--- /dev/null
+++ b/gcc/testsuite/g++.dg/diagnostic/diagnose-as5.C
@@ -0,0 +1,21 @@
+// { dg-options "-fdiagnostics-use-aliases -fpretty-templates" }
+// { dg-do compile { target c++11 } }
+
+namespace std
+{
+ template <class 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'" }
+}
diff --git a/gcc/testsuite/g++.dg/diagnostic/diagnose-as6.C b/gcc/testsuite/g++.dg/diagnostic/diagnose-as6.C
new file mode 100644
index 00000000000..94747a7aa6e
--- /dev/null
+++ b/gcc/testsuite/g++.dg/diagnostic/diagnose-as6.C
@@ -0,0 +1,45 @@
+// { dg-options "-fdiagnostics-use-aliases -fpretty-templates" }
+// { dg-do compile { target c++11 } }
+
+template <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'" }
+}
^ permalink raw reply [flat|nested] 24+ messages in thread
* Re: [PATCH v4] c++: Add gnu::diagnose_as attribute
2021-07-23 8:58 ` [PATCH v4] " Matthias Kretz
@ 2021-08-17 18:31 ` Jason Merrill
2021-11-08 16:40 ` [RFC] c++: Print function template parms when relevant (was: [PATCH v4] c++: Add gnu::diagnose_as attribute) Matthias Kretz
2021-09-08 2:21 ` [PATCH v4] c++: Add gnu::diagnose_as attribute Jason Merrill
1 sibling, 1 reply; 24+ messages in thread
From: Jason Merrill @ 2021-08-17 18:31 UTC (permalink / raw)
To: Matthias Kretz, gcc-patches
[-- Attachment #1: Type: text/plain, Size: 9585 bytes --]
On 7/23/21 4:58 AM, Matthias Kretz wrote:
> Hi Jason,
Hi, thanks for your patience; I've been out on PTO a lot in the last
month, and will be again this week.
> I found a few regressions from the last patch in the meantime. Version 4 of
> the patch is attached.
>
> Questions:
>
> 1. I simplified the condition for calling dump_template_parms in
> dump_function_name. !DECL_FRIEND_PSEUDO_TEMPLATE_INSTANTIATION (t) is
> equivalent to DECL_USE_TEMPLATE (t) in this context; implying that
> dump_template_parms is unconditionally called with `primary = false`. Or am I
> missing something?
Ah, good catch. That suggests that
DECL_FRIEND_PSEUDO_TEMPLATE_INSTANTIATION gives false positives;
DECL_USE_TEMPLATE is also 0 for template patterns themselves, which
would be why for
template <class T>
void f() { foo; }
the error refers to 'void f()' rather than 'void f<T>()'
The macro should probably also check DECL_FRIEND_CONTEXT.
And then the use of !DECL_USE_TEMPLATE is intending to check whether t
is a primary template pattern, and pass true in that case. This would
be more correct if it also checked instantiates_primary_template_p.
> 2. Given a DECL_TI_ARGS tree, can I query whether an argument was deduced or
> explicitly specified? I'm asking because I still consider diagnostics of
> function templates unfortunate. `template <class T> void f()` is fine, as is
> `void f(T) [with T = float]`, but `void f() [with T = float]` could be better.
> I.e. if the template parameter appears somewhere in the function parameter
> list, dump_template_parms would only produce noise. If, however, the template
> parameter was given explicitly, it would be nice if it could show up
> accordingly in diagnostics.
NON_DEFAULT_TEMPLATE_ARGS_COUNT has that information, though there are
some issues with it. Attached is my WIP from May to improve it
somewhat, if that's interesting.
> 3. When parsing tentatively and the parse is rejected, input_location is not
> reset, correct? In the attached patch I therefore made
> cp_parser_namespace_alias_definition reset input_location on a failed
> tentative parse. But it feels wrong. Shouldn't input_location be restored on
> cp_parser_parse_definitely?
Makes sense, I guess cp_lexer_rollback_tokens should call
cp_lexer_set_source_position_from_token.
I'll look at the patch soon.
> --
>
> This attribute overrides the diagnostics output string for the entity it
> appertains to. The motivation is to improve QoI for library TS
> implementations, where diagnostics have a very bad signal-to-noise ratio
> due to the long namespaces involved.
>
> With the attribute, it is possible to solve PR89370 and make
> std::__cxx11::basic_string<_CharT, _Traits, _Alloc> appear as
> std::string in diagnostic output without extra hacks to recognize the
> type in the C++ frontend.
>
> Signed-off-by: Matthias Kretz <m.kretz@gsi.de>
>
> gcc/ChangeLog:
>
> PR c++/89370
> * doc/extend.texi: Document the diagnose_as attribute.
> * doc/invoke.texi: Document -fno-diagnostics-use-aliases.
>
> gcc/c-family/ChangeLog:
>
> PR c++/89370
> * c.opt (fdiagnostics-use-aliases): New diagnostics flag.
>
> gcc/cp/ChangeLog:
>
> PR c++/89370
> * cp-tree.h: Add is_alias_template_p declaration.
> * decl2.c (is_alias_template_p): New function. Determines
> whether a given TYPE_DECL is actually an alias template that is
> still missing its template_info.
> (is_late_template_attribute): Decls with diagnose_as attribute
> are early attributes only if they are alias templates.
> * error.c (dump_scope): When printing the name of a namespace,
> look for the diagnose_as attribute. If found, print the
> associated string instead of calling dump_decl.
> (dump_decl_name_or_diagnose_as): New function to replace
> dump_decl (pp, DECL_NAME(t), flags) and inspect the tree for the
> diagnose_as attribute before printing the DECL_NAME.
> (dump_template_scope): New function. Prints the scope of a
> template instance correctly applying diagnose_as attributes and
> adjusting the list of template parms accordingly.
> (dump_aggr_type): If the type has a diagnose_as attribute, print
> the associated string instead of printing the original type
> name. Print template parms only if the attribute was not applied
> to the instantiation / full specialization. Delay call to
> dump_scope until the diagnose_as attribute is found. If the
> attribute has a second argument, use it to override the context
> passed to dump_scope.
> (dump_simple_decl): Call dump_decl_name_or_diagnose_as instead
> of dump_decl.
> (dump_decl): Ditto.
> (lang_decl_name): Ditto.
> (dump_function_decl): Walk the functions context list to
> determine whether a call to dump_template_scope is required.
> Ensure function templates diagnosed with pretty templates set
> TFF_TEMPLATE_NAME to skip dump_template_parms.
> (dump_function_name): Replace the function's identifier with the
> diagnose_as attribute value, if set. Expand
> DECL_FRIEND_PSEUDO_TEMPLATE_INSTANTIATION to DECL_USE_TEMPLATE
> and consequently call dump_template_parms with primary = false.
> (comparable_template_types_p): Consider the types not a template
> if one carries a diagnose_as attribute.
> (print_template_differences): Replace the identifier with the
> diagnose_as attribute value on the most general template, if it
> is set.
> * name-lookup.c (handle_namespace_attrs): Handle the diagnose_as
> attribute on namespaces. Ensure exactly one string argument.
> Ensure previous diagnose_as attributes used the same name.
> 'diagnose_as' on namespace aliases are forwarded to the original
> namespace. Support no-argument 'diagnose_as' on namespace
> aliases.
> (do_namespace_alias): Add attributes parameter and call
> handle_namespace_attrs.
> * name-lookup.h (do_namespace_alias): Add attributes tree
> parameter.
> * parser.c (cp_parser_declaration): If the next token is
> RID_NAMESPACE, tentatively parse a namespace alias definition.
> If this fails expect a namespace definition.
> (cp_parser_namespace_alias_definition): Allow optional
> attributes before and after the identifier. Fast exit, restoring
> input_location, if the expected CPP_EQ token is missing. Pass
> attributes to do_namespace_alias.
> * tree.c (cxx_attribute_table): Add diagnose_as attribute to the
> table.
> (check_diagnose_as_redeclaration): New function; copied and
> adjusted from check_abi_tag_redeclaration.
> (handle_diagnose_as_attribute): New function; copied and
> adjusted from handle_abi_tag_attribute. If the given *node is a
> TYPE_DECL: allow no argument to the attribute, using DECL_NAME
> instead; apply the attribute to the type on the RHS in place,
> even if the type is complete. Allow 2 arguments when called from
> handle_diagnose_as_attribute. For type aliases, append
> CP_DECL_CONTEXT as second attribute argument when the RHS type
> has a different context. Warn about alias templates without
> matching template arguments. Apply the attribute to the primary
> template type for alias templates.
>
> gcc/testsuite/ChangeLog:
>
> PR c++/89370
> * g++.dg/diagnostic/diagnose-as1.C: New test.
> * g++.dg/diagnostic/diagnose-as2.C: New test.
> * g++.dg/diagnostic/diagnose-as3.C: New test.
> * g++.dg/diagnostic/diagnose-as4.C: New test.
> * g++.dg/diagnostic/diagnose-as5.C: New test.
> * g++.dg/diagnostic/diagnose-as6.C: New test.
> ---
> gcc/c-family/c.opt | 4 +
> gcc/cp/cp-tree.h | 1 +
> gcc/cp/decl2.c | 45 ++++
> gcc/cp/error.c | 247 ++++++++++++++++--
> gcc/cp/name-lookup.c | 52 +++-
> gcc/cp/name-lookup.h | 2 +-
> gcc/cp/parser.c | 41 +--
> gcc/cp/tree.c | 148 +++++++++++
> gcc/doc/extend.texi | 45 ++++
> gcc/doc/invoke.texi | 9 +-
> .../g++.dg/diagnostic/diagnose-as1.C | 213 +++++++++++++++
> .../g++.dg/diagnostic/diagnose-as2.C | 144 ++++++++++
> .../g++.dg/diagnostic/diagnose-as3.C | 152 +++++++++++
> .../g++.dg/diagnostic/diagnose-as4.C | 158 +++++++++++
> .../g++.dg/diagnostic/diagnose-as5.C | 21 ++
> .../g++.dg/diagnostic/diagnose-as6.C | 45 ++++
> 16 files changed, 1291 insertions(+), 36 deletions(-)
> create mode 100644 gcc/testsuite/g++.dg/diagnostic/diagnose-as1.C
> create mode 100644 gcc/testsuite/g++.dg/diagnostic/diagnose-as2.C
> create mode 100644 gcc/testsuite/g++.dg/diagnostic/diagnose-as3.C
> create mode 100644 gcc/testsuite/g++.dg/diagnostic/diagnose-as4.C
> create mode 100644 gcc/testsuite/g++.dg/diagnostic/diagnose-as5.C
> create mode 100644 gcc/testsuite/g++.dg/diagnostic/diagnose-as6.C
>
[-- Attachment #2: 0001-default.patch --]
[-- Type: text/x-patch, Size: 5751 bytes --]
From 857e349f43a59c88634664ec8635ae49dd7bd50f Mon Sep 17 00:00:00 2001
From: Jason Merrill <jason@redhat.com>
Date: Thu, 27 May 2021 14:04:45 -0400
Subject: [PATCH] default
To: gcc-patches@gcc.gnu.org
---
gcc/cp/error.c | 13 +++++++------
gcc/cp/pt.c | 44 +++++++++++++++++++++++++++-----------------
2 files changed, 34 insertions(+), 23 deletions(-)
diff --git a/gcc/cp/error.c b/gcc/cp/error.c
index 9cb8bfc413e..cac4d3e2089 100644
--- a/gcc/cp/error.c
+++ b/gcc/cp/error.c
@@ -87,8 +87,6 @@ static void dump_exception_spec (cxx_pretty_printer *, tree, int);
static void dump_template_argument (cxx_pretty_printer *, tree, int);
static void dump_template_argument_list (cxx_pretty_printer *, tree, int);
static void dump_template_parameter (cxx_pretty_printer *, tree, int);
-static void dump_template_bindings (cxx_pretty_printer *, tree, tree,
- vec<tree, va_gc> *);
static void dump_scope (cxx_pretty_printer *, tree, int);
static void dump_template_parms (cxx_pretty_printer *, tree, int, int);
static int get_non_default_template_args_count (tree, int);
@@ -375,7 +373,7 @@ dump_template_parameter (cxx_pretty_printer *pp, tree parm, int flags)
static void
dump_template_bindings (cxx_pretty_printer *pp, tree parms, tree args,
- vec<tree, va_gc> *typenames)
+ vec<tree, va_gc> *typenames, int flags)
{
/* Print "[with" and ']', conditional on whether anything is printed at all.
This is tied to whether a semicolon is needed to separate multiple template
@@ -425,7 +423,8 @@ dump_template_bindings (cxx_pretty_printer *pp, tree parms, tree args,
if (TMPL_ARGS_DEPTH (args) >= lvl)
lvl_args = TMPL_ARGS_LEVEL (args, lvl);
- for (i = 0; i < TREE_VEC_LENGTH (p); ++i)
+ int len = get_non_default_template_args_count (lvl_args, flags);
+ for (i = 0; i < len; ++i)
{
tree arg = NULL_TREE;
@@ -1663,7 +1662,8 @@ dump_substitution (cxx_pretty_printer *pp,
&& !(flags & TFF_NO_TEMPLATE_BINDINGS))
{
vec<tree, va_gc> *typenames = t ? find_typenames (t) : NULL;
- dump_template_bindings (pp, template_parms, template_args, typenames);
+ dump_template_bindings (pp, template_parms, template_args, typenames,
+ flags);
}
}
@@ -3449,7 +3449,8 @@ subst_to_string (tree p)
return "";
dump_template_decl (cxx_pp, TREE_PURPOSE (p), flags);
- dump_substitution (cxx_pp, NULL, tparms, targs, /*flags=*/0);
+ dump_substitution (cxx_pp, NULL, tparms, targs,
+ /*TFF_NO_OMIT_DEFAULT_TEMPLATE_ARGUMENTS*/0);
return pp_ggc_formatted_text (cxx_pp);
}
diff --git a/gcc/cp/pt.c b/gcc/cp/pt.c
index 0870ccdc9f6..691f1b82535 100644
--- a/gcc/cp/pt.c
+++ b/gcc/cp/pt.c
@@ -2518,7 +2518,7 @@ determine_specialization (tree template_id,
if (candidates)
{
tree fn = TREE_VALUE (candidates);
- *targs_out = copy_node (DECL_TI_ARGS (fn));
+ *targs_out = copy_template_args (DECL_TI_ARGS (fn));
/* Propagate the candidate's constraints to the declaration. */
if (tsk != tsk_template)
@@ -4816,11 +4816,16 @@ template_parms_level_to_args (tree parms)
{
tree a = copy_node (parms);
TREE_TYPE (a) = NULL_TREE;
+ int nondefault = 0;
for (int i = TREE_VEC_LENGTH (a) - 1; i >= 0; --i)
- TREE_VEC_ELT (a, i) = template_parm_to_arg (TREE_VEC_ELT (a, i));
+ {
+ tree elt = TREE_VEC_ELT (a, i);
+ TREE_VEC_ELT (a, i) = template_parm_to_arg (elt);
+ if (!elt || elt == error_mark_node || !TREE_PURPOSE (elt))
+ ++nondefault;
+ }
- if (CHECKING_P)
- SET_NON_DEFAULT_TEMPLATE_ARGS_COUNT (a, TREE_VEC_LENGTH (a));
+ SET_NON_DEFAULT_TEMPLATE_ARGS_COUNT (a, nondefault);
return a;
}
@@ -13276,8 +13281,9 @@ copy_template_args (tree t)
TREE_VEC_ELT (new_vec, i) = elt;
}
- NON_DEFAULT_TEMPLATE_ARGS_COUNT (new_vec)
- = NON_DEFAULT_TEMPLATE_ARGS_COUNT (t);
+ if (!TMPL_ARGS_HAVE_MULTIPLE_LEVELS (t))
+ NON_DEFAULT_TEMPLATE_ARGS_COUNT (new_vec)
+ = NON_DEFAULT_TEMPLATE_ARGS_COUNT (t);
return new_vec;
}
@@ -22339,9 +22345,17 @@ type_unification_real (tree tparms,
be NULL_TREE or ERROR_MARK_NODE, so we do not need
to explicitly check cxx_dialect here. */
if (TREE_PURPOSE (TREE_VEC_ELT (tparms, i)))
- /* OK, there is a default argument. Wait until after the
- conversion check to do substitution. */
- continue;
+ {
+ /* The position of the first default template argument,
+ is also the number of non-defaulted arguments in TARGS.
+ Record that. */
+ if (!NON_DEFAULT_TEMPLATE_ARGS_COUNT (targs))
+ SET_NON_DEFAULT_TEMPLATE_ARGS_COUNT (targs, i);
+
+ /* OK, there is a default argument. Wait until after the
+ conversion check to do substitution. */
+ continue;
+ }
/* If the type parameter is a parameter pack, then it will
be deduced to an empty parameter pack. */
@@ -22444,14 +22458,7 @@ type_unification_real (tree tparms,
if (arg == error_mark_node)
return 1;
else if (arg)
- {
- TREE_VEC_ELT (targs, i) = arg;
- /* The position of the first default template argument,
- is also the number of non-defaulted arguments in TARGS.
- Record that. */
- if (!NON_DEFAULT_TEMPLATE_ARGS_COUNT (targs))
- SET_NON_DEFAULT_TEMPLATE_ARGS_COUNT (targs, i);
- }
+ TREE_VEC_ELT (targs, i) = arg;
}
if (saw_undeduced++ == 1)
@@ -24821,6 +24828,9 @@ get_partial_spec_bindings (tree tmpl, tree spec_tmpl, tree args)
if (!template_template_parm_bindings_ok_p (tparms, deduced_args))
return NULL_TREE;
+ if (CHECKING_P)
+ SET_NON_DEFAULT_TEMPLATE_ARGS_COUNT (deduced_args, ntparms);
+
return deduced_args;
}
--
2.27.0
^ permalink raw reply [flat|nested] 24+ messages in thread
* Re: [PATCH v4] c++: Add gnu::diagnose_as attribute
2021-07-23 8:58 ` [PATCH v4] " Matthias Kretz
2021-08-17 18:31 ` Jason Merrill
@ 2021-09-08 2:21 ` Jason Merrill
2021-11-15 0:35 ` [PATCH v5] " Matthias Kretz
1 sibling, 1 reply; 24+ messages in thread
From: Jason Merrill @ 2021-09-08 2:21 UTC (permalink / raw)
To: Matthias Kretz, gcc-patches
On 7/23/21 4:58 AM, Matthias Kretz wrote:
> Hi Jason,
>
> I found a few regressions from the last patch in the meantime. Version 4 of
> the patch is attached.
>
> Questions:
>
> 1. I simplified the condition for calling dump_template_parms in
> dump_function_name. !DECL_FRIEND_PSEUDO_TEMPLATE_INSTANTIATION (t) is
> equivalent to DECL_USE_TEMPLATE (t) in this context; implying that
> dump_template_parms is unconditionally called with `primary = false`. Or am I
> missing something?
>
> 2. Given a DECL_TI_ARGS tree, can I query whether an argument was deduced or
> explicitly specified? I'm asking because I still consider diagnostics of
> function templates unfortunate. `template <class T> void f()` is fine, as is
> `void f(T) [with T = float]`, but `void f() [with T = float]` could be better.
> I.e. if the template parameter appears somewhere in the function parameter
> list, dump_template_parms would only produce noise. If, however, the template
> parameter was given explicitly, it would be nice if it could show up
> accordingly in diagnostics.
>
> 3. When parsing tentatively and the parse is rejected, input_location is not
> reset, correct? In the attached patch I therefore made
> cp_parser_namespace_alias_definition reset input_location on a failed
> tentative parse. But it feels wrong. Shouldn't input_location be restored on
> cp_parser_parse_definitely?
>
> --
>
> This attribute overrides the diagnostics output string for the entity it
> appertains to. The motivation is to improve QoI for library TS
> implementations, where diagnostics have a very bad signal-to-noise ratio
> due to the long namespaces involved.
>
> With the attribute, it is possible to solve PR89370 and make
> std::__cxx11::basic_string<_CharT, _Traits, _Alloc> appear as
> std::string in diagnostic output without extra hacks to recognize the
> type in the C++ frontend.
>
> Signed-off-by: Matthias Kretz <m.kretz@gsi.de>
>
> gcc/ChangeLog:
>
> PR c++/89370
> * doc/extend.texi: Document the diagnose_as attribute.
> * doc/invoke.texi: Document -fno-diagnostics-use-aliases.
>
> gcc/c-family/ChangeLog:
>
> PR c++/89370
> * c.opt (fdiagnostics-use-aliases): New diagnostics flag.
>
> gcc/cp/ChangeLog:
>
> PR c++/89370
> * cp-tree.h: Add is_alias_template_p declaration.
> * decl2.c (is_alias_template_p): New function. Determines
> whether a given TYPE_DECL is actually an alias template that is
> still missing its template_info.
I still think you want to share code with get_underlying_template. For
the case where the alias doesn't have DECL_TEMPLATE_INFO yet, you can
compare to current_template_args (). Or you could do some initial
processing that doesn't care about templates in the handler, and then do
more in cp_parser_alias_declaration after the call to grokfield/start_decl.
If you still think you need this function, let's call it
is_renaming_alias_template or renaming_alias_template_p; using both is_
and _p is redundant. I don't have a strong preference which.
> (is_late_template_attribute): Decls with diagnose_as attribute
> are early attributes only if they are alias templates.
Is there a reason not to apply it early to other templates as well?
> * error.c (dump_scope): When printing the name of a namespace,
> look for the diagnose_as attribute. If found, print the
> associated string instead of calling dump_decl.
Did you decide not to handle this in dump_decl, so we use the
diagnose_as when referring to the namespace in non-scope contexts as well?
> (dump_decl_name_or_diagnose_as): New function to replace
> dump_decl (pp, DECL_NAME(t), flags) and inspect the tree for the
> diagnose_as attribute before printing the DECL_NAME.
> + 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))));
This pattern is used several places outside this function; can we factor
it into something like
if (maybe_print_diagnose_as (special))
/* OK */;
?
> (dump_template_scope): New function. Prints the scope of a
> template instance correctly applying diagnose_as attributes and
> adjusting the list of template parms accordingly.
> + 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)));
CLASSTYPE_SPECIALIZATION_OF_PRIMARY_TEMPLATE_P?
> + tmplate ? &TREE_CHAIN(*parms) : parms, flags);
Missing space before (
> + if (tmplate)
> + TREE_VALUE (*parms) = make_tree_vec (0);
This could use a comment.
> (dump_aggr_type): If the type has a diagnose_as attribute, print
> the associated string instead of printing the original type
> name. Print template parms only if the attribute was not applied
> to the instantiation / full specialization. Delay call to
> dump_scope until the diagnose_as attribute is found. If the
> attribute has a second argument, use it to override the context
> passed to dump_scope.
> + 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;
> + }
> + }
I think you want any_dependent_template_args_p (args)
Checking WILDCARD_TYPE_P is generally not what you want; plenty of
dependent types don't show up specifically as wildcards. T*, for instance.
> + if (diagnose_as)
> + pp_cxx_ws_string (pp, TREE_STRING_POINTER (
> + TREE_VALUE (TREE_VALUE (diagnose_as))));
( needs to go on the next line. I'd format this as
if (diagnose_as)
pp_cxx_ws_string (pp, (TREE_STRING_POINTER
(TREE_VALUE (TREE_VALUE (diagnose_as)))));
There's a lot of this formatting pattern in the patch.
> (dump_simple_decl): Call dump_decl_name_or_diagnose_as instead
> of dump_decl.
> (dump_decl): Ditto.
> + dump_decl_name_or_diagnose_as(pp, t, flags);
Missing space before (
> (lang_decl_name): Ditto.
> (dump_function_decl): Walk the functions context list to
> determine whether a call to dump_template_scope is required.
> Ensure function templates diagnosed with pretty templates set
> TFF_TEMPLATE_NAME to skip dump_template_parms.
> (dump_function_name): Replace the function's identifier with the
> diagnose_as attribute value, if set. Expand
> DECL_FRIEND_PSEUDO_TEMPLATE_INSTANTIATION to DECL_USE_TEMPLATE
> and consequently call dump_template_parms with primary = false.
> (comparable_template_types_p): Consider the types not a template
> if one carries a diagnose_as attribute.
I'd think it would be more helpful to suppress diagnose_as if the types
are comparable.
> (print_template_differences): Replace the identifier with the
> diagnose_as attribute value on the most general template, if it
> is set.
> * name-lookup.c (handle_namespace_attrs): Handle the diagnose_as
> attribute on namespaces. Ensure exactly one string argument.
> Ensure previous diagnose_as attributes used the same name.
> 'diagnose_as' on namespace aliases are forwarded to the original
> namespace. Support no-argument 'diagnose_as' on namespace
> aliases.
> (do_namespace_alias): Add attributes parameter and call
> handle_namespace_attrs.
> * name-lookup.h (do_namespace_alias): Add attributes tree
> parameter.
> * parser.c (cp_parser_declaration): If the next token is
> RID_NAMESPACE, tentatively parse a namespace alias definition.
> If this fails expect a namespace definition.
> (cp_parser_namespace_alias_definition): Allow optional
> attributes before and after the identifier. Fast exit, restoring
> input_location, if the expected CPP_EQ token is missing. Pass
> attributes to do_namespace_alias.
> + if (attributes
> + && !cp_parser_uncommitted_to_tentative_parse_p (parser))
> + pedwarn (input_location, OPT_Wpedantic,
> + "standard attributes on namespaces aliases must follow "
> + "the namespace alias name");
Maybe remember where we saw attributes and warn later, after we've
committed to parsing as an alias. Or use cp_parser_skip_attributes_opt
to avoid tentative parsing in the first place.
> + if (nullptr == cp_parser_require (parser, CPP_EQ, RT_EQ))
The usual pattern is
if (!cp_parser_require
> * tree.c (cxx_attribute_table): Add diagnose_as attribute to the
> table.
> (check_diagnose_as_redeclaration): New function; copied and
> adjusted from check_abi_tag_redeclaration.
> (handle_diagnose_as_attribute): New function; copied and
> adjusted from handle_abi_tag_attribute. If the given *node is a
> TYPE_DECL: allow no argument to the attribute, using DECL_NAME
> instead; apply the attribute to the type on the RHS in place,
> even if the type is complete. Allow 2 arguments when called from
> handle_diagnose_as_attribute. For type aliases, append
> CP_DECL_CONTEXT as second attribute argument when the RHS type
> has a different context. Warn about alias templates without
> matching template arguments. Apply the attribute to the primary
> template type for alias templates.
All this description of semantics should be in a comment rather than the
CHangeLog.
> + /* Reject alias templates without wildcards on the innermost template arg
> s
> + 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))
> + && PRIMARY_TEMPLATE_P (DECL_TI_TEMPLATE (decl)))
> + return error_mark_node;
I don't think this is doing anything useful; if we had already done
push_template_decl by this point, it would reject all alias templates.
> + // Add the DECL_CONTEXT of the alias to the attribute if it is different
> + // to the context of the type.
How about using the alias TYPE_DECL itself as the argument to the
attribute on the type? Then we wouldn't need to copy its name into a
string, either.
> gcc/testsuite/ChangeLog:
>
> PR c++/89370
> * g++.dg/diagnostic/diagnose-as1.C: New test.
> * g++.dg/diagnostic/diagnose-as2.C: New test.
> * g++.dg/diagnostic/diagnose-as3.C: New test.
> * g++.dg/diagnostic/diagnose-as4.C: New test.
> * g++.dg/diagnostic/diagnose-as5.C: New test.
> * g++.dg/diagnostic/diagnose-as6.C: New test.
> ---
> gcc/c-family/c.opt | 4 +
> gcc/cp/cp-tree.h | 1 +
> gcc/cp/decl2.c | 45 ++++
> gcc/cp/error.c | 247 ++++++++++++++++--
> gcc/cp/name-lookup.c | 52 +++-
> gcc/cp/name-lookup.h | 2 +-
> gcc/cp/parser.c | 41 +--
> gcc/cp/tree.c | 148 +++++++++++
> gcc/doc/extend.texi | 45 ++++
> gcc/doc/invoke.texi | 9 +-
> .../g++.dg/diagnostic/diagnose-as1.C | 213 +++++++++++++++
> .../g++.dg/diagnostic/diagnose-as2.C | 144 ++++++++++
> .../g++.dg/diagnostic/diagnose-as3.C | 152 +++++++++++
> .../g++.dg/diagnostic/diagnose-as4.C | 158 +++++++++++
> .../g++.dg/diagnostic/diagnose-as5.C | 21 ++
> .../g++.dg/diagnostic/diagnose-as6.C | 45 ++++
> 16 files changed, 1291 insertions(+), 36 deletions(-)
> create mode 100644 gcc/testsuite/g++.dg/diagnostic/diagnose-as1.C
> create mode 100644 gcc/testsuite/g++.dg/diagnostic/diagnose-as2.C
> create mode 100644 gcc/testsuite/g++.dg/diagnostic/diagnose-as3.C
> create mode 100644 gcc/testsuite/g++.dg/diagnostic/diagnose-as4.C
> create mode 100644 gcc/testsuite/g++.dg/diagnostic/diagnose-as5.C
> create mode 100644 gcc/testsuite/g++.dg/diagnostic/diagnose-as6.C
>
^ permalink raw reply [flat|nested] 24+ messages in thread
* [RFC] c++: Print function template parms when relevant (was: [PATCH v4] c++: Add gnu::diagnose_as attribute)
2021-08-17 18:31 ` Jason Merrill
@ 2021-11-08 16:40 ` Matthias Kretz
2021-11-08 20:00 ` Matthias Kretz
2021-11-17 6:09 ` Jason Merrill
0 siblings, 2 replies; 24+ messages in thread
From: Matthias Kretz @ 2021-11-08 16:40 UTC (permalink / raw)
To: gcc-patches, Jason Merrill
[-- Attachment #1: Type: text/plain, Size: 6057 bytes --]
On Tuesday, 17 August 2021 20:31:54 CET Jason Merrill wrote:
> > 2. Given a DECL_TI_ARGS tree, can I query whether an argument was deduced
> > or explicitly specified? I'm asking because I still consider diagnostics
> > of function templates unfortunate. `template <class T> void f()` is fine,
> > as is `void f(T) [with T = float]`, but `void f() [with T = float]` could
> > be better. I.e. if the template parameter appears somewhere in the
> > function parameter list, dump_template_parms would only produce noise.
> > If, however, the template parameter was given explicitly, it would be
> > nice if it could show up accordingly in diagnostics.
>
> NON_DEFAULT_TEMPLATE_ARGS_COUNT has that information, though there are
> some issues with it. Attached is my WIP from May to improve it
> somewhat, if that's interesting.
It is interesting. I used your patch to come up with the attached. Patch. I
must say, I didn't try to read through all the cp/pt.c code to understand all
of what you did there (which is why my ChangeLog entry says "Jason?"), but it
works for me (and all of `make check`).
Anyway, I'd like to propose the following before finishing my diagnose_as
patch. I believe it's useful to fix this part first. The diagnostic/default-
template-args-[12].C tests show a lot of examples of the intent of this patch.
And the remaining changes to the testsuite show how it changes diagnostic
output.
---------------------- 8< --------------------
The choice when to print a function template parameter was still
suboptimal. That's because sometimes the function template parameter
list only adds noise, while in other situations the lack of a function
template parameter list makes diagnostic messages hard to understand.
The general idea of this change is to print template parms wherever they
would appear in the source code as well. Thus, the diagnostics code
needs to know whether any template parameter was given explicitly.
Signed-off-by: Matthias Kretz <m.kretz@gsi.de>
gcc/testsuite/ChangeLog:
* g++.dg/debug/dwarf2/template-params-12n.C: Optionally, allow
DW_AT_default_value.
* g++.dg/diagnostic/default-template-args-1.C: New.
* g++.dg/diagnostic/default-template-args-2.C: New.
* g++.dg/diagnostic/param-type-mismatch-2.C: Expect template
parms in diagnostic.
* g++.dg/ext/pretty1.C: Expect function template specialization
to not pretty-print template parms.
* g++.old-deja/g++.ext/pretty3.C: Ditto.
* g++.old-deja/g++.pt/memtemp77.C: Ditto.
* g++.dg/goacc/template.C: Expect function template parms for
explicit arguments.
* g++.dg/gomp/declare-variant-7.C: Expect no function template
parms for deduced arguments.
* g++.dg/template/error40.C: Expect only non-default template
arguments in diagnostic.
gcc/cp/ChangeLog:
* cp-tree.h (GET_NON_DEFAULT_TEMPLATE_ARGS_COUNT): Return
absolute value of stored constant.
(EXPLICIT_TEMPLATE_ARGS_P): New.
(SET_EXPLICIT_TEMPLATE_ARGS_P): New.
(TFF_AS_PRIMARY): New constant.
* error.c (get_non_default_template_args_count): Avoid
GET_NON_DEFAULT_TEMPLATE_ARGS_COUNT if
NON_DEFAULT_TEMPLATE_ARGS_COUNT is a NULL_TREE. Make independent
of flag_pretty_templates.
(dump_template_bindings): Add flags parameter to be passed to
get_non_default_template_args_count. Print only non-default
template arguments.
(dump_function_decl): Call dump_function_name and dump_type of
the DECL_CONTEXT with specialized template and set
TFF_AS_PRIMARY for their flags.
(dump_function_name): Add and document conditions for calling
dump_template_parms.
(dump_template_parms): Print only non-default template
parameters.
* pt.c (determine_specialization): Jason?
(template_parms_level_to_args): Jason?
(copy_template_args): Jason?
(fn_type_unification): Set EXPLICIT_TEMPLATE_ARGS_P on the
template arguments tree if any template parameter was explicitly
given.
(type_unification_real): Jason?
(get_partial_spec_bindings): Jason?
(tsubst_template_args): Determine number of defaulted arguments
from new argument vector, if possible.
---
gcc/cp/cp-tree.h | 18 +++-
gcc/cp/error.c | 83 ++++++++++++++-----
gcc/cp/pt.c | 58 +++++++++----
.../g++.dg/debug/dwarf2/template-params-12n.C | 2 +-
.../diagnostic/default-template-args-1.C | 73 ++++++++++++++++
.../diagnostic/default-template-args-2.C | 37 +++++++++
.../g++.dg/diagnostic/param-type-mismatch-2.C | 2 +-
gcc/testsuite/g++.dg/ext/pretty1.C | 2 +-
gcc/testsuite/g++.dg/goacc/template.C | 8 +-
gcc/testsuite/g++.dg/gomp/declare-variant-7.C | 4 +-
gcc/testsuite/g++.dg/template/error40.C | 6 +-
gcc/testsuite/g++.old-deja/g++.ext/pretty3.C | 2 +-
gcc/testsuite/g++.old-deja/g++.pt/memtemp77.C | 2 +-
13 files changed, 242 insertions(+), 55 deletions(-)
create mode 100644 gcc/testsuite/g++.dg/diagnostic/default-template-args-1.C
create mode 100644 gcc/testsuite/g++.dg/diagnostic/default-template-args-2.C
--
──────────────────────────────────────────────────────────────────────────
Dr. Matthias Kretz https://mattkretz.github.io
GSI Helmholtz Centre for Heavy Ion Research https://gsi.de
stdₓ::simd
──────────────────────────────────────────────────────────────────────────
[-- Attachment #2: 0001-c-Print-function-template-parms-when-relevant.patch --]
[-- Type: text/x-patch, Size: 27049 bytes --]
diff --git a/gcc/cp/cp-tree.h b/gcc/cp/cp-tree.h
index f387b5036d2..27f11e12812 100644
--- a/gcc/cp/cp-tree.h
+++ b/gcc/cp/cp-tree.h
@@ -3686,7 +3686,8 @@ struct GTY(()) lang_decl {
TREE_LANG_FLAG_1 (TEMPLATE_INFO_CHECK (NODE))
/* For a given TREE_VEC containing a template argument list,
this property contains the number of arguments that are not
- defaulted. */
+ defaulted. The sign of the number is negative for function templates with
+ explicitly given template arguments (i.e. neither deduced nor defaulted). */
#define NON_DEFAULT_TEMPLATE_ARGS_COUNT(NODE) \
TREE_CHAIN (TREE_VEC_CHECK (NODE))
@@ -3696,14 +3697,21 @@ struct GTY(()) lang_decl {
NON_DEFAULT_TEMPLATE_ARGS_COUNT(NODE) = build_int_cst (NULL_TREE, INT_VALUE)
#if CHECKING_P
#define GET_NON_DEFAULT_TEMPLATE_ARGS_COUNT(NODE) \
- int_cst_value (NON_DEFAULT_TEMPLATE_ARGS_COUNT (NODE))
+ abs (int_cst_value (NON_DEFAULT_TEMPLATE_ARGS_COUNT (NODE)))
#else
#define GET_NON_DEFAULT_TEMPLATE_ARGS_COUNT(NODE) \
NON_DEFAULT_TEMPLATE_ARGS_COUNT (NODE) \
- ? int_cst_value (NON_DEFAULT_TEMPLATE_ARGS_COUNT (NODE)) \
+ ? abs (int_cst_value (NON_DEFAULT_TEMPLATE_ARGS_COUNT (NODE))) \
: TREE_VEC_LENGTH (INNERMOST_TEMPLATE_ARGS (NODE))
#endif
+#define EXPLICIT_TEMPLATE_ARGS_P(NODE) \
+ (int_cst_value (NON_DEFAULT_TEMPLATE_ARGS_COUNT (NODE)) < 0)
+
+#define SET_EXPLICIT_TEMPLATE_ARGS_P(NODE) \
+ SET_NON_DEFAULT_TEMPLATE_ARGS_COUNT \
+ (NODE, -GET_NON_DEFAULT_TEMPLATE_ARGS_COUNT (NODE))
+
/* The list of access checks that were deferred during parsing
which need to be performed at template instantiation time.
@@ -5938,7 +5946,8 @@ 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: show the template like a primary template. */
#define TFF_PLAIN_IDENTIFIER (0)
#define TFF_SCOPE (1)
@@ -5956,6 +5965,7 @@ enum auto_deduction_context
#define TFF_NO_OMIT_DEFAULT_TEMPLATE_ARGUMENTS (1 << 12)
#define TFF_NO_TEMPLATE_BINDINGS (1 << 13)
#define TFF_POINTER (1 << 14)
+#define TFF_AS_PRIMARY (1 << 15)
/* These constants can be used as bit flags to control strip_typedefs.
diff --git a/gcc/cp/error.c b/gcc/cp/error.c
index 012a4ecddf4..86e9d12103a 100644
--- a/gcc/cp/error.c
+++ b/gcc/cp/error.c
@@ -87,7 +87,7 @@ static void dump_template_argument (cxx_pretty_printer *, tree, int);
static void dump_template_argument_list (cxx_pretty_printer *, tree, int);
static void dump_template_parameter (cxx_pretty_printer *, tree, int);
static void dump_template_bindings (cxx_pretty_printer *, tree, tree,
- vec<tree, va_gc> *);
+ vec<tree, va_gc> *, int);
static void dump_scope (cxx_pretty_printer *, tree, int);
static void dump_template_parms (cxx_pretty_printer *, tree, int, int);
static int get_non_default_template_args_count (tree, int);
@@ -278,7 +278,8 @@ dump_template_argument (cxx_pretty_printer *pp, tree arg, int flags)
static int
get_non_default_template_args_count (tree args, int flags)
{
- int n = TREE_VEC_LENGTH (INNERMOST_TEMPLATE_ARGS (args));
+ args = INNERMOST_TEMPLATE_ARGS (args);
+ int n = TREE_VEC_LENGTH (args);
if (/* We use this flag when generating debug information. We don't
want to expand templates at this point, for this may generate
@@ -286,10 +287,10 @@ get_non_default_template_args_count (tree args, int flags)
turn cause codegen differences between compilations with and
without -g. */
(flags & TFF_NO_OMIT_DEFAULT_TEMPLATE_ARGUMENTS) != 0
- || !flag_pretty_templates)
+ || !NON_DEFAULT_TEMPLATE_ARGS_COUNT (args))
return n;
- return GET_NON_DEFAULT_TEMPLATE_ARGS_COUNT (INNERMOST_TEMPLATE_ARGS (args));
+ return GET_NON_DEFAULT_TEMPLATE_ARGS_COUNT (args);
}
/* Dump a template-argument-list ARGS (always a TREE_VEC) under control
@@ -369,7 +370,7 @@ dump_template_parameter (cxx_pretty_printer *pp, tree parm, int flags)
static void
dump_template_bindings (cxx_pretty_printer *pp, tree parms, tree args,
- vec<tree, va_gc> *typenames)
+ vec<tree, va_gc> *typenames, int flags)
{
/* Print "[with" and ']', conditional on whether anything is printed at all.
This is tied to whether a semicolon is needed to separate multiple template
@@ -411,11 +412,16 @@ dump_template_bindings (cxx_pretty_printer *pp, tree parms, tree args,
int i;
tree lvl_args = NULL_TREE;
+ int len = TREE_VEC_LENGTH (p);
/* Don't crash if we had an invalid argument list. */
if (TMPL_ARGS_DEPTH (args) >= lvl)
- lvl_args = TMPL_ARGS_LEVEL (args, lvl);
+ {
+ lvl_args = TMPL_ARGS_LEVEL (args, lvl);
+ len = MIN (len,
+ get_non_default_template_args_count (lvl_args, flags));
+ }
- for (i = 0; i < TREE_VEC_LENGTH (p); ++i)
+ for (i = 0; i < len; ++i)
{
tree arg = NULL_TREE;
@@ -1635,7 +1641,8 @@ dump_substitution (cxx_pretty_printer *pp,
&& !(flags & TFF_NO_TEMPLATE_BINDINGS))
{
vec<tree, va_gc> *typenames = t ? find_typenames (t) : NULL;
- dump_template_bindings (pp, template_parms, template_args, typenames);
+ dump_template_bindings (pp, template_parms, template_args, typenames,
+ flags);
}
}
@@ -1688,8 +1695,15 @@ 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);
- /* Pretty print template instantiations only. */
- if (DECL_USE_TEMPLATE (t) && DECL_TEMPLATE_INFO (t)
+ /* Keep t before the following branch makes t point to a more general
+ template. Without the specialized template, the information about defaulted
+ template arguments is lost. */
+ tree specialized_t = t;
+ int specialized_flags = 0;
+
+ /* Pretty print only template instantiations. Don't pretty print explicit
+ specializations like 'template <> void fun<int> (int)'. */
+ if (DECL_TEMPLATE_INSTANTIATION (t) && DECL_TEMPLATE_INFO (t)
&& !(flags & TFF_NO_TEMPLATE_BINDINGS)
&& flag_pretty_templates)
{
@@ -1701,6 +1715,9 @@ dump_function_decl (cxx_pretty_printer *pp, tree t, int flags)
{
template_parms = DECL_TEMPLATE_PARMS (tmpl);
t = tmpl;
+ /* The "[with ...]" clause is printed, thus dump functions printing
+ SPECIALIZED_T need to add TFF_AS_PRIMARY to their flags. */
+ specialized_flags = TFF_AS_PRIMARY;
}
}
@@ -1710,8 +1727,8 @@ dump_function_decl (cxx_pretty_printer *pp, tree t, int flags)
fntype = TREE_TYPE (t);
parmtypes = FUNCTION_FIRST_USER_PARMTYPE (t);
- if (DECL_CLASS_SCOPE_P (t))
- cname = DECL_CONTEXT (t);
+ if (DECL_CLASS_SCOPE_P (specialized_t))
+ cname = DECL_CONTEXT (specialized_t);
/* This is for partially instantiated template methods. */
else if (TREE_CODE (fntype) == METHOD_TYPE)
cname = TREE_TYPE (TREE_VALUE (parmtypes));
@@ -1749,13 +1766,14 @@ dump_function_decl (cxx_pretty_printer *pp, tree t, int flags)
/* Nothing. */;
else if (cname)
{
- dump_type (pp, cname, flags);
+ dump_type (pp, cname, flags | specialized_flags);
pp_cxx_colon_colon (pp);
}
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 | specialized_flags);
if (!(flags & TFF_NO_FUNCTION_ARGUMENTS))
{
@@ -1968,13 +1986,35 @@ dump_function_name (cxx_pretty_printer *pp, tree t, int flags)
dump_module_suffix (pp, t);
+/* Print function template parameters if:
+ 1. t is template, and
+ 2. flags did not request "show only template-name", and
+ 3. t is a specialization of a template (Why is this needed? This was present
+ since 1999 via !DECL_FRIEND_PSEUDO_TEMPLATE_INSTANTIATION: "Don't crash if
+ given a friend pseudo-instantiation". The DECL_USE_TEMPLATE should likely
+ inform the PRIMARY parameter of dump_template_parms.), and
+ 4. either
+ - flags requests to show no function arguments, in which case deduced
+ types could be hidden, or
+ - at least one function template argument was given explicitly, or
+ - we're printing a DWARF name,
+ and
+ 5. either
+ - t is a member friend template of a template class (see DECL_TI_TEMPLATE
+ documentation), or
+ -
+ */
if (DECL_TEMPLATE_INFO (t)
&& !(flags & TFF_TEMPLATE_NAME)
- && !DECL_FRIEND_PSEUDO_TEMPLATE_INSTANTIATION (t)
+ && DECL_USE_TEMPLATE (t)
+ && ((flags & TFF_NO_FUNCTION_ARGUMENTS)
+ || (DECL_TI_ARGS (t)
+ && EXPLICIT_TEMPLATE_ARGS_P (INNERMOST_TEMPLATE_ARGS
+ (DECL_TI_ARGS (t))))
+ || (pp->flags & pp_c_flag_gnu_v3) != 0)
&& (TREE_CODE (DECL_TI_TEMPLATE (t)) != TEMPLATE_DECL
|| PRIMARY_TEMPLATE_P (DECL_TI_TEMPLATE (t))))
- dump_template_parms (pp, DECL_TEMPLATE_INFO (t), !DECL_USE_TEMPLATE (t),
- flags);
+ dump_template_parms (pp, DECL_TEMPLATE_INFO (t), false, flags);
}
/* Dump the template parameters from the template info INFO under control of
@@ -1989,6 +2029,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);
@@ -1998,10 +2040,11 @@ dump_template_parms (cxx_pretty_printer *pp, tree info,
to crash producing error messages. */
if (args && !primary)
{
- int len, ix;
- len = get_non_default_template_args_count (args, flags);
+ int ix;
args = INNERMOST_TEMPLATE_ARGS (args);
+ const int len = MIN (NUM_TMPL_ARGS (args),
+ get_non_default_template_args_count (args, flags));
for (ix = 0; ix != len; ix++)
{
tree arg = TREE_VEC_ELT (args, ix);
@@ -2028,6 +2071,8 @@ dump_template_parms (cxx_pretty_printer *pp, tree info,
parms = TREE_CODE (parms) == TREE_LIST ? TREE_VALUE (parms) : NULL_TREE;
len = parms ? TREE_VEC_LENGTH (parms) : 0;
+ if (args)
+ len = MIN (len, get_non_default_template_args_count (args, flags));
for (ix = 0; ix != len; ix++)
{
diff --git a/gcc/cp/pt.c b/gcc/cp/pt.c
index 66040035b2f..800249f0933 100644
--- a/gcc/cp/pt.c
+++ b/gcc/cp/pt.c
@@ -2504,7 +2504,7 @@ determine_specialization (tree template_id,
if (candidates)
{
tree fn = TREE_VALUE (candidates);
- *targs_out = copy_node (DECL_TI_ARGS (fn));
+ *targs_out = copy_template_args (DECL_TI_ARGS (fn));
/* Propagate the candidate's constraints to the declaration. */
if (tsk != tsk_template)
@@ -4828,11 +4828,16 @@ template_parms_level_to_args (tree parms)
{
tree a = copy_node (parms);
TREE_TYPE (a) = NULL_TREE;
+ int nondefault = 0;
for (int i = TREE_VEC_LENGTH (a) - 1; i >= 0; --i)
- TREE_VEC_ELT (a, i) = template_parm_to_arg (TREE_VEC_ELT (a, i));
+ {
+ tree elt = TREE_VEC_ELT (a, i);
+ TREE_VEC_ELT (a, i) = template_parm_to_arg (elt);
+ if (!elt || elt == error_mark_node || !TREE_PURPOSE (elt))
+ ++nondefault;
+ }
- if (CHECKING_P)
- SET_NON_DEFAULT_TEMPLATE_ARGS_COUNT (a, TREE_VEC_LENGTH (a));
+ SET_NON_DEFAULT_TEMPLATE_ARGS_COUNT (a, nondefault);
return a;
}
@@ -13332,8 +13337,9 @@ copy_template_args (tree t)
TREE_VEC_ELT (new_vec, i) = elt;
}
- NON_DEFAULT_TEMPLATE_ARGS_COUNT (new_vec)
- = NON_DEFAULT_TEMPLATE_ARGS_COUNT (t);
+ if (!TMPL_ARGS_HAVE_MULTIPLE_LEVELS (t))
+ NON_DEFAULT_TEMPLATE_ARGS_COUNT (new_vec)
+ = NON_DEFAULT_TEMPLATE_ARGS_COUNT (t);
return new_vec;
}
@@ -13433,7 +13439,13 @@ tsubst_template_args (tree t, tree args, tsubst_flags_t complain, tree in_decl)
if it doesn't contain any nested TREE_VEC. */
if (NON_DEFAULT_TEMPLATE_ARGS_COUNT (orig_t))
{
- int count = GET_NON_DEFAULT_TEMPLATE_ARGS_COUNT (orig_t);
+ /* If ARGS defines a number for the defaulted argument count then that's
+ the correct number to propagate. Otherwise, assume the number of
+ defaulted arguments after substitution equals the number of default
+ arguments before substitution (i.e. ORIG_T). */
+ int count = args && NON_DEFAULT_TEMPLATE_ARGS_COUNT (args)
+ ? GET_NON_DEFAULT_TEMPLATE_ARGS_COUNT (args)
+ : GET_NON_DEFAULT_TEMPLATE_ARGS_COUNT (orig_t);
count += expanded_len_adjust;
SET_NON_DEFAULT_TEMPLATE_ARGS_COUNT (t, count);
}
@@ -21874,6 +21886,12 @@ fn_type_unification (tree fn,
excessive_deduction_depth = false;
}
+ /* If all template parameters were explicitly given, treat them like default
+ template arguments for diagnostics. NON_DEFAULT_TEMPLATE_ARGS_COUNT must be
+ present for SET_EXPLICIT_TEMPLATE_ARGS_P. */
+ if (explicit_targs && NON_DEFAULT_TEMPLATE_ARGS_COUNT (targs))
+ SET_EXPLICIT_TEMPLATE_ARGS_P (targs);
+
return r;
}
@@ -22516,9 +22534,17 @@ type_unification_real (tree tparms,
be NULL_TREE or ERROR_MARK_NODE, so we do not need
to explicitly check cxx_dialect here. */
if (TREE_PURPOSE (TREE_VEC_ELT (tparms, i)))
- /* OK, there is a default argument. Wait until after the
- conversion check to do substitution. */
- continue;
+ {
+ /* The position of the first default template argument,
+ is also the number of non-defaulted arguments in TARGS.
+ Record that. */
+ if (!NON_DEFAULT_TEMPLATE_ARGS_COUNT (targs))
+ SET_NON_DEFAULT_TEMPLATE_ARGS_COUNT (targs, i);
+
+ /* OK, there is a default argument. Wait until after the
+ conversion check to do substitution. */
+ continue;
+ }
/* If the type parameter is a parameter pack, then it will
be deduced to an empty parameter pack. */
@@ -22621,14 +22647,7 @@ type_unification_real (tree tparms,
if (arg == error_mark_node)
return 1;
else if (arg)
- {
- TREE_VEC_ELT (targs, i) = arg;
- /* The position of the first default template argument,
- is also the number of non-defaulted arguments in TARGS.
- Record that. */
- if (!NON_DEFAULT_TEMPLATE_ARGS_COUNT (targs))
- SET_NON_DEFAULT_TEMPLATE_ARGS_COUNT (targs, i);
- }
+ TREE_VEC_ELT (targs, i) = arg;
}
if (saw_undeduced++ == 1)
@@ -24998,6 +25017,9 @@ get_partial_spec_bindings (tree tmpl, tree spec_tmpl, tree args)
if (!template_template_parm_bindings_ok_p (tparms, deduced_args))
return NULL_TREE;
+ if (CHECKING_P)
+ SET_NON_DEFAULT_TEMPLATE_ARGS_COUNT (deduced_args, ntparms);
+
return deduced_args;
}
diff --git a/gcc/testsuite/g++.dg/debug/dwarf2/template-params-12n.C b/gcc/testsuite/g++.dg/debug/dwarf2/template-params-12n.C
index d3c1f589f87..b88bf7d4b34 100644
--- a/gcc/testsuite/g++.dg/debug/dwarf2/template-params-12n.C
+++ b/gcc/testsuite/g++.dg/debug/dwarf2/template-params-12n.C
@@ -1,6 +1,6 @@
// { dg-options "-gdwarf-2 -dA" }
// { dg-final { scan-assembler-times "DIE \\(\[^\n\]*\\) DW_TAG_template_value_param" 1 } }
-// { dg-final { scan-assembler-times "DIE \\(\[^\n\]*\\) DW_TAG_template_value_param\[^\n\]*\n\[^\n\]* DW_AT_name\n\[^\n\]* DW_AT_type\n\[^\n\]* DW_AT_const_value" 1 } }
+// { dg-final { scan-assembler-times "DIE \\(\[^\n\]*\\) DW_TAG_template_value_param\[^\n\]*\n\[^\n\]* DW_AT_name\n\[^\n\]* DW_AT_type\n(?:\[^\n\]* DW_AT_default_value\n)?\[^\n\]* DW_AT_const_value" 1 } }
#include "template-params-12.H"
/* We get const_value for NULL pointers to member functions. */
#if __cplusplus > 199711L // Ugh, C++98 barfs at both the cast and the overload.
diff --git a/gcc/testsuite/g++.dg/diagnostic/default-template-args-1.C b/gcc/testsuite/g++.dg/diagnostic/default-template-args-1.C
new file mode 100644
index 00000000000..7a535515740
--- /dev/null
+++ b/gcc/testsuite/g++.dg/diagnostic/default-template-args-1.C
@@ -0,0 +1,73 @@
+// { dg-options "-fpretty-templates" }
+// { dg-do compile { target c++11 } }
+
+template <int a = 1>
+ [[deprecated]] void f0(); // { dg-message "'template<int a> void f0\\(\\)'" }
+
+template <int a>
+ [[deprecated]] void f1(); // { dg-message "'template<int a> void f1\\(\\)'" }
+
+template <class a, int b = 1>
+ [[deprecated]] void f2(); // { dg-message "'template<class a, int b> void f2\\(\\)'" }
+
+template <class a, int b = 1>
+ [[deprecated]] void f3(a); // { dg-message "'template<class a, int b> void f3\\(a\\)'" }
+
+template <class a>
+ [[deprecated]] void f4(a); // { dg-message "'template<class a> void f4\\(a\\)'" }
+
+template <>
+ [[deprecated]] void f4<int>(int);
+
+template <>
+ [[deprecated]] void f4(float);
+
+template <class a, class b = int>
+ [[deprecated]] void f5(a);
+
+template void f5<float>(float); // { dg-error "'void f5<a>\\(a\\) .with a = float.'" }
+
+template <class a, class b = int>
+ struct c
+ {
+ template <class d, class e = int>
+ [[deprecated]] static void f(d);
+ };
+
+template <class T>
+struct B { typedef T X; };
+
+template <class U>
+struct D
+{
+ template <class V = typename B<U>::X>
+ [[deprecated]] static void foo (typename B<V>::X);
+};
+
+int main()
+{
+ f0(); // { dg-warning "'void f0\\(\\)'" }
+ f1<1>(); // { dg-warning "'void f1<a>\\(\\) .with int a = 1.'" }
+ f2<int>(); // { dg-warning "'void f2<a>\\(\\) .with a = int.'" }
+ f3(1); // { dg-warning "'void f3\\(a\\) .with a = int.'" }
+ f3<float>(1); // { dg-warning "'void f3<a>\\(a\\) .with a = float.'" }
+ f3<float, 2>(1); // { dg-warning "'void f3<a, b>\\(a\\) .with a = float; int b = 2.'" }
+ f4(1.); // { dg-warning "'void f4\\(a\\) .with a = double.'" }
+ f4(1); // { dg-warning "'void f4<int>\\(int\\)'" }
+ f4(1.f); // { dg-warning "'void f4\\(float\\)'" }
+
+ f0(0); // { dg-error "" }
+ f1(0); // { dg-error "" }
+ f2(0); // { dg-error "" }
+ f3(); // { dg-error "" }
+ f4(); // { dg-error "" }
+
+ c<int>::f(1.); // { dg-warning "'static void c<a>::f\\(d\\) .with d = double; a = int.'" }
+ c<int>::f<int>(1); // { dg-warning "'static void c<a>::f<d>\\(d\\) .with d = int; a = int.'" }
+ c<int>::f<float, int>(1.f); // { dg-warning "'static void c<a>::f<d, e>\\(d\\) .with d = float; e = int; a = int.'" }
+ 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.'" }
+
+ D<int>::foo(1); // { dg-warning "'static void D<U>::foo\\(typename B<V>::X\\) .with U = int; typename B<V>::X = int.'" }
+}
diff --git a/gcc/testsuite/g++.dg/diagnostic/default-template-args-2.C b/gcc/testsuite/g++.dg/diagnostic/default-template-args-2.C
new file mode 100644
index 00000000000..a82709f5785
--- /dev/null
+++ b/gcc/testsuite/g++.dg/diagnostic/default-template-args-2.C
@@ -0,0 +1,37 @@
+// { dg-options "-fno-pretty-templates" }
+// { dg-do compile { target c++11 } }
+
+template <int a = 1>
+ [[deprecated]] void f0();
+
+template <int a>
+ [[deprecated]] void f1();
+
+template <class a, int b = 1>
+ [[deprecated]] void f2();
+
+template <class a, int b = 1>
+ [[deprecated]] void f3(a);
+
+template <class a>
+ [[deprecated]] void f4(a);
+
+template <>
+ [[deprecated]] void f4<int>(int);
+
+template <class a, class b = int>
+ [[deprecated]] void f5(a);
+
+template void f5<float>(float); // { dg-error "'void f5<float>\\(float\\)'" }
+
+int main()
+{
+ f0(); // { dg-warning "'void f0\\(\\)'" }
+ f1<1>(); // { dg-warning "'void f1<1>\\(\\)'" }
+ f2<int>(); // { dg-warning "'void f2<int>\\(\\)'" }
+ f3(1); // { dg-warning "'void f3\\(int\\)'" }
+ f3<float>(1); // { dg-warning "'void f3<float>\\(float\\)'" }
+ f3<float, 2>(1); // { dg-warning "'void f3<float, 2>\\(float\\)'" }
+ f4(1.); // { dg-warning "'void f4\\(double\\)'" }
+ f4(1); // { dg-warning "'void f4<int>\\(int\\)'" }
+}
diff --git a/gcc/testsuite/g++.dg/diagnostic/param-type-mismatch-2.C b/gcc/testsuite/g++.dg/diagnostic/param-type-mismatch-2.C
index de7570a6efa..0aa45404283 100644
--- a/gcc/testsuite/g++.dg/diagnostic/param-type-mismatch-2.C
+++ b/gcc/testsuite/g++.dg/diagnostic/param-type-mismatch-2.C
@@ -141,7 +141,7 @@ int test_7 (int first, const char *second, float third)
|
const char*
{ dg-end-multiline-output "" } */
- // { dg-message "initializing argument 2 of 'int test_7\\(int, T, float\\) .with T = const char\\*\\*.'" "" { target *-*-* } test_7_decl }
+ // { dg-message "initializing argument 2 of 'int test_7<T>\\(int, T, float\\) .with T = const char\\*\\*.'" "" { target *-*-* } test_7_decl }
/* { dg-begin-multiline-output "" }
int test_7 (int one, T two, float three);
~~^~~
diff --git a/gcc/testsuite/g++.dg/ext/pretty1.C b/gcc/testsuite/g++.dg/ext/pretty1.C
index 06608ae30eb..c5bfd6082a7 100644
--- a/gcc/testsuite/g++.dg/ext/pretty1.C
+++ b/gcc/testsuite/g++.dg/ext/pretty1.C
@@ -60,7 +60,7 @@ __assert_fail (const char *cond, const char *file, unsigned int line,
abort ();
}
-// { dg-final { scan-assembler "int bar\\(T\\).*with T = int" } }
+// { dg-final { scan-assembler "int bar<int>\\(int\\)" } }
// { dg-final { scan-assembler "top level" } }
// { dg-final { scan-assembler "int main\\(\\)" } }
// { dg-final { scan-assembler "int bar\\(T\\).*with T = double" } }
diff --git a/gcc/testsuite/g++.dg/goacc/template.C b/gcc/testsuite/g++.dg/goacc/template.C
index 10d3f446da7..4fcd88bfc56 100644
--- a/gcc/testsuite/g++.dg/goacc/template.C
+++ b/gcc/testsuite/g++.dg/goacc/template.C
@@ -157,12 +157,12 @@ main ()
}
/* { dg-final { scan-tree-dump-times {(?n)^OpenACC routine '[^']+' has 'nohost' clause\.$} 4 oaccloops } }
- { dg-final { scan-tree-dump-times {(?n)^OpenACC routine 'T accDouble\(int\) \[with T = char\]' has 'nohost' clause\.$} 1 oaccloops { target { ! offloading_enabled } } } }
+ { dg-final { scan-tree-dump-times {(?n)^OpenACC routine 'T accDouble<T>\(int\) \[with T = char\]' has 'nohost' clause\.$} 1 oaccloops { target { ! offloading_enabled } } } }
{ dg-final { scan-tree-dump-times {(?n)^OpenACC routine 'accDouble<char>\(int\)char' has 'nohost' clause\.$} 1 oaccloops { target offloading_enabled } } }
- { dg-final { scan-tree-dump-times {(?n)^OpenACC routine 'T accDouble\(int\) \[with T = int\]' has 'nohost' clause\.$} 1 oaccloops { target { ! offloading_enabled } } } }
+ { dg-final { scan-tree-dump-times {(?n)^OpenACC routine 'T accDouble<T>\(int\) \[with T = int\]' has 'nohost' clause\.$} 1 oaccloops { target { ! offloading_enabled } } } }
{ dg-final { scan-tree-dump-times {(?n)^OpenACC routine 'accDouble<int>\(int\)int' has 'nohost' clause\.$} 1 oaccloops { target offloading_enabled } } }
- { dg-final { scan-tree-dump-times {(?n)^OpenACC routine 'T accDouble\(int\) \[with T = float\]' has 'nohost' clause\.$} 1 oaccloops { target { ! offloading_enabled } } } }
+ { dg-final { scan-tree-dump-times {(?n)^OpenACC routine 'T accDouble<T>\(int\) \[with T = float\]' has 'nohost' clause\.$} 1 oaccloops { target { ! offloading_enabled } } } }
{ dg-final { scan-tree-dump-times {(?n)^OpenACC routine 'accDouble<float>\(int\)float' has 'nohost' clause\.$} 1 oaccloops { target offloading_enabled } } }
- { dg-final { scan-tree-dump-times {(?n)^OpenACC routine 'T accDouble\(int\) \[with T = double\]' has 'nohost' clause\.$} 1 oaccloops { target { ! offloading_enabled } } } }
+ { dg-final { scan-tree-dump-times {(?n)^OpenACC routine 'T accDouble<T>\(int\) \[with T = double\]' has 'nohost' clause\.$} 1 oaccloops { target { ! offloading_enabled } } } }
{ dg-final { scan-tree-dump-times {(?n)^OpenACC routine 'accDouble<double>\(int\)double' has 'nohost' clause\.$} 1 oaccloops { target offloading_enabled } } }
TODO See PR101551 for 'offloading_enabled' differences. */
diff --git a/gcc/testsuite/g++.dg/gomp/declare-variant-7.C b/gcc/testsuite/g++.dg/gomp/declare-variant-7.C
index 7dda899578a..9cb654cb5e6 100644
--- a/gcc/testsuite/g++.dg/gomp/declare-variant-7.C
+++ b/gcc/testsuite/g++.dg/gomp/declare-variant-7.C
@@ -70,6 +70,6 @@ test ()
s.f12 (0.0); // { dg-final { scan-tree-dump-times "S<1>::f11<double> \\\(&s, 0.0\\\);" 1 "gimple" } }
s.f14 (0LL); // { dg-final { scan-tree-dump-times "S<1>::f13<long long int> \\\(&s, 0\\\);" 1 "gimple" } }
T<0> t;
- t.f16 (s); // { dg-final { scan-tree-dump-times "T<0>::f16<S<1> > \\\(&t, s\\\);" 1 "gimple" } }
- t.f18 (s); // { dg-final { scan-tree-dump-times "T<0>::f18<S<1> > \\\(&t, s\\\);" 1 "gimple" } }
+ t.f16 (s); // { dg-final { scan-tree-dump-times "T<0>::f16 \\\(&t, s\\\);" 1 "gimple" } }
+ t.f18 (s); // { dg-final { scan-tree-dump-times "T<0>::f18 \\\(&t, s\\\);" 1 "gimple" } }
}
diff --git a/gcc/testsuite/g++.dg/template/error40.C b/gcc/testsuite/g++.dg/template/error40.C
index c5df56fc1be..16a44d1819f 100644
--- a/gcc/testsuite/g++.dg/template/error40.C
+++ b/gcc/testsuite/g++.dg/template/error40.C
@@ -8,11 +8,11 @@ struct A
void foo(void)
{
- A<void> a = 0; // { dg-error "A<void, 0, 1>" }
+ A<void> a = 0; // { dg-error "A<void>" }
}
-template <class T> T f(T); // { dg-message "int f<int>.int." }
-template <class T> T f(T, int = 0); // { dg-message "" }
+template <class T> T f(T); // { dg-message "int f.int." }
+template <class T> T f(T, int = 0); // { dg-message "int f.int, int." }
template <class T>
struct B
diff --git a/gcc/testsuite/g++.old-deja/g++.ext/pretty3.C b/gcc/testsuite/g++.old-deja/g++.ext/pretty3.C
index 6348ae1ee67..30c7ecd5065 100644
--- a/gcc/testsuite/g++.old-deja/g++.ext/pretty3.C
+++ b/gcc/testsuite/g++.old-deja/g++.ext/pretty3.C
@@ -35,7 +35,7 @@ template<> void f1<int> (int)
if (strcmp (function, "f1<int>"))
bad = true;
- if (strcmp (pretty, "void f1(T) [with T = int]"))
+ if (strcmp (pretty, "void f1<int>(int)"))
bad = true;
}
diff --git a/gcc/testsuite/g++.old-deja/g++.pt/memtemp77.C b/gcc/testsuite/g++.old-deja/g++.pt/memtemp77.C
index 6dd4b546c0c..93dbf4b489f 100644
--- a/gcc/testsuite/g++.old-deja/g++.pt/memtemp77.C
+++ b/gcc/testsuite/g++.old-deja/g++.pt/memtemp77.C
@@ -19,7 +19,7 @@ const char* S3<char>::h(int) { return __PRETTY_FUNCTION__; }
int main()
{
if (strcmp (S3<double>::h(7),
- "static const char* S3<T>::h(U) [with U = int; T = double]") == 0)
+ "static const char* S3<double>::h(int)") == 0)
return 0;
else
return 1;
^ permalink raw reply [flat|nested] 24+ messages in thread
* Re: [RFC] c++: Print function template parms when relevant (was: [PATCH v4] c++: Add gnu::diagnose_as attribute)
2021-11-08 16:40 ` [RFC] c++: Print function template parms when relevant (was: [PATCH v4] c++: Add gnu::diagnose_as attribute) Matthias Kretz
@ 2021-11-08 20:00 ` Matthias Kretz
2021-11-16 20:25 ` Jason Merrill
2021-11-17 6:09 ` Jason Merrill
1 sibling, 1 reply; 24+ messages in thread
From: Matthias Kretz @ 2021-11-08 20:00 UTC (permalink / raw)
To: gcc-patches, Jason Merrill
I forgot to mention why I tagged it [RFC]: I needed one more bit of
information on the template args TREE_VEC to encode EXPLICIT_TEMPLATE_ARGS_P.
Its TREE_CHAIN already points to an integer constant denoting the number of
non-default arguments, so I couldn't trivially replace that. Therefore, I used
the sign of that integer. I was hoping to find a cleaner solution, though.
-Matthias
On Monday, 8 November 2021 17:40:44 CET Matthias Kretz wrote:
> On Tuesday, 17 August 2021 20:31:54 CET Jason Merrill wrote:
> > > 2. Given a DECL_TI_ARGS tree, can I query whether an argument was
> > > deduced
> > > or explicitly specified? I'm asking because I still consider diagnostics
> > > of function templates unfortunate. `template <class T> void f()` is
> > > fine,
> > > as is `void f(T) [with T = float]`, but `void f() [with T = float]`
> > > could
> > > be better. I.e. if the template parameter appears somewhere in the
> > > function parameter list, dump_template_parms would only produce noise.
> > > If, however, the template parameter was given explicitly, it would be
> > > nice if it could show up accordingly in diagnostics.
> >
> > NON_DEFAULT_TEMPLATE_ARGS_COUNT has that information, though there are
> > some issues with it. Attached is my WIP from May to improve it
> > somewhat, if that's interesting.
>
> It is interesting. I used your patch to come up with the attached. Patch. I
> must say, I didn't try to read through all the cp/pt.c code to understand
> all of what you did there (which is why my ChangeLog entry says "Jason?"),
> but it works for me (and all of `make check`).
>
> Anyway, I'd like to propose the following before finishing my diagnose_as
> patch. I believe it's useful to fix this part first. The diagnostic/default-
> template-args-[12].C tests show a lot of examples of the intent of this
> patch. And the remaining changes to the testsuite show how it changes
> diagnostic output.
>
> ---------------------- 8< --------------------
>
> The choice when to print a function template parameter was still
> suboptimal. That's because sometimes the function template parameter
> list only adds noise, while in other situations the lack of a function
> template parameter list makes diagnostic messages hard to understand.
>
> The general idea of this change is to print template parms wherever they
> would appear in the source code as well. Thus, the diagnostics code
> needs to know whether any template parameter was given explicitly.
>
> Signed-off-by: Matthias Kretz <m.kretz@gsi.de>
>
> gcc/testsuite/ChangeLog:
>
> * g++.dg/debug/dwarf2/template-params-12n.C: Optionally, allow
> DW_AT_default_value.
> * g++.dg/diagnostic/default-template-args-1.C: New.
> * g++.dg/diagnostic/default-template-args-2.C: New.
> * g++.dg/diagnostic/param-type-mismatch-2.C: Expect template
> parms in diagnostic.
> * g++.dg/ext/pretty1.C: Expect function template specialization
> to not pretty-print template parms.
> * g++.old-deja/g++.ext/pretty3.C: Ditto.
> * g++.old-deja/g++.pt/memtemp77.C: Ditto.
> * g++.dg/goacc/template.C: Expect function template parms for
> explicit arguments.
> * g++.dg/gomp/declare-variant-7.C: Expect no function template
> parms for deduced arguments.
> * g++.dg/template/error40.C: Expect only non-default template
> arguments in diagnostic.
>
> gcc/cp/ChangeLog:
>
> * cp-tree.h (GET_NON_DEFAULT_TEMPLATE_ARGS_COUNT): Return
> absolute value of stored constant.
> (EXPLICIT_TEMPLATE_ARGS_P): New.
> (SET_EXPLICIT_TEMPLATE_ARGS_P): New.
> (TFF_AS_PRIMARY): New constant.
> * error.c (get_non_default_template_args_count): Avoid
> GET_NON_DEFAULT_TEMPLATE_ARGS_COUNT if
> NON_DEFAULT_TEMPLATE_ARGS_COUNT is a NULL_TREE. Make independent
> of flag_pretty_templates.
> (dump_template_bindings): Add flags parameter to be passed to
> get_non_default_template_args_count. Print only non-default
> template arguments.
> (dump_function_decl): Call dump_function_name and dump_type of
> the DECL_CONTEXT with specialized template and set
> TFF_AS_PRIMARY for their flags.
> (dump_function_name): Add and document conditions for calling
> dump_template_parms.
> (dump_template_parms): Print only non-default template
> parameters.
> * pt.c (determine_specialization): Jason?
> (template_parms_level_to_args): Jason?
> (copy_template_args): Jason?
> (fn_type_unification): Set EXPLICIT_TEMPLATE_ARGS_P on the
> template arguments tree if any template parameter was explicitly
> given.
> (type_unification_real): Jason?
> (get_partial_spec_bindings): Jason?
> (tsubst_template_args): Determine number of defaulted arguments
> from new argument vector, if possible.
> ---
> gcc/cp/cp-tree.h | 18 +++-
> gcc/cp/error.c | 83 ++++++++++++++-----
> gcc/cp/pt.c | 58 +++++++++----
> .../g++.dg/debug/dwarf2/template-params-12n.C | 2 +-
> .../diagnostic/default-template-args-1.C | 73 ++++++++++++++++
> .../diagnostic/default-template-args-2.C | 37 +++++++++
> .../g++.dg/diagnostic/param-type-mismatch-2.C | 2 +-
> gcc/testsuite/g++.dg/ext/pretty1.C | 2 +-
> gcc/testsuite/g++.dg/goacc/template.C | 8 +-
> gcc/testsuite/g++.dg/gomp/declare-variant-7.C | 4 +-
> gcc/testsuite/g++.dg/template/error40.C | 6 +-
> gcc/testsuite/g++.old-deja/g++.ext/pretty3.C | 2 +-
> gcc/testsuite/g++.old-deja/g++.pt/memtemp77.C | 2 +-
> 13 files changed, 242 insertions(+), 55 deletions(-)
> create mode 100644
> gcc/testsuite/g++.dg/diagnostic/default-template-args-1.C create mode
> 100644 gcc/testsuite/g++.dg/diagnostic/default-template-args-2.C
--
──────────────────────────────────────────────────────────────────────────
Dr. Matthias Kretz https://mattkretz.github.io
GSI Helmholtz Centre for Heavy Ion Research https://gsi.de
stdₓ::simd
──────────────────────────────────────────────────────────────────────────
^ permalink raw reply [flat|nested] 24+ messages in thread
* [PATCH v5] c++: Add gnu::diagnose_as attribute
2021-09-08 2:21 ` [PATCH v4] c++: Add gnu::diagnose_as attribute Jason Merrill
@ 2021-11-15 0:35 ` Matthias Kretz
0 siblings, 0 replies; 24+ messages in thread
From: Matthias Kretz @ 2021-11-15 0:35 UTC (permalink / raw)
To: gcc-patches, Jason Merrill
[-- Attachment #1: Type: text/plain, Size: 17825 bytes --]
Sorry for taking so long. I hope we can still get this done for GCC 12.
One open question: If we change std::__cxx11::basic_string<char> to
std::string with this feature, should DWARF strings change or not? I.e. should
diagnose_as be conditional on (pp->flags & pp_c_flag_gnu_v3)? If these strings
are only for user consumption, I think the DWARF strings should be affected by
the attribute...
Oh, and note that the current patch depends on the "c++: Print function
template parms when relevant" patch I sent on Nov 8th.
On Wednesday, 8 September 2021 04:21:51 CEST Jason Merrill wrote:
> On 7/23/21 4:58 AM, Matthias Kretz wrote:
> > gcc/cp/ChangeLog:
> > PR c++/89370
> > * cp-tree.h: Add is_alias_template_p declaration.
> > * decl2.c (is_alias_template_p): New function. Determines
> > whether a given TYPE_DECL is actually an alias template that is
> > still missing its template_info.
>
> I still think you want to share code with get_underlying_template. For
> the case where the alias doesn't have DECL_TEMPLATE_INFO yet, you can
> compare to current_template_args (). Or you could do some initial
> processing that doesn't care about templates in the handler, and then do
> more in cp_parser_alias_declaration after the call to grokfield/start_decl.
I still don't understand how I could make use of get_underlying_template. I.e.
I don't even understand how get_underlying_template answers any of the
questions I need answered. I used way too much time trying to make this
work...
> If you still think you need this function, let's call it
> is_renaming_alias_template or renaming_alias_template_p; using both is_
> and _p is redundant. I don't have a strong preference which.
OK.
> > (is_late_template_attribute): Decls with diagnose_as attribute
> > are early attributes only if they are alias templates.
>
> Is there a reason not to apply it early to other templates as well?
Unconditionally returning false for diagnose_as in is_late_template_attribute
makes renamed class templates print without template parameter list. E.g.
template <class T> struct [[diagnose_as("foo")]] A;
using bar [[diagnose_as]] = A<int>;
template <class T> struct A {
template <class U> struct B {};
using C [[diagnose_as]] = B<int>;
};
could query for attributes. So IIUC, member types of class templates require
late attributes.
> > * error.c (dump_scope): When printing the name of a namespace,
> > look for the diagnose_as attribute. If found, print the
> > associated string instead of calling dump_decl.
>
> Did you decide not to handle this in dump_decl, so we use the
> diagnose_as when referring to the namespace in non-scope contexts as well?
Good question. dump_decl is the more general place for handling the attribute
and that's where I moved it to.
> > + 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))));
>
> This pattern is used several places outside this function; can we factor
> it into something like
>
> if (maybe_print_diagnose_as (special))
> /* OK */;
Yes, I added the functions lookup_diagnose_as_attribute and
dump_diagnose_as_alias to remove code duplication.
> Missing space before (
OK. I think I found and fixed all of them.
> > + if (tmplate)
> > + TREE_VALUE (*parms) = make_tree_vec (0);
>
> This could use a comment.
Added.
> > (dump_aggr_type): If the type has a diagnose_as attribute, print
> > the associated string instead of printing the original type
> > name. Print template parms only if the attribute was not applied
> > to the instantiation / full specialization. Delay call to
> > dump_scope until the diagnose_as attribute is found. If the
> > attribute has a second argument, use it to override the context
> > passed to dump_scope.
> >
> > + 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;
> > + }
> > + }
>
> I think you want any_dependent_template_args_p (args)
Yes, except that I need `++processing_template_decl` before calling it (and
decrement after it, of course). Is that acceptable?
> Checking WILDCARD_TYPE_P is generally not what you want; plenty of
> dependent types don't show up specifically as wildcards. T*, for instance.
Right, that's why I had `while (INDIRECT_TYPE_P (arg)) arg = TREE_TYPE
(arg);` before the wildcard test. But I replaced all of those with calls to
any_dependent_template_arguments_p now.
> > + if (diagnose_as)
> > + pp_cxx_ws_string (pp, TREE_STRING_POINTER (
> > + TREE_VALUE (TREE_VALUE (diagnose_as))));
>
> ( needs to go on the next line. I'd format this as
>
> if (diagnose_as)
> pp_cxx_ws_string (pp, (TREE_STRING_POINTER
> (TREE_VALUE (TREE_VALUE (diagnose_as)))));
>
> There's a lot of this formatting pattern in the patch.
All fixed.
> Missing space before (
fixed.
> > (lang_decl_name): Ditto.
> > (dump_function_decl): Walk the functions context list to
> > determine whether a call to dump_template_scope is required.
> > Ensure function templates diagnosed with pretty templates set
> > TFF_TEMPLATE_NAME to skip dump_template_parms.
> > (dump_function_name): Replace the function's identifier with the
> > diagnose_as attribute value, if set. Expand
> > DECL_FRIEND_PSEUDO_TEMPLATE_INSTANTIATION to DECL_USE_TEMPLATE
> > and consequently call dump_template_parms with primary = false.
> > (comparable_template_types_p): Consider the types not a template
> > if one carries a diagnose_as attribute.
>
> I'd think it would be more helpful to suppress diagnose_as if the types
> are comparable.
So instead of diagnosing e.g. 'std::string' vs. 'std::wstring' it should print
'std::basic_string<char, ...>' vs. 'std::basic_string<wchar_t, ...>' here? I
believe that would be more confusing than helpful. Either switch all aliases
on or all of them off.
> > (cp_parser_namespace_alias_definition): Allow optional
> > attributes before and after the identifier. Fast exit, restoring
> > input_location, if the expected CPP_EQ token is missing. Pass
> > attributes to do_namespace_alias.
> >
> > + if (attributes
> > + && !cp_parser_uncommitted_to_tentative_parse_p (parser))
> > + pedwarn (input_location, OPT_Wpedantic,
> > + "standard attributes on namespaces aliases must follow "
> > + "the namespace alias name");
>
> Maybe remember where we saw attributes and warn later, after we've
> committed to parsing as an alias.
OK.
> Or use cp_parser_skip_attributes_opt
> to avoid tentative parsing in the first place.
I don't see how to do that easily. We need to parse up to the RT_EQ token
before we know whether it's a namespace alias or namespace definition.
> > + if (nullptr == cp_parser_require (parser, CPP_EQ, RT_EQ))
>
> The usual pattern is
>
> if (!cp_parser_require
done
> > (handle_diagnose_as_attribute): New function; copied and
> > adjusted from handle_abi_tag_attribute. If the given *node is a
> > TYPE_DECL: allow no argument to the attribute, using DECL_NAME
> > instead; apply the attribute to the type on the RHS in place,
> > even if the type is complete. Allow 2 arguments when called from
> > handle_diagnose_as_attribute. For type aliases, append
> > CP_DECL_CONTEXT as second attribute argument when the RHS type
> > has a different context. Warn about alias templates without
> > matching template arguments. Apply the attribute to the primary
> > template type for alias templates.
>
> All this description of semantics should be in a comment rather than the
> CHangeLog.
done
> > + /* Reject alias templates without wildcards on the innermost
> > template arg s
> > + 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))
> > + && PRIMARY_TEMPLATE_P (DECL_TI_TEMPLATE (decl)))
> > + return error_mark_node;
>
> I don't think this is doing anything useful; if we had already done
> push_template_decl by this point, it would reject all alias templates.
Without this test e.g.
template <class T>
using foo [[gnu::diagnose_as]] = typename bar<T>::inner;
is not rejected. I want to reject cases where the RHS is not a primary
template because I cannot apply the attribute to bar<T>::inner until foo<U>
instantiates bar<U>. Thus, if bar<U>::inner is used before the alias is used
the diagnose_as attribute is not present.
> > + // Add the DECL_CONTEXT of the alias to the attribute if it is
> > different + // to the context of the type.
>
> How about using the alias TYPE_DECL itself as the argument to the
> attribute on the type? Then we wouldn't need to copy its name into a
> string, either.
Done.
New patch:
This attribute overrides the diagnostics output string for the entity it
appertains to. The motivation is to improve QoI for library TS
implementations, where diagnostics have a very bad signal-to-noise ratio
due to the long namespaces involved.
With the attribute, it is possible to solve PR89370 and make
std::__cxx11::basic_string<_CharT, _Traits, _Alloc> appear as
std::string in diagnostic output without extra hacks to recognize the
type in the C++ frontend.
Signed-off-by: Matthias Kretz <m.kretz@gsi.de>
gcc/ChangeLog:
PR c++/89370
* doc/extend.texi: Document the diagnose_as attribute.
* doc/invoke.texi: Document -fno-diagnostics-use-aliases.
gcc/c-family/ChangeLog:
PR c++/89370
* c.opt (fdiagnostics-use-aliases): New diagnostics flag.
gcc/cp/ChangeLog:
PR c++/89370
* cp-tree.h: Add is_renaming_alias_template declaration.
* decl2.c (is_renaming_alias_template): New function. Determines
whether a given TYPE_DECL is actually an alias template that is
still missing its template_info.
(is_late_template_attribute): Decls with diagnose_as attribute
are early attributes only if they are alias templates.
* error.c (dump_scope): Don't remove TFF_AS_PRIMARY flag.
(lookup_diagnose_as_attribute): New function.
(dump_diagnose_as_alias): New function.
(dump_decl_name_or_diagnose_as): New function to replace
dump_decl (pp, DECL_NAME(t), flags) and inspect the tree for the
diagnose_as attribute before printing the DECL_NAME.
(dump_template_scope): New function. Prints the scope of a
template instance correctly applying diagnose_as attributes and
adjusting the list of template parms accordingly.
(dump_aggr_type): If the type has a diagnose_as attribute, print
the associated string instead of printing the original type
name. Print template parms only if the attribute was not applied
to the instantiation / full specialization. Delay call to
dump_scope until the diagnose_as attribute is found. If the
attribute has a second argument, use it to override the context
passed to dump_scope.
(dump_simple_decl): Call dump_decl_name_or_diagnose_as instead
of dump_decl.
(lang_decl_name): Ditto.
(dump_decl): Ditto. When printing the name of a namespace, look
for the diagnose_as attribute. If found, print the associated
string instead of calling dump_decl.
(dump_function_decl): Walk the functions context list to
determine whether a call to dump_template_scope is required.
(dump_function_name): Replace the function's identifier with the
diagnose_as attribute value, if set.
(comparable_template_types_p): Consider the types not a template
if one carries a diagnose_as attribute.
(print_template_differences): Replace the identifier with the
diagnose_as attribute value on the most general template, if it
is set.
* name-lookup.c (handle_namespace_attrs): Handle the diagnose_as
attribute on namespaces. Ensure exactly one string argument.
Ensure previous diagnose_as attributes used the same name.
'diagnose_as' on namespace aliases are forwarded to the original
namespace. Support no-argument 'diagnose_as' on namespace
aliases.
(do_namespace_alias): Add attributes parameter and call
handle_namespace_attrs.
* name-lookup.h (do_namespace_alias): Add attributes tree
parameter.
* parser.c (cp_parser_declaration): If the next token is
RID_NAMESPACE, tentatively parse a namespace alias definition.
If this fails expect a namespace definition.
(cp_parser_namespace_alias_definition): Allow optional
attributes before and after the identifier. Fast exit, restoring
input_location, if the expected CPP_EQ token is missing. Pass
attributes to do_namespace_alias.
* tree.c (cxx_attribute_table): Add diagnose_as attribute to the
table.
(check_diagnose_as_redeclaration): New function; copied and
adjusted from check_abi_tag_redeclaration.
(handle_diagnose_as_attribute): New function; copied and
adjusted from handle_abi_tag_attribute. If the given *node is a
TYPE_DECL: allow no argument to the attribute, using the
TYPE_DECL as attribute value instead; apply the attribute to the
type on the RHS in place, even if the type is complete. Allow 2
arguments when called from handle_diagnose_as_attribute. For
type aliases, append CP_DECL_CONTEXT as second attribute
argument when the RHS type has a different context. Warn about
alias templates without matching template arguments. Apply the
attribute to the primary template type for alias templates.
gcc/testsuite/ChangeLog:
PR c++/89370
* g++.dg/diagnostic/default-template-args-1.C: Add testcase with
partial specialization.
* g++.dg/diagnostic/diagnose-as1.C: New test.
* g++.dg/diagnostic/diagnose-as2.C: New test.
* g++.dg/diagnostic/diagnose-as3.C: New test.
* g++.dg/diagnostic/diagnose-as4.C: New test.
* g++.dg/diagnostic/diagnose-as5.C: New test.
* g++.dg/diagnostic/diagnose-as6.C: New test.
* g++.dg/diagnostic/diagnose-as7.C: New test.
---
gcc/c-family/c.opt | 4 +
gcc/cp/cp-tree.h | 1 +
gcc/cp/decl2.c | 37 +++
gcc/cp/error.c | 289 ++++++++++++++++--
gcc/cp/name-lookup.c | 53 +++-
gcc/cp/name-lookup.h | 2 +-
gcc/cp/parser.c | 40 ++-
gcc/cp/tree.c | 166 ++++++++++
gcc/doc/extend.texi | 45 +++
gcc/doc/invoke.texi | 9 +-
.../diagnostic/default-template-args-1.C | 8 +
.../g++.dg/diagnostic/diagnose-as1.C | 228 ++++++++++++++
.../g++.dg/diagnostic/diagnose-as2.C | 144 +++++++++
.../g++.dg/diagnostic/diagnose-as3.C | 152 +++++++++
.../g++.dg/diagnostic/diagnose-as4.C | 158 ++++++++++
.../g++.dg/diagnostic/diagnose-as5.C | 21 ++
.../g++.dg/diagnostic/diagnose-as6.C | 45 +++
.../g++.dg/diagnostic/diagnose-as7.C | 43 +++
18 files changed, 1400 insertions(+), 45 deletions(-)
create mode 100644 gcc/testsuite/g++.dg/diagnostic/diagnose-as1.C
create mode 100644 gcc/testsuite/g++.dg/diagnostic/diagnose-as2.C
create mode 100644 gcc/testsuite/g++.dg/diagnostic/diagnose-as3.C
create mode 100644 gcc/testsuite/g++.dg/diagnostic/diagnose-as4.C
create mode 100644 gcc/testsuite/g++.dg/diagnostic/diagnose-as5.C
create mode 100644 gcc/testsuite/g++.dg/diagnostic/diagnose-as6.C
create mode 100644 gcc/testsuite/g++.dg/diagnostic/diagnose-as7.C
--
──────────────────────────────────────────────────────────────────────────
Dr. Matthias Kretz https://mattkretz.github.io
GSI Helmholtz Centre for Heavy Ion Research https://gsi.de
stdₓ::simd
──────────────────────────────────────────────────────────────────────────
[-- Attachment #2: 0001-c-Add-gnu-diagnose_as-attribute.patch --]
[-- Type: text/x-patch, Size: 69964 bytes --]
diff --git a/gcc/c-family/c.opt b/gcc/c-family/c.opt
index 06457ac739e..b1e809cfef9 100644
--- a/gcc/c-family/c.opt
+++ b/gcc/c-family/c.opt
@@ -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.
diff --git a/gcc/cp/cp-tree.h b/gcc/cp/cp-tree.h
index e1aa95afd05..13b2bd2aaae 100644
--- a/gcc/cp/cp-tree.h
+++ b/gcc/cp/cp-tree.h
@@ -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);
diff --git a/gcc/cp/decl2.c b/gcc/cp/decl2.c
index 32d3fe3636d..6f11850f875 100644
--- a/gcc/cp/decl2.c
+++ b/gcc/cp/decl2.c
@@ -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);
+}
+
\f
/* 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;
diff --git a/gcc/cp/error.c b/gcc/cp/error.c
index 86e9d12103a..9c09a3c57fa 100644
--- a/gcc/cp/error.c
+++ b/gcc/cp/error.c
@@ -35,6 +35,7 @@ along with GCC; see the file COPYING3. If not see
#include "internal-fn.h"
#include "gcc-rich-location.h"
#include "cp-name-hint.h"
+#include "attribs.h"
#define pp_separate_with_comma(PP) pp_cxx_separate_with (PP, ',')
#define pp_separate_with_semicolon(PP) pp_cxx_separate_with (PP, ';')
@@ -66,6 +67,7 @@ static void dump_alias_template_specialization (cxx_pretty_printer *, tree, int)
static void dump_type (cxx_pretty_printer *, tree, int);
static void dump_typename (cxx_pretty_printer *, tree, int);
static void dump_simple_decl (cxx_pretty_printer *, tree, tree, int);
+static void dump_decl_name_or_diagnose_as (cxx_pretty_printer *, tree, int);
static void dump_decl (cxx_pretty_printer *, tree, int);
static void dump_template_decl (cxx_pretty_printer *, tree, int);
static void dump_function_decl (cxx_pretty_printer *, tree, int);
@@ -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);
diff --git a/gcc/cp/name-lookup.c b/gcc/cp/name-lookup.c
index 7f5e41ab610..e1a451aff40 100644
--- a/gcc/cp/name-lookup.c
+++ b/gcc/cp/name-lookup.c
@@ -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. */
diff --git a/gcc/cp/name-lookup.h b/gcc/cp/name-lookup.h
index f63c4f5b8bb..37f62a81823 100644
--- a/gcc/cp/name-lookup.h
+++ b/gcc/cp/name-lookup.h
@@ -435,7 +435,7 @@ extern tree cp_namespace_decls (tree);
extern void set_decl_namespace (tree, tree, bool);
extern void push_decl_namespace (tree);
extern void pop_decl_namespace (void);
-extern void do_namespace_alias (tree, tree);
+extern void do_namespace_alias (tree, tree, tree = 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);
diff --git a/gcc/cp/parser.c b/gcc/cp/parser.c
index 12c598798db..fb10574943c 100644
--- a/gcc/cp/parser.c
+++ b/gcc/cp/parser.c
@@ -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.
diff --git a/gcc/cp/tree.c b/gcc/cp/tree.c
index 32ddf835a91..a2ce39925a2 100644
--- a/gcc/cp/tree.c
+++ b/gcc/cp/tree.c
@@ -46,6 +46,7 @@ static tree verify_stmt_tree_r (tree *, int *, void *);
static tree handle_init_priority_attribute (tree *, tree, tree, int, bool *);
static tree handle_abi_tag_attribute (tree *, tree, tree, int, bool *);
+static tree handle_diagnose_as_attribute (tree *, tree, tree, int, bool *);
/* If REF is an lvalue, returns the kind of lvalue that REF is.
Otherwise, returns clk_none. */
@@ -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. */
diff --git a/gcc/doc/extend.texi b/gcc/doc/extend.texi
index ef654d7b878..a97f617f16d 100644
--- a/gcc/doc/extend.texi
+++ b/gcc/doc/extend.texi
@@ -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
diff --git a/gcc/doc/invoke.texi b/gcc/doc/invoke.texi
index a7c4d24a762..c3af0c5d4bf 100644
--- a/gcc/doc/invoke.texi
+++ b/gcc/doc/invoke.texi
@@ -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.
diff --git a/gcc/testsuite/g++.dg/diagnostic/default-template-args-1.C b/gcc/testsuite/g++.dg/diagnostic/default-template-args-1.C
index 7a535515740..8086ea16fcd 100644
--- a/gcc/testsuite/g++.dg/diagnostic/default-template-args-1.C
+++ b/gcc/testsuite/g++.dg/diagnostic/default-template-args-1.C
@@ -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.'" }
}
diff --git a/gcc/testsuite/g++.dg/diagnostic/diagnose-as1.C b/gcc/testsuite/g++.dg/diagnostic/diagnose-as1.C
new file mode 100644
index 00000000000..c62e5a7424d
--- /dev/null
+++ b/gcc/testsuite/g++.dg/diagnostic/diagnose-as1.C
@@ -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();
+}
diff --git a/gcc/testsuite/g++.dg/diagnostic/diagnose-as2.C b/gcc/testsuite/g++.dg/diagnostic/diagnose-as2.C
new file mode 100644
index 00000000000..b1d46d12024
--- /dev/null
+++ b/gcc/testsuite/g++.dg/diagnostic/diagnose-as2.C
@@ -0,0 +1,144 @@
+// { dg-options "-fdiagnostics-use-aliases -fno-pretty-templates" }
+
+#ifdef __cpp_constexpr
+template <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();
+}
diff --git a/gcc/testsuite/g++.dg/diagnostic/diagnose-as3.C b/gcc/testsuite/g++.dg/diagnostic/diagnose-as3.C
new file mode 100644
index 00000000000..2020443bcef
--- /dev/null
+++ b/gcc/testsuite/g++.dg/diagnostic/diagnose-as3.C
@@ -0,0 +1,152 @@
+// { dg-options "-fdiagnostics-use-aliases -fpretty-templates" }
+// { dg-do compile { target c++11 } }
+
+const char* volatile s;
+
+namespace foo
+{
+ template <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>()));
+}
diff --git a/gcc/testsuite/g++.dg/diagnostic/diagnose-as4.C b/gcc/testsuite/g++.dg/diagnostic/diagnose-as4.C
new file mode 100644
index 00000000000..a36f38aeac0
--- /dev/null
+++ b/gcc/testsuite/g++.dg/diagnostic/diagnose-as4.C
@@ -0,0 +1,158 @@
+// { dg-options "-fdiagnostics-use-aliases -fno-pretty-templates" }
+// { dg-do compile { target c++11 } }
+
+const char* volatile s;
+
+namespace foo
+{
+ template <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 }
+}
diff --git a/gcc/testsuite/g++.dg/diagnostic/diagnose-as5.C b/gcc/testsuite/g++.dg/diagnostic/diagnose-as5.C
new file mode 100644
index 00000000000..2a46d64964f
--- /dev/null
+++ b/gcc/testsuite/g++.dg/diagnostic/diagnose-as5.C
@@ -0,0 +1,21 @@
+// { dg-options "-fdiagnostics-use-aliases -fpretty-templates" }
+// { dg-do compile { target c++11 } }
+
+namespace std
+{
+ template <class 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'" }
+}
diff --git a/gcc/testsuite/g++.dg/diagnostic/diagnose-as6.C b/gcc/testsuite/g++.dg/diagnostic/diagnose-as6.C
new file mode 100644
index 00000000000..94747a7aa6e
--- /dev/null
+++ b/gcc/testsuite/g++.dg/diagnostic/diagnose-as6.C
@@ -0,0 +1,45 @@
+// { dg-options "-fdiagnostics-use-aliases -fpretty-templates" }
+// { dg-do compile { target c++11 } }
+
+template <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'" }
+}
diff --git a/gcc/testsuite/g++.dg/diagnostic/diagnose-as7.C b/gcc/testsuite/g++.dg/diagnostic/diagnose-as7.C
new file mode 100644
index 00000000000..3005c787615
--- /dev/null
+++ b/gcc/testsuite/g++.dg/diagnostic/diagnose-as7.C
@@ -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" }
+}
^ permalink raw reply [flat|nested] 24+ messages in thread
* Re: [RFC] c++: Print function template parms when relevant (was: [PATCH v4] c++: Add gnu::diagnose_as attribute)
2021-11-08 20:00 ` Matthias Kretz
@ 2021-11-16 20:25 ` Jason Merrill
2021-11-16 20:42 ` Matthias Kretz
0 siblings, 1 reply; 24+ messages in thread
From: Jason Merrill @ 2021-11-16 20:25 UTC (permalink / raw)
To: Matthias Kretz, gcc-patches
On 11/8/21 15:00, Matthias Kretz wrote:
> I forgot to mention why I tagged it [RFC]: I needed one more bit of
> information on the template args TREE_VEC to encode EXPLICIT_TEMPLATE_ARGS_P.
> Its TREE_CHAIN already points to an integer constant denoting the number of
> non-default arguments, so I couldn't trivially replace that. Therefore, I used
> the sign of that integer. I was hoping to find a cleaner solution, though.
It seems that we aren't using any TREE_LANG_FLAG_n on TREE_VEC, so that
would be a cleaner solution.
> On Monday, 8 November 2021 17:40:44 CET Matthias Kretz wrote:
>> On Tuesday, 17 August 2021 20:31:54 CET Jason Merrill wrote:
>>>> 2. Given a DECL_TI_ARGS tree, can I query whether an argument was
>>>> deduced
>>>> or explicitly specified? I'm asking because I still consider diagnostics
>>>> of function templates unfortunate. `template <class T> void f()` is
>>>> fine,
>>>> as is `void f(T) [with T = float]`, but `void f() [with T = float]`
>>>> could
>>>> be better. I.e. if the template parameter appears somewhere in the
>>>> function parameter list, dump_template_parms would only produce noise.
>>>> If, however, the template parameter was given explicitly, it would be
>>>> nice if it could show up accordingly in diagnostics.
>>>
>>> NON_DEFAULT_TEMPLATE_ARGS_COUNT has that information, though there are
>>> some issues with it. Attached is my WIP from May to improve it
>>> somewhat, if that's interesting.
>>
>> It is interesting. I used your patch to come up with the attached. Patch. I
>> must say, I didn't try to read through all the cp/pt.c code to understand
>> all of what you did there (which is why my ChangeLog entry says "Jason?"),
>> but it works for me (and all of `make check`).
>>
>> Anyway, I'd like to propose the following before finishing my diagnose_as
>> patch. I believe it's useful to fix this part first. The diagnostic/default-
>> template-args-[12].C tests show a lot of examples of the intent of this
>> patch. And the remaining changes to the testsuite show how it changes
>> diagnostic output.
>>
>> ---------------------- 8< --------------------
>>
>> The choice when to print a function template parameter was still
>> suboptimal. That's because sometimes the function template parameter
>> list only adds noise, while in other situations the lack of a function
>> template parameter list makes diagnostic messages hard to understand.
>>
>> The general idea of this change is to print template parms wherever they
>> would appear in the source code as well. Thus, the diagnostics code
>> needs to know whether any template parameter was given explicitly.
>>
>> Signed-off-by: Matthias Kretz <m.kretz@gsi.de>
>>
>> gcc/testsuite/ChangeLog:
>>
>> * g++.dg/debug/dwarf2/template-params-12n.C: Optionally, allow
>> DW_AT_default_value.
>> * g++.dg/diagnostic/default-template-args-1.C: New.
>> * g++.dg/diagnostic/default-template-args-2.C: New.
>> * g++.dg/diagnostic/param-type-mismatch-2.C: Expect template
>> parms in diagnostic.
>> * g++.dg/ext/pretty1.C: Expect function template specialization
>> to not pretty-print template parms.
>> * g++.old-deja/g++.ext/pretty3.C: Ditto.
>> * g++.old-deja/g++.pt/memtemp77.C: Ditto.
>> * g++.dg/goacc/template.C: Expect function template parms for
>> explicit arguments.
>> * g++.dg/gomp/declare-variant-7.C: Expect no function template
>> parms for deduced arguments.
>> * g++.dg/template/error40.C: Expect only non-default template
>> arguments in diagnostic.
>>
>> gcc/cp/ChangeLog:
>>
>> * cp-tree.h (GET_NON_DEFAULT_TEMPLATE_ARGS_COUNT): Return
>> absolute value of stored constant.
>> (EXPLICIT_TEMPLATE_ARGS_P): New.
>> (SET_EXPLICIT_TEMPLATE_ARGS_P): New.
>> (TFF_AS_PRIMARY): New constant.
>> * error.c (get_non_default_template_args_count): Avoid
>> GET_NON_DEFAULT_TEMPLATE_ARGS_COUNT if
>> NON_DEFAULT_TEMPLATE_ARGS_COUNT is a NULL_TREE. Make independent
>> of flag_pretty_templates.
>> (dump_template_bindings): Add flags parameter to be passed to
>> get_non_default_template_args_count. Print only non-default
>> template arguments.
>> (dump_function_decl): Call dump_function_name and dump_type of
>> the DECL_CONTEXT with specialized template and set
>> TFF_AS_PRIMARY for their flags.
>> (dump_function_name): Add and document conditions for calling
>> dump_template_parms.
>> (dump_template_parms): Print only non-default template
>> parameters.
>> * pt.c (determine_specialization): Jason?
>> (template_parms_level_to_args): Jason?
>> (copy_template_args): Jason?
>> (fn_type_unification): Set EXPLICIT_TEMPLATE_ARGS_P on the
>> template arguments tree if any template parameter was explicitly
>> given.
>> (type_unification_real): Jason?
>> (get_partial_spec_bindings): Jason?
>> (tsubst_template_args): Determine number of defaulted arguments
>> from new argument vector, if possible.
>> ---
>> gcc/cp/cp-tree.h | 18 +++-
>> gcc/cp/error.c | 83 ++++++++++++++-----
>> gcc/cp/pt.c | 58 +++++++++----
>> .../g++.dg/debug/dwarf2/template-params-12n.C | 2 +-
>> .../diagnostic/default-template-args-1.C | 73 ++++++++++++++++
>> .../diagnostic/default-template-args-2.C | 37 +++++++++
>> .../g++.dg/diagnostic/param-type-mismatch-2.C | 2 +-
>> gcc/testsuite/g++.dg/ext/pretty1.C | 2 +-
>> gcc/testsuite/g++.dg/goacc/template.C | 8 +-
>> gcc/testsuite/g++.dg/gomp/declare-variant-7.C | 4 +-
>> gcc/testsuite/g++.dg/template/error40.C | 6 +-
>> gcc/testsuite/g++.old-deja/g++.ext/pretty3.C | 2 +-
>> gcc/testsuite/g++.old-deja/g++.pt/memtemp77.C | 2 +-
>> 13 files changed, 242 insertions(+), 55 deletions(-)
>> create mode 100644
>> gcc/testsuite/g++.dg/diagnostic/default-template-args-1.C create mode
>> 100644 gcc/testsuite/g++.dg/diagnostic/default-template-args-2.C
>
>
^ permalink raw reply [flat|nested] 24+ messages in thread
* Re: [RFC] c++: Print function template parms when relevant (was: [PATCH v4] c++: Add gnu::diagnose_as attribute)
2021-11-16 20:25 ` Jason Merrill
@ 2021-11-16 20:42 ` Matthias Kretz
2021-11-16 20:49 ` Jason Merrill
0 siblings, 1 reply; 24+ messages in thread
From: Matthias Kretz @ 2021-11-16 20:42 UTC (permalink / raw)
To: gcc-patches, Jason Merrill
On Tuesday, 16 November 2021 21:25:33 CET Jason Merrill wrote:
> On 11/8/21 15:00, Matthias Kretz wrote:
> > I forgot to mention why I tagged it [RFC]: I needed one more bit of
> > information on the template args TREE_VEC to encode
> > EXPLICIT_TEMPLATE_ARGS_P. Its TREE_CHAIN already points to an integer
> > constant denoting the number of non-default arguments, so I couldn't
> > trivially replace that. Therefore, I used the sign of that integer. I was
> > hoping to find a cleaner solution, though.
> It seems that we aren't using any TREE_LANG_FLAG_n on TREE_VEC, so that
> would be a cleaner solution.
I tried that first but realized that TREE_VEC doesn't allow any
TREE_LANG_FLAGs (it uses those bits for the length IIRC). And setting the
TREE_LANG_FLAGs on the TREE_CHAIN of the TREE_VEC can't work either (since the
int constants are shared between many trees).
Should I maybe turn the TREE_CHAIN into a TREE_LIST using TREE_PURPOSE and
TREE_VALUE for EXPLICIT_TEMPLATE_ARGS_P and non-default arguments,
respectively? (And where would I document this?)
--
──────────────────────────────────────────────────────────────────────────
Dr. Matthias Kretz https://mattkretz.github.io
GSI Helmholtz Centre for Heavy Ion Research https://gsi.de
stdₓ::simd
──────────────────────────────────────────────────────────────────────────
^ permalink raw reply [flat|nested] 24+ messages in thread
* Re: [RFC] c++: Print function template parms when relevant (was: [PATCH v4] c++: Add gnu::diagnose_as attribute)
2021-11-16 20:42 ` Matthias Kretz
@ 2021-11-16 20:49 ` Jason Merrill
2021-11-16 20:51 ` Matthias Kretz
0 siblings, 1 reply; 24+ messages in thread
From: Jason Merrill @ 2021-11-16 20:49 UTC (permalink / raw)
To: Matthias Kretz, gcc-patches
On 11/16/21 15:42, Matthias Kretz wrote:
> On Tuesday, 16 November 2021 21:25:33 CET Jason Merrill wrote:
>> On 11/8/21 15:00, Matthias Kretz wrote:
>>> I forgot to mention why I tagged it [RFC]: I needed one more bit of
>>> information on the template args TREE_VEC to encode
>>> EXPLICIT_TEMPLATE_ARGS_P. Its TREE_CHAIN already points to an integer
>>> constant denoting the number of non-default arguments, so I couldn't
>>> trivially replace that. Therefore, I used the sign of that integer. I was
>>> hoping to find a cleaner solution, though.
>> It seems that we aren't using any TREE_LANG_FLAG_n on TREE_VEC, so that
>> would be a cleaner solution.
>
> I tried that first but realized that TREE_VEC doesn't allow any
> TREE_LANG_FLAGs (it uses those bits for the length IIRC). And setting the
> TREE_LANG_FLAGs on the TREE_CHAIN of the TREE_VEC can't work either (since the
> int constants are shared between many trees).
>
> Should I maybe turn the TREE_CHAIN into a TREE_LIST using TREE_PURPOSE and
> TREE_VALUE for EXPLICIT_TEMPLATE_ARGS_P and non-default arguments,
> respectively? (And where would I document this?)
Maybe a TREE_LIST if there are explicit template arguments to a function
template, where TREE_PURPOSE is the number of explicit arguments and
TREE_VALUE is the number of non-default arguments.
I'd document it at the definition of NON_DEFAULT_TEMPLATE_ARGS_COUNT.
The SET/GET macros should become functions.
Jason
^ permalink raw reply [flat|nested] 24+ messages in thread
* Re: [RFC] c++: Print function template parms when relevant (was: [PATCH v4] c++: Add gnu::diagnose_as attribute)
2021-11-16 20:49 ` Jason Merrill
@ 2021-11-16 20:51 ` Matthias Kretz
0 siblings, 0 replies; 24+ messages in thread
From: Matthias Kretz @ 2021-11-16 20:51 UTC (permalink / raw)
To: gcc-patches, Jason Merrill
On Tuesday, 16 November 2021 21:49:31 CET Jason Merrill wrote:
> On 11/16/21 15:42, Matthias Kretz wrote:
> > On Tuesday, 16 November 2021 21:25:33 CET Jason Merrill wrote:
> >> On 11/8/21 15:00, Matthias Kretz wrote:
> >>> I forgot to mention why I tagged it [RFC]: I needed one more bit of
> >>> information on the template args TREE_VEC to encode
> >>> EXPLICIT_TEMPLATE_ARGS_P. Its TREE_CHAIN already points to an integer
> >>> constant denoting the number of non-default arguments, so I couldn't
> >>> trivially replace that. Therefore, I used the sign of that integer. I
> >>> was
> >>> hoping to find a cleaner solution, though.
> >>
> >> It seems that we aren't using any TREE_LANG_FLAG_n on TREE_VEC, so that
> >> would be a cleaner solution.
> >
> > I tried that first but realized that TREE_VEC doesn't allow any
> > TREE_LANG_FLAGs (it uses those bits for the length IIRC). And setting the
> > TREE_LANG_FLAGs on the TREE_CHAIN of the TREE_VEC can't work either (since
> > the int constants are shared between many trees).
> >
> > Should I maybe turn the TREE_CHAIN into a TREE_LIST using TREE_PURPOSE and
> > TREE_VALUE for EXPLICIT_TEMPLATE_ARGS_P and non-default arguments,
> > respectively? (And where would I document this?)
>
> Maybe a TREE_LIST if there are explicit template arguments to a function
> template, where TREE_PURPOSE is the number of explicit arguments and
> TREE_VALUE is the number of non-default arguments.
>
> I'd document it at the definition of NON_DEFAULT_TEMPLATE_ARGS_COUNT.
> The SET/GET macros should become functions.
Sounds good. I'll come up with a new patch ASAP.
--
──────────────────────────────────────────────────────────────────────────
Dr. Matthias Kretz https://mattkretz.github.io
GSI Helmholtz Centre for Heavy Ion Research https://gsi.de
stdₓ::simd
──────────────────────────────────────────────────────────────────────────
^ permalink raw reply [flat|nested] 24+ messages in thread
* Re: [RFC] c++: Print function template parms when relevant (was: [PATCH v4] c++: Add gnu::diagnose_as attribute)
2021-11-08 16:40 ` [RFC] c++: Print function template parms when relevant (was: [PATCH v4] c++: Add gnu::diagnose_as attribute) Matthias Kretz
2021-11-08 20:00 ` Matthias Kretz
@ 2021-11-17 6:09 ` Jason Merrill
2021-11-17 9:04 ` Matthias Kretz
1 sibling, 1 reply; 24+ messages in thread
From: Jason Merrill @ 2021-11-17 6:09 UTC (permalink / raw)
To: Matthias Kretz, gcc-patches
On 11/8/21 11:40, Matthias Kretz wrote:
> On Tuesday, 17 August 2021 20:31:54 CET Jason Merrill wrote:
>>> 2. Given a DECL_TI_ARGS tree, can I query whether an argument was deduced
>>> or explicitly specified? I'm asking because I still consider diagnostics
>>> of function templates unfortunate. `template <class T> void f()` is fine,
>>> as is `void f(T) [with T = float]`, but `void f() [with T = float]` could
>>> be better. I.e. if the template parameter appears somewhere in the
>>> function parameter list, dump_template_parms would only produce noise.
>>> If, however, the template parameter was given explicitly, it would be
>>> nice if it could show up accordingly in diagnostics.
>>
>> NON_DEFAULT_TEMPLATE_ARGS_COUNT has that information, though there are
>> some issues with it. Attached is my WIP from May to improve it
>> somewhat, if that's interesting.
>
> It is interesting. I used your patch to come up with the attached. Patch. I
> must say, I didn't try to read through all the cp/pt.c code to understand all
> of what you did there (which is why my ChangeLog entry says "Jason?"), but it
> works for me (and all of `make check`).
>
> Anyway, I'd like to propose the following before finishing my diagnose_as
> patch. I believe it's useful to fix this part first. The diagnostic/default-
> template-args-[12].C tests show a lot of examples of the intent of this patch.
> And the remaining changes to the testsuite show how it changes diagnostic
> output.
>
> ---------------------- 8< --------------------
>
> The choice when to print a function template parameter was still
> suboptimal. That's because sometimes the function template parameter
> list only adds noise, while in other situations the lack of a function
> template parameter list makes diagnostic messages hard to understand.
>
> The general idea of this change is to print template parms wherever they
> would appear in the source code as well. Thus, the diagnostics code
> needs to know whether any template parameter was given explicitly.
>
> Signed-off-by: Matthias Kretz <m.kretz@gsi.de>
>
> gcc/testsuite/ChangeLog:
>
> * g++.dg/debug/dwarf2/template-params-12n.C: Optionally, allow
> DW_AT_default_value.
> * g++.dg/diagnostic/default-template-args-1.C: New.
> * g++.dg/diagnostic/default-template-args-2.C: New.
> * g++.dg/diagnostic/param-type-mismatch-2.C: Expect template
> parms in diagnostic.
> * g++.dg/ext/pretty1.C: Expect function template specialization
> to not pretty-print template parms.
> * g++.old-deja/g++.ext/pretty3.C: Ditto.
> * g++.old-deja/g++.pt/memtemp77.C: Ditto.
> * g++.dg/goacc/template.C: Expect function template parms for
> explicit arguments.
> * g++.dg/gomp/declare-variant-7.C: Expect no function template
> parms for deduced arguments.
> * g++.dg/template/error40.C: Expect only non-default template
> arguments in diagnostic.
>
> gcc/cp/ChangeLog:
>
> * cp-tree.h (GET_NON_DEFAULT_TEMPLATE_ARGS_COUNT): Return
> absolute value of stored constant.
> (EXPLICIT_TEMPLATE_ARGS_P): New.
> (SET_EXPLICIT_TEMPLATE_ARGS_P): New.
> (TFF_AS_PRIMARY): New constant.
> * error.c (get_non_default_template_args_count): Avoid
> GET_NON_DEFAULT_TEMPLATE_ARGS_COUNT if
> NON_DEFAULT_TEMPLATE_ARGS_COUNT is a NULL_TREE. Make independent
> of flag_pretty_templates.
> (dump_template_bindings): Add flags parameter to be passed to
> get_non_default_template_args_count. Print only non-default
> template arguments.
> (dump_function_decl): Call dump_function_name and dump_type of
> the DECL_CONTEXT with specialized template and set
> TFF_AS_PRIMARY for their flags.
> (dump_function_name): Add and document conditions for calling
> dump_template_parms.
> (dump_template_parms): Print only non-default template
> parameters.
> * pt.c (determine_specialization): Jason?
Also copy the inner TREE_VECs.
> (template_parms_level_to_args): Jason?
Always count non-default args. Also, I think
> - if (CHECKING_P)
> - SET_NON_DEFAULT_TEMPLATE_ARGS_COUNT (a, TREE_VEC_LENGTH (a));
> + SET_NON_DEFAULT_TEMPLATE_ARGS_COUNT (a, nondefault);
should have been
if (CHECKING_P || nondefault != TREE_VEC_LENGTH (a))
SET_NON_DEFAULT_TEMPLATE_ARGS_COUNT (a, nondefault);
> (copy_template_args): Jason?
Only copy the non-default template args count on TREE_VECs that should
have it.
> (fn_type_unification): Set EXPLICIT_TEMPLATE_ARGS_P on the
> template arguments tree if any template parameter was explicitly
> given.
> (type_unification_real): Jason?
Count non-default args sooner.
> (get_partial_spec_bindings): Jason?
Set non-default args count.
> (tsubst_template_args): Determine number of defaulted arguments
> from new argument vector, if possible.
> ---
> gcc/cp/cp-tree.h | 18 +++-
> gcc/cp/error.c | 83 ++++++++++++++-----
> gcc/cp/pt.c | 58 +++++++++----
> .../g++.dg/debug/dwarf2/template-params-12n.C | 2 +-
> .../diagnostic/default-template-args-1.C | 73 ++++++++++++++++
> .../diagnostic/default-template-args-2.C | 37 +++++++++
> .../g++.dg/diagnostic/param-type-mismatch-2.C | 2 +-
> gcc/testsuite/g++.dg/ext/pretty1.C | 2 +-
> gcc/testsuite/g++.dg/goacc/template.C | 8 +-
> gcc/testsuite/g++.dg/gomp/declare-variant-7.C | 4 +-
> gcc/testsuite/g++.dg/template/error40.C | 6 +-
> gcc/testsuite/g++.old-deja/g++.ext/pretty3.C | 2 +-
> gcc/testsuite/g++.old-deja/g++.pt/memtemp77.C | 2 +-
> 13 files changed, 242 insertions(+), 55 deletions(-)
> create mode 100644 gcc/testsuite/g++.dg/diagnostic/default-template-args-1.C
> create mode 100644 gcc/testsuite/g++.dg/diagnostic/default-template-args-2.C
>
> + /* Pretty print only template instantiations. Don't pretty print explicit
> + specializations like 'template <> void fun<int> (int)'. */
This seems like a significant change of behavior unrelated to printing
default template arguments. What's the rationale for handling
specializations differently from instantiations?
I also don't understand the purpose of TFF_AS_PRIMARY.
> +/* Print function template parameters if:
> + 1. t is template, and
> + 2. flags did not request "show only template-name", and
> + 3. t is a specialization of a template (Why is this needed? This was present
> + since 1999 via !DECL_FRIEND_PSEUDO_TEMPLATE_INSTANTIATION: "Don't crash if
> + given a friend pseudo-instantiation". The DECL_USE_TEMPLATE should likely
> + inform the PRIMARY parameter of dump_template_parms.), and
Good question. It seems that the existing
!DECL_FRIEND_PSEUDO_TEMPLATE_INSTANTIATION has mostly been excluding the
most general template; removing that line changes things like
int bar(T) [with T = int]
to
int bar<T>(T) [with T = int]
> + 4. either
> + - flags requests to show no function arguments, in which case deduced
> + types could be hidden, or
> + - at least one function template argument was given explicitly, or
> + - we're printing a DWARF name,
...but perhaps this can replace the above. Though, why do we want
different behavior here when printing a DWARF name?
> + 5. either
> + - t is a member friend template of a template class (see DECL_TI_TEMPLATE
> + documentation), or
> + -
Missing the last item. :)
> + */
*/ doesn't get its own line.
> if (DECL_TEMPLATE_INFO (t)
> && !(flags & TFF_TEMPLATE_NAME)
> - && !DECL_FRIEND_PSEUDO_TEMPLATE_INSTANTIATION (t)
> + && DECL_USE_TEMPLATE (t)
> + && ((flags & TFF_NO_FUNCTION_ARGUMENTS)
> + || (DECL_TI_ARGS (t)
> + && EXPLICIT_TEMPLATE_ARGS_P (INNERMOST_TEMPLATE_ARGS
> + (DECL_TI_ARGS (t))))
> + || (pp->flags & pp_c_flag_gnu_v3) != 0)
> && (TREE_CODE (DECL_TI_TEMPLATE (t)) != TEMPLATE_DECL
> || PRIMARY_TEMPLATE_P (DECL_TI_TEMPLATE (t))))
> - dump_template_parms (pp, DECL_TEMPLATE_INFO (t), !DECL_USE_TEMPLATE (t),
> - flags);
> + dump_template_parms (pp, DECL_TEMPLATE_INFO (t), false, flags);
Jason
^ permalink raw reply [flat|nested] 24+ messages in thread
* Re: [RFC] c++: Print function template parms when relevant (was: [PATCH v4] c++: Add gnu::diagnose_as attribute)
2021-11-17 6:09 ` Jason Merrill
@ 2021-11-17 9:04 ` Matthias Kretz
2021-11-17 18:25 ` Jason Merrill
0 siblings, 1 reply; 24+ messages in thread
From: Matthias Kretz @ 2021-11-17 9:04 UTC (permalink / raw)
To: gcc-patches, Jason Merrill
On Wednesday, 17 November 2021 07:09:18 CET Jason Merrill wrote:
> > - if (CHECKING_P)
> > - SET_NON_DEFAULT_TEMPLATE_ARGS_COUNT (a, TREE_VEC_LENGTH (a));
> > + SET_NON_DEFAULT_TEMPLATE_ARGS_COUNT (a, nondefault);
>
> should have been
>
> if (CHECKING_P || nondefault != TREE_VEC_LENGTH (a))
> SET_NON_DEFAULT_TEMPLATE_ARGS_COUNT (a, nondefault);
TBH, I don't understand the purpose of CHECKING_P here, or rather it makes me
nervous because AFAIU I'm only testing with CHECKING_P enabled. Why make
behavior dependent on CHECKING_P? I expected CHECKING_P to basically only add
more assertions.
> > (copy_template_args): Jason?
>
> Only copy the non-default template args count on TREE_VECs that should
> have it.
Why not simply set the count on all args? Is it a performance concern? The
INTEGER_CST the TREE_CHAIN has to point to exists anyway, so it's not wasting
any memory, right?
>
> > + /* Pretty print only template instantiations. Don't pretty print
> > explicit
> > + specializations like 'template <> void fun<int> (int)'.
>
> This seems like a significant change of behavior unrelated to printing
> default template arguments. What's the rationale for handling
> specializations differently from instantiations?
Right, this is about "The general idea of this change is to print template
parms wherever they would appear in the source code as well".
Initially, the change to print function template arguments/parameters only if
the args were explicitly specified lead to printing 'void fun (T) [with T =
...]' or 'template <> void fun (int)'. Both are not telling the full story,
even if the former is how the function would be called. But if the reader
should quickly recognize what code is getting called, it is helpful to see
right away that a function template specialization is called. (It might also
reveal an implementation detail of a library, so it's not 100% obvious how to
choose here.) Also, saying 'T = int' is kind of wrong. Yes, 'int' was deduced.
But there's no T in fun<int>:
template <class T> void fun (T);
template <> void fun<int> (int);
__FUNCTION__ was 'fun<int>' all the time, but __PRETTY_FUNCTION__ was 'void
fun(T) [with T = int]'. It's more consistent that __PRETTY_FUNCTION__ contains
__FUNCTION__, IMHO, so it would have to be at least 'void fun<int>(T) [with T
= int]'. But that's strange: How it uses T and int for the same type. So I
settled on 'void fun<int>(int)'.
> I also don't understand the purpose of TFF_AS_PRIMARY.
dump_function_decl generalizes the TEMPLATE_DECL (if flag_pretty_templates is
true) and, before this change, passes the generalized TEMPLATE_DECL to
dump_type (... DECL_CONTEXT (t) ...) and dump_function_name (... t ...).
That's why the whole template is printed as primary template (i.e. with
template parms instead of template args, as is needed for
flag_pretty_templates). But this drops the count of non-default template args.
To retain the count, dump_type and dump_function_name need to be called with
the original TEMPLATE_DECL. But if I do this, pretty-templates is broken.
'template <class T> struct A { template <class U> void f(T, U); };' would
print as 'A<int>::f<float>(T, U) [with U = float, T = int]'. To get back to
'A<T>::f<U>(T, U) [with U = float, T = int]' I needed to tell
dump_template_parms that even though the template args are there, it should
print only the template parms. The most obvious way to do that was to carry it
through via flags.
Note that this creates another problem. Given
template <class T0, class T1 = int> struct Outer {
template <class T, class U> struct A;
template <class X> struct A<X, int> {
void f();
};
};
we want to print e.g. 'void Outer<T0>::A<X, int>::f() [with X = int, T0 =
int]', but certainly not 'void Outer<T0>::A<T, U>::f() [with X = int, T0 =
int]'. However, specialized_t holds A<int, int> which is printed as A<T, U>
with TFF_AS_PRIMARY. Only most_general_template of the function's
TEMPLATE_DECL can give us A<X, int> as DECL_CONTEXT.
I have a solution in the diagnose_as patch, where I had to solve a similar
problem because for the diagnose_as attribute (dump_template_scope).
> > +/* Print function template parameters if:
> > + 1. t is template, and
> > + 2. flags did not request "show only template-name", and
> > + 3. t is a specialization of a template (Why is this needed? This was
> > present + since 1999 via !DECL_FRIEND_PSEUDO_TEMPLATE_INSTANTIATION:
> > "Don't crash if + given a friend pseudo-instantiation". The
> > DECL_USE_TEMPLATE should likely + inform the PRIMARY parameter of
> > dump_template_parms.), and
>
> Good question. It seems that the existing
> !DECL_FRIEND_PSEUDO_TEMPLATE_INSTANTIATION has mostly been excluding the
> most general template; removing that line changes things like
>
> int bar(T) [with T = int]
>
> to
>
> int bar<T>(T) [with T = int]
>
>
>
> > + 4. either
> > + - flags requests to show no function arguments, in which case
> > deduced + types could be hidden, or
> > + - at least one function template argument was given explicitly, or
> > + - we're printing a DWARF name,
>
> ...but perhaps this can replace the above.
I'll rerun the tests with DECL_USE_TEMPLATE moved to the PRIMARY parameter of
dump_template_parms.
> Though, why do we want
> different behavior here when printing a DWARF name?
Sorry, I should have asked ... I also added the same issue as an open question
on the diagnose_as patch. When I ran the whole testsuite I had failures in the
DWARF tests. This change resolved them. I don't know enough about how those
strings are used and whether they may change between GCC versions. Anyway, the
DWARF strings in that test had only the function name and template argument
list (i.e. no function arguments). If the template argument list was removed
(as was the case without the condition here), the DWARF strings lost important
information, giving several different functions the same name. That seemed
like a regression. So either the DWARF strings need to include the function
arguments, or we need this condition to keep showing the template arguments
independent of whether they were explicitly given or not.
> > + 5. either
> > + - t is a member friend template of a template class (see
> > DECL_TI_TEMPLATE + documentation), or
> > + -
>
> Missing the last item. :)
Oh, I got distracted while trying to figure this out. :) I'll try to
understand why PRIMARY_TEMPLATE_P (DECL_TI_TEMPLATE (t)) is needed and
document it.
--
──────────────────────────────────────────────────────────────────────────
Dr. Matthias Kretz https://mattkretz.github.io
GSI Helmholtz Centre for Heavy Ion Research https://gsi.de
stdₓ::simd
──────────────────────────────────────────────────────────────────────────
^ permalink raw reply [flat|nested] 24+ messages in thread
* Re: [RFC] c++: Print function template parms when relevant (was: [PATCH v4] c++: Add gnu::diagnose_as attribute)
2021-11-17 9:04 ` Matthias Kretz
@ 2021-11-17 18:25 ` Jason Merrill
2021-11-17 22:51 ` Matthias Kretz
0 siblings, 1 reply; 24+ messages in thread
From: Jason Merrill @ 2021-11-17 18:25 UTC (permalink / raw)
To: Matthias Kretz, gcc-patches
On 11/17/21 04:04, Matthias Kretz wrote:
> On Wednesday, 17 November 2021 07:09:18 CET Jason Merrill wrote:
>>> - if (CHECKING_P)
>>> - SET_NON_DEFAULT_TEMPLATE_ARGS_COUNT (a, TREE_VEC_LENGTH (a));
>>> + SET_NON_DEFAULT_TEMPLATE_ARGS_COUNT (a, nondefault);
>>
>> should have been
>>
>> if (CHECKING_P || nondefault != TREE_VEC_LENGTH (a))
>> SET_NON_DEFAULT_TEMPLATE_ARGS_COUNT (a, nondefault);
>
> TBH, I don't understand the purpose of CHECKING_P here, or rather it makes me
> nervous because AFAIU I'm only testing with CHECKING_P enabled. Why make
> behavior dependent on CHECKING_P? I expected CHECKING_P to basically only add
> more assertions.
The idea when NON_DEFAULT_TEMPLATE_ARGS_COUNT was added years back was
to leave the TREE_CHAIN null when !CHECKING_P and treat that as
equivalent to TREE_VEC_LENGTH (args). But perhaps you're right that
it's not a savings worth the complexity.
>>> (copy_template_args): Jason?
>>
>> Only copy the non-default template args count on TREE_VECs that should
>> have it.
>
> Why not simply set the count on all args? Is it a performance concern? The
> INTEGER_CST the TREE_CHAIN has to point to exists anyway, so it's not wasting
> any memory, right?
In this case the TREE_VEC we're excluding is the one wrapping multiple
levels of template args; it doesn't contain args directly, so setting
NON_DEFAULT_ARGS_COUNT on it doesn't make sense.
>>> + /* Pretty print only template instantiations. Don't pretty print
>>> explicit
>>> + specializations like 'template <> void fun<int> (int)'.
>>
>> This seems like a significant change of behavior unrelated to printing
>> default template arguments. What's the rationale for handling
>> specializations differently from instantiations?
>
> Right, this is about "The general idea of this change is to print template
> parms wherever they would appear in the source code as well".
>
> Initially, the change to print function template arguments/parameters only if
> the args were explicitly specified lead to printing 'void fun (T) [with T =
> ...]' or 'template <> void fun (int)'. Both are not telling the full story,
> even if the former is how the function would be called.
and the latter is how I expect the specialization to be declared, not
with the deducible template argument made explicit.
> But if the reader
> should quickly recognize what code is getting called, it is helpful to see
> right away that a function template specialization is called. (It might also
> reveal an implementation detail of a library, so it's not 100% obvious how to
> choose here.) Also, saying 'T = int' is kind of wrong. Yes, 'int' was deduced.
> But there's no T in fun<int>:
>
> template <class T> void fun (T);
> template <> void fun<int> (int);
There's a T in the template, and as you said above, that's how it's
called (and mangled).
> __FUNCTION__ was 'fun<int>' all the time, but __PRETTY_FUNCTION__ was 'void
> fun(T) [with T = int]'.
Isn't that true for instantiations, as well?
> It's more consistent that __PRETTY_FUNCTION__ contains __FUNCTION__, IMHO
I suppose, but I don't see that as a strong enough motivation to mix
this up.
> so it would have to be at least 'void fun<int>(T) [with T
> = int]'. But that's strange: How it uses T and int for the same type. So I
> settled on 'void fun<int>(int)'.
>
>> I also don't understand the purpose of TFF_AS_PRIMARY.
>
> dump_function_decl generalizes the TEMPLATE_DECL (if flag_pretty_templates is
> true) and, before this change, passes the generalized TEMPLATE_DECL to
> dump_type (... DECL_CONTEXT (t) ...) and dump_function_name (... t ...).
> That's why the whole template is printed as primary template (i.e. with
> template parms instead of template args, as is needed for
> flag_pretty_templates). But this drops the count of non-default template args.
Ah, you're trying to omit defaulted parms from the <list>? I'm not sure
that's necessary, leaving them out of the [with ...] list should be
sufficient.
> To retain the count, dump_type and dump_function_name need to be called with
> the original TEMPLATE_DECL. But if I do this, pretty-templates is broken.
> 'template <class T> struct A { template <class U> void f(T, U); };' would
> print as 'A<int>::f<float>(T, U) [with U = float, T = int]'. To get back to
> 'A<T>::f<U>(T, U) [with U = float, T = int]' I needed to tell
> dump_template_parms that even though the template args are there, it should
> print only the template parms. The most obvious way to do that was to carry it
> through via flags.
>
> Note that this creates another problem. Given
>
> template <class T0, class T1 = int> struct Outer {
> template <class T, class U> struct A;
> template <class X> struct A<X, int> {
> void f();
> };
> };
>
> we want to print e.g. 'void Outer<T0>::A<X, int>::f() [with X = int, T0 =
> int]', but certainly not 'void Outer<T0>::A<T, U>::f() [with X = int, T0 =
> int]'. However, specialized_t holds A<int, int> which is printed as A<T, U>
> with TFF_AS_PRIMARY. Only most_general_template of the function's
> TEMPLATE_DECL can give us A<X, int> as DECL_CONTEXT.
>
> I have a solution in the diagnose_as patch, where I had to solve a similar
> problem because for the diagnose_as attribute (dump_template_scope).
>
>>> +/* Print function template parameters if:
>>> + 1. t is template, and
>>> + 2. flags did not request "show only template-name", and
>>> + 3. t is a specialization of a template (Why is this needed? This was
>>> present + since 1999 via !DECL_FRIEND_PSEUDO_TEMPLATE_INSTANTIATION:
>>> "Don't crash if + given a friend pseudo-instantiation". The
>>> DECL_USE_TEMPLATE should likely + inform the PRIMARY parameter of
>>> dump_template_parms.), and
>>
>> Good question. It seems that the existing
>> !DECL_FRIEND_PSEUDO_TEMPLATE_INSTANTIATION has mostly been excluding the
>> most general template; removing that line changes things like
>>
>> int bar(T) [with T = int]
>>
>> to
>>
>> int bar<T>(T) [with T = int]
>>
>>
>>
>>> + 4. either
>>> + - flags requests to show no function arguments, in which case
>>> deduced + types could be hidden, or
>>> + - at least one function template argument was given explicitly, or
>>> + - we're printing a DWARF name,
>>
>> ...but perhaps this can replace the above.
>
> I'll rerun the tests with DECL_USE_TEMPLATE moved to the PRIMARY parameter of
> dump_template_parms.
>
>> Though, why do we want
>> different behavior here when printing a DWARF name?
>
> Sorry, I should have asked ... I also added the same issue as an open question
> on the diagnose_as patch. When I ran the whole testsuite I had failures in the
> DWARF tests. This change resolved them. I don't know enough about how those
> strings are used and whether they may change between GCC versions. Anyway, the
> DWARF strings in that test had only the function name and template argument
> list (i.e. no function arguments).
If they only have arguments, isn't TFF_NO_FUNCTION_ARGUMENTS set?
> If the template argument list was removed
> (as was the case without the condition here), the DWARF strings lost important
> information, giving several different functions the same name. That seemed
> like a regression. So either the DWARF strings need to include the function
> arguments, or we need this condition to keep showing the template arguments
> independent of whether they were explicitly given or not.
>
>>> + 5. either
>>> + - t is a member friend template of a template class (see
>>> DECL_TI_TEMPLATE + documentation), or
>>> + -
>>
>> Missing the last item. :)
>
> Oh, I got distracted while trying to figure this out. :) I'll try to
> understand why PRIMARY_TEMPLATE_P (DECL_TI_TEMPLATE (t)) is needed and
> document it.
>
^ permalink raw reply [flat|nested] 24+ messages in thread
* Re: [RFC] c++: Print function template parms when relevant (was: [PATCH v4] c++: Add gnu::diagnose_as attribute)
2021-11-17 18:25 ` Jason Merrill
@ 2021-11-17 22:51 ` Matthias Kretz
2021-11-18 19:24 ` Jason Merrill
0 siblings, 1 reply; 24+ messages in thread
From: Matthias Kretz @ 2021-11-17 22:51 UTC (permalink / raw)
To: gcc-patches, Jason Merrill
On Wednesday, 17 November 2021 19:25:46 CET Jason Merrill wrote:
> On 11/17/21 04:04, Matthias Kretz wrote:
> > On Wednesday, 17 November 2021 07:09:18 CET Jason Merrill wrote:
> >>> - if (CHECKING_P)
> >>> - SET_NON_DEFAULT_TEMPLATE_ARGS_COUNT (a, TREE_VEC_LENGTH (a));
> >>> + SET_NON_DEFAULT_TEMPLATE_ARGS_COUNT (a, nondefault);
> >>
> >> should have been
> >>
> >> if (CHECKING_P || nondefault != TREE_VEC_LENGTH (a))
> >>
> >> SET_NON_DEFAULT_TEMPLATE_ARGS_COUNT (a, nondefault);
> >
> > TBH, I don't understand the purpose of CHECKING_P here, or rather it makes
> > me nervous because AFAIU I'm only testing with CHECKING_P enabled. Why
> > make behavior dependent on CHECKING_P? I expected CHECKING_P to basically
> > only add more assertions.
>
> The idea when NON_DEFAULT_TEMPLATE_ARGS_COUNT was added years back was
> to leave the TREE_CHAIN null when !CHECKING_P and treat that as
> equivalent to TREE_VEC_LENGTH (args). But perhaps you're right that
> it's not a savings worth the complexity.
Thanks, now I understand.
> >>> (copy_template_args): Jason?
> >>
> >> Only copy the non-default template args count on TREE_VECs that should
> >> have it.
> >
> > Why not simply set the count on all args? Is it a performance concern? The
> > INTEGER_CST the TREE_CHAIN has to point to exists anyway, so it's not
> > wasting any memory, right?
>
> In this case the TREE_VEC we're excluding is the one wrapping multiple
> levels of template args; it doesn't contain args directly, so setting
> NON_DEFAULT_ARGS_COUNT on it doesn't make sense.
Right, I had already added a `gcc_assert (!TMPL_ARGS_HAVE_MULTIPLE_LEVELS
(args))` to my new set_non_default_template_args_count function and found cp/
constraint.cc:2896 (get_mapped_args), which calls
SET_NON_DEFAULT_TEMPLATE_ARGS_COUNT on the outer TREE_VEC. Was this supposed
to apply to all inner TREE_VECs? Or is deleting the line the correct fix?
> >>> + /* Pretty print only template instantiations. Don't pretty print
> >>> explicit
> >>> + specializations like 'template <> void fun<int> (int)'.
> >>
> >> This seems like a significant change of behavior unrelated to printing
> >> default template arguments. What's the rationale for handling
> >> specializations differently from instantiations?
> >
> > Right, this is about "The general idea of this change is to print template
> > parms wherever they would appear in the source code as well".
> >
> > Initially, the change to print function template arguments/parameters only
> > if the args were explicitly specified lead to printing 'void fun (T)
> > [with T = ...]' or 'template <> void fun (int)'. Both are not telling the
> > full story, even if the former is how the function would be called.
>
> and the latter is how I expect the specialization to be declared, not
> with the deducible template argument made explicit.
You're right. From my tests:
template <class a>
[[deprecated]] void f4(a);
template <>
[[deprecated]] void f4<int>(int);
template <>
[[deprecated]] void f4(float);
f4(1.); // { dg-warning "'void f4\\(a\\) .with a = double.'" }
f4(1); // { dg-warning "'void f4<int>\\(int\\)'" }
f4(1.f); // { dg-warning "'void f4\\(float\\)'" }
So how it's printed depends on how the specialization is declared. It just
falls out that way and it didn't seem awfully wrong... ;)
> > But if the reader
> > should quickly recognize what code is getting called, it is helpful to see
> > right away that a function template specialization is called. (It might
> > also reveal an implementation detail of a library, so it's not 100%
> > obvious how to choose here.) Also, saying 'T = int' is kind of wrong.
> > Yes, 'int' was deduced. But there's no T in fun<int>:
> >
> > template <class T> void fun (T);
> > template <> void fun<int> (int);
>
> There's a T in the template, and as you said above, that's how it's
> called (and mangled).
>
> > __FUNCTION__ was 'fun<int>' all the time, but __PRETTY_FUNCTION__ was
> > 'void
> > fun(T) [with T = int]'.
>
> Isn't that true for instantiations, as well?
No, instantiations don't have template args/parms in __FUNCTION__.
> > It's more consistent that __PRETTY_FUNCTION__ contains __FUNCTION__, IMHO
>
> I suppose, but I don't see that as a strong enough motivation to mix
> this up.
What about
template <class T> void f();
template <> void f<int>();
With -fpretty-templates shouldn't it print as 'void f<T>() [with T = float]'
and 'void f<int>()'? Yes, it's probably too subtle for most users to notice
the difference. But I find it's more consistent this way.
> > so it would have to be at least 'void fun<int>(T) [with T
> > = int]'. But that's strange: How it uses T and int for the same type. So I
> > settled on 'void fun<int>(int)'.
> >
> >> I also don't understand the purpose of TFF_AS_PRIMARY.
> >
> > dump_function_decl generalizes the TEMPLATE_DECL (if flag_pretty_templates
> > is true) and, before this change, passes the generalized TEMPLATE_DECL to
> > dump_type (... DECL_CONTEXT (t) ...) and dump_function_name (... t ...).
> > That's why the whole template is printed as primary template (i.e. with
> > template parms instead of template args, as is needed for
> > flag_pretty_templates). But this drops the count of non-default template
> > args.
> Ah, you're trying to omit defaulted parms from the <list>? I'm not sure
> that's necessary, leaving them out of the [with ...] list should be
> sufficient.
I was thinking about all the std::allocator defaults in the standard library.
I don't want to see them. E.g. vector<int>::clear() on const object:
error: passing 'const std::vector<int>' as 'this' argument discards qualifiers
[...]/stl_vector.h:1498:7: note: in call to 'void std::vector<_Tp,
_Alloc>::clear() [with _Tp = int; _Alloc = std::allocator<int>]'
With my patch the last line becomes
[...]/stl_vector.h:1498:7: note: in call to 'void std::vector<_Tp>::clear()
[with _Tp = int]'
Another case I didn't consider before:
template <class T, class U = int> struct A {
[[deprecated]] void f(U);
};
A<float> a; a.f(1);
With my patch it prints 'void A<T>::f(U) [with T = float]', with your
suggestion 'void A<T, U>::f(U) [with T = float]'. Both are missing important
information in the substitution list, IMHO. Would 'void A<T, U = int>::f(U)
[with T = float]' be an improvement? Or should find_typenames (in cp/error.c)
find defaulted template parms and add them to its list? IIUC find_typenames
would find all template parms and couldn't know whether they're defaulted.
> >>> + 4. either
> >>> + - flags requests to show no function arguments, in which case
> >>> deduced + types could be hidden, or
> >>> + - at least one function template argument was given explicitly,
> >>> or
> >>> + - we're printing a DWARF name,
> >>
> >> Though, why do we want
> >> different behavior here when printing a DWARF name?
> >
> > Sorry, I should have asked ... I also added the same issue as an open
> > question on the diagnose_as patch. When I ran the whole testsuite I had
> > failures in the DWARF tests. This change resolved them. I don't know
> > enough about how those strings are used and whether they may change
> > between GCC versions. Anyway, the DWARF strings in that test had only the
> > function name and template argument list (i.e. no function arguments).
>
> If they only have arguments, isn't TFF_NO_FUNCTION_ARGUMENTS set?
No, it isn't set. I could try to set it for DWARF names. It sounds like the
right solution here.
--
──────────────────────────────────────────────────────────────────────────
Dr. Matthias Kretz https://mattkretz.github.io
GSI Helmholtz Centre for Heavy Ion Research https://gsi.de
stdₓ::simd
──────────────────────────────────────────────────────────────────────────
^ permalink raw reply [flat|nested] 24+ messages in thread
* Re: [RFC] c++: Print function template parms when relevant (was: [PATCH v4] c++: Add gnu::diagnose_as attribute)
2021-11-17 22:51 ` Matthias Kretz
@ 2021-11-18 19:24 ` Jason Merrill
2021-11-19 9:53 ` Matthias Kretz
0 siblings, 1 reply; 24+ messages in thread
From: Jason Merrill @ 2021-11-18 19:24 UTC (permalink / raw)
To: Matthias Kretz, gcc-patches
On 11/17/21 17:51, Matthias Kretz wrote:
> On Wednesday, 17 November 2021 19:25:46 CET Jason Merrill wrote:
>> On 11/17/21 04:04, Matthias Kretz wrote:
>>> On Wednesday, 17 November 2021 07:09:18 CET Jason Merrill wrote:
>>>>> - if (CHECKING_P)
>>>>> - SET_NON_DEFAULT_TEMPLATE_ARGS_COUNT (a, TREE_VEC_LENGTH (a));
>>>>> + SET_NON_DEFAULT_TEMPLATE_ARGS_COUNT (a, nondefault);
>>>>
>>>> should have been
>>>>
>>>> if (CHECKING_P || nondefault != TREE_VEC_LENGTH (a))
>>>>
>>>> SET_NON_DEFAULT_TEMPLATE_ARGS_COUNT (a, nondefault);
>>>
>>> TBH, I don't understand the purpose of CHECKING_P here, or rather it makes
>>> me nervous because AFAIU I'm only testing with CHECKING_P enabled. Why
>>> make behavior dependent on CHECKING_P? I expected CHECKING_P to basically
>>> only add more assertions.
>>
>> The idea when NON_DEFAULT_TEMPLATE_ARGS_COUNT was added years back was
>> to leave the TREE_CHAIN null when !CHECKING_P and treat that as
>> equivalent to TREE_VEC_LENGTH (args). But perhaps you're right that
>> it's not a savings worth the complexity.
>
> Thanks, now I understand.
>
>>>>> (copy_template_args): Jason?
>>>>
>>>> Only copy the non-default template args count on TREE_VECs that should
>>>> have it.
>>>
>>> Why not simply set the count on all args? Is it a performance concern? The
>>> INTEGER_CST the TREE_CHAIN has to point to exists anyway, so it's not
>>> wasting any memory, right?
>>
>> In this case the TREE_VEC we're excluding is the one wrapping multiple
>> levels of template args; it doesn't contain args directly, so setting
>> NON_DEFAULT_ARGS_COUNT on it doesn't make sense.
>
> Right, I had already added a `gcc_assert (!TMPL_ARGS_HAVE_MULTIPLE_LEVELS
> (args))` to my new set_non_default_template_args_count function and found cp/
> constraint.cc:2896 (get_mapped_args), which calls
> SET_NON_DEFAULT_TEMPLATE_ARGS_COUNT on the outer TREE_VEC. Was this supposed
> to apply to all inner TREE_VECs? Or is deleting the line the correct fix?
That should apply to the inner TREE_VECs (and probably use list.length)
>>>>> + /* Pretty print only template instantiations. Don't pretty print
>>>>> explicit
>>>>> + specializations like 'template <> void fun<int> (int)'.
>>>>
>>>> This seems like a significant change of behavior unrelated to printing
>>>> default template arguments. What's the rationale for handling
>>>> specializations differently from instantiations?
>>>
>>> Right, this is about "The general idea of this change is to print template
>>> parms wherever they would appear in the source code as well".
>>>
>>> Initially, the change to print function template arguments/parameters only
>>> if the args were explicitly specified lead to printing 'void fun (T)
>>> [with T = ...]' or 'template <> void fun (int)'. Both are not telling the
>>> full story, even if the former is how the function would be called.
>>
>> and the latter is how I expect the specialization to be declared, not
>> with the deducible template argument made explicit.
>
> You're right. From my tests:
>
> template <class a>
> [[deprecated]] void f4(a);
>
> template <>
> [[deprecated]] void f4<int>(int);
>
> template <>
> [[deprecated]] void f4(float);
>
> f4(1.); // { dg-warning "'void f4\\(a\\) .with a = double.'" }
> f4(1); // { dg-warning "'void f4<int>\\(int\\)'" }
> f4(1.f); // { dg-warning "'void f4\\(float\\)'" }
>
> So how it's printed depends on how the specialization is declared. It just
> falls out that way and it didn't seem awfully wrong... ;)
>
>>> But if the reader
>>> should quickly recognize what code is getting called, it is helpful to see
>>> right away that a function template specialization is called. (It might
>>> also reveal an implementation detail of a library, so it's not 100%
>>> obvious how to choose here.) Also, saying 'T = int' is kind of wrong.
>>> Yes, 'int' was deduced. But there's no T in fun<int>:
>>>
>>> template <class T> void fun (T);
>>> template <> void fun<int> (int);
>>
>> There's a T in the template, and as you said above, that's how it's
>> called (and mangled).
>>
>>> __FUNCTION__ was 'fun<int>' all the time, but __PRETTY_FUNCTION__ was
>>> 'void
>>> fun(T) [with T = int]'.
>>
>> Isn't that true for instantiations, as well?
>
> No, instantiations don't have template args/parms in __FUNCTION__.
Hmm, that inconsistency seems like a bug, though I'm not sure whether it
should have the template args or not; I lean toward not. The standard
says that the value of __func__ is implementation-defined, the GCC
manual says it's "the unadorned name of the function".
>>> It's more consistent that __PRETTY_FUNCTION__ contains __FUNCTION__, IMHO
>>
>> I suppose, but I don't see that as a strong enough motivation to mix
>> this up.
>
> What about
>
> template <class T> void f();
> template <> void f<int>();
>
> With -fpretty-templates shouldn't it print as 'void f<T>() [with T = float]'
> and 'void f<int>()'? Yes, it's probably too subtle for most users to notice
> the difference. But I find it's more consistent this way.
I disagree; the function signature is the same whether a particular
function is an explicit specialization or an instantiation.
>>> so it would have to be at least 'void fun<int>(T) [with T
>>> = int]'. But that's strange: How it uses T and int for the same type. So I
>>> settled on 'void fun<int>(int)'.
>>>
>>>> I also don't understand the purpose of TFF_AS_PRIMARY.
>>>
>>> dump_function_decl generalizes the TEMPLATE_DECL (if flag_pretty_templates
>>> is true) and, before this change, passes the generalized TEMPLATE_DECL to
>>> dump_type (... DECL_CONTEXT (t) ...) and dump_function_name (... t ...).
>>> That's why the whole template is printed as primary template (i.e. with
>>> template parms instead of template args, as is needed for
>>> flag_pretty_templates). But this drops the count of non-default template
>>> args.
>> Ah, you're trying to omit defaulted parms from the <list>? I'm not sure
>> that's necessary, leaving them out of the [with ...] list should be
>> sufficient.
>
> I was thinking about all the std::allocator defaults in the standard library.
> I don't want to see them. E.g. vector<int>::clear() on const object:
>
> error: passing 'const std::vector<int>' as 'this' argument discards qualifiers
> [...]/stl_vector.h:1498:7: note: in call to 'void std::vector<_Tp,
> _Alloc>::clear() [with _Tp = int; _Alloc = std::allocator<int>]'
>
> With my patch the last line becomes
> [...]/stl_vector.h:1498:7: note: in call to 'void std::vector<_Tp>::clear()
> [with _Tp = int]'
>
>
> Another case I didn't consider before:
>
> template <class T, class U = int> struct A {
> [[deprecated]] void f(U);
> };
>
> A<float> a; a.f(1);
>
> With my patch it prints 'void A<T>::f(U) [with T = float]', with your
> suggestion 'void A<T, U>::f(U) [with T = float]'. Both are missing important
> information in the substitution list, IMHO. Would 'void A<T, U = int>::f(U)
> [with T = float]' be an improvement? Or should find_typenames (in cp/error.c)
> find defaulted template parms and add them to its list? IIUC find_typenames
> would find all template parms and couldn't know whether they're defaulted.
That sounds good: omit defaulted parms only if they don't appear in the
signature (other than as another default template argument).
>>>>> + 4. either
>>>>> + - flags requests to show no function arguments, in which case
>>>>> deduced + types could be hidden, or
>>>>> + - at least one function template argument was given explicitly,
>>>>> or
>>>>> + - we're printing a DWARF name,
>>>>
>>>> Though, why do we want
>>>> different behavior here when printing a DWARF name?
>>>
>>> Sorry, I should have asked ... I also added the same issue as an open
>>> question on the diagnose_as patch. When I ran the whole testsuite I had
>>> failures in the DWARF tests. This change resolved them. I don't know
>>> enough about how those strings are used and whether they may change
>>> between GCC versions. Anyway, the DWARF strings in that test had only the
>>> function name and template argument list (i.e. no function arguments).
>>
>> If they only have arguments, isn't TFF_NO_FUNCTION_ARGUMENTS set?
>
> No, it isn't set. I could try to set it for DWARF names. It sounds like the
> right solution here.
I agree.
Jason
^ permalink raw reply [flat|nested] 24+ messages in thread
* Re: [RFC] c++: Print function template parms when relevant (was: [PATCH v4] c++: Add gnu::diagnose_as attribute)
2021-11-18 19:24 ` Jason Merrill
@ 2021-11-19 9:53 ` Matthias Kretz
2021-11-19 12:02 ` Matthias Kretz
2021-11-19 22:26 ` Jason Merrill
0 siblings, 2 replies; 24+ messages in thread
From: Matthias Kretz @ 2021-11-19 9:53 UTC (permalink / raw)
To: gcc-patches, Jason Merrill
On Thursday, 18 November 2021 20:24:36 CET Jason Merrill wrote:
> On 11/17/21 17:51, Matthias Kretz wrote:
> > Right, I had already added a `gcc_assert (!TMPL_ARGS_HAVE_MULTIPLE_LEVELS
> > (args))` to my new set_non_default_template_args_count function and found
> > cp/ constraint.cc:2896 (get_mapped_args), which calls
> > SET_NON_DEFAULT_TEMPLATE_ARGS_COUNT on the outer TREE_VEC. Was this
> > supposed to apply to all inner TREE_VECs? Or is deleting the line the
> > correct fix?
>
> That should apply to the inner TREE_VECs (and probably use list.length)
Like this?
@@ -2890,10 +2890,11 @@ get_mapped_args (tree map)
tree level = make_tree_vec (list.length ());
for (unsigned j = 0; j < list.length(); ++j)
TREE_VEC_ELT (level, j) = list[j];
+ /* None of the args at any level are defaulted. */
+ SET_NON_DEFAULT_TEMPLATE_ARGS_COUNT (level, list.length());
SET_TMPL_ARGS_LEVEL (args, i + 1, level);
list.release ();
}
- SET_NON_DEFAULT_TEMPLATE_ARGS_COUNT (args, 0);
return args;
}
> >>> __FUNCTION__ was 'fun<int>' all the time, but __PRETTY_FUNCTION__ was
> >>> 'void fun(T) [with T = int]'.
> >>
> >> Isn't that true for instantiations, as well?
> >
> > No, instantiations don't have template args/parms in __FUNCTION__.
>
> Hmm, that inconsistency seems like a bug, though I'm not sure whether it
> should have the template args or not; I lean toward not. The standard
> says that the value of __func__ is implementation-defined, the GCC
> manual says it's "the unadorned name of the function".
So you think f1<int> in testsuite/g++.old-deja/g++.ext/pretty3.C needs to test
for
if (strcmp (function, "f1"))
bad = true;
if (strcmp (pretty, "void f1(T) [with T = int]"))
bad = true;
Or should the latter be "void f1(int)"?
> >>> It's more consistent that __PRETTY_FUNCTION__ contains __FUNCTION__,
> >>> IMHO
> >>
> >> I suppose, but I don't see that as a strong enough motivation to mix
> >> this up.
> >
> > What about
> >
> > template <class T> void f();
> > template <> void f<int>();
> >
> > With -fpretty-templates shouldn't it print as 'void f<T>() [with T =
> > float]' and 'void f<int>()'? Yes, it's probably too subtle for most users
> > to notice the difference. But I find it's more consistent this way.
>
> I disagree; the function signature is the same whether a particular
> function is an explicit specialization or an instantiation.
Yes, the call signature is the same. But what it calls is different. There's
no obvious answer for my stated goal "print template parms wherever they
would appear in the source code as well", since it depends on whether the user
is interested in recognizing the exact function body that was called.
My motivation for printing a function template specialization differently is:
1. It's a different function definition that's being called. The user (caller)
might be surprised to realize this is the case as he forgot about the
specialization and was expecting his change to the general template to make a
difference.
2. There's no T in
template <> void f<int>() {
// no T here, but of course I can define one:
using T = int;
}
so presenting the function that was called as 'void f<T>() [with T = int]' is
not exactly correct. In this case it wasn't even the case that T was deduced
to be 'int', which we could argue to be useful information that might get
lost.
For
template <class T> void f(T);
template <> void f(int);
the whole story is "'void f(int)' was called for 'template <class T> void f(T)
[with T = int]'". What the user wants to see depends on what is more important
to fix the bug: that T was deduced to be int, or that the specialization of f
was called instead of the general template. I'd still go with 'void f(int)',
though I'd be happier if I had some indication that a template was involved.
> >> Ah, you're trying to omit defaulted parms from the <list>? I'm not sure
> >> that's necessary, leaving them out of the [with ...] list should be
> >> sufficient.
> >
> > I was thinking about all the std::allocator defaults in the standard
> > library. I don't want to see them. E.g. vector<int>::clear() on const
> > object:
> >
> > error: passing 'const std::vector<int>' as 'this' argument discards
> > qualifiers [...]/stl_vector.h:1498:7: note: in call to 'void
> > std::vector<_Tp, _Alloc>::clear() [with _Tp = int; _Alloc =
> > std::allocator<int>]'
> >
> > With my patch the last line becomes
> > [...]/stl_vector.h:1498:7: note: in call to 'void
> > std::vector<_Tp>::clear() [with _Tp = int]'
> >
> >
> > Another case I didn't consider before:
> >
> > template <class T, class U = int> struct A {
> >
> > [[deprecated]] void f(U);
> >
> > };
> >
> > A<float> a; a.f(1);
> >
> > With my patch it prints 'void A<T>::f(U) [with T = float]', with your
> > suggestion 'void A<T, U>::f(U) [with T = float]'. Both are missing
> > important information in the substitution list, IMHO. Would 'void A<T, U
> > = int>::f(U) [with T = float]' be an improvement? Or should
> > find_typenames (in cp/error.c) find defaulted template parms and add them
> > to its list? IIUC find_typenames would find all template parms and
> > couldn't know whether they're defaulted.
>
> That sounds good: omit defaulted parms only if they don't appear in the
> signature (other than as another default template argument).
Let me check whether I have the right idea:
I could extend find_typenames (which walks the complete) tree to look for
TEMPLATE_TYPE_PARM (and the 3 others I don't recall right now). But since that
walks the *complete* tree, it'll simply find all parms with no indication
whether they appear in the signature. Ideas:
1. Count occurrences: with 2 occurrences, one of them must be a use in the
signature.
2. Walk only over TYPE_ARG_TYPES (TREE_TYPE (DECL_TEMPLATE_RESULT (fn))) to
collect TEMPLATE_TYPE_PARMs.
--
──────────────────────────────────────────────────────────────────────────
Dr. Matthias Kretz https://mattkretz.github.io
GSI Helmholtz Centre for Heavy Ion Research https://gsi.de
stdₓ::simd
──────────────────────────────────────────────────────────────────────────
^ permalink raw reply [flat|nested] 24+ messages in thread
* Re: [RFC] c++: Print function template parms when relevant (was: [PATCH v4] c++: Add gnu::diagnose_as attribute)
2021-11-19 9:53 ` Matthias Kretz
@ 2021-11-19 12:02 ` Matthias Kretz
2021-11-19 22:26 ` Jason Merrill
1 sibling, 0 replies; 24+ messages in thread
From: Matthias Kretz @ 2021-11-19 12:02 UTC (permalink / raw)
To: gcc-patches, Jason Merrill
On Friday, 19 November 2021 10:53:27 CET Matthias Kretz wrote:
> > >> Ah, you're trying to omit defaulted parms from the <list>? I'm not
> > >> sure
> > >> that's necessary, leaving them out of the [with ...] list should be
> > >> sufficient.
> > >
> > > I was thinking about all the std::allocator defaults in the standard
> > > library. I don't want to see them. E.g. vector<int>::clear() on const
> > > object:
> > >
> > > error: passing 'const std::vector<int>' as 'this' argument discards
> > > qualifiers [...]/stl_vector.h:1498:7: note: in call to 'void
> > > std::vector<_Tp, _Alloc>::clear() [with _Tp = int; _Alloc =
> > > std::allocator<int>]'
> > >
> > > With my patch the last line becomes
> > > [...]/stl_vector.h:1498:7: note: in call to 'void
> > > std::vector<_Tp>::clear() [with _Tp = int]'
> > >
> > >
> > > Another case I didn't consider before:
> > >
> > > template <class T, class U = int> struct A {
> > >
> > > [[deprecated]] void f(U);
> > >
> > > };
> > >
> > > A<float> a; a.f(1);
> > >
> > > With my patch it prints 'void A<T>::f(U) [with T = float]', with your
> > > suggestion 'void A<T, U>::f(U) [with T = float]'. Both are missing
> > > important information in the substitution list, IMHO. Would 'void A<T, U
> > > = int>::f(U) [with T = float]' be an improvement? Or should
> > > find_typenames (in cp/error.c) find defaulted template parms and add
> > > them
> > > to its list? IIUC find_typenames would find all template parms and
> > > couldn't know whether they're defaulted.
> >
> > That sounds good: omit defaulted parms only if they don't appear in the
> > signature (other than as another default template argument).
>
> Let me check whether I have the right idea:
>
> I could extend find_typenames (which walks the complete) tree to look for
> TEMPLATE_TYPE_PARM (and the 3 others I don't recall right now). But since
> that walks the *complete* tree, it'll simply find all parms with no
> indication whether they appear in the signature. Ideas:
>
> 1. Count occurrences: with 2 occurrences, one of them must be a use in the
> signature.
>
> 2. Walk only over TYPE_ARG_TYPES (TREE_TYPE (DECL_TEMPLATE_RESULT (fn))) to
> collect TEMPLATE_TYPE_PARMs.
I tried the latter:
@@ -1641,8 +1652,11 @@ dump_substitution (cxx_pretty_printer *pp,
&& !(flags & TFF_NO_TEMPLATE_BINDINGS))
{
vec<tree, va_gc> *typenames = t ? find_typenames (t) : NULL;
- dump_template_bindings (pp, template_parms, template_args, typenames,
- flags);
+ tree fn_arguments = TYPE_ARG_TYPES (TREE_TYPE (DECL_TEMPLATE_RESULT
(t)));
+ tree used_template_parms = find_template_parameters (fn_arguments,
+ template_parms);
+ dump_template_bindings (pp, template_parms, template_args,
+ used_template_parms, typenames, flags);
}
}
Now in dump_template_bindings it skips all defaulted template_parms that are
not in used_template_parms. Makes this test pass:
template <class T>
struct id
{ using type = T; };
template <class T0, class T1 = int>
struct A
{
template <class U0 = const T1&>
[[deprecated]] static void
f(typename id<U0>::type);
};
int main()
{
A<int>::f(0); // { dg-warning "'static void A<T0>::f\\(typename
id<U0>::type\\) .with U0 = const int&; T0 = int; typename id<U0>::type = const
int&.'" }
}
--
──────────────────────────────────────────────────────────────────────────
Dr. Matthias Kretz https://mattkretz.github.io
GSI Helmholtz Centre for Heavy Ion Research https://gsi.de
stdₓ::simd
──────────────────────────────────────────────────────────────────────────
^ permalink raw reply [flat|nested] 24+ messages in thread
* Re: [RFC] c++: Print function template parms when relevant (was: [PATCH v4] c++: Add gnu::diagnose_as attribute)
2021-11-19 9:53 ` Matthias Kretz
2021-11-19 12:02 ` Matthias Kretz
@ 2021-11-19 22:26 ` Jason Merrill
2021-11-19 23:11 ` Matthias Kretz
2021-11-26 15:23 ` [PATCH 0/2] " Matthias Kretz
1 sibling, 2 replies; 24+ messages in thread
From: Jason Merrill @ 2021-11-19 22:26 UTC (permalink / raw)
To: Matthias Kretz, gcc-patches
On 11/19/21 04:53, Matthias Kretz wrote:
> On Thursday, 18 November 2021 20:24:36 CET Jason Merrill wrote:
>> On 11/17/21 17:51, Matthias Kretz wrote:
>>> Right, I had already added a `gcc_assert (!TMPL_ARGS_HAVE_MULTIPLE_LEVELS
>>> (args))` to my new set_non_default_template_args_count function and found
>>> cp/ constraint.cc:2896 (get_mapped_args), which calls
>>> SET_NON_DEFAULT_TEMPLATE_ARGS_COUNT on the outer TREE_VEC. Was this
>>> supposed to apply to all inner TREE_VECs? Or is deleting the line the
>>> correct fix?
>>
>> That should apply to the inner TREE_VECs (and probably use list.length)
>
> Like this?
Yes.
> @@ -2890,10 +2890,11 @@ get_mapped_args (tree map)
> tree level = make_tree_vec (list.length ());
> for (unsigned j = 0; j < list.length(); ++j)
> TREE_VEC_ELT (level, j) = list[j];
> + /* None of the args at any level are defaulted. */
> + SET_NON_DEFAULT_TEMPLATE_ARGS_COUNT (level, list.length());
> SET_TMPL_ARGS_LEVEL (args, i + 1, level);
> list.release ();
> }
> - SET_NON_DEFAULT_TEMPLATE_ARGS_COUNT (args, 0);
>
> return args;
> }
>
>>>>> __FUNCTION__ was 'fun<int>' all the time, but __PRETTY_FUNCTION__ was
>>>>> 'void fun(T) [with T = int]'.
>>>>
>>>> Isn't that true for instantiations, as well?
>>>
>>> No, instantiations don't have template args/parms in __FUNCTION__.
>>
>> Hmm, that inconsistency seems like a bug, though I'm not sure whether it
>> should have the template args or not; I lean toward not. The standard
>> says that the value of __func__ is implementation-defined, the GCC
>> manual says it's "the unadorned name of the function".
>
> So you think f1<int> in testsuite/g++.old-deja/g++.ext/pretty3.C needs to test
> for
>
> if (strcmp (function, "f1"))
> bad = true;
> if (strcmp (pretty, "void f1(T) [with T = int]"))
> bad = true;
I think so.
>>>>> It's more consistent that __PRETTY_FUNCTION__ contains __FUNCTION__,
>>>>> IMHO
>>>>
>>>> I suppose, but I don't see that as a strong enough motivation to mix
>>>> this up.
>>>
>>> What about
>>>
>>> template <class T> void f();
>>> template <> void f<int>();
>>>
>>> With -fpretty-templates shouldn't it print as 'void f<T>() [with T =
>>> float]' and 'void f<int>()'? Yes, it's probably too subtle for most users
>>> to notice the difference. But I find it's more consistent this way.
>>
>> I disagree; the function signature is the same whether a particular
>> function is an explicit specialization or an instantiation.
>
> Yes, the call signature is the same. But what it calls is different. There's
> no obvious answer for my stated goal "print template parms wherever they
> would appear in the source code as well", since it depends on whether the user
> is interested in recognizing the exact function body that was called.
>
> My motivation for printing a function template specialization differently is:
>
> 1. It's a different function definition that's being called. The user (caller)
> might be surprised to realize this is the case as he forgot about the
> specialization and was expecting his change to the general template to make a
> difference.
>
> 2. There's no T in
>
> template <> void f<int>() {
> // no T here, but of course I can define one:
> using T = int;
> }
>
> so presenting the function that was called as 'void f<T>() [with T = int]' is
> not exactly correct. In this case it wasn't even the case that T was deduced
> to be 'int', which we could argue to be useful information that might get
> lost.
On the other hand, this tells us what template this specialization is
specializing, which could be unclear if there are multiple overloaded
function templates.
There's always -fno-pretty-templates if you want the form without
template args.
Incidentally, the contents of __PRETTY_FUNCTION__ probably shouldn't
vary with that flag...
> For
>
> template <class T> void f(T);
> template <> void f(int);
>
> the whole story is "'void f(int)' was called for 'template <class T> void f(T)
> [with T = int]'". What the user wants to see depends on what is more important
> to fix the bug: that T was deduced to be int, or that the specialization of f
> was called instead of the general template. I'd still go with 'void f(int)',
> though I'd be happier if I had some indication that a template was involved.
The current form tells you about the template, and the line number
points you at the declaration.
>>>> Ah, you're trying to omit defaulted parms from the <list>? I'm not sure
>>>> that's necessary, leaving them out of the [with ...] list should be
>>>> sufficient.
>>>
>>> I was thinking about all the std::allocator defaults in the standard
>>> library. I don't want to see them. E.g. vector<int>::clear() on const
>>> object:
>>>
>>> error: passing 'const std::vector<int>' as 'this' argument discards
>>> qualifiers [...]/stl_vector.h:1498:7: note: in call to 'void
>>> std::vector<_Tp, _Alloc>::clear() [with _Tp = int; _Alloc =
>>> std::allocator<int>]'
>>>
>>> With my patch the last line becomes
>>> [...]/stl_vector.h:1498:7: note: in call to 'void
>>> std::vector<_Tp>::clear() [with _Tp = int]'
>>>
>>>
>>> Another case I didn't consider before:
>>>
>>> template <class T, class U = int> struct A {
>>>
>>> [[deprecated]] void f(U);
>>>
>>> };
>>>
>>> A<float> a; a.f(1);
>>>
>>> With my patch it prints 'void A<T>::f(U) [with T = float]', with your
>>> suggestion 'void A<T, U>::f(U) [with T = float]'. Both are missing
>>> important information in the substitution list, IMHO. Would 'void A<T, U
>>> = int>::f(U) [with T = float]' be an improvement? Or should
>>> find_typenames (in cp/error.c) find defaulted template parms and add them
>>> to its list? IIUC find_typenames would find all template parms and
>>> couldn't know whether they're defaulted.
>>
>> That sounds good: omit defaulted parms only if they don't appear in the
>> signature (other than as another default template argument).
>
> Let me check whether I have the right idea:
>
> I could extend find_typenames (which walks the complete) tree to look for
> TEMPLATE_TYPE_PARM (and the 3 others I don't recall right now). But since that
> walks the *complete* tree, it'll simply find all parms with no indication
> whether they appear in the signature. Ideas:
Hmm, since it walks DECL_TEMPLATE_RESULT, I wouldn't expect it to find
template parms that aren't in the function signature.
> 1. Count occurrences: with 2 occurrences, one of them must be a use in the
> signature.
>
> 2. Walk only over TYPE_ARG_TYPES (TREE_TYPE (DECL_TEMPLATE_RESULT (fn))) to
> collect TEMPLATE_TYPE_PARMs.
Jason
^ permalink raw reply [flat|nested] 24+ messages in thread
* Re: [RFC] c++: Print function template parms when relevant (was: [PATCH v4] c++: Add gnu::diagnose_as attribute)
2021-11-19 22:26 ` Jason Merrill
@ 2021-11-19 23:11 ` Matthias Kretz
2021-11-26 15:23 ` [PATCH 0/2] " Matthias Kretz
1 sibling, 0 replies; 24+ messages in thread
From: Matthias Kretz @ 2021-11-19 23:11 UTC (permalink / raw)
To: gcc-patches, Jason Merrill; +Cc: Ville Voutilainen
On Friday, 19 November 2021 23:26:57 CET Jason Merrill wrote:
> On 11/19/21 04:53, Matthias Kretz wrote:
> > My motivation for printing a function template specialization differently
> > is:
> >
> > 1. It's a different function definition that's being called. The user
> > (caller) might be surprised to realize this is the case as he forgot
> > about the specialization and was expecting his change to the general
> > template to make a difference.
> >
> > 2. There's no T in
> >
> > template <> void f<int>() {
> > // no T here, but of course I can define one:
> > using T = int;
> > }
> >
> > so presenting the function that was called as 'void f<T>() [with T = int]'
> > is not exactly correct. In this case it wasn't even the case that T was
> > deduced to be 'int', which we could argue to be useful information that
> > might get lost.
>
> On the other hand, this tells us what template this specialization is
> specializing, which could be unclear if there are multiple overloaded
> function templates.
I don't disagree that printing the primary template + substitution has value.
I just believe that being more explicit about a function specialization
getting called has more value. But I guess we'll just have to live with our
disagreement.
I'll re-add the condition for "pretty-printing" specialized functions for the
next patch revision, unless someone else wants to & can convince you. :)
> There's always -fno-pretty-templates if you want the form without
> template args.
>
> Incidentally, the contents of __PRETTY_FUNCTION__ probably shouldn't
> vary with that flag...
That ship is sailing since -fpretty-templates was implemented AFAICS. Do you
want it to come back? ;)
> >> That sounds good: omit defaulted parms only if they don't appear in the
> >> signature (other than as another default template argument).
> >
> > Let me check whether I have the right idea:
> >
> > I could extend find_typenames (which walks the complete) tree to look for
> > TEMPLATE_TYPE_PARM (and the 3 others I don't recall right now). But since
> > that walks the *complete* tree, it'll simply find all parms with no
> > indication
> > whether they appear in the signature. Ideas:
>
> Hmm, since it walks DECL_TEMPLATE_RESULT, I wouldn't expect it to find
> template parms that aren't in the function signature.
Maybe I made an error elsewhere, but that's what I saw when I tried it. I'll
try again (next week, I think).
--
──────────────────────────────────────────────────────────────────────────
Dr. Matthias Kretz https://mattkretz.github.io
GSI Helmholtz Centre for Heavy Ion Research https://gsi.de
stdₓ::simd
──────────────────────────────────────────────────────────────────────────
^ permalink raw reply [flat|nested] 24+ messages in thread
* [PATCH 0/2] c++: Print function template parms when relevant (was: [PATCH v4] c++: Add gnu::diagnose_as attribute)
2021-11-19 22:26 ` Jason Merrill
2021-11-19 23:11 ` Matthias Kretz
@ 2021-11-26 15:23 ` Matthias Kretz
2021-11-26 15:24 ` [PATCH 1/2] c++: Print function template parms when relevant Matthias Kretz
2021-11-26 15:24 ` [PATCH 2/2] c++: Print function template parms when relevant [part 2] Matthias Kretz
1 sibling, 2 replies; 24+ messages in thread
From: Matthias Kretz @ 2021-11-26 15:23 UTC (permalink / raw)
To: gcc-patches, Jason Merrill
On Friday, 19 November 2021 23:26:57 CET Jason Merrill wrote:
> On 11/19/21 04:53, Matthias Kretz wrote:
> > On Thursday, 18 November 2021 20:24:36 CET Jason Merrill wrote:
> >> On 11/17/21 17:51, Matthias Kretz wrote:
> >>>>> __FUNCTION__ was 'fun<int>' all the time, but __PRETTY_FUNCTION__ was
> >>>>> 'void fun(T) [with T = int]'.
> >>>>
> >>>> Isn't that true for instantiations, as well?
> >>>
> >>> No, instantiations don't have template args/parms in __FUNCTION__.
> >>
> >> Hmm, that inconsistency seems like a bug, though I'm not sure whether it
> >> should have the template args or not; I lean toward not. The standard
> >> says that the value of __func__ is implementation-defined, the GCC
> >> manual says it's "the unadorned name of the function".
> >
> > So you think f1<int> in testsuite/g++.old-deja/g++.ext/pretty3.C needs to
> > test for
> >
> > if (strcmp (function, "f1"))
> >
> > bad = true;
> >
> > if (strcmp (pretty, "void f1(T) [with T = int]"))
> >
> > bad = true;
>
> I think so.
I just found out that the behavior of __FUNCTION__ and DWARF names is related
because both go through lang_decl_name in cp/error.c. I.e by removing the test
for pp_c_flag_gnu_v3 in dump_function_name and requesting
TFF_NO_FUNCTION_ARGUMENTS from lang_decl_name I turned the __FUNCTION__ string
into "f1<T>" / "f1<int>". I can filter the former by rejecting the most
general template (the DECL_USE_TEMPLATE in dump_function_name we were
wondering about). But I can't turn "f1<int>" into "f1" without adding the test
for pp_c_flag_gnu_v3 back in dump_function_name.
So far __FUNCTION__ and DWARF names want to be the same string. If you want to
keep it like this, let me know how the patch should go: "f1" or "f1<int>" (the
latter is the status quo and disambiguates different DWARF strings)
The __PRETTY_FUNCTION__ string wants to be "void f1<T>(T) [with T = int]",
i.e. with the function tparms, because the template args are marked as
explicitly specified. This depends on how the function specialization is
declared, i.e. 'template<> void f1(int)' vs 'template<> void f1<int>(int)'. I
don't know if I can/want to do anything about that. Is that an acceptable
result?
I'll send two patches: The first patch is what I'd push. The second restores
the diagnostics of specialized function templates.
> >> That sounds good: omit defaulted parms only if they don't appear in the
> >> signature (other than as another default template argument).
> >
> > Let me check whether I have the right idea:
> >
> > I could extend find_typenames (which walks the complete) tree to look for
> > TEMPLATE_TYPE_PARM (and the 3 others I don't recall right now). But since
> > that walks the *complete* tree, it'll simply find all parms with no
> > indication
> > whether they appear in the signature. Ideas:
> Hmm, since it walks DECL_TEMPLATE_RESULT, I wouldn't expect it to find
> template parms that aren't in the function signature.
You were right, walking TREE_TYPE (DECL_TEMPLATE_RESULT (t)) does what I need.
--
──────────────────────────────────────────────────────────────────────────
Dr. Matthias Kretz https://mattkretz.github.io
GSI Helmholtz Centre for Heavy Ion Research https://gsi.de
stdₓ::simd
──────────────────────────────────────────────────────────────────────────
^ permalink raw reply [flat|nested] 24+ messages in thread
* [PATCH 1/2] c++: Print function template parms when relevant
2021-11-26 15:23 ` [PATCH 0/2] " Matthias Kretz
@ 2021-11-26 15:24 ` Matthias Kretz
2021-12-02 8:35 ` [PATCH v2 " Matthias Kretz
2021-11-26 15:24 ` [PATCH 2/2] c++: Print function template parms when relevant [part 2] Matthias Kretz
1 sibling, 1 reply; 24+ messages in thread
From: Matthias Kretz @ 2021-11-26 15:24 UTC (permalink / raw)
To: gcc-patches, jason
[-- Attachment #1: Type: text/plain, Size: 6917 bytes --]
The choice when to print a function template parameter was still
suboptimal. That's because sometimes the function template parameter
list only adds noise, while in other situations the lack of a function
template parameter list makes diagnostic messages hard to understand.
The general idea of this change is to print template parms wherever they
would appear in the source code as well. Thus, the diagnostics code
needs to know whether any template parameter was given explicitly.
DWARF names of functions use dump_function_name to produce a name
without function arguments (function arguments are printed from
dump_function_decl). However, FLAGS should still state the intent of
printing a name without function arguments (TFF_NO_FUNCTION_ARGUMENTS so
that dump_function_name can decide correctly whether to call
dump_template_parms.
Based on an initial patch from Jason Merrill <jason@redhat.com>.
Signed-off-by: Matthias Kretz <m.kretz@gsi.de>
gcc/testsuite/ChangeLog:
* g++.dg/debug/dwarf2/template-params-12n.C: Optionally, allow
DW_AT_default_value.
* g++.dg/diagnostic/default-template-args-1.C: New test.
* g++.dg/diagnostic/default-template-args-2.C: New test.
* g++.dg/diagnostic/default-template-args-3.C: New test.
* g++.dg/diagnostic/default-template-args-4.C: New test.
* g++.dg/diagnostic/default-template-args-5.C: New test.
* g++.dg/diagnostic/param-type-mismatch-2.C: Expect template
parms in diagnostic.
* g++.dg/ext/pretty1.C: Expect function template specialization
to not pretty-print template parms.
* g++.old-deja/g++.ext/pretty3.C: Ditto.
* g++.old-deja/g++.pt/memtemp77.C: Ditto.
* g++.dg/goacc/template.C: Expect function template parms for
explicit arguments.
* g++.dg/gomp/declare-variant-7.C: Expect no function template
parms for deduced arguments.
* g++.dg/template/error40.C: Expect only non-default template
arguments in diagnostic.
gcc/cp/ChangeLog:
* constraint.cc (get_mapped_args): Remove incorrect non-default
args count on multi-level template args; instead set the
non-default args count on each inner TREE_VEC.
* cp-tree.h: Rewrite NON_DEFAULT_TEMPLATE_ARGS_COUNT
implementation to store the number explicitly specified
arguments in a TREE_LIST.
(EXPLICIT_TEMPLATE_ARGS_P): New.
(SET_EXPLICIT_TEMPLATE_ARGS_P): New.
(set_non_default_template_args_count): New declaration.
(get_non_default_template_args_count): New declaration.
(set_explicit_template_args_count): New declaration.
(get_explicit_template_args_count): New declaration.
(TFF_AS_PRIMARY): New constant.
* decl.c (grokfndecl): Mark all template arguments in a friend
declaration as explicitly specified.
* error.c (args_or_non_default_template_args_count): Renamed
from get_non_default_template_args_count (name-clash). Make
independent of flag_pretty_templates.
(dump_template_bindings): Add flags parameter to be passed to
get_non_default_template_args_count. Print only non-default
template arguments. Add used_parms parameter: print defaulted
template bindings if the parameter name is part of used_parms.
(dump_substitution): Walk the function_decl to find all used
template parameters.
(dump_function_decl): Call dump_function_name and dump_type of
the DECL_CONTEXT with specialized template and set
TFF_AS_PRIMARY for their flags. Don't print template arguments.
dump_function_name already does so.
(dump_function_name): Add and document conditions for calling
dump_template_parms. Move DECL_USE_TEMPLATE to PRIMARY parameter
of dump_template_parms.
(dump_template_parms): Print only non-default template
parameters.
(lang_decl_name): Add TFF_NO_FUNCTION_ARGUMENTS to
dump_function_name flags.
* pt.c (set_non_default_template_args_count): New function.
(get_non_default_template_args_count): New function.
(set_explicit_template_args_count): New function.
(get_explicit_template_args_count): New function.
(expand_template_argument_pack): Always adjust and set
the adjusted non-default args count.
(template_parm_to_arg): SET_NON_DEFAULT_TEMPLATE_ARGS_COUNT
independent of CHECKING_P.
(coerce_template_parameter_pack): Ditto.
(coerce_template_parms): Ditto.
(fn_type_unification): Ditto.
(type_unification_real): Ditto.
(get_partial_spec_bindings): Ditto.
(determine_specialization): Also copy the inner TREE_VECs.
(template_parms_level_to_args): Always count non-default args.
(copy_template_args): Only copy the non-default template args
count on TREE_VECs that should have it.
(fn_type_unification): Set EXPLICIT_TEMPLATE_ARGS_P on the
template arguments tree if any template parameter was explicitly
given.
(type_unification_real): Count non-default args sooner.
(get_partial_spec_bindings): Set non-default args count.
(tsubst_template_args): Take a shortcut for multi-level args to
avoid a lot of unnecessary checks and simplify non-default args
counting. Fix the count of non-default template arguments.
gcc/cp/constraint.cc | 3 +-
gcc/cp/cp-tree.h | 36 ++-
gcc/cp/decl.c | 4 +
gcc/cp/error.c | 200 +++++++++------
gcc/cp/pt.c | 232 ++++++++++++++----
.../g++.dg/debug/dwarf2/template-params-12n.C | 2 +-
.../diagnostic/default-template-args-1.C | 61 +++++
.../diagnostic/default-template-args-2.C | 37 +++
.../diagnostic/default-template-args-3.C | 29 +++
.../diagnostic/default-template-args-4.C | 19 ++
.../diagnostic/default-template-args-5.C | 12 +
.../g++.dg/diagnostic/param-type-mismatch-2.C | 2 +-
gcc/testsuite/g++.dg/ext/pretty1.C | 2 +-
gcc/testsuite/g++.dg/goacc/template.C | 8 +-
gcc/testsuite/g++.dg/gomp/declare-variant-7.C | 4 +-
gcc/testsuite/g++.dg/template/error40.C | 6 +-
gcc/testsuite/g++.old-deja/g++.ext/pretty3.C | 2 +-
gcc/testsuite/g++.old-deja/g++.pt/memtemp77.C | 2 +-
18 files changed, 508 insertions(+), 153 deletions(-)
create mode 100644 gcc/testsuite/g++.dg/diagnostic/default-template-args-1.C
create mode 100644 gcc/testsuite/g++.dg/diagnostic/default-template-args-2.C
create mode 100644 gcc/testsuite/g++.dg/diagnostic/default-template-args-3.C
create mode 100644 gcc/testsuite/g++.dg/diagnostic/default-template-args-4.C
create mode 100644 gcc/testsuite/g++.dg/diagnostic/default-template-args-5.C
Dr. Matthias Kretz https://mattkretz.github.io
GSI Helmholtz Centre for Heavy Ion Research https://gsi.de
stdₓ::simd
──────────────────────────────────────────────────────────────────────────
[-- Attachment #2: 0001-c-Print-function-template-parms-when-relevant.patch --]
[-- Type: text/x-patch, Size: 47690 bytes --]
diff --git a/gcc/cp/constraint.cc b/gcc/cp/constraint.cc
index 2896efdd7f2..17dce107491 100644
--- a/gcc/cp/constraint.cc
+++ b/gcc/cp/constraint.cc
@@ -2890,10 +2890,11 @@ get_mapped_args (tree map)
tree level = make_tree_vec (list.length ());
for (unsigned j = 0; j < list.length(); ++j)
TREE_VEC_ELT (level, j) = list[j];
+ /* None of the args at any level are defaulted. */
+ SET_NON_DEFAULT_TEMPLATE_ARGS_COUNT (level, list.length());
SET_TMPL_ARGS_LEVEL (args, i + 1, level);
list.release ();
}
- SET_NON_DEFAULT_TEMPLATE_ARGS_COUNT (args, 0);
return args;
}
diff --git a/gcc/cp/cp-tree.h b/gcc/cp/cp-tree.h
index f387b5036d2..b5350bb8409 100644
--- a/gcc/cp/cp-tree.h
+++ b/gcc/cp/cp-tree.h
@@ -3684,25 +3684,29 @@ struct GTY(()) lang_decl {
((struct tree_template_info*)TEMPLATE_INFO_CHECK (NODE))->args
#define TI_PENDING_TEMPLATE_FLAG(NODE) \
TREE_LANG_FLAG_1 (TEMPLATE_INFO_CHECK (NODE))
-/* For a given TREE_VEC containing a template argument list,
- this property contains the number of arguments that are not
- defaulted. */
+/* For a given TREE_VEC containing a template argument list (but not multiple
+ levels of arguments), this property contains the number of arguments that are
+ not defaulted and optionally the number of explicitly specified template
+ arguments. It is either a INT_CST denoting the number of non-default
+ arguments, or a TREE_LIST with TREE_PURPOSE denoting the number of explicitly
+ given template arguments of a function template, and TREE_VALUE denoting the
+ number of non-default arguments. */
#define NON_DEFAULT_TEMPLATE_ARGS_COUNT(NODE) \
TREE_CHAIN (TREE_VEC_CHECK (NODE))
/* Below are the setter and getter of the NON_DEFAULT_TEMPLATE_ARGS_COUNT
property. */
#define SET_NON_DEFAULT_TEMPLATE_ARGS_COUNT(NODE, INT_VALUE) \
- NON_DEFAULT_TEMPLATE_ARGS_COUNT(NODE) = build_int_cst (NULL_TREE, INT_VALUE)
-#if CHECKING_P
-#define GET_NON_DEFAULT_TEMPLATE_ARGS_COUNT(NODE) \
- int_cst_value (NON_DEFAULT_TEMPLATE_ARGS_COUNT (NODE))
-#else
+ set_non_default_template_args_count (TREE_VEC_CHECK (NODE), INT_VALUE)
+
#define GET_NON_DEFAULT_TEMPLATE_ARGS_COUNT(NODE) \
- NON_DEFAULT_TEMPLATE_ARGS_COUNT (NODE) \
- ? int_cst_value (NON_DEFAULT_TEMPLATE_ARGS_COUNT (NODE)) \
- : TREE_VEC_LENGTH (INNERMOST_TEMPLATE_ARGS (NODE))
-#endif
+ get_non_default_template_args_count (TREE_VEC_CHECK (NODE))
+
+#define EXPLICIT_TEMPLATE_ARGS_P(NODE) \
+ (get_explicit_template_args_count (TREE_VEC_CHECK (NODE)) > 0)
+
+#define SET_EXPLICIT_TEMPLATE_ARGS_COUNT(NODE, INT_VALUE) \
+ set_explicit_template_args_count (TREE_VEC_CHECK (NODE), INT_VALUE)
/* The list of access checks that were deferred during parsing
which need to be performed at template instantiation time.
@@ -5938,7 +5942,8 @@ 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: show the template like a primary template. */
#define TFF_PLAIN_IDENTIFIER (0)
#define TFF_SCOPE (1)
@@ -5956,6 +5961,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.
@@ -7153,6 +7159,10 @@ extern void pop_access_scope (tree);
extern bool check_template_shadow (tree);
extern bool check_auto_in_tmpl_args (tree, tree);
extern tree get_innermost_template_args (tree, int);
+extern void set_non_default_template_args_count (tree args, int count);
+extern int get_non_default_template_args_count (tree args);
+extern void set_explicit_template_args_count (tree args, int count);
+extern bool get_explicit_template_args_count (tree args);
extern void maybe_begin_member_template_processing (tree);
extern void maybe_end_member_template_processing (void);
extern tree finish_member_template_decl (tree);
diff --git a/gcc/cp/decl.c b/gcc/cp/decl.c
index 7c2a134e406..74b0cac0f2f 100644
--- a/gcc/cp/decl.c
+++ b/gcc/cp/decl.c
@@ -9985,6 +9985,10 @@ grokfndecl (tree ctype,
/* A friend declaration of the form friend void f<>(). Record
the information in the TEMPLATE_ID_EXPR. */
SET_DECL_IMPLICIT_INSTANTIATION (decl);
+ /* Set the template args as explicitly specified - they were certainly
+ not deduced from function arguments. */
+ if (args)
+ SET_EXPLICIT_TEMPLATE_ARGS_COUNT (args, NUM_TMPL_ARGS (args));
gcc_assert (identifier_p (fns) || OVL_P (fns));
DECL_TEMPLATE_INFO (decl) = build_template_info (fns, args);
diff --git a/gcc/cp/error.c b/gcc/cp/error.c
index 012a4ecddf4..fb62adbeb0b 100644
--- a/gcc/cp/error.c
+++ b/gcc/cp/error.c
@@ -86,11 +86,11 @@ static void dump_exception_spec (cxx_pretty_printer *, tree, int);
static void dump_template_argument (cxx_pretty_printer *, tree, int);
static void dump_template_argument_list (cxx_pretty_printer *, tree, int);
static void dump_template_parameter (cxx_pretty_printer *, tree, int);
-static void dump_template_bindings (cxx_pretty_printer *, tree, tree,
- vec<tree, va_gc> *);
+static void dump_template_bindings (cxx_pretty_printer *, tree, tree, tree,
+ vec<tree, va_gc> *, int);
static void dump_scope (cxx_pretty_printer *, tree, int);
static void dump_template_parms (cxx_pretty_printer *, tree, int, int);
-static int get_non_default_template_args_count (tree, int);
+static int args_or_non_default_template_args_count (tree, int);
static const char *function_category (tree);
static void maybe_print_constexpr_context (diagnostic_context *);
static void maybe_print_instantiation_context (diagnostic_context *);
@@ -272,24 +272,23 @@ dump_template_argument (cxx_pretty_printer *pp, tree arg, int flags)
}
}
-/* Count the number of template arguments ARGS whose value does not
- match the (optional) default template parameter in PARAMS */
+/* Returns GET_NON_DEFAULT_TEMPLATE_ARGS_COUNT (ARGS) unless FLAGS requests the
+ full count of template args. */
static int
-get_non_default_template_args_count (tree args, int flags)
+args_or_non_default_template_args_count (tree args, int flags)
{
- int n = TREE_VEC_LENGTH (INNERMOST_TEMPLATE_ARGS (args));
+ gcc_assert (!TMPL_ARGS_HAVE_MULTIPLE_LEVELS (args));
if (/* We use this flag when generating debug information. We don't
want to expand templates at this point, for this may generate
new decls, which gets decl counts out of sync, which may in
turn cause codegen differences between compilations with and
without -g. */
- (flags & TFF_NO_OMIT_DEFAULT_TEMPLATE_ARGUMENTS) != 0
- || !flag_pretty_templates)
- return n;
+ (flags & TFF_NO_OMIT_DEFAULT_TEMPLATE_ARGUMENTS) != 0)
+ return NUM_TMPL_ARGS (args);
- return GET_NON_DEFAULT_TEMPLATE_ARGS_COUNT (INNERMOST_TEMPLATE_ARGS (args));
+ return GET_NON_DEFAULT_TEMPLATE_ARGS_COUNT (args);
}
/* Dump a template-argument-list ARGS (always a TREE_VEC) under control
@@ -298,7 +297,7 @@ get_non_default_template_args_count (tree args, int flags)
static void
dump_template_argument_list (cxx_pretty_printer *pp, tree args, int flags)
{
- int n = get_non_default_template_args_count (args, flags);
+ int n = args_or_non_default_template_args_count (args, flags);
int need_comma = 0;
int i;
@@ -369,7 +368,7 @@ dump_template_parameter (cxx_pretty_printer *pp, tree parm, int flags)
static void
dump_template_bindings (cxx_pretty_printer *pp, tree parms, tree args,
- vec<tree, va_gc> *typenames)
+ tree used_parms, vec<tree, va_gc> *typenames, int flags)
{
/* Print "[with" and ']', conditional on whether anything is printed at all.
This is tied to whether a semicolon is needed to separate multiple template
@@ -414,23 +413,47 @@ dump_template_bindings (cxx_pretty_printer *pp, tree parms, tree args,
/* Don't crash if we had an invalid argument list. */
if (TMPL_ARGS_DEPTH (args) >= lvl)
lvl_args = TMPL_ARGS_LEVEL (args, lvl);
+ const int len = TREE_VEC_LENGTH (p);
+ const int non_default_len
+ = lvl_args ? args_or_non_default_template_args_count (lvl_args, flags)
+ : len;
- for (i = 0; i < TREE_VEC_LENGTH (p); ++i)
+ for (i = 0; i < len; ++i, ++arg_idx)
{
+ const tree parm_i = TREE_VEC_ELT (p, i);
+ gcc_assert (TREE_CODE (parm_i) == TREE_LIST);
+ const tree parm_val = TREE_VALUE (parm_i);
+ const tree parm_type = TREE_CODE (parm_val) == TYPE_DECL
+ || TREE_CODE (parm_val) == TEMPLATE_DECL
+ ? TREE_TYPE (parm_val)
+ : TREE_CODE (parm_val) == PARM_DECL
+ ? DECL_ARG_TYPE (parm_val)
+ : NULL_TREE;
+
+ /* If the template parameter is defaulted and does not appear in
+ used_parms (function arguments, return type, or exception
+ specifier), skip the parameter. */
+ if (i >= non_default_len)
+ {
+ tree it;
+ for (it = used_parms; it && TREE_VALUE (it) != parm_type;
+ it = TREE_CHAIN (it))
+ ;
+ if (!it)
+ continue;
+ }
+
tree arg = NULL_TREE;
/* Don't crash if we had an invalid argument list. */
if (lvl_args && NUM_TMPL_ARGS (lvl_args) > arg_idx)
arg = TREE_VEC_ELT (lvl_args, arg_idx);
- tree parm_i = TREE_VEC_ELT (p, i);
/* If the template argument repeats the template parameter (T = T),
skip the parameter.*/
if (arg && TREE_CODE (arg) == TEMPLATE_TYPE_PARM
- && TREE_CODE (parm_i) == TREE_LIST
- && TREE_CODE (TREE_VALUE (parm_i)) == TYPE_DECL
- && TREE_CODE (TREE_TYPE (TREE_VALUE (parm_i)))
- == TEMPLATE_TYPE_PARM
+ && TREE_CODE (parm_val) == TYPE_DECL
+ && TREE_CODE (TREE_TYPE (parm_val)) == TEMPLATE_TYPE_PARM
&& DECL_NAME (TREE_VALUE (parm_i))
== DECL_NAME (TREE_CHAIN (arg)))
continue;
@@ -450,8 +473,6 @@ dump_template_bindings (cxx_pretty_printer *pp, tree parms, tree args,
}
else
pp_string (pp, M_("<missing>"));
-
- ++arg_idx;
}
parms = TREE_CHAIN (parms);
@@ -1634,8 +1655,16 @@ dump_substitution (cxx_pretty_printer *pp,
if (template_parms != NULL_TREE && template_args != NULL_TREE
&& !(flags & TFF_NO_TEMPLATE_BINDINGS))
{
- vec<tree, va_gc> *typenames = t ? find_typenames (t) : NULL;
- dump_template_bindings (pp, template_parms, template_args, typenames);
+ vec<tree, va_gc> *typenames = nullptr;
+ tree used_parms = NULL_TREE;
+ if (t)
+ {
+ typenames = find_typenames (t);
+ const tree fn = TREE_TYPE (DECL_TEMPLATE_RESULT (t));
+ used_parms = find_template_parameters (fn, template_parms);
+ }
+ dump_template_bindings (pp, template_parms, template_args, used_parms,
+ typenames, flags);
}
}
@@ -1688,8 +1717,15 @@ 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);
- /* Pretty print template instantiations only. */
- if (DECL_USE_TEMPLATE (t) && DECL_TEMPLATE_INFO (t)
+ /* Keep t before the following branch makes t point to a more general
+ template. Without the specialized template, the information about defaulted
+ template arguments is lost. */
+ tree specialized_t = t;
+ int specialized_flags = 0;
+
+ /* Pretty print only template instantiations. Don't pretty print explicit
+ specializations like 'template <> void fun<int> (int)'. */
+ if (DECL_TEMPLATE_INSTANTIATION (t) && DECL_TEMPLATE_INFO (t)
&& !(flags & TFF_NO_TEMPLATE_BINDINGS)
&& flag_pretty_templates)
{
@@ -1701,6 +1737,9 @@ dump_function_decl (cxx_pretty_printer *pp, tree t, int flags)
{
template_parms = DECL_TEMPLATE_PARMS (tmpl);
t = tmpl;
+ /* The "[with ...]" clause is printed, thus dump functions printing
+ SPECIALIZED_T need to add TFF_AS_PRIMARY to their flags. */
+ specialized_flags = TFF_AS_PRIMARY;
}
}
@@ -1710,8 +1749,8 @@ dump_function_decl (cxx_pretty_printer *pp, tree t, int flags)
fntype = TREE_TYPE (t);
parmtypes = FUNCTION_FIRST_USER_PARMTYPE (t);
- if (DECL_CLASS_SCOPE_P (t))
- cname = DECL_CONTEXT (t);
+ if (DECL_CLASS_SCOPE_P (specialized_t))
+ cname = DECL_CONTEXT (specialized_t);
/* This is for partially instantiated template methods. */
else if (TREE_CODE (fntype) == METHOD_TYPE)
cname = TREE_TYPE (TREE_VALUE (parmtypes));
@@ -1749,13 +1788,14 @@ dump_function_decl (cxx_pretty_printer *pp, tree t, int flags)
/* Nothing. */;
else if (cname)
{
- dump_type (pp, cname, flags);
+ dump_type (pp, cname, flags | specialized_flags);
pp_cxx_colon_colon (pp);
}
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 | specialized_flags);
if (!(flags & TFF_NO_FUNCTION_ARGUMENTS))
{
@@ -1802,26 +1842,6 @@ dump_function_decl (cxx_pretty_printer *pp, tree t, int flags)
pp_character (pp, ']');
}
}
- else if (template_args)
- {
- bool need_comma = false;
- int i;
- pp_cxx_begin_template_argument_list (pp);
- template_args = INNERMOST_TEMPLATE_ARGS (template_args);
- for (i = 0; i < TREE_VEC_LENGTH (template_args); ++i)
- {
- tree arg = TREE_VEC_ELT (template_args, i);
- if (need_comma)
- pp_separate_with_comma (pp);
- if (ARGUMENT_PACK_P (arg))
- pp_cxx_left_brace (pp);
- dump_template_argument (pp, arg, TFF_PLAIN_IDENTIFIER);
- if (ARGUMENT_PACK_P (arg))
- pp_cxx_right_brace (pp);
- need_comma = true;
- }
- pp_cxx_end_template_argument_list (pp);
- }
}
/* Print a parameter list. If this is for a member function, the
@@ -1968,13 +1988,35 @@ dump_function_name (cxx_pretty_printer *pp, tree t, int flags)
dump_module_suffix (pp, t);
+/* Print function template parameters if:
+ 1. t is template, and
+ 2. the caller didn't request to only print the template-name, and
+ 3. t actually has template parameters that are not indirect parameters from
+ enclosing scopes, i.e. either
+ - t is a friend template specialization
+ (eg. template<class T> struct X { friend void foo<T>(int); }; since
+ PRIMARY_TEMPLATE_P requires a TEMPLATE_DECL, this case must be checked
+ before PRIMARY_TEMPLATE_P is safe to call), or
+ - t is a primary template (own template header),
+ and
+ 4. either
+ - flags requests to show no function arguments, in which case deduced
+ types could be hidden and thus need to be printed, or
+ - at least one function template argument was given explicitly and the
+ diagnostics output should reflect the code as it was written,
+
+ Whether t is a specialization of a template informs the PRIMARY parameter of
+ dump_template_parms.
+ */
if (DECL_TEMPLATE_INFO (t)
&& !(flags & TFF_TEMPLATE_NAME)
- && !DECL_FRIEND_PSEUDO_TEMPLATE_INSTANTIATION (t)
&& (TREE_CODE (DECL_TI_TEMPLATE (t)) != TEMPLATE_DECL
- || PRIMARY_TEMPLATE_P (DECL_TI_TEMPLATE (t))))
- dump_template_parms (pp, DECL_TEMPLATE_INFO (t), !DECL_USE_TEMPLATE (t),
- flags);
+ || PRIMARY_TEMPLATE_P (DECL_TI_TEMPLATE (t)))
+ && ((flags & TFF_NO_FUNCTION_ARGUMENTS)
+ || (DECL_TI_ARGS (t)
+ && EXPLICIT_TEMPLATE_ARGS_P (INNERMOST_TEMPLATE_ARGS
+ (DECL_TI_ARGS (t))))))
+ dump_template_parms (pp, DECL_TEMPLATE_INFO (t), !DECL_USE_TEMPLATE (t), flags);
}
/* Dump the template parameters from the template info INFO under control of
@@ -1989,6 +2031,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);
@@ -1998,10 +2042,11 @@ dump_template_parms (cxx_pretty_printer *pp, tree info,
to crash producing error messages. */
if (args && !primary)
{
- int len, ix;
- len = get_non_default_template_args_count (args, flags);
+ int ix;
args = INNERMOST_TEMPLATE_ARGS (args);
+ int len = args_or_non_default_template_args_count (args, flags);
+ gcc_assert (len <= NUM_TMPL_ARGS (args));
for (ix = 0; ix != len; ix++)
{
tree arg = TREE_VEC_ELT (args, ix);
@@ -2026,25 +2071,35 @@ dump_template_parms (cxx_pretty_printer *pp, tree info,
tree parms = DECL_TEMPLATE_PARMS (tpl);
int len, ix;
- parms = TREE_CODE (parms) == TREE_LIST ? TREE_VALUE (parms) : NULL_TREE;
- len = parms ? TREE_VEC_LENGTH (parms) : 0;
-
- for (ix = 0; ix != len; ix++)
+ if (TREE_CODE (parms) == TREE_LIST)
{
- tree parm;
+ parms = INNERMOST_TEMPLATE_PARMS (parms);
+ if (args)
+ {
+ len = args_or_non_default_template_args_count
+ (INNERMOST_TEMPLATE_ARGS (args), flags);
+ gcc_assert (len <= TREE_VEC_LENGTH (parms));
+ }
+ else
+ len = TREE_VEC_LENGTH (parms);
- if (TREE_VEC_ELT (parms, ix) == error_mark_node)
- {
- pp_string (pp, M_("<template parameter error>"));
- continue;
- }
+ for (ix = 0; ix != len; ix++)
+ {
+ tree parm;
- parm = TREE_VALUE (TREE_VEC_ELT (parms, ix));
+ if (TREE_VEC_ELT (parms, ix) == error_mark_node)
+ {
+ pp_string (pp, M_("<template parameter error>"));
+ continue;
+ }
- if (ix)
- pp_separate_with_comma (pp);
+ parm = TREE_VALUE (TREE_VEC_ELT (parms, ix));
- dump_decl (pp, parm, flags & ~TFF_DECL_SPECIFIERS);
+ if (ix)
+ pp_separate_with_comma (pp);
+
+ dump_decl (pp, parm, flags & ~TFF_DECL_SPECIFIERS);
+ }
}
}
pp_cxx_end_template_argument_list (pp);
@@ -3163,7 +3218,8 @@ lang_decl_name (tree decl, int v, bool translate)
}
if (TREE_CODE (decl) == FUNCTION_DECL)
- dump_function_name (cxx_pp, decl, TFF_PLAIN_IDENTIFIER);
+ dump_function_name (cxx_pp, decl,
+ TFF_PLAIN_IDENTIFIER | TFF_NO_FUNCTION_ARGUMENTS);
else if ((DECL_NAME (decl) == NULL_TREE)
&& TREE_CODE (decl) == NAMESPACE_DECL)
dump_decl (cxx_pp, decl, TFF_PLAIN_IDENTIFIER | TFF_UNQUALIFIED_NAME);
@@ -4060,10 +4116,10 @@ print_template_differences (pretty_printer *pp, tree type_a, tree type_b,
gcc_assert (TREE_CODE (args_a) == TREE_VEC);
gcc_assert (TREE_CODE (args_b) == TREE_VEC);
int flags = 0;
- int len_a = get_non_default_template_args_count (args_a, flags);
args_a = INNERMOST_TEMPLATE_ARGS (args_a);
- int len_b = get_non_default_template_args_count (args_b, flags);
+ int len_a = args_or_non_default_template_args_count (args_a, flags);
args_b = INNERMOST_TEMPLATE_ARGS (args_b);
+ int len_b = args_or_non_default_template_args_count (args_b, flags);
/* Determine the maximum range of args for which non-default template args
were used; beyond this, only default args (if any) were used, and so
they will be equal from this point onwards.
diff --git a/gcc/cp/pt.c b/gcc/cp/pt.c
index 66040035b2f..2460bdc0fe4 100644
--- a/gcc/cp/pt.c
+++ b/gcc/cp/pt.c
@@ -689,6 +689,63 @@ strip_innermost_template_args (tree args, int extra_levels)
return new_args;
}
+/* Let ARGS know that there are COUNT entries which are not defaulted. ARGS must
+ * be a single level of arguments. */
+
+void
+set_non_default_template_args_count (tree args, int count)
+{
+ gcc_assert (!TMPL_ARGS_HAVE_MULTIPLE_LEVELS (args));
+ gcc_assert (count <= NUM_TMPL_ARGS (args));
+ tree cst = build_int_cst (integer_type_node, count);
+ if (TREE_CHAIN (args) && TREE_CODE (TREE_CHAIN (args)) == TREE_LIST)
+ TREE_VALUE (TREE_CHAIN (args)) = cst;
+ else
+ TREE_CHAIN (args) = cst;
+}
+
+/* Query the number of non-defaulted template arguments in a single level of
+ template ARGS. */
+
+int
+get_non_default_template_args_count (tree args)
+{
+ gcc_assert (!TMPL_ARGS_HAVE_MULTIPLE_LEVELS (args));
+ tree node = TREE_CHAIN (args);
+ if (!node)
+ return NUM_TMPL_ARGS (args);
+ if (TREE_CODE (node) == TREE_LIST)
+ node = TREE_VALUE (node);
+ const int count = int_cst_value (node);
+ gcc_assert (count <= NUM_TMPL_ARGS (args));
+ return count;
+}
+
+/* Set the COUNT of explicitly specified function template ARGS. */
+
+void
+set_explicit_template_args_count (tree args, int count)
+{
+ args = INNERMOST_TEMPLATE_ARGS (args);
+ tree non_default
+ = TREE_CHAIN (args)
+ ? INTEGER_CST_CHECK (TREE_CHAIN (args))
+ : build_int_cst (integer_type_node, TREE_VEC_LENGTH (args));
+ TREE_CHAIN (args) = build_tree_list (build_int_cst (integer_type_node, count),
+ non_default);
+}
+
+/* Query the number of explicity specified function template ARGS. */
+
+bool
+get_explicit_template_args_count (tree args)
+{
+ tree node = TREE_CHAIN (INNERMOST_TEMPLATE_ARGS (args));
+ return node && TREE_CODE (node) == TREE_LIST
+ ? int_cst_value (TREE_PURPOSE (node))
+ : 0;
+}
+
/* We've got a template header coming up; push to a new level for storing
the parms. */
@@ -2504,7 +2561,7 @@ determine_specialization (tree template_id,
if (candidates)
{
tree fn = TREE_VALUE (candidates);
- *targs_out = copy_node (DECL_TI_ARGS (fn));
+ *targs_out = copy_template_args (DECL_TI_ARGS (fn));
/* Propagate the candidate's constraints to the declaration. */
if (tsk != tsk_template)
@@ -4334,7 +4391,6 @@ expand_template_argument_pack (tree args)
tree result_args = NULL_TREE;
int in_arg, out_arg = 0, nargs = args ? TREE_VEC_LENGTH (args) : 0;
int num_result_args = -1;
- int non_default_args_count = -1;
/* First, determine if we need to expand anything, and the number of
slots we'll need. */
@@ -4364,9 +4420,7 @@ expand_template_argument_pack (tree args)
/* Expand arguments. */
result_args = make_tree_vec (num_result_args);
- if (NON_DEFAULT_TEMPLATE_ARGS_COUNT (args))
- non_default_args_count =
- GET_NON_DEFAULT_TEMPLATE_ARGS_COUNT (args);
+ int non_default_args_count = GET_NON_DEFAULT_TEMPLATE_ARGS_COUNT (args);
for (in_arg = 0; in_arg < nargs; ++in_arg)
{
tree arg = TREE_VEC_ELT (args, in_arg);
@@ -4376,8 +4430,7 @@ expand_template_argument_pack (tree args)
int i, num_packed = TREE_VEC_LENGTH (packed);
for (i = 0; i < num_packed; ++i, ++out_arg)
TREE_VEC_ELT (result_args, out_arg) = TREE_VEC_ELT(packed, i);
- if (non_default_args_count > 0)
- non_default_args_count += num_packed - 1;
+ non_default_args_count += num_packed - 1;
}
else
{
@@ -4385,8 +4438,7 @@ expand_template_argument_pack (tree args)
++out_arg;
}
}
- if (non_default_args_count >= 0)
- SET_NON_DEFAULT_TEMPLATE_ARGS_COUNT (result_args, non_default_args_count);
+ SET_NON_DEFAULT_TEMPLATE_ARGS_COUNT (result_args, non_default_args_count);
return result_args;
}
@@ -4789,8 +4841,7 @@ template_parm_to_arg (tree t)
/* Turn this argument into a TYPE_ARGUMENT_PACK
with a single element, which expands T. */
tree vec = make_tree_vec (1);
- if (CHECKING_P)
- SET_NON_DEFAULT_TEMPLATE_ARGS_COUNT (vec, TREE_VEC_LENGTH (vec));
+ SET_NON_DEFAULT_TEMPLATE_ARGS_COUNT (vec, TREE_VEC_LENGTH (vec));
TREE_VEC_ELT (vec, 0) = make_pack_expansion (t);
@@ -4805,8 +4856,7 @@ template_parm_to_arg (tree t)
/* Turn this argument into a NONTYPE_ARGUMENT_PACK
with a single element, which expands T. */
tree vec = make_tree_vec (1);
- if (CHECKING_P)
- SET_NON_DEFAULT_TEMPLATE_ARGS_COUNT (vec, TREE_VEC_LENGTH (vec));
+ SET_NON_DEFAULT_TEMPLATE_ARGS_COUNT (vec, TREE_VEC_LENGTH (vec));
t = convert_from_reference (t);
TREE_VEC_ELT (vec, 0) = make_pack_expansion (t);
@@ -4828,11 +4878,16 @@ template_parms_level_to_args (tree parms)
{
tree a = copy_node (parms);
TREE_TYPE (a) = NULL_TREE;
+ int nondefault = 0;
for (int i = TREE_VEC_LENGTH (a) - 1; i >= 0; --i)
- TREE_VEC_ELT (a, i) = template_parm_to_arg (TREE_VEC_ELT (a, i));
+ {
+ tree elt = TREE_VEC_ELT (a, i);
+ TREE_VEC_ELT (a, i) = template_parm_to_arg (elt);
+ if (!elt || elt == error_mark_node || !TREE_PURPOSE (elt))
+ ++nondefault;
+ }
- if (CHECKING_P)
- SET_NON_DEFAULT_TEMPLATE_ARGS_COUNT (a, TREE_VEC_LENGTH (a));
+ SET_NON_DEFAULT_TEMPLATE_ARGS_COUNT (a, nondefault);
return a;
}
@@ -8721,9 +8776,8 @@ coerce_template_parameter_pack (tree parms,
}
SET_ARGUMENT_PACK_ARGS (argument_pack, packed_args);
- if (CHECKING_P)
- SET_NON_DEFAULT_TEMPLATE_ARGS_COUNT (packed_args,
- TREE_VEC_LENGTH (packed_args));
+ SET_NON_DEFAULT_TEMPLATE_ARGS_COUNT (packed_args,
+ TREE_VEC_LENGTH (packed_args));
return argument_pack;
}
@@ -9087,7 +9141,7 @@ coerce_template_parms (tree parms,
return error_mark_node;
}
- if (CHECKING_P && !NON_DEFAULT_TEMPLATE_ARGS_COUNT (new_inner_args))
+ if (!NON_DEFAULT_TEMPLATE_ARGS_COUNT (new_inner_args))
SET_NON_DEFAULT_TEMPLATE_ARGS_COUNT (new_inner_args,
TREE_VEC_LENGTH (new_inner_args));
@@ -13332,8 +13386,9 @@ copy_template_args (tree t)
TREE_VEC_ELT (new_vec, i) = elt;
}
- NON_DEFAULT_TEMPLATE_ARGS_COUNT (new_vec)
- = NON_DEFAULT_TEMPLATE_ARGS_COUNT (t);
+ if (!TMPL_ARGS_HAVE_MULTIPLE_LEVELS (t))
+ NON_DEFAULT_TEMPLATE_ARGS_COUNT (new_vec)
+ = NON_DEFAULT_TEMPLATE_ARGS_COUNT (t);
return new_vec;
}
@@ -13373,13 +13428,33 @@ tree
tsubst_template_args (tree t, tree args, tsubst_flags_t complain, tree in_decl)
{
tree orig_t = t;
- int len, need_new = 0, i, expanded_len_adjust = 0, out;
+ int need_new = 0, i, expanded_len_adjust = 0, out;
tree *elts;
if (t == error_mark_node)
return error_mark_node;
- len = TREE_VEC_LENGTH (t);
+ const int len = TREE_VEC_LENGTH (t);
+ if (len == 0)
+ return t;
+
+ /* Shortcut if T has multiple levels. All elts must be TREE_VECs and we simply
+ recurse. The remainder of this function doesn't apply to multi-level
+ args. */
+ if (TMPL_ARGS_HAVE_MULTIPLE_LEVELS (t))
+ {
+ tree r = make_tree_vec (len);
+ for (i = 0; i < len; i++)
+ {
+ tree new_arg = tsubst_template_args (TREE_VEC_ELT (t, i), args,
+ complain, in_decl);
+ if (new_arg == error_mark_node)
+ return error_mark_node;
+ TREE_VEC_ELT (r, i) = new_arg;
+ }
+ return r;
+ }
+
elts = XALLOCAVEC (tree, len);
for (i = 0; i < len; i++)
@@ -13389,8 +13464,6 @@ tsubst_template_args (tree t, tree args, tsubst_flags_t complain, tree in_decl)
if (!orig_arg)
new_arg = NULL_TREE;
- else if (TREE_CODE (orig_arg) == TREE_VEC)
- new_arg = tsubst_template_args (orig_arg, args, complain, in_decl);
else if (PACK_EXPANSION_P (orig_arg))
{
/* Substitute into an expansion expression. */
@@ -13424,19 +13497,6 @@ tsubst_template_args (tree t, tree args, tsubst_flags_t complain, tree in_decl)
/* Make space for the expanded arguments coming from template
argument packs. */
t = make_tree_vec (len + expanded_len_adjust);
- /* ORIG_T can contain TREE_VECs. That happens if ORIG_T contains the
- arguments for a member template.
- In that case each TREE_VEC in ORIG_T represents a level of template
- arguments, and ORIG_T won't carry any non defaulted argument count.
- It will rather be the nested TREE_VECs that will carry one.
- In other words, ORIG_T carries a non defaulted argument count only
- if it doesn't contain any nested TREE_VEC. */
- if (NON_DEFAULT_TEMPLATE_ARGS_COUNT (orig_t))
- {
- int count = GET_NON_DEFAULT_TEMPLATE_ARGS_COUNT (orig_t);
- count += expanded_len_adjust;
- SET_NON_DEFAULT_TEMPLATE_ARGS_COUNT (t, count);
- }
for (i = 0, out = 0; i < len; i++)
{
tree orig_arg = TREE_VEC_ELT (orig_t, i);
@@ -13457,6 +13517,65 @@ tsubst_template_args (tree t, tree args, tsubst_flags_t complain, tree in_decl)
}
}
+ /* The non-default template count can only be copied from ARGS if ORIG_T
+ requests a substitution of a complete level from ARGS. If the resulting T
+ contains the same entries as ARGS, then simply return the relevant level
+ from ARGS, which already has the correct non-default count. */
+ int non_default_count = GET_NON_DEFAULT_TEMPLATE_ARGS_COUNT (orig_t)
+ + expanded_len_adjust;
+ if (expanded_len_adjust == 0)
+ {
+ auto level_of_parm = [](tree vec, int i) -> int {
+ tree parm = TREE_VEC_ELT (vec, i);
+ while (ARGUMENT_PACK_P (parm) || PACK_EXPANSION_P (parm))
+ {
+ if (ARGUMENT_PACK_P (parm))
+ parm = ARGUMENT_PACK_ARGS (parm);
+ if (PACK_EXPANSION_P (parm))
+ parm = PACK_EXPANSION_PATTERN (parm);
+ if (TREE_CODE (parm) == TREE_VEC && TREE_VEC_LENGTH (parm) == 1)
+ parm = TREE_VEC_ELT (parm, 0);
+ }
+ if ((TREE_CODE (parm) == TEMPLATE_TYPE_PARM
+ || TREE_CODE (parm) == TEMPLATE_TEMPLATE_PARM
+ || TREE_CODE (parm) == BOUND_TEMPLATE_TEMPLATE_PARM)
+ && i == TEMPLATE_TYPE_IDX (parm))
+ return TEMPLATE_TYPE_LEVEL (parm);
+ else if (TREE_CODE (parm) == TEMPLATE_PARM_INDEX
+ && i == TEMPLATE_PARM_IDX (parm))
+ return TEMPLATE_PARM_LEVEL (parm);
+ return -1;
+ };
+
+ const int level = level_of_parm (orig_t, 0);
+ if (level > 0 && level <= TMPL_ARGS_DEPTH (args))
+ {
+ args = TMPL_ARGS_LEVEL (args, level);
+ if (len == NUM_TMPL_ARGS (args))
+ {
+ bool t_equals_args
+ = TREE_VEC_ELT (args, 0) == TREE_VEC_ELT (t, 0);
+ int i;
+ for (i = 1; i < len; ++i)
+ {
+ if (level_of_parm (orig_t, i) == level)
+ t_equals_args = t_equals_args && TREE_VEC_ELT (args, i)
+ == TREE_VEC_ELT (t, i);
+ else
+ break;
+ }
+ if (i == len && t_equals_args)
+ {
+ ggc_free (t);
+ return args;
+ }
+ if (i == len && NON_DEFAULT_TEMPLATE_ARGS_COUNT (args))
+ non_default_count = GET_NON_DEFAULT_TEMPLATE_ARGS_COUNT (args);
+ }
+ }
+ }
+ SET_NON_DEFAULT_TEMPLATE_ARGS_COUNT (t, non_default_count);
+
return t;
}
@@ -21687,8 +21806,7 @@ fn_type_unification (tree fn,
explicit_targs = INNERMOST_TEMPLATE_ARGS (explicit_targs);
for (i = NUM_TMPL_ARGS (explicit_targs); i--;)
TREE_VEC_ELT (targs, i) = TREE_VEC_ELT (explicit_targs, i);
- if (!incomplete && CHECKING_P
- && !NON_DEFAULT_TEMPLATE_ARGS_COUNT (targs))
+ if (!incomplete && !NON_DEFAULT_TEMPLATE_ARGS_COUNT (targs))
SET_NON_DEFAULT_TEMPLATE_ARGS_COUNT
(targs, NUM_TMPL_ARGS (explicit_targs));
}
@@ -21874,6 +21992,11 @@ fn_type_unification (tree fn,
excessive_deduction_depth = false;
}
+ /* If all template parameters were explicitly given, treat them like default
+ template arguments for diagnostics. */
+ if (explicit_targs && explicit_targs != error_mark_node)
+ SET_EXPLICIT_TEMPLATE_ARGS_COUNT (targs, NUM_TMPL_ARGS (explicit_targs));
+
return r;
}
@@ -22516,9 +22639,17 @@ type_unification_real (tree tparms,
be NULL_TREE or ERROR_MARK_NODE, so we do not need
to explicitly check cxx_dialect here. */
if (TREE_PURPOSE (TREE_VEC_ELT (tparms, i)))
- /* OK, there is a default argument. Wait until after the
- conversion check to do substitution. */
- continue;
+ {
+ /* The position of the first default template argument,
+ is also the number of non-defaulted arguments in TARGS.
+ Record that. */
+ if (!NON_DEFAULT_TEMPLATE_ARGS_COUNT (targs))
+ SET_NON_DEFAULT_TEMPLATE_ARGS_COUNT (targs, i);
+
+ /* OK, there is a default argument. Wait until after the
+ conversion check to do substitution. */
+ continue;
+ }
/* If the type parameter is a parameter pack, then it will
be deduced to an empty parameter pack. */
@@ -22621,21 +22752,14 @@ type_unification_real (tree tparms,
if (arg == error_mark_node)
return 1;
else if (arg)
- {
- TREE_VEC_ELT (targs, i) = arg;
- /* The position of the first default template argument,
- is also the number of non-defaulted arguments in TARGS.
- Record that. */
- if (!NON_DEFAULT_TEMPLATE_ARGS_COUNT (targs))
- SET_NON_DEFAULT_TEMPLATE_ARGS_COUNT (targs, i);
- }
+ TREE_VEC_ELT (targs, i) = arg;
}
if (saw_undeduced++ == 1)
goto again;
}
- if (CHECKING_P && !NON_DEFAULT_TEMPLATE_ARGS_COUNT (targs))
+ if (!NON_DEFAULT_TEMPLATE_ARGS_COUNT (targs))
SET_NON_DEFAULT_TEMPLATE_ARGS_COUNT (targs, TREE_VEC_LENGTH (targs));
return unify_success (explain_p);
@@ -24998,6 +25122,8 @@ get_partial_spec_bindings (tree tmpl, tree spec_tmpl, tree args)
if (!template_template_parm_bindings_ok_p (tparms, deduced_args))
return NULL_TREE;
+ SET_NON_DEFAULT_TEMPLATE_ARGS_COUNT (deduced_args, ntparms);
+
return deduced_args;
}
diff --git a/gcc/testsuite/g++.dg/debug/dwarf2/template-params-12n.C b/gcc/testsuite/g++.dg/debug/dwarf2/template-params-12n.C
index d3c1f589f87..b88bf7d4b34 100644
--- a/gcc/testsuite/g++.dg/debug/dwarf2/template-params-12n.C
+++ b/gcc/testsuite/g++.dg/debug/dwarf2/template-params-12n.C
@@ -1,6 +1,6 @@
// { dg-options "-gdwarf-2 -dA" }
// { dg-final { scan-assembler-times "DIE \\(\[^\n\]*\\) DW_TAG_template_value_param" 1 } }
-// { dg-final { scan-assembler-times "DIE \\(\[^\n\]*\\) DW_TAG_template_value_param\[^\n\]*\n\[^\n\]* DW_AT_name\n\[^\n\]* DW_AT_type\n\[^\n\]* DW_AT_const_value" 1 } }
+// { dg-final { scan-assembler-times "DIE \\(\[^\n\]*\\) DW_TAG_template_value_param\[^\n\]*\n\[^\n\]* DW_AT_name\n\[^\n\]* DW_AT_type\n(?:\[^\n\]* DW_AT_default_value\n)?\[^\n\]* DW_AT_const_value" 1 } }
#include "template-params-12.H"
/* We get const_value for NULL pointers to member functions. */
#if __cplusplus > 199711L // Ugh, C++98 barfs at both the cast and the overload.
diff --git a/gcc/testsuite/g++.dg/diagnostic/default-template-args-1.C b/gcc/testsuite/g++.dg/diagnostic/default-template-args-1.C
new file mode 100644
index 00000000000..6481798a69b
--- /dev/null
+++ b/gcc/testsuite/g++.dg/diagnostic/default-template-args-1.C
@@ -0,0 +1,61 @@
+// { dg-options "-fpretty-templates" }
+// { dg-do compile { target c++11 } }
+
+template <int a = 1>
+ [[deprecated]] void f0(); // { dg-message "'template<int a> void f0\\(\\)'" }
+
+template <int a>
+ [[deprecated]] void f1(); // { dg-message "'template<int a> void f1\\(\\)'" }
+
+template <class a, int b = 1>
+ [[deprecated]] void f2(); // { dg-message "'template<class a, int b> void f2\\(\\)'" }
+
+template <class a, int b = 1>
+ [[deprecated]] void f3(a); // { dg-message "'template<class a, int b> void f3\\(a\\)'" }
+
+template <class a>
+ [[deprecated]] void f4(a); // { dg-message "'template<class a> void f4\\(a\\)'" }
+
+template <>
+ [[deprecated]] void f4<int>(int);
+
+template <>
+ [[deprecated]] void f4(float);
+
+template <class a, class b = int>
+ [[deprecated]] void f5(a);
+
+template void f5<float>(float); // { dg-error "'void f5<a>\\(a\\) .with a = float.'" }
+
+template <class a, class b = int>
+ struct c
+ {
+ template <class d, class e = int>
+ [[deprecated]] static void f(d);
+ };
+
+int main()
+{
+ f0(); // { dg-warning "'void f0\\(\\)'" }
+ f1<1>(); // { dg-warning "'void f1<a>\\(\\) .with int a = 1.'" }
+ f2<int>(); // { dg-warning "'void f2<a>\\(\\) .with a = int.'" }
+ f3(1); // { dg-warning "'void f3\\(a\\) .with a = int.'" }
+ f3<float>(1); // { dg-warning "'void f3<a>\\(a\\) .with a = float.'" }
+ f3<float, 2>(1); // { dg-warning "'void f3<a, b>\\(a\\) .with a = float; int b = 2.'" }
+ f4(1.); // { dg-warning "'void f4\\(a\\) .with a = double.'" }
+ f4(1); // { dg-warning "'void f4<int>\\(int\\)'" }
+ f4(1.f); // { dg-warning "'void f4\\(float\\)'" }
+
+ f0(0); // { dg-error "" }
+ f1(0); // { dg-error "" }
+ f2(0); // { dg-error "" }
+ f3(); // { dg-error "" }
+ f4(); // { dg-error "" }
+
+ c<int>::f(1.); // { dg-warning "'static void c<a>::f\\(d\\) .with d = double; a = int.'" }
+ c<int>::f<int>(1); // { dg-warning "'static void c<a>::f<d>\\(d\\) .with d = int; a = int.'" }
+ c<int>::f<float, int>(1.f); // { dg-warning "'static void c<a>::f<d, e>\\(d\\) .with d = float; e = int; a = int.'" }
+ 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.'" }
+}
diff --git a/gcc/testsuite/g++.dg/diagnostic/default-template-args-2.C b/gcc/testsuite/g++.dg/diagnostic/default-template-args-2.C
new file mode 100644
index 00000000000..a82709f5785
--- /dev/null
+++ b/gcc/testsuite/g++.dg/diagnostic/default-template-args-2.C
@@ -0,0 +1,37 @@
+// { dg-options "-fno-pretty-templates" }
+// { dg-do compile { target c++11 } }
+
+template <int a = 1>
+ [[deprecated]] void f0();
+
+template <int a>
+ [[deprecated]] void f1();
+
+template <class a, int b = 1>
+ [[deprecated]] void f2();
+
+template <class a, int b = 1>
+ [[deprecated]] void f3(a);
+
+template <class a>
+ [[deprecated]] void f4(a);
+
+template <>
+ [[deprecated]] void f4<int>(int);
+
+template <class a, class b = int>
+ [[deprecated]] void f5(a);
+
+template void f5<float>(float); // { dg-error "'void f5<float>\\(float\\)'" }
+
+int main()
+{
+ f0(); // { dg-warning "'void f0\\(\\)'" }
+ f1<1>(); // { dg-warning "'void f1<1>\\(\\)'" }
+ f2<int>(); // { dg-warning "'void f2<int>\\(\\)'" }
+ f3(1); // { dg-warning "'void f3\\(int\\)'" }
+ f3<float>(1); // { dg-warning "'void f3<float>\\(float\\)'" }
+ f3<float, 2>(1); // { dg-warning "'void f3<float, 2>\\(float\\)'" }
+ f4(1.); // { dg-warning "'void f4\\(double\\)'" }
+ f4(1); // { dg-warning "'void f4<int>\\(int\\)'" }
+}
diff --git a/gcc/testsuite/g++.dg/diagnostic/default-template-args-3.C b/gcc/testsuite/g++.dg/diagnostic/default-template-args-3.C
new file mode 100644
index 00000000000..c5dceafca29
--- /dev/null
+++ b/gcc/testsuite/g++.dg/diagnostic/default-template-args-3.C
@@ -0,0 +1,29 @@
+// { dg-options "-fpretty-templates" }
+// { dg-do compile { target c++11 } }
+
+template <class T>
+ struct id
+ { using type = T; };
+
+template <class T0, class T1 = int, template <class> class foo = id>
+ struct A
+ {
+ template <class U0 = const T1&>
+ [[deprecated]] static void
+ f(typename foo<U0>::type);
+ };
+
+template <int> struct B;
+
+template <> struct B<1>
+ { using type = int; };
+
+template <class T, int x = 1>
+ [[deprecated]] typename B<x>::type
+ f0(T);
+
+int main()
+{
+ A<int>::f(0); // { dg-warning "'static void A<T0>::f\\(typename foo<U0>::type\\) .with U0 = const int&; T0 = int; foo = id; typename foo<U0>::type = const int&.'" }
+ f0(0); // { dg-warning "'typename B<x>::type f0\\(T\\) .with T = int; int x = 1; typename B<x>::type = int.'" }
+}
diff --git a/gcc/testsuite/g++.dg/diagnostic/default-template-args-4.C b/gcc/testsuite/g++.dg/diagnostic/default-template-args-4.C
new file mode 100644
index 00000000000..14704b6a29e
--- /dev/null
+++ b/gcc/testsuite/g++.dg/diagnostic/default-template-args-4.C
@@ -0,0 +1,19 @@
+// { dg-options "-fpretty-templates" }
+// { dg-do compile { target c++11 } }
+
+template <class T> struct X {};
+template <class... T> struct Y {};
+
+template <template <class> class Tpl, template <class...> class VTpl,
+ class T0 = float, class U0 = VTpl<Tpl<T0>>, int x0 = 2>
+ struct S
+ {
+ template <class T, class... Ps, class U = int, int x = 1>
+ [[deprecated]] static U0
+ f(T, Ps&&...);
+ };
+
+void g()
+{
+ S<X, Y>::f(1, '2', 3, 4.); // { dg-warning "'static U0 S<Tpl, VTpl>::f\\(T, Ps&& ...\\) .with T = int; Ps = {char, int, double}; Tpl = X; VTpl = Y; U0 = Y<X<float> >.'" }
+}
diff --git a/gcc/testsuite/g++.dg/diagnostic/default-template-args-5.C b/gcc/testsuite/g++.dg/diagnostic/default-template-args-5.C
new file mode 100644
index 00000000000..0f13466fb27
--- /dev/null
+++ b/gcc/testsuite/g++.dg/diagnostic/default-template-args-5.C
@@ -0,0 +1,12 @@
+// { dg-options "-fpretty-templates" }
+// { dg-do compile { target c++11 } }
+//
+// Tests the case where DECL_TI_TEMPLATE doesn't return a TEMPLATE_DECL for the
+// FUNCTION_DECL of foo<U>(int).
+
+template <class T0, class T1 = int, int x = 1> void foo (int);
+
+template <class U, int y = 2>
+class Q {
+ friend void foo<U> (int = 3); // { dg-error "'void foo<U>\\(int\\)'" }
+};
diff --git a/gcc/testsuite/g++.dg/diagnostic/param-type-mismatch-2.C b/gcc/testsuite/g++.dg/diagnostic/param-type-mismatch-2.C
index de7570a6efa..0aa45404283 100644
--- a/gcc/testsuite/g++.dg/diagnostic/param-type-mismatch-2.C
+++ b/gcc/testsuite/g++.dg/diagnostic/param-type-mismatch-2.C
@@ -141,7 +141,7 @@ int test_7 (int first, const char *second, float third)
|
const char*
{ dg-end-multiline-output "" } */
- // { dg-message "initializing argument 2 of 'int test_7\\(int, T, float\\) .with T = const char\\*\\*.'" "" { target *-*-* } test_7_decl }
+ // { dg-message "initializing argument 2 of 'int test_7<T>\\(int, T, float\\) .with T = const char\\*\\*.'" "" { target *-*-* } test_7_decl }
/* { dg-begin-multiline-output "" }
int test_7 (int one, T two, float three);
~~^~~
diff --git a/gcc/testsuite/g++.dg/ext/pretty1.C b/gcc/testsuite/g++.dg/ext/pretty1.C
index 06608ae30eb..c5bfd6082a7 100644
--- a/gcc/testsuite/g++.dg/ext/pretty1.C
+++ b/gcc/testsuite/g++.dg/ext/pretty1.C
@@ -60,7 +60,7 @@ __assert_fail (const char *cond, const char *file, unsigned int line,
abort ();
}
-// { dg-final { scan-assembler "int bar\\(T\\).*with T = int" } }
+// { dg-final { scan-assembler "int bar<int>\\(int\\)" } }
// { dg-final { scan-assembler "top level" } }
// { dg-final { scan-assembler "int main\\(\\)" } }
// { dg-final { scan-assembler "int bar\\(T\\).*with T = double" } }
diff --git a/gcc/testsuite/g++.dg/goacc/template.C b/gcc/testsuite/g++.dg/goacc/template.C
index 10d3f446da7..4fcd88bfc56 100644
--- a/gcc/testsuite/g++.dg/goacc/template.C
+++ b/gcc/testsuite/g++.dg/goacc/template.C
@@ -157,12 +157,12 @@ main ()
}
/* { dg-final { scan-tree-dump-times {(?n)^OpenACC routine '[^']+' has 'nohost' clause\.$} 4 oaccloops } }
- { dg-final { scan-tree-dump-times {(?n)^OpenACC routine 'T accDouble\(int\) \[with T = char\]' has 'nohost' clause\.$} 1 oaccloops { target { ! offloading_enabled } } } }
+ { dg-final { scan-tree-dump-times {(?n)^OpenACC routine 'T accDouble<T>\(int\) \[with T = char\]' has 'nohost' clause\.$} 1 oaccloops { target { ! offloading_enabled } } } }
{ dg-final { scan-tree-dump-times {(?n)^OpenACC routine 'accDouble<char>\(int\)char' has 'nohost' clause\.$} 1 oaccloops { target offloading_enabled } } }
- { dg-final { scan-tree-dump-times {(?n)^OpenACC routine 'T accDouble\(int\) \[with T = int\]' has 'nohost' clause\.$} 1 oaccloops { target { ! offloading_enabled } } } }
+ { dg-final { scan-tree-dump-times {(?n)^OpenACC routine 'T accDouble<T>\(int\) \[with T = int\]' has 'nohost' clause\.$} 1 oaccloops { target { ! offloading_enabled } } } }
{ dg-final { scan-tree-dump-times {(?n)^OpenACC routine 'accDouble<int>\(int\)int' has 'nohost' clause\.$} 1 oaccloops { target offloading_enabled } } }
- { dg-final { scan-tree-dump-times {(?n)^OpenACC routine 'T accDouble\(int\) \[with T = float\]' has 'nohost' clause\.$} 1 oaccloops { target { ! offloading_enabled } } } }
+ { dg-final { scan-tree-dump-times {(?n)^OpenACC routine 'T accDouble<T>\(int\) \[with T = float\]' has 'nohost' clause\.$} 1 oaccloops { target { ! offloading_enabled } } } }
{ dg-final { scan-tree-dump-times {(?n)^OpenACC routine 'accDouble<float>\(int\)float' has 'nohost' clause\.$} 1 oaccloops { target offloading_enabled } } }
- { dg-final { scan-tree-dump-times {(?n)^OpenACC routine 'T accDouble\(int\) \[with T = double\]' has 'nohost' clause\.$} 1 oaccloops { target { ! offloading_enabled } } } }
+ { dg-final { scan-tree-dump-times {(?n)^OpenACC routine 'T accDouble<T>\(int\) \[with T = double\]' has 'nohost' clause\.$} 1 oaccloops { target { ! offloading_enabled } } } }
{ dg-final { scan-tree-dump-times {(?n)^OpenACC routine 'accDouble<double>\(int\)double' has 'nohost' clause\.$} 1 oaccloops { target offloading_enabled } } }
TODO See PR101551 for 'offloading_enabled' differences. */
diff --git a/gcc/testsuite/g++.dg/gomp/declare-variant-7.C b/gcc/testsuite/g++.dg/gomp/declare-variant-7.C
index 7dda899578a..9cb654cb5e6 100644
--- a/gcc/testsuite/g++.dg/gomp/declare-variant-7.C
+++ b/gcc/testsuite/g++.dg/gomp/declare-variant-7.C
@@ -70,6 +70,6 @@ test ()
s.f12 (0.0); // { dg-final { scan-tree-dump-times "S<1>::f11<double> \\\(&s, 0.0\\\);" 1 "gimple" } }
s.f14 (0LL); // { dg-final { scan-tree-dump-times "S<1>::f13<long long int> \\\(&s, 0\\\);" 1 "gimple" } }
T<0> t;
- t.f16 (s); // { dg-final { scan-tree-dump-times "T<0>::f16<S<1> > \\\(&t, s\\\);" 1 "gimple" } }
- t.f18 (s); // { dg-final { scan-tree-dump-times "T<0>::f18<S<1> > \\\(&t, s\\\);" 1 "gimple" } }
+ t.f16 (s); // { dg-final { scan-tree-dump-times "T<0>::f16 \\\(&t, s\\\);" 1 "gimple" } }
+ t.f18 (s); // { dg-final { scan-tree-dump-times "T<0>::f18 \\\(&t, s\\\);" 1 "gimple" } }
}
diff --git a/gcc/testsuite/g++.dg/template/error40.C b/gcc/testsuite/g++.dg/template/error40.C
index c5df56fc1be..16a44d1819f 100644
--- a/gcc/testsuite/g++.dg/template/error40.C
+++ b/gcc/testsuite/g++.dg/template/error40.C
@@ -8,11 +8,11 @@ struct A
void foo(void)
{
- A<void> a = 0; // { dg-error "A<void, 0, 1>" }
+ A<void> a = 0; // { dg-error "A<void>" }
}
-template <class T> T f(T); // { dg-message "int f<int>.int." }
-template <class T> T f(T, int = 0); // { dg-message "" }
+template <class T> T f(T); // { dg-message "int f.int." }
+template <class T> T f(T, int = 0); // { dg-message "int f.int, int." }
template <class T>
struct B
diff --git a/gcc/testsuite/g++.old-deja/g++.ext/pretty3.C b/gcc/testsuite/g++.old-deja/g++.ext/pretty3.C
index 6348ae1ee67..30c7ecd5065 100644
--- a/gcc/testsuite/g++.old-deja/g++.ext/pretty3.C
+++ b/gcc/testsuite/g++.old-deja/g++.ext/pretty3.C
@@ -35,7 +35,7 @@ template<> void f1<int> (int)
if (strcmp (function, "f1<int>"))
bad = true;
- if (strcmp (pretty, "void f1(T) [with T = int]"))
+ if (strcmp (pretty, "void f1<int>(int)"))
bad = true;
}
diff --git a/gcc/testsuite/g++.old-deja/g++.pt/memtemp77.C b/gcc/testsuite/g++.old-deja/g++.pt/memtemp77.C
index 6dd4b546c0c..93dbf4b489f 100644
--- a/gcc/testsuite/g++.old-deja/g++.pt/memtemp77.C
+++ b/gcc/testsuite/g++.old-deja/g++.pt/memtemp77.C
@@ -19,7 +19,7 @@ const char* S3<char>::h(int) { return __PRETTY_FUNCTION__; }
int main()
{
if (strcmp (S3<double>::h(7),
- "static const char* S3<T>::h(U) [with U = int; T = double]") == 0)
+ "static const char* S3<double>::h(int)") == 0)
return 0;
else
return 1;
^ permalink raw reply [flat|nested] 24+ messages in thread
* [PATCH 2/2] c++: Print function template parms when relevant [part 2]
2021-11-26 15:23 ` [PATCH 0/2] " Matthias Kretz
2021-11-26 15:24 ` [PATCH 1/2] c++: Print function template parms when relevant Matthias Kretz
@ 2021-11-26 15:24 ` Matthias Kretz
1 sibling, 0 replies; 24+ messages in thread
From: Matthias Kretz @ 2021-11-26 15:24 UTC (permalink / raw)
To: gcc-patches, jason
[-- Attachment #1: Type: text/plain, Size: 1546 bytes --]
Restore status-quo how function template specializations were diagnosed.
Signed-off-by: Matthias Kretz <m.kretz@gsi.de>
gcc/testsuite/ChangeLog:
* g++.dg/diagnostic/default-template-args-1.C: Adjust for
different presentation of function template specializations.
* g++.dg/ext/pretty1.C: Ditto.
* g++.old-deja/g++.ext/pretty3.C: Ditto.
* g++.old-deja/g++.pt/memtemp77.C: Ditto.
gcc/cp/ChangeLog:
* error.c (dump_function_decl): Pretty print all template
specializations.
(dump_function_name): Only print template parms if given a
function template specialization.
---
gcc/cp/error.c | 18 ++++++++++--------
.../diagnostic/default-template-args-1.C | 4 ++--
gcc/testsuite/g++.dg/ext/pretty1.C | 2 +-
gcc/testsuite/g++.old-deja/g++.ext/pretty3.C | 2 +-
gcc/testsuite/g++.old-deja/g++.pt/memtemp77.C | 2 +-
5 files changed, 15 insertions(+), 13 deletions(-)
--
──────────────────────────────────────────────────────────────────────────
Dr. Matthias Kretz https://mattkretz.github.io
GSI Helmholtz Centre for Heavy Ion Research https://gsi.de
stdₓ::simd
──────────────────────────────────────────────────────────────────────────
[-- Attachment #2: 0002-c-Print-function-template-parms-when-relevant-part-2.patch --]
[-- Type: text/x-patch, Size: 5217 bytes --]
diff --git a/gcc/cp/error.c b/gcc/cp/error.c
index fb62adbeb0b..9365e42dd76 100644
--- a/gcc/cp/error.c
+++ b/gcc/cp/error.c
@@ -1723,9 +1723,8 @@ dump_function_decl (cxx_pretty_printer *pp, tree t, int flags)
tree specialized_t = t;
int specialized_flags = 0;
- /* Pretty print only template instantiations. Don't pretty print explicit
- specializations like 'template <> void fun<int> (int)'. */
- if (DECL_TEMPLATE_INSTANTIATION (t) && DECL_TEMPLATE_INFO (t)
+ /* Pretty print template instantiations only. */
+ if (DECL_USE_TEMPLATE (t) && DECL_TEMPLATE_INFO (t)
&& !(flags & TFF_NO_TEMPLATE_BINDINGS)
&& flag_pretty_templates)
{
@@ -1989,9 +1988,11 @@ dump_function_name (cxx_pretty_printer *pp, tree t, int flags)
dump_module_suffix (pp, t);
/* Print function template parameters if:
- 1. t is template, and
- 2. the caller didn't request to only print the template-name, and
- 3. t actually has template parameters that are not indirect parameters from
+ 1. t is a template, and
+ 2. t is a specialization of a template (so that __FUNCTION__ of
+ 'template <class T> void f(T)' is 'f' and not 'f<T>'), and
+ 3. the caller didn't request to only print the template-name, and
+ 4. t actually has template parameters that are not indirect parameters from
enclosing scopes, i.e. either
- t is a friend template specialization
(eg. template<class T> struct X { friend void foo<T>(int); }; since
@@ -1999,7 +2000,7 @@ dump_function_name (cxx_pretty_printer *pp, tree t, int flags)
before PRIMARY_TEMPLATE_P is safe to call), or
- t is a primary template (own template header),
and
- 4. either
+ 5. either
- flags requests to show no function arguments, in which case deduced
types could be hidden and thus need to be printed, or
- at least one function template argument was given explicitly and the
@@ -2009,6 +2010,7 @@ dump_function_name (cxx_pretty_printer *pp, tree t, int flags)
dump_template_parms.
*/
if (DECL_TEMPLATE_INFO (t)
+ && DECL_USE_TEMPLATE (t)
&& !(flags & TFF_TEMPLATE_NAME)
&& (TREE_CODE (DECL_TI_TEMPLATE (t)) != TEMPLATE_DECL
|| PRIMARY_TEMPLATE_P (DECL_TI_TEMPLATE (t)))
@@ -2016,7 +2018,7 @@ dump_function_name (cxx_pretty_printer *pp, tree t, int flags)
|| (DECL_TI_ARGS (t)
&& EXPLICIT_TEMPLATE_ARGS_P (INNERMOST_TEMPLATE_ARGS
(DECL_TI_ARGS (t))))))
- dump_template_parms (pp, DECL_TEMPLATE_INFO (t), !DECL_USE_TEMPLATE (t), flags);
+ dump_template_parms (pp, DECL_TEMPLATE_INFO (t), false, flags);
}
/* Dump the template parameters from the template info INFO under control of
diff --git a/gcc/testsuite/g++.dg/diagnostic/default-template-args-1.C b/gcc/testsuite/g++.dg/diagnostic/default-template-args-1.C
index 6481798a69b..1b82ea46bb5 100644
--- a/gcc/testsuite/g++.dg/diagnostic/default-template-args-1.C
+++ b/gcc/testsuite/g++.dg/diagnostic/default-template-args-1.C
@@ -43,8 +43,8 @@ int main()
f3<float>(1); // { dg-warning "'void f3<a>\\(a\\) .with a = float.'" }
f3<float, 2>(1); // { dg-warning "'void f3<a, b>\\(a\\) .with a = float; int b = 2.'" }
f4(1.); // { dg-warning "'void f4\\(a\\) .with a = double.'" }
- f4(1); // { dg-warning "'void f4<int>\\(int\\)'" }
- f4(1.f); // { dg-warning "'void f4\\(float\\)'" }
+ f4(1); // { dg-warning "'void f4<a>\\(a\\) .with a = int.'" }
+ f4(1.f); // { dg-warning "'void f4\\(a\\) .with a = float.'" }
f0(0); // { dg-error "" }
f1(0); // { dg-error "" }
diff --git a/gcc/testsuite/g++.dg/ext/pretty1.C b/gcc/testsuite/g++.dg/ext/pretty1.C
index c5bfd6082a7..07cc9a6f2cd 100644
--- a/gcc/testsuite/g++.dg/ext/pretty1.C
+++ b/gcc/testsuite/g++.dg/ext/pretty1.C
@@ -60,7 +60,7 @@ __assert_fail (const char *cond, const char *file, unsigned int line,
abort ();
}
-// { dg-final { scan-assembler "int bar<int>\\(int\\)" } }
+// { dg-final { scan-assembler "int bar<T>\\(T\\).*with T = int" } }
// { dg-final { scan-assembler "top level" } }
// { dg-final { scan-assembler "int main\\(\\)" } }
// { dg-final { scan-assembler "int bar\\(T\\).*with T = double" } }
diff --git a/gcc/testsuite/g++.old-deja/g++.ext/pretty3.C b/gcc/testsuite/g++.old-deja/g++.ext/pretty3.C
index 30c7ecd5065..6c82f5b6061 100644
--- a/gcc/testsuite/g++.old-deja/g++.ext/pretty3.C
+++ b/gcc/testsuite/g++.old-deja/g++.ext/pretty3.C
@@ -35,7 +35,7 @@ template<> void f1<int> (int)
if (strcmp (function, "f1<int>"))
bad = true;
- if (strcmp (pretty, "void f1<int>(int)"))
+ if (strcmp (pretty, "void f1<T>(T) [with T = int]"))
bad = true;
}
diff --git a/gcc/testsuite/g++.old-deja/g++.pt/memtemp77.C b/gcc/testsuite/g++.old-deja/g++.pt/memtemp77.C
index 93dbf4b489f..6dd4b546c0c 100644
--- a/gcc/testsuite/g++.old-deja/g++.pt/memtemp77.C
+++ b/gcc/testsuite/g++.old-deja/g++.pt/memtemp77.C
@@ -19,7 +19,7 @@ const char* S3<char>::h(int) { return __PRETTY_FUNCTION__; }
int main()
{
if (strcmp (S3<double>::h(7),
- "static const char* S3<double>::h(int)") == 0)
+ "static const char* S3<T>::h(U) [with U = int; T = double]") == 0)
return 0;
else
return 1;
^ permalink raw reply [flat|nested] 24+ messages in thread
* Re: [PATCH v2 1/2] c++: Print function template parms when relevant
2021-11-26 15:24 ` [PATCH 1/2] c++: Print function template parms when relevant Matthias Kretz
@ 2021-12-02 8:35 ` Matthias Kretz
0 siblings, 0 replies; 24+ messages in thread
From: Matthias Kretz @ 2021-12-02 8:35 UTC (permalink / raw)
To: gcc-patches, jason
[-- Attachment #1: Type: text/plain, Size: 8351 bytes --]
On Friday, 26 November 2021 16:23:43 CET Matthias Kretz wrote:
> > Hmm, since it walks DECL_TEMPLATE_RESULT, I wouldn't expect it to find
> > template parms that aren't in the function signature.
>
> You were right, walking TREE_TYPE (DECL_TEMPLATE_RESULT (t)) does what I
> need.
... for FUNCTION_TYPE, but not for METHOD_TYPE. Also, even though I did
regression testing, I found a few minor issues while looking for new
regressions in the diagnose_as patch. Therefore, the revised patch (part 1):
--------------------------
The choice when to print a function template parameter was still
suboptimal. That's because sometimes the function template parameter
list only adds noise, while in other situations the lack of a function
template parameter list makes diagnostic messages hard to understand.
The general idea of this change is to print template parms wherever they
would appear in the source code as well. Thus, the diagnostics code
needs to know whether any template parameter was given explicitly.
DWARF names of functions use dump_function_name to produce a name
without function arguments (function arguments are printed from
dump_function_decl). However, FLAGS should still state the intent of
printing a name without function arguments (TFF_NO_FUNCTION_ARGUMENTS so
that dump_function_name can decide correctly whether to call
dump_template_parms.
Based on an initial patch from Jason Merrill <jason@redhat.com>.
Signed-off-by: Matthias Kretz <m.kretz@gsi.de>
gcc/testsuite/ChangeLog:
* g++.dg/debug/dwarf2/template-params-12n.C: Optionally, allow
DW_AT_default_value.
* g++.dg/diagnostic/default-template-args-1.C: New test.
* g++.dg/diagnostic/default-template-args-2.C: New test.
* g++.dg/diagnostic/default-template-args-3.C: New test.
* g++.dg/diagnostic/default-template-args-4.C: New test.
* g++.dg/diagnostic/default-template-args-5.C: New test.
* g++.dg/diagnostic/param-type-mismatch-2.C: Expect template
parms in diagnostic.
* g++.dg/ext/pretty1.C: Expect function template specialization
to not pretty-print template parms.
* g++.old-deja/g++.ext/pretty3.C: Ditto.
* g++.old-deja/g++.pt/memtemp77.C: Ditto.
* g++.dg/goacc/template.C: Expect function template parms for
explicit arguments.
* g++.dg/template/error40.C: Expect only non-default template
arguments in diagnostic.
gcc/cp/ChangeLog:
* constraint.cc (get_mapped_args): Remove incorrect non-default
args count on multi-level template args; instead set the
non-default args count on each inner TREE_VEC.
* cp-tree.h: Rewrite NON_DEFAULT_TEMPLATE_ARGS_COUNT
implementation to store the number explicitly specified
arguments in a TREE_LIST.
(EXPLICIT_TEMPLATE_ARGS_P): New.
(SET_EXPLICIT_TEMPLATE_ARGS_P): New.
(set_non_default_template_args_count): New declaration.
(get_non_default_template_args_count): New declaration.
(set_explicit_template_args_count): New declaration.
(get_explicit_template_args_count): New declaration.
(TFF_AS_PRIMARY): New constant.
* decl.c (grokfndecl): Mark all template arguments in a friend
declaration as explicitly specified.
* error.c (args_or_non_default_template_args_count): Renamed
from get_non_default_template_args_count (name-clash). Make
independent of flag_pretty_templates.
(dump_template_bindings): Add flags parameter to be passed to
get_non_default_template_args_count. Print only non-default
template arguments. Add used_parms parameter: print defaulted
template bindings if the parameter name is part of used_parms.
(dump_substitution): Walk the function_decl to find all used
template parameters. Non-static member functions need a special
case to not find all template parameters of enclosing scopes.
(dump_function_decl): Call dump_function_name and dump_type of
the DECL_CONTEXT with specialized template and set
TFF_AS_PRIMARY for their flags. Don't print template arguments.
dump_function_name already does so.
(dump_function_name): Add and document conditions for calling
dump_template_parms. Move DECL_USE_TEMPLATE to PRIMARY parameter
of dump_template_parms.
(dump_template_parms): Print only non-default template
parameters.
(lang_decl_name): Add TFF_NO_FUNCTION_ARGUMENTS to
dump_function_name flags.
* module.cc (trees_out::core_vals): Accept TREE_LISTs in
TREE_CHAIN of TREE_VEC.
* pt.c (set_non_default_template_args_count): New function.
(get_non_default_template_args_count): New function.
(set_explicit_template_args_count): New function.
(get_explicit_template_args_count): New function.
(expand_template_argument_pack): Always adjust and set
the adjusted non-default args count.
(template_parm_to_arg): SET_NON_DEFAULT_TEMPLATE_ARGS_COUNT
independent of CHECKING_P.
(coerce_template_parameter_pack): Ditto.
(coerce_template_parms): Ditto.
(fn_type_unification): Ditto.
(type_unification_real): Ditto.
(get_partial_spec_bindings): Ditto. And, set non-default
template args count on inner args.
(determine_specialization): Also copy the inner TREE_VECs.
(template_parms_level_to_args): Always count non-default args.
(copy_template_args): Only copy the non-default template args
count on TREE_VECs that should have it.
(fn_type_unification): Set EXPLICIT_TEMPLATE_ARGS_P on the
template arguments tree if any template parameter was explicitly
given.
(type_unification_real): Count non-default args sooner.
(tsubst_template_args): Take a shortcut for multi-level args to
avoid a lot of unnecessary checks and simplify non-default args
counting. Fix the count of non-default template arguments.
---
gcc/cp/constraint.cc | 3 +-
gcc/cp/cp-tree.h | 36 ++-
gcc/cp/decl.c | 4 +
gcc/cp/error.c | 214 ++++++++++------
gcc/cp/module.cc | 4 +-
gcc/cp/pt.c | 231 ++++++++++++++----
.../g++.dg/debug/dwarf2/template-params-12n.C | 2 +-
.../diagnostic/default-template-args-1.C | 61 +++++
.../diagnostic/default-template-args-2.C | 37 +++
.../diagnostic/default-template-args-3.C | 29 +++
.../diagnostic/default-template-args-4.C | 24 ++
.../diagnostic/default-template-args-5.C | 12 +
.../g++.dg/diagnostic/param-type-mismatch-2.C | 2 +-
gcc/testsuite/g++.dg/ext/pretty1.C | 2 +-
gcc/testsuite/g++.dg/goacc/template.C | 8 +-
gcc/testsuite/g++.dg/template/error40.C | 6 +-
gcc/testsuite/g++.old-deja/g++.ext/pretty3.C | 2 +-
gcc/testsuite/g++.old-deja/g++.pt/memtemp77.C | 2 +-
18 files changed, 527 insertions(+), 152 deletions(-)
create mode 100644 gcc/testsuite/g++.dg/diagnostic/default-template-args-1.C
create mode 100644 gcc/testsuite/g++.dg/diagnostic/default-template-args-2.C
create mode 100644 gcc/testsuite/g++.dg/diagnostic/default-template-args-3.C
create mode 100644 gcc/testsuite/g++.dg/diagnostic/default-template-args-4.C
create mode 100644 gcc/testsuite/g++.dg/diagnostic/default-template-args-5.C
--
──────────────────────────────────────────────────────────────────────────
Dr. Matthias Kretz https://mattkretz.github.io
GSI Helmholtz Centre for Heavy Ion Research https://gsi.de
stdₓ::simd
──────────────────────────────────────────────────────────────────────────
[-- Attachment #2: 0001-c-Print-function-template-parms-when-relevant.patch --]
[-- Type: text/x-patch, Size: 48225 bytes --]
diff --git a/gcc/cp/constraint.cc b/gcc/cp/constraint.cc
index 2896efdd7f2..17dce107491 100644
--- a/gcc/cp/constraint.cc
+++ b/gcc/cp/constraint.cc
@@ -2890,10 +2890,11 @@ get_mapped_args (tree map)
tree level = make_tree_vec (list.length ());
for (unsigned j = 0; j < list.length(); ++j)
TREE_VEC_ELT (level, j) = list[j];
+ /* None of the args at any level are defaulted. */
+ SET_NON_DEFAULT_TEMPLATE_ARGS_COUNT (level, list.length());
SET_TMPL_ARGS_LEVEL (args, i + 1, level);
list.release ();
}
- SET_NON_DEFAULT_TEMPLATE_ARGS_COUNT (args, 0);
return args;
}
diff --git a/gcc/cp/cp-tree.h b/gcc/cp/cp-tree.h
index 1ee2c57e83c..97d5efec2e1 100644
--- a/gcc/cp/cp-tree.h
+++ b/gcc/cp/cp-tree.h
@@ -3689,25 +3689,29 @@ struct GTY(()) lang_decl {
((struct tree_template_info*)TEMPLATE_INFO_CHECK (NODE))->args
#define TI_PENDING_TEMPLATE_FLAG(NODE) \
TREE_LANG_FLAG_1 (TEMPLATE_INFO_CHECK (NODE))
-/* For a given TREE_VEC containing a template argument list,
- this property contains the number of arguments that are not
- defaulted. */
+/* For a given TREE_VEC containing a template argument list (but not multiple
+ levels of arguments), this property contains the number of arguments that are
+ not defaulted and optionally the number of explicitly specified template
+ arguments. It is either a INT_CST denoting the number of non-default
+ arguments, or a TREE_LIST with TREE_PURPOSE denoting the number of explicitly
+ given template arguments of a function template, and TREE_VALUE denoting the
+ number of non-default arguments. */
#define NON_DEFAULT_TEMPLATE_ARGS_COUNT(NODE) \
TREE_CHAIN (TREE_VEC_CHECK (NODE))
/* Below are the setter and getter of the NON_DEFAULT_TEMPLATE_ARGS_COUNT
property. */
#define SET_NON_DEFAULT_TEMPLATE_ARGS_COUNT(NODE, INT_VALUE) \
- NON_DEFAULT_TEMPLATE_ARGS_COUNT(NODE) = build_int_cst (NULL_TREE, INT_VALUE)
-#if CHECKING_P
-#define GET_NON_DEFAULT_TEMPLATE_ARGS_COUNT(NODE) \
- int_cst_value (NON_DEFAULT_TEMPLATE_ARGS_COUNT (NODE))
-#else
+ set_non_default_template_args_count (TREE_VEC_CHECK (NODE), INT_VALUE)
+
#define GET_NON_DEFAULT_TEMPLATE_ARGS_COUNT(NODE) \
- NON_DEFAULT_TEMPLATE_ARGS_COUNT (NODE) \
- ? int_cst_value (NON_DEFAULT_TEMPLATE_ARGS_COUNT (NODE)) \
- : TREE_VEC_LENGTH (INNERMOST_TEMPLATE_ARGS (NODE))
-#endif
+ get_non_default_template_args_count (TREE_VEC_CHECK (NODE))
+
+#define EXPLICIT_TEMPLATE_ARGS_P(NODE) \
+ (get_explicit_template_args_count (TREE_VEC_CHECK (NODE)) > 0)
+
+#define SET_EXPLICIT_TEMPLATE_ARGS_COUNT(NODE, INT_VALUE) \
+ set_explicit_template_args_count (TREE_VEC_CHECK (NODE), INT_VALUE)
/* The list of access checks that were deferred during parsing
which need to be performed at template instantiation time.
@@ -5949,7 +5953,8 @@ 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: show the template like a primary template. */
#define TFF_PLAIN_IDENTIFIER (0)
#define TFF_SCOPE (1)
@@ -5967,6 +5972,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.
@@ -7171,6 +7177,10 @@ extern void pop_access_scope (tree);
extern bool check_template_shadow (tree);
extern bool check_auto_in_tmpl_args (tree, tree);
extern tree get_innermost_template_args (tree, int);
+extern void set_non_default_template_args_count (tree args, int count);
+extern int get_non_default_template_args_count (tree args);
+extern void set_explicit_template_args_count (tree args, int count);
+extern bool get_explicit_template_args_count (tree args);
extern void maybe_begin_member_template_processing (tree);
extern void maybe_end_member_template_processing (void);
extern tree finish_member_template_decl (tree);
diff --git a/gcc/cp/decl.c b/gcc/cp/decl.c
index 56f80775ca0..24d6ee51db7 100644
--- a/gcc/cp/decl.c
+++ b/gcc/cp/decl.c
@@ -9968,6 +9968,10 @@ grokfndecl (tree ctype,
/* A friend declaration of the form friend void f<>(). Record
the information in the TEMPLATE_ID_EXPR. */
SET_DECL_IMPLICIT_INSTANTIATION (decl);
+ /* Set the template args as explicitly specified - they were certainly
+ not deduced from function arguments. */
+ if (args)
+ SET_EXPLICIT_TEMPLATE_ARGS_COUNT (args, NUM_TMPL_ARGS (args));
gcc_assert (identifier_p (fns) || OVL_P (fns));
DECL_TEMPLATE_INFO (decl) = build_template_info (fns, args);
diff --git a/gcc/cp/error.c b/gcc/cp/error.c
index 98c1f0e4bdf..5b5e67e6963 100644
--- a/gcc/cp/error.c
+++ b/gcc/cp/error.c
@@ -86,11 +86,11 @@ static void dump_exception_spec (cxx_pretty_printer *, tree, int);
static void dump_template_argument (cxx_pretty_printer *, tree, int);
static void dump_template_argument_list (cxx_pretty_printer *, tree, int);
static void dump_template_parameter (cxx_pretty_printer *, tree, int);
-static void dump_template_bindings (cxx_pretty_printer *, tree, tree,
- vec<tree, va_gc> *);
+static void dump_template_bindings (cxx_pretty_printer *, tree, tree, tree,
+ vec<tree, va_gc> *, int);
static void dump_scope (cxx_pretty_printer *, tree, int);
static void dump_template_parms (cxx_pretty_printer *, tree, int, int);
-static int get_non_default_template_args_count (tree, int);
+static int args_or_non_default_template_args_count (tree, int);
static const char *function_category (tree);
static void maybe_print_constexpr_context (diagnostic_context *);
static void maybe_print_instantiation_context (diagnostic_context *);
@@ -272,24 +272,23 @@ dump_template_argument (cxx_pretty_printer *pp, tree arg, int flags)
}
}
-/* Count the number of template arguments ARGS whose value does not
- match the (optional) default template parameter in PARAMS */
+/* Returns GET_NON_DEFAULT_TEMPLATE_ARGS_COUNT (ARGS) unless FLAGS requests the
+ full count of template args. */
static int
-get_non_default_template_args_count (tree args, int flags)
+args_or_non_default_template_args_count (tree args, int flags)
{
- int n = TREE_VEC_LENGTH (INNERMOST_TEMPLATE_ARGS (args));
+ gcc_assert (!TMPL_ARGS_HAVE_MULTIPLE_LEVELS (args));
if (/* We use this flag when generating debug information. We don't
want to expand templates at this point, for this may generate
new decls, which gets decl counts out of sync, which may in
turn cause codegen differences between compilations with and
without -g. */
- (flags & TFF_NO_OMIT_DEFAULT_TEMPLATE_ARGUMENTS) != 0
- || !flag_pretty_templates)
- return n;
+ (flags & TFF_NO_OMIT_DEFAULT_TEMPLATE_ARGUMENTS) != 0)
+ return NUM_TMPL_ARGS (args);
- return GET_NON_DEFAULT_TEMPLATE_ARGS_COUNT (INNERMOST_TEMPLATE_ARGS (args));
+ return GET_NON_DEFAULT_TEMPLATE_ARGS_COUNT (args);
}
/* Dump a template-argument-list ARGS (always a TREE_VEC) under control
@@ -298,7 +297,7 @@ get_non_default_template_args_count (tree args, int flags)
static void
dump_template_argument_list (cxx_pretty_printer *pp, tree args, int flags)
{
- int n = get_non_default_template_args_count (args, flags);
+ int n = args_or_non_default_template_args_count (args, flags);
int need_comma = 0;
int i;
@@ -369,7 +368,7 @@ dump_template_parameter (cxx_pretty_printer *pp, tree parm, int flags)
static void
dump_template_bindings (cxx_pretty_printer *pp, tree parms, tree args,
- vec<tree, va_gc> *typenames)
+ tree used_parms, vec<tree, va_gc> *typenames, int flags)
{
/* Print "[with" and ']', conditional on whether anything is printed at all.
This is tied to whether a semicolon is needed to separate multiple template
@@ -414,23 +413,47 @@ dump_template_bindings (cxx_pretty_printer *pp, tree parms, tree args,
/* Don't crash if we had an invalid argument list. */
if (TMPL_ARGS_DEPTH (args) >= lvl)
lvl_args = TMPL_ARGS_LEVEL (args, lvl);
+ const int len = TREE_VEC_LENGTH (p);
+ const int non_default_len
+ = lvl_args ? args_or_non_default_template_args_count (lvl_args, flags)
+ : len;
- for (i = 0; i < TREE_VEC_LENGTH (p); ++i)
+ for (i = 0; i < len; ++i, ++arg_idx)
{
+ const tree parm_i = TREE_VEC_ELT (p, i);
+ gcc_assert (TREE_CODE (parm_i) == TREE_LIST);
+ const tree parm_val = TREE_VALUE (parm_i);
+ const tree parm_type = TREE_CODE (parm_val) == TYPE_DECL
+ || TREE_CODE (parm_val) == TEMPLATE_DECL
+ ? TREE_TYPE (parm_val)
+ : TREE_CODE (parm_val) == PARM_DECL
+ ? DECL_ARG_TYPE (parm_val)
+ : NULL_TREE;
+
+ /* If the template parameter is defaulted and does not appear in
+ used_parms (function arguments, return type, or exception
+ specifier), skip the parameter. */
+ if (i >= non_default_len)
+ {
+ tree it;
+ for (it = used_parms; it && TREE_VALUE (it) != parm_type;
+ it = TREE_CHAIN (it))
+ ;
+ if (!it)
+ continue;
+ }
+
tree arg = NULL_TREE;
/* Don't crash if we had an invalid argument list. */
if (lvl_args && NUM_TMPL_ARGS (lvl_args) > arg_idx)
arg = TREE_VEC_ELT (lvl_args, arg_idx);
- tree parm_i = TREE_VEC_ELT (p, i);
/* If the template argument repeats the template parameter (T = T),
skip the parameter.*/
if (arg && TREE_CODE (arg) == TEMPLATE_TYPE_PARM
- && TREE_CODE (parm_i) == TREE_LIST
- && TREE_CODE (TREE_VALUE (parm_i)) == TYPE_DECL
- && TREE_CODE (TREE_TYPE (TREE_VALUE (parm_i)))
- == TEMPLATE_TYPE_PARM
+ && TREE_CODE (parm_val) == TYPE_DECL
+ && TREE_CODE (TREE_TYPE (parm_val)) == TEMPLATE_TYPE_PARM
&& DECL_NAME (TREE_VALUE (parm_i))
== DECL_NAME (TREE_CHAIN (arg)))
continue;
@@ -450,8 +473,6 @@ dump_template_bindings (cxx_pretty_printer *pp, tree parms, tree args,
}
else
pp_string (pp, M_("<missing>"));
-
- ++arg_idx;
}
parms = TREE_CHAIN (parms);
@@ -1634,8 +1655,30 @@ dump_substitution (cxx_pretty_printer *pp,
if (template_parms != NULL_TREE && template_args != NULL_TREE
&& !(flags & TFF_NO_TEMPLATE_BINDINGS))
{
- vec<tree, va_gc> *typenames = t ? find_typenames (t) : NULL;
- dump_template_bindings (pp, template_parms, template_args, typenames);
+ vec<tree, va_gc> *typenames = nullptr;
+ tree used_parms = NULL_TREE;
+ if (t)
+ {
+ typenames = find_typenames (t);
+ const tree fn = TREE_TYPE (DECL_TEMPLATE_RESULT (t));
+ if (TREE_CODE (fn) == METHOD_TYPE)
+ {
+ /* For methods, we have to skip walking TYPE_METHOD_BASETYPE and
+ the hidden "self" function parameter. Otherwise, we'll discover
+ all template parameters of class templates in the function's
+ context. */
+ used_parms = find_template_parameters (TREE_TYPE (fn),
+ template_parms);
+ for (tree arg = TREE_CHAIN (TYPE_ARG_TYPES (fn)); arg;
+ arg = TREE_CHAIN (arg))
+ used_parms = chainon (used_parms, find_template_parameters
+ (arg, template_parms));
+ }
+ else
+ used_parms = find_template_parameters (fn, template_parms);
+ }
+ dump_template_bindings (pp, template_parms, template_args, used_parms,
+ typenames, flags);
}
}
@@ -1689,8 +1732,15 @@ dump_function_decl (cxx_pretty_printer *pp, tree t, int flags)
constexpr_p = (DECL_DECLARED_CONSTEXPR_P (t)
&& !decl_implicit_constexpr_p (t));
- /* Pretty print template instantiations only. */
- if (DECL_USE_TEMPLATE (t) && DECL_TEMPLATE_INFO (t)
+ /* Keep t before the following branch makes t point to a more general
+ template. Without the specialized template, the information about defaulted
+ template arguments is lost. */
+ tree specialized_t = t;
+ int specialized_flags = 0;
+
+ /* Pretty print only template instantiations. Don't pretty print explicit
+ specializations like 'template <> void fun<int> (int)'. */
+ if (DECL_TEMPLATE_INSTANTIATION (t) && DECL_TEMPLATE_INFO (t)
&& !(flags & TFF_NO_TEMPLATE_BINDINGS)
&& flag_pretty_templates)
{
@@ -1702,6 +1752,9 @@ dump_function_decl (cxx_pretty_printer *pp, tree t, int flags)
{
template_parms = DECL_TEMPLATE_PARMS (tmpl);
t = tmpl;
+ /* The "[with ...]" clause is printed, thus dump functions printing
+ SPECIALIZED_T need to add TFF_AS_PRIMARY to their flags. */
+ specialized_flags = TFF_AS_PRIMARY;
}
}
@@ -1711,8 +1764,8 @@ dump_function_decl (cxx_pretty_printer *pp, tree t, int flags)
fntype = TREE_TYPE (t);
parmtypes = FUNCTION_FIRST_USER_PARMTYPE (t);
- if (DECL_CLASS_SCOPE_P (t))
- cname = DECL_CONTEXT (t);
+ if (DECL_CLASS_SCOPE_P (specialized_t))
+ cname = DECL_CONTEXT (specialized_t);
/* This is for partially instantiated template methods. */
else if (TREE_CODE (fntype) == METHOD_TYPE)
cname = TREE_TYPE (TREE_VALUE (parmtypes));
@@ -1750,13 +1803,14 @@ dump_function_decl (cxx_pretty_printer *pp, tree t, int flags)
/* Nothing. */;
else if (cname)
{
- dump_type (pp, cname, flags);
+ dump_type (pp, cname, flags | specialized_flags);
pp_cxx_colon_colon (pp);
}
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 | specialized_flags);
if (!(flags & TFF_NO_FUNCTION_ARGUMENTS))
{
@@ -1803,26 +1857,6 @@ dump_function_decl (cxx_pretty_printer *pp, tree t, int flags)
pp_character (pp, ']');
}
}
- else if (template_args)
- {
- bool need_comma = false;
- int i;
- pp_cxx_begin_template_argument_list (pp);
- template_args = INNERMOST_TEMPLATE_ARGS (template_args);
- for (i = 0; i < TREE_VEC_LENGTH (template_args); ++i)
- {
- tree arg = TREE_VEC_ELT (template_args, i);
- if (need_comma)
- pp_separate_with_comma (pp);
- if (ARGUMENT_PACK_P (arg))
- pp_cxx_left_brace (pp);
- dump_template_argument (pp, arg, TFF_PLAIN_IDENTIFIER);
- if (ARGUMENT_PACK_P (arg))
- pp_cxx_right_brace (pp);
- need_comma = true;
- }
- pp_cxx_end_template_argument_list (pp);
- }
}
/* Print a parameter list. If this is for a member function, the
@@ -1969,13 +2003,35 @@ dump_function_name (cxx_pretty_printer *pp, tree t, int flags)
dump_module_suffix (pp, t);
+/* Print function template parameters if:
+ 1. t is template, and
+ 2. the caller didn't request to only print the template-name, and
+ 3. t actually has template parameters that are not indirect parameters from
+ enclosing scopes, i.e. either
+ - t is a friend template specialization
+ (eg. template<class T> struct X { friend void foo<T>(int); }; since
+ PRIMARY_TEMPLATE_P requires a TEMPLATE_DECL, this case must be checked
+ before PRIMARY_TEMPLATE_P is safe to call), or
+ - t is a primary template (own template header),
+ and
+ 4. either
+ - flags requests to show no function arguments, in which case deduced
+ types could be hidden and thus need to be printed, or
+ - at least one function template argument was given explicitly and the
+ diagnostics output should reflect the code as it was written,
+
+ Whether t is a specialization of a template informs the PRIMARY parameter of
+ dump_template_parms.
+ */
if (DECL_TEMPLATE_INFO (t)
&& !(flags & TFF_TEMPLATE_NAME)
- && !DECL_FRIEND_PSEUDO_TEMPLATE_INSTANTIATION (t)
&& (TREE_CODE (DECL_TI_TEMPLATE (t)) != TEMPLATE_DECL
- || PRIMARY_TEMPLATE_P (DECL_TI_TEMPLATE (t))))
- dump_template_parms (pp, DECL_TEMPLATE_INFO (t), !DECL_USE_TEMPLATE (t),
- flags);
+ || PRIMARY_TEMPLATE_P (DECL_TI_TEMPLATE (t)))
+ && ((flags & TFF_NO_FUNCTION_ARGUMENTS)
+ || (DECL_TI_ARGS (t)
+ && EXPLICIT_TEMPLATE_ARGS_P (INNERMOST_TEMPLATE_ARGS
+ (DECL_TI_ARGS (t))))))
+ dump_template_parms (pp, DECL_TEMPLATE_INFO (t), !DECL_USE_TEMPLATE (t), flags);
}
/* Dump the template parameters from the template info INFO under control of
@@ -1990,6 +2046,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);
@@ -1999,10 +2057,11 @@ dump_template_parms (cxx_pretty_printer *pp, tree info,
to crash producing error messages. */
if (args && !primary)
{
- int len, ix;
- len = get_non_default_template_args_count (args, flags);
+ int ix;
args = INNERMOST_TEMPLATE_ARGS (args);
+ int len = args_or_non_default_template_args_count (args, flags);
+ gcc_assert (len <= NUM_TMPL_ARGS (args));
for (ix = 0; ix != len; ix++)
{
tree arg = TREE_VEC_ELT (args, ix);
@@ -2027,25 +2086,35 @@ dump_template_parms (cxx_pretty_printer *pp, tree info,
tree parms = DECL_TEMPLATE_PARMS (tpl);
int len, ix;
- parms = TREE_CODE (parms) == TREE_LIST ? TREE_VALUE (parms) : NULL_TREE;
- len = parms ? TREE_VEC_LENGTH (parms) : 0;
-
- for (ix = 0; ix != len; ix++)
+ if (TREE_CODE (parms) == TREE_LIST)
{
- tree parm;
+ parms = INNERMOST_TEMPLATE_PARMS (parms);
+ if (args)
+ {
+ len = args_or_non_default_template_args_count
+ (INNERMOST_TEMPLATE_ARGS (args), flags);
+ gcc_assert (len <= TREE_VEC_LENGTH (parms));
+ }
+ else
+ len = TREE_VEC_LENGTH (parms);
- if (TREE_VEC_ELT (parms, ix) == error_mark_node)
- {
- pp_string (pp, M_("<template parameter error>"));
- continue;
- }
+ for (ix = 0; ix != len; ix++)
+ {
+ tree parm;
+
+ if (TREE_VEC_ELT (parms, ix) == error_mark_node)
+ {
+ pp_string (pp, M_("<template parameter error>"));
+ continue;
+ }
- parm = TREE_VALUE (TREE_VEC_ELT (parms, ix));
+ parm = TREE_VALUE (TREE_VEC_ELT (parms, ix));
- if (ix)
- pp_separate_with_comma (pp);
+ if (ix)
+ pp_separate_with_comma (pp);
- dump_decl (pp, parm, flags & ~TFF_DECL_SPECIFIERS);
+ dump_decl (pp, parm, flags & ~TFF_DECL_SPECIFIERS);
+ }
}
}
pp_cxx_end_template_argument_list (pp);
@@ -3164,7 +3233,8 @@ lang_decl_name (tree decl, int v, bool translate)
}
if (TREE_CODE (decl) == FUNCTION_DECL)
- dump_function_name (cxx_pp, decl, TFF_PLAIN_IDENTIFIER);
+ dump_function_name (cxx_pp, decl,
+ TFF_PLAIN_IDENTIFIER | TFF_NO_FUNCTION_ARGUMENTS);
else if ((DECL_NAME (decl) == NULL_TREE)
&& TREE_CODE (decl) == NAMESPACE_DECL)
dump_decl (cxx_pp, decl, TFF_PLAIN_IDENTIFIER | TFF_UNQUALIFIED_NAME);
@@ -4061,10 +4131,10 @@ print_template_differences (pretty_printer *pp, tree type_a, tree type_b,
gcc_assert (TREE_CODE (args_a) == TREE_VEC);
gcc_assert (TREE_CODE (args_b) == TREE_VEC);
int flags = 0;
- int len_a = get_non_default_template_args_count (args_a, flags);
args_a = INNERMOST_TEMPLATE_ARGS (args_a);
- int len_b = get_non_default_template_args_count (args_b, flags);
+ int len_a = args_or_non_default_template_args_count (args_a, flags);
args_b = INNERMOST_TEMPLATE_ARGS (args_b);
+ int len_b = args_or_non_default_template_args_count (args_b, flags);
/* Determine the maximum range of args for which non-default template args
were used; beyond this, only default args (if any) were used, and so
they will be equal from this point onwards.
diff --git a/gcc/cp/module.cc b/gcc/cp/module.cc
index 71d0fab411f..3b1b5ca0ac0 100644
--- a/gcc/cp/module.cc
+++ b/gcc/cp/module.cc
@@ -6154,7 +6154,9 @@ trees_out::core_vals (tree t)
/* We stash NON_DEFAULT_TEMPLATE_ARGS_COUNT on TREE_CHAIN! */
gcc_checking_assert (!t->type_common.common.chain
|| (TREE_CODE (t->type_common.common.chain)
- == INTEGER_CST));
+ == INTEGER_CST)
+ || (TREE_CODE (t->type_common.common.chain)
+ == TREE_LIST));
WT (t->type_common.common.chain);
break;
diff --git a/gcc/cp/pt.c b/gcc/cp/pt.c
index 74323701a7d..2ba7622a669 100644
--- a/gcc/cp/pt.c
+++ b/gcc/cp/pt.c
@@ -689,6 +689,63 @@ strip_innermost_template_args (tree args, int extra_levels)
return new_args;
}
+/* Let ARGS know that there are COUNT entries which are not defaulted. ARGS must
+ * be a single level of arguments. */
+
+void
+set_non_default_template_args_count (tree args, int count)
+{
+ gcc_assert (!TMPL_ARGS_HAVE_MULTIPLE_LEVELS (args));
+ gcc_assert (count <= NUM_TMPL_ARGS (args));
+ tree cst = build_int_cst (integer_type_node, count);
+ if (TREE_CHAIN (args) && TREE_CODE (TREE_CHAIN (args)) == TREE_LIST)
+ TREE_VALUE (TREE_CHAIN (args)) = cst;
+ else
+ TREE_CHAIN (args) = cst;
+}
+
+/* Query the number of non-defaulted template arguments in a single level of
+ template ARGS. */
+
+int
+get_non_default_template_args_count (tree args)
+{
+ gcc_assert (!TMPL_ARGS_HAVE_MULTIPLE_LEVELS (args));
+ tree node = TREE_CHAIN (args);
+ if (!node)
+ return NUM_TMPL_ARGS (args);
+ if (TREE_CODE (node) == TREE_LIST)
+ node = TREE_VALUE (node);
+ const int count = int_cst_value (node);
+ gcc_assert (count <= NUM_TMPL_ARGS (args));
+ return count;
+}
+
+/* Set the COUNT of explicitly specified function template ARGS. */
+
+void
+set_explicit_template_args_count (tree args, int count)
+{
+ args = INNERMOST_TEMPLATE_ARGS (args);
+ tree non_default
+ = TREE_CHAIN (args)
+ ? INTEGER_CST_CHECK (TREE_CHAIN (args))
+ : build_int_cst (integer_type_node, TREE_VEC_LENGTH (args));
+ TREE_CHAIN (args) = build_tree_list (build_int_cst (integer_type_node, count),
+ non_default);
+}
+
+/* Query the number of explicity specified function template ARGS. */
+
+bool
+get_explicit_template_args_count (tree args)
+{
+ tree node = TREE_CHAIN (INNERMOST_TEMPLATE_ARGS (args));
+ return node && TREE_CODE (node) == TREE_LIST
+ ? int_cst_value (TREE_PURPOSE (node))
+ : 0;
+}
+
/* We've got a template header coming up; push to a new level for storing
the parms. */
@@ -2502,7 +2559,7 @@ determine_specialization (tree template_id,
if (candidates)
{
tree fn = TREE_VALUE (candidates);
- *targs_out = copy_node (DECL_TI_ARGS (fn));
+ *targs_out = copy_template_args (DECL_TI_ARGS (fn));
/* Propagate the candidate's constraints to the declaration. */
if (tsk != tsk_template)
@@ -4332,7 +4389,6 @@ expand_template_argument_pack (tree args)
tree result_args = NULL_TREE;
int in_arg, out_arg = 0, nargs = args ? TREE_VEC_LENGTH (args) : 0;
int num_result_args = -1;
- int non_default_args_count = -1;
/* First, determine if we need to expand anything, and the number of
slots we'll need. */
@@ -4362,9 +4418,7 @@ expand_template_argument_pack (tree args)
/* Expand arguments. */
result_args = make_tree_vec (num_result_args);
- if (NON_DEFAULT_TEMPLATE_ARGS_COUNT (args))
- non_default_args_count =
- GET_NON_DEFAULT_TEMPLATE_ARGS_COUNT (args);
+ int non_default_args_count = GET_NON_DEFAULT_TEMPLATE_ARGS_COUNT (args);
for (in_arg = 0; in_arg < nargs; ++in_arg)
{
tree arg = TREE_VEC_ELT (args, in_arg);
@@ -4374,8 +4428,7 @@ expand_template_argument_pack (tree args)
int i, num_packed = TREE_VEC_LENGTH (packed);
for (i = 0; i < num_packed; ++i, ++out_arg)
TREE_VEC_ELT (result_args, out_arg) = TREE_VEC_ELT(packed, i);
- if (non_default_args_count > 0)
- non_default_args_count += num_packed - 1;
+ non_default_args_count += num_packed - 1;
}
else
{
@@ -4383,8 +4436,7 @@ expand_template_argument_pack (tree args)
++out_arg;
}
}
- if (non_default_args_count >= 0)
- SET_NON_DEFAULT_TEMPLATE_ARGS_COUNT (result_args, non_default_args_count);
+ SET_NON_DEFAULT_TEMPLATE_ARGS_COUNT (result_args, non_default_args_count);
return result_args;
}
@@ -4787,8 +4839,7 @@ template_parm_to_arg (tree t)
/* Turn this argument into a TYPE_ARGUMENT_PACK
with a single element, which expands T. */
tree vec = make_tree_vec (1);
- if (CHECKING_P)
- SET_NON_DEFAULT_TEMPLATE_ARGS_COUNT (vec, TREE_VEC_LENGTH (vec));
+ SET_NON_DEFAULT_TEMPLATE_ARGS_COUNT (vec, TREE_VEC_LENGTH (vec));
TREE_VEC_ELT (vec, 0) = make_pack_expansion (t);
@@ -4803,8 +4854,7 @@ template_parm_to_arg (tree t)
/* Turn this argument into a NONTYPE_ARGUMENT_PACK
with a single element, which expands T. */
tree vec = make_tree_vec (1);
- if (CHECKING_P)
- SET_NON_DEFAULT_TEMPLATE_ARGS_COUNT (vec, TREE_VEC_LENGTH (vec));
+ SET_NON_DEFAULT_TEMPLATE_ARGS_COUNT (vec, TREE_VEC_LENGTH (vec));
t = convert_from_reference (t);
TREE_VEC_ELT (vec, 0) = make_pack_expansion (t);
@@ -4826,11 +4876,16 @@ template_parms_level_to_args (tree parms)
{
tree a = copy_node (parms);
TREE_TYPE (a) = NULL_TREE;
+ int nondefault = 0;
for (int i = TREE_VEC_LENGTH (a) - 1; i >= 0; --i)
- TREE_VEC_ELT (a, i) = template_parm_to_arg (TREE_VEC_ELT (a, i));
+ {
+ tree elt = TREE_VEC_ELT (a, i);
+ TREE_VEC_ELT (a, i) = template_parm_to_arg (elt);
+ if (!elt || elt == error_mark_node || !TREE_PURPOSE (elt))
+ ++nondefault;
+ }
- if (CHECKING_P)
- SET_NON_DEFAULT_TEMPLATE_ARGS_COUNT (a, TREE_VEC_LENGTH (a));
+ SET_NON_DEFAULT_TEMPLATE_ARGS_COUNT (a, nondefault);
return a;
}
@@ -8727,9 +8782,8 @@ coerce_template_parameter_pack (tree parms,
}
SET_ARGUMENT_PACK_ARGS (argument_pack, packed_args);
- if (CHECKING_P)
- SET_NON_DEFAULT_TEMPLATE_ARGS_COUNT (packed_args,
- TREE_VEC_LENGTH (packed_args));
+ SET_NON_DEFAULT_TEMPLATE_ARGS_COUNT (packed_args,
+ TREE_VEC_LENGTH (packed_args));
return argument_pack;
}
@@ -9093,7 +9147,7 @@ coerce_template_parms (tree parms,
return error_mark_node;
}
- if (CHECKING_P && !NON_DEFAULT_TEMPLATE_ARGS_COUNT (new_inner_args))
+ if (!NON_DEFAULT_TEMPLATE_ARGS_COUNT (new_inner_args))
SET_NON_DEFAULT_TEMPLATE_ARGS_COUNT (new_inner_args,
TREE_VEC_LENGTH (new_inner_args));
@@ -13357,8 +13411,9 @@ copy_template_args (tree t)
TREE_VEC_ELT (new_vec, i) = elt;
}
- NON_DEFAULT_TEMPLATE_ARGS_COUNT (new_vec)
- = NON_DEFAULT_TEMPLATE_ARGS_COUNT (t);
+ if (!TMPL_ARGS_HAVE_MULTIPLE_LEVELS (t))
+ NON_DEFAULT_TEMPLATE_ARGS_COUNT (new_vec)
+ = NON_DEFAULT_TEMPLATE_ARGS_COUNT (t);
return new_vec;
}
@@ -13398,13 +13453,33 @@ tree
tsubst_template_args (tree t, tree args, tsubst_flags_t complain, tree in_decl)
{
tree orig_t = t;
- int len, need_new = 0, i, expanded_len_adjust = 0, out;
+ int need_new = 0, i, expanded_len_adjust = 0, out;
tree *elts;
if (t == error_mark_node)
return error_mark_node;
- len = TREE_VEC_LENGTH (t);
+ const int len = TREE_VEC_LENGTH (t);
+ if (len == 0)
+ return t;
+
+ /* Shortcut if T has multiple levels. All elts must be TREE_VECs and we simply
+ recurse. The remainder of this function doesn't apply to multi-level
+ args. */
+ if (TMPL_ARGS_HAVE_MULTIPLE_LEVELS (t))
+ {
+ tree r = make_tree_vec (len);
+ for (i = 0; i < len; i++)
+ {
+ tree new_arg = tsubst_template_args (TREE_VEC_ELT (t, i), args,
+ complain, in_decl);
+ if (new_arg == error_mark_node)
+ return error_mark_node;
+ TREE_VEC_ELT (r, i) = new_arg;
+ }
+ return r;
+ }
+
elts = XALLOCAVEC (tree, len);
for (i = 0; i < len; i++)
@@ -13414,8 +13489,6 @@ tsubst_template_args (tree t, tree args, tsubst_flags_t complain, tree in_decl)
if (!orig_arg)
new_arg = NULL_TREE;
- else if (TREE_CODE (orig_arg) == TREE_VEC)
- new_arg = tsubst_template_args (orig_arg, args, complain, in_decl);
else if (PACK_EXPANSION_P (orig_arg))
{
/* Substitute into an expansion expression. */
@@ -13449,19 +13522,6 @@ tsubst_template_args (tree t, tree args, tsubst_flags_t complain, tree in_decl)
/* Make space for the expanded arguments coming from template
argument packs. */
t = make_tree_vec (len + expanded_len_adjust);
- /* ORIG_T can contain TREE_VECs. That happens if ORIG_T contains the
- arguments for a member template.
- In that case each TREE_VEC in ORIG_T represents a level of template
- arguments, and ORIG_T won't carry any non defaulted argument count.
- It will rather be the nested TREE_VECs that will carry one.
- In other words, ORIG_T carries a non defaulted argument count only
- if it doesn't contain any nested TREE_VEC. */
- if (NON_DEFAULT_TEMPLATE_ARGS_COUNT (orig_t))
- {
- int count = GET_NON_DEFAULT_TEMPLATE_ARGS_COUNT (orig_t);
- count += expanded_len_adjust;
- SET_NON_DEFAULT_TEMPLATE_ARGS_COUNT (t, count);
- }
for (i = 0, out = 0; i < len; i++)
{
tree orig_arg = TREE_VEC_ELT (orig_t, i);
@@ -13482,6 +13542,65 @@ tsubst_template_args (tree t, tree args, tsubst_flags_t complain, tree in_decl)
}
}
+ /* The non-default template count can only be copied from ARGS if ORIG_T
+ requests a substitution of a complete level from ARGS. If the resulting T
+ contains the same entries as ARGS, then simply return the relevant level
+ from ARGS, which already has the correct non-default count. */
+ int non_default_count = GET_NON_DEFAULT_TEMPLATE_ARGS_COUNT (orig_t)
+ + expanded_len_adjust;
+ if (expanded_len_adjust == 0)
+ {
+ auto level_of_parm = [](tree vec, int i) -> int {
+ tree parm = TREE_VEC_ELT (vec, i);
+ while (ARGUMENT_PACK_P (parm) || PACK_EXPANSION_P (parm))
+ {
+ if (ARGUMENT_PACK_P (parm))
+ parm = ARGUMENT_PACK_ARGS (parm);
+ if (PACK_EXPANSION_P (parm))
+ parm = PACK_EXPANSION_PATTERN (parm);
+ if (TREE_CODE (parm) == TREE_VEC && TREE_VEC_LENGTH (parm) == 1)
+ parm = TREE_VEC_ELT (parm, 0);
+ }
+ if ((TREE_CODE (parm) == TEMPLATE_TYPE_PARM
+ || TREE_CODE (parm) == TEMPLATE_TEMPLATE_PARM
+ || TREE_CODE (parm) == BOUND_TEMPLATE_TEMPLATE_PARM)
+ && i == TEMPLATE_TYPE_IDX (parm))
+ return TEMPLATE_TYPE_LEVEL (parm);
+ else if (TREE_CODE (parm) == TEMPLATE_PARM_INDEX
+ && i == TEMPLATE_PARM_IDX (parm))
+ return TEMPLATE_PARM_LEVEL (parm);
+ return -1;
+ };
+
+ const int level = level_of_parm (orig_t, 0);
+ if (level > 0 && level <= TMPL_ARGS_DEPTH (args))
+ {
+ args = TMPL_ARGS_LEVEL (args, level);
+ if (len == NUM_TMPL_ARGS (args))
+ {
+ bool t_equals_args
+ = TREE_VEC_ELT (args, 0) == TREE_VEC_ELT (t, 0);
+ int i;
+ for (i = 1; i < len; ++i)
+ {
+ if (level_of_parm (orig_t, i) == level)
+ t_equals_args = t_equals_args && TREE_VEC_ELT (args, i)
+ == TREE_VEC_ELT (t, i);
+ else
+ break;
+ }
+ if (i == len && t_equals_args)
+ {
+ ggc_free (t);
+ return args;
+ }
+ if (i == len && NON_DEFAULT_TEMPLATE_ARGS_COUNT (args))
+ non_default_count = GET_NON_DEFAULT_TEMPLATE_ARGS_COUNT (args);
+ }
+ }
+ }
+ SET_NON_DEFAULT_TEMPLATE_ARGS_COUNT (t, non_default_count);
+
return t;
}
@@ -21785,8 +21904,7 @@ fn_type_unification (tree fn,
explicit_targs = INNERMOST_TEMPLATE_ARGS (explicit_targs);
for (i = NUM_TMPL_ARGS (explicit_targs); i--;)
TREE_VEC_ELT (targs, i) = TREE_VEC_ELT (explicit_targs, i);
- if (!incomplete && CHECKING_P
- && !NON_DEFAULT_TEMPLATE_ARGS_COUNT (targs))
+ if (!incomplete && !NON_DEFAULT_TEMPLATE_ARGS_COUNT (targs))
SET_NON_DEFAULT_TEMPLATE_ARGS_COUNT
(targs, NUM_TMPL_ARGS (explicit_targs));
}
@@ -21972,6 +22090,11 @@ fn_type_unification (tree fn,
excessive_deduction_depth = false;
}
+ /* If all template parameters were explicitly given, treat them like default
+ template arguments for diagnostics. */
+ if (explicit_targs && explicit_targs != error_mark_node)
+ SET_EXPLICIT_TEMPLATE_ARGS_COUNT (targs, NUM_TMPL_ARGS (explicit_targs));
+
return r;
}
@@ -22614,9 +22737,17 @@ type_unification_real (tree tparms,
be NULL_TREE or ERROR_MARK_NODE, so we do not need
to explicitly check cxx_dialect here. */
if (TREE_PURPOSE (TREE_VEC_ELT (tparms, i)))
- /* OK, there is a default argument. Wait until after the
- conversion check to do substitution. */
- continue;
+ {
+ /* The position of the first default template argument,
+ is also the number of non-defaulted arguments in TARGS.
+ Record that. */
+ if (!NON_DEFAULT_TEMPLATE_ARGS_COUNT (targs))
+ SET_NON_DEFAULT_TEMPLATE_ARGS_COUNT (targs, i);
+
+ /* OK, there is a default argument. Wait until after the
+ conversion check to do substitution. */
+ continue;
+ }
/* If the type parameter is a parameter pack, then it will
be deduced to an empty parameter pack. */
@@ -22719,21 +22850,14 @@ type_unification_real (tree tparms,
if (arg == error_mark_node)
return 1;
else if (arg)
- {
- TREE_VEC_ELT (targs, i) = arg;
- /* The position of the first default template argument,
- is also the number of non-defaulted arguments in TARGS.
- Record that. */
- if (!NON_DEFAULT_TEMPLATE_ARGS_COUNT (targs))
- SET_NON_DEFAULT_TEMPLATE_ARGS_COUNT (targs, i);
- }
+ TREE_VEC_ELT (targs, i) = arg;
}
if (saw_undeduced++ == 1)
goto again;
}
- if (CHECKING_P && !NON_DEFAULT_TEMPLATE_ARGS_COUNT (targs))
+ if (!NON_DEFAULT_TEMPLATE_ARGS_COUNT (targs))
SET_NON_DEFAULT_TEMPLATE_ARGS_COUNT (targs, TREE_VEC_LENGTH (targs));
return unify_success (explain_p);
@@ -25022,6 +25146,7 @@ get_partial_spec_bindings (tree tmpl, tree spec_tmpl, tree args)
tree innermost_deduced_args;
innermost_deduced_args = make_tree_vec (ntparms);
+ SET_NON_DEFAULT_TEMPLATE_ARGS_COUNT (innermost_deduced_args, ntparms);
if (TMPL_ARGS_HAVE_MULTIPLE_LEVELS (args))
{
deduced_args = copy_node (args);
diff --git a/gcc/testsuite/g++.dg/debug/dwarf2/template-params-12n.C b/gcc/testsuite/g++.dg/debug/dwarf2/template-params-12n.C
index d3c1f589f87..b88bf7d4b34 100644
--- a/gcc/testsuite/g++.dg/debug/dwarf2/template-params-12n.C
+++ b/gcc/testsuite/g++.dg/debug/dwarf2/template-params-12n.C
@@ -1,6 +1,6 @@
// { dg-options "-gdwarf-2 -dA" }
// { dg-final { scan-assembler-times "DIE \\(\[^\n\]*\\) DW_TAG_template_value_param" 1 } }
-// { dg-final { scan-assembler-times "DIE \\(\[^\n\]*\\) DW_TAG_template_value_param\[^\n\]*\n\[^\n\]* DW_AT_name\n\[^\n\]* DW_AT_type\n\[^\n\]* DW_AT_const_value" 1 } }
+// { dg-final { scan-assembler-times "DIE \\(\[^\n\]*\\) DW_TAG_template_value_param\[^\n\]*\n\[^\n\]* DW_AT_name\n\[^\n\]* DW_AT_type\n(?:\[^\n\]* DW_AT_default_value\n)?\[^\n\]* DW_AT_const_value" 1 } }
#include "template-params-12.H"
/* We get const_value for NULL pointers to member functions. */
#if __cplusplus > 199711L // Ugh, C++98 barfs at both the cast and the overload.
diff --git a/gcc/testsuite/g++.dg/diagnostic/default-template-args-1.C b/gcc/testsuite/g++.dg/diagnostic/default-template-args-1.C
new file mode 100644
index 00000000000..6481798a69b
--- /dev/null
+++ b/gcc/testsuite/g++.dg/diagnostic/default-template-args-1.C
@@ -0,0 +1,61 @@
+// { dg-options "-fpretty-templates" }
+// { dg-do compile { target c++11 } }
+
+template <int a = 1>
+ [[deprecated]] void f0(); // { dg-message "'template<int a> void f0\\(\\)'" }
+
+template <int a>
+ [[deprecated]] void f1(); // { dg-message "'template<int a> void f1\\(\\)'" }
+
+template <class a, int b = 1>
+ [[deprecated]] void f2(); // { dg-message "'template<class a, int b> void f2\\(\\)'" }
+
+template <class a, int b = 1>
+ [[deprecated]] void f3(a); // { dg-message "'template<class a, int b> void f3\\(a\\)'" }
+
+template <class a>
+ [[deprecated]] void f4(a); // { dg-message "'template<class a> void f4\\(a\\)'" }
+
+template <>
+ [[deprecated]] void f4<int>(int);
+
+template <>
+ [[deprecated]] void f4(float);
+
+template <class a, class b = int>
+ [[deprecated]] void f5(a);
+
+template void f5<float>(float); // { dg-error "'void f5<a>\\(a\\) .with a = float.'" }
+
+template <class a, class b = int>
+ struct c
+ {
+ template <class d, class e = int>
+ [[deprecated]] static void f(d);
+ };
+
+int main()
+{
+ f0(); // { dg-warning "'void f0\\(\\)'" }
+ f1<1>(); // { dg-warning "'void f1<a>\\(\\) .with int a = 1.'" }
+ f2<int>(); // { dg-warning "'void f2<a>\\(\\) .with a = int.'" }
+ f3(1); // { dg-warning "'void f3\\(a\\) .with a = int.'" }
+ f3<float>(1); // { dg-warning "'void f3<a>\\(a\\) .with a = float.'" }
+ f3<float, 2>(1); // { dg-warning "'void f3<a, b>\\(a\\) .with a = float; int b = 2.'" }
+ f4(1.); // { dg-warning "'void f4\\(a\\) .with a = double.'" }
+ f4(1); // { dg-warning "'void f4<int>\\(int\\)'" }
+ f4(1.f); // { dg-warning "'void f4\\(float\\)'" }
+
+ f0(0); // { dg-error "" }
+ f1(0); // { dg-error "" }
+ f2(0); // { dg-error "" }
+ f3(); // { dg-error "" }
+ f4(); // { dg-error "" }
+
+ c<int>::f(1.); // { dg-warning "'static void c<a>::f\\(d\\) .with d = double; a = int.'" }
+ c<int>::f<int>(1); // { dg-warning "'static void c<a>::f<d>\\(d\\) .with d = int; a = int.'" }
+ c<int>::f<float, int>(1.f); // { dg-warning "'static void c<a>::f<d, e>\\(d\\) .with d = float; e = int; a = int.'" }
+ 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.'" }
+}
diff --git a/gcc/testsuite/g++.dg/diagnostic/default-template-args-2.C b/gcc/testsuite/g++.dg/diagnostic/default-template-args-2.C
new file mode 100644
index 00000000000..a82709f5785
--- /dev/null
+++ b/gcc/testsuite/g++.dg/diagnostic/default-template-args-2.C
@@ -0,0 +1,37 @@
+// { dg-options "-fno-pretty-templates" }
+// { dg-do compile { target c++11 } }
+
+template <int a = 1>
+ [[deprecated]] void f0();
+
+template <int a>
+ [[deprecated]] void f1();
+
+template <class a, int b = 1>
+ [[deprecated]] void f2();
+
+template <class a, int b = 1>
+ [[deprecated]] void f3(a);
+
+template <class a>
+ [[deprecated]] void f4(a);
+
+template <>
+ [[deprecated]] void f4<int>(int);
+
+template <class a, class b = int>
+ [[deprecated]] void f5(a);
+
+template void f5<float>(float); // { dg-error "'void f5<float>\\(float\\)'" }
+
+int main()
+{
+ f0(); // { dg-warning "'void f0\\(\\)'" }
+ f1<1>(); // { dg-warning "'void f1<1>\\(\\)'" }
+ f2<int>(); // { dg-warning "'void f2<int>\\(\\)'" }
+ f3(1); // { dg-warning "'void f3\\(int\\)'" }
+ f3<float>(1); // { dg-warning "'void f3<float>\\(float\\)'" }
+ f3<float, 2>(1); // { dg-warning "'void f3<float, 2>\\(float\\)'" }
+ f4(1.); // { dg-warning "'void f4\\(double\\)'" }
+ f4(1); // { dg-warning "'void f4<int>\\(int\\)'" }
+}
diff --git a/gcc/testsuite/g++.dg/diagnostic/default-template-args-3.C b/gcc/testsuite/g++.dg/diagnostic/default-template-args-3.C
new file mode 100644
index 00000000000..c5dceafca29
--- /dev/null
+++ b/gcc/testsuite/g++.dg/diagnostic/default-template-args-3.C
@@ -0,0 +1,29 @@
+// { dg-options "-fpretty-templates" }
+// { dg-do compile { target c++11 } }
+
+template <class T>
+ struct id
+ { using type = T; };
+
+template <class T0, class T1 = int, template <class> class foo = id>
+ struct A
+ {
+ template <class U0 = const T1&>
+ [[deprecated]] static void
+ f(typename foo<U0>::type);
+ };
+
+template <int> struct B;
+
+template <> struct B<1>
+ { using type = int; };
+
+template <class T, int x = 1>
+ [[deprecated]] typename B<x>::type
+ f0(T);
+
+int main()
+{
+ A<int>::f(0); // { dg-warning "'static void A<T0>::f\\(typename foo<U0>::type\\) .with U0 = const int&; T0 = int; foo = id; typename foo<U0>::type = const int&.'" }
+ f0(0); // { dg-warning "'typename B<x>::type f0\\(T\\) .with T = int; int x = 1; typename B<x>::type = int.'" }
+}
diff --git a/gcc/testsuite/g++.dg/diagnostic/default-template-args-4.C b/gcc/testsuite/g++.dg/diagnostic/default-template-args-4.C
new file mode 100644
index 00000000000..3cb604a28ea
--- /dev/null
+++ b/gcc/testsuite/g++.dg/diagnostic/default-template-args-4.C
@@ -0,0 +1,24 @@
+// { dg-options "-fpretty-templates" }
+// { dg-do compile { target c++11 } }
+
+template <class T> struct X {};
+template <class... T> struct Y {};
+
+template <template <class> class Tpl, template <class...> class VTpl,
+ class T0 = float, class U0 = VTpl<Tpl<T0>>, int x0 = 2>
+ struct S
+ {
+ template <class T, class... Ps, class U = int, int x = 1>
+ [[deprecated]] static U0
+ f(T, Ps&&...);
+
+ template <class T, class... Ps, class U = int, int x = 1>
+ [[deprecated]] U0
+ g(T, Ps&&...);
+ };
+
+void g()
+{
+ S<X, Y>::f(1, '2', 3, 4.); // { dg-warning "'static U0 S<Tpl, VTpl>::f\\(T, Ps&& ...\\) .with T = int; Ps = {char, int, double}; Tpl = X; VTpl = Y; U0 = Y<X<float> >.'" }
+ S<X, Y>().g(1, '2', 3, 4.); // { dg-warning "'U0 S<Tpl, VTpl>::g\\(T, Ps&& ...\\) .with T = int; Ps = {char, int, double}; Tpl = X; VTpl = Y; U0 = Y<X<float> >.'" }
+}
diff --git a/gcc/testsuite/g++.dg/diagnostic/default-template-args-5.C b/gcc/testsuite/g++.dg/diagnostic/default-template-args-5.C
new file mode 100644
index 00000000000..0f13466fb27
--- /dev/null
+++ b/gcc/testsuite/g++.dg/diagnostic/default-template-args-5.C
@@ -0,0 +1,12 @@
+// { dg-options "-fpretty-templates" }
+// { dg-do compile { target c++11 } }
+//
+// Tests the case where DECL_TI_TEMPLATE doesn't return a TEMPLATE_DECL for the
+// FUNCTION_DECL of foo<U>(int).
+
+template <class T0, class T1 = int, int x = 1> void foo (int);
+
+template <class U, int y = 2>
+class Q {
+ friend void foo<U> (int = 3); // { dg-error "'void foo<U>\\(int\\)'" }
+};
diff --git a/gcc/testsuite/g++.dg/diagnostic/param-type-mismatch-2.C b/gcc/testsuite/g++.dg/diagnostic/param-type-mismatch-2.C
index de7570a6efa..0aa45404283 100644
--- a/gcc/testsuite/g++.dg/diagnostic/param-type-mismatch-2.C
+++ b/gcc/testsuite/g++.dg/diagnostic/param-type-mismatch-2.C
@@ -141,7 +141,7 @@ int test_7 (int first, const char *second, float third)
|
const char*
{ dg-end-multiline-output "" } */
- // { dg-message "initializing argument 2 of 'int test_7\\(int, T, float\\) .with T = const char\\*\\*.'" "" { target *-*-* } test_7_decl }
+ // { dg-message "initializing argument 2 of 'int test_7<T>\\(int, T, float\\) .with T = const char\\*\\*.'" "" { target *-*-* } test_7_decl }
/* { dg-begin-multiline-output "" }
int test_7 (int one, T two, float three);
~~^~~
diff --git a/gcc/testsuite/g++.dg/ext/pretty1.C b/gcc/testsuite/g++.dg/ext/pretty1.C
index 06608ae30eb..c5bfd6082a7 100644
--- a/gcc/testsuite/g++.dg/ext/pretty1.C
+++ b/gcc/testsuite/g++.dg/ext/pretty1.C
@@ -60,7 +60,7 @@ __assert_fail (const char *cond, const char *file, unsigned int line,
abort ();
}
-// { dg-final { scan-assembler "int bar\\(T\\).*with T = int" } }
+// { dg-final { scan-assembler "int bar<int>\\(int\\)" } }
// { dg-final { scan-assembler "top level" } }
// { dg-final { scan-assembler "int main\\(\\)" } }
// { dg-final { scan-assembler "int bar\\(T\\).*with T = double" } }
diff --git a/gcc/testsuite/g++.dg/goacc/template.C b/gcc/testsuite/g++.dg/goacc/template.C
index 10d3f446da7..4fcd88bfc56 100644
--- a/gcc/testsuite/g++.dg/goacc/template.C
+++ b/gcc/testsuite/g++.dg/goacc/template.C
@@ -157,12 +157,12 @@ main ()
}
/* { dg-final { scan-tree-dump-times {(?n)^OpenACC routine '[^']+' has 'nohost' clause\.$} 4 oaccloops } }
- { dg-final { scan-tree-dump-times {(?n)^OpenACC routine 'T accDouble\(int\) \[with T = char\]' has 'nohost' clause\.$} 1 oaccloops { target { ! offloading_enabled } } } }
+ { dg-final { scan-tree-dump-times {(?n)^OpenACC routine 'T accDouble<T>\(int\) \[with T = char\]' has 'nohost' clause\.$} 1 oaccloops { target { ! offloading_enabled } } } }
{ dg-final { scan-tree-dump-times {(?n)^OpenACC routine 'accDouble<char>\(int\)char' has 'nohost' clause\.$} 1 oaccloops { target offloading_enabled } } }
- { dg-final { scan-tree-dump-times {(?n)^OpenACC routine 'T accDouble\(int\) \[with T = int\]' has 'nohost' clause\.$} 1 oaccloops { target { ! offloading_enabled } } } }
+ { dg-final { scan-tree-dump-times {(?n)^OpenACC routine 'T accDouble<T>\(int\) \[with T = int\]' has 'nohost' clause\.$} 1 oaccloops { target { ! offloading_enabled } } } }
{ dg-final { scan-tree-dump-times {(?n)^OpenACC routine 'accDouble<int>\(int\)int' has 'nohost' clause\.$} 1 oaccloops { target offloading_enabled } } }
- { dg-final { scan-tree-dump-times {(?n)^OpenACC routine 'T accDouble\(int\) \[with T = float\]' has 'nohost' clause\.$} 1 oaccloops { target { ! offloading_enabled } } } }
+ { dg-final { scan-tree-dump-times {(?n)^OpenACC routine 'T accDouble<T>\(int\) \[with T = float\]' has 'nohost' clause\.$} 1 oaccloops { target { ! offloading_enabled } } } }
{ dg-final { scan-tree-dump-times {(?n)^OpenACC routine 'accDouble<float>\(int\)float' has 'nohost' clause\.$} 1 oaccloops { target offloading_enabled } } }
- { dg-final { scan-tree-dump-times {(?n)^OpenACC routine 'T accDouble\(int\) \[with T = double\]' has 'nohost' clause\.$} 1 oaccloops { target { ! offloading_enabled } } } }
+ { dg-final { scan-tree-dump-times {(?n)^OpenACC routine 'T accDouble<T>\(int\) \[with T = double\]' has 'nohost' clause\.$} 1 oaccloops { target { ! offloading_enabled } } } }
{ dg-final { scan-tree-dump-times {(?n)^OpenACC routine 'accDouble<double>\(int\)double' has 'nohost' clause\.$} 1 oaccloops { target offloading_enabled } } }
TODO See PR101551 for 'offloading_enabled' differences. */
diff --git a/gcc/testsuite/g++.dg/template/error40.C b/gcc/testsuite/g++.dg/template/error40.C
index c5df56fc1be..16a44d1819f 100644
--- a/gcc/testsuite/g++.dg/template/error40.C
+++ b/gcc/testsuite/g++.dg/template/error40.C
@@ -8,11 +8,11 @@ struct A
void foo(void)
{
- A<void> a = 0; // { dg-error "A<void, 0, 1>" }
+ A<void> a = 0; // { dg-error "A<void>" }
}
-template <class T> T f(T); // { dg-message "int f<int>.int." }
-template <class T> T f(T, int = 0); // { dg-message "" }
+template <class T> T f(T); // { dg-message "int f.int." }
+template <class T> T f(T, int = 0); // { dg-message "int f.int, int." }
template <class T>
struct B
diff --git a/gcc/testsuite/g++.old-deja/g++.ext/pretty3.C b/gcc/testsuite/g++.old-deja/g++.ext/pretty3.C
index 6348ae1ee67..30c7ecd5065 100644
--- a/gcc/testsuite/g++.old-deja/g++.ext/pretty3.C
+++ b/gcc/testsuite/g++.old-deja/g++.ext/pretty3.C
@@ -35,7 +35,7 @@ template<> void f1<int> (int)
if (strcmp (function, "f1<int>"))
bad = true;
- if (strcmp (pretty, "void f1(T) [with T = int]"))
+ if (strcmp (pretty, "void f1<int>(int)"))
bad = true;
}
diff --git a/gcc/testsuite/g++.old-deja/g++.pt/memtemp77.C b/gcc/testsuite/g++.old-deja/g++.pt/memtemp77.C
index 6dd4b546c0c..93dbf4b489f 100644
--- a/gcc/testsuite/g++.old-deja/g++.pt/memtemp77.C
+++ b/gcc/testsuite/g++.old-deja/g++.pt/memtemp77.C
@@ -19,7 +19,7 @@ const char* S3<char>::h(int) { return __PRETTY_FUNCTION__; }
int main()
{
if (strcmp (S3<double>::h(7),
- "static const char* S3<T>::h(U) [with U = int; T = double]") == 0)
+ "static const char* S3<double>::h(int)") == 0)
return 0;
else
return 1;
^ permalink raw reply [flat|nested] 24+ messages in thread
end of thread, other threads:[~2021-12-02 8:35 UTC | newest]
Thread overview: 24+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2021-07-15 12:21 [PATCH v3] c++: Add gnu::diagnose_as attribute Matthias Kretz
2021-07-23 8:58 ` [PATCH v4] " Matthias Kretz
2021-08-17 18:31 ` Jason Merrill
2021-11-08 16:40 ` [RFC] c++: Print function template parms when relevant (was: [PATCH v4] c++: Add gnu::diagnose_as attribute) Matthias Kretz
2021-11-08 20:00 ` Matthias Kretz
2021-11-16 20:25 ` Jason Merrill
2021-11-16 20:42 ` Matthias Kretz
2021-11-16 20:49 ` Jason Merrill
2021-11-16 20:51 ` Matthias Kretz
2021-11-17 6:09 ` Jason Merrill
2021-11-17 9:04 ` Matthias Kretz
2021-11-17 18:25 ` Jason Merrill
2021-11-17 22:51 ` Matthias Kretz
2021-11-18 19:24 ` Jason Merrill
2021-11-19 9:53 ` Matthias Kretz
2021-11-19 12:02 ` Matthias Kretz
2021-11-19 22:26 ` Jason Merrill
2021-11-19 23:11 ` Matthias Kretz
2021-11-26 15:23 ` [PATCH 0/2] " Matthias Kretz
2021-11-26 15:24 ` [PATCH 1/2] c++: Print function template parms when relevant Matthias Kretz
2021-12-02 8:35 ` [PATCH v2 " Matthias Kretz
2021-11-26 15:24 ` [PATCH 2/2] c++: Print function template parms when relevant [part 2] Matthias Kretz
2021-09-08 2:21 ` [PATCH v4] c++: Add gnu::diagnose_as attribute Jason Merrill
2021-11-15 0:35 ` [PATCH v5] " Matthias Kretz
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).