[v2] c++/reflection: reject invalid template splice [PR123998]
Checks
Commit Message
On Thu, Apr 02, 2026 at 05:41:38PM -0400, Jason Merrill wrote:
> On 3/20/26 4:51 PM, Marek Polacek wrote:
> > Bootstrapped/regtested on x86_64-pc-linux-gnu, ok for trunk?
> >
> > -- >8 --
> > When we have "template [:R:]" without template arguments, the
> > splice has to designate a function template. E.g.,
> >
> > void foo (int);
> > template [: ^^foo :] (0); // invalid
> >
> > We check this in cp_parser_splice_expression. But when the splice
> > is dependent, we don't check it when instantiating, thus missing
> > the error in
> >
> > template [: N == 0 ? ^^foo : ^^:: :] (0);
> >
> > This patch introduces SPLICE_EXPR_TEMPLATE_P, and moves the checking
> > into check_splice_expr.
> >
> > PR c++/123998
> >
> > gcc/cp/ChangeLog:
> >
> > * cp-tree.h (SPLICE_EXPR_TEMPLATE_P): Define.
> > (SET_SPLICE_EXPR_TEMPLATE_P): Define.
> > (check_splice_expr): Adjust.
> > * parser.cc (cp_parser_splice_expression): Do
> > SET_SPLICE_EXPR_TEMPLATE_P. Adjust the call to check_splice_expr.
> > Move the template_p checking into check_splice_expr.
> > * pt.cc (tsubst_splice_expr): Do SET_SPLICE_EXPR_TEMPLATE_P.
> > Adjust the call to check_splice_expr.
> > * reflect.cc (eval_constant_of): Adjust the call to
> > check_splice_expr.
> > (check_splice_expr): Two new bool parameters. Add the template_p
> > checking from cp_parser_splice_expression. Allow
> > variable_template_p in the assert.
> >
> > gcc/testsuite/ChangeLog:
> >
> > * g++.dg/reflect/splice5.C: Adjust dg-error.
> > ---
> > gcc/cp/cp-tree.h | 14 +++++++--
> > gcc/cp/parser.cc | 40 +++++++-------------------
> > gcc/cp/pt.cc | 7 ++++-
> > gcc/cp/reflect.cc | 40 +++++++++++++++++++++++---
> > gcc/testsuite/g++.dg/reflect/splice5.C | 7 ++---
> > 5 files changed, 67 insertions(+), 41 deletions(-)
> >
> > diff --git a/gcc/cp/cp-tree.h b/gcc/cp/cp-tree.h
> > index ea3cb049785..e31f47b92af 100644
> > --- a/gcc/cp/cp-tree.h
> > +++ b/gcc/cp/cp-tree.h
> > @@ -522,6 +522,7 @@ extern GTY(()) tree cp_global_trees[CPTI_MAX];
> > TARGET_EXPR_ELIDING_P (in TARGET_EXPR)
> > IF_STMT_VACUOUS_INIT_P (IF_STMT)
> > TYPENAME_IS_RESOLVING_P (in TYPENAME_TYPE)
> > + SPLICE_EXPR_TEMPLATE_P (in SPLICE_EXPR)
> > 4: IDENTIFIER_MARKED (IDENTIFIER_NODEs)
> > TREE_HAS_CONSTRUCTOR (in INDIRECT_REF, SAVE_EXPR, CONSTRUCTOR,
> > CALL_EXPR, or FIELD_DECL).
> > @@ -1979,6 +1980,15 @@ enum reflect_kind : addr_space_t {
> > (SPLICE_EXPR_ADDRESS_P (TREE_CODE (NODE) == SPLICE_EXPR \
> > ? NODE : TREE_OPERAND (NODE, 0)) = (VAL))
> > +/* True if this SPLICE_EXPR was decorated with 'template'. */
> > +#define SPLICE_EXPR_TEMPLATE_P(NODE) \
> > + TREE_LANG_FLAG_3 (SPLICE_EXPR_CHECK (NODE))
> > +
> > +/* Helper macro to set SPLICE_EXPR_TEMPLATE_P. */
> > +#define SET_SPLICE_EXPR_TEMPLATE_P(NODE, VAL) \
> > + (SPLICE_EXPR_TEMPLATE_P (TREE_CODE (NODE) == SPLICE_EXPR \
> > + ? NODE : TREE_OPERAND (NODE, 0)) = (VAL))
> > +
> > /* The expression in question for a SPLICE_SCOPE. */
> > #define SPLICE_SCOPE_EXPR(NODE) \
> > (TYPE_VALUES_RAW (SPLICE_SCOPE_CHECK (NODE)))
> > @@ -9414,8 +9424,8 @@ extern bool consteval_only_p (tree) ATTRIBUTE_PURE;
> > extern bool compare_reflections (tree, tree) ATTRIBUTE_PURE;
> > extern bool valid_splice_type_p (const_tree) ATTRIBUTE_PURE;
> > extern bool valid_splice_scope_p (const_tree) ATTRIBUTE_PURE;
> > -extern bool check_splice_expr (location_t, location_t, tree, bool, bool, bool)
> > - ATTRIBUTE_PURE;
> > +extern bool check_splice_expr (location_t, location_t, tree, bool, bool, bool,
> > + bool, bool) ATTRIBUTE_PURE;
> > extern tree make_splice_scope (tree, bool);
> > extern bool dependent_splice_p (const_tree) ATTRIBUTE_PURE;
> > extern tree reflection_mangle_prefix (tree, char [3]);
> > diff --git a/gcc/cp/parser.cc b/gcc/cp/parser.cc
> > index 8d88dc9c312..cca2edc1e47 100644
> > --- a/gcc/cp/parser.cc
> > +++ b/gcc/cp/parser.cc
> > @@ -6313,6 +6313,7 @@ cp_parser_splice_expression (cp_parser *parser, bool template_p,
> > SET_SPLICE_EXPR_EXPRESSION_P (t);
> > SET_SPLICE_EXPR_MEMBER_ACCESS_P (t, member_access_p);
> > SET_SPLICE_EXPR_ADDRESS_P (t, address_p);
> > + SET_SPLICE_EXPR_TEMPLATE_P (t, template_p);
> > return t;
> > }
> > @@ -6325,38 +6326,17 @@ cp_parser_splice_expression (cp_parser *parser, bool template_p,
> > /* Make sure this splice-expression produces an expression. */
> > if (!check_splice_expr (loc, expr.get_start (), t, address_p,
> > - member_access_p, /*complain=*/true))
> > + member_access_p, template_p, targs_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
> > - && !really_overloaded_fn (t)
>
> Can we switch to using a different function name for testing for a
> reflection of a function template, even if it just returns
> really_overloaded_fn? This can be a later patch.
Sure. For 16 or 17?
> > - && !dependent_splice_p (t))
> > - {
> > - 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. 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 function
> > - template? */
> > - || really_overloaded_fn (t))
> > - && warning_enabled_at (loc, OPT_Wmissing_template_keyword))
> > + if (!template_p
> > + /* No 'template' but there were template arguments? */
> > + && (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);
> > diff --git a/gcc/cp/pt.cc b/gcc/cp/pt.cc
> > index 020a70c1112..913331c7b94 100644
> > --- a/gcc/cp/pt.cc
> > +++ b/gcc/cp/pt.cc
> > @@ -16810,13 +16810,18 @@ tsubst_splice_expr (tree t, tree args, tsubst_flags_t complain, tree in_decl)
> > SET_SPLICE_EXPR_MEMBER_ACCESS_P (op, true);
> > if (SPLICE_EXPR_ADDRESS_P (t))
> > SET_SPLICE_EXPR_ADDRESS_P (op, true);
> > + if (SPLICE_EXPR_TEMPLATE_P (t))
> > + SET_SPLICE_EXPR_TEMPLATE_P (op, true);
> > return op;
> > }
> > + const bool targs_p = (TREE_CODE (t) == TEMPLATE_ID_EXPR
> > + || variable_template_p (op));
>
> Why || variable_template_p? Can't you have a variable template reflection
> without targs?
I was trying to avoid adding SPLICE_EXPR_TARGS_P but it's probably not
going to work too well. And the == TEMPLATE_ID_EXPR check can never
be true.
So this patch adds SPLICE_EXPR_TARGS_P so that we can check we're
coming from [:R:]<args> in the SPLICE_EXPR. With that, I had to
add an error for DECL_TYPE_TEMPLATE_P in check_splice_expr to avoid
crashing in crash11.C.
Bootstrapped/regtested on x86_64-pc-linux-gnu, ok for trunk?
-- >8 --
When we have "template [:R:]" without template arguments, the
splice has to designate a function template. E.g.,
void foo (int);
template [: ^^foo :] (0); // invalid
We check this in cp_parser_splice_expression. But when the splice
is dependent, we don't check it when instantiating, thus missing
the error in
template [: N == 0 ? ^^foo : ^^:: :] (0);
This patch introduces SPLICE_EXPR_TEMPLATE_P and SPLICE_EXPR_TARGS_P,
and moves the checking into check_splice_expr. It also adds a missing
check for DECL_TYPE_TEMPLATE_P in case we have a splice-expression of
the form template splice-specialization-specifier.
PR c++/123998
gcc/cp/ChangeLog:
* cp-tree.h (SPLICE_EXPR_TEMPLATE_P): Define.
(SET_SPLICE_EXPR_TEMPLATE_P): Define.
(SPLICE_EXPR_TARGS_P): Define.
(SET_SPLICE_EXPR_TARGS_P): Define.
(check_splice_expr): Adjust.
* parser.cc (cp_parser_splice_expression): Do
SET_SPLICE_EXPR_TEMPLATE_P and SET_SPLICE_EXPR_TARGS_P. Adjust
the call to check_splice_expr. Move the template_p checking into
check_splice_expr.
* pt.cc (tsubst_splice_expr): Do SET_SPLICE_EXPR_TEMPLATE_P and
SET_SPLICE_EXPR_TARGS_P. Adjust the call to check_splice_expr.
* reflect.cc (eval_constant_of): Adjust the call to
check_splice_expr.
(check_splice_expr): Two new bool parameters. Add the template_p
checking from cp_parser_splice_expression. Allow
variable_template_p in the assert. Add a check for
DECL_TYPE_TEMPLATE_P.
gcc/testsuite/ChangeLog:
* g++.dg/reflect/crash11.C: Adjust dg-error.
* g++.dg/reflect/splice5.C: Likewise.
---
gcc/cp/cp-tree.h | 24 +++++++++++-
gcc/cp/parser.cc | 41 ++++++--------------
gcc/cp/pt.cc | 6 +++
gcc/cp/reflect.cc | 53 ++++++++++++++++++++++++--
gcc/testsuite/g++.dg/reflect/crash11.C | 4 +-
gcc/testsuite/g++.dg/reflect/splice5.C | 7 ++--
6 files changed, 93 insertions(+), 42 deletions(-)
base-commit: 6be9db000810a44c5b6b5af320723b3af175bb8a
Comments
On 4/8/26 3:35 PM, Marek Polacek wrote:
> On Thu, Apr 02, 2026 at 05:41:38PM -0400, Jason Merrill wrote:
>> Can we switch to using a different function name for testing for a
>> reflection of a function template, even if it just returns
>> really_overloaded_fn? This can be a later patch.
>
> Sure. For 16 or 17?
For 16, to avoid backporting hassle. Though I suppose that patch could
be backported first.
>>> @@ -16810,13 +16810,18 @@ tsubst_splice_expr (tree t, tree args, tsubst_flags_t complain, tree in_decl)
>>> SET_SPLICE_EXPR_MEMBER_ACCESS_P (op, true);
>>> if (SPLICE_EXPR_ADDRESS_P (t))
>>> SET_SPLICE_EXPR_ADDRESS_P (op, true);
>>> + if (SPLICE_EXPR_TEMPLATE_P (t))
>>> + SET_SPLICE_EXPR_TEMPLATE_P (op, true);
>>> return op;
>>> }
>>> + const bool targs_p = (TREE_CODE (t) == TEMPLATE_ID_EXPR
>>> + || variable_template_p (op));
>>
>> Why || variable_template_p? Can't you have a variable template reflection
>> without targs?
>
> I was trying to avoid adding SPLICE_EXPR_TARGS_P but it's probably not
> going to work too well. And the == TEMPLATE_ID_EXPR check can never
> be true.
>
> So this patch adds SPLICE_EXPR_TARGS_P so that we can check we're
> coming from [:R:]<args> in the SPLICE_EXPR. With that, I had to
> add an error for DECL_TYPE_TEMPLATE_P in check_splice_expr to avoid
> crashing in crash11.C.
>
> Bootstrapped/regtested on x86_64-pc-linux-gnu, ok for trunk?
OK.
Jason
@@ -522,6 +522,7 @@ extern GTY(()) tree cp_global_trees[CPTI_MAX];
TARGET_EXPR_ELIDING_P (in TARGET_EXPR)
IF_STMT_VACUOUS_INIT_P (IF_STMT)
TYPENAME_IS_RESOLVING_P (in TYPENAME_TYPE)
+ SPLICE_EXPR_TEMPLATE_P (in SPLICE_EXPR)
4: IDENTIFIER_MARKED (IDENTIFIER_NODEs)
TREE_HAS_CONSTRUCTOR (in INDIRECT_REF, SAVE_EXPR, CONSTRUCTOR,
CALL_EXPR, or FIELD_DECL).
@@ -533,6 +534,7 @@ extern GTY(()) tree cp_global_trees[CPTI_MAX];
TARGET_EXPR_INTERNAL_P (in TARGET_EXPR)
CONTRACT_CONST (in ASSERTION_, PRECONDITION_, POSTCONDITION_STMT)
DECL_HAS_DEFAULT_ARGUMENT_P (in PARM_DECL)
+ SPLICE_EXPR_TARGS_P (in SPLICE_EXPR)
5: IDENTIFIER_VIRTUAL_P (in IDENTIFIER_NODE)
FUNCTION_RVALUE_QUALIFIED (in FUNCTION_TYPE, METHOD_TYPE)
CALL_EXPR_REVERSE_ARGS (in CALL_EXPR, AGGR_INIT_EXPR)
@@ -1979,6 +1981,24 @@ enum reflect_kind : addr_space_t {
(SPLICE_EXPR_ADDRESS_P (TREE_CODE (NODE) == SPLICE_EXPR \
? NODE : TREE_OPERAND (NODE, 0)) = (VAL))
+/* True if this SPLICE_EXPR was decorated with 'template'. */
+#define SPLICE_EXPR_TEMPLATE_P(NODE) \
+ TREE_LANG_FLAG_3 (SPLICE_EXPR_CHECK (NODE))
+
+/* Helper macro to set SPLICE_EXPR_TEMPLATE_P. */
+#define SET_SPLICE_EXPR_TEMPLATE_P(NODE, VAL) \
+ (SPLICE_EXPR_TEMPLATE_P (TREE_CODE (NODE) == SPLICE_EXPR \
+ ? NODE : TREE_OPERAND (NODE, 0)) = (VAL))
+
+/* True if this SPLICE_EXPR has template arguments. */
+#define SPLICE_EXPR_TARGS_P(NODE) \
+ TREE_LANG_FLAG_4 (SPLICE_EXPR_CHECK (NODE))
+
+/* Helper macro to set SPLICE_EXPR_TARGS_P. */
+#define SET_SPLICE_EXPR_TARGS_P(NODE, VAL) \
+ (SPLICE_EXPR_TARGS_P (TREE_CODE (NODE) == SPLICE_EXPR \
+ ? NODE : TREE_OPERAND (NODE, 0)) = (VAL))
+
/* The expression in question for a SPLICE_SCOPE. */
#define SPLICE_SCOPE_EXPR(NODE) \
(TYPE_VALUES_RAW (SPLICE_SCOPE_CHECK (NODE)))
@@ -9416,8 +9436,8 @@ extern bool consteval_only_p (tree) ATTRIBUTE_PURE;
extern bool compare_reflections (tree, tree) ATTRIBUTE_PURE;
extern bool valid_splice_type_p (const_tree) ATTRIBUTE_PURE;
extern bool valid_splice_scope_p (const_tree) ATTRIBUTE_PURE;
-extern bool check_splice_expr (location_t, location_t, tree, bool, bool, bool)
- ATTRIBUTE_PURE;
+extern bool check_splice_expr (location_t, location_t, tree, bool, bool, bool,
+ bool, bool) ATTRIBUTE_PURE;
extern tree make_splice_scope (tree, bool);
extern bool dependent_splice_p (const_tree) ATTRIBUTE_PURE;
extern tree reflection_mangle_prefix (tree, char [3]);
@@ -6318,6 +6318,8 @@ cp_parser_splice_expression (cp_parser *parser, bool template_p,
SET_SPLICE_EXPR_EXPRESSION_P (t);
SET_SPLICE_EXPR_MEMBER_ACCESS_P (t, member_access_p);
SET_SPLICE_EXPR_ADDRESS_P (t, address_p);
+ SET_SPLICE_EXPR_TEMPLATE_P (t, template_p);
+ SET_SPLICE_EXPR_TARGS_P (t, targs_p);
return t;
}
@@ -6330,38 +6332,17 @@ cp_parser_splice_expression (cp_parser *parser, bool template_p,
/* Make sure this splice-expression produces an expression. */
if (!check_splice_expr (loc, expr.get_start (), t, address_p,
- member_access_p, /*complain=*/true))
+ member_access_p, template_p, targs_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
- && !really_overloaded_fn (t)
- && !dependent_splice_p (t))
- {
- 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. 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 function
- template? */
- || really_overloaded_fn (t))
- && warning_enabled_at (loc, OPT_Wmissing_template_keyword))
+ if (!template_p
+ /* No 'template' but there were template arguments? */
+ && (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);
@@ -16833,12 +16833,18 @@ tsubst_splice_expr (tree t, tree args, tsubst_flags_t complain, tree in_decl)
SET_SPLICE_EXPR_MEMBER_ACCESS_P (op, true);
if (SPLICE_EXPR_ADDRESS_P (t))
SET_SPLICE_EXPR_ADDRESS_P (op, true);
+ if (SPLICE_EXPR_TEMPLATE_P (t))
+ SET_SPLICE_EXPR_TEMPLATE_P (op, true);
+ if (SPLICE_EXPR_TARGS_P (t))
+ SET_SPLICE_EXPR_TARGS_P (op, true);
return op;
}
if (SPLICE_EXPR_EXPRESSION_P (t)
&& !check_splice_expr (input_location, UNKNOWN_LOCATION, op,
SPLICE_EXPR_ADDRESS_P (t),
SPLICE_EXPR_MEMBER_ACCESS_P (t),
+ SPLICE_EXPR_TEMPLATE_P (t),
+ SPLICE_EXPR_TARGS_P (t),
(complain & tf_error)))
return error_mark_node;
@@ -2752,6 +2752,8 @@ eval_constant_of (location_t loc, const constexpr_ctx *ctx, tree r,
else if (!check_splice_expr (loc, UNKNOWN_LOCATION, r,
/*address_p=*/false,
/*member_access_p=*/false,
+ /*template_p=*/false,
+ /*targs_p=*/false,
/*complain_p=*/false)
/* One cannot query the value of a function template.
??? But if [:^^X:] where X is a template is OK, should we
@@ -8812,13 +8814,15 @@ check_consteval_only_fn (tree decl)
/* Check if T is a valid result of splice-expression. ADDRESS_P is true if
we are taking the address of the splice. MEMBER_ACCESS_P is true if this
- splice is used in foo.[: bar :] or foo->[: bar :] context. COMPLAIN_P is
- true if any errors should be emitted. Returns true is no problems are
- found, false otherwise. */
+ splice is used in foo.[: bar :] or foo->[: bar :] context. TEMPLATE_P is
+ true if the splice-expression was preceded by 'template'. TARGS_P is true
+ if there were template arguments. COMPLAIN_P is true if any errors should
+ be emitted. Returns true is no problems are found, false otherwise. */
bool
check_splice_expr (location_t loc, location_t start_loc, tree t,
- bool address_p, bool member_access_p, bool complain_p)
+ bool address_p, bool member_access_p, bool template_p,
+ bool targs_p, bool complain_p)
{
/* We may not have gotten an expression. */
if (TREE_CODE (t) == TYPE_DECL
@@ -8949,6 +8953,47 @@ check_splice_expr (location_t loc, location_t start_loc, tree t,
return false;
}
+ 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 (!really_overloaded_fn (t) && !dependent_splice_p (t))
+ {
+ if (complain_p)
+ {
+ auto_diagnostic_group d;
+ error_at (loc, "expected a reflection of a function "
+ "template");
+ inform_tree_category (t);
+ }
+ return false;
+ }
+ }
+ /* [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. The
+ template should be a function template or a variable template. */
+ else if (DECL_TYPE_TEMPLATE_P (t))
+ {
+ if (complain_p)
+ {
+ auto_diagnostic_group d;
+ error_at (loc, "expected a reflection of a function or variable "
+ "template");
+ inform_tree_category (t);
+ }
+ return false;
+ }
+ gcc_checking_assert (really_overloaded_fn (t)
+ || get_template_info (t)
+ || TREE_CODE (t) == TEMPLATE_ID_EXPR
+ || variable_template_p (t)
+ || dependent_splice_p (t));
+ }
+
return true;
}
@@ -12,8 +12,8 @@ template<typename T, auto R>
void
g ()
{
- template [: R :]<int> c0; // { dg-error "not a function template|expected" }
- template [: T::r :]<int> c1; // { dg-error "not a function template|expected" }
+ template [: R :]<int> c0; // { dg-error "function or variable template|expected" }
+ template [: T::r :]<int> c1; // { dg-error "function or variable template|expected" }
}
void
@@ -25,10 +25,9 @@ template <int N>
void
qux (S &s)
{
- // TODO: We don't reject this one.
- template [: N == 0 ? ^^foo : ^^:: :] (0); // { dg-error "reflection 'foo' not usable in a template splice" "" { xfail *-*-* } }
- template [: N == 0 ? ^^bar : ^^:: :] (0); // { dg-message "only function templates are allowed here" "" { xfail *-*-* } .-1 }
- s.template [: N == 0 ? ^^S::foo : ^^:: :] (0); // { dg-error "reflection 'foo' not usable in a template splice" "" { xfail *-*-* } }
+ template [: N == 0 ? ^^foo : ^^:: :] (0); // { dg-error "expected a reflection of a function template" }
+ template [: N == 0 ? ^^bar : ^^:: :] (0);
+ s.template [: N == 0 ? ^^S::foo : ^^:: :] (0); // { dg-error "expected a reflection of a function template" }
s.template [: N == 0 ? ^^S::bar : ^^:: :] (0);
}