Skip to content

Commit 9d208e1

Browse files
committed
[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. git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@288866 91177308-0d34-0410-b5e6-96231b3b80d8
1 parent 31a3f52 commit 9d208e1

18 files changed

+428
-68
lines changed

include/clang/AST/Expr.h

+6-1
Original file line numberDiff line numberDiff line change
@@ -3786,7 +3786,7 @@ class InitListExpr : public Expr {
37863786

37873787
/// \brief Build an empty initializer list.
37883788
explicit InitListExpr(EmptyShell Empty)
3789-
: Expr(InitListExprClass, Empty) { }
3789+
: Expr(InitListExprClass, Empty), AltForm(nullptr, true) { }
37903790

37913791
unsigned getNumInits() const { return InitExprs.size(); }
37923792

@@ -3894,6 +3894,11 @@ class InitListExpr : public Expr {
38943894
// literal or an @encode?
38953895
bool isStringLiteralInit() const;
38963896

3897+
/// Is this a transparent initializer list (that is, an InitListExpr that is
3898+
/// purely syntactic, and whose semantics are that of the sole contained
3899+
/// initializer)?
3900+
bool isTransparent() const;
3901+
38973902
SourceLocation getLBraceLoc() const { return LBraceLoc; }
38983903
void setLBraceLoc(SourceLocation Loc) { LBraceLoc = Loc; }
38993904
SourceLocation getRBraceLoc() const { return RBraceLoc; }

lib/AST/Expr.cpp

+25-4
Original file line numberDiff line numberDiff line change
@@ -1864,6 +1864,24 @@ bool InitListExpr::isStringLiteralInit() const {
18641864
return isa<StringLiteral>(Init) || isa<ObjCEncodeExpr>(Init);
18651865
}
18661866

1867+
bool InitListExpr::isTransparent() const {
1868+
assert(isSemanticForm() && "syntactic form never semantically transparent");
1869+
1870+
// A glvalue InitListExpr is always just sugar.
1871+
if (isGLValue()) {
1872+
assert(getNumInits() == 1 && "multiple inits in glvalue init list");
1873+
return true;
1874+
}
1875+
1876+
// Otherwise, we're sugar if and only if we have exactly one initializer that
1877+
// is of the same type.
1878+
if (getNumInits() != 1 || !getInit(0))
1879+
return false;
1880+
1881+
return getType().getCanonicalType() ==
1882+
getInit(0)->getType().getCanonicalType();
1883+
}
1884+
18671885
SourceLocation InitListExpr::getLocStart() const {
18681886
if (InitListExpr *SyntacticForm = getSyntacticForm())
18691887
return SyntacticForm->getLocStart();
@@ -2246,12 +2264,15 @@ bool Expr::isUnusedResultAWarning(const Expr *&WarnE, SourceLocation &Loc,
22462264
// effects (e.g. a placement new with an uninitialized POD).
22472265
case CXXDeleteExprClass:
22482266
return false;
2267+
case MaterializeTemporaryExprClass:
2268+
return cast<MaterializeTemporaryExpr>(this)->GetTemporaryExpr()
2269+
->isUnusedResultAWarning(WarnE, Loc, R1, R2, Ctx);
22492270
case CXXBindTemporaryExprClass:
2250-
return (cast<CXXBindTemporaryExpr>(this)
2251-
->getSubExpr()->isUnusedResultAWarning(WarnE, Loc, R1, R2, Ctx));
2271+
return cast<CXXBindTemporaryExpr>(this)->getSubExpr()
2272+
->isUnusedResultAWarning(WarnE, Loc, R1, R2, Ctx);
22522273
case ExprWithCleanupsClass:
2253-
return (cast<ExprWithCleanups>(this)
2254-
->getSubExpr()->isUnusedResultAWarning(WarnE, Loc, R1, R2, Ctx));
2274+
return cast<ExprWithCleanups>(this)->getSubExpr()
2275+
->isUnusedResultAWarning(WarnE, Loc, R1, R2, Ctx);
22552276
}
22562277
}
22572278

lib/AST/ExprConstant.cpp

+3
Original file line numberDiff line numberDiff line change
@@ -5670,6 +5670,9 @@ bool RecordExprEvaluator::VisitCastExpr(const CastExpr *E) {
56705670
}
56715671

56725672
bool RecordExprEvaluator::VisitInitListExpr(const InitListExpr *E) {
5673+
if (E->isTransparent())
5674+
return Visit(E->getInit(0));
5675+
56735676
const RecordDecl *RD = E->getType()->castAs<RecordType>()->getDecl();
56745677
if (RD->isInvalidDecl()) return false;
56755678
const ASTRecordLayout &Layout = Info.Ctx.getASTRecordLayout(RD);

lib/CodeGen/CGExpr.cpp

+1-1
Original file line numberDiff line numberDiff line change
@@ -3522,7 +3522,7 @@ LValue CodeGenFunction::EmitInitListLValue(const InitListExpr *E) {
35223522
return EmitAggExprToLValue(E);
35233523

35243524
// An lvalue initializer list must be initializing a reference.
3525-
assert(E->getNumInits() == 1 && "reference init with multiple values");
3525+
assert(E->isTransparent() && "non-transparent glvalue init list");
35263526
return EmitLValue(E->getInit(0));
35273527
}
35283528

lib/CodeGen/CGExprAgg.cpp

+3-13
Original file line numberDiff line numberDiff line change
@@ -1145,15 +1145,15 @@ void AggExprEmitter::VisitInitListExpr(InitListExpr *E) {
11451145
if (E->hadArrayRangeDesignator())
11461146
CGF.ErrorUnsupported(E, "GNU array range designator extension");
11471147

1148+
if (E->isTransparent())
1149+
return Visit(E->getInit(0));
1150+
11481151
AggValueSlot Dest = EnsureSlot(E->getType());
11491152

11501153
LValue DestLV = CGF.MakeAddrLValue(Dest.getAddress(), E->getType());
11511154

11521155
// Handle initialization of an array.
11531156
if (E->getType()->isArrayType()) {
1154-
if (E->isStringLiteralInit())
1155-
return Visit(E->getInit(0));
1156-
11571157
QualType elementType =
11581158
CGF.getContext().getAsArrayType(E->getType())->getElementType();
11591159

@@ -1162,16 +1162,6 @@ void AggExprEmitter::VisitInitListExpr(InitListExpr *E) {
11621162
return;
11631163
}
11641164

1165-
if (E->getType()->isAtomicType()) {
1166-
// An _Atomic(T) object can be list-initialized from an expression
1167-
// of the same type.
1168-
assert(E->getNumInits() == 1 &&
1169-
CGF.getContext().hasSameUnqualifiedType(E->getInit(0)->getType(),
1170-
E->getType()) &&
1171-
"unexpected list initialization for atomic object");
1172-
return Visit(E->getInit(0));
1173-
}
1174-
11751165
assert(E->getType()->isRecordType() && "Only support structs/unions here!");
11761166

11771167
// Do struct initialization; this code just sets each individual member

lib/CodeGen/CGExprConstant.cpp

+3-3
Original file line numberDiff line numberDiff line change
@@ -778,9 +778,6 @@ class ConstExprEmitter :
778778
}
779779

780780
llvm::Constant *EmitArrayInitialization(InitListExpr *ILE) {
781-
if (ILE->isStringLiteralInit())
782-
return Visit(ILE->getInit(0));
783-
784781
llvm::ArrayType *AType =
785782
cast<llvm::ArrayType>(ConvertType(ILE->getType()));
786783
llvm::Type *ElemTy = AType->getElementType();
@@ -845,6 +842,9 @@ class ConstExprEmitter :
845842
}
846843

847844
llvm::Constant *VisitInitListExpr(InitListExpr *ILE) {
845+
if (ILE->isTransparent())
846+
return Visit(ILE->getInit(0));
847+
848848
if (ILE->getType()->isArrayType())
849849
return EmitArrayInitialization(ILE);
850850

lib/Sema/SemaExprCXX.cpp

+10
Original file line numberDiff line numberDiff line change
@@ -6836,6 +6836,16 @@ ExprResult Sema::IgnoredValueConversions(Expr *E) {
68366836
return E;
68376837
E = Res.get();
68386838
}
6839+
6840+
// C++1z:
6841+
// If the expression is a prvalue after this optional conversion, the
6842+
// temporary materialization conversion is applied.
6843+
//
6844+
// We skip this step: IR generation is able to synthesize the storage for
6845+
// itself in the aggregate case, and adding the extra node to the AST is
6846+
// just clutter.
6847+
// FIXME: We don't emit lifetime markers for the temporaries due to this.
6848+
// FIXME: Do any other AST consumers care about this?
68396849
return E;
68406850
}
68416851

lib/Sema/SemaInit.cpp

+67-12
Original file line numberDiff line numberDiff line change
@@ -3546,15 +3546,50 @@ static void TryConstructorInitialization(Sema &S,
35463546
InitializationSequence &Sequence,
35473547
bool IsListInit = false,
35483548
bool IsInitListCopy = false) {
3549-
assert((!IsListInit || (Args.size() == 1 && isa<InitListExpr>(Args[0]))) &&
3550-
"IsListInit must come with a single initializer list argument.");
3549+
assert(((!IsListInit && !IsInitListCopy) ||
3550+
(Args.size() == 1 && isa<InitListExpr>(Args[0]))) &&
3551+
"IsListInit/IsInitListCopy must come with a single initializer list "
3552+
"argument.");
3553+
InitListExpr *ILE =
3554+
(IsListInit || IsInitListCopy) ? cast<InitListExpr>(Args[0]) : nullptr;
3555+
MultiExprArg UnwrappedArgs =
3556+
ILE ? MultiExprArg(ILE->getInits(), ILE->getNumInits()) : Args;
35513557

35523558
// The type we're constructing needs to be complete.
35533559
if (!S.isCompleteType(Kind.getLocation(), DestType)) {
35543560
Sequence.setIncompleteTypeFailure(DestType);
35553561
return;
35563562
}
35573563

3564+
// C++1z [dcl.init]p17:
3565+
// - If the initializer expression is a prvalue and the cv-unqualified
3566+
// version of the source type is the same class as the class of the
3567+
// destination, the initializer expression is used to initialize the
3568+
// destination object.
3569+
// Per DR (no number yet), this does not apply when initializing a base
3570+
// class or delegating to another constructor from a mem-initializer.
3571+
if (S.getLangOpts().CPlusPlus1z &&
3572+
Entity.getKind() != InitializedEntity::EK_Base &&
3573+
Entity.getKind() != InitializedEntity::EK_Delegating &&
3574+
UnwrappedArgs.size() == 1 && UnwrappedArgs[0]->isRValue() &&
3575+
S.Context.hasSameUnqualifiedType(UnwrappedArgs[0]->getType(), DestType)) {
3576+
// Convert qualifications if necessary.
3577+
QualType InitType = UnwrappedArgs[0]->getType();
3578+
ImplicitConversionSequence ICS;
3579+
ICS.setStandard();
3580+
ICS.Standard.setAsIdentityConversion();
3581+
ICS.Standard.setFromType(InitType);
3582+
ICS.Standard.setAllToTypes(InitType);
3583+
if (!S.Context.hasSameType(InitType, DestType)) {
3584+
ICS.Standard.Third = ICK_Qualification;
3585+
ICS.Standard.setToType(2, DestType);
3586+
}
3587+
Sequence.AddConversionSequenceStep(ICS, DestType);
3588+
if (ILE)
3589+
Sequence.RewrapReferenceInitList(DestType, ILE);
3590+
return;
3591+
}
3592+
35583593
const RecordType *DestRecordType = DestType->getAs<RecordType>();
35593594
assert(DestRecordType && "Constructor initialization requires record type");
35603595
CXXRecordDecl *DestRecordDecl
@@ -3588,20 +3623,16 @@ static void TryConstructorInitialization(Sema &S,
35883623
// constructors of the class T and the argument list consists of the
35893624
// initializer list as a single argument.
35903625
if (IsListInit) {
3591-
InitListExpr *ILE = cast<InitListExpr>(Args[0]);
35923626
AsInitializerList = true;
35933627

35943628
// If the initializer list has no elements and T has a default constructor,
35953629
// the first phase is omitted.
3596-
if (ILE->getNumInits() != 0 || !DestRecordDecl->hasDefaultConstructor())
3630+
if (!(UnwrappedArgs.empty() && DestRecordDecl->hasDefaultConstructor()))
35973631
Result = ResolveConstructorOverload(S, Kind.getLocation(), Args,
35983632
CandidateSet, Ctors, Best,
35993633
CopyInitialization, AllowExplicit,
36003634
/*OnlyListConstructor=*/true,
36013635
IsListInit);
3602-
3603-
// Time to unwrap the init list.
3604-
Args = MultiExprArg(ILE->getInits(), ILE->getNumInits());
36053636
}
36063637

36073638
// C++11 [over.match.list]p1:
@@ -3611,7 +3642,7 @@ static void TryConstructorInitialization(Sema &S,
36113642
// elements of the initializer list.
36123643
if (Result == OR_No_Viable_Function) {
36133644
AsInitializerList = false;
3614-
Result = ResolveConstructorOverload(S, Kind.getLocation(), Args,
3645+
Result = ResolveConstructorOverload(S, Kind.getLocation(), UnwrappedArgs,
36153646
CandidateSet, Ctors, Best,
36163647
CopyInitialization, AllowExplicit,
36173648
/*OnlyListConstructors=*/false,
@@ -3821,8 +3852,8 @@ static void TryListInitialization(Sema &S,
38213852
QualType InitType = InitList->getInit(0)->getType();
38223853
if (S.Context.hasSameUnqualifiedType(InitType, DestType) ||
38233854
S.IsDerivedFrom(InitList->getLocStart(), InitType, DestType)) {
3824-
Expr *InitAsExpr = InitList->getInit(0);
3825-
TryConstructorInitialization(S, Entity, Kind, InitAsExpr, DestType,
3855+
Expr *InitListAsExpr = InitList;
3856+
TryConstructorInitialization(S, Entity, Kind, InitListAsExpr, DestType,
38263857
Sequence, /*InitListSyntax*/ false,
38273858
/*IsInitListCopy*/ true);
38283859
return;
@@ -4332,16 +4363,21 @@ static void TryReferenceInitializationCore(Sema &S,
43324363
}
43334364

43344365
// - If the initializer expression
4366+
// C++14-and-before:
43354367
// - is an xvalue, class prvalue, array prvalue, or function lvalue and
43364368
// "cv1 T1" is reference-compatible with "cv2 T2"
4369+
// C++1z:
4370+
// - is an rvalue or function lvalue and "cv1 T1" is reference-compatible
4371+
// with "cv2 T2"
43374372
// Note: functions are handled below.
43384373
if (!T1Function &&
43394374
(RefRelationship == Sema::Ref_Compatible ||
43404375
(Kind.isCStyleOrFunctionalCast() &&
43414376
RefRelationship == Sema::Ref_Related)) &&
43424377
(InitCategory.isXValue() ||
4343-
(InitCategory.isPRValue() && T2->isRecordType()) ||
4344-
(InitCategory.isPRValue() && T2->isArrayType()))) {
4378+
(InitCategory.isPRValue() &&
4379+
(S.getLangOpts().CPlusPlus1z || T2->isRecordType() ||
4380+
T2->isArrayType())))) {
43454381
ExprValueKind ValueKind = InitCategory.isXValue()? VK_XValue : VK_RValue;
43464382
if (InitCategory.isPRValue() && T2->isRecordType()) {
43474383
// The corresponding bullet in C++03 [dcl.init.ref]p5 gives the
@@ -6604,7 +6640,26 @@ InitializationSequence::Perform(Sema &S,
66046640
CreatedObject = Conversion->getReturnType()->isRecordType();
66056641
}
66066642

6643+
// C++14 and before:
6644+
// - if the function is a constructor, the call initializes a temporary
6645+
// of the cv-unqualified version of the destination type [...]
6646+
// C++1z:
6647+
// - if the function is a constructor, the call is a prvalue of the
6648+
// cv-unqualified version of the destination type whose return object
6649+
// is initialized by the constructor [...]
6650+
// Both:
6651+
// The [..] call is used to direct-initialize, according to the rules
6652+
// above, the object that is the destination of the
6653+
// copy-initialization.
6654+
// In C++14 and before, that always means the "constructors are
6655+
// considered" bullet, because we have arrived at a reference-related
6656+
// type. In C++1z, it only means that if the types are different or we
6657+
// didn't produce a prvalue, so just check for that case here.
66076658
bool RequiresCopy = !IsCopy && !isReferenceBinding(Steps.back());
6659+
if (S.getLangOpts().CPlusPlus1z && CurInit.get()->isRValue() &&
6660+
S.Context.hasSameUnqualifiedType(
6661+
Entity.getType().getNonReferenceType(), CurInit.get()->getType()))
6662+
RequiresCopy = false;
66086663
bool MaybeBindToTemp = RequiresCopy || shouldBindAsTemporary(Entity);
66096664

66106665
if (!MaybeBindToTemp && CreatedObject && shouldDestroyTemporary(Entity)) {

lib/Sema/SemaOverload.cpp

+1-1
Original file line numberDiff line numberDiff line change
@@ -4979,7 +4979,7 @@ TryObjectArgumentInitialization(Sema &S, SourceLocation Loc, QualType FromType,
49794979
// cv-qualification on the member function declaration.
49804980
//
49814981
// However, when finding an implicit conversion sequence for the argument, we
4982-
// are not allowed to create temporaries or perform user-defined conversions
4982+
// are not allowed to perform user-defined conversions
49834983
// (C++ [over.match.funcs]p5). We perform a simplified version of
49844984
// reference binding here, that allows class rvalues to bind to
49854985
// non-constant references.

0 commit comments

Comments
 (0)