Skip to content

Commit 55297ae

Browse files
authored
Merge pull request #11724 from slavapestov/inherited-conformances-are-confusing
Remove ProtocolConformanceRef::getInherited()
2 parents 0af574a + 96822b1 commit 55297ae

File tree

9 files changed

+139
-81
lines changed

9 files changed

+139
-81
lines changed

include/swift/AST/ASTContext.h

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -866,6 +866,11 @@ class ASTContext {
866866
/// like `<T>`.
867867
CanGenericSignature getSingleGenericParameterSignature() const;
868868

869+
/// Retrieve a generic signature with a single type parameter conforming
870+
/// to the given existential type.
871+
CanGenericSignature getExistentialSignature(CanType existential,
872+
ModuleDecl *mod);
873+
869874
/// Whether our effective Swift version is in the Swift 3 family.
870875
bool isSwiftVersion3() const { return LangOpts.isSwiftVersion3(); }
871876

include/swift/AST/ProtocolConformanceRef.h

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -86,11 +86,6 @@ class ProtocolConformanceRef {
8686
/// Return the protocol requirement.
8787
ProtocolDecl *getRequirement() const;
8888

89-
/// Get the inherited conformance corresponding to the given protocol.
90-
/// Returns `this` if `parent` is already the same as the protocol this
91-
/// conformance represents.
92-
ProtocolConformanceRef getInherited(ProtocolDecl *parent) const;
93-
9489
/// Apply a substitution to the conforming type.
9590
ProtocolConformanceRef subst(Type origType,
9691
TypeSubstitutionFn subs,

lib/AST/ASTContext.cpp

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -261,6 +261,9 @@ FOR_KNOWN_FOUNDATION_TYPES(CACHE_FOUNDATION_DECL)
261261
/// The single-parameter generic signature with no constraints, <T>.
262262
CanGenericSignature SingleGenericParameterSignature;
263263

264+
/// The existential signature <T : P> for each P.
265+
llvm::DenseMap<CanType, CanGenericSignature> ExistentialSignatures;
266+
264267
/// \brief Structure that captures data that is segregated into different
265268
/// arenas.
266269
struct Arena {
@@ -4509,6 +4512,35 @@ CanGenericSignature ASTContext::getSingleGenericParameterSignature() const {
45094512
return canonicalSig;
45104513
}
45114514

4515+
CanGenericSignature ASTContext::getExistentialSignature(CanType existential,
4516+
ModuleDecl *mod) {
4517+
auto found = Impl.ExistentialSignatures.find(existential);
4518+
if (found != Impl.ExistentialSignatures.end())
4519+
return found->second;
4520+
4521+
assert(existential.isExistentialType());
4522+
4523+
GenericSignatureBuilder builder(*this, LookUpConformanceInModule(mod));
4524+
4525+
auto genericParam = GenericTypeParamType::get(0, 0, *this);
4526+
builder.addGenericParameter(genericParam);
4527+
4528+
Requirement requirement(RequirementKind::Conformance, genericParam,
4529+
existential);
4530+
auto source =
4531+
GenericSignatureBuilder::FloatingRequirementSource::forAbstract();
4532+
builder.addRequirement(requirement, source, nullptr);
4533+
4534+
CanGenericSignature genericSig(builder.computeGenericSignature(SourceLoc()));
4535+
4536+
auto result = Impl.ExistentialSignatures.insert(
4537+
std::make_pair(existential, genericSig));
4538+
assert(result.second);
4539+
(void) result;
4540+
4541+
return genericSig;
4542+
}
4543+
45124544
SILLayout *SILLayout::get(ASTContext &C,
45134545
CanGenericSignature Generics,
45144546
ArrayRef<SILField> Fields) {

lib/AST/ProtocolConformance.cpp

Lines changed: 0 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -77,30 +77,6 @@ ProtocolDecl *ProtocolConformanceRef::getRequirement() const {
7777
}
7878
}
7979

80-
ProtocolConformanceRef
81-
ProtocolConformanceRef::getInherited(ProtocolDecl *parent) const {
82-
assert((getRequirement() == parent ||
83-
getRequirement()->inheritsFrom(parent)) &&
84-
"not a parent of this protocol");
85-
86-
if (parent == getRequirement())
87-
return *this;
88-
89-
// For an abstract requirement, simply produce a new abstract requirement
90-
// for the parent.
91-
if (isAbstract()) {
92-
return ProtocolConformanceRef(parent);
93-
}
94-
95-
// Navigate concrete conformances.
96-
if (isConcrete()) {
97-
return ProtocolConformanceRef(
98-
getConcrete()->getInheritedConformance(parent));
99-
}
100-
101-
llvm_unreachable("unhandled ProtocolConformanceRef");
102-
}
103-
10480
ProtocolConformanceRef
10581
ProtocolConformanceRef::subst(Type origType,
10682
TypeSubstitutionFn subs,

lib/IRGen/GenProto.cpp

Lines changed: 3 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -2347,9 +2347,7 @@ llvm::Value *irgen::emitWitnessTableRef(IRGenFunction &IGF,
23472347
// more concrete than we're expecting.
23482348
// TODO: make a best effort to devirtualize, maybe?
23492349
auto concreteConformance = conformance.getConcrete();
2350-
if (concreteConformance->getProtocol() != proto) {
2351-
concreteConformance = concreteConformance->getInheritedConformance(proto);
2352-
}
2350+
assert(concreteConformance->getProtocol() == proto);
23532351

23542352
// Check immediately for an existing cache entry.
23552353
auto wtable = IGF.tryGetLocalTypeData(
@@ -2823,11 +2821,10 @@ irgen::emitWitnessMethodValue(IRGenFunction &IGF,
28232821
CanSILFunctionType fnType) {
28242822
auto fn = cast<AbstractFunctionDecl>(member.getDecl());
28252823
auto fnProto = cast<ProtocolDecl>(fn->getDeclContext());
2826-
2827-
conformance = conformance.getInherited(fnProto);
2824+
2825+
assert(conformance.getRequirement() == fnProto);
28282826

28292827
// Find the witness table.
2830-
// FIXME conformance for concrete type
28312828
llvm::Value *wtable = emitWitnessTableRef(IGF, baseTy, baseMetadataCache,
28322829
conformance);
28332830

lib/SIL/SILInstructions.cpp

Lines changed: 14 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1572,19 +1572,27 @@ DynamicMethodBranchInst::create(SILDebugLocation Loc, SILValue Operand,
15721572
DynamicMethodBranchInst(Loc, Operand, Member, HasMethodBB, NoMethodBB);
15731573
}
15741574

1575-
/// Create a witness method, creating a witness table declaration if we don't
1576-
/// have a witness table for it. Later on if someone wants the real definition,
1577-
/// lookUpWitnessTable will deserialize it for us if we can.
1575+
/// Create a witness method call of a protocol requirement, passing in a lookup
1576+
/// type and conformance.
15781577
///
1579-
/// This is following the same model of how we deal with SILFunctions in
1580-
/// function_ref. There we always just create a declaration and then later
1581-
/// deserialize the actual function definition if we need to.
1578+
/// At runtime, the witness is looked up in the conformance of the lookup type
1579+
/// to the protocol.
1580+
///
1581+
/// The lookup type is usually an archetype, but it will be concrete if the
1582+
/// witness_method instruction is inside a function body that was specialized.
1583+
///
1584+
/// The conformance must exactly match the requirement; the caller must handle
1585+
/// the case where the requirement is defined in a base protocol that is
1586+
/// refined by the conforming protocol.
15821587
WitnessMethodInst *
15831588
WitnessMethodInst::create(SILDebugLocation Loc, CanType LookupType,
15841589
ProtocolConformanceRef Conformance, SILDeclRef Member,
15851590
SILType Ty, SILFunction *F,
15861591
SILOpenedArchetypesState &OpenedArchetypes,
15871592
bool Volatile) {
1593+
assert(cast<ProtocolDecl>(Member.getDecl()->getDeclContext())
1594+
== Conformance.getRequirement());
1595+
15881596
SILModule &Mod = F->getModule();
15891597
SmallVector<SILValue, 8> TypeDependentOperands;
15901598
collectTypeDependentOperands(TypeDependentOperands, OpenedArchetypes, *F,

lib/SILOptimizer/SILCombiner/SILCombinerApplyVisitors.cpp

Lines changed: 41 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -710,7 +710,8 @@ SILCombiner::createApplyWithConcreteType(FullApplySite AI,
710710
[&](CanType origTy, Type substTy,
711711
ProtocolType *proto) -> Optional<ProtocolConformanceRef> {
712712
if (substTy->isEqual(ConcreteType)) {
713-
return Conformance.getInherited(proto->getDecl());
713+
assert(proto->getDecl() == Conformance.getRequirement());
714+
return Conformance;
714715
}
715716
return ProtocolConformanceRef(proto->getDecl());
716717
});
@@ -752,69 +753,70 @@ SILCombiner::createApplyWithConcreteType(FullApplySite AI,
752753

753754
/// Derive a concrete type of self and conformance from the init_existential
754755
/// instruction.
755-
static Optional<std::tuple<ProtocolConformanceRef, CanType, SILValue>>
756-
getConformanceAndConcreteType(FullApplySite AI,
756+
static Optional<std::tuple<ProtocolConformanceRef, CanType, SILValue, SILValue>>
757+
getConformanceAndConcreteType(ASTContext &Ctx,
758+
FullApplySite AI,
757759
SILInstruction *InitExistential,
758760
ProtocolDecl *Protocol,
759-
SILValue &NewSelf,
760761
ArrayRef<ProtocolConformanceRef> &Conformances) {
761762
// Try to derive the concrete type of self from the found init_existential.
762763
CanType ConcreteType;
764+
// The existential type result of the found init_existential.
765+
CanType ExistentialType;
763766
SILValue ConcreteTypeDef;
767+
SILValue NewSelf;
768+
769+
// FIXME: Factor this out. All we really need here is the ExistentialSig
770+
// below, which should be stored directly in the SILInstruction.
764771
if (auto IE = dyn_cast<InitExistentialAddrInst>(InitExistential)) {
765772
Conformances = IE->getConformances();
766773
ConcreteType = IE->getFormalConcreteType();
767774
NewSelf = IE;
775+
ExistentialType = IE->getOperand()->getType().getSwiftRValueType();
768776
} else if (auto IER = dyn_cast<InitExistentialRefInst>(InitExistential)) {
769777
Conformances = IER->getConformances();
770778
ConcreteType = IER->getFormalConcreteType();
771779
NewSelf = IER->getOperand();
780+
ExistentialType = IER->getType().getSwiftRValueType();
772781
} else if (auto IEM = dyn_cast<InitExistentialMetatypeInst>(InitExistential)){
773782
Conformances = IEM->getConformances();
774783
NewSelf = IEM->getOperand();
775784
ConcreteType = NewSelf->getType().getSwiftRValueType();
776-
777-
auto ExType = IEM->getType().getSwiftRValueType();
778-
while (auto ExMetatype = dyn_cast<ExistentialMetatypeType>(ExType)) {
779-
ExType = ExMetatype.getInstanceType();
785+
ExistentialType = IEM->getType().getSwiftRValueType();
786+
while (auto InstanceType = dyn_cast<ExistentialMetatypeType>(ExistentialType)) {
787+
ExistentialType = InstanceType.getInstanceType();
780788
ConcreteType = cast<MetatypeType>(ConcreteType).getInstanceType();
781789
}
782790
} else {
783791
return None;
784792
}
785793

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

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

820822
/// Propagate information about a concrete type from init_existential_addr
@@ -827,6 +829,8 @@ SILCombiner::propagateConcreteTypeOfInitExistential(FullApplySite AI,
827829
ProtocolDecl *Protocol,
828830
llvm::function_ref<void(CanType , ProtocolConformanceRef)> Propagate) {
829831

832+
ASTContext &Ctx = Builder.getASTContext();
833+
830834
// Get the self argument.
831835
SILValue Self;
832836
if (auto *Apply = dyn_cast<ApplyInst>(AI)) {
@@ -851,16 +855,16 @@ SILCombiner::propagateConcreteTypeOfInitExistential(FullApplySite AI,
851855
// Try to derive the concrete type of self and a related conformance from
852856
// the found init_existential.
853857
ArrayRef<ProtocolConformanceRef> Conformances;
854-
auto NewSelf = SILValue();
855858
auto ConformanceAndConcreteType =
856-
getConformanceAndConcreteType(AI, InitExistential,
857-
Protocol, NewSelf, Conformances);
859+
getConformanceAndConcreteType(Ctx, AI, InitExistential,
860+
Protocol, Conformances);
858861
if (!ConformanceAndConcreteType)
859862
return nullptr;
860863

861864
ProtocolConformanceRef Conformance = std::get<0>(*ConformanceAndConcreteType);
862865
CanType ConcreteType = std::get<1>(*ConformanceAndConcreteType);
863866
SILValue ConcreteTypeDef = std::get<2>(*ConformanceAndConcreteType);
867+
SILValue NewSelf = std::get<3>(*ConformanceAndConcreteType);
864868

865869
SILOpenedArchetypesTracker *OldOpenedArchetypesTracker =
866870
Builder.getOpenedArchetypesTracker();

lib/Sema/TypeCheckProtocol.cpp

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -69,9 +69,49 @@ namespace {
6969
/// generic requirements. See the \c Witness class for more information about
7070
/// this synthetic environment.
7171
class RequirementEnvironment {
72+
/// A generic signature that combines the generic parameters of the
73+
/// concrete conforming type with the generic parameters of the
74+
/// requirement.
75+
///
76+
///
77+
/// For example, if you have:
78+
///
79+
/// protocol P { func f<T>(_: T) }
80+
/// struct S<A, B> : P { func f<T>(_: T) }
81+
///
82+
/// The requirement and witness signatures are, respectively:
83+
///
84+
/// <Self : P, T>
85+
/// <A, B, T>
86+
///
87+
/// The synthetic signature in this case is just the witness signature.
88+
///
89+
/// It may be that the witness is more generic than the requirement,
90+
/// for example:
91+
///
92+
/// protocol P { func f(_: Int) }
93+
/// struct S<A, B> : P { func f<T>(_: T) { } }
94+
///
95+
/// Here, the requirement signature and witness signatures are:
96+
///
97+
/// <Self : P>
98+
/// <A, B, T>
99+
///
100+
/// The synthetic signature is just:
101+
///
102+
/// <A, B>
103+
///
104+
/// The witness thunk emitted by SILGen uses the synthetic signature.
105+
/// Therefore one invariant we preserve is that the witness thunk is
106+
/// ABI compatible with the requirement's function type.
72107
GenericSignature *syntheticSignature = nullptr;
73108
GenericEnvironment *syntheticEnvironment = nullptr;
109+
110+
/// The generic signature of the protocol requirement member.
74111
GenericSignature *reqSig = nullptr;
112+
113+
/// A substitution map mapping the requirement signature to the
114+
/// generic parameters of the synthetic signature.
75115
SubstitutionMap reqToSyntheticEnvMap;
76116

77117
public:

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)