-
Notifications
You must be signed in to change notification settings - Fork 183
Auto traits #54
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Auto traits #54
Changes from 11 commits
d0ab9ac
7796360
779ae25
12baae7
2216603
3c66e1d
70f70f5
a555776
7f34963
d76c379
82ed96b
6863ff1
4844044
72e6553
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -24,8 +24,11 @@ pub struct Program { | |
/// For each trait: | ||
pub trait_data: HashMap<ItemId, TraitDatum>, | ||
|
||
/// For each trait: | ||
/// For each associated ty: | ||
pub associated_ty_data: HashMap<ItemId, AssociatedTyDatum>, | ||
|
||
/// For each default impl (automatically generated for auto traits): | ||
pub default_impl_data: Vec<DefaultImplDatum>, | ||
} | ||
|
||
impl Program { | ||
|
@@ -84,8 +87,11 @@ impl Environment { | |
pub fn add_clauses<I>(&self, clauses: I) -> Arc<Environment> | ||
where I: IntoIterator<Item = DomainGoal> | ||
{ | ||
use std::collections::HashSet; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. not that I mind, but why not move this to the top of the module? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I'm perfectly ok with moving it to the top. I don't know why but some times I prefer local imports if they're used only at one little place. Not every time though haha. |
||
|
||
let mut env = self.clone(); | ||
env.clauses.extend(clauses); | ||
let env_clauses: HashSet<_> = env.clauses.into_iter().chain(clauses).collect(); | ||
env.clauses = env_clauses.into_iter().collect(); | ||
Arc::new(env) | ||
} | ||
|
||
|
@@ -182,12 +188,23 @@ pub struct ImplDatum { | |
|
||
#[derive(Clone, Debug, PartialEq, Eq, Hash)] | ||
pub struct ImplDatumBound { | ||
pub trait_ref: TraitRef, | ||
pub trait_ref: PolarizedTraitRef, | ||
pub where_clauses: Vec<DomainGoal>, | ||
pub associated_ty_values: Vec<AssociatedTyValue>, | ||
pub specialization_priority: usize, | ||
} | ||
|
||
#[derive(Clone, Debug, PartialEq, Eq, Hash)] | ||
pub struct DefaultImplDatum { | ||
pub binders: Binders<DefaultImplDatumBound>, | ||
} | ||
|
||
#[derive(Clone, Debug, PartialEq, Eq, Hash)] | ||
pub struct DefaultImplDatumBound { | ||
pub trait_ref: TraitRef, | ||
pub accessible_tys: Vec<Ty>, | ||
} | ||
|
||
#[derive(Clone, Debug, PartialEq, Eq, Hash)] | ||
pub struct StructDatum { | ||
pub binders: Binders<StructDatumBound>, | ||
|
@@ -209,6 +226,7 @@ pub struct TraitDatum { | |
pub struct TraitDatumBound { | ||
pub trait_ref: TraitRef, | ||
pub where_clauses: Vec<DomainGoal>, | ||
pub auto: bool, | ||
} | ||
|
||
#[derive(Clone, Debug, PartialEq, Eq, Hash)] | ||
|
@@ -343,6 +361,28 @@ pub struct TraitRef { | |
pub parameters: Vec<Parameter>, | ||
} | ||
|
||
#[derive(Clone, PartialEq, Eq, Hash, PartialOrd, Ord, Debug)] | ||
pub enum PolarizedTraitRef { | ||
Positive(TraitRef), | ||
Negative(TraitRef), | ||
} | ||
|
||
impl PolarizedTraitRef { | ||
pub fn is_positive(&self) -> bool { | ||
match *self { | ||
PolarizedTraitRef::Positive(_) => true, | ||
PolarizedTraitRef::Negative(_) => false, | ||
} | ||
} | ||
|
||
pub fn trait_ref(&self) -> &TraitRef { | ||
match *self { | ||
PolarizedTraitRef::Positive(ref tr) | | ||
PolarizedTraitRef::Negative(ref tr) => tr | ||
} | ||
} | ||
} | ||
|
||
/// A "domain goal" is a goal that is directly about Rust, rather than a pure | ||
/// logical statement. As much as possible, the Chalk solver should avoid | ||
/// decomposing this enum, and instead treat its values opaquely. | ||
|
@@ -374,9 +414,8 @@ impl DomainGoal { | |
pub fn expanded(self, program: &Program) -> impl Iterator<Item = DomainGoal> { | ||
let mut expanded = vec![]; | ||
match self { | ||
DomainGoal::Implemented(ref trait_ref) => { | ||
expanded.push(WellFormed::TraitRef(trait_ref.clone()).cast()); | ||
} | ||
DomainGoal::Implemented(ref trait_ref) => | ||
expanded.push(WellFormed::TraitRef(trait_ref.clone()).cast()), | ||
DomainGoal::Normalize(Normalize { ref projection, .. }) => { | ||
let (associated_ty_data, trait_params, _) = program.split_projection(&projection); | ||
let trait_ref = TraitRef { | ||
|
@@ -500,6 +539,31 @@ pub enum FullyReducedGoal { | |
DomainGoal(Canonical<InEnvironment<DomainGoal>>), | ||
} | ||
|
||
impl FullyReducedGoal { | ||
pub fn into_binders(self) -> Vec<ParameterKind<UniverseIndex>> { | ||
match self { | ||
FullyReducedGoal::EqGoal(Canonical { binders, .. }) | | ||
FullyReducedGoal::DomainGoal(Canonical { binders, ..}) => binders, | ||
} | ||
} | ||
|
||
/// A goal has coinductive semantics if it is of the form `T: AutoTrait`. | ||
pub fn is_coinductive(&self, program: &ProgramEnvironment) -> bool { | ||
if let FullyReducedGoal::DomainGoal(Canonical { | ||
value: InEnvironment { | ||
goal: DomainGoal::Implemented(ref tr), | ||
.. | ||
}, | ||
.. | ||
}) = *self { | ||
let trait_datum = &program.trait_data[&tr.trait_id]; | ||
return trait_datum.binders.value.auto; | ||
} | ||
|
||
false | ||
} | ||
} | ||
|
||
impl<T> Canonical<T> { | ||
pub fn map<OP, U>(self, op: OP) -> Canonical<U> | ||
where OP: FnOnce(T) -> U | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,80 @@ | ||
use ir::*; | ||
use solve::infer::InferenceTable; | ||
use cast::Cast; | ||
|
||
impl Program { | ||
pub(super) fn add_default_impls(&mut self) { | ||
// For each auto trait `MyAutoTrait` and for each struct/type `MyStruct` | ||
for auto_trait in self.trait_data.values().filter(|t| t.binders.value.auto) { | ||
for struct_datum in self.struct_data.values() { | ||
|
||
// `MyStruct: MyAutoTrait` | ||
let trait_ref = TraitRef { | ||
trait_id: auto_trait.binders.value.trait_ref.trait_id, | ||
parameters: vec![ | ||
ParameterKind::Ty(Ty::Apply(struct_datum.binders.value.self_ty.clone())) | ||
] | ||
}; | ||
|
||
// If a positive or negative impl is already provided for a type family | ||
// which includes `MyStruct`, we do not generate a default impl. | ||
if self.provide_impl_for(trait_ref.clone(), struct_datum) { | ||
continue; | ||
} | ||
|
||
self.default_impl_data.push(DefaultImplDatum { | ||
binders: Binders { | ||
binders: struct_datum.binders.binders.clone(), | ||
value: DefaultImplDatumBound { | ||
trait_ref, | ||
accessible_tys: struct_datum.binders.value.fields.clone(), | ||
} | ||
} | ||
}); | ||
} | ||
} | ||
} | ||
|
||
fn provide_impl_for(&self, trait_ref: TraitRef, struct_datum: &StructDatum) -> bool { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Nit: let's call this There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Right, actually I was reading this like "self provide_impl_for struct ?", but I agree this can be confusing. |
||
let goal: DomainGoal = trait_ref.cast(); | ||
|
||
let env = Environment::new(); | ||
let mut infer = InferenceTable::new(); | ||
|
||
let goal = infer.instantiate_in(env.universe, struct_datum.binders.binders.clone(), &goal); | ||
|
||
for impl_datum in self.impl_data.values() { | ||
// We retrieve the trait ref given by the positive impl (even if the actual impl is negative) | ||
let impl_goal: DomainGoal = impl_datum.binders.value.trait_ref.trait_ref().clone().cast(); | ||
|
||
let impl_goal = infer.instantiate_in(env.universe, impl_datum.binders.binders.clone(), &impl_goal); | ||
|
||
// We check whether the impl `MyStruct: (!)MyAutoTrait` unifies with an existing impl. | ||
// Examples: | ||
// | ||
// ``` | ||
// struct MyStruct; | ||
// impl<T> Send for T where T: Foo { } | ||
// ``` | ||
// `MyStruct: Send` unifies with `T: Send` so no default impl is generated for `MyStruct`. | ||
// | ||
// ``` | ||
// struct MyStruct; | ||
// impl<T> Send for Vec<T> where T: Foo { } | ||
// ``` | ||
// `Vec<i32>: Send` unifies with `Vec<T>: Send` so no default impl is generated for `Vec<i32>`. | ||
// But a default impl is generated for `MyStruct`. | ||
// | ||
// ``` | ||
// struct MyStruct; | ||
// impl<T> !Send for T where T: Foo { } | ||
// ``` | ||
// `MyStruct: !Send` unifies with `T: !Send` so no default impl is generated for `MyStruct`. | ||
if infer.unify(&Environment::new(), &goal, &impl_goal).is_ok() { | ||
return true; | ||
} | ||
} | ||
|
||
false | ||
} | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
if you wanted to make
auto
usable as an identifier, you could do this:There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah I see, but actually I think
#[auto]
is fine now :)