Special constructor for %sys_argv primitive#10570
Conversation
|
To reproduce the bug: This caused a compilation failure in the obytelib library when compiling with Flambda 2 (which depends more strictly on the primitive arities). A pull request has already been made there to work around this issue (bvaugon/obytelib#5). |
|
Playing devil's advocate: Mis-using a Suggesting a simpler fix: in the current code it might work to just ignore the extra arguments: |
|
Playing deviler advocate: |
|
To clarify a bit:
This patch does not fix a real bug, but brings consistency to the code translating external primitives so we thought it was worth submitting as a PR here in addition to the PR on the Flambda 2 branch. |
gasche
left a comment
There was a problem hiding this comment.
I discussed this issue again with @zapashcanon (we are both attending the Mirage retreat): we looked for an OCaml implementation of the OCaml bytecode interpreter, and found obytelib which suffers from a minor bug coming from this issue (see bvaugon/obytelib#5 ).
The PR code looks clean to me -- it adds a distinction between two distinct things. I am in favor of merging.
lambda/translprim.ml
Outdated
| let primitive_needs_event_after = function | ||
| | Primitive (prim,_) -> lambda_primitive_needs_event_after prim | ||
| | External _ -> true | ||
| | External _ | Sys_argv -> true |
There was a problem hiding this comment.
Nitpick: the Sys_argv case should go on line 938 below, where the other non-external primitives are handled.
There was a problem hiding this comment.
I don't agree: External and Sys_argv are both compiled to Pccall, which makes it natural to handle them in the same place.
Lines 937-938 are mostly about generic application primitives (plus Lazy_force), so I don't think Sys_argv fits particularly well there.
There was a problem hiding this comment.
Actually caml_sys_argv returns a constant directly (it does not raise an exception or allocate), so we could return false here if I understand correctly. In any case, I don't like the proposed code organization because it has no apparent structure (if I add a new primitive, where should it go? There are two groups now). Before there was "External _" and "everything else", this is clear and I think we should follow it. If you have a different structure in mind, there must be a comment that explains what it is.
There was a problem hiding this comment.
As I understand it:
- If it calls a C function, it should have an event. This covers
ExternalandSys_argv. The code inlambda_primitive_needs_event_afterspecifies that if the primitive does not allocate or raise, an event is not needed, butcaml_sys_argvis declared with~alloc:trueso for consistency it should get an event. - If it calls an OCaml function, it should have an event. That is the case for
Lazy_force, the variousSendprimitives, and theApplyprimitives.
Basically, I don't agree with your classification of lines 937-938 as "everything else". To me it looks like "everything that calls an arbitrary OCaml function", which is not the case for Sys_argv. If you want to put it in lines 939-943, then fine (this does look like "everything else" to me), although we should probably update the declaration of prim_sys_argv to ~alloc:false for consistency if we do that.
There was a problem hiding this comment.
I've pushed a commit which adds a Changes entry and a comment here. I will merge later this week if I don't forget.
There was a problem hiding this comment.
I think that this is worse than before because (1) whether a primitive is implemented by a C call or not is not a property of the primitive at the Lambda level, only of the implementation choices further down, (2) this is more work to classify as we have to check if stuff emits a C call or not. But you seem very convinced that this new distinction is a good idea -- it is probably not just your esprit de contradiction at my earlier comment -- and you know the middle-end better than I do... oh well.
There was a problem hiding this comment.
Keep in mind that this is not even the Lambda level, this is an intermediary step restricted to Translprim. In terms of Lambda, Sys_argv doesn't exist, we only see a regular Pccall.
There was a problem hiding this comment.
Also: the whole point of this function is to decide whether the primitive needs an event or not. The primitive needs an event if it can call arbitrary OCaml code, either directly or by running a GC. So you cannot avoid, at this point, having to know exactly how each primitive is implemented; otherwise you cannot make the right decision.
The conservative decision (for the cases where you're not sure if you need an event or not) is to always add an event. Previously, the only case fitting that was the External case (where we would always add an event, even if the primitive was declared with [@noalloc]). So in a way, this is the right place to put constructors where you do not really want to find out if you need an event or not. All the other cases have clear reasons why they require an event or not, and adding unrelated constructors to them would only make the code more confusing.
|
Thanks @gasche for moving this PR forward (and thanks @Keryan-dev for submitting the patch). |
This fixes a bug in the case
caml_sys_argvwould be called directly by the user.This would end up as a function with two arguments because it would get caught in the special case of
ocaml/lambda/translprim.ml
Lines 651 to 652 in 7f58ef0
that was meant to handle
%sys_argv.Somehow, the additional argument doesn't seem to bother the compiler but it might lead to runtime errors.
The additional constructor avoid this by allowing
caml_sys_argvto be handled as any other C call.