r337767 - Support lifetime-extension of conditional temporaries.

Richard Smith via cfe-commits cfe-commits at lists.llvm.org
Mon Jul 23 15:56:45 PDT 2018


Author: rsmith
Date: Mon Jul 23 15:56:45 2018
New Revision: 337767

URL: http://llvm.org/viewvc/llvm-project?rev=337767&view=rev
Log:
Support lifetime-extension of conditional temporaries.

Modified:
    cfe/trunk/include/clang/Basic/DiagnosticSemaKinds.td
    cfe/trunk/lib/CodeGen/CGCleanup.cpp
    cfe/trunk/lib/CodeGen/CGDecl.cpp
    cfe/trunk/lib/CodeGen/CodeGenFunction.h
    cfe/trunk/lib/Sema/SemaInit.cpp
    cfe/trunk/test/CodeGenCXX/temporaries.cpp
    cfe/trunk/test/SemaCXX/conditional-expr.cpp

Modified: cfe/trunk/include/clang/Basic/DiagnosticSemaKinds.td
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/Basic/DiagnosticSemaKinds.td?rev=337767&r1=337766&r2=337767&view=diff
==============================================================================
--- cfe/trunk/include/clang/Basic/DiagnosticSemaKinds.td (original)
+++ cfe/trunk/include/clang/Basic/DiagnosticSemaKinds.td Mon Jul 23 15:56:45 2018
@@ -7897,14 +7897,12 @@ def warn_new_dangling_initializer_list :
   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>;
 

Modified: cfe/trunk/lib/CodeGen/CGCleanup.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/CodeGen/CGCleanup.cpp?rev=337767&r1=337766&r2=337767&view=diff
==============================================================================
--- cfe/trunk/lib/CodeGen/CGCleanup.cpp (original)
+++ cfe/trunk/lib/CodeGen/CGCleanup.cpp Mon Jul 23 15:56:45 2018
@@ -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::initFullExprCleanu
   // 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);
 }

Modified: cfe/trunk/lib/CodeGen/CGDecl.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/CodeGen/CGDecl.cpp?rev=337767&r1=337766&r2=337767&view=diff
==============================================================================
--- cfe/trunk/lib/CodeGen/CGDecl.cpp (original)
+++ cfe/trunk/lib/CodeGen/CGDecl.cpp Mon Jul 23 15:56:45 2018
@@ -1651,9 +1651,6 @@ void CodeGenFunction::pushStackRestore(C
 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.

Modified: cfe/trunk/lib/CodeGen/CodeGenFunction.h
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/CodeGen/CodeGenFunction.h?rev=337767&r1=337766&r2=337767&view=diff
==============================================================================
--- cfe/trunk/lib/CodeGen/CodeGenFunction.h (original)
+++ cfe/trunk/lib/CodeGen/CodeGenFunction.h Mon Jul 23 15:56:45 2018
@@ -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;
-
-  /// 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;
-
-  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);
-  }
-};
+inline DominatingLLVMValue::saved_type
+DominatingLLVMValue::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);
+}
+
+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, 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

Modified: cfe/trunk/lib/Sema/SemaInit.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Sema/SemaInit.cpp?rev=337767&r1=337766&r2=337767&view=diff
==============================================================================
--- cfe/trunk/lib/Sema/SemaInit.cpp (original)
+++ cfe/trunk/lib/Sema/SemaInit.cpp Mon Jul 23 15:56:45 2018
@@ -6362,7 +6362,6 @@ using Local = Expr*;
 struct IndirectLocalPathEntry {
   enum EntryKind {
     DefaultInit,
-    Conditional,
     AddressOf,
     VarInit,
     LValToRVal,
@@ -6498,7 +6497,6 @@ static void visitLocalsRetainedByReferen
 
   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 visitLocalsRetainedByInitial
 
   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 visitLocalsRetainedByInitial
 /// 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(co
     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(cons
         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(cons
       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;

Modified: cfe/trunk/test/CodeGenCXX/temporaries.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/CodeGenCXX/temporaries.cpp?rev=337767&r1=337766&r2=337767&view=diff
==============================================================================
--- cfe/trunk/test/CodeGenCXX/temporaries.cpp (original)
+++ cfe/trunk/test/CodeGenCXX/temporaries.cpp Mon Jul 23 15:56:45 2018
@@ -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());
+}

Modified: cfe/trunk/test/SemaCXX/conditional-expr.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/SemaCXX/conditional-expr.cpp?rev=337767&r1=337766&r2=337767&view=diff
==============================================================================
--- cfe/trunk/test/SemaCXX/conditional-expr.cpp (original)
+++ cfe/trunk/test/SemaCXX/conditional-expr.cpp Mon Jul 23 15:56:45 2018
@@ -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 at +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 at +2 2{{sorry, lifetime extension of temporary created within conditional expression is not supported}}
-#endif
     D d = b ? D{B()} : D{C()};
   }
 }




More information about the cfe-commits mailing list