diff options
author | Richard Smith <richard-llvm@metafoo.co.uk> | 2018-07-23 22:56:45 +0000 |
---|---|---|
committer | Richard Smith <richard-llvm@metafoo.co.uk> | 2018-07-23 22:56:45 +0000 |
commit | b68cfed35f46283d6a8df0a96dacc806720384a2 (patch) | |
tree | 14af5485bb7c3f00c60b8b985356df07190ad221 | |
parent | fc6962eb2acb82e92637824fd3bb02b28619a195 (diff) | |
download | clang-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.td | 6 | ||||
-rw-r--r-- | lib/CodeGen/CGCleanup.cpp | 15 | ||||
-rw-r--r-- | lib/CodeGen/CGDecl.cpp | 3 | ||||
-rw-r--r-- | lib/CodeGen/CodeGenFunction.h | 239 | ||||
-rw-r--r-- | lib/Sema/SemaInit.cpp | 9 | ||||
-rw-r--r-- | test/CodeGenCXX/temporaries.cpp | 98 | ||||
-rw-r--r-- | test/SemaCXX/conditional-expr.cpp | 6 |
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()}; } } |