Skip to content

[5.8] Sema: Relax availability checking for @MainActor #64538

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
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
2 changes: 2 additions & 0 deletions include/swift/AST/ActorIsolation.h
Original file line number Diff line number Diff line change
Expand Up @@ -160,6 +160,8 @@ class ActorIsolation {
return getKind() == GlobalActor || getKind() == GlobalActorUnsafe;
}

bool isMainActor() const;

bool isDistributedActor() const;

Type getGlobalActor() const {
Expand Down
3 changes: 3 additions & 0 deletions include/swift/AST/Decl.h
Original file line number Diff line number Diff line change
Expand Up @@ -3834,6 +3834,9 @@ class NominalTypeDecl : public GenericTypeDecl, public IterableDeclContext {
/// Whether this nominal type qualifies as any actor (plain or distributed).
bool isAnyActor() const;

/// Whether this nominal type is the `MainActor` global actor.
bool isMainActor() const;

/// Return the range of semantics attributes attached to this NominalTypeDecl.
auto getSemanticsAttrs() const
-> decltype(getAttrs().getSemanticsAttrs()) {
Expand Down
14 changes: 14 additions & 0 deletions lib/AST/Decl.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -4567,6 +4567,11 @@ bool NominalTypeDecl::isAnyActor() const {
return isActor() || isDistributedActor();
}

bool NominalTypeDecl::isMainActor() const {
return getName().is("MainActor") &&
getParentModule()->getName() == getASTContext().Id_Concurrency;
}

GenericTypeDecl::GenericTypeDecl(DeclKind K, DeclContext *DC,
Identifier name, SourceLoc nameLoc,
ArrayRef<InheritedEntry> inherited,
Expand Down Expand Up @@ -9614,6 +9619,15 @@ void swift::simple_display(llvm::raw_ostream &out, AnyFunctionRef fn) {
out << "closure";
}

bool ActorIsolation::isMainActor() const {
if (isGlobalActor()) {
if (auto *nominal = getGlobalActor()->getAnyNominal())
return nominal->isMainActor();
}

return false;
}

bool ActorIsolation::isDistributedActor() const {
return getKind() == ActorInstance && getActor()->isDistributedActor();
}
Expand Down
16 changes: 2 additions & 14 deletions lib/AST/DiagnosticEngine.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -576,18 +576,6 @@ static bool typeSpellingIsAmbiguous(Type type,
return false;
}

/// Determine whether this is the main actor type.
static bool isMainActor(Type type) {
if (auto nominal = type->getAnyNominal()) {
if (nominal->getName().is("MainActor") &&
nominal->getParentModule()->getName() ==
nominal->getASTContext().Id_Concurrency)
return true;
}

return false;
}

void swift::printClangDeclName(const clang::NamedDecl *ND,
llvm::raw_ostream &os) {
ND->getNameForDiagnostic(os, ND->getASTContext().getPrintingPolicy(), false);
Expand Down Expand Up @@ -841,10 +829,10 @@ static void formatDiagnosticArgument(StringRef Modifier,

case ActorIsolation::GlobalActor:
case ActorIsolation::GlobalActorUnsafe: {
Type globalActor = isolation.getGlobalActor();
if (isMainActor(globalActor)) {
if (isolation.isMainActor()) {
Out << "main actor-isolated";
} else {
Type globalActor = isolation.getGlobalActor();
Out << "global actor " << FormatOpts.OpeningQuotationMark
<< globalActor.getString()
<< FormatOpts.ClosingQuotationMark << "-isolated";
Expand Down
5 changes: 5 additions & 0 deletions lib/SILGen/SILGenProlog.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -712,6 +712,11 @@ void SILGenFunction::emitProlog(CaptureInfo captureInfo,
!isInActorDestructor(FunctionDC) &&
!F.isDefer();

// FIXME: Avoid loading and checking the expected executor if concurrency is
// unavailable. This is specifically relevant for MainActor isolated contexts,
// which are allowed to be available on OSes where concurrency is not
// available. rdar://106827064

// Local function to load the expected executor from a local actor
auto loadExpectedExecutorForLocalVar = [&](VarDecl *var) {
auto loc = RegularLocation::getAutoGeneratedLocation(F.getLocation());
Expand Down
20 changes: 15 additions & 5 deletions lib/Sema/TypeCheckAccess.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1744,13 +1744,23 @@ class DeclAvailabilityChecker : public DeclVisitor<DeclAvailabilityChecker> {
explicit DeclAvailabilityChecker(ExportContext where)
: Where(where) {}

void checkGlobalActor(Decl *D) {
auto globalActor = D->getGlobalActorAttr();
if (!globalActor)
return;

// Avoid checking the availability for a @MainActor constraint since it does
// not carry an inherent ABI impact.
if (globalActor->second->isMainActor())
return;

auto customAttr = globalActor->first;
checkType(customAttr->getType(), customAttr->getTypeRepr(), D);
}

void visit(Decl *D) {
DeclVisitor<DeclAvailabilityChecker>::visit(D);

if (auto globalActor = D->getGlobalActorAttr()) {
auto customAttr = globalActor->first;
checkType(customAttr->getType(), customAttr->getTypeRepr(), D);
}
checkGlobalActor(D);
}

// Force all kinds to be handled at a lower level.
Expand Down
11 changes: 1 addition & 10 deletions lib/Sema/TypeCheckAttr.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3538,15 +3538,6 @@ void AttributeChecker::visitFrozenAttr(FrozenAttr *attr) {
}
}

/// Determine whether this is the main actor type.
/// FIXME: the diagnostics engine and TypeCheckConcurrency both have a copy of
/// this
static bool isMainActor(NominalTypeDecl *nominal) {
return nominal->getName().is("MainActor") &&
nominal->getParentModule()->getName() ==
nominal->getASTContext().Id_Concurrency;
}

void AttributeChecker::visitCustomAttr(CustomAttr *attr) {
auto dc = D->getDeclContext();

Expand Down Expand Up @@ -3582,7 +3573,7 @@ void AttributeChecker::visitCustomAttr(CustomAttr *attr) {
return;
}

if (isMainActor(nominal) && Ctx.LangOpts.isConcurrencyModelTaskToThread() &&
if (nominal->isMainActor() && Ctx.LangOpts.isConcurrencyModelTaskToThread() &&
!AvailableAttr::isUnavailable(D)) {
Ctx.Diags.diagnose(attr->getLocation(),
diag::concurrency_task_to_thread_model_main_actor,
Expand Down
9 changes: 2 additions & 7 deletions lib/Sema/TypeCheckConcurrency.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1193,14 +1193,9 @@ void swift::diagnoseMissingExplicitSendable(NominalTypeDecl *nominal) {
}

/// Determine whether this is the main actor type.
/// FIXME: the diagnostics engine has a copy of this.
static bool isMainActor(Type type) {
if (auto nominal = type->getAnyNominal()) {
if (nominal->getName().is("MainActor") &&
nominal->getParentModule()->getName() ==
nominal->getASTContext().Id_Concurrency)
return true;
}
if (auto nominal = type->getAnyNominal())
return nominal->isMainActor();

return false;
}
Expand Down
20 changes: 20 additions & 0 deletions test/Sema/availability_main_actor.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
// RUN: %target-typecheck-verify-swift

// REQUIRES: concurrency

// This test is meant to verify that a @MainActor constraint is accepted without
// any availability restrictions for all targets.

@MainActor
struct AlwaysAvailable {}

@MainActor(unsafe)
struct AlwaysAvailableUnsafe {}

@available(SwiftStdlib 5.1, *)
@MainActor
struct AvailableSwift5_1 {}

@available(SwiftStdlib 5.1, *)
@MainActor(unsafe)
struct AvailableSwift5_1Unsafe {}
14 changes: 14 additions & 0 deletions validation-test/ParseableInterface/unsafe-mainactor.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
// RUN: %empty-directory(%t)
// RUN: %target-swift-emit-module-interface(%t/Library.swiftinterface) %s -module-name Library
// RUN: %target-swift-typecheck-module-from-interface(%t/Library.swiftinterface) -module-name Library
// RUN: %FileCheck %s < %t/Library.swiftinterface

// REQUIRES: OS=macosx

import AppKit

// CHECK: @objc @_inheritsConvenienceInitializers @_Concurrency.MainActor(unsafe) public class Subclass : AppKit.NSView {
public class Subclass: NSView {
// CHECK: @_Concurrency.MainActor(unsafe) @objc override dynamic public init(frame frameRect: Foundation.NSRect)
// CHECK: @_Concurrency.MainActor(unsafe) @objc required dynamic public init?(coder: Foundation.NSCoder)
}