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
type_let: be more careful generalizing parts of the pattern #2317
Conversation
When reading this PR description and code I was very confused as to why the interactive toplevel would need special a type-checking path. By |
No, I really mean the toplevel as in "REPL", and it does because of this: https://github.com/ocaml/ocaml/blob/trunk/toplevel/toploop.ml#L286 |
Btw: I could move the changes related to the toplevel to a separate commit @gasche, it would probably produce a nicer history. Also, I don't think I said it explicitly, but I think this should go in 4.08. |
I don't feel qualified to review this PR. One person that I think would have useful things to say about this is François @fpottier Pottier (I remember discussing generalization of patterns with him, for example during Jonathan Protzenko's M2 internship), but given that he is not involved in the nitty-gritty details of the implementation that may require a bit more high-level presentation of the situation. (And I don't know if he would be interested.) |
Ok, I'll look at this next week. |
I'm not sure I understand your argument for not reverting to the previous behavior of iterating on all pattern types, rather than just types associated to pattern variables (I think I had missed this change). |
I'm not sure what you're referring to. If you are saying I should revert #1745, then there is some misunderstanding, because that is not the source of the issue here. The issue manifested when we removed this line and the one that follows it, and is IMO due to this line that I remove in the present PR. To explain again why #1745 was needed, and why I think this PR has the right approach, let me start by reacting to:
That is correct. However I believe it is expected that people fetching types from cmt files will call Indeed, while I'm not sure whether the invariant you stated was already violated for patterns (though I'd guess it was), I can say it definitely is for expressions, take this example: type 'a t = { x: 'a list }
let l = { x = [] }.x If you look at the type of
The only part of the type which is generalized is the part that survives the expression. As a result of #1745 and the present PR, we would be in a similar state for patterns: the only parts that are generalized, are the ones that escape the patterns, that is: the types of the pattern variables. The reason why we want to be in this state for patterns (that is: the initial reason for #1745), is that before generalizing any pattern type, we're checking that it can actually escape the pattern, which might not always be the case: remember that we now allow GADTs under or-patterns. type 'a t =
| I : int * bool -> int t
| C: char * bool -> char t
let f (type a) (x : a t) =
match x with
| I (3, b)
| C('c', b) -> b
| _ -> false Here In |
I'm of course aware of the situation for expressions, where the assumption is that one will not reuse the internal type of an expression after Your explanation about why one cannot iterate on patterns with However, I'm still wondering why you wouldn't keep the following iterative call to As to why there was a call to |
Well, it might not be a problem... as long as you also call So at this point, Sure, that invariant is cute, but I'm honestly not very enthusiastic about adding gratuitous operations just to preserve an invariant that isn't documented anywhere.
Yes, I'm not a fan either. |
I agree that there is not clear reason that the behavior with patterns should be different from that with expressions. I'm always afraid of removing an invariant without knowing why it was there in the first place, but if you have done enough checks and tests, then it is probably fine. At this point it is hard to justify making the code more complex than it was before. Then I think I would be happy with doing as you suggest, i.e. replacing the |
511a4ac
to
433c13d
Compare
I did the change we talked about, and rebased on top of trunk. I'll wait for your explicit approval before merging. @damiendoligez (or @gasche as substitute): since this fixes a regression I'm planning to cherry-pick to 4.08. |
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.
Fine to merge. I would prefer a shorter comment, but I leave that to you.
type_let: be more careful generalizing parts of the pattern (cherry picked from commit 1211b9c)
Thanks @gasche. |
This fixes a bug noticed when trying to build merlin on 4.08: bindings to polymorphic record fields introduced by
let
s of expansive expressions are not polymorphic anymore (they were in 4.07).See the example added by the first commit.
In #1748, we removed some calls to
generalize
fromtype_pat
, assuming that they were needed only for the check that followed in the next few lines. Indeed, the variables would get generalized later fromtype_cases
, so there is no need to generalize then and there.However, in the case of a let, since we're first typing the pattern and then the expression, we might end up calling
generalize_expansive
on that binding before callinggeneralize
, which will then do nothing.The issue could be fix by reintroducing the call to
generalize
intype_pat
, but that felt a bit weird. Also, it didn't answer the question: why isgeneralize_expansive
called on the type of the pattern variable? Question which can also be read as: "why isgeneralize_expansive
called on every subpattern?".As a result, the patch proposed here ends up "cleaning up" the generalization story inside
type_let
in a way that is very similar to #1745 (the subtleties come from the fact that the binding might be recursive).And then some tests (the first two real tests of
testsuite/tests/typing-warnings/application.ml
) started failing, because the toplevel/expect_test has some special handling oflet _ =
, and so some plumbing was added to make sure the type which is printed is still generic (search for "if toplevel
").