mirror of
https://github.com/apple/swift.git
synced 2025-12-21 12:14:44 +01:00
It's ok to drop the global-actor qualifier `@G` from a function's type if:
- the cast is happening in a context isolated to global-actor `G`
- the function value will not be `@Sendable`
- the function value is not `async`
It's primarily safe to drop the attribute because we're already in the
same isolation domain. So it's OK to simply drop the global-actor
if we prevent the value from later leaving that isolation domain.
This means we no longer need to warn about code like this:
```
@MainActor func doIt(_ x: [Int], _ f: @MainActor (Int) -> ()) {
x.forEach(f)
// warning: converting function value of type '@MainActor (Int) -> ()' to '(Int) throws -> Void' loses global actor 'MainActor'
}
```
NOTE: this implementation is a bit gross in that the constraint solver
might emit false warnings about casts it introduced that are actually
safe. This is mainly because closure isolation is only fully determined
after constraint solving. See the FIXME's for more details.
resolves rdar://94462333
266 lines
8.9 KiB
C++
266 lines
8.9 KiB
C++
//===--- 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 "swift/AST/Type.h"
|
|
#include "llvm/ADT/Hashing.h"
|
|
|
|
namespace llvm {
|
|
class raw_ostream;
|
|
}
|
|
|
|
namespace swift {
|
|
class DeclContext;
|
|
class ModuleDecl;
|
|
class VarDecl;
|
|
class NominalTypeDecl;
|
|
class SubstitutionMap;
|
|
class AbstractFunctionDecl;
|
|
class AbstractClosureExpr;
|
|
class ClosureActorIsolation;
|
|
|
|
/// Trampoline for AbstractClosureExpr::getActorIsolation.
|
|
ClosureActorIsolation
|
|
__AbstractClosureExpr_getActorIsolation(AbstractClosureExpr *CE);
|
|
|
|
/// Returns a function reference to \c __AbstractClosureExpr_getActorIsolation.
|
|
/// This is needed so we can use it as a default argument for
|
|
/// \c getActorIsolationOfContext without knowing the layout of
|
|
/// \c ClosureActorIsolation.
|
|
llvm::function_ref<ClosureActorIsolation(AbstractClosureExpr *)>
|
|
_getRef__AbstractClosureExpr_getActorIsolation();
|
|
|
|
/// Determine whether the given types are (canonically) equal, declared here
|
|
/// to avoid having to include Types.h.
|
|
bool areTypesEqual(Type type1, Type type2);
|
|
|
|
/// Determine whether the given type is suitable as a concurrent value type.
|
|
bool isSendableType(ModuleDecl *module, Type type);
|
|
|
|
/// Determines if the 'let' can be read from anywhere within the given module,
|
|
/// regardless of the isolation or async-ness of the context in which
|
|
/// the var is read.
|
|
bool isLetAccessibleAnywhere(const ModuleDecl *fromModule, VarDecl *let);
|
|
|
|
/// Describes the actor isolation of a given declaration, which determines
|
|
/// the actors with which it can interact.
|
|
class ActorIsolation {
|
|
public:
|
|
enum Kind : uint8_t {
|
|
/// 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.
|
|
/// For example, a mutable stored property or synchronous function within
|
|
/// the actor is isolated to the instance of that actor.
|
|
ActorInstance,
|
|
/// 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,
|
|
/// The declaration is isolated to a global actor. It can refer to other
|
|
/// entities with the same global actor.
|
|
GlobalActor,
|
|
/// The declaration is isolated to a global actor but with the "unsafe"
|
|
/// annotation, which means that we only enforce the isolation if we're
|
|
/// coming from something with specific isolation.
|
|
GlobalActorUnsafe,
|
|
};
|
|
|
|
private:
|
|
union {
|
|
NominalTypeDecl *actor;
|
|
Type globalActor;
|
|
void *pointer;
|
|
};
|
|
unsigned kind : 3;
|
|
unsigned isolatedByPreconcurrency : 1;
|
|
unsigned parameterIndex : 28;
|
|
|
|
ActorIsolation(Kind kind, NominalTypeDecl *actor, unsigned parameterIndex)
|
|
: actor(actor), kind(kind), isolatedByPreconcurrency(false),
|
|
parameterIndex(parameterIndex) { }
|
|
|
|
ActorIsolation(Kind kind, Type globalActor)
|
|
: globalActor(globalActor), kind(kind), isolatedByPreconcurrency(false),
|
|
parameterIndex(0) { }
|
|
|
|
public:
|
|
static ActorIsolation forUnspecified() {
|
|
return ActorIsolation(Unspecified, nullptr);
|
|
}
|
|
|
|
static ActorIsolation forIndependent() {
|
|
return ActorIsolation(Independent, nullptr);
|
|
}
|
|
|
|
static ActorIsolation forActorInstanceSelf(NominalTypeDecl *actor) {
|
|
return ActorIsolation(ActorInstance, actor, 0);
|
|
}
|
|
|
|
static ActorIsolation forActorInstanceParameter(NominalTypeDecl *actor,
|
|
unsigned parameterIndex) {
|
|
return ActorIsolation(ActorInstance, actor, parameterIndex + 1);
|
|
}
|
|
|
|
static ActorIsolation forGlobalActor(Type globalActor, bool unsafe) {
|
|
return ActorIsolation(
|
|
unsafe ? GlobalActorUnsafe : GlobalActor, globalActor);
|
|
}
|
|
|
|
Kind getKind() const { return (Kind)kind; }
|
|
|
|
operator Kind() const { return getKind(); }
|
|
|
|
bool isUnspecified() const { return kind == Unspecified; }
|
|
|
|
bool isIndependent() const { return kind == Independent; }
|
|
|
|
/// Retrieve the parameter to which actor-instance isolation applies.
|
|
///
|
|
/// Parameter 0 is `self`.
|
|
unsigned getActorInstanceParameter() const {
|
|
assert(getKind() == ActorInstance);
|
|
return parameterIndex;
|
|
}
|
|
|
|
bool isActorIsolated() const {
|
|
switch (getKind()) {
|
|
case ActorInstance:
|
|
case GlobalActor:
|
|
case GlobalActorUnsafe:
|
|
return true;
|
|
|
|
case Unspecified:
|
|
case Independent:
|
|
return false;
|
|
}
|
|
}
|
|
|
|
NominalTypeDecl *getActor() const {
|
|
assert(getKind() == ActorInstance);
|
|
return actor;
|
|
}
|
|
|
|
bool isGlobalActor() const {
|
|
return getKind() == GlobalActor || getKind() == GlobalActorUnsafe;
|
|
}
|
|
|
|
bool isDistributedActor() const;
|
|
|
|
Type getGlobalActor() const {
|
|
assert(isGlobalActor());
|
|
return globalActor;
|
|
}
|
|
|
|
bool preconcurrency() const {
|
|
return isolatedByPreconcurrency;
|
|
}
|
|
|
|
ActorIsolation withPreconcurrency(bool value) const {
|
|
auto copy = *this;
|
|
copy.isolatedByPreconcurrency = value;
|
|
return copy;
|
|
}
|
|
|
|
/// Determine whether this isolation will require substitution to be
|
|
/// evaluated.
|
|
bool requiresSubstitution() const;
|
|
|
|
/// Substitute into types within the actor isolation.
|
|
ActorIsolation subst(SubstitutionMap subs) const;
|
|
|
|
friend bool operator==(const ActorIsolation &lhs,
|
|
const ActorIsolation &rhs) {
|
|
if (lhs.isGlobalActor() && rhs.isGlobalActor())
|
|
return areTypesEqual(lhs.globalActor, rhs.globalActor);
|
|
|
|
if (lhs.getKind() != rhs.getKind())
|
|
return false;
|
|
|
|
switch (lhs.getKind()) {
|
|
case Independent:
|
|
case Unspecified:
|
|
return true;
|
|
|
|
case ActorInstance:
|
|
return lhs.actor == rhs.actor && lhs.parameterIndex == rhs.parameterIndex;
|
|
|
|
case GlobalActor:
|
|
case GlobalActorUnsafe:
|
|
llvm_unreachable("Global actors handled above");
|
|
}
|
|
}
|
|
|
|
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.pointer, state.isolatedByPreconcurrency,
|
|
state.parameterIndex);
|
|
}
|
|
};
|
|
|
|
/// Determine how the given value declaration is isolated.
|
|
ActorIsolation getActorIsolation(ValueDecl *value);
|
|
|
|
/// Determine how the given declaration context is isolated.
|
|
/// \p getClosureActorIsolation allows the specification of actor isolation for
|
|
/// closures that haven't been saved been saved to the AST yet. This is useful
|
|
/// for solver-based code completion which doesn't modify the AST but stores the
|
|
/// actor isolation of closures in the constraint system solution.
|
|
ActorIsolation getActorIsolationOfContext(
|
|
DeclContext *dc,
|
|
llvm::function_ref<ClosureActorIsolation(AbstractClosureExpr *)>
|
|
getClosureActorIsolation =
|
|
_getRef__AbstractClosureExpr_getActorIsolation());
|
|
|
|
/// Check if both the value, and context are isolated to the same actor.
|
|
bool isSameActorIsolated(ValueDecl *value, DeclContext *dc);
|
|
|
|
/// Determines whether this function's body uses flow-sensitive isolation.
|
|
bool usesFlowSensitiveIsolation(AbstractFunctionDecl const *fn);
|
|
|
|
/// Check if it is safe for the \c globalActor qualifier to be removed from
|
|
/// \c ty, when the function value of that type is isolated to that actor.
|
|
///
|
|
/// In general this is safe in a narrow but common case: a global actor
|
|
/// qualifier can be dropped from a function type while in a DeclContext
|
|
/// isolated to that same actor, as long as the value is not Sendable.
|
|
///
|
|
/// \param dc the innermost context in which the cast to remove the global actor
|
|
/// is happening.
|
|
/// \param globalActor global actor that was dropped from \c ty.
|
|
/// \param ty a function type where \c globalActor was removed from it.
|
|
/// \param getClosureActorIsolation function that knows how to produce accurate
|
|
/// information about the isolation of a closure.
|
|
/// \return true if it is safe to drop the global-actor qualifier.
|
|
bool safeToDropGlobalActor(
|
|
DeclContext *dc, Type globalActor, Type ty,
|
|
llvm::function_ref<ClosureActorIsolation(AbstractClosureExpr *)>
|
|
getClosureActorIsolation =
|
|
_getRef__AbstractClosureExpr_getActorIsolation());
|
|
|
|
void simple_display(llvm::raw_ostream &out, const ActorIsolation &state);
|
|
|
|
} // end namespace swift
|
|
|
|
#endif /* SWIFT_AST_ACTORISOLATIONSTATE_H */
|