Skip to content

Commit 53395b0

Browse files
nikomatsakistshepang
authored andcommitted
add draft chapter
1 parent e764363 commit 53395b0

File tree

2 files changed

+175
-0
lines changed

2 files changed

+175
-0
lines changed

src/SUMMARY.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -121,6 +121,7 @@
121121
- [Method Lookup](./method-lookup.md)
122122
- [Variance](./variance.md)
123123
- [Opaque Types](./opaque-types-type-alias-impl-trait.md)
124+
- [Inference details](./opaque-types-type-alias-impl-trait-inference.md)
124125
- [Pattern and Exhaustiveness Checking](./pat-exhaustive-checking.md)
125126
- [MIR dataflow](./mir/dataflow.md)
126127
- [Drop elaboration](./mir/drop-elaboration.md)
Lines changed: 174 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,174 @@
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

Comments
 (0)