Skip to content

Commit

Permalink
[Concurrency] Introduce @actorIndependent attribute.
Browse files Browse the repository at this point in the history
Introduce a new attribute `@actorIndependent` that specifies that a
given declaration is considered to be independent of any actor.
Actor-independent declarations do not have access to actor-isolated
state, even when they are declared as instance members of the actor.

On the other hand, actor-independent declarations can be used to
conform to (synchronous) requirements in protocols.
  • Loading branch information
DougGregor committed Sep 19, 2020
1 parent ab4c584 commit 2762405
Show file tree
Hide file tree
Showing 16 changed files with 434 additions and 28 deletions.
1 change: 1 addition & 0 deletions include/swift/AST/ASTTypeIDZone.def
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
//
//===----------------------------------------------------------------------===//

SWIFT_TYPEID(ActorIsolation)
SWIFT_TYPEID(AncestryFlags)
SWIFT_TYPEID(CtorInitializerKind)
SWIFT_TYPEID(FunctionBuilderBodyPreCheck)
Expand Down
1 change: 1 addition & 0 deletions include/swift/AST/ASTTypeIDs.h
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
namespace swift {

class AbstractFunctionDecl;
class ActorIsolation;
class BraceStmt;
class ClosureExpr;
class CodeCompletionCallbacksFactory;
Expand Down
111 changes: 111 additions & 0 deletions include/swift/AST/ActorIsolation.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,111 @@
//===--- ActorIsolation.h - Actor isolation ---------------------*- C++ -*-===//
//
// This source file is part of the Swift.org open source project
//
// Copyright (c) 2014 - 2020 Apple Inc. and the Swift project authors
// Licensed under Apache License v2.0 with Runtime Library Exception
//
// See https://swift.org/LICENSE.txt for license information
// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
//
//===----------------------------------------------------------------------===//
//
// This file provides a description of actor isolation state.
//
//===----------------------------------------------------------------------===//
#ifndef SWIFT_AST_ACTORISOLATIONSTATE_H
#define SWIFT_AST_ACTORISOLATIONSTATE_H

#include "llvm/ADT/Hashing.h"

namespace llvm {
class raw_ostream;
}

namespace swift {
class ClassDecl;

/// Describes the actor isolation of a given declaration, which determines
/// the actors with which it can interact.
class ActorIsolation {
public:
enum Kind {
/// The actor isolation has not been specified. It is assumed to be
/// unsafe to interact with this declaration from any actor.
Unspecified = 0,
/// The declaration is isolated to the instance of an actor class.
/// For example, a mutable stored property or synchronous function within
/// the actor is isolated to the instance of that actor.
ActorInstance,
/// The declaration can refer to actor-isolated state, but can also be
//// referenced from outside the actor.
ActorPrivileged,
/// The declaration is explicitly specified to be independent of any actor,
/// meaning that it can be used from any actor but is also unable to
/// refer to the isolated state of any given actor.
Independent,
};

private:
Kind kind;
ClassDecl *actor;

ActorIsolation(Kind kind, ClassDecl *actor) : kind(kind), actor(actor) { }

public:
static ActorIsolation forUnspecified() {
return ActorIsolation(Unspecified, nullptr);
}

static ActorIsolation forIndependent() {
return ActorIsolation(Independent, nullptr);
}

static ActorIsolation forActorPrivileged(ClassDecl *actor) {
return ActorIsolation(ActorPrivileged, actor);
}

static ActorIsolation forActorInstance(ClassDecl *actor) {
return ActorIsolation(ActorInstance, actor);
}

Kind getKind() const { return kind; }

operator Kind() const { return getKind(); }

ClassDecl *getActor() const {
assert(getKind() == ActorInstance || getKind() == ActorPrivileged);
return actor;
}

friend bool operator==(const ActorIsolation &lhs,
const ActorIsolation &rhs) {
if (lhs.kind != rhs.kind)
return false;

switch (lhs.kind) {
case Independent:
case Unspecified:
return true;

case ActorInstance:
case ActorPrivileged:
return lhs.actor == rhs.actor;
}
}

friend bool operator!=(const ActorIsolation &lhs,
const ActorIsolation &rhs) {
return !(lhs == rhs);
}

friend llvm::hash_code hash_value(const ActorIsolation &state) {
return llvm::hash_combine(state.kind, state.actor);
}
};

void simple_display(llvm::raw_ostream &out, const ActorIsolation &state);

} // end namespace swift

#endif /* SWIFT_AST_ACTORISOLATIONSTATE_H */
6 changes: 6 additions & 0 deletions include/swift/AST/Attr.def
Original file line number Diff line number Diff line change
Expand Up @@ -572,6 +572,12 @@ CONTEXTUAL_SIMPLE_DECL_ATTR(actor, Actor,
APIBreakingToAdd | APIBreakingToRemove,
102)

SIMPLE_DECL_ATTR(actorIndependent, ActorIndependent,
OnFunc | OnVar | OnSubscript | ConcurrencyOnly |
ABIStableToAdd | ABIStableToRemove |
APIStableToAdd | APIBreakingToRemove,
103)

#undef TYPE_ATTR
#undef DECL_ATTR_ALIAS
#undef CONTEXTUAL_DECL_ATTR_ALIAS
Expand Down
22 changes: 22 additions & 0 deletions include/swift/AST/DiagnosticsSema.def
Original file line number Diff line number Diff line change
Expand Up @@ -4146,6 +4146,10 @@ ERROR(actor_isolated_non_self_reference,none,
"actor-isolated %0 %1 can only be referenced "
"%select{inside the actor|on 'self'}2",
(DescriptiveDeclKind, DeclName, bool))
ERROR(actor_isolated_self_independent_context,none,
"actor-isolated %0 %1 can not be referenced from an "
"'@actorIndependent' context",
(DescriptiveDeclKind, DeclName))
WARNING(concurrent_access_local,none,
"local %0 %1 is unsafe to reference in code that may execute "
"concurrently",
Expand All @@ -4170,6 +4174,24 @@ NOTE(actor_isolated_witness_could_be_async_handler,none,
"did you mean to make it an asychronous handler?",
(DescriptiveDeclKind, DeclName))

ERROR(actorisolated_let,none,
"'@actorIsolated' is meaningless on 'let' declarations because "
"they are immutable",
())
ERROR(actorisolated_mutable_storage,none,
"'@actorIsolated' can not be applied to stored properties",
())
ERROR(actorisolated_local_var,none,
"'@actorIsolated' can not be applied to local variables",
())
ERROR(actorisolated_not_actor_member,none,
"'@actorIsolated' can only be applied to actor members and "
"global/static variables",
())
ERROR(actorisolated_not_actor_instance_member,none,
"'@actorIsolated' can only be applied to instance members of actors",
())

//------------------------------------------------------------------------------
// MARK: Type Check Types
//------------------------------------------------------------------------------
Expand Down
19 changes: 19 additions & 0 deletions include/swift/AST/TypeCheckRequests.h
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
#ifndef SWIFT_TYPE_CHECK_REQUESTS_H
#define SWIFT_TYPE_CHECK_REQUESTS_H

#include "swift/AST/ActorIsolation.h"
#include "swift/AST/AnyFunctionRef.h"
#include "swift/AST/ASTTypeIDs.h"
#include "swift/AST/GenericSignature.h"
Expand Down Expand Up @@ -817,6 +818,24 @@ class IsActorRequest :
bool isCached() const { return true; }
};

/// Determine the actor isolation for the given declaration.
class ActorIsolationRequest :
public SimpleRequest<ActorIsolationRequest,
ActorIsolation(ValueDecl *),
RequestFlags::Cached> {
public:
using SimpleRequest::SimpleRequest;

private:
friend SimpleRequest;

ActorIsolation evaluate(Evaluator &evaluator, ValueDecl *value) const;

public:
// Caching
bool isCached() const { return true; }
};

/// Request whether the storage has a mutating getter.
class IsGetterMutatingRequest :
public SimpleRequest<IsGetterMutatingRequest,
Expand Down
3 changes: 3 additions & 0 deletions include/swift/AST/TypeCheckerTypeIDZone.def
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,9 @@ SWIFT_REQUEST(TypeChecker, IsAsyncHandlerRequest, bool(FuncDecl *),
Cached, NoLocationInfo)
SWIFT_REQUEST(TypeChecker, IsActorRequest, bool(ClassDecl *),
Cached, NoLocationInfo)
SWIFT_REQUEST(TypeChecker, ActorIsolationRequest,
ActorIsolationState(ValueDecl *),
Cached, NoLocationInfo)
SWIFT_REQUEST(TypeChecker, FunctionOperatorRequest, OperatorDecl *(FuncDecl *),
Cached, NoLocationInfo)
SWIFT_REQUEST(NameLookup, GenericSignatureRequest,
Expand Down
22 changes: 22 additions & 0 deletions lib/AST/TypeCheckRequests.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1496,3 +1496,25 @@ void CustomAttrTypeRequest::cacheResult(Type value) const {
auto *attr = std::get<0>(getStorage());
attr->setType(value);
}


void swift::simple_display(
llvm::raw_ostream &out, const ActorIsolation &state) {
switch (state) {
case ActorIsolation::ActorInstance:
out << "actor-isolated to instance of " << state.getActor()->getName();
break;

case ActorIsolation::ActorPrivileged:
out << "actor-privileged to instance of " << state.getActor()->getName();
break;

case ActorIsolation::Independent:
out << "actor-independent";
break;

case ActorIsolation::Unspecified:
out << "unspecified actor isolation";
break;
}
}
47 changes: 47 additions & 0 deletions lib/Sema/TypeCheckAttr.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -276,6 +276,53 @@ class AttributeChecker : public AttributeVisitor<AttributeChecker> {

(void)classDecl->isActor();
}

void visitActorIndependentAttr(ActorIndependentAttr *attr) {
// @actorIndependent can be applied to global and static/class variables
// that do not have storage.
auto dc = D->getDeclContext();
if (auto var = dyn_cast<VarDecl>(D)) {
// @actorIndependent is meaningless on a `let`.
if (var->isLet()) {
diagnoseAndRemoveAttr(attr, diag::actorisolated_let);
return;
}

// @actorIndependent can not be applied to stored properties.
if (var->hasStorage()) {
diagnoseAndRemoveAttr(attr, diag::actorisolated_mutable_storage);
return;
}

// @actorIndependent can not be applied to local properties.
if (dc->isLocalContext()) {
diagnoseAndRemoveAttr(attr, diag::actorisolated_local_var);
return;
}

// If this is a static or global variable, we're all set.
if (dc->isModuleScopeContext() ||
(dc->isTypeContext() && var->isStatic())) {
return;
}

// Otherwise, fall through to make sure we're in an appropriate
// context.
}

// @actorIndependent only makes sense on an actor instance member.
if (!dc->getSelfClassDecl() ||
!dc->getSelfClassDecl()->isActor()) {
diagnoseAndRemoveAttr(attr, diag::actorisolated_not_actor_member);
return;
}

if (!cast<ValueDecl>(D)->isInstanceMember()) {
diagnoseAndRemoveAttr(
attr, diag::actorisolated_not_actor_instance_member);
return;
}
}
};
} // end anonymous namespace

Expand Down
Loading

0 comments on commit 2762405

Please sign in to comment.