forked from OSchip/llvm-project
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. llvm-svn: 187025
This commit is contained in:
parent
6bcb34b180
commit
08d6a2cc7a
|
@ -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">;
|
||||
|
|
|
@ -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 &Info, const Expr *E,
|
|||
|
||||
// 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 &Info, const Expr *E,
|
|||
// 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 *E, const CompleteObject &Obj,
|
|||
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 *E, const CompleteObject &Obj,
|
|||
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(EvalInfo &Info, const Expr *E, AccessKinds AK,
|
|||
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, const Decl *D) {
|
|||
|
||||
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, const Decl *D) {
|
|||
/// 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(APValue &Result, EvalInfo &Info,
|
|||
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(APValue &Result, EvalInfo &Info,
|
|||
/// 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(APValue &Result, EvalInfo &Info,
|
|||
// 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(APValue &Result, EvalInfo &Info,
|
|||
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(APValue &Result, EvalInfo &Info,
|
|||
}
|
||||
}
|
||||
|
||||
// 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(APValue &Result, EvalInfo &Info,
|
|||
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(APValue &Result, EvalInfo &Info,
|
|||
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(APValue &Result, EvalInfo &Info,
|
|||
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(APValue &Result, EvalInfo &Info,
|
|||
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(APValue &Result, EvalInfo &Info,
|
|||
|
||||
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(APValue &Result, EvalInfo &Info,
|
|||
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(APValue &Result, EvalInfo &Info,
|
|||
|
||||
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(SourceLocation CallLoc, const LValue &This,
|
|||
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(SourceLocation CallLoc, const LValue &This,
|
|||
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(const Expr *E, const VarDecl *VD) {
|
|||
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::VisitMaterializeTemporaryExpr(
|
|||
*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(const CastExpr* E) {
|
|||
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, EvalInfo &Info, const Expr *E) {
|
|||
} 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)
|
||||
|
|
|
@ -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}}
|
||||
}
|
||||
|
|
|
@ -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), "");
|
||||
}
|
||||
|
|
|
@ -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}}
|
||||
}
|
||||
|
|
|
@ -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>
|
||||
|
|
Loading…
Reference in New Issue