PR17295: Do not allow explicit conversion functions to be used in cases where

an additional conversion (other than a qualification conversion) would be
required after the explicit conversion.

Conversely, do allow explicit conversion functions to be used when initializing
a temporary for a reference binding in direct-list-initialization.

llvm-svn: 191150
This commit is contained in:
Richard Smith 2013-09-21 21:55:46 +00:00
parent 4e9c091dd7
commit 089c31637f
5 changed files with 127 additions and 8 deletions

View File

@ -853,6 +853,10 @@ public:
const InitializationKind &Kind,
MultiExprArg Args,
bool InInitList = false);
void InitializeFrom(Sema &S, const InitializedEntity &Entity,
const InitializationKind &Kind, MultiExprArg Args,
bool InInitList);
~InitializationSequence();
/// \brief Perform the actual initialization of the given entity based on

View File

@ -3346,6 +3346,33 @@ static void TryListInitialization(Sema &S,
return;
}
}
if (S.getLangOpts().CPlusPlus && !DestType->isAggregateType() &&
InitList->getNumInits() == 1 &&
InitList->getInit(0)->getType()->isRecordType()) {
// - Otherwise, if the initializer list has a single element of type E
// [...references are handled above...], the object or reference is
// initialized from that element; if a narrowing conversion is required
// to convert the element to T, the program is ill-formed.
//
// Per core-24034, this is direct-initialization if we were performing
// direct-list-initialization and copy-initialization otherwise.
// We can't use InitListChecker for this, because it always performs
// copy-initialization. This only matters if we might use an 'explicit'
// conversion operator, so we only need to handle the cases where the source
// is of record type.
InitializationKind SubKind =
Kind.getKind() == InitializationKind::IK_DirectList
? InitializationKind::CreateDirect(Kind.getLocation(),
InitList->getLBraceLoc(),
InitList->getRBraceLoc())
: Kind;
Expr *SubInit[1] = { InitList->getInit(0) };
Sequence.InitializeFrom(S, Entity, SubKind, SubInit,
/*TopLevelOfInitList*/true);
if (Sequence)
Sequence.RewrapReferenceInitList(Entity.getType(), InitList);
return;
}
InitListChecker CheckInitList(S, Entity, InitList,
DestType, /*VerifyOnly=*/true);
@ -4366,6 +4393,14 @@ InitializationSequence::InitializationSequence(Sema &S,
MultiExprArg Args,
bool TopLevelOfInitList)
: FailedCandidateSet(Kind.getLocation()) {
InitializeFrom(S, Entity, Kind, Args, TopLevelOfInitList);
}
void InitializationSequence::InitializeFrom(Sema &S,
const InitializedEntity &Entity,
const InitializationKind &Kind,
MultiExprArg Args,
bool TopLevelOfInitList) {
ASTContext &Context = S.Context;
// Eliminate non-overload placeholder types in the arguments. We

View File

@ -5829,6 +5829,17 @@ Sema::AddConversionCandidate(CXXConversionDecl *Conversion,
ConvType = Conversion->getConversionType().getNonReferenceType();
}
// Per C++ [over.match.conv]p1, [over.match.ref]p1, an explicit conversion
// operator is only a candidate if its return type is the target type or
// can be converted to the target type with a qualification conversion.
bool ObjCLifetimeConversion;
QualType ToNonRefType = ToType.getNonReferenceType();
if (Conversion->isExplicit() &&
!Context.hasSameUnqualifiedType(ConvType, ToNonRefType) &&
!IsQualificationConversion(ConvType, ToNonRefType, /*CStyle*/false,
ObjCLifetimeConversion))
return;
// Overload resolution is always an unevaluated context.
EnterExpressionEvaluationContext Unevaluated(*this, Sema::Unevaluated);

View File

@ -3372,7 +3372,9 @@ void Sema::BuildVariableInstantiation(
OldVar->isPreviousDeclInSameBlockScope());
NewVar->setAccess(OldVar->getAccess());
if (!OldVar->isStaticDataMember()) {
// For local variables, inherit the 'used' and 'referenced' flags from the
// primary template.
if (OldVar->getLexicalDeclContext()->isFunctionOrMethod()) {
NewVar->setIsUsed(OldVar->isUsed(false));
NewVar->setReferenced(OldVar->isReferenced());
}

View File

@ -4,11 +4,11 @@ struct A {
A(int);
};
struct B {
struct B { // expected-note+ {{candidate}}
explicit B(int);
};
B::B(int) { }
B::B(int) { } // expected-note+ {{here}}
struct C {
void f(const A&);
@ -18,6 +18,22 @@ struct C {
void f(C c) {
c.f(10);
}
A a0 = 0;
A a1(0);
A &&a2 = 0;
A &&a3(0);
A a4{0};
A &&a5 = {0};
A &&a6{0};
B b0 = 0; // expected-error {{no viable conversion}}
B b1(0);
B &&b2 = 0; // expected-error {{could not bind}}
B &&b3(0); // expected-error {{could not bind}}
B b4{0};
B &&b5 = {0}; // expected-error {{chosen constructor is explicit}}
B &&b6{0};
}
namespace Conversion {
@ -40,12 +56,11 @@ namespace Conversion {
void testExplicit()
{
// Taken from 12.3.2p2
class Y { }; // expected-note {{candidate constructor (the implicit copy constructor) not viable: no known conversion from 'Z' to 'const Y &' for 1st argument}} \
expected-note {{candidate constructor (the implicit move constructor) not viable: no known conversion from 'Z' to 'Y &&' for 1st argument}} \
expected-note {{candidate constructor (the implicit copy constructor) not viable: no known conversion from 'Z' to 'const Y &' for 1st argument}} \
expected-note {{candidate constructor (the implicit move constructor) not viable: no known conversion from 'Z' to 'Y &&' for 1st argument}}
class X { X(); }; // expected-note+ {{candidate constructor}}
class Y { }; // expected-note+ {{candidate constructor (the implicit}}
struct Z {
explicit operator X() const;
explicit operator Y() const;
explicit operator int() const;
};
@ -53,6 +68,7 @@ namespace Conversion {
Z z;
// 13.3.1.4p1 & 8.5p16:
Y y2 = z; // expected-error {{no viable conversion from 'Z' to 'Y'}}
Y y2b(z);
Y y3 = (Y)z;
Y y4 = Y(z);
Y y5 = static_cast<Y>(z);
@ -63,7 +79,24 @@ namespace Conversion {
int i4(z);
// 13.3.1.6p1 & 8.5.3p5:
const Y& y6 = z; // expected-error {{no viable conversion from 'Z' to 'const Y'}}
const int& y7(z);
const int& y7 = z; // expected-error {{no viable conversion from 'Z' to 'const int'}}
const Y& y8(z);
const int& y9(z);
// Y is an aggregate, so aggregate-initialization is performed and the
// conversion function is not considered.
const Y y10{z}; // expected-error {{excess elements}}
const Y& y11{z}; // expected-error {{no viable conversion from 'Z' to 'const Y'}}
const int& y12{z};
// X is not an aggregate, so constructors are considered.
// However, by 13.3.3.1/4, only standard conversion sequences and
// ellipsis conversion sequences are considered here, so this is not
// allowed.
// FIXME: It's not really clear that this is a sensible restriction for this
// case. g++ allows this, EDG does not.
const X x1{z}; // expected-error {{no matching constructor}}
const X& x2{z}; // expected-error {{no matching constructor}}
}
void testBool() {
@ -119,6 +152,40 @@ namespace Conversion {
// 6.5.3:
for (;b;) {}
for (;n;) {}
// 13.3.1.5p1:
bool direct1(b);
bool direct2(n);
int direct3(b);
int direct4(n); // expected-error {{no viable conversion}}
const bool &direct5(b);
const bool &direct6(n);
const int &direct7(b);
const int &direct8(n); // expected-error {{no viable conversion}}
bool directList1{b};
bool directList2{n};
int directList3{b};
int directList4{n}; // expected-error {{no viable conversion}}
const bool &directList5{b};
const bool &directList6{n};
const int &directList7{b};
const int &directList8{n}; // expected-error {{no viable conversion}}
bool copy1 = b;
bool copy2 = n; // expected-error {{no viable conversion}}
int copy3 = b;
int copy4 = n; // expected-error {{no viable conversion}}
const bool &copy5 = b;
const bool &copy6 = n; // expected-error {{no viable conversion}}
const int &copy7 = b;
const int &copy8 = n; // expected-error {{no viable conversion}}
bool copyList1 = {b};
bool copyList2 = {n}; // expected-error {{no viable conversion}}
int copyList3 = {b};
int copyList4 = {n}; // expected-error {{no viable conversion}}
const bool &copyList5 = {b};
const bool &copyList6 = {n}; // expected-error {{no viable conversion}}
const int &copyList7 = {b};
const int &copyList8 = {n}; // expected-error {{no viable conversion}}
}
void testNew()