summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRichard Smith <richard-llvm@metafoo.co.uk>2018-07-23 22:56:45 +0000
committerRichard Smith <richard-llvm@metafoo.co.uk>2018-07-23 22:56:45 +0000
commitb68cfed35f46283d6a8df0a96dacc806720384a2 (patch)
tree14af5485bb7c3f00c60b8b985356df07190ad221
parentfc6962eb2acb82e92637824fd3bb02b28619a195 (diff)
downloadclang-b68cfed35f46283d6a8df0a96dacc806720384a2.tar.gz
Support lifetime-extension of conditional temporaries.
git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@337767 91177308-0d34-0410-b5e6-96231b3b80d8
-rw-r--r--include/clang/Basic/DiagnosticSemaKinds.td6
-rw-r--r--lib/CodeGen/CGCleanup.cpp15
-rw-r--r--lib/CodeGen/CGDecl.cpp3
-rw-r--r--lib/CodeGen/CodeGenFunction.h239
-rw-r--r--lib/Sema/SemaInit.cpp9
-rw-r--r--test/CodeGenCXX/temporaries.cpp98
-rw-r--r--test/SemaCXX/conditional-expr.cpp6
7 files changed, 247 insertions, 129 deletions
diff --git a/include/clang/Basic/DiagnosticSemaKinds.td b/include/clang/Basic/DiagnosticSemaKinds.td
index 98770935d1..62fd7de8bb 100644
--- a/include/clang/Basic/DiagnosticSemaKinds.td
+++ b/include/clang/Basic/DiagnosticSemaKinds.td
@@ -7897,14 +7897,12 @@ def warn_new_dangling_initializer_list : Warning<
InGroup<DanglingInitializerList>;
def warn_unsupported_temporary_not_extended : Warning<
"sorry, lifetime extension of temporary created "
- "%select{by aggregate initialization using default member initializer|"
- "within conditional expression}0 "
+ "by aggregate initialization using default member initializer "
"is not supported; lifetime of temporary "
"will end at the end of the full-expression">, InGroup<DanglingField>;
def warn_unsupported_init_list_not_extended : Warning<
"sorry, lifetime extension of backing array of initializer list created "
- "%select{by aggregate initialization using default member initializer|"
- "within conditional expression}0 "
+ "by aggregate initialization using default member initializer "
"is not supported; lifetime of backing array will end at the end of the "
"full-expression">, InGroup<DanglingInitializerList>;
diff --git a/lib/CodeGen/CGCleanup.cpp b/lib/CodeGen/CGCleanup.cpp
index 34bfd07caf..cfd230997e 100644
--- a/lib/CodeGen/CGCleanup.cpp
+++ b/lib/CodeGen/CGCleanup.cpp
@@ -281,7 +281,7 @@ void EHScopeStack::popNullFixups() {
BranchFixups.pop_back();
}
-void CodeGenFunction::initFullExprCleanup() {
+Address CodeGenFunction::createCleanupActiveFlag() {
// Create a variable to decide whether the cleanup needs to be run.
Address active = CreateTempAllocaWithoutCast(
Builder.getInt1Ty(), CharUnits::One(), "cleanup.cond");
@@ -293,10 +293,14 @@ void CodeGenFunction::initFullExprCleanup() {
// Initialize it to true at the current location.
Builder.CreateStore(Builder.getTrue(), active);
+ return active;
+}
+
+void CodeGenFunction::initFullExprCleanupWithFlag(Address ActiveFlag) {
// Set that as the active flag in the cleanup.
EHCleanupScope &cleanup = cast<EHCleanupScope>(*EHStack.begin());
assert(!cleanup.hasActiveFlag() && "cleanup already has active flag?");
- cleanup.setActiveFlag(active);
+ cleanup.setActiveFlag(ActiveFlag);
if (cleanup.isNormalCleanup()) cleanup.setTestFlagInNormalCleanup();
if (cleanup.isEHCleanup()) cleanup.setTestFlagInEHCleanup();
@@ -494,6 +498,13 @@ void CodeGenFunction::PopCleanupBlocks(
&LifetimeExtendedCleanupStack[I],
Header.getSize());
I += Header.getSize();
+
+ if (Header.isConditional()) {
+ Address ActiveFlag =
+ reinterpret_cast<Address &>(LifetimeExtendedCleanupStack[I]);
+ initFullExprCleanupWithFlag(ActiveFlag);
+ I += sizeof(ActiveFlag);
+ }
}
LifetimeExtendedCleanupStack.resize(OldLifetimeExtendedSize);
}
diff --git a/lib/CodeGen/CGDecl.cpp b/lib/CodeGen/CGDecl.cpp
index 89c5789c3e..16646f0a6a 100644
--- a/lib/CodeGen/CGDecl.cpp
+++ b/lib/CodeGen/CGDecl.cpp
@@ -1651,9 +1651,6 @@ void CodeGenFunction::pushStackRestore(CleanupKind Kind, Address SPMem) {
void CodeGenFunction::pushLifetimeExtendedDestroy(
CleanupKind cleanupKind, Address addr, QualType type,
Destroyer *destroyer, bool useEHCleanupForArray) {
- assert(!isInConditionalBranch() &&
- "performing lifetime extension from within conditional");
-
// Push an EH-only cleanup for the object now.
// FIXME: When popping normal cleanups, we need to keep this EH cleanup
// around in case a temporary's destructor throws an exception.
diff --git a/lib/CodeGen/CodeGenFunction.h b/lib/CodeGen/CodeGenFunction.h
index 1fc445c66d..752d670a5f 100644
--- a/lib/CodeGen/CodeGenFunction.h
+++ b/lib/CodeGen/CodeGenFunction.h
@@ -138,6 +138,88 @@ enum SanitizerHandler {
#undef SANITIZER_CHECK
};
+/// Helper class with most of the code for saving a value for a
+/// conditional expression cleanup.
+struct DominatingLLVMValue {
+ typedef llvm::PointerIntPair<llvm::Value*, 1, bool> saved_type;
+
+ /// Answer whether the given value needs extra work to be saved.
+ static bool needsSaving(llvm::Value *value) {
+ // If it's not an instruction, we don't need to save.
+ if (!isa<llvm::Instruction>(value)) return false;
+
+ // If it's an instruction in the entry block, we don't need to save.
+ llvm::BasicBlock *block = cast<llvm::Instruction>(value)->getParent();
+ return (block != &block->getParent()->getEntryBlock());
+ }
+
+ static saved_type save(CodeGenFunction &CGF, llvm::Value *value);
+ static llvm::Value *restore(CodeGenFunction &CGF, saved_type value);
+};
+
+/// A partial specialization of DominatingValue for llvm::Values that
+/// might be llvm::Instructions.
+template <class T> struct DominatingPointer<T,true> : DominatingLLVMValue {
+ typedef T *type;
+ static type restore(CodeGenFunction &CGF, saved_type value) {
+ return static_cast<T*>(DominatingLLVMValue::restore(CGF, value));
+ }
+};
+
+/// A specialization of DominatingValue for Address.
+template <> struct DominatingValue<Address> {
+ typedef Address type;
+
+ struct saved_type {
+ DominatingLLVMValue::saved_type SavedValue;
+ CharUnits Alignment;
+ };
+
+ static bool needsSaving(type value) {
+ return DominatingLLVMValue::needsSaving(value.getPointer());
+ }
+ static saved_type save(CodeGenFunction &CGF, type value) {
+ return { DominatingLLVMValue::save(CGF, value.getPointer()),
+ value.getAlignment() };
+ }
+ static type restore(CodeGenFunction &CGF, saved_type value) {
+ return Address(DominatingLLVMValue::restore(CGF, value.SavedValue),
+ value.Alignment);
+ }
+};
+
+/// A specialization of DominatingValue for RValue.
+template <> struct DominatingValue<RValue> {
+ typedef RValue type;
+ class saved_type {
+ enum Kind { ScalarLiteral, ScalarAddress, AggregateLiteral,
+ AggregateAddress, ComplexAddress };
+
+ llvm::Value *Value;
+ unsigned K : 3;
+ unsigned Align : 29;
+ saved_type(llvm::Value *v, Kind k, unsigned a = 0)
+ : Value(v), K(k), Align(a) {}
+
+ public:
+ static bool needsSaving(RValue value);
+ static saved_type save(CodeGenFunction &CGF, RValue value);
+ RValue restore(CodeGenFunction &CGF);
+
+ // implementations in CGCleanup.cpp
+ };
+
+ static bool needsSaving(type value) {
+ return saved_type::needsSaving(value);
+ }
+ static saved_type save(CodeGenFunction &CGF, type value) {
+ return saved_type::save(CGF, value);
+ }
+ static type restore(CodeGenFunction &CGF, saved_type value) {
+ return value.restore(CGF);
+ }
+};
+
/// CodeGenFunction - This class organizes the per-function state that is used
/// while generating LLVM code.
class CodeGenFunction : public CodeGenTypeCache {
@@ -427,10 +509,13 @@ public:
/// The size of the following cleanup object.
unsigned Size;
/// The kind of cleanup to push: a value from the CleanupKind enumeration.
- CleanupKind Kind;
+ unsigned Kind : 31;
+ /// Whether this is a conditional cleanup.
+ unsigned IsConditional : 1;
size_t getSize() const { return Size; }
- CleanupKind getKind() const { return Kind; }
+ CleanupKind getKind() const { return (CleanupKind)Kind; }
+ bool isConditional() const { return IsConditional; }
};
/// i32s containing the indexes of the cleanup destinations.
@@ -529,24 +614,48 @@ public:
/// full-expression.
template <class T, class... As>
void pushCleanupAfterFullExpr(CleanupKind Kind, As... A) {
- assert(!isInConditionalBranch() && "can't defer conditional cleanup");
+ if (!isInConditionalBranch())
+ return pushCleanupAfterFullExprImpl<T>(Kind, Address::invalid(), A...);
+
+ Address ActiveFlag = createCleanupActiveFlag();
+ assert(!DominatingValue<Address>::needsSaving(ActiveFlag) &&
+ "cleanup active flag should never need saving");
+
+ typedef std::tuple<typename DominatingValue<As>::saved_type...> SavedTuple;
+ SavedTuple Saved{saveValueInCond(A)...};
+
+ typedef EHScopeStack::ConditionalCleanup<T, As...> CleanupType;
+ pushCleanupAfterFullExprImpl<CleanupType>(Kind, ActiveFlag, Saved);
+ }
- LifetimeExtendedCleanupHeader Header = { sizeof(T), Kind };
+ template <class T, class... As>
+ void pushCleanupAfterFullExprImpl(CleanupKind Kind, Address ActiveFlag,
+ As... A) {
+ LifetimeExtendedCleanupHeader Header = {sizeof(T), Kind,
+ ActiveFlag.isValid()};
size_t OldSize = LifetimeExtendedCleanupStack.size();
LifetimeExtendedCleanupStack.resize(
- LifetimeExtendedCleanupStack.size() + sizeof(Header) + Header.Size);
+ LifetimeExtendedCleanupStack.size() + sizeof(Header) + Header.Size +
+ (Header.IsConditional ? sizeof(ActiveFlag) : 0));
static_assert(sizeof(Header) % alignof(T) == 0,
"Cleanup will be allocated on misaligned address");
char *Buffer = &LifetimeExtendedCleanupStack[OldSize];
new (Buffer) LifetimeExtendedCleanupHeader(Header);
new (Buffer + sizeof(Header)) T(A...);
+ if (Header.IsConditional)
+ new (Buffer + sizeof(Header) + sizeof(T)) Address(ActiveFlag);
}
- /// Set up the last cleaup that was pushed as a conditional
+ /// Set up the last cleanup that was pushed as a conditional
/// full-expression cleanup.
- void initFullExprCleanup();
+ void initFullExprCleanup() {
+ initFullExprCleanupWithFlag(createCleanupActiveFlag());
+ }
+
+ void initFullExprCleanupWithFlag(Address ActiveFlag);
+ Address createCleanupActiveFlag();
/// PushDestructorCleanup - Push a cleanup to call the
/// complete-object destructor of an object of the given type at the
@@ -4176,107 +4285,29 @@ private:
FormResolverCondition(const TargetMultiVersionResolverOption &RO);
};
-/// Helper class with most of the code for saving a value for a
-/// conditional expression cleanup.
-struct DominatingLLVMValue {
- typedef llvm::PointerIntPair<llvm::Value*, 1, bool> saved_type;
-
- /// Answer whether the given value needs extra work to be saved.
- static bool needsSaving(llvm::Value *value) {
- // If it's not an instruction, we don't need to save.
- if (!isa<llvm::Instruction>(value)) return false;
-
- // If it's an instruction in the entry block, we don't need to save.
- llvm::BasicBlock *block = cast<llvm::Instruction>(value)->getParent();
- return (block != &block->getParent()->getEntryBlock());
- }
-
- /// Try to save the given value.
- static saved_type save(CodeGenFunction &CGF, llvm::Value *value) {
- if (!needsSaving(value)) return saved_type(value, false);
-
- // Otherwise, we need an alloca.
- auto align = CharUnits::fromQuantity(
- CGF.CGM.getDataLayout().getPrefTypeAlignment(value->getType()));
- Address alloca =
- CGF.CreateTempAlloca(value->getType(), align, "cond-cleanup.save");
- CGF.Builder.CreateStore(value, alloca);
-
- return saved_type(alloca.getPointer(), true);
- }
-
- static llvm::Value *restore(CodeGenFunction &CGF, saved_type value) {
- // If the value says it wasn't saved, trust that it's still dominating.
- if (!value.getInt()) return value.getPointer();
-
- // Otherwise, it should be an alloca instruction, as set up in save().
- auto alloca = cast<llvm::AllocaInst>(value.getPointer());
- return CGF.Builder.CreateAlignedLoad(alloca, alloca->getAlignment());
- }
-};
-
-/// A partial specialization of DominatingValue for llvm::Values that
-/// might be llvm::Instructions.
-template <class T> struct DominatingPointer<T,true> : DominatingLLVMValue {
- typedef T *type;
- static type restore(CodeGenFunction &CGF, saved_type value) {
- return static_cast<T*>(DominatingLLVMValue::restore(CGF, value));
- }
-};
-
-/// A specialization of DominatingValue for Address.
-template <> struct DominatingValue<Address> {
- typedef Address type;
+inline DominatingLLVMValue::saved_type
+DominatingLLVMValue::save(CodeGenFunction &CGF, llvm::Value *value) {
+ if (!needsSaving(value)) return saved_type(value, false);
- struct saved_type {
- DominatingLLVMValue::saved_type SavedValue;
- CharUnits Alignment;
- };
+ // Otherwise, we need an alloca.
+ auto align = CharUnits::fromQuantity(
+ CGF.CGM.getDataLayout().getPrefTypeAlignment(value->getType()));
+ Address alloca =
+ CGF.CreateTempAlloca(value->getType(), align, "cond-cleanup.save");
+ CGF.Builder.CreateStore(value, alloca);
- static bool needsSaving(type value) {
- return DominatingLLVMValue::needsSaving(value.getPointer());
- }
- static saved_type save(CodeGenFunction &CGF, type value) {
- return { DominatingLLVMValue::save(CGF, value.getPointer()),
- value.getAlignment() };
- }
- static type restore(CodeGenFunction &CGF, saved_type value) {
- return Address(DominatingLLVMValue::restore(CGF, value.SavedValue),
- value.Alignment);
- }
-};
-
-/// A specialization of DominatingValue for RValue.
-template <> struct DominatingValue<RValue> {
- typedef RValue type;
- class saved_type {
- enum Kind { ScalarLiteral, ScalarAddress, AggregateLiteral,
- AggregateAddress, ComplexAddress };
-
- llvm::Value *Value;
- unsigned K : 3;
- unsigned Align : 29;
- saved_type(llvm::Value *v, Kind k, unsigned a = 0)
- : Value(v), K(k), Align(a) {}
-
- public:
- static bool needsSaving(RValue value);
- static saved_type save(CodeGenFunction &CGF, RValue value);
- RValue restore(CodeGenFunction &CGF);
+ return saved_type(alloca.getPointer(), true);
+}
- // implementations in CGCleanup.cpp
- };
+inline llvm::Value *DominatingLLVMValue::restore(CodeGenFunction &CGF,
+ saved_type value) {
+ // If the value says it wasn't saved, trust that it's still dominating.
+ if (!value.getInt()) return value.getPointer();
- static bool needsSaving(type value) {
- return saved_type::needsSaving(value);
- }
- static saved_type save(CodeGenFunction &CGF, type value) {
- return saved_type::save(CGF, value);
- }
- static type restore(CodeGenFunction &CGF, saved_type value) {
- return value.restore(CGF);
- }
-};
+ // Otherwise, it should be an alloca instruction, as set up in save().
+ auto alloca = cast<llvm::AllocaInst>(value.getPointer());
+ return CGF.Builder.CreateAlignedLoad(alloca, alloca->getAlignment());
+}
} // end namespace CodeGen
} // end namespace clang
diff --git a/lib/Sema/SemaInit.cpp b/lib/Sema/SemaInit.cpp
index 208576e75d..be284daab0 100644
--- a/lib/Sema/SemaInit.cpp
+++ b/lib/Sema/SemaInit.cpp
@@ -6362,7 +6362,6 @@ using Local = Expr*;
struct IndirectLocalPathEntry {
enum EntryKind {
DefaultInit,
- Conditional,
AddressOf,
VarInit,
LValToRVal,
@@ -6498,7 +6497,6 @@ static void visitLocalsRetainedByReferenceBinding(IndirectLocalPath &Path,
case Stmt::ConditionalOperatorClass:
case Stmt::BinaryConditionalOperatorClass: {
- Path.push_back({IndirectLocalPathEntry::Conditional, Init});
auto *C = cast<AbstractConditionalOperator>(Init);
if (!C->getTrueExpr()->getType()->isVoidType())
visitLocalsRetainedByReferenceBinding(Path, C->getTrueExpr(), RK, Visit);
@@ -6685,7 +6683,6 @@ static void visitLocalsRetainedByInitializer(IndirectLocalPath &Path,
case Stmt::ConditionalOperatorClass:
case Stmt::BinaryConditionalOperatorClass: {
- Path.push_back({IndirectLocalPathEntry::Conditional, Init});
auto *C = cast<AbstractConditionalOperator>(Init);
// In C++, we can have a throw-expression operand, which has 'void' type
// and isn't interesting from a lifetime perspective.
@@ -6717,8 +6714,7 @@ static void visitLocalsRetainedByInitializer(IndirectLocalPath &Path,
/// supposed to lifetime-extend along (but don't).
static bool shouldLifetimeExtendThroughPath(const IndirectLocalPath &Path) {
for (auto Elem : Path) {
- if (Elem.Kind != IndirectLocalPathEntry::DefaultInit &&
- Elem.Kind != IndirectLocalPathEntry::Conditional)
+ if (Elem.Kind != IndirectLocalPathEntry::DefaultInit)
return false;
}
return true;
@@ -6731,7 +6727,6 @@ static SourceRange nextPathEntryRange(const IndirectLocalPath &Path, unsigned I,
switch (Path[I].Kind) {
case IndirectLocalPathEntry::AddressOf:
case IndirectLocalPathEntry::LValToRVal:
- case IndirectLocalPathEntry::Conditional:
// These exist primarily to mark the path as not permitting or
// supporting lifetime extension.
break;
@@ -6791,7 +6786,6 @@ void Sema::checkInitializerLifetime(const InitializedEntity &Entity,
Diag(DiagLoc, RK == RK_ReferenceBinding
? diag::warn_unsupported_temporary_not_extended
: diag::warn_unsupported_init_list_not_extended)
- << (Path.front().Kind == IndirectLocalPathEntry::Conditional)
<< DiagRange;
} else {
// FIXME: Warn on this.
@@ -6898,7 +6892,6 @@ void Sema::checkInitializerLifetime(const InitializedEntity &Entity,
switch (Elem.Kind) {
case IndirectLocalPathEntry::AddressOf:
case IndirectLocalPathEntry::LValToRVal:
- case IndirectLocalPathEntry::Conditional:
// These exist primarily to mark the path as not permitting or
// supporting lifetime extension.
break;
diff --git a/test/CodeGenCXX/temporaries.cpp b/test/CodeGenCXX/temporaries.cpp
index 2eb27e71cb..294ff29a8e 100644
--- a/test/CodeGenCXX/temporaries.cpp
+++ b/test/CodeGenCXX/temporaries.cpp
@@ -1,5 +1,6 @@
-// RUN: %clang_cc1 -emit-llvm %s -o - -triple=x86_64-apple-darwin9 -std=c++11 | FileCheck %s -check-prefixes=CHECK,NULL-INVALID
-// RUN: %clang_cc1 -emit-llvm %s -o - -triple=x86_64-apple-darwin9 -std=c++11 -fno-delete-null-pointer-checks | FileCheck %s -check-prefixes=CHECK,NULL-VALID
+// RUN: %clang_cc1 -emit-llvm %s -o - -triple=x86_64-apple-darwin9 -std=c++11 | FileCheck %s -check-prefixes=CHECK,NULL-INVALID,CHECK-CXX11
+// RUN: %clang_cc1 -emit-llvm %s -o - -triple=x86_64-apple-darwin9 -std=c++17 | FileCheck %s -check-prefixes=CHECK,NULL-INVALID,CHECK-CXX17
+// RUN: %clang_cc1 -emit-llvm %s -o - -triple=x86_64-apple-darwin9 -std=c++11 -fno-delete-null-pointer-checks | FileCheck %s -check-prefixes=CHECK,NULL-VALID,CHECK-CXX11
namespace PR16263 {
const unsigned int n = 1234;
@@ -46,7 +47,9 @@ namespace PR20227 {
namespace BraceInit {
typedef const int &CIR;
CIR x = CIR{3};
- // CHECK: @_ZGRN9BraceInit1xE_ = internal constant i32 3
+ // CHECK-CXX11: @_ZGRN9BraceInit1xE_ = internal constant i32 3
+ // FIXME: This should still be emitted as 'constant' in C++17.
+ // CHECK-CXX17: @_ZGRN9BraceInit1xE_ = internal global i32 3
// CHECK: @_ZN9BraceInit1xE = constant i32* @_ZGRN9BraceInit1xE_
}
@@ -804,3 +807,92 @@ namespace PR14130 {
// CHECK: call void @_ZN7PR141301SC1Ei({{.*}} @_ZGRN7PR141301vE_, i32 0)
// CHECK: store {{.*}} @_ZGRN7PR141301vE_, {{.*}} @_ZN7PR141301vE
}
+
+namespace Conditional {
+ struct A {};
+ struct B : A { B(); ~B(); };
+ struct C : A { C(); ~C(); };
+
+ void g();
+
+ // CHECK-LABEL: define {{.*}} @_ZN11Conditional1fEb(
+ void f(bool b) {
+ // CHECK: store i1 false, i1* %[[CLEANUP_B:.*]],
+ // CHECK: store i1 false, i1* %[[CLEANUP_C:.*]],
+ // CHECK: br i1
+ //
+ // CHECK: call {{.*}} @_ZN11Conditional1BC1Ev(
+ // CHECK: store i1 true, i1* %[[CLEANUP_B]],
+ // CHECK: br label
+ //
+ // CHECK: call {{.*}} @_ZN11Conditional1CC1Ev(
+ // CHECK: store i1 true, i1* %[[CLEANUP_C]],
+ // CHECK: br label
+ A &&r = b ? static_cast<A&&>(B()) : static_cast<A&&>(C());
+
+ // CHECK: call {{.*}} @_ZN11Conditional1gEv(
+ g();
+
+ // CHECK: load {{.*}} %[[CLEANUP_C]]
+ // CHECK: br i1
+ // CHECK: call {{.*}} @_ZN11Conditional1CD1Ev(
+ // CHECK: br label
+
+ // CHECK: load {{.*}} %[[CLEANUP_B]]
+ // CHECK: br i1
+ // CHECK: call {{.*}} @_ZN11Conditional1BD1Ev(
+ // CHECK: br label
+ }
+
+ struct D { A &&a; };
+ // CHECK-LABEL: define {{.*}} @_ZN11Conditional10f_indirectEb(
+ void f_indirect(bool b) {
+ // CHECK: store i1 false, i1* %[[CLEANUP_B:.*]],
+ // CHECK: store i1 false, i1* %[[CLEANUP_C:.*]],
+ // CHECK: br i1
+ //
+ // CHECK: call {{.*}} @_ZN11Conditional1BC1Ev(
+ // CHECK: store i1 true, i1* %[[CLEANUP_B]],
+ // CHECK: br label
+ //
+ // CHECK: call {{.*}} @_ZN11Conditional1CC1Ev(
+ // CHECK: store i1 true, i1* %[[CLEANUP_C]],
+ // CHECK: br label
+ D d = b ? D{B()} : D{C()};
+
+ // In C++17, the expression D{...} directly initializes the 'd' object, so
+ // lifetime-extending the temporaries to the lifetime of the D object
+ // extends them past the call to g().
+ //
+ // In C++14 and before, D is move-constructed from the result of the
+ // conditional expression, so no lifetime extension occurs.
+
+ // CHECK-CXX17: call {{.*}} @_ZN11Conditional1gEv(
+
+ // CHECK: load {{.*}} %[[CLEANUP_C]]
+ // CHECK: br i1
+ // CHECK: call {{.*}} @_ZN11Conditional1CD1Ev(
+ // CHECK: br label
+
+ // CHECK: load {{.*}} %[[CLEANUP_B]]
+ // CHECK: br i1
+ // CHECK: call {{.*}} @_ZN11Conditional1BD1Ev(
+ // CHECK: br label
+
+ // CHECK-CXX11: call {{.*}} @_ZN11Conditional1gEv(
+ g();
+ }
+
+ extern bool b;
+ // CHECK: load {{.*}} @_ZN11Conditional1b
+ // CHECK: br i1
+ //
+ // CHECK: call {{.*}} @_ZN11Conditional1BC1Ev({{.*}} @_ZGRN11Conditional1rE_)
+ // CHECK: call {{.*}} @__cxa_atexit({{.*}} @_ZN11Conditional1BD1Ev {{.*}} @_ZGRN11Conditional1rE_,
+ // CHECK: br label
+ //
+ // CHECK: call {{.*}} @_ZN11Conditional1CC1Ev({{.*}} @_ZGRN11Conditional1rE0_)
+ // CHECK: call {{.*}} @__cxa_atexit({{.*}} @_ZN11Conditional1CD1Ev {{.*}} @_ZGRN11Conditional1rE0_,
+ // CHECK: br label
+ A &&r = b ? static_cast<A&&>(B()) : static_cast<A&&>(C());
+}
diff --git a/test/SemaCXX/conditional-expr.cpp b/test/SemaCXX/conditional-expr.cpp
index ab19ce54b6..8d0555ea50 100644
--- a/test/SemaCXX/conditional-expr.cpp
+++ b/test/SemaCXX/conditional-expr.cpp
@@ -229,7 +229,7 @@ void test()
// be properly tested at runtime, though.
const Abstract &abstract1 = true ? static_cast<const Abstract&>(Derived1()) : Derived2(); // expected-error {{allocating an object of abstract class type 'const Abstract'}}
- const Abstract &abstract2 = true ? static_cast<const Abstract&>(Derived1()) : throw 3; // expected-warning-re {{sorry, lifetime extension {{.*}} not supported}}
+ const Abstract &abstract2 = true ? static_cast<const Abstract&>(Derived1()) : throw 3;
}
namespace PR6595 {
@@ -401,15 +401,11 @@ namespace lifetime_extension {
struct C : A { C(); ~C(); };
void f(bool b) {
- // expected-warning@+1 2{{sorry, lifetime extension of temporary created within conditional expression is not supported}}
A &&r = b ? static_cast<A&&>(B()) : static_cast<A&&>(C());
}
struct D { A &&a; };
void f_indirect(bool b) {
-#if __cplusplus >= 201702L
- // expected-warning@+2 2{{sorry, lifetime extension of temporary created within conditional expression is not supported}}
-#endif
D d = b ? D{B()} : D{C()};
}
}