[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:
Richard Smith 2016-12-06 23:52:28 +00:00
parent 2187bb8a89
commit 122f88d481
18 changed files with 428 additions and 68 deletions

View File

@ -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; }

View File

@ -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);
}
}

View File

@ -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);

View File

@ -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));
}

View File

@ -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

View File

@ -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);

View File

@ -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;
}

View File

@ -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)) {

View File

@ -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.

View File

@ -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

View File

@ -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 {

View File

@ -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

View File

@ -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() {

View File

@ -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: }
}

View File

@ -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 };
}

View File

@ -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}}
};

View File

@ -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>

View File

@ -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>