https://github.com/kodlan created 
https://github.com/llvm/llvm-project/pull/221488

List-initializing a temporary array of unknown bound from an empty initializer 
list creates a zero-length array whose elements are never initialized, so 
nothing ever completes the element type. If that type is a class template 
specialization that has not been instantiated yet (or a forward-declared 
class), the destructor lookup for the temporary in `MaybeBindToTemporary` runs 
on a record without a definition and trips the 
`CanDeclareSpecialMemberFunction` assertion in `LookupSpecialMember`.

```cpp
template <typename> struct Q {};
const Q<int> (&r)[] = {};
```

`TryListInitialization` already requires a record destination type to be 
complete. This extends that check to the base element type of an incomplete 
array destination (so `T (&)[][N]` is covered too), which mirrors what 
`TryListConversion` in SemaOverload.cpp does for the same case. The template 
specialization gets instantiated and the program is accepted (as with GCC); a 
forward-declared element type now gets "initialization of incomplete type" 
instead of a crash. A deleted destructor on the element type is diagnosed 
through the existing `checkDestructorReference` path, matching GCC. One 
consequence: a specialization whose instantiation is ill-formed now produces 
that error here instead of being silently skipped, which is also what GCC does.

#205973 guarded one destructor lookup for this family of crashes (sized array, 
incomplete element type); this handles the unbound array case that reaches the 
next lookup.

Fixes #217883

>From c7b363325cff0c485db311cbb9eeeaab5c120c36 Mon Sep 17 00:00:00 2001
From: Stanislav Bardyuk <[email protected]>
Date: Sat, 5 Sep 2026 19:46:42 +0000
Subject: [PATCH] [Clang] Complete the element type when list-initializing an
 array of unknown bound

List-initializing a temporary array of unknown bound from an empty
initializer list creates a zero-length array whose elements are never
initialized, so nothing ever completes the element type. If that type is
a class template specialization that has not been instantiated yet (or a
forward-declared class), the destructor lookup for the temporary in
MaybeBindToTemporary runs on a record without a definition and trips the
CanDeclareSpecialMemberFunction assertion in LookupSpecialMember.

TryListInitialization already requires a record destination type to be
complete. Extend that check to the base element type of an incomplete
array destination, mirroring what TryListConversion does in overload
resolution. The specialization gets instantiated and the program is
accepted (as with GCC); a forward-declared element type is now diagnosed
as an incomplete type instead of crashing.

Fixes #217883
---
 clang/docs/ReleaseNotes.md                    |  4 +++
 clang/lib/Sema/SemaInit.cpp                   | 12 +++++--
 ...-list-unbound-array-incomplete-element.cpp | 35 +++++++++++++++++++
 3 files changed, 48 insertions(+), 3 deletions(-)
 create mode 100644 
clang/test/SemaCXX/init-list-unbound-array-incomplete-element.cpp

diff --git a/clang/docs/ReleaseNotes.md b/clang/docs/ReleaseNotes.md
index a43ed2b924622..06830897083e2 100644
--- a/clang/docs/ReleaseNotes.md
+++ b/clang/docs/ReleaseNotes.md
@@ -641,6 +641,10 @@ 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 when list-initializing an array of unknown bound from an empty
+  initializer list whose element type is an uninstantiated class template
+  specialization or an incomplete class; the element type is now completed
+  first. (#GH217883)
 
 #### Bug Fixes to AST Handling
 
diff --git a/clang/lib/Sema/SemaInit.cpp b/clang/lib/Sema/SemaInit.cpp
index 48ce51863c2c0..064f3080a6915 100644
--- a/clang/lib/Sema/SemaInit.cpp
+++ b/clang/lib/Sema/SemaInit.cpp
@@ -5053,9 +5053,15 @@ static void TryListInitialization(Sema &S,
     return;
   }
 
-  if (DestType->isRecordType() &&
-      !S.isCompleteType(InitList->getBeginLoc(), DestType)) {
-    Sequence.setIncompleteTypeFailure(DestType);
+  // For an array of unknown bound (C++20), the base element type must be
+  // complete: an empty list creates a zero-length array whose elements are
+  // never initialized, so nothing else would complete it.
+  QualType InitTy = DestType;
+  if (const auto *IAT = S.Context.getAsIncompleteArrayType(DestType))
+    InitTy = S.Context.getBaseElementType(IAT);
+  if (InitTy->isRecordType() &&
+      !S.isCompleteType(InitList->getBeginLoc(), InitTy)) {
+    Sequence.setIncompleteTypeFailure(InitTy);
     return;
   }
 
diff --git a/clang/test/SemaCXX/init-list-unbound-array-incomplete-element.cpp 
b/clang/test/SemaCXX/init-list-unbound-array-incomplete-element.cpp
new file mode 100644
index 0000000000000..7970570c5eea2
--- /dev/null
+++ b/clang/test/SemaCXX/init-list-unbound-array-incomplete-element.cpp
@@ -0,0 +1,35 @@
+// RUN: %clang_cc1 -std=c++17 -fsyntax-only -verify %s
+// RUN: %clang_cc1 -std=c++20 -fsyntax-only -verify %s
+
+// List-initializing a temporary array of unknown bound from an empty list
+// creates a zero-length array whose element type was never completed, and
+// destructor lookup on the uninstantiated (or forward-declared) element type
+// crashed. https://github.com/llvm/llvm-project/issues/217883
+
+namespace gh217883 {
+template <typename> struct Q {};
+
+const Q<int> (&r1)[] = {};
+Q<int> (&&r2)[] = {};
+const Q<int> (&r3)[][2] = {};
+
+void call(void (*f)(const Q<int> (&)[])) { f({}); }
+void call_rvalue(void (*f)(Q<int> (&&)[])) { f({}); }
+void call_nested(void (*f)(const Q<int> (&)[][2])) { f({}); }
+
+#if __cplusplus >= 202002L
+static_assert(requires(void f(const Q<int> (&)[])) { f({}); });
+#endif
+
+template <typename> struct DeletedDtor { ~DeletedDtor() = delete; }; // 
expected-note {{marked deleted here}}
+void call_deleted(void (*f)(const DeletedDtor<int> (&)[])) {
+  f({}); // expected-error {{attempt to use a deleted function}}
+}
+
+struct Incomplete; // expected-note 3 {{forward declaration of 
'gh217883::Incomplete'}}
+const Incomplete (&r4)[] = {}; // expected-error {{initialization of 
incomplete type 'const Incomplete'}}
+const Incomplete (&r5)[][2] = {}; // expected-error {{initialization of 
incomplete type 'const Incomplete'}}
+void call_incomplete(void (*f)(const Incomplete (&)[])) {
+  f({}); // expected-error {{initialization of incomplete type 'const 
Incomplete'}}
+}
+} // namespace gh217883

_______________________________________________
cfe-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to