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.  Then during unification of foo<c> with
foo<42>, do_auto_deduction checks c's constraint against the argument 42.
Due to the mentioned conflation this is done with a different ORIG_LEVEL
auto, which causes the ORIG_LEVEL workaround in do_auto_deduction to
misbehave and we crash.

This patch fixes this issue by preferring to use the in-scope version
of a constrained auto NTTP when a mismatch is noticed.  Note this
workaround is needed even after r16-7491 for non-simple constrained
autos as in the second and fourth testcases.

This approach, while ugly, is a narrow workaround that should be
relatively safe to backport.  Another approach might be to "unconstrain"
auto NTTP arguments in strip_typedefs_expr when forming a dependent
specialization so that later unification doesn't hit this situation,
but that'd mean rebuilding arbitrarily complex autos, e.g. auto***&.

It occurred to me that checking auto constraints when unifying foo<c>
with foo<42> is redundant here, because the specialization wouldn't
have been formed in the first place if the the constraint was invalid.
So yet another approach would be to make unify aware of this and avoid
checking constraints when recursing into specialization arguments.

        PR c++/120136

gcc/cp/ChangeLog:

        * pt.cc (unify) <case TEMPLATE_PARM_INDEX>: Before doing
        do_auto_deduction on a constrained auto NTTP, prefer using the
        in-scope version of the parameter.

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/pt.cc                                  | 31 ++++++++++++++++++
 .../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, 143 insertions(+)
 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/pt.cc b/gcc/cp/pt.cc
index 20fd30a8b473..512e29980f7c 100644
--- a/gcc/cp/pt.cc
+++ b/gcc/cp/pt.cc
@@ -26174,6 +26174,37 @@ unify (tree tparms, tree targs, tree parm, tree arg, 
int strict,
 
          if (tree a = type_uses_auto (tparm))
            {
+             if (tree a_ci = PLACEHOLDER_TYPE_CONSTRAINTS_INFO (a))
+               {
+                 /* Constrained auto NTTP checking is sensitive to
+                    TEMPLATE_TYPE_ORIG_LEVEL and the set of in-scope template
+                    parameters (i.e. TREE_PURPOSE (a_ci)), but our notion of
+                    template argument equivalence isn't.  So we'd conflate two
+                    specializations A<V> where V is a lowered constrained auto
+                    NTTP from different template contexts, which may break
+                    constraint checking for V when unifying A<V> with A<...>.
+                    Prefer using the in-scope V in this case, which matches
+                    the current template context.  */
+                 tree scope_tparm
+                   = TREE_TYPE (TREE_VALUE (TREE_VEC_ELT (tparms, idx)));
+                 tree b = type_uses_auto (scope_tparm);
+                 tree b_ci = PLACEHOLDER_TYPE_CONSTRAINTS_INFO (b);
+                 gcc_checking_assert (equivalent_placeholder_constraints
+                                      (TREE_VALUE (a_ci), TREE_VALUE (b_ci)));
+                 if (TEMPLATE_TYPE_ORIG_LEVEL (b)
+                     != TEMPLATE_TYPE_ORIG_LEVEL (a))
+                   {
+                     ++processing_template_decl;
+                     scope_tparm
+                       = tsubst (scope_tparm, targs, tf_none, NULL_TREE);
+                     --processing_template_decl;
+                     b = type_uses_auto (scope_tparm);
+                     gcc_checking_assert (same_type_p (tparm, scope_tparm));
+                     tparm = scope_tparm;
+                     a = b;
+                   }
+               }
+
              tparm = do_auto_deduction (tparm, arg, a,
                                         complain, adc_unify, targs,
                                         LOOKUP_NORMAL,
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.80.g6fcee47852

Reply via email to