]> granicus.if.org Git - clang/commitdiff
PR16263: Implement current direction of core issue 1376. Binding a reference to
authorRichard Smith <richard-llvm@metafoo.co.uk>
Sat, 15 Jun 2013 00:30:29 +0000 (00:30 +0000)
committerRichard Smith <richard-llvm@metafoo.co.uk>
Sat, 15 Jun 2013 00:30:29 +0000 (00:30 +0000)
the result of a cast-to-reference-type lifetime-extends the object to which the
reference inside the cast binds.

This requires us to look for subobject adjustments on both the inside and the
outside of the MaterializeTemporaryExpr when looking for a temporary to
lifetime-extend (which we also need for core issue 616, and possibly 1213).

git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@184024 91177308-0d34-0410-b5e6-96231b3b80d8

lib/AST/Expr.cpp
lib/Sema/SemaInit.cpp
test/CXX/expr/expr.prim/expr.prim.lambda/p11-1y.cpp
test/CodeGenCXX/temporaries.cpp

index aaf80207920f840de348769ac5611467621e3fd2..c4be32ea70d7ab4f33840efd8ffc061aa78f7706 100644 (file)
@@ -73,10 +73,10 @@ const Expr *Expr::skipRValueSubobjectAdjustments(
         continue;
       }
     } else if (const MemberExpr *ME = dyn_cast<MemberExpr>(E)) {
-      if (!ME->isArrow() && ME->getBase()->isRValue()) {
+      if (!ME->isArrow()) {
         assert(ME->getBase()->getType()->isRecordType());
         if (FieldDecl *Field = dyn_cast<FieldDecl>(ME->getMemberDecl())) {
-          if (!Field->isBitField()) {
+          if (!Field->isBitField() && !Field->getType()->isReferenceType()) {
             E = ME->getBase();
             Adjustments.push_back(SubobjectAdjustment(Field));
             continue;
index cf595b49de6c8530868741463240ec4ebe2b389f..863079c6d8f142f93caf934ba75563611f3b041c 100644 (file)
@@ -5236,7 +5236,7 @@ getDeclForTemporaryLifetimeExtension(const InitializedEntity &Entity,
   case InitializedEntity::EK_Exception:
   case InitializedEntity::EK_VectorElement:
   case InitializedEntity::EK_ComplexElement:
-    llvm_unreachable("should not materialize a temporary to initialize this");
+    return 0;
   }
   llvm_unreachable("unknown entity kind");
 }
@@ -5245,7 +5245,8 @@ static void performLifetimeExtension(Expr *Init, const ValueDecl *ExtendingD);
 
 /// Update a glvalue expression that is used as the initializer of a reference
 /// to note that its lifetime is extended.
-static void performReferenceExtension(Expr *Init, const ValueDecl *ExtendingD) {
+/// \return \c true if any temporary had its lifetime extended.
+static bool performReferenceExtension(Expr *Init, const ValueDecl *ExtendingD) {
   if (InitListExpr *ILE = dyn_cast<InitListExpr>(Init)) {
     if (ILE->getNumInits() == 1 && ILE->isGLValue()) {
       // This is just redundant braces around an initializer. Step over it.
@@ -5253,12 +5254,38 @@ static void performReferenceExtension(Expr *Init, const ValueDecl *ExtendingD) {
     }
   }
 
+  // Walk past any constructs which we can lifetime-extend across.
+  Expr *Old;
+  do {
+    Old = Init;
+
+    // Step over any subobject adjustments; we may have a materialized
+    // temporary inside them.
+    SmallVector<const Expr *, 2> CommaLHSs;
+    SmallVector<SubobjectAdjustment, 2> Adjustments;
+    Init = const_cast<Expr *>(
+        Init->skipRValueSubobjectAdjustments(CommaLHSs, Adjustments));
+
+    // Per current approach for DR1376, look through casts to reference type
+    // when performing lifetime extension.
+    if (CastExpr *CE = dyn_cast<CastExpr>(Init))
+      if (CE->getSubExpr()->isGLValue())
+        Init = CE->getSubExpr();
+
+    // FIXME: Per DR1213, subscripting on an array temporary produces an xvalue.
+    // It's unclear if binding a reference to that xvalue extends the array
+    // temporary.
+  } while (Init != Old);
+
   if (MaterializeTemporaryExpr *ME = dyn_cast<MaterializeTemporaryExpr>(Init)) {
     // Update the storage duration of the materialized temporary.
     // FIXME: Rebuild the expression instead of mutating it.
     ME->setExtendingDecl(ExtendingD);
     performLifetimeExtension(ME->GetTemporaryExpr(), ExtendingD);
+    return true;
   }
+
+  return false;
 }
 
 /// Update a prvalue expression that is going to be materialized as a
@@ -5274,8 +5301,10 @@ static void performLifetimeExtension(Expr *Init, const ValueDecl *ExtendingD) {
     Init = BTE->getSubExpr();
 
   if (CXXStdInitializerListExpr *ILE =
-          dyn_cast<CXXStdInitializerListExpr>(Init))
-    return performReferenceExtension(ILE->getSubExpr(), ExtendingD);
+          dyn_cast<CXXStdInitializerListExpr>(Init)) {
+    performReferenceExtension(ILE->getSubExpr(), ExtendingD);
+    return;
+  }
 
   if (InitListExpr *ILE = dyn_cast<InitListExpr>(Init)) {
     if (ILE->getType()->isArrayType()) {
@@ -5582,6 +5611,16 @@ InitializationSequence::Perform(Sema &S,
       if (S.CheckExceptionSpecCompatibility(CurInit.get(), DestType))
         return ExprError();
 
+      // Even though we didn't materialize a temporary, the binding may still
+      // extend the lifetime of a temporary. This happens if we bind a reference
+      // to the result of a cast to reference type.
+      if (const ValueDecl *ExtendingDecl =
+              getDeclForTemporaryLifetimeExtension(Entity)) {
+        if (performReferenceExtension(CurInit.get(), ExtendingDecl))
+          warnOnLifetimeExtension(S, Entity, CurInit.get(), false,
+                                  ExtendingDecl);
+      }
+
       break;
 
     case SK_BindReferenceToTemporary: {
index 0eec331b28cc8372d0f0fa57579c34b4432a8a28..be39ded6ff3b9d6c98677e84d9a622a8b0760058 100644 (file)
@@ -69,7 +69,7 @@ auto use_multi_return() {
   return nm.n + nm.m;
 }
 
-auto a = [a(4), b = 5, &c = static_cast<const int&&>(0)] {
+auto a = [a(4), b = 5, &c = static_cast<const int&&>(0)] { // expected-warning {{binding reference member 'c' to a temporary value}} expected-note {{here}}
   static_assert(sizeof(a) == sizeof(int), "");
   static_assert(sizeof(b) == sizeof(int), "");
   using T = decltype(c);
index e8b4613a20daf7e8457d85253f09361be44daeae..2ea1f1fb66b390f340ef271396216f0ef3d503c8 100644 (file)
@@ -1,4 +1,32 @@
 // RUN: %clang_cc1 -emit-llvm %s -o - -triple=x86_64-apple-darwin9 -std=c++11 | FileCheck %s
+
+namespace PR16263 {
+  const unsigned int n = 1234;
+  extern const int &r = (const int&)n;
+  // CHECK: @_ZGRN7PR162631rE = private constant i32 1234,
+  // CHECK: @_ZN7PR162631rE = constant i32* @_ZGRN7PR162631rE,
+
+  extern const int &s = reinterpret_cast<const int&>(n);
+  // CHECK: @_ZN7PR16263L1nE = internal constant i32 1234, align 4
+  // CHECK: @_ZN7PR162631sE = constant i32* @_ZN7PR16263L1nE, align 8
+
+  struct A { int n; };
+  struct B { int n; };
+  struct C : A, B {};
+  extern const A &&a = (A&&)(A&&)(C&&)(C{});
+  // CHECK: @_ZGRN7PR162631aE = private global {{.*}} zeroinitializer,
+  // CHECK: @_ZN7PR162631aE = constant {{.*}} bitcast ({{.*}}* @_ZGRN7PR162631aE to
+
+  extern const int &&t = ((B&&)C{}).n;
+  // CHECK: @_ZGRN7PR162631tE = private global {{.*}} zeroinitializer,
+  // CHECK: @_ZN7PR162631tE = constant i32* {{.*}}* @_ZGRN7PR162631tE {{.*}} 4
+
+  struct D { double d; C c; };
+  extern const int &&u = (123, static_cast<B&&>(0, ((D&&)D{}).*&D::c).n);
+  // CHECK: @_ZGRN7PR162631uE = private global {{.*}} zeroinitializer
+  // CHECK: @_ZN7PR162631uE = constant i32* {{.*}} @_ZGRN7PR162631uE {{.*}} 12
+}
+
 struct A {
   A();
   ~A();