[gcc r17-163] c++/reflection: improve diagnostic for dependent splices
Marek Polacek
mpolacek@gcc.gnu.org
Tue Apr 28 19:35:44 GMT 2026
https://gcc.gnu.org/g:f4de514436b4c534f27b3bcfe6a581ad60db85c8
commit r17-163-gf4de514436b4c534f27b3bcfe6a581ad60db85c8
Author: Marek Polacek <polacek@redhat.com>
Date: Mon Apr 20 13:00:52 2026 -0400
c++/reflection: improve diagnostic for dependent splices
In the parser we've changed the "not usable in a splice" error messages
to the more helpful "expected a reflection of ...", but tsubst_splice_scope
still uses the former. This patch updates the diagnostic there as well.
Let's also teach inform_tree_category about concepts and alias templates
now that a testcase exercises them.
gcc/cp/ChangeLog:
* error.cc (inform_tree_category): Also print concept and alias
template.
* pt.cc (tsubst_splice_scope): Reword the diagnostic messages.
Call inform_tree_category.
gcc/testsuite/ChangeLog:
* g++.dg/reflect/ns5.C: Adjust expected diagnostics.
* g++.dg/reflect/type9.C: Likewise.
Reviewed-by: Jason Merrill <jason@redhat.com>
Diff:
---
gcc/cp/error.cc | 4 ++++
gcc/cp/pt.cc | 16 ++++++++-----
gcc/testsuite/g++.dg/reflect/ns5.C | 41 ++++++++++++++++++++++-----------
gcc/testsuite/g++.dg/reflect/type9.C | 44 ++++++++++++++++++++++++------------
4 files changed, 72 insertions(+), 33 deletions(-)
diff --git a/gcc/cp/error.cc b/gcc/cp/error.cc
index e7121dc10175..2ea9d1a7bcd6 100644
--- a/gcc/cp/error.cc
+++ b/gcc/cp/error.cc
@@ -4013,12 +4013,16 @@ inform_tree_category (tree 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 (DECL_ALIAS_TEMPLATE_P (t))
+ inform (loc, "but %qE is an alias template", t);
else if (variable_template_p (t))
inform (loc, "but %qE is a variable 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);
+ else if (concept_definition_p (t))
+ inform (loc, "but %qE is a concept", t);
}
/* Disable warnings about missing quoting in GCC diagnostics for
diff --git a/gcc/cp/pt.cc b/gcc/cp/pt.cc
index ce324c87ecf8..05ecda2a0d8a 100644
--- a/gcc/cp/pt.cc
+++ b/gcc/cp/pt.cc
@@ -16902,21 +16902,25 @@ tsubst_splice_scope (tree t, tree args, tsubst_flags_t complain, tree in_decl)
tree r = tsubst (SPLICE_SCOPE_EXPR (t), args, complain, in_decl);
if (r == error_mark_node)
return r;
+ const bool type_p = SPLICE_SCOPE_TYPE_P (t);
if (dependent_splice_p (r))
- return make_splice_scope (r, SPLICE_SCOPE_TYPE_P (t));
- if (SPLICE_SCOPE_TYPE_P (t) && ctad_template_p (r))
+ return make_splice_scope (r, type_p);
+ if (type_p && ctad_template_p (r))
r = make_template_placeholder (r);
- if (SPLICE_SCOPE_TYPE_P (t)
+ if (type_p
? !valid_splice_type_p (r)
: !valid_splice_scope_p (r))
{
if (complain & tf_error)
{
const location_t loc = EXPR_LOCATION (SPLICE_SCOPE_EXPR (t));
- if (SPLICE_SCOPE_TYPE_P (t))
- error_at (loc, "%qE is not usable in a splice type", r);
+ auto_diagnostic_group d;
+ if (type_p)
+ error_at (loc, "expected a reflection of a type");
else
- error_at (loc, "%qE is not usable in a splice scope", r);
+ error_at (loc, "expected a reflection of a class, namespace, or "
+ "enumeration");
+ inform_tree_category (r);
}
return error_mark_node;
}
diff --git a/gcc/testsuite/g++.dg/reflect/ns5.C b/gcc/testsuite/g++.dg/reflect/ns5.C
index 236b29274530..eb75481d9f1f 100644
--- a/gcc/testsuite/g++.dg/reflect/ns5.C
+++ b/gcc/testsuite/g++.dg/reflect/ns5.C
@@ -5,12 +5,16 @@ using info = decltype(^^void);
template<typename>
void foo () { }
+template<typename>
+void foo2 () { }
void bar () { }
namespace N { }
namespace M = N;
template<typename T>
T vt{};
template<typename T>
+T vt2{};
+template<typename T>
concept C = true;
static int i;
enum E { X };
@@ -20,23 +24,34 @@ struct D { static T di; };
template<typename T>
using Z = D<T>;
-template<info R> void fn1 () { typename [:R:]::X x; } // { dg-error "not usable in a splice scope" }
-template<info R> void fn2 () { typename [:R:]::X x; } // { dg-error "not usable in a splice scope" }
-template<info R> void fn3 () { typename [:R:]::X x; } // { dg-error "not usable in a splice scope" }
-template<info R> void fn4 () { typename [:R:]::X x; } // { dg-error "not usable in a splice scope" }
-template<info R> void fn5 () { typename [:R:]::X x; } // { dg-error "not usable in a splice scope" }
-template<info R> void fn6 () { typename [:R:]::X x; } // { dg-error "not usable in a splice scope" }
-template<info R> void fn7 () { typename [:R:]::X x; } // { dg-error "not usable in a splice scope" }
-template<info R> void fn8 () { typename [:R:]::X x; } // { dg-error "not usable in a splice scope" }
-template<info R> void fn9 () { typename [:R:]::X x; } // { dg-error "not usable in a splice scope" }
-template<info R> void fn10 () { typename [:R:]::X x; } // { dg-error "not usable in a splice scope" }
-template<info R> void fn11 () { typename [:R:]::X x; } // { dg-error "not usable in a splice scope" }
+template<info R> void fn1 () { typename [:R:]::X x; } // { dg-error "expected a reflection of a class, namespace, or enumeration" }
+// { dg-message "but .foo<int>. is a function" "" { target *-*-* } 7 }
+template<info R> void fn2 () { typename [:R:]::X x; } // { dg-error "expected a reflection of a class, namespace, or enumeration" }
+// { dg-message "but .foo2. is a function template" "" { target *-*-* } 9 }
+template<info R> void fn3 () { typename [:R:]::X x; } // { dg-error "expected a reflection of a class, namespace, or enumeration" }
+// { dg-message "but .bar. is a function" "" { target *-*-* } 10 }
+template<info R> void fn4 () { typename [:R:]::X x; } // { dg-error "expected a reflection of a class, namespace, or enumeration" }
+// { dg-message "but .vt<int>. is a variable" "" { target *-*-* } 14 }
+template<info R> void fn5 () { typename [:R:]::X x; } // { dg-error "expected a reflection of a class, namespace, or enumeration" }
+// { dg-message "but .vt2<T>. is a variable template" "" { target *-*-* } 16 }
+template<info R> void fn6 () { typename [:R:]::X x; } // { dg-error "expected a reflection of a class, namespace, or enumeration" }
+// { dg-message "but .C. is a concept" "" { target *-*-* } 18 }
+template<info R> void fn7 () { typename [:R:]::X x; } // { dg-error "expected a reflection of a class, namespace, or enumeration" }
+// { dg-message "but .i. is a variable" "" { target *-*-* } 19 }
+template<info R> void fn8 () { typename [:R:]::X x; } // { dg-error "expected a reflection of a class, namespace, or enumeration" }
+// { dg-message "but .X. is an enumerator" "" { target *-*-* } 20 }
+template<info R> void fn9 () { typename [:R:]::X x; } // { dg-error "expected a reflection of a class, namespace, or enumeration" }
+// { dg-message "but .S::si. is a variable" "" { target *-*-* } 21 }
+template<info R> void fn10 () { typename [:R:]::X x; } // { dg-error "expected a reflection of a class, namespace, or enumeration" }
+// { dg-message "but .D<int>::di. is a variable" "" { target *-*-* } 23 }
+template<info R> void fn11 () { typename [:R:]::X x; } // { dg-error "expected a reflection of a class, namespace, or enumeration" }
+// { dg-message "but .Z. is an alias template" "" { target *-*-* } 25 }
template void fn1<^^foo<int>>(); // { dg-message "required from here" }
-template void fn2<^^foo>(); // { dg-message "required from here" }
+template void fn2<^^foo2>(); // { dg-message "required from here" }
template void fn3<^^bar>(); // { dg-message "required from here" }
template void fn4<^^vt<int>>(); // { dg-message "required from here" }
-template void fn5<^^vt>(); // { dg-message "required from here" }
+template void fn5<^^vt2>(); // { dg-message "required from here" }
template void fn6<^^C>(); // { dg-message "required from here" }
template void fn7<^^i>(); // { dg-message "required from here" }
template void fn8<^^X>(); // { dg-message "required from here" }
diff --git a/gcc/testsuite/g++.dg/reflect/type9.C b/gcc/testsuite/g++.dg/reflect/type9.C
index 2d0409e4b864..99c6fd2781b9 100644
--- a/gcc/testsuite/g++.dg/reflect/type9.C
+++ b/gcc/testsuite/g++.dg/reflect/type9.C
@@ -5,12 +5,16 @@ using info = decltype(^^void);
template<typename>
void foo () { }
+template<typename>
+void foo2 () { }
void bar () { }
namespace N { }
namespace M = N;
template<typename T>
T vt{};
template<typename T>
+T vt2{};
+template<typename T>
concept C = true;
static int i;
enum E { X };
@@ -20,27 +24,39 @@ struct D { static T di; };
template<typename T>
using Z = D<T>;
-template<info R> void fn1 () { int n = typename [:R:](42); } // { dg-error "not usable in a splice type" }
-template<info R> void fn2 () { int n = typename [:R:](42); } // { dg-error "not usable in a splice type" }
-template<info R> void fn3 () { int n = typename [:R:](42); } // { dg-error "not usable in a splice type" }
-template<info R> void fn4 () { int n = typename [:R:](42); } // { dg-error "not usable in a splice type" }
-template<info R> void fn5 () { int n = typename [:R:](42); } // { dg-error "not usable in a splice type" }
-template<info R> void fn6 () { int n = typename [:R:](42); } // { dg-error "not usable in a splice type" }
-template<info R> void fn7 () { int n = typename [:R:](42); } // { dg-error "not usable in a splice type" }
-template<info R> void fn8 () { int n = typename [:R:](42); } // { dg-error "not usable in a splice type" }
-template<info R> void fn9 () { int n = typename [:R:](42); } // { dg-error "not usable in a splice type" }
-template<info R> void fn10 () { int n = typename [:R:](42); } // { dg-error "not usable in a splice type" }
-template<info R> void fn11 () { int n = typename [:R:](42); } // { dg-error "not usable in a splice type" }
-template<info R> void fn12 () { int n = typename [:R:](42); } // { dg-error "not usable in a splice type" }
+template<info R> void fn1 () { int n = typename [:R:](42); } // { dg-error "expected a reflection of a type" }
+// { dg-message "but .foo<int>. is a function" "" { target *-*-* } 7 }
+template<info R> void fn2 () { int n = typename [:R:](42); } // { dg-error "expected a reflection of a type" }
+// { dg-message "but .foo2. is a function template" "" { target *-*-* } 9 }
+template<info R> void fn3 () { int n = typename [:R:](42); } // { dg-error "expected a reflection of a type" }
+// { dg-message "but .N. is a namespace" "" { target *-*-* } 11 }
+template<info R> void fn4 () { int n = typename [:R:](42); } // { dg-error "expected a reflection of a type" }
+// { dg-message "but .M. is a namespace" "" { target *-*-* } 12 }
+template<info R> void fn5 () { int n = typename [:R:](42); } // { dg-error "expected a reflection of a type" }
+// { dg-message "but .bar. is a function" "" { target *-*-* } 10 }
+template<info R> void fn6 () { int n = typename [:R:](42); } // { dg-error "expected a reflection of a type" }
+// { dg-message "but .vt<int>. is a variable" "" { target *-*-* } 14 }
+template<info R> void fn7 () { int n = typename [:R:](42); } // { dg-error "expected a reflection of a type" }
+// { dg-message "but .vt2<T>. is a variable template" "" { target *-*-* } 16 }
+template<info R> void fn8 () { int n = typename [:R:](42); } // { dg-error "expected a reflection of a type" }
+// { dg-message "but .C. is a concept" "" { target *-*-* } 18 }
+template<info R> void fn9 () { int n = typename [:R:](42); } // { dg-error "expected a reflection of a type" }
+// { dg-message "but .i. is a variable" "" { target *-*-* } 19 }
+template<info R> void fn10 () { int n = typename [:R:](42); } // { dg-error "expected a reflection of a type" }
+// { dg-message "but .X. is an enumerator" "" { target *-*-* } 20 }
+template<info R> void fn11 () { int n = typename [:R:](42); } // { dg-error "expected a reflection of a type" }
+// { dg-message "but .S::si. is a variable" "" { target *-*-* } 21 }
+template<info R> void fn12 () { int n = typename [:R:](42); } // { dg-error "expected a reflection of a type" }
+// { dg-message "but .D<int>::di. is a variable" "" { target *-*-* } 23 }
template<info R> void fn13 () { int n = typename [:R:](42); } // { dg-error "class template argument deduction failed|no matching" }
template void fn1<^^foo<int>>(); // { dg-message "required from here" }
-template void fn2<^^foo>(); // { dg-message "required from here" }
+template void fn2<^^foo2>(); // { dg-message "required from here" }
template void fn3<^^N>(); // { dg-message "required from here" }
template void fn4<^^M>(); // { dg-message "required from here" }
template void fn5<^^bar>(); // { dg-message "required from here" }
template void fn6<^^vt<int>>(); // { dg-message "required from here" }
-template void fn7<^^vt>(); // { dg-message "required from here" }
+template void fn7<^^vt2>(); // { dg-message "required from here" }
template void fn8<^^C>(); // { dg-message "required from here" }
template void fn9<^^i>(); // { dg-message "required from here" }
template void fn10<^^X>(); // { dg-message "required from here" }
More information about the Gcc-cvs
mailing list