Skip to content

[Clang] Preserve CXXParenListInitExpr in TreeTransform. #138518

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 2 commits into from
May 5, 2025

Conversation

cor3ntin
Copy link
Contributor

@cor3ntin cor3ntin commented May 5, 2025

We were converting a CXXParenListInitExpr to a ParenListExpr in TreeTransform.

However, ParenListExpr is typeless, so Clang could not rebuild the correct initialization sequence in some contexts.

Fixes #72880

We were converting a CXXParenListInitExpr to a ParenListExpr
in Treetransform.

However, ParenListExpr is typeless so clang could not rebuild
the correct initialization sequence in some contexts.

Fixes 72880
@llvmbot llvmbot added clang Clang issues not falling into any other category clang:frontend Language frontend issues, e.g. anything involving "Sema" labels May 5, 2025
@llvmbot
Copy link
Member

llvmbot commented May 5, 2025

@llvm/pr-subscribers-clang

Author: cor3ntin (cor3ntin)

Changes

We were converting a CXXParenListInitExpr to a ParenListExpr in TreeTransform.

However, ParenListExpr is typeless, so Clang could not rebuild the correct initialization sequence in some contexts.

#Fixes 72880


Full diff: https://github.com/llvm/llvm-project/pull/138518.diff

6 Files Affected:

  • (modified) clang/docs/ReleaseNotes.rst (+1)
  • (modified) clang/include/clang/AST/ExprCXX.h (+2-2)
  • (modified) clang/include/clang/Sema/Sema.h (+5)
  • (modified) clang/lib/Sema/SemaExpr.cpp (+9)
  • (modified) clang/lib/Sema/TreeTransform.h (+28-4)
  • (modified) clang/test/SemaCXX/paren-list-agg-init.cpp (+32-5)
diff --git a/clang/docs/ReleaseNotes.rst b/clang/docs/ReleaseNotes.rst
index 4bd9d904e1ea9..55a46fb6f947e 100644
--- a/clang/docs/ReleaseNotes.rst
+++ b/clang/docs/ReleaseNotes.rst
@@ -642,6 +642,7 @@ Bug Fixes to C++ Support
   (#GH136432), (#GH137014), (#GH138018)
 - Fixed an assertion when trying to constant-fold various builtins when the argument
   referred to a reference to an incomplete type. (#GH129397)
+- Fixed a crash when a cast involved a parenthesized aggregate initialization in dependent context. (#GH72880)
 
 Bug Fixes to AST Handling
 ^^^^^^^^^^^^^^^^^^^^^^^^^
diff --git a/clang/include/clang/AST/ExprCXX.h b/clang/include/clang/AST/ExprCXX.h
index 844f6dd90ae1d..04d08b022c562 100644
--- a/clang/include/clang/AST/ExprCXX.h
+++ b/clang/include/clang/AST/ExprCXX.h
@@ -5124,8 +5124,8 @@ class CXXParenListInitExpr final
 
   void updateDependence() { setDependence(computeDependence(this)); }
 
-  ArrayRef<Expr *> getInitExprs() {
-    return ArrayRef(getTrailingObjects<Expr *>(), NumExprs);
+  MutableArrayRef<Expr *> getInitExprs() {
+    return MutableArrayRef(getTrailingObjects<Expr *>(), NumExprs);
   }
 
   const ArrayRef<Expr *> getInitExprs() const {
diff --git a/clang/include/clang/Sema/Sema.h b/clang/include/clang/Sema/Sema.h
index 19343eb0af092..741951cb9ea0e 100644
--- a/clang/include/clang/Sema/Sema.h
+++ b/clang/include/clang/Sema/Sema.h
@@ -7167,6 +7167,11 @@ class Sema final : public SemaBase {
   ExprResult ActOnParenExpr(SourceLocation L, SourceLocation R, Expr *E);
   ExprResult ActOnParenListExpr(SourceLocation L, SourceLocation R,
                                 MultiExprArg Val);
+  ExprResult ActOnCXXParenListInitExpr(ArrayRef<Expr *> Args, QualType T,
+                                       unsigned NumUserSpecifiedExprs,
+                                       SourceLocation InitLoc,
+                                       SourceLocation LParenLoc,
+                                       SourceLocation RParenLoc);
 
   /// ActOnStringLiteral - The specified tokens were lexed as pasted string
   /// fragments (e.g. "foo" "bar" L"baz").  The result string has to handle
diff --git a/clang/lib/Sema/SemaExpr.cpp b/clang/lib/Sema/SemaExpr.cpp
index d72d97addfac2..787b07c4080ea 100644
--- a/clang/lib/Sema/SemaExpr.cpp
+++ b/clang/lib/Sema/SemaExpr.cpp
@@ -7985,6 +7985,15 @@ ExprResult Sema::ActOnParenListExpr(SourceLocation L,
   return ParenListExpr::Create(Context, L, Val, R);
 }
 
+ExprResult Sema::ActOnCXXParenListInitExpr(ArrayRef<Expr *> Args, QualType T,
+                                           unsigned NumUserSpecifiedExprs,
+                                           SourceLocation InitLoc,
+                                           SourceLocation LParenLoc,
+                                           SourceLocation RParenLoc) {
+  return CXXParenListInitExpr::Create(Context, Args, T, NumUserSpecifiedExprs,
+                                      InitLoc, LParenLoc, RParenLoc);
+}
+
 bool Sema::DiagnoseConditionalForNull(const Expr *LHSExpr, const Expr *RHSExpr,
                                       SourceLocation QuestionLoc) {
   const Expr *NullExpr = LHSExpr;
diff --git a/clang/lib/Sema/TreeTransform.h b/clang/lib/Sema/TreeTransform.h
index aed00e0ff06cd..a3120f61f0d9c 100644
--- a/clang/lib/Sema/TreeTransform.h
+++ b/clang/lib/Sema/TreeTransform.h
@@ -3099,6 +3099,15 @@ class TreeTransform {
     return getSema().ActOnParenListExpr(LParenLoc, RParenLoc, SubExprs);
   }
 
+  ExprResult RebuildCXXParenListInitExpr(ArrayRef<Expr *> Args, QualType T,
+                                         unsigned NumUserSpecifiedExprs,
+                                         SourceLocation InitLoc,
+                                         SourceLocation LParenLoc,
+                                         SourceLocation RParenLoc) {
+    return getSema().ActOnCXXParenListInitExpr(Args, T, NumUserSpecifiedExprs,
+                                               InitLoc, LParenLoc, RParenLoc);
+  }
+
   /// Build a new address-of-label expression.
   ///
   /// By default, performs semantic analysis, using the name of the label
@@ -3315,6 +3324,11 @@ class TreeTransform {
       return getSema().BuildCXXTypeConstructExpr(
           TInfo, LParenLoc, MultiExprArg(PLE->getExprs(), PLE->getNumExprs()),
           RParenLoc, ListInitialization);
+
+    if (auto *PLE = dyn_cast<CXXParenListInitExpr>(Sub))
+      return getSema().BuildCXXTypeConstructExpr(
+          TInfo, LParenLoc, PLE->getInitExprs(), RParenLoc, ListInitialization);
+
     return getSema().BuildCXXTypeConstructExpr(TInfo, LParenLoc,
                                                MultiExprArg(&Sub, 1), RParenLoc,
                                                ListInitialization);
@@ -16487,12 +16501,22 @@ ExprResult
 TreeTransform<Derived>::TransformCXXParenListInitExpr(CXXParenListInitExpr *E) {
   SmallVector<Expr *, 4> TransformedInits;
   ArrayRef<Expr *> InitExprs = E->getInitExprs();
-  if (TransformExprs(InitExprs.data(), InitExprs.size(), true,
-                     TransformedInits))
+
+  QualType T = getDerived().TransformType(E->getType());
+
+  bool ArgChanged = false;
+  ;
+
+  if (getDerived().TransformExprs(InitExprs.data(), InitExprs.size(), true,
+                                  TransformedInits, &ArgChanged))
     return ExprError();
 
-  return getDerived().RebuildParenListExpr(E->getBeginLoc(), TransformedInits,
-                                           E->getEndLoc());
+  if (!ArgChanged && T == E->getType())
+    return E;
+
+  return getDerived().RebuildCXXParenListInitExpr(
+      TransformedInits, T, E->getUserSpecifiedInitExprs().size(),
+      E->getInitLoc(), E->getBeginLoc(), E->getEndLoc());
 }
 
 template<typename Derived>
diff --git a/clang/test/SemaCXX/paren-list-agg-init.cpp b/clang/test/SemaCXX/paren-list-agg-init.cpp
index b2d0f2564bb59..ba7dffdc1af9f 100644
--- a/clang/test/SemaCXX/paren-list-agg-init.cpp
+++ b/clang/test/SemaCXX/paren-list-agg-init.cpp
@@ -83,7 +83,7 @@ template <typename T, char CH>
 void bar() {
   T t = 0;
   A a(CH, 1.1); // OK; C++ paren list constructors are supported in semantic tree transformations.
-  // beforecxx20-warning@-1 2{{aggregate initialization of type 'A' from a parenthesized list of values is a C++20 extension}}
+  // beforecxx20-warning@-1 {{aggregate initialization of type 'A' from a parenthesized list of values is a C++20 extension}}
 }
 
 template <class T, class... Args>
@@ -157,9 +157,6 @@ void foo(int n) { // expected-note {{declared here}}
   constexpr F f2(1, 1); // OK: f2.b is initialized by a constant expression.
   // beforecxx20-warning@-1 {{aggregate initialization of type 'const F' from a parenthesized list of values is a C++20 extension}}
 
-  bar<int, 'a'>();
-  // beforecxx20-note@-1 {{in instantiation of function template specialization 'bar<int, 'a'>' requested here}}
-
   G<char> g('b', 'b');
   // beforecxx20-warning@-1 {{aggregate initialization of type 'G<char>' from a parenthesized list of values is a C++20 extension}}
 
@@ -354,7 +351,7 @@ using Td = int[]; Td d(42,43);
 // beforecxx20-warning@-1 {{aggregate initialization of type 'int[2]' from a parenthesized list of values is a C++20 extension}}
 template<typename T, int Sz> using ThroughAlias = T[Sz];
 ThroughAlias<int, 1> e(42);
-// beforecxx20-warning@-1 {{aggregate initialization of type 'ThroughAlias<int, 1>' (aka 'int[1]') from a parenthesized list of values is a C++20 extension}} 
+// beforecxx20-warning@-1 {{aggregate initialization of type 'ThroughAlias<int, 1>' (aka 'int[1]') from a parenthesized list of values is a C++20 extension}}
 
 }
 
@@ -376,3 +373,33 @@ static_assert(S(1, 2) == S(3, 4));
 // beforecxx20-warning@-1 2{{C++20 extension}}
 
 }
+
+namespace GH72880 {
+struct Base {};
+struct Derived : Base {
+    int count = 42;
+};
+
+template <typename T>
+struct BaseTpl {};
+template <typename T>
+struct DerivedTpl : BaseTpl<T> {
+    int count = 43;
+};
+template <typename T> struct S {
+  void f() {
+      Derived a = static_cast<Derived>(Base());
+      // beforecxx20-warning@-1 {{C++20 extension}}
+      DerivedTpl b = static_cast<DerivedTpl<T>>(BaseTpl<T>());
+      // beforecxx20-warning@-1 {{C++20 extension}}
+      static_assert(static_cast<Derived>(Base()).count == 42);
+      // beforecxx20-warning@-1 {{C++20 extension}}
+      static_assert(static_cast<DerivedTpl<T>>(BaseTpl<T>()).count == 43);
+      // beforecxx20-warning@-1 {{C++20 extension}}
+  }
+};
+
+void test() {
+    S<int>{}.f(); // beforecxx20-note {{requested here}}
+}
+}

Copy link
Collaborator

@erichkeane erichkeane left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please change the commit message to do Fixes # instead of the reverse, so this actually closes the bug :)

QualType T = getDerived().TransformType(E->getType());

bool ArgChanged = false;
;
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

extra semicolon for no reason?

Comment on lines 16514 to 16515
if (!ArgChanged && T == E->getType())
return E;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do we want to handle AlwaysRebuild?

@cor3ntin cor3ntin merged commit 13926e1 into llvm:main May 5, 2025
12 checks passed
@cor3ntin cor3ntin deleted the gh72880 branch May 5, 2025 16:46
IanWood1 pushed a commit to IanWood1/llvm-project that referenced this pull request May 6, 2025
We were converting a CXXParenListInitExpr to a ParenListExpr in
TreeTransform.

However, ParenListExpr is typeless, so Clang could not rebuild the
correct initialization sequence in some contexts.

Fixes llvm#72880
IanWood1 pushed a commit to IanWood1/llvm-project that referenced this pull request May 6, 2025
We were converting a CXXParenListInitExpr to a ParenListExpr in
TreeTransform.

However, ParenListExpr is typeless, so Clang could not rebuild the
correct initialization sequence in some contexts.

Fixes llvm#72880
IanWood1 pushed a commit to IanWood1/llvm-project that referenced this pull request May 6, 2025
We were converting a CXXParenListInitExpr to a ParenListExpr in
TreeTransform.

However, ParenListExpr is typeless, so Clang could not rebuild the
correct initialization sequence in some contexts.

Fixes llvm#72880
GeorgeARM pushed a commit to GeorgeARM/llvm-project that referenced this pull request May 7, 2025
We were converting a CXXParenListInitExpr to a ParenListExpr in
TreeTransform.

However, ParenListExpr is typeless, so Clang could not rebuild the
correct initialization sequence in some contexts.

Fixes llvm#72880
@alexfh
Copy link
Contributor

alexfh commented May 23, 2025

@cor3ntin this commit causes an assertion failure: https://gcc.godbolt.org/z/7hW4ssrfq

$ cat test.cc
struct E {
  virtual void f();
};
struct G {
  E e;
};
template <typename>
struct Test {
  virtual void f() {
    E e;
    G g(e);
  }
};
auto *t = new Test<int>;

$ ./clang-bad -c -o /dev/null -std=c++20 test.cc
assertion failed at clang/lib/CodeGen/CGExprAgg.cpp:2303 in void clang::CodeGen::CodeGenFunction::EmitAggregateCopy(LValue, LValue, QualType, AggValueSlot::Overlap_t, bool): (Record->hasTrivialCopyConstructor() || Record->hasTrivialCopyAssignment() || Record->hasTrivialMoveConstructor() || Record->hasTrivialMoveAssignment() || Record->hasAttr<TrivialABIAttr>() || Record->isUnion()) && "Trying to aggregate-copy a type without a trivial copy/move " "constructor or assignment operator"
    @     0x55e4e4e3cfe4  __assert_fail
    @     0x55e4df16f247  clang::CodeGen::CodeGenFunction::EmitAggregateCopy()
    @     0x55e4df175f3e  (anonymous namespace)::AggExprEmitter::EmitCopy()
    @     0x55e4df16e4b6  (anonymous namespace)::AggExprEmitter::EmitFinalDestCopy()
    @     0x55e4df1762dc  (anonymous namespace)::AggExprEmitter::EmitAggLoadOfLValue()
    @     0x55e4df16de02  clang::CodeGen::CodeGenFunction::EmitAggExpr()
    @     0x55e4df00e593  clang::CodeGen::CodeGenFunction::EmitInitializationToLValue()
    @     0x55e4df177fea  (anonymous namespace)::AggExprEmitter::VisitCXXParenListOrInitListExpr()
    @     0x55e4df16de02  clang::CodeGen::CodeGenFunction::EmitAggExpr()
    @     0x55e4df08640c  clang::CodeGen::CodeGenFunction::EmitExprAsInit()
    @     0x55e4df08314b  clang::CodeGen::CodeGenFunction::EmitAutoVarInit()
    @     0x55e4df07bfe7  clang::CodeGen::CodeGenFunction::EmitVarDecl()
    @     0x55e4df07ba62  clang::CodeGen::CodeGenFunction::EmitDecl()
    @     0x55e4df115940  clang::CodeGen::CodeGenFunction::EmitDeclStmt()
    @     0x55e4df10a5de  clang::CodeGen::CodeGenFunction::EmitSimpleStmt()
    @     0x55e4df10983f  clang::CodeGen::CodeGenFunction::EmitStmt()
    @     0x55e4df116d61  clang::CodeGen::CodeGenFunction::EmitCompoundStmtWithoutScope()
    @     0x55e4df2725f4  clang::CodeGen::CodeGenFunction::GenerateCode()
    @     0x55e4df29cd88  clang::CodeGen::CodeGenModule::EmitGlobalFunctionDefinition()
    @     0x55e4df293f22  clang::CodeGen::CodeGenModule::EmitGlobalDefinition()
    @     0x55e4df284de2  clang::CodeGen::CodeGenModule::EmitDeferred()
    @     0x55e4df284dfe  clang::CodeGen::CodeGenModule::EmitDeferred()
    @     0x55e4df284dfe  clang::CodeGen::CodeGenModule::EmitDeferred()
    @     0x55e4df281a2d  clang::CodeGen::CodeGenModule::Release()
    @     0x55e4df3c750e  (anonymous namespace)::CodeGeneratorImpl::HandleTranslationUnit()
    @     0x55e4def2ef1a  clang::BackendConsumer::HandleTranslationUnit()
    @     0x55e4dfe45d68  clang::ParseAST()
    @     0x55e4dfb7e7aa  clang::FrontendAction::Execute()
    @     0x55e4dfaf319d  clang::CompilerInstance::ExecuteAction()
    @     0x55e4def2e4ab  clang::ExecuteCompilerInvocation()
    @     0x55e4def21f17  cc1_main()

Actually, I can reproduce the assertion failure in Clang before this commit using a different example, but I haven't yet reduced it. It might be a distinct problem though despite the same assertion failure.

@alexfh
Copy link
Contributor

alexfh commented May 23, 2025

@cor3ntin yep, the second test case I have doesn't seem to be directly related. It causes crashes to Clang since version 7: https://gcc.godbolt.org/z/6M13c4q3T

@alexfh
Copy link
Contributor

alexfh commented May 23, 2025

I've filed #141242 for the second report. However #138518 (comment) is new and still needs to be addressed.

@alexfh
Copy link
Contributor

alexfh commented May 24, 2025

Apart from the assertion failure in CodeGen, we have two distinct test cases that started failing after this commit. The code in both cases is sanitizer-clean and looks valid. The failures reproduce with -O0, so it's not about optimizations. Looks like Clang codegen is broken for these cases. Reducing the test cases is not as easy as with Clang assertion failures, but I'll try to produce a test case early next week. So far I just managed to compare x86 assembly produced by clang -O0 without this commit and with it. The diff boils down to a few instances of

-       movq    $0, (%rsi)
+       movl    $0, (%rsi)

In IR the only diffs are (--- before +++ after this commit):

   %73 = getelementptr inbounds nuw %"SomeType", ptr %69, i32 0, i32 0
-  store i64 0, ptr %73, align 8
+  store i32 0, ptr %73, align 8
   %74 = getelementptr inbounds nuw %"SomeType", ptr %69, i32 0, i32 1
   store ptr null, ptr %74, align 8

@alexfh
Copy link
Contributor

alexfh commented May 24, 2025

@cor3ntin ^^

@alexfh
Copy link
Contributor

alexfh commented May 24, 2025

Reduced test case (hopefully, no important bits were lost during automated reduction):

template <class _Fn>
void invoke(_Fn __f) {
  __f();
}
struct Duration {
  int lo_;
  int rep_hi_;
  int rep_lo_;
};
Duration Seconds(int);
struct Time {
  friend bool operator<(Time, Time);
  Duration rep_;
};
Time operator+(Time, Duration);
Time Now();
struct Node {
  long val;
  Node *n;
};
struct IntrusiveMPSCQueue {
  void Push(Node *);
  int notifier_;
};
int *L;
template <class>
struct C {};
struct CoreImpl {
  template <class Q>
  CoreImpl(C<Q>) {
    (void)invoke(Q(L));
  }
};
struct AnyInvocable : CoreImpl {
  template <class F>
  AnyInvocable(F f) : CoreImpl(C<F &&>()) {}
};
void S(AnyInvocable);
template <int SleepWhenEmpty>
void BatchPull() {
  IntrusiveMPSCQueue q;
  S([&] {
    Time stop = Now() + Seconds(3);
    long i;
    for (long j; j; ++j) q.Push(new Node(++i));
    while (Now() < stop);
    q.Push(new Node(0));
  });
}
void TestBody() { (void)BatchPull<true>; }
$ diff -u10 <(./clang-good -fnew-alignment=8 -fsanitize=alignment,null -O0 -std=c++20 test.cc -emit-llvm -S -o -) <(./clang-bad -fnew-alignment=8 -fsanitize=alignment,null -O0 -std=c++20 test.cc -emit-llvm -S -o -)
--- /dev/fd/63  2025-05-24 07:54:29.833789023 +0000
+++ /dev/fd/62  2025-05-24 07:54:29.833789023 +0000
@@ -275,21 +275,21 @@
   %91 = icmp eq i64 %90, 0, !nosanitize !4
   %92 = and i1 %88, %91, !nosanitize !4
   br i1 %92, label %94, label %93, !prof !5, !nosanitize !4

 93:                                               ; preds = %86
   call void @__ubsan_handle_type_mismatch_v1(ptr @13, i64 %89) #6, !nosanitize !4
   br label %94, !nosanitize !4

 94:                                               ; preds = %93, %86
   %95 = getelementptr inbounds nuw %struct.Node, ptr %87, i32 0, i32 0
-  store i64 0, ptr %95, align 8
+  store i32 0, ptr %95, align 8
   %96 = getelementptr inbounds nuw %struct.Node, ptr %87, i32 0, i32 1
   store ptr null, ptr %96, align 8
   call void @_ZN18IntrusiveMPSCQueue4PushEP4Node(ptr noundef nonnull align 4 dereferenceable(4) %81, ptr noundef %87)
   ret void
 }

 declare dso_local { i64, i32 } @_Zpl4Time8Duration(i64, i32, i64, i32) #2

 declare dso_local { i64, i32 } @_Z3Nowv() #2

cor3ntin added a commit to cor3ntin/llvm-project that referenced this pull request May 24, 2025
We did not handle the case where a variable could be
initialized by a CXXParenListInitExpr.
cor3ntin added a commit to cor3ntin/llvm-project that referenced this pull request May 24, 2025
We did not handle the case where a variable could be
initialized by a CXXParenListInitExpr.
cor3ntin added a commit that referenced this pull request May 28, 2025
We did not handle the case where a variable could be initialized by a
CXXParenListInitExpr.

---------

Co-authored-by: Shafik Yaghmour <[email protected]>
@alexfh
Copy link
Contributor

alexfh commented May 28, 2025

@cor3ntin thank you for the fix for the assertion failure! The problem described in #138518 (comment) still remains though.

@alexfh
Copy link
Contributor

alexfh commented Jun 2, 2025

@cor3ntin friendly ping. Please take a look at the remaining issue here. Thanks!

@cor3ntin
Copy link
Contributor Author

cor3ntin commented Jun 2, 2025

@alexfh I don't have the resources to look into this issue. Please revert if you need to.

@erichkeane
Copy link
Collaborator

@alexfh I don't have the resources to look into this issue. Please revert if you need to.

This has been in trunk for a long time, a revert here would be HIGHLY unfortunate. I would vastly prefer someone spend time trying to figure out why we are confused with the variable init here instead. So:

q.Push(new Node(0));

Seems to be the line that isn't producing the same value. We're initializing it with an int instead of a 64-bit-long. I see here:
https://godbolt.org/z/KofsM8hdj

|               `-CXXMemberCallExpr <line:47:5, col:23> 'void'
|                 |-MemberExpr <col:5, col:7> '<bound member function type>' .Push 0x28726e38
|                 | `-DeclRefExpr <col:5> 'IntrusiveMPSCQueue' lvalue Var 0x28735d18 'q' 'IntrusiveMPSCQueue' refers_to_enclosing_variable_or_capture
|                 `-CXXNewExpr <col:12, col:22> 'Node *' Function 0x28731c68 'operator new' 'void *(unsigned long)'
|                   `-CXXParenListInitExpr <col:20, col:22> 'Node'
|                     |-IntegerLiteral <col:21> 'int' 0

IN a non-template version (i removed the template head itself), we get:

|               `-CXXNewExpr <col:12, col:22> 'Node *' Function 0x22566848 'operator new' 'void *(unsigned long)'
|                 `-CXXParenListInitExpr <col:20, col:22> 'Node'
|                   |-ImplicitCastExpr <col:21> 'long' <IntegralCast>
|                   | `-IntegerLiteral <col:21> 'int' 0
|                   `-ImplicitValueInitExpr 'Node *'

In the primary template we have:

| |       |       `-CXXNewExpr <col:12, col:22> 'Node *' Function 0x22566848 'operator new' 'void *(unsigned long)'
| |       |         `-CXXParenListInitExpr <col:20, col:22> 'Node'
| |       |           |-ImplicitCastExpr <col:21> 'long' <IntegralCast>
| |       |           | `-IntegerLiteral <col:21> 'int' 0

SO something about instantiating the expressions lost their cast? I'm not sure how that happens?

sivan-shani pushed a commit to sivan-shani/llvm-project that referenced this pull request Jun 3, 2025
We did not handle the case where a variable could be initialized by a
CXXParenListInitExpr.

---------

Co-authored-by: Shafik Yaghmour <[email protected]>
@alexfh
Copy link
Contributor

alexfh commented Jun 5, 2025

@alexfh I don't have the resources to look into this issue. Please revert if you need to.

This has been in trunk for a long time, a revert here would be HIGHLY unfortunate. I would vastly prefer someone spend time trying to figure out why we are confused with the variable init here instead.

It would be really unfortunate indeed, if we have to revert this. But that's a clearly incorrect codegen, so the only alternative is to get a fix.

I looked a bit into this and came up with a slightly more compact test case: https://gcc.godbolt.org/z/Pcj8954Ye

struct Node {
  long val;
};
void Push(Node *);
template <bool>
void BatchPull() {
    Push(new Node(0));
}
void TestBody() {
    (void)BatchPull<true>; 
}

Similarly to #138518 (comment), AST dump already shows the problem (https://godbolt.org/z/PYeG1v5Yz) - ImplicitCastExpr to long is dropped from the template instantiation:

|-FunctionTemplateDecl <line:5:1, line:8:1> line:6:6 BatchPull
| |-NonTypeTemplateParmDecl <line:5:11> col:15 'bool' depth 0 index 0
| |-FunctionDecl <line:6:1, line:8:1> line:6:6 BatchPull 'void ()'
| | `-CompoundStmt <col:18, line:8:1>
| |   `-CallExpr <line:7:5, col:21> 'void'
| |     |-ImplicitCastExpr <col:5> 'void (*)(Node *)' <FunctionToPointerDecay>
| |     | `-DeclRefExpr <col:5> 'void (Node *)' lvalue Function 0x389dae98 'Push' 'void (Node *)'
| |     `-CXXNewExpr <col:10, col:20> 'Node *' Function 0x389db540 'operator new' 'void *(unsigned long)'
| |       `-CXXParenListInitExpr <col:18, col:20> 'Node'
| |         `-ImplicitCastExpr <col:19> 'long' <IntegralCast>
| |           `-IntegerLiteral <col:19> 'int' 0
| `-FunctionDecl <line:6:1, line:8:1> line:6:6 used BatchPull 'void ()' implicit_instantiation
|   |-TemplateArgument integral 'true'
|   `-CompoundStmt <col:18, line:8:1>
|     `-CallExpr <line:7:5, col:21> 'void'
|       |-ImplicitCastExpr <col:5> 'void (*)(Node *)' <FunctionToPointerDecay>
|       | `-DeclRefExpr <col:5> 'void (Node *)' lvalue Function 0x389dae98 'Push' 'void (Node *)'
|       `-CXXNewExpr <col:10, col:20> 'Node *' Function 0x389db540 'operator new' 'void *(unsigned long)'
|         `-CXXParenListInitExpr <col:18, col:20> 'Node'
|           `-IntegerLiteral <col:19> 'int' 0

I'm trying to figure out how this commit affects the relevant template instantiation logic, but no ideas so far.

@alexfh
Copy link
Contributor

alexfh commented Jun 5, 2025

Before this change the cast used to be added here:

* thread #1, name = 'clang', stop reason = step over
    frame #0: 0x00005555667e8ff2 clang`clang::Sema::BuildCXXNew(this=0x00005092ffd24000, Range=SourceRange @ 0x00007ffffffef4f8, UseGlobal=false, PlacementLParen=(ID = 94), PlacementArgs=clang::MultiExprArg @ 0x00007ffffffef4e0, PlacementRParen=(ID = 94), TypeIdParens=SourceRange @ 0x00007ffffffef548, AllocType=QualType @ 0x00007ffffffef4d0, AllocTypeInfo=0x00005092ffd23a28, ArraySize= Has Value=false , DirectInitRange=SourceRange @ 0x00007ffffffef570, Initializer=0x00005092ffd66230) at SemaExprCXX.cpp:2591:18
   2588       = InitializedEntity::InitializeNew(StartLoc, InitType);
   2589     InitializationSequence InitSeq(*this, Entity, Kind, Exprs);
   2590     ExprResult FullInit = InitSeq.Perform(*this, Entity, Kind, Exprs);
-> 2591     if (FullInit.isInvalid())
   2592       return ExprError();
   2593
   2594     // FullInit is our initializer; strip off CXXBindTemporaryExprs, because
(lldb) p Exprs[0]->dump()
IntegerLiteral 0x5092ffd239e8 'int' 0
(lldb) p FullInit.get()->dump()
CXXParenListInitExpr 0x5092ffd66268 'Node':'struct Node'
`-ImplicitCastExpr 0x5092ffd66250 'long' <IntegralCast>
  `-IntegerLiteral 0x5092ffd239e8 'int' 0

Now this doesn't happen:

* thread #1, name = 'clang', stop reason = step over
    frame #0: 0x0000555566a23c92 clang`clang::Sema::BuildCXXNew(this=0x00005092ffd26000, Range=SourceRange @ 0x00007ffffffef458, UseGlobal=false, PlacementLParen=(ID = 94), PlacementArgs=clang::MultiExprArg @ 0x00007ffffffef440, PlacementRParen=(ID = 94), TypeIdParens=SourceRange @ 0x00007ffffffef4a8, AllocType=QualType @ 0x00007ffffffef430, AllocTypeInfo=0x00005092ffd25a28, ArraySize= Has Value=false , DirectInitRange=SourceRange @ 0x00007ffffffef4d0, Initializer=0x00005092ffd6b230) at SemaExprCXX.cpp:2584:18
   2581       = InitializedEntity::InitializeNew(StartLoc, InitType);
   2582     InitializationSequence InitSeq(*this, Entity, Kind, Exprs);
   2583     ExprResult FullInit = InitSeq.Perform(*this, Entity, Kind, Exprs);
-> 2584     if (FullInit.isInvalid())
   2585       return ExprError();
   2586
   2587     // FullInit is our initializer; strip off CXXBindTemporaryExprs, because
(lldb) p Exprs.size()
(size_t) 1
(lldb) p Exprs[0]->dump()
CXXParenListInitExpr 0x5092ffd6b230 'Node':'struct Node'
`-IntegerLiteral 0x5092ffd259e8 'int' 0
(lldb) p FullInit.get()->dump()
CXXParenListInitExpr 0x5092ffd6b230 'Node':'struct Node'
`-IntegerLiteral 0x5092ffd259e8 'int' 0

(probably because the argument of a new is a CXXParenListInitExpr rather than an IntegerLiteral).

In both cases this is happening here:

(lldb) bt
* thread #1, name = 'clang', stop reason = step over
  * frame #0: 0x00005555667e8ff2 clang`clang::Sema::BuildCXXNew(this=0x00005092ffd24000, Range=SourceRange @ 0x00007ffffffef4f8, UseGlobal=false, PlacementLParen=(ID = 94), PlacementArgs=clang::MultiExprArg @ 0x00007ffffffef4e0, PlacementRParen=(ID = 94), TypeIdParens=SourceRange @ 0x00007ffffffef548, AllocType=QualType @ 0x00007ffffffef4d0, AllocTypeInfo=0x00005092ffd23a28, ArraySize= Has Value=false , DirectInitRange=SourceRange @ 0x00007ffffffef570, Initializer=0x00005092ffd66230) at SemaExprCXX.cpp:2591:18
frame #1: 0x000055556756d373 clang`clang::TreeTransform<(anonymous namespace)::TemplateInstantiator>::RebuildCXXNewExpr(this=0x00007fffffff1640, StartLoc=(ID = 94), UseGlobal=false, PlacementLParen=(ID = 94), PlacementArgs=clang::MultiExprArg @ 0x00007ffffffef610, PlacementRParen=(ID = 94), TypeIdParens=SourceRange @ 0x00007ffffffef668, AllocatedType=QualType @ 0x00007ffffffef600, AllocatedTypeInfo=0x00005092ffd23a28, ArraySize= Has Value=false , DirectInitRange=SourceRange @ 0x00007ffffffef690, Initializer=0x00005092ffd66230) at TreeTransform.h:3442:22
    frame #2: 0x000055556754f773 clang`clang::TreeTransform<(anonymous namespace)::TemplateInstantiator>::TransformCXXNewExpr(this=0x00007fffffff1640, E=0x00005092ffd64cf8) at TreeTransform.h:14541:23
    frame #3: 0x0000555567530256 clang`clang::TreeTransform<(anonymous namespace)::TemplateInstantiator>::TransformExpr(this=0x00007fffffff1640, E=0x00005092ffd64cf8) at StmtNodes.inc:718:1
    frame #4: 0x0000555567530f17 clang`clang::TreeTransform<(anonymous namespace)::TemplateInstantiator>::TransformInitializer(this=0x00007fffffff1640, Init=0x00005092ffd64cf8, NotCopyInit=false) at TreeTransform.h:4375:25
    frame #5: 0x000055556753212c clang`clang::TreeTransform<(anonymous namespace)::TemplateInstantiator>::TransformExprs(this=0x00007fffffff1640, Inputs=0x00005092ffd64df8, NumInputs=1, IsCall=true, Outputs=0x00007fffffff0628, ArgChanged=0x00007fffffff067f) at TreeTransform.h:4531:29
    frame #6: 0x000055556754ae51 clang`clang::TreeTransform<(anonymous namespace)::TemplateInstantiator>::TransformCallExpr(this=0x00007fffffff1640, E=0x00005092ffd64dd8) at TreeTransform.h:13380:20
    frame #7: 0x000055556752fe5a clang`clang::TreeTransform<(anonymous namespace)::TemplateInstantiator>::TransformExpr(this=0x00007fffffff1640, E=0x00005092ffd64dd8) at StmtNodes.inc:614:1
    frame #8: 0x000055556752d68f clang`clang::TreeTransform<(anonymous namespace)::TemplateInstantiator>::TransformStmt(this=0x00007fffffff1640, S=0x00005092ffd64dd8, SDK=Discarded) at TreeTransform.h:4294:35
    frame #9: 0x00005555675572bd clang`clang::TreeTransform<(anonymous namespace)::TemplateInstantiator>::TransformCompoundStmt(this=0x00007fffffff1640, S=0x00005092ffd64e00, IsStmtExpr=false) at TreeTransform.h:8088:38
    frame #10: 0x00005555675a5a9a clang`clang::TreeTransform<(anonymous namespace)::TemplateInstantiator>::TransformCompoundStmt(this=0x00007fffffff1640, S=0x00005092ffd64e00) at TreeTransform.h:8070:23
    frame #11: 0x000055556752d4af clang`clang::TreeTransform<(anonymous namespace)::TemplateInstantiator>::TransformStmt(this=0x00007fffffff1640, S=0x00005092ffd64e00, SDK=Discarded) at StmtNodes.inc:1652:1
    frame #12: 0x000055556752b78e clang`clang::Sema::SubstStmt(this=0x00005092ffd24000, S=0x00005092ffd64e00, TemplateArgs=0x00007fffffff1b68) at SemaTemplateInstantiate.cpp:4357:23
    frame #13: 0x0000555567633a63 clang`clang::Sema::InstantiateFunctionDefinition(this=0x00005092ffd24000, PointOfInstantiation=(ID = 138), Function=0x00005092ffd66088, Recursive=true, DefinitionRequired=false, AtEndOfTU=true) at SemaTemplateInstantiateDecl.cpp:5819:14
    frame #14: 0x0000555567637ae3 clang`clang::Sema::PerformPendingInstantiations(this=0x00005092ffd24000, LocalOnly=false, AtEndOfTU=true) at SemaTemplateInstantiateDecl.cpp:7061:9
    frame #15: 0x0000555565e12515 clang`clang::Sema::ActOnEndOfTranslationUnitFragment(this=0x00005092ffd24000, Kind=Normal) at Sema.cpp:1197:5
    frame #16: 0x0000555565e12c1b clang`clang::Sema::ActOnEndOfTranslationUnit(this=0x00005092ffd24000) at Sema.cpp:1230:5
    frame #17: 0x00005555656a82d2 clang`clang::Parser::ParseTopLevelDecl(this=0x00005092ffc06000, Result=0x00007fffffff4288, ImportState=0x00007fffffff4284) at Parser.cpp:749:13
    frame #18: 0x00005555656a0bc6 clang`clang::ParseAST(S=0x00005092ffd24000, PrintStats=false, SkipFunctionBodies=false) at ParseAST.cpp:171:20
    frame #19: 0x0000555565043c85 clang`clang::ASTFrontendAction::ExecuteAction(this=0x00005092ffe0daa0) at FrontendAction.cpp:1343:3
    frame #20: 0x000055556504323b clang`clang::FrontendAction::Execute(this=0x00005092ffe0daa0) at FrontendAction.cpp:1225:3
    frame #21: 0x0000555564ef050d clang`clang::CompilerInstance::ExecuteAction(this=0x00005092ffe0e6e0, Act=0x00005092ffe0daa0) at CompilerInstance.cpp:1056:33
    frame #22: 0x0000555562960225 clang`clang::ExecuteCompilerInvocation(Clang=0x00005092ffe0e6e0) at ExecuteCompilerInvocation.cpp:300:25
    frame #23: 0x000055556292d9d8 clang`cc1_main(Argv=ArrayRef @ 0x00007fffffff8028, Argv0="clang", MainAddr=0x0000555562919430) at cc1_main.cpp:297:15

@alexfh
Copy link
Contributor

alexfh commented Jun 5, 2025

Okay, I think, I have a fix.

alexfh added a commit to alexfh/llvm-project that referenced this pull request Jun 5, 2025
alexfh added a commit that referenced this pull request Jun 5, 2025
CXXParenListInitExpr arguments would lose casts leading to incorrect types being used (e.g. only 32 bits of a 64 bit value being initialized). See #138518 (comment) and #138518 (comment) for details and context.
llvm-sync bot pushed a commit to arm/arm-toolchain that referenced this pull request Jun 5, 2025
CXXParenListInitExpr arguments would lose casts leading to incorrect types being used (e.g. only 32 bits of a 64 bit value being initialized). See llvm/llvm-project#138518 (comment) and llvm/llvm-project#138518 (comment) for details and context.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
clang:frontend Language frontend issues, e.g. anything involving "Sema" clang Clang issues not falling into any other category
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Clang crashes while compiling my project
5 participants