Skip to content

Commit 4cd24bd

Browse files
authored
flambda-backend: mode crossing of LHS of arrow types by coercing (#1701)
* mode crossing of LHS of arrow types by coercing * address comment * quick fix * mode crossing more complete.
1 parent 910914d commit 4cd24bd

File tree

4 files changed

+104
-8
lines changed

4 files changed

+104
-8
lines changed

testsuite/tests/typing-local/crossing.ml

Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -358,3 +358,82 @@ type _ t_gadt = Int : int t_gadt
358358
type 'a t_rec = { fld : 'a; }
359359
val f : 'a t_gadt -> 'a -> 'a = <fun>
360360
|}]
361+
362+
(* Mode crossing in coercing arrow types *)
363+
let foo : int -> int = fun x -> x
364+
[%%expect{|
365+
val foo : int -> int = <fun>
366+
|}]
367+
368+
let foo' : int -> local_ int = fun x -> local_ x
369+
[%%expect{|
370+
val foo' : int -> local_ int = <fun>
371+
|}]
372+
373+
374+
375+
let bar (f : local_ int -> int) = f 42
376+
[%%expect{|
377+
val bar : (local_ int -> int) -> int = <fun>
378+
|}]
379+
380+
(* Implicit mode crossing is not good enough *)
381+
let _ = bar foo
382+
[%%expect{|
383+
Line 1, characters 12-15:
384+
1 | let _ = bar foo
385+
^^^
386+
Error: This expression has type int -> int
387+
but an expression was expected of type local_ int -> int
388+
|}]
389+
390+
let _ = bar (foo :> local_ int -> int)
391+
[%%expect{|
392+
- : int = 42
393+
|}]
394+
395+
let _ = bar (foo : int -> int :> local_ int -> int)
396+
[%%expect{|
397+
- : int = 42
398+
|}]
399+
400+
(* Only the RHS type of :> is looked at for mode crossing *)
401+
let _ = bar (foo : int -> int :> local_ _ -> _)
402+
[%%expect{|
403+
Line 1, characters 12-47:
404+
1 | let _ = bar (foo : int -> int :> local_ _ -> _)
405+
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
406+
Error: Type int -> int is not a subtype of local_ 'a -> 'b
407+
|}]
408+
409+
410+
(* An example: the RHS allows mode crossing but the LHS doesn't *)
411+
let foo = function
412+
| `A -> ()
413+
| `B (s : string) -> ()
414+
[%%expect{|
415+
val foo : [< `A | `B of string ] -> unit = <fun>
416+
|}]
417+
418+
let foo_ = (foo : [`A | `B of string] -> unit :> local_ [`A] -> unit)
419+
[%%expect{|
420+
val foo_ : local_ [ `A ] -> unit = <fun>
421+
|}]
422+
423+
let foo_ = (foo : [`A | `B of string] -> unit :> local_ [`B of string] -> unit)
424+
[%%expect{|
425+
Line 1, characters 11-79:
426+
1 | let foo_ = (foo : [`A | `B of string] -> unit :> local_ [`B of string] -> unit)
427+
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
428+
Error: Type [ `A | `B of string ] -> unit is not a subtype of
429+
local_ [ `B of string ] -> unit
430+
|}]
431+
432+
(* You can't erase the info that a function might allocate in parent region *)
433+
let _ = bar (foo' :> local_ int -> int)
434+
[%%expect{|
435+
Line 1, characters 12-39:
436+
1 | let _ = bar (foo' :> local_ int -> int)
437+
^^^^^^^^^^^^^^^^^^^^^^^^^^^
438+
Error: Type int -> local_ int is not a subtype of local_ int -> int
439+
|}]

typing/ctype.ml

Lines changed: 21 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2114,6 +2114,9 @@ let unification_layout_check env ty layout =
21142114
| Delay_checks r -> r := (ty,layout) :: !r
21152115
| Skip_checks -> ()
21162116

2117+
let is_principal ty =
2118+
not !Clflags.principal || get_level ty = generic_level
2119+
21172120
let is_always_global env ty =
21182121
let perform_check () =
21192122
Result.is_ok (check_type_layout env ty
@@ -2129,6 +2132,9 @@ let is_always_global env ty =
21292132
else
21302133
perform_check ()
21312134

2135+
let mode_cross env (ty : type_expr) =
2136+
is_principal ty && is_always_global env ty
2137+
21322138
(* Recursively expand the head of a type.
21332139
Also expand #-types.
21342140
@@ -5341,7 +5347,17 @@ let rec build_subtype env (visited : transient_expr list)
53415347
let (t1', c1) = build_subtype env visited loops (not posi) level t1 in
53425348
let (t2', c2) = build_subtype env visited loops posi level t2 in
53435349
let (a', c3) =
5344-
if level > 2 then build_submode (not posi) a else a, Unchanged
5350+
if level > 2 then begin
5351+
(* If posi, then t1' >= t1, and we pick t1; otherwise we pick t1'. In
5352+
either case we pick the smaller type which is the "real" type of
5353+
runtime values, and easier to cross modes (and thus making the
5354+
mode-crossing more complete). *)
5355+
let t1 = if posi then t1 else t1' in
5356+
if is_always_global env t1 then
5357+
Mode.Alloc.newvar (), Changed
5358+
else
5359+
build_submode (not posi) a
5360+
end else a, Unchanged
53455361
in
53465362
let (r', c4) =
53475363
if level > 2 then build_submode posi r else r, Unchanged
@@ -5557,7 +5573,10 @@ let rec subtype_rec env trace t1 t2 cstrs =
55575573
t2 t1
55585574
cstrs
55595575
in
5560-
subtype_alloc_mode env trace a2 a1;
5576+
if not (is_always_global env t2) then
5577+
subtype_alloc_mode env trace a2 a1;
5578+
(* RHS mode of arrow types indicates allocation in the parent region
5579+
and is not subject to mode crossing *)
55615580
subtype_alloc_mode env trace r1 r2;
55625581
subtype_rec
55635582
env

typing/ctype.mli

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -520,11 +520,15 @@ val check_type_layout :
520520
val constrain_type_layout :
521521
Env.t -> type_expr -> layout -> (unit, Layout.Violation.t) result
522522

523+
val is_principal : type_expr -> bool
524+
523525
(* True if a type is always global (i.e., it mode crosses for local). This is
524526
true for all immediate and immediate64 types. To make it sound for
525527
immediate64, we've disabled stack allocation on 32-bit builds. *)
526528
val is_always_global : Env.t -> type_expr -> bool
527529

530+
val mode_cross : Env.t -> type_expr -> bool
531+
528532
(* For use with ocamldebug *)
529533
type global_state
530534
val global_state : global_state

typing/typecore.ml

Lines changed: 0 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -709,9 +709,6 @@ let extract_label_names env ty =
709709
| Record_type (_, _,fields, _) -> List.map (fun l -> l.Types.ld_id) fields
710710
| Not_a_record_type | Maybe_a_record_type -> assert false
711711

712-
let is_principal ty =
713-
not !Clflags.principal || get_level ty = generic_level
714-
715712
let has_local_attr loc attrs =
716713
match Builtin_attributes.has_local attrs with
717714
| Ok l -> l
@@ -733,9 +730,6 @@ let has_poly_constraint spat =
733730
end
734731
| _ -> false
735732

736-
let mode_cross env (ty : type_expr) =
737-
is_principal ty && is_always_global env ty
738-
739733
let mode_cross_to_min env ty mode =
740734
if mode_cross env ty then
741735
Value.min_mode

0 commit comments

Comments
 (0)