Skip to content
Merged
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
8 changes: 8 additions & 0 deletions include/swift/AST/DiagnosticsSema.def
Original file line number Diff line number Diff line change
Expand Up @@ -8699,6 +8699,14 @@ ERROR(inherit_actor_context_only_on_async_or_isolation_erased_params,none,
"asynchronous function types",
(DeclAttribute))

ERROR(inherit_actor_context_with_concurrent,none,
"'@_inheritActorContext' attribute cannot be used together with %0",
(const TypeAttribute *))

ERROR(inherit_actor_context_with_nonisolated_nonsending,none,
"'@_inheritActorContext' attribute cannot be used together with %0",
(TypeRepr *))

//===----------------------------------------------------------------------===//
// MARK: @concurrent and nonisolated(nonsending) attributes
//===----------------------------------------------------------------------===//
Expand Down
3 changes: 3 additions & 0 deletions lib/Sema/TypeCheckAttr.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -7871,6 +7871,9 @@ void AttributeChecker::visitInheritActorContextAttr(
return;

auto paramTy = P->getInterfaceType();
if (paramTy->hasError())
return;

auto *funcTy =
paramTy->lookThroughAllOptionalTypes()->getAs<AnyFunctionType>();
if (!funcTy) {
Expand Down
3 changes: 3 additions & 0 deletions lib/Sema/TypeCheckDecl.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2337,6 +2337,9 @@ static Type validateParameterType(ParamDecl *decl) {
if (dc->isInSpecializeExtensionContext())
options |= TypeResolutionFlags::AllowUsableFromInline;

if (decl->getAttrs().hasAttribute<InheritActorContextAttr>())
options |= TypeResolutionFlags::InheritsActorContext;

Type Ty;

auto *nestedRepr = decl->getTypeRepr();
Expand Down
37 changes: 25 additions & 12 deletions lib/Sema/TypeCheckType.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -4219,6 +4219,11 @@ NeverNullType TypeResolver::resolveASTFunctionType(
attr->getAttrName());
}

if (options.contains(TypeResolutionFlags::InheritsActorContext)) {
diagnoseInvalid(repr, attr->getAttrLoc(),
diag::inherit_actor_context_with_concurrent, attr);
}

switch (isolation.getKind()) {
case FunctionTypeIsolation::Kind::NonIsolated:
break;
Expand Down Expand Up @@ -4265,18 +4270,20 @@ NeverNullType TypeResolver::resolveASTFunctionType(
if (!repr->isInvalid())
isolation = FunctionTypeIsolation::forNonIsolated();
} else if (!getWithoutClaiming<CallerIsolatedTypeRepr>(attrs)) {
// Infer async function type as `nonisolated(nonsending)` if there is
// no `@concurrent` or `nonisolated(nonsending)` attribute and isolation
// is nonisolated.
if (ctx.LangOpts.hasFeature(Feature::NonisolatedNonsendingByDefault) &&
repr->isAsync() && isolation.isNonIsolated()) {
isolation = FunctionTypeIsolation::forNonIsolatedCaller();
} else if (ctx.LangOpts
.getFeatureState(Feature::NonisolatedNonsendingByDefault)
.isEnabledForMigration()) {
// Diagnose only in the interface stage, which is run once.
if (inStage(TypeResolutionStage::Interface)) {
warnAboutNewNonisolatedAsyncExecutionBehavior(ctx, repr, isolation);
if (!options.contains(TypeResolutionFlags::InheritsActorContext)) {
// Infer async function type as `nonisolated(nonsending)` if there is
// no `@concurrent` or `nonisolated(nonsending)` attribute and isolation
// is nonisolated.
if (ctx.LangOpts.hasFeature(Feature::NonisolatedNonsendingByDefault) &&
repr->isAsync() && isolation.isNonIsolated()) {
isolation = FunctionTypeIsolation::forNonIsolatedCaller();
} else if (ctx.LangOpts
.getFeatureState(Feature::NonisolatedNonsendingByDefault)
.isEnabledForMigration()) {
// Diagnose only in the interface stage, which is run once.
if (inStage(TypeResolutionStage::Interface)) {
warnAboutNewNonisolatedAsyncExecutionBehavior(ctx, repr, isolation);
}
}
}
}
Expand Down Expand Up @@ -5326,6 +5333,12 @@ TypeResolver::resolveCallerIsolatedTypeRepr(CallerIsolatedTypeRepr *repr,
return ErrorType::get(getASTContext());
}

if (options.contains(TypeResolutionFlags::InheritsActorContext)) {
diagnoseInvalid(repr, repr->getLoc(),
diag::inherit_actor_context_with_nonisolated_nonsending,
repr);
}

if (!fnType->isAsync()) {
diagnoseInvalid(repr, repr->getStartLoc(),
diag::nonisolated_nonsending_only_on_async, repr);
Expand Down
3 changes: 3 additions & 0 deletions lib/Sema/TypeCheckType.h
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,9 @@ enum class TypeResolutionFlags : uint16_t {

/// Whether the immediate context has an @escaping attribute.
DirectEscaping = 1 << 14,

/// We are in a `@_inheritActorContext` parameter declaration.
InheritsActorContext = 1 << 15,
};

/// Type resolution contexts that require special handling.
Expand Down
13 changes: 13 additions & 0 deletions test/Concurrency/attr_execution/attr_execution.swift
Original file line number Diff line number Diff line change
Expand Up @@ -80,3 +80,16 @@ func testClosure() {
takesClosure {
}
}

// CHECK-LABEL: // testInheritsActor(fn:)
// CHECK: Isolation: global_actor. type: MainActor
// CHECK: sil hidden [ossa] @$s14attr_execution17testInheritsActor2fnyyyYaYbXE_tYaF : $@convention(thin) @async (@guaranteed @noescape @Sendable @async @callee_guaranteed () -> ()) -> ()
// CHECK: bb0([[FN:%.*]] : @guaranteed $@noescape @Sendable @async @callee_guaranteed () -> ()):
// CHECK: [[FN_COPY:%.*]] = copy_value [[FN]]
// CHECK: [[BORROWED_FN_COPY:%.*]] = begin_borrow [[FN_COPY]]
// CHECK: apply [[BORROWED_FN_COPY]]() : $@noescape @Sendable @async @callee_guaranteed () -> ()
// CHECK: } // end sil function '$s14attr_execution17testInheritsActor2fnyyyYaYbXE_tYaF'
@MainActor
func testInheritsActor(@_inheritActorContext(always) fn: @Sendable () async -> Void) async {
await fn()
}
6 changes: 6 additions & 0 deletions test/Concurrency/attr_execution/migration_mode.swift
Original file line number Diff line number Diff line change
Expand Up @@ -393,3 +393,9 @@ do {
}
}
}

// @_inheritActorContext prevents `nonisolated(nonsending)` inference.
do {
func testInherit1(@_inheritActorContext _: @Sendable () async -> Void) {}
func testInherit2(@_inheritActorContext(always) _: (@Sendable () async -> Void)?) {}
}
Original file line number Diff line number Diff line change
Expand Up @@ -8,3 +8,8 @@ func testCasts() {
// expected-error@-1 {{cannot convert value of type '(nonisolated(nonsending) () async -> ()).Type' to type '(() async -> ()).Type' in coercion}}
_ = defaultedType as (nonisolated(nonsending) () async -> ()).Type // Ok
}

func test(@_inheritActorContext fn: @Sendable () async -> Void) {
let _: Int = fn
// expected-error@-1 {{cannot convert value of type '@Sendable () async -> Void' to specified type 'Int'}}
}
7 changes: 7 additions & 0 deletions test/Parse/execution_behavior_attrs.swift
Original file line number Diff line number Diff line change
Expand Up @@ -84,3 +84,10 @@ do {
nonisolated(0) // expected-warning {{result of call to 'nonisolated' is unused}}
print("hello")
}

do {
func testActorInheriting1(@_inheritActorContext _: @concurrent @Sendable () async -> Void) {}
// expected-error@-1 {{'@_inheritActorContext' attribute cannot be used together with '@concurrent'}}
func testActorInheriting2(@_inheritActorContext _: nonisolated(nonsending) @Sendable () async -> Void) {}
// expected-error@-1 {{'@_inheritActorContext' attribute cannot be used together with 'nonisolated(nonsending)'}}
}