-
Notifications
You must be signed in to change notification settings - Fork 237
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
Fixes for typeclasses #3134
Fixes for typeclasses #3134
Conversation
If we happen to unify the implicits for two typeclass constraints (likely the same one), we need to retain the tag so this has any chance of being solved. This changes makes the unifier retain the meta tag, with a preference for the left one, which is of course totally arbitrary.
I thought this was needed for backwards-compatibility, but apparently not. The few examples that break are ambiguous, and having this restriction in place seems like the right thing to do and brings some peace of mind.
I've now done this, turns out everything works fine without it given the other changes! This means we are essentially forcing typeclasses (meta args in general) to run in topological order of dependencies, and we will never attempt a problem that is not fully defined, so we should not see anything unexpected. This doesn't solve #2602 though, |
This one fails now. It's a bit strange, it doesn't use a fancy typeclass dependency. class ord_leq (a:eqtype) =
{ leq: a -> a -> bool
; trans: (x:a) -> (y:a) -> (z:a) -> Lemma (x `leq` y /\ y `leq` z ==> x `leq` z)
}
let transitivity {| ord_leq 'a |} (x y z : 'a)
: Lemma (x `leq` y /\ y `leq` z ==> x `leq` z)
[SMTPat (x `leq` y); SMTPat (x `leq` z)]
= trans x y z
let transitivity_forall #a {| ord_leq a |} unit
: Lemma (forall (x y z : a). x `leq` y /\ y `leq` z ==> x `leq` z )
= () |
(looks like typeclass resolution is bamboozled by the |
Ah, yes, the typeclass tactic will now not run in contexts with unresolved types in them, so this I'll try to at least make it clear that the tcresolve tactic is not running. |
I agree, the code looks a bit fishy and should probably be fixed! Although in that case I would indeed expect to have Here is something else that works with master but not this PR (from Comparse tests): class bytes_like (bytes:Type0) = {
x:unit;
}
assume val t: bytes:Type0 -> {|bytes_like bytes|} -> a:Type -> Type
irreducible let with_parser (#bytes:Type0) {|bytes_like bytes|} (#a:Type0) (x:t bytes a) = ()
assume val test_dep_nat_e: bytes:Type0 -> {|bytes_like bytes|} -> nat -> Type0
assume val ps_test_dep_nat_e: #bytes:Type0 -> {|bytes_like bytes|} -> n:nat -> t bytes (test_dep_nat_e bytes n)
assume val ps_option: #bytes:Type0 -> {|bytes_like bytes|} -> #a:Type -> t bytes a -> t bytes (option a)
noeq type test_with_parser (bytes:Type0) {|bytes_like bytes|} = {
[@@@ with_parser #bytes (ps_option (ps_test_dep_nat_e 256))]
f2: option (test_dep_nat_e bytes 256);
[@@@ with_parser #bytes (ps_option (ps_test_dep_nat_e 256))]
f3: option (test_dep_nat_e bytes 256);
} |
Trying to force it would make typeclass instantiation run now, when there are possibly yet-unresolved variables in the environment, which would prevent the tactic from running. See the Bug3130f example. This patch makes the typechecker just accumulate the guard, and force it at the end, where the free uvars restriction will cause the constraints to be solved in order.
Fixes Bug3130e
I updated the branch. The example with the extra The other snippet also works after a fix to the checking of binder attributes. I have one more tiny regression, which I'm still looking at. |
Leave that to the rest of the typechecker.
We had previously made the choise to run meta argument tactics in the environment in which they were introduced, instead of the environment of the uvar that the meta is attached to. These two can differ if the uvar is unified with other uvars, possibly in more constrained environments. But, this leads to all sorts of weirdness, such as getting trying to solve a uvar in an environment that contains itself, if we have something like: let v : ty ?u1 = ... in let w : ty ?u2 = ... in (* something that unifies the type of v and w *) We may have set ?u2:=?u1 and copied the meta over, so when we try to run it to solve ?u1, we should not have ?u1 in scope.
I think everything should work now, still with the restriction of not running meta args in any open context/type. Turns out we did rely on it in one place (FStarLang/steel#127) but I think we can just set a compat option for now. Maybe the right thing to do is add a marker to the tactic specifying it "accepts" being called in unresolved contexts. We could even do that for typeclasses in order to introduce functional dependencies, allowing |
Hmm, this files still fails with me: https://github.com/TWal/comparse/blob/main/src/Comparse.Bytes.Typeclass.fsti . |
It's still there, just in my fork mtzguido@8e9f09a. And I just downloaded that file and it passes for me. Are you sure you're on bbbcff1? I did have to bump the checked file version, so maybe a |
Hmm, sorry I did the test between two meetings and completely messed-up my git-fu. |
I just had a look through it. It look good to me. Note to self. I see the main semantic changes are:
Also, great to see the more widespread use of show etc. throughout. Thanks! |
Awesome! Thanks for the all the examples, they were super helpful. |
Most importantly, trying to defer the execution of meta-args until their environment and goal are fully defined. This reduces the order dependency observed in #3130, but it's not completely eliminated. In particular, we F* can still try to run a meta-arg in a partially-unresolved context, but maybe we should remove that possibility altogether.