summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--clang/include/clang/Sema/Initialization.h16
-rw-r--r--clang/lib/AST/ExprConstant.cpp15
-rw-r--r--clang/lib/CodeGen/CGExprCXX.cpp19
-rw-r--r--clang/lib/Sema/Sema.cpp2
-rw-r--r--clang/lib/Sema/SemaCoroutine.cpp2
-rw-r--r--clang/lib/Sema/SemaDeclCXX.cpp9
-rw-r--r--clang/lib/Sema/SemaExpr.cpp2
-rw-r--r--clang/lib/Sema/SemaExprCXX.cpp5
-rw-r--r--clang/lib/Sema/SemaLambda.cpp3
-rw-r--r--clang/lib/Sema/SemaObjCProperty.cpp3
-rw-r--r--clang/lib/Sema/SemaStmt.cpp8
-rw-r--r--clang/test/CodeGen/nrvo-tracking.cpp37
-rw-r--r--clang/test/CodeGenCXX/copy-elision.cpp34
13 files changed, 122 insertions, 33 deletions
diff --git a/clang/include/clang/Sema/Initialization.h b/clang/include/clang/Sema/Initialization.h
index 8feb66995f95..420803f8e33c 100644
--- a/clang/include/clang/Sema/Initialization.h
+++ b/clang/include/clang/Sema/Initialization.h
@@ -298,8 +298,8 @@ public:
/// Create the initialization entity for the result of a function.
static InitializedEntity InitializeResult(SourceLocation ReturnLoc,
- QualType Type, bool NRVO) {
- return InitializedEntity(EK_Result, ReturnLoc, Type, NRVO);
+ QualType Type) {
+ return InitializedEntity(EK_Result, ReturnLoc, Type);
}
static InitializedEntity InitializeStmtExprResult(SourceLocation ReturnLoc,
@@ -308,20 +308,20 @@ public:
}
static InitializedEntity InitializeBlock(SourceLocation BlockVarLoc,
- QualType Type, bool NRVO) {
- return InitializedEntity(EK_BlockElement, BlockVarLoc, Type, NRVO);
+ QualType Type) {
+ return InitializedEntity(EK_BlockElement, BlockVarLoc, Type);
}
static InitializedEntity InitializeLambdaToBlock(SourceLocation BlockVarLoc,
- QualType Type, bool NRVO) {
+ QualType Type) {
return InitializedEntity(EK_LambdaToBlockConversionBlockElement,
- BlockVarLoc, Type, NRVO);
+ BlockVarLoc, Type);
}
/// Create the initialization entity for an exception object.
static InitializedEntity InitializeException(SourceLocation ThrowLoc,
- QualType Type, bool NRVO) {
- return InitializedEntity(EK_Exception, ThrowLoc, Type, NRVO);
+ QualType Type) {
+ return InitializedEntity(EK_Exception, ThrowLoc, Type);
}
/// Create the initialization entity for an object allocated via new.
diff --git a/clang/lib/AST/ExprConstant.cpp b/clang/lib/AST/ExprConstant.cpp
index 01c0168d61a4..8fe0061a9025 100644
--- a/clang/lib/AST/ExprConstant.cpp
+++ b/clang/lib/AST/ExprConstant.cpp
@@ -9931,10 +9931,19 @@ bool RecordExprEvaluator::VisitCXXConstructExpr(const CXXConstructExpr *E,
return false;
// Avoid materializing a temporary for an elidable copy/move constructor.
- if (E->isElidable() && !ZeroInit)
- if (const MaterializeTemporaryExpr *ME
- = dyn_cast<MaterializeTemporaryExpr>(E->getArg(0)))
+ if (E->isElidable() && !ZeroInit) {
+ // FIXME: This only handles the simplest case, where the source object
+ // is passed directly as the first argument to the constructor.
+ // This should also handle stepping though implicit casts and
+ // and conversion sequences which involve two steps, with a
+ // conversion operator followed by a converting constructor.
+ const Expr *SrcObj = E->getArg(0);
+ assert(SrcObj->isTemporaryObject(Info.Ctx, FD->getParent()));
+ assert(Info.Ctx.hasSameUnqualifiedType(E->getType(), SrcObj->getType()));
+ if (const MaterializeTemporaryExpr *ME =
+ dyn_cast<MaterializeTemporaryExpr>(SrcObj))
return Visit(ME->getSubExpr());
+ }
if (ZeroInit && !ZeroInitialization(E, T))
return false;
diff --git a/clang/lib/CodeGen/CGExprCXX.cpp b/clang/lib/CodeGen/CGExprCXX.cpp
index 96cf977ca290..f42759e9db50 100644
--- a/clang/lib/CodeGen/CGExprCXX.cpp
+++ b/clang/lib/CodeGen/CGExprCXX.cpp
@@ -609,15 +609,18 @@ CodeGenFunction::EmitCXXConstructExpr(const CXXConstructExpr *E,
return;
// Elide the constructor if we're constructing from a temporary.
- // The temporary check is required because Sema sets this on NRVO
- // returns.
if (getLangOpts().ElideConstructors && E->isElidable()) {
- assert(getContext().hasSameUnqualifiedType(E->getType(),
- E->getArg(0)->getType()));
- if (E->getArg(0)->isTemporaryObject(getContext(), CD->getParent())) {
- EmitAggExpr(E->getArg(0), Dest);
- return;
- }
+ // FIXME: This only handles the simplest case, where the source object
+ // is passed directly as the first argument to the constructor.
+ // This should also handle stepping though implicit casts and
+ // conversion sequences which involve two steps, with a
+ // conversion operator followed by a converting constructor.
+ const Expr *SrcObj = E->getArg(0);
+ assert(SrcObj->isTemporaryObject(getContext(), CD->getParent()));
+ assert(
+ getContext().hasSameUnqualifiedType(E->getType(), SrcObj->getType()));
+ EmitAggExpr(SrcObj, Dest);
+ return;
}
if (const ArrayType *arrayType
diff --git a/clang/lib/Sema/Sema.cpp b/clang/lib/Sema/Sema.cpp
index a54bd8719178..191d89ea75c9 100644
--- a/clang/lib/Sema/Sema.cpp
+++ b/clang/lib/Sema/Sema.cpp
@@ -2010,7 +2010,7 @@ static void checkEscapingByref(VarDecl *VD, Sema &S) {
Expr *VarRef =
new (S.Context) DeclRefExpr(S.Context, VD, false, T, VK_LValue, Loc);
ExprResult Result;
- auto IE = InitializedEntity::InitializeBlock(Loc, T, false);
+ auto IE = InitializedEntity::InitializeBlock(Loc, T);
if (S.getLangOpts().CPlusPlus2b) {
auto *E = ImplicitCastExpr::Create(S.Context, T, CK_NoOp, VarRef, nullptr,
VK_XValue, FPOptionsOverride());
diff --git a/clang/lib/Sema/SemaCoroutine.cpp b/clang/lib/Sema/SemaCoroutine.cpp
index 94c728093e7c..3d1899a57c72 100644
--- a/clang/lib/Sema/SemaCoroutine.cpp
+++ b/clang/lib/Sema/SemaCoroutine.cpp
@@ -1533,7 +1533,7 @@ bool CoroutineStmtBuilder::makeGroDeclAndReturnStmt() {
if (GroType->isVoidType()) {
// Trigger a nice error message.
InitializedEntity Entity =
- InitializedEntity::InitializeResult(Loc, FnRetType, false);
+ InitializedEntity::InitializeResult(Loc, FnRetType);
S.PerformCopyInitialization(Entity, SourceLocation(), ReturnValue);
noteMemberDeclaredHere(S, ReturnValue, Fn);
return false;
diff --git a/clang/lib/Sema/SemaDeclCXX.cpp b/clang/lib/Sema/SemaDeclCXX.cpp
index da4f4f862095..ac01beb1bf93 100644
--- a/clang/lib/Sema/SemaDeclCXX.cpp
+++ b/clang/lib/Sema/SemaDeclCXX.cpp
@@ -15262,8 +15262,17 @@ Sema::BuildCXXConstructExpr(SourceLocation ConstructLoc, QualType DeclInitType,
// can be omitted by constructing the temporary object
// directly into the target of the omitted copy/move
if (ConstructKind == CXXConstructExpr::CK_Complete && Constructor &&
+ // FIXME: Converting constructors should also be accepted.
+ // But to fix this, the logic that digs down into a CXXConstructExpr
+ // to find the source object needs to handle it.
+ // Right now it assumes the source object is passed directly as the
+ // first argument.
Constructor->isCopyOrMoveConstructor() && hasOneRealArgument(ExprArgs)) {
Expr *SubExpr = ExprArgs[0];
+ // FIXME: Per above, this is also incorrect if we want to accept
+ // converting constructors, as isTemporaryObject will
+ // reject temporaries with different type from the
+ // CXXRecord itself.
Elidable = SubExpr->isTemporaryObject(
Context, cast<CXXRecordDecl>(FoundDecl->getDeclContext()));
}
diff --git a/clang/lib/Sema/SemaExpr.cpp b/clang/lib/Sema/SemaExpr.cpp
index 0e6c933cd4f3..f04eb9199024 100644
--- a/clang/lib/Sema/SemaExpr.cpp
+++ b/clang/lib/Sema/SemaExpr.cpp
@@ -15683,7 +15683,7 @@ ExprResult Sema::ActOnBlockStmtExpr(SourceLocation CaretLoc,
if (!Result.isInvalid()) {
Result = PerformCopyInitialization(
InitializedEntity::InitializeBlock(Var->getLocation(),
- Cap.getCaptureType(), false),
+ Cap.getCaptureType()),
Loc, Result.get());
}
diff --git a/clang/lib/Sema/SemaExprCXX.cpp b/clang/lib/Sema/SemaExprCXX.cpp
index 111ffa1f04a0..7961e7941813 100644
--- a/clang/lib/Sema/SemaExprCXX.cpp
+++ b/clang/lib/Sema/SemaExprCXX.cpp
@@ -893,9 +893,8 @@ ExprResult Sema::BuildCXXThrow(SourceLocation OpLoc, Expr *Ex,
if (CheckCXXThrowOperand(OpLoc, ExceptionObjectTy, Ex))
return ExprError();
- InitializedEntity Entity = InitializedEntity::InitializeException(
- OpLoc, ExceptionObjectTy,
- /*NRVO=*/NRInfo.isCopyElidable());
+ InitializedEntity Entity =
+ InitializedEntity::InitializeException(OpLoc, ExceptionObjectTy);
ExprResult Res = PerformMoveOrCopyInitialization(Entity, NRInfo, Ex);
if (Res.isInvalid())
return ExprError();
diff --git a/clang/lib/Sema/SemaLambda.cpp b/clang/lib/Sema/SemaLambda.cpp
index eb1e9c3e5f7e..1fcc03d997c1 100644
--- a/clang/lib/Sema/SemaLambda.cpp
+++ b/clang/lib/Sema/SemaLambda.cpp
@@ -1975,8 +1975,7 @@ ExprResult Sema::BuildBlockForLambdaConversion(SourceLocation CurrentLocation,
CallOperator->markUsed(Context);
ExprResult Init = PerformCopyInitialization(
- InitializedEntity::InitializeLambdaToBlock(ConvLocation, Src->getType(),
- /*NRVO=*/false),
+ InitializedEntity::InitializeLambdaToBlock(ConvLocation, Src->getType()),
CurrentLocation, Src);
if (!Init.isInvalid())
Init = ActOnFinishFullExpr(Init.get(), /*DiscardedValue*/ false);
diff --git a/clang/lib/Sema/SemaObjCProperty.cpp b/clang/lib/Sema/SemaObjCProperty.cpp
index a329d0f22b03..74c73ace3c5f 100644
--- a/clang/lib/Sema/SemaObjCProperty.cpp
+++ b/clang/lib/Sema/SemaObjCProperty.cpp
@@ -1467,8 +1467,7 @@ Decl *Sema::ActOnPropertyImplDecl(Scope *S,
LoadSelfExpr, true, true);
ExprResult Res = PerformCopyInitialization(
InitializedEntity::InitializeResult(PropertyDiagLoc,
- getterMethod->getReturnType(),
- /*NRVO=*/false),
+ getterMethod->getReturnType()),
PropertyDiagLoc, IvarRefExpr);
if (!Res.isInvalid()) {
Expr *ResExpr = Res.getAs<Expr>();
diff --git a/clang/lib/Sema/SemaStmt.cpp b/clang/lib/Sema/SemaStmt.cpp
index f7e4110e6110..03e9d7bc87a2 100644
--- a/clang/lib/Sema/SemaStmt.cpp
+++ b/clang/lib/Sema/SemaStmt.cpp
@@ -3653,8 +3653,8 @@ StmtResult Sema::ActOnCapScopeReturnStmt(SourceLocation ReturnLoc,
// In C++ the return statement is handled via a copy initialization.
// the C version of which boils down to CheckSingleAssignmentConstraints.
- InitializedEntity Entity = InitializedEntity::InitializeResult(
- ReturnLoc, FnRetType, NRVOCandidate != nullptr);
+ InitializedEntity Entity =
+ InitializedEntity::InitializeResult(ReturnLoc, FnRetType);
ExprResult Res = PerformMoveOrCopyInitialization(
Entity, NRInfo, RetValExp, SupressSimplerImplicitMoves);
if (Res.isInvalid()) {
@@ -4085,8 +4085,8 @@ StmtResult Sema::BuildReturnStmt(SourceLocation ReturnLoc, Expr *RetValExp) {
// the C version of which boils down to CheckSingleAssignmentConstraints.
if (!HasDependentReturnType && !RetValExp->isTypeDependent()) {
// we have a non-void function with an expression, continue checking
- InitializedEntity Entity = InitializedEntity::InitializeResult(
- ReturnLoc, RetType, NRVOCandidate != nullptr);
+ InitializedEntity Entity =
+ InitializedEntity::InitializeResult(ReturnLoc, RetType);
ExprResult Res = PerformMoveOrCopyInitialization(
Entity, NRInfo, RetValExp, SupressSimplerImplicitMoves);
if (Res.isInvalid()) {
diff --git a/clang/test/CodeGen/nrvo-tracking.cpp b/clang/test/CodeGen/nrvo-tracking.cpp
index 2d6eb9efeca2..be405878f6f9 100644
--- a/clang/test/CodeGen/nrvo-tracking.cpp
+++ b/clang/test/CodeGen/nrvo-tracking.cpp
@@ -282,3 +282,40 @@ X t5() {
}
} // namespace test_alignas
+
+namespace PR51862 {
+
+template <class T> T test() {
+ T a;
+ T b;
+ if (0)
+ return a;
+ return b;
+}
+
+struct A {
+ A();
+ A(A &);
+ A(int);
+ operator int();
+};
+
+// CHECK-LABEL: define{{.*}} void @_ZN7PR518624testINS_1AEEET_v
+// CHECK: call i32 @_ZN7PR518621AcviEv
+// CHECK-NEXT: call void @_ZN7PR518621AC1Ei
+// CHECK-NEXT: call void @llvm.lifetime.end
+template A test<A>();
+
+struct BSub {};
+struct B : BSub {
+ B();
+ B(B &);
+ B(const BSub &);
+};
+
+// CHECK-LABEL: define{{.*}} void @_ZN7PR518624testINS_1BEEET_v
+// CHECK: call void @_ZN7PR518621BC1ERKNS_4BSubE
+// CHECK-NEXT: call void @llvm.lifetime.end
+template B test<B>();
+
+} // namespace PR51862
diff --git a/clang/test/CodeGenCXX/copy-elision.cpp b/clang/test/CodeGenCXX/copy-elision.cpp
new file mode 100644
index 000000000000..72b2a5e00264
--- /dev/null
+++ b/clang/test/CodeGenCXX/copy-elision.cpp
@@ -0,0 +1,34 @@
+// RUN: %clang_cc1 -std=c++14 -triple x86_64-unknown-unknown-gnu -emit-llvm -O1 -fexperimental-new-pass-manager -o - %s | FileCheck %s
+
+template <class T> T test() {
+ return T();
+}
+
+struct A {
+ A();
+ A(A &);
+ A(int);
+ operator int();
+};
+
+// FIXME: There should be copy elision here.
+// CHECK-LABEL: define{{.*}} void @_Z4testI1AET_v
+// CHECK: call void @_ZN1AC1Ev
+// CHECK-NEXT: call i32 @_ZN1AcviEv
+// CHECK-NEXT: call void @_ZN1AC1Ei
+// CHECK-NEXT: call void @llvm.lifetime.end
+template A test<A>();
+
+struct BSub {};
+struct B : BSub {
+ B();
+ B(B &);
+ B(const BSub &);
+};
+
+// FIXME: There should be copy elision here.
+// CHECK-LABEL: define{{.*}} void @_Z4testI1BET_v
+// CHECK: call void @_ZN1BC1Ev
+// CHECK: call void @_ZN1BC1ERK4BSub
+// CHECK-NEXT: call void @llvm.lifetime.end
+template B test<B>();