Skip to content

[concurrency] Add support for NonIsolatedAsyncInheritsIsolationFromContext behind a flag #78206

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
merged 8 commits into from
Jan 3, 2025
Merged
23 changes: 19 additions & 4 deletions include/swift/AST/ActorIsolation.h
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,11 @@ class ActorIsolation {
/// The actor isolation iss statically erased, as for a call to
/// an isolated(any) function. This is not possible for declarations.
Erased,
/// Inherits isolation from the caller of the given function.
///
/// DISCUSSION: This is used for nonisolated asynchronous functions that we
/// want to inherit from their context the context's actor isolation.
CallerIsolationInheriting,
};

private:
Expand Down Expand Up @@ -107,6 +112,12 @@ class ActorIsolation {
return ActorIsolation(unsafe ? NonisolatedUnsafe : Nonisolated);
}

static ActorIsolation forCallerIsolationInheriting() {
// NOTE: We do not use parameter indices since the parameter is implicit
// from the perspective of the AST.
return ActorIsolation(CallerIsolationInheriting);
}

static ActorIsolation forActorInstanceSelf(ValueDecl *decl);

/// Create an ActorIsolation appropriate for a type that is self.
Expand Down Expand Up @@ -152,8 +163,11 @@ class ActorIsolation {
ActorIsolation::NonisolatedUnsafe))
.Case("global_actor",
std::optional<ActorIsolation>(ActorIsolation::GlobalActor))
.Case("global_actor_unsafe", std::optional<ActorIsolation>(
ActorIsolation::GlobalActor))
.Case("global_actor_unsafe",
std::optional<ActorIsolation>(ActorIsolation::GlobalActor))
.Case("caller_isolation_inheriting",
std::optional<ActorIsolation>(
ActorIsolation::CallerIsolationInheriting))
.Default(std::nullopt);
if (kind == std::nullopt)
return std::nullopt;
Expand All @@ -180,8 +194,8 @@ class ActorIsolation {
return parameterIndex;
}

/// Returns true if this actor-instance isolation applies to the self
/// parameter of a method.
/// Returns true if this is an actor-instance isolation that additionally
/// applies to the self parameter of a method.
bool isActorInstanceForSelfParameter() const {
return getActorInstanceParameter() == 0;
}
Expand All @@ -198,6 +212,7 @@ class ActorIsolation {
case Unspecified:
case Nonisolated:
case NonisolatedUnsafe:
case CallerIsolationInheriting:
return false;
}
}
Expand Down
1 change: 1 addition & 0 deletions include/swift/AST/TypeAttr.def
Original file line number Diff line number Diff line change
Expand Up @@ -111,6 +111,7 @@ SIMPLE_SIL_TYPE_ATTR(captures_generics, CapturesGenerics)
SIMPLE_SIL_TYPE_ATTR(moveOnly, MoveOnly)
SIMPLE_SIL_TYPE_ATTR(sil_isolated, SILIsolated)
SIMPLE_SIL_TYPE_ATTR(sil_sending, SILSending)
SIMPLE_SIL_TYPE_ATTR(sil_implicit_leading_param, SILImplicitLeadingParam)

// SIL metatype attributes.
SIMPLE_SIL_TYPE_ATTR(thin, Thin)
Expand Down
16 changes: 16 additions & 0 deletions include/swift/AST/Types.h
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,7 @@ class PlaceholderTypeRepr;
enum class ReferenceCounting : uint8_t;
enum class ResilienceExpansion : unsigned;
class SILModule;
class SILFunction;
class SILType;
class SourceLoc;
class TypeAliasDecl;
Expand Down Expand Up @@ -4438,6 +4439,16 @@ class SILParameterInfo {
/// that the parameter must be an Optional actor or something that conforms
/// to AnyActor.
Isolated = 0x4,

/// Set if the given parameter is an implicit parameter that is not part of
/// the formal type. These are always located after the return values and
/// before the main parameters. This is because we want to at the SIL level
/// generally treat them as normal parameters... but when working with the
/// AST during lowering, we need to handle ignoring them appropriately.
///
/// DISCUSSION: These are enforced by the SIL verifier to always be in
/// between indirect results and the explicit parameters.
ImplicitLeading = 0x8,
};

using Options = OptionSet<Flag>;
Expand All @@ -4464,6 +4475,11 @@ class SILParameterInfo {
///
/// \c t must refer back to the function type this is a parameter for.
CanType getArgumentType(SILModule &M, const SILFunctionType *t, TypeExpansionContext context) const;

/// Helper function that just grabs the module, type, and context out of \arg
/// fn and then calls getArgumentType.
CanType getArgumentType(SILFunction *fn) const;

ParameterConvention getConvention() const { return convention; }
// Does this parameter convention require indirect storage? This reflects a
// SILFunctionType's formal (immutable) conventions, as opposed to the
Expand Down
6 changes: 5 additions & 1 deletion include/swift/Basic/Features.def
Original file line number Diff line number Diff line change
Expand Up @@ -430,14 +430,18 @@ EXPERIMENTAL_FEATURE(CoroutineAccessorsUnwindOnCallerError, false)
/// modify/read coroutines use the callee-allocated ABI
EXPERIMENTAL_FEATURE(CoroutineAccessorsAllocateInCallee, false)

// When a parameter has unspecified isolation, infer it as main actor isolated.
/// When a parameter has unspecified isolation, infer it as main actor isolated.
EXPERIMENTAL_FEATURE(GenerateForceToMainActorThunks, false)

EXPERIMENTAL_FEATURE(AddressableParameters, true)

/// Allow the @abi attribute.
SUPPRESSIBLE_EXPERIMENTAL_FEATURE(ABIAttribute, true)

/// Functions with nonisolated isolation inherit their isolation from the
/// calling context.
EXPERIMENTAL_FEATURE(NonIsolatedAsyncInheritsIsolationFromContext, false)

#undef EXPERIMENTAL_FEATURE_EXCLUDED_FROM_MODULE_INTERFACE
#undef EXPERIMENTAL_FEATURE
#undef UPCOMING_FEATURE
Expand Down
4 changes: 4 additions & 0 deletions lib/AST/ASTDumper.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2959,6 +2959,10 @@ class PrintExpr : public ExprVisitor<PrintExpr, void, StringRef>,
printFlag(true, "dynamically_isolated", CapturesColor);
break;

case ActorIsolation::CallerIsolationInheriting:
printFlag(true, "isolated_to_caller_isolation", CapturesColor);
break;

case ActorIsolation::ActorInstance:
printFieldQuoted(isolation.getActorInstance()->printRef(),
"actor_isolated", CapturesColor);
Expand Down
5 changes: 5 additions & 0 deletions lib/AST/ASTPrinter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -7583,6 +7583,11 @@ void SILParameterInfo::print(
Printer.printLifetimeDependence(*lifetimeDependence);
}

if (options.contains(SILParameterInfo::ImplicitLeading)) {
options -= SILParameterInfo::ImplicitLeading;
Printer << "@sil_implicit_leading_param ";
}

// If we did not handle a case in Options, this code was not updated
// appropriately.
assert(!bool(options) && "Code not updated for introduced option");
Expand Down
5 changes: 5 additions & 0 deletions lib/AST/ActorIsolation.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -174,6 +174,11 @@ bool ActorIsolation::isEqual(const ActorIsolation &lhs,
// to answer.
return false;

case CallerIsolationInheriting:
// This returns false for the same reason as erased. The caller has to check
// against the actual caller isolation.
return false;

case ActorInstance: {
auto *lhsActor = lhs.getActorInstance();
auto *rhsActor = rhs.getActorInstance();
Expand Down
2 changes: 2 additions & 0 deletions lib/AST/Decl.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2708,6 +2708,7 @@ static bool deferMatchesEnclosingAccess(const FuncDecl *defer) {

return true;

case ActorIsolation::CallerIsolationInheriting:
case ActorIsolation::ActorInstance:
case ActorIsolation::Nonisolated:
case ActorIsolation::Erased: // really can't happen
Expand Down Expand Up @@ -11341,6 +11342,7 @@ bool VarDecl::isSelfParamCaptureIsolated() const {
case ActorIsolation::NonisolatedUnsafe:
case ActorIsolation::GlobalActor:
case ActorIsolation::Erased:
case ActorIsolation::CallerIsolationInheriting:
return false;

case ActorIsolation::ActorInstance:
Expand Down
1 change: 1 addition & 0 deletions lib/AST/FeatureSet.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -120,6 +120,7 @@ UNINTERESTING_FEATURE(Volatile)
UNINTERESTING_FEATURE(SuppressedAssociatedTypes)
UNINTERESTING_FEATURE(StructLetDestructuring)
UNINTERESTING_FEATURE(MacrosOnImports)
UNINTERESTING_FEATURE(NonIsolatedAsyncInheritsIsolationFromContext)

static bool usesFeatureNonescapableTypes(Decl *decl) {
auto containsNonEscapable =
Expand Down
17 changes: 17 additions & 0 deletions lib/AST/TypeCheckRequests.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1856,6 +1856,7 @@ SourceLoc MacroDefinitionRequest::getNearestLoc() const {

bool ActorIsolation::requiresSubstitution() const {
switch (kind) {
case CallerIsolationInheriting:
case ActorInstance:
case Nonisolated:
case NonisolatedUnsafe:
Expand All @@ -1871,6 +1872,7 @@ bool ActorIsolation::requiresSubstitution() const {
ActorIsolation ActorIsolation::subst(SubstitutionMap subs) const {
switch (kind) {
case ActorInstance:
case CallerIsolationInheriting:
case Nonisolated:
case NonisolatedUnsafe:
case Unspecified:
Expand All @@ -1891,6 +1893,11 @@ void ActorIsolation::printForDiagnostics(llvm::raw_ostream &os,
os << "actor" << (asNoun ? " isolation" : "-isolated");
break;

case ActorIsolation::CallerIsolationInheriting:
os << "caller isolation inheriting"
<< (asNoun ? " isolation" : "-isolated");
break;

case ActorIsolation::GlobalActor: {
if (isMainActor()) {
os << "main actor" << (asNoun ? " isolation" : "-isolated");
Expand Down Expand Up @@ -1927,6 +1934,9 @@ void ActorIsolation::print(llvm::raw_ostream &os) const {
os << ". name: '" << vd->getBaseIdentifier() << "'";
}
return;
case CallerIsolationInheriting:
os << "caller_isolation_inheriting";
return;
case Nonisolated:
os << "nonisolated";
return;
Expand All @@ -1951,6 +1961,9 @@ void ActorIsolation::printForSIL(llvm::raw_ostream &os) const {
case ActorInstance:
os << "actor_instance";
return;
case CallerIsolationInheriting:
os << "caller_isolation_inheriting";
return;
case Nonisolated:
os << "nonisolated";
return;
Expand Down Expand Up @@ -1996,6 +2009,10 @@ void swift::simple_display(
}
break;

case ActorIsolation::CallerIsolationInheriting:
out << "isolated to isolation of caller";
break;

case ActorIsolation::Nonisolated:
case ActorIsolation::NonisolatedUnsafe:
out << "nonisolated";
Expand Down
1 change: 1 addition & 0 deletions lib/ASTGen/Sources/ASTGen/TypeAttrs.swift
Original file line number Diff line number Diff line change
Expand Up @@ -122,6 +122,7 @@ extension ASTGenVisitor {
.silUnowned,
.silWeak,
.silSending,
.silImplicitLeadingParam,
.unownedInnerPointer:
break;

Expand Down
1 change: 1 addition & 0 deletions lib/IDE/CompletionLookup.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -827,6 +827,7 @@ void CompletionLookup::analyzeActorIsolation(
break;
case ActorIsolation::Unspecified:
case ActorIsolation::Nonisolated:
case ActorIsolation::CallerIsolationInheriting:
case ActorIsolation::NonisolatedUnsafe:
return;
}
Expand Down
44 changes: 40 additions & 4 deletions lib/SIL/IR/SILFunctionType.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -108,6 +108,11 @@ CanSILFunctionType SILFunctionType::getUnsubstitutedType(SILModule &M) const {
getWitnessMethodConformanceOrInvalid());
}

CanType SILParameterInfo::getArgumentType(SILFunction *fn) const {
return getArgumentType(fn->getModule(), fn->getLoweredFunctionType(),
fn->getTypeExpansionContext());
}

CanType SILParameterInfo::getArgumentType(SILModule &M,
const SILFunctionType *t,
TypeExpansionContext context) const {
Expand Down Expand Up @@ -1560,6 +1565,7 @@ class DestructureInputs {
TypeConverter &TC;
const Conventions &Convs;
const ForeignInfo &Foreign;
std::optional<ActorIsolation> IsolationInfo;
struct ForeignSelfInfo {
AbstractionPattern OrigSelfParam;
AnyFunctionType::CanParam SubstSelfParam;
Expand All @@ -1572,9 +1578,10 @@ class DestructureInputs {
public:
DestructureInputs(TypeExpansionContext expansion, TypeConverter &TC,
const Conventions &conventions, const ForeignInfo &foreign,
std::optional<ActorIsolation> isolationInfo,
SmallVectorImpl<SILParameterInfo> &inputs)
: expansion(expansion), TC(TC), Convs(conventions), Foreign(foreign),
Inputs(inputs) {}
IsolationInfo(isolationInfo), Inputs(inputs) {}

void destructure(AbstractionPattern origType,
CanAnyFunctionType::CanParamArrayRef params,
Expand Down Expand Up @@ -1636,6 +1643,23 @@ class DestructureInputs {
};
}

// If we are an async function that is unspecified or nonisolated, insert an
// isolated parameter if NonIsolatedAsyncInheritsIsolationFromContext is
// enabled.
if (TC.Context.LangOpts.hasFeature(
Feature::NonIsolatedAsyncInheritsIsolationFromContext) &&
IsolationInfo &&
IsolationInfo->getKind() == ActorIsolation::CallerIsolationInheriting &&
extInfoBuilder.isAsync()) {
auto actorProtocol = TC.Context.getProtocol(KnownProtocolKind::Actor);
auto actorType =
ExistentialType::get(actorProtocol->getDeclaredInterfaceType());
addParameter(CanType(actorType).wrapInOptionalType(),
ParameterConvention::Direct_Guaranteed,
ParameterTypeFlags().withIsolated(true),
true /*implicit leading parameter*/);
}

// Add any foreign parameters that are positioned at the start
// of the sequence. visit() will add foreign parameters that are
// positioned after any parameters it adds.
Expand Down Expand Up @@ -1817,7 +1841,7 @@ class DestructureInputs {
/// Add a parameter that we derived from deconstructing the
/// formal type.
void addParameter(CanType loweredType, ParameterConvention convention,
ParameterTypeFlags origFlags) {
ParameterTypeFlags origFlags, bool isImplicit = false) {
SILParameterInfo param(loweredType, convention);

if (origFlags.isNoDerivative())
Expand All @@ -1826,6 +1850,8 @@ class DestructureInputs {
param = param.addingOption(SILParameterInfo::Sending);
if (origFlags.isIsolated())
param = param.addingOption(SILParameterInfo::Isolated);
if (isImplicit)
param = param.addingOption(SILParameterInfo::ImplicitLeading);

Inputs.push_back(param);
maybeAddForeignParameters();
Expand Down Expand Up @@ -2465,8 +2491,17 @@ static CanSILFunctionType getSILFunctionType(
// Destructure the input tuple type.
SmallVector<SILParameterInfo, 8> inputs;
{
std::optional<ActorIsolation> actorIsolation;
if (constant) {
if (constant->kind == SILDeclRef::Kind::Deallocator) {
actorIsolation = ActorIsolation::forNonisolated(false);
} else {
actorIsolation =
getActorIsolationOfContext(constant->getInnermostDeclContext());
}
}
DestructureInputs destructurer(expansionContext, TC, conventions,
foreignInfo, inputs);
foreignInfo, actorIsolation, inputs);
destructurer.destructure(origType, substFnInterfaceType.getParams(),
extInfoBuilder, unimplementable);
}
Expand Down Expand Up @@ -2554,8 +2589,9 @@ static CanSILFunctionType getSILFunctionTypeForInitAccessor(
{
bool unimplementable = false;
ForeignInfo foreignInfo;
std::optional<ActorIsolation> actorIsolation; // For now always null.
DestructureInputs destructurer(context, TC, conventions, foreignInfo,
inputs);
actorIsolation, inputs);
destructurer.destructure(
origType, substAccessorType.getParams(),
extInfoBuilder.withRepresentation(SILFunctionTypeRepresentation::Thin),
Expand Down
20 changes: 14 additions & 6 deletions lib/SIL/Verifier/SILVerifier.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -7230,22 +7230,30 @@ class SILVerifier : public SILVerifierBase<SILVerifier> {

void verifyParentFunctionSILFunctionType(CanSILFunctionType FTy) {
bool foundIsolatedParameter = false;
bool foundExplicitParameter = false;

for (const auto &parameterInfo : FTy->getParameters()) {
foundExplicitParameter |=
!parameterInfo.hasOption(SILParameterInfo::ImplicitLeading);
require(!foundExplicitParameter ||
!parameterInfo.hasOption(SILParameterInfo::ImplicitLeading),
"Implicit parameters must be before /all/ explicit parameters");

if (parameterInfo.hasOption(SILParameterInfo::Isolated)) {
auto argType = parameterInfo.getArgumentType(F.getModule(),
FTy,
F.getTypeExpansionContext());
auto argType = parameterInfo.getArgumentType(
F.getModule(), FTy, F.getTypeExpansionContext());

if (argType->isOptional())
argType = argType->lookThroughAllOptionalTypes()->getCanonicalType();

auto genericSig = FTy->getInvocationGenericSignature();
auto &ctx = F.getASTContext();
auto *actorProtocol = ctx.getProtocol(KnownProtocolKind::Actor);
auto *distributedProtocol = ctx.getProtocol(KnownProtocolKind::DistributedActor);
auto *distributedProtocol =
ctx.getProtocol(KnownProtocolKind::DistributedActor);
require(argType->isAnyActorType() ||
genericSig->requiresProtocol(argType, actorProtocol) ||
genericSig->requiresProtocol(argType, distributedProtocol),
genericSig->requiresProtocol(argType, actorProtocol) ||
genericSig->requiresProtocol(argType, distributedProtocol),
"Only any actor types can be isolated");
require(!foundIsolatedParameter, "Two isolated parameters");
foundIsolatedParameter = true;
Expand Down
Loading