···484484 type annotations.
485485 (Chris Casinghino, review by Florian Angeletti and Gabriel Scherer)
486486487487+- #13875, #13878: Add dedicated constructor for mutable variable access in
488488+ Cmm to prevent bugs linked to incorrect handling of coeffects.
489489+ (Vincent Laviron, review by Gabriel Scherer)
490490+487491OCaml 5.3.0 (8 January 2025)
488492----------------------------
489493
+1-1
asmcomp/afl_instrument.ml
···9898 (* these are base cases and have no logging *)
9999 | Cconst_int _ | Cconst_natint _ | Cconst_float _
100100 | Cconst_symbol _ | Creturn_addr
101101- | Cvar _ as c -> c
101101+ | Cvar _ | Cvar_mut _ as c -> c
102102103103let instrument_function c dbg =
104104 with_afl_logging c dbg
···2424val bind_load :
2525 string -> expression -> (expression -> expression) -> expression
26262727-(** Same as [bind], but does not treat variables as simple *)
2828-val bind_nonvar :
2929- string -> expression -> (expression -> expression) -> expression
3030-3127(** Headers *)
32283329(** A null header with GC bits set to black *)
+77-11
asmcomp/cmm_invariants.ml
···14141515[@@@ocaml.warning "-40"]
16161717+module V = Backend_var
1818+module VP = Backend_var.With_provenance
1719module Int = Numbers.Int
18201919-(* Check a number of continuation-related invariants *)
2121+(* Check a number of invariants around continuation and variable uses *)
2222+2323+type mutability = Mutable | Immutable
2424+2525+let equal_mutability m1 m2 =
2626+ match m1, m2 with
2727+ | Mutable, Mutable | Immutable, Immutable -> true
2828+ | Mutable, Immutable | Immutable, Mutable -> false
2929+3030+let mutability_to_string m =
3131+ match m with
3232+ | Mutable -> "mutable"
3333+ | Immutable -> "immutable"
20342135module Env : sig
2236 type t
···2640 val handler : t -> cont:int -> arg_num:int -> t
27412842 val jump : t -> cont:int -> arg_num:int -> unit
4343+4444+ val bind_var : t -> V.t -> mutability -> t
4545+4646+ val bind_params : t -> (VP.t * _) list -> t
4747+4848+ val use_var : t -> V.t -> mutability -> unit
29493050 val report : Format.formatter -> bool
3151end = struct
3252 type t = {
3353 bound_handlers : int Int.Map.t;
5454+ bound_variables : mutability V.Map.t;
3455 }
35563657 type error =
···3859 | Multiple_handlers of { cont: int; }
3960 | Wrong_arguments_number of
4061 { cont: int; handler_args: int; jump_args: int; }
6262+ | Unbound_variable of { var : V.t; mut : mutability }
6363+ | Wrong_mutability of
6464+ { var : V.t; binding_mut : mutability; use_mut : mutability }
41654266 module Error = struct
4367 type t = error
···7498 state.errors <- ErrorSet.empty;
7599 {
76100 bound_handlers = Int.Map.empty;
101101+ bound_variables = V.Map.empty;
77102 }
7810379104 let handler t ~cont ~arg_num =
80105 if Int.Set.mem cont state.all_handlers then multiple_handler cont;
81106 state.all_handlers <- Int.Set.add cont state.all_handlers;
82107 let bound_handlers = Int.Map.add cont arg_num t.bound_handlers in
8383- { bound_handlers; }
108108+ { t with bound_handlers; }
8410985110 let jump t ~cont ~arg_num =
86111 match Int.Map.find cont t.bound_handlers with
···89114 wrong_arguments cont handler_args arg_num
90115 | exception Not_found -> unbound_handler cont
91116117117+ let bind_var t var mut =
118118+ let bound_variables = V.Map.add var mut t.bound_variables in
119119+ { t with bound_variables }
120120+121121+ let bind_params t params =
122122+ let bound_variables =
123123+ List.fold_left (fun bound_vars (var, _) ->
124124+ V.Map.add (VP.var var) Immutable bound_vars)
125125+ t.bound_variables params
126126+ in
127127+ { t with bound_variables }
128128+129129+ let use_var t var use_mut =
130130+ match V.Map.find_opt var t.bound_variables with
131131+ | Some binding_mut ->
132132+ if equal_mutability use_mut binding_mut
133133+ then ()
134134+ else record_error (Wrong_mutability { var; binding_mut; use_mut })
135135+ | None ->
136136+ record_error (Unbound_variable { var; mut = use_mut })
137137+92138 let print_error ppf error =
93139 match error with
94140 | Unbound_handler { cont } ->
···110156 cont
111157 handler_args
112158 jump_args
159159+ | Unbound_variable { var; mut } ->
160160+ Format.fprintf ppf
161161+ "Variable %a (%s) was unbound or used outside the scope of its binder"
162162+ V.print var (mutability_to_string mut)
163163+ | Wrong_mutability { var; binding_mut; use_mut } ->
164164+ Format.fprintf ppf
165165+ "Variable %a was bound as %s but used as %s"
166166+ V.print var
167167+ (mutability_to_string binding_mut)
168168+ (mutability_to_string use_mut)
113169114170 let print_error_newline ppf error =
115171 Format.fprintf ppf "%a@." print_error error
···125181let rec check env (expr : Cmm.expression) =
126182 match expr with
127183 | Cconst_int _ | Cconst_natint _ | Cconst_float _ | Cconst_symbol _
128128- | Cvar _ | Creturn_addr ->
184184+ | Creturn_addr ->
129185 ()
130130- | Clet (_, expr, body)
131131- | Clet_mut (_, _, expr, body) ->
186186+ | Cvar id ->
187187+ Env.use_var env id Immutable
188188+ | Cvar_mut id ->
189189+ Env.use_var env id Mutable
190190+ | Clet (id, expr, body) ->
191191+ check env expr;
192192+ check (Env.bind_var env (VP.var id) Immutable) body
193193+ | Clet_mut (id, _, expr, body) ->
132194 check env expr;
133133- check env body
195195+ check (Env.bind_var env (VP.var id) Mutable) body
134196 | Cphantom_let (_, _, expr) ->
135197 check env expr
136136- | Cassign (_, expr) ->
198198+ | Cassign (id, expr) ->
199199+ Env.use_var env id Mutable;
137200 check env expr
138201 | Ctuple exprs ->
139202 List.iter (check env) exprs
···163226 | Recursive -> env_extended
164227 | Nonrecursive -> env
165228 in
166166- List.iter (fun (_, _, handler, _) -> check env_handler handler) handlers
229229+ List.iter (fun (_, args, handler, _) ->
230230+ let env_handler = Env.bind_params env_handler args in
231231+ check env_handler handler)
232232+ handlers
167233 | Cexit (cont, args) ->
168234 Env.jump env ~cont ~arg_num:(List.length args)
169169- | Ctrywith (body, _, handler, _) ->
235235+ | Ctrywith (body, id, handler, _) ->
170236 (* Jumping from inside a trywith body to outside isn't very nice,
171237 but it's handled correctly by Linearize, as it happens
172238 when compiling match ... with exception ..., for instance, so it is
173239 not reported as an error. *)
174240 check env body;
175175- check env handler
241241+ check (Env.bind_var env (VP.var id) Immutable) handler
176242177243let run ppf (fundecl : Cmm.fundecl) =
178178- let env = Env.init () in
244244+ let env = Env.bind_params (Env.init ()) fundecl.fun_args in
179245 check env fundecl.fun_body;
180246 Env.report ppf
+41-12
asmcomp/cmmgen.ml
···39394040type env = {
4141 unboxed_ids : (V.t * boxed_number) V.tbl;
4242+ mutable_ids : V.Set.t;
4243 notify_catch : (Cmm.expression list -> unit) IntMap.t;
4344 environment_param : V.t option;
4445}
···6162let empty_env =
6263 {
6364 unboxed_ids = V.empty;
6565+ mutable_ids = V.Set.empty;
6466 notify_catch = IntMap.empty;
6567 environment_param = None;
6668 }
···7779let add_unboxed_id id unboxed_id bn env =
7880 { env with
7981 unboxed_ids = V.add id (unboxed_id, bn) env.unboxed_ids;
8282+ }
8383+8484+let is_mutable_id id env =
8585+ V.Set.mem id env.mutable_ids
8686+8787+let add_mutable_id id env =
8888+ { env with
8989+ mutable_ids = V.Set.add id env.mutable_ids;
8090 }
81918292let add_notify_catch n f env =
···354364 match e with
355365 Uvar id ->
356366 begin match is_unboxed_id id env with
357357- | None -> Cvar id
358358- | Some (unboxed_id, bn) -> box_number bn (Cvar unboxed_id)
367367+ | None ->
368368+ if is_mutable_id id env
369369+ then Cvar_mut id
370370+ else Cvar id
371371+ | Some (unboxed_id, bn) ->
372372+ let var =
373373+ if is_mutable_id unboxed_id env
374374+ then Cvar_mut unboxed_id
375375+ else Cvar unboxed_id
376376+ in
377377+ box_number bn var
359378 end
360379 | Uconst sc ->
361380 transl_constant Debuginfo.none sc
···654673 let inc = match dir with Upto -> Caddi | Downto -> Csubi in
655674 let raise_num = next_raise_count () in
656675 let id_prev = VP.create (V.create_local "*id_prev*") in
676676+ let env = add_mutable_id (VP.var id) env in
657677 return_unit dbg
658678 (Clet_mut
659679 (id, typ_int, transl env low,
660660- bind_nonvar "bound" (transl env high) (fun high ->
680680+ bind "bound" (transl env high) (fun high ->
661681 ccatch
662682 (raise_num, [],
663683 Cifthenelse
664664- (Cop(Ccmpi tst, [Cvar (VP.var id); high], dbg),
684684+ (Cop(Ccmpi tst, [Cvar_mut (VP.var id); high], dbg),
665685 dbg,
666686 Cexit (raise_num, []),
667687 dbg,
668688 create_loop
669689 (Csequence
670690 (remove_unit(transl env body),
671671- Clet(id_prev, Cvar (VP.var id),
691691+ Clet(id_prev, Cvar_mut (VP.var id),
672692 Csequence
673693 (Cassign(VP.var id,
674674- Cop(inc, [Cvar (VP.var id); Cconst_int (2, dbg)],
694694+ Cop(inc, [Cvar_mut (VP.var id);
695695+ Cconst_int (2, dbg)],
675696 dbg)),
676697 Cifthenelse
677698 (Cop(Ccmpi Ceq, [Cvar (VP.var id_prev); high],
···12381259 (* N.B. [body] must still be traversed even if [exp] will never return:
12391260 there may be constant closures inside that need lifting out. *)
12401261 begin match str, kind with
12411241- | Immutable, _ -> Clet(id, cexp, transl_body env)
12421242- | Mutable, Pintval -> Clet_mut(id, typ_int, cexp, transl_body env)
12431243- | Mutable, _ -> Clet_mut(id, typ_val, cexp, transl_body env)
12621262+ | Immutable, _ ->
12631263+ Clet(id, cexp, transl_body env)
12641264+ | Mutable, Pintval ->
12651265+ Clet_mut(id, typ_int, cexp,
12661266+ transl_body (add_mutable_id (VP.var id) env))
12671267+ | Mutable, _ ->
12681268+ Clet_mut(id, typ_val, cexp,
12691269+ transl_body (add_mutable_id (VP.var id) env))
12441270 end
12451271 | Boxed (boxed_number, false) ->
12461272 let unboxed_id = V.create_local (VP.name id) in
12471273 let v = VP.create unboxed_id in
12481274 let cexp = unbox_number dbg boxed_number cexp in
12491249- let body =
12751275+ let body env =
12501276 transl_body (add_unboxed_id (VP.var id) unboxed_id boxed_number env) in
12511277 begin match str, boxed_number with
12521252- | Immutable, _ -> Clet (v, cexp, body)
12531253- | Mutable, bn -> Clet_mut (v, typ_of_boxed_number bn, cexp, body)
12781278+ | Immutable, _ ->
12791279+ Clet (v, cexp, body env)
12801280+ | Mutable, bn ->
12811281+ Clet_mut (v, typ_of_boxed_number bn, cexp,
12821282+ body (add_mutable_id unboxed_id env))
12541283 end
1255128412561285and make_catch ncatch body handler dbg = match body with
+1
asmcomp/printcmm.ml
···168168 | Cconst_float (n, _dbg) -> fprintf ppf "%F" n
169169 | Cconst_symbol (s, _dbg) -> fprintf ppf "\"%s\"" s
170170 | Cvar id -> V.print ppf id
171171+ | Cvar_mut id -> fprintf ppf "!%a" V.print id
171172 | Creturn_addr -> fprintf ppf "return_addr"
172173 | Clet(id, def, (Clet(_, _, _) as body)) ->
173174 let print_binding id ppf def =
+5-4
asmcomp/selectgen.ml
···110110 | Cconst_symbol _ ->
111111 Arch.size_addr
112112 | Cconst_float _ -> Arch.size_float
113113- | Cvar id ->
113113+ | Cvar id | Cvar_mut id ->
114114 begin try
115115 V.Map.find id localenv
116116 with Not_found ->
···336336 List.for_all self#is_simple_expr args
337337 end
338338 | Cassign _ | Cifthenelse _ | Cswitch _ | Ccatch _ | Cexit _
339339- | Ctrywith _ -> false
339339+ | Ctrywith _ | Cvar_mut _ -> false
340340341341(* Analyses the effects and coeffects of an expression. This is used across
342342 a whole list of expressions with a view to determining which expressions
···355355 match exp with
356356 | Cconst_int _ | Cconst_natint _ | Cconst_float _ | Cconst_symbol _
357357 | Cvar _ | Creturn_addr -> EC.none
358358+ | Cvar_mut _ -> EC.coeffect_only Coeffect.Read_mutable
358359 | Ctuple el -> EC.join_list_map el self#effects_of
359360 | Clet (_id, arg, body) | Clet_mut (_id, _, arg, body) ->
360361 EC.join (self#effects_of arg) (self#effects_of body)
···605606 | Creturn_addr ->
606607 let r = self#regs_for typ_int in
607608 Some(self#insert_op env Ireturn_addr [||] r)
608608- | Cvar v ->
609609+ | Cvar v | Cvar_mut v ->
609610 begin try
610611 Some(env_find v env)
611612 with Not_found ->
···11221123 end
11231124 | Cop _
11241125 | Cconst_int _ | Cconst_natint _ | Cconst_float _ | Cconst_symbol _
11251125- | Cvar _
11261126+ | Cvar _ | Cvar_mut _
11261127 | Creturn_addr
11271128 | Cassign _
11281129 | Ctuple _
+2-2
asmcomp/thread_sanitizer.ml
···154154 | Cconst_natint (_, _)
155155 | Cconst_float (_, _)
156156 | Cconst_symbol (_, _)
157157- | Cvar _ | Ctuple _ | Creturn_addr ) as expr ->
157157+ | Cvar _ | Cvar_mut _ | Ctuple _ | Creturn_addr ) as expr ->
158158 let id = VP.create (V.create_local "res") in
159159 Clet (id, expr, Csequence (call_exit, Cvar (VP.var id)))
160160 in
···285285 Cswitch (aux e, cases, handlers, dbg_none)
286286 (* no instrumentation *)
287287 | ( Cconst_int _ | Cconst_natint _ | Cconst_float _ | Cconst_symbol _
288288- | Cvar _ | Creturn_addr ) as c ->
288288+ | Cvar _ | Cvar_mut _ | Creturn_addr ) as c ->
289289 c
290290 in
291291 body |> aux |> wrap_entry_exit
+10
testsuite/tests/asmcomp/issue13875.ml
···11+(* TEST *)
22+33+let () =
44+ let o1 = object val x = 123 method get () = x end in
55+ let o2 = object method get () = 456 end in
66+ let r = ref o1 in
77+ let n = (!r)#get (r := o2) in
88+ (* Order of evaluation here is not really specified or consistent,
99+ but we want to get either 123 or 456 *)
1010+ assert (n = 123 || n = 456)
+8
testsuite/tests/basic/eval_order_9.ml
···11+(* TEST *)
22+33+(* From #12440, by Jeremy Yallop *)
44+let _ =
55+ let r = ref true in
66+ match Fun.id ((r := false), !r) with
77+ | _, true -> print_endline "Ok"
88+ | _, false -> print_endline "ERROR"