Skip to content

Commit fb62977

Browse files
committed
[IRGen] Emit default associated conformance witnesses.
For a resilient protocol that has defaulted associated types, emit default associated conformance witnesses that compute associated conformances based on that default witness. This completes the implementation of resilience protocols that add new, defaulted associated types, rdar://problem/44167982.
1 parent fde7eb5 commit fb62977

File tree

9 files changed

+182
-5
lines changed

9 files changed

+182
-5
lines changed

lib/IRGen/GenDecl.cpp

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3982,6 +3982,25 @@ IRGenModule::getAddrOfDefaultAssociatedTypeMetadataAccessFunction(
39823982
return entry;
39833983
}
39843984

3985+
llvm::Function *
3986+
IRGenModule::getAddrOfDefaultAssociatedConformanceAccessor(
3987+
AssociatedConformance requirement) {
3988+
auto forDefinition = ForDefinition;
3989+
3990+
LinkEntity entity =
3991+
LinkEntity::forDefaultAssociatedConformanceAccessor(requirement);
3992+
llvm::Function *&entry = GlobalFuncs[entity];
3993+
if (entry) {
3994+
if (forDefinition) updateLinkageForDefinition(*this, entry, entity);
3995+
return entry;
3996+
}
3997+
3998+
auto signature = getAssociatedTypeWitnessTableAccessFunctionSignature();
3999+
LinkInfo link = LinkInfo::get(*this, entity, forDefinition);
4000+
entry = createFunction(*this, link, signature);
4001+
return entry;
4002+
}
4003+
39854004
llvm::Function *
39864005
IRGenModule::getAddrOfContinuationPrototype(CanSILFunctionType fnType) {
39874006
LinkEntity entity = LinkEntity::forCoroutineContinuationPrototype(fnType);

lib/IRGen/GenMeta.cpp

Lines changed: 91 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -647,7 +647,14 @@ namespace {
647647

648648
if (entry.isAssociatedConformance()) {
649649
auto flags = Flags(Flags::Kind::AssociatedConformanceAccessFunction);
650-
return { flags, nullptr };
650+
651+
// Look for a default witness.
652+
llvm::Constant *defaultImpl =
653+
findDefaultAssociatedConformanceWitness(
654+
entry.getAssociatedConformancePath(),
655+
entry.getAssociatedConformanceRequirement());
656+
657+
return { flags, defaultImpl };
651658
}
652659

653660
assert(entry.isFunction());
@@ -802,6 +809,89 @@ namespace {
802809
IGF.Builder.CreateRet(returnValue);
803810
return accessor;
804811
}
812+
813+
llvm::Constant *findDefaultAssociatedConformanceWitness(
814+
CanType association,
815+
ProtocolDecl *requirement) {
816+
if (!DefaultWitnesses) return nullptr;
817+
818+
for (auto &entry : DefaultWitnesses->getEntries()) {
819+
if (!entry.isValid() ||
820+
entry.getKind() != SILWitnessTable::AssociatedTypeProtocol ||
821+
entry.getAssociatedTypeProtocolWitness().Protocol != requirement ||
822+
entry.getAssociatedTypeProtocolWitness().Requirement != association)
823+
continue;
824+
825+
auto witness = entry.getAssociatedTypeProtocolWitness().Witness;
826+
return getDefaultAssociatedConformanceAccessFunction(
827+
AssociatedConformance(Proto, association, requirement),
828+
witness);
829+
}
830+
831+
return nullptr;
832+
}
833+
834+
llvm::Constant *getDefaultAssociatedConformanceAccessFunction(
835+
AssociatedConformance requirement,
836+
ProtocolConformanceRef conformance) {
837+
auto accessor =
838+
IGM.getAddrOfDefaultAssociatedConformanceAccessor(requirement);
839+
840+
IRGenFunction IGF(IGM, accessor);
841+
if (IGM.DebugInfo)
842+
IGM.DebugInfo->emitArtificialFunction(IGF, accessor);
843+
844+
Explosion parameters = IGF.collectParameters();
845+
846+
llvm::Value *associatedTypeMetadata = parameters.claimNext();
847+
llvm::Value *self = parameters.claimNext();
848+
llvm::Value *wtable = parameters.claimNext();
849+
850+
bool hasArchetype =
851+
!conformance.isConcrete() ||
852+
conformance.getConcrete()->getType()->hasArchetype();
853+
if (hasArchetype) {
854+
// Bind local Self type data from the metadata argument.
855+
CanType selfInContext =
856+
Proto->mapTypeIntoContext(Proto->getProtocolSelfType())
857+
->getCanonicalType();
858+
IGF.bindLocalTypeDataFromTypeMetadata(selfInContext, IsExact, self,
859+
MetadataState::Abstract);
860+
IGF.setUnscopedLocalTypeData(
861+
selfInContext,
862+
LocalTypeDataKind::forAbstractProtocolWitnessTable(Proto),
863+
wtable);
864+
865+
// Bind the associated type metadata.
866+
IGF.bindLocalTypeDataFromTypeMetadata(requirement.getAssociation(),
867+
IsExact,
868+
associatedTypeMetadata,
869+
MetadataState::Abstract);
870+
}
871+
872+
// For a concrete witness table, call it.
873+
ProtocolDecl *associatedProtocol = requirement.getAssociatedRequirement();
874+
if (conformance.isConcrete()) {
875+
auto conformanceI = &IGM.getConformanceInfo(associatedProtocol,
876+
conformance.getConcrete());
877+
auto returnValue = conformanceI->getTable(IGF, &associatedTypeMetadata);
878+
IGF.Builder.CreateRet(returnValue);
879+
return accessor;
880+
}
881+
882+
// For an abstract table, emit a reference to the witness table.
883+
CanType associatedTypeInContext
884+
= Proto->mapTypeIntoContext(requirement.getAssociation())
885+
->getCanonicalType();
886+
auto returnValue =
887+
emitArchetypeWitnessTableRef(
888+
IGF,
889+
cast<ArchetypeType>(associatedTypeInContext),
890+
associatedProtocol);
891+
IGF.Builder.CreateRet(returnValue);
892+
return accessor;
893+
}
894+
805895
void addAssociatedTypeNames() {
806896
std::string AssociatedTypeNames;
807897

lib/IRGen/IRGenModule.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1291,6 +1291,8 @@ private: \
12911291
const AssociatedConformance &association);
12921292
llvm::Function *getAddrOfDefaultAssociatedTypeMetadataAccessFunction(
12931293
AssociatedType association);
1294+
llvm::Function *getAddrOfDefaultAssociatedConformanceAccessor(
1295+
AssociatedConformance requirement);
12941296

12951297
Address getAddrOfObjCISAMask();
12961298

lib/SILGen/SILGenType.cpp

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -769,7 +769,19 @@ class SILGenDefaultWitnessTable
769769
}
770770

771771
void addAssociatedConformance(const AssociatedConformance &req) {
772-
addMissingDefault();
772+
auto witness =
773+
Proto->getDefaultAssociatedConformanceWitness(
774+
req.getAssociation(),
775+
req.getAssociatedRequirement());
776+
if (!witness)
777+
return addMissingDefault();
778+
779+
auto entry =
780+
SILWitnessTable::AssociatedTypeProtocolWitness{
781+
req.getAssociation(),
782+
req.getAssociatedRequirement(),
783+
*witness};
784+
DefaultWitnesses.push_back(entry);
773785
}
774786
};
775787

test/IRGen/protocol_resilience_descriptors.swift

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414

1515
// Protocol descriptor
1616
// CHECK-DEFINITION-LABEL: @"$S18resilient_protocol29ProtocolWithAssocTypeDefaultsMp" ={{( protected)?}} constant
17+
// CHECK-DEFINITION-SAME: @"$S18resilient_protocol29ProtocolWithAssocTypeDefaultsP2T2_AA014OtherResilientC0TN"
1718
// CHECK-DEFINITION-SAME: $S2T118resilient_protocol29ProtocolWithAssocTypeDefaultsPTM
1819
// CHECK-DEFINITION-SAME: $S2T218resilient_protocol29ProtocolWithAssocTypeDefaultsPTM
1920

@@ -25,13 +26,18 @@
2526
// CHECK-DEFINITION: @"$S1T18resilient_protocol24ProtocolWithRequirementsPTl" ={{( dllexport)?}}{{( protected)?}} alias
2627
// CHECK-DEFINITION: @"$S18resilient_protocol29ProtocolWithAssocTypeDefaultsP2T2_AA014OtherResilientC0Tn" ={{( dllexport)?}}{{( protected)?}} alias
2728

29+
// Default associated conformance witnesses
30+
// CHECK-DEFINITION-LABEL: define internal swiftcc i8** @"$S18resilient_protocol29ProtocolWithAssocTypeDefaultsP2T2_AA014OtherResilientC0TN"
31+
2832
// Default associated type witnesses
2933
// CHECK-DEFINITION-LABEL: define internal swiftcc %swift.metadata_response @"$S2T118resilient_protocol29ProtocolWithAssocTypeDefaultsPTM"
3034

3135
// CHECK-DEFINITION-LABEL: define internal swiftcc %swift.metadata_response @"$S2T218resilient_protocol29ProtocolWithAssocTypeDefaultsPTM"
3236
// CHECK-DEFINITION: getelementptr inbounds i8*, i8** [[WTABLE:%.*]], i32 2
3337
// CHECK-DEFINITION: call{{.*}}S18resilient_protocol7WrapperVMa
3438

39+
// CHECK-DEFINITION-LABEL: define internal swiftcc %swift.metadata_response @"$S9AssocType18resilient_protocol20ResilientSelfDefaultPTM
40+
3541
import resilient_protocol
3642

3743
// ----------------------------------------------------------------------------

test/Inputs/resilient_protocol.swift

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,3 +34,7 @@ public protocol ProtocolWithAssocTypeDefaults {
3434
associatedtype T1 = Self
3535
associatedtype T2: OtherResilientProtocol = Wrapper<T1>
3636
}
37+
38+
public protocol ResilientSelfDefault : ResilientBaseProtocol {
39+
associatedtype AssocType: ResilientBaseProtocol = Self
40+
}

test/SILGen/protocol_resilience.swift

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -251,6 +251,13 @@ func inoutResilientProtocol(_ x: inout OtherConformingType) {
251251
inoutFunc(&OtherConformingType.staticPropertyInExtension)
252252
}
253253

254+
// Protocol is public -- needs resilient witness table
255+
public struct ConformsToP: P { }
256+
257+
public protocol ResilientAssocTypes {
258+
associatedtype AssocType: P = ConformsToP
259+
}
260+
254261
// CHECK-LABEL: sil_default_witness_table P {
255262
// CHECK-NEXT: }
256263

@@ -312,3 +319,8 @@ func inoutResilientProtocol(_ x: inout OtherConformingType) {
312319
// CHECK-NEXT: method #ReabstractSelfRefined.callback!setter.1: {{.*}} : @$S19protocol_resilience21ReabstractSelfRefinedP8callbackyxxcvs
313320
// CHECK-NEXT: method #ReabstractSelfRefined.callback!modify.1: {{.*}} : @$S19protocol_resilience21ReabstractSelfRefinedP8callbackyxxcvM
314321
// CHECK-NEXT: }
322+
323+
// CHECK-LABEL: sil_default_witness_table ResilientAssocTypes {
324+
// CHECK-NEXT: associated_type_protocol (AssocType: P): ConformsToP: P module protocol_resilience
325+
// CHECK-NEXT: associated_type AssocType: ConformsToP
326+
// CHECK-NEXT: }

validation-test/Evolution/Inputs/protocol_add_requirements.swift

Lines changed: 24 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -176,7 +176,15 @@ public func doSomething<T : AddSubscriptProtocol>(_ t: inout T, k1: T.Key, k2: T
176176
#endif
177177
}
178178

179-
public struct Wrapper<T> { }
179+
public protocol SimpleProtocol {
180+
static func getString() -> String
181+
}
182+
183+
public struct Wrapper<T>: SimpleProtocol {
184+
public static func getString() -> String {
185+
return "I am a wrapper for \(T.self)"
186+
}
187+
}
180188

181189
public protocol AddAssocTypesProtocol {
182190
// FIXME: The presence of a single method requirement causes us to
@@ -185,15 +193,14 @@ public protocol AddAssocTypesProtocol {
185193

186194
#if AFTER
187195
associatedtype AssocType = Self
188-
associatedtype AssocType2 = Wrapper<AssocType>
196+
associatedtype AssocType2: SimpleProtocol = Wrapper<AssocType>
189197
#endif
190198
}
191199

192200
extension AddAssocTypesProtocol {
193201
public func dummy() { }
194202
}
195203

196-
197204
public func doSomethingWithAssocTypes<T: AddAssocTypesProtocol>(_ value: T)
198205
-> String {
199206
#if AFTER
@@ -202,3 +209,17 @@ public func doSomethingWithAssocTypes<T: AddAssocTypesProtocol>(_ value: T)
202209
return "there are no associated types yet"
203210
#endif
204211
}
212+
213+
public func doSomethingWithAssocConformances<T: AddAssocTypesProtocol>(_ value: T)
214+
-> String {
215+
#if AFTER
216+
let at2: Any.Type = T.AssocType2.self
217+
if let simpleType = at2 as? SimpleProtocol.Type {
218+
return simpleType.getString()
219+
}
220+
221+
return "missing associated conformance"
222+
#else
223+
return "there are no associated conformances yet"
224+
#endif
225+
}

validation-test/Evolution/test_protocol_add_requirements.swift

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -154,5 +154,16 @@ ProtocolAddRequirementsTest.test("AddAssociatedTypeRequirements") {
154154
}
155155
}
156156

157+
ProtocolAddRequirementsTest.test("AddAssociatedConformanceRequirements") {
158+
let addString = AddAssociatedType<String>()
159+
let stringResult = doSomethingWithAssocConformances(addString)
160+
161+
if getVersion() == 0 {
162+
expectEqual("there are no associated conformances yet", stringResult)
163+
} else {
164+
expectEqual("I am a wrapper for AddAssociatedType<String>", stringResult)
165+
}
166+
}
167+
157168
runAllTests()
158169

0 commit comments

Comments
 (0)