Skip to content
Closed
Show file tree
Hide file tree
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
3 changes: 3 additions & 0 deletions include/swift/AST/Decl.h
Original file line number Diff line number Diff line change
Expand Up @@ -3285,6 +3285,9 @@ class ValueDecl : public Decl {
/// `AbstractStorageDecl`, returns `false`.
bool isAsync() const;

/// Returns whether this function represents a defer body.
bool isDeferBody() const;

private:
bool isObjCDynamic() const {
return isObjC() && isDynamic();
Expand Down
33 changes: 27 additions & 6 deletions lib/AST/Decl.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -9443,7 +9443,18 @@ void ParamDecl::setDefaultExpr(Expr *E) {
}

void ParamDecl::setTypeCheckedDefaultExpr(Expr *E) {
assert(E || getDefaultArgumentKind() == DefaultArgumentKind::Inherited);
// The type-checker will only produce a null expression here if the
// default argument is inherited, so if we're called with a null pointer
// in any other case, it must be from a request cycle. Don't crash;
// just wrap the original expression with an ErrorExpr and proceed.
if (!E && getDefaultArgumentKind() != DefaultArgumentKind::Inherited) {
auto *initExpr = getStructuralDefaultExpr();
assert(initExpr);
auto &ctx = getASTContext();
E = new (ctx) ErrorExpr(initExpr->getSourceRange(), ErrorType::get(ctx),
initExpr);
}

setDefaultExpr(E);

auto *defaultInfo = DefaultValueAndFlags.getPointer();
Expand Down Expand Up @@ -11868,6 +11879,12 @@ PrecedenceGroupDecl *InfixOperatorDecl::getPrecedenceGroup() const {
nullptr);
}

bool ValueDecl::isDeferBody() const {
if (auto fn = dyn_cast<FuncDecl>(this))
return fn->isDeferBody();
return false;
}

bool FuncDecl::isDeferBody() const {
return getBaseIdentifier() == getASTContext().getIdentifier("$defer");
}
Expand Down Expand Up @@ -12061,12 +12078,16 @@ ActorIsolation swift::getActorIsolationOfContext(
getClosureActorIsolation) {
auto &ctx = dc->getASTContext();
auto dcToUse = dc;
// Defer bodies share actor isolation of their enclosing context.
if (auto FD = dyn_cast<FuncDecl>(dcToUse)) {
if (FD->isDeferBody()) {
dcToUse = FD->getDeclContext();
}

// Defer bodies share the actor isolation of their enclosing context.
// We don't actually have to do this check here because
// getActorIsolation does consider it already, but it's nice to
// avoid some extra request evaluation in a trivial case.
while (auto FD = dyn_cast<FuncDecl>(dcToUse)) {
if (!FD->isDeferBody()) break;
dcToUse = FD->getDeclContext();
}

if (auto *vd = dyn_cast_or_null<ValueDecl>(dcToUse->getAsDecl()))
return getActorIsolation(vd);

Expand Down
15 changes: 8 additions & 7 deletions lib/SIL/IR/SILFunctionType.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1261,6 +1261,11 @@ class Conventions {

ConventionsKind getKind() const { return kind; }

bool hasCallerIsolationParameter() const {
return kind == ConventionsKind::Default ||
kind == ConventionsKind::Deallocator;
}

virtual ParameterConvention
getIndirectParameter(unsigned index,
const AbstractionPattern &type,
Expand Down Expand Up @@ -1700,14 +1705,10 @@ class DestructureInputs {
};
}

// If we are an async function that is unspecified or nonisolated, insert an
// isolated parameter if NonisolatedNonsendingByDefault is enabled.
//
// NOTE: The parameter is not inserted for async functions imported
// from ObjC because they are handled in a special way that doesn't
// require it.
// If the function has nonisolated(nonsending) isolation, insert the
// implicit isolation parameter.
if (IsolationInfo && IsolationInfo->isCallerIsolationInheriting() &&
extInfoBuilder.isAsync() && !Foreign.async) {
Convs.hasCallerIsolationParameter()) {
auto actorProtocol = TC.Context.getProtocol(KnownProtocolKind::Actor);
auto actorType =
ExistentialType::get(actorProtocol->getDeclaredInterfaceType());
Expand Down
4 changes: 2 additions & 2 deletions lib/SILGen/SILGenConcurrency.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -190,7 +190,7 @@ void SILGenFunction::emitExpectedExecutorProlog() {
}

case ActorIsolation::CallerIsolationInheriting:
assert(F.isAsync());
assert(F.isAsync() || F.isDefer());
setExpectedExecutorForParameterIsolation(*this, actorIsolation);
break;

Expand Down Expand Up @@ -255,7 +255,7 @@ void SILGenFunction::emitExpectedExecutorProlog() {
RegularLocation::getDebugOnlyLocation(F.getLocation(), getModule()),
executor,
/*mandatory*/ false);
} else {
} else if (wantDataRaceChecks) {
// For a synchronous function, check that we're on the same executor.
// Note: if we "know" that the code is completely Sendable-safe, this
// is unnecessary. The type checker will need to make this determination.
Expand Down
8 changes: 8 additions & 0 deletions lib/Sema/TypeCheckConcurrency.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -6353,6 +6353,14 @@ static bool shouldSelfIsolationOverrideDefault(

static InferredActorIsolation computeActorIsolation(Evaluator &evaluator,
ValueDecl *value) {
// Defer bodies share the actor isolation of their enclosing context.
if (value->isDeferBody()) {
return {
getActorIsolationOfContext(value->getDeclContext()),
IsolationSource()
};
}

// If this declaration has actor-isolated "self", it's isolated to that
// actor.
if (evaluateOrDefault(evaluator, HasIsolatedSelfRequest{value}, false)) {
Expand Down
10 changes: 10 additions & 0 deletions test/Concurrency/actor_defer.swift
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,16 @@ func testNonDefer_negative() {
// CHECK-NEXT: function_ref
// CHECK-NEXT: apply

@MainActor func testGlobalActor_nested_positive() {
defer {
defer {
requiresMainActor()
}
doSomething()
}
doSomething()
}

#if NEGATIVES
// expected-note @+1 {{add '@MainActor' to make global function 'testGlobalActor_negative()' part of global actor 'MainActor'}}
func testGlobalActor_negative() {
Expand Down
53 changes: 53 additions & 0 deletions test/Concurrency/isolated_nonsending_isolation_macro_sil.swift
Original file line number Diff line number Diff line change
Expand Up @@ -19,3 +19,56 @@ func take(iso: (any Actor)?) {}
// CHECK: apply [[FUNC]]([[ACTOR]]) : $@convention(thin) (@guaranteed Optional<any Actor>) -> ()
// CHECK: release_value [[ACTOR]]
// CHECK: } // end sil function '$s39isolated_nonsending_isolation_macro_sil21nonisolatedNonsendingyyYaF'

// Check that we emit #isolation correctly in defer bodies.
nonisolated(nonsending)
func hasDefer() async {
defer {
take(iso: #isolation)
}
do {}
}
// CHECK-LABEL: sil hidden @$s39isolated_nonsending_isolation_macro_sil8hasDeferyyYaF :
// CHECK: bb0(%0 : $Optional<any Actor>):
// CHECK: // function_ref $defer
// CHECK-NEXT: [[DEFER:%.*]] = function_ref
// CHECK-NEXT: apply [[DEFER]](%0)

// CHECK-LABEL: // $defer #1 () in hasDefer()
// CHECK-NEXT: // Isolation: caller_isolation_inheriting
// CHECK: bb0(%0 : $Optional<any Actor>):
// CHECK-NEXT: // function_ref take(iso:)
// CHECK-NEXT: [[FN:%.*]] = function_ref @
// CHECK-NEXT: apply [[FN]](%0)

// Check that we emit #isolation correctly in nested defer bodies.
nonisolated(nonsending)
func hasNestedDefer() async {
defer {
defer {
take(iso: #isolation)
}
do {}
}
do {}
}

// CHECK-LABEL: sil hidden @$s39isolated_nonsending_isolation_macro_sil14hasNestedDeferyyYaF :
// CHECK: bb0(%0 : $Optional<any Actor>):
// CHECK: // function_ref $defer #1 () in hasNestedDefer()
// CHECK-NEXT: [[DEFER:%.*]] = function_ref
// CHECK-NEXT: apply [[DEFER]](%0)

// CHECK-LABEL: // $defer #1 () in hasNestedDefer()
// CHECK-NEXT: // Isolation: caller_isolation_inheriting
// CHECK: bb0(%0 : $Optional<any Actor>):
// CHECK: // function_ref $defer #1 () in $defer #1 () in hasNestedDefer()
// CHECK-NEXT: [[DEFER:%.*]] = function_ref
// CHECK-NEXT: apply [[DEFER]](%0)

// CHECK-LABEL: // $defer #1 () in $defer #1 () in hasNestedDefer()
// CHECK-NEXT: // Isolation: caller_isolation_inheriting
// CHECK: bb0(%0 : $Optional<any Actor>):
// CHECK-NEXT: // function_ref take(iso:)
// CHECK-NEXT: [[FN:%.*]] = function_ref @
// CHECK-NEXT: apply [[FN]](%0)