Better error reporting in case of missing 'rec' in let-bindings#1472
Better error reporting in case of missing 'rec' in let-bindings#1472gasche merged 6 commits intoocaml:trunkfrom
Conversation
gasche
left a comment
There was a problem hiding this comment.
I haven't done a full review of the patch yet. Two remarks:
- I think it would be nice if the patchset was nice. Here you have Arthur's ugly patch first, and then a more general approach as a diff over it. Maybe it would be possible to add the more general mechanism first, and then implement Arthur's idea on top of it? This impacts the code: in 293b522 you document your
Env_ghost_valueby saying that it is aboutlet rec, but this is wrong, the explanation is over-specialized. This should be a more generic concept.
- You chose to implement "ghost value bindings" as one extra kind of environment. I find it surprising as I would rather have expected that you would add a
Val_ghostkind to the "value kinds" stored in the value environment. I don't know which of the two approaches is best, but I think it deserves discussion.
Having a separate environment means that there is no ordering between value bindings and ghost value bindings. Having a single environment means that ghost bindings will shadow value bindings of the same name and conversely. In your case it doesn't really matter as the "unbound identifier" error will only arise if there are no normal-value-bindings for that name, but for other uses of ghost bindings, what would be the better behavior?
(One thing that is a bit strange about having it as an extra part of the environment is that it is only about value names, not type names or module names.)
typing/env.ml
Outdated
| comp_components = Tbl.empty; comp_classes = Tbl.empty; | ||
| comp_cltypes = Tbl.empty } | ||
| comp_cltypes = Tbl.empty; | ||
| comp_ghost_values = Tbl.empty; } |
There was a problem hiding this comment.
You should break a line before } to make future additions a one-line diff.
typing/env.ml
Outdated
| comp_components = Tbl.empty; comp_classes = Tbl.empty; | ||
| comp_cltypes = Tbl.empty } in | ||
| comp_cltypes = Tbl.empty; | ||
| comp_ghost_values = Tbl.empty; } in |
|
Implementing "ghost value bindings" as a different value kind should indeed result in a cleaner and less invasive patch. I'll rebase the patch and do that. |
|
However, with this way of doing things it seems harder to convince myself that I don't break things: with a separate environment, it's impossible for existing parts of the code to look up ghost bindings by mistake. With a different value kind, it seems possible or even likely for the new "ghost" kind to be matched by a wildcard pattern somewhere and handled as something else. |
|
I guess that Note that there is also |
|
Armael (2017/11/11 13:26 +0000):
However, with this way of doing things it seems harder to convince
myself that I don't break things: with a separate environment, it's
impossible for existing parts of the code to look up ghost bindings by
mistake. With a different value kind, it seems possible or even likely
for the new "ghost" kind to be matched by a wildcard pattern somewhere
and handled as something else.
Sure, but having a separate environment does, IMO, not scale. You
wouldn't imagine adding one new kind of environment for every different
kind of value you'd want to handle, would you?
To prevent the kind of problem you are mentionning, i.e. exhaustive
pattern illegitimately matching your ghost values, isn't it okay to
simply get rid of all the exhaustive patterns in the code? I didn't look
into it myself so no idea whether this is realistic, but I think we have
a warning that can be turned into an error to have the compiler
verifying such kind of matchings do not occur, right?
|
Yes, I agree it was a bad idea. I like the idea of reusing |
By playing with the idea of reusing Adding a ghost binding in the environment of the lhs of the second let ( |
|
You have to check that no identifier of that name already exists in scope if you want to add a ghost/unbound binding for it. If a name already exists, there is no point in adding the ghost binding anyway, as there will not be an unbound-identifier error later. |
ae58c1c to
fae85a6
Compare
|
I pushed a new patchset, based on the idea of reusing |
|
Umm, travis seems to have failed, but I have no idea why? |
|
Travis is only failing due to the lack of new change entry in Changes. |
|
Thanks. I added a Changes entry. |
|
@alainfrisch, I think we need your opinion on the |
Yes, I think it is reasonable, the code looks good, and I agree there might be other reasons in the future to need the flag anyway. I'm only wondering whether we shouldn't make the parameter non-optional. It'd be even more invasive (and not really related to the goal of this PR), but this might catch instances where marking is actually not desired, and at least make things more explicit. |
I could submit an other PR that would do that (but maybe after this one is merged?). |
|
Just an out-of-curiosity question.
Would it make sense to have two families of lookup functions, one that
marks and one that does not?
|
Yes, ok, no need to "pollute" this PR.
There is a risk of combinatorial explosion if we ever need to introduce other flags on the lookup mechanism. And even if we don't, having a single |
gasche
left a comment
There was a problem hiding this comment.
I like the final state of the patch and I'm considering merging it (sometime next week?). @garrigue, @alainfrisch, if you have second thoughts about it, now is the time to voice them.
|
I just pushed a batch of small tweaks. These should be rebased into the previous ones before merging, but have not been rebased yet for easy reviewing. |
|
The tweaks are good (I just reviewed them) but they don't deserve separate commits. I don't want to squash the whole PRs because the first 4 commits are cleanly separated. Could you squash the tweaks into the relevant commit(s)? |
Original design and idea by Arthur Charguéraud
149c7af to
804774a
Compare
|
Done. |
Changes
Outdated
|
|
||
| - GPR#1472: Improve error reporting for missing 'rec' in let-bindings | ||
| (Arthur Charguéraud and Armaël Guéneau, with help from Gabriel Scherer and | ||
| Frédéric Bour) |
There was a problem hiding this comment.
This should be in the "Compiler user-interface and warnings" category, I think.
804774a to
0b2d2ce
Compare
|
In #1678, @xclerc proposed with an example where the warning is inappropriate: # let myvar1 = 0;;
val myvar1 : int = 0
# let myvar2 = myvar2 + 1;;
Error: Unbound value myvar2.
Hint: You are probably missing the `rec' keyword on line 1.@mshinwell reports that there are many wrong hints given when working on the Flambda codebase, and proposes to revert this PR for the time being, which sounds like a reasonable idea. |
|
In preparation for the 4.07 freeze and after discussing with @mshinwell and @Armael, I have reverted this PR (commit 90fbe53), except for commit 3b77d91 (Generalize Env.lookup_* functions to allow disabling marking), which is an independent change and used by a subsequent PR. A modified version will have to be proposed, see #1678. |
This is a first attempt at splitting "easy" and self-contained bits of PR #102 by @charguer, and follows an offline discussion involving @charguer, @gasche, @let-def and myself.
As described in http://chargueraud.org/research/2015/ocaml_errors/ocaml_errors.pdf (section 5,
Message for missing "rec"), the purpose of the code extracted in this PR is to provide better error reporting for the case of a forgottenreckeyword.In essence, what this PR does is additionally track in the environment "ghost" bindings for the non-recursive let-bindings that have been encountered. Then, in case of an unbound identifier, we additionally check if this identifier is present as a ghost binding in the environment. If this is the case, we display a error message indicating that a
reckeyword may be missing at the binding site of this identifier.In Arthur's original patch (and in the first commit of this PR), ghost bindings were implemented by adding a special string marker at the beginning of normal identifier. As this is relatively fragile, I rewrote this mechanism by adding a new kind of "ghost" bindings to typing environments.
This is my first dive in this part of OCaml's source code, so it is very possible that this patch contains rookie mistakes. I'll be happy to take remarks and criticism :-) .