llvmorg-github-actions[bot] wrote:
<!--LLVM PR SUMMARY COMMENT-->
@llvm/pr-subscribers-clang
Author: Stanislav Bardyuk (kodlan)
<details>
<summary>Changes</summary>
When a function template contains a temporary built with a consteval
constructor, like `(void)S{1}`, Sema wraps the `CXXTemporaryObjectExpr` in a
`ConstantExpr` marked as an immediate invocation and caches the value.
`TreeTransform::TransformConstantExpr` drops that wrapper on purpose and
expects the subexpression to be rebuilt through Sema, which re-creates it. But
when the type, constructor and arguments all come out of the instantiation
unchanged, `TransformCXXTemporaryObjectExpr` takes its reuse shortcut and
returns the bare node, so the instantiated function ends up with an unwrapped
call to the consteval constructor. CodeGen then emits it (and the constructor
body), which trips the "trying to emit a call to an immediate function"
assertion, or without assertions produces a real call to a consteval function
and a link error.
```cpp
consteval void f() {}
struct S { consteval S(int) { f(); } };
template <typename T> void g(T) { (void)S{1}; }
template void g<int>(int);
```
This runs the reused node through `CheckForImmediateInvocation` before
`MaybeBindToTemporary`, the same two steps `InitializationSequence::Perform`
does when it creates the node, so it gets the same wrapper a rebuilt one would
get (and the destructor cleanup of the temporary stays outside the
`ConstantExpr`, so it is still emitted). Non-consteval constructors are
unaffected since `CheckForImmediateInvocation` returns its input untouched for
them.
Besides the braced one-argument form from the issue, the same shortcut is
reached for `S(1, 2)`, `S{}`, and the same expressions inside generic lambdas
and class template members, so the test covers those too. `S(1)` with a single
parenthesized argument is a `CXXFunctionalCastExpr` and was always rebuilt,
which is why the reporter saw it working.
The reuse shortcut in `TransformCallExpr` has the same problem for a consteval
member function called on a non-dependent object (`g.m()` with `constexpr S g;`
inside a template still reaches CodeGen bare, also in clang 18). That is a
separate shape and is left for a follow-up.
Fixes #<!-- -->219272
---
Full diff: https://github.com/llvm/llvm-project/pull/221816.diff
3 Files Affected:
- (modified) clang/docs/ReleaseNotes.md (+3)
- (modified) clang/lib/Sema/TreeTransform.h (+6-1)
- (modified) clang/test/CodeGenCXX/cxx20-consteval-crash.cpp (+47)
``````````diff
diff --git a/clang/docs/ReleaseNotes.md b/clang/docs/ReleaseNotes.md
index a49971adef86f..d223e5a924c5e 100644
--- a/clang/docs/ReleaseNotes.md
+++ b/clang/docs/ReleaseNotes.md
@@ -645,6 +645,9 @@ features cannot lower the translation-unit ABI level;
(#GH214128)
- Fixed a crash when a coroutine keyword appeared inside a mem-initializer on a
function that is not a constructor. (#GH194298)
+- Fixed a crash (and, without assertions, a call to a consteval function being
+ emitted) when a temporary created with a `consteval` constructor, such as
+ `S{1}`, was instantiated from a function template. (#GH219272)
#### Bug Fixes to AST Handling
diff --git a/clang/lib/Sema/TreeTransform.h b/clang/lib/Sema/TreeTransform.h
index c8458fda58a88..75eb954163ab5 100644
--- a/clang/lib/Sema/TreeTransform.h
+++ b/clang/lib/Sema/TreeTransform.h
@@ -16183,7 +16183,12 @@
TreeTransform<Derived>::TransformCXXTemporaryObjectExpr(
!ArgumentChanged) {
// FIXME: Instantiation-specific
SemaRef.MarkFunctionReferenced(E->getBeginLoc(), Constructor);
- return SemaRef.MaybeBindToTemporary(E);
+ // The immediate-invocation wrapper was stripped by TransformConstantExpr;
+ // put it back before binding the temporary, as SemaInit does.
+ ExprResult Res = SemaRef.CheckForImmediateInvocation(E, Constructor);
+ if (Res.isInvalid())
+ return ExprError();
+ return SemaRef.MaybeBindToTemporary(Res.get());
}
SourceLocation LParenLoc = T->getTypeLoc().getEndLoc();
diff --git a/clang/test/CodeGenCXX/cxx20-consteval-crash.cpp
b/clang/test/CodeGenCXX/cxx20-consteval-crash.cpp
index 9c9324f428bec..f5c01729559ca 100644
--- a/clang/test/CodeGenCXX/cxx20-consteval-crash.cpp
+++ b/clang/test/CodeGenCXX/cxx20-consteval-crash.cpp
@@ -140,3 +140,50 @@ void b() {
// CHECK-NOT: define {{.*}}foo{{.*}}()
} // namespace GH61142
+
+namespace GH219272 {
+
+consteval void f() {}
+void g();
+
+struct S {
+ consteval S() { f(); }
+ consteval S(int) { f(); }
+ consteval S(int, int) { f(); }
+};
+
+struct D {
+ consteval D(int) { f(); }
+ constexpr ~D() {
+ if (!__builtin_is_constant_evaluated())
+ g();
+ }
+};
+
+template <typename T> void dtor(T) { (void)D{1}; }
+template <typename T> void braces(T) { (void)S{1}; }
+template <typename T> void parens(T) { (void)S(1, 2); }
+template <typename T> void empty_braces(T) { (void)S{}; }
+template <typename T> void lambda(T) { [](auto) { (void)S{1}; }(0); }
+template <typename T> struct C {
+ void m() { (void)S{1}; }
+};
+template <typename T> void member(T) { C<T>{}.m(); }
+
+template void dtor<int>(int);
+template void braces<int>(int);
+template void parens<int>(int);
+template void empty_braces<int>(int);
+template void lambda<int>(int);
+template void member<int>(int);
+
+// The temporary is constant-evaluated, but its destructor still runs.
+// CHECK: define {{.*}} @_ZN8GH2192724dtorIiEEvT_(
+// CHECK-NOT: call {{.*}}GH2192721DC
+// CHECK: call void @_ZN8GH2192721DD1Ev(
+
+// Make sure the consteval constructors are neither called nor emitted.
+// CHECK-NOT: call {{.*}}GH2192721{{S|D}}C
+// CHECK-NOT: define {{.*}}GH2192721{{S|D}}C
+
+} // namespace GH219272
``````````
</details>
https://github.com/llvm/llvm-project/pull/221816
_______________________________________________
cfe-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits