forked from OSchip/llvm-project
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:
parent
4e9c091dd7
commit
089c31637f
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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());
|
||||
}
|
||||
|
|
|
@ -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 ©5 = b;
|
||||
const bool ©6 = n; // expected-error {{no viable conversion}}
|
||||
const int ©7 = b;
|
||||
const int ©8 = 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 ©List5 = {b};
|
||||
const bool ©List6 = {n}; // expected-error {{no viable conversion}}
|
||||
const int ©List7 = {b};
|
||||
const int ©List8 = {n}; // expected-error {{no viable conversion}}
|
||||
}
|
||||
|
||||
void testNew()
|
||||
|
|
Loading…
Reference in New Issue