Sema: Don't complain about implied Sendable conformance of imported type being retroactive

- Fixes rdar://145184871.
This commit is contained in:
Slava Pestov
2025-05-16 18:15:54 -04:00
parent a232a2d1e3
commit 8b8aafb798
5 changed files with 46 additions and 11 deletions

View File

@@ -6816,6 +6816,9 @@ static bool checkSendableInstanceStorage(
bool swift::checkSendableConformance(
ProtocolConformance *conformance, SendableCheck check) {
ASSERT(conformance->getProtocol()->isSpecificProtocol(
KnownProtocolKind::Sendable));
auto conformanceDC = conformance->getDeclContext();
auto nominal = conformance->getType()->getAnyNominal();
if (!nominal)
@@ -6905,13 +6908,16 @@ bool swift::checkSendableConformance(
return false;
}
// An implied conformance is generated when you state a conformance to
// a protocol P that inherits from Sendable.
bool wasImplied = (conformance->getSourceKind() ==
ConformanceEntryKind::Implied);
// Sendable can only be used in the same source file.
auto conformanceDecl = conformanceDC->getAsDecl();
SendableCheckContext checkContext(conformanceDC, check);
DiagnosticBehavior behavior = checkContext.defaultDiagnosticBehavior();
if (conformance->getSourceKind() == ConformanceEntryKind::Implied &&
conformance->getProtocol()->isSpecificProtocol(
KnownProtocolKind::Sendable)) {
if (wasImplied) {
if (auto optBehavior = checkContext.preconcurrencyBehavior(
nominal, /*ignoreExplicitConformance=*/true))
behavior = *optBehavior;
@@ -6920,12 +6926,14 @@ bool swift::checkSendableConformance(
if (conformanceDC->getOutermostParentSourceFile() &&
conformanceDC->getOutermostParentSourceFile() !=
nominal->getOutermostParentSourceFile()) {
conformanceDecl->diagnose(diag::concurrent_value_outside_source_file,
nominal)
.limitBehaviorUntilSwiftVersion(behavior, 6);
if (!(nominal->hasClangNode() && wasImplied)) {
conformanceDecl->diagnose(diag::concurrent_value_outside_source_file,
nominal)
.limitBehaviorUntilSwiftVersion(behavior, 6);
if (behavior == DiagnosticBehavior::Unspecified)
return true;
if (behavior == DiagnosticBehavior::Unspecified)
return true;
}
}
if (classDecl && classDecl->getParentSourceFile()) {
@@ -6963,7 +6971,7 @@ bool swift::checkSendableConformance(
// a Sendable conformance. The implied conformance is unconditional, so check
// the storage for sendability as if the conformance was declared on the nominal,
// and not some (possibly constrained) extension.
if (conformance->getSourceKind() == ConformanceEntryKind::Implied)
if (wasImplied)
conformanceDC = nominal;
return checkSendableInstanceStorage(nominal, conformanceDC, check);
}

View File

@@ -1843,6 +1843,18 @@ static void diagnoseRetroactiveConformances(
bool inserted = protocols.insert(std::make_pair(
proto, conformance->isRetroactive())).second;
ASSERT(inserted);
if (proto->isSpecificProtocol(KnownProtocolKind::SendableMetatype)) {
protocolsWithRetroactiveAttr.insert(proto);
}
// Implied conformance to Sendable is a special case that should not be
// diagnosed. Pretend it's always @retroactive.
if (conformance->getSourceKind() == ConformanceEntryKind::Implied &&
proto->isSpecificProtocol(KnownProtocolKind::Sendable) &&
extendedNominalDecl->hasClangNode()) {
protocolsWithRetroactiveAttr.insert(proto);
}
}
for (const InheritedEntry &entry : ext->getInherited().getEntries()) {

View File

@@ -0,0 +1,15 @@
// RUN: %target-swift-frontend(mock-sdk: %clang-importer-sdk) -emit-sil -o /dev/null -I %S/Inputs/custom-modules %s -verify -parse-as-library -swift-version 6
// REQUIRES: objc_interop
// REQUIRES: concurrency
import Foundation
extension CGRect: Sendable {}
// expected-warning@-1 {{extension declares a conformance of imported type 'CGRect' to imported protocol 'Sendable'; this will not behave correctly if the owners of 'CoreGraphics' introduce this conformance in the future}}
// expected-note@-2 {{add '@retroactive' to silence this warning}}
// expected-error@-3 {{conformance to 'Sendable' must occur in the same source file as struct 'CGRect'; use '@unchecked Sendable' for retroactive conformance}}
protocol P: Sendable {}
extension CGPoint: P {}

View File

@@ -3,7 +3,7 @@
protocol _CFObject: Hashable {}
#if CGFLOAT_IN_COREFOUNDATION
public struct CGFloat {
public struct CGFloat: @unchecked Sendable {
#if _pointerBitWidth(_32)
public typealias UnderlyingType = Float
#elseif _pointerBitWidth(_64)

View File

@@ -6,7 +6,7 @@ public func == (lhs: CGPoint, rhs: CGPoint) -> Bool {
}
#if !CGFLOAT_IN_COREFOUNDATION
public struct CGFloat {
public struct CGFloat: Sendable {
#if _pointerBitWidth(_32)
public typealias UnderlyingType = Float
#elseif _pointerBitWidth(_64)