https://github.com/kodlan updated https://github.com/llvm/llvm-project/pull/221488
>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 1/3] [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 >From 52364996ba37eda2ecbda2fe66253f6261607d84 Mon Sep 17 00:00:00 2001 From: Stanislav Bardyuk <[email protected]> Date: Mon, 7 Sep 2026 20:40:08 +0200 Subject: [PATCH 2/3] Update clang/docs/ReleaseNotes.md Co-authored-by: Shengxin Pei <[email protected]> --- clang/docs/ReleaseNotes.md | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/clang/docs/ReleaseNotes.md b/clang/docs/ReleaseNotes.md index 06830897083e2..5bc1fdb124d30 100644 --- a/clang/docs/ReleaseNotes.md +++ b/clang/docs/ReleaseNotes.md @@ -643,8 +643,7 @@ features cannot lower the translation-unit ABI level; 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) + specialization or an incomplete class. (#GH217883) #### Bug Fixes to AST Handling >From a99bb2dde9ad29c89939c08e753d245c9f0af3c5 Mon Sep 17 00:00:00 2001 From: Stanislav Bardyuk <[email protected]> Date: Mon, 7 Sep 2026 19:00:29 +0000 Subject: [PATCH 3/3] [Clang] Move the GH217883 tests into cxx20-p0388-unbound-ary.cpp --- .../test/SemaCXX/cxx20-p0388-unbound-ary.cpp | 28 +++++++++++++++ ...-list-unbound-array-incomplete-element.cpp | 35 ------------------- 2 files changed, 28 insertions(+), 35 deletions(-) delete mode 100644 clang/test/SemaCXX/init-list-unbound-array-incomplete-element.cpp diff --git a/clang/test/SemaCXX/cxx20-p0388-unbound-ary.cpp b/clang/test/SemaCXX/cxx20-p0388-unbound-ary.cpp index f2d5cabad235d..7ac0204e612b0 100644 --- a/clang/test/SemaCXX/cxx20-p0388-unbound-ary.cpp +++ b/clang/test/SemaCXX/cxx20-p0388-unbound-ary.cpp @@ -171,3 +171,31 @@ void g3() { } // namespace Eight #endif + +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 >= 202002 +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 diff --git a/clang/test/SemaCXX/init-list-unbound-array-incomplete-element.cpp b/clang/test/SemaCXX/init-list-unbound-array-incomplete-element.cpp deleted file mode 100644 index 7970570c5eea2..0000000000000 --- a/clang/test/SemaCXX/init-list-unbound-array-incomplete-element.cpp +++ /dev/null @@ -1,35 +0,0 @@ -// 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
