From 21b2f099def0108483b02762b9470a85a4207726 Mon Sep 17 00:00:00 2001 From: Doug Gregor Date: Wed, 25 Sep 2024 11:54:30 -0700 Subject: [PATCH 1/2] 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 9988de7c6bc95..f26306999967a 100644 --- a/lib/IRGen/GenProto.cpp +++ b/lib/IRGen/GenProto.cpp @@ -959,8 +959,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 = @@ -992,16 +997,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; } @@ -1185,7 +1192,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 aa81382c0a48a..42f3c996c7890 100644 --- a/lib/IRGen/IRGenModule.h +++ b/lib/IRGen/IRGenModule.h @@ -1119,8 +1119,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"() From 624570db5760b19c0735314dca00059a5b5b5fdc Mon Sep 17 00:00:00 2001 From: Doug Gregor Date: Wed, 25 Sep 2024 13:05:22 -0700 Subject: [PATCH 2/2] Improve naming of the isResilientConformance flag that disables optimizations Thank you, Arnold, for talking through this with me. --- lib/IRGen/GenProto.cpp | 14 +++++++------- lib/IRGen/IRGenModule.h | 4 ++-- 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/lib/IRGen/GenProto.cpp b/lib/IRGen/GenProto.cpp index f26306999967a..cdbeee80d29b1 100644 --- a/lib/IRGen/GenProto.cpp +++ b/lib/IRGen/GenProto.cpp @@ -960,11 +960,11 @@ 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. +/// If disableOptimizations is true, skip optimizations that treat +/// formally-resilient conformances as non-resilient. bool IRGenModule::isResilientConformance( const NormalProtocolConformance *conformance, - bool ignoreGenericity + bool disableOptimizations ) { // If the protocol is not resilient, the conformance is not resilient // either. @@ -998,7 +998,7 @@ bool IRGenModule::isResilientConformance( // resiliently become dependent. if (!conformance->getDeclContext()->isGenericContext() && conformanceModule == conformance->getProtocol()->getParentModule() && - !ignoreGenericity) + !disableOptimizations) return false; // We have a resilient conformance. @@ -1006,9 +1006,9 @@ bool IRGenModule::isResilientConformance( } bool IRGenModule::isResilientConformance(const RootProtocolConformance *root, - bool ignoreGenericity) { + bool disableOptimizations) { if (auto normal = dyn_cast(root)) - return isResilientConformance(normal, ignoreGenericity); + return isResilientConformance(normal, disableOptimizations); // Self-conformances never require this. return false; } @@ -1193,7 +1193,7 @@ bool IRGenModule::isDependentConformance( llvm::SmallPtrSet visited; return ::isDependentConformance( *this, conformance, - isResilientConformance(conformance, /*ignoreGenericity=*/true), + isResilientConformance(conformance, /*disableOptimizations=*/true), visited); } diff --git a/lib/IRGen/IRGenModule.h b/lib/IRGen/IRGenModule.h index 42f3c996c7890..8ee9edf61fc86 100644 --- a/lib/IRGen/IRGenModule.h +++ b/lib/IRGen/IRGenModule.h @@ -1120,9 +1120,9 @@ class IRGenModule { TypeExpansionContext getMaximalTypeExpansionContext() const; bool isResilientConformance(const NormalProtocolConformance *conformance, - bool ignoreGenericity = false); + bool disableOptimizations = false); bool isResilientConformance(const RootProtocolConformance *root, - bool ignoreGenericity = false); + bool disableOptimizations = false); bool isDependentConformance(const RootProtocolConformance *conformance); Alignment getCappedAlignment(Alignment alignment);