r187025 - C++1y: track object lifetime during constexpr evaluation, and don't allow

Richard Smith richard-llvm at metafoo.co.uk
Wed Jul 24 00:11:57 PDT 2013


Author: rsmith
Date: Wed Jul 24 02:11:57 2013
New Revision: 187025

URL: http://llvm.org/viewvc/llvm-project?rev=187025&view=rev
Log:
C++1y: track object lifetime during constexpr evaluation, and don't allow
objects to be used once their lifetimes end. This completes the C++1y
constexpr extensions.

Modified:
    cfe/trunk/include/clang/Basic/DiagnosticASTKinds.td
    cfe/trunk/lib/AST/ExprConstant.cpp
    cfe/trunk/test/SemaCXX/constant-expression-cxx11.cpp
    cfe/trunk/test/SemaCXX/constant-expression-cxx1y.cpp
    cfe/trunk/test/SemaCXX/i-c-e-cxx.cpp
    cfe/trunk/www/cxx_status.html

Modified: cfe/trunk/include/clang/Basic/DiagnosticASTKinds.td
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/Basic/DiagnosticASTKinds.td?rev=187025&r1=187024&r2=187025&view=diff
==============================================================================
--- cfe/trunk/include/clang/Basic/DiagnosticASTKinds.td (original)
+++ cfe/trunk/include/clang/Basic/DiagnosticASTKinds.td Wed Jul 24 02:11:57 2013
@@ -93,6 +93,9 @@ def note_constexpr_lifetime_ended : Note
 def note_constexpr_access_uninit : Note<
   "%select{read of|assignment to|increment of|decrement of}0 "
   "object outside its lifetime is not allowed in a constant expression">;
+def note_constexpr_use_uninit_reference : Note<
+  "use of reference outside its lifetime "
+  "is not allowed in a constant expression">;
 def note_constexpr_modify_const_type : Note<
   "modification of object of const-qualified type %0 is not allowed "
   "in a constant expression">;

Modified: cfe/trunk/lib/AST/ExprConstant.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/AST/ExprConstant.cpp?rev=187025&r1=187024&r2=187025&view=diff
==============================================================================
--- cfe/trunk/lib/AST/ExprConstant.cpp (original)
+++ cfe/trunk/lib/AST/ExprConstant.cpp Wed Jul 24 02:11:57 2013
@@ -317,6 +317,12 @@ namespace {
                    const FunctionDecl *Callee, const LValue *This,
                    APValue *Arguments);
     ~CallStackFrame();
+
+    APValue *getTemporary(const void *Key) {
+      MapTy::iterator I = Temporaries.find(Key);
+      return I == Temporaries.end() ? 0 : &I->second;
+    }
+    APValue &createTemporary(const void *Key, bool IsLifetimeExtended);
   };
 
   /// Temporarily override 'this'.
@@ -369,6 +375,20 @@ namespace {
     }
   };
 
+  /// A cleanup, and a flag indicating whether it is lifetime-extended.
+  class Cleanup {
+    llvm::PointerIntPair<APValue*, 1, bool> Value;
+
+  public:
+    Cleanup(APValue *Val, bool IsLifetimeExtended)
+        : Value(Val, IsLifetimeExtended) {}
+
+    bool isLifetimeExtended() const { return Value.getInt(); }
+    void endLifetime() {
+      *Value.getPointer() = APValue();
+    }
+  };
+
   /// EvalInfo - This is a private struct used by the evaluator to capture
   /// information about a subexpression as it is folded.  It retains information
   /// about the AST context, but also maintains information about the folded
@@ -407,6 +427,10 @@ namespace {
     /// initialized after CurrentCall and CallStackDepth.
     CallStackFrame BottomFrame;
 
+    /// A stack of values whose lifetimes end at the end of some surrounding
+    /// evaluation frame.
+    llvm::SmallVector<Cleanup, 16> CleanupStack;
+
     /// EvaluatingDecl - This is the declaration whose initializer is being
     /// evaluated, if any.
     APValue::LValueBase EvaluatingDecl;
@@ -423,7 +447,7 @@ namespace {
     /// expression is a potential constant expression? If so, some diagnostics
     /// are suppressed.
     bool CheckingPotentialConstantExpression;
-    
+
     bool IntOverflowCheckMode;
 
     EvalInfo(const ASTContext &C, Expr::EvalStatus &S,
@@ -601,6 +625,42 @@ namespace {
       Info.EvalStatus = Old;
     }
   };
+
+  /// RAII object wrapping a full-expression or block scope, and handling
+  /// the ending of the lifetime of temporaries created within it.
+  template<bool IsFullExpression>
+  class ScopeRAII {
+    EvalInfo &Info;
+    unsigned OldStackSize;
+  public:
+    ScopeRAII(EvalInfo &Info)
+        : Info(Info), OldStackSize(Info.CleanupStack.size()) {}
+    ~ScopeRAII() {
+      // Body moved to a static method to encourage the compiler to inline away
+      // instances of this class.
+      cleanup(Info, OldStackSize);
+    }
+  private:
+    static void cleanup(EvalInfo &Info, unsigned OldStackSize) {
+      unsigned NewEnd = OldStackSize;
+      for (unsigned I = OldStackSize, N = Info.CleanupStack.size();
+           I != N; ++I) {
+        if (IsFullExpression && Info.CleanupStack[I].isLifetimeExtended()) {
+          // Full-expression cleanup of a lifetime-extended temporary: nothing
+          // to do, just move this cleanup to the right place in the stack.
+          std::swap(Info.CleanupStack[I], Info.CleanupStack[NewEnd]);
+          ++NewEnd;
+        } else {
+          // End the lifetime of the object.
+          Info.CleanupStack[I].endLifetime();
+        }
+      }
+      Info.CleanupStack.erase(Info.CleanupStack.begin() + NewEnd,
+                              Info.CleanupStack.end());
+    }
+  };
+  typedef ScopeRAII<false> BlockScopeRAII;
+  typedef ScopeRAII<true> FullExpressionRAII;
 }
 
 bool SubobjectDesignator::checkSubobject(EvalInfo &Info, const Expr *E,
@@ -643,6 +703,14 @@ CallStackFrame::~CallStackFrame() {
   Info.CurrentCall = Caller;
 }
 
+APValue &CallStackFrame::createTemporary(const void *Key,
+                                         bool IsLifetimeExtended) {
+  APValue &Result = Temporaries[Key];
+  assert(Result.isUninit() && "temporary created multiple times");
+  Info.CleanupStack.push_back(Cleanup(&Result, IsLifetimeExtended));
+  return Result;
+}
+
 static void describeCall(CallStackFrame *Frame, raw_ostream &Out);
 
 void EvalInfo::addCallStack(unsigned Limit) {
@@ -1710,11 +1778,9 @@ static bool evaluateVarDeclInit(EvalInfo
 
   // If this is a local variable, dig out its value.
   if (Frame) {
-    Result = &Frame->Temporaries[VD];
-    // If we've carried on past an unevaluatable local variable initializer,
-    // we can't go any further. This can happen during potential constant
-    // expression checking.
-    return !Result->isUninit();
+    Result = Frame->getTemporary(VD);
+    assert(Result && "missing value for local variable");
+    return true;
   }
 
   // Dig out the initializer, and use the declaration which it's attached to.
@@ -1731,7 +1797,7 @@ static bool evaluateVarDeclInit(EvalInfo
   // in-flight value.
   if (Info.EvaluatingDecl.dyn_cast<const ValueDecl*>() == VD) {
     Result = Info.EvaluatingDeclValue;
-    return !Result->isUninit();
+    return true;
   }
 
   // Never evaluate the initializer of a weak variable. We can't be sure that
@@ -1884,16 +1950,20 @@ findSubobject(EvalInfo &Info, const Expr
       Info.Diag(E);
     return handler.failed();
   }
-  if (Sub.Entries.empty())
-    return handler.found(*Obj.Value, Obj.Type);
-  if (Info.CheckingPotentialConstantExpression && Obj.Value->isUninit())
-    // This object might be initialized later.
-    return handler.failed();
 
   APValue *O = Obj.Value;
   QualType ObjType = Obj.Type;
   // Walk the designator's path to find the subobject.
-  for (unsigned I = 0, N = Sub.Entries.size(); I != N; ++I) {
+  for (unsigned I = 0, N = Sub.Entries.size(); /**/; ++I) {
+    if (O->isUninit()) {
+      if (!Info.CheckingPotentialConstantExpression)
+        Info.Diag(E, diag::note_constexpr_access_uninit) << handler.AccessKind;
+      return handler.failed();
+    }
+
+    if (I == N)
+      return handler.found(*O, ObjType);
+
     if (ObjType->isArrayType()) {
       // Next subobject is an array element.
       const ConstantArrayType *CAT = Info.Ctx.getAsConstantArrayType(ObjType);
@@ -2006,15 +2076,7 @@ findSubobject(EvalInfo &Info, const Expr
       if (WasConstQualified)
         ObjType.addConst();
     }
-
-    if (O->isUninit()) {
-      if (!Info.CheckingPotentialConstantExpression)
-        Info.Diag(E, diag::note_constexpr_access_uninit) << handler.AccessKind;
-      return handler.failed();
-    }
   }
-
-  return handler.found(*O, ObjType);
 }
 
 namespace {
@@ -2328,7 +2390,8 @@ CompleteObject findCompleteObject(EvalIn
         return CompleteObject();
       }
     } else {
-      BaseVal = &Frame->Temporaries[Base];
+      BaseVal = Frame->getTemporary(Base);
+      assert(BaseVal && "missing value for temporary");
     }
 
     // Volatile temporary objects cannot be accessed in constant expressions.
@@ -2887,7 +2950,7 @@ static bool EvaluateDecl(EvalInfo &Info,
 
     LValue Result;
     Result.set(VD, Info.CurrentCall->Index);
-    APValue &Val = Info.CurrentCall->Temporaries[VD];
+    APValue &Val = Info.CurrentCall->createTemporary(VD, true);
 
     if (!VD->getInit()) {
       Info.Diag(D->getLocStart(), diag::note_constexpr_uninitialized)
@@ -2910,6 +2973,7 @@ static bool EvaluateDecl(EvalInfo &Info,
 /// Evaluate a condition (either a variable declaration or an expression).
 static bool EvaluateCond(EvalInfo &Info, const VarDecl *CondDecl,
                          const Expr *Cond, bool &Result) {
+  FullExpressionRAII Scope(Info);
   if (CondDecl && !EvaluateDecl(Info, CondDecl))
     return false;
   return EvaluateAsBooleanCondition(Cond, Result, Info);
@@ -2922,6 +2986,7 @@ static EvalStmtResult EvaluateStmt(APVal
 static EvalStmtResult EvaluateLoopBody(APValue &Result, EvalInfo &Info,
                                        const Stmt *Body,
                                        const SwitchCase *Case = 0) {
+  BlockScopeRAII Scope(Info);
   switch (EvalStmtResult ESR = EvaluateStmt(Result, Info, Body, Case)) {
   case ESR_Break:
     return ESR_Succeeded;
@@ -2939,13 +3004,18 @@ static EvalStmtResult EvaluateLoopBody(A
 /// Evaluate a switch statement.
 static EvalStmtResult EvaluateSwitch(APValue &Result, EvalInfo &Info,
                                      const SwitchStmt *SS) {
+  BlockScopeRAII Scope(Info);
+
   // Evaluate the switch condition.
-  if (SS->getConditionVariable() &&
-      !EvaluateDecl(Info, SS->getConditionVariable()))
-    return ESR_Failed;
   APSInt Value;
-  if (!EvaluateInteger(SS->getCond(), Value, Info))
-    return ESR_Failed;
+  {
+    FullExpressionRAII Scope(Info);
+    if (SS->getConditionVariable() &&
+        !EvaluateDecl(Info, SS->getConditionVariable()))
+      return ESR_Failed;
+    if (!EvaluateInteger(SS->getCond(), Value, Info))
+      return ESR_Failed;
+  }
 
   // Find the switch case corresponding to the value of the condition.
   // FIXME: Cache this lookup.
@@ -3021,6 +3091,11 @@ static EvalStmtResult EvaluateStmt(APVal
       // FIXME: Precompute which side of an 'if' we would jump to, and go
       // straight there rather than scanning both sides.
       const IfStmt *IS = cast<IfStmt>(S);
+
+      // Wrap the evaluation in a block scope, in case it's a DeclStmt
+      // preceded by our switch label.
+      BlockScopeRAII Scope(Info);
+
       EvalStmtResult ESR = EvaluateStmt(Result, Info, IS->getThen(), Case);
       if (ESR != ESR_CaseNotFound || !IS->getElse())
         return ESR;
@@ -3041,8 +3116,11 @@ static EvalStmtResult EvaluateStmt(APVal
           EvaluateLoopBody(Result, Info, FS->getBody(), Case);
       if (ESR != ESR_Continue)
         return ESR;
-      if (FS->getInc() && !EvaluateIgnoredValue(Info, FS->getInc()))
-        return ESR_Failed;
+      if (FS->getInc()) {
+        FullExpressionRAII IncScope(Info);
+        if (!EvaluateIgnoredValue(Info, FS->getInc()))
+          return ESR_Failed;
+      }
       break;
     }
 
@@ -3054,13 +3132,12 @@ static EvalStmtResult EvaluateStmt(APVal
     }
   }
 
-  // FIXME: Mark all temporaries in the current frame as destroyed at
-  // the end of each full-expression.
   switch (S->getStmtClass()) {
   default:
     if (const Expr *E = dyn_cast<Expr>(S)) {
       // Don't bother evaluating beyond an expression-statement which couldn't
       // be evaluated.
+      FullExpressionRAII Scope(Info);
       if (!EvaluateIgnoredValue(Info, E))
         return ESR_Failed;
       return ESR_Succeeded;
@@ -3075,20 +3152,28 @@ static EvalStmtResult EvaluateStmt(APVal
   case Stmt::DeclStmtClass: {
     const DeclStmt *DS = cast<DeclStmt>(S);
     for (DeclStmt::const_decl_iterator DclIt = DS->decl_begin(),
-           DclEnd = DS->decl_end(); DclIt != DclEnd; ++DclIt)
+           DclEnd = DS->decl_end(); DclIt != DclEnd; ++DclIt) {
+      // Each declaration initialization is its own full-expression.
+      // FIXME: This isn't quite right; if we're performing aggregate
+      // initialization, each braced subexpression is its own full-expression.
+      FullExpressionRAII Scope(Info);
       if (!EvaluateDecl(Info, *DclIt) && !Info.keepEvaluatingAfterFailure())
         return ESR_Failed;
+    }
     return ESR_Succeeded;
   }
 
   case Stmt::ReturnStmtClass: {
     const Expr *RetExpr = cast<ReturnStmt>(S)->getRetValue();
+    FullExpressionRAII Scope(Info);
     if (RetExpr && !Evaluate(Result, Info, RetExpr))
       return ESR_Failed;
     return ESR_Returned;
   }
 
   case Stmt::CompoundStmtClass: {
+    BlockScopeRAII Scope(Info);
+
     const CompoundStmt *CS = cast<CompoundStmt>(S);
     for (CompoundStmt::const_body_iterator BI = CS->body_begin(),
            BE = CS->body_end(); BI != BE; ++BI) {
@@ -3105,6 +3190,7 @@ static EvalStmtResult EvaluateStmt(APVal
     const IfStmt *IS = cast<IfStmt>(S);
 
     // Evaluate the condition, as either a var decl or as an expression.
+    BlockScopeRAII Scope(Info);
     bool Cond;
     if (!EvaluateCond(Info, IS->getConditionVariable(), IS->getCond(), Cond))
       return ESR_Failed;
@@ -3120,6 +3206,7 @@ static EvalStmtResult EvaluateStmt(APVal
   case Stmt::WhileStmtClass: {
     const WhileStmt *WS = cast<WhileStmt>(S);
     while (true) {
+      BlockScopeRAII Scope(Info);
       bool Continue;
       if (!EvaluateCond(Info, WS->getConditionVariable(), WS->getCond(),
                         Continue))
@@ -3143,6 +3230,7 @@ static EvalStmtResult EvaluateStmt(APVal
         return ESR;
       Case = 0;
 
+      FullExpressionRAII CondScope(Info);
       if (!EvaluateAsBooleanCondition(DS->getCond(), Continue, Info))
         return ESR_Failed;
     } while (Continue);
@@ -3151,12 +3239,14 @@ static EvalStmtResult EvaluateStmt(APVal
 
   case Stmt::ForStmtClass: {
     const ForStmt *FS = cast<ForStmt>(S);
+    BlockScopeRAII Scope(Info);
     if (FS->getInit()) {
       EvalStmtResult ESR = EvaluateStmt(Result, Info, FS->getInit());
       if (ESR != ESR_Succeeded)
         return ESR;
     }
     while (true) {
+      BlockScopeRAII Scope(Info);
       bool Continue = true;
       if (FS->getCond() && !EvaluateCond(Info, FS->getConditionVariable(),
                                          FS->getCond(), Continue))
@@ -3168,14 +3258,18 @@ static EvalStmtResult EvaluateStmt(APVal
       if (ESR != ESR_Continue)
         return ESR;
 
-      if (FS->getInc() && !EvaluateIgnoredValue(Info, FS->getInc()))
-        return ESR_Failed;
+      if (FS->getInc()) {
+        FullExpressionRAII IncScope(Info);
+        if (!EvaluateIgnoredValue(Info, FS->getInc()))
+          return ESR_Failed;
+      }
     }
     return ESR_Succeeded;
   }
 
   case Stmt::CXXForRangeStmtClass: {
     const CXXForRangeStmt *FS = cast<CXXForRangeStmt>(S);
+    BlockScopeRAII Scope(Info);
 
     // Initialize the __range variable.
     EvalStmtResult ESR = EvaluateStmt(Result, Info, FS->getRangeStmt());
@@ -3189,13 +3283,17 @@ static EvalStmtResult EvaluateStmt(APVal
 
     while (true) {
       // Condition: __begin != __end.
-      bool Continue = true;
-      if (!EvaluateAsBooleanCondition(FS->getCond(), Continue, Info))
-        return ESR_Failed;
-      if (!Continue)
-        break;
+      {
+        bool Continue = true;
+        FullExpressionRAII CondExpr(Info);
+        if (!EvaluateAsBooleanCondition(FS->getCond(), Continue, Info))
+          return ESR_Failed;
+        if (!Continue)
+          break;
+      }
 
       // User's variable declaration, initialized by *__begin.
+      BlockScopeRAII InnerScope(Info);
       ESR = EvaluateStmt(Result, Info, FS->getLoopVarStmt());
       if (ESR != ESR_Succeeded)
         return ESR;
@@ -3410,6 +3508,9 @@ static bool HandleConstructorCall(Source
   if (RD->isInvalidDecl()) return false;
   const ASTRecordLayout &Layout = Info.Ctx.getASTRecordLayout(RD);
 
+  // A scope for temporaries lifetime-extended by reference members.
+  BlockScopeRAII LifetimeExtendedScope(Info);
+
   bool Success = true;
   unsigned BasesSeen = 0;
 #ifndef NDEBUG
@@ -3476,6 +3577,7 @@ static bool HandleConstructorCall(Source
       llvm_unreachable("unknown base initializer kind");
     }
 
+    FullExpressionRAII InitScope(Info);
     if (!EvaluateInPlace(*Value, Info, Subobject, (*I)->getInit())) {
       // If we're checking for a potential constant expression, evaluate all
       // initializers even if some of them fail.
@@ -3633,7 +3735,7 @@ public:
   RetTy VisitBinaryConditionalOperator(const BinaryConditionalOperator *E) {
     // Evaluate and cache the common expression. We treat it as a temporary,
     // even though it's not quite the same thing.
-    if (!Evaluate(Info.CurrentCall->Temporaries[E->getOpaqueValue()],
+    if (!Evaluate(Info.CurrentCall->createTemporary(E->getOpaqueValue(), false),
                   Info, E->getCommon()))
       return false;
 
@@ -3668,18 +3770,17 @@ public:
   }
 
   RetTy VisitOpaqueValueExpr(const OpaqueValueExpr *E) {
-    APValue &Value = Info.CurrentCall->Temporaries[E];
-    if (Value.isUninit()) {
-      const Expr *Source = E->getSourceExpr();
-      if (!Source)
-        return Error(E);
-      if (Source == E) { // sanity checking.
-        assert(0 && "OpaqueValueExpr recursively refers to itself");
-        return Error(E);
-      }
-      return StmtVisitorTy::Visit(Source);
+    if (APValue *Value = Info.CurrentCall->getTemporary(E))
+      return DerivedSuccess(*Value, E);
+
+    const Expr *Source = E->getSourceExpr();
+    if (!Source)
+      return Error(E);
+    if (Source == E) { // sanity checking.
+      assert(0 && "OpaqueValueExpr recursively refers to itself");
+      return Error(E);
     }
-    return DerivedSuccess(Value, E);
+    return StmtVisitorTy::Visit(Source);
   }
 
   RetTy VisitCallExpr(const CallExpr *E) {
@@ -3870,6 +3971,7 @@ public:
     if (Info.getIntOverflowCheckMode())
       return Error(E);
 
+    BlockScopeRAII Scope(Info);
     const CompoundStmt *CS = E->getSubStmt();
     for (CompoundStmt::const_body_iterator BI = CS->body_begin(),
                                            BE = CS->body_end();
@@ -4121,6 +4223,11 @@ bool LValueExprEvaluator::VisitVarDecl(c
   APValue *V;
   if (!evaluateVarDeclInit(Info, E, VD, Frame, V))
     return false;
+  if (V->isUninit()) {
+    if (!Info.CheckingPotentialConstantExpression)
+      Info.Diag(E, diag::note_constexpr_use_uninit_reference);
+    return false;
+  }
   return Success(*V, E);
 }
 
@@ -4146,7 +4253,8 @@ bool LValueExprEvaluator::VisitMateriali
     *Value = APValue();
     Result.set(E);
   } else {
-    Value = &Info.CurrentCall->Temporaries[E];
+    Value = &Info.CurrentCall->
+        createTemporary(E, E->getStorageDuration() == SD_Automatic);
     Result.set(E, Info.CurrentCall->Index);
   }
 
@@ -4490,7 +4598,7 @@ bool PointerExprEvaluator::VisitCastExpr
         return false;
     } else {
       Result.set(SubExpr, Info.CurrentCall->Index);
-      if (!EvaluateInPlace(Info.CurrentCall->Temporaries[SubExpr],
+      if (!EvaluateInPlace(Info.CurrentCall->createTemporary(SubExpr, false),
                            Info, Result, SubExpr))
         return false;
     }
@@ -4940,7 +5048,8 @@ public:
   /// Visit an expression which constructs the value of this temporary.
   bool VisitConstructExpr(const Expr *E) {
     Result.set(E, Info.CurrentCall->Index);
-    return EvaluateInPlace(Info.CurrentCall->Temporaries[E], Info, Result, E);
+    return EvaluateInPlace(Info.CurrentCall->createTemporary(E, false),
+                           Info, Result, E);
   }
 
   bool VisitCastExpr(const CastExpr *E) {
@@ -7644,15 +7753,17 @@ static bool Evaluate(APValue &Result, Ev
   } else if (T->isArrayType()) {
     LValue LV;
     LV.set(E, Info.CurrentCall->Index);
-    if (!EvaluateArray(E, LV, Info.CurrentCall->Temporaries[E], Info))
+    APValue &Value = Info.CurrentCall->createTemporary(E, false);
+    if (!EvaluateArray(E, LV, Value, Info))
       return false;
-    Result = Info.CurrentCall->Temporaries[E];
+    Result = Value;
   } else if (T->isRecordType()) {
     LValue LV;
     LV.set(E, Info.CurrentCall->Index);
-    if (!EvaluateRecord(E, LV, Info.CurrentCall->Temporaries[E], Info))
+    APValue &Value = Info.CurrentCall->createTemporary(E, false);
+    if (!EvaluateRecord(E, LV, Value, Info))
       return false;
-    Result = Info.CurrentCall->Temporaries[E];
+    Result = Value;
   } else if (T->isVoidType()) {
     if (!Info.getLangOpts().CPlusPlus11)
       Info.CCEDiag(E, diag::note_constexpr_nonliteral)

Modified: cfe/trunk/test/SemaCXX/constant-expression-cxx11.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/SemaCXX/constant-expression-cxx11.cpp?rev=187025&r1=187024&r2=187025&view=diff
==============================================================================
--- cfe/trunk/test/SemaCXX/constant-expression-cxx11.cpp (original)
+++ cfe/trunk/test/SemaCXX/constant-expression-cxx11.cpp Wed Jul 24 02:11:57 2013
@@ -1709,3 +1709,21 @@ namespace ConstexprConstructorRecovery {
   }; 
   constexpr X x{};
 }
+
+namespace Lifetime {
+  void f() {
+    constexpr int &n = n; // expected-error {{constant expression}} expected-note {{use of reference outside its lifetime}} expected-warning {{not yet bound to a value}}
+    constexpr int m = m; // expected-error {{constant expression}} expected-note {{read of object outside its lifetime}}
+  }
+
+  constexpr int &get(int &&n) { return n; }
+  struct S {
+    int &&r; // expected-note 2{{declared here}}
+    int &s;
+    int t;
+    constexpr S() : r(0), s(get(0)), t(r) {} // expected-warning {{temporary}}
+    constexpr S(int) : r(0), s(get(0)), t(s) {} // expected-warning {{temporary}} expected-note {{read of object outside its lifetime}}
+  };
+  constexpr int k1 = S().t; // ok, int is lifetime-extended to end of constructor
+  constexpr int k2 = S(0).t; // expected-error {{constant expression}} expected-note {{in call}}
+}

Modified: cfe/trunk/test/SemaCXX/constant-expression-cxx1y.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/SemaCXX/constant-expression-cxx1y.cpp?rev=187025&r1=187024&r2=187025&view=diff
==============================================================================
--- cfe/trunk/test/SemaCXX/constant-expression-cxx1y.cpp (original)
+++ cfe/trunk/test/SemaCXX/constant-expression-cxx1y.cpp Wed Jul 24 02:11:57 2013
@@ -814,3 +814,59 @@ namespace VirtualFromBase {
   constexpr X<S2> *q = const_cast<X<X<S2>>*>(&xxs2);
   static_assert(q->f() == sizeof(X<S2>), ""); // expected-error {{constant expression}} expected-note {{virtual function call}}
 }
+
+namespace Lifetime {
+  constexpr int &get(int &&r) { return r; }
+  constexpr int f() {
+    int &r = get(123);
+    return r; // expected-note {{read of object outside its lifetime}}
+  }
+  static_assert(f() == 123, ""); // expected-error {{constant expression}} expected-note {{in call}}
+
+  constexpr int g() {
+    int *p = 0;
+    {
+      int n = 0;
+      p = &n;
+      n = 42;
+    }
+    *p = 123; // expected-note {{assignment to object outside its lifetime}}
+    return *p;
+  }
+  static_assert(g() == 42, ""); // expected-error {{constant expression}} expected-note {{in call}}
+
+  constexpr int h(int n) {
+    int *p[4] = {};
+    int &&r = 1;
+    p[0] = &r;
+    while (int a = 1) {
+      p[1] = &a;
+      for (int b = 1; int c = 1; ) {
+        p[2] = &b, p[3] = &c;
+        break;
+      }
+      break;
+    }
+    *p[n] = 0; // expected-note 3{{assignment to object outside its lifetime}}
+    return *p[n];
+  }
+  static_assert(h(0) == 0, ""); // ok, lifetime-extended
+  static_assert(h(1) == 0, ""); // expected-error {{constant expression}} expected-note {{in call}}
+  static_assert(h(2) == 0, ""); // expected-error {{constant expression}} expected-note {{in call}}
+  static_assert(h(3) == 0, ""); // expected-error {{constant expression}} expected-note {{in call}}
+
+  // FIXME: This function should be treated as non-constant.
+  constexpr void lifetime_versus_loops() {
+    int *p = 0;
+    for (int i = 0; i != 2; ++i) {
+      int *q = p;
+      int n = 0;
+      p = &n;
+      if (i)
+        // This modifies the 'n' from the previous iteration of the loop outside
+        // its lifetime.
+        ++*q;
+    }
+  }
+  static_assert((lifetime_versus_loops(), true), "");
+}

Modified: cfe/trunk/test/SemaCXX/i-c-e-cxx.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/SemaCXX/i-c-e-cxx.cpp?rev=187025&r1=187024&r2=187025&view=diff
==============================================================================
--- cfe/trunk/test/SemaCXX/i-c-e-cxx.cpp (original)
+++ cfe/trunk/test/SemaCXX/i-c-e-cxx.cpp Wed Jul 24 02:11:57 2013
@@ -16,7 +16,7 @@ void f() {
 }
 
 int a() {
-  const int t=t; // expected-note {{declared here}}
+  const int t=t; // expected-note {{declared here}} expected-note {{read of object outside its lifetime}}
   switch(1) { // expected-warning {{no case matching constant switch condition '1'}}
     case t:; // expected-error {{not an integral constant expression}} expected-note {{initializer of 't' is not a constant expression}}
   }

Modified: cfe/trunk/www/cxx_status.html
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/www/cxx_status.html?rev=187025&r1=187024&r2=187025&view=diff
==============================================================================
--- cfe/trunk/www/cxx_status.html (original)
+++ cfe/trunk/www/cxx_status.html Wed Jul 24 02:11:57 2013
@@ -457,7 +457,7 @@ available.</p>
     <tr>
       <td>Relaxing requirements on constexpr functions</td>
       <td><a href="http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2013/n3652.html">N3652</a></td>
-      <td class="partial" align="center">Partial</td>
+      <td class="svn" align="center">SVN</td>
     </tr>
     <tr>
       <td>Member initializers and aggregates</td>





More information about the cfe-commits mailing list