Home Explore Blog CI



rustc

2nd chunk of `src/name-resolution.md`
c01970c22cbdc6fab8ca1c120dc6a72f2c41fe158067c99e00000001000009ee
lower-case letter while types with upper-case one, but this is only a
convention. This is legal Rust code that will compile (with warnings):

```rust
type x = u32;
let x: x = 1;
let y: x = 2; // See? x is still a type here.
```

To cope with this, and with slightly different scoping rules for these
namespaces, the resolver keeps them separated and builds separate structures for
them.

In other words, when the code talks about namespaces, it doesn't mean the module
hierarchy, it's types vs. values vs. macros.

## Scopes and ribs

A name is visible only in certain area in the source code. This forms a
hierarchical structure, but not necessarily a simple one ‒ if one scope is
part of another, it doesn't mean a name visible in the outer scope is also
visible in the inner scope, or that it refers to the same thing.

To cope with that, the compiler introduces the concept of [`Rib`]s. This is
an abstraction of a scope. Every time the set of visible names potentially changes,
a new [`Rib`] is pushed onto a stack. The places where this can happen include for
example:


* The obvious places ‒ curly braces enclosing a block, function boundaries,
  modules.
* Introducing a `let` binding ‒ this can shadow another binding with the same
  name.
* Macro expansion border ‒ to cope with macro hygiene.

When searching for a name, the stack of [`ribs`] is traversed from the innermost
outwards. This helps to find the closest meaning of the name (the one not
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

Title: Namespaces, Scopes, and Ribs in Name Resolution
Summary
This section discusses how Rust handles different namespaces (types, values, macros) and scopes during name resolution. The resolver keeps these namespaces separate and uses Ribs, which are abstractions of scopes, to manage the visibility of names. A new Rib is pushed onto a stack whenever the set of visible names potentially changes, such as when entering a block, introducing a `let` binding, or at a macro expansion border. The stack of Ribs is traversed from innermost to outermost when searching for a name.