@preconcurrencyについて調べるスレ
@preconcurrency
の効果について理解が不足してるので調べる。まずは preconcurrency attribute
でググって見つけたこれを読む(前提swift5.6から導入されたらしい)
[原文] Apply this attribute to a declaration, to suppress strict concurrency checking. You can apply this attribute to the following kinds of declarations:
・Imports
・Structures, classes, and actors
・Enumerations and enumeration cases
・Protocols
・Variables and constants
・Subscripts
・Initializers
・Functions
[和訳] この属性(preconcurrency)を宣言に適用して、厳密な並行性チェックを抑制する。属性は以下の宣言に適用できる。
(宣言部分は自明なので略)Subscripts等にも適用できるのね。
[原文] On an import declaration, this attribute reduces the strictness of concurrency checking for code that uses types from the imported module. Specifically, types from the imported module that aren’t explicitly marked as nonsendable can be used in a context that requires sendable types.
[和訳] import宣言において、この属性は、importされたモジュールの型を使用するコードの並行性チェックの厳密性を下げます。具体的には、import されたモジュールで明示的に nonsendable にマークされていない型は、sendable な型を必要とするコンテキストで使用することができます。
[原文] On other declarations, this attribute reduces the strictness of concurrency checking for code that uses the symbol being declared. When you use this symbol in a scope that has minimal concurrency checking, concurrency-related constraints specified by that symbol, such as Sendable requirements or global actors, aren’t checked.
You can use this attribute as follows, to aid in migrating code to strict concurrency checking:
- Enable strict checking.
- Annotate imports with the preconcurrency attribute for modules that haven’t enabled strict checking.
- After migrating a module to strict checking, remove the preconcurrency attribute. The compiler warns you about any places where the preconcurrency attribute on an import no longer has an effect and should be removed.
For other declarations, add the preconcurrency attribute when you add concurrency-related constraints to the declaration, if you still have clients that haven’t migrated to strict checking. Remove the preconcurrency attribute after all your clients have migrated.
Declarations from Objective-C are always imported as if they were marked with the preconcurrency attribute.[和訳] 他の宣言では、この属性は、宣言されているシンボルを使用するコードの並行性チェックの厳密さを低下させます。最小限の並行チェックが行われるスコープでこのシンボルを使用すると、送信可能な要件やグローバル アクターなど、そのシンボルによって指定された並行関連の制約はチェックされません。
この属性を次のように使用すると、コードを厳密な並行性チェックに移行するのに役立ちます。
- 厳密なチェックを有効にする
- 厳密なチェックが有効になっていないモジュールの preconcurrencyを使用してインポートに注釈を付けます。
- モジュールを厳密なチェックに移行した後、preconcurrency属性を削除します。コンパイラは、インポートの preconcurrency が効果を持たなくなり、削除する必要がある場所について警告します。
他の宣言の場合、厳密なチェックに移行していないクライアントがまだある場合は、並行性関連の制約を宣言に追加するときに preconcurrency 属性を追加します。すべてのクライアントが移行された後、preconcurrency属性を削除します。
Objective-C からの宣言は、preconcurrency属性でマークされているかのように常にインポートされます。
具体的なユースケースはざっくりと以下で理解。
- @preconcurrency import
- モジュールの型を使う際に出る並行性の警告を抑制したいとき
- 主に古いモジュールを使う際に利用者側で指定するもの
- @preconccurency付きのprotocol準拠
- 隔離不一致の警告抑制のために主にモジュールの利用者側で指定するもの
- https://www.swift.org/migration/documentation/swift-6-concurrency-migration-guide/commonproblems#Preconcurrency-Conformance
- @preconcurrency をクラスなどの宣言に対して指定
- モジュールの後方互換性を保ちながら漸進的にswift concurrencyに対応したいとき
- この用法については Swift6 Migration Guide にも記載があった
- 主にモジュールの作成者側が指定するもの
- モジュールの後方互換性を保ちながら漸進的にswift concurrencyに対応したいとき
@preconcurrency import
はモジュール由来の型(non-sendableに推論されるもの)を利用者側で隔離境界を跨いで渡すケースは勿論、以下のようなglobal変数を参照するケースの警告も抑制してくれる。
ライブラリ側
public class A {
public static var a = 10
}
利用者側
// 以下の警告は `@preconcurrency import Module` で抑制された
let a = A.a // Reference to class property 'a' is not concurrency-safe because it involves shared mutable state; this is an error in Swift 6
print(a)
However, if we add @preconcurrency to the declaration of doSomethingThenFollowUp, its type is adjusted to remove both the @MainActor and the @Sendable, eliminating the errors and providing the same type inference from before concurrency was adopted by doSomethingThenFollowUp. The difference is visible in the type of doSomethingThenFollowUp in a minimal vs. a strict context:
https://github.com/swiftlang/swift-evolution/blob/main/proposals/0337-support-incremental-migration-to-concurrency-checking.md#preconcurrency-attribute-on-nominal-declarations
上記の挙動も実際に確認したが、strinct concurrency checkingがminimalだとMainActorやSendableが確かに推論される型から削除され呼び出し元からの後方互換性を保っていた。
func minimal() {
let fn = doSomethingThenFollowUp // type is (( )-> Void) -> Void
}
func strict() async {
let fn = doSomethingThenFollowUp // type is @MainActor (@Sendable ( )-> Void) -> Void
}