forked from OSchip/llvm-project
Reinstate r142844 (reverted in r142872) now that lvalue-to-rvalue conversions
are present in all the necessary places: In constant expression evaluation, evaluate lvalues as lvalues and rvalues as rvalues. Remove special case for caching reference initialization and fix a cyclic initialization crash in the process. llvm-svn: 143204
This commit is contained in:
parent
effd094438
commit
11562c5e10
|
@ -1274,7 +1274,7 @@ public:
|
|||
CanQualType getCanonicalParamType(QualType T) const;
|
||||
|
||||
/// \brief Determine whether the given types are equivalent.
|
||||
bool hasSameType(QualType T1, QualType T2) {
|
||||
bool hasSameType(QualType T1, QualType T2) const {
|
||||
return getCanonicalType(T1) == getCanonicalType(T2);
|
||||
}
|
||||
|
||||
|
@ -1294,7 +1294,7 @@ public:
|
|||
|
||||
/// \brief Determine whether the given types are equivalent after
|
||||
/// cvr-qualifiers have been removed.
|
||||
bool hasSameUnqualifiedType(QualType T1, QualType T2) {
|
||||
bool hasSameUnqualifiedType(QualType T1, QualType T2) const {
|
||||
return getCanonicalType(T1).getTypePtr() ==
|
||||
getCanonicalType(T2).getTypePtr();
|
||||
}
|
||||
|
|
|
@ -465,7 +465,8 @@ public:
|
|||
/// Evaluate - Return true if this is a constant which we can fold using
|
||||
/// any crazy technique (that has nothing to do with language standards) that
|
||||
/// we want to. If this function returns true, it returns the folded constant
|
||||
/// in Result.
|
||||
/// in Result. If this expression is a glvalue, an lvalue-to-rvalue conversion
|
||||
/// will be applied.
|
||||
bool Evaluate(EvalResult &Result, const ASTContext &Ctx) const;
|
||||
|
||||
/// EvaluateAsBooleanCondition - Return true if this is a constant
|
||||
|
|
|
@ -148,10 +148,13 @@ static bool IsGlobalLValue(const Expr* E) {
|
|||
if (const CompoundLiteralExpr *CLE = dyn_cast<CompoundLiteralExpr>(E))
|
||||
return CLE->isFileScope();
|
||||
|
||||
if (isa<MemberExpr>(E))
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool EvalPointerValueAsBool(LValue& Value, bool& Result) {
|
||||
static bool EvalPointerValueAsBool(const LValue &Value, bool &Result) {
|
||||
const Expr* Base = Value.Base;
|
||||
|
||||
// A null base expression indicates a null pointer. These are always
|
||||
|
@ -183,40 +186,44 @@ static bool EvalPointerValueAsBool(LValue& Value, bool& Result) {
|
|||
return true;
|
||||
}
|
||||
|
||||
static bool HandleConversionToBool(const Expr* E, bool& Result,
|
||||
EvalInfo &Info) {
|
||||
if (E->getType()->isIntegralOrEnumerationType()) {
|
||||
APSInt IntResult;
|
||||
if (!EvaluateInteger(E, IntResult, Info))
|
||||
return false;
|
||||
Result = IntResult != 0;
|
||||
static bool HandleConversionToBool(const APValue &Val, bool &Result) {
|
||||
switch (Val.getKind()) {
|
||||
case APValue::Uninitialized:
|
||||
return false;
|
||||
case APValue::Int:
|
||||
Result = Val.getInt().getBoolValue();
|
||||
return true;
|
||||
} else if (E->getType()->isRealFloatingType()) {
|
||||
APFloat FloatResult(0.0);
|
||||
if (!EvaluateFloat(E, FloatResult, Info))
|
||||
return false;
|
||||
Result = !FloatResult.isZero();
|
||||
case APValue::Float:
|
||||
Result = !Val.getFloat().isZero();
|
||||
return true;
|
||||
} else if (E->getType()->hasPointerRepresentation()) {
|
||||
LValue PointerResult;
|
||||
if (!EvaluatePointer(E, PointerResult, Info))
|
||||
return false;
|
||||
return EvalPointerValueAsBool(PointerResult, Result);
|
||||
} else if (E->getType()->isAnyComplexType()) {
|
||||
ComplexValue ComplexResult;
|
||||
if (!EvaluateComplex(E, ComplexResult, Info))
|
||||
return false;
|
||||
if (ComplexResult.isComplexFloat()) {
|
||||
Result = !ComplexResult.getComplexFloatReal().isZero() ||
|
||||
!ComplexResult.getComplexFloatImag().isZero();
|
||||
} else {
|
||||
Result = ComplexResult.getComplexIntReal().getBoolValue() ||
|
||||
ComplexResult.getComplexIntImag().getBoolValue();
|
||||
case APValue::ComplexInt:
|
||||
Result = Val.getComplexIntReal().getBoolValue() ||
|
||||
Val.getComplexIntImag().getBoolValue();
|
||||
return true;
|
||||
case APValue::ComplexFloat:
|
||||
Result = !Val.getComplexFloatReal().isZero() ||
|
||||
!Val.getComplexFloatImag().isZero();
|
||||
return true;
|
||||
case APValue::LValue:
|
||||
{
|
||||
LValue PointerResult;
|
||||
PointerResult.setFrom(Val);
|
||||
return EvalPointerValueAsBool(PointerResult, Result);
|
||||
}
|
||||
return true;
|
||||
case APValue::Vector:
|
||||
return false;
|
||||
}
|
||||
|
||||
return false;
|
||||
llvm_unreachable("unknown APValue kind");
|
||||
}
|
||||
|
||||
static bool EvaluateAsBooleanCondition(const Expr *E, bool &Result,
|
||||
EvalInfo &Info) {
|
||||
assert(E->isRValue() && "missing lvalue-to-rvalue conv in bool condition");
|
||||
APValue Val;
|
||||
if (!Evaluate(Val, Info, E))
|
||||
return false;
|
||||
return HandleConversionToBool(Val, Result);
|
||||
}
|
||||
|
||||
static APSInt HandleFloatToIntCast(QualType DestType, QualType SrcType,
|
||||
|
@ -263,6 +270,8 @@ static APFloat HandleIntToFloatCast(QualType DestType, QualType SrcType,
|
|||
|
||||
/// Try to evaluate the initializer for a variable declaration.
|
||||
static APValue *EvaluateVarDeclInit(EvalInfo &Info, const VarDecl *VD) {
|
||||
// FIXME: If this is a parameter to an active constexpr function call, perform
|
||||
// substitution now.
|
||||
if (isa<ParmVarDecl>(VD))
|
||||
return 0;
|
||||
|
||||
|
@ -278,10 +287,11 @@ static APValue *EvaluateVarDeclInit(EvalInfo &Info, const VarDecl *VD) {
|
|||
|
||||
VD->setEvaluatingValue();
|
||||
|
||||
// FIXME: If the initializer isn't a constant expression, propagate up any
|
||||
// diagnostic explaining why not.
|
||||
Expr::EvalResult EResult;
|
||||
if (Init->Evaluate(EResult, Info.Ctx) && !EResult.HasSideEffects)
|
||||
EvalInfo InitInfo(Info.Ctx, EResult);
|
||||
// FIXME: The caller will need to know whether the value was a constant
|
||||
// expression. If not, we should propagate up a diagnostic.
|
||||
if (Evaluate(EResult.Val, InitInfo, Init))
|
||||
VD->setEvaluatedValue(EResult.Val);
|
||||
else
|
||||
VD->setEvaluatedValue(APValue());
|
||||
|
@ -289,11 +299,92 @@ static APValue *EvaluateVarDeclInit(EvalInfo &Info, const VarDecl *VD) {
|
|||
return VD->getEvaluatedValue();
|
||||
}
|
||||
|
||||
bool IsConstNonVolatile(QualType T) {
|
||||
static bool IsConstNonVolatile(QualType T) {
|
||||
Qualifiers Quals = T.getQualifiers();
|
||||
return Quals.hasConst() && !Quals.hasVolatile();
|
||||
}
|
||||
|
||||
bool HandleLValueToRValueConversion(EvalInfo &Info, QualType Type,
|
||||
const LValue &LVal, APValue &RVal) {
|
||||
const Expr *Base = LVal.Base;
|
||||
|
||||
// FIXME: Indirection through a null pointer deserves a diagnostic.
|
||||
if (!Base)
|
||||
return false;
|
||||
|
||||
// FIXME: Support accessing subobjects of objects of literal types. A simple
|
||||
// byte offset is insufficient for C++11 semantics: we need to know how the
|
||||
// reference was formed (which union member was named, for instance).
|
||||
// FIXME: Support subobjects of StringLiteral and PredefinedExpr.
|
||||
if (!LVal.Offset.isZero())
|
||||
return false;
|
||||
|
||||
const Decl *D = 0;
|
||||
|
||||
if (const DeclRefExpr *DRE = dyn_cast<DeclRefExpr>(Base)) {
|
||||
// If the lvalue has been cast to some other type, don't try to read it.
|
||||
// FIXME: Could simulate a bitcast here.
|
||||
if (!Info.Ctx.hasSameUnqualifiedType(Type, DRE->getType()))
|
||||
return false;
|
||||
D = DRE->getDecl();
|
||||
}
|
||||
|
||||
// FIXME: Static data members accessed via a MemberExpr are represented as
|
||||
// that MemberExpr. We should use the Decl directly instead.
|
||||
if (const MemberExpr *ME = dyn_cast<MemberExpr>(Base)) {
|
||||
if (!Info.Ctx.hasSameUnqualifiedType(Type, ME->getType()))
|
||||
return false;
|
||||
D = ME->getMemberDecl();
|
||||
assert(!isa<FieldDecl>(D) && "shouldn't see fields here");
|
||||
}
|
||||
|
||||
if (D) {
|
||||
// In C++98, const, non-volatile integers initialized with ICEs are ICEs.
|
||||
// In C++11, constexpr, non-volatile variables initialized with constant
|
||||
// expressions are constant expressions too.
|
||||
// In C, such things can also be folded, although they are not ICEs.
|
||||
//
|
||||
// FIXME: Allow folding any const variable of literal type initialized with
|
||||
// a constant expression. For now, we only allow variables with integral and
|
||||
// floating types to be folded.
|
||||
const VarDecl *VD = dyn_cast<VarDecl>(D);
|
||||
if (!VD || !IsConstNonVolatile(VD->getType()) ||
|
||||
(!Type->isIntegralOrEnumerationType() && !Type->isRealFloatingType()))
|
||||
return false;
|
||||
|
||||
APValue *V = EvaluateVarDeclInit(Info, VD);
|
||||
if (!V || V->isUninit())
|
||||
return false;
|
||||
|
||||
if (!VD->getAnyInitializer()->isLValue()) {
|
||||
RVal = *V;
|
||||
return true;
|
||||
}
|
||||
|
||||
// The declaration was initialized by an lvalue, with no lvalue-to-rvalue
|
||||
// conversion. This happens when the declaration and the lvalue should be
|
||||
// considered synonymous, for instance when initializing an array of char
|
||||
// from a string literal. Continue as if the initializer lvalue was the
|
||||
// value we were originally given.
|
||||
Base = V->getLValueBase();
|
||||
if (!V->getLValueOffset().isZero())
|
||||
return false;
|
||||
}
|
||||
|
||||
// FIXME: C++11: Support MaterializeTemporaryExpr in LValueExprEvaluator and
|
||||
// here.
|
||||
|
||||
// In C99, a CompoundLiteralExpr is an lvalue, and we defer evaluating the
|
||||
// initializer until now for such expressions. Such an expression can't be
|
||||
// an ICE in C, so this only matters for fold.
|
||||
if (const CompoundLiteralExpr *CLE = dyn_cast<CompoundLiteralExpr>(Base)) {
|
||||
assert(!Info.getLangOpts().CPlusPlus && "lvalue compound literal in c++?");
|
||||
return Evaluate(RVal, Info, CLE->getInitializer());
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
namespace {
|
||||
class HasSideEffect
|
||||
: public ConstStmtVisitor<HasSideEffect, bool> {
|
||||
|
@ -454,7 +545,7 @@ public:
|
|||
return DerivedError(E);
|
||||
|
||||
bool cond;
|
||||
if (!HandleConversionToBool(E->getCond(), cond, Info))
|
||||
if (!EvaluateAsBooleanCondition(E->getCond(), cond, Info))
|
||||
return DerivedError(E);
|
||||
|
||||
return StmtVisitorTy::Visit(cond ? E->getTrueExpr() : E->getFalseExpr());
|
||||
|
@ -462,10 +553,10 @@ public:
|
|||
|
||||
RetTy VisitConditionalOperator(const ConditionalOperator *E) {
|
||||
bool BoolResult;
|
||||
if (!HandleConversionToBool(E->getCond(), BoolResult, Info))
|
||||
if (!EvaluateAsBooleanCondition(E->getCond(), BoolResult, Info))
|
||||
return DerivedError(E);
|
||||
|
||||
Expr* EvalExpr = BoolResult ? E->getTrueExpr() : E->getFalseExpr();
|
||||
Expr *EvalExpr = BoolResult ? E->getTrueExpr() : E->getFalseExpr();
|
||||
return StmtVisitorTy::Visit(EvalExpr);
|
||||
}
|
||||
|
||||
|
@ -477,6 +568,9 @@ public:
|
|||
return DerivedSuccess(*value, E);
|
||||
}
|
||||
|
||||
RetTy VisitCompoundLiteralExpr(const CompoundLiteralExpr *E) {
|
||||
return StmtVisitorTy::Visit(E->getInitializer());
|
||||
}
|
||||
RetTy VisitInitListExpr(const InitListExpr *E) {
|
||||
if (Info.getLangOpts().CPlusPlus0x) {
|
||||
if (E->getNumInits() == 0)
|
||||
|
@ -493,6 +587,28 @@ public:
|
|||
return DerivedValueInitialization(E);
|
||||
}
|
||||
|
||||
RetTy VisitCastExpr(const CastExpr *E) {
|
||||
switch (E->getCastKind()) {
|
||||
default:
|
||||
break;
|
||||
|
||||
case CK_NoOp:
|
||||
return StmtVisitorTy::Visit(E->getSubExpr());
|
||||
|
||||
case CK_LValueToRValue: {
|
||||
LValue LVal;
|
||||
if (EvaluateLValue(E->getSubExpr(), LVal, Info)) {
|
||||
APValue RVal;
|
||||
if (HandleLValueToRValueConversion(Info, E->getType(), LVal, RVal))
|
||||
return DerivedSuccess(RVal, E);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return DerivedError(E);
|
||||
}
|
||||
|
||||
/// Visit a value which is evaluated, but whose value is ignored.
|
||||
void VisitIgnoredValue(const Expr *E) {
|
||||
APValue Scratch;
|
||||
|
@ -505,6 +621,23 @@ public:
|
|||
|
||||
//===----------------------------------------------------------------------===//
|
||||
// LValue Evaluation
|
||||
//
|
||||
// This is used for evaluating lvalues (in C and C++), xvalues (in C++11),
|
||||
// function designators (in C), decl references to void objects (in C), and
|
||||
// temporaries (if building with -Wno-address-of-temporary).
|
||||
//
|
||||
// LValue evaluation produces values comprising a base expression of one of the
|
||||
// following types:
|
||||
// * DeclRefExpr
|
||||
// * MemberExpr for a static member
|
||||
// * CompoundLiteralExpr in C
|
||||
// * StringLiteral
|
||||
// * PredefinedExpr
|
||||
// * ObjCEncodeExpr
|
||||
// * AddrLabelExpr
|
||||
// * BlockExpr
|
||||
// * CallExpr for a MakeStringConstant builtin
|
||||
// plus an offset in bytes.
|
||||
//===----------------------------------------------------------------------===//
|
||||
namespace {
|
||||
class LValueExprEvaluator
|
||||
|
@ -530,6 +663,8 @@ public:
|
|||
return false;
|
||||
}
|
||||
|
||||
bool VisitVarDecl(const Expr *E, const VarDecl *VD);
|
||||
|
||||
bool VisitDeclRefExpr(const DeclRefExpr *E);
|
||||
bool VisitPredefinedExpr(const PredefinedExpr *E) { return Success(E); }
|
||||
bool VisitCompoundLiteralExpr(const CompoundLiteralExpr *E);
|
||||
|
@ -542,13 +677,13 @@ public:
|
|||
bool VisitCastExpr(const CastExpr *E) {
|
||||
switch (E->getCastKind()) {
|
||||
default:
|
||||
return false;
|
||||
return ExprEvaluatorBaseTy::VisitCastExpr(E);
|
||||
|
||||
case CK_NoOp:
|
||||
case CK_LValueBitCast:
|
||||
return Visit(E->getSubExpr());
|
||||
|
||||
// FIXME: Support CK_DerivedToBase and friends.
|
||||
// FIXME: Support CK_DerivedToBase and CK_UncheckedDerivedToBase.
|
||||
// Reuse PointerExprEvaluator::VisitCastExpr for these.
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -557,39 +692,52 @@ public:
|
|||
};
|
||||
} // end anonymous namespace
|
||||
|
||||
/// Evaluate an expression as an lvalue. This can be legitimately called on
|
||||
/// expressions which are not glvalues, in a few cases:
|
||||
/// * function designators in C,
|
||||
/// * "extern void" objects,
|
||||
/// * temporaries, if building with -Wno-address-of-temporary.
|
||||
static bool EvaluateLValue(const Expr* E, LValue& Result, EvalInfo &Info) {
|
||||
assert((E->isGLValue() || E->getType()->isFunctionType() ||
|
||||
E->getType()->isVoidType() || isa<CXXTemporaryObjectExpr>(E)) &&
|
||||
"can't evaluate expression as an lvalue");
|
||||
return LValueExprEvaluator(Info, Result).Visit(E);
|
||||
}
|
||||
|
||||
bool LValueExprEvaluator::VisitDeclRefExpr(const DeclRefExpr *E) {
|
||||
if (isa<FunctionDecl>(E->getDecl())) {
|
||||
if (isa<FunctionDecl>(E->getDecl()))
|
||||
return Success(E);
|
||||
} else if (const VarDecl* VD = dyn_cast<VarDecl>(E->getDecl())) {
|
||||
if (!VD->getType()->isReferenceType())
|
||||
return Success(E);
|
||||
// Reference parameters can refer to anything even if they have an
|
||||
// "initializer" in the form of a default argument.
|
||||
if (!isa<ParmVarDecl>(VD)) {
|
||||
// FIXME: Check whether VD might be overridden!
|
||||
if (const VarDecl* VD = dyn_cast<VarDecl>(E->getDecl()))
|
||||
return VisitVarDecl(E, VD);
|
||||
return Error(E);
|
||||
}
|
||||
|
||||
// Check for recursive initializers of references.
|
||||
if (PrevDecl == VD)
|
||||
return Error(E);
|
||||
PrevDecl = VD;
|
||||
if (const Expr *Init = VD->getAnyInitializer())
|
||||
return Visit(Init);
|
||||
}
|
||||
}
|
||||
bool LValueExprEvaluator::VisitVarDecl(const Expr *E, const VarDecl *VD) {
|
||||
if (!VD->getType()->isReferenceType())
|
||||
return Success(E);
|
||||
|
||||
return ExprEvaluatorBaseTy::VisitDeclRefExpr(E);
|
||||
APValue *V = EvaluateVarDeclInit(Info, VD);
|
||||
if (V && !V->isUninit())
|
||||
return Success(*V, E);
|
||||
|
||||
return Error(E);
|
||||
}
|
||||
|
||||
bool
|
||||
LValueExprEvaluator::VisitCompoundLiteralExpr(const CompoundLiteralExpr *E) {
|
||||
assert(!Info.getLangOpts().CPlusPlus && "lvalue compound literal in c++?");
|
||||
// Defer visiting the literal until the lvalue-to-rvalue conversion. We can
|
||||
// only see this when folding in C, so there's no standard to follow here.
|
||||
return Success(E);
|
||||
}
|
||||
|
||||
bool LValueExprEvaluator::VisitMemberExpr(const MemberExpr *E) {
|
||||
// Handle static data members.
|
||||
if (const VarDecl *VD = dyn_cast<VarDecl>(E->getMemberDecl())) {
|
||||
VisitIgnoredValue(E->getBase());
|
||||
return VisitVarDecl(E, VD);
|
||||
}
|
||||
|
||||
QualType Ty;
|
||||
if (E->isArrow()) {
|
||||
if (!EvaluatePointer(E->getBase(), Result, Info))
|
||||
|
@ -617,6 +765,10 @@ bool LValueExprEvaluator::VisitMemberExpr(const MemberExpr *E) {
|
|||
}
|
||||
|
||||
bool LValueExprEvaluator::VisitArraySubscriptExpr(const ArraySubscriptExpr *E) {
|
||||
// FIXME: Deal with vectors as array subscript bases.
|
||||
if (E->getBase()->getType()->isVectorType())
|
||||
return false;
|
||||
|
||||
if (!EvaluatePointer(E->getBase(), Result, Info))
|
||||
return false;
|
||||
|
||||
|
@ -684,7 +836,7 @@ public:
|
|||
} // end anonymous namespace
|
||||
|
||||
static bool EvaluatePointer(const Expr* E, LValue& Result, EvalInfo &Info) {
|
||||
assert(E->getType()->hasPointerRepresentation());
|
||||
assert(E->isRValue() && E->getType()->hasPointerRepresentation());
|
||||
return PointerExprEvaluator(Info, Result).Visit(E);
|
||||
}
|
||||
|
||||
|
@ -740,7 +892,6 @@ bool PointerExprEvaluator::VisitCastExpr(const CastExpr* E) {
|
|||
default:
|
||||
break;
|
||||
|
||||
case CK_NoOp:
|
||||
case CK_BitCast:
|
||||
case CK_CPointerToObjCPointerCast:
|
||||
case CK_BlockPointerToObjCPointerCast:
|
||||
|
@ -810,7 +961,7 @@ bool PointerExprEvaluator::VisitCastExpr(const CastExpr* E) {
|
|||
return EvaluateLValue(SubExpr, Result, Info);
|
||||
}
|
||||
|
||||
return false;
|
||||
return ExprEvaluatorBaseTy::VisitCastExpr(E);
|
||||
}
|
||||
|
||||
bool PointerExprEvaluator::VisitCallExpr(const CallExpr *E) {
|
||||
|
@ -852,7 +1003,6 @@ namespace {
|
|||
bool VisitUnaryReal(const UnaryOperator *E)
|
||||
{ return Visit(E->getSubExpr()); }
|
||||
bool VisitCastExpr(const CastExpr* E);
|
||||
bool VisitCompoundLiteralExpr(const CompoundLiteralExpr *E);
|
||||
bool VisitInitListExpr(const InitListExpr *E);
|
||||
bool VisitUnaryImag(const UnaryOperator *E);
|
||||
// FIXME: Missing: unary -, unary ~, binary add/sub/mul/div,
|
||||
|
@ -864,8 +1014,7 @@ namespace {
|
|||
} // end anonymous namespace
|
||||
|
||||
static bool EvaluateVector(const Expr* E, APValue& Result, EvalInfo &Info) {
|
||||
if (!E->getType()->isVectorType())
|
||||
return false;
|
||||
assert(E->isRValue() && E->getType()->isVectorType() &&"not a vector rvalue");
|
||||
return VectorExprEvaluator(Info, Result).Visit(E);
|
||||
}
|
||||
|
||||
|
@ -927,19 +1076,11 @@ bool VectorExprEvaluator::VisitCastExpr(const CastExpr* E) {
|
|||
}
|
||||
return Success(Elts, E);
|
||||
}
|
||||
case CK_LValueToRValue:
|
||||
case CK_NoOp:
|
||||
return Visit(SE);
|
||||
default:
|
||||
return Error(E);
|
||||
return ExprEvaluatorBaseTy::VisitCastExpr(E);
|
||||
}
|
||||
}
|
||||
|
||||
bool
|
||||
VectorExprEvaluator::VisitCompoundLiteralExpr(const CompoundLiteralExpr *E) {
|
||||
return Visit(E->getInitializer());
|
||||
}
|
||||
|
||||
bool
|
||||
VectorExprEvaluator::VisitInitListExpr(const InitListExpr *E) {
|
||||
const VectorType *VT = E->getType()->castAs<VectorType>();
|
||||
|
@ -1022,6 +1163,10 @@ bool VectorExprEvaluator::VisitUnaryImag(const UnaryOperator *E) {
|
|||
|
||||
//===----------------------------------------------------------------------===//
|
||||
// Integer Evaluation
|
||||
//
|
||||
// As a GNU extension, we support casting pointers to sufficiently-wide integer
|
||||
// types and back in constant folding. Integer values are thus represented
|
||||
// either as an integer-valued APValue, or as an lvalue-valued APValue.
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
namespace {
|
||||
|
@ -1105,8 +1250,7 @@ public:
|
|||
}
|
||||
bool VisitMemberExpr(const MemberExpr *E) {
|
||||
if (CheckReferencedDecl(E, E->getMemberDecl())) {
|
||||
// Conservatively assume a MemberExpr will have side-effects
|
||||
Info.EvalStatus.HasSideEffects = true;
|
||||
VisitIgnoredValue(E->getBase());
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -1161,14 +1305,20 @@ private:
|
|||
};
|
||||
} // end anonymous namespace
|
||||
|
||||
/// EvaluateIntegerOrLValue - Evaluate an rvalue integral-typed expression, and
|
||||
/// produce either the integer value or a pointer.
|
||||
///
|
||||
/// GCC has a heinous extension which folds casts between pointer types and
|
||||
/// pointer-sized integral types. We support this by allowing the evaluation of
|
||||
/// an integer rvalue to produce a pointer (represented as an lvalue) instead.
|
||||
/// Some simple arithmetic on such values is supported (they are treated much
|
||||
/// like char*).
|
||||
static bool EvaluateIntegerOrLValue(const Expr* E, APValue &Result, EvalInfo &Info) {
|
||||
assert(E->getType()->isIntegralOrEnumerationType());
|
||||
assert(E->isRValue() && E->getType()->isIntegralOrEnumerationType());
|
||||
return IntExprEvaluator(Info, Result).Visit(E);
|
||||
}
|
||||
|
||||
static bool EvaluateInteger(const Expr* E, APSInt &Result, EvalInfo &Info) {
|
||||
assert(E->getType()->isIntegralOrEnumerationType());
|
||||
|
||||
APValue Val;
|
||||
if (!EvaluateIntegerOrLValue(E, Val, Info) || !Val.isInt())
|
||||
return false;
|
||||
|
@ -1197,18 +1347,6 @@ bool IntExprEvaluator::CheckReferencedDecl(const Expr* E, const Decl* D) {
|
|||
return Success(Val, E);
|
||||
}
|
||||
}
|
||||
|
||||
// In C++, const, non-volatile integers initialized with ICEs are ICEs.
|
||||
// In C, they can also be folded, although they are not ICEs.
|
||||
if (IsConstNonVolatile(E->getType())) {
|
||||
if (const VarDecl *VD = dyn_cast<VarDecl>(D)) {
|
||||
APValue *V = EvaluateVarDeclInit(Info, VD);
|
||||
if (V && V->isInt())
|
||||
return Success(V->getInt(), E);
|
||||
}
|
||||
}
|
||||
|
||||
// Otherwise, random variable references are not constants.
|
||||
return false;
|
||||
}
|
||||
|
||||
|
@ -1411,6 +1549,9 @@ bool IntExprEvaluator::VisitCallExpr(const CallExpr *E) {
|
|||
}
|
||||
|
||||
bool IntExprEvaluator::VisitBinaryOperator(const BinaryOperator *E) {
|
||||
if (E->isAssignmentOp())
|
||||
return Error(E->getOperatorLoc(), diag::note_invalid_subexpr_in_ice, E);
|
||||
|
||||
if (E->getOpcode() == BO_Comma) {
|
||||
VisitIgnoredValue(E->getLHS());
|
||||
return Visit(E->getRHS());
|
||||
|
@ -1421,20 +1562,20 @@ bool IntExprEvaluator::VisitBinaryOperator(const BinaryOperator *E) {
|
|||
// necessarily integral
|
||||
bool lhsResult, rhsResult;
|
||||
|
||||
if (HandleConversionToBool(E->getLHS(), lhsResult, Info)) {
|
||||
if (EvaluateAsBooleanCondition(E->getLHS(), lhsResult, Info)) {
|
||||
// We were able to evaluate the LHS, see if we can get away with not
|
||||
// evaluating the RHS: 0 && X -> 0, 1 || X -> 1
|
||||
if (lhsResult == (E->getOpcode() == BO_LOr))
|
||||
return Success(lhsResult, E);
|
||||
|
||||
if (HandleConversionToBool(E->getRHS(), rhsResult, Info)) {
|
||||
if (EvaluateAsBooleanCondition(E->getRHS(), rhsResult, Info)) {
|
||||
if (E->getOpcode() == BO_LOr)
|
||||
return Success(lhsResult || rhsResult, E);
|
||||
else
|
||||
return Success(lhsResult && rhsResult, E);
|
||||
}
|
||||
} else {
|
||||
if (HandleConversionToBool(E->getRHS(), rhsResult, Info)) {
|
||||
if (EvaluateAsBooleanCondition(E->getRHS(), rhsResult, Info)) {
|
||||
// We can't evaluate the LHS; however, sometimes the result
|
||||
// is determined by the RHS: X && 0 -> 0, X || 1 -> 1.
|
||||
if (rhsResult == (E->getOpcode() == BO_LOr) ||
|
||||
|
@ -1590,58 +1731,60 @@ bool IntExprEvaluator::VisitBinaryOperator(const BinaryOperator *E) {
|
|||
}
|
||||
|
||||
// The LHS of a constant expr is always evaluated and needed.
|
||||
if (!Visit(E->getLHS()))
|
||||
APValue LHSVal;
|
||||
if (!EvaluateIntegerOrLValue(E->getLHS(), LHSVal, Info))
|
||||
return false; // error in subexpression.
|
||||
|
||||
APValue RHSVal;
|
||||
if (!EvaluateIntegerOrLValue(E->getRHS(), RHSVal, Info))
|
||||
if (!Visit(E->getRHS()))
|
||||
return false;
|
||||
APValue &RHSVal = Result;
|
||||
|
||||
// Handle cases like (unsigned long)&a + 4.
|
||||
if (E->isAdditiveOp() && Result.isLValue() && RHSVal.isInt()) {
|
||||
CharUnits Offset = Result.getLValueOffset();
|
||||
if (E->isAdditiveOp() && LHSVal.isLValue() && RHSVal.isInt()) {
|
||||
CharUnits Offset = LHSVal.getLValueOffset();
|
||||
CharUnits AdditionalOffset = CharUnits::fromQuantity(
|
||||
RHSVal.getInt().getZExtValue());
|
||||
if (E->getOpcode() == BO_Add)
|
||||
Offset += AdditionalOffset;
|
||||
else
|
||||
Offset -= AdditionalOffset;
|
||||
Result = APValue(Result.getLValueBase(), Offset);
|
||||
Result = APValue(LHSVal.getLValueBase(), Offset);
|
||||
return true;
|
||||
}
|
||||
|
||||
// Handle cases like 4 + (unsigned long)&a
|
||||
if (E->getOpcode() == BO_Add &&
|
||||
RHSVal.isLValue() && Result.isInt()) {
|
||||
RHSVal.isLValue() && LHSVal.isInt()) {
|
||||
CharUnits Offset = RHSVal.getLValueOffset();
|
||||
Offset += CharUnits::fromQuantity(Result.getInt().getZExtValue());
|
||||
Offset += CharUnits::fromQuantity(LHSVal.getInt().getZExtValue());
|
||||
Result = APValue(RHSVal.getLValueBase(), Offset);
|
||||
return true;
|
||||
}
|
||||
|
||||
// All the following cases expect both operands to be an integer
|
||||
if (!Result.isInt() || !RHSVal.isInt())
|
||||
if (!LHSVal.isInt() || !RHSVal.isInt())
|
||||
return false;
|
||||
|
||||
APSInt& RHS = RHSVal.getInt();
|
||||
APSInt &LHS = LHSVal.getInt();
|
||||
APSInt &RHS = RHSVal.getInt();
|
||||
|
||||
switch (E->getOpcode()) {
|
||||
default:
|
||||
return Error(E->getOperatorLoc(), diag::note_invalid_subexpr_in_ice, E);
|
||||
case BO_Mul: return Success(Result.getInt() * RHS, E);
|
||||
case BO_Add: return Success(Result.getInt() + RHS, E);
|
||||
case BO_Sub: return Success(Result.getInt() - RHS, E);
|
||||
case BO_And: return Success(Result.getInt() & RHS, E);
|
||||
case BO_Xor: return Success(Result.getInt() ^ RHS, E);
|
||||
case BO_Or: return Success(Result.getInt() | RHS, E);
|
||||
case BO_Mul: return Success(LHS * RHS, E);
|
||||
case BO_Add: return Success(LHS + RHS, E);
|
||||
case BO_Sub: return Success(LHS - RHS, E);
|
||||
case BO_And: return Success(LHS & RHS, E);
|
||||
case BO_Xor: return Success(LHS ^ RHS, E);
|
||||
case BO_Or: return Success(LHS | RHS, E);
|
||||
case BO_Div:
|
||||
if (RHS == 0)
|
||||
return Error(E->getOperatorLoc(), diag::note_expr_divide_by_zero, E);
|
||||
return Success(Result.getInt() / RHS, E);
|
||||
return Success(LHS / RHS, E);
|
||||
case BO_Rem:
|
||||
if (RHS == 0)
|
||||
return Error(E->getOperatorLoc(), diag::note_expr_divide_by_zero, E);
|
||||
return Success(Result.getInt() % RHS, E);
|
||||
return Success(LHS % RHS, E);
|
||||
case BO_Shl: {
|
||||
// During constant-folding, a negative shift is an opposite shift.
|
||||
if (RHS.isSigned() && RHS.isNegative()) {
|
||||
|
@ -1651,8 +1794,8 @@ bool IntExprEvaluator::VisitBinaryOperator(const BinaryOperator *E) {
|
|||
|
||||
shift_left:
|
||||
unsigned SA
|
||||
= (unsigned) RHS.getLimitedValue(Result.getInt().getBitWidth()-1);
|
||||
return Success(Result.getInt() << SA, E);
|
||||
= (unsigned) RHS.getLimitedValue(LHS.getBitWidth()-1);
|
||||
return Success(LHS << SA, E);
|
||||
}
|
||||
case BO_Shr: {
|
||||
// During constant-folding, a negative shift is an opposite shift.
|
||||
|
@ -1663,16 +1806,16 @@ bool IntExprEvaluator::VisitBinaryOperator(const BinaryOperator *E) {
|
|||
|
||||
shift_right:
|
||||
unsigned SA =
|
||||
(unsigned) RHS.getLimitedValue(Result.getInt().getBitWidth()-1);
|
||||
return Success(Result.getInt() >> SA, E);
|
||||
(unsigned) RHS.getLimitedValue(LHS.getBitWidth()-1);
|
||||
return Success(LHS >> SA, E);
|
||||
}
|
||||
|
||||
case BO_LT: return Success(Result.getInt() < RHS, E);
|
||||
case BO_GT: return Success(Result.getInt() > RHS, E);
|
||||
case BO_LE: return Success(Result.getInt() <= RHS, E);
|
||||
case BO_GE: return Success(Result.getInt() >= RHS, E);
|
||||
case BO_EQ: return Success(Result.getInt() == RHS, E);
|
||||
case BO_NE: return Success(Result.getInt() != RHS, E);
|
||||
case BO_LT: return Success(LHS < RHS, E);
|
||||
case BO_GT: return Success(LHS > RHS, E);
|
||||
case BO_LE: return Success(LHS <= RHS, E);
|
||||
case BO_GE: return Success(LHS >= RHS, E);
|
||||
case BO_EQ: return Success(LHS == RHS, E);
|
||||
case BO_NE: return Success(LHS != RHS, E);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1833,7 +1976,7 @@ bool IntExprEvaluator::VisitUnaryOperator(const UnaryOperator *E) {
|
|||
if (E->getOpcode() == UO_LNot) {
|
||||
// LNot's operand isn't necessarily an integer, so we handle it specially.
|
||||
bool bres;
|
||||
if (!HandleConversionToBool(E->getSubExpr(), bres, Info))
|
||||
if (!EvaluateAsBooleanCondition(E->getSubExpr(), bres, Info))
|
||||
return false;
|
||||
return Success(!bres, E);
|
||||
}
|
||||
|
@ -1842,8 +1985,9 @@ bool IntExprEvaluator::VisitUnaryOperator(const UnaryOperator *E) {
|
|||
if (!E->getSubExpr()->getType()->isIntegralOrEnumerationType())
|
||||
return false;
|
||||
|
||||
// Get the operand value into 'Result'.
|
||||
if (!Visit(E->getSubExpr()))
|
||||
// Get the operand value.
|
||||
APValue Val;
|
||||
if (!Evaluate(Val, Info, E->getSubExpr()))
|
||||
return false;
|
||||
|
||||
switch (E->getOpcode()) {
|
||||
|
@ -1854,16 +1998,16 @@ bool IntExprEvaluator::VisitUnaryOperator(const UnaryOperator *E) {
|
|||
case UO_Extension:
|
||||
// FIXME: Should extension allow i-c-e extension expressions in its scope?
|
||||
// If so, we could clear the diagnostic ID.
|
||||
return true;
|
||||
return Success(Val, E);
|
||||
case UO_Plus:
|
||||
// The result is always just the subexpr.
|
||||
return true;
|
||||
// The result is just the value.
|
||||
return Success(Val, E);
|
||||
case UO_Minus:
|
||||
if (!Result.isInt()) return false;
|
||||
return Success(-Result.getInt(), E);
|
||||
if (!Val.isInt()) return false;
|
||||
return Success(-Val.getInt(), E);
|
||||
case UO_Not:
|
||||
if (!Result.isInt()) return false;
|
||||
return Success(~Result.getInt(), E);
|
||||
if (!Val.isInt()) return false;
|
||||
return Success(~Val.getInt(), E);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1918,7 +2062,7 @@ bool IntExprEvaluator::VisitCastExpr(const CastExpr *E) {
|
|||
|
||||
case CK_LValueToRValue:
|
||||
case CK_NoOp:
|
||||
return Visit(E->getSubExpr());
|
||||
return ExprEvaluatorBaseTy::VisitCastExpr(E);
|
||||
|
||||
case CK_MemberPointerToBoolean:
|
||||
case CK_PointerToBoolean:
|
||||
|
@ -1927,7 +2071,7 @@ bool IntExprEvaluator::VisitCastExpr(const CastExpr *E) {
|
|||
case CK_FloatingComplexToBoolean:
|
||||
case CK_IntegralComplexToBoolean: {
|
||||
bool BoolResult;
|
||||
if (!HandleConversionToBool(SubExpr, BoolResult, Info))
|
||||
if (!EvaluateAsBooleanCondition(SubExpr, BoolResult, Info))
|
||||
return false;
|
||||
return Success(BoolResult, E);
|
||||
}
|
||||
|
@ -2050,15 +2194,13 @@ public:
|
|||
bool VisitUnaryReal(const UnaryOperator *E);
|
||||
bool VisitUnaryImag(const UnaryOperator *E);
|
||||
|
||||
bool VisitDeclRefExpr(const DeclRefExpr *E);
|
||||
|
||||
// FIXME: Missing: array subscript of vector, member of vector,
|
||||
// ImplicitValueInitExpr
|
||||
};
|
||||
} // end anonymous namespace
|
||||
|
||||
static bool EvaluateFloat(const Expr* E, APFloat& Result, EvalInfo &Info) {
|
||||
assert(E->getType()->isRealFloatingType());
|
||||
assert(E->isRValue() && E->getType()->isRealFloatingType());
|
||||
return FloatExprEvaluator(Info, Result).Visit(E);
|
||||
}
|
||||
|
||||
|
@ -2141,21 +2283,6 @@ bool FloatExprEvaluator::VisitCallExpr(const CallExpr *E) {
|
|||
}
|
||||
}
|
||||
|
||||
bool FloatExprEvaluator::VisitDeclRefExpr(const DeclRefExpr *E) {
|
||||
if (ExprEvaluatorBaseTy::VisitDeclRefExpr(E))
|
||||
return true;
|
||||
|
||||
const VarDecl *VD = dyn_cast<VarDecl>(E->getDecl());
|
||||
if (VD && IsConstNonVolatile(VD->getType())) {
|
||||
APValue *V = EvaluateVarDeclInit(Info, VD);
|
||||
if (V && V->isFloat()) {
|
||||
Result = V->getFloat();
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool FloatExprEvaluator::VisitUnaryReal(const UnaryOperator *E) {
|
||||
if (E->getSubExpr()->getType()->isAnyComplexType()) {
|
||||
ComplexValue CV;
|
||||
|
@ -2245,11 +2372,7 @@ bool FloatExprEvaluator::VisitCastExpr(const CastExpr *E) {
|
|||
|
||||
switch (E->getCastKind()) {
|
||||
default:
|
||||
return false;
|
||||
|
||||
case CK_LValueToRValue:
|
||||
case CK_NoOp:
|
||||
return Visit(SubExpr);
|
||||
return ExprEvaluatorBaseTy::VisitCastExpr(E);
|
||||
|
||||
case CK_IntegralToFloating: {
|
||||
APSInt IntResult;
|
||||
|
@ -2317,7 +2440,7 @@ public:
|
|||
|
||||
static bool EvaluateComplex(const Expr *E, ComplexValue &Result,
|
||||
EvalInfo &Info) {
|
||||
assert(E->getType()->isAnyComplexType());
|
||||
assert(E->isRValue() && E->getType()->isAnyComplexType());
|
||||
return ComplexExprEvaluator(Info, Result).Visit(E);
|
||||
}
|
||||
|
||||
|
@ -2390,7 +2513,7 @@ bool ComplexExprEvaluator::VisitCastExpr(const CastExpr *E) {
|
|||
|
||||
case CK_LValueToRValue:
|
||||
case CK_NoOp:
|
||||
return Visit(E->getSubExpr());
|
||||
return ExprEvaluatorBaseTy::VisitCastExpr(E);
|
||||
|
||||
case CK_Dependent:
|
||||
case CK_GetObjCProperty:
|
||||
|
@ -2634,27 +2757,28 @@ bool ComplexExprEvaluator::VisitUnaryOperator(const UnaryOperator *E) {
|
|||
//===----------------------------------------------------------------------===//
|
||||
|
||||
static bool Evaluate(APValue &Result, EvalInfo &Info, const Expr *E) {
|
||||
if (E->getType()->isVectorType()) {
|
||||
// In C, function designators are not lvalues, but we evaluate them as if they
|
||||
// are.
|
||||
if (E->isGLValue() || E->getType()->isFunctionType()) {
|
||||
LValue LV;
|
||||
if (!EvaluateLValue(E, LV, Info))
|
||||
return false;
|
||||
LV.moveInto(Result);
|
||||
} else if (E->getType()->isVectorType()) {
|
||||
if (!EvaluateVector(E, Result, Info))
|
||||
return false;
|
||||
} else if (E->getType()->isIntegralOrEnumerationType()) {
|
||||
if (!IntExprEvaluator(Info, Result).Visit(E))
|
||||
return false;
|
||||
if (Result.isLValue() &&
|
||||
!IsGlobalLValue(Result.getLValueBase()))
|
||||
return false;
|
||||
} else if (E->getType()->hasPointerRepresentation()) {
|
||||
LValue LV;
|
||||
if (!EvaluatePointer(E, LV, Info))
|
||||
return false;
|
||||
if (!IsGlobalLValue(LV.Base))
|
||||
return false;
|
||||
LV.moveInto(Result);
|
||||
} else if (E->getType()->isRealFloatingType()) {
|
||||
llvm::APFloat F(0.0);
|
||||
if (!EvaluateFloat(E, F, Info))
|
||||
return false;
|
||||
|
||||
Result = APValue(F);
|
||||
} else if (E->getType()->isAnyComplexType()) {
|
||||
ComplexValue C;
|
||||
|
@ -2667,36 +2791,50 @@ static bool Evaluate(APValue &Result, EvalInfo &Info, const Expr *E) {
|
|||
return true;
|
||||
}
|
||||
|
||||
|
||||
/// Evaluate - Return true if this is a constant which we can fold using
|
||||
/// any crazy technique (that has nothing to do with language standards) that
|
||||
/// we want to. If this function returns true, it returns the folded constant
|
||||
/// in Result.
|
||||
/// in Result. If this expression is a glvalue, an lvalue-to-rvalue conversion
|
||||
/// will be applied to the result.
|
||||
bool Expr::Evaluate(EvalResult &Result, const ASTContext &Ctx) const {
|
||||
EvalInfo Info(Ctx, Result);
|
||||
return ::Evaluate(Result.Val, Info, this);
|
||||
|
||||
if (!::Evaluate(Result.Val, Info, this))
|
||||
return false;
|
||||
|
||||
if (isGLValue()) {
|
||||
LValue LV;
|
||||
LV.setFrom(Result.Val);
|
||||
return HandleLValueToRValueConversion(Info, getType(), LV, Result.Val);
|
||||
}
|
||||
|
||||
// FIXME: We don't allow expressions to fold to pointers or references to
|
||||
// locals. Code which calls Evaluate() isn't ready for that yet.
|
||||
return !Result.Val.isLValue() || IsGlobalLValue(Result.Val.getLValueBase());
|
||||
}
|
||||
|
||||
bool Expr::EvaluateAsBooleanCondition(bool &Result,
|
||||
const ASTContext &Ctx) const {
|
||||
EvalStatus Scratch;
|
||||
EvalInfo Info(Ctx, Scratch);
|
||||
|
||||
return HandleConversionToBool(this, Result, Info);
|
||||
EvalResult Scratch;
|
||||
return Evaluate(Scratch, Ctx) && HandleConversionToBool(Scratch.Val, Result);
|
||||
}
|
||||
|
||||
bool Expr::EvaluateAsInt(APSInt &Result, const ASTContext &Ctx) const {
|
||||
EvalStatus Scratch;
|
||||
EvalInfo Info(Ctx, Scratch);
|
||||
|
||||
return EvaluateInteger(this, Result, Info) && !Scratch.HasSideEffects;
|
||||
EvalResult ExprResult;
|
||||
if (!Evaluate(ExprResult, Ctx) || ExprResult.HasSideEffects ||
|
||||
!ExprResult.Val.isInt()) {
|
||||
return false;
|
||||
}
|
||||
Result = ExprResult.Val.getInt();
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Expr::EvaluateAsLValue(EvalResult &Result, const ASTContext &Ctx) const {
|
||||
EvalInfo Info(Ctx, Result);
|
||||
|
||||
LValue LV;
|
||||
if (EvaluateLValue(this, LV, Info) &&
|
||||
!Result.HasSideEffects &&
|
||||
if (EvaluateLValue(this, LV, Info) && !Result.HasSideEffects &&
|
||||
IsGlobalLValue(LV.Base)) {
|
||||
LV.moveInto(Result.Val);
|
||||
return true;
|
||||
|
@ -3193,11 +3331,7 @@ bool Expr::isIntegerConstantExpr(llvm::APSInt &Result, ASTContext &Ctx,
|
|||
if (Loc) *Loc = d.Loc;
|
||||
return false;
|
||||
}
|
||||
EvalResult EvalResult;
|
||||
if (!Evaluate(EvalResult, Ctx))
|
||||
if (!EvaluateAsInt(Result, Ctx))
|
||||
llvm_unreachable("ICE cannot be evaluated!");
|
||||
assert(!EvalResult.HasSideEffects && "ICE with side effects!");
|
||||
assert(EvalResult.Val.isInt() && "ICE that isn't integer!");
|
||||
Result = EvalResult.Val.getInt();
|
||||
return true;
|
||||
}
|
||||
|
|
|
@ -64,3 +64,17 @@ namespace test3 {
|
|||
// CHECK-NEXT: br label
|
||||
// CHECK: ret void
|
||||
}
|
||||
|
||||
// Test that we can fold member lookup expressions which resolve to static data
|
||||
// members.
|
||||
namespace test4 {
|
||||
struct A {
|
||||
static const int n = 76;
|
||||
};
|
||||
|
||||
int f(A *a) {
|
||||
// CHECK: define i32 @_ZN5test41fEPNS_1AE
|
||||
// CHECK: ret i32 76
|
||||
return a->n;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -26,3 +26,17 @@ namespace CaseStatements {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
extern int &Recurse1;
|
||||
int &Recurse2 = Recurse1, &Recurse1 = Recurse2;
|
||||
constexpr int &Recurse3 = Recurse2; // expected-error {{must be initialized by a constant expression}}
|
||||
|
||||
namespace MemberEnum {
|
||||
struct WithMemberEnum {
|
||||
enum E { A = 42 };
|
||||
} wme;
|
||||
// FIXME: b's initializer is not treated as a constant expression yet, but we
|
||||
// can at least fold it.
|
||||
constexpr bool b = wme.A == 42;
|
||||
int n[b];
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue