[AST] Enhance the const expression evaluator to support error-dependent exprs.
Fix a crash when evaluating a constexpr function which contains recovery-exprs. https://bugs.llvm.org/show_bug.cgi?id=46837 Would be nice to have constant expression evaluator support general template value-dependent expressions, but it requires more work. This patch is a good start I think, to handle the error-only value-dependent expressions. Differential Revision: https://reviews.llvm.org/D84637
This commit is contained in:
parent
8270f8c252
commit
bd4662cd3f
|
@ -1942,6 +1942,7 @@ void CallStackFrame::describe(raw_ostream &Out) {
|
|||
/// 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 HandleConversionToBool(const APValue &Val, bool &Result) {
|
|||
|
||||
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 @@ static bool EvaluateVarDecl(EvalInfo &Info, const VarDecl *VD) {
|
|||
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 @@ static bool EvaluateDecl(EvalInfo &Info, const Decl *D) {
|
|||
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 @@ static EvalStmtResult EvaluateStmt(StmtResult &Result, EvalInfo &Info,
|
|||
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 @@ static EvalStmtResult EvaluateStmt(StmtResult &Result, EvalInfo &Info,
|
|||
switch (S->getStmtClass()) {
|
||||
default:
|
||||
if (const Expr *E = dyn_cast<Expr>(S)) {
|
||||
// Don't bother evaluating beyond an expression-statement which couldn't
|
||||
// be evaluated.
|
||||
// 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 @@ static EvalStmtResult EvaluateStmt(StmtResult &Result, EvalInfo &Info,
|
|||
case Stmt::ReturnStmtClass: {
|
||||
const Expr *RetExpr = cast<ReturnStmt>(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 @@ static EvalStmtResult EvaluateStmt(StmtResult &Result, EvalInfo &Info,
|
|||
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 @@ static EvalStmtResult EvaluateStmt(StmtResult &Result, EvalInfo &Info,
|
|||
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 @@ static EvalStmtResult EvaluateStmt(StmtResult &Result, EvalInfo &Info,
|
|||
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 @@ static EvalStmtResult EvaluateStmt(StmtResult &Result, EvalInfo &Info,
|
|||
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 @@ static bool CheckConstexprFunction(EvalInfo &Info, SourceLocation CallLoc,
|
|||
return false;
|
||||
}
|
||||
|
||||
if (const auto *CtorDecl = dyn_cast_or_null<CXXConstructorDecl>(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 @@ static bool HandleConstructorCall(const Expr *E, const LValue &This,
|
|||
// 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 @@ static bool HandleConstructorCall(const Expr *E, const LValue &This,
|
|||
// 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<CXXDefaultInitExpr>(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<CXXDefaultInitExpr>(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 @@ public:
|
|||
/// * @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<ObjCSelectorExpr>(E));
|
||||
return LValueExprEvaluator(Info, Result, InvalidBaseOK).Visit(E);
|
||||
|
@ -8617,6 +8664,7 @@ public:
|
|||
|
||||
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 @@ public:
|
|||
|
||||
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 @@ bool RecordExprEvaluator::VisitLambdaExpr(const LambdaExpr *E) {
|
|||
|
||||
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 @@ public:
|
|||
|
||||
/// 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 @@ namespace {
|
|||
|
||||
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 EvaluateArray(const Expr *E, const LValue &This,
|
|||
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 @@ static bool EvaluateArrayNewConstructExpr(EvalInfo &Info, LValue &This,
|
|||
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 @@ class FixedPointExprEvaluator
|
|||
/// 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 @@ bool IntExprEvaluator::VisitSourceLocExpr(const SourceLocExpr *E) {
|
|||
|
||||
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 EvaluateFixedPoint(const Expr *E, APFixedPoint &Result,
|
|||
|
||||
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 @@ template <class SuccessCB, class AfterCB>
|
|||
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 @@ public:
|
|||
} // 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 @@ public:
|
|||
|
||||
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 @@ public:
|
|||
|
||||
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 @@ bool VoidExprEvaluator::VisitCXXDeleteExpr(const CXXDeleteExpr *E) {
|
|||
}
|
||||
|
||||
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 EvaluateVoid(const Expr *E, EvalInfo &Info) {
|
|||
//===----------------------------------------------------------------------===//
|
||||
|
||||
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 @@ static bool EvaluateInPlace(APValue &Result, EvalInfo &Info, const LValue &This,
|
|||
/// 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 hasUnacceptableSideEffect(Expr::EvalStatus &Result,
|
|||
|
||||
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 @@ static bool EvaluateAsInt(const Expr *E, Expr::EvalResult &ExprResult,
|
|||
const ASTContext &Ctx,
|
||||
Expr::SideEffectsKind AllowSideEffects,
|
||||
EvalInfo &Info) {
|
||||
assert(!E->isValueDependent());
|
||||
if (!E->getType()->isIntegralOrEnumerationType())
|
||||
return false;
|
||||
|
||||
|
@ -14618,6 +14685,7 @@ static bool EvaluateAsFixedPoint(const Expr *E, Expr::EvalResult &ExprResult,
|
|||
const ASTContext &Ctx,
|
||||
Expr::SideEffectsKind AllowSideEffects,
|
||||
EvalInfo &Info) {
|
||||
assert(!E->isValueDependent());
|
||||
if (!E->getType()->isFixedPointType())
|
||||
return false;
|
||||
|
||||
|
@ -15553,15 +15621,6 @@ bool Expr::isPotentialConstantExpr(const FunctionDecl *FD,
|
|||
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<CXXConstructorDecl>(FD)) {
|
||||
for (const auto *InitExpr : Ctor->inits()) {
|
||||
if (InitExpr->getInit() && InitExpr->getInit()->containsErrors())
|
||||
return false;
|
||||
}
|
||||
}
|
||||
Expr::EvalStatus Status;
|
||||
Status.Diag = &Diags;
|
||||
|
||||
|
|
|
@ -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<int x> constexpr int f(int y) { // expected-note {{candidate template ignored}}
|
||||
return x * y;
|
||||
}
|
||||
constexpr int test9(int x) {
|
||||
return f<1>(f<x>(1)); // expected-error {{no matching function for call to 'f'}}
|
||||
}
|
|
@ -414,8 +414,8 @@ static_assert(templated<1>() == 1, "");
|
|||
|
||||
template <int N> constexpr int callTemplated() { return templated<N>(); }
|
||||
|
||||
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, "");
|
||||
}
|
||||
|
||||
|
|
|
@ -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 @@ constexpr int Z = X().Y; // expected-error {{constexpr variable 'Z' must be init
|
|||
|
||||
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'}}
|
||||
};
|
||||
|
||||
|
|
|
@ -53,14 +53,12 @@ struct AA {
|
|||
return 2;
|
||||
}
|
||||
static constexpr int foo2() {
|
||||
return AA<T>::getB(); // expected-error{{no matching function for call to 'getB'}} \
|
||||
// expected-note {{subexpression not valid in a constant expression}}
|
||||
return AA<T>::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<int>::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.
|
||||
|
|
Loading…
Reference in New Issue