01.06.2015, 17:31, "Jonathan Roelofs" <[email protected]>:
> +cfe-commits
>
> On 6/1/15 7:56 AM, Косов Евгений wrote:
>> Hello.
>>
>> https://llvm.org/bugs/show_bug.cgi?id=21945
>> Fix is simple - do not enter in a loop body iterating an empty container.
>
> Going forward, Clang patches should be sent to [email protected].
Thanks for the info.
> Also, please upload patches with more context (i.e. `git diff -U999`).
Why so much? Because you don't apply patch but just read through it?
>> If the patch is ok how can it be committed into trunk?
>
> While you're here, mind putting an llvm_unreachable after the loop body?
Sure, why not?
>> Do I need to sign something?
>
> No. You can get commit access by following:
> http://llvm.org/docs/DeveloperPolicy.html#id16, or one of us can commit
> it for you after you've got a "LGTM" on it.
For now I don't need commit rights. So if my patch is ok please commit it.
>
> Cheers,
>
> Jon
>> Eugene
>>
>> _______________________________________________
>> llvm-commits mailing list
>> [email protected]
>> http://lists.cs.uiuc.edu/mailman/listinfo/llvm-commits
>
> --
> Jon Roelofs
> [email protected]
> CodeSourcery / Mentor Embedded
Eugene
Index: lib/AST/ExprConstant.cpp
===================================================================
--- lib/AST/ExprConstant.cpp (revision 238723)
+++ lib/AST/ExprConstant.cpp (working copy)
@@ -3280,2023 +3280,2028 @@
static EvalStmtResult EvaluateSwitch(APValue &Result, EvalInfo &Info,
const SwitchStmt *SS) {
BlockScopeRAII Scope(Info);
// Evaluate the switch condition.
APSInt Value;
{
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.
const SwitchCase *Found = nullptr;
for (const SwitchCase *SC = SS->getSwitchCaseList(); SC;
SC = SC->getNextSwitchCase()) {
if (isa<DefaultStmt>(SC)) {
Found = SC;
continue;
}
const CaseStmt *CS = cast<CaseStmt>(SC);
APSInt LHS = CS->getLHS()->EvaluateKnownConstInt(Info.Ctx);
APSInt RHS = CS->getRHS() ? CS->getRHS()->EvaluateKnownConstInt(Info.Ctx)
: LHS;
if (LHS <= Value && Value <= RHS) {
Found = SC;
break;
}
}
if (!Found)
return ESR_Succeeded;
// Search the switch body for the switch case and evaluate it from there.
switch (EvalStmtResult ESR = EvaluateStmt(Result, Info, SS->getBody(), Found)) {
case ESR_Break:
return ESR_Succeeded;
case ESR_Succeeded:
case ESR_Continue:
case ESR_Failed:
case ESR_Returned:
return ESR;
case ESR_CaseNotFound:
// This can only happen if the switch case is nested within a statement
// expression. We have no intention of supporting that.
Info.Diag(Found->getLocStart(), diag::note_constexpr_stmt_expr_unsupported);
return ESR_Failed;
}
llvm_unreachable("Invalid EvalStmtResult!");
}
// Evaluate a statement.
static EvalStmtResult EvaluateStmt(APValue &Result, EvalInfo &Info,
const Stmt *S, const SwitchCase *Case) {
if (!Info.nextStep(S))
return ESR_Failed;
// If we're hunting down a 'case' or 'default' label, recurse through
// substatements until we hit the label.
if (Case) {
// FIXME: We don't start the lifetime of objects whose initialization we
// jump over. However, such objects must be of class type with a trivial
// default constructor that initialize all subobjects, so must be empty,
// so this almost never matters.
switch (S->getStmtClass()) {
case Stmt::CompoundStmtClass:
// FIXME: Precompute which substatement of a compound statement we
// would jump to, and go straight there rather than performing a
// linear scan each time.
case Stmt::LabelStmtClass:
case Stmt::AttributedStmtClass:
case Stmt::DoStmtClass:
break;
case Stmt::CaseStmtClass:
case Stmt::DefaultStmtClass:
if (Case == S)
Case = nullptr;
break;
case Stmt::IfStmtClass: {
// 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;
return EvaluateStmt(Result, Info, IS->getElse(), Case);
}
case Stmt::WhileStmtClass: {
EvalStmtResult ESR =
EvaluateLoopBody(Result, Info, cast<WhileStmt>(S)->getBody(), Case);
if (ESR != ESR_Continue)
return ESR;
break;
}
case Stmt::ForStmtClass: {
const ForStmt *FS = cast<ForStmt>(S);
EvalStmtResult ESR =
EvaluateLoopBody(Result, Info, FS->getBody(), Case);
if (ESR != ESR_Continue)
return ESR;
if (FS->getInc()) {
FullExpressionRAII IncScope(Info);
if (!EvaluateIgnoredValue(Info, FS->getInc()))
return ESR_Failed;
}
break;
}
case Stmt::DeclStmtClass:
// FIXME: If the variable has initialization that can't be jumped over,
// bail out of any immediately-surrounding compound-statement too.
default:
return ESR_CaseNotFound;
}
}
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;
}
Info.Diag(S->getLocStart());
return ESR_Failed;
case Stmt::NullStmtClass:
return ESR_Succeeded;
case Stmt::DeclStmtClass: {
const DeclStmt *DS = cast<DeclStmt>(S);
for (const auto *DclIt : DS->decls()) {
// 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 (const auto *BI : CS->body()) {
EvalStmtResult ESR = EvaluateStmt(Result, Info, BI, Case);
if (ESR == ESR_Succeeded)
Case = nullptr;
else if (ESR != ESR_CaseNotFound)
return ESR;
}
return Case ? ESR_CaseNotFound : ESR_Succeeded;
}
case Stmt::IfStmtClass: {
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;
if (const Stmt *SubStmt = Cond ? IS->getThen() : IS->getElse()) {
EvalStmtResult ESR = EvaluateStmt(Result, Info, SubStmt);
if (ESR != ESR_Succeeded)
return ESR;
}
return ESR_Succeeded;
}
case Stmt::WhileStmtClass: {
const WhileStmt *WS = cast<WhileStmt>(S);
while (true) {
BlockScopeRAII Scope(Info);
bool Continue;
if (!EvaluateCond(Info, WS->getConditionVariable(), WS->getCond(),
Continue))
return ESR_Failed;
if (!Continue)
break;
EvalStmtResult ESR = EvaluateLoopBody(Result, Info, WS->getBody());
if (ESR != ESR_Continue)
return ESR;
}
return ESR_Succeeded;
}
case Stmt::DoStmtClass: {
const DoStmt *DS = cast<DoStmt>(S);
bool Continue;
do {
EvalStmtResult ESR = EvaluateLoopBody(Result, Info, DS->getBody(), Case);
if (ESR != ESR_Continue)
return ESR;
Case = nullptr;
FullExpressionRAII CondScope(Info);
if (!EvaluateAsBooleanCondition(DS->getCond(), Continue, Info))
return ESR_Failed;
} while (Continue);
return ESR_Succeeded;
}
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))
return ESR_Failed;
if (!Continue)
break;
EvalStmtResult ESR = EvaluateLoopBody(Result, Info, FS->getBody());
if (ESR != ESR_Continue)
return ESR;
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());
if (ESR != ESR_Succeeded)
return ESR;
// Create the __begin and __end iterators.
ESR = EvaluateStmt(Result, Info, FS->getBeginEndStmt());
if (ESR != ESR_Succeeded)
return ESR;
while (true) {
// Condition: __begin != __end.
{
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;
// Loop body.
ESR = EvaluateLoopBody(Result, Info, FS->getBody());
if (ESR != ESR_Continue)
return ESR;
// Increment: ++__begin
if (!EvaluateIgnoredValue(Info, FS->getInc()))
return ESR_Failed;
}
return ESR_Succeeded;
}
case Stmt::SwitchStmtClass:
return EvaluateSwitch(Result, Info, cast<SwitchStmt>(S));
case Stmt::ContinueStmtClass:
return ESR_Continue;
case Stmt::BreakStmtClass:
return ESR_Break;
case Stmt::LabelStmtClass:
return EvaluateStmt(Result, Info, cast<LabelStmt>(S)->getSubStmt(), Case);
case Stmt::AttributedStmtClass:
// As a general principle, C++11 attributes can be ignored without
// any semantic impact.
return EvaluateStmt(Result, Info, cast<AttributedStmt>(S)->getSubStmt(),
Case);
case Stmt::CaseStmtClass:
case Stmt::DefaultStmtClass:
return EvaluateStmt(Result, Info, cast<SwitchCase>(S)->getSubStmt(), Case);
}
}
/// CheckTrivialDefaultConstructor - Check whether a constructor is a trivial
/// default constructor. If so, we'll fold it whether or not it's marked as
/// constexpr. If it is marked as constexpr, we will never implicitly define it,
/// so we need special handling.
static bool CheckTrivialDefaultConstructor(EvalInfo &Info, SourceLocation Loc,
const CXXConstructorDecl *CD,
bool IsValueInitialization) {
if (!CD->isTrivial() || !CD->isDefaultConstructor())
return false;
// Value-initialization does not call a trivial default constructor, so such a
// call is a core constant expression whether or not the constructor is
// constexpr.
if (!CD->isConstexpr() && !IsValueInitialization) {
if (Info.getLangOpts().CPlusPlus11) {
// FIXME: If DiagDecl is an implicitly-declared special member function,
// we should be much more explicit about why it's not constexpr.
Info.CCEDiag(Loc, diag::note_constexpr_invalid_function, 1)
<< /*IsConstexpr*/0 << /*IsConstructor*/1 << CD;
Info.Note(CD->getLocation(), diag::note_declared_at);
} else {
Info.CCEDiag(Loc, diag::note_invalid_subexpr_in_const_expr);
}
}
return true;
}
/// CheckConstexprFunction - Check that a function can be called in a constant
/// expression.
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;
// Bail out with no diagnostic if the function declaration itself is invalid.
// We will have produced a relevant diagnostic while parsing it.
if (Declaration->isInvalidDecl())
return false;
// Can we evaluate this function call?
if (Definition && Definition->isConstexpr() && !Definition->isInvalidDecl())
return true;
if (Info.getLangOpts().CPlusPlus11) {
const FunctionDecl *DiagDecl = Definition ? Definition : Declaration;
// FIXME: If DiagDecl is an implicitly-declared special member function, we
// should be much more explicit about why it's not constexpr.
Info.Diag(CallLoc, diag::note_constexpr_invalid_function, 1)
<< DiagDecl->isConstexpr() << isa<CXXConstructorDecl>(DiagDecl)
<< DiagDecl;
Info.Note(DiagDecl->getLocation(), diag::note_declared_at);
} else {
Info.Diag(CallLoc, diag::note_invalid_subexpr_in_const_expr);
}
return false;
}
/// Determine if a class has any fields that might need to be copied by a
/// trivial copy or move operation.
static bool hasFields(const CXXRecordDecl *RD) {
if (!RD || RD->isEmpty())
return false;
for (auto *FD : RD->fields()) {
if (FD->isUnnamedBitfield())
continue;
return true;
}
for (auto &Base : RD->bases())
if (hasFields(Base.getType()->getAsCXXRecordDecl()))
return true;
return false;
}
namespace {
typedef SmallVector<APValue, 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)) {
// 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(SourceLocation CallLoc,
const FunctionDecl *Callee, const LValue *This,
ArrayRef<const Expr*> Args, const Stmt *Body,
EvalInfo &Info, APValue &Result) {
ArgVector ArgValues(Args.size());
if (!EvaluateArgs(Args, ArgValues, Info))
return false;
if (!Info.CheckCallLimit(CallLoc))
return false;
CallStackFrame Frame(Info, CallLoc, Callee, This, ArgValues.data());
// For a trivial copy or move assignment, perform an APValue copy. This is
// essential for unions, where the operations performed by the assignment
// operator cannot be represented as statements.
//
// Skip this for non-union classes with no fields; in that case, the defaulted
// copy/move does not actually read the object.
const CXXMethodDecl *MD = dyn_cast<CXXMethodDecl>(Callee);
if (MD && MD->isDefaulted() &&
(MD->getParent()->isUnion() ||
(MD->isTrivial() && hasFields(MD->getParent())))) {
assert(This &&
(MD->isCopyAssignmentOperator() || MD->isMoveAssignmentOperator()));
LValue RHS;
RHS.setFrom(Info.Ctx, ArgValues[0]);
APValue RHSValue;
if (!handleLValueToRValueConversion(Info, Args[0], Args[0]->getType(),
RHS, RHSValue))
return false;
if (!handleAssignment(Info, Args[0], *This, MD->getThisType(Info.Ctx),
RHSValue))
return false;
This->moveInto(Result);
return true;
}
EvalStmtResult ESR = EvaluateStmt(Result, Info, Body);
if (ESR == ESR_Succeeded) {
if (Callee->getReturnType()->isVoidType())
return true;
Info.Diag(Callee->getLocEnd(), diag::note_constexpr_no_return);
}
return ESR == ESR_Returned;
}
/// Evaluate a constructor call.
static bool HandleConstructorCall(SourceLocation CallLoc, const LValue &This,
ArrayRef<const Expr*> Args,
const CXXConstructorDecl *Definition,
EvalInfo &Info, APValue &Result) {
ArgVector ArgValues(Args.size());
if (!EvaluateArgs(Args, ArgValues, Info))
return false;
if (!Info.CheckCallLimit(CallLoc))
return false;
const CXXRecordDecl *RD = Definition->getParent();
if (RD->getNumVBases()) {
Info.Diag(CallLoc, diag::note_constexpr_virtual_base) << RD;
return false;
}
CallStackFrame Frame(Info, CallLoc, Definition, &This, ArgValues.data());
// If it's a delegating constructor, just delegate.
if (Definition->isDelegatingConstructor()) {
CXXConstructorDecl::init_const_iterator I = Definition->init_begin();
{
FullExpressionRAII InitScope(Info);
if (!EvaluateInPlace(Result, Info, This, (*I)->getInit()))
return false;
}
return EvaluateStmt(Result, Info, Definition->getBody()) != ESR_Failed;
}
// For a trivial copy or move constructor, perform an APValue copy. This is
// essential for unions (or classes with anonymous union members), where the
// operations performed by the constructor cannot be represented by
// ctor-initializers.
//
// Skip this for empty non-union classes; we should not perform an
// lvalue-to-rvalue conversion on them because their copy constructor does not
// actually read them.
if (Definition->isDefaulted() && Definition->isCopyOrMoveConstructor() &&
(Definition->getParent()->isUnion() ||
(Definition->isTrivial() && hasFields(Definition->getParent())))) {
LValue RHS;
RHS.setFrom(Info.Ctx, ArgValues[0]);
return handleLValueToRValueConversion(Info, Args[0], Args[0]->getType(),
RHS, Result);
}
// Reserve space for the struct members.
if (!RD->isUnion() && Result.isUninit())
Result = APValue(APValue::UninitStruct(), RD->getNumBases(),
std::distance(RD->field_begin(), RD->field_end()));
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
CXXRecordDecl::base_class_const_iterator BaseIt = RD->bases_begin();
#endif
for (const auto *I : Definition->inits()) {
LValue Subobject = This;
APValue *Value = &Result;
// Determine the subobject to initialize.
FieldDecl *FD = nullptr;
if (I->isBaseInitializer()) {
QualType BaseType(I->getBaseClass(), 0);
#ifndef NDEBUG
// Non-virtual base classes are initialized in the order in the class
// definition. We have already checked for virtual base classes.
assert(!BaseIt->isVirtual() && "virtual base for literal type");
assert(Info.Ctx.hasSameType(BaseIt->getType(), BaseType) &&
"base class initializers not in expected order");
++BaseIt;
#endif
if (!HandleLValueDirectBase(Info, I->getInit(), Subobject, RD,
BaseType->getAsCXXRecordDecl(), &Layout))
return false;
Value = &Result.getStructBase(BasesSeen++);
} else if ((FD = I->getMember())) {
if (!HandleLValueMember(Info, I->getInit(), Subobject, FD, &Layout))
return false;
if (RD->isUnion()) {
Result = APValue(FD);
Value = &Result.getUnionValue();
} else {
Value = &Result.getStructField(FD->getFieldIndex());
}
} else if (IndirectFieldDecl *IFD = I->getIndirectMember()) {
// Walk the indirect field decl's chain to find the object to initialize,
// and make sure we've initialized every step along it.
for (auto *C : IFD->chain()) {
FD = cast<FieldDecl>(C);
CXXRecordDecl *CD = cast<CXXRecordDecl>(FD->getParent());
// Switch the union field if it differs. This happens if we had
// preceding zero-initialization, and we're now initializing a union
// subobject other than the first.
// FIXME: In this case, the values of the other subobjects are
// specified, since zero-initialization sets all padding bits to zero.
if (Value->isUninit() ||
(Value->isUnion() && Value->getUnionField() != FD)) {
if (CD->isUnion())
*Value = APValue(FD);
else
*Value = APValue(APValue::UninitStruct(), CD->getNumBases(),
std::distance(CD->field_begin(), CD->field_end()));
}
if (!HandleLValueMember(Info, I->getInit(), Subobject, FD))
return false;
if (CD->isUnion())
Value = &Value->getUnionValue();
else
Value = &Value->getStructField(FD->getFieldIndex());
}
} else {
llvm_unreachable("unknown base initializer kind");
}
FullExpressionRAII InitScope(Info);
if (!EvaluateInPlace(*Value, Info, Subobject, I->getInit()) ||
(FD && FD->isBitField() && !truncateBitfieldValue(Info, I->getInit(),
*Value, FD))) {
// 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 &&
EvaluateStmt(Result, Info, Definition->getBody()) != ESR_Failed;
}
//===----------------------------------------------------------------------===//
// Generic Evaluation
//===----------------------------------------------------------------------===//
namespace {
template <class Derived>
class ExprEvaluatorBase
: public ConstStmtVisitor<Derived, bool> {
private:
bool DerivedSuccess(const APValue &V, const Expr *E) {
return static_cast<Derived*>(this)->Success(V, E);
}
bool DerivedZeroInitialization(const Expr *E) {
return static_cast<Derived*>(this)->ZeroInitialization(E);
}
// Check whether a conditional operator with a non-constant condition is a
// potential constant expression. If neither arm is a potential constant
// expression, then the conditional operator is not either.
template<typename ConditionalOperator>
void CheckPotentialConstantConditional(const ConditionalOperator *E) {
assert(Info.checkingPotentialConstantExpression());
// Speculatively evaluate both arms.
{
SmallVector<PartialDiagnosticAt, 8> Diag;
SpeculativeEvaluationRAII Speculate(Info, &Diag);
StmtVisitorTy::Visit(E->getFalseExpr());
if (Diag.empty())
return;
Diag.clear();
StmtVisitorTy::Visit(E->getTrueExpr());
if (Diag.empty())
return;
}
Error(E, diag::note_constexpr_conditional_never_const);
}
template<typename ConditionalOperator>
bool HandleConditionalOperator(const ConditionalOperator *E) {
bool BoolResult;
if (!EvaluateAsBooleanCondition(E->getCond(), BoolResult, Info)) {
if (Info.checkingPotentialConstantExpression())
CheckPotentialConstantConditional(E);
return false;
}
Expr *EvalExpr = BoolResult ? E->getTrueExpr() : E->getFalseExpr();
return StmtVisitorTy::Visit(EvalExpr);
}
protected:
EvalInfo &Info;
typedef ConstStmtVisitor<Derived, bool> StmtVisitorTy;
typedef ExprEvaluatorBase ExprEvaluatorBaseTy;
OptionalDiagnostic CCEDiag(const Expr *E, diag::kind D) {
return Info.CCEDiag(E, D);
}
bool ZeroInitialization(const Expr *E) { return Error(E); }
public:
ExprEvaluatorBase(EvalInfo &Info) : Info(Info) {}
EvalInfo &getEvalInfo() { return Info; }
/// Report an evaluation error. This should only be called when an error is
/// first discovered. When propagating an error, just return false.
bool Error(const Expr *E, diag::kind D) {
Info.Diag(E, D);
return false;
}
bool Error(const Expr *E) {
return Error(E, diag::note_invalid_subexpr_in_const_expr);
}
bool VisitStmt(const Stmt *) {
llvm_unreachable("Expression evaluator should not be called on stmts");
}
bool VisitExpr(const Expr *E) {
return Error(E);
}
bool VisitParenExpr(const ParenExpr *E)
{ return StmtVisitorTy::Visit(E->getSubExpr()); }
bool VisitUnaryExtension(const UnaryOperator *E)
{ return StmtVisitorTy::Visit(E->getSubExpr()); }
bool VisitUnaryPlus(const UnaryOperator *E)
{ return StmtVisitorTy::Visit(E->getSubExpr()); }
bool VisitChooseExpr(const ChooseExpr *E)
{ return StmtVisitorTy::Visit(E->getChosenSubExpr()); }
bool VisitGenericSelectionExpr(const GenericSelectionExpr *E)
{ return StmtVisitorTy::Visit(E->getResultExpr()); }
bool VisitSubstNonTypeTemplateParmExpr(const SubstNonTypeTemplateParmExpr *E)
{ return StmtVisitorTy::Visit(E->getReplacement()); }
bool VisitCXXDefaultArgExpr(const CXXDefaultArgExpr *E)
{ return StmtVisitorTy::Visit(E->getExpr()); }
bool VisitCXXDefaultInitExpr(const CXXDefaultInitExpr *E) {
// The initializer may not have been parsed yet, or might be erroneous.
if (!E->getExpr())
return Error(E);
return StmtVisitorTy::Visit(E->getExpr());
}
// We cannot create any objects for which cleanups are required, so there is
// nothing to do here; all cleanups must come from unevaluated subexpressions.
bool VisitExprWithCleanups(const ExprWithCleanups *E)
{ return StmtVisitorTy::Visit(E->getSubExpr()); }
bool VisitCXXReinterpretCastExpr(const CXXReinterpretCastExpr *E) {
CCEDiag(E, diag::note_constexpr_invalid_cast) << 0;
return static_cast<Derived*>(this)->VisitCastExpr(E);
}
bool VisitCXXDynamicCastExpr(const CXXDynamicCastExpr *E) {
CCEDiag(E, diag::note_constexpr_invalid_cast) << 1;
return static_cast<Derived*>(this)->VisitCastExpr(E);
}
bool VisitBinaryOperator(const BinaryOperator *E) {
switch (E->getOpcode()) {
default:
return Error(E);
case BO_Comma:
VisitIgnoredValue(E->getLHS());
return StmtVisitorTy::Visit(E->getRHS());
case BO_PtrMemD:
case BO_PtrMemI: {
LValue Obj;
if (!HandleMemberPointerAccess(Info, E, Obj))
return false;
APValue Result;
if (!handleLValueToRValueConversion(Info, E, E->getType(), Obj, Result))
return false;
return DerivedSuccess(Result, E);
}
}
}
bool 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->createTemporary(E->getOpaqueValue(), false),
Info, E->getCommon()))
return false;
return HandleConditionalOperator(E);
}
bool VisitConditionalOperator(const ConditionalOperator *E) {
bool IsBcpCall = false;
// If the condition (ignoring parens) is a __builtin_constant_p call,
// the result is a constant expression if it can be folded without
// side-effects. This is an important GNU extension. See GCC PR38377
// for discussion.
if (const CallExpr *CallCE =
dyn_cast<CallExpr>(E->getCond()->IgnoreParenCasts()))
if (CallCE->getBuiltinCallee() == Builtin::BI__builtin_constant_p)
IsBcpCall = true;
// Always assume __builtin_constant_p(...) ? ... : ... is a potential
// constant expression; we can't check whether it's potentially foldable.
if (Info.checkingPotentialConstantExpression() && IsBcpCall)
return false;
FoldConstant Fold(Info, IsBcpCall);
if (!HandleConditionalOperator(E)) {
Fold.keepDiagnostics();
return false;
}
return true;
}
bool VisitOpaqueValueExpr(const OpaqueValueExpr *E) {
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 StmtVisitorTy::Visit(Source);
}
bool VisitCallExpr(const CallExpr *E) {
const Expr *Callee = E->getCallee()->IgnoreParens();
QualType CalleeType = Callee->getType();
const FunctionDecl *FD = nullptr;
LValue *This = nullptr, ThisVal;
auto Args = llvm::makeArrayRef(E->getArgs(), E->getNumArgs());
bool HasQualifier = false;
// Extract function decl and 'this' pointer from the callee.
if (CalleeType->isSpecificBuiltinType(BuiltinType::BoundMember)) {
const ValueDecl *Member = nullptr;
if (const MemberExpr *ME = dyn_cast<MemberExpr>(Callee)) {
// Explicit bound member calls, such as x.f() or p->g();
if (!EvaluateObjectArgument(Info, ME->getBase(), ThisVal))
return false;
Member = ME->getMemberDecl();
This = &ThisVal;
HasQualifier = ME->hasQualifier();
} else if (const BinaryOperator *BE = dyn_cast<BinaryOperator>(Callee)) {
// Indirect bound member calls ('.*' or '->*').
Member = HandleMemberPointerAccess(Info, BE, ThisVal, false);
if (!Member) return false;
This = &ThisVal;
} else
return Error(Callee);
FD = dyn_cast<FunctionDecl>(Member);
if (!FD)
return Error(Callee);
} else if (CalleeType->isFunctionPointerType()) {
LValue Call;
if (!EvaluatePointer(Callee, Call, Info))
return false;
if (!Call.getLValueOffset().isZero())
return Error(Callee);
FD = dyn_cast_or_null<FunctionDecl>(
Call.getLValueBase().dyn_cast<const ValueDecl*>());
if (!FD)
return Error(Callee);
// Overloaded operator calls to member functions are represented as normal
// calls with '*this' as the first argument.
const CXXMethodDecl *MD = dyn_cast<CXXMethodDecl>(FD);
if (MD && !MD->isStatic()) {
// FIXME: When selecting an implicit conversion for an overloaded
// operator delete, we sometimes try to evaluate calls to conversion
// operators without a 'this' parameter!
if (Args.empty())
return Error(E);
if (!EvaluateObjectArgument(Info, Args[0], ThisVal))
return false;
This = &ThisVal;
Args = Args.slice(1);
}
// Don't call function pointers which have been cast to some other type.
if (!Info.Ctx.hasSameType(CalleeType->getPointeeType(), FD->getType()))
return Error(E);
} else
return Error(E);
if (This && !This->checkSubobject(Info, E, CSK_This))
return false;
// DR1358 allows virtual constexpr functions in some cases. Don't allow
// calls to such functions in constant expressions.
if (This && !HasQualifier &&
isa<CXXMethodDecl>(FD) && cast<CXXMethodDecl>(FD)->isVirtual())
return Error(E, diag::note_constexpr_virtual_call);
const FunctionDecl *Definition = nullptr;
Stmt *Body = FD->getBody(Definition);
APValue Result;
if (!CheckConstexprFunction(Info, E->getExprLoc(), FD, Definition) ||
!HandleFunctionCall(E->getExprLoc(), Definition, This, Args, Body,
Info, Result))
return false;
return DerivedSuccess(Result, E);
}
bool VisitCompoundLiteralExpr(const CompoundLiteralExpr *E) {
return StmtVisitorTy::Visit(E->getInitializer());
}
bool VisitInitListExpr(const InitListExpr *E) {
if (E->getNumInits() == 0)
return DerivedZeroInitialization(E);
if (E->getNumInits() == 1)
return StmtVisitorTy::Visit(E->getInit(0));
return Error(E);
}
bool VisitImplicitValueInitExpr(const ImplicitValueInitExpr *E) {
return DerivedZeroInitialization(E);
}
bool VisitCXXScalarValueInitExpr(const CXXScalarValueInitExpr *E) {
return DerivedZeroInitialization(E);
}
bool VisitCXXNullPtrLiteralExpr(const CXXNullPtrLiteralExpr *E) {
return DerivedZeroInitialization(E);
}
/// A member expression where the object is a prvalue is itself a prvalue.
bool VisitMemberExpr(const MemberExpr *E) {
assert(!E->isArrow() && "missing call to bound member function?");
APValue Val;
if (!Evaluate(Val, Info, E->getBase()))
return false;
QualType BaseTy = E->getBase()->getType();
const FieldDecl *FD = dyn_cast<FieldDecl>(E->getMemberDecl());
if (!FD) return Error(E);
assert(!FD->getType()->isReferenceType() && "prvalue reference?");
assert(BaseTy->castAs<RecordType>()->getDecl()->getCanonicalDecl() ==
FD->getParent()->getCanonicalDecl() && "record / field mismatch");
CompleteObject Obj(&Val, BaseTy);
SubobjectDesignator Designator(BaseTy);
Designator.addDeclUnchecked(FD);
APValue Result;
return extractSubobject(Info, E, Obj, Designator, Result) &&
DerivedSuccess(Result, E);
}
bool VisitCastExpr(const CastExpr *E) {
switch (E->getCastKind()) {
default:
break;
case CK_AtomicToNonAtomic: {
APValue AtomicVal;
if (!EvaluateAtomic(E->getSubExpr(), AtomicVal, Info))
return false;
return DerivedSuccess(AtomicVal, E);
}
case CK_NoOp:
case CK_UserDefinedConversion:
return StmtVisitorTy::Visit(E->getSubExpr());
case CK_LValueToRValue: {
LValue LVal;
if (!EvaluateLValue(E->getSubExpr(), LVal, Info))
return false;
APValue RVal;
// Note, we use the subexpression's type in order to retain cv-qualifiers.
if (!handleLValueToRValueConversion(Info, E, E->getSubExpr()->getType(),
LVal, RVal))
return false;
return DerivedSuccess(RVal, E);
}
}
return Error(E);
}
bool VisitUnaryPostInc(const UnaryOperator *UO) {
return VisitUnaryPostIncDec(UO);
}
bool VisitUnaryPostDec(const UnaryOperator *UO) {
return VisitUnaryPostIncDec(UO);
}
bool VisitUnaryPostIncDec(const UnaryOperator *UO) {
if (!Info.getLangOpts().CPlusPlus14 && !Info.keepEvaluatingAfterFailure())
return Error(UO);
LValue LVal;
if (!EvaluateLValue(UO->getSubExpr(), LVal, Info))
return false;
APValue RVal;
if (!handleIncDec(this->Info, UO, LVal, UO->getSubExpr()->getType(),
UO->isIncrementOp(), &RVal))
return false;
return DerivedSuccess(RVal, UO);
}
bool VisitStmtExpr(const StmtExpr *E) {
// We will have checked the full-expressions inside the statement expression
// when they were completed, and don't need to check them again now.
if (Info.checkingForOverflow())
return Error(E);
BlockScopeRAII Scope(Info);
const CompoundStmt *CS = E->getSubStmt();
+ if (CS->body_empty())
+ return true;
+
for (CompoundStmt::const_body_iterator BI = CS->body_begin(),
BE = CS->body_end();
/**/; ++BI) {
if (BI + 1 == BE) {
const Expr *FinalExpr = dyn_cast<Expr>(*BI);
if (!FinalExpr) {
Info.Diag((*BI)->getLocStart(),
diag::note_constexpr_stmt_expr_unsupported);
return false;
}
return this->Visit(FinalExpr);
}
APValue ReturnValue;
EvalStmtResult ESR = EvaluateStmt(ReturnValue, Info, *BI);
if (ESR != ESR_Succeeded) {
// FIXME: If the statement-expression terminated due to 'return',
// 'break', or 'continue', it would be nice to propagate that to
// the outer statement evaluation rather than bailing out.
if (ESR != ESR_Failed)
Info.Diag((*BI)->getLocStart(),
diag::note_constexpr_stmt_expr_unsupported);
return false;
}
}
+
+ llvm_unreachable("Return from function from the loop above.");
}
/// Visit a value which is evaluated, but whose value is ignored.
void VisitIgnoredValue(const Expr *E) {
EvaluateIgnoredValue(Info, E);
}
};
}
//===----------------------------------------------------------------------===//
// Common base class for lvalue and temporary evaluation.
//===----------------------------------------------------------------------===//
namespace {
template<class Derived>
class LValueExprEvaluatorBase
: public ExprEvaluatorBase<Derived> {
protected:
LValue &Result;
typedef LValueExprEvaluatorBase LValueExprEvaluatorBaseTy;
typedef ExprEvaluatorBase<Derived> ExprEvaluatorBaseTy;
bool Success(APValue::LValueBase B) {
Result.set(B);
return true;
}
public:
LValueExprEvaluatorBase(EvalInfo &Info, LValue &Result) :
ExprEvaluatorBaseTy(Info), Result(Result) {}
bool Success(const APValue &V, const Expr *E) {
Result.setFrom(this->Info.Ctx, V);
return true;
}
bool VisitMemberExpr(const MemberExpr *E) {
// Handle non-static data members.
QualType BaseTy;
if (E->isArrow()) {
if (!EvaluatePointer(E->getBase(), Result, this->Info))
return false;
BaseTy = E->getBase()->getType()->castAs<PointerType>()->getPointeeType();
} else if (E->getBase()->isRValue()) {
assert(E->getBase()->getType()->isRecordType());
if (!EvaluateTemporary(E->getBase(), Result, this->Info))
return false;
BaseTy = E->getBase()->getType();
} else {
if (!this->Visit(E->getBase()))
return false;
BaseTy = E->getBase()->getType();
}
const ValueDecl *MD = E->getMemberDecl();
if (const FieldDecl *FD = dyn_cast<FieldDecl>(E->getMemberDecl())) {
assert(BaseTy->getAs<RecordType>()->getDecl()->getCanonicalDecl() ==
FD->getParent()->getCanonicalDecl() && "record / field mismatch");
(void)BaseTy;
if (!HandleLValueMember(this->Info, E, Result, FD))
return false;
} else if (const IndirectFieldDecl *IFD = dyn_cast<IndirectFieldDecl>(MD)) {
if (!HandleLValueIndirectMember(this->Info, E, Result, IFD))
return false;
} else
return this->Error(E);
if (MD->getType()->isReferenceType()) {
APValue RefValue;
if (!handleLValueToRValueConversion(this->Info, E, MD->getType(), Result,
RefValue))
return false;
return Success(RefValue, E);
}
return true;
}
bool VisitBinaryOperator(const BinaryOperator *E) {
switch (E->getOpcode()) {
default:
return ExprEvaluatorBaseTy::VisitBinaryOperator(E);
case BO_PtrMemD:
case BO_PtrMemI:
return HandleMemberPointerAccess(this->Info, E, Result);
}
}
bool VisitCastExpr(const CastExpr *E) {
switch (E->getCastKind()) {
default:
return ExprEvaluatorBaseTy::VisitCastExpr(E);
case CK_DerivedToBase:
case CK_UncheckedDerivedToBase:
if (!this->Visit(E->getSubExpr()))
return false;
// Now figure out the necessary offset to add to the base LV to get from
// the derived class to the base class.
return HandleLValueBasePath(this->Info, E, E->getSubExpr()->getType(),
Result);
}
}
};
}
//===----------------------------------------------------------------------===//
// 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:
// - Declarations
// * VarDecl
// * FunctionDecl
// - Literals
// * CompoundLiteralExpr in C
// * StringLiteral
// * CXXTypeidExpr
// * PredefinedExpr
// * ObjCStringLiteralExpr
// * ObjCEncodeExpr
// * AddrLabelExpr
// * BlockExpr
// * CallExpr for a MakeStringConstant builtin
// - Locals and temporaries
// * MaterializeTemporaryExpr
// * Any Expr, with a CallIndex indicating the function in which the temporary
// was evaluated, for cases where the MaterializeTemporaryExpr is missing
// from the AST (FIXME).
// * A MaterializeTemporaryExpr that has static storage duration, with no
// CallIndex, for a lifetime-extended temporary.
// plus an offset in bytes.
//===----------------------------------------------------------------------===//
namespace {
class LValueExprEvaluator
: public LValueExprEvaluatorBase<LValueExprEvaluator> {
public:
LValueExprEvaluator(EvalInfo &Info, LValue &Result) :
LValueExprEvaluatorBaseTy(Info, Result) {}
bool VisitVarDecl(const Expr *E, const VarDecl *VD);
bool VisitUnaryPreIncDec(const UnaryOperator *UO);
bool VisitDeclRefExpr(const DeclRefExpr *E);
bool VisitPredefinedExpr(const PredefinedExpr *E) { return Success(E); }
bool VisitMaterializeTemporaryExpr(const MaterializeTemporaryExpr *E);
bool VisitCompoundLiteralExpr(const CompoundLiteralExpr *E);
bool VisitMemberExpr(const MemberExpr *E);
bool VisitStringLiteral(const StringLiteral *E) { return Success(E); }
bool VisitObjCEncodeExpr(const ObjCEncodeExpr *E) { return Success(E); }
bool VisitCXXTypeidExpr(const CXXTypeidExpr *E);
bool VisitCXXUuidofExpr(const CXXUuidofExpr *E);
bool VisitArraySubscriptExpr(const ArraySubscriptExpr *E);
bool VisitUnaryDeref(const UnaryOperator *E);
bool VisitUnaryReal(const UnaryOperator *E);
bool VisitUnaryImag(const UnaryOperator *E);
bool VisitUnaryPreInc(const UnaryOperator *UO) {
return VisitUnaryPreIncDec(UO);
}
bool VisitUnaryPreDec(const UnaryOperator *UO) {
return VisitUnaryPreIncDec(UO);
}
bool VisitBinAssign(const BinaryOperator *BO);
bool VisitCompoundAssignOperator(const CompoundAssignOperator *CAO);
bool VisitCastExpr(const CastExpr *E) {
switch (E->getCastKind()) {
default:
return LValueExprEvaluatorBaseTy::VisitCastExpr(E);
case CK_LValueBitCast:
this->CCEDiag(E, diag::note_constexpr_invalid_cast) << 2;
if (!Visit(E->getSubExpr()))
return false;
Result.Designator.setInvalid();
return true;
case CK_BaseToDerived:
if (!Visit(E->getSubExpr()))
return false;
return HandleBaseToDerivedCast(Info, E, Result);
}
}
};
} // end anonymous namespace
/// Evaluate an expression as an lvalue. This can be legitimately called on
/// expressions which are not glvalues, in two cases:
/// * function designators in C, and
/// * "extern void" objects
static bool EvaluateLValue(const Expr *E, LValue &Result, EvalInfo &Info) {
assert(E->isGLValue() || E->getType()->isFunctionType() ||
E->getType()->isVoidType());
return LValueExprEvaluator(Info, Result).Visit(E);
}
bool LValueExprEvaluator::VisitDeclRefExpr(const DeclRefExpr *E) {
if (const FunctionDecl *FD = dyn_cast<FunctionDecl>(E->getDecl()))
return Success(FD);
if (const VarDecl *VD = dyn_cast<VarDecl>(E->getDecl()))
return VisitVarDecl(E, VD);
return Error(E);
}
bool LValueExprEvaluator::VisitVarDecl(const Expr *E, const VarDecl *VD) {
CallStackFrame *Frame = nullptr;
if (VD->hasLocalStorage() && Info.CurrentCall->Index > 1)
Frame = Info.CurrentCall;
if (!VD->getType()->isReferenceType()) {
if (Frame) {
Result.set(VD, Frame->Index);
return true;
}
return Success(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);
}
bool LValueExprEvaluator::VisitMaterializeTemporaryExpr(
const MaterializeTemporaryExpr *E) {
// Walk through the expression to find the materialized temporary itself.
SmallVector<const Expr *, 2> CommaLHSs;
SmallVector<SubobjectAdjustment, 2> Adjustments;
const Expr *Inner = E->GetTemporaryExpr()->
skipRValueSubobjectAdjustments(CommaLHSs, Adjustments);
// If we passed any comma operators, evaluate their LHSs.
for (unsigned I = 0, N = CommaLHSs.size(); I != N; ++I)
if (!EvaluateIgnoredValue(Info, CommaLHSs[I]))
return false;
// A materialized temporary with static storage duration can appear within the
// result of a constant expression evaluation, so we need to preserve its
// value for use outside this evaluation.
APValue *Value;
if (E->getStorageDuration() == SD_Static) {
Value = Info.Ctx.getMaterializedTemporaryValue(E, true);
*Value = APValue();
Result.set(E);
} else {
Value = &Info.CurrentCall->
createTemporary(E, E->getStorageDuration() == SD_Automatic);
Result.set(E, Info.CurrentCall->Index);
}
QualType Type = Inner->getType();
// Materialize the temporary itself.
if (!EvaluateInPlace(*Value, Info, Result, Inner) ||
(E->getStorageDuration() == SD_Static &&
!CheckConstantExpression(Info, E->getExprLoc(), Type, *Value))) {
*Value = APValue();
return false;
}
// Adjust our lvalue to refer to the desired subobject.
for (unsigned I = Adjustments.size(); I != 0; /**/) {
--I;
switch (Adjustments[I].Kind) {
case SubobjectAdjustment::DerivedToBaseAdjustment:
if (!HandleLValueBasePath(Info, Adjustments[I].DerivedToBase.BasePath,
Type, Result))
return false;
Type = Adjustments[I].DerivedToBase.BasePath->getType();
break;
case SubobjectAdjustment::FieldAdjustment:
if (!HandleLValueMember(Info, E, Result, Adjustments[I].Field))
return false;
Type = Adjustments[I].Field->getType();
break;
case SubobjectAdjustment::MemberPointerAdjustment:
if (!HandleMemberPointerAccess(this->Info, Type, Result,
Adjustments[I].Ptr.RHS))
return false;
Type = Adjustments[I].Ptr.MPT->getPointeeType();
break;
}
}
return true;
}
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::VisitCXXTypeidExpr(const CXXTypeidExpr *E) {
if (!E->isPotentiallyEvaluated())
return Success(E);
Info.Diag(E, diag::note_constexpr_typeid_polymorphic)
<< E->getExprOperand()->getType()
<< E->getExprOperand()->getSourceRange();
return false;
}
bool LValueExprEvaluator::VisitCXXUuidofExpr(const CXXUuidofExpr *E) {
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);
}
// Handle static member functions.
if (const CXXMethodDecl *MD = dyn_cast<CXXMethodDecl>(E->getMemberDecl())) {
if (MD->isStatic()) {
VisitIgnoredValue(E->getBase());
return Success(MD);
}
}
// Handle non-static data members.
return LValueExprEvaluatorBaseTy::VisitMemberExpr(E);
}
bool LValueExprEvaluator::VisitArraySubscriptExpr(const ArraySubscriptExpr *E) {
// FIXME: Deal with vectors as array subscript bases.
if (E->getBase()->getType()->isVectorType())
return Error(E);
if (!EvaluatePointer(E->getBase(), Result, Info))
return false;
APSInt Index;
if (!EvaluateInteger(E->getIdx(), Index, Info))
return false;
return HandleLValueArrayAdjustment(Info, E, Result, E->getType(),
getExtValue(Index));
}
bool LValueExprEvaluator::VisitUnaryDeref(const UnaryOperator *E) {
return EvaluatePointer(E->getSubExpr(), Result, Info);
}
bool LValueExprEvaluator::VisitUnaryReal(const UnaryOperator *E) {
if (!Visit(E->getSubExpr()))
return false;
// __real is a no-op on scalar lvalues.
if (E->getSubExpr()->getType()->isAnyComplexType())
HandleLValueComplexElement(Info, E, Result, E->getType(), false);
return true;
}
bool LValueExprEvaluator::VisitUnaryImag(const UnaryOperator *E) {
assert(E->getSubExpr()->getType()->isAnyComplexType() &&
"lvalue __imag__ on scalar?");
if (!Visit(E->getSubExpr()))
return false;
HandleLValueComplexElement(Info, E, Result, E->getType(), true);
return true;
}
bool LValueExprEvaluator::VisitUnaryPreIncDec(const UnaryOperator *UO) {
if (!Info.getLangOpts().CPlusPlus14 && !Info.keepEvaluatingAfterFailure())
return Error(UO);
if (!this->Visit(UO->getSubExpr()))
return false;
return handleIncDec(
this->Info, UO, Result, UO->getSubExpr()->getType(),
UO->isIncrementOp(), nullptr);
}
bool LValueExprEvaluator::VisitCompoundAssignOperator(
const CompoundAssignOperator *CAO) {
if (!Info.getLangOpts().CPlusPlus14 && !Info.keepEvaluatingAfterFailure())
return Error(CAO);
APValue RHS;
// The overall lvalue result is the result of evaluating the LHS.
if (!this->Visit(CAO->getLHS())) {
if (Info.keepEvaluatingAfterFailure())
Evaluate(RHS, this->Info, CAO->getRHS());
return false;
}
if (!Evaluate(RHS, this->Info, CAO->getRHS()))
return false;
return handleCompoundAssignment(
this->Info, CAO,
Result, CAO->getLHS()->getType(), CAO->getComputationLHSType(),
CAO->getOpForCompoundAssignment(CAO->getOpcode()), RHS);
}
bool LValueExprEvaluator::VisitBinAssign(const BinaryOperator *E) {
if (!Info.getLangOpts().CPlusPlus14 && !Info.keepEvaluatingAfterFailure())
return Error(E);
APValue NewVal;
if (!this->Visit(E->getLHS())) {
if (Info.keepEvaluatingAfterFailure())
Evaluate(NewVal, this->Info, E->getRHS());
return false;
}
if (!Evaluate(NewVal, this->Info, E->getRHS()))
return false;
return handleAssignment(this->Info, E, Result, E->getLHS()->getType(),
NewVal);
}
//===----------------------------------------------------------------------===//
// Pointer Evaluation
//===----------------------------------------------------------------------===//
namespace {
class PointerExprEvaluator
: public ExprEvaluatorBase<PointerExprEvaluator> {
LValue &Result;
bool Success(const Expr *E) {
Result.set(E);
return true;
}
public:
PointerExprEvaluator(EvalInfo &info, LValue &Result)
: ExprEvaluatorBaseTy(info), Result(Result) {}
bool Success(const APValue &V, const Expr *E) {
Result.setFrom(Info.Ctx, V);
return true;
}
bool ZeroInitialization(const Expr *E) {
return Success((Expr*)nullptr);
}
bool VisitBinaryOperator(const BinaryOperator *E);
bool VisitCastExpr(const CastExpr* E);
bool VisitUnaryAddrOf(const UnaryOperator *E);
bool VisitObjCStringLiteral(const ObjCStringLiteral *E)
{ return Success(E); }
bool VisitObjCBoxedExpr(const ObjCBoxedExpr *E)
{ return Success(E); }
bool VisitAddrLabelExpr(const AddrLabelExpr *E)
{ return Success(E); }
bool VisitCallExpr(const CallExpr *E);
bool VisitBlockExpr(const BlockExpr *E) {
if (!E->getBlockDecl()->hasCaptures())
return Success(E);
return Error(E);
}
bool VisitCXXThisExpr(const CXXThisExpr *E) {
// Can't look at 'this' when checking a potential constant expression.
if (Info.checkingPotentialConstantExpression())
return false;
if (!Info.CurrentCall->This) {
if (Info.getLangOpts().CPlusPlus11)
Info.Diag(E, diag::note_constexpr_this) << E->isImplicit();
else
Info.Diag(E);
return false;
}
Result = *Info.CurrentCall->This;
return true;
}
// FIXME: Missing: @protocol, @selector
};
} // end anonymous namespace
static bool EvaluatePointer(const Expr* E, LValue& Result, EvalInfo &Info) {
assert(E->isRValue() && E->getType()->hasPointerRepresentation());
return PointerExprEvaluator(Info, Result).Visit(E);
}
bool PointerExprEvaluator::VisitBinaryOperator(const BinaryOperator *E) {
if (E->getOpcode() != BO_Add &&
E->getOpcode() != BO_Sub)
return ExprEvaluatorBaseTy::VisitBinaryOperator(E);
const Expr *PExp = E->getLHS();
const Expr *IExp = E->getRHS();
if (IExp->getType()->isPointerType())
std::swap(PExp, IExp);
bool EvalPtrOK = EvaluatePointer(PExp, Result, Info);
if (!EvalPtrOK && !Info.keepEvaluatingAfterFailure())
return false;
llvm::APSInt Offset;
if (!EvaluateInteger(IExp, Offset, Info) || !EvalPtrOK)
return false;
int64_t AdditionalOffset = getExtValue(Offset);
if (E->getOpcode() == BO_Sub)
AdditionalOffset = -AdditionalOffset;
QualType Pointee = PExp->getType()->castAs<PointerType>()->getPointeeType();
return HandleLValueArrayAdjustment(Info, E, Result, Pointee,
AdditionalOffset);
}
bool PointerExprEvaluator::VisitUnaryAddrOf(const UnaryOperator *E) {
return EvaluateLValue(E->getSubExpr(), Result, Info);
}
bool PointerExprEvaluator::VisitCastExpr(const CastExpr* E) {
const Expr* SubExpr = E->getSubExpr();
switch (E->getCastKind()) {
default:
break;
case CK_BitCast:
case CK_CPointerToObjCPointerCast:
case CK_BlockPointerToObjCPointerCast:
case CK_AnyPointerToBlockPointerCast:
case CK_AddressSpaceConversion:
if (!Visit(SubExpr))
return false;
// Bitcasts to cv void* are static_casts, not reinterpret_casts, so are
// permitted in constant expressions in C++11. Bitcasts from cv void* are
// also static_casts, but we disallow them as a resolution to DR1312.
if (!E->getType()->isVoidPointerType()) {
Result.Designator.setInvalid();
if (SubExpr->getType()->isVoidPointerType())
CCEDiag(E, diag::note_constexpr_invalid_cast)
<< 3 << SubExpr->getType();
else
CCEDiag(E, diag::note_constexpr_invalid_cast) << 2;
}
return true;
case CK_DerivedToBase:
case CK_UncheckedDerivedToBase:
if (!EvaluatePointer(E->getSubExpr(), Result, Info))
return false;
if (!Result.Base && Result.Offset.isZero())
return true;
// Now figure out the necessary offset to add to the base LV to get from
// the derived class to the base class.
return HandleLValueBasePath(Info, E, E->getSubExpr()->getType()->
castAs<PointerType>()->getPointeeType(),
Result);
case CK_BaseToDerived:
if (!Visit(E->getSubExpr()))
return false;
if (!Result.Base && Result.Offset.isZero())
return true;
return HandleBaseToDerivedCast(Info, E, Result);
case CK_NullToPointer:
VisitIgnoredValue(E->getSubExpr());
return ZeroInitialization(E);
case CK_IntegralToPointer: {
CCEDiag(E, diag::note_constexpr_invalid_cast) << 2;
APValue Value;
if (!EvaluateIntegerOrLValue(SubExpr, Value, Info))
break;
if (Value.isInt()) {
unsigned Size = Info.Ctx.getTypeSize(E->getType());
uint64_t N = Value.getInt().extOrTrunc(Size).getZExtValue();
Result.Base = (Expr*)nullptr;
Result.Offset = CharUnits::fromQuantity(N);
Result.CallIndex = 0;
Result.Designator.setInvalid();
return true;
} else {
// Cast is of an lvalue, no need to change value.
Result.setFrom(Info.Ctx, Value);
return true;
}
}
case CK_ArrayToPointerDecay:
if (SubExpr->isGLValue()) {
if (!EvaluateLValue(SubExpr, Result, Info))
return false;
} else {
Result.set(SubExpr, Info.CurrentCall->Index);
if (!EvaluateInPlace(Info.CurrentCall->createTemporary(SubExpr, false),
Info, Result, SubExpr))
return false;
}
// The result is a pointer to the first element of the array.
if (const ConstantArrayType *CAT
= Info.Ctx.getAsConstantArrayType(SubExpr->getType()))
Result.addArray(Info, E, CAT);
else
Result.Designator.setInvalid();
return true;
case CK_FunctionToPointerDecay:
return EvaluateLValue(SubExpr, Result, Info);
}
return ExprEvaluatorBaseTy::VisitCastExpr(E);
}
static CharUnits GetAlignOfType(EvalInfo &Info, QualType T) {
// C++ [expr.alignof]p3:
// When alignof is applied to a reference type, the result is the
// alignment of the referenced type.
if (const ReferenceType *Ref = T->getAs<ReferenceType>())
T = Ref->getPointeeType();
// __alignof is defined to return the preferred alignment.
return Info.Ctx.toCharUnitsFromBits(
Info.Ctx.getPreferredTypeAlign(T.getTypePtr()));
}
static CharUnits GetAlignOfExpr(EvalInfo &Info, const Expr *E) {
E = E->IgnoreParens();
// The kinds of expressions that we have special-case logic here for
// should be kept up to date with the special checks for those
// expressions in Sema.
// alignof decl is always accepted, even if it doesn't make sense: we default
// to 1 in those cases.
if (const DeclRefExpr *DRE = dyn_cast<DeclRefExpr>(E))
return Info.Ctx.getDeclAlign(DRE->getDecl(),
/*RefAsPointee*/true);
if (const MemberExpr *ME = dyn_cast<MemberExpr>(E))
return Info.Ctx.getDeclAlign(ME->getMemberDecl(),
/*RefAsPointee*/true);
return GetAlignOfType(Info, E->getType());
}
bool PointerExprEvaluator::VisitCallExpr(const CallExpr *E) {
if (IsStringLiteralCall(E))
return Success(E);
switch (E->getBuiltinCallee()) {
case Builtin::BI__builtin_addressof:
return EvaluateLValue(E->getArg(0), Result, Info);
case Builtin::BI__builtin_assume_aligned: {
// We need to be very careful here because: if the pointer does not have the
// asserted alignment, then the behavior is undefined, and undefined
// behavior is non-constant.
if (!EvaluatePointer(E->getArg(0), Result, Info))
return false;
LValue OffsetResult(Result);
APSInt Alignment;
if (!EvaluateInteger(E->getArg(1), Alignment, Info))
return false;
CharUnits Align = CharUnits::fromQuantity(getExtValue(Alignment));
if (E->getNumArgs() > 2) {
APSInt Offset;
if (!EvaluateInteger(E->getArg(2), Offset, Info))
return false;
int64_t AdditionalOffset = -getExtValue(Offset);
OffsetResult.Offset += CharUnits::fromQuantity(AdditionalOffset);
}
// If there is a base object, then it must have the correct alignment.
if (OffsetResult.Base) {
CharUnits BaseAlignment;
if (const ValueDecl *VD =
OffsetResult.Base.dyn_cast<const ValueDecl*>()) {
BaseAlignment = Info.Ctx.getDeclAlign(VD);
} else {
BaseAlignment =
GetAlignOfExpr(Info, OffsetResult.Base.get<const Expr*>());
}
if (BaseAlignment < Align) {
Result.Designator.setInvalid();
// FIXME: Quantities here cast to integers because the plural modifier
// does not work on APSInts yet.
CCEDiag(E->getArg(0),
diag::note_constexpr_baa_insufficient_alignment) << 0
<< (int) BaseAlignment.getQuantity()
<< (unsigned) getExtValue(Alignment);
return false;
}
}
// The offset must also have the correct alignment.
if (OffsetResult.Offset.RoundUpToAlignment(Align) != OffsetResult.Offset) {
Result.Designator.setInvalid();
APSInt Offset(64, false);
Offset = OffsetResult.Offset.getQuantity();
if (OffsetResult.Base)
CCEDiag(E->getArg(0),
diag::note_constexpr_baa_insufficient_alignment) << 1
<< (int) getExtValue(Offset) << (unsigned) getExtValue(Alignment);
else
CCEDiag(E->getArg(0),
diag::note_constexpr_baa_value_insufficient_alignment)
<< Offset << (unsigned) getExtValue(Alignment);
return false;
}
return true;
}
default:
return ExprEvaluatorBaseTy::VisitCallExpr(E);
}
}
//===----------------------------------------------------------------------===//
// Member Pointer Evaluation
//===----------------------------------------------------------------------===//
namespace {
class MemberPointerExprEvaluator
: public ExprEvaluatorBase<MemberPointerExprEvaluator> {
MemberPtr &Result;
bool Success(const ValueDecl *D) {
Result = MemberPtr(D);
return true;
}
public:
MemberPointerExprEvaluator(EvalInfo &Info, MemberPtr &Result)
: ExprEvaluatorBaseTy(Info), Result(Result) {}
bool Success(const APValue &V, const Expr *E) {
Result.setFrom(V);
return true;
}
bool ZeroInitialization(const Expr *E) {
return Success((const ValueDecl*)nullptr);
}
bool VisitCastExpr(const CastExpr *E);
bool VisitUnaryAddrOf(const UnaryOperator *E);
};
} // end anonymous namespace
static bool EvaluateMemberPointer(const Expr *E, MemberPtr &Result,
EvalInfo &Info) {
assert(E->isRValue() && E->getType()->isMemberPointerType());
return MemberPointerExprEvaluator(Info, Result).Visit(E);
}
bool MemberPointerExprEvaluator::VisitCastExpr(const CastExpr *E) {
switch (E->getCastKind()) {
default:
return ExprEvaluatorBaseTy::VisitCastExpr(E);
case CK_NullToMemberPointer:
VisitIgnoredValue(E->getSubExpr());
return ZeroInitialization(E);
case CK_BaseToDerivedMemberPointer: {
if (!Visit(E->getSubExpr()))
return false;
if (E->path_empty())
return true;
// Base-to-derived member pointer casts store the path in derived-to-base
// order, so iterate backwards. The CXXBaseSpecifier also provides us with
// the wrong end of the derived->base arc, so stagger the path by one class.
typedef std::reverse_iterator<CastExpr::path_const_iterator> ReverseIter;
for (ReverseIter PathI(E->path_end() - 1), PathE(E->path_begin());
PathI != PathE; ++PathI) {
assert(!(*PathI)->isVirtual() && "memptr cast through vbase");
const CXXRecordDecl *Derived = (*PathI)->getType()->getAsCXXRecordDecl();
if (!Result.castToDerived(Derived))
return Error(E);
}
const Type *FinalTy = E->getType()->castAs<MemberPointerType>()->getClass();
if (!Result.castToDerived(FinalTy->getAsCXXRecordDecl()))
return Error(E);
return true;
}
case CK_DerivedToBaseMemberPointer:
if (!Visit(E->getSubExpr()))
return false;
for (CastExpr::path_const_iterator PathI = E->path_begin(),
PathE = E->path_end(); PathI != PathE; ++PathI) {
assert(!(*PathI)->isVirtual() && "memptr cast through vbase");
const CXXRecordDecl *Base = (*PathI)->getType()->getAsCXXRecordDecl();
if (!Result.castToBase(Base))
return Error(E);
}
return true;
}
}
bool MemberPointerExprEvaluator::VisitUnaryAddrOf(const UnaryOperator *E) {
// C++11 [expr.unary.op]p3 has very strict rules on how the address of a
// member can be formed.
return Success(cast<DeclRefExpr>(E->getSubExpr())->getDecl());
}
//===----------------------------------------------------------------------===//
// Record Evaluation
//===----------------------------------------------------------------------===//
namespace {
class RecordExprEvaluator
: public ExprEvaluatorBase<RecordExprEvaluator> {
const LValue &This;
APValue &Result;
public:
RecordExprEvaluator(EvalInfo &info, const LValue &This, APValue &Result)
: ExprEvaluatorBaseTy(info), This(This), Result(Result) {}
bool Success(const APValue &V, const Expr *E) {
Result = V;
return true;
}
bool ZeroInitialization(const Expr *E);
bool VisitCastExpr(const CastExpr *E);
bool VisitInitListExpr(const InitListExpr *E);
bool VisitCXXConstructExpr(const CXXConstructExpr *E);
bool VisitCXXStdInitializerListExpr(const CXXStdInitializerListExpr *E);
};
}
/// Perform zero-initialization on an object of non-union class type.
/// C++11 [dcl.init]p5:
/// To zero-initialize an object or reference of type T means:
/// [...]
/// -- if T is a (possibly cv-qualified) non-union class type,
/// each non-static data member and each base-class subobject is
/// zero-initialized
static bool HandleClassZeroInitialization(EvalInfo &Info, const Expr *E,
const RecordDecl *RD,
const LValue &This, APValue &Result) {
assert(!RD->isUnion() && "Expected non-union class type");
const CXXRecordDecl *CD = dyn_cast<CXXRecordDecl>(RD);
Result = APValue(APValue::UninitStruct(), CD ? CD->getNumBases() : 0,
std::distance(RD->field_begin(), RD->field_end()));
if (RD->isInvalidDecl()) return false;
const ASTRecordLayout &Layout = Info.Ctx.getASTRecordLayout(RD);
if (CD) {
unsigned Index = 0;
for (CXXRecordDecl::base_class_const_iterator I = CD->bases_begin(),
End = CD->bases_end(); I != End; ++I, ++Index) {
const CXXRecordDecl *Base = I->getType()->getAsCXXRecordDecl();
LValue Subobject = This;
if (!HandleLValueDirectBase(Info, E, Subobject, CD, Base, &Layout))
return false;
if (!HandleClassZeroInitialization(Info, E, Base, Subobject,
Result.getStructBase(Index)))
return false;
}
}
for (const auto *I : RD->fields()) {
// -- if T is a reference type, no initialization is performed.
if (I->getType()->isReferenceType())
continue;
LValue Subobject = This;
if (!HandleLValueMember(Info, E, Subobject, I, &Layout))
return false;
ImplicitValueInitExpr VIE(I->getType());
if (!EvaluateInPlace(
Result.getStructField(I->getFieldIndex()), Info, Subobject, &VIE))
return false;
}
return true;
}
bool RecordExprEvaluator::ZeroInitialization(const Expr *E) {
const RecordDecl *RD = E->getType()->castAs<RecordType>()->getDecl();
if (RD->isInvalidDecl()) return false;
if (RD->isUnion()) {
// C++11 [dcl.init]p5: If T is a (possibly cv-qualified) union type, the
// object's first non-static named data member is zero-initialized
RecordDecl::field_iterator I = RD->field_begin();
if (I == RD->field_end()) {
Result = APValue((const FieldDecl*)nullptr);
return true;
}
LValue Subobject = This;
if (!HandleLValueMember(Info, E, Subobject, *I))
return false;
Result = APValue(*I);
ImplicitValueInitExpr VIE(I->getType());
return EvaluateInPlace(Result.getUnionValue(), Info, Subobject, &VIE);
}
if (isa<CXXRecordDecl>(RD) && cast<CXXRecordDecl>(RD)->getNumVBases()) {
Info.Diag(E, diag::note_constexpr_virtual_base) << RD;
return false;
}
return HandleClassZeroInitialization(Info, E, RD, This, Result);
}
bool RecordExprEvaluator::VisitCastExpr(const CastExpr *E) {
switch (E->getCastKind()) {
default:
return ExprEvaluatorBaseTy::VisitCastExpr(E);
case CK_ConstructorConversion:
return Visit(E->getSubExpr());
case CK_DerivedToBase:
case CK_UncheckedDerivedToBase: {
APValue DerivedObject;
if (!Evaluate(DerivedObject, Info, E->getSubExpr()))
return false;
if (!DerivedObject.isStruct())
return Error(E->getSubExpr());
// Derived-to-base rvalue conversion: just slice off the derived part.
APValue *Value = &DerivedObject;
const CXXRecordDecl *RD = E->getSubExpr()->getType()->getAsCXXRecordDecl();
for (CastExpr::path_const_iterator PathI = E->path_begin(),
PathE = E->path_end(); PathI != PathE; ++PathI) {
assert(!(*PathI)->isVirtual() && "record rvalue with virtual base");
const CXXRecordDecl *Base = (*PathI)->getType()->getAsCXXRecordDecl();
Value = &Value->getStructBase(getBaseIndex(RD, Base));
RD = Base;
}
Result = *Value;
return true;
}
}
}
bool RecordExprEvaluator::VisitInitListExpr(const InitListExpr *E) {
const RecordDecl *RD = E->getType()->castAs<RecordType>()->getDecl();
if (RD->isInvalidDecl()) return false;
const ASTRecordLayout &Layout = Info.Ctx.getASTRecordLayout(RD);
if (RD->isUnion()) {
const FieldDecl *Field = E->getInitializedFieldInUnion();
Result = APValue(Field);
if (!Field)
return true;
// If the initializer list for a union does not contain any elements, the
// first element of the union is value-initialized.
// FIXME: The element should be initialized from an initializer list.
// Is this difference ever observable for initializer lists which
// we don't build?
ImplicitValueInitExpr VIE(Field->getType());
const Expr *InitExpr = E->getNumInits() ? E->getInit(0) : &VIE;
LValue Subobject = This;
if (!HandleLValueMember(Info, InitExpr, Subobject, Field, &Layout))
return false;
// Temporarily override This, in case there's a CXXDefaultInitExpr in here.
ThisOverrideRAII ThisOverride(*Info.CurrentCall, &This,
isa<CXXDefaultInitExpr>(InitExpr));
return EvaluateInPlace(Result.getUnionValue(), Info, Subobject, InitExpr);
}
assert((!isa<CXXRecordDecl>(RD) || !cast<CXXRecordDecl>(RD)->getNumBases()) &&
"initializer list for class with base classes");
Result = APValue(APValue::UninitStruct(), 0,
std::distance(RD->field_begin(), RD->field_end()));
unsigned ElementNo = 0;
bool Success = true;
for (const auto *Field : RD->fields()) {
// Anonymous bit-fields are not considered members of the class for
// purposes of aggregate initialization.
if (Field->isUnnamedBitfield())
Index: test/Sema/const-eval.c
===================================================================
--- test/Sema/const-eval.c (revision 238723)
+++ test/Sema/const-eval.c (working copy)
@@ -1,136 +1,139 @@
// RUN: %clang_cc1 -fsyntax-only -verify -triple i686-linux %s -Wno-tautological-pointer-compare
#define EVAL_EXPR(testno, expr) int test##testno = sizeof(struct{char qq[expr];});
int x;
EVAL_EXPR(1, (_Bool)&x)
EVAL_EXPR(2, (int)(1.0+(double)4))
EVAL_EXPR(3, (int)(1.0+(float)4.0))
EVAL_EXPR(4, (_Bool)(1 ? (void*)&x : 0))
EVAL_EXPR(5, (_Bool)(int[]){0})
struct y {int x,y;};
EVAL_EXPR(6, (int)(1+(struct y*)0))
EVAL_EXPR(7, (int)&((struct y*)0)->y)
EVAL_EXPR(8, (_Bool)"asdf")
EVAL_EXPR(9, !!&x)
EVAL_EXPR(10, ((void)1, 12))
void g0(void);
EVAL_EXPR(11, (g0(), 12)) // expected-error {{must have a constant size}}
EVAL_EXPR(12, 1.0&&2.0)
EVAL_EXPR(13, x || 3.0) // expected-error {{must have a constant size}}
unsigned int l_19 = 1;
EVAL_EXPR(14, (1 ^ l_19) && 1); // expected-error {{fields must have a constant size}}
void f()
{
int a;
EVAL_EXPR(15, (_Bool)&a);
}
// FIXME: Turn into EVAL_EXPR test once we have more folding.
_Complex float g16 = (1.0f + 1.0fi);
// ?: in constant expressions.
int g17[(3?:1) - 2];
EVAL_EXPR(18, ((int)((void*)10 + 10)) == 20 ? 1 : -1);
struct s {
int a[(int)-1.0f]; // expected-error {{'a' declared as an array with a negative size}}
};
EVAL_EXPR(19, ((int)&*(char*)10 == 10 ? 1 : -1));
EVAL_EXPR(20, __builtin_constant_p(*((int*) 10)));
EVAL_EXPR(21, (__imag__ 2i) == 2 ? 1 : -1);
EVAL_EXPR(22, (__real__ (2i+3)) == 3 ? 1 : -1);
int g23[(int)(1.0 / 1.0)] = { 1 };
int g24[(int)(1.0 / 1.0)] = { 1 , 2 }; // expected-warning {{excess elements in array initializer}}
int g25[(int)(1.0 + 1.0)], g26 = sizeof(g25);
EVAL_EXPR(26, (_Complex double)0 ? -1 : 1)
EVAL_EXPR(27, (_Complex int)0 ? -1 : 1)
EVAL_EXPR(28, (_Complex double)1 ? 1 : -1)
EVAL_EXPR(29, (_Complex int)1 ? 1 : -1)
// PR4027 + rdar://6808859
struct a { int x, y; };
static struct a V2 = (struct a)(struct a){ 1, 2};
static const struct a V1 = (struct a){ 1, 2};
EVAL_EXPR(30, (int)(_Complex float)((1<<30)-1) == (1<<30) ? 1 : -1)
EVAL_EXPR(31, (int*)0 == (int*)0 ? 1 : -1)
EVAL_EXPR(32, (int*)0 != (int*)0 ? -1 : 1)
EVAL_EXPR(33, (void*)0 - (void*)0 == 0 ? 1 : -1)
void foo(void) {}
EVAL_EXPR(34, (foo == (void *)0) ? -1 : 1)
// No PR. Mismatched bitwidths lead to a crash on second evaluation.
const _Bool constbool = 0;
EVAL_EXPR(35, constbool)
EVAL_EXPR(36, constbool)
EVAL_EXPR(37, (1,2.0) == 2.0 ? 1 : -1)
EVAL_EXPR(38, __builtin_expect(1,1) == 1 ? 1 : -1)
// PR7884
EVAL_EXPR(39, __real__(1.f) == 1 ? 1 : -1)
EVAL_EXPR(40, __imag__(1.f) == 0 ? 1 : -1)
// From gcc testsuite
EVAL_EXPR(41, (int)(1+(_Complex unsigned)2))
// rdar://8875946
void rdar8875946() {
double _Complex P;
float _Complex P2 = 3.3f + P;
}
double d = (d = 0.0); // expected-error {{not a compile-time constant}}
double d2 = ++d; // expected-error {{not a compile-time constant}}
int n = 2;
int intLvalue[*(int*)((long)&n ?: 1)] = { 1, 2 }; // expected-error {{variable length array}}
union u { int a; char b[4]; };
char c = ((union u)(123456)).b[0]; // expected-error {{not a compile-time constant}}
extern const int weak_int __attribute__((weak));
const int weak_int = 42;
int weak_int_test = weak_int; // expected-error {{not a compile-time constant}}
int literalVsNull1 = "foo" == 0;
int literalVsNull2 = 0 == "foo";
// PR11385.
int castViaInt[*(int*)(unsigned long)"test"]; // expected-error {{variable length array}}
// PR11391.
struct PR11391 { _Complex float f; } pr11391;
EVAL_EXPR(42, __builtin_constant_p(pr11391.f = 1))
// PR12043
float varfloat;
const float constfloat = 0;
EVAL_EXPR(43, varfloat && constfloat) // expected-error {{must have a constant size}}
// <rdar://problem/11205586>
// (Make sure we continue to reject this.)
EVAL_EXPR(44, "x"[0]); // expected-error {{variable length array}}
// <rdar://problem/10962435>
EVAL_EXPR(45, ((char*)-1) + 1 == 0 ? 1 : -1)
EVAL_EXPR(46, ((char*)-1) + 1 < (char*) -1 ? 1 : -1)
EVAL_EXPR(47, &x < &x + 1 ? 1 : -1)
EVAL_EXPR(48, &x != &x - 1 ? 1 : -1)
EVAL_EXPR(49, &x < &x - 100 ? 1 : -1) // expected-error {{must have a constant size}}
extern struct Test50S Test50;
EVAL_EXPR(50, &Test50 < (struct Test50S*)((unsigned)&Test50 + 10)) // expected-error {{must have a constant size}}
// <rdar://problem/11874571>
EVAL_EXPR(51, 0 != (float)1e99)
+
+// PR21945
+void PR21945() { int i = (({}), 0l); }
_______________________________________________
cfe-commits mailing list
[email protected]
http://lists.cs.uiuc.edu/mailman/listinfo/cfe-commits