Skip to content

AST, Sema: Fix 2 NonisolatedNonsendingByDefault bugs #81538

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 2 commits into from
May 16, 2025
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
20 changes: 11 additions & 9 deletions lib/AST/DiagnosticEngine.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -393,15 +393,17 @@ InFlightDiagnostic::fixItAddAttribute(const DeclAttribute *Attr,
const ClosureExpr *E) {
ASSERT(!E->isImplicit());

SourceLoc insertionLoc;

if (auto *paramList = E->getParameters()) {
// HACK: Don't set insertion loc to param list start loc if it's equal to
// closure start loc (meaning it's implicit).
// FIXME: Don't set the start loc of an implicit param list, or put an
// isImplicit bit on ParameterList.
if (paramList->getStartLoc() != E->getStartLoc()) {
insertionLoc = paramList->getStartLoc();
SourceLoc insertionLoc = E->getBracketRange().Start;

if (insertionLoc.isInvalid()) {
if (auto *paramList = E->getParameters()) {
// HACK: Don't set insertion loc to param list start loc if it's equal to
// closure start loc (meaning it's implicit).
// FIXME: Don't set the start loc of an implicit param list, or put an
// isImplicit bit on ParameterList.
if (paramList->getStartLoc() != E->getStartLoc()) {
insertionLoc = paramList->getStartLoc();
}
}
}

Expand Down
58 changes: 45 additions & 13 deletions lib/Sema/TypeCheckType.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2407,6 +2407,11 @@ namespace {
class TypeAttrSet {
const ASTContext &ctx;

/// FIXME:
/// `nonisolated(nonsending)` is modeled as a separate `TypeRepr`, but
/// needs to be considered together with subsequent attributes.
CallerIsolatedTypeRepr *nonisolatedNonsendingAttr;

llvm::TinyPtrVector<CustomAttr*> customAttrs;
EnumMap<TypeAttrKind, TypeAttribute *> typeAttrs;

Expand All @@ -2418,7 +2423,9 @@ namespace {
#endif

public:
TypeAttrSet(const ASTContext &ctx) : ctx(ctx) {}
TypeAttrSet(const ASTContext &ctx,
CallerIsolatedTypeRepr *nonisolatedNonsendingAttr = nullptr)
: ctx(ctx), nonisolatedNonsendingAttr(nonisolatedNonsendingAttr) {}

TypeAttrSet(const TypeAttrSet &) = delete;
TypeAttrSet &operator=(const TypeAttrSet &) = delete;
Expand All @@ -2437,6 +2444,10 @@ namespace {
/// will be diagnosed.
void accumulate(ArrayRef<TypeOrCustomAttr> attrs);

CallerIsolatedTypeRepr *getNonisolatedNonsendingAttr() const {
return nonisolatedNonsendingAttr;
}

/// Return all of the custom attributes.
ArrayRef<CustomAttr*> getCustomAttrs() const {
return customAttrs;
Expand Down Expand Up @@ -2536,9 +2547,17 @@ namespace {
}

template <class AttrClass>
AttrClass *getWithoutClaiming(TypeAttrSet *attrs) {
std::enable_if_t<std::is_base_of_v<TypeAttribute, AttrClass>, AttrClass *>
getWithoutClaiming(TypeAttrSet *attrs) {
return (attrs ? getWithoutClaiming<AttrClass>(*attrs) : nullptr);
}

template <class AttrClass>
std::enable_if_t<std::is_same_v<AttrClass, CallerIsolatedTypeRepr>,
CallerIsolatedTypeRepr *>
getWithoutClaiming(TypeAttrSet *attrs) {
return attrs ? attrs->getNonisolatedNonsendingAttr() : nullptr;
}
} // end anonymous namespace

Type TypeResolution::resolveContextualType(
Expand Down Expand Up @@ -4233,10 +4252,19 @@ NeverNullType TypeResolver::resolveASTFunctionType(
};

if (auto concurrentAttr = claim<ConcurrentTypeAttr>(attrs)) {
if (auto *nonisolatedNonsendingAttr =
getWithoutClaiming<CallerIsolatedTypeRepr>(attrs)) {
diagnoseInvalid(
nonisolatedNonsendingAttr, nonisolatedNonsendingAttr->getStartLoc(),
diag::cannot_use_nonisolated_nonsending_together_with_concurrent,
nonisolatedNonsendingAttr);
}

checkExecutionBehaviorAttribute(concurrentAttr);

if (!repr->isInvalid())
isolation = FunctionTypeIsolation::forNonIsolated();
} else {
} else if (!getWithoutClaiming<CallerIsolatedTypeRepr>(attrs)) {
if (ctx.LangOpts.getFeatureState(Feature::NonisolatedNonsendingByDefault)
.isEnabledForMigration()) {
// Diagnose only in the interface stage, which is run once.
Expand Down Expand Up @@ -5267,7 +5295,20 @@ TypeResolver::resolveSendingTypeRepr(SendingTypeRepr *repr,
NeverNullType
TypeResolver::resolveCallerIsolatedTypeRepr(CallerIsolatedTypeRepr *repr,
TypeResolutionOptions options) {
Type type = resolveType(repr->getBase(), options);
Type type;
{
TypeAttrSet attrs(getASTContext(), repr);

auto *baseRepr = repr->getBase();
if (auto *attrRepr = dyn_cast<AttributedTypeRepr>(baseRepr)) {
baseRepr = attrs.accumulate(attrRepr);
}

type = resolveAttributedType(baseRepr, options, attrs);

attrs.diagnoseUnclaimed(resolution, options, type);
}

if (type->hasError())
return ErrorType::get(getASTContext());

Expand All @@ -5283,15 +5324,6 @@ TypeResolver::resolveCallerIsolatedTypeRepr(CallerIsolatedTypeRepr *repr,
diag::nonisolated_nonsending_only_on_async, repr);
}

if (auto *ATR = dyn_cast<AttributedTypeRepr>(repr->getBase())) {
if (ATR->get(TypeAttrKind::Concurrent)) {
diagnoseInvalid(
repr, repr->getStartLoc(),
diag::cannot_use_nonisolated_nonsending_together_with_concurrent,
repr);
}
}

switch (fnType->getIsolation().getKind()) {
case FunctionTypeIsolation::Kind::NonIsolated:
break;
Expand Down
93 changes: 67 additions & 26 deletions test/Concurrency/attr_execution/adoption_mode.swift
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,8 @@ struct G<T> {
// MARK: Functions
do {
func syncF() {}
@concurrent func executionConcurrentAsyncF() async {}
nonisolated(nonsending) func executionCallerAsyncF() async {}
@concurrent func concurrentAsyncF() async {}
nonisolated(nonsending) func nonisolatedNonsendingAsyncF() async {}
@MainActor func mainActorAsyncF() async {}
func isolatedParamAsyncF(
isolation: isolated (any Actor)? = #isolation
Expand All @@ -24,13 +24,15 @@ do {

struct S {
init(sync: ()) {}
@concurrent init(executionAsync: ()) async {}
@concurrent init(concurrentAsync: ()) async {}
nonisolated(nonsending) init(nonisolatedNonsendingAsync: ()) async {}
@MainActor init(mainActorAsync: ()) async {}
// expected-warning@+1:5 {{feature 'NonisolatedNonsendingByDefault' will cause nonisolated async initializer 'init' to run on the caller's actor; use @concurrent to preserve behavior}}{{5-5=@concurrent }}{{none}}
init(async: ()) async {}

func syncF() {}
@concurrent func executionAsyncF() async {}
@concurrent func concurrentAsyncF() async {}
nonisolated(nonsending) func nonisolatedNonsendingAsyncF() async {}
@MainActor func mainActorAsyncF() async {}
// expected-warning@+2:17 {{feature 'NonisolatedNonsendingByDefault' will cause nonisolated async instance method 'asyncF' to run on the caller's actor; use @concurrent to preserve behavior}}{{-1:5-5=@concurrent }}{{none}}
nonisolated
Expand All @@ -39,13 +41,15 @@ do {

protocol P {
init(sync: ())
@concurrent init(executionAsync: ()) async
@concurrent init(concurrentAsync: ()) async
nonisolated(nonsending) init(nonisolatedNonsendingAsync: ()) async
@MainActor init(mainActorAsync: ()) async
// expected-warning@+1:5 {{feature 'NonisolatedNonsendingByDefault' will cause nonisolated async initializer 'init' to run on the caller's actor; use @concurrent to preserve behavior}}{{5-5=@concurrent }}{{none}}
init(async: ()) async

func syncF()
@concurrent func executionAsyncF() async
@concurrent func concurrentAsyncF() async
nonisolated(nonsending) func nonisolatedNonsendingAsyncF() async
@MainActor func mainActorAsyncF() async
// expected-warning@+1:10 {{feature 'NonisolatedNonsendingByDefault' will cause nonisolated async instance method 'asyncF' to run on the caller's actor; use @concurrent to preserve behavior}}{{5-5=@concurrent }}{{none}}
func asyncF() async
Expand All @@ -54,13 +58,15 @@ do {
protocol Functions {}
extension Functions {
init(sync: ()) {}
@concurrent init(executionAsync: ()) async {}
@concurrent init(concurrentAsync: ()) async {}
nonisolated(nonsending) init(nonisolatedNonsendingAsync: ()) async {}
@MainActor init(mainActorAsync: ()) async {}
// expected-warning@+1:3 {{feature 'NonisolatedNonsendingByDefault' will cause nonisolated async initializer 'init' to run on the caller's actor; use @concurrent to preserve behavior}}{{3-3=@concurrent }}{{none}}
init(async: ()) async {}

func syncF() {}
@concurrent func executionAsyncF() async {}
@concurrent func concurrentAsyncF() async {}
nonisolated(nonsending) func nonisolatedNonsendingAsyncF() async {}
@MainActor func mainActorAsyncF() async {}
// expected-warning@+1:8 {{feature 'NonisolatedNonsendingByDefault' will cause nonisolated async instance method 'asyncF' to run on the caller's actor; use @concurrent to preserve behavior}}{{3-3=@concurrent }}{{none}}
func asyncF() async {}
Expand All @@ -75,8 +81,11 @@ do {
var syncS: Int { get {} set {} }
subscript(syncS _: Int) -> Int { get {} }

@concurrent var executionAsyncS: Int { get async {} }
@concurrent subscript(executionAsyncS _: Int) -> Int { get async {} }
@concurrent var concurrentAsyncS: Int { get async {} }
@concurrent subscript(concurrentAsyncS _: Int) -> Int { get async {} }

nonisolated(nonsending) var nonisolatedNonsendingAsyncS: Int { get async {} }
nonisolated(nonsending) subscript(nonisolatedNonsendingAsyncS _: Int) -> Int { get async {} }

@MainActor var mainActorAsyncS: Int { get async {} }
@MainActor subscript(mainActorAsyncS _: Int) -> Int { get async {} }
Expand All @@ -95,8 +104,11 @@ do {
var syncS: Int { get }
subscript(syncS _: Int) -> Int { get }

@concurrent var executionAsyncS: Int { get async }
@concurrent subscript(executionAsyncS _: Int) -> Int { get async }
@concurrent var concurrentAsyncS: Int { get async }
@concurrent subscript(concurrentAsyncS _: Int) -> Int { get async }

nonisolated(nonsending) var nonisolatedNonsendingAsyncS: Int { get async }
nonisolated(nonsending) subscript(nonisolatedNonsendingAsyncS _: Int) -> Int { get async }

@MainActor var mainActorAsyncS: Int { get async }
@MainActor subscript(mainActorAsyncS _: Int) -> Int { get async }
Expand All @@ -112,8 +124,11 @@ extension Storage {
var syncS: Int { get {} set {} }
subscript(syncS _: Int) -> Int { get {} }

@concurrent var executionAsyncS: Int { get async {} }
@concurrent subscript(executionAsyncS _: Int) -> Int { get async {} }
@concurrent var concurrentAsyncS: Int { get async {} }
@concurrent subscript(concurrentAsyncS _: Int) -> Int { get async {} }

nonisolated(nonsending) var nonisolatedNonsendingAsyncS: Int { get async {} }
nonisolated(nonsending) subscript(nonisolatedNonsendingAsyncS _: Int) -> Int { get async {} }

@MainActor var mainActorAsyncS: Int { get async {} }
@MainActor subscript(mainActorAsyncS _: Int) -> Int { get async {} }
Expand All @@ -134,7 +149,8 @@ do {
enum E {
case esac(
sync: () -> Void,
executionAsync: @concurrent () async -> Void,
concurrentAsync: @concurrent () async -> Void,
nonisolatedNonsendingAsync: nonisolated(nonsending) () async -> Void,
mainActorAsync: @MainActor () async -> Void,
// expected-warning@+1:14 {{feature 'NonisolatedNonsendingByDefault' will cause nonisolated async function type to be treated as specified to run on the caller's actor; use @concurrent to preserve behavior}}{{14-14=@concurrent }}{{none}}
async: () async -> Void,
Expand All @@ -146,7 +162,8 @@ do {
struct S {
subscript(
sync: () -> Void,
executionAsync: @concurrent () async -> Void,
concurrentAsync: @concurrent () async -> Void,
nonisolatedNonsendingAsync: nonisolated(nonsending) () async -> Void,
mainActorAsync: @MainActor () async -> Void,
// expected-warning@+1:14 {{feature 'NonisolatedNonsendingByDefault' will cause nonisolated async function type to be treated as specified to run on the caller's actor; use @concurrent to preserve behavior}}{{14-14=@concurrent }}{{none}}
async: () async -> Void,
Expand All @@ -159,7 +176,8 @@ do {

func foo(
sync: () -> Void,
executionAsync: @concurrent () async -> Void,
concurrentAsync: @concurrent () async -> Void,
nonisolatedNonsendingAsync: nonisolated(nonsending) () async -> Void,
mainActorAsync: @MainActor () async -> Void,
// expected-warning@+1:12 {{feature 'NonisolatedNonsendingByDefault' will cause nonisolated async function type to be treated as specified to run on the caller's actor; use @concurrent to preserve behavior}}{{12-12=@concurrent }}{{none}}
async: () async -> Void,
Expand All @@ -169,7 +187,8 @@ do {

let _ = { (
sync: () -> Void,
executionAsync: @concurrent () async -> Void,
concurrentAsync: @concurrent () async -> Void,
nonisolatedNonsendingAsync: nonisolated(nonsending) () async -> Void,
mainActorAsync: @MainActor () async -> Void,
// expected-warning@+1:12 {{feature 'NonisolatedNonsendingByDefault' will cause nonisolated async function type to be treated as specified to run on the caller's actor; use @concurrent to preserve behavior}}{{12-12=@concurrent }}{{none}}
async: () async -> Void,
Expand All @@ -183,7 +202,8 @@ do {
do {
struct G<T> {
struct Sync where T == () -> Void {}
struct ExecutionAsync where T == @concurrent () async -> Void {}
struct ConcurrentAsync where T == @concurrent () async -> Void {}
struct NonisolatedNonsendingAsync where T == nonisolated(nonsending) () async -> Void {}
struct MainActorAsync where T == @MainActor () async -> Void {}
// expected-warning@+1:29 {{feature 'NonisolatedNonsendingByDefault' will cause nonisolated async function type to be treated as specified to run on the caller's actor; use @concurrent to preserve behavior}}{{29-29=@concurrent }}{{none}}
struct Async where T == () async -> Void {}
Expand All @@ -196,6 +216,7 @@ do {
do {
let _: () -> Void
let _: @concurrent () async -> Void
let _: nonisolated(nonsending) () async -> Void
let _: @MainActor () async -> Void
// expected-warning@+1:10 {{feature 'NonisolatedNonsendingByDefault' will cause nonisolated async function type to be treated as specified to run on the caller's actor; use @concurrent to preserve behavior}}{{10-10=@concurrent }}{{none}}
let _: () async -> Void
Expand All @@ -209,6 +230,7 @@ do {

let _ = anything as? () -> Void
let _ = anything as? @concurrent () async -> Void
let _ = anything as? nonisolated(nonsending) () async -> Void
let _ = anything as? @MainActor () async -> Void
// expected-warning@+1:24 {{feature 'NonisolatedNonsendingByDefault' will cause nonisolated async function type to be treated as specified to run on the caller's actor; use @concurrent to preserve behavior}}{{24-24=@concurrent }}{{none}}
let _ = anything as? () async -> Void
Expand All @@ -220,45 +242,64 @@ do {
do {
nonisolated
func nonisolatedF() {
let x = 0

let _ = { () -> Void in }

let _ = { @concurrent () async -> Void in }
let _ = { @MainActor () async -> Void in }

// expected-warning@+1:13 {{feature 'NonisolatedNonsendingByDefault' will cause nonisolated async closure to run on the caller's actor; use @concurrent to preserve behavior}}{{15-15=@concurrent }}{{none}}
let _ = { () async -> Void in }

func takesInts(_: Int...) {}
// expected-warning@+1:13 {{feature 'NonisolatedNonsendingByDefault' will cause nonisolated async closure to run on the caller's actor; use @concurrent to preserve behavior}}{{15-15=@concurrent }}{{none}}
let _ = { [x] () async -> Void in _ = x }

// expected-warning@+1:13 {{feature 'NonisolatedNonsendingByDefault' will cause nonisolated async closure to run on the caller's actor; use @concurrent to preserve behavior}}{{14-14= @concurrent in }}{{none}}
let _ = {await globalAsyncF()}
// expected-warning@+1:13 {{feature 'NonisolatedNonsendingByDefault' will cause nonisolated async closure to run on the caller's actor; use @concurrent to preserve behavior}}{{14-14=@concurrent }}{{none}}
let _ = {[x] in await globalAsyncF(); _ = x}

// expected-warning@+1:13 {{feature 'NonisolatedNonsendingByDefault' will cause nonisolated async closure to run on the caller's actor; use @concurrent to preserve behavior}}{{14-14= @concurrent in }}{{none}}
let _ = {
await globalAsyncF()
}
// expected-warning@+1:13 {{feature 'NonisolatedNonsendingByDefault' will cause nonisolated async closure to run on the caller's actor; use @concurrent to preserve behavior}}{{14-14= @concurrent in }}{{none}}
let _ = {
await globalAsyncF()
func takesInts(_: Int...) {}
takesInts($0, $1, $2)
}

// expected-warning@+1:13 {{feature 'NonisolatedNonsendingByDefault' will cause nonisolated async closure to run on the caller's actor; use @concurrent to preserve behavior}}{{25-25=@concurrent }}{{none}}
let _ = { @Sendable in
await globalAsyncF()
}
// expected-warning@+1:13 {{feature 'NonisolatedNonsendingByDefault' will cause nonisolated async closure to run on the caller's actor; use @concurrent to preserve behavior}}{{25-25=@concurrent }}{{none}}
let _ = { @Sendable [x] in
_ = x
await globalAsyncF()
}

// expected-warning@+2:18 {{feature 'NonisolatedNonsendingByDefault' will cause nonisolated async function type to be treated as specified to run on the caller's actor; use @concurrent to preserve behavior}}{{18-18=@concurrent }}{{none}}
// expected-warning@+1:45 {{feature 'NonisolatedNonsendingByDefault' will cause nonisolated async closure to run on the caller's actor; use @concurrent to preserve behavior}}{{47-47=@concurrent }}{{none}}
var closure: (Int, Int) async -> Void = { a, b in
await globalAsyncF()
}
// expected-warning@+1:15 {{feature 'NonisolatedNonsendingByDefault' will cause nonisolated async closure to run on the caller's actor; use @concurrent to preserve behavior}}{{17-17=@concurrent }}{{none}}
closure = { [x] a, b in _ = x }
// expected-warning@+1:15 {{feature 'NonisolatedNonsendingByDefault' will cause nonisolated async closure to run on the caller's actor; use @concurrent to preserve behavior}}{{+1:7-7=@concurrent }}{{none}}
closure = {
a, b async in
await globalAsyncF()
a, b async in ()
}
// expected-warning@+1:15 {{feature 'NonisolatedNonsendingByDefault' will cause nonisolated async closure to run on the caller's actor; use @concurrent to preserve behavior}}{{17-17=@concurrent }}{{none}}
closure = { (a, b) in
await globalAsyncF()
// expected-warning@+1:15 {{feature 'NonisolatedNonsendingByDefault' will cause nonisolated async closure to run on the caller's actor; use @concurrent to preserve behavior}}{{+1:7-7=@concurrent }}{{none}}
closure = {
[x] a, b async in _ = x
}
// expected-warning@+1:15 {{feature 'NonisolatedNonsendingByDefault' will cause nonisolated async closure to run on the caller's actor; use @concurrent to preserve behavior}}{{17-17=@concurrent }}{{none}}
closure = { (a, b) in () }

// expected-warning@+1:15 {{feature 'NonisolatedNonsendingByDefault' will cause nonisolated async closure to run on the caller's actor; use @concurrent to preserve behavior}}{{17-17=@concurrent }}{{none}}
closure = { [x] (a, b) in _ = x }

let _ = closure
}
Expand Down