forked from OSchip/llvm-project
[c++17] P0135R1: Guaranteed copy elision.
When an object of class type is initialized from a prvalue of the same type (ignoring cv qualifications), use the prvalue to initialize the object directly instead of inserting a redundant elidable call to a copy constructor. llvm-svn: 288866
This commit is contained in:
parent
2187bb8a89
commit
122f88d481
|
@ -3786,7 +3786,7 @@ public:
|
|||
|
||||
/// \brief Build an empty initializer list.
|
||||
explicit InitListExpr(EmptyShell Empty)
|
||||
: Expr(InitListExprClass, Empty) { }
|
||||
: Expr(InitListExprClass, Empty), AltForm(nullptr, true) { }
|
||||
|
||||
unsigned getNumInits() const { return InitExprs.size(); }
|
||||
|
||||
|
@ -3894,6 +3894,11 @@ public:
|
|||
// literal or an @encode?
|
||||
bool isStringLiteralInit() const;
|
||||
|
||||
/// Is this a transparent initializer list (that is, an InitListExpr that is
|
||||
/// purely syntactic, and whose semantics are that of the sole contained
|
||||
/// initializer)?
|
||||
bool isTransparent() const;
|
||||
|
||||
SourceLocation getLBraceLoc() const { return LBraceLoc; }
|
||||
void setLBraceLoc(SourceLocation Loc) { LBraceLoc = Loc; }
|
||||
SourceLocation getRBraceLoc() const { return RBraceLoc; }
|
||||
|
|
|
@ -1864,6 +1864,24 @@ bool InitListExpr::isStringLiteralInit() const {
|
|||
return isa<StringLiteral>(Init) || isa<ObjCEncodeExpr>(Init);
|
||||
}
|
||||
|
||||
bool InitListExpr::isTransparent() const {
|
||||
assert(isSemanticForm() && "syntactic form never semantically transparent");
|
||||
|
||||
// A glvalue InitListExpr is always just sugar.
|
||||
if (isGLValue()) {
|
||||
assert(getNumInits() == 1 && "multiple inits in glvalue init list");
|
||||
return true;
|
||||
}
|
||||
|
||||
// Otherwise, we're sugar if and only if we have exactly one initializer that
|
||||
// is of the same type.
|
||||
if (getNumInits() != 1 || !getInit(0))
|
||||
return false;
|
||||
|
||||
return getType().getCanonicalType() ==
|
||||
getInit(0)->getType().getCanonicalType();
|
||||
}
|
||||
|
||||
SourceLocation InitListExpr::getLocStart() const {
|
||||
if (InitListExpr *SyntacticForm = getSyntacticForm())
|
||||
return SyntacticForm->getLocStart();
|
||||
|
@ -2246,12 +2264,15 @@ bool Expr::isUnusedResultAWarning(const Expr *&WarnE, SourceLocation &Loc,
|
|||
// effects (e.g. a placement new with an uninitialized POD).
|
||||
case CXXDeleteExprClass:
|
||||
return false;
|
||||
case MaterializeTemporaryExprClass:
|
||||
return cast<MaterializeTemporaryExpr>(this)->GetTemporaryExpr()
|
||||
->isUnusedResultAWarning(WarnE, Loc, R1, R2, Ctx);
|
||||
case CXXBindTemporaryExprClass:
|
||||
return (cast<CXXBindTemporaryExpr>(this)
|
||||
->getSubExpr()->isUnusedResultAWarning(WarnE, Loc, R1, R2, Ctx));
|
||||
return cast<CXXBindTemporaryExpr>(this)->getSubExpr()
|
||||
->isUnusedResultAWarning(WarnE, Loc, R1, R2, Ctx);
|
||||
case ExprWithCleanupsClass:
|
||||
return (cast<ExprWithCleanups>(this)
|
||||
->getSubExpr()->isUnusedResultAWarning(WarnE, Loc, R1, R2, Ctx));
|
||||
return cast<ExprWithCleanups>(this)->getSubExpr()
|
||||
->isUnusedResultAWarning(WarnE, Loc, R1, R2, Ctx);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -5670,6 +5670,9 @@ bool RecordExprEvaluator::VisitCastExpr(const CastExpr *E) {
|
|||
}
|
||||
|
||||
bool RecordExprEvaluator::VisitInitListExpr(const InitListExpr *E) {
|
||||
if (E->isTransparent())
|
||||
return Visit(E->getInit(0));
|
||||
|
||||
const RecordDecl *RD = E->getType()->castAs<RecordType>()->getDecl();
|
||||
if (RD->isInvalidDecl()) return false;
|
||||
const ASTRecordLayout &Layout = Info.Ctx.getASTRecordLayout(RD);
|
||||
|
|
|
@ -3522,7 +3522,7 @@ LValue CodeGenFunction::EmitInitListLValue(const InitListExpr *E) {
|
|||
return EmitAggExprToLValue(E);
|
||||
|
||||
// An lvalue initializer list must be initializing a reference.
|
||||
assert(E->getNumInits() == 1 && "reference init with multiple values");
|
||||
assert(E->isTransparent() && "non-transparent glvalue init list");
|
||||
return EmitLValue(E->getInit(0));
|
||||
}
|
||||
|
||||
|
|
|
@ -1145,15 +1145,15 @@ void AggExprEmitter::VisitInitListExpr(InitListExpr *E) {
|
|||
if (E->hadArrayRangeDesignator())
|
||||
CGF.ErrorUnsupported(E, "GNU array range designator extension");
|
||||
|
||||
if (E->isTransparent())
|
||||
return Visit(E->getInit(0));
|
||||
|
||||
AggValueSlot Dest = EnsureSlot(E->getType());
|
||||
|
||||
LValue DestLV = CGF.MakeAddrLValue(Dest.getAddress(), E->getType());
|
||||
|
||||
// Handle initialization of an array.
|
||||
if (E->getType()->isArrayType()) {
|
||||
if (E->isStringLiteralInit())
|
||||
return Visit(E->getInit(0));
|
||||
|
||||
QualType elementType =
|
||||
CGF.getContext().getAsArrayType(E->getType())->getElementType();
|
||||
|
||||
|
@ -1162,16 +1162,6 @@ void AggExprEmitter::VisitInitListExpr(InitListExpr *E) {
|
|||
return;
|
||||
}
|
||||
|
||||
if (E->getType()->isAtomicType()) {
|
||||
// An _Atomic(T) object can be list-initialized from an expression
|
||||
// of the same type.
|
||||
assert(E->getNumInits() == 1 &&
|
||||
CGF.getContext().hasSameUnqualifiedType(E->getInit(0)->getType(),
|
||||
E->getType()) &&
|
||||
"unexpected list initialization for atomic object");
|
||||
return Visit(E->getInit(0));
|
||||
}
|
||||
|
||||
assert(E->getType()->isRecordType() && "Only support structs/unions here!");
|
||||
|
||||
// Do struct initialization; this code just sets each individual member
|
||||
|
|
|
@ -778,9 +778,6 @@ public:
|
|||
}
|
||||
|
||||
llvm::Constant *EmitArrayInitialization(InitListExpr *ILE) {
|
||||
if (ILE->isStringLiteralInit())
|
||||
return Visit(ILE->getInit(0));
|
||||
|
||||
llvm::ArrayType *AType =
|
||||
cast<llvm::ArrayType>(ConvertType(ILE->getType()));
|
||||
llvm::Type *ElemTy = AType->getElementType();
|
||||
|
@ -845,6 +842,9 @@ public:
|
|||
}
|
||||
|
||||
llvm::Constant *VisitInitListExpr(InitListExpr *ILE) {
|
||||
if (ILE->isTransparent())
|
||||
return Visit(ILE->getInit(0));
|
||||
|
||||
if (ILE->getType()->isArrayType())
|
||||
return EmitArrayInitialization(ILE);
|
||||
|
||||
|
|
|
@ -6836,6 +6836,16 @@ ExprResult Sema::IgnoredValueConversions(Expr *E) {
|
|||
return E;
|
||||
E = Res.get();
|
||||
}
|
||||
|
||||
// C++1z:
|
||||
// If the expression is a prvalue after this optional conversion, the
|
||||
// temporary materialization conversion is applied.
|
||||
//
|
||||
// We skip this step: IR generation is able to synthesize the storage for
|
||||
// itself in the aggregate case, and adding the extra node to the AST is
|
||||
// just clutter.
|
||||
// FIXME: We don't emit lifetime markers for the temporaries due to this.
|
||||
// FIXME: Do any other AST consumers care about this?
|
||||
return E;
|
||||
}
|
||||
|
||||
|
|
|
@ -3546,8 +3546,14 @@ static void TryConstructorInitialization(Sema &S,
|
|||
InitializationSequence &Sequence,
|
||||
bool IsListInit = false,
|
||||
bool IsInitListCopy = false) {
|
||||
assert((!IsListInit || (Args.size() == 1 && isa<InitListExpr>(Args[0]))) &&
|
||||
"IsListInit must come with a single initializer list argument.");
|
||||
assert(((!IsListInit && !IsInitListCopy) ||
|
||||
(Args.size() == 1 && isa<InitListExpr>(Args[0]))) &&
|
||||
"IsListInit/IsInitListCopy must come with a single initializer list "
|
||||
"argument.");
|
||||
InitListExpr *ILE =
|
||||
(IsListInit || IsInitListCopy) ? cast<InitListExpr>(Args[0]) : nullptr;
|
||||
MultiExprArg UnwrappedArgs =
|
||||
ILE ? MultiExprArg(ILE->getInits(), ILE->getNumInits()) : Args;
|
||||
|
||||
// The type we're constructing needs to be complete.
|
||||
if (!S.isCompleteType(Kind.getLocation(), DestType)) {
|
||||
|
@ -3555,6 +3561,35 @@ static void TryConstructorInitialization(Sema &S,
|
|||
return;
|
||||
}
|
||||
|
||||
// C++1z [dcl.init]p17:
|
||||
// - If the initializer expression is a prvalue and the cv-unqualified
|
||||
// version of the source type is the same class as the class of the
|
||||
// destination, the initializer expression is used to initialize the
|
||||
// destination object.
|
||||
// Per DR (no number yet), this does not apply when initializing a base
|
||||
// class or delegating to another constructor from a mem-initializer.
|
||||
if (S.getLangOpts().CPlusPlus1z &&
|
||||
Entity.getKind() != InitializedEntity::EK_Base &&
|
||||
Entity.getKind() != InitializedEntity::EK_Delegating &&
|
||||
UnwrappedArgs.size() == 1 && UnwrappedArgs[0]->isRValue() &&
|
||||
S.Context.hasSameUnqualifiedType(UnwrappedArgs[0]->getType(), DestType)) {
|
||||
// Convert qualifications if necessary.
|
||||
QualType InitType = UnwrappedArgs[0]->getType();
|
||||
ImplicitConversionSequence ICS;
|
||||
ICS.setStandard();
|
||||
ICS.Standard.setAsIdentityConversion();
|
||||
ICS.Standard.setFromType(InitType);
|
||||
ICS.Standard.setAllToTypes(InitType);
|
||||
if (!S.Context.hasSameType(InitType, DestType)) {
|
||||
ICS.Standard.Third = ICK_Qualification;
|
||||
ICS.Standard.setToType(2, DestType);
|
||||
}
|
||||
Sequence.AddConversionSequenceStep(ICS, DestType);
|
||||
if (ILE)
|
||||
Sequence.RewrapReferenceInitList(DestType, ILE);
|
||||
return;
|
||||
}
|
||||
|
||||
const RecordType *DestRecordType = DestType->getAs<RecordType>();
|
||||
assert(DestRecordType && "Constructor initialization requires record type");
|
||||
CXXRecordDecl *DestRecordDecl
|
||||
|
@ -3588,20 +3623,16 @@ static void TryConstructorInitialization(Sema &S,
|
|||
// constructors of the class T and the argument list consists of the
|
||||
// initializer list as a single argument.
|
||||
if (IsListInit) {
|
||||
InitListExpr *ILE = cast<InitListExpr>(Args[0]);
|
||||
AsInitializerList = true;
|
||||
|
||||
// If the initializer list has no elements and T has a default constructor,
|
||||
// the first phase is omitted.
|
||||
if (ILE->getNumInits() != 0 || !DestRecordDecl->hasDefaultConstructor())
|
||||
if (!(UnwrappedArgs.empty() && DestRecordDecl->hasDefaultConstructor()))
|
||||
Result = ResolveConstructorOverload(S, Kind.getLocation(), Args,
|
||||
CandidateSet, Ctors, Best,
|
||||
CopyInitialization, AllowExplicit,
|
||||
/*OnlyListConstructor=*/true,
|
||||
IsListInit);
|
||||
|
||||
// Time to unwrap the init list.
|
||||
Args = MultiExprArg(ILE->getInits(), ILE->getNumInits());
|
||||
}
|
||||
|
||||
// C++11 [over.match.list]p1:
|
||||
|
@ -3611,7 +3642,7 @@ static void TryConstructorInitialization(Sema &S,
|
|||
// elements of the initializer list.
|
||||
if (Result == OR_No_Viable_Function) {
|
||||
AsInitializerList = false;
|
||||
Result = ResolveConstructorOverload(S, Kind.getLocation(), Args,
|
||||
Result = ResolveConstructorOverload(S, Kind.getLocation(), UnwrappedArgs,
|
||||
CandidateSet, Ctors, Best,
|
||||
CopyInitialization, AllowExplicit,
|
||||
/*OnlyListConstructors=*/false,
|
||||
|
@ -3821,8 +3852,8 @@ static void TryListInitialization(Sema &S,
|
|||
QualType InitType = InitList->getInit(0)->getType();
|
||||
if (S.Context.hasSameUnqualifiedType(InitType, DestType) ||
|
||||
S.IsDerivedFrom(InitList->getLocStart(), InitType, DestType)) {
|
||||
Expr *InitAsExpr = InitList->getInit(0);
|
||||
TryConstructorInitialization(S, Entity, Kind, InitAsExpr, DestType,
|
||||
Expr *InitListAsExpr = InitList;
|
||||
TryConstructorInitialization(S, Entity, Kind, InitListAsExpr, DestType,
|
||||
Sequence, /*InitListSyntax*/ false,
|
||||
/*IsInitListCopy*/ true);
|
||||
return;
|
||||
|
@ -4332,16 +4363,21 @@ static void TryReferenceInitializationCore(Sema &S,
|
|||
}
|
||||
|
||||
// - If the initializer expression
|
||||
// C++14-and-before:
|
||||
// - is an xvalue, class prvalue, array prvalue, or function lvalue and
|
||||
// "cv1 T1" is reference-compatible with "cv2 T2"
|
||||
// C++1z:
|
||||
// - is an rvalue or function lvalue and "cv1 T1" is reference-compatible
|
||||
// with "cv2 T2"
|
||||
// Note: functions are handled below.
|
||||
if (!T1Function &&
|
||||
(RefRelationship == Sema::Ref_Compatible ||
|
||||
(Kind.isCStyleOrFunctionalCast() &&
|
||||
RefRelationship == Sema::Ref_Related)) &&
|
||||
(InitCategory.isXValue() ||
|
||||
(InitCategory.isPRValue() && T2->isRecordType()) ||
|
||||
(InitCategory.isPRValue() && T2->isArrayType()))) {
|
||||
(InitCategory.isPRValue() &&
|
||||
(S.getLangOpts().CPlusPlus1z || T2->isRecordType() ||
|
||||
T2->isArrayType())))) {
|
||||
ExprValueKind ValueKind = InitCategory.isXValue()? VK_XValue : VK_RValue;
|
||||
if (InitCategory.isPRValue() && T2->isRecordType()) {
|
||||
// The corresponding bullet in C++03 [dcl.init.ref]p5 gives the
|
||||
|
@ -6604,7 +6640,26 @@ InitializationSequence::Perform(Sema &S,
|
|||
CreatedObject = Conversion->getReturnType()->isRecordType();
|
||||
}
|
||||
|
||||
// C++14 and before:
|
||||
// - if the function is a constructor, the call initializes a temporary
|
||||
// of the cv-unqualified version of the destination type [...]
|
||||
// C++1z:
|
||||
// - if the function is a constructor, the call is a prvalue of the
|
||||
// cv-unqualified version of the destination type whose return object
|
||||
// is initialized by the constructor [...]
|
||||
// Both:
|
||||
// The [..] call is used to direct-initialize, according to the rules
|
||||
// above, the object that is the destination of the
|
||||
// copy-initialization.
|
||||
// In C++14 and before, that always means the "constructors are
|
||||
// considered" bullet, because we have arrived at a reference-related
|
||||
// type. In C++1z, it only means that if the types are different or we
|
||||
// didn't produce a prvalue, so just check for that case here.
|
||||
bool RequiresCopy = !IsCopy && !isReferenceBinding(Steps.back());
|
||||
if (S.getLangOpts().CPlusPlus1z && CurInit.get()->isRValue() &&
|
||||
S.Context.hasSameUnqualifiedType(
|
||||
Entity.getType().getNonReferenceType(), CurInit.get()->getType()))
|
||||
RequiresCopy = false;
|
||||
bool MaybeBindToTemp = RequiresCopy || shouldBindAsTemporary(Entity);
|
||||
|
||||
if (!MaybeBindToTemp && CreatedObject && shouldDestroyTemporary(Entity)) {
|
||||
|
|
|
@ -4979,7 +4979,7 @@ TryObjectArgumentInitialization(Sema &S, SourceLocation Loc, QualType FromType,
|
|||
// cv-qualification on the member function declaration.
|
||||
//
|
||||
// However, when finding an implicit conversion sequence for the argument, we
|
||||
// are not allowed to create temporaries or perform user-defined conversions
|
||||
// are not allowed to perform user-defined conversions
|
||||
// (C++ [over.match.funcs]p5). We perform a simplified version of
|
||||
// reference binding here, that allows class rvalues to bind to
|
||||
// non-constant references.
|
||||
|
|
|
@ -248,7 +248,7 @@ namespace dr20 { // dr20: yes
|
|||
private:
|
||||
X(const X&); // expected-note {{here}}
|
||||
};
|
||||
X f();
|
||||
X &f();
|
||||
X x = f(); // expected-error {{private}}
|
||||
}
|
||||
|
||||
|
@ -316,8 +316,15 @@ namespace dr25 { // dr25: yes
|
|||
namespace dr26 { // dr26: yes
|
||||
struct A { A(A, const A & = A()); }; // expected-error {{must pass its first argument by reference}}
|
||||
struct B {
|
||||
B(); // expected-note {{candidate}}
|
||||
B(const B &, B = B()); // expected-error {{no matching constructor}} expected-note {{candidate}} expected-note {{here}}
|
||||
B(); // expected-note 0-1{{candidate}}
|
||||
B(const B &, B = B());
|
||||
#if __cplusplus <= 201402L
|
||||
// expected-error@-2 {{no matching constructor}} expected-note@-2 {{candidate}} expected-note@-2 {{here}}
|
||||
#endif
|
||||
};
|
||||
struct C {
|
||||
static C &f();
|
||||
C(const C &, C = f()); // expected-error {{no matching constructor}} expected-note {{candidate}} expected-note {{here}}
|
||||
};
|
||||
}
|
||||
|
||||
|
@ -662,25 +669,33 @@ namespace dr58 { // dr58: yes
|
|||
|
||||
namespace dr59 { // dr59: yes
|
||||
template<typename T> struct convert_to { operator T() const; };
|
||||
struct A {}; // expected-note 2{{volatile qualifier}} expected-note 2{{requires 0 arguments}}
|
||||
struct B : A {}; // expected-note 2{{volatile qualifier}} expected-note 2{{requires 0 arguments}}
|
||||
#if __cplusplus >= 201103L // move constructors
|
||||
// expected-note@-3 2{{volatile qualifier}}
|
||||
// expected-note@-3 2{{volatile qualifier}}
|
||||
#endif
|
||||
struct A {}; // expected-note 5+{{candidate}}
|
||||
struct B : A {}; // expected-note 0+{{candidate}}
|
||||
|
||||
A a1 = convert_to<A>();
|
||||
A a2 = convert_to<A&>();
|
||||
A a3 = convert_to<const A>();
|
||||
A a4 = convert_to<const volatile A>(); // expected-error {{no viable}}
|
||||
A a4 = convert_to<const volatile A>();
|
||||
#if __cplusplus <= 201402L
|
||||
// expected-error@-2 {{no viable}}
|
||||
#endif
|
||||
A a5 = convert_to<const volatile A&>(); // expected-error {{no viable}}
|
||||
|
||||
B b1 = convert_to<B>();
|
||||
B b2 = convert_to<B&>();
|
||||
B b3 = convert_to<const B>();
|
||||
B b4 = convert_to<const volatile B>(); // expected-error {{no viable}}
|
||||
B b4 = convert_to<const volatile B>();
|
||||
#if __cplusplus <= 201402L
|
||||
// expected-error@-2 {{no viable}}
|
||||
#endif
|
||||
B b5 = convert_to<const volatile B&>(); // expected-error {{no viable}}
|
||||
|
||||
A c1 = convert_to<B>();
|
||||
A c2 = convert_to<B&>();
|
||||
A c3 = convert_to<const B>();
|
||||
A c4 = convert_to<const volatile B>(); // expected-error {{no viable}}
|
||||
A c5 = convert_to<const volatile B&>(); // expected-error {{no viable}}
|
||||
|
||||
int n1 = convert_to<int>();
|
||||
int n2 = convert_to<int&>();
|
||||
int n3 = convert_to<const int>();
|
||||
|
@ -920,14 +935,17 @@ namespace dr84 { // dr84: yes
|
|||
struct A { operator B() const; };
|
||||
struct C {};
|
||||
struct B {
|
||||
B(B&); // expected-note {{candidate}}
|
||||
B(C); // expected-note {{no known conversion from 'dr84::B' to 'dr84::C'}}
|
||||
B(B&); // expected-note 0-1{{candidate}}
|
||||
B(C); // expected-note 0-1{{no known conversion from 'dr84::B' to 'dr84::C'}}
|
||||
operator C() const;
|
||||
};
|
||||
A a;
|
||||
// Cannot use B(C) / operator C() pair to construct the B from the B temporary
|
||||
// here.
|
||||
B b = a; // expected-error {{no viable}}
|
||||
// here. In C++1z, we initialize the B object directly using 'A::operator B()'.
|
||||
B b = a;
|
||||
#if __cplusplus <= 201402L
|
||||
// expected-error@-2 {{no viable}}
|
||||
#endif
|
||||
}
|
||||
|
||||
namespace dr85 { // dr85: yes
|
||||
|
|
|
@ -3,8 +3,6 @@
|
|||
// RUN: %clang_cc1 -std=c++14 %s -verify -fexceptions -fcxx-exceptions -pedantic-errors
|
||||
// RUN: %clang_cc1 -std=c++1z %s -verify -fexceptions -fcxx-exceptions -pedantic-errors
|
||||
|
||||
// expected-no-diagnostics
|
||||
|
||||
namespace std {
|
||||
__extension__ typedef __SIZE_TYPE__ size_t;
|
||||
|
||||
|
@ -32,6 +30,18 @@ namespace dr1048 { // dr1048: 3.6
|
|||
#endif
|
||||
}
|
||||
|
||||
namespace dr1054 { // dr1054: no
|
||||
// FIXME: Test is incomplete.
|
||||
struct A {} volatile a;
|
||||
void f() {
|
||||
// FIXME: This is wrong: an lvalue-to-rvalue conversion is applied here,
|
||||
// which copy-initializes a temporary from 'a'. Therefore this is
|
||||
// ill-formed because A does not have a volatile copy constructor.
|
||||
// (We might want to track this aspect under dr1383 instead?)
|
||||
a; // expected-warning {{assign into a variable to force a volatile load}}
|
||||
}
|
||||
}
|
||||
|
||||
namespace dr1070 { // dr1070: 3.5
|
||||
#if __cplusplus >= 201103L
|
||||
struct A {
|
||||
|
|
|
@ -576,11 +576,18 @@ namespace dr151 { // dr151: yes
|
|||
|
||||
namespace dr152 { // dr152: yes
|
||||
struct A {
|
||||
A(); // expected-note {{not viable}}
|
||||
A(); // expected-note 0-2{{not viable}}
|
||||
explicit A(const A&);
|
||||
};
|
||||
A a1 = A(); // expected-error {{no matching constructor}}
|
||||
A a1 = A();
|
||||
#if __cplusplus <= 201402L
|
||||
// expected-error@-2 {{no matching constructor}}
|
||||
#endif
|
||||
A a2((A()));
|
||||
|
||||
A &f();
|
||||
A a3 = f(); // expected-error {{no matching constructor}}
|
||||
A a4(f());
|
||||
}
|
||||
|
||||
// dr153: na
|
||||
|
@ -823,11 +830,20 @@ namespace dr176 { // dr176: yes
|
|||
namespace dr177 { // dr177: yes
|
||||
struct B {};
|
||||
struct A {
|
||||
A(A &); // expected-note {{not viable: expects an l-value}}
|
||||
A(const B &); // expected-note {{not viable: no known conversion from 'dr177::A' to}}
|
||||
A(A &); // expected-note 0-1{{not viable: expects an l-value}}
|
||||
A(const B &); // expected-note 0-1{{not viable: no known conversion from 'dr177::A' to}}
|
||||
};
|
||||
B b;
|
||||
A a = b; // expected-error {{no viable constructor copying variable}}
|
||||
A a = b;
|
||||
#if __cplusplus <= 201402L
|
||||
// expected-error@-2 {{no viable constructor copying variable}}
|
||||
#endif
|
||||
|
||||
struct C { C(C&); }; // expected-note {{not viable: no known conversion from 'dr177::D' to 'dr177::C &'}}
|
||||
struct D : C {};
|
||||
struct E { operator D(); };
|
||||
E e;
|
||||
C c = e; // expected-error {{no viable constructor copying variable of type 'dr177::D'}}
|
||||
}
|
||||
|
||||
namespace dr178 { // dr178: yes
|
||||
|
|
|
@ -553,12 +553,21 @@ namespace dr446 { // dr446: yes
|
|||
void(b ? a : a);
|
||||
b ? A() : a; // expected-error {{deleted}}
|
||||
b ? a : A(); // expected-error {{deleted}}
|
||||
b ? A() : A(); // expected-error {{deleted}}
|
||||
b ? A() : A();
|
||||
#if __cplusplus <= 201402L
|
||||
// expected-error@-2 {{deleted}}
|
||||
#endif
|
||||
|
||||
void(b ? a : c);
|
||||
b ? a : C(); // expected-error {{deleted}}
|
||||
b ? c : A(); // expected-error {{deleted}}
|
||||
b ? A() : C(); // expected-error {{deleted}}
|
||||
b ? c : A();
|
||||
#if __cplusplus <= 201402L
|
||||
// expected-error@-2 {{deleted}}
|
||||
#endif
|
||||
b ? A() : C();
|
||||
#if __cplusplus <= 201402L
|
||||
// expected-error@-2 {{deleted}}
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -874,10 +883,12 @@ namespace dr479 { // dr479: yes
|
|||
void f() {
|
||||
throw S();
|
||||
// expected-error@-1 {{temporary of type 'dr479::S' has private destructor}}
|
||||
// expected-error@-2 {{calling a private constructor}}
|
||||
// expected-error@-3 {{exception object of type 'dr479::S' has private destructor}}
|
||||
// expected-error@-2 {{exception object of type 'dr479::S' has private destructor}}
|
||||
#if __cplusplus < 201103L
|
||||
// expected-error@-5 {{C++98 requires an accessible copy constructor}}
|
||||
// expected-error@-4 {{C++98 requires an accessible copy constructor}}
|
||||
#endif
|
||||
#if __cplusplus <= 201402L
|
||||
// expected-error@-7 {{calling a private constructor}} (copy ctor)
|
||||
#endif
|
||||
}
|
||||
void g() {
|
||||
|
|
|
@ -0,0 +1,81 @@
|
|||
// RUN: %clang_cc1 -std=c++1z -emit-llvm -triple x86_64-linux-gnu -o - %s | FileCheck %s
|
||||
|
||||
struct A {
|
||||
A(int);
|
||||
A(A&&);
|
||||
A(const A&);
|
||||
~A();
|
||||
|
||||
int arr[10];
|
||||
};
|
||||
|
||||
A f();
|
||||
void h();
|
||||
|
||||
// CHECK-LABEL: define {{.*}} @_Z1gv(
|
||||
void g() {
|
||||
// CHECK: %[[A:.*]] = alloca
|
||||
// CHECK-NOT: alloca
|
||||
// CHECK-NOT: call
|
||||
// CHECK: call {{.*}} @_Z1fv({{.*}}* sret %[[A]])
|
||||
A a = A( A{ f() } );
|
||||
// CHECK-NOT: call
|
||||
|
||||
// CHECK: call void @_Z1hv(
|
||||
h();
|
||||
// CHECK-NOT: call
|
||||
|
||||
// CHECK: call void @_ZN1AD1Ev({{.*}}* %[[A]])
|
||||
// CHECK-NOT: call
|
||||
// CHECK-LABEL: }
|
||||
}
|
||||
|
||||
void f(A);
|
||||
|
||||
// CHECK-LABEL: define {{.*}} @_Z1hv(
|
||||
void h() {
|
||||
// CHECK: %[[A:.*]] = alloca
|
||||
// CHECK-NOT: alloca
|
||||
// CHECK-NOT: call
|
||||
|
||||
// CHECK: call {{.*}} @_Z1fv({{.*}}* sret %[[A]])
|
||||
// CHECK-NOT: call
|
||||
// CHECK: call {{.*}} @_Z1f1A({{.*}}* %[[A]])
|
||||
f(f());
|
||||
// CHECK-NOT: call
|
||||
// CHECK: call void @_ZN1AD1Ev({{.*}}* %[[A]])
|
||||
|
||||
// CHECK: call void @_Z1hv(
|
||||
h();
|
||||
|
||||
// CHECK-NOT: call
|
||||
// CHECK-LABEL: }
|
||||
}
|
||||
|
||||
// We still pass classes with trivial copy/move constructors and destructors in
|
||||
// registers, even if the copy is formally omitted.
|
||||
struct B {
|
||||
B(int);
|
||||
int n;
|
||||
};
|
||||
|
||||
B fB();
|
||||
void fB(B);
|
||||
|
||||
// CHECK-LABEL: define {{.*}} @_Z1iv(
|
||||
void i() {
|
||||
// CHECK: %[[B:.*]] = alloca
|
||||
// CHECK-NOT: alloca
|
||||
// CHECK-NOT: call
|
||||
|
||||
// CHECK: %[[B_N:.*]] = call i32 @_Z2fBv()
|
||||
// CHECK-NOT: call
|
||||
// CHECK: store i32 %[[B_N]],
|
||||
// CHECK-NOT: call
|
||||
// CHECK: %[[B_N:.*]] = load i32
|
||||
// CHECK-NOT: call
|
||||
// CHECK: call void @_Z2fB1B(i32 %[[B_N]])
|
||||
fB(fB());
|
||||
|
||||
// CHECK-LABEL: }
|
||||
}
|
|
@ -57,7 +57,7 @@ struct A {
|
|||
A(int);
|
||||
~A();
|
||||
|
||||
A(const A&) = delete; // expected-note 2 {{'A' has been explicitly marked deleted here}}
|
||||
A(const A&) = delete; // expected-note 0-2{{'A' has been explicitly marked deleted here}}
|
||||
};
|
||||
|
||||
struct B {
|
||||
|
@ -70,10 +70,16 @@ struct C {
|
|||
|
||||
void f() {
|
||||
A as1[1] = { };
|
||||
A as2[1] = { 1 }; // expected-error {{copying array element of type 'A' invokes deleted constructor}}
|
||||
A as2[1] = { 1 };
|
||||
#if __cplusplus <= 201402L
|
||||
// expected-error@-2 {{copying array element of type 'A' invokes deleted constructor}}
|
||||
#endif
|
||||
|
||||
B b1 = { };
|
||||
B b2 = { 1 }; // expected-error {{copying member subobject of type 'A' invokes deleted constructor}}
|
||||
B b2 = { 1 };
|
||||
#if __cplusplus <= 201402L
|
||||
// expected-error@-2 {{copying member subobject of type 'A' invokes deleted constructor}}
|
||||
#endif
|
||||
|
||||
C c1 = { 1 };
|
||||
}
|
||||
|
|
|
@ -0,0 +1,134 @@
|
|||
// RUN: %clang_cc1 -std=c++1z -verify %s
|
||||
|
||||
struct Noncopyable {
|
||||
Noncopyable();
|
||||
Noncopyable(const Noncopyable &) = delete; // expected-note 1+{{deleted}}
|
||||
virtual ~Noncopyable();
|
||||
};
|
||||
struct Derived : Noncopyable {};
|
||||
struct NoncopyableAggr {
|
||||
Noncopyable nc;
|
||||
};
|
||||
struct Indestructible {
|
||||
Indestructible();
|
||||
~Indestructible() = delete; // expected-note 1+{{deleted}}
|
||||
};
|
||||
struct Incomplete; // expected-note 1+{{declar}}
|
||||
|
||||
Noncopyable make(int kind = 0) {
|
||||
switch (kind) {
|
||||
case 0: return {};
|
||||
case 1: return Noncopyable();
|
||||
case 2: return Noncopyable{};
|
||||
case 3: return make();
|
||||
}
|
||||
__builtin_unreachable();
|
||||
}
|
||||
|
||||
Indestructible make_indestructible();
|
||||
Incomplete make_incomplete(); // expected-note 1+{{here}}
|
||||
|
||||
void take(Noncopyable nc) {}
|
||||
|
||||
Noncopyable nrvo() {
|
||||
Noncopyable nrvo;
|
||||
return nrvo; // expected-error {{deleted constructor}}
|
||||
}
|
||||
|
||||
Noncopyable nc1 = make();
|
||||
Noncopyable nc2 = Noncopyable();
|
||||
Noncopyable nc3 = Derived(); // expected-error {{deleted constructor}}
|
||||
|
||||
NoncopyableAggr nca1 = NoncopyableAggr{};
|
||||
NoncopyableAggr nca2 = NoncopyableAggr{{}};
|
||||
NoncopyableAggr nca3 = NoncopyableAggr{NoncopyableAggr{Noncopyable()}};
|
||||
|
||||
void test_expressions(bool b) {
|
||||
auto lambda = [a = make()] {};
|
||||
|
||||
take({});
|
||||
take(Noncopyable());
|
||||
take(Noncopyable{});
|
||||
take(make());
|
||||
|
||||
Noncopyable &&dc1 = dynamic_cast<Noncopyable&&>(Noncopyable());
|
||||
Noncopyable &&dc2 = dynamic_cast<Noncopyable&&>(nc1);
|
||||
Noncopyable &&dc3 = dynamic_cast<Noncopyable&&>(Derived());
|
||||
|
||||
Noncopyable sc1 = static_cast<Noncopyable>(Noncopyable());
|
||||
Noncopyable sc2 = static_cast<Noncopyable>(nc1); // expected-error {{deleted}}
|
||||
Noncopyable sc3 = static_cast<Noncopyable&&>(Noncopyable()); // expected-error {{deleted}}
|
||||
Noncopyable sc4 = static_cast<Noncopyable>(static_cast<Noncopyable&&>(Noncopyable())); // expected-error {{deleted}}
|
||||
|
||||
Noncopyable cc1 = (Noncopyable)Noncopyable();
|
||||
Noncopyable cc2 = (Noncopyable)Derived(); // expected-error {{deleted}}
|
||||
|
||||
Noncopyable fc1 = Noncopyable(Noncopyable());
|
||||
Noncopyable fc2 = Noncopyable(Derived()); // expected-error {{deleted}}
|
||||
|
||||
// We must check for a complete type for every materialized temporary. (Note
|
||||
// that in the special case of the top level of a decltype, no temporary is
|
||||
// materialized.)
|
||||
make_incomplete(); // expected-error {{incomplete}}
|
||||
make_incomplete().a; // expected-error {{incomplete}}
|
||||
make_incomplete().*(int Incomplete::*)nullptr; // expected-error {{incomplete}}
|
||||
dynamic_cast<Incomplete&&>(make_incomplete()); // expected-error {{incomplete}}
|
||||
const_cast<Incomplete&&>(make_incomplete()); // expected-error {{incomplete}}
|
||||
|
||||
sizeof(Indestructible{}); // expected-error {{deleted}}
|
||||
sizeof(make_indestructible()); // expected-error {{deleted}}
|
||||
sizeof(make_incomplete()); // expected-error {{incomplete}}
|
||||
typeid(Indestructible{}); // expected-error {{deleted}}
|
||||
typeid(make_indestructible()); // expected-error {{deleted}}
|
||||
typeid(make_incomplete()); // expected-error {{incomplete}}
|
||||
|
||||
// FIXME: The first two cases here are now also valid in C++17 onwards.
|
||||
using I = decltype(Indestructible()); // expected-error {{deleted}}
|
||||
using I = decltype(Indestructible{}); // expected-error {{deleted}}
|
||||
using I = decltype(make_indestructible());
|
||||
using J = decltype(make_incomplete());
|
||||
|
||||
Noncopyable cond1 = b ? Noncopyable() : make();
|
||||
Noncopyable cond2 = b ? Noncopyable() : Derived(); // expected-error {{incompatible}}
|
||||
Noncopyable cond3 = b ? Derived() : Noncopyable(); // expected-error {{incompatible}}
|
||||
Noncopyable cond4 = b ? Noncopyable() : nc1; // expected-error {{deleted}}
|
||||
Noncopyable cond5 = b ? nc1 : Noncopyable(); // expected-error {{deleted}}
|
||||
// Could convert both to an xvalue of type Noncopyable here, but we're not
|
||||
// permitted to consider that.
|
||||
Noncopyable &&cond6 = b ? Noncopyable() : Derived(); // expected-error {{incompatible}}
|
||||
Noncopyable &&cond7 = b ? Derived() : Noncopyable(); // expected-error {{incompatible}}
|
||||
// Could convert both to a const lvalue of type Noncopyable here, but we're
|
||||
// not permitted to consider that, either.
|
||||
const Noncopyable cnc;
|
||||
const Noncopyable &cond8 = b ? cnc : Derived(); // expected-error {{incompatible}}
|
||||
const Noncopyable &cond9 = b ? Derived() : cnc; // expected-error {{incompatible}}
|
||||
|
||||
extern const volatile Noncopyable make_cv();
|
||||
Noncopyable cv_difference1 = make_cv();
|
||||
const volatile Noncopyable cv_difference2 = make();
|
||||
}
|
||||
|
||||
template<typename T> struct ConversionFunction { operator T(); };
|
||||
Noncopyable cf1 = ConversionFunction<Noncopyable>();
|
||||
Noncopyable cf2 = ConversionFunction<Noncopyable&&>(); // expected-error {{deleted}}
|
||||
Noncopyable cf3 = ConversionFunction<const volatile Noncopyable>();
|
||||
const volatile Noncopyable cf4 = ConversionFunction<Noncopyable>();
|
||||
Noncopyable cf5 = ConversionFunction<Derived>(); // expected-error {{deleted}}
|
||||
|
||||
struct AsMember {
|
||||
Noncopyable member;
|
||||
AsMember() : member(make()) {}
|
||||
};
|
||||
// FIXME: DR (no number yet): we still get a copy for base or delegating construction.
|
||||
struct AsBase : Noncopyable {
|
||||
AsBase() : Noncopyable(make()) {} // expected-error {{deleted}}
|
||||
};
|
||||
struct AsDelegating final {
|
||||
AsDelegating(const AsDelegating &) = delete;
|
||||
static AsDelegating make(int);
|
||||
|
||||
// The base constructor version of this is problematic; the complete object
|
||||
// version would be OK. Perhaps we could allow copy omission here for final
|
||||
// classes?
|
||||
AsDelegating(int n) : AsDelegating(make(n)) {} // expected-error {{deleted}}
|
||||
};
|
|
@ -6139,7 +6139,7 @@ and <I>POD class</I></td>
|
|||
<td><a href="http://www.open-std.org/jtc1/sc22/wg21/docs/cwg_defects.html#1054">1054</a></td>
|
||||
<td>C++11</td>
|
||||
<td>Lvalue-to-rvalue conversions in expression statements</td>
|
||||
<td class="none" align="center">Unknown</td>
|
||||
<td class="none" align="center">No</td>
|
||||
</tr>
|
||||
<tr id="1055">
|
||||
<td><a href="http://www.open-std.org/jtc1/sc22/wg21/docs/cwg_defects.html#1055">1055</a></td>
|
||||
|
|
|
@ -694,7 +694,7 @@ as the draft C++1z standard evolves.
|
|||
<tr>
|
||||
<td>Guaranteed copy elision</td>
|
||||
<td><a href="http://wg21.link/p0135r1">P0135R1</a></td>
|
||||
<td class="none" align="center">No</td>
|
||||
<td class="svn" align="center">SVN</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td rowspan=2>Stricter expression evaluation order</td>
|
||||
|
|
Loading…
Reference in New Issue