]> git.ipfire.org Git - thirdparty/gcc.git/commitdiff
c++: constrained memfn vs corresponding using [PR121351]
authorPatrick Palka <ppalka@redhat.com>
Mon, 4 Aug 2025 20:51:00 +0000 (16:51 -0400)
committerPatrick Palka <ppalka@redhat.com>
Mon, 4 Aug 2025 20:51:00 +0000 (16:51 -0400)
The b.f(42) calls in the below testcases started to get rejected as
ambiguous after r15-3740 which corrected our inheritedness tiebreaker to
only apply to constructors (and not all member functions) as per CWG2273.

But arguably these calls should still be valid regardless of the
tiebreaker because B::f corresponds to and therefore hides A::f, so
there should only be a single candidate in the first place.  This
doesn't happen because when determining correspondence we compare
the members' uninstantiated constraints instead of their partially
substituted constraints as in other declaration matching situations.
It doesn't really make sense to compare uninstantiated constraints
from two different template contexts.

This patch fixes this by substituting in outer template arguments before
comparing constraints of two potentially corresponding member functions.

PR c++/121351
PR c++/119859

gcc/cp/ChangeLog:

* class.cc (add_method): Substitute outer template arguments
into constraints before comparing them if the declarations are
from different classes.

gcc/testsuite/ChangeLog:

* g++.dg/cpp2a/concepts-using5.C: New test.
* g++.dg/cpp2a/concepts-using5a.C: New test.

Reviewed-by: Jason Merrill <jason@redhat.com>
gcc/cp/class.cc
gcc/testsuite/g++.dg/cpp2a/concepts-using5.C [new file with mode: 0644]
gcc/testsuite/g++.dg/cpp2a/concepts-using5a.C [new file with mode: 0644]

index f5d20e5c53922b2d16c9e579c7448c21c607b1a9..14acb9c23c01e787e57229a497e523c8c97fccbb 100644 (file)
@@ -1356,7 +1356,30 @@ add_method (tree type, tree method, bool via_using)
       if (!compparms (parms1, parms2))
        continue;
 
-      if (!equivalently_constrained (fn, method))
+      tree fn_constraints = get_constraints (fn);
+      tree method_constraints = get_constraints (method);
+
+      if (fn_constraints && method_constraints
+         && DECL_CONTEXT (fn) != type
+         && !processing_template_decl)
+       {
+         if (TREE_CODE (fn) == TEMPLATE_DECL)
+           ++processing_template_decl;
+         if (tree ti = CLASSTYPE_TEMPLATE_INFO (DECL_CONTEXT (fn)))
+           fn_constraints = tsubst_constraint_info (fn_constraints,
+                                                    TI_ARGS (ti),
+                                                    tf_warning_or_error,
+                                                    fn);
+         if (tree ti = CLASSTYPE_TEMPLATE_INFO (DECL_CONTEXT (method)))
+           method_constraints = tsubst_constraint_info (method_constraints,
+                                                        TI_ARGS (ti),
+                                                        tf_warning_or_error,
+                                                        method);
+         if (TREE_CODE (fn) == TEMPLATE_DECL)
+           --processing_template_decl;
+       }
+
+      if (!equivalent_constraints (fn_constraints, method_constraints))
        {
          if (processing_template_decl)
            /* We can't check satisfaction in dependent context, wait until
diff --git a/gcc/testsuite/g++.dg/cpp2a/concepts-using5.C b/gcc/testsuite/g++.dg/cpp2a/concepts-using5.C
new file mode 100644 (file)
index 0000000..d42b8a0
--- /dev/null
@@ -0,0 +1,19 @@
+// PR c++/121351
+// { dg-do compile { target c++20 } }
+
+template<class T> concept C = true;
+
+template<class T>
+struct A {
+  template<class U> void f(U) requires C<T> = delete; // #1
+};
+
+struct B : A<int> {
+  using A::f;
+  template<class U> void f(U) requires C<int>; // #2
+};
+
+int main() {
+  B b;
+  b.f(42); // OK, #2 corresponds to and therefore hides #1
+}
diff --git a/gcc/testsuite/g++.dg/cpp2a/concepts-using5a.C b/gcc/testsuite/g++.dg/cpp2a/concepts-using5a.C
new file mode 100644 (file)
index 0000000..5d31948
--- /dev/null
@@ -0,0 +1,20 @@
+// PR c++/121351
+// { dg-do compile { target c++20 } }
+// A version of concepts-using5a.C where B instead of A is a template.
+
+template<class T> concept C = true;
+
+struct A {
+  template<class U> void f(U) requires C<int> = delete; // #1
+};
+
+template<class T>
+struct B : A {
+  using A::f;
+  template<class U> void f(U) requires C<T>; // #2
+};
+
+int main() {
+  B<int> b;
+  b.f(42); // OK, #2 corresponds to and therefore hides #1
+}