Fix ABI mismatch involving Sendable-refining protocols and deployment targets

A change to the way we determined whether a protocol conformance is
"dependent" for marker protocols caused an ABI break for
Sendable-refining protocols built with pre-6.0 Swift compilers. The
fix for this issue (https://github.com/swiftlang/swift/pull/75769)
gated the change on deployment target.

The deployment target change fixed the original problem, then caused a
related issue when a project mixes deployment targets (pre-6.0 and
6.0+) with non-resilient protocols. Exempt non-resilient protocols from
this change so we get consistent behavior.

Fixes rdar://134953989.
This commit is contained in:
Doug Gregor
2024-09-10 11:03:48 -07:00
parent 863d2e4656
commit cd52e2d4e5
3 changed files with 41 additions and 10 deletions

View File

@@ -1037,7 +1037,10 @@ static bool isSynthesizedNonUnique(const RootProtocolConformance *conformance) {
}
/// Determine whether a protocol can ever have a dependent conformance.
static bool protocolCanHaveDependentConformance(ProtocolDecl *proto) {
static bool protocolCanHaveDependentConformance(
ProtocolDecl *proto,
bool isResilient
) {
// Objective-C protocols have never been able to have a dependent conformance.
if (proto->isObjC())
return false;
@@ -1047,13 +1050,14 @@ static bool protocolCanHaveDependentConformance(ProtocolDecl *proto) {
// is a marker protocol (since they don't have requirements), but we must
// retain backward compatibility with binaries built for earlier deployment
// targets that concluded that these protocols might involve dependent
// conformances.
ASTContext &ctx = proto->getASTContext();
if (auto runtimeCompatVersion = getSwiftRuntimeCompatibilityVersionForTarget(
ctx.LangOpts.Target)) {
if (runtimeCompatVersion < llvm::VersionTuple(6, 0) &&
proto->isSpecificProtocol(KnownProtocolKind::Sendable))
return true;
// conformances. Only do this for resilient protocols.
if (isResilient && proto->isSpecificProtocol(KnownProtocolKind::Sendable)) {
ASTContext &ctx = proto->getASTContext();
if (auto runtimeCompatVersion = getSwiftRuntimeCompatibilityVersionForTarget(
ctx.LangOpts.Target)) {
if (runtimeCompatVersion < llvm::VersionTuple(6, 0))
return true;
}
}
return Lowering::TypeConverter::protocolRequiresWitnessTable(proto);
@@ -1062,6 +1066,7 @@ static bool protocolCanHaveDependentConformance(ProtocolDecl *proto) {
static bool isDependentConformance(
IRGenModule &IGM,
const RootProtocolConformance *rootConformance,
bool isResilient,
llvm::SmallPtrSet<const NormalProtocolConformance *, 4> &visited){
// Self-conformances are never dependent.
auto conformance = dyn_cast<NormalProtocolConformance>(rootConformance);
@@ -1091,7 +1096,8 @@ static bool isDependentConformance(
continue;
auto assocProtocol = req.getProtocolDecl();
if (!protocolCanHaveDependentConformance(assocProtocol))
if (!protocolCanHaveDependentConformance(
assocProtocol, isResilient))
continue;
auto assocConformance =
@@ -1105,6 +1111,7 @@ static bool isDependentConformance(
isDependentConformance(IGM,
assocConformance.getConcrete()
->getRootConformance(),
isResilient,
visited))
return true;
}
@@ -1173,7 +1180,8 @@ static bool hasConditionalConformances(IRGenModule &IGM,
bool IRGenModule::isDependentConformance(
const RootProtocolConformance *conformance) {
llvm::SmallPtrSet<const NormalProtocolConformance *, 4> visited;
return ::isDependentConformance(*this, conformance, visited);
return ::isDependentConformance(
*this, conformance, conformance->getProtocol()->isResilient(), visited);
}
static llvm::Value *

View File

@@ -1,6 +1,7 @@
// RUN: %empty-directory(%t)
// RUN: %target-swift-frontend -emit-module -enable-library-evolution -emit-module-path=%t/resilient_protocol.swiftmodule -module-name=resilient_protocol %S/../Inputs/resilient_protocol.swift
// RUN: %target-swift-frontend -emit-module -emit-module-path=%t/non_resilient_protocol.swiftmodule -module-name=non_resilient_protocol %S/../Inputs/non_resilient_protocol.swift
// RUN: %target-swift-frontend -I %t -emit-ir %s -target %target-cpu-apple-macos14.0 | %FileCheck %s -DINT=i%target-ptrsize -check-prefix=CHECK-USAGE -check-prefix=CHECK-USAGE-BEFORE
// RUN: %target-swift-frontend -I %t -emit-ir %s -target %target-cpu-apple-macos15.0 | %FileCheck %s -DINT=i%target-ptrsize -check-prefix=CHECK-USAGE -check-prefix=CHECK-USAGE-AFTER
@@ -8,6 +9,7 @@
// REQUIRES: OS=macosx
import resilient_protocol
import non_resilient_protocol
func acceptResilientSendableBase<T: ResilientSendableBase>(_: T.Type) { }
@@ -20,3 +22,12 @@ func passResilientSendableBase() {
// CHECK-USAGE-AFTER-NEXT: call swiftcc void @"$s28protocol_resilience_sendable27acceptResilientSendableBaseyyxm010resilient_A00efG0RzlF"(ptr [[METATYPE]], ptr [[METATYPE]], ptr @"$s18resilient_protocol27ConformsToResilientSendableVAA0eF4BaseAAWP")
acceptResilientSendableBase(ConformsToResilientSendable.self)
}
func acceptNonResilientSendableBase<T: NonResilientSendableBase>(_: T.Type) { }
// CHECK-USAGE: define{{.*}}swiftcc void @"$s28protocol_resilience_sendable28passNonResilientSendableBaseyyF"()
func passNonResilientSendableBase() {
// CHECK-USAGE-NOT: ret
// CHECK-USAGE: call swiftcc void @"$s28protocol_resilience_sendable30acceptNonResilientSendableBaseyyxm014non_resilient_A00efgH0RzlF"(ptr @"$s22non_resilient_protocol30ConformsToNonResilientSendableVN", ptr @"$s22non_resilient_protocol30ConformsToNonResilientSendableVN", ptr @"$s22non_resilient_protocol30ConformsToNonResilientSendableVAA0fgH4BaseAAWP")
acceptNonResilientSendableBase(ConformsToNonResilientSendable.self)
}

View File

@@ -0,0 +1,12 @@
public protocol NonResilientSendableBase: Sendable {
func f()
}
public protocol NonResilientSendable: NonResilientSendableBase {
func g()
}
public struct ConformsToNonResilientSendable: NonResilientSendable {
public func f() { }
public func g() { }
}