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);
 }

Reply via email to