[2/2] c++: CWG 2789 and usings [PR116492]

Message ID 20240918205946.4082171-2-ppalka@redhat.com
State New
Headers
Series [1/2] c++: CWG 2273 and non-constructors |

Checks

Context Check Description
linaro-tcwg-bot/tcwg_gcc_build--master-arm success Build passed
linaro-tcwg-bot/tcwg_gcc_build--master-aarch64 success Build passed
linaro-tcwg-bot/tcwg_gcc_check--master-aarch64 success Test passed
linaro-tcwg-bot/tcwg_gcc_check--master-arm success Test passed

Commit Message

Patrick Palka Sept. 18, 2024, 8:59 p.m. UTC
  Bootstrapped and regtested on x86_64-pc-linux-gnu, does this
look OK for trunk?  I'm not sure this is worth backporting
without the previous CWG 2273 tweak since it'll mean inconsistent
behavior between implict vs explicit object members in GCC 14: the call
to S<>{}.f() in concepts-memfun4.C would now return 10 (due to the CWG
2273 tiebreaker incorrectly triggering), while the g() and h() calls
would be ambiguous (since that tiebreaker doesn't consider object
correspondence).

Also I'm not 100% sure if I'm interpreting "both are direct members of
the same class" correctly here to mean ruling out using'd vs non-using'd
members, since https://eel.is/c++draft/namespace.udecl#note-5 says
using'd members are "treated as though they were direct members of the
derived class"...?

-- >8 --

After CWG 2789, the "more constrained" tiebreaker for non-template
functions should exclude members that come from different classes
via using.  This patch implements this missing refinement.  In turn
we can get rid of four-parameter overload of object_parms_correspond
and call the main overload directly since we know correspondence is now
only checked for members from the same class.

	PR c++/116492
	DR 2789

gcc/cp/ChangeLog:

	* call.cc (object_parms_correspond): Remove.
	(cand_parms_match): Return false for member functions
	that come from different classes.  Adjust call to
	object_parms_correspond.
	(joust): Update comment for the non-template "more
	constrained" case.

gcc/testsuite/ChangeLog:

	* g++.dg/cpp2a/concepts-memfun4.C: Expect ambiguity when
	candidates come from different classes.
	* g++.dg/cpp2a/concepts-inherit-ctor12.C: New test.
---
 gcc/cp/call.cc                                | 54 +++++++------------
 .../g++.dg/cpp2a/concepts-inherit-ctor12.C    | 16 ++++++
 gcc/testsuite/g++.dg/cpp2a/concepts-memfun4.C | 24 +++++----
 3 files changed, 49 insertions(+), 45 deletions(-)
 create mode 100644 gcc/testsuite/g++.dg/cpp2a/concepts-inherit-ctor12.C
  

Comments

Jason Merrill Sept. 20, 2024, 2:36 p.m. UTC | #1
On 9/18/24 10:59 PM, Patrick Palka wrote:
> Bootstrapped and regtested on x86_64-pc-linux-gnu, does this
> look OK for trunk?  I'm not sure this is worth backporting
> without the previous CWG 2273 tweak since it'll mean inconsistent
> behavior between implict vs explicit object members in GCC 14: the call
> to S<>{}.f() in concepts-memfun4.C would now return 10 (due to the CWG
> 2273 tiebreaker incorrectly triggering), while the g() and h() calls
> would be ambiguous (since that tiebreaker doesn't consider object
> correspondence).

Agreed, I wouldn't backport.

> Also I'm not 100% sure if I'm interpreting "both are direct members of
> the same class" correctly here to mean ruling out using'd vs non-using'd
> members, since https://eel.is/c++draft/namespace.udecl#note-5 says
> using'd members are "treated as though they were direct members of the
> derived class"...?

Looking back at the CWG wiki notes, I see that ruling out using vs 
non-using was intended.

> +++ b/gcc/cp/call.cc
> @@ -12893,16 +12872,19 @@ cand_parms_match (z_candidate *c1, z_candidate *c2, pmatch match_kind)
>     tree parms1 = TYPE_ARG_TYPES (TREE_TYPE (fn1));
>     tree parms2 = TYPE_ARG_TYPES (TREE_TYPE (fn2));
>   
> +  if (DECL_FUNCTION_MEMBER_P (fn1)
> +      && DECL_FUNCTION_MEMBER_P (fn2))
>       {
> +      tree base1 = DECL_CONTEXT (strip_inheriting_ctors (fn1));
> +      tree base2 = DECL_CONTEXT (strip_inheriting_ctors (fn2));
> +      if (base1 != base2)
> +	return false;
> +
> +      /* CWG2789 is not adequate, it should specify corresponding object
> +	 parameters, not same typed object parameters.  */

With this change, the difference is no longer significant; the comment 
should change to something like

/* Use object_parms_correspond to simplify comparing iobj/xobj/static
    member functions.  */

> +++ b/gcc/testsuite/g++.dg/cpp2a/concepts-memfun4.C
> @@ -20,12 +23,14 @@ struct S : B<> {
>     constexpr operator int () const { return 10; }
>     constexpr int g() { return 10; }
>     constexpr int h(this S&&) { return 10; }
> +  // { dg-warning "explicit object" "" { target c++20_only } .-1 }
>   };
>   
> -// implicit object parms match, B::f is more constrained
> -static_assert(S<>{}.f() == 5);
> -static_assert(S<>{}.g() == 5);
> -static_assert(S<>{}.h() == 5);
> +// ambiguous, constraints aren't considered since the candidates
> +// are defined from different classes

"defined in" or "come from"

OK with these two tweaks.

Jason
  
Patrick Palka Sept. 20, 2024, 4:51 p.m. UTC | #2
On Fri, 20 Sep 2024, Jason Merrill wrote:

> On 9/18/24 10:59 PM, Patrick Palka wrote:
> > Bootstrapped and regtested on x86_64-pc-linux-gnu, does this
> > look OK for trunk?  I'm not sure this is worth backporting
> > without the previous CWG 2273 tweak since it'll mean inconsistent
> > behavior between implict vs explicit object members in GCC 14: the call
> > to S<>{}.f() in concepts-memfun4.C would now return 10 (due to the CWG
> > 2273 tiebreaker incorrectly triggering), while the g() and h() calls
> > would be ambiguous (since that tiebreaker doesn't consider object
> > correspondence).
> 
> Agreed, I wouldn't backport.

What do you think about enforcing this part of CWG 2789 only for
constructors in GCC 14?  That would sidestep the inconsistency that
arises if we don't also backport the CWG 2273, and still fix the
testcase in the PR.

Like so?

-- >8 --

Subject: [PATCH GCC 14] c++: CWG 2789 and usings [PR116492]

For GCC 14, narrowly fix this PR by enforcing the unimplemented

  - if they are member functions, both are direct members of the same
    class, and

part of CWG 2789 for constructors only.

	PR c++/116492
	DR 2789

gcc/cp/ChangeLog:

	* call.cc (cand_parms_match): Return false for constructors that
	come from different classes.

gcc/testsuite/ChangeLog:

	* g++.dg/cpp2a/concepts-inherit-ctor12.C: New test.
---
 gcc/cp/call.cc                                   |  4 ++++
 .../g++.dg/cpp2a/concepts-inherit-ctor12.C       | 16 ++++++++++++++++
 2 files changed, 20 insertions(+)
 create mode 100644 gcc/testsuite/g++.dg/cpp2a/concepts-inherit-ctor12.C

diff --git a/gcc/cp/call.cc b/gcc/cp/call.cc
index 0f4eeeb5395..5c7aaa6dcf1 100644
--- a/gcc/cp/call.cc
+++ b/gcc/cp/call.cc
@@ -12826,6 +12826,10 @@ cand_parms_match (z_candidate *c1, z_candidate *c2, pmatch match_kind)
 	&& DECL_FUNCTION_MEMBER_P (fn2)))
     /* Early escape.  */;
 
+  else if ((DECL_INHERITED_CTOR (fn1) || DECL_INHERITED_CTOR (fn2))
+	   && (DECL_CONTEXT (strip_inheriting_ctors (fn1))
+	       != DECL_CONTEXT (strip_inheriting_ctors (fn2))))
+    return false;
   /* CWG2789 is not adequate, it should specify corresponding object
      parameters, not same typed object parameters.  */
   else if (!object_parms_correspond (c1, fn1, c2, fn2))
diff --git a/gcc/testsuite/g++.dg/cpp2a/concepts-inherit-ctor12.C b/gcc/testsuite/g++.dg/cpp2a/concepts-inherit-ctor12.C
new file mode 100644
index 00000000000..3e5dbfc37ad
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp2a/concepts-inherit-ctor12.C
@@ -0,0 +1,16 @@
+// PR c++/116492
+// CWG 2789
+// { dg-do compile { target c++20 } }
+
+template<class T>
+struct A {
+  A() requires true = delete;
+};
+
+struct B : A<int> {
+  B();
+  using A<int>::A;
+};
+
+B b; // OK, selects the non-inherited constructor over the more constrained
+     // inherited constructor.
  
Jason Merrill Sept. 20, 2024, 5:23 p.m. UTC | #3
On 9/20/24 6:51 PM, Patrick Palka wrote:
> On Fri, 20 Sep 2024, Jason Merrill wrote:
> 
>> On 9/18/24 10:59 PM, Patrick Palka wrote:
>>> Bootstrapped and regtested on x86_64-pc-linux-gnu, does this
>>> look OK for trunk?  I'm not sure this is worth backporting
>>> without the previous CWG 2273 tweak since it'll mean inconsistent
>>> behavior between implict vs explicit object members in GCC 14: the call
>>> to S<>{}.f() in concepts-memfun4.C would now return 10 (due to the CWG
>>> 2273 tiebreaker incorrectly triggering), while the g() and h() calls
>>> would be ambiguous (since that tiebreaker doesn't consider object
>>> correspondence).
>>
>> Agreed, I wouldn't backport.
> 
> What do you think about enforcing this part of CWG 2789 only for
> constructors in GCC 14?  That would sidestep the inconsistency that
> arises if we don't also backport the CWG 2273, and still fix the
> testcase in the PR.
> 
> Like so?

That seems reasonable with a comment.

> -- >8 --
> 
> Subject: [PATCH GCC 14] c++: CWG 2789 and usings [PR116492]
> 
> For GCC 14, narrowly fix this PR by enforcing the unimplemented
> 
>    - if they are member functions, both are direct members of the same
>      class, and
> 
> part of CWG 2789 for constructors only.
> 
> 	PR c++/116492
> 	DR 2789
> 
> gcc/cp/ChangeLog:
> 
> 	* call.cc (cand_parms_match): Return false for constructors that
> 	come from different classes.
> 
> gcc/testsuite/ChangeLog:
> 
> 	* g++.dg/cpp2a/concepts-inherit-ctor12.C: New test.
> ---
>   gcc/cp/call.cc                                   |  4 ++++
>   .../g++.dg/cpp2a/concepts-inherit-ctor12.C       | 16 ++++++++++++++++
>   2 files changed, 20 insertions(+)
>   create mode 100644 gcc/testsuite/g++.dg/cpp2a/concepts-inherit-ctor12.C
> 
> diff --git a/gcc/cp/call.cc b/gcc/cp/call.cc
> index 0f4eeeb5395..5c7aaa6dcf1 100644
> --- a/gcc/cp/call.cc
> +++ b/gcc/cp/call.cc
> @@ -12826,6 +12826,10 @@ cand_parms_match (z_candidate *c1, z_candidate *c2, pmatch match_kind)
>   	&& DECL_FUNCTION_MEMBER_P (fn2)))
>       /* Early escape.  */;
>   
> +  else if ((DECL_INHERITED_CTOR (fn1) || DECL_INHERITED_CTOR (fn2))
> +	   && (DECL_CONTEXT (strip_inheriting_ctors (fn1))
> +	       != DECL_CONTEXT (strip_inheriting_ctors (fn2))))
> +    return false;
>     /* CWG2789 is not adequate, it should specify corresponding object
>        parameters, not same typed object parameters.  */
>     else if (!object_parms_correspond (c1, fn1, c2, fn2))
> diff --git a/gcc/testsuite/g++.dg/cpp2a/concepts-inherit-ctor12.C b/gcc/testsuite/g++.dg/cpp2a/concepts-inherit-ctor12.C
> new file mode 100644
> index 00000000000..3e5dbfc37ad
> --- /dev/null
> +++ b/gcc/testsuite/g++.dg/cpp2a/concepts-inherit-ctor12.C
> @@ -0,0 +1,16 @@
> +// PR c++/116492
> +// CWG 2789
> +// { dg-do compile { target c++20 } }
> +
> +template<class T>
> +struct A {
> +  A() requires true = delete;
> +};
> +
> +struct B : A<int> {
> +  B();
> +  using A<int>::A;
> +};
> +
> +B b; // OK, selects the non-inherited constructor over the more constrained
> +     // inherited constructor.
  
Patrick Palka Sept. 20, 2024, 5:34 p.m. UTC | #4
On Fri, 20 Sep 2024, Jason Merrill wrote:

> On 9/20/24 6:51 PM, Patrick Palka wrote:
> > On Fri, 20 Sep 2024, Jason Merrill wrote:
> > 
> > > On 9/18/24 10:59 PM, Patrick Palka wrote:
> > > > Bootstrapped and regtested on x86_64-pc-linux-gnu, does this
> > > > look OK for trunk?  I'm not sure this is worth backporting
> > > > without the previous CWG 2273 tweak since it'll mean inconsistent
> > > > behavior between implict vs explicit object members in GCC 14: the call
> > > > to S<>{}.f() in concepts-memfun4.C would now return 10 (due to the CWG
> > > > 2273 tiebreaker incorrectly triggering), while the g() and h() calls
> > > > would be ambiguous (since that tiebreaker doesn't consider object
> > > > correspondence).
> > > 
> > > Agreed, I wouldn't backport.
> > 
> > What do you think about enforcing this part of CWG 2789 only for
> > constructors in GCC 14?  That would sidestep the inconsistency that
> > arises if we don't also backport the CWG 2273, and still fix the
> > testcase in the PR.
> > 
> > Like so?
> 
> That seems reasonable with a comment.

Ack, does

    /* This really really be checked for all member functions as per
       CWG2789, but for GCC 14 we check this only for constructors since
       without r15-3740 doing so would result in inconsistent handling
       of object parameters (such as in concepts-memfun4.C). */

work?


> 
> > -- >8 --
> > 
> > Subject: [PATCH GCC 14] c++: CWG 2789 and usings [PR116492]
> > 
> > For GCC 14, narrowly fix this PR by enforcing the unimplemented
> > 
> >    - if they are member functions, both are direct members of the same
> >      class, and
> > 
> > part of CWG 2789 for constructors only.
> > 
> > 	PR c++/116492
> > 	DR 2789
> > 
> > gcc/cp/ChangeLog:
> > 
> > 	* call.cc (cand_parms_match): Return false for constructors that
> > 	come from different classes.
> > 
> > gcc/testsuite/ChangeLog:
> > 
> > 	* g++.dg/cpp2a/concepts-inherit-ctor12.C: New test.
> > ---
> >   gcc/cp/call.cc                                   |  4 ++++
> >   .../g++.dg/cpp2a/concepts-inherit-ctor12.C       | 16 ++++++++++++++++
> >   2 files changed, 20 insertions(+)
> >   create mode 100644 gcc/testsuite/g++.dg/cpp2a/concepts-inherit-ctor12.C
> > 
> > diff --git a/gcc/cp/call.cc b/gcc/cp/call.cc
> > index 0f4eeeb5395..5c7aaa6dcf1 100644
> > --- a/gcc/cp/call.cc
> > +++ b/gcc/cp/call.cc
> > @@ -12826,6 +12826,10 @@ cand_parms_match (z_candidate *c1, z_candidate *c2,
> > pmatch match_kind)
> >   	&& DECL_FUNCTION_MEMBER_P (fn2)))
> >       /* Early escape.  */;
> >   +  else if ((DECL_INHERITED_CTOR (fn1) || DECL_INHERITED_CTOR (fn2))
> > +	   && (DECL_CONTEXT (strip_inheriting_ctors (fn1))
> > +	       != DECL_CONTEXT (strip_inheriting_ctors (fn2))))
> > +    return false;
> >     /* CWG2789 is not adequate, it should specify corresponding object
> >        parameters, not same typed object parameters.  */
> >     else if (!object_parms_correspond (c1, fn1, c2, fn2))
> > diff --git a/gcc/testsuite/g++.dg/cpp2a/concepts-inherit-ctor12.C
> > b/gcc/testsuite/g++.dg/cpp2a/concepts-inherit-ctor12.C
> > new file mode 100644
> > index 00000000000..3e5dbfc37ad
> > --- /dev/null
> > +++ b/gcc/testsuite/g++.dg/cpp2a/concepts-inherit-ctor12.C
> > @@ -0,0 +1,16 @@
> > +// PR c++/116492
> > +// CWG 2789
> > +// { dg-do compile { target c++20 } }
> > +
> > +template<class T>
> > +struct A {
> > +  A() requires true = delete;
> > +};
> > +
> > +struct B : A<int> {
> > +  B();
> > +  using A<int>::A;
> > +};
> > +
> > +B b; // OK, selects the non-inherited constructor over the more constrained
> > +     // inherited constructor.
> 
>
  
Jason Merrill Sept. 20, 2024, 7:28 p.m. UTC | #5
On 9/20/24 7:34 PM, Patrick Palka wrote:
> On Fri, 20 Sep 2024, Jason Merrill wrote:
> 
>> On 9/20/24 6:51 PM, Patrick Palka wrote:
>>> On Fri, 20 Sep 2024, Jason Merrill wrote:
>>>
>>>> On 9/18/24 10:59 PM, Patrick Palka wrote:
>>>>> Bootstrapped and regtested on x86_64-pc-linux-gnu, does this
>>>>> look OK for trunk?  I'm not sure this is worth backporting
>>>>> without the previous CWG 2273 tweak since it'll mean inconsistent
>>>>> behavior between implict vs explicit object members in GCC 14: the call
>>>>> to S<>{}.f() in concepts-memfun4.C would now return 10 (due to the CWG
>>>>> 2273 tiebreaker incorrectly triggering), while the g() and h() calls
>>>>> would be ambiguous (since that tiebreaker doesn't consider object
>>>>> correspondence).
>>>>
>>>> Agreed, I wouldn't backport.
>>>
>>> What do you think about enforcing this part of CWG 2789 only for
>>> constructors in GCC 14?  That would sidestep the inconsistency that
>>> arises if we don't also backport the CWG 2273, and still fix the
>>> testcase in the PR.
>>>
>>> Like so?
>>
>> That seems reasonable with a comment.
> 
> Ack, does
> 
>      /* This really really be checked for all member functions as per
>         CWG2789, but for GCC 14 we check this only for constructors since
>         without r15-3740 doing so would result in inconsistent handling
>         of object parameters (such as in concepts-memfun4.C). */
> 
> work?

With "really really" fixed, yes.

>>> -- >8 --
>>>
>>> Subject: [PATCH GCC 14] c++: CWG 2789 and usings [PR116492]
>>>
>>> For GCC 14, narrowly fix this PR by enforcing the unimplemented
>>>
>>>     - if they are member functions, both are direct members of the same
>>>       class, and
>>>
>>> part of CWG 2789 for constructors only.
>>>
>>> 	PR c++/116492
>>> 	DR 2789
>>>
>>> gcc/cp/ChangeLog:
>>>
>>> 	* call.cc (cand_parms_match): Return false for constructors that
>>> 	come from different classes.
>>>
>>> gcc/testsuite/ChangeLog:
>>>
>>> 	* g++.dg/cpp2a/concepts-inherit-ctor12.C: New test.
>>> ---
>>>    gcc/cp/call.cc                                   |  4 ++++
>>>    .../g++.dg/cpp2a/concepts-inherit-ctor12.C       | 16 ++++++++++++++++
>>>    2 files changed, 20 insertions(+)
>>>    create mode 100644 gcc/testsuite/g++.dg/cpp2a/concepts-inherit-ctor12.C
>>>
>>> diff --git a/gcc/cp/call.cc b/gcc/cp/call.cc
>>> index 0f4eeeb5395..5c7aaa6dcf1 100644
>>> --- a/gcc/cp/call.cc
>>> +++ b/gcc/cp/call.cc
>>> @@ -12826,6 +12826,10 @@ cand_parms_match (z_candidate *c1, z_candidate *c2,
>>> pmatch match_kind)
>>>    	&& DECL_FUNCTION_MEMBER_P (fn2)))
>>>        /* Early escape.  */;
>>>    +  else if ((DECL_INHERITED_CTOR (fn1) || DECL_INHERITED_CTOR (fn2))
>>> +	   && (DECL_CONTEXT (strip_inheriting_ctors (fn1))
>>> +	       != DECL_CONTEXT (strip_inheriting_ctors (fn2))))
>>> +    return false;
>>>      /* CWG2789 is not adequate, it should specify corresponding object
>>>         parameters, not same typed object parameters.  */
>>>      else if (!object_parms_correspond (c1, fn1, c2, fn2))
>>> diff --git a/gcc/testsuite/g++.dg/cpp2a/concepts-inherit-ctor12.C
>>> b/gcc/testsuite/g++.dg/cpp2a/concepts-inherit-ctor12.C
>>> new file mode 100644
>>> index 00000000000..3e5dbfc37ad
>>> --- /dev/null
>>> +++ b/gcc/testsuite/g++.dg/cpp2a/concepts-inherit-ctor12.C
>>> @@ -0,0 +1,16 @@
>>> +// PR c++/116492
>>> +// CWG 2789
>>> +// { dg-do compile { target c++20 } }
>>> +
>>> +template<class T>
>>> +struct A {
>>> +  A() requires true = delete;
>>> +};
>>> +
>>> +struct B : A<int> {
>>> +  B();
>>> +  using A<int>::A;
>>> +};
>>> +
>>> +B b; // OK, selects the non-inherited constructor over the more constrained
>>> +     // inherited constructor.
>>
>>
>
  

Patch

diff --git a/gcc/cp/call.cc b/gcc/cp/call.cc
index 87b54291b51..de742da6927 100644
--- a/gcc/cp/call.cc
+++ b/gcc/cp/call.cc
@@ -12808,27 +12808,6 @@  class_of_implicit_object (z_candidate *cand)
   return BINFO_TYPE (cand->conversion_path);
 }
 
-/* True if candidates C1 and C2 have corresponding object parameters per
-   [basic.scope.scope].  */
-
-static bool
-object_parms_correspond (z_candidate *c1, tree fn1, z_candidate *c2, tree fn2)
-{
-  tree context = class_of_implicit_object (c1);
-  tree ctx2 = class_of_implicit_object (c2);
-  if (!ctx2)
-    /* Leave context as is. */;
-  else if (!context)
-    context = ctx2;
-  else if (context != ctx2)
-    /* This can't happen for normal function calls, since it means finding
-       functions in multiple bases which would fail with an ambiguous lookup,
-       but it can occur with reversed operators.  */
-    return false;
-
-  return object_parms_correspond (fn1, fn2, context);
-}
-
 /* Return whether the first parameter of C1 matches the second parameter
    of C2.  */
 
@@ -12893,16 +12872,19 @@  cand_parms_match (z_candidate *c1, z_candidate *c2, pmatch match_kind)
   tree parms1 = TYPE_ARG_TYPES (TREE_TYPE (fn1));
   tree parms2 = TYPE_ARG_TYPES (TREE_TYPE (fn2));
 
-  if (!(DECL_FUNCTION_MEMBER_P (fn1)
-	&& DECL_FUNCTION_MEMBER_P (fn2)))
-    /* Early escape.  */;
-
-  /* CWG2789 is not adequate, it should specify corresponding object
-     parameters, not same typed object parameters.  */
-  else if (!object_parms_correspond (c1, fn1, c2, fn2))
-    return false;
-  else
+  if (DECL_FUNCTION_MEMBER_P (fn1)
+      && DECL_FUNCTION_MEMBER_P (fn2))
     {
+      tree base1 = DECL_CONTEXT (strip_inheriting_ctors (fn1));
+      tree base2 = DECL_CONTEXT (strip_inheriting_ctors (fn2));
+      if (base1 != base2)
+	return false;
+
+      /* CWG2789 is not adequate, it should specify corresponding object
+	 parameters, not same typed object parameters.  */
+      if (!object_parms_correspond (fn1, fn2, base1))
+	return false;
+
       /* We just compared the object parameters, if they don't correspond
 	 we already returned false.  */
       auto skip_parms = [] (tree fn, tree parms)
@@ -13269,10 +13251,14 @@  joust (struct z_candidate *cand1, struct z_candidate *cand2, bool warn,
 	return winner;
     }
 
-  /* Concepts: F1 and F2 are non-template functions with the same
-     parameter-type-lists, and F1 is more constrained than F2 according to the
-     partial ordering of constraints described in 13.5.4.  */
-
+  /* F1 and F2 are non-template functions and
+     - they have the same non-object-parameter-type-lists ([dcl.fct]), and
+     - if they are member functions, both are direct members of the same
+       class, and
+     - if both are non-static member functions, they have the same types for
+       their object parameters, and
+     - F1 is more constrained than F2 according to the partial ordering of
+       constraints described in [temp.constr.order].  */
   if (flag_concepts && DECL_P (cand1->fn) && DECL_P (cand2->fn)
       && !cand1->template_decl && !cand2->template_decl
       && cand_parms_match (cand1, cand2, pmatch::current))
diff --git a/gcc/testsuite/g++.dg/cpp2a/concepts-inherit-ctor12.C b/gcc/testsuite/g++.dg/cpp2a/concepts-inherit-ctor12.C
new file mode 100644
index 00000000000..3e5dbfc37ad
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp2a/concepts-inherit-ctor12.C
@@ -0,0 +1,16 @@ 
+// PR c++/116492
+// CWG 2789
+// { dg-do compile { target c++20 } }
+
+template<class T>
+struct A {
+  A() requires true = delete;
+};
+
+struct B : A<int> {
+  B();
+  using A<int>::A;
+};
+
+B b; // OK, selects the non-inherited constructor over the more constrained
+     // inherited constructor.
diff --git a/gcc/testsuite/g++.dg/cpp2a/concepts-memfun4.C b/gcc/testsuite/g++.dg/cpp2a/concepts-memfun4.C
index 91e34f1cd7a..62e304b5322 100644
--- a/gcc/testsuite/g++.dg/cpp2a/concepts-memfun4.C
+++ b/gcc/testsuite/g++.dg/cpp2a/concepts-memfun4.C
@@ -1,5 +1,7 @@ 
 // PR c++/113191
-// { dg-do compile { target c++23 } }
+// CWG 2789
+// { dg-do compile { target c++20 } }
+// { dg-additional-options "-Wno-error=c++23-extensions" { target c++20_only } }
 
 template<typename> struct S;
 
@@ -8,6 +10,7 @@  struct B {
   constexpr int f() const requires true { return 5; }
   constexpr operator int () const requires true { return 5; }
   constexpr int g(this S<T>&&) requires true { return 5; }
+  // { dg-warning "explicit object" "" { target c++20_only } .-1 }
   constexpr int h() requires true { return 5; }
 };
 
@@ -20,12 +23,14 @@  struct S : B<> {
   constexpr operator int () const { return 10; }
   constexpr int g() { return 10; }
   constexpr int h(this S&&) { return 10; }
+  // { dg-warning "explicit object" "" { target c++20_only } .-1 }
 };
 
-// implicit object parms match, B::f is more constrained
-static_assert(S<>{}.f() == 5);
-static_assert(S<>{}.g() == 5);
-static_assert(S<>{}.h() == 5);
+// ambiguous, constraints aren't considered since the candidates
+// are defined from different classes
+static_assert(S<>{}.f() == 5); // { dg-error "ambiguous" }
+static_assert(S<>{}.g() == 5); // { dg-error "ambiguous" }
+static_assert(S<>{}.h() == 5); // { dg-error "ambiguous" }
 
 template <typename = void>
 struct C {
@@ -36,9 +41,8 @@  struct C {
 template <typename = void>
 struct S2: B<>, C<> { };
 
-// implicit object parms for conversion functions are all considered to be from
-// the class of the object argument
-static_assert(S2<>{} == 5);
+// ambiguous as above
+static_assert(S2<>{} == 5); // { dg-error "ambiguous" }
 
 // ambiguous lookup, so we never actually compare the candidates
 // if we did, implicit object parms don't match due to different classes
@@ -51,7 +55,6 @@  struct S3 : B<> {
   constexpr int f() volatile { return 10; }
 };
 
-// implicit object parms don't match due to different cv-quals
 static_assert(S3<>{}.f() == 5);	// { dg-error "ambiguous" }
 
 template <typename = void>
@@ -60,8 +63,7 @@  struct S4 : B<> {
   constexpr int f() const & { return 10; }
 };
 
-// no ref-qual matches any ref-qual
-static_assert(S4<>{}.f() == 5);
+static_assert(S4<>{}.f() == 5); // { dg-error "ambiguous" }
 
 template <typename = void>
 struct C2 {