Skip to content

Commit b9eb17c

Browse files
authored
Merge pull request #22737 from slavapestov/fix-protocol-resilience
Fix problem with protocol resilience
2 parents 4ea9dc8 + 71ab1bb commit b9eb17c

File tree

6 files changed

+139
-13
lines changed

6 files changed

+139
-13
lines changed

lib/IRGen/GenProto.cpp

Lines changed: 29 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -948,23 +948,35 @@ namespace {
948948

949949
/// Return true if the witness table requires runtime instantiation to
950950
/// handle resiliently-added requirements with default implementations.
951-
static bool isResilientConformance(const NormalProtocolConformance *conformance) {
951+
bool IRGenModule::isResilientConformance(
952+
const NormalProtocolConformance *conformance) {
952953
// If the protocol is not resilient, the conformance is not resilient
953954
// either.
954955
if (!conformance->getProtocol()->isResilient())
955956
return false;
956957

957-
// If the protocol is in the same module as the conformance, we're
958-
// not resilient.
959-
if (conformance->getDeclContext()->getParentModule()
960-
== conformance->getProtocol()->getParentModule())
958+
auto *conformanceModule = conformance->getDeclContext()->getParentModule();
959+
960+
// If the protocol and the conformance are both in the current module,
961+
// they're not resilient.
962+
if (conformanceModule == getSwiftModule() &&
963+
conformanceModule == conformance->getProtocol()->getParentModule())
964+
return false;
965+
966+
// If the protocol and the conformance are in the same module and the
967+
// conforming type is not generic, they're not resilient.
968+
//
969+
// This is an optimization -- a conformance of a non-generic type cannot
970+
// resiliently become dependent.
971+
if (!conformance->getDeclContext()->isGenericContext() &&
972+
conformanceModule == conformance->getProtocol()->getParentModule())
961973
return false;
962974

963975
// We have a resilient conformance.
964976
return true;
965977
}
966978

967-
static bool isResilientConformance(const RootProtocolConformance *root) {
979+
bool IRGenModule::isResilientConformance(const RootProtocolConformance *root) {
968980
if (auto normal = dyn_cast<NormalProtocolConformance>(root))
969981
return isResilientConformance(normal);
970982
// Self-conformances never require this.
@@ -997,6 +1009,7 @@ static bool hasDependentTypeWitness(
9971009
}
9981010

9991011
static bool isDependentConformance(
1012+
IRGenModule &IGM,
10001013
const RootProtocolConformance *rootConformance,
10011014
bool considerResilience,
10021015
llvm::SmallPtrSet<const NormalProtocolConformance *, 4> &visited){
@@ -1011,7 +1024,7 @@ static bool isDependentConformance(
10111024
return false;
10121025

10131026
// If the conformance is resilient, this is always true.
1014-
if (considerResilience && isResilientConformance(conformance))
1027+
if (considerResilience && IGM.isResilientConformance(conformance))
10151028
return true;
10161029

10171030
// Check whether any of the conformances are dependent.
@@ -1027,7 +1040,8 @@ static bool isDependentConformance(
10271040
auto assocConformance =
10281041
conformance->getAssociatedConformance(req.getFirstType(), assocProtocol);
10291042
if (assocConformance.isAbstract() ||
1030-
isDependentConformance(assocConformance.getConcrete()
1043+
isDependentConformance(IGM,
1044+
assocConformance.getConcrete()
10311045
->getRootConformance(),
10321046
considerResilience,
10331047
visited))
@@ -1045,10 +1059,12 @@ static bool isDependentConformance(
10451059

10461060
/// Is there anything about the given conformance that requires witness
10471061
/// tables to be dependently-generated?
1048-
static bool isDependentConformance(const RootProtocolConformance *conformance,
1049-
bool considerResilience) {
1062+
bool IRGenModule::isDependentConformance(
1063+
const RootProtocolConformance *conformance,
1064+
bool considerResilience) {
10501065
llvm::SmallPtrSet<const NormalProtocolConformance *, 4> visited;
1051-
return ::isDependentConformance(conformance, considerResilience, visited);
1066+
return ::isDependentConformance(*this, conformance, considerResilience,
1067+
visited);
10521068
}
10531069

10541070
static bool isSynthesizedNonUnique(const RootProtocolConformance *conformance) {
@@ -1286,7 +1302,7 @@ class AccessorConformanceInfo : public ConformanceInfo {
12861302
Conformance.getDeclContext())),
12871303
SILEntries(SILWT->getEntries()),
12881304
SILConditionalConformances(SILWT->getConditionalConformances()),
1289-
ResilientConformance(isResilientConformance(&Conformance)),
1305+
ResilientConformance(IGM.isResilientConformance(&Conformance)),
12901306
PI(IGM.getProtocolInfo(SILWT->getConformance()->getProtocol(),
12911307
(ResilientConformance
12921308
? ProtocolInfoKind::RequirementSignature
@@ -2086,7 +2102,7 @@ void IRGenerator::ensureRelativeSymbolCollocation(SILWitnessTable &wt) {
20862102

20872103
// Only resilient conformances use relative pointers for witness methods.
20882104
if (wt.isDeclaration() || isAvailableExternally(wt.getLinkage()) ||
2089-
!isResilientConformance(wt.getConformance()))
2105+
!CurrentIGM->isResilientConformance(wt.getConformance()))
20902106
return;
20912107

20922108
for (auto &entry : wt.getEntries()) {

lib/IRGen/IRGenModule.h

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -792,6 +792,11 @@ class IRGenModule {
792792
ResilienceExpansion getResilienceExpansionForLayout(NominalTypeDecl *decl);
793793
ResilienceExpansion getResilienceExpansionForLayout(SILGlobalVariable *var);
794794

795+
bool isResilientConformance(const NormalProtocolConformance *conformance);
796+
bool isResilientConformance(const RootProtocolConformance *root);
797+
bool isDependentConformance(const RootProtocolConformance *conformance,
798+
bool considerResilience);
799+
795800
Alignment getCappedAlignment(Alignment alignment);
796801

797802
SpareBitVector getSpareBitsForType(llvm::Type *scalarTy, Size size);
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
// RUN: %empty-directory(%t)
2+
// RUN: %target-swift-frontend -emit-module -enable-resilience -emit-module-path=%t/resilient_protocol.swiftmodule -module-name=resilient_protocol %S/../Inputs/resilient_protocol.swift
3+
// RUN: %target-swift-frontend -I %t -emit-ir -enable-resilience %s | %FileCheck %s -DINT=i%target-ptrsize
4+
// RUN: %target-swift-frontend -I %t -emit-ir -enable-resilience -O %s
5+
6+
import resilient_protocol
7+
8+
// CHECK-LABEL: define{{( protected)?}} swiftcc void @"$s22conformance_resilience14useConformanceyyx18resilient_protocol22OtherResilientProtocolRzlF"(%swift.opaque* noalias nocapture, %swift.type* %T, i8** %T.OtherResilientProtocol)
9+
public func useConformance<T : OtherResilientProtocol>(_: T) {}
10+
11+
// CHECK-LABEL: define{{( protected)?}} swiftcc void @"$s22conformance_resilience14getConformanceyy18resilient_protocol7WrapperVyxGlF"(%swift.opaque* noalias nocapture, %swift.type* %T)
12+
public func getConformance<T>(_ w: Wrapper<T>) {
13+
// CHECK: [[RESPONSE:%.*]] = call swiftcc %swift.metadata_response @"$s18resilient_protocol7WrapperVMa"([[INT]] 0, %swift.type* %T)
14+
// CHECK: [[META:%.*]] = extractvalue %swift.metadata_response [[RESPONSE]], 0
15+
// CHECK: [[WTABLE:%.*]] = call i8** @swift_getWitnessTable(%swift.protocol_conformance_descriptor* @"$s18resilient_protocol7WrapperVyxGAA22OtherResilientProtocolAAMc", %swift.type* [[META]], i8*** undef)
16+
// CHECK: call swiftcc void @"$s22conformance_resilience14useConformanceyyx18resilient_protocol22OtherResilientProtocolRzlF"(%swift.opaque* noalias nocapture %0, %swift.type* [[META]], i8** [[WTABLE]])
17+
// CHECK: ret void
18+
useConformance(w)
19+
}
20+
21+
// CHECK-LABEL: define{{( protected)?}} swiftcc void @"$s22conformance_resilience14getConformanceyy18resilient_protocol15ConcreteWrapperVF"(%swift.opaque* noalias nocapture)
22+
public func getConformance(_ w: ConcreteWrapper) {
23+
// CHECK: [[RESPONSE:%.*]] = call swiftcc %swift.metadata_response @"$s18resilient_protocol15ConcreteWrapperVMa"([[INT]] 0)
24+
// CHECK: [[META:%.*]] = extractvalue %swift.metadata_response [[RESPONSE]], 0
25+
// CHECK: call swiftcc void @"$s22conformance_resilience14useConformanceyyx18resilient_protocol22OtherResilientProtocolRzlF"(%swift.opaque* noalias nocapture %0, %swift.type* [[META]], i8** @"$s18resilient_protocol15ConcreteWrapperVAA22OtherResilientProtocolAAWP")
26+
// CHECK: ret void
27+
useConformance(w)
28+
}

test/Inputs/resilient_protocol.swift

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,8 @@ public protocol ProtocolWithRequirements {
3030

3131
public struct Wrapper<T>: OtherResilientProtocol { }
3232

33+
public struct ConcreteWrapper: OtherResilientProtocol { }
34+
3335
public protocol ProtocolWithAssocTypeDefaults {
3436
associatedtype T1 = Self
3537
associatedtype T2: OtherResilientProtocol = Wrapper<T1>
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
2+
public func getVersion() -> Int {
3+
#if BEFORE
4+
return 0
5+
#else
6+
return 1
7+
#endif
8+
}
9+
10+
11+
public protocol BaseProtocol {
12+
#if AFTER
13+
associatedtype Assoc = Self
14+
#endif
15+
}
16+
17+
public protocol DerivedProtocol : BaseProtocol {}
18+
19+
20+
public struct FirstGeneric<T> : BaseProtocol {
21+
public init() {}
22+
}
23+
24+
public struct SecondGeneric<T> : DerivedProtocol {
25+
public init() {}
26+
}
27+
28+
extension BaseProtocol {
29+
public func getMeAType() -> Any.Type {
30+
#if BEFORE
31+
return Self.self
32+
#else
33+
return Assoc.self
34+
#endif
35+
}
36+
}
Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
// RUN: %target-resilience-test --no-symbol-diff
2+
// REQUIRES: executable_test
3+
4+
import StdlibUnittest
5+
import conformance_reference
6+
7+
8+
var ConformanceReferenceTest = TestSuite("ConformanceReference")
9+
10+
11+
func useBase<T : BaseProtocol>(_: T) {}
12+
13+
ConformanceReferenceTest.test("BaseConformance") {
14+
useBase(FirstGeneric<Int>())
15+
useBase(SecondGeneric<Int>())
16+
}
17+
18+
19+
func useDerived<T : DerivedProtocol>(_: T) {}
20+
21+
ConformanceReferenceTest.test("DerivedConformance") {
22+
useDerived(SecondGeneric<Int>())
23+
}
24+
25+
26+
protocol EvenMoreDerivedProtocol : DerivedProtocol {}
27+
28+
extension FirstGeneric : EvenMoreDerivedProtocol {}
29+
30+
func useEvenMoreDerived<T : EvenMoreDerivedProtocol>(_ t: T) -> Any.Type {
31+
return t.getMeAType()
32+
}
33+
34+
ConformanceReferenceTest.test("EvenMoreDerivedConformance") {
35+
expectTrue(FirstGeneric<Int>.self == useEvenMoreDerived(FirstGeneric<Int>()))
36+
expectTrue(FirstGeneric<String>.self == useEvenMoreDerived(FirstGeneric<String>()))
37+
}
38+
39+
runAllTests()

0 commit comments

Comments
 (0)