Skip to content

Fix problem with protocol resilience #22737

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 2 commits into from
Feb 21, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
42 changes: 29 additions & 13 deletions lib/IRGen/GenProto.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -948,23 +948,35 @@ namespace {

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

// If the protocol is in the same module as the conformance, we're
// not resilient.
if (conformance->getDeclContext()->getParentModule()
== conformance->getProtocol()->getParentModule())
auto *conformanceModule = conformance->getDeclContext()->getParentModule();

// If the protocol and the conformance are both in the current module,
// they're not resilient.
if (conformanceModule == getSwiftModule() &&
conformanceModule == conformance->getProtocol()->getParentModule())
return false;

// If the protocol and the conformance are in the same module and the
// conforming type is not generic, they're not resilient.
//
// This is an optimization -- a conformance of a non-generic type cannot
// resiliently become dependent.
if (!conformance->getDeclContext()->isGenericContext() &&
conformanceModule == conformance->getProtocol()->getParentModule())
return false;

// We have a resilient conformance.
return true;
}

static bool isResilientConformance(const RootProtocolConformance *root) {
bool IRGenModule::isResilientConformance(const RootProtocolConformance *root) {
if (auto normal = dyn_cast<NormalProtocolConformance>(root))
return isResilientConformance(normal);
// Self-conformances never require this.
Expand Down Expand Up @@ -997,6 +1009,7 @@ static bool hasDependentTypeWitness(
}

static bool isDependentConformance(
IRGenModule &IGM,
const RootProtocolConformance *rootConformance,
bool considerResilience,
llvm::SmallPtrSet<const NormalProtocolConformance *, 4> &visited){
Expand All @@ -1011,7 +1024,7 @@ static bool isDependentConformance(
return false;

// If the conformance is resilient, this is always true.
if (considerResilience && isResilientConformance(conformance))
if (considerResilience && IGM.isResilientConformance(conformance))
return true;

// Check whether any of the conformances are dependent.
Expand All @@ -1027,7 +1040,8 @@ static bool isDependentConformance(
auto assocConformance =
conformance->getAssociatedConformance(req.getFirstType(), assocProtocol);
if (assocConformance.isAbstract() ||
isDependentConformance(assocConformance.getConcrete()
isDependentConformance(IGM,
assocConformance.getConcrete()
->getRootConformance(),
considerResilience,
visited))
Expand All @@ -1045,10 +1059,12 @@ static bool isDependentConformance(

/// Is there anything about the given conformance that requires witness
/// tables to be dependently-generated?
static bool isDependentConformance(const RootProtocolConformance *conformance,
bool considerResilience) {
bool IRGenModule::isDependentConformance(
const RootProtocolConformance *conformance,
bool considerResilience) {
llvm::SmallPtrSet<const NormalProtocolConformance *, 4> visited;
return ::isDependentConformance(conformance, considerResilience, visited);
return ::isDependentConformance(*this, conformance, considerResilience,
visited);
}

static bool isSynthesizedNonUnique(const RootProtocolConformance *conformance) {
Expand Down Expand Up @@ -1286,7 +1302,7 @@ class AccessorConformanceInfo : public ConformanceInfo {
Conformance.getDeclContext())),
SILEntries(SILWT->getEntries()),
SILConditionalConformances(SILWT->getConditionalConformances()),
ResilientConformance(isResilientConformance(&Conformance)),
ResilientConformance(IGM.isResilientConformance(&Conformance)),
PI(IGM.getProtocolInfo(SILWT->getConformance()->getProtocol(),
(ResilientConformance
? ProtocolInfoKind::RequirementSignature
Expand Down Expand Up @@ -2086,7 +2102,7 @@ void IRGenerator::ensureRelativeSymbolCollocation(SILWitnessTable &wt) {

// Only resilient conformances use relative pointers for witness methods.
if (wt.isDeclaration() || isAvailableExternally(wt.getLinkage()) ||
!isResilientConformance(wt.getConformance()))
!CurrentIGM->isResilientConformance(wt.getConformance()))
return;

for (auto &entry : wt.getEntries()) {
Expand Down
5 changes: 5 additions & 0 deletions lib/IRGen/IRGenModule.h
Original file line number Diff line number Diff line change
Expand Up @@ -792,6 +792,11 @@ class IRGenModule {
ResilienceExpansion getResilienceExpansionForLayout(NominalTypeDecl *decl);
ResilienceExpansion getResilienceExpansionForLayout(SILGlobalVariable *var);

bool isResilientConformance(const NormalProtocolConformance *conformance);
bool isResilientConformance(const RootProtocolConformance *root);
bool isDependentConformance(const RootProtocolConformance *conformance,
bool considerResilience);

Alignment getCappedAlignment(Alignment alignment);

SpareBitVector getSpareBitsForType(llvm::Type *scalarTy, Size size);
Expand Down
28 changes: 28 additions & 0 deletions test/IRGen/conformance_resilience.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
// RUN: %empty-directory(%t)
// RUN: %target-swift-frontend -emit-module -enable-resilience -emit-module-path=%t/resilient_protocol.swiftmodule -module-name=resilient_protocol %S/../Inputs/resilient_protocol.swift
// RUN: %target-swift-frontend -I %t -emit-ir -enable-resilience %s | %FileCheck %s -DINT=i%target-ptrsize
// RUN: %target-swift-frontend -I %t -emit-ir -enable-resilience -O %s

import resilient_protocol

// CHECK-LABEL: define{{( protected)?}} swiftcc void @"$s22conformance_resilience14useConformanceyyx18resilient_protocol22OtherResilientProtocolRzlF"(%swift.opaque* noalias nocapture, %swift.type* %T, i8** %T.OtherResilientProtocol)
public func useConformance<T : OtherResilientProtocol>(_: T) {}

// CHECK-LABEL: define{{( protected)?}} swiftcc void @"$s22conformance_resilience14getConformanceyy18resilient_protocol7WrapperVyxGlF"(%swift.opaque* noalias nocapture, %swift.type* %T)
public func getConformance<T>(_ w: Wrapper<T>) {
// CHECK: [[RESPONSE:%.*]] = call swiftcc %swift.metadata_response @"$s18resilient_protocol7WrapperVMa"([[INT]] 0, %swift.type* %T)
// CHECK: [[META:%.*]] = extractvalue %swift.metadata_response [[RESPONSE]], 0
// CHECK: [[WTABLE:%.*]] = call i8** @swift_getWitnessTable(%swift.protocol_conformance_descriptor* @"$s18resilient_protocol7WrapperVyxGAA22OtherResilientProtocolAAMc", %swift.type* [[META]], i8*** undef)
// CHECK: call swiftcc void @"$s22conformance_resilience14useConformanceyyx18resilient_protocol22OtherResilientProtocolRzlF"(%swift.opaque* noalias nocapture %0, %swift.type* [[META]], i8** [[WTABLE]])
// CHECK: ret void
useConformance(w)
}

// CHECK-LABEL: define{{( protected)?}} swiftcc void @"$s22conformance_resilience14getConformanceyy18resilient_protocol15ConcreteWrapperVF"(%swift.opaque* noalias nocapture)
public func getConformance(_ w: ConcreteWrapper) {
// CHECK: [[RESPONSE:%.*]] = call swiftcc %swift.metadata_response @"$s18resilient_protocol15ConcreteWrapperVMa"([[INT]] 0)
// CHECK: [[META:%.*]] = extractvalue %swift.metadata_response [[RESPONSE]], 0
// CHECK: call swiftcc void @"$s22conformance_resilience14useConformanceyyx18resilient_protocol22OtherResilientProtocolRzlF"(%swift.opaque* noalias nocapture %0, %swift.type* [[META]], i8** @"$s18resilient_protocol15ConcreteWrapperVAA22OtherResilientProtocolAAWP")
// CHECK: ret void
useConformance(w)
}
2 changes: 2 additions & 0 deletions test/Inputs/resilient_protocol.swift
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,8 @@ public protocol ProtocolWithRequirements {

public struct Wrapper<T>: OtherResilientProtocol { }

public struct ConcreteWrapper: OtherResilientProtocol { }

public protocol ProtocolWithAssocTypeDefaults {
associatedtype T1 = Self
associatedtype T2: OtherResilientProtocol = Wrapper<T1>
Expand Down
36 changes: 36 additions & 0 deletions validation-test/Evolution/Inputs/conformance_reference.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@

public func getVersion() -> Int {
#if BEFORE
return 0
#else
return 1
#endif
}


public protocol BaseProtocol {
#if AFTER
associatedtype Assoc = Self
#endif
}

public protocol DerivedProtocol : BaseProtocol {}


public struct FirstGeneric<T> : BaseProtocol {
public init() {}
}

public struct SecondGeneric<T> : DerivedProtocol {
public init() {}
}

extension BaseProtocol {
public func getMeAType() -> Any.Type {
#if BEFORE
return Self.self
#else
return Assoc.self
#endif
}
}
39 changes: 39 additions & 0 deletions validation-test/Evolution/test_conformance_reference.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
// RUN: %target-resilience-test --no-symbol-diff
// REQUIRES: executable_test

import StdlibUnittest
import conformance_reference


var ConformanceReferenceTest = TestSuite("ConformanceReference")


func useBase<T : BaseProtocol>(_: T) {}

ConformanceReferenceTest.test("BaseConformance") {
useBase(FirstGeneric<Int>())
useBase(SecondGeneric<Int>())
}


func useDerived<T : DerivedProtocol>(_: T) {}

ConformanceReferenceTest.test("DerivedConformance") {
useDerived(SecondGeneric<Int>())
}


protocol EvenMoreDerivedProtocol : DerivedProtocol {}

extension FirstGeneric : EvenMoreDerivedProtocol {}

func useEvenMoreDerived<T : EvenMoreDerivedProtocol>(_ t: T) -> Any.Type {
return t.getMeAType()
}

ConformanceReferenceTest.test("EvenMoreDerivedConformance") {
expectTrue(FirstGeneric<Int>.self == useEvenMoreDerived(FirstGeneric<Int>()))
expectTrue(FirstGeneric<String>.self == useEvenMoreDerived(FirstGeneric<String>()))
}

runAllTests()