Home Explore Blog CI



rustc

3rd chunk of `src/name-resolution.md`
c589490774c6baf404d2e82d6f875ce565f59a363c9c43060000000100000dcd
shadowed by anything else). The transition to outer [`Rib`] may also affect
what names are usable ‒ if there are nested functions (not closures),
the inner one can't access parameters and local bindings of the outer one,
even though they should be visible by ordinary scoping rules. An example:


```rust
fn do_something<T: Default>(val: T) { // <- New rib in both types and values (1)
    // `val` is accessible, as is the helper function
    // `T` is accessible
   let helper = || { // New rib on the block (2)
        // `val` is accessible here
    }; // End of (2), new rib on `helper` (3)
    // `val` is accessible, `helper` variable shadows `helper` function
    fn helper() { // <- New rib in both types and values (4)
        // `val` is not accessible here, (4) is not transparent for locals
        // `T` is not accessible here
    } // End of (4)
    let val = T::default(); // New rib (5)
    // `val` is the variable, not the parameter here
} // End of (5), (3) and (1)
```

Because the rules for different namespaces are a bit different, each namespace
has its own independent [`Rib`] stack that is constructed in parallel to the others.
In addition, there's also a [`Rib`] stack for local labels (e.g. names of loops or
blocks), which isn't a full namespace in its own right.

## Overall strategy

To perform the name resolution of the whole crate, the syntax tree is traversed
top-down and every encountered name is resolved. This works for most kinds of
names, because at the point of use of a name it is already introduced in the [`Rib`]
hierarchy.

There are some exceptions to this. Items are bit tricky, because they can be
used even before encountered ‒ therefore every block needs to be first scanned
for items to fill in its [`Rib`].

Other, even more problematic ones, are imports which need recursive fixed-point
resolution and macros, that need to be resolved and expanded before the rest of
the code can be processed.

Therefore, the resolution is performed in multiple stages.

## Speculative crate loading

To give useful errors, rustc suggests importing paths into scope if they're
not found. How does it do this? It looks through every module of every crate
and looks for possible matches. This even includes crates that haven't yet
been loaded!

Eagerly loading crates to include import suggestions that haven't yet been
loaded is called _speculative crate loading_, because any errors it encounters
shouldn't be reported: [`rustc_resolve`] decided to load them, not the user. The function
that does this is [`lookup_import_candidates`] and lives in
[`rustc_resolve::diagnostics`].


To tell the difference between speculative loads and loads initiated by the
user, [`rustc_resolve`] passes around a `record_used` parameter, which is `false` when
the load is speculative.

## TODO: [#16](https://github.com/rust-lang/rustc-dev-guide/issues/16)

This is a result of the first pass of learning the code. It is definitely
incomplete and not detailed enough. It also might be inaccurate in places.
Still, it probably provides useful first guidepost to what happens in there.

* What exactly does it link to and how is that published and consumed by
  following stages of compilation?
* Who calls it and how it is actually used.
* Is it a pass and then the result is only used, or can it be computed
  incrementally?
* The overall strategy description is a bit vague.
* Where does the name `Rib` come from?
* Does this thing have its own tests, or is it tested only as part of some e2e
  testing?

Title: Name Resolution Strategy, Speculative Crate Loading, and TODOs
Summary
Each namespace has its own independent Rib stack. The name resolution of the whole crate is performed by traversing the syntax tree top-down and resolving every encountered name. Items, imports, and macros are exceptions and need special handling. Rustc performs speculative crate loading to provide import suggestions for unfound names, even from crates that haven't been loaded. The `record_used` parameter differentiates between speculative loads and user-initiated loads. The section concludes with a list of TODO items for further understanding and improvement of the name resolution process.