-
Notifications
You must be signed in to change notification settings - Fork 182
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
Auto traits #54
Conversation
@@ -47,12 +47,17 @@ StructDefn: StructDefn = { | |||
} | |||
}; | |||
|
|||
AutoKeyword: () = "#" "[" "auto" "]"; |
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:
Id: Identifier = {
<l:@L> <s:RawIdent> <r:@R> => Identifier {
str: intern(s),
span: Span::new(l, r),
}
};
RawIdent: &'input str = {
"auto",
r"([A-Za-z]|_)([A-Za-z0-9]|_)*",
};
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 :)
src/solve/solver.rs
Outdated
// if all the components of the cycle also have coinductive semantics, we accept | ||
// the cycle `(?0: AutoTrait) :- ... :- (?0: AutoTrait)` as an infinite proof for | ||
// `?0: AutoTrait` and we do not perform any substitution. | ||
if self.stack.iter().skip(index).all(|s| s.goal.is_coinductive(&*self.program)) { |
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.
Actually I missed chain(Some(goal.clone()))
here, adding that...
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.
Looks great! Left a few minor comments.
src/ir/mod.rs
Outdated
@@ -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 comment
The 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 comment
The 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.
src/lower/default.rs
Outdated
} | ||
} | ||
|
||
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 comment
The reason will be displayed to describe this comment to others. Learn more.
Nit: let's call this impl_provided_for
-- this name makes it sound to me like the fn is going to synthesize an impl
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.
Right, actually I was reading this like "self provide_impl_for struct ?", but I agree this can be confusing.
|
||
goal { | ||
forall<T> { | ||
Vec<T>: Send |
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.
So this through me for a bit, but I guess it's true because struct Vec<T> { }
has no fields
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.
Indeed, but actually this test is not great, I'm going to change that.
src/solve/test.rs
Outdated
// In fact, as soon as there is a field which is referencing `T` with an indirection like `Foo<Bar<T>>`, | ||
// we need to add the `WellFormed(T)` because the `if (T: Send)` elaborates `WellFormed(T: Send)` but not | ||
// `WellFormed(T)`. This is not an issue, but is maybe a bit inconsistent. | ||
goal { |
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.
@nikomatsakis so actually I added a comment here, which I think is interesting, and which is why I wanted the WF(T)
reverse rules at first.
src/solve/solver.rs
Outdated
if self.stack.iter() | ||
.skip(index) | ||
.map(|s| &s.goal) | ||
.chain(Some(&goal)) |
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.
Heh. =) Funny oversight. Maybe add a test for this? Maybe not worth it.
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.
Actually I realised that from a correctness point of view, that's the same thing because we already know that there is a cycle starting at index index
, so the very first element of our iterator is also our current goal already. Anyway, let's keep it that way :)
Actually the comment I've added on my last commit made me think of something which is not great. Given: #[auto] trait Send { }
trait Clone { }
struct Clonable<T> where T: Clone { }
struct S<T> where T: Clone {
data: Clonable<T>
}
// default impl generated:
// impl<T> Send for S<T> where Clonable<T>: Send, WF(Clonable<T>: Send)
//
// WF(Clonable<T>: Send) :- WF(Clonable<T>)
// WF(Clonable<T>) :- WF(T), T: Clone, WF(T: Clone)
// WF(T: Clone) :- WF(T) we only have:
With implied bounds, it's a bit better:
Basically it exposes WF checks for fields. I don't know if this is a problem, but it feels awkward. I think we shouldn't expand the WF bounds on default implementations? This seems fine because expanding WF bounds on impls is only there to ensure that in things like: trait SuperTrait { }
trait Trait : SuperTrait { }
impl<T> Foo for MyStruct<T> where T: Trait { } we correctly check that As was discussed previously in PR #49, we shouldn't expose WF checks for fields (especially for proving things like |
@scalexm remind me, are we waiting for any further changes here? |
I guess we decided to tweak (or maybe tweak) how we approach the |
@nikomatsakis Removing the I'll open a second PR later for tweaking WF rules + implied bounds (I think what we discussed on Gitter works pretty well). |
Implement auto traits and negative impls.
AutoTrait
, we generate a default impl for each typeMyType
whereMyType: (!)AutoTrait
does not unify with an existing positive or negative impl. A default impl will basically retrieve the field typesFieldType1<...>, ..., FieldTypeN<...>
withinMyType
and generate the impl:Negative impls do not generate any rule during lowering and are just here to deactivate default implementations (although they can be implemented for any trait, not just auto traits). Negative impls can overlap arbitrarily. Positive and negative impls for a same trait cannot overlap. Negative impls cannot define associated type values.
Goals of the form
T: AutoTrait
have coinductive semantics. If we encounter a cycle whose all components have coinductive semantics, we accept the cycle as an infinite proof and returnOk
without performing any substitution.The syntax for auto traits is
#[auto] trait Send { }