mirror of git://gcc.gnu.org/git/gcc.git
c++: CWG 2789 and usings [PR116492]
After CWG 2789, the "more constrained" tiebreaker for non-template functions should exclude member functions that are defined in different classes. This patch implements this missing refinement. In turn we can get rid of four-parameter version of object_parms_correspond and call the main overload directly since now correspondence is only 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: Also compile in C++20 mode. Expect ambiguity when candidates come from different classes. * g++.dg/cpp2a/concepts-inherit-ctor12.C: New test. Reviewed-by: Jason Merrill <jason@redhat.com>
This commit is contained in:
parent
06557ba12b
commit
ee3efe06c9
|
|
@ -12808,27 +12808,6 @@ class_of_implicit_object (z_candidate *cand)
|
||||||
return BINFO_TYPE (cand->conversion_path);
|
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
|
/* Return whether the first parameter of C1 matches the second parameter
|
||||||
of C2. */
|
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 parms1 = TYPE_ARG_TYPES (TREE_TYPE (fn1));
|
||||||
tree parms2 = TYPE_ARG_TYPES (TREE_TYPE (fn2));
|
tree parms2 = TYPE_ARG_TYPES (TREE_TYPE (fn2));
|
||||||
|
|
||||||
if (!(DECL_FUNCTION_MEMBER_P (fn1)
|
if (DECL_FUNCTION_MEMBER_P (fn1)
|
||||||
&& DECL_FUNCTION_MEMBER_P (fn2)))
|
&& 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
|
|
||||||
{
|
{
|
||||||
|
tree base1 = DECL_CONTEXT (strip_inheriting_ctors (fn1));
|
||||||
|
tree base2 = DECL_CONTEXT (strip_inheriting_ctors (fn2));
|
||||||
|
if (base1 != base2)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
/* Use object_parms_correspond to simplify comparing iobj/xobj/static
|
||||||
|
member functions. */
|
||||||
|
if (!object_parms_correspond (fn1, fn2, base1))
|
||||||
|
return false;
|
||||||
|
|
||||||
/* We just compared the object parameters, if they don't correspond
|
/* We just compared the object parameters, if they don't correspond
|
||||||
we already returned false. */
|
we already returned false. */
|
||||||
auto skip_parms = [] (tree fn, tree parms)
|
auto skip_parms = [] (tree fn, tree parms)
|
||||||
|
|
@ -13269,10 +13251,14 @@ joust (struct z_candidate *cand1, struct z_candidate *cand2, bool warn,
|
||||||
return winner;
|
return winner;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Concepts: F1 and F2 are non-template functions with the same
|
/* F1 and F2 are non-template functions and
|
||||||
parameter-type-lists, and F1 is more constrained than F2 according to the
|
- they have the same non-object-parameter-type-lists ([dcl.fct]), and
|
||||||
partial ordering of constraints described in 13.5.4. */
|
- 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)
|
if (flag_concepts && DECL_P (cand1->fn) && DECL_P (cand2->fn)
|
||||||
&& !cand1->template_decl && !cand2->template_decl
|
&& !cand1->template_decl && !cand2->template_decl
|
||||||
&& cand_parms_match (cand1, cand2, pmatch::current))
|
&& cand_parms_match (cand1, cand2, pmatch::current))
|
||||||
|
|
|
||||||
|
|
@ -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.
|
||||||
|
|
@ -1,5 +1,7 @@
|
||||||
// PR c++/113191
|
// 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;
|
template<typename> struct S;
|
||||||
|
|
||||||
|
|
@ -8,6 +10,7 @@ struct B {
|
||||||
constexpr int f() const requires true { return 5; }
|
constexpr int f() const requires true { return 5; }
|
||||||
constexpr operator int () const requires true { return 5; }
|
constexpr operator int () const requires true { return 5; }
|
||||||
constexpr int g(this S<T>&&) 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; }
|
constexpr int h() requires true { return 5; }
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -20,12 +23,14 @@ struct S : B<> {
|
||||||
constexpr operator int () const { return 10; }
|
constexpr operator int () const { return 10; }
|
||||||
constexpr int g() { return 10; }
|
constexpr int g() { return 10; }
|
||||||
constexpr int h(this S&&) { 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
|
// ambiguous, constraints aren't considered since the candidates
|
||||||
static_assert(S<>{}.f() == 5);
|
// come from different classes
|
||||||
static_assert(S<>{}.g() == 5);
|
static_assert(S<>{}.f() == 5); // { dg-error "ambiguous" }
|
||||||
static_assert(S<>{}.h() == 5);
|
static_assert(S<>{}.g() == 5); // { dg-error "ambiguous" }
|
||||||
|
static_assert(S<>{}.h() == 5); // { dg-error "ambiguous" }
|
||||||
|
|
||||||
template <typename = void>
|
template <typename = void>
|
||||||
struct C {
|
struct C {
|
||||||
|
|
@ -36,9 +41,8 @@ struct C {
|
||||||
template <typename = void>
|
template <typename = void>
|
||||||
struct S2: B<>, C<> { };
|
struct S2: B<>, C<> { };
|
||||||
|
|
||||||
// implicit object parms for conversion functions are all considered to be from
|
// ambiguous as above
|
||||||
// the class of the object argument
|
static_assert(S2<>{} == 5); // { dg-error "ambiguous" }
|
||||||
static_assert(S2<>{} == 5);
|
|
||||||
|
|
||||||
// ambiguous lookup, so we never actually compare the candidates
|
// ambiguous lookup, so we never actually compare the candidates
|
||||||
// if we did, implicit object parms don't match due to different classes
|
// 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; }
|
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" }
|
static_assert(S3<>{}.f() == 5); // { dg-error "ambiguous" }
|
||||||
|
|
||||||
template <typename = void>
|
template <typename = void>
|
||||||
|
|
@ -60,8 +63,7 @@ struct S4 : B<> {
|
||||||
constexpr int f() const & { return 10; }
|
constexpr int f() const & { return 10; }
|
||||||
};
|
};
|
||||||
|
|
||||||
// no ref-qual matches any ref-qual
|
static_assert(S4<>{}.f() == 5); // { dg-error "ambiguous" }
|
||||||
static_assert(S4<>{}.f() == 5);
|
|
||||||
|
|
||||||
template <typename = void>
|
template <typename = void>
|
||||||
struct C2 {
|
struct C2 {
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue