Skip to content

Commit a90b515

Browse files
committed
Fix type lowering of ~Escapable generics.
Factor AbstractionPattern::conformsToKnownProtocol and lower ~Escapable using the same logic as ~Copyable. Adds support for conditionally Escapable enums. Correctly sets the SILType::isTrivial flags for conditionally escapable structs and enums in environments (extensions) that provide an Escapable conformance, such as: struct NE<E: ~Escapable> : ~Escapable {} extension NE: Escapable { func foo() -> Self { // Self is both Escapable and trivial here. self } } Fixes rdar://125950218 ([nonescapable] support conditionally escapable enums)
1 parent 9bd9218 commit a90b515

File tree

4 files changed

+119
-29
lines changed

4 files changed

+119
-29
lines changed

include/swift/SIL/AbstractionPattern.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1014,7 +1014,10 @@ class AbstractionPattern {
10141014

10151015
bool requiresClass() const;
10161016
LayoutConstraint getLayoutConstraint() const;
1017+
bool conformsToKnownProtocol(
1018+
CanType substTy, KnownProtocolKind protocolKind) const;
10171019
bool isNoncopyable(CanType substTy) const;
1020+
bool isEscapable(CanType substTy) const;
10181021

10191022
/// Return the Swift type which provides structure for this
10201023
/// abstraction pattern.

lib/SIL/IR/AbstractionPattern.cpp

Lines changed: 28 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -284,62 +284,64 @@ LayoutConstraint AbstractionPattern::getLayoutConstraint() const {
284284
}
285285
}
286286

287-
bool AbstractionPattern::isNoncopyable(CanType substTy) const {
288-
auto copyable
289-
= substTy->getASTContext().getProtocol(KnownProtocolKind::Copyable);
287+
bool AbstractionPattern::conformsToKnownProtocol(
288+
CanType substTy, KnownProtocolKind protocolKind) const {
289+
auto suppressible
290+
= substTy->getASTContext().getProtocol(protocolKind);
290291

291-
auto isDefinitelyCopyable = [&](CanType t) -> bool {
292-
auto result = copyable->getParentModule()
293-
->checkConformanceWithoutContext(t, copyable,
292+
auto definitelyConforms = [&](CanType t) -> bool {
293+
auto result = suppressible->getParentModule()
294+
->checkConformanceWithoutContext(t, suppressible,
294295
/*allowMissing=*/false);
295296
return result.has_value() && !result.value().isInvalid();
296297
};
297298

298299
// If the substituted type definitely conforms, that's authoritative.
299-
if (isDefinitelyCopyable(substTy)) {
300-
return false;
300+
if (definitelyConforms(substTy)) {
301+
return true;
301302
}
302303

303304
// If the substituted type is fully concrete, that's it. If there are unbound
304305
// type variables in the type, then we may have to account for the upper
305306
// abstraction bound from the abstraction pattern.
306307
if (!substTy->hasTypeParameter()) {
307-
return true;
308+
return false;
308309
}
309310

310311
switch (getKind()) {
311312
case Kind::Opaque: {
312313
// The abstraction pattern doesn't provide any more specific bounds.
313-
return true;
314+
return false;
314315
}
315316
case Kind::Type:
316317
case Kind::Discard:
317318
case Kind::ClangType: {
318319
// See whether the abstraction pattern's context gives us an upper bound
319-
// that ensures the type is copyable.
320+
// that ensures the type conforms.
320321
auto type = getType();
321322
if (hasGenericSignature() && getType()->hasTypeParameter()) {
322323
type = GenericEnvironment::mapTypeIntoContext(
323324
getGenericSignature().getGenericEnvironment(), getType())
324325
->getReducedType(getGenericSignature());
325326
}
326327

327-
return !isDefinitelyCopyable(type);
328+
return definitelyConforms(type);
328329
}
329330
case Kind::Tuple: {
330-
// A tuple is noncopyable if any element is.
331+
// A tuple conforms if all elements do.
331332
if (doesTupleVanish()) {
332333
return getVanishingTupleElementPatternType().value()
333-
.isNoncopyable(substTy);
334+
.conformsToKnownProtocol(substTy, protocolKind);
334335
}
335336
auto substTupleTy = cast<TupleType>(substTy);
336337

337338
for (unsigned i = 0, e = getNumTupleElements(); i < e; ++i) {
338-
if (getTupleElementType(i).isNoncopyable(substTupleTy.getElementType(i))){
339-
return true;
339+
if (!getTupleElementType(i).conformsToKnownProtocol(
340+
substTupleTy.getElementType(i), protocolKind)) {
341+
return false;
340342
}
341343
}
342-
return false;
344+
return true;
343345
}
344346
// Functions are, at least for now, always copyable.
345347
case Kind::CurriedObjCMethodType:
@@ -354,13 +356,21 @@ bool AbstractionPattern::isNoncopyable(CanType substTy) const {
354356
case Kind::PartialCurriedCXXMethodType:
355357
case Kind::OpaqueFunction:
356358
case Kind::OpaqueDerivativeFunction:
357-
return false;
359+
return true;
358360

359361
case Kind::Invalid:
360362
llvm_unreachable("asking invalid abstraction pattern");
361363
}
362364
}
363365

366+
bool AbstractionPattern::isNoncopyable(CanType substTy) const {
367+
return !conformsToKnownProtocol(substTy, KnownProtocolKind::Copyable);
368+
}
369+
370+
bool AbstractionPattern::isEscapable(CanType substTy) const {
371+
return conformsToKnownProtocol(substTy, KnownProtocolKind::Escapable);
372+
}
373+
364374
bool AbstractionPattern::matchesTuple(CanType substType) const {
365375
switch (getKind()) {
366376
case Kind::Invalid:

lib/SIL/IR/TypeLowering.cpp

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2441,7 +2441,9 @@ namespace {
24412441
return new (TC) MoveOnlyLoadableStructTypeLowering(
24422442
structType, properties, Expansion);
24432443
}
2444-
if (D->canBeEscapable() != TypeDecl::CanBeInvertible::Always) {
2444+
// Regardless of their member types, Nonescapable values have ownership
2445+
// for lifetime diagnostics.
2446+
if (!origType.isEscapable(structType)) {
24452447
properties.setNonTrivial();
24462448
}
24472449
return handleAggregateByProperties<LoadableStructTypeLowering>(structType,
@@ -2538,10 +2540,11 @@ namespace {
25382540
return new (TC)
25392541
MoveOnlyLoadableEnumTypeLowering(enumType, properties, Expansion);
25402542
}
2541-
2542-
assert(D->canBeEscapable() == TypeDecl::CanBeInvertible::Always
2543-
&& "missing typelowering case here!");
2544-
2543+
// Regardless of their member types, Nonescapable values have ownership
2544+
// for lifetime diagnostics.
2545+
if (!origType.isEscapable(enumType)) {
2546+
properties.setNonTrivial();
2547+
}
25452548
return handleAggregateByProperties<LoadableEnumTypeLowering>(enumType,
25462549
properties);
25472550
}

test/SILGen/typelowering_inverses.swift

Lines changed: 80 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
// RUN: %target-swift-frontend -emit-silgen -enable-experimental-feature NoncopyableGenerics -disable-availability-checking -module-name main %s | %FileCheck %s
1+
// RUN: %target-swift-frontend -emit-silgen -enable-experimental-feature NoncopyableGenerics -enable-experimental-feature NonescapableTypes -disable-availability-checking -module-name main %s | %FileCheck %s
22

33
protocol NoCopyP: ~Copyable {}
44

@@ -24,7 +24,31 @@ enum CondCopyableEnum<T: ~Copyable>: ~Copyable {
2424

2525
extension CondCopyableEnum: Copyable {}
2626

27-
// MARK: ensure certain types are treated as trivial (no ownership in func signature).
27+
protocol NoEscapeP: ~Escapable {}
28+
29+
struct NE: ~Escapable {}
30+
31+
struct TooRudeStruct<T: ~Escapable>: Escapable {
32+
let thing: Int
33+
}
34+
35+
enum TooRudeEnum<T: ~Escapable>: Escapable {
36+
case holder(Int)
37+
case whatever
38+
}
39+
40+
struct CondEscapableStruct<T: ~Escapable>: ~Escapable {}
41+
42+
extension CondEscapableStruct: Escapable {}
43+
44+
enum CondEscapableEnum<T: ~Escapable>: ~Escapable {
45+
case some(T)
46+
case none
47+
}
48+
49+
extension CondEscapableEnum: Escapable {}
50+
51+
// MARK: ensure certain conditionally Copyable types are treated as trivial (no ownership in func signature).
2852

2953
// CHECK: sil hidden [ossa] @$s4main5checkyyAA10RudeStructVySiGF : $@convention(thin) (RudeStruct<Int>) -> () {
3054
func check(_ t: RudeStruct<Int>) {}
@@ -68,18 +92,68 @@ func check(_ t: consuming any NoCopyP & ~Copyable) {}
6892
// CHECK: sil hidden [ossa] @$s4main5checkyyAA7NoCopyP_pRi_s_XPzF : $@convention(thin) (@inout any NoCopyP & ~Copyable) -> () {
6993
func check(_ t: inout any NoCopyP & ~Copyable) {}
7094

71-
struct MyStruct<T: ~Copyable>: ~Copyable {
95+
// MARK: ensure certain conditionally Escapable types are treated as trivial (no ownership in func signature).
96+
97+
// CHECK: sil hidden [ossa] @$s4main5checkyyAA13TooRudeStructVySiGF : $@convention(thin) (TooRudeStruct<Int>) -> () {
98+
func check(_ t: TooRudeStruct<Int>) {}
99+
100+
// CHECK: sil hidden [ossa] @$s4main5checkyyAA13TooRudeStructVyAA2NEVGF : $@convention(thin) (TooRudeStruct<NE>) -> () {
101+
func check(_ t: TooRudeStruct<NE>) {}
102+
103+
// CHECK: sil hidden [ossa] @$s4main5checkyyAA11TooRudeEnumOySiGF : $@convention(thin) (TooRudeEnum<Int>) -> () {
104+
func check(_ t: TooRudeEnum<Int>) {}
105+
106+
// CHECK: sil hidden [ossa] @$s4main5checkyyAA11TooRudeEnumOyAA2NEVGF : $@convention(thin) (TooRudeEnum<NE>) -> () {
107+
func check(_ t: TooRudeEnum<NE>) {}
108+
109+
// CHECK: sil hidden [ossa] @$s4main5checkyyAA19CondEscapableStructVySiGF : $@convention(thin) (CondEscapableStruct<Int>) -> () {
110+
func check(_ t: CondEscapableStruct<Int>) {}
111+
112+
// CHECK: sil hidden [ossa] @$s4main5checkyyAA19CondEscapableStructVyAA2NEVGF : $@convention(thin) (@guaranteed CondEscapableStruct<NE>) -> () {
113+
func check(_ t: borrowing CondEscapableStruct<NE>) {}
114+
115+
// CHECK: sil hidden [ossa] @$s4main5checkyyAA17CondEscapableEnumOySiGF : $@convention(thin) (CondEscapableEnum<Int>) -> () {
116+
func check(_ t: CondEscapableEnum<Int>) {}
117+
118+
// CHECK: sil hidden [ossa] @$s4main5checkyyAA17CondEscapableEnumOyAA2NEVGF : $@convention(thin) (@guaranteed CondEscapableEnum<NE>) -> () {
119+
func check(_ t: borrowing CondEscapableEnum<NE>) {}
120+
121+
// CHECK: sil hidden [ossa] @$s4main5checkyyAA17CondEscapableEnumOyxGlF : $@convention(thin) <T> (@in_guaranteed CondEscapableEnum<T>) -> () {
122+
func check<T>(_ t: CondEscapableEnum<T>) {}
123+
124+
// CHECK: sil hidden [ossa] @$s4main5checkyyAA17CondEscapableEnumOyxGRi0_zlF : $@convention(thin) <U where U : ~Escapable> (@in_guaranteed CondEscapableEnum<U>) -> () {
125+
func check<U: ~Escapable>(_ t: borrowing CondEscapableEnum<U>) {}
126+
127+
// CHECK: sil hidden [ossa] @$s4main5checkyyAA9NoEscapeP_pF : $@convention(thin) (@in_guaranteed any NoEscapeP) -> () {
128+
func check(_ t: any NoEscapeP) {}
129+
130+
// CHECK: sil hidden [ossa] @$s4main5checkyyAA9NoEscapeP_pRi0_s_XPF : $@convention(thin) (@in_guaranteed any NoEscapeP & ~Escapable) -> () {
131+
func check(_ t: borrowing any NoEscapeP & ~Escapable) {}
132+
133+
// CHECK: sil hidden [ossa] @$s4main5checkyyAA9NoEscapeP_pRi0_s_XPnF : $@convention(thin) (@in any NoEscapeP & ~Escapable) -> () {
134+
func check(_ t: consuming any NoEscapeP & ~Escapable) {}
135+
136+
// CHECK: sil hidden [ossa] @$s4main5checkyyAA9NoEscapeP_pRi0_s_XPzF : $@convention(thin) (@inout any NoEscapeP & ~Escapable) -> () {
137+
func check(_ t: inout any NoEscapeP & ~Escapable) {}
138+
139+
// MARK: conditionally Copyable & Escapable SILGen
140+
141+
struct MyStruct<T: ~Copyable & ~Escapable>: ~Copyable & ~Escapable {
72142
var x: T
73143
}
74144

75-
extension MyStruct: Copyable where T: Copyable {}
145+
extension MyStruct: Copyable where T: Copyable & ~Escapable {}
76146

77-
enum MyEnum<T: ~Copyable>: ~Copyable {
147+
extension MyStruct: Escapable where T: Escapable & ~Copyable {}
148+
149+
enum MyEnum<T: ~Copyable & ~Escapable>: ~Copyable & ~Escapable {
78150
case x(T)
79151
case knoll
80152
}
81153

82-
extension MyEnum: Copyable where T: Copyable {}
154+
extension MyEnum: Copyable where T: Copyable & ~Escapable {}
155+
156+
extension MyEnum: Escapable where T: Escapable & ~Copyable {}
83157

84158
enum Trivial {
85159
case a, b, c

0 commit comments

Comments
 (0)