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