Improve our handling of user-defined conversions when computing

implicit conversion sequences. In particular, model the "standard
conversion" from a class to its own type (or a base type) directly as
a standard conversion in the normal path *without* trying to determine
if there is a valid copy constructor. This appears to match the intent
of C++ [over.best.ics]p6 and more closely matches GCC and EDG.

As part of this, model non-lvalue reference initialization via
user-defined conversion in overloading the same way we handle it in
InitializationSequence, separating the "general user-defined
conversion" and "conversion to compatible class type" cases.

The churn in the overload-call-copycon.cpp test case is because the
test case was originally wrong; it assumed that we should do more
checking for copy constructors that we actually should, which affected
overload resolution.

Fixes PR7055. Bootstrapped okay.

llvm-svn: 110773
This commit is contained in:
Douglas Gregor 2010-08-11 02:15:33 +00:00
parent 8c9c9c77c8
commit 836a7e8468
5 changed files with 150 additions and 79 deletions

View File

@ -1212,7 +1212,7 @@ def note_ovl_candidate_bad_cvr_this : Note<"candidate "
"%select{|function|||function||||"
"function (the implicit copy assignment operator)}0 not viable: "
"'this' argument has type %2, but method is not marked "
"%select{const|volatile|const or volatile|restrict|const or restrict|"
"%select{const|restrict|const or restrict|volatile|const or volatile|"
"volatile or restrict|const, volatile, or restrict}3">;
def note_ovl_candidate_bad_cvr : Note<"candidate "
"%select{function|function|constructor|"
@ -1221,7 +1221,7 @@ def note_ovl_candidate_bad_cvr : Note<"candidate "
"constructor (the implicit copy constructor)|"
"function (the implicit copy assignment operator)}0%1 not viable: "
"%ordinal4 argument (%2) would lose "
"%select{const|volatile|const and volatile|restrict|const and restrict|"
"%select{const|restrict|const and restrict|volatile|const and volatile|"
"volatile and restrict|const, volatile, and restrict}3 qualifier"
"%select{||s||s|s|s}3">;
def note_ovl_candidate_bad_base_to_derived_conv : Note<"candidate "

View File

@ -685,24 +685,17 @@ Sema::TryImplicitConversion(Expr* From, QualType ToType,
return ICS;
}
if (SuppressUserConversions) {
// C++ [over.ics.user]p4:
// A conversion of an expression of class type to the same class
// type is given Exact Match rank, and a conversion of an
// expression of class type to a base class of that type is
// given Conversion rank, in spite of the fact that a copy/move
// constructor (i.e., a user-defined conversion function) is
// called for those cases.
QualType FromType = From->getType();
if (!ToType->getAs<RecordType>() || !FromType->getAs<RecordType>() ||
!(Context.hasSameUnqualifiedType(FromType, ToType) ||
IsDerivedFrom(FromType, ToType))) {
// We're not in the case above, so there is no conversion that
// we can perform.
ICS.setBad(BadConversionSequence::no_conversion, From, ToType);
return ICS;
}
// C++ [over.ics.user]p4:
// A conversion of an expression of class type to the same class
// type is given Exact Match rank, and a conversion of an
// expression of class type to a base class of that type is
// given Conversion rank, in spite of the fact that a copy/move
// constructor (i.e., a user-defined conversion function) is
// called for those cases.
QualType FromType = From->getType();
if (ToType->getAs<RecordType>() && FromType->getAs<RecordType>() &&
(Context.hasSameUnqualifiedType(FromType, ToType) ||
IsDerivedFrom(FromType, ToType))) {
ICS.setStandard();
ICS.Standard.setAsIdentityConversion();
ICS.Standard.setFromType(FromType);
@ -713,11 +706,18 @@ Sema::TryImplicitConversion(Expr* From, QualType ToType,
// exists. When we actually perform initialization, we'll find the
// appropriate constructor to copy the returned object, if needed.
ICS.Standard.CopyConstructor = 0;
// Determine whether this is considered a derived-to-base conversion.
if (!Context.hasSameUnqualifiedType(FromType, ToType))
ICS.Standard.Second = ICK_Derived_To_Base;
return ICS;
}
if (SuppressUserConversions) {
// We're not in the case above, so there is no conversion that
// we can perform.
ICS.setBad(BadConversionSequence::no_conversion, From, ToType);
return ICS;
}
@ -2630,16 +2630,21 @@ Sema::CompareReferenceRelationship(SourceLocation Loc,
return Ref_Related;
}
/// \brief Look for a user-defined conversion to an lvalue reference-compatible
/// \brief Look for a user-defined conversion to an value reference-compatible
/// with DeclType. Return true if something definite is found.
static bool
FindConversionToLValue(Sema &S, ImplicitConversionSequence &ICS,
QualType DeclType, SourceLocation DeclLoc,
Expr *Init, QualType T2, bool AllowExplicit) {
FindConversionForRefInit(Sema &S, ImplicitConversionSequence &ICS,
QualType DeclType, SourceLocation DeclLoc,
Expr *Init, QualType T2, bool AllowRvalues,
bool AllowExplicit) {
assert(T2->isRecordType() && "Can only find conversions of record types.");
CXXRecordDecl *T2RecordDecl
= dyn_cast<CXXRecordDecl>(T2->getAs<RecordType>()->getDecl());
QualType ToType
= AllowRvalues? DeclType->getAs<ReferenceType>()->getPointeeType()
: DeclType;
OverloadCandidateSet CandidateSet(DeclLoc);
const UnresolvedSetImpl *Conversions
= T2RecordDecl->getVisibleConversionFunctions();
@ -2658,21 +2663,40 @@ FindConversionToLValue(Sema &S, ImplicitConversionSequence &ICS,
else
Conv = cast<CXXConversionDecl>(D);
// If the conversion function doesn't return a reference type,
// it can't be considered for this conversion. An rvalue reference
// is only acceptable if its referencee is a function type.
const ReferenceType *RefType =
Conv->getConversionType()->getAs<ReferenceType>();
if (RefType && (RefType->isLValueReferenceType() ||
RefType->getPointeeType()->isFunctionType()) &&
(AllowExplicit || !Conv->isExplicit())) {
if (ConvTemplate)
S.AddTemplateConversionCandidate(ConvTemplate, I.getPair(), ActingDC,
Init, DeclType, CandidateSet);
else
S.AddConversionCandidate(Conv, I.getPair(), ActingDC, Init,
DeclType, CandidateSet);
// If this is an explicit conversion, and we're not allowed to consider
// explicit conversions, skip it.
if (!AllowExplicit && Conv->isExplicit())
continue;
if (AllowRvalues) {
bool DerivedToBase = false;
bool ObjCConversion = false;
if (!ConvTemplate &&
S.CompareReferenceRelationship(DeclLoc,
Conv->getConversionType().getNonReferenceType().getUnqualifiedType(),
DeclType.getNonReferenceType().getUnqualifiedType(),
DerivedToBase, ObjCConversion)
== Sema::Ref_Incompatible)
continue;
} else {
// If the conversion function doesn't return a reference type,
// it can't be considered for this conversion. An rvalue reference
// is only acceptable if its referencee is a function type.
const ReferenceType *RefType =
Conv->getConversionType()->getAs<ReferenceType>();
if (!RefType ||
(!RefType->isLValueReferenceType() &&
!RefType->getPointeeType()->isFunctionType()))
continue;
}
if (ConvTemplate)
S.AddTemplateConversionCandidate(ConvTemplate, I.getPair(), ActingDC,
Init, ToType, CandidateSet);
else
S.AddConversionCandidate(Conv, I.getPair(), ActingDC, Init,
ToType, CandidateSet);
}
OverloadCandidateSet::iterator Best;
@ -2808,8 +2832,9 @@ TryReferenceInit(Sema &S, Expr *&Init, QualType DeclType,
if (!SuppressUserConversions && T2->isRecordType() &&
!S.RequireCompleteType(DeclLoc, T2, 0) &&
RefRelationship == Sema::Ref_Incompatible) {
if (FindConversionToLValue(S, ICS, DeclType, DeclLoc,
Init, T2, AllowExplicit))
if (FindConversionForRefInit(S, ICS, DeclType, DeclLoc,
Init, T2, /*AllowRvalues=*/false,
AllowExplicit))
return ICS;
}
}
@ -2861,28 +2886,37 @@ TryReferenceInit(Sema &S, Expr *&Init, QualType DeclType,
// that is the result of the conversion in the second case
// (or, in either case, to the appropriate base class
// subobject of the object).
//
// We're only checking the first case here, which is a direct
// binding in C++0x but not in C++03.
if (InitCategory.isRValue() && T2->isRecordType() &&
RefRelationship >= Sema::Ref_Compatible_With_Added_Qualification) {
ICS.setStandard();
ICS.Standard.First = ICK_Identity;
ICS.Standard.Second = DerivedToBase? ICK_Derived_To_Base
: ObjCConversion? ICK_Compatible_Conversion
: ICK_Identity;
ICS.Standard.Third = ICK_Identity;
ICS.Standard.FromTypePtr = T2.getAsOpaquePtr();
ICS.Standard.setToType(0, T2);
ICS.Standard.setToType(1, T1);
ICS.Standard.setToType(2, T1);
ICS.Standard.ReferenceBinding = true;
ICS.Standard.DirectBinding = S.getLangOptions().CPlusPlus0x;
ICS.Standard.RRefBinding = isRValRef;
ICS.Standard.CopyConstructor = 0;
return ICS;
if (T2->isRecordType()) {
// First case: "cv1 T1" is reference-compatible with "cv2 T2". This is a
// direct binding in C++0x but not in C++03.
if (InitCategory.isRValue() &&
RefRelationship >= Sema::Ref_Compatible_With_Added_Qualification) {
ICS.setStandard();
ICS.Standard.First = ICK_Identity;
ICS.Standard.Second = DerivedToBase? ICK_Derived_To_Base
: ObjCConversion? ICK_Compatible_Conversion
: ICK_Identity;
ICS.Standard.Third = ICK_Identity;
ICS.Standard.FromTypePtr = T2.getAsOpaquePtr();
ICS.Standard.setToType(0, T2);
ICS.Standard.setToType(1, T1);
ICS.Standard.setToType(2, T1);
ICS.Standard.ReferenceBinding = true;
ICS.Standard.DirectBinding = S.getLangOptions().CPlusPlus0x;
ICS.Standard.RRefBinding = isRValRef;
ICS.Standard.CopyConstructor = 0;
return ICS;
}
// Second case: not reference-related.
if (RefRelationship == Sema::Ref_Incompatible &&
!S.RequireCompleteType(DeclLoc, T2, 0) &&
FindConversionForRefInit(S, ICS, DeclType, DeclLoc,
Init, T2, /*AllowRvalues=*/true,
AllowExplicit))
return ICS;
}
// -- Otherwise, a temporary of type "cv1 T1" is created and
// initialized from the initializer expression using the
// rules for a non-reference copy initialization (8.5). The

View File

@ -1,12 +1,11 @@
// RUN: %clang_cc1 -verify %s
struct S; // expected-note {{forward declaration of 'S'}}
struct S; // expected-note 2{{forward declaration of 'S'}}
extern S a;
extern S f(); // expected-note {{'f' declared here}}
extern void g(S a); // expected-note {{candidate function}}
extern void g(S a);
void h() {
// FIXME: This diagnostic could be better.
g(a); // expected-error {{no matching function for call to 'g'}}
g(a); // expected-error {{argument type 'S' is incomplete}}
f(); // expected-error {{calling 'f' with incomplete return type 'S'}}
}

View File

@ -214,3 +214,37 @@ struct Other {
void test_any() {
Any any = Other(); // expected-error{{cannot pass object of non-POD type 'Other' through variadic constructor; call will abort at runtime}}
}
namespace PR7055 {
// Make sure that we don't allow too many conversions in an
// auto_ptr-like template. In particular, we can't create multiple
// temporary objects when binding to a reference.
struct auto_ptr {
struct auto_ptr_ref { };
auto_ptr(auto_ptr&);
auto_ptr(auto_ptr_ref);
explicit auto_ptr(int *);
operator auto_ptr_ref();
};
struct X {
X(auto_ptr);
};
X f() {
X x(auto_ptr(new int));
return X(auto_ptr(new int));
}
auto_ptr foo();
X e(foo());
struct Y {
Y(X);
};
Y f2(foo());
}

View File

@ -1,40 +1,44 @@
// RUN: %clang_cc1 -fsyntax-only -verify %s -Wnon-pod-varargs
class X { };
class X { }; // expected-note {{the implicit copy constructor}} \
// expected-note{{the implicit default constructor}}
int& copycon(X x);
int& copycon(X x); // expected-note{{passing argument to parameter}}
float& copycon(...);
void test_copycon(X x, X const xc, X volatile xv) {
int& i1 = copycon(x);
int& i2 = copycon(xc);
float& f1 = copycon(xv);
copycon(xv); // expected-error{{no matching constructor}}
}
class A {
public:
A(A&);
A(A&); // expected-note{{would lose const qualifier}} \
// expected-note{{no known conversion}}
};
class B : public A { };
class B : public A { }; // expected-note{{would lose const qualifier}} \
// expected-note{{would lose volatile qualifier}} \
// expected-note 2{{requires 0 arguments}}
short& copycon2(A a);
int& copycon2(B b);
short& copycon2(A a); // expected-note{{passing argument to parameter}}
int& copycon2(B b); // expected-note 2{{passing argument to parameter}}
float& copycon2(...);
void test_copycon2(A a, const A ac, B b, B const bc, B volatile bv) {
int& i1 = copycon2(b);
float& f1 = copycon2(bc); // expected-warning {{cannot pass object of non-POD type}}
float& f2 = copycon2(bv); // expected-warning {{cannot pass object of non-POD type}}
copycon2(bc); // expected-error{{no matching constructor}}
copycon2(bv); // expected-error{{no matching constructor}}
short& s1 = copycon2(a);
float& f3 = copycon2(ac); // expected-warning {{cannot pass object of non-POD type}}
copycon2(ac); // expected-error{{no matching constructor}}
}
int& copycon3(A a);
int& copycon3(A a); // expected-note{{passing argument to parameter 'a' here}}
float& copycon3(...);
void test_copycon3(B b, const B bc) {
int& i1 = copycon3(b);
float& f1 = copycon3(bc); // expected-warning {{cannot pass object of non-POD type}}
copycon3(bc); // expected-error{{no matching constructor}}
}
class C : public B { };