Skip to content

Align unavailable Sendable diagnostics with the proposal for staging Sendable #40360

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
merged 1 commit into from
Dec 2, 2021
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
52 changes: 23 additions & 29 deletions lib/Sema/TypeCheckAvailability.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1779,6 +1779,22 @@ void TypeChecker::diagnosePotentialAccessorUnavailability(
fixAvailability(ReferenceRange, ReferenceDC, RequiredRange, Context);
}

static DiagnosticBehavior
behaviorLimitForExplicitUnavailability(
const RootProtocolConformance *rootConf,
const DeclContext *fromDC) {
auto protoDecl = rootConf->getProtocol();

// Soften errors about unavailable `Sendable` conformances depending on the
// concurrency checking mode
if (protoDecl->isSpecificProtocol(KnownProtocolKind::Sendable) ||
protoDecl->isSpecificProtocol(KnownProtocolKind::UnsafeSendable)) {
return SendableCheckContext(fromDC).defaultDiagnosticBehavior();
}

return DiagnosticBehavior::Unspecified;
}

void TypeChecker::diagnosePotentialUnavailability(
const RootProtocolConformance *rootConf,
const ExtensionDecl *ext,
Expand All @@ -1795,11 +1811,13 @@ void TypeChecker::diagnosePotentialUnavailability(
auto diagID = (ctx.LangOpts.EnableConformanceAvailabilityErrors
? diag::conformance_availability_only_version_newer
: diag::conformance_availability_only_version_newer_warn);
auto behavior = behaviorLimitForExplicitUnavailability(rootConf, dc);
auto err =
ctx.Diags.diagnose(
loc, diagID,
type, proto, prettyPlatformString(targetPlatform(ctx.LangOpts)),
reason.getRequiredOSVersionRange().getLowerEndpoint());
err.limitBehavior(behavior);

// Direct a fixit to the error if an existing guard is nearly-correct
if (fixAvailabilityByNarrowingNearbyVersionCheck(loc, dc,
Expand Down Expand Up @@ -2422,31 +2440,6 @@ bool swift::diagnoseExplicitUnavailability(const ValueDecl *D, SourceRange R,
});
}

static DiagnosticBehavior
behaviorLimitForExplicitUnavailability(const RootProtocolConformance *rootConf) {
auto protoDecl = rootConf->getProtocol();

// Soften errors about unavailable `Sendable` conformances depending on the
// concurrency checking mode
if (protoDecl->isSpecificProtocol(KnownProtocolKind::Sendable) ||
protoDecl->isSpecificProtocol(KnownProtocolKind::UnsafeSendable)) {
// TODO: Base this on concurrency checking mode from ExportContext so it
// detects when you're in concurrency code without -warn-concurrency.
auto &langOpts = protoDecl->getASTContext().LangOpts;
if (langOpts.isSwiftVersionAtLeast(6))
/* fall through */;
else if (!langOpts.WarnConcurrency)
// TODO: Needs more conditions--should only do this if we aren't in a
// concurrent context, and either the import or the declaration is
// @predatesConcurrency.
return DiagnosticBehavior::Ignore;
else
return DiagnosticBehavior::Warning;
}

return DiagnosticBehavior::Unspecified;
}

/// Emit a diagnostic for references to declarations that have been
/// marked as unavailable, either through "unavailable" or "obsoleted:".
bool swift::diagnoseExplicitUnavailability(SourceLoc loc,
Expand Down Expand Up @@ -2482,11 +2475,12 @@ bool swift::diagnoseExplicitUnavailability(SourceLoc loc,
// This was platform-specific; indicate the platform.
platform = attr->prettyPlatformString();
break;
} else {
// Downgrade unavailable Sendable conformances to warnings prior to
// Swift 6.
behavior = behaviorLimitForExplicitUnavailability(rootConf);
}

// Downgrade unavailable Sendable conformance diagnostics where
// appropriate.
behavior = behaviorLimitForExplicitUnavailability(
rootConf, where.getDeclContext());
LLVM_FALLTHROUGH;

case PlatformAgnosticAvailabilityKind::SwiftVersionSpecific:
Expand Down
42 changes: 42 additions & 0 deletions test/Concurrency/sendable_checking.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
// RUN: %target-typecheck-verify-swift
// REQUIRES: concurrency
// REQUIRES: OS=macosx

@available(SwiftStdlib 5.1, *)
struct NS1 { }

@available(SwiftStdlib 5.1, *)
@available(*, unavailable)
extension NS1: Sendable { }
// expected-note@-1 2{{conformance of 'NS1' to 'Sendable' has been explicitly marked unavailable here}}

@available(SwiftStdlib 5.1, *)
struct NS2 { // expected-note{{consider making struct 'NS2' conform to the 'Sendable' protocol}}
var ns1: NS1
}

@available(SwiftStdlib 5.1, *)
struct NS3 { }

@available(SwiftStdlib 5.3, *)
extension NS3: Sendable { }

@available(SwiftStdlib 5.1, *)
func acceptCV<T: Sendable>(_: T) { }

@available(SwiftStdlib 5.1, *)
func testCV(ns1: NS1, ns1array: [NS1], ns2: NS2, ns3: NS3) {
acceptCV(ns1)
acceptCV(ns1array)
acceptCV(ns2)
acceptCV(ns3)
}

@available(SwiftStdlib 5.1, *)
func testCV(ns1: NS1, ns1array: [NS1], ns2: NS2, ns3: NS3) async {
acceptCV(ns1) // expected-warning{{conformance of 'NS1' to 'Sendable' is unavailable}}
acceptCV(ns1array) // expected-warning{{conformance of 'NS1' to 'Sendable' is unavailable}}
acceptCV(ns2) // expected-warning{{type 'NS2' does not conform to the 'Sendable' protocol}}
acceptCV(ns3) // expected-warning{{conformance of 'NS3' to 'Sendable' is only available in macOS 11.0 or newer}}
// expected-note@-1{{add 'if #available' version check}}
}