[v2] c++/reflection: reject invalid template splice [PR123998]

Message ID adat_NaPJs-bKBVb@redhat.com
State New
Headers
Series [v2] c++/reflection: reject invalid template splice [PR123998] |

Checks

Context Check Description
linaro-tcwg-bot/tcwg_gcc_build--master-aarch64 success Build passed
linaro-tcwg-bot/tcwg_gcc_build--master-arm success Build passed
linaro-tcwg-bot/tcwg_gcc_check--master-aarch64 fail Patch failed to apply
linaro-tcwg-bot/tcwg_gcc_check--master-arm fail Patch failed to apply

Commit Message

Marek Polacek April 8, 2026, 7:35 p.m. UTC
  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

Jason Merrill April 9, 2026, 6:10 p.m. UTC | #1
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
  

Patch

diff --git a/gcc/cp/cp-tree.h b/gcc/cp/cp-tree.h
index 1ea3319c37b..1080203ac8a 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 bbebc125765..fe27a15a283 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 c10ca09cdb1..18aec7ebb56 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 78d48db29ba..8925bf49465 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 3aef5d0b2fe..1d8e78994c2 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 4f0d2fe6134..96d3192eb55 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);
 }