Skip to content

Commit 1a308ef

Browse files
committed
PerformanceDiagnostic: give an error if a generic non-copyable value with a deinit is captured by an escaping closure.
Otherwise IRGen would crash. It needs a bit of work to support alloc_box of generic non-copyable structs/enums with deinit, because we need to specialize the deinit functions, though they are not explicitly referenced in SIL. Until this is supported, give an error in such cases. Fixes a compiler crash in IRGen rdar://130283111
1 parent 47baa9f commit 1a308ef

File tree

3 files changed

+164
-0
lines changed

3 files changed

+164
-0
lines changed

include/swift/AST/DiagnosticsSIL.def

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -398,6 +398,8 @@ ERROR(embedded_swift_allocating_type,none,
398398
"cannot use allocating type %0 in -no-allocations mode", (Type))
399399
ERROR(embedded_swift_allocating,none,
400400
"cannot use allocating operation in -no-allocations mode", ())
401+
ERROR(embedded_capture_of_generic_value_with_deinit,none,
402+
"capturing generic non-copyable type with deinit in escaping closure not supported in embedded Swift", ())
401403
NOTE(performance_called_from,none,
402404
"called from here", ())
403405

lib/SILOptimizer/Mandatory/PerformanceDiagnostics.cpp

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -157,6 +157,46 @@ static bool isEffectFreeArraySemanticCall(SILInstruction *inst) {
157157
}
158158
}
159159

160+
static bool hasGenericValueDeinit(SILType ty, SILFunction *f) {
161+
if (!ty.isMoveOnly())
162+
return false;
163+
NominalTypeDecl *nominal = ty.getNominalOrBoundGenericNominal();
164+
if (!nominal)
165+
return false;
166+
167+
if (nominal->getGenericSignature() && nominal->getValueTypeDestructor())
168+
return true;
169+
170+
if (isa<StructDecl>(nominal)) {
171+
for (unsigned i = 0, n = ty.getNumNominalFields(); i < n; ++i) {
172+
if (hasGenericValueDeinit(ty.getFieldType(i, f), f))
173+
return true;
174+
}
175+
} else if (auto *en = dyn_cast<EnumDecl>(nominal)) {
176+
for (EnumElementDecl *element : en->getAllElements()) {
177+
if (element->hasAssociatedValues()) {
178+
if (hasGenericValueDeinit(ty.getEnumElementType(element, f), f))
179+
return true;
180+
}
181+
}
182+
}
183+
184+
return false;
185+
}
186+
187+
static bool allocsGenericValueTypeWithDeinit(AllocBoxInst *abi) {
188+
CanSILBoxType boxTy = abi->getBoxType();
189+
SILFunction *f = abi->getFunction();
190+
unsigned numFields = boxTy->getLayout()->getFields().size();
191+
for (unsigned fieldIdx = 0; fieldIdx < numFields; ++fieldIdx) {
192+
SILType fieldTy = getSILBoxFieldType(TypeExpansionContext(*f), boxTy,
193+
abi->getModule().Types, fieldIdx);
194+
if (hasGenericValueDeinit(fieldTy, f))
195+
return true;
196+
}
197+
return false;
198+
}
199+
160200
/// Prints Embedded Swift specific performance diagnostics (no existentials,
161201
/// no metatypes, optionally no allocations) for \p function.
162202
bool PerformanceDiagnostics::visitFunctionEmbeddedSwift(
@@ -220,6 +260,16 @@ bool PerformanceDiagnostics::visitFunctionEmbeddedSwift(
220260
default:
221261
break;
222262
}
263+
} else if (auto *abi = dyn_cast<AllocBoxInst>(&inst)) {
264+
// It needs a bit of work to support alloc_box of generic non-copyable
265+
// structs/enums with deinit, because we need to specialize the deinit
266+
// functions, though they are not explicitly referenced in SIL.
267+
// Until this is supported, give an error in such cases. Otherwise
268+
// IRGen would crash.
269+
if (allocsGenericValueTypeWithDeinit(abi)) {
270+
LocWithParent loc(abi->getLoc().getSourceLoc(), parentLoc);
271+
diagnose(loc, diag::embedded_capture_of_generic_value_with_deinit);
272+
}
223273
}
224274
}
225275
}
Lines changed: 112 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,112 @@
1+
// RUN: %target-swift-emit-ir %s -DIGNORE_FAILS -enable-experimental-feature Embedded -wmo -o /dev/null
2+
// RUN: %target-swift-emit-ir %s -enable-experimental-feature Embedded -wmo -verify
3+
4+
struct MyStruct<Item> : ~Copyable {
5+
var member = "42"
6+
7+
init() {}
8+
deinit {}
9+
mutating func foo() {}
10+
}
11+
12+
var escape: (()->())?
13+
14+
#if !IGNORE_FAILS
15+
16+
public func test() {
17+
var s = MyStruct<Int>() // expected-error {{capturing generic non-copyable type with deinit in escaping closure not supported in embedded Swift}}
18+
s.foo()
19+
escape = {
20+
s.foo()
21+
}
22+
}
23+
24+
//
25+
26+
struct Outer: ~Copyable {
27+
var inner: MyStruct<Int>
28+
}
29+
30+
public func testNested() {
31+
var s = Outer(inner: MyStruct<Int>()) // expected-error {{capturing generic non-copyable type with deinit in escaping closure not supported in embedded Swift}}
32+
s.inner.foo()
33+
escape = {
34+
s.inner.foo()
35+
}
36+
}
37+
38+
//
39+
40+
enum E: ~Copyable {
41+
case A(MyStruct<Int>)
42+
case B
43+
44+
mutating func foo() {}
45+
}
46+
47+
public func testEnum() {
48+
var s = E.A(MyStruct<Int>()) // expected-error {{capturing generic non-copyable type with deinit in escaping closure not supported in embedded Swift}}
49+
s.foo()
50+
escape = {
51+
s.foo()
52+
}
53+
}
54+
55+
#endif
56+
57+
//
58+
59+
struct StructWithoutDeinit<Item> {
60+
var member = "42"
61+
62+
init() {}
63+
mutating func foo() {}
64+
}
65+
66+
public func testWithoutDeinit() {
67+
var s = StructWithoutDeinit<Int>()
68+
s.foo()
69+
escape = {
70+
s.foo()
71+
}
72+
}
73+
74+
//
75+
76+
struct NonCopyableStructWithoutDeinit<Item>: ~Copyable {
77+
var member = "42"
78+
79+
init() {}
80+
mutating func foo() {}
81+
}
82+
83+
public func testNonCopyableWithoutDeinit() {
84+
var s = NonCopyableStructWithoutDeinit<Int>()
85+
s.foo()
86+
escape = {
87+
s.foo()
88+
}
89+
}
90+
91+
//
92+
93+
struct NonGenericStruct : ~Copyable {
94+
var member = "42"
95+
96+
init() {
97+
}
98+
99+
deinit {
100+
}
101+
102+
mutating func foo() {
103+
}
104+
}
105+
106+
public func testNonGeneric() {
107+
var s = NonGenericStruct()
108+
s.foo()
109+
escape = {
110+
s.foo()
111+
}
112+
}

0 commit comments

Comments
 (0)