On 9/18/25 9:16 PM, Patrick Palka wrote:
Bootstrapped and regtested on x86_64-pc-linux-gnu, does this look OK for trunk and eventually backports?
OK.
-- >8 -- Here the normal form of the two immediately-declared D<<placeholder>, V> constraints is the same, so we rightfully share the normal form between them. However we first normalize the constraint from the context of auto deduction for W in which case the placeholder has level 2 and so the set of in-scope template parameters has depth 2 (a dummy level is added from normalize_placeholder_type_constraints). This depth 2 means that find_template_parameters when it sees V within the requires-expr will recurse into its TREE_TYPE, an auto of level 2, and mark the atomic constraint as depending on the template parameter of depth 2 index 0 (alongside the obvious depth 1 index 0 dependence), which is clearly wrong. Later during satisfaction of B's constraints we lack two levels of template arguments and crash when consulting the satisfaction cache for the shared atomic constraint. I think when find_template_parameters sees an NTTP, it doesn't need to walk its TREE_TYPE because NTTP substitution is done oblviously with respect to its type -- only the corresponding NTTP argument matters. This is more clearly true for (unconstrained) auto NTTPs, but I think applies to all NTTPs. Doing so fixes the testcase because we no longer record any depth 2 dependence when walking V. PR c++/121981 gcc/cp/ChangeLog: * pt.cc (any_template_parm_r) <case TEMPLATE_TYPE_PARM>: Don't walk TREE_TYPE. gcc/testsuite/ChangeLog: * g++.dg/cpp2a/concepts-placeholder15.C: New test. --- gcc/cp/pt.cc | 4 +++- .../g++.dg/cpp2a/concepts-placeholder15.C | 17 +++++++++++++++++ 2 files changed, 20 insertions(+), 1 deletion(-) create mode 100644 gcc/testsuite/g++.dg/cpp2a/concepts-placeholder15.C diff --git a/gcc/cp/pt.cc b/gcc/cp/pt.cc index b7cb807d8daa..276e1a4c7bb7 100644 --- a/gcc/cp/pt.cc +++ b/gcc/cp/pt.cc @@ -11125,7 +11125,9 @@ any_template_parm_r (tree t, void *data) break;case TEMPLATE_PARM_INDEX:- WALK_SUBTREE (TREE_TYPE (t)); + /* No need to walk TREE_TYPE: substitution into an NTTP is done + directly with the corresponding template argument, and its + type only came into play earlier during coercion. */ break;case TEMPLATE_DECL:diff --git a/gcc/testsuite/g++.dg/cpp2a/concepts-placeholder15.C b/gcc/testsuite/g++.dg/cpp2a/concepts-placeholder15.C new file mode 100644 index 000000000000..e6571e9f18cd --- /dev/null +++ b/gcc/testsuite/g++.dg/cpp2a/concepts-placeholder15.C @@ -0,0 +1,17 @@ +// PR c++/121981 +// { dg-do compile { target c++20 } } + +template<auto V> +concept C = requires { V; }; + +template<class T, auto V> +concept D = C<V>; + +template<auto V, D<V> auto W> +struct A { }; + +template<auto V, D<V> T> +struct B { }; + +A<0, 1> a; +B<0, int> b;
