-
Notifications
You must be signed in to change notification settings - Fork 98
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
Update trunk AST #407
Update trunk AST #407
Conversation
This change is semantically incorrect but may be enough to support trunk for 5.0 code
Thanks for the PR! Indeed, now that we have a "trunk" branch we should be reactive with parsetree changes... In this case, I don't think the PR is enough though:
I'll push an updated version fixing those two points soon, on this PR if you don't mind. About the segfaults in |
@shym, thanks for opening the PR! It's great that we have help maintaining this branch! @panglesd, thanks for looking into the segfaults! About the migrations, @panglesd: We are in a new situation here. As @shym pointed out, the compiler parsetree change we're adapting to here is a new parsing feature, not a new syntax feature as usual. So we have two options:
The downside of 1. is that when using this Ppxlib, people will test |
My choice was not so elaborate, as I was consciously just tricking the typechecker into accepting to build on |
Oh, is "and conversely" possible? My understanding was that the parsetree was enriched in a way that e.g. (copying Octachron's example from the PR)
and
yield the same parsetree before the change and different parsetrees after the change. In that case, a perfect mapping from "how it would have been parsed by 5.0" to "how 5.1 parsed it" wouldn't be possible. However, I've only superficially skimmed the parsetree change we're talking about. @panglesd , you've looked into it in more detail, haven't you? Could you let me know if this understanding is right?
👍 |
Yes. So, in all OCaml version, let f: type a b c. a -> b -> c = fun x y -> ... is supposed to be equivalent to let f: 'a 'b 'c. 'a -> 'b -> 'c = (fun (type a) (type b) (type c) -> (fun x y -> ... : a -> b -> c)) In OCaml 5.0.0 and below, the first version was directly transformed into the second when building the parsetree. So, parsing one or the other would yield the same parsetree. However, there is also the need to pretty-print the parsetree, and there are some efforts in the let f : int -> int = fun x -> x (*short form *) is parsed and pretty-printed into let (f : int -> int) = (fun x -> x : int -> int) (* long form *) The pretty-printer tries to distinguish whether the parsetree comes from a parsed short form or from a parsed long form. For polymorphic types, it's not really possible: so the pretty-printer assumes it comes from the short form whenever that's compatible with the parsetree it has. For non-polymorphic ones, it can distinguish between the two forms, since they are actually encoded differently: the short form uses an empty In OCaml 5.1 and above, the two forms are represented differently in the parsetree, (but they are equivalent later on). This has two advantages:
So, migrating from 5.1 to 5.0 is easy, since we know in which situation we are.
|
Signed-off-by: Paul-Elliot <[email protected]>
Signed-off-by: Paul-Elliot <[email protected]>
I added the two migrations. Are there some tests checking that the migration are sound? I haven't looked at the segfaults yet... |
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, thanks a lot for taking the time to explain the compiler change in so much detail, @panglesd! So, from what I understand, we do have the two options 1. and 2. I mentioned above, and in the case of parsing features like here, we all agree that 1. is the better option. It is important, though, that we're aware that we're changing the compiler behavior in a way.
Thanks for implementing this in a solid way! I haven't looked through the details, but it seems right. Is there any part you have doubts about?
Are there some tests checking that the migration are sound?
That depends on what you call "sound". Do you mean a test checking that first upward migrating and then downward migrating yields the identity?
let typ_poly = | ||
{ | ||
typ with | ||
ptyp_attributes = []; |
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.
Why do you remove the attributes here and everywhere else? Are they duplicated, and you're de-duplicating them?
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 is because my code is misleading... I hesitated writing it like this and probably should not, but the result was shorter... (very bad reason)
I'm actually creating a new node, with the same location value as typ
. I'm modifying all other fields (attributes
and desc
), and as you can see, ptyp
is used in desc
: so the attributes are just in the original node.
Now that I checked, it seems that some of the locations in the crafted nodes should be "ghosted" to make the test "parsing with 5.1 then migrating to 5.0 == parsing with 5.0" pass. But not all of them...
If there is a convenient way to test this, and get a diff, that would be great!
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.
Cool, thanks for explaining!
About the locations: to make sure that the migrated parsetree fulfills the two location invariants "non-ghost children are nested" and "non-ghost siblings are disjoint and ordered", you could run a trivial ppxlib driver with -perform_locations_check
. With "trivial" driver I mean one with no PPXs linked. However, I think that those two invariants should be fulfilled here anyways. You'll know better than me! I don't think we have more location checks than that.
If there is a convenient way to test this, and get a diff, that would be great!
You might already have thought about the following, but just in case: The following should work I think. You could write a few-liner program using Ppxlib, which parses a file and pprints its ast. Then, to get the "parsing with 5.1 then migrating to 5.0" AST, you can compile that with OCaml trunk
and this Ppxlib branch. And for the "parsing with 5.0", you can compile it with OCaml 5.0 and the main
Ppxlib branch. Btw, if we want to take more time at some point, it would also be valuable to make a CI job out of this!
(About the "parsing with 5.1 then migrating to 5.0" part: When pprinting an AST, Ppxlib pprints the AST in its Ppxlib version, i.e. currently 5.0/4.14. In my opinion, that's not an ideal choice of ours, but that's a different topic...Also, in this particular situation it comes in nice.)
I'm not sure what you mean by "feature" the change is just a change in the way some information is encoded in the parsetree: no new functionallity/syntax! I don't think ppxlib failing whenever there is a let-binding with a type constraint (such as in
Could you precise? I'm not sure to understand, since I wouldn't say we are changing the compiler behaviour!
That would be one good test, but I would also like to test that parsing with 5.1 is the same as parsing with 5.0 and then migrating to 5.1 ; and similarly, that parsing with 5.1 and migrating to 5.0 is the same as parsing with 5.0! |
Regarding the segfault, it indeed appears exactly at the commit that brings the change. |
When the magic numbers aren't unique, before this commit, the binary AST reader would read into the first AST version matching the magic number. With this commit, it takes the last such version. That's important for trunk support. Signed-off-by: Sonja Heinze <[email protected]>
Thanks, @shym , that's a very very good point I've just pushed a one-liner change that should fix the problem to a separate branch: https://github.com/pitag-ha/ppxlib/tree/shym_trunk_support_update. I don't have a 5.1 switch yet, so I haven't tested it yet, but I'm pretty sure that it should fix the segfaults. I'll test it tomorrow and if it does what I expect, I'll push it to your branch. |
@panglesd , about your answer:
I was calling the change a "parsing feature"/"parsetree feature", because it enriches the AST by making it more concrete: with the change, the parsetree distinguishes syntax that before wasn't distinguished. I was explicitly saying that this is different from syntax features. We can also simply call it "parsing change" instead of "parsing feature", though.
Hah - that's a good point! I was assuming the new Start edit:
Finish edit
We're changing the parsing behavior: When using the trivial Ppxlib driver (i.e. the one without any PPX linked), one would expect the identity driver, and usually, that's the case. However, on this branch, the output parsetree can be different from the input parsetree. So, apart from expanding the AST, the driver also modifies the AST. If you want, I can give you an example!
Are you sure that's the case? If that's the case, what I said in the last paragraph might be different.
Yes, I agree that that'd be great to test! I've pointed out in the code comment above how we could do that. I don't know how much time you want to spend on this, though. |
Tested, approved and pushed here 😄 |
The only case when let f: 'a 'b 'c. 'a -> 'b -> 'c = (fun (type a) (type b) (type c) -> (fun x y -> ... : a -> b -> c)) (the type has to be explicitly polymorphic, otherwise the migration is the identity) I'm pretty sure such code appear in no OCaml codebase! But I agree that, if we are breaking a pre-existing invariant about AST migration, we should be cautious about this (we haven't really a choice...) Nice fix for the segfaults! |
Oh! By the way, diff --git c/ast/versions.ml w/ast/versions.ml
index 8c3456dcd9..e5e71b8fe8 100644
--- c/ast/versions.ml
+++ w/ast/versions.ml
@@ -587,5 +587,5 @@ module Find_version = struct
else
loop tail
in
- loop all_versions
+ loop @@ (module OCaml_current : OCaml_version) :: all_versions
end With that I get no segfault on either version. |
@panglesd, to come back to our conversation about this kind of parsetree change being a new situation for us: I think we were more or less agreeing all way long, but focussing on different perspectives. Both perspectives are important and I'm very glad we've had this long discussion! To clarify one last misunderstanding: Given that the only case where
that case would also be the only case in which we would error if we went for option 2. We would not error for every let binding with type constraint! |
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 don't think there's anything blocking this from being merged, is there? I'd merge this so that people have a working trunk-support
branch again. We can (and should) fix the potential location problem and add the tests we've talked about once it's merged. Does that sound good to you, @panglesd?
This branch is working excellently from me on current OCaml trunk and unblocked my development. Thanks! |
Yes, that sounds good to me! (Especially since I won't be available to work on this next week, let's not block |
My proposal for fixing the test suite on the stable compiler is available in my shym:extend-support branch. I’m fine with either including in this PR (as mostly reviewed already) or postponing it to another PR, along with the other changes suggested. |
Thank you, @shym! Could you open a PR to |
Perfectly good. I’ve opened #409 with that commit. |
ocaml/ocaml#12119 modified the parsetree on
trunk
.With this PR,
ppxlib
compiles withtrunk
and these changes in this PR are enough for our use case. As this use case doesn’t exercise the particular syntax for which parsing changed, those changes might not be enough even for an attempt to get the minimal support for 5.0 code ontrunk
.In particular,
dune runtest
reports a few segfaults.