Link to slides: slides.com/sunjay/coherence-in-chalk
Coherence in Chalk
- Chalk
- Coherence
- Logic Programming
- Lowering Rust Code
- The Orphan Check
- The Overlap Check
- Summary
Link to slides: slides.com/sunjay/coherence-in-chalk
@sunjay03
How It Is Today
Your Code
Lots of cool stuff I don't know about
Lots of cool stuff I don't know about
Machine Code
Machine Code
Current Traits Implementation
@sunjay03
How It Is Today
Your Code
Lots of cool stuff I don't know about
Lots of cool stuff I don't know about
Machine Code
Machine Code
New Traits Implementation
Your Code
Chalk
Machine Code
Machine Code
Current Traits Implementation
Logic Programming!
@sunjay03
Coherence
@sunjay03
Coherence
trait FavoriteColor {
fn fav() -> &'static str;
}
struct Sunjay;
mod summer {
use super::*;
impl FavoriteColor for Sunjay {
fn fav() -> &'static str { "yellow" }
}
}
mod winter {
use super::*;
impl FavoriteColor for Sunjay {
fn fav() -> &'static str { "blue" }
}
}
Conflicting Implementations
@sunjay03
// Uses Hash impl A
fn insert<T: Hash>(...)
// Uses Hash impl B
fn get<T: Hash>(...)
The Hash Table Problem
// In the nom crate
impl Iterator for &[u8] { ... }
// In the serde crate
impl Iterator for &[u8] { ... }
Multiple Dependency Versions
Ecosystem Split
pub struct Foo;
// Only I can add this impl
// because Foo is MY type
impl Display for Foo { ... }
Backwards Compatibility
// In crate A:
trait Foo { ... }
impl<T> Foo for T { ... }
// In crate B:
struct Bar { ... }
// This impl is more specialized
impl Foo for Bar { ... }
// If crate C uses both A and B,
// a single, correct impl should
// always be used consistently
Specialization
@sunjay03
Coherence
- For any given trait, there are either zero or one impls of that trait that apply for a given set of types
- For every impl that could exist, it only exists in one place
MyTrait::foo() // <-- Maps to ONE implementation
impl<T> MyTrait for T { ... } // <-- Can only be written in ONE place
@sunjay03
Bay Area Rust Meetup March 2017
by boats (@withoutboats)
@sunjay03
Coherence in rustc
The Orphan Check
+
The Overlap Check
@sunjay03
- Every impl abides by the orphan rules
- Every impl can only exist in one place
- No two impls overlap
- Only up to one impl of a trait for a given set of types
The Orphan Rules
- RFC 1023: Rebalancing Coherence
- Trait Implementation Coherence in the Rust Reference
- E0210: A violation of the orphan rules in the Rust Error Index
- Little Orphan Impls by Niko Matsakis
- Most authoritative: The Rust Compiler Source Code
@sunjay03
The Orphan Check in rustc
The OrphanChecker in
The orphan_check function and the orphan_check_trait_ref function in
@sunjay03
The Orphan Rules
Given an impl of the form impl<T0…Tn> Trait<P1…Pn> for P0, the impl is allowed if:
- Trait is local to the current crate
- Trait is upstream to the current crate and:
- There is at least one type parameter Pi which, taking fundamental types into account, is local to the current crate (fundamental = &T, &mut T, Box<T> for any T)
- Within the type Pi, all type parameters are covered by Pi
- All types Pj such that j < i do not contain T0…Tn at any level of depth (i.e. the types are fully visible — “visible” meaning that the type is a known type and not a type parameter or variable)
@sunjay03
impl<T, U, V, ...> MyTrait<P1...Pn> for P0 { ... }
Impl Type Parameters
Trait
Trait Type Parameters
Implemented Type
@sunjay03
impl<T, U, V, ...> MyTrait<P1...Pn> for P0 { ... }
Is this locally defined in the current crate or is this an Upstream Trait?
At least one local type
Must not contain any of the type parameters T, U, V, ...
Local Trait: Good To Go 🎉
Upstream Trait: Check P0, P1, P2, ..., Pn
Impl Type Parameters
If any of these conditions aren't met, the impl is considered an orphan impl
The Overlap Check
- Goes through all pairs of impls and checks for any overlap
- Needs to check all possible compatible universes in order to enforce coherence
- compatible universes only have semver compatible changes
trait Foo { }
impl<T: Copy> Foo for T { }
impl<T: Eq> Foo for T { }
@sunjay03
Logic Programming
@sunjay03
Logic Programming
- Take facts, and rules and try to come to some conclusions
fact
rule
conclusion
👨🏽 💖 🍬
Provable because of our fact and rule
"Sunjay loves cake"
"___ loves candy if ___ loves cake"
"Sunjay loves candy"
@sunjay03
Prolog
loves(sunjay, cake)
loves(T, candy) :- loves(T, cake)
loves predicate = 💖
if
?- loves(sunjay, candy)
Yes
Answer
Query
@sunjay03
Chalk
// Each type T implements MyTrait
forall<T> { Implemented(T: MyTrait) }
forall<T> describes all types T
Means "T implements MyTrait"
// There is at least one type T
// that implements MyTrait
exists<T> { Implemented(T: MyTrait) }
exists<T> says that there is some T
forall<T> { ... }
exists<T> { ... }
@sunjay03
Lowering Structs, Traits, and Impls to Logic
impl Foo for Bar { ... }
Implemented(Bar: Foo)
@sunjay03
Lowering to Logic
- Take each declaration in your program and use what you know about it to produce facts and rules
// In crate "people":
extern crate favorites;
// In crate "favorites":
pub struct Taco { }
pub trait FavoriteColor {
fn fav() -> &'static str;
}
impl<T: Copy> FavoriteColor for T {
fn fav() -> &'static str { "purple" }
}
// In crate "std":
pub trait Copy { }
use favorites::*;
struct Sunjay;
impl FavoriteColor for Sunjay {
fn fav() -> &'static str { "red" }
}
struct Manish;
impl FavoriteColor for Manish {
fn fav() -> &'static str { "orange" }
}
IsLocal(Sunjay)
IsLocal(Manish)
Implemented(Sunjay: FavoriteColor)
Implemented(Manish: FavoriteColor)
IsUpstream(Taco)
forall<T> { Implemented(T: FavoriteColor)
:- Implemented(T: Copy) }
DependsOn(people, favorites)
DefinedIn(Sunjay, people)
DefinedIn(Manish, people)
"if"
upstream
current crate
The rustc Guide
@sunjay03
The Orphan Check in Chalk
@sunjay03
The Orphan Rules: Example
// In crate "foo"
pub trait MyTrait<T, U> { }
pub struct Foo { }
// In crate "bar"
extern crate foo;
use foo::*;
struct Bar { }
impl<T> MyTrait<Bar, T> for Foo { }
// Type parameters in order: Foo, Bar, T
impl type parameter
upstream trait
first local type
only fully visible types before the first local type
Good to Go!
The Orphan Rules don't care about any of the types after the first local type
fully visible = no type parameters (e.g. Foo<Bar> but not Foo<T>)
@sunjay03
LocalImplAllowed(T: Trait)
- LocalImplAllowed(Type: Trait) = "Based on the orphan rules, Type is allowed to implement Trait in the current crate"
struct Turtle;
// Is LocalImplAllowed(Turtle: Display) provable?
// Yes. Turtle is a local type
impl Display for Turtle { ... }
// Is LocalImplAllowed(Vec<Turtle>: Display) provable?
// No. Vec<T> is defined in std and the orphan rules
// say that only std can add impls for it
impl Display for Vec<Turtle> { ... }
@sunjay03
The Orphan Check in Chalk
- If the trait is local to the current crate:
- If the trait is upstream to the current crate:
forall<Self, T, U> { LocalImplAllowed(Self: MyTrait<T, U>) }
forall<Self, T, U> {
LocalImplAllowed(Self: MyTrait<T, U>) :-
IsLocal(Self)
}
forall<Self, T, U> {
LocalImplAllowed(Self: MyTrait<T, U>) :-
IsFullyVisible(Self),
IsLocal(T)
}
forall<Self, T, U> {
LocalImplAllowed(Self: MyTrait<T, U>) :-
IsFullyVisible(Self),
IsFullyVisible(T),
IsLocal(U)
}
trait MyTrait<T, U> { }
Simulates "finding" the first local type
IsFullyVisible and IsLocal
// In crate "foo":
pub struct Foo { }
// In crate "bar":
extern crate foo;
struct Bar { }
pub struct Spam<T, U> { ... }
IsFullyVisible(Foo)
IsFullyVisible(Bar)
// Notice that there isn't
// any IsFullyVisible(T)
forall<T, U> {
IsFullyVisible(Spam<T, U>) :-
IsFullyVisible(T),
IsFullyVisible(U)
}
// In crate "foo": (current)
pub struct Foo<T> { ... }
// In crate "bar":
extern crate foo;
struct Bar<T> { ... }
forall<T> { IsLocal(Foo<T>) }
@sunjay03
Fully Visible
Not sure if fully visible
Local
Upstream
Foo is local regardless of its type parameter
Chalk
@sunjay03
The Overlap Check in Chalk
@sunjay03
The Overlap Check in Chalk
- Active area of research
- Tricky because we need to model all possible compatible impls in the entire universe now and forever in the future
- Will be figured out soon!
@sunjay03
Summary
- Coherence is good and very important!
- Logic programming
- Modeling the orphan check with logic
Thank you Niko, boats, and the many others who have helped figure all of this out!
@sunjay03
Thank you!
Link to slides: slides.com/sunjay/coherence-in-chalk
Coherence in Chalk
By Sunjay Varma
Coherence in Chalk
- 1,751