[clang] [Clang] Handle consteval expression in array bounds expressions (PR #66222)
via cfe-commits
cfe-commits at lists.llvm.org
Wed Sep 27 23:20:54 PDT 2023
https://github.com/cor3ntin updated https://github.com/llvm/llvm-project/pull/66222
>From b2475193769051ae4d0c340237fd5df76dfe8c49 Mon Sep 17 00:00:00 2001
From: Corentin Jabot <corentinjabot at gmail.com>
Date: Wed, 13 Sep 2023 17:36:39 +0200
Subject: [PATCH 1/6] [Clang] Handle consteval expression in array bounds
expressions
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
---
clang/docs/ReleaseNotes.rst | 3 +
clang/include/clang/Parse/Parser.h | 1 +
clang/include/clang/Sema/Sema.h | 60 +++++++++++---------
clang/lib/Parse/ParseDecl.cpp | 2 +-
clang/lib/Parse/ParseExpr.cpp | 9 +++
clang/lib/Sema/SemaCUDA.cpp | 2 +-
clang/lib/Sema/SemaChecking.cpp | 55 ++++++++++--------
clang/lib/Sema/SemaExpr.cpp | 4 +-
clang/lib/Sema/TreeTransform.h | 3 +
clang/test/SemaCXX/cxx2a-consteval.cpp | 21 +++++--
clang/unittests/Support/TimeProfilerTest.cpp | 1 +
11 files changed, 100 insertions(+), 61 deletions(-)
diff --git a/clang/docs/ReleaseNotes.rst b/clang/docs/ReleaseNotes.rst
index 68172d5317a13ba..207022331a1b691 100644
--- a/clang/docs/ReleaseNotes.rst
+++ b/clang/docs/ReleaseNotes.rst
@@ -358,6 +358,9 @@ Bug Fixes to C++ Support
- Fix crash caused by a spaceship operator returning a comparision category by
reference. Fixes:
(`#64162 <https://github.com/llvm/llvm-project/issues/64162>`_)
+- Fix a crash when calling a consteval function in an expression used as
+ the size of an array.
+ (`#65520 <https://github.com/llvm/llvm-project/issues/65520>`_)
Bug Fixes to AST Handling
^^^^^^^^^^^^^^^^^^^^^^^^^
diff --git a/clang/include/clang/Parse/Parser.h b/clang/include/clang/Parse/Parser.h
index f599b8b98d031fb..7303db939de7fe0 100644
--- a/clang/include/clang/Parse/Parser.h
+++ b/clang/include/clang/Parse/Parser.h
@@ -1766,6 +1766,7 @@ class Parser : public CodeCompletionHandler {
ExprResult ParseConstantExpressionInExprEvalContext(
TypeCastState isTypeCast = NotTypeCast);
ExprResult ParseConstantExpression();
+ ExprResult ParseArrayBoundExpression();
ExprResult ParseCaseExpression(SourceLocation CaseLoc);
ExprResult ParseConstraintExpression();
ExprResult
diff --git a/clang/include/clang/Sema/Sema.h b/clang/include/clang/Sema/Sema.h
index 712db0a3dd895d5..ad129bee073c35c 100644
--- a/clang/include/clang/Sema/Sema.h
+++ b/clang/include/clang/Sema/Sema.h
@@ -1062,20 +1062,6 @@ class Sema final {
}
};
- /// Whether the AST is currently being rebuilt to correct immediate
- /// invocations. Immediate invocation candidates and references to consteval
- /// functions aren't tracked when this is set.
- bool RebuildingImmediateInvocation = false;
-
- /// Used to change context to isConstantEvaluated without pushing a heavy
- /// ExpressionEvaluationContextRecord object.
- bool isConstantEvaluatedOverride;
-
- bool isConstantEvaluated() const {
- return ExprEvalContexts.back().isConstantEvaluated() ||
- isConstantEvaluatedOverride;
- }
-
/// RAII object to handle the state changes required to synthesize
/// a function body.
class SynthesizedFunctionScope {
@@ -1361,6 +1347,10 @@ class Sema final {
bool IsCurrentlyCheckingDefaultArgumentOrInitializer = false;
+ // We are in a constant context, but we also allow
+ // non constant expressions, for example for array bounds (which may be VLAs).
+ bool InConditionallyConstantEvaluateContext = false;
+
// When evaluating immediate functions in the initializer of a default
// argument or default member initializer, this is the declaration whose
// default initializer is being evaluated and the location of the call
@@ -9845,30 +9835,44 @@ class Sema final {
/// diagnostics that will be suppressed.
std::optional<sema::TemplateDeductionInfo *> isSFINAEContext() const;
- /// Determines whether we are currently in a context that
- /// is not evaluated as per C++ [expr] p5.
- bool isUnevaluatedContext() const {
+ /// Whether the AST is currently being rebuilt to correct immediate
+ /// invocations. Immediate invocation candidates and references to consteval
+ /// functions aren't tracked when this is set.
+ bool RebuildingImmediateInvocation = false;
+
+ /// Used to change context to isConstantEvaluated without pushing a heavy
+ /// ExpressionEvaluationContextRecord object.
+ bool isConstantEvaluatedOverride;
+
+ const ExpressionEvaluationContextRecord ¤tEvaluationContext() const {
assert(!ExprEvalContexts.empty() &&
"Must be in an expression evaluation context");
- return ExprEvalContexts.back().isUnevaluated();
- }
+ return ExprEvalContexts.back();
+ };
bool isConstantEvaluatedContext() const {
- assert(!ExprEvalContexts.empty() &&
- "Must be in an expression evaluation context");
- return ExprEvalContexts.back().isConstantEvaluated();
+ return currentEvaluationContext().isConstantEvaluated() ||
+ isConstantEvaluatedOverride;
+ }
+
+ bool isAlwaysConstantEvaluatedContext() const {
+ const ExpressionEvaluationContextRecord &Ctx = currentEvaluationContext();
+ return (Ctx.isConstantEvaluated() || isConstantEvaluatedOverride) &&
+ !Ctx.InConditionallyConstantEvaluateContext;
+ }
+
+ /// Determines whether we are currently in a context that
+ /// is not evaluated as per C++ [expr] p5.
+ bool isUnevaluatedContext() const {
+ return currentEvaluationContext().isUnevaluated();
}
bool isImmediateFunctionContext() const {
- assert(!ExprEvalContexts.empty() &&
- "Must be in an expression evaluation context");
- return ExprEvalContexts.back().isImmediateFunctionContext();
+ return currentEvaluationContext().isImmediateFunctionContext();
}
bool isCheckingDefaultArgumentOrInitializer() const {
- assert(!ExprEvalContexts.empty() &&
- "Must be in an expression evaluation context");
- const ExpressionEvaluationContextRecord &Ctx = ExprEvalContexts.back();
+ const ExpressionEvaluationContextRecord &Ctx = currentEvaluationContext();
return (Ctx.Context ==
ExpressionEvaluationContext::PotentiallyEvaluatedIfUsed) ||
Ctx.IsCurrentlyCheckingDefaultArgumentOrInitializer;
diff --git a/clang/lib/Parse/ParseDecl.cpp b/clang/lib/Parse/ParseDecl.cpp
index 748b9d53c9f5b33..5114e181582d91a 100644
--- a/clang/lib/Parse/ParseDecl.cpp
+++ b/clang/lib/Parse/ParseDecl.cpp
@@ -7687,7 +7687,7 @@ void Parser::ParseBracketDeclarator(Declarator &D) {
// Parse the constant-expression or assignment-expression now (depending
// on dialect).
if (getLangOpts().CPlusPlus) {
- NumElements = ParseConstantExpression();
+ NumElements = ParseArrayBoundExpression();
} else {
EnterExpressionEvaluationContext Unevaluated(
Actions, Sema::ExpressionEvaluationContext::ConstantEvaluated);
diff --git a/clang/lib/Parse/ParseExpr.cpp b/clang/lib/Parse/ParseExpr.cpp
index 74664c34abdbd89..00e1de0d7de84cd 100644
--- a/clang/lib/Parse/ParseExpr.cpp
+++ b/clang/lib/Parse/ParseExpr.cpp
@@ -221,6 +221,15 @@ ExprResult Parser::ParseConstantExpression() {
return ParseConstantExpressionInExprEvalContext(NotTypeCast);
}
+ExprResult Parser::ParseArrayBoundExpression() {
+ EnterExpressionEvaluationContext ConstantEvaluated(
+ Actions, Sema::ExpressionEvaluationContext::ConstantEvaluated);
+ // If we parse the bound of a VLA... we parse a non-constant
+ // constant-expression!
+ Actions.ExprEvalContexts.back().InConditionallyConstantEvaluateContext = true;
+ return ParseConstantExpressionInExprEvalContext(NotTypeCast);
+}
+
ExprResult Parser::ParseCaseExpression(SourceLocation CaseLoc) {
EnterExpressionEvaluationContext ConstantEvaluated(
Actions, Sema::ExpressionEvaluationContext::ConstantEvaluated);
diff --git a/clang/lib/Sema/SemaCUDA.cpp b/clang/lib/Sema/SemaCUDA.cpp
index 88f5484575db17a..bc676be371b8cf3 100644
--- a/clang/lib/Sema/SemaCUDA.cpp
+++ b/clang/lib/Sema/SemaCUDA.cpp
@@ -803,7 +803,7 @@ bool Sema::CheckCUDACall(SourceLocation Loc, FunctionDecl *Callee) {
assert(getLangOpts().CUDA && "Should only be called during CUDA compilation");
assert(Callee && "Callee may not be null.");
- auto &ExprEvalCtx = ExprEvalContexts.back();
+ const auto &ExprEvalCtx = currentEvaluationContext();
if (ExprEvalCtx.isUnevaluated() || ExprEvalCtx.isConstantEvaluated())
return true;
diff --git a/clang/lib/Sema/SemaChecking.cpp b/clang/lib/Sema/SemaChecking.cpp
index 6a3b5fa61d59456..082cfc3474d148b 100644
--- a/clang/lib/Sema/SemaChecking.cpp
+++ b/clang/lib/Sema/SemaChecking.cpp
@@ -1076,7 +1076,7 @@ static bool ProcessFormatStringLiteral(const Expr *FormatExpr,
void Sema::checkFortifiedBuiltinMemoryFunction(FunctionDecl *FD,
CallExpr *TheCall) {
if (TheCall->isValueDependent() || TheCall->isTypeDependent() ||
- isConstantEvaluated())
+ isConstantEvaluatedContext())
return;
bool UseDABAttr = false;
@@ -3192,7 +3192,7 @@ bool Sema::CheckCDEBuiltinFunctionCall(const TargetInfo &TI, unsigned BuiltinID,
bool Sema::CheckARMCoprocessorImmediate(const TargetInfo &TI,
const Expr *CoprocArg, bool WantCDE) {
- if (isConstantEvaluated())
+ if (isConstantEvaluatedContext())
return false;
// We can't check the value of a dependent argument.
@@ -6616,7 +6616,7 @@ static void CheckNonNullArguments(Sema &S,
assert((FDecl || Proto) && "Need a function declaration or prototype");
// Already checked by constant evaluator.
- if (S.isConstantEvaluated())
+ if (S.isConstantEvaluatedContext())
return;
// Check the attributes attached to the method/function itself.
llvm::SmallBitVector NonNullArgs;
@@ -8973,7 +8973,7 @@ bool Sema::SemaBuiltinConstantArg(CallExpr *TheCall, int ArgNum,
/// TheCall is a constant expression in the range [Low, High].
bool Sema::SemaBuiltinConstantArgRange(CallExpr *TheCall, int ArgNum,
int Low, int High, bool RangeIsError) {
- if (isConstantEvaluated())
+ if (isConstantEvaluatedContext())
return false;
llvm::APSInt Result;
@@ -9687,7 +9687,7 @@ checkFormatStringExpr(Sema &S, const Expr *E, ArrayRef<const Expr *> Args,
llvm::SmallBitVector &CheckedVarArgs,
UncoveredArgHandler &UncoveredArg, llvm::APSInt Offset,
bool IgnoreStringsWithoutSpecifiers = false) {
- if (S.isConstantEvaluated())
+ if (S.isConstantEvaluatedContext())
return SLCT_NotALiteral;
tryAgain:
assert(Offset.isSigned() && "invalid offset");
@@ -9727,8 +9727,8 @@ checkFormatStringExpr(Sema &S, const Expr *E, ArrayRef<const Expr *> Args,
bool CheckLeft = true, CheckRight = true;
bool Cond;
- if (C->getCond()->EvaluateAsBooleanCondition(Cond, S.getASTContext(),
- S.isConstantEvaluated())) {
+ if (C->getCond()->EvaluateAsBooleanCondition(
+ Cond, S.getASTContext(), S.isConstantEvaluatedContext())) {
if (Cond)
CheckRight = false;
else
@@ -9992,9 +9992,11 @@ checkFormatStringExpr(Sema &S, const Expr *E, ArrayRef<const Expr *> Args,
Expr::EvalResult LResult, RResult;
bool LIsInt = BinOp->getLHS()->EvaluateAsInt(
- LResult, S.Context, Expr::SE_NoSideEffects, S.isConstantEvaluated());
+ LResult, S.Context, Expr::SE_NoSideEffects,
+ S.isConstantEvaluatedContext());
bool RIsInt = BinOp->getRHS()->EvaluateAsInt(
- RResult, S.Context, Expr::SE_NoSideEffects, S.isConstantEvaluated());
+ RResult, S.Context, Expr::SE_NoSideEffects,
+ S.isConstantEvaluatedContext());
if (LIsInt != RIsInt) {
BinaryOperatorKind BinOpKind = BinOp->getOpcode();
@@ -10022,7 +10024,7 @@ checkFormatStringExpr(Sema &S, const Expr *E, ArrayRef<const Expr *> Args,
Expr::EvalResult IndexResult;
if (ASE->getRHS()->EvaluateAsInt(IndexResult, S.Context,
Expr::SE_NoSideEffects,
- S.isConstantEvaluated())) {
+ S.isConstantEvaluatedContext())) {
sumOffsets(Offset, IndexResult.Val.getInt(), BO_Add,
/*RHS is int*/ true);
E = ASE->getBase();
@@ -13953,7 +13955,7 @@ static bool CheckTautologicalComparison(Sema &S, BinaryOperator *E,
return false;
IntRange OtherValueRange = GetExprRange(
- S.Context, Other, S.isConstantEvaluated(), /*Approximate*/ false);
+ S.Context, Other, S.isConstantEvaluatedContext(), /*Approximate*/ false);
QualType OtherT = Other->getType();
if (const auto *AT = OtherT->getAs<AtomicType>())
@@ -14168,8 +14170,9 @@ static void AnalyzeComparison(Sema &S, BinaryOperator *E) {
}
// Otherwise, calculate the effective range of the signed operand.
- IntRange signedRange = GetExprRange(
- S.Context, signedOperand, S.isConstantEvaluated(), /*Approximate*/ true);
+ IntRange signedRange =
+ GetExprRange(S.Context, signedOperand, S.isConstantEvaluatedContext(),
+ /*Approximate*/ true);
// Go ahead and analyze implicit conversions in the operands. Note
// that we skip the implicit conversions on both sides.
@@ -14187,7 +14190,7 @@ static void AnalyzeComparison(Sema &S, BinaryOperator *E) {
if (E->isEqualityOp()) {
unsigned comparisonWidth = S.Context.getIntWidth(T);
IntRange unsignedRange =
- GetExprRange(S.Context, unsignedOperand, S.isConstantEvaluated(),
+ GetExprRange(S.Context, unsignedOperand, S.isConstantEvaluatedContext(),
/*Approximate*/ true);
// We should never be unable to prove that the unsigned operand is
@@ -15051,7 +15054,7 @@ static void CheckImplicitConversion(Sema &S, Expr *E, QualType T,
if (Target->isUnsaturatedFixedPointType()) {
Expr::EvalResult Result;
if (E->EvaluateAsFixedPoint(Result, S.Context, Expr::SE_AllowSideEffects,
- S.isConstantEvaluated())) {
+ S.isConstantEvaluatedContext())) {
llvm::APFixedPoint Value = Result.Val.getFixedPoint();
llvm::APFixedPoint MaxVal = S.Context.getFixedPointMax(T);
llvm::APFixedPoint MinVal = S.Context.getFixedPointMin(T);
@@ -15066,7 +15069,7 @@ static void CheckImplicitConversion(Sema &S, Expr *E, QualType T,
}
} else if (Target->isIntegerType()) {
Expr::EvalResult Result;
- if (!S.isConstantEvaluated() &&
+ if (!S.isConstantEvaluatedContext() &&
E->EvaluateAsFixedPoint(Result, S.Context,
Expr::SE_AllowSideEffects)) {
llvm::APFixedPoint FXResult = Result.Val.getFixedPoint();
@@ -15089,7 +15092,7 @@ static void CheckImplicitConversion(Sema &S, Expr *E, QualType T,
} else if (Target->isUnsaturatedFixedPointType()) {
if (Source->isIntegerType()) {
Expr::EvalResult Result;
- if (!S.isConstantEvaluated() &&
+ if (!S.isConstantEvaluatedContext() &&
E->EvaluateAsInt(Result, S.Context, Expr::SE_AllowSideEffects)) {
llvm::APSInt Value = Result.Val.getInt();
@@ -15115,8 +15118,9 @@ static void CheckImplicitConversion(Sema &S, Expr *E, QualType T,
if (SourceBT && TargetBT && SourceBT->isIntegerType() &&
TargetBT->isFloatingType() && !IsListInit) {
// Determine the number of precision bits in the source integer type.
- IntRange SourceRange = GetExprRange(S.Context, E, S.isConstantEvaluated(),
- /*Approximate*/ true);
+ IntRange SourceRange =
+ GetExprRange(S.Context, E, S.isConstantEvaluatedContext(),
+ /*Approximate*/ true);
unsigned int SourcePrecision = SourceRange.Width;
// Determine the number of precision bits in the
@@ -15184,8 +15188,8 @@ static void CheckImplicitConversion(Sema &S, Expr *E, QualType T,
IntRange SourceTypeRange =
IntRange::forTargetOfCanonicalType(S.Context, Source);
- IntRange LikelySourceRange =
- GetExprRange(S.Context, E, S.isConstantEvaluated(), /*Approximate*/ true);
+ IntRange LikelySourceRange = GetExprRange(
+ S.Context, E, S.isConstantEvaluatedContext(), /*Approximate*/ true);
IntRange TargetRange = IntRange::forTargetOfCanonicalType(S.Context, Target);
if (LikelySourceRange.Width > TargetRange.Width) {
@@ -15193,7 +15197,7 @@ static void CheckImplicitConversion(Sema &S, Expr *E, QualType T,
// TODO: this should happen for bitfield stores, too.
Expr::EvalResult Result;
if (E->EvaluateAsInt(Result, S.Context, Expr::SE_AllowSideEffects,
- S.isConstantEvaluated())) {
+ S.isConstantEvaluatedContext())) {
llvm::APSInt Value(32);
Value = Result.Val.getInt();
@@ -16057,7 +16061,8 @@ class SequenceChecker : public ConstEvaluatedExprVisitor<SequenceChecker> {
if (!EvalOK || E->isValueDependent())
return false;
EvalOK = E->EvaluateAsBooleanCondition(
- Result, Self.SemaRef.Context, Self.SemaRef.isConstantEvaluated());
+ Result, Self.SemaRef.Context,
+ Self.SemaRef.isConstantEvaluatedContext());
return EvalOK;
}
@@ -17184,7 +17189,7 @@ void Sema::CheckArrayAccess(const Expr *BaseExpr, const Expr *IndexExpr,
const ArraySubscriptExpr *ASE,
bool AllowOnePastEnd, bool IndexNegated) {
// Already diagnosed by the constant evaluator.
- if (isConstantEvaluated())
+ if (isConstantEvaluatedContext())
return;
IndexExpr = IndexExpr->IgnoreParenImpCasts();
@@ -18573,7 +18578,7 @@ void Sema::CheckArgumentWithTypeTag(const ArgumentWithTypeTagAttr *Attr,
TypeTagData TypeInfo;
if (!GetMatchingCType(ArgumentKind, TypeTagExpr, Context,
TypeTagForDatatypeMagicValues.get(), FoundWrongKind,
- TypeInfo, isConstantEvaluated())) {
+ TypeInfo, isConstantEvaluatedContext())) {
if (FoundWrongKind)
Diag(TypeTagExpr->getExprLoc(),
diag::warn_type_tag_for_datatype_wrong_kind)
diff --git a/clang/lib/Sema/SemaExpr.cpp b/clang/lib/Sema/SemaExpr.cpp
index 92496b03ecabe54..667444917a9b31e 100644
--- a/clang/lib/Sema/SemaExpr.cpp
+++ b/clang/lib/Sema/SemaExpr.cpp
@@ -18282,7 +18282,7 @@ void Sema::MarkExpressionAsImmediateEscalating(Expr *E) {
ExprResult Sema::CheckForImmediateInvocation(ExprResult E, FunctionDecl *Decl) {
if (isUnevaluatedContext() || !E.isUsable() || !Decl ||
- !Decl->isImmediateFunction() || isConstantEvaluated() ||
+ !Decl->isImmediateFunction() || isAlwaysConstantEvaluatedContext() ||
isCheckingDefaultArgumentOrInitializer() ||
RebuildingImmediateInvocation || isImmediateFunctionContext())
return E;
@@ -20668,7 +20668,7 @@ void Sema::MarkDeclRefReferenced(DeclRefExpr *E, const Expr *Base) {
OdrUse = false;
if (auto *FD = dyn_cast<FunctionDecl>(E->getDecl())) {
- if (!isUnevaluatedContext() && !isConstantEvaluated() &&
+ if (!isUnevaluatedContext() && !isConstantEvaluatedContext() &&
!isImmediateFunctionContext() &&
!isCheckingDefaultArgumentOrInitializer() &&
FD->isImmediateFunction() && !RebuildingImmediateInvocation &&
diff --git a/clang/lib/Sema/TreeTransform.h b/clang/lib/Sema/TreeTransform.h
index 0fc5ad8e3bde6c6..9a03be105c2ca12 100644
--- a/clang/lib/Sema/TreeTransform.h
+++ b/clang/lib/Sema/TreeTransform.h
@@ -5490,6 +5490,9 @@ TreeTransform<Derived>::TransformDependentSizedArrayType(TypeLocBuilder &TLB,
EnterExpressionEvaluationContext Unevaluated(
SemaRef, Sema::ExpressionEvaluationContext::ConstantEvaluated);
+ // VLA bounds are not truly constant.
+ SemaRef.ExprEvalContexts.back().InConditionallyConstantEvaluateContext = true;
+
// Prefer the expression from the TypeLoc; the other may have been uniqued.
Expr *origSize = TL.getSizeExpr();
if (!origSize) origSize = T->getSizeExpr();
diff --git a/clang/test/SemaCXX/cxx2a-consteval.cpp b/clang/test/SemaCXX/cxx2a-consteval.cpp
index a091fadfa3094bd..1dcff1a9ef81365 100644
--- a/clang/test/SemaCXX/cxx2a-consteval.cpp
+++ b/clang/test/SemaCXX/cxx2a-consteval.cpp
@@ -8,7 +8,7 @@ consteval int f1(int i) {
return i;
}
-consteval constexpr int f2(int i) {
+consteval constexpr int f2(int i) {
//expected-error at -1 {{cannot combine}}
return i;
}
@@ -195,7 +195,7 @@ auto ptr = ret1(0);
struct A {
consteval int f(int) {
// expected-note at -1+ {{declared here}}
- return 0;
+ return 0;
}
};
@@ -239,7 +239,7 @@ constexpr int f_c(int i) {
int t = f(i);
// expected-error at -1 {{is not a constant expression}}
// expected-note at -2 {{function parameter}}
- return f(0);
+ return f(0);
}
consteval int f_eval(int i) {
@@ -675,7 +675,7 @@ Bar<derp> a; // expected-note {{in instantiation of member function 'issue_55601
struct constantDerp {
// Can be used in a constant expression.
- consteval constantDerp(int) {}
+ consteval constantDerp(int) {}
consteval operator int() const { return 5; }
};
Bar<constantDerp> b;
@@ -1175,4 +1175,17 @@ struct T {
static constexpr auto xx = ns::foo(A{}); // expected-error {{cannot take address of consteval function 'foo' outside of an immediate invocation}}
};
+namespace GH65520 {
+
+consteval int bar (int i) { if (i != 1) return 1/0; return 0; }
+// expected-note at -1{{division by zero}}
+
+void
+g ()
+{
+ int a_ok[bar(1)];
+ int a_err[bar(3)]; // expected-error {{call to consteval function 'GH65520::bar' is not a constant expression}} \
+ // expected-note {{in call to 'bar(3)'}}
+}
+
}
diff --git a/clang/unittests/Support/TimeProfilerTest.cpp b/clang/unittests/Support/TimeProfilerTest.cpp
index fdfbbfe4e3a9dff..a7ca2bf91e474ef 100644
--- a/clang/unittests/Support/TimeProfilerTest.cpp
+++ b/clang/unittests/Support/TimeProfilerTest.cpp
@@ -190,6 +190,7 @@ Frontend
| EvaluateAsInitializer (slow_value)
| EvaluateAsConstantExpr (<test.cc:17:33, col:59>)
| EvaluateAsConstantExpr (<test.cc:18:11, col:37>)
+| EvaluateAsConstantExpr (<test.cc:23:31, col:57>)
| EvaluateAsRValue (<test.cc:22:14, line:23:58>)
| EvaluateAsInitializer (slow_init_list)
| PerformPendingInstantiations
>From 8d38d4d19f15c9c467ab1a3dee77fd8a8f49231b Mon Sep 17 00:00:00 2001
From: Corentin Jabot <corentinjabot at gmail.com>
Date: Thu, 14 Sep 2023 09:21:05 +0200
Subject: [PATCH 2/6] Move the initialization of isConstantEvaluatedOverride
---
clang/include/clang/Sema/Sema.h | 2 +-
clang/lib/Sema/Sema.cpp | 1 -
2 files changed, 1 insertion(+), 2 deletions(-)
diff --git a/clang/include/clang/Sema/Sema.h b/clang/include/clang/Sema/Sema.h
index ad129bee073c35c..94e2f180e227f11 100644
--- a/clang/include/clang/Sema/Sema.h
+++ b/clang/include/clang/Sema/Sema.h
@@ -9842,7 +9842,7 @@ class Sema final {
/// Used to change context to isConstantEvaluated without pushing a heavy
/// ExpressionEvaluationContextRecord object.
- bool isConstantEvaluatedOverride;
+ bool isConstantEvaluatedOverride = false;
const ExpressionEvaluationContextRecord ¤tEvaluationContext() const {
assert(!ExprEvalContexts.empty() &&
diff --git a/clang/lib/Sema/Sema.cpp b/clang/lib/Sema/Sema.cpp
index a401017d4c1c0b8..14d6694edc5ad96 100644
--- a/clang/lib/Sema/Sema.cpp
+++ b/clang/lib/Sema/Sema.cpp
@@ -221,7 +221,6 @@ Sema::Sema(Preprocessor &pp, ASTContext &ctxt, ASTConsumer &consumer,
CurScope(nullptr), Ident_super(nullptr) {
assert(pp.TUKind == TUKind);
TUScope = nullptr;
- isConstantEvaluatedOverride = false;
LoadedExternalKnownNamespaces = false;
for (unsigned I = 0; I != NSAPI::NumNSNumberLiteralMethods; ++I)
>From 2309c6e414734934904f9eae5266fa72f1a38917 Mon Sep 17 00:00:00 2001
From: Corentin Jabot <corentinjabot at gmail.com>
Date: Tue, 26 Sep 2023 20:54:25 +0200
Subject: [PATCH 3/6] Add test in immediate context
---
clang/lib/Sema/SemaChecking.cpp | 4 ++--
clang/test/SemaCXX/cxx2a-consteval.cpp | 8 ++++++++
2 files changed, 10 insertions(+), 2 deletions(-)
diff --git a/clang/lib/Sema/SemaChecking.cpp b/clang/lib/Sema/SemaChecking.cpp
index 082cfc3474d148b..441a4fc0b0a3311 100644
--- a/clang/lib/Sema/SemaChecking.cpp
+++ b/clang/lib/Sema/SemaChecking.cpp
@@ -13955,7 +13955,7 @@ static bool CheckTautologicalComparison(Sema &S, BinaryOperator *E,
return false;
IntRange OtherValueRange = GetExprRange(
- S.Context, Other, S.isConstantEvaluatedContext(), /*Approximate*/ false);
+ S.Context, Other, S.isConstantEvaluatedContext(), /*Approximate=*/ false);
QualType OtherT = Other->getType();
if (const auto *AT = OtherT->getAs<AtomicType>())
@@ -15189,7 +15189,7 @@ static void CheckImplicitConversion(Sema &S, Expr *E, QualType T,
IntRange SourceTypeRange =
IntRange::forTargetOfCanonicalType(S.Context, Source);
IntRange LikelySourceRange = GetExprRange(
- S.Context, E, S.isConstantEvaluatedContext(), /*Approximate*/ true);
+ S.Context, E, S.isConstantEvaluatedContext(), /*Approximate=*/ true);
IntRange TargetRange = IntRange::forTargetOfCanonicalType(S.Context, Target);
if (LikelySourceRange.Width > TargetRange.Width) {
diff --git a/clang/test/SemaCXX/cxx2a-consteval.cpp b/clang/test/SemaCXX/cxx2a-consteval.cpp
index 1dcff1a9ef81365..7243233d38069e2 100644
--- a/clang/test/SemaCXX/cxx2a-consteval.cpp
+++ b/clang/test/SemaCXX/cxx2a-consteval.cpp
@@ -1188,4 +1188,12 @@ g ()
// expected-note {{in call to 'bar(3)'}}
}
+consteval int undefined(); // expected-note {{declared here}}
+
+consteval void immediate() {
+ int a [undefined()]; // expected-note {{undefined function 'undefined' cannot be used in a constant expression}} \
+ // expected-error {{call to consteval function 'GH65520::undefined' is not a constant expression}}
+}
+
+
}
>From d36330174f5bd1e8c91c6c7d66f1ce6477a4a98c Mon Sep 17 00:00:00 2001
From: Corentin Jabot <corentinjabot at gmail.com>
Date: Wed, 27 Sep 2023 11:32:02 +0200
Subject: [PATCH 4/6] Fix rebase and format
---
clang/lib/Sema/SemaChecking.cpp | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/clang/lib/Sema/SemaChecking.cpp b/clang/lib/Sema/SemaChecking.cpp
index 441a4fc0b0a3311..7addc9d8daa36c6 100644
--- a/clang/lib/Sema/SemaChecking.cpp
+++ b/clang/lib/Sema/SemaChecking.cpp
@@ -13955,7 +13955,7 @@ static bool CheckTautologicalComparison(Sema &S, BinaryOperator *E,
return false;
IntRange OtherValueRange = GetExprRange(
- S.Context, Other, S.isConstantEvaluatedContext(), /*Approximate=*/ false);
+ S.Context, Other, S.isConstantEvaluatedContext(), /*Approximate=*/false);
QualType OtherT = Other->getType();
if (const auto *AT = OtherT->getAs<AtomicType>())
@@ -15189,7 +15189,7 @@ static void CheckImplicitConversion(Sema &S, Expr *E, QualType T,
IntRange SourceTypeRange =
IntRange::forTargetOfCanonicalType(S.Context, Source);
IntRange LikelySourceRange = GetExprRange(
- S.Context, E, S.isConstantEvaluatedContext(), /*Approximate=*/ true);
+ S.Context, E, S.isConstantEvaluatedContext(), /*Approximate=*/true);
IntRange TargetRange = IntRange::forTargetOfCanonicalType(S.Context, Target);
if (LikelySourceRange.Width > TargetRange.Width) {
>From a22f4a6cbb4cf08751d4d45dbfde2cc5c5b2540c Mon Sep 17 00:00:00 2001
From: Corentin Jabot <corentinjabot at gmail.com>
Date: Wed, 27 Sep 2023 12:20:09 +0200
Subject: [PATCH 5/6] Fix test
---
clang/test/SemaCXX/cxx2a-consteval.cpp | 5 ++++-
1 file changed, 4 insertions(+), 1 deletion(-)
diff --git a/clang/test/SemaCXX/cxx2a-consteval.cpp b/clang/test/SemaCXX/cxx2a-consteval.cpp
index 7243233d38069e2..38cc4be32a27c20 100644
--- a/clang/test/SemaCXX/cxx2a-consteval.cpp
+++ b/clang/test/SemaCXX/cxx2a-consteval.cpp
@@ -1175,6 +1175,8 @@ struct T {
static constexpr auto xx = ns::foo(A{}); // expected-error {{cannot take address of consteval function 'foo' outside of an immediate invocation}}
};
+}
+
namespace GH65520 {
consteval int bar (int i) { if (i != 1) return 1/0; return 0; }
@@ -1192,7 +1194,8 @@ consteval int undefined(); // expected-note {{declared here}}
consteval void immediate() {
int a [undefined()]; // expected-note {{undefined function 'undefined' cannot be used in a constant expression}} \
- // expected-error {{call to consteval function 'GH65520::undefined' is not a constant expression}}
+ // expected-error {{call to consteval function 'GH65520::undefined' is not a constant expression}} \
+ // expected-error {{variable of non-literal type 'int[undefined()]' cannot be defined in a constexpr function before C++23}}
}
>From 2872661f277b52bc52c7ce896177bf2e5392e69c Mon Sep 17 00:00:00 2001
From: Corentin Jabot <corentinjabot at gmail.com>
Date: Wed, 27 Sep 2023 22:21:43 +0200
Subject: [PATCH 6/6] Improve comment using Shafik's suggestion
---
clang/lib/Sema/TreeTransform.h | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/clang/lib/Sema/TreeTransform.h b/clang/lib/Sema/TreeTransform.h
index 9a03be105c2ca12..bcaad0950fa923b 100644
--- a/clang/lib/Sema/TreeTransform.h
+++ b/clang/lib/Sema/TreeTransform.h
@@ -5490,7 +5490,7 @@ TreeTransform<Derived>::TransformDependentSizedArrayType(TypeLocBuilder &TLB,
EnterExpressionEvaluationContext Unevaluated(
SemaRef, Sema::ExpressionEvaluationContext::ConstantEvaluated);
- // VLA bounds are not truly constant.
+ // If we have a VLA then it won't be a constant.
SemaRef.ExprEvalContexts.back().InConditionallyConstantEvaluateContext = true;
// Prefer the expression from the TypeLoc; the other may have been uniqued.
More information about the cfe-commits
mailing list