Skip to content

Commit c72d3a0

Browse files
authored
[Clang] Handle consteval expression in array bounds expressions (#66222)
The bounds of a c++ array is a _constant-expression_. And in C++ it is also a constant expression. But we also support VLAs, ie arrays with non-constant bounds. We need to take care to handle the case of a consteval function (which are specified to be only immediately called in non-constant contexts) that appear in arrays bounds. This introduces `Sema::isAlwayConstantEvaluatedContext`, and a flag in ExpressionEvaluationContextRecord, such that immediate functions in array bounds are always immediately invoked. Sema had both `isConstantEvaluatedContext` and `isConstantEvaluated`, so I took the opportunity to cleanup that. The change in `TimeProfilerTest.cpp` is an unfortunate manifestation of the problem that #66203 seeks to address. Fixes #65520
1 parent c64a098 commit c72d3a0

File tree

12 files changed

+113
-63
lines changed

12 files changed

+113
-63
lines changed

clang/docs/ReleaseNotes.rst

+3
Original file line numberDiff line numberDiff line change
@@ -434,6 +434,9 @@ Bug Fixes to C++ Support
434434
- Fix crash caused by a spaceship operator returning a comparision category by
435435
reference. Fixes:
436436
(`#64162 <https://github.com/llvm/llvm-project/issues/64162>`_)
437+
- Fix a crash when calling a consteval function in an expression used as
438+
the size of an array.
439+
(`#65520 <https://github.com/llvm/llvm-project/issues/65520>`_)
437440

438441
- Clang no longer tries to capture non-odr-used variables that appear
439442
in the enclosing expression of a lambda expression with a noexcept specifier.

clang/include/clang/Parse/Parser.h

+1
Original file line numberDiff line numberDiff line change
@@ -1766,6 +1766,7 @@ class Parser : public CodeCompletionHandler {
17661766
ExprResult ParseConstantExpressionInExprEvalContext(
17671767
TypeCastState isTypeCast = NotTypeCast);
17681768
ExprResult ParseConstantExpression();
1769+
ExprResult ParseArrayBoundExpression();
17691770
ExprResult ParseCaseExpression(SourceLocation CaseLoc);
17701771
ExprResult ParseConstraintExpression();
17711772
ExprResult

clang/include/clang/Sema/Sema.h

+33-28
Original file line numberDiff line numberDiff line change
@@ -1062,20 +1062,6 @@ class Sema final {
10621062
}
10631063
};
10641064

1065-
/// Whether the AST is currently being rebuilt to correct immediate
1066-
/// invocations. Immediate invocation candidates and references to consteval
1067-
/// functions aren't tracked when this is set.
1068-
bool RebuildingImmediateInvocation = false;
1069-
1070-
/// Used to change context to isConstantEvaluated without pushing a heavy
1071-
/// ExpressionEvaluationContextRecord object.
1072-
bool isConstantEvaluatedOverride;
1073-
1074-
bool isConstantEvaluated() const {
1075-
return ExprEvalContexts.back().isConstantEvaluated() ||
1076-
isConstantEvaluatedOverride;
1077-
}
1078-
10791065
/// RAII object to handle the state changes required to synthesize
10801066
/// a function body.
10811067
class SynthesizedFunctionScope {
@@ -1361,6 +1347,11 @@ class Sema final {
13611347

13621348
bool IsCurrentlyCheckingDefaultArgumentOrInitializer = false;
13631349

1350+
// We are in a constant context, but we also allow
1351+
// non constant expressions, for example for array bounds (which may be
1352+
// VLAs).
1353+
bool InConditionallyConstantEvaluateContext = false;
1354+
13641355
// When evaluating immediate functions in the initializer of a default
13651356
// argument or default member initializer, this is the declaration whose
13661357
// default initializer is being evaluated and the location of the call
@@ -9878,30 +9869,44 @@ class Sema final {
98789869
/// diagnostics that will be suppressed.
98799870
std::optional<sema::TemplateDeductionInfo *> isSFINAEContext() const;
98809871

9881-
/// Determines whether we are currently in a context that
9882-
/// is not evaluated as per C++ [expr] p5.
9883-
bool isUnevaluatedContext() const {
9872+
/// Whether the AST is currently being rebuilt to correct immediate
9873+
/// invocations. Immediate invocation candidates and references to consteval
9874+
/// functions aren't tracked when this is set.
9875+
bool RebuildingImmediateInvocation = false;
9876+
9877+
/// Used to change context to isConstantEvaluated without pushing a heavy
9878+
/// ExpressionEvaluationContextRecord object.
9879+
bool isConstantEvaluatedOverride = false;
9880+
9881+
const ExpressionEvaluationContextRecord &currentEvaluationContext() const {
98849882
assert(!ExprEvalContexts.empty() &&
98859883
"Must be in an expression evaluation context");
9886-
return ExprEvalContexts.back().isUnevaluated();
9887-
}
9884+
return ExprEvalContexts.back();
9885+
};
98889886

98899887
bool isConstantEvaluatedContext() const {
9890-
assert(!ExprEvalContexts.empty() &&
9891-
"Must be in an expression evaluation context");
9892-
return ExprEvalContexts.back().isConstantEvaluated();
9888+
return currentEvaluationContext().isConstantEvaluated() ||
9889+
isConstantEvaluatedOverride;
9890+
}
9891+
9892+
bool isAlwaysConstantEvaluatedContext() const {
9893+
const ExpressionEvaluationContextRecord &Ctx = currentEvaluationContext();
9894+
return (Ctx.isConstantEvaluated() || isConstantEvaluatedOverride) &&
9895+
!Ctx.InConditionallyConstantEvaluateContext;
9896+
}
9897+
9898+
/// Determines whether we are currently in a context that
9899+
/// is not evaluated as per C++ [expr] p5.
9900+
bool isUnevaluatedContext() const {
9901+
return currentEvaluationContext().isUnevaluated();
98939902
}
98949903

98959904
bool isImmediateFunctionContext() const {
9896-
assert(!ExprEvalContexts.empty() &&
9897-
"Must be in an expression evaluation context");
9898-
return ExprEvalContexts.back().isImmediateFunctionContext();
9905+
return currentEvaluationContext().isImmediateFunctionContext();
98999906
}
99009907

99019908
bool isCheckingDefaultArgumentOrInitializer() const {
9902-
assert(!ExprEvalContexts.empty() &&
9903-
"Must be in an expression evaluation context");
9904-
const ExpressionEvaluationContextRecord &Ctx = ExprEvalContexts.back();
9909+
const ExpressionEvaluationContextRecord &Ctx = currentEvaluationContext();
99059910
return (Ctx.Context ==
99069911
ExpressionEvaluationContext::PotentiallyEvaluatedIfUsed) ||
99079912
Ctx.IsCurrentlyCheckingDefaultArgumentOrInitializer;

clang/lib/Parse/ParseDecl.cpp

+1-1
Original file line numberDiff line numberDiff line change
@@ -7703,7 +7703,7 @@ void Parser::ParseBracketDeclarator(Declarator &D) {
77037703
// Parse the constant-expression or assignment-expression now (depending
77047704
// on dialect).
77057705
if (getLangOpts().CPlusPlus) {
7706-
NumElements = ParseConstantExpression();
7706+
NumElements = ParseArrayBoundExpression();
77077707
} else {
77087708
EnterExpressionEvaluationContext Unevaluated(
77097709
Actions, Sema::ExpressionEvaluationContext::ConstantEvaluated);

clang/lib/Parse/ParseExpr.cpp

+9
Original file line numberDiff line numberDiff line change
@@ -221,6 +221,15 @@ ExprResult Parser::ParseConstantExpression() {
221221
return ParseConstantExpressionInExprEvalContext(NotTypeCast);
222222
}
223223

224+
ExprResult Parser::ParseArrayBoundExpression() {
225+
EnterExpressionEvaluationContext ConstantEvaluated(
226+
Actions, Sema::ExpressionEvaluationContext::ConstantEvaluated);
227+
// If we parse the bound of a VLA... we parse a non-constant
228+
// constant-expression!
229+
Actions.ExprEvalContexts.back().InConditionallyConstantEvaluateContext = true;
230+
return ParseConstantExpressionInExprEvalContext(NotTypeCast);
231+
}
232+
224233
ExprResult Parser::ParseCaseExpression(SourceLocation CaseLoc) {
225234
EnterExpressionEvaluationContext ConstantEvaluated(
226235
Actions, Sema::ExpressionEvaluationContext::ConstantEvaluated);

clang/lib/Sema/Sema.cpp

-1
Original file line numberDiff line numberDiff line change
@@ -221,7 +221,6 @@ Sema::Sema(Preprocessor &pp, ASTContext &ctxt, ASTConsumer &consumer,
221221
CurScope(nullptr), Ident_super(nullptr) {
222222
assert(pp.TUKind == TUKind);
223223
TUScope = nullptr;
224-
isConstantEvaluatedOverride = false;
225224

226225
LoadedExternalKnownNamespaces = false;
227226
for (unsigned I = 0; I != NSAPI::NumNSNumberLiteralMethods; ++I)

clang/lib/Sema/SemaCUDA.cpp

+1-1
Original file line numberDiff line numberDiff line change
@@ -812,7 +812,7 @@ bool Sema::CheckCUDACall(SourceLocation Loc, FunctionDecl *Callee) {
812812
assert(getLangOpts().CUDA && "Should only be called during CUDA compilation");
813813
assert(Callee && "Callee may not be null.");
814814

815-
auto &ExprEvalCtx = ExprEvalContexts.back();
815+
const auto &ExprEvalCtx = currentEvaluationContext();
816816
if (ExprEvalCtx.isUnevaluated() || ExprEvalCtx.isConstantEvaluated())
817817
return true;
818818

clang/lib/Sema/SemaChecking.cpp

+31-26
Original file line numberDiff line numberDiff line change
@@ -1076,7 +1076,7 @@ static bool ProcessFormatStringLiteral(const Expr *FormatExpr,
10761076
void Sema::checkFortifiedBuiltinMemoryFunction(FunctionDecl *FD,
10771077
CallExpr *TheCall) {
10781078
if (TheCall->isValueDependent() || TheCall->isTypeDependent() ||
1079-
isConstantEvaluated())
1079+
isConstantEvaluatedContext())
10801080
return;
10811081

10821082
bool UseDABAttr = false;
@@ -3192,7 +3192,7 @@ bool Sema::CheckCDEBuiltinFunctionCall(const TargetInfo &TI, unsigned BuiltinID,
31923192

31933193
bool Sema::CheckARMCoprocessorImmediate(const TargetInfo &TI,
31943194
const Expr *CoprocArg, bool WantCDE) {
3195-
if (isConstantEvaluated())
3195+
if (isConstantEvaluatedContext())
31963196
return false;
31973197

31983198
// We can't check the value of a dependent argument.
@@ -6616,7 +6616,7 @@ static void CheckNonNullArguments(Sema &S,
66166616
assert((FDecl || Proto) && "Need a function declaration or prototype");
66176617

66186618
// Already checked by constant evaluator.
6619-
if (S.isConstantEvaluated())
6619+
if (S.isConstantEvaluatedContext())
66206620
return;
66216621
// Check the attributes attached to the method/function itself.
66226622
llvm::SmallBitVector NonNullArgs;
@@ -8980,7 +8980,7 @@ bool Sema::SemaBuiltinConstantArg(CallExpr *TheCall, int ArgNum,
89808980
/// TheCall is a constant expression in the range [Low, High].
89818981
bool Sema::SemaBuiltinConstantArgRange(CallExpr *TheCall, int ArgNum,
89828982
int Low, int High, bool RangeIsError) {
8983-
if (isConstantEvaluated())
8983+
if (isConstantEvaluatedContext())
89848984
return false;
89858985
llvm::APSInt Result;
89868986

@@ -9694,7 +9694,7 @@ checkFormatStringExpr(Sema &S, const Expr *E, ArrayRef<const Expr *> Args,
96949694
llvm::SmallBitVector &CheckedVarArgs,
96959695
UncoveredArgHandler &UncoveredArg, llvm::APSInt Offset,
96969696
bool IgnoreStringsWithoutSpecifiers = false) {
9697-
if (S.isConstantEvaluated())
9697+
if (S.isConstantEvaluatedContext())
96989698
return SLCT_NotALiteral;
96999699
tryAgain:
97009700
assert(Offset.isSigned() && "invalid offset");
@@ -9734,8 +9734,8 @@ checkFormatStringExpr(Sema &S, const Expr *E, ArrayRef<const Expr *> Args,
97349734
bool CheckLeft = true, CheckRight = true;
97359735

97369736
bool Cond;
9737-
if (C->getCond()->EvaluateAsBooleanCondition(Cond, S.getASTContext(),
9738-
S.isConstantEvaluated())) {
9737+
if (C->getCond()->EvaluateAsBooleanCondition(
9738+
Cond, S.getASTContext(), S.isConstantEvaluatedContext())) {
97399739
if (Cond)
97409740
CheckRight = false;
97419741
else
@@ -9999,9 +9999,11 @@ checkFormatStringExpr(Sema &S, const Expr *E, ArrayRef<const Expr *> Args,
99999999
Expr::EvalResult LResult, RResult;
1000010000

1000110001
bool LIsInt = BinOp->getLHS()->EvaluateAsInt(
10002-
LResult, S.Context, Expr::SE_NoSideEffects, S.isConstantEvaluated());
10002+
LResult, S.Context, Expr::SE_NoSideEffects,
10003+
S.isConstantEvaluatedContext());
1000310004
bool RIsInt = BinOp->getRHS()->EvaluateAsInt(
10004-
RResult, S.Context, Expr::SE_NoSideEffects, S.isConstantEvaluated());
10005+
RResult, S.Context, Expr::SE_NoSideEffects,
10006+
S.isConstantEvaluatedContext());
1000510007

1000610008
if (LIsInt != RIsInt) {
1000710009
BinaryOperatorKind BinOpKind = BinOp->getOpcode();
@@ -10029,7 +10031,7 @@ checkFormatStringExpr(Sema &S, const Expr *E, ArrayRef<const Expr *> Args,
1002910031
Expr::EvalResult IndexResult;
1003010032
if (ASE->getRHS()->EvaluateAsInt(IndexResult, S.Context,
1003110033
Expr::SE_NoSideEffects,
10032-
S.isConstantEvaluated())) {
10034+
S.isConstantEvaluatedContext())) {
1003310035
sumOffsets(Offset, IndexResult.Val.getInt(), BO_Add,
1003410036
/*RHS is int*/ true);
1003510037
E = ASE->getBase();
@@ -13959,7 +13961,7 @@ static bool CheckTautologicalComparison(Sema &S, BinaryOperator *E,
1395913961
return false;
1396013962

1396113963
IntRange OtherValueRange = GetExprRange(
13962-
S.Context, Other, S.isConstantEvaluated(), /*Approximate*/ false);
13964+
S.Context, Other, S.isConstantEvaluatedContext(), /*Approximate=*/false);
1396313965

1396413966
QualType OtherT = Other->getType();
1396513967
if (const auto *AT = OtherT->getAs<AtomicType>())
@@ -14174,8 +14176,9 @@ static void AnalyzeComparison(Sema &S, BinaryOperator *E) {
1417414176
}
1417514177

1417614178
// Otherwise, calculate the effective range of the signed operand.
14177-
IntRange signedRange = GetExprRange(
14178-
S.Context, signedOperand, S.isConstantEvaluated(), /*Approximate*/ true);
14179+
IntRange signedRange =
14180+
GetExprRange(S.Context, signedOperand, S.isConstantEvaluatedContext(),
14181+
/*Approximate=*/true);
1417914182

1418014183
// Go ahead and analyze implicit conversions in the operands. Note
1418114184
// that we skip the implicit conversions on both sides.
@@ -14193,8 +14196,8 @@ static void AnalyzeComparison(Sema &S, BinaryOperator *E) {
1419314196
if (E->isEqualityOp()) {
1419414197
unsigned comparisonWidth = S.Context.getIntWidth(T);
1419514198
IntRange unsignedRange =
14196-
GetExprRange(S.Context, unsignedOperand, S.isConstantEvaluated(),
14197-
/*Approximate*/ true);
14199+
GetExprRange(S.Context, unsignedOperand, S.isConstantEvaluatedContext(),
14200+
/*Approximate=*/true);
1419814201

1419914202
// We should never be unable to prove that the unsigned operand is
1420014203
// non-negative.
@@ -15057,7 +15060,7 @@ static void CheckImplicitConversion(Sema &S, Expr *E, QualType T,
1505715060
if (Target->isUnsaturatedFixedPointType()) {
1505815061
Expr::EvalResult Result;
1505915062
if (E->EvaluateAsFixedPoint(Result, S.Context, Expr::SE_AllowSideEffects,
15060-
S.isConstantEvaluated())) {
15063+
S.isConstantEvaluatedContext())) {
1506115064
llvm::APFixedPoint Value = Result.Val.getFixedPoint();
1506215065
llvm::APFixedPoint MaxVal = S.Context.getFixedPointMax(T);
1506315066
llvm::APFixedPoint MinVal = S.Context.getFixedPointMin(T);
@@ -15072,7 +15075,7 @@ static void CheckImplicitConversion(Sema &S, Expr *E, QualType T,
1507215075
}
1507315076
} else if (Target->isIntegerType()) {
1507415077
Expr::EvalResult Result;
15075-
if (!S.isConstantEvaluated() &&
15078+
if (!S.isConstantEvaluatedContext() &&
1507615079
E->EvaluateAsFixedPoint(Result, S.Context,
1507715080
Expr::SE_AllowSideEffects)) {
1507815081
llvm::APFixedPoint FXResult = Result.Val.getFixedPoint();
@@ -15095,7 +15098,7 @@ static void CheckImplicitConversion(Sema &S, Expr *E, QualType T,
1509515098
} else if (Target->isUnsaturatedFixedPointType()) {
1509615099
if (Source->isIntegerType()) {
1509715100
Expr::EvalResult Result;
15098-
if (!S.isConstantEvaluated() &&
15101+
if (!S.isConstantEvaluatedContext() &&
1509915102
E->EvaluateAsInt(Result, S.Context, Expr::SE_AllowSideEffects)) {
1510015103
llvm::APSInt Value = Result.Val.getInt();
1510115104

@@ -15121,8 +15124,9 @@ static void CheckImplicitConversion(Sema &S, Expr *E, QualType T,
1512115124
if (SourceBT && TargetBT && SourceBT->isIntegerType() &&
1512215125
TargetBT->isFloatingType() && !IsListInit) {
1512315126
// Determine the number of precision bits in the source integer type.
15124-
IntRange SourceRange = GetExprRange(S.Context, E, S.isConstantEvaluated(),
15125-
/*Approximate*/ true);
15127+
IntRange SourceRange =
15128+
GetExprRange(S.Context, E, S.isConstantEvaluatedContext(),
15129+
/*Approximate=*/true);
1512615130
unsigned int SourcePrecision = SourceRange.Width;
1512715131

1512815132
// Determine the number of precision bits in the
@@ -15190,16 +15194,16 @@ static void CheckImplicitConversion(Sema &S, Expr *E, QualType T,
1519015194

1519115195
IntRange SourceTypeRange =
1519215196
IntRange::forTargetOfCanonicalType(S.Context, Source);
15193-
IntRange LikelySourceRange =
15194-
GetExprRange(S.Context, E, S.isConstantEvaluated(), /*Approximate*/ true);
15197+
IntRange LikelySourceRange = GetExprRange(
15198+
S.Context, E, S.isConstantEvaluatedContext(), /*Approximate=*/true);
1519515199
IntRange TargetRange = IntRange::forTargetOfCanonicalType(S.Context, Target);
1519615200

1519715201
if (LikelySourceRange.Width > TargetRange.Width) {
1519815202
// If the source is a constant, use a default-on diagnostic.
1519915203
// TODO: this should happen for bitfield stores, too.
1520015204
Expr::EvalResult Result;
1520115205
if (E->EvaluateAsInt(Result, S.Context, Expr::SE_AllowSideEffects,
15202-
S.isConstantEvaluated())) {
15206+
S.isConstantEvaluatedContext())) {
1520315207
llvm::APSInt Value(32);
1520415208
Value = Result.Val.getInt();
1520515209

@@ -16063,7 +16067,8 @@ class SequenceChecker : public ConstEvaluatedExprVisitor<SequenceChecker> {
1606316067
if (!EvalOK || E->isValueDependent())
1606416068
return false;
1606516069
EvalOK = E->EvaluateAsBooleanCondition(
16066-
Result, Self.SemaRef.Context, Self.SemaRef.isConstantEvaluated());
16070+
Result, Self.SemaRef.Context,
16071+
Self.SemaRef.isConstantEvaluatedContext());
1606716072
return EvalOK;
1606816073
}
1606916074

@@ -17190,7 +17195,7 @@ void Sema::CheckArrayAccess(const Expr *BaseExpr, const Expr *IndexExpr,
1719017195
const ArraySubscriptExpr *ASE,
1719117196
bool AllowOnePastEnd, bool IndexNegated) {
1719217197
// Already diagnosed by the constant evaluator.
17193-
if (isConstantEvaluated())
17198+
if (isConstantEvaluatedContext())
1719417199
return;
1719517200

1719617201
IndexExpr = IndexExpr->IgnoreParenImpCasts();
@@ -18579,7 +18584,7 @@ void Sema::CheckArgumentWithTypeTag(const ArgumentWithTypeTagAttr *Attr,
1857918584
TypeTagData TypeInfo;
1858018585
if (!GetMatchingCType(ArgumentKind, TypeTagExpr, Context,
1858118586
TypeTagForDatatypeMagicValues.get(), FoundWrongKind,
18582-
TypeInfo, isConstantEvaluated())) {
18587+
TypeInfo, isConstantEvaluatedContext())) {
1858318588
if (FoundWrongKind)
1858418589
Diag(TypeTagExpr->getExprLoc(),
1858518590
diag::warn_type_tag_for_datatype_wrong_kind)

clang/lib/Sema/SemaExpr.cpp

+2-2
Original file line numberDiff line numberDiff line change
@@ -18312,7 +18312,7 @@ void Sema::MarkExpressionAsImmediateEscalating(Expr *E) {
1831218312

1831318313
ExprResult Sema::CheckForImmediateInvocation(ExprResult E, FunctionDecl *Decl) {
1831418314
if (isUnevaluatedContext() || !E.isUsable() || !Decl ||
18315-
!Decl->isImmediateFunction() || isConstantEvaluated() ||
18315+
!Decl->isImmediateFunction() || isAlwaysConstantEvaluatedContext() ||
1831618316
isCheckingDefaultArgumentOrInitializer() ||
1831718317
RebuildingImmediateInvocation || isImmediateFunctionContext())
1831818318
return E;
@@ -20736,7 +20736,7 @@ void Sema::MarkDeclRefReferenced(DeclRefExpr *E, const Expr *Base) {
2073620736
OdrUse = false;
2073720737

2073820738
if (auto *FD = dyn_cast<FunctionDecl>(E->getDecl())) {
20739-
if (!isUnevaluatedContext() && !isConstantEvaluated() &&
20739+
if (!isUnevaluatedContext() && !isConstantEvaluatedContext() &&
2074020740
!isImmediateFunctionContext() &&
2074120741
!isCheckingDefaultArgumentOrInitializer() &&
2074220742
FD->isImmediateFunction() && !RebuildingImmediateInvocation &&

clang/lib/Sema/TreeTransform.h

+3
Original file line numberDiff line numberDiff line change
@@ -5490,6 +5490,9 @@ TreeTransform<Derived>::TransformDependentSizedArrayType(TypeLocBuilder &TLB,
54905490
EnterExpressionEvaluationContext Unevaluated(
54915491
SemaRef, Sema::ExpressionEvaluationContext::ConstantEvaluated);
54925492

5493+
// If we have a VLA then it won't be a constant.
5494+
SemaRef.ExprEvalContexts.back().InConditionallyConstantEvaluateContext = true;
5495+
54935496
// Prefer the expression from the TypeLoc; the other may have been uniqued.
54945497
Expr *origSize = TL.getSizeExpr();
54955498
if (!origSize) origSize = T->getSizeExpr();

0 commit comments

Comments
 (0)