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 @@ -1942,6 +1942,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. @@ -2497,6 +2498,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)) @@ -4795,9 +4797,11 @@ ScopeKind::Block, Result); const Expr *InitE = VD->getInit(); - if (!InitE) + if (!InitE) { + if (VD->getType()->isDependentType()) + return Info.noteSideEffect(); return getDefaultInitValue(VD->getType(), Val); - + } if (InitE->isValueDependent()) return false; @@ -4825,10 +4829,20 @@ return OK; } +static bool EvaluateDependentExpr(const Expr *E, EvalInfo &Info) { + assert(E->isValueDependent()); + if (Info.noteSideEffect()) + return true; + assert(E->containsErrors() && "valid value-dependent expression should never " + "reach invalid code path."); + return false; +} /// 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; @@ -5053,10 +5067,15 @@ EvaluateLoopBody(Result, Info, FS->getBody(), Case); if (ESR != ESR_Continue) return ESR; - if (FS->getInc()) { - FullExpressionRAII IncScope(Info); - if (!EvaluateIgnoredValue(Info, FS->getInc()) || !IncScope.destroy()) - return ESR_Failed; + if (const auto *Inc = FS->getInc()) { + if (Inc->isValueDependent()) { + if (!EvaluateDependentExpr(Inc, Info)) + return ESR_Failed; + } else { + FullExpressionRAII IncScope(Info); + if (!EvaluateIgnoredValue(Info, Inc) || !IncScope.destroy()) + return ESR_Failed; + } } break; } @@ -5086,13 +5105,18 @@ switch (S->getStmtClass()) { default: if (const Expr *E = dyn_cast(S)) { - // Don't bother evaluating beyond an expression-statement which couldn't - // be evaluated. - // FIXME: Do we need the FullExpressionRAII object here? - // VisitExprWithCleanups should create one when necessary. - FullExpressionRAII Scope(Info); - if (!EvaluateIgnoredValue(Info, E) || !Scope.destroy()) - return ESR_Failed; + if (E->isValueDependent()) { + if (!EvaluateDependentExpr(E, Info)) + return ESR_Failed; + } else { + // Don't bother evaluating beyond an expression-statement which couldn't + // be evaluated. + // FIXME: Do we need the FullExpressionRAII object here? + // VisitExprWithCleanups should create one when necessary. + FullExpressionRAII Scope(Info); + if (!EvaluateIgnoredValue(Info, E) || !Scope.destroy()) + return ESR_Failed; + } return ESR_Succeeded; } @@ -5118,6 +5142,8 @@ case Stmt::ReturnStmtClass: { const Expr *RetExpr = cast(S)->getRetValue(); FullExpressionRAII Scope(Info); + if (RetExpr && RetExpr->isValueDependent()) + return EvaluateDependentExpr(RetExpr, Info) ? ESR_Returned : ESR_Failed; if (RetExpr && !(Result.Slot ? EvaluateInPlace(Result.Value, Info, *Result.Slot, RetExpr) @@ -5205,6 +5231,11 @@ return ESR; Case = nullptr; + if (DS->getCond()->isValueDependent()) { + EvaluateDependentExpr(DS->getCond(), Info); + // Bailout as we don't know whether to keep going or terminate the loop. + return ESR_Failed; + } FullExpressionRAII CondScope(Info); if (!EvaluateAsBooleanCondition(DS->getCond(), Continue, Info) || !CondScope.destroy()) @@ -5240,10 +5271,15 @@ return ESR; } - if (FS->getInc()) { - FullExpressionRAII IncScope(Info); - if (!EvaluateIgnoredValue(Info, FS->getInc()) || !IncScope.destroy()) - return ESR_Failed; + if (const auto *Inc = FS->getInc()) { + if (Inc->isValueDependent()) { + if (!EvaluateDependentExpr(Inc, Info)) + return ESR_Failed; + } else { + FullExpressionRAII IncScope(Info); + if (!EvaluateIgnoredValue(Info, Inc) || !IncScope.destroy()) + return ESR_Failed; + } } if (!IterScope.destroy()) @@ -5291,6 +5327,11 @@ while (true) { // Condition: __begin != __end. { + if (FS->getCond()->isValueDependent()) { + EvaluateDependentExpr(FS->getCond(), Info); + // We don't know whether to keep going or terminate the loop. + return ESR_Failed; + } bool Continue = true; FullExpressionRAII CondExpr(Info); if (!EvaluateAsBooleanCondition(FS->getCond(), Continue, Info)) @@ -5315,10 +5356,14 @@ return ESR_Failed; return ESR; } - - // Increment: ++__begin - if (!EvaluateIgnoredValue(Info, FS->getInc())) - return ESR_Failed; + if (FS->getInc()->isValueDependent()) { + if (!EvaluateDependentExpr(FS->getInc(), Info)) + return ESR_Failed; + } else { + // Increment: ++__begin + if (!EvaluateIgnoredValue(Info, FS->getInc())) + return ESR_Failed; + } if (!InnerScope.destroy()) return ESR_Failed; @@ -5413,13 +5458,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; @@ -6107,7 +6145,10 @@ // If it's a delegating constructor, delegate. if (Definition->isDelegatingConstructor()) { CXXConstructorDecl::init_const_iterator I = Definition->init_begin(); - { + if ((*I)->getInit()->isValueDependent()) { + if (!EvaluateDependentExpr((*I)->getInit(), Info)) + return false; + } else { FullExpressionRAII InitScope(Info); if (!EvaluateInPlace(Result, Info, This, (*I)->getInit()) || !InitScope.destroy()) @@ -6248,17 +6289,22 @@ // This refers to innermost anonymous struct/union containing initializer, // not to currently constructed class. const Expr *Init = I->getInit(); - ThisOverrideRAII ThisOverride(*Info.CurrentCall, &SubobjectParent, - isa(Init)); - FullExpressionRAII InitScope(Info); - if (!EvaluateInPlace(*Value, Info, Subobject, Init) || - (FD && FD->isBitField() && - !truncateBitfieldValue(Info, Init, *Value, FD))) { - // If we're checking for a potential constant expression, evaluate all - // initializers even if some of them fail. - if (!Info.noteFailure()) + if (Init->isValueDependent()) { + if (!EvaluateDependentExpr(Init, Info)) return false; - Success = false; + } else { + ThisOverrideRAII ThisOverride(*Info.CurrentCall, &SubobjectParent, + isa(Init)); + FullExpressionRAII InitScope(Info); + if (!EvaluateInPlace(*Value, Info, Subobject, Init) || + (FD && FD->isBitField() && + !truncateBitfieldValue(Info, Init, *Value, FD))) { + // If we're checking for a potential constant expression, evaluate all + // initializers even if some of them fail. + if (!Info.noteFailure()) + return false; + Success = false; + } } // This is the point at which the dynamic type of the object becomes this @@ -8057,6 +8103,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); @@ -8617,6 +8664,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); } @@ -9504,6 +9552,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); } @@ -9980,6 +10029,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); @@ -10035,6 +10085,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); } @@ -10321,6 +10372,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); } @@ -10328,6 +10380,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) @@ -10338,6 +10391,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) @@ -10715,11 +10769,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; @@ -10741,6 +10797,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)) @@ -10756,6 +10813,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; @@ -12423,6 +12481,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()) && @@ -13505,6 +13564,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); } @@ -13757,6 +13817,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); } @@ -14283,6 +14344,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); } @@ -14407,6 +14469,7 @@ } static bool EvaluateVoid(const Expr *E, EvalInfo &Info) { + assert(!E->isValueDependent()); assert(E->isRValue() && E->getType()->isVoidType()); return VoidExprEvaluator(Info).Visit(E); } @@ -14416,6 +14479,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(); @@ -14528,6 +14592,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; @@ -14592,6 +14657,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; @@ -14603,6 +14669,7 @@ const ASTContext &Ctx, Expr::SideEffectsKind AllowSideEffects, EvalInfo &Info) { + assert(!E->isValueDependent()); if (!E->getType()->isIntegralOrEnumerationType()) return false; @@ -14618,6 +14685,7 @@ const ASTContext &Ctx, Expr::SideEffectsKind AllowSideEffects, EvalInfo &Info) { + assert(!E->isValueDependent()); if (!E->getType()->isFixedPointType()) return false; @@ -15553,15 +15621,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,68 @@ +// RUN: %clang_cc1 %s -std=c++20 -fsyntax-only -fcxx-exceptions -verify + +// verify no value-dependent-assertion crash in constexpr function body and no +// bogus diagnostics. +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() { // expected-error {{constexpr function never produce}} + for (;; a++); // expected-error {{use of undeclared identifier}} \ + expected-note {{constexpr evaluation hit maximum step limit; possible infinite loop?}} + return 1; +} + +constexpr int test6() { // expected-error {{constexpr function never produce}} + int n = 0; + switch (n) { + for (;; a++) { // expected-error {{use of undeclared identifier}} + case 0:; // expected-note {{constexpr evaluation hit maximum step limit; possible infinite loop?}} + } + } + return 0; +} + +constexpr bool test7() { + for (int n = 0; ; invalid()) { if (n == 1) return true; } // expected-error {{use of undeclared identifier}} + throw "bad"; +} + +constexpr void test8() { + do {} while (invalid()); // expected-error {{use of undeclared identifier}} + throw "bad"; +} + +template constexpr int f(int y) { // expected-note {{candidate template ignored}} + return x * y; +} +constexpr int test9(int x) { + return f<1>(f(1)); // expected-error {{no matching function for call to 'f'}} +} 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.