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 にある健全性チェックを発火させないために、いくつかの不変条件を守る必要があります。
- 作成された
HirIdは使用されなければなりません。 したがって、lower_node_idを使用する場合は、得られたNodeIdまたはHirIdを必ず使用しなければなりません(どちらでも問題ありません。HIR内の任意のNodeIdは、対応するHirIdが存在するかチェックされるためです)。 HirIdの lowering は、そのHirIdを所有するアイテムのスコープ内で行われなければなりません。 これは、現在 lowering されているものとは別のアイテムの一部を作成している場合、with_hir_id_ownerを使用する必要があることを意味します。 これは、たとえば存在的なimpl Traitの lowering 中に発生します。- HIR 構造に配置される
NodeIdは、そのHirIdが未使用であっても lowering されなければなりません。let _ = self.lower_node_id(node_id);を呼び出すことは完全に正当です。 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 か所に集中させることで、リファクタリングや推論が容易になります。