Skip to content

Commit

Permalink
Merge pull request #64538 from tshortli/relax-unsafe-main-actor-avail…
Browse files Browse the repository at this point in the history
…ability-checking-5.8

[5.8] Sema: Relax availability checking for `@MainActor`
  • Loading branch information
tkremenek authored Mar 23, 2023
2 parents 46cd560 + 48cb50e commit 7db51a5
Show file tree
Hide file tree
Showing 10 changed files with 78 additions and 36 deletions.
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)
}

0 comments on commit 7db51a5

Please sign in to comment.