From e42ac61d9f98796cae78c790879bbe37a5e34395 Mon Sep 17 00:00:00 2001 From: Pavel Yaskevich Date: Mon, 7 Jul 2025 15:48:46 -0700 Subject: [PATCH] [Concurrency] Prevent use of `nonisolated(nonsending)` and `@concurrent` on `@_inheritActorContext` parameters `@_inheritActorContext` is a form of isolation which precludes direct use of inference of `nonisolated(nonsending)` and `@concurrent` just like other isolation attributes/modifiers would i.e. `isolated` or `@isolated(any)`. --- include/swift/AST/DiagnosticsSema.def | 8 ++++ lib/Sema/TypeCheckAttr.cpp | 3 ++ lib/Sema/TypeCheckDecl.cpp | 3 ++ lib/Sema/TypeCheckType.cpp | 37 +++++++++++++------ lib/Sema/TypeCheckType.h | 3 ++ .../attr_execution/attr_execution.swift | 13 +++++++ .../attr_execution/migration_mode.swift | 6 +++ .../nonisolated_nonsending_by_default.swift | 5 +++ test/Parse/execution_behavior_attrs.swift | 7 ++++ 9 files changed, 73 insertions(+), 12 deletions(-) diff --git a/include/swift/AST/DiagnosticsSema.def b/include/swift/AST/DiagnosticsSema.def index 4b1f41ca64aab..4ecef93958ec8 100644 --- a/include/swift/AST/DiagnosticsSema.def +++ b/include/swift/AST/DiagnosticsSema.def @@ -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 //===----------------------------------------------------------------------===// diff --git a/lib/Sema/TypeCheckAttr.cpp b/lib/Sema/TypeCheckAttr.cpp index 72823e718a73b..99b1948f78f76 100644 --- a/lib/Sema/TypeCheckAttr.cpp +++ b/lib/Sema/TypeCheckAttr.cpp @@ -7871,6 +7871,9 @@ void AttributeChecker::visitInheritActorContextAttr( return; auto paramTy = P->getInterfaceType(); + if (paramTy->hasError()) + return; + auto *funcTy = paramTy->lookThroughAllOptionalTypes()->getAs(); if (!funcTy) { diff --git a/lib/Sema/TypeCheckDecl.cpp b/lib/Sema/TypeCheckDecl.cpp index 2540469873b4f..aff024004c3f1 100644 --- a/lib/Sema/TypeCheckDecl.cpp +++ b/lib/Sema/TypeCheckDecl.cpp @@ -2337,6 +2337,9 @@ static Type validateParameterType(ParamDecl *decl) { if (dc->isInSpecializeExtensionContext()) options |= TypeResolutionFlags::AllowUsableFromInline; + if (decl->getAttrs().hasAttribute()) + options |= TypeResolutionFlags::InheritsActorContext; + Type Ty; auto *nestedRepr = decl->getTypeRepr(); diff --git a/lib/Sema/TypeCheckType.cpp b/lib/Sema/TypeCheckType.cpp index 1fc7ccd5e1d97..a9f4d968a92f0 100644 --- a/lib/Sema/TypeCheckType.cpp +++ b/lib/Sema/TypeCheckType.cpp @@ -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; @@ -4265,18 +4270,20 @@ NeverNullType TypeResolver::resolveASTFunctionType( if (!repr->isInvalid()) isolation = FunctionTypeIsolation::forNonIsolated(); } else if (!getWithoutClaiming(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); + } } } } @@ -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); diff --git a/lib/Sema/TypeCheckType.h b/lib/Sema/TypeCheckType.h index ecf18d2f05e00..9e622246bde17 100644 --- a/lib/Sema/TypeCheckType.h +++ b/lib/Sema/TypeCheckType.h @@ -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. diff --git a/test/Concurrency/attr_execution/attr_execution.swift b/test/Concurrency/attr_execution/attr_execution.swift index 93702c1c8aacb..0c409a760534e 100644 --- a/test/Concurrency/attr_execution/attr_execution.swift +++ b/test/Concurrency/attr_execution/attr_execution.swift @@ -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() +} diff --git a/test/Concurrency/attr_execution/migration_mode.swift b/test/Concurrency/attr_execution/migration_mode.swift index 08929d2186651..25d08906ac06e 100644 --- a/test/Concurrency/attr_execution/migration_mode.swift +++ b/test/Concurrency/attr_execution/migration_mode.swift @@ -393,3 +393,9 @@ do { } } } + +// @_inheritActorContext prevents `nonisolated(nonsending)` inference. +do { + func testInherit1(@_inheritActorContext _: @Sendable () async -> Void) {} + func testInherit2(@_inheritActorContext(always) _: (@Sendable () async -> Void)?) {} +} diff --git a/test/Concurrency/attr_execution/nonisolated_nonsending_by_default.swift b/test/Concurrency/attr_execution/nonisolated_nonsending_by_default.swift index 6c240ec88f364..2a94b64d30819 100644 --- a/test/Concurrency/attr_execution/nonisolated_nonsending_by_default.swift +++ b/test/Concurrency/attr_execution/nonisolated_nonsending_by_default.swift @@ -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'}} +} diff --git a/test/Parse/execution_behavior_attrs.swift b/test/Parse/execution_behavior_attrs.swift index 6f0a1035ff6c0..2ad748d1f289d 100644 --- a/test/Parse/execution_behavior_attrs.swift +++ b/test/Parse/execution_behavior_attrs.swift @@ -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)'}} +}