Skip to content

[concurrency] Split out the default inferred actor isolation computation into a helper from ActorIsolationRequest::evaluate. #79145

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
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
162 changes: 88 additions & 74 deletions lib/Sema/TypeCheckConcurrency.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -5533,6 +5533,82 @@ static void addAttributesForActorIsolation(ValueDecl *value,
}
}

/// Determine the default isolation and isolation source for this declaration,
/// which may still be overridden by other inference rules.
static std::tuple<InferredActorIsolation, ValueDecl *,
std::optional<ActorIsolation>>
computeDefaultInferredActorIsolation(ValueDecl *value) {
auto &ctx = value->getASTContext();

// If we are supposed to infer main actor isolation by default for entities
// within our module, make our default isolation main actor.
if (ctx.LangOpts.hasFeature(Feature::UnspecifiedMeansMainActorIsolated) &&
value->getModuleContext() == ctx.MainModule) {

// Default global actor isolation does not apply to any declarations
// within actors and distributed actors.
bool inActorContext = false;
auto *dc = value->getInnermostDeclContext();
while (dc && !inActorContext) {
if (auto *nominal = dc->getSelfNominalTypeDecl()) {
inActorContext = nominal->isAnyActor();
}
dc = dc->getParent();
}

if (!inActorContext) {
// FIXME: deinit should be implicitly MainActor too.
if (isa<TypeDecl>(value) || isa<ExtensionDecl>(value) ||
isa<AbstractStorageDecl>(value) || isa<FuncDecl>(value) ||
isa<ConstructorDecl>(value)) {
return {{ActorIsolation::forMainActor(ctx), {}}, nullptr, {}};
}
}
}

// If we have an async function... by default we inherit isolation.
if (ctx.LangOpts.hasFeature(
Feature::NonIsolatedAsyncInheritsIsolationFromContext)) {
if (auto *func = dyn_cast<AbstractFunctionDecl>(value);
func && func->hasAsync() &&
func->getModuleContext() == ctx.MainModule) {
return {{ActorIsolation::forNonisolated(false /*is unsafe*/), {}},
nullptr,
{}};
}
}

if (auto func = dyn_cast<AbstractFunctionDecl>(value)) {
// A @Sendable function is assumed to be actor-independent.
if (func->isSendable()) {
return {
{ActorIsolation::forConcurrent(/*unsafe=*/false), {}}, nullptr, {}};
}
}

// When no other isolation applies, an actor's non-async init is independent
if (auto nominal = value->getDeclContext()->getSelfNominalTypeDecl())
if (nominal->isAnyActor())
if (auto ctor = dyn_cast<ConstructorDecl>(value))
if (!ctor->hasAsync())
return {{ActorIsolation::forConcurrent(/*unsafe=*/false), {}},
nullptr,
{}};

// Look for and remember the overridden declaration's isolation.
if (auto *overriddenValue = value->getOverriddenDeclOrSuperDeinit()) {
// Use the overridden decl's iso as the default isolation for this decl.
auto isolation = getOverriddenIsolationFor(value);
return {{isolation,
IsolationSource(overriddenValue, IsolationSource::Override)},
overriddenValue,
isolation};
}

// We did not find anything special, return unspecified.
return {{ActorIsolation::forUnspecified(), {}}, nullptr, {}};
}

InferredActorIsolation ActorIsolationRequest::evaluate(
Evaluator &evaluator, ValueDecl *value) const {
// If this declaration has actor-isolated "self", it's isolated to that
Expand Down Expand Up @@ -5576,7 +5652,7 @@ InferredActorIsolation ActorIsolationRequest::evaluate(
value->getAttrs().add(preconcurrency);
}

if (FuncDecl *fd = dyn_cast<FuncDecl>(value)) {
if (auto *fd = dyn_cast<FuncDecl>(value)) {
// Main.main() and Main.$main are implicitly MainActor-protected.
// Any other isolation is an error.
std::optional<ActorIsolation> mainIsolation =
Expand Down Expand Up @@ -5609,74 +5685,11 @@ InferredActorIsolation ActorIsolationRequest::evaluate(
IsolationSource(/*source*/ nullptr, IsolationSource::Explicit)};
}

// Determine the default isolation for this declaration, which may still be
// overridden by other inference rules.
ActorIsolation defaultIsolation = ActorIsolation::forUnspecified();
IsolationSource defaultIsolationSource;

// If we are supposed to infer main actor isolation by default for entities
// within our module, make our default isolation main actor.
if (ctx.LangOpts.hasFeature(Feature::UnspecifiedMeansMainActorIsolated) &&
value->getModuleContext() == ctx.MainModule) {

// Default global actor isolation does not apply to any declarations
// within actors and distributed actors.
bool inActorContext = false;
auto *dc = value->getInnermostDeclContext();
while (dc && !inActorContext) {
if (auto *nominal = dc->getSelfNominalTypeDecl()) {
inActorContext = nominal->isAnyActor();
}
dc = dc->getParent();
}

if (!inActorContext) {
// FIXME: deinit should be implicitly MainActor too.
if (isa<TypeDecl>(value) || isa<ExtensionDecl>(value) ||
isa<AbstractStorageDecl>(value) || isa<FuncDecl>(value) ||
isa<ConstructorDecl>(value)) {
defaultIsolation = ActorIsolation::forMainActor(ctx);
}
}
}

// If we have an async function... by default we inherit isolation.
if (ctx.LangOpts.hasFeature(
Feature::NonIsolatedAsyncInheritsIsolationFromContext)) {
if (auto *func = dyn_cast<AbstractFunctionDecl>(value);
func && func->hasAsync() &&
func->getModuleContext() == ctx.MainModule) {
defaultIsolation = ActorIsolation::forNonisolated(false /*is unsafe*/);
}
}

if (auto func = dyn_cast<AbstractFunctionDecl>(value)) {
// A @Sendable function is assumed to be actor-independent.
if (func->isSendable()) {
defaultIsolation = ActorIsolation::forConcurrent(/*unsafe=*/false);
}
}

// When no other isolation applies, an actor's non-async init is independent
if (auto nominal = value->getDeclContext()->getSelfNominalTypeDecl())
if (nominal->isAnyActor())
if (auto ctor = dyn_cast<ConstructorDecl>(value))
if (!ctor->hasAsync())
defaultIsolation = ActorIsolation::forConcurrent(/*unsafe=*/false);

// Look for and remember the overridden declaration's isolation.
std::optional<ActorIsolation> overriddenIso;
ValueDecl *overriddenValue = value->getOverriddenDeclOrSuperDeinit();
if (overriddenValue) {
// use the overridden decl's iso as the default isolation for this decl.
defaultIsolation = getOverriddenIsolationFor(value);
defaultIsolationSource =
IsolationSource(overriddenValue, IsolationSource::Override);
overriddenIso = defaultIsolation;
}

// NOTE: After this point, the default has been set. Only touch the default
// isolation above this point since code below assumes it is now constant.
InferredActorIsolation defaultIsolation;
ValueDecl *overriddenValue;
std::optional<ActorIsolation> overriddenIsolation;
std::tie(defaultIsolation, overriddenValue, overriddenIsolation) =
computeDefaultInferredActorIsolation(value);

// Function used when returning an inferred isolation.
auto inferredIsolation = [&](ActorIsolation inferred,
Expand All @@ -5687,16 +5700,17 @@ InferredActorIsolation ActorIsolationRequest::evaluate(
// if the inferred isolation is not valid, then carry-over the overridden
// declaration's isolation as this decl's inferred isolation.
switch (validOverrideIsolation(value, inferred, overriddenValue,
*overriddenIso)) {
*overriddenIsolation)) {
case OverrideIsolationResult::Allowed:
case OverrideIsolationResult::Sendable:
break;

case OverrideIsolationResult::Disallowed:
if (overriddenValue->hasClangNode() && overriddenIso->isUnspecified()) {
inferred = overriddenIso->withPreconcurrency(true);
if (overriddenValue->hasClangNode() &&
overriddenIsolation->isUnspecified()) {
inferred = overriddenIsolation->withPreconcurrency(true);
} else {
inferred = *overriddenIso;
inferred = *overriddenIsolation;
}
break;
}
Expand Down Expand Up @@ -5955,7 +5969,7 @@ InferredActorIsolation ActorIsolationRequest::evaluate(
}

// Default isolation for this member.
return {defaultIsolation, defaultIsolationSource};
return defaultIsolation;
}

bool HasIsolatedSelfRequest::evaluate(
Expand Down