From 357362d00b04a23ee93ad0b097f96fb3b1eca3c7 Mon Sep 17 00:00:00 2001 From: Richard Smith Date: Tue, 13 Dec 2011 06:39:58 +0000 Subject: [PATCH] Add checks and diagnostics for many of the cases which C++11 considers to not be constant expressions. llvm-svn: 146479 --- clang/include/clang/AST/Decl.h | 6 + .../include/clang/Basic/DiagnosticASTKinds.td | 18 + .../clang/Basic/DiagnosticCommonKinds.td | 1 + .../clang/Basic/DiagnosticSemaKinds.td | 1 - clang/lib/AST/ExprConstant.cpp | 292 +++++++++----- clang/test/CXX/expr/expr.const/p2-0x.cpp | 355 +++++++++++++++++- .../SemaCXX/constant-expression-cxx11.cpp | 6 +- clang/test/SemaCXX/enum-bitfield.cpp | 4 +- 8 files changed, 587 insertions(+), 96 deletions(-) diff --git a/clang/include/clang/AST/Decl.h b/clang/include/clang/AST/Decl.h index c8120eb361a5..92a6a5469c8d 100644 --- a/clang/include/clang/AST/Decl.h +++ b/clang/include/clang/AST/Decl.h @@ -3162,6 +3162,12 @@ inline const DiagnosticBuilder &operator<<(const DiagnosticBuilder &DB, DiagnosticsEngine::ak_nameddecl); return DB; } +inline const PartialDiagnostic &operator<<(const PartialDiagnostic &PD, + const NamedDecl* ND) { + PD.AddTaggedVal(reinterpret_cast(ND), + DiagnosticsEngine::ak_nameddecl); + return PD; +} template void Redeclarable::setPreviousDeclaration(decl_type *PrevDecl) { diff --git a/clang/include/clang/Basic/DiagnosticASTKinds.td b/clang/include/clang/Basic/DiagnosticASTKinds.td index 4f7169dade4f..e9b314752191 100644 --- a/clang/include/clang/Basic/DiagnosticASTKinds.td +++ b/clang/include/clang/Basic/DiagnosticASTKinds.td @@ -15,6 +15,24 @@ def note_expr_divide_by_zero : Note<"division by zero">; def note_constexpr_invalid_cast : Note< "%select{reinterpret_cast|dynamic_cast|cast which performs the conversions of" " a reinterpret_cast|cast from %1}0 is not allowed in a constant expression">; +def note_constexpr_overflow : Note< + "value %0 is outside the range of representable values of type %1">; +def note_constexpr_invalid_function : Note< + "%select{non-constexpr|undefined}0 %select{function|constructor}1 %2 cannot " + "be used in a constant expression">; +def note_constexpr_nonliteral : Note< + "non-literal type %0 cannot be used in a constant expression">; +def note_constexpr_non_global : Note< + "%select{pointer|reference}0 to %select{|subobject of }1" + "%select{temporary|%4}2 %select{is not a constant expression|" + "cannot be returned from a constexpr function|" + "cannot be used to initialize a member in a constant expression}3">; +def note_constexpr_past_end : Note< + "dereferenced pointer past the end of %select{|subobject of}0 " + "%select{temporary|%2}1 is not a constant expression">; +def note_constexpr_temporary_here : Note<"temporary created here">; +def note_constexpr_depth_limit_exceeded : Note< + "constexpr evaluation exceeded maximum depth of %0 calls">; // inline asm related. let CategoryName = "Inline Assembly Issue" in { diff --git a/clang/include/clang/Basic/DiagnosticCommonKinds.td b/clang/include/clang/Basic/DiagnosticCommonKinds.td index b02edab1f3f2..077b24fd1d73 100644 --- a/clang/include/clang/Basic/DiagnosticCommonKinds.td +++ b/clang/include/clang/Basic/DiagnosticCommonKinds.td @@ -18,6 +18,7 @@ let Component = "Common" in { def fatal_too_many_errors : Error<"too many errors emitted, stopping now">, DefaultFatal; +def note_declared_at : Note<"declared here">; def note_previous_definition : Note<"previous definition is here">; def note_previous_declaration : Note<"previous declaration is here">; def note_previous_implicit_declaration : Note< diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td index 7c7b920119e4..56d534827529 100644 --- a/clang/include/clang/Basic/DiagnosticSemaKinds.td +++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td @@ -485,7 +485,6 @@ def warn_strict_multiple_method_decl : Warning< "multiple methods named %0 found">, InGroup, DefaultIgnore; def warn_accessor_property_type_mismatch : Warning< "type of property %0 does not match type of accessor %1">; -def note_declared_at : Note<"declared here">; def note_method_declared_at : Note<"method declared here">; def err_setter_type_void : Error<"type of setter must be void">; def err_duplicate_method_decl : Error<"duplicate declaration of method %0">; diff --git a/clang/lib/AST/ExprConstant.cpp b/clang/lib/AST/ExprConstant.cpp index 3a897ab34842..dd1110ab11c3 100644 --- a/clang/lib/AST/ExprConstant.cpp +++ b/clang/lib/AST/ExprConstant.cpp @@ -293,11 +293,15 @@ namespace { /// declaration whose initializer is being evaluated, if any. APValue *EvaluatingDeclValue; + /// HasActiveDiagnostic - Was the previous diagnostic stored? If so, further + /// notes attached to it will also be stored, otherwise they will not be. + bool HasActiveDiagnostic; + EvalInfo(const ASTContext &C, Expr::EvalStatus &S) : Ctx(const_cast(C)), EvalStatus(S), CurrentCall(0), CallStackDepth(0), BottomFrame(*this, 0, 0), EvaluatingDecl(0), - EvaluatingDeclValue(0) {} + EvaluatingDeclValue(0), HasActiveDiagnostic(false) {} const CCValue *getOpaqueValue(const OpaqueValueExpr *e) const { MapTy::const_iterator i = OpaqueValues.find(e); @@ -312,33 +316,55 @@ namespace { const LangOptions &getLangOpts() const { return Ctx.getLangOptions(); } - bool atCallLimit() const { - return CallStackDepth > getLangOpts().ConstexprCallDepth; + bool CheckCallLimit(SourceLocation Loc) { + if (CallStackDepth <= getLangOpts().ConstexprCallDepth) + return true; + Diag(Loc, diag::note_constexpr_depth_limit_exceeded) + << getLangOpts().ConstexprCallDepth; + return false; } + private: + /// Add a diagnostic to the diagnostics list. + PartialDiagnostic &addDiag(SourceLocation Loc, diag::kind DiagId) { + PartialDiagnostic PD(DiagId, Ctx.getDiagAllocator()); + EvalStatus.Diag->push_back(std::make_pair(Loc, PD)); + return EvalStatus.Diag->back().second; + } + + public: /// Diagnose that the evaluation cannot be folded. - OptionalDiagnostic Diag(SourceLocation Loc, diag::kind DiagId) { + OptionalDiagnostic Diag(SourceLocation Loc, diag::kind DiagId, + unsigned ExtraNotes = 0) { // If we have a prior diagnostic, it will be noting that the expression // isn't a constant expression. This diagnostic is more important. // FIXME: We might want to show both diagnostics to the user. if (EvalStatus.Diag) { + HasActiveDiagnostic = true; EvalStatus.Diag->clear(); - EvalStatus.Diag->reserve(1); - PartialDiagnostic PD(DiagId, Ctx.getDiagAllocator()); - EvalStatus.Diag->push_back(std::make_pair(Loc, PD)); + EvalStatus.Diag->reserve(1 + ExtraNotes); // FIXME: Add a call stack for constexpr evaluation. - return OptionalDiagnostic(&EvalStatus.Diag->back().second); + return OptionalDiagnostic(&addDiag(Loc, DiagId)); } + HasActiveDiagnostic = false; return OptionalDiagnostic(); } /// Diagnose that the evaluation does not produce a C++11 core constant /// expression. - OptionalDiagnostic CCEDiag(SourceLocation Loc, diag::kind DiagId) { + OptionalDiagnostic CCEDiag(SourceLocation Loc, diag::kind DiagId, + unsigned ExtraNotes = 0) { // Don't override a previous diagnostic. if (!EvalStatus.Diag || !EvalStatus.Diag->empty()) return OptionalDiagnostic(); - return Diag(Loc, DiagId); + return Diag(Loc, DiagId, ExtraNotes); + } + + /// Add a note to a prior diagnostic. + OptionalDiagnostic Note(SourceLocation Loc, diag::kind DiagId) { + if (!HasActiveDiagnostic) + return OptionalDiagnostic(); + return OptionalDiagnostic(&addDiag(Loc, DiagId)); } }; @@ -515,11 +541,20 @@ namespace { return castBack(Base); } }; + + /// Kinds of constant expression checking, for diagnostics. + enum CheckConstantExpressionKind { + CCEK_Constant, ///< A normal constant. + CCEK_ReturnValue, ///< A constexpr function return value. + CCEK_MemberInit ///< A constexpr constructor mem-initializer. + }; } static bool Evaluate(CCValue &Result, EvalInfo &Info, const Expr *E); static bool EvaluateConstantExpression(APValue &Result, EvalInfo &Info, - const LValue &This, const Expr *E); + const LValue &This, const Expr *E, + CheckConstantExpressionKind CCEK + = CCEK_Constant); static bool EvaluateLValue(const Expr *E, LValue &Result, EvalInfo &Info); static bool EvaluatePointer(const Expr *E, LValue &Result, EvalInfo &Info); static bool EvaluateMemberPointer(const Expr *E, MemberPtr &Result, @@ -586,22 +621,53 @@ static bool IsGlobalLValue(APValue::LValueBase B) { /// value for a constant expression. Type T should be either LValue or CCValue. template static bool CheckLValueConstantExpression(EvalInfo &Info, const Expr *E, - const T &LVal, APValue &Value) { - if (!IsGlobalLValue(LVal.getLValueBase())) { - Info.Diag(E->getExprLoc(), diag::note_invalid_subexpr_in_const_expr); + const T &LVal, APValue &Value, + CheckConstantExpressionKind CCEK) { + APValue::LValueBase Base = LVal.getLValueBase(); + const SubobjectDesignator &Designator = LVal.getLValueDesignator(); + + if (!IsGlobalLValue(Base)) { + if (Info.getLangOpts().CPlusPlus0x) { + const ValueDecl *VD = Base.dyn_cast(); + Info.Diag(E->getExprLoc(), diag::note_constexpr_non_global, 1) + << E->isGLValue() << !Designator.Entries.empty() + << !!VD << CCEK << VD; + if (VD) + Info.Note(VD->getLocation(), diag::note_declared_at); + else + Info.Note(Base.dyn_cast()->getExprLoc(), + diag::note_constexpr_temporary_here); + } else { + Info.Diag(E->getExprLoc(), diag::note_invalid_subexpr_in_const_expr); + } return false; } - const SubobjectDesignator &Designator = LVal.getLValueDesignator(); // A constant expression must refer to an object or be a null pointer. if (Designator.Invalid || (!LVal.getLValueBase() && !Designator.Entries.empty())) { - // FIXME: This is not a constant expression. + // FIXME: This is not a core constant expression. We should have already + // produced a CCE diagnostic. Value = APValue(LVal.getLValueBase(), LVal.getLValueOffset(), APValue::NoLValuePath()); return true; } + // Does this refer one past the end of some object? + // This is technically not an address constant expression nor a reference + // constant expression, but we allow it for address constant expressions. + if (E->isGLValue() && Base && Designator.OnePastTheEnd) { + const ValueDecl *VD = Base.dyn_cast(); + Info.Diag(E->getExprLoc(), diag::note_constexpr_past_end, 1) + << !Designator.Entries.empty() << !!VD << VD; + if (VD) + Info.Note(VD->getLocation(), diag::note_declared_at); + else + Info.Note(Base.dyn_cast()->getExprLoc(), + diag::note_constexpr_temporary_here); + return false; + } + Value = APValue(LVal.getLValueBase(), LVal.getLValueOffset(), Designator.Entries, Designator.OnePastTheEnd); return true; @@ -611,12 +677,14 @@ static bool CheckLValueConstantExpression(EvalInfo &Info, const Expr *E, /// constant expression, and if it is, produce the corresponding constant value. /// If not, report an appropriate diagnostic. static bool CheckConstantExpression(EvalInfo &Info, const Expr *E, - const CCValue &CCValue, APValue &Value) { + const CCValue &CCValue, APValue &Value, + CheckConstantExpressionKind CCEK + = CCEK_Constant) { if (!CCValue.isLValue()) { Value = CCValue; return true; } - return CheckLValueConstantExpression(Info, E, CCValue, Value); + return CheckLValueConstantExpression(Info, E, CCValue, Value, CCEK); } const ValueDecl *GetLValueBaseDecl(const LValue &LVal) { @@ -693,26 +761,41 @@ static bool EvaluateAsBooleanCondition(const Expr *E, bool &Result, return HandleConversionToBool(Val, Result); } -static APSInt HandleFloatToIntCast(QualType DestType, QualType SrcType, - APFloat &Value, const ASTContext &Ctx) { - unsigned DestWidth = Ctx.getIntWidth(DestType); +template +static bool HandleOverflow(EvalInfo &Info, const Expr *E, + const T &SrcValue, QualType DestType) { + llvm::SmallVector Buffer; + SrcValue.toString(Buffer); + Info.Diag(E->getExprLoc(), diag::note_constexpr_overflow) + << StringRef(Buffer.data(), Buffer.size()) << DestType; + return false; +} + +static bool HandleFloatToIntCast(EvalInfo &Info, const Expr *E, + QualType SrcType, const APFloat &Value, + QualType DestType, APSInt &Result) { + unsigned DestWidth = Info.Ctx.getIntWidth(DestType); // Determine whether we are converting to unsigned or signed. bool DestSigned = DestType->isSignedIntegerOrEnumerationType(); - // FIXME: Warning for overflow. - APSInt Result(DestWidth, !DestSigned); + Result = APSInt(DestWidth, !DestSigned); bool ignored; - (void)Value.convertToInteger(Result, llvm::APFloat::rmTowardZero, &ignored); - return Result; + if (Value.convertToInteger(Result, llvm::APFloat::rmTowardZero, &ignored) + & APFloat::opInvalidOp) + return HandleOverflow(Info, E, Value, DestType); + return true; } -static APFloat HandleFloatToFloatCast(QualType DestType, QualType SrcType, - APFloat &Value, const ASTContext &Ctx) { +static bool HandleFloatToFloatCast(EvalInfo &Info, const Expr *E, + QualType SrcType, QualType DestType, + APFloat &Result) { + APFloat Value = Result; bool ignored; - APFloat Result = Value; - Result.convert(Ctx.getFloatTypeSemantics(DestType), - APFloat::rmNearestTiesToEven, &ignored); - return Result; + if (Result.convert(Info.Ctx.getFloatTypeSemantics(DestType), + APFloat::rmNearestTiesToEven, &ignored) + & APFloat::opOverflow) + return HandleOverflow(Info, E, Value, DestType); + return true; } static APSInt HandleIntToIntCast(QualType DestType, QualType SrcType, @@ -726,13 +809,15 @@ static APSInt HandleIntToIntCast(QualType DestType, QualType SrcType, return Result; } -static APFloat HandleIntToFloatCast(QualType DestType, QualType SrcType, - APSInt &Value, const ASTContext &Ctx) { - - APFloat Result(Ctx.getFloatTypeSemantics(DestType), 1); - Result.convertFromAPInt(Value, Value.isSigned(), - APFloat::rmNearestTiesToEven); - return Result; +static bool HandleIntToFloatCast(EvalInfo &Info, const Expr *E, + QualType SrcType, const APSInt &Value, + QualType DestType, APFloat &Result) { + Result = APFloat(Info.Ctx.getFloatTypeSemantics(DestType), 1); + if (Result.convertFromAPInt(Value, Value.isSigned(), + APFloat::rmNearestTiesToEven) + & APFloat::opOverflow) + return HandleOverflow(Info, E, Value, DestType); + return true; } static bool FindMostDerivedObject(EvalInfo &Info, const LValue &LVal, @@ -1311,7 +1396,7 @@ enum EvalStmtResult { } // Evaluate a statement. -static EvalStmtResult EvaluateStmt(CCValue &Result, EvalInfo &Info, +static EvalStmtResult EvaluateStmt(APValue &Result, EvalInfo &Info, const Stmt *S) { switch (S->getStmtClass()) { default: @@ -1321,10 +1406,15 @@ static EvalStmtResult EvaluateStmt(CCValue &Result, EvalInfo &Info, case Stmt::DeclStmtClass: return ESR_Succeeded; - case Stmt::ReturnStmtClass: - if (Evaluate(Result, Info, cast(S)->getRetValue())) - return ESR_Returned; - return ESR_Failed; + case Stmt::ReturnStmtClass: { + CCValue CCResult; + const Expr *RetExpr = cast(S)->getRetValue(); + if (!Evaluate(CCResult, Info, RetExpr) || + !CheckConstantExpression(Info, RetExpr, CCResult, Result, + CCEK_ReturnValue)) + return ESR_Failed; + return ESR_Returned; + } case Stmt::CompoundStmtClass: { const CompoundStmt *CS = cast(S); @@ -1339,6 +1429,27 @@ static EvalStmtResult EvaluateStmt(CCValue &Result, EvalInfo &Info, } } +/// 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) { + // Can we evaluate this function call? + if (Definition && Definition->isConstexpr() && !Definition->isInvalidDecl()) + return true; + + if (Info.getLangOpts().CPlusPlus0x) { + const FunctionDecl *DiagDecl = Definition ? Definition : Declaration; + Info.Diag(CallLoc, diag::note_constexpr_invalid_function, 1) + << DiagDecl->isConstexpr() << isa(DiagDecl) + << DiagDecl; + Info.Note(DiagDecl->getLocation(), diag::note_declared_at); + } else { + Info.Diag(CallLoc, diag::note_invalid_subexpr_in_const_expr); + } + return false; +} + namespace { typedef SmallVector ArgVector; } @@ -1356,12 +1467,9 @@ static bool EvaluateArgs(ArrayRef Args, ArgVector &ArgValues, /// Evaluate a function call. static bool HandleFunctionCall(const Expr *CallExpr, const LValue *This, ArrayRef Args, const Stmt *Body, - EvalInfo &Info, CCValue &Result) { - if (Info.atCallLimit()) { - // FIXME: Add a specific proper diagnostic for this. - Info.Diag(CallExpr->getExprLoc(), diag::note_invalid_subexpr_in_const_expr); + EvalInfo &Info, APValue &Result) { + if (!Info.CheckCallLimit(CallExpr->getExprLoc())) return false; - } ArgVector ArgValues(Args.size()); if (!EvaluateArgs(Args, ArgValues, Info)) @@ -1377,11 +1485,8 @@ static bool HandleConstructorCall(const Expr *CallExpr, const LValue &This, const CXXConstructorDecl *Definition, EvalInfo &Info, APValue &Result) { - if (Info.atCallLimit()) { - // FIXME: Add a specific diagnostic for this. - Info.Diag(CallExpr->getExprLoc(), diag::note_invalid_subexpr_in_const_expr); + if (!Info.CheckCallLimit(CallExpr->getExprLoc())) return false; - } ArgVector ArgValues(Args.size()); if (!EvaluateArgs(Args, ArgValues, Info)) @@ -1430,12 +1535,12 @@ static bool HandleConstructorCall(const Expr *CallExpr, const LValue &This, HandleLValueMember(Info, Subobject, FD, &Layout); if (RD->isUnion()) { Result = APValue(FD); - if (!EvaluateConstantExpression(Result.getUnionValue(), Info, - Subobject, (*I)->getInit())) + if (!EvaluateConstantExpression(Result.getUnionValue(), Info, Subobject, + (*I)->getInit(), CCEK_MemberInit)) return false; } else if (!EvaluateConstantExpression( Result.getStructField(FD->getFieldIndex()), - Info, Subobject, (*I)->getInit())) + Info, Subobject, (*I)->getInit(), CCEK_MemberInit)) return false; } else { // FIXME: handle indirect field initializers @@ -1745,16 +1850,12 @@ public: } else return Error(E); - const FunctionDecl *Definition; + const FunctionDecl *Definition = 0; Stmt *Body = FD->getBody(Definition); - CCValue CCResult; APValue Result; - if (!Body || !Definition->isConstexpr() || Definition->isInvalidDecl()) - return Error(E); - - if (!HandleFunctionCall(E, This, Args, Body, Info, CCResult) || - !CheckConstantExpression(Info, E, CCResult, Result)) + if (!CheckConstexprFunction(Info, E->getExprLoc(), FD, Definition) || + !HandleFunctionCall(E, This, Args, Body, Info, Result)) return false; return DerivedSuccess(CCValue(Result, CCValue::GlobalValue()), E); @@ -1878,6 +1979,10 @@ public: if (!EvaluatePointer(E->getBase(), Result, this->Info)) return false; BaseTy = E->getBase()->getType()->getAs()->getPointeeType(); + } else if (E->getBase()->isRValue()) { + if (!EvaluateTemporary(E->getBase(), Result, this->Info)) + return false; + BaseTy = E->getBase()->getType(); } else { if (!this->Visit(E->getBase())) return false; @@ -2516,8 +2621,8 @@ bool RecordExprEvaluator::VisitCXXConstructExpr(const CXXConstructExpr *E) { const FunctionDecl *Definition = 0; FD->getBody(Definition); - if (!Definition || !Definition->isConstexpr() || Definition->isInvalidDecl()) - return Error(E); + if (!CheckConstexprFunction(Info, E->getExprLoc(), FD, Definition)) + return false; // FIXME: Elide the copy/move construction wherever we can. if (E->isElidable()) @@ -2837,8 +2942,8 @@ bool ArrayExprEvaluator::VisitCXXConstructExpr(const CXXConstructExpr *E) { const FunctionDecl *Definition = 0; FD->getBody(Definition); - if (!Definition || !Definition->isConstexpr() || Definition->isInvalidDecl()) - return Error(E); + if (!CheckConstexprFunction(Info, E->getExprLoc(), FD, Definition)) + return false; // FIXME: The Subobject here isn't necessarily right. This rarely matters, // but sometimes does: @@ -3881,7 +3986,10 @@ bool IntExprEvaluator::VisitCastExpr(const CastExpr *E) { if (!EvaluateFloat(SubExpr, F, Info)) return false; - return Success(HandleFloatToIntCast(DestType, SrcType, F, Info.Ctx), E); + APSInt Value; + if (!HandleFloatToIntCast(Info, E, SrcType, F, DestType, Value)) + return false; + return Success(Value, E); } } @@ -4130,19 +4238,16 @@ bool FloatExprEvaluator::VisitCastExpr(const CastExpr *E) { case CK_IntegralToFloating: { APSInt IntResult; - if (!EvaluateInteger(SubExpr, IntResult, Info)) - return false; - Result = HandleIntToFloatCast(E->getType(), SubExpr->getType(), - IntResult, Info.Ctx); - return true; + return EvaluateInteger(SubExpr, IntResult, Info) && + HandleIntToFloatCast(Info, E, SubExpr->getType(), IntResult, + E->getType(), Result); } case CK_FloatingCast: { if (!Visit(SubExpr)) return false; - Result = HandleFloatToFloatCast(E->getType(), SubExpr->getType(), - Result, Info.Ctx); - return true; + return HandleFloatToFloatCast(Info, E, SubExpr->getType(), E->getType(), + Result); } case CK_FloatingComplexToReal: { @@ -4289,11 +4394,8 @@ bool ComplexExprEvaluator::VisitCastExpr(const CastExpr *E) { QualType From = E->getSubExpr()->getType()->getAs()->getElementType(); - Result.FloatReal - = HandleFloatToFloatCast(To, From, Result.FloatReal, Info.Ctx); - Result.FloatImag - = HandleFloatToFloatCast(To, From, Result.FloatImag, Info.Ctx); - return true; + return HandleFloatToFloatCast(Info, E, From, To, Result.FloatReal) && + HandleFloatToFloatCast(Info, E, From, To, Result.FloatImag); } case CK_FloatingComplexToIntegralComplex: { @@ -4304,9 +4406,10 @@ bool ComplexExprEvaluator::VisitCastExpr(const CastExpr *E) { QualType From = E->getSubExpr()->getType()->getAs()->getElementType(); Result.makeComplexInt(); - Result.IntReal = HandleFloatToIntCast(To, From, Result.FloatReal, Info.Ctx); - Result.IntImag = HandleFloatToIntCast(To, From, Result.FloatImag, Info.Ctx); - return true; + return HandleFloatToIntCast(Info, E, From, Result.FloatReal, + To, Result.IntReal) && + HandleFloatToIntCast(Info, E, From, Result.FloatImag, + To, Result.IntImag); } case CK_IntegralRealToComplex: { @@ -4340,9 +4443,10 @@ bool ComplexExprEvaluator::VisitCastExpr(const CastExpr *E) { QualType From = E->getSubExpr()->getType()->getAs()->getElementType(); Result.makeComplexFloat(); - Result.FloatReal = HandleIntToFloatCast(To, From, Result.IntReal, Info.Ctx); - Result.FloatImag = HandleIntToFloatCast(To, From, Result.IntImag, Info.Ctx); - return true; + return HandleIntToFloatCast(Info, E, From, Result.IntReal, + To, Result.FloatReal) && + HandleIntToFloatCast(Info, E, From, Result.IntImag, + To, Result.FloatImag); } } @@ -4581,8 +4685,16 @@ static bool Evaluate(CCValue &Result, EvalInfo &Info, const Expr *E) { return false; Result = Info.CurrentCall->Temporaries[E]; } else if (E->getType()->isVoidType()) { + if (Info.getLangOpts().CPlusPlus0x) + Info.CCEDiag(E->getExprLoc(), diag::note_constexpr_nonliteral) + << E->getType(); + else + Info.CCEDiag(E->getExprLoc(), diag::note_invalid_subexpr_in_const_expr); if (!EvaluateVoid(E, Info)) return false; + } else if (Info.getLangOpts().CPlusPlus0x) { + Info.Diag(E->getExprLoc(), diag::note_constexpr_nonliteral) << E->getType(); + return false; } else { Info.Diag(E->getExprLoc(), diag::note_invalid_subexpr_in_const_expr); return false; @@ -4596,7 +4708,8 @@ static bool Evaluate(CCValue &Result, EvalInfo &Info, const Expr *E) { /// since later initializers for an object can indirectly refer to subobjects /// which were initialized earlier. static bool EvaluateConstantExpression(APValue &Result, EvalInfo &Info, - const LValue &This, const Expr *E) { + const LValue &This, const Expr *E, + CheckConstantExpressionKind CCEK) { if (E->isRValue() && E->getType()->isLiteralType()) { // Evaluate arrays and record types in-place, so that later initializers can // refer to earlier-initialized members of the object. @@ -4609,7 +4722,7 @@ static bool EvaluateConstantExpression(APValue &Result, EvalInfo &Info, // For any other type, in-place evaluation is unimportant. CCValue CoreConstResult; return Evaluate(CoreConstResult, Info, E) && - CheckConstantExpression(Info, E, CoreConstResult, Result); + CheckConstantExpression(Info, E, CoreConstResult, Result, CCEK); } /// EvaluateAsRValue - Try to evaluate this expression, performing an implicit @@ -4681,7 +4794,8 @@ bool Expr::EvaluateAsLValue(EvalResult &Result, const ASTContext &Ctx) const { LValue LV; return EvaluateLValue(this, LV, Info) && !Result.HasSideEffects && - CheckLValueConstantExpression(Info, this, LV, Result.Val); + CheckLValueConstantExpression(Info, this, LV, Result.Val, + CCEK_Constant); } /// isEvaluatable - Call EvaluateAsRValue to see if this expression can be diff --git a/clang/test/CXX/expr/expr.const/p2-0x.cpp b/clang/test/CXX/expr/expr.const/p2-0x.cpp index 2c6a46b3beaf..dcb4bf4619de 100644 --- a/clang/test/CXX/expr/expr.const/p2-0x.cpp +++ b/clang/test/CXX/expr/expr.const/p2-0x.cpp @@ -1,4 +1,357 @@ -// RUN: %clang_cc1 -fsyntax-only -std=c++11 -verify %s +// RUN: %clang_cc1 -fsyntax-only -std=c++11 -pedantic -verify -fcxx-exceptions %s + +// A conditional-expression is a core constant expression unless it involves one +// of the following as a potentially evaluated subexpression [...]: + +// - this (5.1.1 [expr.prim.general]) [Note: when evaluating a constant +// expression, function invocation substitution (7.1.5 [dcl.constexpr]) +// replaces each occurrence of this in a constexpr member function with a +// pointer to the class object. -end note]; +struct This { + int this1 : this1; // expected-error {{undeclared}} + int this2 : this->this1; // expected-error {{invalid}} + void this3() { + int n1[this->this1]; // expected-warning {{variable length array}} + int n2[this1]; // expected-warning {{variable length array}} + (void)n1, (void)n2; + } +}; + +// - an invocation of a function other than a constexpr constructor for a +// literal class or a constexpr function [ Note: Overload resolution (13.3) +// is applied as usual - end note ]; +struct NonConstexpr1 { + static int f() { return 1; } // expected-note {{here}} + int n : f(); // expected-error {{constant expression}} expected-note {{non-constexpr function 'f' cannot be used in a constant expression}} +}; +struct NonConstexpr2 { + constexpr NonConstexpr2(); // expected-note {{here}} + int n; +}; +struct NonConstexpr3 { + NonConstexpr3(); + int m : NonConstexpr2().n; // expected-error {{constant expression}} expected-note {{undefined constructor 'NonConstexpr2'}} +}; +struct NonConstexpr4 { + NonConstexpr4(); + int n; +}; +struct NonConstexpr5 { + int n : NonConstexpr4().n; // expected-error {{constant expression}} expected-note {{non-literal type 'NonConstexpr4' cannot be used in a constant expression}} +}; + +// - an invocation of an undefined constexpr function or an undefined +// constexpr constructor; +struct UndefinedConstexpr { + constexpr UndefinedConstexpr(); + static constexpr int undefinedConstexpr1(); // expected-note {{here}} + int undefinedConstexpr2 : undefinedConstexpr1(); // expected-error {{constant expression}} expected-note {{undefined function 'undefinedConstexpr1' cannot be used in a constant expression}} +}; + +// - an invocation of a constexpr function with arguments that, when substituted +// by function invocation substitution (7.1.5), do not produce a constant +// expression; +namespace NonConstExprReturn { + static constexpr const int &id_ref(const int &n) { + return n; // expected-note {{reference to temporary cannot be returned from a constexpr function}} + } + struct NonConstExprFunction { + int n : id_ref( // expected-error {{constant expression}} + 16 // expected-note {{temporary created here}} + ); + }; + constexpr const int *address_of(const int &a) { + return &a; // expected-note {{pointer to 'n' cannot be returned from a constexpr function}} + } + constexpr const int *return_param(int n) { // expected-note {{declared here}} + return address_of(n); + } + struct S { + int n : *return_param(0); // expected-error {{constant expression}} + }; +} + +// - an invocation of a constexpr constructor with arguments that, when +// substituted by function invocation substitution (7.1.5), do not produce all +// constant expressions for the constructor calls and full-expressions in the +// mem-initializers (including conversions); +namespace NonConstExprCtor { + struct T { + constexpr T(const int &r) : + r(r) { // expected-note {{reference to temporary cannot be used to initialize a member in a constant expression}} + } + const int &r; + }; + constexpr int n = 0; + constexpr T t1(n); // ok + constexpr T t2(0); // expected-error {{must be initialized by a constant expression}} + + struct S { + int n : T(4).r; // expected-error {{constant expression}} expected-note {{temporary created here}} + }; +} + +// - an invocation of a constexpr function or a constexpr constructor that would +// exceed the implementation-defined recursion limits (see Annex B); +namespace RecursionLimits { + constexpr int RecurseForever(int n) { + return n + RecurseForever(n+1); // expected-note {{constexpr evaluation exceeded maximum depth of 512 calls}} + } + struct AlsoRecurseForever { + constexpr AlsoRecurseForever(int n) : + n(AlsoRecurseForever(n+1).n) // expected-note {{constexpr evaluation exceeded maximum depth of 512 calls}} + {} + int n; + }; + struct S { + int k : RecurseForever(0); // expected-error {{constant expression}} + int l : AlsoRecurseForever(0).n; // expected-error {{constant expression}} + }; +} + +// FIXME: +// - an operation that would have undefined behavior [Note: including, for +// example, signed integer overflow (Clause 5 [expr]), certain pointer +// arithmetic (5.7 [expr.add]), division by zero (5.6 [expr.mul]), or certain +// shift operations (5.8 [expr.shift]) -end note]; +namespace UndefinedBehavior { + void f(int n) { + switch (n) { + case (int)4.4e9: // expected-error {{constant expression}} expected-note {{value 4.4E+9 is outside the range of representable values of type 'int'}} + case (int)(unsigned)(long long)4.4e9: // ok + case (float)1e300: // expected-error {{constant expression}} expected-note {{value 1.0E+300 is outside the range of representable values of type 'float'}} + case (int)((float)1e37 / 1e30): // ok + case (int)(__fp16)65536: // expected-error {{constant expression}} expected-note {{value 65536 is outside the range of representable values of type 'half'}} + break; + } + } + + struct S { + int m; + }; + constexpr S s = { 5 }; // expected-note {{declared here}} + constexpr const int *p = &s.m + 1; + constexpr const int &f(const int *q) { + return q[0]; // expected-note {{dereferenced pointer past the end of subobject of 's' is not a constant expression}} + } + struct T { + int n : f(p); // expected-error {{not an integer constant expression}} + }; +} + +// - a lambda-expression (5.1.2); +struct Lambda { + // FIXME: clang crashes when trying to parse this! Revisit this check once + // lambdas are fully implemented. + //int n : []{ return 1; }(); +}; + +// FIXME: +// - an lvalue-to-rvalue conversion (4.1) unless it is applied to +// +// - a non-volatile glvalue of integral or enumeration type that refers to a +// non-volatile const object with a preceding initialization, initialized with +// a constant expression [Note: a string literal (2.14.5 [lex.string]) +// corresponds to an array of such objects. -end note], or +// +// - a non-volatile glvalue of literal type that refers to a non-volatile +// object defined with constexpr, or that refers to a sub-object of such an +// object, or +// +// - a non-volatile glvalue of literal type that refers to a non-volatile +// temporary object whose lifetime has not ended, initialized with a constant +// expression; + +// FIXME: +// +// DR1312: The proposed wording for this defect has issues, so we instead +// prohibit casts from pointers to cv void (see core-20842 and core-20845). +// +// - an lvalue-to-rvalue conversion (4.1 [conv.lval]) that is applied to a +// glvalue of type cv1 T that refers to an object of type cv2 U, where T and U +// are neither the same type nor similar types (4.4 [conv.qual]); + +// FIXME: +// - an lvalue-to-rvalue conversion (4.1) that is applied to a glvalue that +// refers to a non-active member of a union or a subobject thereof; + +// FIXME: +// - an id-expression that refers to a variable or data member of reference type +// unless the reference has a preceding initialization, initialized with a +// constant expression; +namespace References { + const int a = 2; + int &b = *const_cast(&a); + int c = 10; + int &d = c; + constexpr int e = 42; + int &f = const_cast(e); + extern int &g; + constexpr int &h(); // expected-note {{here}} + int &i = h(); + constexpr int &j() { return b; } + int &k = j(); + + struct S { + int A : a; + int B : b; + int C : c; // expected-error {{constant expression}} + int D : d; // expected-error {{constant expression}} + int D2 : &d - &c + 1; + int E : e / 2; + int F : f - 11; + int G : g; // expected-error {{constant expression}} + int H : h(); // expected-error {{constant expression}} expected-note {{undefined function 'h'}} + int I : i; // expected-error {{constant expression}} + int J : j(); + int K : k; + }; +} + +// - a dynamic_cast (5.2.7); +namespace DynamicCast { + struct S { int n; }; + constexpr S s { 16 }; + struct T { + int n : dynamic_cast(&s)->n; // expected-warning {{constant expression}} expected-note {{dynamic_cast}} + }; +} + +// - a reinterpret_cast (5.2.10); +namespace ReinterpretCast { + struct S { int n; }; + constexpr S s { 16 }; + struct T { + int n : reinterpret_cast(&s)->n; // expected-warning {{constant expression}} expected-note {{reinterpret_cast}} + }; + struct U { + int m : (long)(S*)6; // expected-warning {{constant expression}} expected-note {{reinterpret_cast}} + }; +} + +// - a pseudo-destructor call (5.2.4); +namespace PseudoDtor { + int k; + typedef int I; + struct T { + int n : (k.~I(), 0); // expected-error {{constant expression}} expected-note{{subexpression}} + }; +} + +// - increment or decrement operations (5.2.6, 5.3.2); +namespace IncDec { + int k = 2; + struct T { + int n : ++k; // expected-error {{constant expression}} + int m : --k; // expected-error {{constant expression}} + }; +} + +// - a typeid expression (5.2.8) whose operand is of a polymorphic class type; +namespace std { + struct type_info { + virtual ~type_info(); + const char *name; + }; +} +namespace TypeId { + struct S { virtual void f(); }; + constexpr S *p = 0; + constexpr const std::type_info &ti1 = typeid(*p); // expected-error {{must be initialized by a constant expression}} + + // FIXME: Implement typeid evaluation. + struct T {} t; + constexpr const std::type_info &ti2 = typeid(t); // unexpected-error {{must be initialized by a constant expression}} +} + +// - a new-expression (5.3.4); +// - a delete-expression (5.3.5); +namespace NewDelete { + int *p = 0; + struct T { + int n : *new int(4); // expected-error {{constant expression}} expected-note {{subexpression}} + int m : (delete p, 2); // expected-error {{constant expression}} expected-note {{subexpression}} + }; +} + +// - a relational (5.9) or equality (5.10) operator where the result is +// unspecified; +namespace UnspecifiedRelations { + int a, b; + constexpr int *p = &a, *q = &b; + // C++11 [expr.rel]p2: If two pointers p and q of the same type point to + // different objects that are not members of the same array or to different + // functions, or if only one of them is null, the results of pq, p<=q, + // and p>=q are unspecified. + constexpr bool u1 = p < q; // expected-error {{constant expression}} + constexpr bool u2 = p > q; // expected-error {{constant expression}} + constexpr bool u3 = p <= q; // expected-error {{constant expression}} + constexpr bool u4 = p >= q; // expected-error {{constant expression}} + constexpr bool u5 = p < 0; // expected-error {{constant expression}} + constexpr bool u6 = p <= 0; // expected-error {{constant expression}} + constexpr bool u7 = p > 0; // expected-error {{constant expression}} + constexpr bool u8 = p >= 0; // expected-error {{constant expression}} + constexpr bool u9 = 0 < q; // expected-error {{constant expression}} + constexpr bool u10 = 0 <= q; // expected-error {{constant expression}} + constexpr bool u11 = 0 > q; // expected-error {{constant expression}} + constexpr bool u12 = 0 >= q; // expected-error {{constant expression}} + void f(), g(); + + constexpr void (*pf)() = &f, (*pg)() = &g; + constexpr bool u13 = pf < pg; // expected-error {{constant expression}} + constexpr bool u14 = pf == pg; + + // FIXME: + // If two pointers point to non-static data members of the same object with + // different access control, the result is unspecified. + + // FIXME: + // [expr.rel]p3: Pointers to void can be compared [...] if both pointers + // represent the same address or are both the null pointer [...]; otherwise + // the result is unspecified. + + // FIXME: Implement comparisons of pointers to members. + // [expr.eq]p2: If either is a pointer to a virtual member function and + // neither is null, the result is unspecified. +} + +// - an assignment or a compound assignment (5.17); or +namespace Assignment { + int k; + struct T { + int n : (k = 9); // expected-error {{constant expression}} + int m : (k *= 2); // expected-error {{constant expression}} + }; + + struct Literal { + constexpr Literal(const char *name) : name(name) {} + const char *name; + }; + struct Expr { + constexpr Expr(Literal l) : IsLiteral(true), l(l) {} + bool IsLiteral; + union { + Literal l; + // ... + }; + }; + struct MulEq { + constexpr MulEq(Expr a, Expr b) : LHS(a), RHS(b) {} + Expr LHS; + Expr RHS; + }; + constexpr MulEq operator*=(Expr a, Expr b) { return MulEq(a, b); } + Literal a("a"); + Literal b("b"); + MulEq c = a *= b; // ok +} + +// - a throw-expression (15.1) +namespace Throw { + struct S { + int n : (throw "hello", 10); // expected-error {{constant expression}} expected-note {{subexpression}} + }; +} // PR9999 template diff --git a/clang/test/SemaCXX/constant-expression-cxx11.cpp b/clang/test/SemaCXX/constant-expression-cxx11.cpp index a7a53e11d478..f284bd6133c3 100644 --- a/clang/test/SemaCXX/constant-expression-cxx11.cpp +++ b/clang/test/SemaCXX/constant-expression-cxx11.cpp @@ -10,7 +10,7 @@ static_assert(false, "test"); // expected-error {{test}} // FIXME: support const T& parameters here. //template constexpr T id(const T &t) { return t; } -template constexpr T id(T t) { return t; } +template constexpr T id(T t) { return t; } // expected-note {{here}} // FIXME: support templates here. //template constexpr T min(const T &a, const T &b) { // return a < b ? a : b; @@ -95,9 +95,9 @@ namespace CaseStatements { void f(int n) { switch (n) { // FIXME: Produce the 'add ()' fixit for this. - case MemberZero().zero: // desired-error {{did you mean to call it with no arguments?}} expected-error {{not an integer constant expression}} + case MemberZero().zero: // desired-error {{did you mean to call it with no arguments?}} expected-error {{not an integer constant expression}} expected-note {{non-literal type ''}} // FIXME: This should be accepted once we implement the new ICE rules. - case id(1): // expected-error {{not an integer constant expression}} + case id(1): // expected-error {{not an integer constant expression}} expected-note {{undefined function}} return; } } diff --git a/clang/test/SemaCXX/enum-bitfield.cpp b/clang/test/SemaCXX/enum-bitfield.cpp index 1a657408f8a6..8cfc21f96557 100644 --- a/clang/test/SemaCXX/enum-bitfield.cpp +++ b/clang/test/SemaCXX/enum-bitfield.cpp @@ -2,7 +2,7 @@ enum E {}; -struct Z {}; +struct Z {}; // expected-note {{here}} typedef int Integer; struct X { @@ -14,5 +14,5 @@ struct X { struct Y { enum E : int(2); - enum E : Z(); // expected-error{{not an integer constant}} + enum E : Z(); // expected-error{{not an integer constant}} expected-note {{non-constexpr constructor 'Z'}} };