llvmorg-github-actions[bot] wrote:

<!--LLVM PR SUMMARY COMMENT-->

@llvm/pr-subscribers-clang

Author: Stanislav Bardyuk (kodlan)

<details>
<summary>Changes</summary>

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 &lt;typename&gt; struct Q {};
const Q&lt;int&gt; (&amp;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 (&amp;)[][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

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


3 Files Affected:

- (modified) clang/docs/ReleaseNotes.md (+4) 
- (modified) clang/lib/Sema/SemaInit.cpp (+9-3) 
- (added) clang/test/SemaCXX/init-list-unbound-array-incomplete-element.cpp 
(+35) 


``````````diff
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

``````````

</details>


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

Reply via email to