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

Reply via email to