-
Notifications
You must be signed in to change notification settings - Fork 183
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
Implied bounds, second take #82
Conversation
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.
First round of comments. Will read in more depth tomorrow, especially the accumulator stuff. This is nice! 💯
@@ -29,17 +29,12 @@ pub Goal: Box<Goal> = { | |||
Goal1: Box<Goal> = { | |||
"forall" "<" <p:Comma<ParameterKind>> ">" "{" <g:Goal> "}" => Box::new(Goal::ForAll(p, g)), | |||
"exists" "<" <p:Comma<ParameterKind>> ">" "{" <g:Goal> "}" => Box::new(Goal::Exists(p, g)), | |||
<i:IfKeyword> "(" <w:Comma<WhereClause>> ")" "{" <g:Goal> "}" => Box::new(Goal::Implies(w, g, i)), | |||
"if" "(" <w:Comma<WhereClause>> ")" "{" <g:Goal> "}" => Box::new(Goal::Implies(w, g)), |
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 am happy to see if_raw
gone, I have to say. That always felt "yucky".
fn parse_and_lower_program(text: &str, solver_choice: SolverChoice, skip_coherence: bool) | ||
-> Result<ir::Program> | ||
{ | ||
if skip_coherence { |
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.
It seems like this parameter is just always false
? Can we just plain remove this?
@@ -108,6 +109,7 @@ fn cycley_recursive_cached(b: &mut Bencher) { | |||
CYCLEY_GOAL, | |||
b, | |||
"Unique", | |||
true |
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.
well I guess it's true here, but not (I take it) for the reasons stated? (why is it true here?)
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.
The recursive solver fails to answer uniquely goals of the form <Bar as Foo>::Assoc: SomeTrait
(or WellFormed(<Bar as Foo>::Assoc: SomeTrait)
). So this is a temporary hack (de-activing checking WF requirements) so that the benchmark tests pass, but I should add a FIXME
or something because this is really not ought to stay.
@@ -747,13 +790,19 @@ impl<T> UCanonical<T> { | |||
} | |||
|
|||
impl UCanonical<InEnvironment<Goal>> { | |||
/// A goal has coinductive semantics if it is of the form `T: AutoTrait`. | |||
/// A goal has coinductive semantics if it is of the form `T: AutoTrait`, or if it is of the | |||
/// form `WellFormed(T: Trait)` where `Trait` is any trait. The latter is needed for dealing |
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 am mildly surprised that we don't want to just make all WellFormed
goals coinductive. But I guess it's only needed for traits...
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.
Well types only have where clauses of the form T: Trait
, there are no WF(Type)
where clauses so you never have the kinds of cycles encountered with traits.
@@ -445,6 +447,10 @@ impl LowerWhereClause<ir::DomainGoal> for WhereClause { | |||
WhereClause::TraitRefWellFormed { ref trait_ref } => { | |||
ir::WellFormed::TraitRef(trait_ref.lower(env)?).cast() | |||
} | |||
WhereClause::TyFromEnv { ref ty } => ir::FromEnv::Ty(ty.lower(env)?).cast(), |
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 am torn on the question of what this should mean:
if (T: Trait) { .. }
I am thinking maybe we should make that equivalent to if (FromEnv(T: Trait)) { .. }
and then have an explicit form if (Implemented(T: Trait)) { .. }
. Basically T: Trait
would parse to an AST form whose expansion depends on whether it appears in a "clause" (in which case it is FromEnv
) or a Goal (in which case it is "Implemented").
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.
(That said, I'm not really sure when we would want to say if (Implemented(T: Trait)) { ... }
.. but maybe it's useful when crafting unit tests or something.)
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 not really sure either, but I think converting to FromEnv(T: Trait)
in clauses is a good thing, and the explicit goal Implemented
would replace the former if_raw
statement in a much nicer way.
src/lower/mod.rs
Outdated
// | ||
// forall<T> { | ||
// Normalize(<T as Foo>::Assoc -> U) :- FromEnv(T: Foo<Assoc = U>) | ||
// } |
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.
As an aside, and not for this PR, I wonder if we can use macros to make this code less tedious to write.
src/lower/test.rs
Outdated
impl<T> A for T where T: B {} | ||
} error_msg { | ||
"trait impl for \"B\" does not meet well-formedness requirements" | ||
} |
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.
nice
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.
(maybe we should add a test where this succeeds?)
type Value; | ||
} | ||
|
||
impl Bar for i32 { |
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.
Maybe add a comment here: this fails because i32: Foo
does not hold
type Value; | ||
} | ||
|
||
impl<K> Foo for Set<K> { |
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.
Here, Wf(Set<K>)
implies K: Hash
and hence OnlyEq<K>
is WF
src/lower/wf.rs
Outdated
|
||
// Type parameters and higher-kinded types do not carry any input types (so we can sort | ||
// of assume they are always WF). | ||
Ty::Var(..) | Ty::ForAll(..) => (), |
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.
Hmm, Ty::ForAll
corresponds to for<'a> fn(&'a u32)
or some such. I don't know that we can assume they are always WF. I think this accumulator probably doesn't want to accumulate into a vector, but rather to invoke a callback, because in the case of for<'a>
we may want to create subuniverses? I have to read this code a bit more carefully I guess.
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 we discussed that and for some reason we decided that higher-kinded types are always WF. This is similar to the fact that if we have this kind of function:
fn foo<T>(arg: HashSet<NotHash<T>>) { ... }
we would not issue any error until the function is called. For lifetime parametric function types, again an error would be issued only if a function of this type is ever called. We discussed that in our dropbox paper (see 2017.07.25).
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.
Oh, right, I forgot about that. Basically we were assuming that for all types introduce their own implied bounds, right? Makes sense.
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.
Can we put a more explicit comment then? =)
@scalexm ok so the changes to make then are:
Do you think you'll have time for that? Do you want me to do it? We should probably make more tests but I'm not inclined to block on that. |
05f2322
to
acd499f
Compare
I read over the changes more thoroughly. Everything looks great. I looked briefly at the on-demand test was failing; for some reason it is now creating However, I did notice one thing. It seems like you are treating The intention is that "projection eq" is the "normal" rule, with Anyway, I pushed a final commit with a test that fails as a result -- but I think is expected to pass. |
This is a full implementation of implied bounds as well as enforcing WF requirements on impls and type declarations.
The rules for WF requirements on traits are detailed in #12 (and in the code). The rules for types are much more simple (no need for co-recursion but we still have two predicates,
WellFormed
andFromEnv
).I was not greatly inspired for the tests, so one can feel free to add some more.
Fixes #11 and incidentally some form of bug related to #70. And eventually #12 will be resolved too.