Swift Digest
SE-0537 | Swift Evolution

関数のセクション配置制御

Section Placement Control for Functions

Proposal
SE-0537
Authors
Doug Gregor
Review Manager
Becca Royal-Gordon
Status
Implemented (Swift Next)

このダイジェストはClaude Opus 4.7 / 4.8によって生成されたものです(License)。原文はこちら

01 何が問題だったのか

Embedded Swift でファームウェアなどを書くとき、リンカやツールは特定の種類のシンボルが特定のセクションに置かれていることを期待します。たとえばファームウェアの起動処理(boot code)は、あらかじめ決められたセクションに配置しておく必要があることがよくあります。

SE-0492 は、オブジェクトファイル中のどのセクションにエンティティを配置するかを指定する @section 属性を導入しました。しかし SE-0492 が対象にしたのはグローバル変数と static メンバー変数だけで、関数への適用は今後の課題(future direction)として残されていました。

一方で、ファームウェアのエントリポイントや起動処理のように、関数そのものを特定のセクションへ置きたい ユースケースは現実に存在します。次のような起動処理を専用セクションに配置したい、というのが典型例です。

// この関数のコードを custom セクションに配置したい
@section("__TEXT,boot")
func firmwareBootEntrypoint() { ... }

また SE-0492 は、@section を付けた変数に didSet / willSet といったプロパティ監視子を持たせられないという制約を課していました。この制約は本質的に不要なものでした。

02 どのように解決されるのか

SE-0492 の @section 属性を、あらゆる種類の関数へ適用できるように拡張します。対象となるのは、通常の関数(func)、イニシャライザ(init)、デイニシャライザ(deinit)、クロージャ、そして get / set などのアクセサです。

@section("__TEXT,boot")
func firmwareBootEntrypoint() { ... }

struct MyBootConfig: ~Copyable {
  @section("__TEXT,boot") init() {
    registerCallback { @section("__TEXT,boot") in
      ...
    }
  }

  @section("__TEXT,boot") deinit { }

  var bootPhase: Int {
    @section("__TEXT,boot") get { ... }
    @section("__TEXT,boot") set { ... }
  }
}

変数に対する @section と違い、関数ではジェネリックな関数やジェネリックな文脈の中にある関数に対する制約がありません。関数はすべてテキストセクションに置かれるためです。これにより、型のインスタンスメソッドにも @section を付けられます。

@section は、その関数だけでなく、実装が生成する関連する関数にも適用されます。

  • async 関数に付けた場合、実装が生成する各 partial function も同じセクションに置かれます。
  • ジェネリック関数に付けた場合、元のジェネリック定義に加え、コンパイラが生成する特殊化(specialization)にも適用されます。
  • @main 型の main 関数に @section を付けた場合、コンパイラが出力する実際の main エントリポイントにも同じセクションが使われます。

アクセサ・クロージャ・ローカル関数への推論

アクセサやクロージャに @section が明示されていない場合、次のように推論されます。

  • クロージャやローカル関数は、(存在すれば)それを囲む関数から @section を推論します。

    @section("__TEXT,boot")
    func firmwareBootEntrypoint() {
      func helper() { // @section("__TEXT,boot") を推論
    
      }
    
      registerCallback { // @section("__TEXT,boot") を推論
        ...
      }
    }
    
  • 実装が合成する読み取り専用アクセサ(get / borrow / yielding borrow など)は、明示的に書かれたこれらのアクセサから @section を推論します。
  • 実装が合成する書き込み系アクセサ(set / mutate / yielding mutate / init / didSet / willSet)は、明示的に書かれたこれらのアクセサから @section を推論します。

    var scratchSpace: MutableSpan<UInt> {
      @section("__TEXT,boot") borrow { ... }
      // 合成される get は borrow から @section("__TEXT,boot") を推論
    
      @section("__TEXT,boot") mutate { ... }
      // 合成される set は mutate から @section("__TEXT,boot") を推論
    }
    

ただし、変数からそのアクセサへ @section が推論されることはありません。コードとデータは通常異なるセクションに置かれるためです。また、ローカルな型(およびそのメンバー)にもセクションは推論されません。

推論の抑制

セクションとして default キーワードを指定すると、そのプラットフォームで既定のセクションを使うことを明示できます。これは、囲む関数などから働く推論を上書きします。たとえば、@section が付いた関数の中にあるローカル関数だけを既定のセクションに戻したい場合は、次のように書きます。

@section("__TEXT,boot")
func firmwareBootEntrypoint() {
  @section(default) func helper() { // @section("__TEXT,boot") の推論を抑制

  }
}

didSet / willSet の制約緩和

SE-0492 では、@section を付けた変数は didSet / willSet を持てないという制約がありました。本Proposalでこの制約を撤廃します。didSet / willSet 自体に @section を付けられるようになり、その値は実装がシンボルを合成する実体(たとえば set)への推論に使われます。

03 今後の見通し

提案では、次のような発展方向が挙げられています。将来の構想であり、実現を約束するものではありません。

データ用とファンクション用の @section の分離

Swift の宣言の中には、データのシンボルと関数のシンボルの両方を生成するものがあります。たとえば型を定義すると、メタデータシンボル(データ)とメタデータアクセサ関数(関数)の両方が生成されることがあります。本Proposalも SE-0492 も、型定義への @section は許していませんが、もし許すとすれば、データ用と関数用で別々のセクションを指定できる仕組みが必要になります。たとえば次のような構文が考えられます。

@section(data: "__DATA,mysection")
@section(function: "__FUNCTION,mysection")
public struct MyStruct { ... }

data / function のいずれも指定しない @section は、本Proposalと SE-0492 が扱う範囲のように、配置先が一意に定まる場所でのみ適用されます。型定義・エクステンション・プロトコル適合や、ファイル単位の既定指定のように、データと関数の両方のシンボルを生成する新しい場所は、この新構文を使えるようになる、という構想です。