summaryrefslogtreecommitdiffstats
path: root/src/doc/rustc-dev-guide/src/traits/goals-and-clauses.md
diff options
context:
space:
mode:
Diffstat (limited to 'src/doc/rustc-dev-guide/src/traits/goals-and-clauses.md')
-rw-r--r--src/doc/rustc-dev-guide/src/traits/goals-and-clauses.md272
1 files changed, 272 insertions, 0 deletions
diff --git a/src/doc/rustc-dev-guide/src/traits/goals-and-clauses.md b/src/doc/rustc-dev-guide/src/traits/goals-and-clauses.md
new file mode 100644
index 000000000..4315b3e0f
--- /dev/null
+++ b/src/doc/rustc-dev-guide/src/traits/goals-and-clauses.md
@@ -0,0 +1,272 @@
+# Goals and clauses
+
+<!-- toc -->
+
+In logic programming terms, a **goal** is something that you must
+prove and a **clause** is something that you know is true. As
+described in the [lowering to logic](./lowering-to-logic.html)
+chapter, Rust's trait solver is based on an extension of hereditary
+harrop (HH) clauses, which extend traditional Prolog Horn clauses with
+a few new superpowers.
+
+## Goals and clauses meta structure
+
+In Rust's solver, **goals** and **clauses** have the following forms
+(note that the two definitions reference one another):
+
+```text
+Goal = DomainGoal // defined in the section below
+ | Goal && Goal
+ | Goal || Goal
+ | exists<K> { Goal } // existential quantification
+ | forall<K> { Goal } // universal quantification
+ | if (Clause) { Goal } // implication
+ | true // something that's trivially true
+ | ambiguous // something that's never provable
+
+Clause = DomainGoal
+ | Clause :- Goal // if can prove Goal, then Clause is true
+ | Clause && Clause
+ | forall<K> { Clause }
+
+K = <type> // a "kind"
+ | <lifetime>
+```
+
+The proof procedure for these sorts of goals is actually quite
+straightforward. Essentially, it's a form of depth-first search. The
+paper
+["A Proof Procedure for the Logic of Hereditary Harrop Formulas"][pphhf]
+gives the details.
+
+In terms of code, these types are defined in
+[`rustc_middle/src/traits/mod.rs`][traits_mod] in rustc, and in
+[`chalk-ir/src/lib.rs`][chalk_ir] in chalk.
+
+[pphhf]: https://rust-lang.github.io/chalk/book/bibliography.html#pphhf
+[traits_mod]: https://github.com/rust-lang/rust/blob/master/compiler/rustc_middle/src/traits/mod.rs
+[chalk_ir]: https://github.com/rust-lang/chalk/blob/master/chalk-ir/src/lib.rs
+
+<a name="domain-goals"></a>
+
+## Domain goals
+
+*Domain goals* are the atoms of the trait logic. As can be seen in the
+definitions given above, general goals basically consist in a combination of
+domain goals.
+
+Moreover, flattening a bit the definition of clauses given previously, one can
+see that clauses are always of the form:
+```text
+forall<K1, ..., Kn> { DomainGoal :- Goal }
+```
+hence domain goals are in fact clauses' LHS. That is, at the most granular level,
+domain goals are what the trait solver will end up trying to prove.
+
+<a name="trait-ref"></a>
+
+To define the set of domain goals in our system, we need to first
+introduce a few simple formulations. A **trait reference** consists of
+the name of a trait along with a suitable set of inputs P0..Pn:
+
+```text
+TraitRef = P0: TraitName<P1..Pn>
+```
+
+So, for example, `u32: Display` is a trait reference, as is `Vec<T>:
+IntoIterator`. Note that Rust surface syntax also permits some extra
+things, like associated type bindings (`Vec<T>: IntoIterator<Item =
+T>`), that are not part of a trait reference.
+
+<a name="projection"></a>
+
+A **projection** consists of an associated item reference along with
+its inputs P0..Pm:
+
+```text
+Projection = <P0 as TraitName<P1..Pn>>::AssocItem<Pn+1..Pm>
+```
+
+Given these, we can define a `DomainGoal` as follows:
+
+```text
+DomainGoal = Holds(WhereClause)
+ | FromEnv(TraitRef)
+ | FromEnv(Type)
+ | WellFormed(TraitRef)
+ | WellFormed(Type)
+ | Normalize(Projection -> Type)
+
+WhereClause = Implemented(TraitRef)
+ | ProjectionEq(Projection = Type)
+ | Outlives(Type: Region)
+ | Outlives(Region: Region)
+```
+
+`WhereClause` refers to a `where` clause that a Rust user would actually be able
+to write in a Rust program. This abstraction exists only as a convenience as we
+sometimes want to only deal with domain goals that are effectively writable in
+Rust.
+
+Let's break down each one of these, one-by-one.
+
+#### Implemented(TraitRef)
+e.g. `Implemented(i32: Copy)`
+
+True if the given trait is implemented for the given input types and lifetimes.
+
+#### ProjectionEq(Projection = Type)
+e.g. `ProjectionEq<T as Iterator>::Item = u8`
+
+The given associated type `Projection` is equal to `Type`; this can be proved
+with either normalization or using placeholder associated types. See
+[the section on associated types in Chalk Book][at].
+
+#### Normalize(Projection -> Type)
+e.g. `ProjectionEq<T as Iterator>::Item -> u8`
+
+The given associated type `Projection` can be [normalized][n] to `Type`.
+
+As discussed in [the section on associated
+types in Chalk Book][at], `Normalize` implies `ProjectionEq`,
+but not vice versa. In general, proving `Normalize(<T as Trait>::Item -> U)`
+also requires proving `Implemented(T: Trait)`.
+
+[n]: https://rust-lang.github.io/chalk/book/clauses/type_equality.html#normalize
+[at]: https://rust-lang.github.io/chalk/book/clauses/type_equality.html
+
+#### FromEnv(TraitRef)
+e.g. `FromEnv(Self: Add<i32>)`
+
+True if the inner `TraitRef` is *assumed* to be true,
+that is, if it can be derived from the in-scope where clauses.
+
+For example, given the following function:
+
+```rust
+fn loud_clone<T: Clone>(stuff: &T) -> T {
+ println!("cloning!");
+ stuff.clone()
+}
+```
+
+Inside the body of our function, we would have `FromEnv(T: Clone)`. In-scope
+where clauses nest, so a function body inside an impl body inherits the
+impl body's where clauses, too.
+
+This and the next rule are used to implement [implied bounds]. As we'll see
+in the section on lowering, `FromEnv(TraitRef)` implies `Implemented(TraitRef)`,
+but not vice versa. This distinction is crucial to implied bounds.
+
+#### FromEnv(Type)
+e.g. `FromEnv(HashSet<K>)`
+
+True if the inner `Type` is *assumed* to be well-formed, that is, if it is an
+input type of a function or an impl.
+
+For example, given the following code:
+
+```rust,ignore
+struct HashSet<K> where K: Hash { ... }
+
+fn loud_insert<K>(set: &mut HashSet<K>, item: K) {
+ println!("inserting!");
+ set.insert(item);
+}
+```
+
+`HashSet<K>` is an input type of the `loud_insert` function. Hence, we assume it
+to be well-formed, so we would have `FromEnv(HashSet<K>)` inside the body of our
+function. As we'll see in the section on lowering, `FromEnv(HashSet<K>)` implies
+`Implemented(K: Hash)` because the
+`HashSet` declaration was written with a `K: Hash` where clause. Hence, we don't
+need to repeat that bound on the `loud_insert` function: we rather automatically
+assume that it is true.
+
+#### WellFormed(Item)
+These goals imply that the given item is *well-formed*.
+
+We can talk about different types of items being well-formed:
+
+* *Types*, like `WellFormed(Vec<i32>)`, which is true in Rust, or
+ `WellFormed(Vec<str>)`, which is not (because `str` is not `Sized`.)
+
+* *TraitRefs*, like `WellFormed(Vec<i32>: Clone)`.
+
+Well-formedness is important to [implied bounds]. In particular, the reason
+it is okay to assume `FromEnv(T: Clone)` in the `loud_clone` example is that we
+_also_ verify `WellFormed(T: Clone)` for each call site of `loud_clone`.
+Similarly, it is okay to assume `FromEnv(HashSet<K>)` in the `loud_insert`
+example because we will verify `WellFormed(HashSet<K>)` for each call site of
+`loud_insert`.
+
+#### Outlives(Type: Region), Outlives(Region: Region)
+e.g. `Outlives(&'a str: 'b)`, `Outlives('a: 'static)`
+
+True if the given type or region on the left outlives the right-hand region.
+
+<a name="coinductive"></a>
+
+## Coinductive goals
+
+Most goals in our system are "inductive". In an inductive goal,
+circular reasoning is disallowed. Consider this example clause:
+
+```text
+ Implemented(Foo: Bar) :-
+ Implemented(Foo: Bar).
+```
+
+Considered inductively, this clause is useless: if we are trying to
+prove `Implemented(Foo: Bar)`, we would then recursively have to prove
+`Implemented(Foo: Bar)`, and that cycle would continue ad infinitum
+(the trait solver will terminate here, it would just consider that
+`Implemented(Foo: Bar)` is not known to be true).
+
+However, some goals are *co-inductive*. Simply put, this means that
+cycles are OK. So, if `Bar` were a co-inductive trait, then the rule
+above would be perfectly valid, and it would indicate that
+`Implemented(Foo: Bar)` is true.
+
+*Auto traits* are one example in Rust where co-inductive goals are used.
+Consider the `Send` trait, and imagine that we have this struct:
+
+```rust
+struct Foo {
+ next: Option<Box<Foo>>
+}
+```
+
+The default rules for auto traits say that `Foo` is `Send` if the
+types of its fields are `Send`. Therefore, we would have a rule like
+
+```text
+Implemented(Foo: Send) :-
+ Implemented(Option<Box<Foo>>: Send).
+```
+
+As you can probably imagine, proving that `Option<Box<Foo>>: Send` is
+going to wind up circularly requiring us to prove that `Foo: Send`
+again. So this would be an example where we wind up in a cycle – but
+that's ok, we *do* consider `Foo: Send` to hold, even though it
+references itself.
+
+In general, co-inductive traits are used in Rust trait solving when we
+want to enumerate a fixed set of possibilities. In the case of auto
+traits, we are enumerating the set of reachable types from a given
+starting point (i.e., `Foo` can reach values of type
+`Option<Box<Foo>>`, which implies it can reach values of type
+`Box<Foo>`, and then of type `Foo`, and then the cycle is complete).
+
+In addition to auto traits, `WellFormed` predicates are co-inductive.
+These are used to achieve a similar "enumerate all the cases" pattern,
+as described in the section on [implied bounds].
+
+[implied bounds]: https://rust-lang.github.io/chalk/book/clauses/implied_bounds.html#implied-bounds
+
+## Incomplete chapter
+
+Some topics yet to be written:
+
+- Elaborate on the proof procedure
+- SLG solving – introduce negative reasoning