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