Skip to content

Commit 877f68d

Browse files
Merge pull request #39137 from AnthonyLatsis/deoptimize
2 parents a67a043 + b81db7a commit 877f68d

File tree

3 files changed

+70
-8
lines changed

3 files changed

+70
-8
lines changed

lib/SILOptimizer/Utils/Devirtualize.cpp

Lines changed: 42 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1094,7 +1094,48 @@ static bool canDevirtualizeWitnessMethod(ApplySite applySite) {
10941094
return false;
10951095
}
10961096

1097-
return true;
1097+
// FIXME: devirtualizeWitnessMethod below does not support cases with
1098+
// covariant 'Self' nested inside a collection type,
1099+
// like '[Self]' or '[* : Self]'.
1100+
const Type interfaceTy = wmi->getMember()
1101+
.getDecl()
1102+
->getInterfaceType()
1103+
// Skip the 'self' parameter.
1104+
->castTo<AnyFunctionType>()
1105+
->getResult();
1106+
1107+
if (!interfaceTy->hasTypeParameter())
1108+
return true;
1109+
1110+
class HasSelfNestedInsideCollection final : public TypeWalker {
1111+
unsigned CollectionDepth;
1112+
1113+
public:
1114+
Action walkToTypePre(Type T) override {
1115+
if (!T->hasTypeParameter())
1116+
return Action::SkipChildren;
1117+
1118+
if (auto *GP = T->getAs<GenericTypeParamType>()) {
1119+
// Only 'Self' will have zero depth in the type of a requirement.
1120+
if (GP->getDepth() == 0 && CollectionDepth)
1121+
return Action::Stop;
1122+
}
1123+
1124+
if (T->isArray() || T->isDictionary())
1125+
++CollectionDepth;
1126+
1127+
return Action::Continue;
1128+
}
1129+
1130+
Action walkToTypePost(Type T) override {
1131+
if (T->isArray() || T->isDictionary())
1132+
--CollectionDepth;
1133+
1134+
return Action::Continue;
1135+
}
1136+
};
1137+
1138+
return !interfaceTy.walk(HasSelfNestedInsideCollection());
10981139
}
10991140

11001141
/// In the cases where we can statically determine the function that

test/SILOptimizer/devirt_protocol_method_invocations.swift

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -288,3 +288,30 @@ func testReabstractedWitness(_ f: ReabstractedP) {
288288
public func testReabstracted(f: Optional<()->()>) {
289289
testReabstractedWitness(f)
290290
}
291+
292+
293+
// Test that we don't devirtualize calls to protocol requirements with covariant `Self` nested
294+
// inside a collection type – the devirtualizer does not support handling these yet.
295+
protocol CovariantSelfInsideCollection {
296+
func array() -> Array<Self>
297+
func dictionary() -> Dictionary<String, Self>
298+
func mixed(_: (Array<(Dictionary<String, String>, Self)>) -> Void)
299+
}
300+
// CHECK-LABEL: sil @$s34devirt_protocol_method_invocations12testNoDevirtyyF
301+
//
302+
// CHECK: witness_method $S, #CovariantSelfInsideCollection.array
303+
// CHECK: witness_method $S, #CovariantSelfInsideCollection.dictionary
304+
// CHECK: witness_method $S, #CovariantSelfInsideCollection.mixed
305+
// CHECK: end sil function '$s34devirt_protocol_method_invocations12testNoDevirtyyF'
306+
public func testNoDevirt() {
307+
struct S: CovariantSelfInsideCollection {
308+
func array() -> Array<Self> { fatalError() }
309+
func dictionary() -> Dictionary<String, Self> { fatalError() }
310+
func mixed(_: (Array<(Dictionary<String, String>, Self)>) -> Void) {}
311+
}
312+
313+
let p: CovariantSelfInsideCollection = S()
314+
_ = p.array()
315+
_ = p.dictionary()
316+
p.mixed { _ in }
317+
}

test/decl/protocol/protocols_with_self_or_assoc_reqs_executable.swift

Lines changed: 1 addition & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -51,10 +51,7 @@ Tests.test("Basic") {
5151
expectEqual(3, collection.count)
5252
}
5353

54-
// FIXME: Teach the devirtualizer how to handle calls to requirements with covariant `Self` nested
55-
// inside known-covariant stdlib types such as an array or dictionary.
56-
@_optimize(none)
57-
func convariantSelfErasureTest() {
54+
Tests.test("Covariant 'Self' erasure") {
5855
struct S: P {
5956
static let str = "Success"
6057
func getString() -> String { Self.str }
@@ -110,7 +107,4 @@ func convariantSelfErasureTest() {
110107
expectEqual(true, S() is P)
111108
}
112109

113-
114-
Tests.test("Covariant 'Self' erasure", convariantSelfErasureTest)
115-
116110
runAllTests()

0 commit comments

Comments
 (0)