Rewrite the "representing types" section to be more comprehensive

This commit is contained in:
Boxy 2024-05-27 22:03:01 +01:00
parent fa325b1081
commit 89536d8e5b
11 changed files with 508 additions and 342 deletions

View File

@ -119,19 +119,20 @@
- [Early vs Late bound parameters](./early-late-bound-params/early-late-bound-summary.md)
- [Implementation nuances of early/late bound parameters](./early-late-bound-params/early-late-bound-implementation-nuances.md)
- [Interactions with turbofishing](./early-late-bound-params/turbofishing-and-early-late-bound.md)
- [`TypeFolder` and `TypeFoldable`](./ty-fold.md)
- [Generic arguments](./generic_arguments.md)
- [The `ty` module: representing types](./ty.md)
- [ADTs and Generic Arguments](./ty_module/generic_arguments.md)
- [Parameter types/consts/regions](./ty_module/param_ty_const_regions.md)
- [`EarlyBinder` and instantiating parameters](./ty_module/early_binder.md)
- [`Binder` and Higher ranked regions](./ty_module/binders.md)
- [Instantiating binders](./ty_module/instantiating_binders.md)
- [Constants in the type system](./constants.md)
- [Bound vars and Parameters](./bound-vars-and-params.md)
- [`TypeFolder` and `TypeFoldable`](./ty-fold.md)
- [Parameter Environments](./param_env/param_env_summary.md)
- [What is it?](./param_env/param_env_what_is_it.md)
- [How are `ParamEnv`'s constructed internally](./param_env/param_env_construction_internals.md)
- [Which `ParamEnv` do I use?](./param_env/param_env_acquisition.md)
- [Type inference](./type-inference.md)
- [Trait solving](./traits/resolution.md)
- [Early and Late Bound Parameter Definitions](./early-late-bound-params/early-late-bound-summary.md)
- [Implementation nuances of early/late bound parameters](./early-late-bound-params/early-late-bound-implementation-nuances.md)
- [Interactions with turbofishing](./early-late-bound-params/turbofishing-and-early-late-bound.md)
- [Higher-ranked trait bounds](./traits/hrtb.md)
- [Caching subtleties](./traits/caching.md)
- [Implied bounds](./traits/implied-bounds.md)

View File

@ -1,59 +0,0 @@
# Bound vars and parameters
## Early-bound parameters
Early-bound parameters in rustc are identified by an index, stored in the
[`ParamTy`] struct for types or the [`EarlyParamRegion`] struct for lifetimes.
The index counts from the outermost declaration in scope. This means that as you
add more binders inside, the index doesn't change.
For example,
```rust,ignore
trait Foo<T> {
type Bar<U> = (Self, T, U);
}
```
Here, the type `(Self, T, U)` would be `($0, $1, $2)`, where `$N` means a
[`ParamTy`] with the index of `N`.
In rustc, the [`Generics`] structure carries this information. So the
[`Generics`] for `Bar` above would be just like for `U` and would indicate the
'parent' generics of `Foo`, which declares `Self` and `T`. You can read more
in [this chapter](./generics.md).
[`ParamTy`]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_middle/ty/struct.ParamTy.html
[`EarlyParamRegion`]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_middle/ty/region/struct.EarlyParamRegion.html
[`Generics`]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_middle/ty/struct.Generics.html
## Late-bound parameters
Late-bound parameters in `rustc` are handled differently. We indicate their
presence by a [`Binder`] type. The [`Binder`] doesn't know how many variables
there are at that binding level. This can only be determined by walking the
type itself and collecting them. So a type like `for<'a, 'b> ('a, 'b)` would be
`for (^0.a, ^0.b)`. Here, we just write `for` because we don't know the names
of the things bound within.
Moreover, a reference to a late-bound lifetime is written `^0.a`:
- The `0` is the index; it identifies that this lifetime is bound in the
innermost binder (the `for`).
- The `a` is the "name"; late-bound lifetimes in rustc are identified by a
"name" -- the [`BoundRegionKind`] enum. This enum can contain a
[`DefId`][defid] or it might have various "anonymous" numbered names. The
latter arise from types like `fn(&u32, &u32)`, which are equivalent to
something like `for<'a, 'b> fn(&'a u32, &'b u32)`, but the names of those
lifetimes must be generated.
This setup of not knowing the full set of variables at a binding level has some
advantages and some disadvantages. The disadvantage is that you must walk the
type to find out what is bound at the given level and so forth. The advantage
is primarily that, when constructing types from Rust syntax, if we encounter
anonymous regions like in `fn(&u32)`, we just create a fresh index and don't have
to update the binder.
[`Binder`]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_middle/ty/struct.Binder.html
[`BoundRegionKind`]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_middle/ty/enum.BoundRegionKind.html
[defid]: ./hir.html#identifiers-in-the-hir

View File

@ -1,50 +0,0 @@
# Generic arguments
A `ty::GenericArg<'tcx>` represents some entity in the type system: a type
(`Ty<'tcx>`), lifetime (`ty::Region<'tcx>`) or constant (`ty::Const<'tcx>`).
`GenericArg` is used to perform instantiation of generic parameters to concrete
arguments, such as when calling a function with generic parameters explicitly
with type arguments. Instantiations are represented using the
[`GenericArgs` type](#genericargs) as described below.
## `GenericArgs`
`ty::GenericArgs<'tcx>` is intuitively simply a slice of `GenericArg<'tcx>`s,
acting as an ordered list of generic parameters instantiated to
concrete arguments (such as types, lifetimes and consts).
For example, given a `HashMap<K, V>` with two type parameters, `K` and `V`, an
instantiation of the parameters, for example `HashMap<i32, u32>`, would be
represented by `&'tcx [tcx.types.i32, tcx.types.u32]`.
`GenericArgs` provides various convenience methods to instantiate generic arguments
given item definitions, which should generally be used rather than explicitly
instantiating such slices.
## `GenericArg`
The actual `GenericArg` struct is optimised for space, storing the type, lifetime or
const as an interned pointer containing a tag identifying its kind (in the
lowest 2 bits). Unless you are working with the `GenericArgs` implementation
specifically, you should generally not have to deal with `GenericArg` and instead
make use of the safe [`GenericArgKind`](#genericargkind) abstraction.
## `GenericArgKind`
As `GenericArg` itself is not type-safe, the `GenericArgKind` enum provides a more
convenient and safe interface for dealing with generic arguments. An
`GenericArgKind` can be converted to a raw `GenericArg` using `GenericArg::from()`
(or simply `.into()` when the context is clear). As mentioned earlier, instantiation
lists store raw `GenericArg`s, so before dealing with them, it is preferable to
convert them to `GenericArgKind`s first. This is done by calling the `.unpack()`
method.
```rust,ignore
// An example of unpacking and packing a generic argument.
fn deal_with_generic_arg<'tcx>(generic_arg: GenericArg<'tcx>) -> GenericArg<'tcx> {
// Unpack a raw `GenericArg` to deal with it safely.
let new_generic_arg: GenericArgKind<'tcx> = match generic_arg.unpack() {
GenericArgKind::Type(ty) => { /* ... */ }
GenericArgKind::Lifetime(lt) => { /* ... */ }
GenericArgKind::Const(ct) => { /* ... */ }
};
// Pack the `GenericArgKind` to store it in a generic args list.
new_generic_arg.into()
}
```

View File

@ -1,144 +0,0 @@
# Generics and GenericArgs
Given a generic type `MyType<A, B, …>`, we may want to swap out the generics `A, B, …` for some
other types (possibly other generics or concrete types). We do this a lot while doing type
inference, type checking, and trait solving. Conceptually, during these routines, we may find out
that one type is equal to another type and want to swap one out for the other and then swap that out
for another type and so on until we eventually get some concrete types (or an error).
In rustc this is done using [GenericArgs].
Conceptually, you can think of `GenericArgs` as a list of types that are to be substituted for
the generic type parameters of the ADT.
`GenericArgs` is a type alias of `&'tcx List<GenericArg<'tcx>>` (see [`List` rustdocs][list]).
[`GenericArg`] is essentially a space-efficient wrapper around [`GenericArgKind`], which is an enum
indicating what kind of generic the type parameter is (type, lifetime, or const).
Thus, `GenericArgs` is conceptually like a `&'tcx [GenericArgKind<'tcx>]` slice (but it is
actually a `List`).
[list]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_middle/ty/struct.List.html
[`GenericArg`]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_middle/ty/struct.GenericArg.html
[`GenericArgKind`]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_middle/ty/enum.GenericArgKind.html
[GenericArgs]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_middle/ty/type.GenericArgs.html
So why do we use this `List` type instead of making it really a slice? It has the length "inline",
so `&List` is only 32 bits. As a consequence, it cannot be "subsliced" (that only works if the
length is out of line).
This also implies that you can check two `List`s for equality via `==` (which would be not be
possible for ordinary slices). This is precisely because they never represent a "sub-list", only the
complete `List`, which has been hashed and interned.
So pulling it all together, lets go back to our example above:
```rust,ignore
struct MyStruct<T>
```
- There would be an `AdtDef` (and corresponding `DefId`) for `MyStruct`.
- There would be a `TyKind::Param` (and corresponding `DefId`) for `T` (more later).
- There would be a `GenericArgs` containing the list `[GenericArgKind::Type(Ty(T))]`
- The `Ty(T)` here is my shorthand for entire other `ty::Ty` that has `TyKind::Param`, which we
mentioned in the previous point.
- This is one `TyKind::Adt` containing the `AdtDef` of `MyStruct` with the `GenericArgs` above.
Finally, we will quickly mention the
[`Generics`](https://doc.rust-lang.org/nightly/nightly-rustc/rustc_middle/ty/struct.Generics.html) type. It
is used to give information about the type parameters of a type.
### Unsubstituted Generics
So above, recall that in our example the `MyStruct` struct had a generic type `T`. When we are (for
example) type checking functions that use `MyStruct`, we will need to be able to refer to this type
`T` without actually knowing what it is. In general, this is true inside all generic definitions: we
need to be able to work with unknown types. This is done via `TyKind::Param` (which we mentioned in
the example above).
Each `TyKind::Param` contains two things: the name and the index. In general, the index fully
defines the parameter and is used by most of the code. The name is included for debug print-outs.
There are two reasons for this. First, the index is convenient, it allows you to include into the
list of generic arguments when substituting. Second, the index is more robust. For example, you
could in principle have two distinct type parameters that use the same name, e.g. `impl<A> Foo<A> {
fn bar<A>() { .. } }`, although the rules against shadowing make this difficult (but those language
rules could change in the future).
The index of the type parameter is an integer indicating its order in the list of the type
parameters. Moreover, we consider the list to include all of the type parameters from outer scopes.
Consider the following example:
```rust,ignore
struct Foo<A, B> {
// A would have index 0
// B would have index 1
.. // some fields
}
impl<X, Y> Foo<X, Y> {
fn method<Z>() {
// inside here, X, Y and Z are all in scope
// X has index 0
// Y has index 1
// Z has index 2
}
}
```
When we are working inside the generic definition, we will use `TyKind::Param` just like any other
`TyKind`; it is just a type after all. However, if we want to use the generic type somewhere, then
we will need to do substitutions.
For example suppose that the `Foo<A, B>` type from the previous example has a field that is a
`Vec<A>`. Observe that `Vec` is also a generic type. We want to tell the compiler that the type
parameter of `Vec` should be replaced with the `A` type parameter of `Foo<A, B>`. We do that with
substitutions:
```rust,ignore
struct Foo<A, B> { // Adt(Foo, &[Param(0), Param(1)])
x: Vec<A>, // Adt(Vec, &[Param(0)])
..
}
fn bar(foo: Foo<u32, f32>) { // Adt(Foo, &[u32, f32])
let y = foo.x; // Vec<Param(0)> => Vec<u32>
}
```
This example has a few different substitutions:
- In the definition of `Foo`, in the type of the field `x`, we replace `Vec`'s type parameter with
`Param(0)`, the first parameter of `Foo<A, B>`, so that the type of `x` is `Vec<A>`.
- In the function `bar`, we specify that we want a `Foo<u32, f32>`. This means that we will
substitute `Param(0)` and `Param(1)` with `u32` and `f32`.
- In the body of `bar`, we access `foo.x`, which has type `Vec<Param(0)>`, but `Param(0)` has been
substituted for `u32`, so `foo.x` has type `Vec<u32>`.
Lets look a bit more closely at that last substitution to see why we use indexes. If we want to
find the type of `foo.x`, we can get generic type of `x`, which is `Vec<Param(0)>`. Now we can take
the index `0` and use it to find the right type substitution: looking at `Foo`'s `GenericArgs`,
we have the list `[u32, f32]` , since we want to replace index `0`, we take the 0-th index of this
list, which is `u32`. Voila!
You may have a couple of followup questions…
**`type_of`** How do we get the "generic type of `x`"? You can get the type of pretty much anything
with the `tcx.type_of(def_id)` query. In this case, we would pass the `DefId` of the field `x`.
The `type_of` query always returns the definition with the generics that are in scope of the
definition. For example, `tcx.type_of(def_id_of_my_struct)` would return the “self-view” of
`MyStruct`: `Adt(Foo, &[Param(0), Param(1)])`.
How do we actually do the substitutions? There is a function for that too! You
use [`instantiate`] to replace a `GenericArgs` with another list of types.
[Here is an example of actually using `instantiate` in the compiler][instantiatex].
The exact details are not too important, but in this piece of code, we happen to be
converting from the `rustc_hir::Ty` to a real `ty::Ty`. You can see that we first get some args
(`args`). Then we call `type_of` to get a type and call `ty.instantiate(tcx, args)` to get a new
version of `ty` with the args made.
[`instantiate`]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_middle/ty/generic_args/struct.EarlyBinder.html#method.instantiate
[instantiatex]: https://github.com/rust-lang/rust/blob/8a562f9671e36cf29c9c794c2646bcf252d55535/compiler/rustc_hir_analysis/src/astconv/mod.rs#L905-L927
**Note on indices:** It is possible for the indices in `Param` to not match with what we expect. For
example, the index could be out of bounds or it could be the index of a lifetime when we were
expecting a type. These sorts of errors would be caught earlier in the compiler when translating
from a `rustc_hir::Ty` to a `ty::Ty`. If they occur later, that is a compiler bug.

View File

@ -1,9 +1,8 @@
# `TypeFoldable` and `TypeFolder`
How is this `subst` query actually implemented? As you can imagine, we might want to do
substitutions on a lot of different things. For example, we might want to do a substitution directly
on a type like we did with `Vec` above. But we might also have a more complex type with other types
nested inside that also need substitutions.
In the previous chapter we discussed instantiating binders. This must involves looking at everything inside of a `Early/Binder`
to find any usages of the bound vars in order to replace them. Binders can wrap an arbitrary rust type `T` not just a `Ty` so
how do we implement the `instantiate` methods on the `Early/Binder` types.
The answer is a couple of traits:
[`TypeFoldable`](https://doc.rust-lang.org/nightly/nightly-rustc/rustc_middle/ty/fold/trait.TypeFoldable.html)

View File

@ -10,7 +10,8 @@ The `ty` module defines how the Rust compiler represents types internally. It al
When we talk about how rustc represents types, we usually refer to a type called `Ty` . There are
quite a few modules and types for `Ty` in the compiler ([Ty documentation][ty]).
[ty]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_middle/ty/index.html
[ty]: https://doc.rust-lang.org/nightly/nightly-rustc/ru
]stc_middle/ty/index.html
The specific `Ty` we are referring to is [`rustc_middle::ty::Ty`][ty_ty] (and not
[`rustc_hir::Ty`][hir_ty]). The distinction is important, so we will discuss it first before going
@ -125,7 +126,7 @@ You can ignore `Interned` in general; you will basically never access it explici
We always hide them within `Ty` and skip over it via `Deref` impls or methods.
`TyKind` is a big enum
with variants to represent many different Rust types
(e.g. primitives, references, abstract data types, generics, lifetimes, etc).
(e.g. primitives, references, algebraic data types, generics, lifetimes, etc).
`WithCachedTypeInfo` has a few cached values like `flags` and `outer_exclusive_binder`. They
are convenient hacks for efficiency and summarize information about the type that we may want to
know, but they dont come into the picture as much here. Finally, [`Interned`](./memory.md) allows
@ -276,54 +277,6 @@ In particular, since they are so common, the `Ty` and `TyCtxt` types are importe
types are often referenced with an explicit `ty::` prefix (e.g. `ty::TraitRef<'tcx>`). But some
modules choose to import a larger or smaller set of names explicitly.
## ADTs Representation
Let's consider the example of a type like `MyStruct<u32>`, where `MyStruct` is defined like so:
```rust,ignore
struct MyStruct<T> { x: u8, y: T }
```
The type `MyStruct<u32>` would be an instance of `TyKind::Adt`:
```rust,ignore
Adt(&'tcx AdtDef, GenericArgs<'tcx>)
// ------------ ---------------
// (1) (2)
//
// (1) represents the `MyStruct` part
// (2) represents the `<u32>`, or "substitutions" / generic arguments
```
There are two parts:
- The [`AdtDef`][adtdef] references the struct/enum/union but without the values for its type
parameters. In our example, this is the `MyStruct` part *without* the argument `u32`.
(Note that in the HIR, structs, enums and unions are represented differently, but in `ty::Ty`,
they are all represented using `TyKind::Adt`.)
- The [`GenericArgs`][GenericArgs] is an interned list of values that are to be substituted
for the generic parameters. In our example of `MyStruct<u32>`, we would end up with a list like
`[u32]`. Well dig more into generics and substitutions in a little bit.
[adtdef]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_middle/ty/struct.AdtDef.html
[GenericArgs]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_middle/ty/type.GenericArgs.html
**`AdtDef` and `DefId`**
For every type defined in the source code, there is a unique `DefId` (see [this
chapter](hir.md#identifiers-in-the-hir)). This includes ADTs and generics. In the `MyStruct<T>`
definition we gave above, there are two `DefId`s: one for `MyStruct` and one for `T`. Notice that
the code above does not generate a new `DefId` for `u32` because it is not defined in that code (it
is only referenced).
`AdtDef` is more or less a wrapper around `DefId` with lots of useful helper methods. There is
essentially a one-to-one relationship between `AdtDef` and `DefId`. You can get the `AdtDef` for a
`DefId` with the [`tcx.adt_def(def_id)` query][adtdefq]. `AdtDef`s are all interned, as shown
by the `'tcx` lifetime.
[adtdefq]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_middle/ty/struct.TyCtxt.html#method.adt_def
## Type errors
There is a `TyKind::Error` that is produced when the user makes a type error. The idea is that
@ -362,32 +315,3 @@ a redundant delayed bug.
[terr]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_middle/ty/struct.Ty.html#method.new_error
[terrmsg]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_middle/ty/struct.Ty.html#method.new_error_with_message
## Question: Why not substitute “inside” the `AdtDef`?
Recall that we represent a generic struct with `(AdtDef, args)`. So why bother with this scheme?
Well, the alternate way we could have chosen to represent types would be to always create a new,
fully-substituted form of the `AdtDef` where all the types are already substituted. This seems like
less of a hassle. However, the `(AdtDef, args)` scheme has some advantages over this.
First, `(AdtDef, args)` scheme has an efficiency win:
```rust,ignore
struct MyStruct<T> {
... 100s of fields ...
}
// Want to do: MyStruct<A> ==> MyStruct<B>
```
in an example like this, we can subst from `MyStruct<A>` to `MyStruct<B>` (and so on) very cheaply,
by just replacing the one reference to `A` with `B`. But if we eagerly substituted all the fields,
that could be a lot more work because we might have to go through all of the fields in the `AdtDef`
and update all of their types.
A bit more deeply, this corresponds to structs in Rust being [*nominal* types][nominal] — which
means that they are defined by their *name* (and that their contents are then indexed from the
definition of that name, and not carried along “within” the type itself).
[nominal]: https://en.wikipedia.org/wiki/Nominal_type_system

52
src/ty_module/binders.md Normal file
View File

@ -0,0 +1,52 @@
# `Binder` and Higher ranked regions
Sometimes we define generic parmeters not on an item but as part of a type or a where clauses. As an example the type `for<'a> fn(&'a u32)` or the where clause `for<'a> T: Trait<'a>` both introduce a generic lifetime parameter named `'a`. Currently there is no stable syntax for `for<T>` or `for<const N: usize>` but on nightly the `non_lifetime_binders` feature can be used to write where clauses (but not types) using `for<T>`/`for<const N: usize>`.
The `for` is referred to as a "binder" because it brings new names into scope. In rustc we use the `Binder` type to track where these parameters are introduced and what the parameters are (i.e. how many and whether they the parameter is a type/const/region). A type such as `for<'a> fn(&'a u32)` would be
represented in rustc as:
```
Binder(
fn(&RegionKind::Bound(DebruijnIndex(0), BoundVar(0)) u32) -> (),
&[BoundVariableKind::Region(...)],
)
```
Usages of these parameters is represented by the `RegionKind::Bound` (or `TyKind::Bound`/`ConstKind::Bound` variants). These bound regions/types/consts are composed of two main pieces of data:
- A [DebruijnIndex](../appendix/background.md#what-is-a-de-bruijn-index) to specify which binder we are referring to.
- A [`BoundVar`] which specifies which of the parameters the `Binder` introduces we are referring to.
- We also sometimes store some extra information for diagnostics reasons via the [`BoundTyKind`]/[`BoundRegionKind`] but this is not important for type equality or more generally the semantics of `Ty`. (omitted from the above example)
In debug output (and also informally when talking to eachother) we tend to write these bound variables in the format of `^DebruijnIndex_BoundVar`. The above example would instead be written as `Binder(fn(&'^0_0), &[BoundVariableKind::Region])`. Sometimes when the `DebruijnIndex` is `0` we just omit it and would write `^0`.
Another concrete example, this time a mixture of `for<'a>` in a where clause and a type:
```
where
for<'a> Foo<for<'b> fn(&'a &'b T)>: Trait,
```
This would be represented as
```
Binder(
Foo<Binder(
fn(&'^1_0 &'^0 T/#0),
[BoundVariableKind::Region(...)]
)>: Trait,
[BoundVariableKind::Region(...)]
)
```
Note how the `'^1_0` refers to the `'a` parameter. We use a `DebruijnIndex` of `1` to refer to the binder one level up from the innermost one, and a var of `0` to refer to the first parameter bound which is `'a`. We also use `'^0` to refer to the `'b` parameter, the `DebruijnIndex` is `0` (referring to the innermost binder) so we omit it, leaving only the boundvar of `0` referring to the first parameter bound which is `'b`.
We did not always explicitly track the set of bound vars introduced by each `Binder`, this caused a number of bugs (read: ICEs [#81193](https://github.com/rust-lang/rust/issues/81193), [#79949](https://github.com/rust-lang/rust/issues/79949), [#83017](https://github.com/rust-lang/rust/issues/83017)). By tracking these explicitly we can assert when constructing higher ranked where clauses/types that there are no escaping bound variables or variables from a different binder. See the following example of an invalid type inside of a binder:
```
Binder(
fn(&'^1_0 &'^1 T/#0),
&[BoundVariarbleKind::Region(...)],
)
```
This would cause all kinds of issues as the region `'^1_0` refers to a binder at a higher level than the outtermost binder i.e. it is an escaping bound var. The `'^1` region (also writeable as `'^0_1`) is also ill formed as the binder it refers to does not introduce a second parameter. Modern day rustc will ICE when constructing this binder due to both of those regions, in the past we would have simply allowed this to work and then ran into issues in other parts of the codebase.
[`Binder`]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_middle/ty/struct.Binder.html
[`BoundVar]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_middle/ty/struct.BoundVar.html
[`BoundRegionKind`]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_middle/ty/enum.BoundRegionKind.html
[`BoundTyKind`]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_middle/ty/enum.BoundTyKind.html

View File

@ -0,0 +1,76 @@
# `EarlyBinder` and instantiating parameters
Given an item that introduces a generic parameter `T`, whenever we refer to types inside of `foo` (i.e. the return type or argument types) from outside of `foo` we must take care to handle the generic parameters defined on `foo`. As an example:
```rust,ignore
fn foo<T, U>(a: T, _b: U) -> T { a }
fn main() {
let c = foo::<i32, u128>(1, 2);
}
```
When type checking `main` we cannot just naively look at the return type of `foo` and assign the type `T` to the variable `a`, after all the function `main` does not define any generic parameters, `T` is completely meaningless in this context. More generally whenever an item introduces (binds) generic parameters, when accessing types inside the item from outside, the generic parameters must be instantiated with values from the outer item.
In rustc we track this via the [`EarlyBinder`] type, the return type of `foo` is represented as an `EarlyBinder<Ty>` with the only way to acess `Ty` being to provide arguments for any generic parameters `Ty` might be using. This is implemented via the [`EarlyBinder::instantiate`] method which discharges the binder returning the inner value with all the generic parameters replaced by the provided arguments.
To go back to our example, when type checking `main` the return type of `foo` would be represented as `EarlyBinder(T/#0)`. Then, because we called the function with `i32, u128` for the generic arguments, we would call `EarlyBinder::instantiate` on the return type with `[i32, u128]` for the args. This would result in an instantiated return type of `i32` that we can use as the type of the local `c`.
Here are some more examples:
```rust,ignore
fn foo<T>() -> Vec<(u32, T)> { Vec::new() }
fn bar() {
// the return type of `foo` before instantiating it would be:
// `EarlyBinder(Adt(Vec, &[Tup(&[u32, T/#=0])]))`
// we then instantiate the binder with `[u64]` resulting in the type:
// `Adt(Vec, &[Tup(&[u32, u64])])`
let a = foo::<u64>();
}
```
```rust,ignore
struct Foo<A, B> {
x: Vec<A>,
..
}
fn bar(foo: Foo<u32, f32>) {
// the type of `foo`'s `x` field before instantiating it would be:
// `EarlyBinder(Vec<A/#0>)`
// we then instantiate the binder with `[u32, f32]` as those are the
// generic arguments to the `Foo` struct. This results in a type of:
// `Vec<u32>`
let y = foo.x;
}
```
In the compiler the `instantiate` call for this is done in [`FieldDef::ty`] ([src][field_def_ty_src]), at some point during type checking `bar` we will wind up calling `FieldDef::ty(x, &[u32, f32])` in order to obtain the type of `foo.x`.
**Note on indices:** It is possible for the indices in `Param` to not match with what the `EarlyBinder` binds. For
example, the index could be out of bounds or it could be the index of a lifetime when we were expecting a type.
These sorts of errors would be caught earlier in the compiler when translating from a `rustc_hir::Ty` to a `ty::Ty`.
If they occur later, that is a compiler bug.
[`FieldDef::ty`]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_middle/ty/struct.FieldDef.html#method.ty
[field_def_ty_src]: https://github.com/rust-lang/rust/blob/44d679b9021f03a79133021b94e6d23e9b55b3ab/compiler/rustc_middle/src/ty/mod.rs#L1421-L1426
[`EarlyBinder`]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_middle/ty/struct.EarlyBinder.html
[`EarlyBinder::instantiate`]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_middle/ty/struct.EarlyBinder.html#method.instantiate
---
As mentioned previously when _outside_ of an item, it is important to instantiate the `EarlyBinder` with generic arguments before accessing the value inside, but the setup for when we are conceptually inside of the binder already is a bit different.
For example:
```rust
impl<T> Trait for Vec<T> {
fn foo(&self, b: Self) {}
}
```
When constructing a `Ty` to represent the `b` parameter's type we need to get the type of `Self` on the impl that we are inside. This can be acquired by calling the [`type_of`] query with the `impl`'s `DefId`, however, this will return a `EarlyBinder<Ty>` as the impl block binds generic parameters that may have to be discharged if we are outside of the impl.
The `EarlyBinder` type provides an [`instantiate_identity`] function for discharging the binder when you are "already inside of it". Conceptually this discharges the binder by instantiating it with placeholders in the root universe (we will talk about what this means in the next few chapters). In practice though it simply returns the inner value with no modification taking place.
[`type_of`]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_middle/ty/context/struct.TyCtxt.html#method.type_of
[`instantiate_identity`]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_middle/ty/struct.EarlyBinder.html#method.instantiate_identity

View File

@ -0,0 +1,126 @@
# ADTs and Generic Arguments
## ADTs Representation
Let's consider the example of a type like `MyStruct<u32>`, where `MyStruct` is defined like so:
```rust,ignore
struct MyStruct<T> { x: u8, y: T }
```
The type `MyStruct<u32>` would be an instance of `TyKind::Adt`:
```rust,ignore
Adt(&'tcx AdtDef, GenericArgs<'tcx>)
// ------------ ---------------
// (1) (2)
//
// (1) represents the `MyStruct` part
// (2) represents the `<u32>`, or "substitutions" / generic arguments
```
There are two parts:
- The [`AdtDef`][adtdef] references the struct/enum/union but without the values for its type
parameters. In our example, this is the `MyStruct` part *without* the argument `u32`.
(Note that in the HIR, structs, enums and unions are represented differently, but in `ty::Ty`,
they are all represented using `TyKind::Adt`.)
- The [`GenericArgs`][GenericArgs] is a list of values that are to be substituted
for the generic parameters. In our example of `MyStruct<u32>`, we would end up with a list like
`[u32]`. Well dig more into generics and substitutions in a little bit.
[adtdef]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_middle/ty/struct.AdtDef.html
[GenericArgs]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_middle/ty/type.GenericArgs.html
**`AdtDef` and `DefId`**
For every type defined in the source code, there is a unique `DefId` (see [this
chapter](hir.md#identifiers-in-the-hir)). This includes ADTs and generics. In the `MyStruct<T>`
definition we gave above, there are two `DefId`s: one for `MyStruct` and one for `T`. Notice that
the code above does not generate a new `DefId` for `u32` because it is not defined in that code (it
is only referenced).
`AdtDef` is more or less a wrapper around `DefId` with lots of useful helper methods. There is
essentially a one-to-one relationship between `AdtDef` and `DefId`. You can get the `AdtDef` for a
`DefId` with the [`tcx.adt_def(def_id)` query][adtdefq]. `AdtDef`s are all interned, as shown
by the `'tcx` lifetime.
[adtdefq]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_middle/ty/struct.TyCtxt.html#method.adt_def
## Question: Why not substitute “inside” the `AdtDef`?
Recall that we represent a generic struct with `(AdtDef, args)`. So why bother with this scheme?
Well, the alternate way we could have chosen to represent types would be to always create a new,
fully-substituted form of the `AdtDef` where all the types are already substituted. This seems like
less of a hassle. However, the `(AdtDef, args)` scheme has some advantages over this.
First, `(AdtDef, args)` scheme has an efficiency win:
```rust,ignore
struct MyStruct<T> {
... 100s of fields ...
}
// Want to do: MyStruct<A> ==> MyStruct<B>
```
in an example like this, we can subst from `MyStruct<A>` to `MyStruct<B>` (and so on) very cheaply,
by just replacing the one reference to `A` with `B`. But if we eagerly substituted all the fields,
that could be a lot more work because we might have to go through all of the fields in the `AdtDef`
and update all of their types.
A bit more deeply, this corresponds to structs in Rust being [*nominal* types][nominal] — which
means that they are defined by their *name* (and that their contents are then indexed from the
definition of that name, and not carried along “within” the type itself).
[nominal]: https://en.wikipedia.org/wiki/Nominal_type_system
## The `GenericArgs` type
Given a generic type `MyType<A, B, …>`, we have to store the list of generic arguments for `MyType`.
In rustc this is done using [GenericArgs]. `GenericArgs` is a thin pointer to a slice of [`GenericArg`] representing a list of generic arguments for a generic item. For example, given a `struct HashMap<K, V>` with two type parameters, `K` and `V`, the `GenericArgs` used to represent the type `HashMap<i32, u32>` would be represented by `&'tcx [tcx.types.i32, tcx.types.u32]`.
`GenericArg` is conceptually an `enum` with three variants, one for type arguments, one for const arguments and one for lifetime arguments.
In practice that is actually represented by [`GenericArgKind`] and [`GenericArg`] is a more space efficient version that has a method to
turn it into a `GenericArgKind`.
The actual `GenericArg` struct stores the type, lifetime or const as an interned pointer with the discriminant stored in the lower 2 bits.
Unless you are working with the `GenericArgs` implementation specifically, you should generally not have to deal with `GenericArg` and instead
make use of the safe [`GenericArgKind`](#genericargkind) abstraction obtainable via the `GenericArg::unpack()` method.
In some cases you may have to construct a `GenericArg`, this can be done via `Ty/Const/Region::into()` or `GenericArgKind::pack`.
```rust,ignore
// An example of unpacking and packing a generic argument.
fn deal_with_generic_arg<'tcx>(generic_arg: GenericArg<'tcx>) -> GenericArg<'tcx> {
// Unpack a raw `GenericArg` to deal with it safely.
let new_generic_arg: GenericArgKind<'tcx> = match generic_arg.unpack() {
GenericArgKind::Type(ty) => { /* ... */ }
GenericArgKind::Lifetime(lt) => { /* ... */ }
GenericArgKind::Const(ct) => { /* ... */ }
};
// Pack the `GenericArgKind` to store it in a generic args list.
new_generic_arg.pack()
}
```
[list]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_middle/ty/struct.List.html
[`GenericArg`]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_middle/ty/struct.GenericArg.html
[`GenericArgKind`]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_middle/ty/enum.GenericArgKind.html
[GenericArgs]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_middle/ty/type.GenericArgs.html
So pulling it all together:
```rust,ignore
struct MyStruct<T>(T);
type Foo = MyStruct<u32>
```
For the `MyStruct<U>` written in the `Foo` type alias, we would represent it in the following way:
- There would be an `AdtDef` (and corresponding `DefId`) for `MyStruct`.
- There would be a `GenericArgs` containing the list `[GenericArgKind::Type(Ty(u32))]`
- This is one `TyKind::Adt` containing the `AdtDef` of `MyStruct` with the `GenericArgs` above.

View File

@ -0,0 +1,142 @@
# Instantiating `Binder`s
Much like [`EarlyBinder`], when accessing the inside of a [`Binder`] we must first discharge it by replacing the bound vars with some other value. This is for much the same reason as with `EarlyBinder`, types referencing parameters introduced by the `Binder` do not make any sense outside of that binder, for example:
```rust,ignore
fn foo<'a>(a: &'a u32) -> &'a u32 {
a
}
fn bar<T>(a: fn(&u32) -> T) -> T {
a(&10)
}
fn main() {
let higher_ranked_fn_ptr = foo as for<'a> fn(&'a u32) -> &'a u32;
let references_bound_vars = bar(higher_ranked_fn_ptr);
}
```
In this example we are providing an argument of type `for<'a> fn(&'^0 u32) -> &'^0 u32` to `bar`, we do not want to allow `T` to be inferred to the type `&'^0 u32` as it would be rather nonsensical (and likely unsound if we did not happen to ICE, `main` has no idea what `'a` is so how would the borrow checker handle a borrow with lifetime `'a`).
Unlike `EarlyBinder` we do not instantiate `Binder` with some concrete set of arguments from the user, i.e. `['b, 'static]` as arguments to a `for<'a1, 'a2> fn(&'a1 u32, &'a2 u32)`. Instead we always instantiate the binder with inference variables of placeholders.
## Instantiating with inference variables
We instantiate binders with inference variables when we are trying to infer a possible instantiation of the binder, i.e. calling higher ranked function pointers or attempting to use a higher ranked where clause to prove some bound (non exhaustive list). For example, given the `higher_ranked_fn_ptr` from the example above, if we were to call it with `&10_u32` we would:
- Instantaite the binder with infer vars yielding a signature of `fn(&'?0 u32) -> &'?0 u32)`
- Equate the type of the provided argument `&10_u32` (&'static u32) with the type in the signature, `&'?0 u32`, inferring `'?0 = 'static`
- The provided arguments were correct as we were successfully able to unify the types of the provided arguments with the types of the arguments in fn ptr signature
As another example of instantiating with infer vars, given some `where for<'a> T: Trait<'a>`, if we were attempting to prove that `T: Trait<'static>` holds we would:
- Instantiate the binder with infer vars yielding a where clause of `T: Trait<'?0>`
- Equate the goal of `T: Trait<'static>` with the instantiated where clause, inferring `'?0 = 'static`
- The goal holds because we were successfully able to unify `T: Trait<'static>` with `T: Trait<'?0>`
Instantiating binders with inference variables can be accomplished by using the [`instantiate_binder_with_fresh_vars`] method on [`InferCtxt`]. Binders should be instantiated with infer vars when we only care about one specific instantiation of the binder, if instead we wish to reason about all possible instantiations of the binder then placeholders should be used instead.
## Instantiating with placeholders
Placeholders are very similar to `Ty/ConstKind::Param`/`ReEarlyParam`, they represent some unknown type that is only equal to itself. `Ty`/`Const` and `Region` all have a `Placeholder` variant that is comprised of a `Universe` and a `BoundVar`.
The `Universe` tracks which binder the placeholder originated from, and the `BoundVar` tracks which parameter on said binder that this placeholder corresponds to. Equality of placeholders is determined solely by whether the universes are equal and the `BoundVar`s are equal. See the [chapter on Placeholders and Universes][ch_placeholders_universes] for more information.
When talking with other rustc devs or seeing `Debug` formatted `Ty`/`Const`/`Region`s, `Placeholder` will often be written as `'!UNIVERSE_IDX`. For example given some type `for<'a> fn(&'a u32, for<'b> fn(&'b &'a u32))`, after instantiating both binders (assuming the `Universe` in the current `InferCtxt` was `U0` beforehand), the type of `&'b &'a u32` would be represented as `&'!2_0 &!1_0 u32`.
When the universe of the placeholder is `0`, it will be entirely omitted from the debug output, i.e. `!0_2` would be printed as `!2`. This rarely happens in practice though as we increase the universe in the `InferCtxt` when instantiating a binder with placeholders so usually the lowest universe placeholders encounterable are ones in `U1`.
`Binder`s can be instantiated with placeholders via the [`enter_forall`] method on `InferCtxt`. It should be used whenever the compiler should care about any possible instantiation of the binder instead of one concrete instantiation.
Note: in the original example of this chapter it was mentioned that we should not infer a local variable to have type `&'^0 u32`. This code is prevented from compiling via universes (as explained in the linked chapter)
### Why have both `RePlaceholder` and `ReBound`?
You may be wondering why we have both of these variants, afterall the data stored in `Placeholder` is effectively equivalent to that of `ReBound`: something to track which binder, and an index to track which parameter the `Binder` introduced.
The main reason for this is that `Bound` is a more syntactic representation of bound variables wheras `Placeholder` is a more semantic representation. As a concrete example:
```rust
impl<'a> Other<'a> for &'a u32 { }
impl<T> Trait for T
where
for<'a> T: Other<'a>,
{ ... }
impl<T> Bar for T
where
for<'a> &'a T: Trait
{ ... }
```
Given these trait implementations `u32: Bar` should _not_ hold. `&'a u32` only implements `Other<'a>` when the lifetime of the borrow and the lifetime on the trait are equal. However if we only used `ReBound` and did not have placeholders it may be easy to accidentally believe that trait bound does hold. To explain this let's walk through an example of trying to prove `u32: Bar` in a world where rustc did not have placeholders:
- We start by trying to prove `u32: Bar`
- We find the `impl<T> Bar for T` impl, we would wind up instantiating the `EarlyBinder` with `u32` (note: this is not _quite_ accurate as we first instantiate the binder with an inference variable that we then infer to be `u32` but that distinction is not super important here)
- There is a where clause `for<'a> &'^0 T: Trait` on the impl, as we instantiated the early binder with `u32` we actually have to prove `for<'a> &'^0 u32: Trait`
- We find the `impl<T> Trait for T` impl, we would wind up instantiating the `EarlyBinder` with `&'^0 u32`
- There is a where clause `for<'a> T: Other<'^0>`, as we instantiated the early binder with `&'^0 u32` we actually have to prove `for<'a> &'^0 u32: Other<'^0>`
- We find the `impl<'a> Other<'a> for &'a u32` and this impl is enoguh to prove the the bound as the lifetime on the borrow and on the trait are both `'^0`
This end result is incorrect as we had two separate binders introducing their own generic parameters, the trait bound should have ended up as something like `for<'a1, 'a2> &'^1 u32: Other<'^0>` which is _not_ satisfied by the `impl<'a> Other<'a> for &'a u32`.
While in theory we could make this work it would be quite involved and more complex than the current setup, we would have to:
- "rewrite" bound variables to have a higher `DebruijnIndex` whenever instantiating a `Binder`/`EarlyBinder` with a `Bound` ty/const/region
- When inferring an inference variable to a bound var, if that bound var is from a binder enterred after creating the infer var, we would have to lower the `DebruijnIndex` of the var.
- Separately track what binder an inference variable was created inside of, also what the innermost binder it can name parameters from (currently we only have to track the latter)
- When resolving inference variables rewrite any bound variables according to the current binder depth of the infcx
- Maybe more (while writing this list items kept getting added so it seems naive to think this is exhaustive)
Fundamentally all of this complexity is because `Bound` ty/const/regions have a different representation for a given parameter on a `Binder` depending on how many other `Binder`s there are between the binder introducing the parameter, and its usage. For example given the following code:
```rust
fn foo<T>()
where
for<'a> T: Trait<'a, for<'b> fn(&'b T, &'a u32)>
{ ... }
```
That where clause would be written as:
`for<'a> T: Trait<'^0, for<'b> fn(&'^0 T, &'^1_0 u32)>`
Despite there being two references to the `'a` parameter they are both represented differently: `^0` and `^1_0`, due to the fact that the latter usage is nested under a second `Binder` for the inner function pointer type.
This is in contrast to `Placeholder` ty/const/regions which do not have this limitation due to the fact that `Universe`s are specific to the current `InferCtxt` not the usage site of the parameter.
It is trivially possible to instantiate `EarlyBinder`s and unify inference variables with existing `Placeholder`s as no matter what context the `Placeholder` is in, it will have the same representation. As an example if we were to instantiate the binder on the higher ranked where clause from above, it would be represented like so:
`T: Trait<'!1_0, for<'b> fn(&'^0 T, &'!1_0 u32)>`
the `RePlaceholder` representation for both usages of `'a` are the same despite one being underneath another `Binder`.
If we were to then instantiate the binder on the function pointer we would get a type such as:
`fn(&'!2_0 T, ^'!1_0 u32)`
the `RePlaceholder` for the `'b` parameter is in a higher universe to track the fact that its binder was instantiated after the binder for `'a`.
## Instantiating with `ReLateParam`
As discussed in a previous chapter, `RegionKind` has two variants for representing generic parameters, `ReLateParam` and `ReEarlyParam`. `ReLateParam` is conceptually a `Placeholder` that is always in the root universe (`U0`). It is used when instantiating late bound parameters on functions/closures. It's actual representation is relatively different from both `ReEarlyParam` and `RePlaceholder`:
- A `DefId` for the item that introduced the late bound generic parameter
- A [`BoundRegionKind`] which either specifies the `DefId` of the generic parameter and its name (via a `Symbol`), or that this placeholder is representing the anonymous lifetime of a `Fn`/`FnMut` closure's self borrow. There is also a variant for `BrAnon` but this is not used for `ReLateParam`.
For example, given the following code:
```rust,ignore
impl Trait for Whatever {
fn foo<'a>(a: &'a u32) -> &'a u32 {
let b: &'a u32 = a;
b
}
}
```
the lifetime `'a` in the type `&'a u32` in the function body would be represented as:
```
ReLateParam(
{impl#0}::foo,
BoundRegionKind::BrNamed({impl#0}::foo::'a, "'a")
)
```
In this specific case of referencing late bound generic parameters of a function from inside the body this is done implicitly during `hir_ty_lowering` rather than explicitly when instantiating a `Binder` somewhere. In some cases however, we do explicitly instantiate a `Binder` with `ReLateParam`s.
Generally whenever we have a `Binder` for late bound parameters on a function/closure and we are conceptually inside of the binder already, we use [`liberate_late_bound_regions`] to instantiate it with `ReLateParam`s. That makes this operation the `Binder` equivalent to `EarlyBinder`'s `instantiate_identity`.
As a concrete example, accessing the signature of a function we are type checking will be represented as `EarlyBinder<Binder<FnSig>>`. As we are already "inside" of these binders, we would call `instantiate_identity` followed by `liberate_late_bound_regions`.
[`liberate_late_bound_regions`]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_middle/ty/context/struct.TyCtxt.html#method.liberate_late_bound_regions
[`BoundRegionKind`]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_middle/ty/enum.BoundRegionKind.html
[`enter_forall`]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_trait_selection/infer/struct.InferCtxt.html#method.enter_forall
[ch_placeholders_universes]: ../borrow_check/region_inference/placeholders_and_universes.md
[`instantiate_binder_with_fresh_vars`]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_trait_selection/infer/struct.InferCtxt.html#method.instantiate_binder_with_fresh_vars
[`InferCtxt`]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_trait_selection/infer/struct.InferCtxt.html
[`EarlyBinder`]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_middle/ty/struct.EarlyBinder.html
[`Binder`]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_middle/ty/type.Binder.html

View File

@ -0,0 +1,99 @@
# Parameter `Ty`/`Const`/`Region`s
When inside of generic items, types can be written that use in scope generic parameters, for example `fn foo<'a, T>(_: &'a Vec<T>)`. In this specific case
the `&'a Vec<T>` type would be represented internally as:
```
TyKind::Ref(
RegionKind::LateParam(DefId(foo), DefId(foo::'a), "'a"),
TyKind::Adt(Vec, &[TyKind::Param("T", 0)])
)
```
There are three separate ways we represent usages of generic parameters:
- [`TyKind::Param`]/[`ConstKind::Param`]/[`RegionKind::EarlyParam`] for early bound generic parameters (note: all type and const parameters are considered early bound, see the [chapter on early vs late bound parameters][ch_early_late_bound] for more information)
- [`TyKind::Bound`]/[`ConstKind::Bound`]/[`RegionKind::Bound`] for references to parameters introduced via higher ranked bounds or higher ranked types i.e. `for<'a> fn(&'a u32)` or `for<'a> T: Trait<'a>`. This will be discussed in the [chapter on `Binder`s][ch_binders].
- [`RegionKind::LateParam`] for late bound lifetime parameters, `LateParam` will be discussed in the [chapter on instantiating `Binder`s][ch_instantiating_binders].
This chapter will only cover `TyKind::Param` `ConstKind::Param` and `RegionKind::EarlyParam`.
## Ty/Const Parameters
As `TyKind::Param` and `ConstKind::Param` are implemented identically this section will only refer to `TyKind::Param` for simplicity. However
you should keep in mind that everything here also is true of `ConstKind::Param`
Each `TyKind::Param` contains two things: the name of the parameter and an index.
See the following concrete example of a usage of `TyKind::Param`:
```rust,ignore
struct Foo<T>(Vec<T>);
```
The `Vec<T>` type is represented as `TyKind::Adt(Vec, &[GenericArgKind::Type(Param("T", 0))])`.
The name is somewhat self explanatory, it's the name of the type parameter. The index of the type parameter is an integer indicating
its order in the list of generic parameters in scope (note: this includes parameters defined on items on outter scopes than the item the parameter is defined on). Consider the following examples:
```rust,ignore
struct Foo<A, B> {
// A would have index 0
// B would have index 1
.. // some fields
}
impl<X, Y> Foo<X, Y> {
fn method<Z>() {
// inside here, X, Y and Z are all in scope
// X has index 0
// Y has index 1
// Z has index 2
}
}
```
Concretely given the `ty::Generics` for the item the parameter is defined on, if the index is `10` then starting from the root `parent`, it will be the eleventh parameter to be introduced.
The index fully defines the `Ty` and is the only part of `TyKind::Param` that matters for reasoning about the code we are compiling.
Generally we do not care what the name is and only use the index is included for diagnostics and debug logs as otherwise it would be
incredibly difficult to understand the output, i.e. `Vec<Param(0)>: Sized` vs `Vec<T>: Sized`. In debug output, parameter types are
often printed out as `{name}/#{index}`, for example in the function `foo` if we were to debug print `Vec<T>` it would be written as `Vec<T/#0>`.
An alternative representation would be to only have the name, however using an index is more efficient as it means we can index into `GenericArgs` when instantiating generic parameters with some arguments. We would otherwise have to store `GenericArgs` as a `HashMap<Symbol, GenericArg>` and do a hashmap lookup everytime we used a generic item.
In theory an index would also allow for having multiple distinct parameters that use the same name, e.g.
`impl<A> Foo<A> { fn bar<A>() { .. } }`.
The rules against shadowing make this difficult but those language rules could change in the future.
### Lifetime parameters
In contrast to `Ty`/`Const`'s `Param` singular `Param` variant, lifetimes have two variants for representing region parameters: [`RegionKind::EarlyParam`] and [`RegionKind::LateParam`]. The reason for this is due to function's distinguishing between [early and late bound parameters](../early-late-bound-params/early-late-bound-summary.md) which is discussed in an earlier chapter (see link).
`RegionKind::EarlyParam` is structured identically to `Ty/Const`'s `Param` variant, it is simply a `u32` index and a `Symbol`. For lifetime parameters defined on non-function items we always use `ReEarlyParam`. For functions we use `ReEarlyParam` for any early bound parameters and `ReLateParam` for any late bound parameters. Note that just like `Ty` and `Const` params we often debug format them as `'SYMBOL/#INDEX`, see for example:
```rust,ignore
// This function would have its signature represented as:
//
// ```
// fn(
// T/#2,
// Ref('a/#0, Ref(ReLateParam(...), u32))
// ) -> Ref(ReLateParam(...), u32)
// ```
fn foo<'a, 'b, T: 'a>(one: T, two: &'a &'b u32) -> &'b u32 {
...
}
```
`RegionKind::LateParam` will be discussed more in the chapter on [instantiating binders][ch_instantiating_binders].
[ch_early_late_bound]: ../early-late-bound-params/early-late-bound-summary.md
[ch_binders]: ./binders.md
[ch_instantiating_binders]: ./instantiating_binders.md
[`BoundRegionKind`]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_middle/ty/enum.BoundRegionKind.html
[`RegionKind::EarlyParam`]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_middle/ty/type.RegionKind.html#variant.ReEarlyParam
[`RegionKind::LateParam`]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_middle/ty/type.RegionKind.html#variant.ReLateParam
[`ConstKind::Param`]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_middle/ty/type.ConstKind.html#variant.Param
[`TyKind::Param`]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_middle/ty/type.TyKind.html#variant.Param
[`TyKind::Bound`]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_middle/ty/type.TyKind.html#variant.Bound
[`ConstKind::Bound`]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_middle/ty/type.ConstKind.html#variant.Bound
[`RegionKind::Bound`]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_middle/ty/type.RegionKind.html#variant.ReBound