| # The `ty` module: representing types | |
| The `ty` module defines how the Rust compiler represents types internally. | |
| It also defines the | |
| *typing context* (`tcx` or `TyCtxt`), which is the central data structure in the compiler. | |
| ## `ty::Ty` | |
| 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 | |
| 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 into the details of `ty::Ty`. | |
| [ty_ty]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_middle/ty/struct.Ty.html | |
| [hir_ty]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_hir/hir/struct.Ty.html | |
| ## `rustc_hir::Ty` vs `ty::Ty` | |
| The HIR in rustc can be thought of as the high-level intermediate representation. | |
| It is more or less the AST (see [this chapter](hir.md)) as it represents the | |
| syntax that the user wrote, and is obtained after parsing and some *desugaring*. It has a | |
| representation of types, but in reality it reflects more of what the user wrote, that is, what they | |
| wrote so as to represent that type. | |
| In contrast, `ty::Ty` represents the semantics of a type, that is, the *meaning* of what the user | |
| wrote. | |
| For example, `rustc_hir::Ty` would record the fact that a user used the name `u32` twice | |
| in their program, but the `ty::Ty` would record the fact that both usages refer to the same type. | |
| **Example: `fn foo(x: u32) → u32 { x }`** | |
| In this function, we see that `u32` appears twice. | |
| We know that that is the same type, | |
| i.e. the function takes an argument and returns an argument of the same type, | |
| but from the point of view of the HIR, | |
| there would be two distinct type instances because these | |
| are occurring in two different places in the program. | |
| That is, they have two different [`Span`s][span] (locations). | |
| [span]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_span/struct.Span.html | |
| **Example: `fn foo(x: &u32) -> &u32`** | |
| In addition, HIR might have information left out. | |
| This type | |
| `&u32` is incomplete, since in the full Rust type there is actually a lifetime, but we didn’t need | |
| to write those lifetimes. | |
| There are also some elision rules that insert information. | |
| The result may look like `fn foo<'a>(x: &'a u32) -> &'a u32`. | |
| In the HIR level, these things are not spelled out and you can say the picture is rather incomplete. | |
| However, at the `ty::Ty` level, these details are added and it is complete. | |
| Moreover, we will have | |
| exactly one `ty::Ty` for a given type, like `u32`, and that `ty::Ty` is used for all `u32`s in the | |
| whole program, not a specific usage, unlike `rustc_hir::Ty`. | |
| Here is a summary: | |
| | [`rustc_hir::Ty`][hir_ty] | [`ty::Ty`][ty_ty] | | |
| | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | |
| | Describe the *syntax* of a type: what the user wrote (with some desugaring). | Describe the *semantics* of a type: the meaning of what the user wrote. | | |
| | Each `rustc_hir::Ty` has its own spans corresponding to the appropriate place in the program. | Doesn’t correspond to a single place in the user’s program. | | |
| | `rustc_hir::Ty` has generics and lifetimes; however, some of those lifetimes are special markers like [`LifetimeKind::Implicit`][implicit]. | `ty::Ty` has the full type, including generics and lifetimes, even if the user left them out | | |
| | `fn foo(x: u32) -> u32 { }` - Two `rustc_hir::Ty` representing each usage of `u32`, each has its own `Span`s, and `rustc_hir::Ty` doesn’t tell us that both are the same type | `fn foo(x: u32) -> u32 { }` - One `ty::Ty` for all instances of `u32` throughout the program, and `ty::Ty` tells us that both usages of `u32` mean the same type. | | |
| | `fn foo(x: &u32) -> &u32 { }` - Two `rustc_hir::Ty` again. Lifetimes for the references show up in the `rustc_hir::Ty`s using a special marker, [`LifetimeKind::Implicit`][implicit]. | `fn foo(x: &u32) -> &u32 { }`- A single `ty::Ty`. The `ty::Ty` has the hidden lifetime param. | | |
| [implicit]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_hir/hir/enum.LifetimeKind.html#variant.Implicit | |
| **Order** | |
| HIR is built directly from the AST, so it happens before any `ty::Ty` is produced. | |
| After HIR is built, some basic type inference and type checking is done. | |
| During the type inference, we | |
| figure out what the `ty::Ty` of everything is and we also check if the type of something is | |
| ambiguous. | |
| The `ty::Ty` is then used for type checking while making sure everything has the expected type. | |
| The [`hir_ty_lowering` module][hir_ty_lowering] is where the code responsible for | |
| lowering a `rustc_hir::Ty` to a `ty::Ty` is located. | |
| The main routine used is `lower_ty`. | |
| This occurs during the type-checking phase, but also in other parts of the compiler that want to ask | |
| questions like "what argument types does this function expect?" | |
| [hir_ty_lowering]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_hir_analysis/hir_ty_lowering/index.html | |
| **How semantics drive the two instances of `Ty`** | |
| You can think of HIR as the perspective of the type information that assumes the least. | |
| We assume two things are distinct until they are proven to be the same thing. | |
| In other words, we know less about them, so we should assume less about them. | |
| They are syntactically two strings: `"u32"` at line N column 20 and `"u32"` at line N column 35. We | |
| don’t know that they are the same yet. | |
| So, in the HIR we treat them as if they are different. | |
| Later, | |
| we determine that they semantically are the same type and that’s the `ty::Ty` we use. | |
| Consider another example: `fn foo<T>(x: T) -> u32`. | |
| Suppose that someone invokes `foo::<u32>(0)`. | |
| This means that `T` and `u32` (in this invocation) actually turns out to be the same type, so we | |
| would eventually end up with the same `ty::Ty` in the end, but we have distinct `rustc_hir::Ty`. | |
| (This is a bit over-simplified, though, since during type checking, we would check the function | |
| generically and would still have a `T` distinct from `u32`. | |
| Later, when doing code generation, | |
| we would always be handling "monomorphized" (fully substituted) versions of each function, | |
| and hence we would know what `T` represents (and specifically that it is `u32`).) | |
| Here is one more example: | |
| ```rust | |
| mod a { | |
| type X = u32; | |
| pub fn foo(x: X) -> u32 { 22 } | |
| } | |
| mod b { | |
| type X = i32; | |
| pub fn foo(x: X) -> i32 { x } | |
| } | |
| ``` | |
| Here the type `X` will vary depending on context, clearly. | |
| If you look at the `rustc_hir::Ty`, | |
| you will get back that `X` is an alias in both cases (though it will be mapped via name resolution | |
| to distinct aliases). | |
| But if you look at the `ty::Ty` signature, it will be either `fn(u32) -> u32` | |
| or `fn(i32) -> i32` (with type aliases fully expanded). | |
| ## `ty::Ty` implementation | |
| [`rustc_middle::ty::Ty`][ty_ty] is actually a wrapper around | |
| [`Interned<WithCachedTypeInfo<TyKind>>`][tykind]. | |
| You can ignore `Interned` in general; you will basically never access it explicitly. | |
| 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, 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 don’t come into the picture as much here. | |
| Finally, [`Interned`](./memory.md) allows the `ty::Ty` to be a thin pointer-like | |
| type. | |
| This allows us to do cheap comparisons for equality, along with the other benefits of interning. | |
| [tykind]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_type_ir/ty_kind/enum.TyKind.html | |
| ## Allocating and working with types | |
| To allocate a new type, you can use the various `new_*` methods defined on | |
| [`Ty`](https://doc.rust-lang.org/nightly/nightly-rustc/rustc_middle/ty/struct.Ty.html). | |
| These have names that correspond mostly to the various kinds of types. | |
| For example: | |
| ```rust,ignore | |
| let array_ty = Ty::new_array_with_const_len(tcx, ty, count); | |
| ``` | |
| These methods all return a `Ty<'tcx>` – note that the lifetime you get back is the lifetime of the | |
| arena that this `tcx` has access to. | |
| Types are always canonicalized and interned (so we never allocate exactly the same type twice). | |
| You can also find various common types in the `tcx` itself by accessing its fields: | |
| `tcx.types.bool`, `tcx.types.char`, etc. (See [`CommonTypes`] for more.) | |
| [`CommonTypes`]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_middle/ty/context/struct.CommonTypes.html | |
| <!-- N.B: This section is linked from the type comparison internal lint. --> | |
| ## Comparing types | |
| Because types are interned, it is possible to compare them for equality efficiently using `==` | |
| – however, this is almost never what you want to do unless you happen to be hashing and looking | |
| for duplicates. | |
| This is because often in Rust there are multiple ways to represent the same type, | |
| particularly once inference is involved. | |
| For example, the type `{integer}` (`ty::Infer(ty::IntVar(..))` an integer inference variable, | |
| the type of an integer literal like `0`) and `u8` (`ty::UInt(..)`) should often be treated as | |
| equal when testing whether they can be assigned to each other (which is a common operation in | |
| diagnostics code). | |
| `==` on them will return `false` though, since they are different types. | |
| The simplest way to compare two types correctly requires an inference context (`infcx`). | |
| If you have one, you can use `infcx.can_eq(param_env, ty1, ty2)` | |
| to check whether the types can be made equal. | |
| This is typically what you want to check during diagnostics, which is concerned with questions such | |
| as whether two types can be assigned to each other, not whether they're represented identically in | |
| the compiler's type-checking layer. | |
| When working with an inference context, you have to be careful to ensure that potential inference | |
| variables inside the types actually belong to that inference context. | |
| If you are in a function that has access to an inference context already, this should be the case. | |
| Specifically, this is the case during HIR type checking or MIR borrow checking. | |
| Another consideration is normalization. | |
| Two types may actually be the same, but one is behind an associated type. | |
| To compare them correctly, you have to normalize the types first. | |
| This is primarily a concern during HIR type checking and with all types from a `TyCtxt` query | |
| (for example from `tcx.type_of()`). | |
| When a `FnCtxt` or an `ObligationCtxt` is available during type checking, `.normalize(ty)` | |
| should be used on them to normalize the type. | |
| After type checking, diagnostics code can use `tcx.normalize_erasing_regions(ty)`. | |
| There are also cases where using `==` on `Ty` is fine. | |
| This is, for example, the case in late lints | |
| or after monomorphization, since type checking has been completed, meaning all inference variables | |
| are resolved and all regions have been erased. | |
| In these cases, if you know that inference variables | |
| or normalization won't be a concern, `#[allow]` or `#[expect]`ing the lint is recommended. | |
| When diagnostics code does not have access to an inference context, it should be threaded through | |
| 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_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_args_for_item`]: https://doc.rust-lang.org/beta/nightly-rustc/rustc_infer/infer/struct.InferCtxt.html#method.fresh_substs_for_item | |
| ## `ty::TyKind` Variants | |
| Note: `TyKind` is **NOT** the functional programming concept of *Kind*. | |
| Whenever working with a `Ty` in the compiler, it is common to match on the kind of type: | |
| ```rust,ignore | |
| fn foo(x: Ty<'tcx>) { | |
| match x.kind { | |
| ... | |
| } | |
| } | |
| ``` | |
| The `kind` field is of type `TyKind<'tcx>`, which is an enum defining all of the different kinds of | |
| types in the compiler. | |
| > N.B. inspecting the `kind` field on types during type inference can be risky, as there may be | |
| > inference variables and other things to consider, or sometimes types are not yet known and will | |
| > become known later. | |
| There are a lot of related types, and we’ll cover them in time (e.g regions/lifetimes, | |
| “substitutions”, etc). | |
| There are many variants on the `TyKind` enum, which you can see by looking at its | |
| [documentation][tykind]. | |
| Here is a sampling: | |
| - [**Algebraic Data Types (ADTs)**][kindadt] An [*algebraic data type*][wikiadt] is a `struct`, | |
| `enum` or `union`. | |
| Under the hood, `struct`, `enum` and `union` are actually implemented | |
| the same way: they are all [`ty::TyKind::Adt`][kindadt]. | |
| It’s basically a user defined type. | |
| We will talk more about these later. | |
| - [**Foreign**][kindforeign] Corresponds to `extern type T`. | |
| - [**Str**][kindstr] Is the type str. | |
| When the user writes `&str`, `Str` is how we represent the `str` part of that type. | |
| - [**Slice**][kindslice] Corresponds to `[T]`. | |
| - [**Array**][kindarray] Corresponds to `[T; n]`. | |
| - [**RawPtr**][kindrawptr] Corresponds to `*mut T` or `*const T`. | |
| - [**Ref**][kindref] `Ref` stands for safe references, `&'a mut T` or `&'a T`. | |
| `Ref` has some | |
| associated parts, like `Ty<'tcx>` which is the type that the reference references. | |
| `Region<'tcx>` is the lifetime or region of the reference and `Mutability` if the reference | |
| is mutable or not. | |
| - [**Param**][kindparam] Represents a type parameter (e.g. the `T` in `Vec<T>`). | |
| - [**Error**][kinderr] Represents a type error somewhere so that we can print better diagnostics. | |
| We will discuss this more later. | |
| - [**And many more**...][kindvars] | |
| [wikiadt]: https://en.wikipedia.org/wiki/Algebraic_data_type | |
| [kindadt]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_type_ir/ty_kind/enum.TyKind.html#variant.Adt | |
| [kindforeign]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_type_ir/ty_kind/enum.TyKind.html#variant.Foreign | |
| [kindstr]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_type_ir/ty_kind/enum.TyKind.html#variant.Str | |
| [kindslice]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_type_ir/ty_kind/enum.TyKind.html#variant.Slice | |
| [kindarray]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_type_ir/ty_kind/enum.TyKind.html#variant.Array | |
| [kindrawptr]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_type_ir/ty_kind/enum.TyKind.html#variant.RawPtr | |
| [kindref]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_type_ir/ty_kind/enum.TyKind.html#variant.Ref | |
| [kindparam]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_type_ir/ty_kind/enum.TyKind.html#variant.Param | |
| [kinderr]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_type_ir/ty_kind/enum.TyKind.html#variant.Error | |
| [kindvars]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_type_ir/ty_kind/enum.TyKind.html#variants | |
| ## Import conventions | |
| Although there is no hard and fast rule, the `ty` module tends to be used like so: | |
| ```rust,ignore | |
| use ty::{self, Ty, TyCtxt}; | |
| ``` | |
| In particular, since they are so common, the `Ty` and `TyCtxt` types are imported directly. | |
| Other | |
| 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. | |
| ## Type errors | |
| There is a `TyKind::Error` that is produced when the user makes a type error. | |
| The idea is that | |
| we would propagate this type and suppress other errors that come up due to it so as not to overwhelm | |
| the user with cascading compiler error messages. | |
| There is an **important invariant** for `TyKind::Error`. | |
| The compiler should **never** produce `Error` unless we **know** that an error has already been | |
| reported to the user. | |
| This is usually | |
| because (a) you just reported it right there or (b) you are propagating an existing Error type (in | |
| which case the error should've been reported when that error type was produced). | |
| It's important to maintain this invariant because the whole point of the `Error` type is to suppress | |
| other errors -- i.e., we don't report them. If we were to produce an `Error` type without actually | |
| emitting an error to the user, then this could cause later errors to be suppressed, and the | |
| compilation might inadvertently succeed! | |
| Sometimes there is a third case. | |
| You believe that an error has been reported, but you believe it | |
| would've been reported earlier in the compilation, not locally. | |
| In that case, you can create a "delayed bug" with [`delayed_bug`] or [`span_delayed_bug`]. | |
| This will make a note that you expect | |
| compilation to yield an error -- if, however, compilation should succeed, then it will trigger a | |
| compiler bug report. | |
| [`delayed_bug`]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_errors/struct.DiagCtxt.html#method.delayed_bug | |
| [`span_delayed_bug`]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_errors/struct.DiagCtxt.html#method.span_delayed_bug | |
| For added safety, it's not actually possible to produce a `TyKind::Error` value | |
| outside of [`rustc_middle::ty`][ty]; there is a private member of | |
| `TyKind::Error` that prevents it from being constructable elsewhere. | |
| Instead, | |
| one should use the [`Ty::new_error`][terr] or [`Ty::new_error_with_message`][terrmsg] methods. | |
| These methods either take an `ErrorGuaranteed` | |
| or call `span_delayed_bug` before returning an interned `Ty` of kind `Error`. | |
| If you were already planning to use [`span_delayed_bug`], then you can just pass the | |
| span and message to [`ty_error_with_message`][terrmsg] instead to avoid 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 | |
| ## `TyKind` variant shorthand syntax | |
| When looking at the debug output of `Ty` or simply talking about different types in the compiler, you may encounter syntax that is not valid Rust but is used to concisely represent internal information about types. | |
| Below is a quick reference cheat sheet to tell what the various syntax actually means: | |
| - Generic parameters: `{name}/#{index}` e.g. `T/#0`, where `index` corresponds to its position in the list of generic parameters | |
| - Inference variables: `?{id}` e.g. `?x`/`?0`, where `id` identifies the inference variable | |
| - Variables from binders: `^{binder}_{index}` e.g. `^0_x`/`^0_2`, where `binder` and `index` identify which variable from which binder is being referred to | |
| - Placeholders: `!{id}` or `!{id}_{universe}` e.g. `!x`/`!0`/`!x_2`/`!0_2`, representing some unique type in the specified universe. The universe is often elided when it is `0` | |
| These should be covered in more depth in later chapters. |