forked from OSchip/llvm-project
constexpr: Implement the [dcl.constexpr]p5 check for whether a constexpr
function definition can produce a constant expression. This also provides the last few checks for [dcl.constexpr]p3 and [dcl.constexpr]p4. llvm-svn: 149108
This commit is contained in:
parent
af0bdfc692
commit
253c2a390a
|
@ -425,6 +425,14 @@ public:
|
|||
bool isCXX11ConstantExpr(ASTContext &Ctx, APValue *Result = 0,
|
||||
SourceLocation *Loc = 0) const;
|
||||
|
||||
/// isPotentialConstantExpr - Return true if this function's definition
|
||||
/// might be usable in a constant expression in C++11, if it were marked
|
||||
/// constexpr. Return false if the function can never produce a constant
|
||||
/// expression, along with diagnostics describing why not.
|
||||
static bool isPotentialConstantExpr(const FunctionDecl *FD,
|
||||
llvm::SmallVectorImpl<
|
||||
PartialDiagnosticAt> &Diags);
|
||||
|
||||
/// isConstantInitializer - Returns true if this expression can be emitted to
|
||||
/// IR as a constant, and thus can be used as a constant initializer in C.
|
||||
bool isConstantInitializer(ASTContext &Ctx, bool ForRef) const;
|
||||
|
|
|
@ -1360,6 +1360,9 @@ def err_constexpr_vla : Error<
|
|||
"%select{function|constructor}1">;
|
||||
def err_constexpr_var_declaration : Error<
|
||||
"variables cannot be declared in a constexpr %select{function|constructor}0">;
|
||||
def err_constexpr_function_never_constant_expr : Error<
|
||||
"constexpr %select{function|constructor}0 never produces "
|
||||
"a constant expression">;
|
||||
def err_constexpr_body_no_return : Error<
|
||||
"no return statement in constexpr function">;
|
||||
def err_constexpr_body_multiple_return : Error<
|
||||
|
|
|
@ -9,6 +9,28 @@
|
|||
//
|
||||
// This file implements the Expr constant evaluator.
|
||||
//
|
||||
// Constant expression evaluation produces four main results:
|
||||
//
|
||||
// * A success/failure flag indicating whether constant folding was successful.
|
||||
// This is the 'bool' return value used by most of the code in this file. A
|
||||
// 'false' return value indicates that constant folding has failed, and any
|
||||
// appropriate diagnostic has already been produced.
|
||||
//
|
||||
// * An evaluated result, valid only if constant folding has not failed.
|
||||
//
|
||||
// * A flag indicating if evaluation encountered (unevaluated) side-effects.
|
||||
// These arise in cases such as (sideEffect(), 0) and (sideEffect() || 1),
|
||||
// where it is possible to determine the evaluated result regardless.
|
||||
//
|
||||
// * A set of notes indicating why the evaluation was not a constant expression
|
||||
// (under the C++11 rules only, at the moment), or, if folding failed too,
|
||||
// why the expression could not be folded.
|
||||
//
|
||||
// If we are checking for a potential constant expression, failure to constant
|
||||
// fold a potential constant sub-expression will be indicated by a 'false'
|
||||
// return value (the expression could not be folded) and no diagnostic (the
|
||||
// expression is not necessarily non-constant).
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#include "clang/AST/APValue.h"
|
||||
|
@ -346,7 +368,7 @@ namespace {
|
|||
MapTy OpaqueValues;
|
||||
|
||||
/// BottomFrame - The frame in which evaluation started. This must be
|
||||
/// initialized last.
|
||||
/// initialized after CurrentCall and CallStackDepth.
|
||||
CallStackFrame BottomFrame;
|
||||
|
||||
/// EvaluatingDecl - This is the declaration whose initializer is being
|
||||
|
@ -361,11 +383,17 @@ namespace {
|
|||
/// notes attached to it will also be stored, otherwise they will not be.
|
||||
bool HasActiveDiagnostic;
|
||||
|
||||
/// CheckingPotentialConstantExpression - Are we checking whether the
|
||||
/// expression is a potential constant expression? If so, some diagnostics
|
||||
/// are suppressed.
|
||||
bool CheckingPotentialConstantExpression;
|
||||
|
||||
|
||||
EvalInfo(const ASTContext &C, Expr::EvalStatus &S)
|
||||
: Ctx(const_cast<ASTContext&>(C)), EvalStatus(S), CurrentCall(0),
|
||||
CallStackDepth(0), BottomFrame(*this, SourceLocation(), 0, 0, 0),
|
||||
EvaluatingDecl(0), EvaluatingDeclValue(0), HasActiveDiagnostic(false) {}
|
||||
EvaluatingDecl(0), EvaluatingDeclValue(0), HasActiveDiagnostic(false),
|
||||
CheckingPotentialConstantExpression(false) {}
|
||||
|
||||
const CCValue *getOpaqueValue(const OpaqueValueExpr *e) const {
|
||||
MapTy::const_iterator i = OpaqueValues.find(e);
|
||||
|
@ -381,6 +409,10 @@ namespace {
|
|||
const LangOptions &getLangOpts() const { return Ctx.getLangOptions(); }
|
||||
|
||||
bool CheckCallLimit(SourceLocation Loc) {
|
||||
// Don't perform any constexpr calls (other than the call we're checking)
|
||||
// when checking a potential constant expression.
|
||||
if (CheckingPotentialConstantExpression && CallStackDepth > 1)
|
||||
return false;
|
||||
if (CallStackDepth <= getLangOpts().ConstexprCallDepth)
|
||||
return true;
|
||||
Diag(Loc, diag::note_constexpr_depth_limit_exceeded)
|
||||
|
@ -412,12 +444,15 @@ namespace {
|
|||
unsigned Limit = Ctx.getDiagnostics().getConstexprBacktraceLimit();
|
||||
if (Limit)
|
||||
CallStackNotes = std::min(CallStackNotes, Limit + 1);
|
||||
if (CheckingPotentialConstantExpression)
|
||||
CallStackNotes = 0;
|
||||
|
||||
HasActiveDiagnostic = true;
|
||||
EvalStatus.Diag->clear();
|
||||
EvalStatus.Diag->reserve(1 + ExtraNotes + CallStackNotes);
|
||||
addDiag(Loc, DiagId);
|
||||
addCallStack(Limit);
|
||||
if (!CheckingPotentialConstantExpression)
|
||||
addCallStack(Limit);
|
||||
return OptionalDiagnostic(&(*EvalStatus.Diag)[0].second);
|
||||
}
|
||||
HasActiveDiagnostic = false;
|
||||
|
@ -449,6 +484,12 @@ namespace {
|
|||
Diags.begin(), Diags.end());
|
||||
}
|
||||
}
|
||||
|
||||
/// Should we continue evaluation as much as possible after encountering a
|
||||
/// construct which can't be folded?
|
||||
bool keepEvaluatingAfterFailure() {
|
||||
return CheckingPotentialConstantExpression && EvalStatus.Diag->empty();
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
|
@ -827,6 +868,14 @@ static bool IsGlobalLValue(APValue::LValueBase B) {
|
|||
// Block variables at global or local static scope.
|
||||
case Expr::BlockExprClass:
|
||||
return !cast<BlockExpr>(E)->getBlockDecl()->hasCaptures();
|
||||
case Expr::ImplicitValueInitExprClass:
|
||||
// FIXME:
|
||||
// We can never form an lvalue with an implicit value initialization as its
|
||||
// base through expression evaluation, so these only appear in one case: the
|
||||
// implicit variable declaration we invent when checking whether a constexpr
|
||||
// constructor can produce a constant expression. We must assume that such
|
||||
// an expression might be a global lvalue.
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1238,6 +1287,10 @@ static bool EvaluateVarDeclInit(EvalInfo &Info, const Expr *E,
|
|||
// If this is a parameter to an active constexpr function call, perform
|
||||
// argument substitution.
|
||||
if (const ParmVarDecl *PVD = dyn_cast<ParmVarDecl>(VD)) {
|
||||
// Assume arguments of a potential constant expression are unknown
|
||||
// constant expressions.
|
||||
if (Info.CheckingPotentialConstantExpression)
|
||||
return false;
|
||||
if (!Frame || !Frame->Arguments) {
|
||||
Info.Diag(E->getExprLoc(), diag::note_invalid_subexpr_in_const_expr);
|
||||
return false;
|
||||
|
@ -1249,7 +1302,10 @@ static bool EvaluateVarDeclInit(EvalInfo &Info, const Expr *E,
|
|||
// Dig out the initializer, and use the declaration which it's attached to.
|
||||
const Expr *Init = VD->getAnyInitializer(VD);
|
||||
if (!Init || Init->isValueDependent()) {
|
||||
Info.Diag(E->getExprLoc(), diag::note_invalid_subexpr_in_const_expr);
|
||||
// If we're checking a potential constant expression, the variable could be
|
||||
// initialized later.
|
||||
if (!Info.CheckingPotentialConstantExpression)
|
||||
Info.Diag(E->getExprLoc(), diag::note_invalid_subexpr_in_const_expr);
|
||||
return false;
|
||||
}
|
||||
|
||||
|
@ -1323,6 +1379,9 @@ static bool ExtractSubobject(EvalInfo &Info, const Expr *E,
|
|||
}
|
||||
if (Sub.Entries.empty())
|
||||
return true;
|
||||
if (Info.CheckingPotentialConstantExpression && Obj.isUninit())
|
||||
// This object might be initialized later.
|
||||
return false;
|
||||
|
||||
assert(!Obj.isLValue() && "extracting subobject of lvalue");
|
||||
const APValue *O = &Obj;
|
||||
|
@ -1383,7 +1442,8 @@ static bool ExtractSubobject(EvalInfo &Info, const Expr *E,
|
|||
}
|
||||
|
||||
if (O->isUninit()) {
|
||||
Info.Diag(E->getExprLoc(), diag::note_constexpr_read_uninit);
|
||||
if (!Info.CheckingPotentialConstantExpression)
|
||||
Info.Diag(E->getExprLoc(), diag::note_constexpr_read_uninit);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
@ -1603,7 +1663,8 @@ static const ValueDecl *HandleMemberPointerAccess(EvalInfo &Info,
|
|||
bool IncludeMember = true) {
|
||||
assert(BO->getOpcode() == BO_PtrMemD || BO->getOpcode() == BO_PtrMemI);
|
||||
|
||||
if (!EvaluateObjectArgument(Info, BO->getLHS(), LV))
|
||||
bool EvalObjOK = EvaluateObjectArgument(Info, BO->getLHS(), LV);
|
||||
if (!EvalObjOK && !Info.keepEvaluatingAfterFailure())
|
||||
return 0;
|
||||
|
||||
MemberPtr MemPtr;
|
||||
|
@ -1615,6 +1676,9 @@ static const ValueDecl *HandleMemberPointerAccess(EvalInfo &Info,
|
|||
if (!MemPtr.getDecl())
|
||||
return 0;
|
||||
|
||||
if (!EvalObjOK)
|
||||
return 0;
|
||||
|
||||
if (MemPtr.isDerivedMember()) {
|
||||
// This is a member of some derived class. Truncate LV appropriately.
|
||||
// The end of the derived-to-base path for the base object must match the
|
||||
|
@ -1786,6 +1850,12 @@ static bool CheckTrivialDefaultConstructor(EvalInfo &Info, SourceLocation Loc,
|
|||
static bool CheckConstexprFunction(EvalInfo &Info, SourceLocation CallLoc,
|
||||
const FunctionDecl *Declaration,
|
||||
const FunctionDecl *Definition) {
|
||||
// Potential constant expressions can contain calls to declared, but not yet
|
||||
// defined, constexpr functions.
|
||||
if (Info.CheckingPotentialConstantExpression && !Definition &&
|
||||
Declaration->isConstexpr())
|
||||
return false;
|
||||
|
||||
// Can we evaluate this function call?
|
||||
if (Definition && Definition->isConstexpr() && !Definition->isInvalidDecl())
|
||||
return true;
|
||||
|
@ -1811,44 +1881,49 @@ typedef SmallVector<CCValue, 8> ArgVector;
|
|||
/// EvaluateArgs - Evaluate the arguments to a function call.
|
||||
static bool EvaluateArgs(ArrayRef<const Expr*> Args, ArgVector &ArgValues,
|
||||
EvalInfo &Info) {
|
||||
bool Success = true;
|
||||
for (ArrayRef<const Expr*>::iterator I = Args.begin(), E = Args.end();
|
||||
I != E; ++I)
|
||||
if (!Evaluate(ArgValues[I - Args.begin()], Info, *I))
|
||||
return false;
|
||||
return true;
|
||||
I != E; ++I) {
|
||||
if (!Evaluate(ArgValues[I - Args.begin()], Info, *I)) {
|
||||
// If we're checking for a potential constant expression, evaluate all
|
||||
// initializers even if some of them fail.
|
||||
if (!Info.keepEvaluatingAfterFailure())
|
||||
return false;
|
||||
Success = false;
|
||||
}
|
||||
}
|
||||
return Success;
|
||||
}
|
||||
|
||||
/// Evaluate a function call.
|
||||
static bool HandleFunctionCall(const Expr *CallExpr, const FunctionDecl *Callee,
|
||||
const LValue *This,
|
||||
static bool HandleFunctionCall(SourceLocation CallLoc,
|
||||
const FunctionDecl *Callee, const LValue *This,
|
||||
ArrayRef<const Expr*> Args, const Stmt *Body,
|
||||
EvalInfo &Info, APValue &Result) {
|
||||
if (!Info.CheckCallLimit(CallExpr->getExprLoc()))
|
||||
return false;
|
||||
|
||||
ArgVector ArgValues(Args.size());
|
||||
if (!EvaluateArgs(Args, ArgValues, Info))
|
||||
return false;
|
||||
|
||||
CallStackFrame Frame(Info, CallExpr->getExprLoc(), Callee, This,
|
||||
ArgValues.data());
|
||||
if (!Info.CheckCallLimit(CallLoc))
|
||||
return false;
|
||||
|
||||
CallStackFrame Frame(Info, CallLoc, Callee, This, ArgValues.data());
|
||||
return EvaluateStmt(Result, Info, Body) == ESR_Returned;
|
||||
}
|
||||
|
||||
/// Evaluate a constructor call.
|
||||
static bool HandleConstructorCall(const Expr *CallExpr, const LValue &This,
|
||||
static bool HandleConstructorCall(SourceLocation CallLoc, const LValue &This,
|
||||
ArrayRef<const Expr*> Args,
|
||||
const CXXConstructorDecl *Definition,
|
||||
EvalInfo &Info, APValue &Result) {
|
||||
if (!Info.CheckCallLimit(CallExpr->getExprLoc()))
|
||||
return false;
|
||||
|
||||
ArgVector ArgValues(Args.size());
|
||||
if (!EvaluateArgs(Args, ArgValues, Info))
|
||||
return false;
|
||||
|
||||
CallStackFrame Frame(Info, CallExpr->getExprLoc(), Definition,
|
||||
&This, ArgValues.data());
|
||||
if (!Info.CheckCallLimit(CallLoc))
|
||||
return false;
|
||||
|
||||
CallStackFrame Frame(Info, CallLoc, Definition, &This, ArgValues.data());
|
||||
|
||||
// If it's a delegating constructor, just delegate.
|
||||
if (Definition->isDelegatingConstructor()) {
|
||||
|
@ -1866,9 +1941,14 @@ static bool HandleConstructorCall(const Expr *CallExpr, const LValue &This,
|
|||
LValue RHS;
|
||||
RHS.setFrom(ArgValues[0]);
|
||||
CCValue Value;
|
||||
return HandleLValueToRValueConversion(Info, Args[0], Args[0]->getType(),
|
||||
RHS, Value) &&
|
||||
CheckConstantExpression(Info, CallExpr, Value, Result);
|
||||
if (!HandleLValueToRValueConversion(Info, Args[0], Args[0]->getType(),
|
||||
RHS, Value))
|
||||
return false;
|
||||
assert((Value.isStruct() || Value.isUnion()) &&
|
||||
"trivial copy/move from non-class type?");
|
||||
// Any CCValue of class type must already be a constant expression.
|
||||
Result = Value;
|
||||
return true;
|
||||
}
|
||||
|
||||
// Reserve space for the struct members.
|
||||
|
@ -1878,12 +1958,17 @@ static bool HandleConstructorCall(const Expr *CallExpr, const LValue &This,
|
|||
|
||||
const ASTRecordLayout &Layout = Info.Ctx.getASTRecordLayout(RD);
|
||||
|
||||
bool Success = true;
|
||||
unsigned BasesSeen = 0;
|
||||
#ifndef NDEBUG
|
||||
CXXRecordDecl::base_class_const_iterator BaseIt = RD->bases_begin();
|
||||
#endif
|
||||
for (CXXConstructorDecl::init_const_iterator I = Definition->init_begin(),
|
||||
E = Definition->init_end(); I != E; ++I) {
|
||||
LValue Subobject = This;
|
||||
APValue *Value = &Result;
|
||||
|
||||
// Determine the subobject to initialize.
|
||||
if ((*I)->isBaseInitializer()) {
|
||||
QualType BaseType((*I)->getBaseClass(), 0);
|
||||
#ifndef NDEBUG
|
||||
|
@ -1894,27 +1979,18 @@ static bool HandleConstructorCall(const Expr *CallExpr, const LValue &This,
|
|||
"base class initializers not in expected order");
|
||||
++BaseIt;
|
||||
#endif
|
||||
LValue Subobject = This;
|
||||
HandleLValueDirectBase(Info, (*I)->getInit(), Subobject, RD,
|
||||
BaseType->getAsCXXRecordDecl(), &Layout);
|
||||
if (!EvaluateConstantExpression(Result.getStructBase(BasesSeen++), Info,
|
||||
Subobject, (*I)->getInit()))
|
||||
return false;
|
||||
Value = &Result.getStructBase(BasesSeen++);
|
||||
} else if (FieldDecl *FD = (*I)->getMember()) {
|
||||
LValue Subobject = This;
|
||||
HandleLValueMember(Info, (*I)->getInit(), Subobject, FD, &Layout);
|
||||
if (RD->isUnion()) {
|
||||
Result = APValue(FD);
|
||||
if (!EvaluateConstantExpression(Result.getUnionValue(), Info, Subobject,
|
||||
(*I)->getInit(), CCEK_MemberInit))
|
||||
return false;
|
||||
} else if (!EvaluateConstantExpression(
|
||||
Result.getStructField(FD->getFieldIndex()),
|
||||
Info, Subobject, (*I)->getInit(), CCEK_MemberInit))
|
||||
return false;
|
||||
Value = &Result.getUnionValue();
|
||||
} else {
|
||||
Value = &Result.getStructField(FD->getFieldIndex());
|
||||
}
|
||||
} else if (IndirectFieldDecl *IFD = (*I)->getIndirectMember()) {
|
||||
LValue Subobject = This;
|
||||
APValue *Value = &Result;
|
||||
// Walk the indirect field decl's chain to find the object to initialize,
|
||||
// and make sure we've initialized every step along it.
|
||||
for (IndirectFieldDecl::chain_iterator C = IFD->chain_begin(),
|
||||
|
@ -1935,21 +2011,28 @@ static bool HandleConstructorCall(const Expr *CallExpr, const LValue &This,
|
|||
*Value = APValue(APValue::UninitStruct(), CD->getNumBases(),
|
||||
std::distance(CD->field_begin(), CD->field_end()));
|
||||
}
|
||||
HandleLValueMember(Info, (*I)->getInit(), Subobject, FD);
|
||||
if (CD->isUnion())
|
||||
Value = &Value->getUnionValue();
|
||||
else
|
||||
Value = &Value->getStructField(FD->getFieldIndex());
|
||||
HandleLValueMember(Info, (*I)->getInit(), Subobject, FD);
|
||||
}
|
||||
if (!EvaluateConstantExpression(*Value, Info, Subobject, (*I)->getInit(),
|
||||
CCEK_MemberInit))
|
||||
return false;
|
||||
} else {
|
||||
llvm_unreachable("unknown base initializer kind");
|
||||
}
|
||||
|
||||
if (!EvaluateConstantExpression(*Value, Info, Subobject, (*I)->getInit(),
|
||||
(*I)->isBaseInitializer()
|
||||
? CCEK_Constant : CCEK_MemberInit)) {
|
||||
// If we're checking for a potential constant expression, evaluate all
|
||||
// initializers even if some of them fail.
|
||||
if (!Info.keepEvaluatingAfterFailure())
|
||||
return false;
|
||||
Success = false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
return Success;
|
||||
}
|
||||
|
||||
namespace {
|
||||
|
@ -2258,7 +2341,8 @@ public:
|
|||
APValue Result;
|
||||
|
||||
if (!CheckConstexprFunction(Info, E->getExprLoc(), FD, Definition) ||
|
||||
!HandleFunctionCall(E, Definition, This, Args, Body, Info, Result))
|
||||
!HandleFunctionCall(E->getExprLoc(), Definition, This, Args, Body,
|
||||
Info, Result))
|
||||
return false;
|
||||
|
||||
return DerivedSuccess(CCValue(Info.Ctx, Result, CCValue::GlobalValue()), E);
|
||||
|
@ -2703,11 +2787,12 @@ bool PointerExprEvaluator::VisitBinaryOperator(const BinaryOperator *E) {
|
|||
if (IExp->getType()->isPointerType())
|
||||
std::swap(PExp, IExp);
|
||||
|
||||
if (!EvaluatePointer(PExp, Result, Info))
|
||||
bool EvalPtrOK = EvaluatePointer(PExp, Result, Info);
|
||||
if (!EvalPtrOK && !Info.keepEvaluatingAfterFailure())
|
||||
return false;
|
||||
|
||||
llvm::APSInt Offset;
|
||||
if (!EvaluateInteger(IExp, Offset, Info))
|
||||
if (!EvaluateInteger(IExp, Offset, Info) || !EvalPtrOK)
|
||||
return false;
|
||||
int64_t AdditionalOffset
|
||||
= Offset.isSigned() ? Offset.getSExtValue()
|
||||
|
@ -3076,6 +3161,7 @@ bool RecordExprEvaluator::VisitInitListExpr(const InitListExpr *E) {
|
|||
Result = APValue(APValue::UninitStruct(), 0,
|
||||
std::distance(RD->field_begin(), RD->field_end()));
|
||||
unsigned ElementNo = 0;
|
||||
bool Success = true;
|
||||
for (RecordDecl::field_iterator Field = RD->field_begin(),
|
||||
FieldEnd = RD->field_end(); Field != FieldEnd; ++Field) {
|
||||
// Anonymous bit-fields are not considered members of the class for
|
||||
|
@ -3085,26 +3171,27 @@ bool RecordExprEvaluator::VisitInitListExpr(const InitListExpr *E) {
|
|||
|
||||
LValue Subobject = This;
|
||||
|
||||
if (ElementNo < E->getNumInits()) {
|
||||
HandleLValueMember(Info, E->getInit(ElementNo), Subobject, *Field,
|
||||
&Layout);
|
||||
if (!EvaluateConstantExpression(
|
||||
Result.getStructField((*Field)->getFieldIndex()),
|
||||
Info, Subobject, E->getInit(ElementNo++)))
|
||||
return false;
|
||||
} else {
|
||||
// Perform an implicit value-initialization for members beyond the end of
|
||||
// the initializer list.
|
||||
HandleLValueMember(Info, E, Subobject, *Field, &Layout);
|
||||
ImplicitValueInitExpr VIE(Field->getType());
|
||||
if (!EvaluateConstantExpression(
|
||||
Result.getStructField((*Field)->getFieldIndex()),
|
||||
Info, Subobject, &VIE))
|
||||
bool HaveInit = ElementNo < E->getNumInits();
|
||||
|
||||
// FIXME: Diagnostics here should point to the end of the initializer
|
||||
// list, not the start.
|
||||
HandleLValueMember(Info, HaveInit ? E->getInit(ElementNo) : E, Subobject,
|
||||
*Field, &Layout);
|
||||
|
||||
// Perform an implicit value-initialization for members beyond the end of
|
||||
// the initializer list.
|
||||
ImplicitValueInitExpr VIE(HaveInit ? Info.Ctx.IntTy : Field->getType());
|
||||
|
||||
if (!EvaluateConstantExpression(
|
||||
Result.getStructField((*Field)->getFieldIndex()),
|
||||
Info, Subobject, HaveInit ? E->getInit(ElementNo++) : &VIE)) {
|
||||
if (!Info.keepEvaluatingAfterFailure())
|
||||
return false;
|
||||
Success = false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
return Success;
|
||||
}
|
||||
|
||||
bool RecordExprEvaluator::VisitCXXConstructExpr(const CXXConstructExpr *E) {
|
||||
|
@ -3143,7 +3230,7 @@ bool RecordExprEvaluator::VisitCXXConstructExpr(const CXXConstructExpr *E) {
|
|||
return false;
|
||||
|
||||
llvm::ArrayRef<const Expr*> Args(E->getArgs(), E->getNumArgs());
|
||||
return HandleConstructorCall(E, This, Args,
|
||||
return HandleConstructorCall(E->getExprLoc(), This, Args,
|
||||
cast<CXXConstructorDecl>(Definition), Info,
|
||||
Result);
|
||||
}
|
||||
|
@ -3454,18 +3541,18 @@ bool ArrayExprEvaluator::VisitInitListExpr(const InitListExpr *E) {
|
|||
for (uint64_t I = 0; I < NumElements; ++I) {
|
||||
CCValue Char;
|
||||
if (!HandleLValueToRValueConversion(Info, E->getInit(0),
|
||||
CAT->getElementType(), LV, Char))
|
||||
return false;
|
||||
if (!CheckConstantExpression(Info, E->getInit(0), Char,
|
||||
Result.getArrayInitializedElt(I)))
|
||||
return false;
|
||||
if (!HandleLValueArrayAdjustment(Info, E->getInit(0), LV,
|
||||
CAT->getElementType(), LV, Char) ||
|
||||
!CheckConstantExpression(Info, E->getInit(0), Char,
|
||||
Result.getArrayInitializedElt(I)) ||
|
||||
!HandleLValueArrayAdjustment(Info, E->getInit(0), LV,
|
||||
CAT->getElementType(), 1))
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Success = true;
|
||||
|
||||
Result = APValue(APValue::UninitArray(), E->getNumInits(),
|
||||
CAT->getSize().getZExtValue());
|
||||
LValue Subobject = This;
|
||||
|
@ -3474,21 +3561,23 @@ bool ArrayExprEvaluator::VisitInitListExpr(const InitListExpr *E) {
|
|||
for (InitListExpr::const_iterator I = E->begin(), End = E->end();
|
||||
I != End; ++I, ++Index) {
|
||||
if (!EvaluateConstantExpression(Result.getArrayInitializedElt(Index),
|
||||
Info, Subobject, cast<Expr>(*I)))
|
||||
return false;
|
||||
if (!HandleLValueArrayAdjustment(Info, cast<Expr>(*I), Subobject,
|
||||
CAT->getElementType(), 1))
|
||||
return false;
|
||||
Info, Subobject, cast<Expr>(*I)) ||
|
||||
!HandleLValueArrayAdjustment(Info, cast<Expr>(*I), Subobject,
|
||||
CAT->getElementType(), 1)) {
|
||||
if (!Info.keepEvaluatingAfterFailure())
|
||||
return false;
|
||||
Success = false;
|
||||
}
|
||||
}
|
||||
|
||||
if (!Result.hasArrayFiller()) return true;
|
||||
if (!Result.hasArrayFiller()) return Success;
|
||||
assert(E->hasArrayFiller() && "no array filler for incomplete init list");
|
||||
// FIXME: The Subobject here isn't necessarily right. This rarely matters,
|
||||
// but sometimes does:
|
||||
// struct S { constexpr S() : p(&p) {} void *p; };
|
||||
// S s[10] = {};
|
||||
return EvaluateConstantExpression(Result.getArrayFiller(), Info,
|
||||
Subobject, E->getArrayFiller());
|
||||
Subobject, E->getArrayFiller()) && Success;
|
||||
}
|
||||
|
||||
bool ArrayExprEvaluator::VisitCXXConstructExpr(const CXXConstructExpr *E) {
|
||||
|
@ -3548,7 +3637,7 @@ bool ArrayExprEvaluator::VisitCXXConstructExpr(const CXXConstructExpr *E) {
|
|||
}
|
||||
|
||||
llvm::ArrayRef<const Expr*> Args(E->getArgs(), E->getNumArgs());
|
||||
return HandleConstructorCall(E, Subobject, Args,
|
||||
return HandleConstructorCall(E->getExprLoc(), Subobject, Args,
|
||||
cast<CXXConstructorDecl>(Definition),
|
||||
Info, Result.getArrayFiller());
|
||||
}
|
||||
|
@ -4072,10 +4161,11 @@ bool IntExprEvaluator::VisitBinaryOperator(const BinaryOperator *E) {
|
|||
assert(RHSTy->isAnyComplexType() && "Invalid comparison");
|
||||
ComplexValue LHS, RHS;
|
||||
|
||||
if (!EvaluateComplex(E->getLHS(), LHS, Info))
|
||||
bool LHSOK = EvaluateComplex(E->getLHS(), LHS, Info);
|
||||
if (!LHSOK && !Info.keepEvaluatingAfterFailure())
|
||||
return false;
|
||||
|
||||
if (!EvaluateComplex(E->getRHS(), RHS, Info))
|
||||
if (!EvaluateComplex(E->getRHS(), RHS, Info) || !LHSOK)
|
||||
return false;
|
||||
|
||||
if (LHS.isComplexFloat()) {
|
||||
|
@ -4114,10 +4204,11 @@ bool IntExprEvaluator::VisitBinaryOperator(const BinaryOperator *E) {
|
|||
RHSTy->isRealFloatingType()) {
|
||||
APFloat RHS(0.0), LHS(0.0);
|
||||
|
||||
if (!EvaluateFloat(E->getRHS(), RHS, Info))
|
||||
bool LHSOK = EvaluateFloat(E->getRHS(), RHS, Info);
|
||||
if (!LHSOK && !Info.keepEvaluatingAfterFailure())
|
||||
return false;
|
||||
|
||||
if (!EvaluateFloat(E->getLHS(), LHS, Info))
|
||||
if (!EvaluateFloat(E->getLHS(), LHS, Info) || !LHSOK)
|
||||
return false;
|
||||
|
||||
APFloat::cmpResult CR = LHS.compare(RHS);
|
||||
|
@ -4145,12 +4236,13 @@ bool IntExprEvaluator::VisitBinaryOperator(const BinaryOperator *E) {
|
|||
|
||||
if (LHSTy->isPointerType() && RHSTy->isPointerType()) {
|
||||
if (E->getOpcode() == BO_Sub || E->isComparisonOp()) {
|
||||
LValue LHSValue;
|
||||
if (!EvaluatePointer(E->getLHS(), LHSValue, Info))
|
||||
LValue LHSValue, RHSValue;
|
||||
|
||||
bool LHSOK = EvaluatePointer(E->getLHS(), LHSValue, Info);
|
||||
if (!LHSOK && Info.keepEvaluatingAfterFailure())
|
||||
return false;
|
||||
|
||||
LValue RHSValue;
|
||||
if (!EvaluatePointer(E->getRHS(), RHSValue, Info))
|
||||
if (!EvaluatePointer(E->getRHS(), RHSValue, Info) || !LHSOK)
|
||||
return false;
|
||||
|
||||
// Reject differing bases from the normal codepath; we special-case
|
||||
|
@ -4241,11 +4333,14 @@ bool IntExprEvaluator::VisitBinaryOperator(const BinaryOperator *E) {
|
|||
|
||||
// The LHS of a constant expr is always evaluated and needed.
|
||||
CCValue LHSVal;
|
||||
if (!EvaluateIntegerOrLValue(E->getLHS(), LHSVal, Info))
|
||||
|
||||
bool LHSOK = EvaluateIntegerOrLValue(E->getLHS(), LHSVal, Info);
|
||||
if (!LHSOK && !Info.keepEvaluatingAfterFailure())
|
||||
return false;
|
||||
|
||||
if (!Visit(E->getRHS()))
|
||||
if (!Visit(E->getRHS()) || !LHSOK)
|
||||
return false;
|
||||
|
||||
CCValue &RHSVal = Result;
|
||||
|
||||
// Handle cases like (unsigned long)&a + 4.
|
||||
|
@ -4860,9 +4955,10 @@ bool FloatExprEvaluator::VisitBinaryOperator(const BinaryOperator *E) {
|
|||
return ExprEvaluatorBaseTy::VisitBinaryOperator(E);
|
||||
|
||||
APFloat RHS(0.0);
|
||||
if (!EvaluateFloat(E->getLHS(), Result, Info))
|
||||
bool LHSOK = EvaluateFloat(E->getLHS(), Result, Info);
|
||||
if (!LHSOK && !Info.keepEvaluatingAfterFailure())
|
||||
return false;
|
||||
if (!EvaluateFloat(E->getRHS(), RHS, Info))
|
||||
if (!EvaluateFloat(E->getRHS(), RHS, Info) || !LHSOK)
|
||||
return false;
|
||||
|
||||
switch (E->getOpcode()) {
|
||||
|
@ -5131,11 +5227,12 @@ bool ComplexExprEvaluator::VisitBinaryOperator(const BinaryOperator *E) {
|
|||
if (E->isPtrMemOp() || E->isAssignmentOp() || E->getOpcode() == BO_Comma)
|
||||
return ExprEvaluatorBaseTy::VisitBinaryOperator(E);
|
||||
|
||||
if (!Visit(E->getLHS()))
|
||||
bool LHSOK = Visit(E->getLHS());
|
||||
if (!LHSOK && !Info.keepEvaluatingAfterFailure())
|
||||
return false;
|
||||
|
||||
ComplexValue RHS;
|
||||
if (!EvaluateComplex(E->getRHS(), RHS, Info))
|
||||
if (!EvaluateComplex(E->getRHS(), RHS, Info) || !LHSOK)
|
||||
return false;
|
||||
|
||||
assert(Result.isComplexFloat() == RHS.isComplexFloat() &&
|
||||
|
@ -6030,3 +6127,41 @@ bool Expr::isCXX11ConstantExpr(ASTContext &Ctx, APValue *Result,
|
|||
|
||||
return IsConstExpr;
|
||||
}
|
||||
|
||||
bool Expr::isPotentialConstantExpr(const FunctionDecl *FD,
|
||||
llvm::SmallVectorImpl<
|
||||
PartialDiagnosticAt> &Diags) {
|
||||
// FIXME: It would be useful to check constexpr function templates, but at the
|
||||
// moment the constant expression evaluator cannot cope with the non-rigorous
|
||||
// ASTs which we build for dependent expressions.
|
||||
if (FD->isDependentContext())
|
||||
return true;
|
||||
|
||||
Expr::EvalStatus Status;
|
||||
Status.Diag = &Diags;
|
||||
|
||||
EvalInfo Info(FD->getASTContext(), Status);
|
||||
Info.CheckingPotentialConstantExpression = true;
|
||||
|
||||
const CXXMethodDecl *MD = dyn_cast<CXXMethodDecl>(FD);
|
||||
const CXXRecordDecl *RD = MD ? MD->getParent()->getCanonicalDecl() : 0;
|
||||
|
||||
// FIXME: Fabricate an arbitrary expression on the stack and pretend that it
|
||||
// is a temporary being used as the 'this' pointer.
|
||||
LValue This;
|
||||
ImplicitValueInitExpr VIE(RD ? Info.Ctx.getRecordType(RD) : Info.Ctx.IntTy);
|
||||
This.set(&VIE, Info.CurrentCall);
|
||||
|
||||
APValue Scratch;
|
||||
ArrayRef<const Expr*> Args;
|
||||
|
||||
SourceLocation Loc = FD->getLocation();
|
||||
|
||||
if (const CXXConstructorDecl *CD = dyn_cast<CXXConstructorDecl>(FD)) {
|
||||
HandleConstructorCall(Loc, This, Args, CD, Info, Scratch);
|
||||
} else
|
||||
HandleFunctionCall(Loc, FD, (MD && MD->isInstance()) ? &This : 0,
|
||||
Args, FD->getBody(), Info, Scratch);
|
||||
|
||||
return Diags.empty();
|
||||
}
|
||||
|
|
|
@ -972,6 +972,15 @@ bool Sema::CheckConstexprFunctionBody(const FunctionDecl *Dcl, Stmt *Body) {
|
|||
}
|
||||
}
|
||||
|
||||
llvm::SmallVector<PartialDiagnosticAt, 8> Diags;
|
||||
if (!Expr::isPotentialConstantExpr(Dcl, Diags)) {
|
||||
Diag(Dcl->getLocation(), diag::err_constexpr_function_never_constant_expr)
|
||||
<< isa<CXXConstructorDecl>(Dcl);
|
||||
for (size_t I = 0, N = Diags.size(); I != N; ++I)
|
||||
Diag(Diags[I].first, Diags[I].second);
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
|
|
@ -127,3 +127,15 @@ constexpr int MultiReturn() {
|
|||
// return value shall be one of those allowed in a constant expression.
|
||||
//
|
||||
// We implement the proposed resolution of DR1364 and ignore this bullet.
|
||||
// However, we implement the spirit of the check as part of the p5 checking that
|
||||
// a constexpr function must be able to produce a constant expression.
|
||||
namespace DR1364 {
|
||||
constexpr int f(int k) {
|
||||
return k; // ok, even though lvalue-to-rvalue conversion of a function
|
||||
// parameter is not allowed in a constant expression.
|
||||
}
|
||||
int kGlobal; // expected-note {{here}}
|
||||
constexpr int f() { // expected-error {{constexpr function never produces a constant expression}}
|
||||
return kGlobal; // expected-note {{read of non-const}}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -14,6 +14,7 @@ struct NonLiteral { // expected-note 2{{no constexpr constructors}}
|
|||
};
|
||||
struct Literal {
|
||||
constexpr Literal() {}
|
||||
explicit Literal(int); // expected-note 2 {{here}}
|
||||
operator int() const { return 0; }
|
||||
};
|
||||
|
||||
|
@ -190,9 +191,17 @@ constexpr int f(enable_shared_from_this<int>);
|
|||
|
||||
// - every constructor involved in initializing non-static data members and base
|
||||
// class sub-objects shall be a constexpr constructor.
|
||||
//
|
||||
// FIXME: Implement this as part of the 'must be able to produce a constant
|
||||
// expression' rules.
|
||||
struct ConstexprBaseMemberCtors : Literal {
|
||||
Literal l;
|
||||
|
||||
constexpr ConstexprBaseMemberCtors() : Literal(), l() {} // ok
|
||||
constexpr ConstexprBaseMemberCtors(char) : // expected-error {{constexpr constructor never produces a constant expression}}
|
||||
Literal(0), // expected-note {{non-constexpr constructor}}
|
||||
l() {}
|
||||
constexpr ConstexprBaseMemberCtors(double) : Literal(), // expected-error {{constexpr constructor never produces a constant expression}}
|
||||
l(0) // expected-note {{non-constexpr constructor}}
|
||||
{}
|
||||
};
|
||||
|
||||
// - every assignment-expression that is an initializer-caluse appearing
|
||||
// directly or indirectly within a brace-or-equal-initializer for a non-static
|
||||
|
@ -215,6 +224,14 @@ struct X {
|
|||
// expression.
|
||||
//
|
||||
// We implement the proposed resolution of DR1364 and ignore this bullet.
|
||||
// However, we implement the intent of this wording as part of the p5 check that
|
||||
// the function must be able to produce a constant expression.
|
||||
int kGlobal; // expected-note {{here}}
|
||||
struct Z {
|
||||
constexpr Z(int a) : n(a) {}
|
||||
constexpr Z() : n(kGlobal) {} // expected-error {{constexpr constructor never produces a constant expression}} expected-note {{read of non-const}}
|
||||
int n;
|
||||
};
|
||||
|
||||
|
||||
namespace StdExample {
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
// RUN: %clang_cc1 -fsyntax-only -verify -std=c++11 %s
|
||||
// RUN: %clang_cc1 -fsyntax-only -verify -std=c++11 -fcxx-exceptions %s
|
||||
|
||||
namespace StdExample {
|
||||
|
||||
|
@ -15,18 +15,61 @@ namespace N {
|
|||
constexpr int c = 0;
|
||||
constexpr int g4() { return N::h(); }
|
||||
|
||||
// FIXME: constexpr calls aren't recognized as ICEs yet, just as foldable.
|
||||
#define JOIN2(a, b) a ## b
|
||||
#define JOIN(a, b) JOIN2(a, b)
|
||||
#define CHECK(n, m) using JOIN(A, __LINE__) = int[n]; using JOIN(A, __LINE__) = int[m];
|
||||
CHECK(f(0), 0)
|
||||
CHECK(f('0'), 1)
|
||||
CHECK(g1(), 0)
|
||||
CHECK(g2(0), 1)
|
||||
CHECK(g2(1), 1)
|
||||
CHECK(g3(0), 1)
|
||||
CHECK(g3(1), 1)
|
||||
CHECK(N::h(), 5)
|
||||
CHECK(g4(), 5)
|
||||
static_assert(f(0) == 0, "");
|
||||
static_assert(f('0') == 1, "");
|
||||
static_assert(g1() == 0, "");
|
||||
static_assert(g2(0) == 1, "");
|
||||
static_assert(g2(1) == 1, "");
|
||||
static_assert(g3(0) == 1, "");
|
||||
static_assert(g3(1) == 1, "");
|
||||
static_assert(N::h() == 5, "");
|
||||
static_assert(g4() == 5, "");
|
||||
|
||||
|
||||
constexpr int f(bool b)
|
||||
{ return b ? throw 0 : 0; } // ok
|
||||
constexpr int f() { return throw 0, 0; } // expected-error {{constexpr function never produces a constant expression}} expected-note {{subexpression}}
|
||||
|
||||
struct B {
|
||||
constexpr B(int x) : i(0) { }
|
||||
int i;
|
||||
};
|
||||
|
||||
int global; // expected-note {{declared here}}
|
||||
|
||||
struct D : B {
|
||||
constexpr D() : B(global) { } // expected-error {{constexpr constructor never produces a constant expression}} expected-note {{read of non-const}}
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
namespace PotentialConstant {
|
||||
|
||||
constexpr int Comma(int n) { return // expected-error {{constexpr function never produces a constant expression}}
|
||||
(void)(n * 2),
|
||||
throw 0, // expected-note {{subexpression}}
|
||||
0;
|
||||
}
|
||||
|
||||
int ng; // expected-note 5{{here}}
|
||||
constexpr int BinaryOp1(int n) { return n + ng; } // expected-error {{never produces}} expected-note {{read}}
|
||||
constexpr int BinaryOp2(int n) { return ng + n; } // expected-error {{never produces}} expected-note {{read}}
|
||||
|
||||
double dg; // expected-note 2{{here}}
|
||||
constexpr double BinaryOp1(double d) { return d + dg; } // expected-error {{never produces}} expected-note {{read}}
|
||||
constexpr double BinaryOp2(double d) { return dg + d; } // expected-error {{never produces}} expected-note {{read}}
|
||||
|
||||
constexpr int Add(int a, int b, int c) { return a + b + c; }
|
||||
constexpr int FunctionArgs(int a) { return Add(a, ng, a); } // expected-error {{never produces}} expected-note {{read}}
|
||||
|
||||
struct S { int a; int b; int c[2]; };
|
||||
constexpr S InitList(int a) { return { a, ng }; }; // expected-error {{never produces}} expected-note {{read}}
|
||||
constexpr S InitList2(int a) { return { a, a, { ng } }; }; // expected-error {{never produces}} expected-note {{read}}
|
||||
|
||||
constexpr S InitList3(int a) { return a ? (S){ a, a } : (S){ a, ng }; }; // ok
|
||||
|
||||
// FIXME: Check both arms of a ?: if the conditional is a potential constant
|
||||
// expression with an unknown value, and diagnose if neither is constant.
|
||||
constexpr S InitList4(int a) { return a ? (S){ a, ng } : (S){ a, ng }; };
|
||||
|
||||
}
|
||||
|
|
|
@ -190,8 +190,8 @@ namespace MemberPtr {
|
|||
|
||||
namespace CrossFuncLabelDiff {
|
||||
// Make sure we refuse to constant-fold the variable b.
|
||||
constexpr long a() { return (long)&&lbl + (0 && ({lbl: 0;})); }
|
||||
void test() { static long b = (long)&&lbl - a(); lbl: return; }
|
||||
constexpr long a(bool x) { return x ? 0 : (long)&&lbl + (0 && ({lbl: 0;})); }
|
||||
void test() { static long b = (long)&&lbl - a(false); lbl: return; }
|
||||
// CHECK: sub nsw i64 ptrtoint (i8* blockaddress(@_ZN18CrossFuncLabelDiff4testEv, {{.*}}) to i64),
|
||||
// CHECK: store i64 {{.*}}, i64* @_ZZN18CrossFuncLabelDiff4testEvE1b, align 8
|
||||
}
|
||||
|
|
|
@ -206,7 +206,9 @@ namespace ParameterScopes {
|
|||
constexpr int b = MaybeReturnNonstaticRef(true, 0); // expected-error {{constant expression}} expected-note {{in call to 'MaybeReturnNonstaticRef(1, 0)'}}
|
||||
|
||||
constexpr int InternalReturnJunk(int n) {
|
||||
// FIXME: We should reject this: it never produces a constant expression.
|
||||
// TODO: We could reject this: it never produces a constant expression.
|
||||
// However, we currently don't evaluate function calls while testing for
|
||||
// potential constant expressions, for performance.
|
||||
return MaybeReturnJunk(true, n); // expected-note {{in call to 'MaybeReturnJunk(1, 0)'}}
|
||||
}
|
||||
constexpr int n3 = InternalReturnJunk(0); // expected-error {{must be initialized by a constant expression}} expected-note {{in call to 'InternalReturnJunk(0)'}}
|
||||
|
@ -969,10 +971,9 @@ namespace PR11595 {
|
|||
struct B { B(); A& x; };
|
||||
static_assert(B().x == 3, ""); // expected-error {{constant expression}} expected-note {{non-literal type 'PR11595::B' cannot be used in a constant expression}}
|
||||
|
||||
constexpr bool f(int k) {
|
||||
constexpr bool f(int k) { // expected-error {{constexpr function never produces a constant expression}}
|
||||
return B().x == k; // expected-note {{non-literal type 'PR11595::B' cannot be used in a constant expression}}
|
||||
}
|
||||
constexpr int n = f(1); // expected-error {{must be initialized by a constant expression}} expected-note {{in call to 'f(1)'}}
|
||||
}
|
||||
|
||||
namespace ExprWithCleanups {
|
||||
|
|
Loading…
Reference in New Issue