I have this Objective-C framework FooModule:
// Foo2.h
#import <Foundation/Foundation.h>
NS_ASSUME_NONNULL_BEGIN
typedef void (^Reply)(id data);
typedef void (^Handler)(Reply reply);
@interface Foo2 : NSObject
- (id)initWithQueue: (dispatch_queue_t)queue;
- (void)setupHandler: (Handler)handler;
@end
NS_ASSUME_NONNULL_END
// Foo2.m
#import "Foo2.h"
@implementation Foo2 {
dispatch_queue_t _queue;
}
- (id)initWithQueue: (dispatch_queue_t)queue {
if ((self = [super init])) {
_queue = queue;
}
return self;
}
- (void)setupHandler:(Handler)handler {
dispatch_async(_queue, ^{
handler(^(id data){
NSLog(@"data is %@", data);
});
});
}
@end
Then in my app binary, I do
@preconcurrency import FooModule
func aGlobalFreeFunction() {
let foo = Foo2(queue: .global())
foo.setupHandler { reply in
Task {
reply("foo")
}
}
}
This gives me error (not warning) in swift 6 mode:
Passing closure as a 'sending' parameter risks causing data races between code in the current task and concurrent execution of the closure
I wonder why @preconcurrency import doesn't silence the error.
I tried out another non-closure type:
@interface FooNonSendable : NSObject
@property int foo;
@end
@implementation FooNonSendable
@end
Then in my swift code, after using @preconcurrent import:
func aGlobalFreeFunction() {
let fooNonSendable = FooNonSendable()
Task {
Task { @MainActor in
print(fooNonSendable)
}
}
}
This code does not give any errors or warnings, meaning that @preconcurrency is able to silence error for fooNonSendable, but not reply
closure.
I have this Objective-C framework FooModule:
// Foo2.h
#import <Foundation/Foundation.h>
NS_ASSUME_NONNULL_BEGIN
typedef void (^Reply)(id data);
typedef void (^Handler)(Reply reply);
@interface Foo2 : NSObject
- (id)initWithQueue: (dispatch_queue_t)queue;
- (void)setupHandler: (Handler)handler;
@end
NS_ASSUME_NONNULL_END
// Foo2.m
#import "Foo2.h"
@implementation Foo2 {
dispatch_queue_t _queue;
}
- (id)initWithQueue: (dispatch_queue_t)queue {
if ((self = [super init])) {
_queue = queue;
}
return self;
}
- (void)setupHandler:(Handler)handler {
dispatch_async(_queue, ^{
handler(^(id data){
NSLog(@"data is %@", data);
});
});
}
@end
Then in my app binary, I do
@preconcurrency import FooModule
func aGlobalFreeFunction() {
let foo = Foo2(queue: .global())
foo.setupHandler { reply in
Task {
reply("foo")
}
}
}
This gives me error (not warning) in swift 6 mode:
Passing closure as a 'sending' parameter risks causing data races between code in the current task and concurrent execution of the closure
I wonder why @preconcurrency import doesn't silence the error.
I tried out another non-closure type:
@interface FooNonSendable : NSObject
@property int foo;
@end
@implementation FooNonSendable
@end
Then in my swift code, after using @preconcurrent import:
func aGlobalFreeFunction() {
let fooNonSendable = FooNonSendable()
Task {
Task { @MainActor in
print(fooNonSendable)
}
}
}
This code does not give any errors or warnings, meaning that @preconcurrency is able to silence error for fooNonSendable, but not reply
closure.
1 Answer
Reset to default 1You ask why “@preconcurrency import
does not silence Sendable
warning?”
The error you are receiving is not about trying to send a non-Sendable
type. It is about your use of this sending
closure (as outlined in SE-0430), which is an specific extension to region-based isolation. Do not conflate Sendable
types with sending
parameters; they are two different things. The @preconcurrency import
addresses the former (temporarily silencing non-Sendable
warnings for legacy types that have not gone through a review for concurrency), but not the latter.
@preconcurrency
reduces an error to a warning. It does not make the facts magically go away. – matt Commented Feb 1 at 0:58viewDidLoad
of an empty template project (i didn't know it matters, but i was clearly wrong, sorry) – HL666 Commented Feb 4 at 1:29