diff --git a/src/ty.md b/src/ty.md index 90e17e85f..f82d299ae 100644 --- a/src/ty.md +++ b/src/ty.md @@ -148,18 +148,62 @@ These methods all return a `Ty<'tcx>` – note that the lifetime you get back is arena that this `tcx` has access to. Types are always canonicalized and interned (so we never allocate exactly the same type twice). -> N.B. -> 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. If you are going to be testing for type equality, you -> probably need to start looking into the inference code to do it right. - 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 + +## 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_substs_for_item`]. This can be used to answer questions +like "can `Vec` for any `T` be unified with `Vec`?". + +[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 + ## `ty::TyKind` Variants Note: `TyKind` is **NOT** the functional programming concept of *Kind*.