Skip to content

Commit 96ee115

Browse files
committed
SILCombiner: Use ASTContext::getExistentialSignature()
This addresses the unsupported case outlined in the FIXME. We have an init_existential_{addr,ref} instruction which constructs an existential of type R from a value whose concrete type conforms to R. Later on, we have a witness_method invoking a method of P, where R refines Q refines P. In order to give the witness_method instruction the correct conformance after substituting the concrete type, we would call ProtocolConformanceRef::getInherited(). However, this only supported a single level of protocol refinement, so we would just bail out in the unsupported case. Instead, this patch builds a SubstitutionMap from the generic signature <T : R>, and uses the SubstitutionMap to perform the conformance lookup of T to P.
1 parent c522b49 commit 96ee115

File tree

2 files changed

+39
-37
lines changed

2 files changed

+39
-37
lines changed

lib/SILOptimizer/SILCombiner/SILCombinerApplyVisitors.cpp

Lines changed: 35 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -753,70 +753,69 @@ SILCombiner::createApplyWithConcreteType(FullApplySite AI,
753753
/// Derive a concrete type of self and conformance from the init_existential
754754
/// instruction.
755755
static Optional<std::tuple<ProtocolConformanceRef, CanType, SILValue, SILValue>>
756-
getConformanceAndConcreteType(FullApplySite AI,
756+
getConformanceAndConcreteType(ASTContext &Ctx,
757+
FullApplySite AI,
757758
SILInstruction *InitExistential,
758759
ProtocolDecl *Protocol,
759760
ArrayRef<ProtocolConformanceRef> &Conformances) {
760761
// Try to derive the concrete type of self from the found init_existential.
761762
CanType ConcreteType;
763+
// The existential type result of the found init_existential.
764+
CanType ExistentialType;
762765
SILValue ConcreteTypeDef;
763766
SILValue NewSelf;
764767

768+
// FIXME: Factor this out. All we really need here is the ExistentialSig
769+
// below, which should be stored directly in the SILInstruction.
765770
if (auto IE = dyn_cast<InitExistentialAddrInst>(InitExistential)) {
766771
Conformances = IE->getConformances();
767772
ConcreteType = IE->getFormalConcreteType();
768773
NewSelf = IE;
774+
ExistentialType = IE->getOperand()->getType().getSwiftRValueType();
769775
} else if (auto IER = dyn_cast<InitExistentialRefInst>(InitExistential)) {
770776
Conformances = IER->getConformances();
771777
ConcreteType = IER->getFormalConcreteType();
772778
NewSelf = IER->getOperand();
779+
ExistentialType = IER->getType().getSwiftRValueType();
773780
} else if (auto IEM = dyn_cast<InitExistentialMetatypeInst>(InitExistential)){
774781
Conformances = IEM->getConformances();
775782
NewSelf = IEM->getOperand();
776783
ConcreteType = NewSelf->getType().getSwiftRValueType();
777-
778-
auto ExType = IEM->getType().getSwiftRValueType();
779-
while (auto ExMetatype = dyn_cast<ExistentialMetatypeType>(ExType)) {
780-
ExType = ExMetatype.getInstanceType();
784+
ExistentialType = IEM->getType().getSwiftRValueType();
785+
while (auto InstanceType = dyn_cast<ExistentialMetatypeType>(ExistentialType)) {
786+
ExistentialType = InstanceType.getInstanceType();
781787
ConcreteType = cast<MetatypeType>(ConcreteType).getInstanceType();
782788
}
783789
} else {
784790
return None;
785791
}
786792

793+
// Construct a substitution map from the existential type's generic
794+
// parameter to the concrete type.
795+
auto ExistentialSig = Ctx.getExistentialSignature(ExistentialType,
796+
AI.getModule().getSwiftModule());
797+
798+
Substitution ConcreteSub(ConcreteType, Conformances);
799+
auto SubMap = ExistentialSig->getSubstitutionMap({&ConcreteSub, 1});
800+
801+
// If the requirement is in a base protocol that is refined by the
802+
// conforming protocol, fish out the exact conformance for the base
803+
// protocol using the substitution map.
804+
auto ExactConformance = SubMap.lookupConformance(
805+
CanType(ExistentialSig->getGenericParams()[0]),
806+
Protocol);
807+
808+
// If the concrete type is another existential, we're "forwarding" an
809+
// opened existential type, so we must keep track of the original
810+
// defining instruction.
787811
if (ConcreteType->isOpenedExistential()) {
788812
assert(!InitExistential->getTypeDependentOperands().empty() &&
789813
"init_existential is supposed to have a typedef operand");
790814
ConcreteTypeDef = InitExistential->getTypeDependentOperands()[0].get();
791815
}
792816

793-
// Find the conformance for the protocol we're interested in.
794-
for (auto Conformance : Conformances) {
795-
auto Requirement = Conformance.getRequirement();
796-
if (Requirement == Protocol) {
797-
return std::make_tuple(Conformance, ConcreteType, ConcreteTypeDef,
798-
NewSelf);
799-
}
800-
// If Requirement != Protocol, then the abstract conformance cannot be
801-
// used as is and we need to create a proper conformance.
802-
// FIXME: We can handle only direct inheritance at the moment due to some
803-
// limitations of the init_existential_* instructions representation.
804-
// Once these instructions start using generic signatures instead of
805-
// conformances lists, it should be fairly easy to support the indirect
806-
// inheritance here by something like:
807-
// Substitution Sub(ConcreteType, Conformances);
808-
// IE->getGenericSignature()
809-
// ->getSubstitutionMap({Sub}).lookupConformance(GP00, Protocol);
810-
auto InheritedProtocols = Requirement->getInheritedProtocols();
811-
if (std::find(InheritedProtocols.begin(), InheritedProtocols.end(),
812-
Protocol) == InheritedProtocols.end())
813-
return None;
814-
// Requirement is directly inherited from Protocol.
815-
return std::make_tuple(Conformance.getInherited(Protocol), ConcreteType,
816-
ConcreteTypeDef, NewSelf);
817-
}
818-
819-
llvm_unreachable("couldn't find matching conformance in substitution?");
817+
return std::make_tuple(*ExactConformance, ConcreteType,
818+
ConcreteTypeDef, NewSelf);
820819
}
821820

822821
/// Propagate information about a concrete type from init_existential_addr
@@ -829,6 +828,8 @@ SILCombiner::propagateConcreteTypeOfInitExistential(FullApplySite AI,
829828
ProtocolDecl *Protocol,
830829
llvm::function_ref<void(CanType , ProtocolConformanceRef)> Propagate) {
831830

831+
ASTContext &Ctx = Builder.getASTContext();
832+
832833
// Get the self argument.
833834
SILValue Self;
834835
if (auto *Apply = dyn_cast<ApplyInst>(AI)) {
@@ -854,8 +855,8 @@ SILCombiner::propagateConcreteTypeOfInitExistential(FullApplySite AI,
854855
// the found init_existential.
855856
ArrayRef<ProtocolConformanceRef> Conformances;
856857
auto ConformanceAndConcreteType =
857-
getConformanceAndConcreteType(AI, InitExistential,
858-
Protocol, Conformances);
858+
getConformanceAndConcreteType(Ctx, AI, InitExistential,
859+
Protocol, Conformances);
859860
if (!ConformanceAndConcreteType)
860861
return nullptr;
861862

test/SILOptimizer/devirt_protocol_method_invocations.swift

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -28,11 +28,12 @@ public func testInheritedConformance() {
2828
(S() as QQQ).f()
2929
}
3030

31-
// Test that a witness_method instructions is not devirtualized yet, because
32-
// it uses indirect inheritance.
31+
// Test that a witness_method instruction using an indirectly-inherited conformance
32+
// is devirtualized.
33+
//
3334
// This test used to crash the compiler because it uses inherited conformances.
3435
// CHECK-LABEL: sil @_T034devirt_protocol_method_invocations34testIndirectlyInheritedConformanceyyF : $@convention(thin) () -> ()
35-
// CHECK: witness_method
36+
// CHECK-NOT: witness_method
3637
// CHECK: apply
3738
// CHECK: // end sil function '_T034devirt_protocol_method_invocations34testIndirectlyInheritedConformanceyyF'
3839
public func testIndirectlyInheritedConformance() {

0 commit comments

Comments
 (0)