On Fri, 20 Feb 2026, Jason Merrill wrote:

> On 2/14/26 12:19 AM, Patrick Palka wrote:
> > Bootstrapped and regtested on x86_64-pc-linux-gnu, does this approach
> > seem reasonable?
> > 
> > -- >8 --
> > 
> > In the first testcase below (pretending r16-7491 hasn't been merged and
> > we still do ad-hoc checking of simple constrained auto NTTPs), we find
> > ourselves conflating two dependent specializations of the form foo<c>
> > where in one c is a constrained auto NTTP with original level 3, and in
> > the other with original level 2.
> 
> Could we not conflate them?  I wonder about in structural_comptypes just never
> considering constrained autos to be equivalent.

Ah, but that'd break redeclaration matching:

    template<C auto* V> void f();
    template<C auto* V> void f(); // now distinct overload instead of redecl

Luckily, PLACEHOLDER_TYPE_CONSTRAINTS_INFO also contains the in-scope
set of template parameters, so we can also compare those and treat
constrained autos as equivalent only if they're from compatible template
contexts.  Like so?

-- >8 --

Subject: [PATCH] c++: equivalence of lowered constrained auto NTTPs [PR120136]

        PR c++/120136

gcc/cp/ChangeLog:

        * typeck.cc (structural_comptypes) <case TEMPLATE_TYPE_PARM>:
        Return false constrained autos from incompatible template contexts.

gcc/testsuite/ChangeLog:

        * g++.dg/cpp2a/concepts-placeholder16.C: New test.
        * g++.dg/cpp2a/concepts-placeholder16b.C: New test.
        * g++.dg/cpp2a/concepts-placeholder17.C: New test.
        * g++.dg/cpp2a/concepts-placeholder17b.C: New test.
---
 gcc/cp/typeck.cc                              | 17 ++++++++--
 .../g++.dg/cpp2a/concepts-placeholder16.C     | 24 ++++++++++++++
 .../g++.dg/cpp2a/concepts-placeholder16b.C    | 27 ++++++++++++++++
 .../g++.dg/cpp2a/concepts-placeholder17.C     | 29 +++++++++++++++++
 .../g++.dg/cpp2a/concepts-placeholder17b.C    | 32 +++++++++++++++++++
 5 files changed, 126 insertions(+), 3 deletions(-)
 create mode 100644 gcc/testsuite/g++.dg/cpp2a/concepts-placeholder16.C
 create mode 100644 gcc/testsuite/g++.dg/cpp2a/concepts-placeholder16b.C
 create mode 100644 gcc/testsuite/g++.dg/cpp2a/concepts-placeholder17.C
 create mode 100644 gcc/testsuite/g++.dg/cpp2a/concepts-placeholder17b.C

diff --git a/gcc/cp/typeck.cc b/gcc/cp/typeck.cc
index 79eb3b5ba286..885109767ce9 100644
--- a/gcc/cp/typeck.cc
+++ b/gcc/cp/typeck.cc
@@ -1604,9 +1604,20 @@ structural_comptypes (tree t1, tree t2, int strict)
                          CLASS_PLACEHOLDER_TEMPLATE (t2)))
        return false;
       /* Constrained 'auto's are distinct from parms that don't have the same
-        constraints.  */
-      if (!equivalent_placeholder_constraints (t1, t2))
-       return false;
+        constraints or are from incompatible template contexts 
(do_auto_deduction
+        is sensitive to their depth).  */
+      if (PLACEHOLDER_TYPE_CONSTRAINTS_INFO (t1)
+         || PLACEHOLDER_TYPE_CONSTRAINTS_INFO (t2))
+       {
+         if (!equivalent_placeholder_constraints (t1, t2))
+           return false;
+         tree parms1 = TREE_PURPOSE (PLACEHOLDER_TYPE_CONSTRAINTS_INFO (t1));
+         tree parms2 = TREE_PURPOSE (PLACEHOLDER_TYPE_CONSTRAINTS_INFO (t2));
+         int depth1 = parms1 ? TMPL_PARMS_DEPTH (parms1) : 0;
+         int depth2 = parms2 ? TMPL_PARMS_DEPTH (parms2) : 0;
+         if (depth1 != depth2)
+           return false;
+       }
       break;
 
     case TYPENAME_TYPE:
diff --git a/gcc/testsuite/g++.dg/cpp2a/concepts-placeholder16.C 
b/gcc/testsuite/g++.dg/cpp2a/concepts-placeholder16.C
new file mode 100644
index 000000000000..bc29f8ed0e96
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp2a/concepts-placeholder16.C
@@ -0,0 +1,24 @@
+// PR c++/120136
+// { dg-do compile { target c++20 } }
+
+template <class> 
+concept car = true;
+
+template <auto... xs> 
+struct set {
+    static auto contains(car auto x) { 
+        return ((x == xs) || ...); 
+    }
+};
+
+template <auto>
+struct foo {
+    template <car auto c>
+    friend bool operator==(foo, foo<c>);
+};
+
+template <car auto c> foo<c> foobar;
+
+auto a = foobar<42>;
+auto b = set<a>{};
+auto c = b.contains(a);
diff --git a/gcc/testsuite/g++.dg/cpp2a/concepts-placeholder16b.C 
b/gcc/testsuite/g++.dg/cpp2a/concepts-placeholder16b.C
new file mode 100644
index 000000000000..e62a3510d2ae
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp2a/concepts-placeholder16b.C
@@ -0,0 +1,27 @@
+// PR c++/120136
+// { dg-do compile { target c++20 } }
+// A version of concepts-placeholder16.C using where the
+// constrained auto NTTP 'c' is an auto&.
+
+template <class> 
+concept car = true;
+
+template <auto... xs> 
+struct set {
+    static auto contains(car auto x) { 
+        return ((x == xs) || ...); 
+    }
+};
+
+template <auto>
+struct foo {
+    template <car auto& c>
+    friend bool operator==(foo, foo<c>);
+};
+
+template <car auto& c> foo<c> foobar;
+
+inline constexpr int n = 42;
+auto a = foobar<n>;
+auto b = set<a>{};
+auto c = b.contains(a);
diff --git a/gcc/testsuite/g++.dg/cpp2a/concepts-placeholder17.C 
b/gcc/testsuite/g++.dg/cpp2a/concepts-placeholder17.C
new file mode 100644
index 000000000000..de8de1b74d6e
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp2a/concepts-placeholder17.C
@@ -0,0 +1,29 @@
+// PR c++/122204
+// { dg-do compile { target c++20 } }
+
+template <int param>
+struct A {};
+
+template <typename T>
+struct IsA {
+  static constexpr bool value = true;
+};
+
+template <typename T>
+concept Ac = IsA<T>::value;
+
+template <Ac auto dim>
+struct V {};
+
+template <typename T>
+struct IsV {};
+template <Ac auto dim>
+struct IsV<V<dim>> {};
+
+template <int i>
+struct B {
+  template <Ac auto dim>
+  void f(V<dim>) const;
+};
+
+void crashing_function() { B<0>{}.f(V<A<0>{}>{}); }
diff --git a/gcc/testsuite/g++.dg/cpp2a/concepts-placeholder17b.C 
b/gcc/testsuite/g++.dg/cpp2a/concepts-placeholder17b.C
new file mode 100644
index 000000000000..fc8e2a6bb0fc
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp2a/concepts-placeholder17b.C
@@ -0,0 +1,32 @@
+// PR c++/122204
+// { dg-do compile { target c++20 } }
+// A version of concepts-placeholder16.C using where the
+// constrained auto NTTP 'dim' is an auto&.
+
+template <int param>
+struct A {};
+
+template <typename T>
+struct IsA {
+  static constexpr bool value = true;
+};
+
+template <typename T>
+concept Ac = IsA<T>::value;
+
+template <Ac auto& dim>
+struct V {};
+
+template <typename T>
+struct IsV {};
+template <Ac auto& dim>
+struct IsV<V<dim>> {};
+
+template <int i>
+struct B {
+  template <Ac auto& dim>
+  void f(V<dim>) const;
+};
+
+inline constexpr A<0> a;
+void crashing_function() { B<0>{}.f(V<a>{}); }
-- 
2.53.0.749.g5361983c07

Reply via email to