From cd52e2d4e5edb2c3ed5e831495fb22279fd77665 Mon Sep 17 00:00:00 2001 From: Doug Gregor Date: Tue, 10 Sep 2024 11:03:48 -0700 Subject: [PATCH] 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. --- lib/IRGen/GenProto.cpp | 28 ++++++++++++------- test/IRGen/protocol_resilience_sendable.swift | 11 ++++++++ test/Inputs/non_resilient_protocol.swift | 12 ++++++++ 3 files changed, 41 insertions(+), 10 deletions(-) create mode 100644 test/Inputs/non_resilient_protocol.swift diff --git a/lib/IRGen/GenProto.cpp b/lib/IRGen/GenProto.cpp index f800509df0668..49fe80a01ad11 100644 --- a/lib/IRGen/GenProto.cpp +++ b/lib/IRGen/GenProto.cpp @@ -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 &visited){ // Self-conformances are never dependent. auto conformance = dyn_cast(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 visited; - return ::isDependentConformance(*this, conformance, visited); + return ::isDependentConformance( + *this, conformance, conformance->getProtocol()->isResilient(), visited); } static llvm::Value * diff --git a/test/IRGen/protocol_resilience_sendable.swift b/test/IRGen/protocol_resilience_sendable.swift index a2d01369d48d5..d4c0fa138b1bd 100644 --- a/test/IRGen/protocol_resilience_sendable.swift +++ b/test/IRGen/protocol_resilience_sendable.swift @@ -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.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.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) +} diff --git a/test/Inputs/non_resilient_protocol.swift b/test/Inputs/non_resilient_protocol.swift new file mode 100644 index 0000000000000..7e2e318eab396 --- /dev/null +++ b/test/Inputs/non_resilient_protocol.swift @@ -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() { } +}