Conversation
typing/printtyped.ml
Outdated
| | None -> line i ppf "value_mode <modevar>\n"); | ||
| ( let consts = Btype.Value_mode.check_const x.exp_mode in | ||
| line i ppf "value_mode "; | ||
| line 0 ppf (match fst consts with |
There was a problem hiding this comment.
This is somewhat ugly as I couldn't find a way to combine two format strings (the way '^' acts on normal strings). Is there a nice operator for that?
There was a problem hiding this comment.
There is but generally I try to avoid it. I would write this by lifting out these two matches into functions called fmt_locality and fmt_uniqueness and then call them using %a. I'd also avoid using fst and snd by replacing let consts with let locality, uniqueness.
typing/printtyped.ml
Outdated
| | None -> line i ppf "value_mode <modevar>\n"); | ||
| ( let consts = Btype.Value_mode.check_const x.exp_mode in | ||
| line i ppf "value_mode "; | ||
| line 0 ppf (match fst consts with |
There was a problem hiding this comment.
There is but generally I try to avoid it. I would write this by lifting out these two matches into functions called fmt_locality and fmt_uniqueness and then call them using %a. I'd also avoid using fst and snd by replacing let consts with let locality, uniqueness.
| ; | ||
| fun_binding: | ||
| strict_binding | ||
| fun_binding_gen(wrap): |
There was a problem hiding this comment.
I think we should spend a bit more time trying to find a simpler solution to this part. The simplest approach that might work would be to separate out the list of parameters from the final EQUAL seq_expr or type_constraint EQUAL seq_expr in a strict_binding. That would allow the type_constraint to get treated differently depending on context. Probably that will require adding a type like:
type parameter =
| Ordinary of arg_label * expression option * pattern
| Type of Longident.t listto represent the parameters before they are folded onto the body expression.
lpw25
left a comment
There was a problem hiding this comment.
I've looked over all of parsing and typing again
parsing/parser.mly
Outdated
| Exp.mk ~loc:Location.none (Pexp_extension(local_ext_loc, PStr [])) | ||
|
|
||
| let mkexp_stack ~loc exp = | ||
| let mkexp_stack ~loc exp = |
There was a problem hiding this comment.
Looks like an accidental whitespace change
| (* the GNU Lesser General Public License version 2.1, with the *) | ||
| (* special exception on linking described in the file LICENSE. *) | ||
| (* *) | ||
| (**************************************************************************) |
There was a problem hiding this comment.
Leaving it like this is fine.
typing/mode.ml
Outdated
| let newvar () = Amodevar (fresh ()) | ||
|
|
||
| let newvar_below = function | ||
| | Amode c when L.eq_const c L.min_const -> Amode L.min_const, false |
There was a problem hiding this comment.
Amode L.min_const is already available as min_mode.
typing/mode.ml
Outdated
| let newvar_below { locality; uniqueness } = | ||
| let l = Locality.newvar_below locality in | ||
| let u = Uniqueness.newvar_below uniqueness in | ||
| { locality = fst l; uniqueness = fst u }, snd l || snd u |
There was a problem hiding this comment.
Better to use patterns to destruct tuples than to use projections
typing/printtyp.ml
Outdated
| | Some Global -> Olm_global | ||
| | Some Local -> Olm_local | ||
| | None -> Olm_unknown | ||
| and uniqueness = match uniqueness with |
typing/typecore.ml
Outdated
| and the update is not unique *) | ||
| let is_unique_with = has_unique_attr_exp sexp in | ||
| let expected_mode = if is_unique_with | ||
| then mode_unique (mode_subcomponent expected_mode).mode |
There was a problem hiding this comment.
This doesn't need the mode_subcomponent. That is needed when we do an allocation, but here we are really doing a mutation.
typing/typecore.ml
Outdated
| in | ||
| let closed = (opt_sexp = None) in | ||
| let rmode = Value_mode.regional_to_global expected_mode.mode in | ||
| let rmode = Mode.Value.regional_to_global expected_mode.mode in |
There was a problem hiding this comment.
This mode needs to be different if this is a { unique_ ... with ... } expression. The values used for these fields might be escaping an unknown number of regions, just like for a mutation. That means that they will need to be at a global mode.
typing/typecore.ml
Outdated
| | Global -> Value_mode.global | ||
| | Nonlocal -> Value_mode.local_to_regional rmode | ||
| | Global -> Mode.Value.global (* lbl_global==Global also implies Shared *) | ||
| | Nonlocal -> Mode.Value.local_to_regional rmode |
There was a problem hiding this comment.
lbl_global==Nonlocal should also imply Shared
typing/typecore.ml
Outdated
| | Nonlocal -> Mode.Value.local_to_regional rmode | ||
| | Unrestricted -> rmode | ||
| in | ||
| let mode, _ = Mode.Value.newvar_above mode in |
There was a problem hiding this comment.
We shouldn't bother making this new mode variable if the label is global. Maybe we should also only do it if the srecord is an ident or a projection from an ident (or a projection from a projection from ...).
We should also only make a variable for the uniqueness part, rather than for the whole mode.
There was a problem hiding this comment.
Maybe we should also only do it if the srecord is an ident or a projection from an ident (or a projection from a projection from ...).
I have not implemented this.
typing/typecore.ml
Outdated
| if has_local || has_unique then begin | ||
| let mode = match has_local, has_unique with | ||
| | true, true -> Mode.Alloc.local_unique | ||
| | true, false -> Mode.Alloc.local |
There was a problem hiding this comment.
I think that this case (and the one below) is too restrictive. It should probably have a variable for its uniqueness. That will allow code like the following (maybe put this in the testsuite):
let foo : local_ unique_ string -> unit = fun (local_ s) -> ()
let bar : local_ unique_ string -> unit = fun (unique_ s) -> ()The additional variable (and the assert false below) could also be avoided by pulling the equality constraint up into these branches and using more specific equality functions (e.g. using equality on just locality rather than the whole mode).
…rrect handling of named args)
lpw25
left a comment
There was a problem hiding this comment.
I made a review pass over the middle-end and back-end parts.
lambda/translcore.ml
Outdated
| They could be Alloc_local, but that would require changes | ||
| to pattern typing, as patterns can close over them. *) | ||
| Lprim (Pmakeblock (Obj.object_tag, Immutable_unique, None, alloc_heap), | ||
| Lprim (Pmakeblock (Obj.object_tag, Immutable, None, alloc_heap_unique), |
There was a problem hiding this comment.
I think this part should be reverted. For annoying reasons that may go away one day Immutable_unique is not quite the same as Immutable and alloc_heap_unique.
| | Alloc_shared | ||
|
|
||
| (* CR-soon: This can be private once it is possible to pattern-match on private aliases *) | ||
| type alloc_mode = locality_mode * uniqueness_mode |
There was a problem hiding this comment.
I think that the alloc_modes in Lsend, lfunction and lambda_apply can all be replaced by just locality_modes. This is because they are all related to the allocation mode of closures, and closures can't actually be mutated even if they are unique. I think the uniqueness part of those modes is currently just being ignored.
There was a problem hiding this comment.
The one in Assignment can also probably just be a locality_mode. I don't think uniqueness affects the write barrier.
lambda/translcore.ml
Outdated
| @@ -553,7 +555,7 @@ and transl_exp0 ~in_new_scope ~scopes e = | |||
| Lprim (Pmakearray (kind, Immutable, mode), ll, | |||
There was a problem hiding this comment.
I think that the mode here should probably always be global_shared. This value can't be local and it will never be used uniquely: it is only used as a template for making new values of mode mode.
lambda/translcore.ml
Outdated
| let maybe_unique_update_on = | ||
| match opt_init_expr with | ||
| | None -> None | ||
| | Some exp -> match Builtin_attributes.has_unique exp.exp_attributes with |
There was a problem hiding this comment.
It would be good to have opt_init_expr already have some explicit data saying whether the update is a unique one. It's best if we only actually check for attributes once in the type-checker, and after that use normal types and data.
lambda/translcore.ml
Outdated
| (fun (_, r) -> match r with | ||
| | Reuse_keep -> false | ||
| | Reuse_set _ -> true) | ||
| (List.combine ll reuses)) in |
There was a problem hiding this comment.
You can use List.filter_map to make this simpler.
lambda/translcore.ml
Outdated
| | Ok false | Error _ -> None | ||
| | Ok true -> Some (transl_exp ~scopes exp) in | ||
| let is_unique_update_on = | ||
| match maybe_unique_update_on with | None -> false | Some _ -> true in |
There was a problem hiding this comment.
I think it would be better to just have is_unique_update_on and get rid of maybe_unique_update_on.
lambda/translcore.ml
Outdated
| Lprim(Pmakeblock(0, mut, Some (Pgenval :: shape), mode), | ||
| slot :: ll, loc) | ||
| end | ||
| | Some id -> |
There was a problem hiding this comment.
I think it would be better to use Lvar init_id in place of id and then bind that to the init expression, just like in the non-unique case.
lambda/translcore.ml
Outdated
| begin match opt_init_expr with | ||
| None -> assert false | ||
| begin match opt_init_expr, maybe_unique_update_on with | ||
| | None, _ | _, Some _ -> lam |
There was a problem hiding this comment.
So this case would become just | None -> and not match on maybe_unique_update_on
asmcomp/printcmm.ml
Outdated
| | Calloc Alloc_heap -> "alloc" ^ location d | ||
| | Calloc Alloc_local -> "alloc_local" ^ location d | ||
| | Calloc (Alloc_heap, _) -> "alloc" ^ location d | ||
| | Calloc (Alloc_local, _) -> "alloc_local" ^ location d |
There was a problem hiding this comment.
I think that we probably can just use locality_mode rather than alloc_mode for everything in cmm. That's because there is no lifting of allocations after this point, so we only need to know whether to put them on the stack or the heap.
asmcomp/printmach.ml
Outdated
| | Ialloc { bytes = n; mode = Alloc_heap, _ } -> | ||
| fprintf ppf "alloc %i" n; | ||
| | Ialloc { bytes = n; mode = Alloc_local } -> | ||
| | Ialloc { bytes = n; mode = Alloc_local, _ } -> |
There was a problem hiding this comment.
As with cmm, we probably only need locality_mode in the mach IR.
|
To release this the following items should be completed:
|
Extends Lambda.function_kind to track modes of curried functions. The supported modes have a heap-returning prefix, followed by a local-returning suffix, both of which may be empty. Functions which do not fit this pattern cannot be expressed as a single Lfunction, and must be explicitly curried in Lambda and Clambda. (See the new Lambda.check_lfunction for the exact invariants) Most of the changes are straightforward plumbing of the new info. Some delicate changes are needed to preserve the new invariants, in: - simplif.ml: optimisations which combine Lfunctions - translcore.ml: transl_curried_function and friends - closure.ml: optimisations for partial and over-application - cmm_helpers.ml: mode-aware variants of caml_curry Caveat: build_apply in translcore is wrong
|
Closed in favour of #138, which has now been merged in the other repo. We still need to add borrowing and support for in-place record update. |
This PR tracks the ongoing development of uniqueness mode.