mirror of
https://github.com/apple/swift.git
synced 2025-12-14 20:36:38 +01:00
[SE-0466] Treat explicit "nonisolated" like implicit "nonisolated" on protocols
Make explicit "nonisolated" also not special on protocols, so a nonisolated protocol does not suppress default isolation. SendableMetatype is the proper way to suppress default isolation for a protocol. Unfortunately, these rules made it appear like issue #82168 was fixed, when in fact it was not. Keep the test case, but as a failing test, and we'll investigate separately.
This commit is contained in:
@@ -5282,6 +5282,33 @@ getIsolationFromAttributes(const Decl *decl, bool shouldDiagnose = true,
|
||||
llvm_unreachable("Forgot about an attribute?");
|
||||
}
|
||||
|
||||
/// Determine the default isolation for the given declaration context.
|
||||
static DefaultIsolation getDefaultIsolationForContext(const DeclContext *dc) {
|
||||
// Check whether there is a file-specific setting.
|
||||
if (auto *sourceFile = dc->getParentSourceFile()) {
|
||||
if (auto defaultIsolationInFile = sourceFile->getDefaultIsolation())
|
||||
return defaultIsolationInFile.value();
|
||||
}
|
||||
|
||||
// If we're in the main module, check the language option.
|
||||
ASTContext &ctx = dc->getASTContext();
|
||||
if (dc->getParentModule() == ctx.MainModule)
|
||||
return ctx.LangOpts.DefaultIsolationBehavior;
|
||||
|
||||
// Otherwise, default to nonisolated.
|
||||
return DefaultIsolation::Nonisolated;
|
||||
}
|
||||
|
||||
/// Determines whether explicit 'nonisolated' is different from 'unspecified'
|
||||
/// in the given context.
|
||||
static bool explicitNonisolatedIsSpecial(const DeclContext *dc) {
|
||||
ASTContext &ctx = dc->getASTContext();
|
||||
if (ctx.LangOpts.hasFeature(Feature::NoExplicitNonIsolated))
|
||||
return false;
|
||||
|
||||
return getDefaultIsolationForContext(dc) == DefaultIsolation::Nonisolated;
|
||||
}
|
||||
|
||||
/// Infer isolation from witnessed protocol requirements.
|
||||
static std::optional<InferredActorIsolation>
|
||||
getIsolationFromWitnessedRequirements(ValueDecl *value) {
|
||||
@@ -5298,11 +5325,13 @@ getIsolationFromWitnessedRequirements(ValueDecl *value) {
|
||||
if (dc->getSelfProtocolDecl())
|
||||
return std::nullopt;
|
||||
|
||||
// Prevent isolation inference from requirements if the conforming type
|
||||
// has an explicit `nonisolated` attribute.
|
||||
if (auto *NTD = dc->getSelfNominalTypeDecl()) {
|
||||
if (NTD->getAttrs().hasAttribute<NonisolatedAttr>())
|
||||
return std::nullopt;
|
||||
if (explicitNonisolatedIsSpecial(dc)) {
|
||||
// Prevent isolation inference from requirements if the conforming type
|
||||
// has an explicit `nonisolated` attribute.
|
||||
if (auto *NTD = dc->getSelfNominalTypeDecl()) {
|
||||
if (NTD->getAttrs().hasAttribute<NonisolatedAttr>())
|
||||
return std::nullopt;
|
||||
}
|
||||
}
|
||||
|
||||
// Walk through each of the conformances in this context, collecting any
|
||||
@@ -5360,8 +5389,13 @@ getIsolationFromWitnessedRequirements(ValueDecl *value) {
|
||||
}
|
||||
|
||||
case ActorIsolation::GlobalActor:
|
||||
break;
|
||||
|
||||
case ActorIsolation::Nonisolated:
|
||||
case ActorIsolation::NonisolatedUnsafe:
|
||||
if (!explicitNonisolatedIsSpecial(requirement->getDeclContext()))
|
||||
continue;
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -5468,7 +5502,8 @@ getIsolationFromConformances(NominalTypeDecl *nominal) {
|
||||
case ActorIsolation::NonisolatedUnsafe:
|
||||
break;
|
||||
case ActorIsolation::Nonisolated:
|
||||
if (inferredIsolation.source.kind == IsolationSource::Kind::Explicit) {
|
||||
if (inferredIsolation.source.kind == IsolationSource::Kind::Explicit &&
|
||||
explicitNonisolatedIsSpecial(nominal)) {
|
||||
if (!foundIsolation) {
|
||||
// We found an explicitly 'nonisolated' protocol.
|
||||
foundIsolation = {
|
||||
@@ -6088,23 +6123,6 @@ static bool sendableConformanceRequiresNonisolated(NominalTypeDecl *nominal) {
|
||||
return requiresNonisolated;
|
||||
}
|
||||
|
||||
/// Determine the default isolation for the given declaration context.
|
||||
static DefaultIsolation getDefaultIsolationForContext(const DeclContext *dc) {
|
||||
// Check whether there is a file-specific setting.
|
||||
if (auto *sourceFile = dc->getParentSourceFile()) {
|
||||
if (auto defaultIsolationInFile = sourceFile->getDefaultIsolation())
|
||||
return defaultIsolationInFile.value();
|
||||
}
|
||||
|
||||
// If we're in the main module, check the language option.
|
||||
ASTContext &ctx = dc->getASTContext();
|
||||
if (dc->getParentModule() == ctx.MainModule)
|
||||
return ctx.LangOpts.DefaultIsolationBehavior;
|
||||
|
||||
// Otherwise, default to nonisolated.
|
||||
return DefaultIsolation::Nonisolated;
|
||||
}
|
||||
|
||||
/// Determine the default isolation and isolation source for this declaration,
|
||||
/// which may still be overridden by other inference rules.
|
||||
static std::tuple<InferredActorIsolation, ValueDecl *,
|
||||
@@ -6300,12 +6318,9 @@ static bool shouldSelfIsolationOverrideDefault(
|
||||
if (isa<NominalTypeDecl>(dc))
|
||||
return true;
|
||||
|
||||
// The NoExplicitNonIsolated feature
|
||||
if (ctx.LangOpts.hasFeature(Feature::NoExplicitNonIsolated))
|
||||
return false;
|
||||
|
||||
// Suppress when the default isolation is nonisolated.
|
||||
return getDefaultIsolationForContext(dc) == DefaultIsolation::Nonisolated;
|
||||
/// Only allow explicit nonisolated to override the default when it's
|
||||
/// treated as special.
|
||||
return explicitNonisolatedIsSpecial(dc);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -227,24 +227,6 @@ actor MyActor2 {
|
||||
print("123")
|
||||
}
|
||||
|
||||
// https://github.com/swiftlang/swift/issues/82168 - used to fail
|
||||
nonisolated protocol P {
|
||||
associatedtype AT
|
||||
static var at: AT { get }
|
||||
}
|
||||
|
||||
nonisolated struct KP<R: P, V> {
|
||||
init(keyPath: KeyPath<R, V>) {}
|
||||
}
|
||||
|
||||
struct S: P {
|
||||
let p: Int
|
||||
struct AT {
|
||||
let kp = KP(keyPath: \S.p)
|
||||
}
|
||||
static let at = AT() // used to fail here
|
||||
}
|
||||
|
||||
nonisolated func localDeclIsolation() async {
|
||||
struct Local {
|
||||
static func f() {}
|
||||
|
||||
@@ -116,3 +116,33 @@ extension MyOtherStruct {
|
||||
// expected-swift6-error@-1{{call to main actor-isolated instance method 'memberOfInt()' in a synchronous nonisolated context}}
|
||||
}
|
||||
}
|
||||
|
||||
nonisolated protocol P {
|
||||
func g()
|
||||
}
|
||||
|
||||
struct MyP: P {
|
||||
func g() {
|
||||
17.memberOfInt() // okay, on main actor
|
||||
}
|
||||
}
|
||||
|
||||
// https://github.com/swiftlang/swift/issues/82168 -
|
||||
nonisolated protocol OtherP {
|
||||
associatedtype AT
|
||||
static var at: AT { get }
|
||||
}
|
||||
|
||||
nonisolated struct KP<R: OtherP, V> {
|
||||
init(keyPath: KeyPath<R, V>) {}
|
||||
}
|
||||
|
||||
struct S: OtherP {
|
||||
let p: Int
|
||||
struct AT {
|
||||
let kp = KP(keyPath: \S.p)
|
||||
}
|
||||
|
||||
// FIXME: This should not be an error.
|
||||
static let at = AT() // expected-swift6-error{{'S.AT' cannot be constructed because it has no accessible initializers}}
|
||||
}
|
||||
|
||||
@@ -2,6 +2,8 @@
|
||||
|
||||
// REQUIRES: concurrency
|
||||
|
||||
@MainActor func onMain() { }
|
||||
|
||||
nonisolated
|
||||
protocol P {
|
||||
func f()
|
||||
@@ -12,8 +14,14 @@ protocol Q {
|
||||
func g()
|
||||
}
|
||||
|
||||
// expected-note@+4{{turn data races into runtime errors with '@preconcurrency'}}
|
||||
// expected-note@+3{{isolate this conformance to the main actor with '@MainActor'}}
|
||||
// expected-note@+2{{mark all declarations used in the conformance 'nonisolated'}}
|
||||
// expected-error@+1{{conformance of 'CImplicitMainActorNonisolatedConformance' to protocol 'P' crosses into main actor-isolated code and can cause data races}}
|
||||
class CImplicitMainActorNonisolatedConformance: nonisolated P {
|
||||
func f() { } // error: explicitly nonisolated conformance
|
||||
func f() { // expected-note{{main actor-isolated instance method 'f()' cannot satisfy nonisolated requirement}}
|
||||
onMain() // okay, f is on @MainActor
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -73,9 +81,6 @@ func acceptSendablePMeta<T: Sendable & P>(_: T.Type) { }
|
||||
func acceptSendableQMeta<T: Sendable & Q>(_: T.Type) { }
|
||||
|
||||
nonisolated func testConformancesFromNonisolated() {
|
||||
let _: any P = CExplicitMainActor() // okay
|
||||
let _: any P = CImplicitMainActor() // okay
|
||||
|
||||
let _: any P = CNonIsolated()
|
||||
let _: any P = CImplicitMainActorNonisolatedConformance()
|
||||
|
||||
@@ -84,6 +89,10 @@ nonisolated func testConformancesFromNonisolated() {
|
||||
let _: any Q = CImplicitMainActor()
|
||||
|
||||
// Error, these are main-actor-isolated conformances
|
||||
let _: any P = CExplicitMainActor() // expected-error{{main actor-isolated conformance of 'CExplicitMainActor' to 'P' cannot be used in nonisolated context}}
|
||||
let _: any P = CImplicitMainActor() // expected-error{{main actor-isolated conformance of 'CImplicitMainActor' to 'P' cannot be used in nonisolated context}}
|
||||
|
||||
|
||||
let _: any Equatable.Type = EquatableStruct.self // expected-error{{main actor-isolated conformance of 'EquatableStruct' to 'Equatable' cannot be used in nonisolated context}}
|
||||
let _: any Hashable.Type = HashableStruct.self // expected-error{{main actor-isolated conformance of 'HashableStruct' to 'Hashable' cannot be used in nonisolated context}}
|
||||
let _: any RawRepresentable.Type = RawRepresentableEnum.self
|
||||
|
||||
Reference in New Issue
Block a user