llvmorg-github-actions[bot] wrote:
<!--LLVM PR SUMMARY COMMENT--> @llvm/pr-subscribers-clang Author: Singl (Singl-Daemon) <details> <summary>Changes</summary> `Sema::ActOnStartOfFunctionDef` pushed the *kind* of the enclosing expression evaluation context (`ExprEvalContexts.back().Context`) for the body of a nested function definition. When a member function of a local class is defined inside a consteval function, or inside the compound-statement of a `consteval if`, that kind is `ImmediateFunctionContext`, so `Sema::CheckForImmediateInvocation` treated every consteval call in the member function as if it were in an immediate function context and never checked it. The ill-formed calls then reached CodeGen unevaluated, tripping the `"trying to emit a call to an immediate function"` assertion (or, in release builds, emitting a real call to the consteval function). Per [expr.const]p14 a function body is in an immediate function context only when the function's own parameter scope belongs to an immediate function; being lexically nested in one does not count. The template instantiation path (`Sema::InstantiateFunctionDefinition`) already passes `PotentiallyEvaluated`, so the same code inside a function template was diagnosed correctly; this makes the non-template path consistent with it. Fix: map an inherited `ImmediateFunctionContext` to `PotentiallyEvaluated` before calling `PushExpressionEvaluationContextForFunction`, which still selects `ImmediateFunctionContext` for consteval functions themselves. Other inherited kinds (e.g. `DiscardedStatement`) are left untouched. This changes accepts-invalid into a diagnostic (`call to consteval function 'h' is not a constant expression`), matching GCC; the release note is therefore in the "C++ Specific Potentially Breaking Changes" section. Code that only used such a member function during constant evaluation compiled before and is rejected now, as required by the standard. Fixes #<!-- -->220226 --- Full diff: https://github.com/llvm/llvm-project/pull/220235.diff 3 Files Affected: - (modified) clang/docs/ReleaseNotes.md (+7) - (modified) clang/lib/Sema/SemaDecl.cpp (+11-3) - (modified) clang/test/SemaCXX/cxx2b-consteval-propagate.cpp (+55) ``````````diff diff --git a/clang/docs/ReleaseNotes.md b/clang/docs/ReleaseNotes.md index 0376f18c3d93f..942371181e1a2 100644 --- a/clang/docs/ReleaseNotes.md +++ b/clang/docs/ReleaseNotes.md @@ -54,6 +54,13 @@ in a future version of Clang. ### C++ Specific Potentially Breaking Changes +- Clang now rejects calls to consteval functions with non-constant arguments + inside member functions of a local class defined within a consteval function + or within the compound-statement of a `consteval if`. Such bodies were + previously treated as an immediate function context, which silently accepted + the ill-formed calls and crashed CodeGen when the member function was + emitted. (#GH220226) + ### Objective-C Specific Potentially Breaking Changes - Fixed an issue where AST consumers based on `RecursiveASTVisitor` would bypass diff --git a/clang/lib/Sema/SemaDecl.cpp b/clang/lib/Sema/SemaDecl.cpp index 07c6157ab8f31..0f22a077c13cc 100644 --- a/clang/lib/Sema/SemaDecl.cpp +++ b/clang/lib/Sema/SemaDecl.cpp @@ -16450,9 +16450,17 @@ Decl *Sema::ActOnStartOfFunctionDef(Scope *FnBodyScope, Decl *D, // Do not push if it is a lambda because one is already pushed when building // the lambda in ActOnStartOfLambdaDefinition(). - if (!isLambdaCallOperator(FD)) - PushExpressionEvaluationContextForFunction(ExprEvalContexts.back().Context, - FD); + if (!isLambdaCallOperator(FD)) { + // C++23 [expr.const]p14: a function body is in an immediate function + // context only if the function itself is an immediate function; it does + // not inherit that property from a lexically enclosing consteval function + // or consteval-if block (e.g. a member function of a local class). + // PushExpressionEvaluationContextForFunction handles consteval FDs. + ExpressionEvaluationContext Ctx = ExprEvalContexts.back().Context; + if (Ctx == ExpressionEvaluationContext::ImmediateFunctionContext) + Ctx = ExpressionEvaluationContext::PotentiallyEvaluated; + PushExpressionEvaluationContextForFunction(Ctx, FD); + } // Check for defining attributes before the check for redefinition. if (const auto *Attr = FD->getAttr<AliasAttr>()) { diff --git a/clang/test/SemaCXX/cxx2b-consteval-propagate.cpp b/clang/test/SemaCXX/cxx2b-consteval-propagate.cpp index 39097d17441f7..6e9493bfa2ae6 100644 --- a/clang/test/SemaCXX/cxx2b-consteval-propagate.cpp +++ b/clang/test/SemaCXX/cxx2b-consteval-propagate.cpp @@ -659,3 +659,58 @@ scope_exit guard( // expected-note {{in instantiation of member function}} ); } + +namespace GH220226 { +// A function body is in an immediate function context only if the function +// itself is an immediate function ([expr.const]p14); being lexically nested in +// a consteval function or in the compound-statement of a consteval if does not +// make the body of a local class member function an immediate function context. +consteval int local_h(int x) { return x; } +struct FnPtrHolder { int (*fp)(int); }; + +consteval FnPtrHolder make_local() { + struct S { + static int g(int x) { // expected-note {{declared here}} + return local_h(x); // expected-error {{call to consteval function 'GH220226::local_h' is not a constant expression}} \ + // expected-note {{function parameter 'x' with unknown value cannot be used in a constant expression}} + } + }; + return FnPtrHolder{&S::g}; +} + +struct Outer { + consteval FnPtrHolder member() { + struct S { + static int g(int x) { // expected-note {{declared here}} + return local_h(x); // expected-error {{call to consteval function 'GH220226::local_h' is not a constant expression}} \ + // expected-note {{function parameter 'x' with unknown value cannot be used in a constant expression}} + } + }; + return FnPtrHolder{&S::g}; + } +}; + +consteval int local_ok(int n) { + struct S { + static constexpr int g() { return local_h(3); } + consteval int k(int x) const { return local_h(x); } + }; + return S::g() + S{}.k(n); +} +static_assert(local_ok(4) == 7); + +#if __cplusplus >= 202302L +constexpr FnPtrHolder make_local_if_consteval() { + if consteval { + struct S { + static int g(int x) { // expected-note {{declared here}} + return local_h(x); // expected-error {{call to consteval function 'GH220226::local_h' is not a constant expression}} \ + // expected-note {{function parameter 'x' with unknown value cannot be used in a constant expression}} + } + }; + return FnPtrHolder{&S::g}; + } + return FnPtrHolder{nullptr}; +} +#endif +} `````````` </details> https://github.com/llvm/llvm-project/pull/220235 _______________________________________________ cfe-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
