On Thu, Feb 12, 2026 at 08:47:37PM +0900, Jason Merrill wrote:
> On 2/12/26 7:33 AM, Marek Polacek wrote:
> > On Wed, Feb 11, 2026 at 06:07:24PM +0100, Jakub Jelinek wrote:
> > > On Wed, Feb 11, 2026 at 11:54:58AM -0500, Marek Polacek wrote:
> > > > > Though, why not just use %qE and make sure to pass a REFLECT_EXPR and
> > > > > ensure it is printed in human readable form (partly kind like
> > > > > display_string_of, but e.g. with ^^ at the start).
> > > >
> > > > Would it be better if tree_category returned e.g.
> > > >
> > > > return G_(" (type)");
> > > >
> > > > and is then used at the end of the error message like this:
> > > >
> > > > error_at (loc, "expected a reflection of ... "
> > > > "instead of %qE" "%s", t, tree_category (t));
> > > >
> > > > or is it still bad wrt translation? %s because I don't want to
> > > > quote the category. This would produce:
> > > >
> > > > error: expected a reflection of a function template instead of 'x'
> > > > (variable)
> > >
> > > Maybe.
> > >
> > > Yet another option would be a pair of diagnostic messages:
> > > auto_diagnostic_group d;
> > > error_at (loc, "expected a reflection of ... ");
> > > inform (loc, "while %qE is a variable", t);
> > > where the inform could be done in a helper function which various
> > > error_at callers would call to explain stuff.
> >
> > That sounds good, albeit a little bit more wordy. But at least
> > we won't have translation problems. Thanks,
> >
> > Bootstrapped/regtested on x86_64-pc-linux-gnu, ok for trunk?
> >
> > -- >8 --
> > This patch improves various reflection diagnostics as discussed in
> > <https://gcc.gnu.org/pipermail/gcc-patches/2025-December/704168.html>.
> >
> > In particular:
> > - reword "not usable" diagnostics to say what kind of reflections we
> > expected,
> > - use the new inform_tree_category to better describe what kind of
> > reflection
> > we actually got,
> > - when we see a missing 'template' keyword, emit a
> > -Wmissing-template-keyword
> > warning instead of giving a hard error (this should only happen when the
> > code would be valid with the 'template' added.
> >
> > gcc/cp/ChangeLog:
> >
> > * cp-tree.h (inform_tree_category): Declare.
> > * error.cc (inform_tree_category): New.
> > * parser.cc (cp_parser_splice_specifier): Use OVL_FIRST when checking
> > for TEMPLATE_DECL.
> > (cp_parser_splice_type_specifier): Reword an error message. Call
> > inform_tree_category.
> > (cp_parser_splice_expression): Check check_splice_expr earlier. Reword
> > error messages. Call inform_tree_category. Turn an error into an
> > assert. Use missing_template_diag instead of giving an error about
> > a missing 'template' keyword.
> > (cp_parser_splice_scope_specifier): Reword an error message. Call
> > inform_tree_category.
> > (missing_template_diag): Forward declare. Drop "enum" in a parameter.
> > * reflect.cc (check_splice_expr): Reword error messages. Call
> > inform_tree_category.
> >
> > gcc/testsuite/ChangeLog:
> >
> > * g++.dg/reflect/crash10.C: Adjust expected diagnostics.
> > * g++.dg/reflect/crash2.C: Likewise.
> > * g++.dg/reflect/crash3.C: Likewise.
> > * g++.dg/reflect/crash7.C: Likewise.
> > * g++.dg/reflect/crash9.C: Likewise.
> > * g++.dg/reflect/dep5.C: Likewise.
> > * g++.dg/reflect/diag1.C: Likewise.
> > * g++.dg/reflect/error10.C: Likewise.
> > * g++.dg/reflect/error12.C: Likewise.
> > * g++.dg/reflect/error5.C: Likewise.
> > * g++.dg/reflect/expr3.C: Likewise.
> > * g++.dg/reflect/member1.C: Likewise.
> > * g++.dg/reflect/ns2.C: Likewise. Test more cases.
> > * g++.dg/reflect/p2996-12.C: Likewise.
> > * g++.dg/reflect/splice5.C: Likewise.
> > * g++.dg/reflect/diag1a.C: New test.
> > * g++.dg/reflect/diag1b.C: New test.
> > ---
> > gcc/cp/cp-tree.h | 1 +
> > gcc/cp/error.cc | 34 +++++++++
> > gcc/cp/parser.cc | 95 ++++++++++---------------
> > gcc/cp/reflect.cc | 11 +--
> > gcc/testsuite/g++.dg/reflect/crash10.C | 6 +-
> > gcc/testsuite/g++.dg/reflect/crash2.C | 3 +-
> > gcc/testsuite/g++.dg/reflect/crash3.C | 6 +-
> > gcc/testsuite/g++.dg/reflect/crash7.C | 3 +-
> > gcc/testsuite/g++.dg/reflect/crash9.C | 6 +-
> > gcc/testsuite/g++.dg/reflect/dep5.C | 6 +-
> > gcc/testsuite/g++.dg/reflect/diag1.C | 6 +-
> > gcc/testsuite/g++.dg/reflect/diag1a.C | 24 +++++++
> > gcc/testsuite/g++.dg/reflect/diag1b.C | 23 ++++++
> > gcc/testsuite/g++.dg/reflect/error10.C | 27 +++++--
> > gcc/testsuite/g++.dg/reflect/error12.C | 30 +++++---
> > gcc/testsuite/g++.dg/reflect/error5.C | 3 +-
> > gcc/testsuite/g++.dg/reflect/expr3.C | 25 ++++---
> > gcc/testsuite/g++.dg/reflect/member1.C | 14 ++--
> > gcc/testsuite/g++.dg/reflect/ns2.C | 24 ++++++-
> > gcc/testsuite/g++.dg/reflect/p2996-12.C | 3 +-
> > gcc/testsuite/g++.dg/reflect/splice5.C | 10 +--
> > 21 files changed, 247 insertions(+), 113 deletions(-)
> > create mode 100644 gcc/testsuite/g++.dg/reflect/diag1a.C
> > create mode 100644 gcc/testsuite/g++.dg/reflect/diag1b.C
> >
> > diff --git a/gcc/cp/cp-tree.h b/gcc/cp/cp-tree.h
> > index fcad67a662c..c9f0ba9c125 100644
> > --- a/gcc/cp/cp-tree.h
> > +++ b/gcc/cp/cp-tree.h
> > @@ -7688,6 +7688,7 @@ extern bool pedwarn_cxx98
> > (location_t,
> > extern location_t location_of (tree);
> > extern void qualified_name_lookup_error (tree, tree, tree,
> > location_t);
> > +void inform_tree_category (location_t, tree);
> > struct decl_location_traits
> > : simple_cache_map_traits<tree_decl_hash, location_t> { };
> > diff --git a/gcc/cp/error.cc b/gcc/cp/error.cc
> > index c184846066e..74c2c0935df 100644
> > --- a/gcc/cp/error.cc
> > +++ b/gcc/cp/error.cc
> > @@ -3976,6 +3976,40 @@ function_category (tree fn)
> > return G_("In function %qD");
> > }
> > +/* We expected some kind of tree but instead got T and emitted a
> > diagnostic.
> > + Print the category of T (type, expression, ...) if possible. */
> > +
> > +void
> > +inform_tree_category (location_t loc, tree t)
> > +{
> > + t = maybe_get_first_fn (t);
> > + if (TREE_CODE (t) == TYPE_DECL)
> > + t = TREE_TYPE (t);
> > +
> > + if (TYPE_P (t))
> > + inform (loc, "but %qE is a type", t);
>
> How about using location_of (t) for these informs since the error was
> already at loc?
That looks pretty good. E.g.:
ns2.C:25:6: error: expected a reflection of a class, namespace, or enumeration
25 | [: ^^foo :]::X;
| ~~~^~~~~~~~
ns2.C:5:6: note: but 'foo' is a function
5 | void foo (int);
| ^~~
ns2.C:27:6: error: expected a reflection of a class, namespace, or enumeration
27 | [: ^^EE :]::Y;
| ~~~^~~~~~~
ns2.C:7:10: note: but 'EE' is an enumerator
7 | enum E { EE };
| ^~
ns2.C:29:6: error: expected a reflection of a class, namespace, or enumeration
29 | [: ^^S :]::Z;
| ~~~^~~~~~
ns2.C:9:8: note: but 'S' is a class template
9 | struct S { int i, j, k; };
| ^
So I did that.
Bootstrapped/regtested on x86_64-pc-linux-gnu, ok for trunk?
-- >8 --
This patch improves various reflection diagnostics as discussed in
<https://gcc.gnu.org/pipermail/gcc-patches/2025-December/704168.html>.
In particular:
- reword "not usable" diagnostics to say what kind of reflections we
expected,
- use the new inform_tree_category to better describe what kind of reflection
we actually got,
- when we see a missing 'template' keyword, emit a -Wmissing-template-keyword
warning instead of giving a hard error (this should only happen when the
code would be valid with the 'template' added.
gcc/cp/ChangeLog:
* cp-tree.h (inform_tree_category): Declare.
* error.cc (inform_tree_category): New.
* parser.cc (cp_parser_splice_specifier): Use OVL_FIRST when checking
for TEMPLATE_DECL.
(cp_parser_splice_type_specifier): Reword an error message. Call
inform_tree_category.
(cp_parser_splice_expression): Check check_splice_expr earlier. Reword
error messages. Call inform_tree_category. Turn an error into an
assert. Use missing_template_diag instead of giving an error about
a missing 'template' keyword.
(cp_parser_splice_scope_specifier): Reword an error message. Call
inform_tree_category.
(missing_template_diag): Forward declare. Drop "enum" in a parameter.
* reflect.cc (check_splice_expr): Reword error messages. Call
inform_tree_category.
gcc/testsuite/ChangeLog:
* g++.dg/reflect/crash10.C: Adjust expected diagnostics.
* g++.dg/reflect/crash2.C: Likewise.
* g++.dg/reflect/crash3.C: Likewise.
* g++.dg/reflect/crash7.C: Likewise.
* g++.dg/reflect/crash9.C: Likewise.
* g++.dg/reflect/dep5.C: Likewise.
* g++.dg/reflect/diag1.C: Likewise.
* g++.dg/reflect/error10.C: Likewise.
* g++.dg/reflect/error12.C: Likewise.
* g++.dg/reflect/error5.C: Likewise.
* g++.dg/reflect/expr3.C: Likewise.
* g++.dg/reflect/member1.C: Likewise.
* g++.dg/reflect/ns2.C: Likewise. Test more cases.
* g++.dg/reflect/p2996-12.C: Likewise.
* g++.dg/reflect/splice5.C: Likewise.
* g++.dg/reflect/diag1a.C: New test.
* g++.dg/reflect/diag1b.C: New test.
---
gcc/cp/cp-tree.h | 1 +
gcc/cp/error.cc | 36 ++++++++++
gcc/cp/parser.cc | 95 ++++++++++---------------
gcc/cp/reflect.cc | 11 +--
gcc/testsuite/g++.dg/reflect/crash10.C | 5 +-
gcc/testsuite/g++.dg/reflect/crash2.C | 3 +-
gcc/testsuite/g++.dg/reflect/crash3.C | 6 +-
gcc/testsuite/g++.dg/reflect/crash7.C | 3 +-
gcc/testsuite/g++.dg/reflect/crash9.C | 5 +-
gcc/testsuite/g++.dg/reflect/dep5.C | 5 +-
gcc/testsuite/g++.dg/reflect/diag1.C | 6 +-
gcc/testsuite/g++.dg/reflect/diag1a.C | 24 +++++++
gcc/testsuite/g++.dg/reflect/diag1b.C | 23 ++++++
gcc/testsuite/g++.dg/reflect/error10.C | 27 +++++--
gcc/testsuite/g++.dg/reflect/error12.C | 21 +++---
gcc/testsuite/g++.dg/reflect/error5.C | 3 +-
gcc/testsuite/g++.dg/reflect/expr3.C | 22 +++---
gcc/testsuite/g++.dg/reflect/member1.C | 14 ++--
gcc/testsuite/g++.dg/reflect/ns2.C | 32 +++++++--
gcc/testsuite/g++.dg/reflect/p2996-12.C | 3 +-
gcc/testsuite/g++.dg/reflect/splice5.C | 10 +--
21 files changed, 241 insertions(+), 114 deletions(-)
create mode 100644 gcc/testsuite/g++.dg/reflect/diag1a.C
create mode 100644 gcc/testsuite/g++.dg/reflect/diag1b.C
diff --git a/gcc/cp/cp-tree.h b/gcc/cp/cp-tree.h
index fcad67a662c..f1cdd715f69 100644
--- a/gcc/cp/cp-tree.h
+++ b/gcc/cp/cp-tree.h
@@ -7688,6 +7688,7 @@ extern bool pedwarn_cxx98
(location_t,
extern location_t location_of (tree);
extern void qualified_name_lookup_error (tree, tree, tree,
location_t);
+void inform_tree_category (tree);
struct decl_location_traits
: simple_cache_map_traits<tree_decl_hash, location_t> { };
diff --git a/gcc/cp/error.cc b/gcc/cp/error.cc
index c184846066e..e64da242152 100644
--- a/gcc/cp/error.cc
+++ b/gcc/cp/error.cc
@@ -3976,6 +3976,42 @@ function_category (tree fn)
return G_("In function %qD");
}
+/* We expected some kind of tree but instead got T and emitted a diagnostic.
+ Print the category of T (type, expression, ...) if possible. */
+
+void
+inform_tree_category (tree t)
+{
+ const location_t loc = location_of (t);
+
+ t = maybe_get_first_fn (t);
+ if (TREE_CODE (t) == TYPE_DECL)
+ t = TREE_TYPE (t);
+
+ if (TYPE_P (t))
+ inform (loc, "but %qE is a type", t);
+ else if (EXPR_P (t))
+ inform (loc, "but %qE is an expression", t);
+ else if (DECL_DECOMPOSITION_P (t) && !DECL_DECOMP_IS_BASE (t))
+ inform (loc, "but %qE is a structured binding", t);
+ else if (VAR_P (t))
+ inform (loc, "but %qE is a variable", t);
+ else if (TREE_CODE (t) == PARM_DECL)
+ inform (loc, "but %qE is a parameter", t);
+ else if (TREE_CODE (t) == FUNCTION_DECL)
+ inform (loc, "but %qE is a function", t);
+ else if (TREE_CODE (t) == FIELD_DECL)
+ inform (loc, "but %qE is a data member", t);
+ else if (DECL_FUNCTION_TEMPLATE_P (t))
+ inform (loc, "but %qE is a function template", t);
+ else if (DECL_CLASS_TEMPLATE_P (t))
+ inform (loc, "but %qE is a class template", t);
+ else if (TREE_CODE (t) == NAMESPACE_DECL)
+ inform (loc, "but %qE is a namespace", t);
+ else if (TREE_CODE (t) == CONST_DECL && !DECL_TEMPLATE_PARM_P (t))
+ inform (loc, "but %qE is an enumerator", t);
+}
+
/* Disable warnings about missing quoting in GCC diagnostics for
the pp_verbatim calls. Their format strings deliberately don't
follow GCC diagnostic conventions. */
diff --git a/gcc/cp/parser.cc b/gcc/cp/parser.cc
index 0f53ff90215..9d53240e549 100644
--- a/gcc/cp/parser.cc
+++ b/gcc/cp/parser.cc
@@ -279,6 +279,8 @@ static tree omp_start_variant_function
static int omp_finish_variant_function
(cp_parser *, tree, tree, tree, bool, bool);
static void maybe_start_implicit_template (cp_parser *parser);
+static void missing_template_diag
+ (location_t, diagnostics::kind = diagnostics::kind::warning);
/* Manifest constants. */
#define CP_LEXER_BUFFER_SIZE ((256 * 1024) / sizeof (cp_token))
@@ -6183,7 +6185,9 @@ cp_parser_splice_specifier (cp_parser *parser, bool
template_p = false,
/* As a courtesy to the user, if there is a < after a template
name, parse the construct as an s-s-s and warn about the missing
'template'; it can't be anything else. */
- && (template_p || typename_p || TREE_CODE (expr) == TEMPLATE_DECL))
+ && (template_p
+ || typename_p
+ || TREE_CODE (OVL_FIRST (expr)) == TEMPLATE_DECL))
{
/* For member access splice-specialization-specifier, try to wrap
non-dependent splice for function template into a BASELINK so
@@ -6252,7 +6256,11 @@ cp_parser_splice_type_specifier (cp_parser *parser)
if (!valid_splice_type_p (type))
{
if (!cp_parser_simulate_error (parser))
- error_at (loc, "reflection %qE not usable in a splice type", type);
+ {
+ auto_diagnostic_group d;
+ error_at (loc, "expected a reflection of a type");
+ inform_tree_category (type);
+ }
type = NULL_TREE;
}
@@ -6313,63 +6321,42 @@ cp_parser_splice_expression (cp_parser *parser, bool
template_p,
return error_mark_node;
}
+ /* Make sure this splice-expression produces an expression. */
+ if (!check_splice_expr (loc, expr.get_start (), t, address_p,
+ member_access_p, /*complain=*/true))
+ return error_mark_node;
+
if (template_p)
{
/* [expr.prim.splice] For a splice-expression of the form template
splice-specifier, the splice-specifier shall designate a function
template. */
- if (!targs_p)
+ if (!targs_p
+ && !really_overloaded_fn (t)
+ && !dependent_splice_p (t))
{
- if (!really_overloaded_fn (t) && !dependent_splice_p (t))
- {
- auto_diagnostic_group d;
- error_at (loc, "reflection %qE not usable in a template splice",
- t);
- inform (loc, "only function templates are allowed here");
- return error_mark_node;
- }
+ auto_diagnostic_group d;
+ error_at (loc, "expected a reflection of a function template");
+ inform_tree_category (t);
+ return error_mark_node;
}
/* [expr.prim.splice] For a splice-expression of the form
template splice-specialization-specifier, the splice-specifier of the
- splice-specialization-specifier shall designate a template. */
- else
- {
- if (really_overloaded_fn (t)
- || get_template_info (t)
- || TREE_CODE (t) == TEMPLATE_ID_EXPR)
- /* OK */;
- else
- {
- auto_diagnostic_group d;
- error_at (loc, "reflection %qE not usable in a template splice",
- t);
- inform (loc, "only templates are allowed here");
- return error_mark_node;
- }
- }
+ splice-specialization-specifier shall designate a template. Since
+ we would have already complained, just check that we have a template.
*/
+ gcc_checking_assert (really_overloaded_fn (t)
+ || get_template_info (t)
+ || TREE_CODE (t) == TEMPLATE_ID_EXPR
+ || dependent_splice_p (t));
}
else if (/* No 'template' but there were template arguments? */
- targs_p
- /* No 'template' but the splice-specifier designates a template? */
- || really_overloaded_fn (t))
- {
- auto_diagnostic_group d;
- if (targs_p)
- error_at (loc, "reflection %qE not usable in a splice expression with "
- "template arguments", t);
- else
- error_at (loc, "reflection %qE not usable in a splice expression", t);
- location_t sloc = expr.get_start ();
- rich_location richloc (line_table, sloc);
- richloc.add_fixit_insert_before (sloc, "template ");
- inform (&richloc, "add %<template%> to denote a template");
- return error_mark_node;
- }
-
- /* Make sure this splice-expression produces an expression. */
- if (!check_splice_expr (loc, expr.get_start (), t, address_p,
- member_access_p, /*complain=*/true))
- return error_mark_node;
+ (targs_p
+ /* No 'template' but the splice-specifier designates a function
+ template? */
+ || really_overloaded_fn (t))
+ && warning_enabled_at (loc, OPT_Wmissing_template_keyword))
+ /* Were 'template' present, this would be valid code, so keep going. */
+ missing_template_diag (loc, diagnostics::kind::pedwarn);
/* When doing foo.[: bar :], cp_parser_postfix_dot_deref_expression wants
to see an identifier or a TEMPLATE_ID_EXPR, if we have something like
@@ -6471,13 +6458,9 @@ cp_parser_splice_scope_specifier (cp_parser *parser,
bool typename_p,
if (!valid_splice_scope_p (scope))
{
auto_diagnostic_group d;
- error_at (loc, "reflection not usable in a splice scope");
- if (TYPE_P (scope))
- inform (loc, "%qT is not a class, namespace, or enumeration",
- tree (scope));
- else
- inform (loc, "%qE is not a class, namespace, or enumeration",
- tree (scope));
+ error_at (loc, "expected a reflection of a class, namespace, or "
+ "enumeration");
+ inform_tree_category (tree (scope));
scope = error_mark_node;
}
@@ -7235,7 +7218,7 @@ cp_parser_primary_expression (cp_parser *parser,
static void
missing_template_diag (location_t loc,
- enum diagnostics::kind diag_kind =
diagnostics::kind::warning)
+ diagnostics::kind diag_kind/*=kind::warning*/)
{
if (warning_suppressed_at (loc, OPT_Wmissing_template_keyword))
return;
diff --git a/gcc/cp/reflect.cc b/gcc/cp/reflect.cc
index d04930d732c..175d111d81b 100644
--- a/gcc/cp/reflect.cc
+++ b/gcc/cp/reflect.cc
@@ -8331,8 +8331,8 @@ check_splice_expr (location_t loc, location_t start_loc,
tree t,
if (TYPE_P (t))
{
auto_diagnostic_group d;
- error_at (loc, "expected a reflection of an expression instead "
- "of type %qT", t);
+ error_at (loc, "expected a reflection of an expression");
+ inform_tree_category (t);
if (start_loc != UNKNOWN_LOCATION)
{
rich_location richloc (line_table, start_loc);
@@ -8345,8 +8345,11 @@ check_splice_expr (location_t loc, location_t start_loc,
tree t,
"a type-only context");
}
else
- error_at (loc, "expected a reflection of an expression instead "
- "of %qD", t);
+ {
+ auto_diagnostic_group d;
+ error_at (loc, "expected a reflection of an expression");
+ inform_tree_category (t);
+ }
}
return false;
}
diff --git a/gcc/testsuite/g++.dg/reflect/crash10.C
b/gcc/testsuite/g++.dg/reflect/crash10.C
index 05845846e23..dddf1e62544 100644
--- a/gcc/testsuite/g++.dg/reflect/crash10.C
+++ b/gcc/testsuite/g++.dg/reflect/crash10.C
@@ -4,8 +4,9 @@
struct S { };
using X = S;
-template <auto V> constexpr int e = [:V:]; // { dg-error "expected a
reflection of an expression instead of type .X." }
-template <auto V> constexpr int e2 = [:V:]; // { dg-error "expected a
reflection of an expression instead of type .X." }
+template <auto V> constexpr int e = [:V:]; // { dg-error "expected a
reflection of an expression" }
+// { dg-message "but .X. is a type" "" { target *-*-* } 4 }
+template <auto V> constexpr int e2 = [:V:]; // { dg-error "expected a
reflection of an expression" }
constexpr auto h = ^^X;
constexpr auto i = e<([:^^h:])>;
constexpr auto j = e2<^^X>;
diff --git a/gcc/testsuite/g++.dg/reflect/crash2.C
b/gcc/testsuite/g++.dg/reflect/crash2.C
index 83927fbdc7c..879c2145b97 100644
--- a/gcc/testsuite/g++.dg/reflect/crash2.C
+++ b/gcc/testsuite/g++.dg/reflect/crash2.C
@@ -13,8 +13,9 @@ void
f ()
{
// Parsed as "S::tfn < S".
- [: ^^T :]::tfn<[: ^^T :]>(); // { dg-error "expected
primary-expression|expected a reflection of an expression instead of type .S." }
+ [: ^^T :]::tfn<[: ^^T :]>(); // { dg-error "expected
primary-expression|expected a reflection of an expression" }
// { dg-warning "expected .template. keyword" "" { target *-*-* } .-1 }
+// { dg-message "but .S. is a type" "" { target *-*-* } 4 }
int i = [: ^^T :]::var<int>; // { dg-error "missing|expected" }
}
diff --git a/gcc/testsuite/g++.dg/reflect/crash3.C
b/gcc/testsuite/g++.dg/reflect/crash3.C
index 51cf3d8b727..f2934fe98fc 100644
--- a/gcc/testsuite/g++.dg/reflect/crash3.C
+++ b/gcc/testsuite/g++.dg/reflect/crash3.C
@@ -1,8 +1,10 @@
// { dg-do compile { target c++26 } }
// { dg-additional-options "-freflection" }
-template <auto V> constexpr int e = [:V:]; // { dg-error "expected a
reflection of an expression instead of type .int." }
-template <auto V> constexpr int e2 = [:V:]; // { dg-error "expected a
reflection of an expression instead of type .int." }
+template <auto V> constexpr int e = [:V:]; // { dg-error "expected a
reflection of an expression" }
+// { dg-message "but .int. is a type" "" { target *-*-* } .-1 }
+template <auto V> constexpr int e2 = [:V:]; // { dg-error "expected a
reflection of an expression" }
+// { dg-message "but .int. is a type" "" { target *-*-* } .-1 }
constexpr auto h = ^^int;
constexpr auto i = e<([:^^h:])>;
constexpr auto j = e2<^^int>;
diff --git a/gcc/testsuite/g++.dg/reflect/crash7.C
b/gcc/testsuite/g++.dg/reflect/crash7.C
index 50905acc7a0..c122ba2066c 100644
--- a/gcc/testsuite/g++.dg/reflect/crash7.C
+++ b/gcc/testsuite/g++.dg/reflect/crash7.C
@@ -12,7 +12,8 @@ template<info R>
void
f ()
{
- int j = s.template [:R:]<int>; // { dg-error "expected a reflection of an
expression instead of type .S." }
+ int j = s.template [:R:]<int>; // { dg-error "expected a reflection of an
expression" }
+ // { dg-message "but .S. is a type" "" { target *-*-* } 6 }
}
void
diff --git a/gcc/testsuite/g++.dg/reflect/crash9.C
b/gcc/testsuite/g++.dg/reflect/crash9.C
index 1f8aff6a715..eaf3214ec1e 100644
--- a/gcc/testsuite/g++.dg/reflect/crash9.C
+++ b/gcc/testsuite/g++.dg/reflect/crash9.C
@@ -3,8 +3,9 @@
namespace N { }
-template <auto V> constexpr int e = [:V:]; // { dg-error "expected a
reflection of an expression instead of .N." }
-template <auto V> constexpr int e2 = [:V:]; // { dg-error "expected a
reflection of an expression instead of .N." }
+template <auto V> constexpr int e = [:V:]; // { dg-error "expected a
reflection of an expression" }
+// { dg-message "but .N. is a namespace" "" { target *-*-* } 4 }
+template <auto V> constexpr int e2 = [:V:]; // { dg-error "expected a
reflection of an expression" }
constexpr auto h = ^^N;
constexpr auto i = e<([:^^h:])>;
constexpr auto j = e2<^^N>;
diff --git a/gcc/testsuite/g++.dg/reflect/dep5.C
b/gcc/testsuite/g++.dg/reflect/dep5.C
index 4a118de1b70..3a04cbd58dd 100644
--- a/gcc/testsuite/g++.dg/reflect/dep5.C
+++ b/gcc/testsuite/g++.dg/reflect/dep5.C
@@ -18,8 +18,9 @@ f ()
static_assert ([: ^^T :]::x == 42);
typename [: ^^T :]::type a = 42;
[: ^^T :]::fn (42);
- [: ^^T :]::template tfn<([: ^^T :])>(); // { dg-error "expected a
reflection of an expression instead of type .S." }
- auto x = [: ^^T :]::template var<([: ^^T :])>; // { dg-error "expected a
reflection of an expression instead of type .S." }
+ [: ^^T :]::template tfn<([: ^^T :])>(); // { dg-error "expected a
reflection of an expression" }
+ // { dg-message "but .S. is a type" "" { target *-*-* } 4 }
+ auto x = [: ^^T :]::template var<([: ^^T :])>; // { dg-error "expected a
reflection of an expression" }
}
void
diff --git a/gcc/testsuite/g++.dg/reflect/diag1.C
b/gcc/testsuite/g++.dg/reflect/diag1.C
index 2d34623a8ac..537929decb7 100644
--- a/gcc/testsuite/g++.dg/reflect/diag1.C
+++ b/gcc/testsuite/g++.dg/reflect/diag1.C
@@ -14,12 +14,10 @@ void
f ()
{
S s;
- s.[: ^^S::tfn :](42); // { dg-error "reflection .S::tfn. not usable in a
splice expression" }
-// { dg-message "add .template. to denote a template" "" { target *-*-* } .-1 }
+ s.[: ^^S::tfn :](42); // { dg-error "expected 'template' keyword before
dependent template name" }
s.template [: ^^S::tfn :](42);
constexpr auto r = ^^fortytwo;
- constexpr int i1 = [:r:]<int>; // { dg-error "reflection .fortytwo<int>. not
usable in a splice expression with template arguments" }
-// { dg-message "add .template. to denote a template" "" { target *-*-* } .-1 }
+ constexpr int i1 = [:r:]<int>; // { dg-error "expected 'template' keyword
before dependent template name" }
constexpr int i2 = template [:r:]<int>;
}
diff --git a/gcc/testsuite/g++.dg/reflect/diag1a.C
b/gcc/testsuite/g++.dg/reflect/diag1a.C
new file mode 100644
index 00000000000..d276a7d22f4
--- /dev/null
+++ b/gcc/testsuite/g++.dg/reflect/diag1a.C
@@ -0,0 +1,24 @@
+// { dg-do compile { target c++26 } }
+// { dg-options "" }
+// { dg-additional-options "-freflection" }
+// Test that we offer some helpful diagnostic.
+
+struct S {
+ template<typename T>
+ void tfn (T) { }
+};
+
+template<typename T>
+constexpr T fortytwo = 42;
+
+void
+f ()
+{
+ S s;
+ s.[: ^^S::tfn :](42); // { dg-warning "expected 'template' keyword before
dependent template name" }
+ s.template [: ^^S::tfn :](42);
+
+ constexpr auto r = ^^fortytwo;
+ constexpr int i1 = [:r:]<int>; // { dg-warning "expected 'template' keyword
before dependent template name" }
+ constexpr int i2 = template [:r:]<int>;
+}
diff --git a/gcc/testsuite/g++.dg/reflect/diag1b.C
b/gcc/testsuite/g++.dg/reflect/diag1b.C
new file mode 100644
index 00000000000..b2c9bade331
--- /dev/null
+++ b/gcc/testsuite/g++.dg/reflect/diag1b.C
@@ -0,0 +1,23 @@
+// { dg-do compile { target c++26 } }
+// { dg-additional-options "-freflection -Wno-missing-template-keyword" }
+// Test that we offer some helpful diagnostic.
+
+struct S {
+ template<typename T>
+ void tfn (T) { }
+};
+
+template<typename T>
+constexpr T fortytwo = 42;
+
+void
+f ()
+{
+ S s;
+ s.[: ^^S::tfn :](42); // { dg-bogus "expected 'template' keyword before
dependent template name" }
+ s.template [: ^^S::tfn :](42);
+
+ constexpr auto r = ^^fortytwo;
+ constexpr int i1 = [:r:]<int>; // { dg-bogus "expected 'template' keyword
before dependent template name" }
+ constexpr int i2 = template [:r:]<int>;
+}
diff --git a/gcc/testsuite/g++.dg/reflect/error10.C
b/gcc/testsuite/g++.dg/reflect/error10.C
index 2ca2b18a1fa..891a643c1a0 100644
--- a/gcc/testsuite/g++.dg/reflect/error10.C
+++ b/gcc/testsuite/g++.dg/reflect/error10.C
@@ -4,29 +4,34 @@
int g;
void
-fn0 (int, typename [: ^^:: :] i) // { dg-error "reflection .::. not usable in
a splice type|declared" }
+fn0 (int, typename [: ^^:: :] i) // { dg-error "expected a reflection of a
type|declared" }
+// { dg-message "but .::. is a namespace" "" { target *-*-* } 0 }
{
}
void
-fn1 (int, typename [: ^^g :] i) // { dg-error "reflection .g. not usable in a
splice type|declared" }
+fn1 (int, typename [: ^^g :] i) // { dg-error "expected a reflection of a
type|declared" }
+// { dg-message "but .g. is a variable" "" { target *-*-* } 4 }
{
}
void
-fn2 (int, typename [: ^^fn1 :] i) // { dg-error "reflection .fn1. not usable
in a splice type|declared" }
+fn2 (int, typename [: ^^fn1 :] i) // { dg-error "expected a reflection of a
type|declared" }
+// { dg-message "but .fn1. is a function" "" { target *-*-* } 13 }
{
}
void
-fn3 (int p, typename [: ^^p :] i) // { dg-error "reflection .p. not usable in
a splice type|declared" }
+fn3 (int p, typename [: ^^p :] i) // { dg-error "expected a reflection of a
type|declared" }
+// { dg-message "but .p. is a parameter" "" { target *-*-* } 25 }
{
}
enum Harold { Budd };
void
-fn4 (int, typename [: ^^Budd :] i) // { dg-error "reflection .Budd. not usable
in a splice type|declared" }
+fn4 (int, typename [: ^^Budd :] i) // { dg-error "expected a reflection of a
type|declared" }
+// { dg-message "but .Budd. is an enumerator" "" { target *-*-* } 30 }
{
}
@@ -34,6 +39,16 @@ template<int>
struct S {};
void
-fn5 (int, typename [: ^^S :] i) // { dg-error "reflection .S. not usable in a
splice type|declared" }
+fn5 (int, typename [: ^^S :] i) // { dg-error "expected a reflection of a
type|declared" }
+// { dg-message "but .S. is a class template" "" { target *-*-* } 39 }
+{
+}
+
+template<int>
+void bar ();
+
+void
+fn6 (int, typename [: ^^bar :] i) // { dg-error "expected a reflection of a
type|declared" }
+// { dg-message "but .bar. is a function template" "" { target *-*-* } 48 }
{
}
diff --git a/gcc/testsuite/g++.dg/reflect/error12.C
b/gcc/testsuite/g++.dg/reflect/error12.C
index 7eb787f010d..8f39b7f74a7 100644
--- a/gcc/testsuite/g++.dg/reflect/error12.C
+++ b/gcc/testsuite/g++.dg/reflect/error12.C
@@ -7,16 +7,17 @@ constexpr auto r = ^^i;
constexpr bool b0 = template [:r:] < 0 > 0; // { dg-error "not a template" }
constexpr bool b1 = template [:r:] < 0 < 0; // { dg-error "not a
template|expected" }
constexpr bool b2 = template [:r:] < 43; // { dg-error "not a
template|expected" }
-constexpr bool b3 = template [:r:] <= 43; // { dg-error "template splice" }
-constexpr bool b4 = template [:r:] > 41; // { dg-error "template splice" }
-constexpr bool b5 = template [:r:] >= 41; // { dg-error "template splice" }
-constexpr bool b6 = template [:r:] == 42; // { dg-error "template splice" }
-constexpr bool b7 = template [:r:] != 41; // { dg-error "template splice" }
+constexpr bool b3 = template [:r:] <= 43; // { dg-error "expected a
reflection of a function template" }
+// { dg-message "but .i. is a variable" "" { target *-*-* } 5 }
+constexpr bool b4 = template [:r:] > 41; // { dg-error "expected a
reflection of a function template" }
+constexpr bool b5 = template [:r:] >= 41; // { dg-error "expected a
reflection of a function template" }
+constexpr bool b6 = template [:r:] == 42; // { dg-error "expected a
reflection of a function template" }
+constexpr bool b7 = template [:r:] != 41; // { dg-error "expected a
reflection of a function template" }
template<bool> struct S { };
S<template [:r:] < 43> s1; // { dg-error "not a template|invalid" }
-S<template [:r:] <= 43> s2; // { dg-error "template splice|invalid" }
-S<(template [:r:] > 41)> s3; // { dg-error "template splice|invalid" }
-S<template [:r:] >= 41> s4; // { dg-error "template splice|invalid" }
-S<template [:r:] == 42> s5; // { dg-error "template splice|invalid" }
-S<template[:r:] != 41> s6; // { dg-error "template splice|invalid" }
+S<template [:r:] <= 43> s2; // { dg-error "expected a reflection|invalid" }
+S<(template [:r:] > 41)> s3; // { dg-error "expected a reflection|invalid" }
+S<template [:r:] >= 41> s4; // { dg-error "expected a reflection|invalid" }
+S<template [:r:] == 42> s5; // { dg-error "expected a reflection|invalid" }
+S<template[:r:] != 41> s6; // { dg-error "expected a reflection|invalid" }
diff --git a/gcc/testsuite/g++.dg/reflect/error5.C
b/gcc/testsuite/g++.dg/reflect/error5.C
index 2b125fffc1a..e27bf8df6c4 100644
--- a/gcc/testsuite/g++.dg/reflect/error5.C
+++ b/gcc/testsuite/g++.dg/reflect/error5.C
@@ -9,7 +9,8 @@ template<info R>
void
f ()
{
- int i = [:R:]; // { dg-error "expected a reflection of an expression instead
of .N." }
+ int i = [:R:]; // { dg-error "expected a reflection of an expression" }
+ // { dg-message "but .N. is a namespace" "" { target *-*-* } 6 }
}
void
diff --git a/gcc/testsuite/g++.dg/reflect/expr3.C
b/gcc/testsuite/g++.dg/reflect/expr3.C
index de7cffe1a56..09b970e56e1 100644
--- a/gcc/testsuite/g++.dg/reflect/expr3.C
+++ b/gcc/testsuite/g++.dg/reflect/expr3.C
@@ -24,28 +24,34 @@ void
g ()
{
int i1 = [: ^^x :];
- int i2 = template [: ^^x :]; // { dg-error "reflection .x. not usable in
a template splice" }
+ int i2 = template [: ^^x :]; // { dg-error "expected a reflection of a
function template" }
+ // { dg-message "but .x. is a variable" "" { target *-*-* } 4 }
int i3 = [: ^^two<int> :];
- int i4 = template [: ^^two<int> :]; // { dg-error "reflection .two<int>. not
usable in a template splice" }
- int i5 = [: ^^foo :](42); // { dg-error "reflection .foo. not
usable in a splice expression" }
+ int i4 = template [: ^^two<int> :]; // { dg-error "expected a reflection of
a function template" }
+ // { dg-message "but .two<int>. is a variable" "" { target *-*-* } 6 }
+ int i5 = [: ^^foo :](42); // { dg-error "expected 'template'
keyword before dependent template name" }
int i6 = template [: ^^foo :](42);
int i7 = [: ^^foo<int> :](42);
- int i8 = template [: ^^foo<int> :](42); // { dg-error "reflection
.foo<int>. not usable in a template splice" }
- int i9 = [: ^^foo :]<int>(42); // { dg-error "reflection .foo. not
usable in a splice expression|expected" }
+ int i8 = template [: ^^foo<int> :](42); // { dg-error "expected a
reflection of a function template" }
+ // { dg-message "but .foo<int>. is a function" "" { target *-*-* } 9 }
+ int i9 = [: ^^foo :]<int>(42); // { dg-error "expected .template.
keyword before dependent template name" }
int i10 = template [: ^^foo :]<int>(42);
int i11 = template [: ^^bar :]<int>(42); // { dg-error "no matching
function for call" }
- int i12 = [: ^^two :]<int>; // { dg-error "reflection
.two<int>. not usable in a splice expression with template arguments" }
+ int i12 = [: ^^two :]<int>; // { dg-error "expected 'template'
keyword before dependent template name" }
int i13 = template [: ^^two :]<int>;
- [: ^^ST :]<int> c1; // { dg-error "reflection .ST<int>. not usable in a
splice expression with template arguments" }
+ [: ^^ST :]<int> c1; // { dg-error "expected a reflection of an expression" }
+ // { dg-message "but .ST<int>. is a type" "" { target *-*-* } 16 }
typename [: ^^ST :]<int> c2;
template [: ^^ST :]<int> c3; // { dg-error "expected a reflection of an
expression" }
[: ^^S :]<int> c4; // { dg-error "expected a reflection of an
expression|expected primary-expression" }
+ // { dg-message "but .S. is a type" "" { target *-*-* } 13 }
+ // { dg-message "add .typename. to denote a type outside a type-only
context" "" { target *-*-* } .-2 }
template [: ^^S :]<int> c5; // { dg-error ".S. is not a template" }
typename [: ^^S :]<int> c6; // { dg-error ".S. is not a template|expected"
}
[: ^^bar :]<int>(); // { dg-error "expected" }
- auto x1 = [: ^^ST :]<int>{}; // { dg-error "reflection .ST<int>. not
usable in a splice expression with template arguments" }
+ auto x1 = [: ^^ST :]<int>{}; // { dg-error "expected a reflection of an
expression" }
auto x2 = template [: ^^ST :]<int>{}; // { dg-error "expected a
reflection of an expression" }
auto x3 = typename [: ^^ST :]<int>{};
}
diff --git a/gcc/testsuite/g++.dg/reflect/member1.C
b/gcc/testsuite/g++.dg/reflect/member1.C
index e9d9a64d0eb..cecb4a4204d 100644
--- a/gcc/testsuite/g++.dg/reflect/member1.C
+++ b/gcc/testsuite/g++.dg/reflect/member1.C
@@ -55,9 +55,9 @@ f ()
sp->template [: ^^S::tfn :](42);
s.template [: ^^S::tfn :]<int>(42);
sp->template [: ^^S::tfn :]<int>(42);
- s.[: ^^S::var :]<int> = 1; // { dg-error "reflection .var<int>. not usable
in a splice expression with template arguments" }
+ s.[: ^^S::var :]<int> = 1; // { dg-error "expected 'template' keyword before
dependent template name" }
s.template [: ^^S::var :]<int> = 1;
- sp->[: ^^S::var :]<int> = 1; // { dg-error "reflection .var<int>. not usable
in a splice expression with template arguments" }
+ sp->[: ^^S::var :]<int> = 1; // { dg-error "expected 'template' keyword
before dependent template name" }
sp->template [: ^^S::var :]<int> = 1;
s.[: ^^S::b :].[: ^^B::a :].val;
sp->[: ^^S::b :].[: ^^B::a :].val;
@@ -74,9 +74,9 @@ f ()
cp->template [: ^^C<int>::tfn :](42);
c.template [: ^^C<int>::tfn :]<int>(42);
cp->template [: ^^C<int>::tfn :]<int>(42);
- c.[: ^^C<int>::var :]<int> = 1; // { dg-error "reflection .var<int>. not
usable in a splice expression with template arguments" }
+ c.[: ^^C<int>::var :]<int> = 1; // { dg-error "expected 'template' keyword
before dependent template name" }
c.template [: ^^C<int>::var :]<int> = 1;
- cp->[: ^^C<int>::var :]<int> = 1; // { dg-error "reflection .var<int>. not
usable in a splice expression with template arguments" }
+ cp->[: ^^C<int>::var :]<int> = 1; // { dg-error "expected 'template' keyword
before dependent template name" }
cp->template [: ^^C<int>::var :]<int> = 1;
c.[: ^^C<int>::b :].[: ^^B::a :].val;
cp->[: ^^C<int>::b :].[: ^^B::a :].val;
@@ -92,8 +92,10 @@ f ()
sp.[: ^^S::x :] = 2; // { dg-error "which is of pointer type" }
c.[: ^^C<char>::x :] = 1; // { dg-error "is not a base of" }
cp->[: ^^C<char>::x :] = 1; // { dg-error "is not a base of" }
- s.template [: ^^S::N :].t; // { dg-error "reflection .S::N. not usable in a
template splice" }
+ s.template [: ^^S::N :].t; // { dg-error "expected a reflection of a
function template" }
+ // { dg-message "but .S::N. is a class template" "" { target *-*-* } 20 }
S::template [: ^^S::N<int> :] e1; // { dg-error "expected unqualified-id" }
C<int>::template [: ^^S::N<int> :] e2; // { dg-error "expected
unqualified-id" }
- s.template [: ^^S::var<int> :] = 1; // { dg-error "reflection .S::var<int>.
not usable in a template splice" }
+ s.template [: ^^S::var<int> :] = 1; // { dg-error "expected a reflection of
a function template" }
+ // { dg-message "but .S::var<int>. is a variable" "" { target *-*-* } 22 }
}
diff --git a/gcc/testsuite/g++.dg/reflect/ns2.C
b/gcc/testsuite/g++.dg/reflect/ns2.C
index 482b8754713..feff62406ea 100644
--- a/gcc/testsuite/g++.dg/reflect/ns2.C
+++ b/gcc/testsuite/g++.dg/reflect/ns2.C
@@ -3,19 +3,43 @@
// Test reflections on namespaces. Invalid stuff.
void foo (int);
+int i;
+enum E { EE };
+template<typename>
+struct S { int i, j, k; };
+template<typename>
+void bar ();
namespace N {
}
void
-f1 ()
+f1 (int p)
{
constexpr auto r = ^^::;
- [: r :] foo (0); // { dg-error "expected" }
+ [: r :] foo (0); // { dg-error "expected a reflection of an expression" }
+ // { dg-message "but .::. is a namespace" "" { target *-*-* } 0 }
constexpr auto q = ^^int;
- [: q :]::T x; // { dg-error "reflection not usable in a splice
scope|expected" }
- // { dg-message ".int. is not a class, namespace, or enumeration" "" {
target *-*-* } .-1 }
+ [: q :]::T x; // { dg-error "expected a reflection of a class, namespace, or
enumeration|expected" }
+ // { dg-message "but .int. is a type" "" { target *-*-* } .-1 }
+ [: ^^foo :]::X; // { dg-error "expected a reflection of a class, namespace,
or enumeration" }
+ // { dg-message "but .foo. is a function" "" { target *-*-* } 5 }
+ [: ^^EE :]::Y; // { dg-error "expected a reflection of a class, namespace,
or enumeration" }
+ // { dg-message "but .EE. is an enumerator" "" { target *-*-* } 7 }
+ [: ^^S :]::Z; // { dg-error "expected a reflection of a class,
namespace, or enumeration" }
+ // { dg-message "but .S. is a class template" "" { target *-*-* } 9 }
+ [: ^^bar :]::W; // { dg-error "expected a reflection of a class, namespace,
or enumeration" }
+ // { dg-message "but .bar. is a function template" "" { target *-*-* } 11 }
+ [: ^^i :]::U; // { dg-error "expected a reflection of a class,
namespace, or enumeration" }
+ // { dg-message "but .i. is a variable" "" { target *-*-* } 6 }
+ [: ^^p :]::V; // { dg-error "expected a reflection of a class,
namespace, or enumeration" }
+ // { dg-message "but .p. is a parameter" "" { target *-*-* } 17 }
+ auto [a, b, c] = S<int>{1, 2, 3};
+ [: ^^a :]::G; // { dg-error "expected a reflection of a class, namespace,
or enumeration" }
+ // { dg-message "but .a. is a structured binding" "" { target *-*-* } 38 }
+ [: ^^S<int>::i :]::H; // { dg-error "expected a reflection of a
class, namespace, or enumeration" }
+ // { dg-message "but .S<int>::i. is a data member" "" { target *-*-* } 9 }
constexpr auto x = ^^N::X; // { dg-error ".N::X. has not been declared" }
}
diff --git a/gcc/testsuite/g++.dg/reflect/p2996-12.C
b/gcc/testsuite/g++.dg/reflect/p2996-12.C
index 58a145693f4..f3aa3a1bd1c 100644
--- a/gcc/testsuite/g++.dg/reflect/p2996-12.C
+++ b/gcc/testsuite/g++.dg/reflect/p2996-12.C
@@ -15,7 +15,8 @@ using alias = [:^^TCls:]<([:^^v:])>;
static_assert(alias::s == 2);
// error: < means less than
-auto o1 = [:^^TCls:]<([:^^v:])>(); // { dg-error "reflection .TCls<1>. not
usable" }
+auto o1 = [:^^TCls:]<([:^^v:])>(); // { dg-error "expected a reflection of an
expression" }
+// { dg-message "but .TCls<1>. is a type" "" { target *-*-* } 8 }
// OK, o2 is an object of type TCls<1>
auto o2 = typename [:^^TCls:]<([:^^v:])>();
diff --git a/gcc/testsuite/g++.dg/reflect/splice5.C
b/gcc/testsuite/g++.dg/reflect/splice5.C
index 78144863337..4f0d2fe6134 100644
--- a/gcc/testsuite/g++.dg/reflect/splice5.C
+++ b/gcc/testsuite/g++.dg/reflect/splice5.C
@@ -13,10 +13,12 @@ struct S {
void
baz (S &s)
{
- template [: ^^foo :] (0); // { dg-error "reflection 'foo'
not usable in a template splice" }
- template [: ^^bar :] (0); // { dg-message "only function
templates are allowed here" "" { target *-*-* } .-1 }
- s.template [: ^^S::foo :] (0); // { dg-error "reflection
'S::foo' not usable in a template splice" }
- s.template [: ^^S::bar :] (0); // { dg-message "only function
templates are allowed here" "" { target *-*-* } .-1 }
+ template [: ^^foo :] (0); // { dg-error "expected a
reflection of a function template" }
+ // { dg-message "but .foo. is a function" "" { target *-*-* } 4 }
+ template [: ^^bar :] (0);
+ s.template [: ^^S::foo :] (0); // { dg-error "expected a
reflection of a function template" }
+ // { dg-message "but .S::foo. is a function" "" { target *-*-* } 8 }
+ s.template [: ^^S::bar :] (0);
}
template <int N>
base-commit: 9db703c556c02e51bc84d168170ae811ec87c011
--
2.53.0