https://gcc.gnu.org/g:c684613dea0b00d222ebbae8439ea8fd8c1f1865
commit r16-8551-gc684613dea0b00d222ebbae8439ea8fd8c1f1865 Author: Marek Polacek <[email protected]> Date: Fri Mar 20 12:34:31 2026 -0400 c++/reflection: reject invalid template splice [PR123998] 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. Reviewed-by: Jason Merrill <[email protected]> Diff: --- 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(-) diff --git a/gcc/cp/cp-tree.h b/gcc/cp/cp-tree.h index 1ea3319c37b8..1080203ac8a4 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). @@ -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]); diff --git a/gcc/cp/parser.cc b/gcc/cp/parser.cc index bbebc1257656..fe27a15a2835 100644 --- a/gcc/cp/parser.cc +++ b/gcc/cp/parser.cc @@ -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); diff --git a/gcc/cp/pt.cc b/gcc/cp/pt.cc index c10ca09cdb16..18aec7ebb561 100644 --- a/gcc/cp/pt.cc +++ b/gcc/cp/pt.cc @@ -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; diff --git a/gcc/cp/reflect.cc b/gcc/cp/reflect.cc index 78d48db29ba2..8925bf49465d 100644 --- a/gcc/cp/reflect.cc +++ b/gcc/cp/reflect.cc @@ -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; } diff --git a/gcc/testsuite/g++.dg/reflect/crash11.C b/gcc/testsuite/g++.dg/reflect/crash11.C index 3aef5d0b2fe5..1d8e78994c24 100644 --- a/gcc/testsuite/g++.dg/reflect/crash11.C +++ b/gcc/testsuite/g++.dg/reflect/crash11.C @@ -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 diff --git a/gcc/testsuite/g++.dg/reflect/splice5.C b/gcc/testsuite/g++.dg/reflect/splice5.C index 4f0d2fe61341..96d3192eb55f 100644 --- a/gcc/testsuite/g++.dg/reflect/splice5.C +++ b/gcc/testsuite/g++.dg/reflect/splice5.C @@ -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); }
