Keyboard shortcuts

Press ← or → to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

AST lowering

AST lowering ステップは AST を HIR に変換します。 これは、型解析や同様の構文に依存しない解析に関係しない多くの構造が削除されることを意味します。 このような構造の例には、以下が含まれますが、これらに限定されません。

  • 括弧
    • 置き換えなしに削除されます。ツリー構造によって順序が明示されるためです
  • for ループ
    • match + loop + match に変換されます
  • 全称的な impl Trait
    • ジェネリック引数に変換されます (ただし、ユーザーがそれらを書いていないことを把握するためのフラグがいくつか付きます)
  • 存在的な impl Trait
    • 仮想的な existential type 宣言に変換されます

AST lowering の実装は rustc_ast_lowering クレートにあります。 エントリーポイントは lower_to_hir で、これは展開後の AST とリゾルバーのデータを TyCtxt から取得し、クレート全体の hir::Crate を構築します。

Lowering は HIR owner を中心に構成されています。 lower_to_hir はまずクレートにインデックスを付け、その後 ItemLowerer::lower_node が各クレート、アイテム、関連アイテム、外部アイテムを lowering します。

lowering ロジックの大部分は LoweringContext 上にあります。 実装は rustc_ast_lowering クレート内の item.rs、expr.rs、pat.rs、path.rs など複数のファイルに分割されていますが、それらはすべて同じ LoweringContext の状態と ID lowering の仕組みを共有しています。

各 owner は、それぞれ独自の with_hir_id_owner スコープ内で lowering されます。 これが、以下の HirId 不変条件が重要である理由です。lower_node_id は AST の NodeId を現在の owner にマップし、一方で next_id は desugaring 中に導入される HIR 専用の新しいノードを作成します。

Lowering では、compiler/rustc_passes/src/hir_id_validator.rs にある健全性チェックを発火させないために、いくつかの不変条件を守る必要があります。

  1. 作成された HirId は使用されなければなりません。 したがって、lower_node_id を使用する場合は、得られた NodeId または HirId を必ず使用しなければなりません(どちらでも問題ありません。HIR 内の任意の NodeId は、対応する HirId が存在するかチェックされるためです)。
  2. HirId の lowering は、その HirId を所有するアイテムのスコープ内で行われなければなりません。 これは、現在 lowering されているものとは別のアイテムの一部を作成している場合、with_hir_id_owner を使用する必要があることを意味します。 これは、たとえば存在的な impl Trait の lowering 中に発生します。
  3. HIR 構造に配置される NodeId は、その HirId が未使用であっても lowering されなければなりません。 let _ = self.lower_node_id(node_id); を呼び出すことは完全に正当です。
  4. AST に存在しなかった新しいノードを作成している場合は、それらに対して新しい ID を必ず作成しなければなりません。 これは next_id メソッドを呼び出すことで行われます。 このメソッドは新しい NodeId を生成すると同時に、それを自動的に lowering するため、HirId も取得できます。

新しい DefId を作成している場合、各 DefId には対応する NodeId が必要なので、lowering 中に新しいものを生成しなくて済むように、これらの NodeId を AST に追加することを推奨します。 これには、何かの DefId をその NodeId 経由で見つける方法を作れるという利点があります。 lowering がこの DefId を複数の場所で必要とする場合、それらすべての場所で新しい NodeId を生成することはできません。そうすると、そのたびに新しい DefId も得られてしまうためです。 AST 由来の NodeId があれば、これは問題になりません。

NodeId があることで、lowering がその場で DefId を生成する必要がなくなり、代わりに DefCollector が DefId を生成できるようにもなります。 DefId の生成を 1 か所に集中させることで、リファクタリングや推論が容易になります。