llvmbot wrote:
<!--LLVM PR SUMMARY COMMENT--> @llvm/pr-subscribers-clang <details> <summary>Changes</summary> 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 -- Patch is 20.71 KiB, truncated to 20.00 KiB below, full version: https://github.com/llvm/llvm-project/pull/66222.diff 11 Files Affected: - (modified) clang/docs/ReleaseNotes.rst (+4) - (modified) clang/include/clang/Parse/Parser.h (+1) - (modified) clang/include/clang/Sema/Sema.h (+32-28) - (modified) clang/lib/Parse/ParseDecl.cpp (+1-1) - (modified) clang/lib/Parse/ParseExpr.cpp (+9) - (modified) clang/lib/Sema/SemaCUDA.cpp (+1-1) - (modified) clang/lib/Sema/SemaChecking.cpp (+30-25) - (modified) clang/lib/Sema/SemaExpr.cpp (+2-2) - (modified) clang/lib/Sema/TreeTransform.h (+3) - (modified) clang/test/SemaCXX/cxx2a-consteval.cpp (+15) - (modified) clang/unittests/Support/TimeProfilerTest.cpp (+1) <pre> diff --git a/clang/docs/ReleaseNotes.rst b/clang/docs/ReleaseNotes.rst index 3cdad2f7b9f0e5a..1d2c0830b2910d5 100644 --- a/clang/docs/ReleaseNotes.rst +++ b/clang/docs/ReleaseNotes.rst @@ -281,6 +281,10 @@ Bug Fixes to C++ Support a non-template inner-class between the function and the class template. (`#65810 &lt;https://github.com/llvm/llvm-project/issues/65810&gt;`_) +- Fix a crash when calling a consteval function in an expression used as + the size of an array. + (`#65520 &lt;https://github.com/llvm/llvm-project/issues/65520&gt;`_) + Bug Fixes to AST Handling ^^^^^^^^^^^^^^^^^^^^^^^^^ - Fixed an import failure of recursive friend class template. 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 304108df9f8d029..77a8cb67be16c91 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&#x27;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 @@ -9844,30 +9834,44 @@ class Sema final { /// diagnostics that will be suppressed. std::optional&lt;sema::TemplateDeductionInfo *&gt; 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&#x27;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 &amp;currentEvaluationContext() const { assert(!ExprEvalContexts.empty() &amp;&amp; &quot;Must be in an expression evaluation context&quot;); - return ExprEvalContexts.back().isUnevaluated(); - } + return ExprEvalContexts.back(); + }; bool isConstantEvaluatedContext() const { - assert(!ExprEvalContexts.empty() &amp;&amp; - &quot;Must be in an expression evaluation context&quot;); - return ExprEvalContexts.back().isConstantEvaluated(); + return currentEvaluationContext().isConstantEvaluated() || + isConstantEvaluatedOverride; + } + + bool isAlwaysConstantEvaluatedContext() const { + const ExpressionEvaluationContextRecord &amp;Ctx = currentEvaluationContext(); + return (Ctx.isConstantEvaluated() || isConstantEvaluatedOverride) &amp;&amp; + !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() &amp;&amp; - &quot;Must be in an expression evaluation context&quot;); - return ExprEvalContexts.back().isImmediateFunctionContext(); + return currentEvaluationContext().isImmediateFunctionContext(); } bool isCheckingDefaultArgumentOrInitializer() const { - assert(!ExprEvalContexts.empty() &amp;&amp; - &quot;Must be in an expression evaluation context&quot;); - const ExpressionEvaluationContextRecord &amp;Ctx = ExprEvalContexts.back(); + const ExpressionEvaluationContextRecord &amp;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 &amp;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 f8bf785da2896a3..e6de742a01f3f6d 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 &amp;&amp; &quot;Should only be called during CUDA compilation&quot;); assert(Callee &amp;&amp; &quot;Callee may not be null.&quot;); - auto &amp;ExprEvalCtx = ExprEvalContexts.back(); + const auto &amp;ExprEvalCtx = currentEvaluationContext(); if (ExprEvalCtx.isUnevaluated() || ExprEvalCtx.isConstantEvaluated()) return true; diff --git a/clang/lib/Sema/SemaChecking.cpp b/clang/lib/Sema/SemaChecking.cpp index fad70223362eddd..5208dbd9d3c8f58 100644 --- a/clang/lib/Sema/SemaChecking.cpp +++ b/clang/lib/Sema/SemaChecking.cpp @@ -1068,7 +1068,7 @@ static bool ProcessFormatStringLiteral(const Expr *FormatExpr, void Sema::checkFortifiedBuiltinMemoryFunction(FunctionDecl *FD, CallExpr *TheCall) { if (TheCall-&gt;isValueDependent() || TheCall-&gt;isTypeDependent() || - isConstantEvaluated()) + isConstantEvaluatedContext()) return; bool UseDABAttr = false; @@ -3175,7 +3175,7 @@ bool Sema::CheckCDEBuiltinFunctionCall(const TargetInfo &amp;TI, unsigned BuiltinID, bool Sema::CheckARMCoprocessorImmediate(const TargetInfo &amp;TI, const Expr *CoprocArg, bool WantCDE) { - if (isConstantEvaluated()) + if (isConstantEvaluatedContext()) return false; // We can&#x27;t check the value of a dependent argument. @@ -6595,7 +6595,7 @@ static void CheckNonNullArguments(Sema &amp;S, assert((FDecl || Proto) &amp;&amp; &quot;Need a function declaration or prototype&quot;); // Already checked by constant evaluator. - if (S.isConstantEvaluated()) + if (S.isConstantEvaluatedContext()) return; // Check the attributes attached to the method/function itself. llvm::SmallBitVector NonNullArgs; @@ -8952,7 +8952,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; @@ -9666,7 +9666,7 @@ checkFormatStringExpr(Sema &amp;S, const Expr *E, ArrayRef&lt;const Expr *&gt; Args, llvm::SmallBitVector &amp;CheckedVarArgs, UncoveredArgHandler &amp;UncoveredArg, llvm::APSInt Offset, bool IgnoreStringsWithoutSpecifiers = false) { - if (S.isConstantEvaluated()) + if (S.isConstantEvaluatedContext()) return SLCT_NotALiteral; tryAgain: assert(Offset.isSigned() &amp;&amp; &quot;invalid offset&quot;); @@ -9706,8 +9706,8 @@ checkFormatStringExpr(Sema &amp;S, const Expr *E, ArrayRef&lt;const Expr *&gt; Args, bool CheckLeft = true, CheckRight = true; bool Cond; - if (C-&gt;getCond()-&gt;EvaluateAsBooleanCondition(Cond, S.getASTContext(), - S.isConstantEvaluated())) { + if (C-&gt;getCond()-&gt;EvaluateAsBooleanCondition( + Cond, S.getASTContext(), S.isConstantEvaluatedContext())) { if (Cond) CheckRight = false; else @@ -9971,9 +9971,11 @@ checkFormatStringExpr(Sema &amp;S, const Expr *E, ArrayRef&lt;const Expr *&gt; Args, Expr::EvalResult LResult, RResult; bool LIsInt = BinOp-&gt;getLHS()-&gt;EvaluateAsInt( - LResult, S.Context, Expr::SE_NoSideEffects, S.isConstantEvaluated()); + LResult, S.Context, Expr::SE_NoSideEffects, + S.isConstantEvaluatedContext()); bool RIsInt = BinOp-&gt;getRHS()-&gt;EvaluateAsInt( - RResult, S.Context, Expr::SE_NoSideEffects, S.isConstantEvaluated()); + RResult, S.Context, Expr::SE_NoSideEffects, + S.isConstantEvaluatedContext()); if (LIsInt != RIsInt) { BinaryOperatorKind BinOpKind = BinOp-&gt;getOpcode(); @@ -10001,7 +10003,7 @@ checkFormatStringExpr(Sema &amp;S, const Expr *E, ArrayRef&lt;const Expr *&gt; Args, Expr::EvalResult IndexResult; if (ASE-&gt;getRHS()-&gt;EvaluateAsInt(IndexResult, S.Context, Expr::SE_NoSideEffects, - S.isConstantEvaluated())) { + S.isConstantEvaluatedContext())) { sumOffsets(Offset, IndexResult.Val.getInt(), BO_Add, /*RHS is int*/ true); E = ASE-&gt;getBase(); @@ -13928,7 +13930,7 @@ static bool CheckTautologicalComparison(Sema &amp;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-&gt;getType(); if (const auto *AT = OtherT-&gt;getAs&lt;AtomicType&gt;()) @@ -14143,8 +14145,9 @@ static void AnalyzeComparison(Sema &amp;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. @@ -14162,7 +14165,7 @@ static void AnalyzeComparison(Sema &amp;S, BinaryOperator *E) { if (E-&gt;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 @@ -15026,7 +15029,7 @@ static void CheckImplicitConversion(Sema &amp;S, Expr *E, QualType T, if (Target-&gt;isUnsaturatedFixedPointType()) { Expr::EvalResult Result; if (E-&gt;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); @@ -15041,7 +15044,7 @@ static void CheckImplicitConversion(Sema &amp;S, Expr *E, QualType T, } } else if (Target-&gt;isIntegerType()) { Expr::EvalResult Result; - if (!S.isConstantEvaluated() &amp;&amp; + if (!S.isConstantEvaluatedContext() &amp;&amp; E-&gt;EvaluateAsFixedPoint(Result, S.Context, Expr::SE_AllowSideEffects)) { llvm::APFixedPoint FXResult = Result.Val.getFixedPoint(); @@ -15064,7 +15067,7 @@ static void CheckImplicitConversion(Sema &amp;S, Expr *E, QualType T, } else if (Target-&gt;isUnsaturatedFixedPointType()) { if (Source-&gt;isIntegerType()) { Expr::EvalResult Result; - if (!S.isConstantEvaluated() &amp;&amp; + if (!S.isConstantEvaluatedContext() &amp;&amp; E-&gt;EvaluateAsInt(Result, S.Context, Expr::SE_AllowSideEffects)) { llvm::APSInt Value = Result.Val.getInt(); @@ -15090,8 +15093,9 @@ static void CheckImplicitConversion(Sema &amp;S, Expr *E, QualType T, if (SourceBT &amp;&amp; TargetBT &amp;&amp; SourceBT-&gt;isIntegerType() &amp;&amp; TargetBT-&gt;isFloatingType() &amp;&amp; !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 @@ -15159,8 +15163,8 @@ static void CheckImplicitConversion(Sema &amp;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 &gt; TargetRange.Width) { @@ -15168,7 +15172,7 @@ static void CheckImplicitConversion(Sema &amp;S, Expr *E, QualType T, // TODO: this should happen for bitfield stores, too. Expr::EvalResult Result; if (E-&gt;EvaluateAsInt(Result, S.Context, Expr::SE_AllowSideEffects, - S.isConstantEvaluated())) { + S.isConstantEvaluatedContext())) { llvm::APSInt Value(32); Value = Result.Val.getInt(); @@ -16032,7 +16036,8 @@ class SequenceChecker : public ConstEvaluatedExprVisitor&lt;SequenceChecker&gt; { if (!EvalOK || E-&gt;isValueDependent()) return false; EvalOK = E-&gt;EvaluateAsBooleanCondition( - Result, Self.SemaRef.Context, Self.SemaRef.isConstantEvaluated()); + Result, Self.SemaRef.Context, + Self.SemaRef.isConstantEvaluatedContext()); return EvalOK; } @@ -17159,7 +17164,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-&gt;IgnoreParenImpCasts(); @@ -18548,7 +18553,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-&gt;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-&gt;isImmediateFunction() || isConstantEvaluated() || + !Decl-&gt;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&lt;FunctionDecl&gt;(E-&gt;getDecl())) { - if (!isUnevaluatedContext() &amp;&amp; !isConstantEvaluated() &amp;&amp; + if (!isUnevaluatedContext() &amp;&amp; !isConstantEvaluatedContext() &amp;&amp; !isImmediateFunctionContext() &amp;&amp; !isCheckingDefaultArgumentOrInitializer() &amp;&amp; FD-&gt;isImmediateFunction() &amp;&amp; !RebuildingImmediateInvocation &amp;&amp; diff --git a/clang/lib/Sema/TreeTransform.h b/clang/lib/Sema/TreeTransform.h index 603a23275889f21..39ac6e9cfbcdcce 100644 --- a/clang/lib/Sema/TreeTransform.h +++ b/clang/lib/Sema/TreeTransform.h @@ -5486,6 +5486,9 @@ TreeTransform&lt;Derived&gt;::TransformDependentSizedArrayType(TypeLocBuilder &amp;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-&gt;getSizeExpr(); diff --git a/clang/test/SemaCX... <truncated> </pre> </details> https://github.com/llvm/llvm-project/pull/66222 _______________________________________________ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits