|
| 1 | +# Inference of opaque types (type alias `impl Trait`) |
| 2 | + |
| 3 | +This page describes how the compiler infers the hidden type for an opaque type. This kind of type inference is particularly complex because, unlike other kinds of type inference, it works across functions and function bodies. |
| 4 | + |
| 5 | +## Running example |
| 6 | + |
| 7 | +To help explain how it works, let's start with a simple example. |
| 8 | + |
| 9 | +### |
| 10 | + |
| 11 | +```rust |
| 12 | +mod m { |
| 13 | + pub type Seq<T> = impl IntoIterator<Item = T>; |
| 14 | + |
| 15 | + pub fn produce_singleton<T>(t: T) -> Seq<T> { |
| 16 | + vec![t] |
| 17 | + } |
| 18 | + |
| 19 | + pub fn produce_doubleton<T>(t: T, u: T) -> Seq<T> { |
| 20 | + vec![t, u] |
| 21 | + } |
| 22 | +} |
| 23 | + |
| 24 | +fn is_send<T: Send>(_: &T) {} |
| 25 | + |
| 26 | +pub fn main() { |
| 27 | + let elems = m::produce_singleton(22); |
| 28 | + |
| 29 | + is_send(&elems); |
| 30 | + |
| 31 | + for elem in elems { |
| 32 | + println!("elem = {:?}", elem); |
| 33 | + } |
| 34 | +} |
| 35 | +``` |
| 36 | + |
| 37 | +* In this example, the opaque type is `Seq<T>`: |
| 38 | + * Its defining scope is the module `m`. |
| 39 | + * Its hidden type is `Vec<T>`, which is inferred from: |
| 40 | + * `m::produce_singleton` |
| 41 | + * `m::produce_doubleton` |
| 42 | +* In the `main` function, the opaque type is out of scope: |
| 43 | + * When `main` calls `m::produce_singleton`, it gets back a reference to the opaque type `Seq<i32>`. |
| 44 | + * The `is_send` call checks that `Seq<i32>: Send`. `Send` is not listed amongst the bounds of the impl trait, but because of auto-trait leakage, we are able to infer that it holds. |
| 45 | + * The for loop desugaring requires that `Seq<T>: IntoIterator`, which is provable from the bounds declared on `Seq<T>`. |
| 46 | + |
| 47 | +### Type-checking `main` |
| 48 | + |
| 49 | +Let's start by looking what happens when we type-check `main`. Initially we invoke `produce_singleton` and the return type is an opaque type [`OpaqueTy`](https://doc.rust-lang.org/nightly/nightly-rustc/rustc_hir/enum.ItemKind.html#variant.OpaqueTy). |
| 50 | + |
| 51 | +#### Type-checking the for loop |
| 52 | + |
| 53 | +* Explain how the for loop works: |
| 54 | + * We look at the bounds, we are able to type check it as is |
| 55 | + |
| 56 | +#### Type-checking the `is_send` call |
| 57 | + |
| 58 | +```mermaid |
| 59 | +flowchart TD |
| 60 | + TypeChecking["type checking `main`"] |
| 61 | + subgraph TypeOfSeq["type_of(Seq<T>) query"] |
| 62 | + WalkModuleHir["Walk the HIR for the module `m`"] |
| 63 | + VisitProduceSingleton["visit produce_singleton"] |
| 64 | + VisitProduceDoubleton["visit produce_doubleton"] |
| 65 | + end |
| 66 | +
|
| 67 | + TypeChecking -- trait code for auto traits --> TypeOfSeq |
| 68 | + TypeOfSeq --> WalkModuleHir |
| 69 | + WalkModuleHir --> VisitProduceSingleton |
| 70 | + VisitProduceSingleton --> VisitProduceDoubleton |
| 71 | +``` |
| 72 | + |
| 73 | +* Explain how it invokes `type_of` |
| 74 | + * We look at the bounds, we are able to type check it as is |
| 75 | + |
| 76 | +### Within the `type_of` query |
| 77 | + |
| 78 | +The `type_of` query, when applied to an opaque type O, returns the hidden type. That hidden type is computed by combining the results from each constraining function within defining scope of O. |
| 79 | + |
| 80 | +```mermaid |
| 81 | +flowchart TD |
| 82 | + TypeOf["type_of query"] |
| 83 | + TypeOf -- find_opaque_ty_constraints --> FindOpaqueTyConstraints |
| 84 | + FindOpaqueTyConstraints --> Iterate |
| 85 | + Iterate["Iterate over each item in defining scope"] |
| 86 | + Iterate -- For each item --> TypeCheck |
| 87 | + TypeCheck["Check typeck(I) to see if it constraints O"] |
| 88 | + TypeCheck -- I does not\nconstrain O --> Iterate |
| 89 | + TypeCheck -- I constrains O --> BorrowCheck |
| 90 | + BorrowCheck["Invoke mir_borrowck(I) to get hidden type\nfor O computed by I"] |
| 91 | + BorrowCheck --> PreviousType |
| 92 | + PreviousType["Hidden type from I\nsame as any previous hidden type\nfound so far?"] |
| 93 | + PreviousType -- Yes --> Complete |
| 94 | + PreviousType -- No --> ReportError |
| 95 | + ReportError["Report an error"] |
| 96 | + ReportError --> Complete["Item I complete"] |
| 97 | + Complete --> Iterate |
| 98 | + |
| 99 | + FindOpaqueTyConstraints -- All constraints found --> Done |
| 100 | + Done["Done"] |
| 101 | +``` |
| 102 | + |
| 103 | +### Within the ordinary type check of a single function |
| 104 | + |
| 105 | +```mermaid |
| 106 | +flowchart TD |
| 107 | + subgraph typecheck["type check comparison routines"] |
| 108 | + equate.rs |
| 109 | + sub.rs |
| 110 | + lub.rs |
| 111 | + end |
| 112 | + |
| 113 | + typecheck -- infcx.opaque_ty_obligation --> Enqueue["Enqueue P of kind PredicateKind::OpaqueType"] |
| 114 | + |
| 115 | + Enqueue -- ... some time later ... --> Fulfill |
| 116 | + |
| 117 | + Fulfill["Fulfillment context dequeues P "] |
| 118 | + |
| 119 | + subgraph handleopaquetype["infcx.handle_opaque_type"] |
| 120 | + Handle["Check anchor to determine if we are in a query"] |
| 121 | + |
| 122 | + Handle -- Have anchor, not query --> TwoSimul |
| 123 | + Handle -- No anchor, in query --> Query |
| 124 | + |
| 125 | + Query["See query section below"] |
| 126 | + |
| 127 | + TwoSimul["Defining two opaque types simultaneously?"] |
| 128 | + |
| 129 | + TwoSimul -- Yes --> ReportError["Report error"] |
| 130 | + |
| 131 | + TwoSimul -- No --> AlreadyHasValue |
| 132 | + |
| 133 | + AlreadyHasValue["Opaque type X already has\na registered value?"] |
| 134 | + |
| 135 | + AlreadyHasValue -- No --> RegisterOpaqueType["Register opaque type with\nother type as value"] |
| 136 | + |
| 137 | + AlreadyHasValue -- Yes --> EquateOpaqueTypes["Equate new hidden type with old hidden type"] |
| 138 | + end |
| 139 | + |
| 140 | + Fulfill --> Handle |
| 141 | +``` |
| 142 | + |
| 143 | +### Interactions with queries |
| 144 | + |
| 145 | +When queries encounter a `opaque_ty_obligation`, they do not try to process them, but instead just store the constraints into the infcx. |
| 146 | + |
| 147 | +```mermaid |
| 148 | +graph TD |
| 149 | + subgraph handleopaquetype["infcx.handle_opaque_type"] |
| 150 | + Handle["Check anchor to determine if we are in a query"] |
| 151 | + Handle -- Have anchor, not query --> NotQuery |
| 152 | + Handle -- No anchor, in query --> HavePrevious |
| 153 | +
|
| 154 | + NotQuery["See 'not query' case above"] |
| 155 | + HavePrevious["Have previous hidden type?"] |
| 156 | + |
| 157 | + HavePrevious -- Yes --> Unify |
| 158 | + HavePrevious -- No --> Install |
| 159 | + |
| 160 | + Unify["Equate new and previous hidden types"] |
| 161 | + Install["Install hidden type into table"] |
| 162 | + end |
| 163 | +``` |
| 164 | + |
| 165 | +The registered hidden types are stored into the `QueryResponse` struct in the `opaque_types` field (the function `take_opaque_types_for_query_response` reads them out). |
| 166 | + |
| 167 | +When the `QueryResponse` is instantiated into the surrounding infcx in `query_response_substitution_guess`, we convert each hidden type constraint by invoking `handle_opaque_type` (as above). |
| 168 | + |
| 169 | +There is one bit of "weirdness". The instantiated opaque types are stored in a *map*, and we have to pick one opaque type to use as the key of that map. We use the one that is considered "expected". But really both of the opaque types may have defining uses. When the query result is instantiated, that will be re-evaluated from the context that is using the query. |
| 170 | + |
| 171 | +### Within the MIR borrow checker |
| 172 | + |
| 173 | +The MIR borrow checker relates things via `nll_relate`... |
| 174 | + |
0 commit comments