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
Fix #3357, mark implicit arguments in Notations #668
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.
Looks good but there are a couple of cases to check in the function used for printing (Notation_ops.match_
). See the annotated code which tells which bk
must be matched again a bk
in the glob_constr
, which are necessaritly Explicit
by construction (those which are used to catch a binder which is part of a recursive binder) and thus deserve an assert
, and thus that apply only when with Explicit
(the eta case).
Also, those NLambda
and NProd
used to catch a binder which is part of a recursive binder should conventially be written without { } (i.e. it makes no sense to write Notation "lambda {x} .. {y} => t" := ... (x binder, y binder, ...).
). Thus, there should be an error if the GLambda
and GProd
in the aux
part of compare_recursive_parts
of notation_ops.ml
expose a Implicit
. (I could not comment on the diff since it is not in the diff.)
On a more general note, since the kernel drops Explicit
/Implicit
(*), a notation with a { } shall never be reused to print a term from the kernel.
(*) Maybe it should not drop them, but as it is now, it does. I always found frustrating to write thinks like
Lemma L (f:forall {A}, A -> A) : foo.
and see f : forall A, A -> A
instead of f : forall {A}, A -> A
in the hypotheses.
interp/constrintern.ml
Outdated
when Name.equal na na' -> | ||
let subinfos,na = traverse_binder subst avoid subinfos na in | ||
let ty = GHole (loc,Evar_kinds.BinderType na,naming,arg) in | ||
GProd (loc,na,Explicit,ty,aux subst' subinfos c') | ||
| NLambda (na,NHole(Evar_kinds.BinderType na',naming,arg),c') | ||
| NLambda (na,_,NHole(Evar_kinds.BinderType na',naming,arg),c') |
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.
In all those four cases, I would say that we should have an assertion bk = Explicit
interp/notation_ops.ml
Outdated
let (decls,b) = match_iterated_binders true [(Inr cp,bk,None,t1)] b1 in | ||
let alp,sigma = bind_bindinglist_env alp sigma x decls in | ||
match_in u alp metas sigma b termin | ||
|
||
(* Matching recursive notations for binders: ad hoc cases supporting let-in *) | ||
| GLambda (_,na1,bk,t1,b1), NBinderList (x,_,NLambda (Name _id2,_,b2),termin)-> | ||
| GLambda (_,na1,bk,t1,b1), NBinderList (x,_,NLambda (Name _id2,_,_,b2),termin)-> |
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.
In the NBinderList
cases, one should also have the assertion bk2 = Explicit
interp/notation_ops.ml
Outdated
let (decls,b) = match_iterated_binders true [(Inr cp,bk,None,t1)] b1 in | ||
let alp,sigma = bind_bindinglist_env alp sigma x decls in | ||
match_in u alp metas sigma b termin | ||
|
||
| GProd (_,na1,bk,t1,b1), NBinderList (x,_,NProd (Name _id2,_,b2),termin) | ||
| GProd (_,na1,bk,t1,b1), NBinderList (x,_,NProd (Name _id2,_,_,b2),termin) |
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.
Idem.
interp/notation_ops.ml
Outdated
when is_bindinglist_meta id metas -> | ||
let alp,sigma = bind_bindinglist_env alp sigma id [(Inl na,bk,None,t)] in | ||
match_in u alp metas sigma b1 b2 | ||
| GProd (_,na,bk,t,b1), NProd (Name id,_,b2) | ||
| GProd (_,na,bk,t,b1), NProd (Name id,_,_,b2) | ||
when is_bindinglist_meta id metas && na != Anonymous -> |
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.
Idem for these case where the NLambda
and NProd
are placeholders for a sequence of binders.
interp/notation_ops.ml
Outdated
match_binders u alp metas na1 na2 (match_in u alp metas sigma t1 t2) b1 b2 | ||
| GProd (_,na1,_,t1,b1), NProd (na2,t2,b2) -> | ||
| GProd (_,na1,_,t1,b1), NProd (na2,_,t2,b2) -> | ||
match_binders u alp metas na1 na2 (match_in u alp metas sigma t1 t2) b1 b2 |
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.
In those cases however, one should have the same bk
on the lhs and rhs.
interp/notation_ops.ml
Outdated
@@ -1090,7 +1090,7 @@ let rec match_ inner u alp metas sigma a1 a2 = | |||
otherwise how to ensure it corresponds to a well-typed eta-expansion; | |||
we make an exception for types which are metavariables: this is useful e.g. | |||
to print "{x:_ & P x}" knowing that notation "{x & P x}" is not defined. *) | |||
| b1, NLambda (Name id as na,(NHole _ | NVar _ as t2),b2) when inner -> | |||
| b1, NLambda (Name id as na,_,(NHole _ | NVar _ as t2),b2) when inner -> |
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.
For the eta-expansion, one need to match exactly an Explicit
for the expansion to make sense.
Typical situation: we don't want to identify let y := fun {A} => g A in t
with let y := g in t
as this would break the implicit argument of y
.
Thanks, I somehow knew it wasn't that simple! I'll make these changes soon. |
I've made the changes suggested in the code comments. Looking into the Implicit check in |
@herbelin I've added a check for implicit argument in the |
In fiat-crypto run by Travis, one of the new asserts failed, at line 686 in constrintern.ml. Not sure what to make of that. |
Here is a small test-case that succeeds in v8.6 and triggers the anomaly here: Local Notation foo := (forall {n : nat}, n = n).
Definition bar : foo := fun _ => eq_refl.
Check bar _ : _ = _. Of course, the implicit status doesn't actually get set in v8.6, but if you're not supporting implicit status, then it should be an error message, not an anomaly. |
Currently, the check for implicit arguments is done by assertion. We could change them to errors. |
Yeah, an error would be better. Anomalies are for code that should be impossible to reach, not for when the user gives Coq garbage input. Is there any reason, though, that Coq can't support this use-case? |
@JasonGross Unfortunately, my force-push deleted @herbelin 's original review comments that asked for these asserts. Maybe he can look at this particular use-case. |
You mean #668 (comment) ? (I think you can expand the hidden review comments.) |
Yes, I meant those comments. I can see them again now, maybe the page was stale in my browser when I tried yesterday. |
I can turn any of these new asserts into errors, but maybe some of them are indeed sanity checks, rather than limitations on user input, so we should clarify which are which. |
interp/notation_ops.ml
Outdated
match_binders u alp metas na1 na2 (match_in u alp metas sigma t1 t2) b1 b2) | ||
| GProd (_,na1,bk1,t1,b1), NProd (na2,bk2,t2,b2) -> | ||
(assert (bk1 = bk2); | ||
match_binders u alp metas na1 na2 (match_in u alp metas sigma t1 t2) b1 b2) |
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.
Hi Paul, the anomaly comes from here presumably. These two assert
should be when binding_kind_eq bk1 bk2
(but an "=" should be ok also).
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.
@herbelin Thanks, should any of the other assert
s in the diffs also be turned into guards?
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.
Should be ok, I think.
I've changed the assert identified by @herbelin to a guard, also the neighboring similar match-case. There may be others to change to guards, or to errors. |
I'd suggest also adding an output test-case in notations that makes use of implicit binders, since apparently there wasn't one before: Notation foo := (forall {n : nat}, n = n).
Axiom f0 : foo. Check f0 _ : _ = _.
Notation "&& x .. y , B" := (forall x , .. (forall y , B) .. ) (at level 0, x binder, y binder).
Axiom f1 : && x y , (x = y :> nat). Check f1 _ _ : _ = _.
Notation "&& x .. y , B" := (forall x , .. (forall {y} , B) .. ) (at level 0, x binder, y binder).
Axiom f2 : && x y , (x = y :> nat). Check f2 _ _ : _ = _.
Notation "&& x .. y , B" := (forall {x} , .. (forall {y} , B) .. ) (at level 0, x binder, y binder).
Axiom f3 : && x y , (x = y :> nat). Check f3 _ _ : _ = _.
Notation "&& x .. y , B" := (forall {x} , .. (forall y , B) .. ) (at level 0, x binder, y binder).
Axiom f4 : && x y , (x = y :> nat). Check f4 _ _ : _ = _. (Should that be |
I'd like to have a look into this before merging if not urgent, thanks! |
| GLambda (_,Name x,bk1,t_x,c), GLambda (_,Name y,bk2,t_y,term) | ||
| GProd (_,Name x,bk1,t_x,c), GProd (_,Name y,bk2,t_y,term) -> | ||
if (bk1 = Implicit || bk2 = Implicit) then | ||
error "Implicit arguments found in recursive parts of a binder" |
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.
By the way, an improvement: one could use user_err
here with the loc taken in one of the GLambda
or GProd
which has an Implicit
.
@JasonGross: The notations 3 to 5 should fail with an error Also, there is no support for I start to feel that we are somehow opening a can of worms. Maybe the |
@herbelin Worms are bad. I'll back off from this PR, unless someone really wants me to push forward. |
@pstecker: Somehow, I would eventually lean towards having implicit arguments annotations in |
I feel like implicits are about references rather than part of a type somehow. ie Definition Id := forall {A}, A -> A.
Definition id : Id := fun A a => a. If this worked would |
@SkySkimmer: Maybe. We need to think more at where |
@ejgallego do you still plan to have a look at this? |
@maximedenes From the discussion, it doesn't look like there's a consensus on how to handle the issue in 3357. If @ejgallego is planning to re-work the Notation machinery, anyway, maybe it's best to give up on this PR. |
Hey @maximedenes I'd like to but I am a bit busy, what does @herbelin think ? Is the bug really critical or we could live with it. My limited understanding of the issue is that it seems a bit complicated; @psteckler my plans for notations are not going well in the sense that I am far from satisfied with the current status of my ideas, so don't count on them for now. |
The bug is not critical and we could resolve it by issuing a warning when a Even, if I feel that in the longer term, something remains to be done. The |
So this PR is actually orphaned. One question, as briefly discussed in CEP#19 , notation unfolding and implicit resolution could be seen as a separate phase, however as @silene has pointed out in the past one has to be careful of capturing the proper "implicit" closure when unfolding notations. So, my question is, do we have a written specification of how internatilization is supposed to work? If not, should we try to come up with a more or less complete description on what the interaction between notations and implicits should be before we continue to work in this PR? |
@herbelin should we close this PR? |
As far as I'm concerned, it can be closed (orphaned and requiring more thinking time). |
Ok, closed, noted left in #3357 so if someone ever tackles the bug again they can track it. |
Notations were converting implicit arguments to explicit arguments in notations when using the NProd and NLambda constructors of
notation_constr
, because there was nothing in those constructors to mark implicitness.Adding a
binding_kind
field for those constructors allows marking implicitness, and propagating it to the stored pattern.The test
3357.v
has theFail
removed, and the file moved tobugs/closed/
.