| # Canonical queries | |
| The "start" of the trait system is the **canonical query** (these are | |
| both queries in the more general sense of the word – something you | |
| would like to know the answer to – and in the | |
| [rustc-specific sense](../query.md)). The idea is that the type | |
| checker or other parts of the system, may in the course of doing their | |
| thing want to know whether some trait is implemented for some type | |
| (e.g., is `u32: Debug` true?). Or they may want to | |
| normalize some associated type. | |
| This section covers queries at a fairly high level of abstraction. The | |
| subsections look a bit more closely at how these ideas are implemented | |
| in rustc. | |
| ## The traditional, interactive Prolog query | |
| In a traditional Prolog system, when you start a query, the solver | |
| will run off and start supplying you with every possible answer it can | |
| find. So given something like this: | |
| ```text | |
| ?- Vec<i32>: AsRef<?U> | |
| ``` | |
| The solver might answer: | |
| ```text | |
| Vec<i32>: AsRef<[i32]> | |
| continue? (y/n) | |
| ``` | |
| This `continue` bit is interesting. The idea in Prolog is that the | |
| solver is finding **all possible** instantiations of your query that | |
| are true. In this case, if we instantiate `?U = [i32]`, then the query | |
| is true (note that a traditional Prolog interface does not, directly, | |
| tell us a value for `?U`, but we can infer one by unifying the | |
| response with our original query – Rust's solver gives back a | |
| substitution instead). If we were to hit `y`, the solver might then | |
| give us another possible answer: | |
| ```text | |
| Vec<i32>: AsRef<Vec<i32>> | |
| continue? (y/n) | |
| ``` | |
| This answer derives from the fact that there is a reflexive impl | |
| (`impl<T> AsRef<T> for T`) for `AsRef`. If were to hit `y` again, | |
| then we might get back a negative response: | |
| ```text | |
| no | |
| ``` | |
| Naturally, in some cases, there may be no possible answers, and hence | |
| the solver will just give me back `no` right away: | |
| ```text | |
| ?- Box<i32>: Copy | |
| no | |
| ``` | |
| In some cases, there might be an infinite number of responses. So for | |
| example if I gave this query, and I kept hitting `y`, then the solver | |
| would never stop giving me back answers: | |
| ```text | |
| ?- Vec<?U>: Clone | |
| Vec<i32>: Clone | |
| continue? (y/n) | |
| Vec<Box<i32>>: Clone | |
| continue? (y/n) | |
| Vec<Box<Box<i32>>>: Clone | |
| continue? (y/n) | |
| Vec<Box<Box<Box<i32>>>>: Clone | |
| continue? (y/n) | |
| ``` | |
| As you can imagine, the solver will gleefully keep adding another | |
| layer of `Box` until we ask it to stop, or it runs out of memory. | |
| Another interesting thing is that queries might still have variables | |
| in them. For example: | |
| ```text | |
| ?- Rc<?T>: Clone | |
| ``` | |
| might produce the answer: | |
| ```text | |
| Rc<?T>: Clone | |
| continue? (y/n) | |
| ``` | |
| After all, `Rc<?T>` is true **no matter what type `?T` is**. | |
| <a id="query-response"></a> | |
| ## A trait query in rustc | |
| The trait queries in rustc work somewhat differently. Instead of | |
| trying to enumerate **all possible** answers for you, they are looking | |
| for an **unambiguous** answer. In particular, when they tell you the | |
| value for a type variable, that means that this is the **only possible | |
| instantiation** that you could use, given the current set of impls and | |
| where-clauses, that would be provable. | |
| The response to a trait query in rustc is typically a | |
| `Result<QueryResult<T>, NoSolution>` (where the `T` will vary a bit | |
| depending on the query itself). The `Err(NoSolution)` case indicates | |
| that the query was false and had no answers (e.g., `Box<i32>: Copy`). | |
| Otherwise, the `QueryResult` gives back information about the possible answer(s) | |
| we did find. It consists of four parts: | |
| - **Certainty:** tells you how sure we are of this answer. It can have two | |
| values: | |
| - `Proven` means that the result is known to be true. | |
| - This might be the result for trying to prove `Vec<i32>: Clone`, | |
| say, or `Rc<?T>: Clone`. | |
| - `Ambiguous` means that there were things we could not yet prove to | |
| be either true *or* false, typically because more type information | |
| was needed. (We'll see an example shortly.) | |
| - This might be the result for trying to prove `Vec<?T>: Clone`. | |
| - **Var values:** Values for each of the unbound inference variables | |
| (like `?T`) that appeared in your original query. (Remember that in Prolog, | |
| we had to infer these.) | |
| - As we'll see in the example below, we can get back var values even | |
| for `Ambiguous` cases. | |
| - **Region constraints:** these are relations that must hold between | |
| the lifetimes that you supplied as inputs. We'll ignore these here. | |
| - **Value:** The query result also comes with a value of type `T`. For | |
| some specialized queries – like normalizing associated types – | |
| this is used to carry back an extra result, but it's often just | |
| `()`. | |
| ### Examples | |
| Let's work through an example query to see what all the parts mean. | |
| Consider [the `Borrow` trait][borrow]. This trait has a number of | |
| impls; among them, there are these two (for clarity, I've written the | |
| `Sized` bounds explicitly): | |
| [borrow]: https://doc.rust-lang.org/std/borrow/trait.Borrow.html | |
| ```rust,ignore | |
| impl<T> Borrow<T> for T where T: ?Sized | |
| impl<T> Borrow<[T]> for Vec<T> where T: Sized | |
| ``` | |
| **Example 1.** Imagine we are type-checking this (rather artificial) | |
| bit of code: | |
| ```rust,ignore | |
| fn foo<A, B>(a: A, vec_b: Option<B>) where A: Borrow<B> { } | |
| fn main() { | |
| let mut t: Vec<_> = vec![]; // Type: Vec<?T> | |
| let mut u: Option<_> = None; // Type: Option<?U> | |
| foo(t, u); // Example 1: requires `Vec<?T>: Borrow<?U>` | |
| ... | |
| } | |
| ``` | |
| As the comments indicate, we first create two variables `t` and `u`; | |
| `t` is an empty vector and `u` is a `None` option. Both of these | |
| variables have unbound inference variables in their type: `?T` | |
| represents the elements in the vector `t` and `?U` represents the | |
| value stored in the option `u`. Next, we invoke `foo`; comparing the | |
| signature of `foo` to its arguments, we wind up with `A = Vec<?T>` and | |
| `B = ?U`. Therefore, the where clause on `foo` requires that `Vec<?T>: | |
| Borrow<?U>`. This is thus our first example trait query. | |
| There are many possible solutions to the query `Vec<?T>: Borrow<?U>`; | |
| for example: | |
| - `?U = Vec<?T>`, | |
| - `?U = [?T]`, | |
| - `?T = u32, ?U = [u32]` | |
| - and so forth. | |
| Therefore, the result we get back would be as follows (I'm going to | |
| ignore region constraints and the "value"): | |
| - Certainty: `Ambiguous` – we're not sure yet if this holds | |
| - Var values: `[?T = ?T, ?U = ?U]` – we learned nothing about the values of | |
| the variables | |
| In short, the query result says that it is too soon to say much about | |
| whether this trait is proven. During type-checking, this is not an | |
| immediate error: instead, the type checker would hold on to this | |
| requirement (`Vec<?T>: Borrow<?U>`) and wait. As we'll see in the next | |
| example, it may happen that `?T` and `?U` wind up constrained from | |
| other sources, in which case we can try the trait query again. | |
| **Example 2.** We can now extend our previous example a bit, | |
| and assign a value to `u`: | |
| ```rust,ignore | |
| fn foo<A, B>(a: A, vec_b: Option<B>) where A: Borrow<B> { } | |
| fn main() { | |
| // What we saw before: | |
| let mut t: Vec<_> = vec![]; // Type: Vec<?T> | |
| let mut u: Option<_> = None; // Type: Option<?U> | |
| foo(t, u); // `Vec<?T>: Borrow<?U>` => ambiguous | |
| // New stuff: | |
| u = Some(vec![]); // ?U = Vec<?V> | |
| } | |
| ``` | |
| As a result of this assignment, the type of `u` is forced to be | |
| `Option<Vec<?V>>`, where `?V` represents the element type of the | |
| vector. This in turn implies that `?U` is [unified] to `Vec<?V>`. | |
| [unified]: ../hir-typeck/summary.md | |
| Let's suppose that the type checker decides to revisit the | |
| "as-yet-unproven" trait obligation we saw before, `Vec<?T>: | |
| Borrow<?U>`. `?U` is no longer an unbound inference variable; it now | |
| has a value, `Vec<?V>`. So, if we "refresh" the query with that value, we get: | |
| ```text | |
| Vec<?T>: Borrow<Vec<?V>> | |
| ``` | |
| This time, there is only one impl that applies, the reflexive impl: | |
| ```text | |
| impl<T> Borrow<T> for T where T: ?Sized | |
| ``` | |
| Therefore, the trait checker will answer: | |
| - Certainty: `Proven` | |
| - Var values: `[?T = ?T, ?V = ?T]` | |
| Here, it is saying that we have indeed proven that the obligation | |
| holds, and we also know that `?T` and `?V` are the same type (but we | |
| don't know what that type is yet!). | |
| (In fact, as the function ends here, the type checker would give an | |
| error at this point, since the element types of `t` and `u` are still | |
| not yet known, even though they are known to be the same.) | |