From 5696c9a15d4a5b5c1e55f11b1c5c301686b26158 Mon Sep 17 00:00:00 2001 From: Doug Gregor Date: Wed, 25 Sep 2024 11:54:30 -0700 Subject: [PATCH] Use "resilient conformance" logic for deciding protocol dependencies involving Sendable When compiling with library evolution and a pre-Swift 6.0 deployment target, a mismatch between the notion of resilience used for determining whether a protocol that inherits Sendable might need to be treated as "dependent" differed from how other parts of IR generation decided whether to conformance should be considered as resilient. The difference came when both the protocol and its conforming type are in the same module as the user. Switch over to the "is this conformance resilient?" query that takes into account such conformances. Fixes rdar://136586922. --- lib/IRGen/GenProto.cpp | 19 ++++++++++---- lib/IRGen/IRGenModule.h | 6 +++-- test/IRGen/protocol_resilience_sendable.swift | 26 +++++++++++++++++++ 3 files changed, 44 insertions(+), 7 deletions(-) diff --git a/lib/IRGen/GenProto.cpp b/lib/IRGen/GenProto.cpp index d20c8ae0ff6f1..3e57408497227 100644 --- a/lib/IRGen/GenProto.cpp +++ b/lib/IRGen/GenProto.cpp @@ -950,8 +950,13 @@ namespace { /// Return true if the witness table requires runtime instantiation to /// handle resiliently-added requirements with default implementations. +/// +/// If ignoreGenericity is true, skip the optimization for non-generic +/// conformances are considered non-resilient. bool IRGenModule::isResilientConformance( - const NormalProtocolConformance *conformance) { + const NormalProtocolConformance *conformance, + bool ignoreGenericity +) { // If the protocol is not resilient, the conformance is not resilient // either. bool shouldTreatProtocolNonResilient = @@ -983,16 +988,18 @@ bool IRGenModule::isResilientConformance( // This is an optimization -- a conformance of a non-generic type cannot // resiliently become dependent. if (!conformance->getDeclContext()->isGenericContext() && - conformanceModule == conformance->getProtocol()->getParentModule()) + conformanceModule == conformance->getProtocol()->getParentModule() && + !ignoreGenericity) return false; // We have a resilient conformance. return true; } -bool IRGenModule::isResilientConformance(const RootProtocolConformance *root) { +bool IRGenModule::isResilientConformance(const RootProtocolConformance *root, + bool ignoreGenericity) { if (auto normal = dyn_cast(root)) - return isResilientConformance(normal); + return isResilientConformance(normal, ignoreGenericity); // Self-conformances never require this. return false; } @@ -1174,7 +1181,9 @@ bool IRGenModule::isDependentConformance( const RootProtocolConformance *conformance) { llvm::SmallPtrSet visited; return ::isDependentConformance( - *this, conformance, conformance->getProtocol()->isResilient(), visited); + *this, conformance, + isResilientConformance(conformance, /*ignoreGenericity=*/true), + visited); } static llvm::Value * diff --git a/lib/IRGen/IRGenModule.h b/lib/IRGen/IRGenModule.h index 1de199a107803..7205d4383c006 100644 --- a/lib/IRGen/IRGenModule.h +++ b/lib/IRGen/IRGenModule.h @@ -1114,8 +1114,10 @@ class IRGenModule { TypeExpansionContext getMaximalTypeExpansionContext() const; - bool isResilientConformance(const NormalProtocolConformance *conformance); - bool isResilientConformance(const RootProtocolConformance *root); + bool isResilientConformance(const NormalProtocolConformance *conformance, + bool ignoreGenericity = false); + bool isResilientConformance(const RootProtocolConformance *root, + bool ignoreGenericity = false); bool isDependentConformance(const RootProtocolConformance *conformance); Alignment getCappedAlignment(Alignment alignment); diff --git a/test/IRGen/protocol_resilience_sendable.swift b/test/IRGen/protocol_resilience_sendable.swift index d4c0fa138b1bd..cdc0c5fb81ac8 100644 --- a/test/IRGen/protocol_resilience_sendable.swift +++ b/test/IRGen/protocol_resilience_sendable.swift @@ -6,11 +6,37 @@ // 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 +// RUN: %target-swift-frontend -I %t -emit-ir %s -target %target-cpu-apple-macos14.0 -enable-library-evolution | %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 -enable-library-evolution | %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 +// CHECK-USAGE: @"$s28protocol_resilience_sendable9LocalTypeVAA0D11SubProtocolAAWP" = hidden constant [3 x ptr] [ +// CHECK-USAGE-SAME: ptr @"$s28protocol_resilience_sendable9LocalTypeVAA0D11SubProtocolAAMc", +// CHECK-USAGE-SAME: ptr @"$s28protocol_resilience_sendable9LocalTypeVAA0D8ProtocolAAWP", +// CHECK-USAGE-SAME: ptr @"$s28protocol_resilience_sendable9LocalTypeVAA0D11SubProtocolA2aDP9subMethodyyFTW" +public protocol LocalProtocol: Sendable { + func method() +} + +protocol LocalSubProtocol: Sendable, LocalProtocol { + func subMethod() +} + +struct LocalType: Sendable, LocalSubProtocol { + func method() { } + func subMethod() { } +} + +func acceptLocalProtocol(_: T.Type) { } +func testLocalType() { + acceptLocalProtocol(LocalType.self) +} + + func acceptResilientSendableBase(_: T.Type) { } // CHECK-USAGE: define{{.*}}swiftcc void @"$s28protocol_resilience_sendable25passResilientSendableBaseyyF"()