sembr src/hir/lowering.md

This commit is contained in:
Tshepang Mbambo
2026-04-22 14:18:09 +02:00
parent e704d0f624
commit 14dd4b2086
+24 -15
View File
@@ -2,8 +2,8 @@
The AST lowering step converts AST to [HIR](../hir.md).
This means many structures are removed if they are irrelevant
for type analysis or similar syntax agnostic analyses. Examples
of such structures include but are not limited to
for type analysis or similar syntax agnostic analyses.
Examples of such structures include but are not limited to
* Parenthesis
* Removed without replacement, the tree structure makes order explicit
@@ -19,16 +19,19 @@ The implementation of AST lowering is in the [`rustc_ast_lowering`] crate.
The entry point is [`lower_to_hir`], which retrieves the post-expansion AST
and resolver data from [`TyCtxt`] and builds the [`hir::Crate`] for the whole crate.
Lowering is organized around HIR owners. [`lower_to_hir`] first indexes the
Lowering is organized around HIR owners.
[`lower_to_hir`] first indexes the
crate and then [`ItemLowerer::lower_node`] lowers each crate, item, associated
item, and foreign item.
Most of the lowering logic lives on [`LoweringContext`]. The implementation is
Most of the lowering logic lives on [`LoweringContext`].
The implementation is
split across multiple files in the [`rustc_ast_lowering`] crate such as `item.rs`,
`expr.rs`, `pat.rs`, `path.rs`, and others, but they all share the same [`LoweringContext`]
state and IDlowering machinery.
Each owner is lowered in its own [`with_hir_id_owner`] scope. This is why the
Each owner is lowered in its own [`with_hir_id_owner`] scope.
This is why the
`HirId` invariants below matter: `lower_node_id` maps AST `NodeId`s into the
current owner, while `next_id` creates fresh HIR-only nodes introduced during
desugaring.
@@ -36,18 +39,20 @@ desugaring.
Lowering needs to uphold several invariants in order to not trigger the
sanity checks in [`compiler/rustc_passes/src/hir_id_validator.rs`][hir_id_validator]:
1. A `HirId` must be used if created. So if you use the `lower_node_id`,
1. A `HirId` must be used if created.
So if you use the `lower_node_id`,
you *must* use the resulting `NodeId` or `HirId` (either is fine, since
any `NodeId`s in the `HIR` are checked for existing `HirId`s)
2. Lowering a `HirId` must be done in the scope of the *owning* item.
This means you need to use `with_hir_id_owner` if you are creating parts
of an item other than the one being currently lowered. This happens for
example during the lowering of existential `impl Trait`
of an item other than the one being currently lowered.
This happens for example during the lowering of existential `impl Trait`
3. A `NodeId` that will be placed into a HIR structure must be lowered,
even if its `HirId` is unused. Calling
`let _ = self.lower_node_id(node_id);` is perfectly legitimate.
even if its `HirId` is unused.
Calling `let _ = self.lower_node_id(node_id);` is perfectly legitimate.
4. If you are creating new nodes that didn't exist in the `AST`, you *must*
create new ids for them. This is done by calling the `next_id` method,
create new ids for them.
This is done by calling the `next_id` method,
which produces both a new `NodeId` as well as automatically lowering it
for you so you also get the `HirId`.
@@ -62,12 +67,16 @@ sanity checks in [`compiler/rustc_passes/src/hir_id_validator.rs`][hir_id_valida
If you are creating new `DefId`s, since each `DefId` needs to have a
corresponding `NodeId`, it is advisable to add these `NodeId`s to the
`AST` so you don't have to generate new ones during lowering. This has
`AST` so you don't have to generate new ones during lowering.
This has
the advantage of creating a way to find the `DefId` of something via its
`NodeId`. If lowering needs this `DefId` in multiple places, you can't
`NodeId`.
If lowering needs this `DefId` in multiple places, you can't
generate a new `NodeId` in all those places because you'd also get a new
`DefId` then. With a `NodeId` from the `AST` this is not an issue.
`DefId` then.
With a `NodeId` from the `AST` this is not an issue.
Having the `NodeId` also allows the `DefCollector` to generate the `DefId`s
instead of lowering having to do it on the fly. Centralizing the `DefId`
instead of lowering having to do it on the fly.
Centralizing the `DefId`
generation in one place makes it easier to refactor and reason about.