Skip to content

Commit 7eafb4b

Browse files
committed
[sil-devirtualizer] Fix devirtualization of partial_apply on generic witness_methods
Properly cast the result of a devirtualized partial_apply, because this may be required in case of classes implementing protocols. More specifically, it came up when there is a derived class of a class implementing an initializer required by the protocol. Fixes rdar://31459426 (SR-4501) and rdar://31479426 (SR-3476)
1 parent 4463bb8 commit 7eafb4b

File tree

2 files changed

+83
-12
lines changed

2 files changed

+83
-12
lines changed

lib/SILOptimizer/Utils/Devirtualize.cpp

Lines changed: 30 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -846,8 +846,9 @@ static void getWitnessMethodSubstitutions(ApplySite AI, SILFunction *F,
846846
/// Generate a new apply of a function_ref to replace an apply of a
847847
/// witness_method when we've determined the actual function we'll end
848848
/// up calling.
849-
static ApplySite devirtualizeWitnessMethod(ApplySite AI, SILFunction *F,
850-
ProtocolConformanceRef C) {
849+
static DevirtualizationResult
850+
devirtualizeWitnessMethod(ApplySite AI, SILFunction *F,
851+
ProtocolConformanceRef C) {
851852
// We know the witness thunk and the corresponding set of substitutions
852853
// required to invoke the protocol method at this point.
853854
auto &Module = AI.getModule();
@@ -893,20 +894,38 @@ static ApplySite devirtualizeWitnessMethod(ApplySite AI, SILFunction *F,
893894
auto ResultSILType = substConv.getSILResultType();
894895
ApplySite SAI;
895896

896-
if (auto *A = dyn_cast<ApplyInst>(AI))
897-
SAI = Builder.createApply(Loc, FRI, SubstCalleeSILType,
898-
ResultSILType, NewSubs, Arguments,
899-
A->isNonThrowing());
897+
SILValue ResultValue;
898+
if (auto *A = dyn_cast<ApplyInst>(AI)) {
899+
auto *NewAI =
900+
Builder.createApply(Loc, FRI, SubstCalleeSILType, ResultSILType,
901+
NewSubs, Arguments, A->isNonThrowing());
902+
// Check if any casting is required for the return value.
903+
ResultValue = castValueToABICompatibleType(&Builder, Loc, NewAI,
904+
NewAI->getType(), AI.getType());
905+
SAI = ApplySite::isa(NewAI);
906+
}
900907
if (auto *TAI = dyn_cast<TryApplyInst>(AI))
901908
SAI = Builder.createTryApply(Loc, FRI, SubstCalleeSILType,
902909
NewSubs, Arguments,
903910
TAI->getNormalBB(), TAI->getErrorBB());
904-
if (auto *PAI = dyn_cast<PartialApplyInst>(AI))
905-
SAI = Builder.createPartialApply(Loc, FRI, SubstCalleeSILType,
906-
NewSubs, Arguments, PAI->getType());
911+
if (auto *PAI = dyn_cast<PartialApplyInst>(AI)) {
912+
auto PartialApplyConvention = PAI->getType()
913+
.getSwiftRValueType()
914+
->getAs<SILFunctionType>()
915+
->getCalleeConvention();
916+
auto PAIResultType = SILBuilder::getPartialApplyResultType(
917+
SubstCalleeSILType, Arguments.size(), Module, {},
918+
PartialApplyConvention);
919+
auto *NewPAI = Builder.createPartialApply(
920+
Loc, FRI, SubstCalleeSILType, NewSubs, Arguments, PAIResultType);
921+
// Check if any casting is required for the return value.
922+
ResultValue = castValueToABICompatibleType(
923+
&Builder, Loc, NewPAI, NewPAI->getType(), PAI->getType());
924+
SAI = ApplySite::isa(NewPAI);
925+
}
907926

908927
NumWitnessDevirt++;
909-
return SAI;
928+
return std::make_pair(ResultValue, SAI);
910929
}
911930

912931
static bool canDevirtualizeWitnessMethod(ApplySite AI) {
@@ -948,8 +967,7 @@ DevirtualizationResult swift::tryDevirtualizeWitnessMethod(ApplySite AI) {
948967
AI.getModule().lookUpFunctionInWitnessTable(WMI->getConformance(),
949968
WMI->getMember());
950969

951-
auto Result = devirtualizeWitnessMethod(AI, F, WMI->getConformance());
952-
return std::make_pair(Result.getInstruction(), Result);
970+
return devirtualizeWitnessMethod(AI, F, WMI->getConformance());
953971
}
954972

955973
//===----------------------------------------------------------------------===//
Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
// RUN: %target-sil-opt -assume-parsing-unqualified-ownership-sil -enable-sil-verify-all %s -devirtualizer | %FileCheck %s
2+
3+
sil_stage canonical
4+
5+
import Builtin
6+
import Swift
7+
import SwiftShims
8+
9+
public protocol Decodable {
10+
init(json: [String : Any])
11+
}
12+
13+
public class Item : Decodable {
14+
required public init(json: [String : Any])
15+
deinit
16+
}
17+
18+
public class ItemSubclass : Item {
19+
deinit
20+
required public init(json: [String : Any])
21+
}
22+
23+
sil @witness_method_impl : $@convention(witness_method) (@owned Dictionary<String, Any>, @thick Item.Type) -> @out Item {
24+
bb0(%0 : $*Item, %1 : $Dictionary<String, Any>, %2 : $@thick Item.Type):
25+
%3 = class_method %2 : $@thick Item.Type, #Item.init!allocator.1 : (Item.Type) -> ([String : Any]) -> Item, $@convention(method) (@owned Dictionary<String, Any>, @thick Item.Type) -> @owned Item
26+
%4 = apply %3(%1, %2) : $@convention(method) (@owned Dictionary<String, Any>, @thick Item.Type) -> @owned Item
27+
store %4 to %0 : $*Item
28+
%6 = tuple ()
29+
return %6 : $()
30+
}
31+
32+
// Check that it is possible to devirtualize a partial_appy of a generic witness_method.
33+
// Since it is a derived class that invokes an implementation from a base class,
34+
// make sure that the resulting closure is properly converted into a required type.
35+
36+
// CHECK-LABEL: sil @test_generic_witness_method_partial_apply_devirt : $@convention(thin) (@thick ItemSubclass.Type) -> @owned @callee_owned (@owned Dictionary<String, Any>) -> @out ItemSubclass
37+
// CHECK: %[[UPCASTED_ARG:[0-9]+]] = upcast %0 : $@thick ItemSubclass.Type to $@thick Item.Type
38+
// CHECK: %[[FUNCTION_REF:[0-9]+]] = function_ref @witness_method_impl
39+
// CHECK: %[[PA:[0-9]+]] = partial_apply %[[FUNCTION_REF]](%[[UPCASTED_ARG]]) : $@convention(witness_method) (@owned Dictionary<String, Any>, @thick Item.Type) -> @out Item
40+
// CHECK: %[[RETURN_VALUE:[0-9]+]] = convert_function %[[PA]] : $@callee_owned (@owned Dictionary<String, Any>) -> @out Item to $@callee_owned (@owned Dictionary<String, Any>) -> @out ItemSubclass
41+
// CHECK: return %[[RETURN_VALUE]] : $@callee_owned (@owned Dictionary<String, Any>) -> @out ItemSubclass
42+
43+
sil @test_generic_witness_method_partial_apply_devirt : $@convention(thin) (@thick ItemSubclass.Type) -> @owned @callee_owned (@owned Dictionary<String, Any>) -> @out ItemSubclass {
44+
bb0(%0 : $@thick ItemSubclass.Type):
45+
%1 = witness_method $ItemSubclass, #Decodable.init!allocator.1 : <Self where Self : Decodable> (Self.Type) -> ([String : Any]) -> Self : $@convention(witness_method) <τ_0_0 where τ_0_0 : Decodable> (@owned Dictionary<String, Any>, @thick τ_0_0.Type) -> @out τ_0_0
46+
%2 = partial_apply %1<ItemSubclass>(%0) : $@convention(witness_method) <τ_0_0 where τ_0_0 : Decodable> (@owned Dictionary<String, Any>, @thick τ_0_0.Type) -> @out τ_0_0
47+
return %2 : $@callee_owned (@owned Dictionary<String, Any>) -> @out ItemSubclass
48+
}
49+
50+
sil_witness_table [serialized] Item: Decodable module main {
51+
method #Decodable.init!allocator.1: <Self where Self : Decodable> (Self.Type) -> ([String : Any]) -> Self : @witness_method_impl
52+
}
53+

0 commit comments

Comments
 (0)