diff --git a/clang/lib/AST/ExprConstant.cpp b/clang/lib/AST/ExprConstant.cpp --- a/clang/lib/AST/ExprConstant.cpp +++ b/clang/lib/AST/ExprConstant.cpp @@ -1870,6 +1870,7 @@ /// result. /// \return \c true if the caller should keep evaluating. static bool EvaluateIgnoredValue(EvalInfo &Info, const Expr *E) { + assert(!E->isValueDependent()); APValue Scratch; if (!Evaluate(Scratch, Info, E)) // We don't need the value, but we might have skipped a side effect here. @@ -2381,6 +2382,7 @@ static bool EvaluateAsBooleanCondition(const Expr *E, bool &Result, EvalInfo &Info) { + assert(!E->isValueDependent()); assert(E->isRValue() && "missing lvalue-to-rvalue conv in bool condition"); APValue Val; if (!Evaluate(Val, Info, E)) @@ -4590,9 +4592,11 @@ Info.CurrentCall->createTemporary(VD, VD->getType(), true, Result); const Expr *InitE = VD->getInit(); - if (!InitE) + if (!InitE) { + if (VD->getType()->isDependentType()) + return false; return getDefaultInitValue(VD->getType(), Val); - + } if (InitE->isValueDependent()) return false; @@ -4620,10 +4624,20 @@ return OK; } +static EvalStmtResult EvaluateDependentExpr(const Expr *E, EvalInfo &Info) { + assert(E->isValueDependent()); + if (Info.noteSideEffect()) + return ESR_Succeeded; + assert(E->containsErrors() && "valid value-dependent expression should never " + "reach invalid code path."); + return ESR_Failed; +} /// Evaluate a condition (either a variable declaration or an expression). static bool EvaluateCond(EvalInfo &Info, const VarDecl *CondDecl, const Expr *Cond, bool &Result) { + if (Cond->isValueDependent()) + return false; FullExpressionRAII Scope(Info); if (CondDecl && !EvaluateDecl(Info, CondDecl)) return false; @@ -4848,9 +4862,11 @@ EvaluateLoopBody(Result, Info, FS->getBody(), Case); if (ESR != ESR_Continue) return ESR; - if (FS->getInc()) { + if (const auto *Inc = FS->getInc()) { + if (Inc->isValueDependent()) + return EvaluateDependentExpr(Inc, Info); FullExpressionRAII IncScope(Info); - if (!EvaluateIgnoredValue(Info, FS->getInc()) || !IncScope.destroy()) + if (!EvaluateIgnoredValue(Info, Inc) || !IncScope.destroy()) return ESR_Failed; } break; @@ -4881,6 +4897,8 @@ switch (S->getStmtClass()) { default: if (const Expr *E = dyn_cast(S)) { + if (E->isValueDependent()) + return EvaluateDependentExpr(E, Info); // Don't bother evaluating beyond an expression-statement which couldn't // be evaluated. // FIXME: Do we need the FullExpressionRAII object here? @@ -4913,6 +4931,10 @@ case Stmt::ReturnStmtClass: { const Expr *RetExpr = cast(S)->getRetValue(); FullExpressionRAII Scope(Info); + if (RetExpr && RetExpr->isValueDependent()) + return EvaluateDependentExpr(RetExpr, Info) == ESR_Succeeded + ? ESR_Returned + : ESR_Failed; if (RetExpr && !(Result.Slot ? EvaluateInPlace(Result.Value, Info, *Result.Slot, RetExpr) @@ -5000,6 +5022,8 @@ return ESR; Case = nullptr; + if (DS->getCond()->isValueDependent()) + return EvaluateDependentExpr(DS->getCond(), Info); FullExpressionRAII CondScope(Info); if (!EvaluateAsBooleanCondition(DS->getCond(), Continue, Info) || !CondScope.destroy()) @@ -5035,9 +5059,11 @@ return ESR; } - if (FS->getInc()) { + if (const auto *Inc = FS->getInc()) { + if (Inc->isValueDependent()) + return EvaluateDependentExpr(Inc, Info); FullExpressionRAII IncScope(Info); - if (!EvaluateIgnoredValue(Info, FS->getInc()) || !IncScope.destroy()) + if (!EvaluateIgnoredValue(Info, Inc) || !IncScope.destroy()) return ESR_Failed; } @@ -5086,6 +5112,8 @@ while (true) { // Condition: __begin != __end. { + if (FS->getCond()->isValueDependent()) + return EvaluateDependentExpr(FS->getCond(), Info); bool Continue = true; FullExpressionRAII CondExpr(Info); if (!EvaluateAsBooleanCondition(FS->getCond(), Continue, Info)) @@ -5110,7 +5138,8 @@ return ESR_Failed; return ESR; } - + if (FS->getInc()->isValueDependent()) + return EvaluateDependentExpr(FS->getInc(), Info); // Increment: ++__begin if (!EvaluateIgnoredValue(Info, FS->getInc())) return ESR_Failed; @@ -5208,13 +5237,6 @@ return false; } - if (const auto *CtorDecl = dyn_cast_or_null(Definition)) { - for (const auto *InitExpr : CtorDecl->inits()) { - if (InitExpr->getInit() && InitExpr->getInit()->containsErrors()) - return false; - } - } - // Can we evaluate this function call? if (Definition && Definition->isConstexpr() && Body) return true; @@ -5871,6 +5893,8 @@ // If it's a delegating constructor, delegate. if (Definition->isDelegatingConstructor()) { CXXConstructorDecl::init_const_iterator I = Definition->init_begin(); + if ((*I)->getInit()->isValueDependent()) + return EvaluateDependentExpr((*I)->getInit(), Info); { FullExpressionRAII InitScope(Info); if (!EvaluateInPlace(Result, Info, This, (*I)->getInit()) || @@ -6015,6 +6039,8 @@ // This refers to innermost anonymous struct/union containing initializer, // not to currently constructed class. const Expr *Init = I->getInit(); + if (Init->isValueDependent()) + return EvaluateDependentExpr(Init, Info); ThisOverrideRAII ThisOverride(*Info.CurrentCall, &SubobjectParent, isa(Init)); FullExpressionRAII InitScope(Info); @@ -7799,6 +7825,7 @@ /// * @selector() expressions in Objective-C static bool EvaluateLValue(const Expr *E, LValue &Result, EvalInfo &Info, bool InvalidBaseOK) { + assert(!E->isValueDependent()); assert(E->isGLValue() || E->getType()->isFunctionType() || E->getType()->isVoidType() || isa(E)); return LValueExprEvaluator(Info, Result, InvalidBaseOK).Visit(E); @@ -8329,6 +8356,7 @@ static bool EvaluatePointer(const Expr* E, LValue& Result, EvalInfo &Info, bool InvalidBaseOK) { + assert(!E->isValueDependent()); assert(E->isRValue() && E->getType()->hasPointerRepresentation()); return PointerExprEvaluator(Info, Result, InvalidBaseOK).Visit(E); } @@ -9216,6 +9244,7 @@ static bool EvaluateMemberPointer(const Expr *E, MemberPtr &Result, EvalInfo &Info) { + assert(!E->isValueDependent()); assert(E->isRValue() && E->getType()->isMemberPointerType()); return MemberPointerExprEvaluator(Info, Result).Visit(E); } @@ -9690,6 +9719,7 @@ static bool EvaluateRecord(const Expr *E, const LValue &This, APValue &Result, EvalInfo &Info) { + assert(!E->isValueDependent()); assert(E->isRValue() && E->getType()->isRecordType() && "can't evaluate expression as a record rvalue"); return RecordExprEvaluator(Info, This, Result).Visit(E); @@ -9745,6 +9775,7 @@ /// Evaluate an expression of record type as a temporary. static bool EvaluateTemporary(const Expr *E, LValue &Result, EvalInfo &Info) { + assert(!E->isValueDependent()); assert(E->isRValue() && E->getType()->isRecordType()); return TemporaryExprEvaluator(Info, Result).Visit(E); } @@ -10031,6 +10062,7 @@ static bool EvaluateArray(const Expr *E, const LValue &This, APValue &Result, EvalInfo &Info) { + assert(!E->isValueDependent()); assert(E->isRValue() && E->getType()->isArrayType() && "not an array rvalue"); return ArrayExprEvaluator(Info, This, Result).Visit(E); } @@ -10038,6 +10070,7 @@ static bool EvaluateArrayNewInitList(EvalInfo &Info, LValue &This, APValue &Result, const InitListExpr *ILE, QualType AllocType) { + assert(!ILE->isValueDependent()); assert(ILE->isRValue() && ILE->getType()->isArrayType() && "not an array rvalue"); return ArrayExprEvaluator(Info, This, Result) @@ -10048,6 +10081,7 @@ APValue &Result, const CXXConstructExpr *CCE, QualType AllocType) { + assert(!CCE->isValueDependent()); assert(CCE->isRValue() && CCE->getType()->isArrayType() && "not an array rvalue"); return ArrayExprEvaluator(Info, This, Result) @@ -10425,11 +10459,13 @@ /// like char*). static bool EvaluateIntegerOrLValue(const Expr *E, APValue &Result, EvalInfo &Info) { + assert(!E->isValueDependent()); assert(E->isRValue() && E->getType()->isIntegralOrEnumerationType()); return IntExprEvaluator(Info, Result).Visit(E); } static bool EvaluateInteger(const Expr *E, APSInt &Result, EvalInfo &Info) { + assert(!E->isValueDependent()); APValue Val; if (!EvaluateIntegerOrLValue(E, Val, Info)) return false; @@ -10451,6 +10487,7 @@ static bool EvaluateFixedPoint(const Expr *E, APFixedPoint &Result, EvalInfo &Info) { + assert(!E->isValueDependent()); if (E->getType()->isFixedPointType()) { APValue Val; if (!FixedPointExprEvaluator(Info, Val).Visit(E)) @@ -10466,6 +10503,7 @@ static bool EvaluateFixedPointOrInteger(const Expr *E, APFixedPoint &Result, EvalInfo &Info) { + assert(!E->isValueDependent()); if (E->getType()->isIntegerType()) { auto FXSema = Info.Ctx.getFixedPointSemantics(E->getType()); APSInt Val; @@ -12130,6 +12168,7 @@ static bool EvaluateComparisonBinaryOperator(EvalInfo &Info, const BinaryOperator *E, SuccessCB &&Success, AfterCB &&DoAfter) { + assert(!E->isValueDependent()); assert(E->isComparisonOp() && "expected comparison operator"); assert((E->getOpcode() == BO_Cmp || E->getType()->isIntegralOrEnumerationType()) && @@ -13181,6 +13220,7 @@ } // end anonymous namespace static bool EvaluateFloat(const Expr* E, APFloat& Result, EvalInfo &Info) { + assert(!E->isValueDependent()); assert(E->isRValue() && E->getType()->isRealFloatingType()); return FloatExprEvaluator(Info, Result).Visit(E); } @@ -13414,6 +13454,7 @@ static bool EvaluateComplex(const Expr *E, ComplexValue &Result, EvalInfo &Info) { + assert(!E->isValueDependent()); assert(E->isRValue() && E->getType()->isAnyComplexType()); return ComplexExprEvaluator(Info, Result).Visit(E); } @@ -13936,6 +13977,7 @@ static bool EvaluateAtomic(const Expr *E, const LValue *This, APValue &Result, EvalInfo &Info) { + assert(!E->isValueDependent()); assert(E->isRValue() && E->getType()->isAtomicType()); return AtomicExprEvaluator(Info, This, Result).Visit(E); } @@ -14060,6 +14102,7 @@ } static bool EvaluateVoid(const Expr *E, EvalInfo &Info) { + assert(!E->isValueDependent()); assert(E->isRValue() && E->getType()->isVoidType()); return VoidExprEvaluator(Info).Visit(E); } @@ -14069,6 +14112,7 @@ //===----------------------------------------------------------------------===// static bool Evaluate(APValue &Result, EvalInfo &Info, const Expr *E) { + assert(!E->isValueDependent()); // In C, function designators are not lvalues, but we evaluate them as if they // are. QualType T = E->getType(); @@ -14179,6 +14223,7 @@ /// EvaluateAsRValue - Try to evaluate this expression, performing an implicit /// lvalue-to-rvalue cast if it is an lvalue. static bool EvaluateAsRValue(EvalInfo &Info, const Expr *E, APValue &Result) { + assert(!E->isValueDependent()); if (Info.EnableNewConstInterp) { if (!Info.Ctx.getInterpContext().evaluateAsRValue(Info, E, Result)) return false; @@ -14242,6 +14287,7 @@ static bool EvaluateAsRValue(const Expr *E, Expr::EvalResult &Result, const ASTContext &Ctx, EvalInfo &Info) { + assert(!E->isValueDependent()); bool IsConst; if (FastEvaluateAsRValue(E, Result, Ctx, IsConst)) return IsConst; @@ -14253,6 +14299,7 @@ const ASTContext &Ctx, Expr::SideEffectsKind AllowSideEffects, EvalInfo &Info) { + assert(!E->isValueDependent()); if (!E->getType()->isIntegralOrEnumerationType()) return false; @@ -14268,6 +14315,7 @@ const ASTContext &Ctx, Expr::SideEffectsKind AllowSideEffects, EvalInfo &Info) { + assert(!E->isValueDependent()); if (!E->getType()->isFixedPointType()) return false; @@ -15175,15 +15223,6 @@ if (FD->isDependentContext()) return true; - // Bail out if a constexpr constructor has an initializer that contains an - // error. We deliberately don't produce a diagnostic, as we have produced a - // relevant diagnostic when parsing the error initializer. - if (const auto *Ctor = dyn_cast(FD)) { - for (const auto *InitExpr : Ctor->inits()) { - if (InitExpr->getInit() && InitExpr->getInit()->containsErrors()) - return false; - } - } Expr::EvalStatus Status; Status.Diag = &Diags; diff --git a/clang/test/SemaCXX/constexpr-function-recovery-crash.cpp b/clang/test/SemaCXX/constexpr-function-recovery-crash.cpp new file mode 100644 --- /dev/null +++ b/clang/test/SemaCXX/constexpr-function-recovery-crash.cpp @@ -0,0 +1,49 @@ +// RUN: %clang_cc1 %s -std=c++20 -fsyntax-only -verify + +// verify no value-dependent-assertion crash in constexpr function body. +class Foo { + constexpr Foo() { + while (invalid()) {} // expected-error {{use of undeclared identifier}} + if (invalid()) {} // expected-error {{use of undeclared identifier}} + } +}; + +constexpr void test1() { + while (invalid()) {} // expected-error {{use of undeclared identifier}} + if (invalid()) {} // expected-error {{use of undeclared identifier}} +} + +struct A { + int *p = new int(invalid()); // expected-error {{use of undeclared identifier}} + constexpr ~A() { delete p; } +}; +constexpr int test2() { + A a; + return 1; +} + +constexpr int test3() { + return invalid(); // expected-error {{use of undeclared identifier}} +} + +constexpr int test4() { + if (invalid()) // expected-error {{use of undeclared identifier}} + return 1; + else + return 0; +} + +constexpr int test5() { + for (;; a++); // expected-error {{use of undeclared identifier}} + return 1; +} + +constexpr int test6() { + int n = 0; + switch (n) { + for (;; a++) { // expected-error {{use of undeclared identifier}} + case 0:; + } + } + return 0; +} diff --git a/clang/test/SemaCXX/enable_if.cpp b/clang/test/SemaCXX/enable_if.cpp --- a/clang/test/SemaCXX/enable_if.cpp +++ b/clang/test/SemaCXX/enable_if.cpp @@ -414,8 +414,8 @@ template constexpr int callTemplated() { return templated(); } -constexpr int B = 10 + // expected-error {{initialized by a constant expression}} - callTemplated<0>(); // expected-error@-3{{no matching function for call to 'templated'}} expected-note{{in instantiation of function template}} expected-note@-10{{candidate disabled}} expected-note {{in call to 'callTemplated()'}} expected-note@-3 {{subexpression not valid in a constant expression}} +constexpr int B = 10 + // expected-error {{initialized by a constant expression}} + callTemplated<0>(); // expected-error@-3{{no matching function for call to 'templated'}} expected-note{{in instantiation of function template}} expected-note@-10{{candidate disabled}} static_assert(callTemplated<1>() == 1, ""); } diff --git a/clang/test/SemaCXX/invalid-constructor-init.cpp b/clang/test/SemaCXX/invalid-constructor-init.cpp --- a/clang/test/SemaCXX/invalid-constructor-init.cpp +++ b/clang/test/SemaCXX/invalid-constructor-init.cpp @@ -2,7 +2,7 @@ struct X { int Y; - constexpr X() // expected-error {{constexpr constructor never produces}} + constexpr X() : Y(foo()) {} // expected-error {{use of undeclared identifier 'foo'}} }; // no crash on evaluating the constexpr ctor. @@ -10,12 +10,12 @@ struct X2 { int Y = foo(); // expected-error {{use of undeclared identifier 'foo'}} - constexpr X2() {} // expected-error {{constexpr constructor never produces a constant expression}} + constexpr X2() {} }; struct X3 { int Y; - constexpr X3() // expected-error {{constexpr constructor never produces}} + constexpr X3() : Y(({foo();})) {} // expected-error {{use of undeclared identifier 'foo'}} }; diff --git a/clang/test/SemaCXX/recovery-expr-type.cpp b/clang/test/SemaCXX/recovery-expr-type.cpp --- a/clang/test/SemaCXX/recovery-expr-type.cpp +++ b/clang/test/SemaCXX/recovery-expr-type.cpp @@ -53,14 +53,12 @@ return 2; } static constexpr int foo2() { - return AA::getB(); // expected-error{{no matching function for call to 'getB'}} \ - // expected-note {{subexpression not valid in a constant expression}} + return AA::getB(); // expected-error{{no matching function for call to 'getB'}} } }; // FIXME: should we suppress the "be initialized by a constant expression" diagnostic? constexpr auto x2 = AA::foo2(); // expected-error {{be initialized by a constant expression}} \ - // expected-note {{in instantiation of member function}} \ - // expected-note {{in call to}} + // expected-note {{in instantiation of member function}} } // verify no assertion failure on violating value category.