mirror of https://github.com/microsoft/clang.git
Support lifetime-extension of conditional temporaries.
git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@337767 91177308-0d34-0410-b5e6-96231b3b80d8
This commit is contained in:
parent
fc6962eb2a
commit
b68cfed35f
|
@ -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>;
|
||||
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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...);
|
||||
|
||||
LifetimeExtendedCleanupHeader Header = { sizeof(T), Kind };
|
||||
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);
|
||||
}
|
||||
|
||||
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;
|
||||
inline DominatingLLVMValue::saved_type
|
||||
DominatingLLVMValue::save(CodeGenFunction &CGF, llvm::Value *value) {
|
||||
if (!needsSaving(value)) return saved_type(value, false);
|
||||
|
||||
/// 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;
|
||||
// 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);
|
||||
|
||||
// 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());
|
||||
}
|
||||
return saved_type(alloca.getPointer(), true);
|
||||
}
|
||||
|
||||
/// Try to save the given value.
|
||||
static saved_type save(CodeGenFunction &CGF, llvm::Value *value) {
|
||||
if (!needsSaving(value)) return saved_type(value, false);
|
||||
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();
|
||||
|
||||
// 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;
|
||||
|
||||
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);
|
||||
}
|
||||
};
|
||||
// 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
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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());
|
||||
}
|
||||
|
|
|
@ -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()};
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue