]>
Commit | Line | Data |
---|---|---|
781aab86 | 1 | # Generics and GenericArgs |
74b04a01 XL |
2 | |
3 | Given a generic type `MyType<A, B, …>`, we may want to swap out the generics `A, B, …` for some | |
4 | other types (possibly other generics or concrete types). We do this a lot while doing type | |
5 | inference, type checking, and trait solving. Conceptually, during these routines, we may find out | |
6 | that one type is equal to another type and want to swap one out for the other and then swap that out | |
7 | for another type and so on until we eventually get some concrete types (or an error). | |
8 | ||
781aab86 FG |
9 | In rustc this is done using [GenericArgsRef]. |
10 | Conceptually, you can think of `GenericArgsRef` as a list of types that are to be substituted for | |
11 | the generic type parameters of the ADT. | |
74b04a01 | 12 | |
781aab86 | 13 | `GenericArgsRef` is a type alias of `&'tcx List<GenericArg<'tcx>>` (see [`List` rustdocs][list]). |
74b04a01 | 14 | [`GenericArg`] is essentially a space-efficient wrapper around [`GenericArgKind`], which is an enum |
781aab86 FG |
15 | indicating what kind of generic the type parameter is (type, lifetime, or const). |
16 | Thus, `GenericArgsRef` is conceptually like a `&'tcx [GenericArgKind<'tcx>]` slice (but it is | |
17 | actually a `List`). | |
74b04a01 | 18 | |
ba9703b0 | 19 | [list]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_middle/ty/struct.List.html |
781aab86 FG |
20 | [`GenericArg`]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_middle/ty/struct.GenericArg.html |
21 | [`GenericArgKind`]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_middle/ty/enum.GenericArgKind.html | |
22 | [GenericArgsRef]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_middle/ty/type.GenericArgsRef.html | |
74b04a01 XL |
23 | |
24 | So why do we use this `List` type instead of making it really a slice? It has the length "inline", | |
25 | so `&List` is only 32 bits. As a consequence, it cannot be "subsliced" (that only works if the | |
26 | length is out of line). | |
27 | ||
28 | This also implies that you can check two `List`s for equality via `==` (which would be not be | |
29 | possible for ordinary slices). This is precisely because they never represent a "sub-list", only the | |
30 | complete `List`, which has been hashed and interned. | |
31 | ||
32 | So pulling it all together, let’s go back to our example above: | |
33 | ||
34 | ```rust,ignore | |
35 | struct MyStruct<T> | |
36 | ``` | |
37 | ||
38 | - There would be an `AdtDef` (and corresponding `DefId`) for `MyStruct`. | |
39 | - There would be a `TyKind::Param` (and corresponding `DefId`) for `T` (more later). | |
781aab86 | 40 | - There would be a `GenericArgsRef` containing the list `[GenericArgKind::Type(Ty(T))]` |
74b04a01 XL |
41 | - The `Ty(T)` here is my shorthand for entire other `ty::Ty` that has `TyKind::Param`, which we |
42 | mentioned in the previous point. | |
781aab86 | 43 | - This is one `TyKind::Adt` containing the `AdtDef` of `MyStruct` with the `GenericArgsRef` above. |
74b04a01 XL |
44 | |
45 | Finally, we will quickly mention the | |
ba9703b0 | 46 | [`Generics`](https://doc.rust-lang.org/nightly/nightly-rustc/rustc_middle/ty/struct.Generics.html) type. It |
74b04a01 XL |
47 | is used to give information about the type parameters of a type. |
48 | ||
49 | ### Unsubstituted Generics | |
50 | ||
51 | So above, recall that in our example the `MyStruct` struct had a generic type `T`. When we are (for | |
52 | example) type checking functions that use `MyStruct`, we will need to be able to refer to this type | |
53 | `T` without actually knowing what it is. In general, this is true inside all generic definitions: we | |
54 | need to be able to work with unknown types. This is done via `TyKind::Param` (which we mentioned in | |
55 | the example above). | |
56 | ||
57 | Each `TyKind::Param` contains two things: the name and the index. In general, the index fully | |
58 | defines the parameter and is used by most of the code. The name is included for debug print-outs. | |
59 | There are two reasons for this. First, the index is convenient, it allows you to include into the | |
60 | list of generic arguments when substituting. Second, the index is more robust. For example, you | |
61 | could in principle have two distinct type parameters that use the same name, e.g. `impl<A> Foo<A> { | |
62 | fn bar<A>() { .. } }`, although the rules against shadowing make this difficult (but those language | |
63 | rules could change in the future). | |
64 | ||
65 | The index of the type parameter is an integer indicating its order in the list of the type | |
66 | parameters. Moreover, we consider the list to include all of the type parameters from outer scopes. | |
67 | Consider the following example: | |
68 | ||
69 | ```rust,ignore | |
70 | struct Foo<A, B> { | |
71 | // A would have index 0 | |
72 | // B would have index 1 | |
73 | ||
74 | .. // some fields | |
75 | } | |
76 | impl<X, Y> Foo<X, Y> { | |
77 | fn method<Z>() { | |
78 | // inside here, X, Y and Z are all in scope | |
79 | // X has index 0 | |
80 | // Y has index 1 | |
81 | // Z has index 2 | |
82 | } | |
83 | } | |
84 | ``` | |
85 | ||
86 | When we are working inside the generic definition, we will use `TyKind::Param` just like any other | |
87 | `TyKind`; it is just a type after all. However, if we want to use the generic type somewhere, then | |
88 | we will need to do substitutions. | |
89 | ||
90 | For example suppose that the `Foo<A, B>` type from the previous example has a field that is a | |
91 | `Vec<A>`. Observe that `Vec` is also a generic type. We want to tell the compiler that the type | |
92 | parameter of `Vec` should be replaced with the `A` type parameter of `Foo<A, B>`. We do that with | |
93 | substitutions: | |
94 | ||
95 | ```rust,ignore | |
96 | struct Foo<A, B> { // Adt(Foo, &[Param(0), Param(1)]) | |
97 | x: Vec<A>, // Adt(Vec, &[Param(0)]) | |
98 | .. | |
99 | } | |
100 | ||
101 | fn bar(foo: Foo<u32, f32>) { // Adt(Foo, &[u32, f32]) | |
102 | let y = foo.x; // Vec<Param(0)> => Vec<u32> | |
103 | } | |
104 | ``` | |
105 | ||
106 | This example has a few different substitutions: | |
107 | ||
108 | - In the definition of `Foo`, in the type of the field `x`, we replace `Vec`'s type parameter with | |
109 | `Param(0)`, the first parameter of `Foo<A, B>`, so that the type of `x` is `Vec<A>`. | |
110 | - In the function `bar`, we specify that we want a `Foo<u32, f32>`. This means that we will | |
111 | substitute `Param(0)` and `Param(1)` with `u32` and `f32`. | |
112 | - In the body of `bar`, we access `foo.x`, which has type `Vec<Param(0)>`, but `Param(0)` has been | |
113 | substituted for `u32`, so `foo.x` has type `Vec<u32>`. | |
114 | ||
115 | Let’s look a bit more closely at that last substitution to see why we use indexes. If we want to | |
116 | find the type of `foo.x`, we can get generic type of `x`, which is `Vec<Param(0)>`. Now we can take | |
781aab86 FG |
117 | the index `0` and use it to find the right type substitution: looking at `Foo`'s `GenericArgsRef`, |
118 | we have the list `[u32, f32]` , since we want to replace index `0`, we take the 0-th index of this | |
74b04a01 XL |
119 | list, which is `u32`. Voila! |
120 | ||
121 | You may have a couple of followup questions… | |
122 | ||
781aab86 | 123 | **`type_of`** How do we get the "generic type of `x`"? You can get the type of pretty much anything |
74b04a01 XL |
124 | with the `tcx.type_of(def_id)` query. In this case, we would pass the `DefId` of the field `x`. |
125 | The `type_of` query always returns the definition with the generics that are in scope of the | |
126 | definition. For example, `tcx.type_of(def_id_of_my_struct)` would return the “self-view” of | |
127 | `MyStruct`: `Adt(Foo, &[Param(0), Param(1)])`. | |
128 | ||
781aab86 FG |
129 | How do we actually do the substitutions? There is a function for that too! You |
130 | use [`instantiate`] to replace a `GenericArgsRef` with another list of types. | |
74b04a01 | 131 | |
781aab86 FG |
132 | [Here is an example of actually using `instantiate` in the compiler][instantiatex]. |
133 | The exact details are not too important, but in this piece of code, we happen to be | |
134 | converting from the `rustc_hir::Ty` to a real `ty::Ty`. You can see that we first get some args | |
135 | (`args`). Then we call `type_of` to get a type and call `ty.instantiate(tcx, args)` to get a new | |
136 | version of `ty` with the args made. | |
74b04a01 | 137 | |
781aab86 FG |
138 | [`instantiate`]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_middle/ty/generic_args/struct.EarlyBinder.html#method.instantiate |
139 | [instantiatex]: https://github.com/rust-lang/rust/blob/8a562f9671e36cf29c9c794c2646bcf252d55535/compiler/rustc_hir_analysis/src/astconv/mod.rs#L905-L927 | |
74b04a01 XL |
140 | |
141 | **Note on indices:** It is possible for the indices in `Param` to not match with what we expect. For | |
142 | example, the index could be out of bounds or it could be the index of a lifetime when we were | |
143 | expecting a type. These sorts of errors would be caught earlier in the compiler when translating | |
144 | from a `rustc_hir::Ty` to a `ty::Ty`. If they occur later, that is a compiler bug. |