Skip to content

Commit

Permalink
update internal terminology: Substs -> GenericArgs
Browse files Browse the repository at this point in the history
  • Loading branch information
chenyukang committed Aug 20, 2023
1 parent 973e4f3 commit 412494e
Show file tree
Hide file tree
Showing 7 changed files with 49 additions and 50 deletions.
2 changes: 1 addition & 1 deletion src/appendix/glossary.md
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ Term | Meaning
<span id="double-ptr">double pointer</span> &nbsp; | A pointer with additional metadata. See "fat pointer" for more.
<span id="drop-glue">drop glue</span> &nbsp; | (internal) compiler-generated instructions that handle calling the destructors (`Drop`) for data types.
<span id="dst">DST</span> &nbsp; | Short for Dynamically-Sized Type, this is a type for which the compiler cannot statically know the size in memory (e.g. `str` or `[u8]`). Such types don't implement `Sized` and cannot be allocated on the stack. They can only occur as the last field in a struct. They can only be used behind a pointer (e.g. `&str` or `&[u8]`).
<span id="ebl">early-bound lifetime</span> &nbsp; | A lifetime region that is substituted at its definition site. Bound in an item's `Generics` and substituted using a `Substs`. Contrast with **late-bound lifetime**. ([see more](https://doc.rust-lang.org/nightly/nightly-rustc/rustc_type_ir/sty/enum.RegionKind.html#bound-regions))
<span id="ebl">early-bound lifetime</span> &nbsp; | A lifetime region that is substituted at its definition site. Bound in an item's `Generics` and substituted using a `GenericArgs`. Contrast with **late-bound lifetime**. ([see more](https://doc.rust-lang.org/nightly/nightly-rustc/rustc_type_ir/sty/enum.RegionKind.html#bound-regions))
<span id="empty-type">empty type</span> &nbsp; | see "uninhabited type".
<span id="fat-ptr">fat pointer</span> &nbsp; | A two word value carrying the address of some value, along with some further information necessary to put the value to use. Rust includes two kinds of "fat pointers": references to slices, and trait objects. A reference to a slice carries the starting address of the slice and its length. A trait object carries a value's address and a pointer to the trait's implementation appropriate to that value. "Fat pointers" are also known as "wide pointers", and "double pointers".
<span id="free-var">free variable</span> &nbsp; | A "free variable" is one that is not bound within an expression or term; see [the background chapter for more](./background.md#free-vs-bound)
Expand Down
22 changes: 11 additions & 11 deletions src/early-late-bound.md
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
# Early and Late Bound Parameter Definitions

Understanding this page likely requires a rudimentary understanding of higher ranked
trait bounds/`for<'a>`and also what types such as `dyn for<'a> Trait<'a>` and
`for<'a> fn(&'a u32)` mean. Reading [the nomincon chapter](https://doc.rust-lang.org/nomicon/hrtb.html)
trait bounds/`for<'a>`and also what types such as `dyn for<'a> Trait<'a>` and
`for<'a> fn(&'a u32)` mean. Reading [the nomincon chapter](https://doc.rust-lang.org/nomicon/hrtb.html)
on HRTB may be useful for understanding this syntax. The meaning of `for<'a> fn(&'a u32)`
is incredibly similar to the meaning of `T: for<'a> Trait<'a>`.

Expand All @@ -21,7 +21,7 @@ fn foo<'a>(_: &'a u32) {}

fn main() {
let b = foo;
// ^ `b` has type `FnDef(foo, [])` (no substs because `'a` is late bound)
// ^ `b` has type `FnDef(foo, [])` (no args because `'a` is late bound)
assert!(std::mem::size_of_val(&b) == 0);
}
```
Expand All @@ -38,7 +38,7 @@ fn main() {
}
```

Because late bound parameters are not part of the `FnDef`'s substs this allows us to prove trait
Because late bound parameters are not part of the `FnDef`'s args this allows us to prove trait
bounds such as `F: for<'a> Fn(&'a u32)` where `F` is `foo`'s `FnDef`. e.g.
```rust
fn foo_early<'a, T: Trait<'a>>(_: &'a u32, _: T) {}
Expand All @@ -52,7 +52,7 @@ fn main() {
// of the borrow in the function argument must be the same as the lifetime
// on the `FnDef`.
accepts_hr_func(foo_early);

// works, the substituted bound is `for<'a> FnDef: Fn(&'a u32, u32)`
accepts_hr_func(foo_late);
}
Expand Down Expand Up @@ -85,7 +85,7 @@ making `generics_of` behave this way.
## What parameters are currently late bound

Below are the current requirements for determining if a generic parameter is late bound. It is worth
keeping in mind that these are not necessarily set in stone and it is almost certainly possible to
keeping in mind that these are not necessarily set in stone and it is almost certainly possible to
be more flexible.

### Must be a lifetime parameter
Expand Down Expand Up @@ -161,7 +161,7 @@ this is simpler than the rules for checking impl headers constrain all the param
We only have to ensure that all late bound parameters appear at least once in the function argument
types outside of an alias (e.g. an associated type).

The requirement that they not indirectly be in the substs of an alias for it to count is the
The requirement that they not indirectly be in the args of an alias for it to count is the
same as why the follow code is forbidden:
```rust
impl<T: Trait> OtherTrait for <T as Trait>::Assoc { type Assoc = T }
Expand All @@ -174,13 +174,13 @@ same is true of the builtin `Fn*` impls.

It is generally considered desirable for more parameters to be late bound as it makes
the builtin `Fn*` impls more flexible. Right now many of the requirements for making
a parameter late bound are overly restrictive as they are tied to what we can currently
a parameter late bound are overly restrictive as they are tied to what we can currently
(or can ever) do with fn ptrs.

It would be theoretically possible to support late bound params in `where`-clauses in the
language by introducing implication types which would allow us to express types such as:
It would be theoretically possible to support late bound params in `where`-clauses in the
language by introducing implication types which would allow us to express types such as:
`for<'a, 'b: 'a> fn(Inv<&'a u32>, Inv<&'b u32>)` which would ensure `'b: 'a` is upheld when
calling the function pointer.
calling the function pointer.

It would also be theoretically possible to support it by making the coercion to a fn ptr
instantiate the parameter with an infer var while still allowing the FnDef to not have the
Expand Down
41 changes: 20 additions & 21 deletions src/generics.md
Original file line number Diff line number Diff line change
@@ -1,24 +1,23 @@
# Generics and substitutions
# 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 [SubstsRef] (“substs” = “substitutions”).
Conceptually, you can think of `SubstsRef` as a list of types that are to be substituted for the
generic type parameters of the ADT.
In rustc this is done using [GenericArgsRef].
Conceptually, you can think of `GenericArgsRef` as a list of types that are to be substituted for the generic type parameters of the ADT.

`SubstsRef` is a type alias of `&'tcx List<GenericArg<'tcx>>` (see [`List` rustdocs][list]).
`GenericArgsRef` 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, `SubstsRef`
indicating what kind of generic the type parameter is (type, lifetime, or const). Thus, `GenericArgsRef`
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/subst/struct.GenericArg.html
[`GenericArgKind`]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_middle/ty/subst/enum.GenericArgKind.html
[SubstsRef]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_middle/ty/subst/type.SubstsRef.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
[GenericArgsRef]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_middle/ty/type.GenericArgsRef.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
Expand All @@ -36,10 +35,10 @@ 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 `SubstsRef` containing the list `[GenericArgKind::Type(Ty(T))]`
- There would be a `GenericArgsRef` 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 `SubstsRef` above.
- This is one `TyKind::Adt` containing the `AdtDef` of `MyStruct` with the `GenericArgsRef` above.

Finally, we will quickly mention the
[`Generics`](https://doc.rust-lang.org/nightly/nightly-rustc/rustc_middle/ty/struct.Generics.html) type. It
Expand Down Expand Up @@ -113,29 +112,29 @@ This example has a few different substitutions:

Let’s 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 `SubstsRef`, we
the index `0` and use it to find the right type substitution: looking at `Foo`'s `GenericArgsRef`, 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
**`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)])`.

**`subst`** How do we actually do the substitutions? There is a function for that too! You use
[`subst`](https://doc.rust-lang.org/nightly/nightly-rustc/rustc_middle/ty/subst/struct.EarlyBinder.html#method.subst) to
replace a `SubstsRef` with another list of types.
**`instantiate`** How do we actually do the substitutions? There is a function for that too! You use
[`instantiate`](https://doc.rust-lang.org/nightly/nightly-rustc/rustc_middle/ty/generic_args/struct.EarlyBinder.html#method.instantiate) to
replace a `GenericArgsRef` with another list of types.

[Here is an example of actually using `subst` in the compiler][substex]. The exact details are not
[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 substitutions (`substs`). Then we call
`type_of` to get a type and call `ty.subst(substs)` to get a new version of `ty` with
the substitutions made.
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.

[substex]: https://github.com/rust-lang/rust/blob/0940040c0486a536be4f8685c7dd9a078f9e87c2/compiler/rustc_hir_analysis/src/astconv/mod.rs#L1231-L1242
[instantiatex]: https://github.com/rust-lang/rust/blob/master/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
Expand Down
10 changes: 5 additions & 5 deletions src/memory.md
Original file line number Diff line number Diff line change
Expand Up @@ -40,16 +40,16 @@ to that buffer is freed and our `'tcx` references would be invalid.
In addition to types, there are a number of other arena-allocated data structures that you can
allocate, and which are found in this module. Here are a few examples:

- [`Substs`][subst], allocated with `mk_substs` – this will intern a slice of types, often used to
specify the values to be substituted for generics (e.g. `HashMap<i32, u32>` would be represented
as a slice `&'tcx [tcx.types.i32, tcx.types.u32]`).
- [`GenericArgs`][subst], allocated with `mk_args` – this will intern a slice of types, often used to
specify the values to be substituted for generics args(e.g. `HashMap<i32, u32>` would be
represented as a slice `&'tcx [tcx.types.i32, tcx.types.u32]`).
- [`TraitRef`], typically passed by value – a **trait reference** consists of a reference to a trait
along with its various type parameters (including `Self`), like `i32: Display` (here, the def-id
would reference the `Display` trait, and the substs would contain `i32`). Note that `def-id` is
would reference the `Display` trait, and the args would contain `i32`). Note that `def-id` is
defined and discussed in depth in the `AdtDef and DefId` section.
- [`Predicate`] defines something the trait system has to prove (see `traits` module).

[subst]: ./generic_arguments.html#subst
[`GenericArgs`]: ./generic_arguments.html#GenericArgs
[`TraitRef`]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_middle/ty/struct.TraitRef.html
[`Predicate`]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_middle/ty/struct.Predicate.html

Expand Down
4 changes: 2 additions & 2 deletions src/return-position-impl-trait-in-trait.md
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ We record `lifetime_mapping`s for the opaque type, described below.
lifetimes into new lifetime parameters local to the opaque. The main
reason we do this is because RPITs need to be able to "reify"[^1] any
captured late-bound arguments, or make them into early-bound ones. This
is so they can be used as substs for the opaque, and later to
is so they can be used as generic args for the opaque, and later to
instantiate hidden types. Since we don't know which lifetimes are early-
or late-bound during AST lowering, we just do this for all lifetimes.

Expand Down Expand Up @@ -355,7 +355,7 @@ error[E0308]: mismatched types

#### Well-formedness checking

We check well-formedness of RPITITs just like regular associated types.
We check well-formedness of RPITITs just like regular associated types.

Since we added lifetime bounds in `predicates_of` that link the
duplicated early-bound lifetimes to their original lifetimes, and we
Expand Down
4 changes: 2 additions & 2 deletions src/rustdoc-internals.md
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ fn clean_lifetime<'tcx>(lifetime: &hir::Lifetime, cx: &mut DocContext<'tcx>) ->
| rbv::ResolvedArg::Free(_, node_id),
) = def
{
if let Some(lt) = cx.substs.get(&node_id).and_then(|p| p.as_lt()).cloned() {
if let Some(lt) = cx.args.get(&node_id).and_then(|p| p.as_lt()).cloned() {
return lt;
}
}
Expand Down Expand Up @@ -109,7 +109,7 @@ Here is the list of passes as of <!-- date-check --> March 2023:
`Go to https://example.com/.` It suggests wrapping the link with angle brackets:
`Go to <https://example.com/>.` to linkify it. This is the code behind the <!--
date-check: may 2022 --> `rustdoc::bare_urls` lint.

- `check_code_block_syntax` validates syntax inside Rust code blocks
(<code>```rust</code>)

Expand Down
16 changes: 8 additions & 8 deletions src/ty.md
Original file line number Diff line number Diff line change
Expand Up @@ -200,11 +200,11 @@ the function calls if one is available in some place (like during type checking)
If no inference context is available at all, then one can be created as described in
[type-inference]. But this is only useful when the involved types (for example, if
they came from a query like `tcx.type_of()`) are actually substituted with fresh
inference variables using [`fresh_substs_for_item`]. This can be used to answer questions
inference variables using [`fresh_args_for_item`]. This can be used to answer questions
like "can `Vec<T>` for any `T` be unified with `Vec<u32>`?".

[type-inference]: ./type-inference.md#creating-an-inference-context
[`fresh_substs_for_item`]: https://doc.rust-lang.org/beta/nightly-rustc/rustc_infer/infer/struct.InferCtxt.html#method.fresh_substs_for_item
[`fresh_args_for_item`]: https://doc.rust-lang.org/beta/nightly-rustc/rustc_infer/infer/struct.InferCtxt.html#method.fresh_args_for_item

## `ty::TyKind` Variants

Expand Down Expand Up @@ -287,7 +287,7 @@ struct MyStruct<T> { x: u32, y: T }
The type `MyStruct<u32>` would be an instance of `TyKind::Adt`:

```rust,ignore
Adt(&'tcx AdtDef, SubstsRef<'tcx>)
Adt(&'tcx AdtDef, GenericArgsRef<'tcx>)
// ------------ ---------------
// (1) (2)
//
Expand All @@ -301,12 +301,12 @@ There are two parts:
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 [`SubstsRef`][substsref] is an interned list of values that are to be substituted for the
- The [`GenericArgsRef`][GenericArgsRef] 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]`.
We’ll 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
[substsref]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_middle/ty/subst/type.SubstsRef.html
[GenericArgsRef]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_middle/ty/subst/type.GenericArgsRef.html

**`AdtDef` and `DefId`**

Expand Down Expand Up @@ -363,13 +363,13 @@ delaying a redundant span bug.

## Question: Why not substitute “inside” the `AdtDef`?

Recall that we represent a generic struct with `(AdtDef, substs)`. So why bother with this scheme?
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, substs)` scheme has some advantages over this.
less of a hassle. However, the `(AdtDef, args)` scheme has some advantages over this.

First, `(AdtDef, substs)` scheme has an efficiency win:
First, `(AdtDef, args)` scheme has an efficiency win:

```rust,ignore
struct MyStruct<T> {
Expand Down

0 comments on commit 412494e

Please sign in to comment.