Skip to content

[6.0.1] Fix ABI mismatch involving Sendable-refining non-resilient protocols and deployment targets #76384

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
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
28 changes: 18 additions & 10 deletions lib/IRGen/GenProto.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1030,7 +1030,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;
Expand All @@ -1040,13 +1043,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);
Expand All @@ -1055,6 +1059,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);
Expand Down Expand Up @@ -1084,7 +1089,8 @@ static bool isDependentConformance(
continue;

auto assocProtocol = req.getProtocolDecl();
if (!protocolCanHaveDependentConformance(assocProtocol))
if (!protocolCanHaveDependentConformance(
assocProtocol, isResilient))
continue;

auto assocConformance =
Expand All @@ -1098,6 +1104,7 @@ static bool isDependentConformance(
isDependentConformance(IGM,
assocConformance.getConcrete()
->getRootConformance(),
isResilient,
visited))
return true;
}
Expand Down Expand Up @@ -1166,7 +1173,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 *
Expand Down
11 changes: 11 additions & 0 deletions test/IRGen/protocol_resilience_sendable.swift
Original file line number Diff line number Diff line change
@@ -1,13 +1,15 @@
// 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

// REQUIRES: OS=macosx

import resilient_protocol
import non_resilient_protocol

func acceptResilientSendableBase<T: ResilientSendableBase>(_: T.Type) { }

Expand All @@ -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)
}
12 changes: 12 additions & 0 deletions test/Inputs/non_resilient_protocol.swift
Original file line number Diff line number Diff line change
@@ -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() { }
}