From f4de514436b4c534f27b3bcfe6a581ad60db85c8 Mon Sep 17 00:00:00 2001 From: Marek Polacek Date: Mon, 20 Apr 2026 13:00:52 -0400 Subject: [PATCH] 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 --- 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 e7121dc1017..2ea9d1a7bcd 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 ce324c87ecf..05ecda2a0d8 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 236b2927453..eb75481d9f1 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 void foo () { } +template +void foo2 () { } void bar () { } namespace N { } namespace M = N; template T vt{}; template +T vt2{}; +template concept C = true; static int i; enum E { X }; @@ -20,23 +24,34 @@ struct D { static T di; }; template using Z = D; -template void fn1 () { typename [:R:]::X x; } // { dg-error "not usable in a splice scope" } -template void fn2 () { typename [:R:]::X x; } // { dg-error "not usable in a splice scope" } -template void fn3 () { typename [:R:]::X x; } // { dg-error "not usable in a splice scope" } -template void fn4 () { typename [:R:]::X x; } // { dg-error "not usable in a splice scope" } -template void fn5 () { typename [:R:]::X x; } // { dg-error "not usable in a splice scope" } -template void fn6 () { typename [:R:]::X x; } // { dg-error "not usable in a splice scope" } -template void fn7 () { typename [:R:]::X x; } // { dg-error "not usable in a splice scope" } -template void fn8 () { typename [:R:]::X x; } // { dg-error "not usable in a splice scope" } -template void fn9 () { typename [:R:]::X x; } // { dg-error "not usable in a splice scope" } -template void fn10 () { typename [:R:]::X x; } // { dg-error "not usable in a splice scope" } -template void fn11 () { typename [:R:]::X x; } // { dg-error "not usable in a splice scope" } +template void fn1 () { typename [:R:]::X x; } // { dg-error "expected a reflection of a class, namespace, or enumeration" } +// { dg-message "but .foo. is a function" "" { target *-*-* } 7 } +template 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 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 void fn4 () { typename [:R:]::X x; } // { dg-error "expected a reflection of a class, namespace, or enumeration" } +// { dg-message "but .vt. is a variable" "" { target *-*-* } 14 } +template void fn5 () { typename [:R:]::X x; } // { dg-error "expected a reflection of a class, namespace, or enumeration" } +// { dg-message "but .vt2. is a variable template" "" { target *-*-* } 16 } +template 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 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 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 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 void fn10 () { typename [:R:]::X x; } // { dg-error "expected a reflection of a class, namespace, or enumeration" } +// { dg-message "but .D::di. is a variable" "" { target *-*-* } 23 } +template 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>(); // { 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>(); // { 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 2d0409e4b86..99c6fd2781b 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 void foo () { } +template +void foo2 () { } void bar () { } namespace N { } namespace M = N; template T vt{}; template +T vt2{}; +template concept C = true; static int i; enum E { X }; @@ -20,27 +24,39 @@ struct D { static T di; }; template using Z = D; -template void fn1 () { int n = typename [:R:](42); } // { dg-error "not usable in a splice type" } -template void fn2 () { int n = typename [:R:](42); } // { dg-error "not usable in a splice type" } -template void fn3 () { int n = typename [:R:](42); } // { dg-error "not usable in a splice type" } -template void fn4 () { int n = typename [:R:](42); } // { dg-error "not usable in a splice type" } -template void fn5 () { int n = typename [:R:](42); } // { dg-error "not usable in a splice type" } -template void fn6 () { int n = typename [:R:](42); } // { dg-error "not usable in a splice type" } -template void fn7 () { int n = typename [:R:](42); } // { dg-error "not usable in a splice type" } -template void fn8 () { int n = typename [:R:](42); } // { dg-error "not usable in a splice type" } -template void fn9 () { int n = typename [:R:](42); } // { dg-error "not usable in a splice type" } -template void fn10 () { int n = typename [:R:](42); } // { dg-error "not usable in a splice type" } -template void fn11 () { int n = typename [:R:](42); } // { dg-error "not usable in a splice type" } -template void fn12 () { int n = typename [:R:](42); } // { dg-error "not usable in a splice type" } +template void fn1 () { int n = typename [:R:](42); } // { dg-error "expected a reflection of a type" } +// { dg-message "but .foo. is a function" "" { target *-*-* } 7 } +template 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 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 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 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 void fn6 () { int n = typename [:R:](42); } // { dg-error "expected a reflection of a type" } +// { dg-message "but .vt. is a variable" "" { target *-*-* } 14 } +template void fn7 () { int n = typename [:R:](42); } // { dg-error "expected a reflection of a type" } +// { dg-message "but .vt2. is a variable template" "" { target *-*-* } 16 } +template 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 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 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 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 void fn12 () { int n = typename [:R:](42); } // { dg-error "expected a reflection of a type" } +// { dg-message "but .D::di. is a variable" "" { target *-*-* } 23 } template void fn13 () { int n = typename [:R:](42); } // { dg-error "class template argument deduction failed|no matching" } template void fn1<^^foo>(); // { 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>(); // { 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" } -- 2.47.3