Skip to content

Commit 8dafa98

Browse files
authored
Improve typing for (||) and (&&) (ocaml-flambda#55)
1 parent 8c64754 commit 8dafa98

File tree

4 files changed

+54
-29
lines changed

4 files changed

+54
-29
lines changed

stdlib/stdlib.ml

+5-5
Original file line numberDiff line numberDiff line change
@@ -79,11 +79,11 @@ external ( != ) : ('a[@local_opt]) -> ('a[@local_opt]) -> bool = "%noteq"
7979

8080
(* Boolean operations *)
8181

82-
external not : bool -> bool = "%boolnot"
83-
external ( & ) : bool -> bool -> bool = "%sequand"
84-
external ( && ) : bool -> bool -> bool = "%sequand"
85-
external ( or ) : bool -> bool -> bool = "%sequor"
86-
external ( || ) : bool -> bool -> bool = "%sequor"
82+
external not : (bool[@local_opt]) -> bool = "%boolnot"
83+
external ( & ) : (bool[@local_opt]) -> (bool[@local_opt]) -> bool = "%sequand"
84+
external ( && ) : (bool[@local_opt]) -> (bool[@local_opt]) -> bool = "%sequand"
85+
external ( or ) : (bool[@local_opt]) -> (bool[@local_opt]) -> bool = "%sequor"
86+
external ( || ) : (bool[@local_opt]) -> (bool[@local_opt]) -> bool = "%sequor"
8787

8888
(* Integer operations *)
8989

stdlib/stdlib.mli

+5-5
Original file line numberDiff line numberDiff line change
@@ -208,30 +208,30 @@ external ( != ) : ('a[@local_opt]) -> ('a[@local_opt]) -> bool = "%noteq"
208208

209209
(** {1 Boolean operations} *)
210210

211-
external not : bool -> bool = "%boolnot"
211+
external not : (bool[@local_opt]) -> bool = "%boolnot"
212212
(** The boolean negation. *)
213213

214-
external ( && ) : bool -> bool -> bool = "%sequand"
214+
external ( && ) : (bool[@local_opt]) -> (bool[@local_opt]) -> bool = "%sequand"
215215
(** The boolean 'and'. Evaluation is sequential, left-to-right:
216216
in [e1 && e2], [e1] is evaluated first, and if it returns [false],
217217
[e2] is not evaluated at all.
218218
Right-associative operator, see {!Ocaml_operators} for more information.
219219
*)
220220

221-
external ( & ) : bool -> bool -> bool = "%sequand"
221+
external ( & ) : (bool[@local_opt]) -> (bool[@local_opt]) -> bool = "%sequand"
222222
[@@ocaml.deprecated "Use (&&) instead."]
223223
(** @deprecated {!Stdlib.( && )} should be used instead.
224224
Right-associative operator, see {!Ocaml_operators} for more information.
225225
*)
226226

227-
external ( || ) : bool -> bool -> bool = "%sequor"
227+
external ( || ) : (bool[@local_opt]) -> (bool[@local_opt]) -> bool = "%sequor"
228228
(** The boolean 'or'. Evaluation is sequential, left-to-right:
229229
in [e1 || e2], [e1] is evaluated first, and if it returns [true],
230230
[e2] is not evaluated at all.
231231
Right-associative operator, see {!Ocaml_operators} for more information.
232232
*)
233233

234-
external ( or ) : bool -> bool -> bool = "%sequor"
234+
external ( or ) : (bool[@local_opt]) -> (bool[@local_opt]) -> bool = "%sequor"
235235
[@@ocaml.deprecated "Use (||) instead."]
236236
(** @deprecated {!Stdlib.( || )} should be used instead.
237237
Right-associative operator, see {!Ocaml_operators} for more information.

testsuite/tests/typing-local/local.ml

+18
Original file line numberDiff line numberDiff line change
@@ -1719,6 +1719,24 @@ let primloc x = let local_ y = Int32.add x 1l in Int32.to_int y
17191719
val primloc : int32 -> int = <fun>
17201720
|}]
17211721

1722+
(* (&&) and (||) tail call on the right *)
1723+
let testbool1 x =
1724+
let local_ b = not x in
1725+
(b || false) && true
1726+
1727+
let testbool2 x =
1728+
let local_ b = not x in
1729+
true && (false || b)
1730+
[%%expect{|
1731+
val testbool1 : bool -> bool = <fun>
1732+
Line 7, characters 20-21:
1733+
7 | true && (false || b)
1734+
^
1735+
Error: This local value escapes its region
1736+
Hint: Cannot return local value without an explicit "local_" annotation
1737+
|}]
1738+
1739+
17221740
(* mode-crossing using unary + *)
17231741
let promote (local_ x) = +x
17241742
[%%expect{|

typing/typecore.ml

+26-19
Original file line numberDiff line numberDiff line change
@@ -281,13 +281,23 @@ let mode_tuple mode tuple_modes =
281281
let escaping_context = None in
282282
{ position; escaping_context; mode; tuple_modes }
283283

284-
let mode_argument ~position ~partial_app alloc_mode =
284+
let mode_argument ~funct ~index ~position ~partial_app alloc_mode =
285285
let vmode = Value_mode.of_alloc alloc_mode in
286-
match position, partial_app with
287-
| Nontail, _ | _, true->
288-
mode_nontail vmode
289-
| Tail, false ->
290-
mode_tailcall_argument (Value_mode.local_to_regional vmode)
286+
if partial_app then mode_nontail vmode
287+
else match funct.exp_desc, index, position with
288+
| Texp_ident (_, _, {val_kind =
289+
Val_prim {Primitive.prim_name = ("%sequor"|"%sequand")}},
290+
Id_prim _), 1, Tail ->
291+
(* The second argument to (&&) and (||) is in
292+
tail position if the call is *)
293+
mode_return (Value_mode.local_to_regional vmode)
294+
| Texp_ident (_, _, _, Id_prim _), _, _ ->
295+
(* Other primitives cannot be tail-called *)
296+
mode_nontail vmode
297+
| _, _, Nontail ->
298+
mode_nontail vmode
299+
| _, _, Tail ->
300+
mode_tailcall_argument (Value_mode.local_to_regional vmode)
291301

292302
let submode ~loc ~env mode expected_mode =
293303
let res =
@@ -5138,20 +5148,22 @@ and type_argument ?explanation ?recarg env (mode : expected_mode) sarg
51385148
unify_exp env texp ty_expected;
51395149
texp
51405150

5141-
and type_apply_arg env ~position ~partial_app (lbl, arg) =
5151+
and type_apply_arg env ~funct ~index ~position ~partial_app (lbl, arg) =
51425152
match arg with
51435153
| Arg (Unknown_arg { sarg; ty_arg; mode_arg }) ->
51445154
let mode = Alloc_mode.newvar () in
51455155
Alloc_mode.submode_exn mode mode_arg;
5146-
let expected_mode = mode_argument ~position ~partial_app mode in
5156+
let expected_mode =
5157+
mode_argument ~funct ~index ~position ~partial_app mode in
51475158
let arg = type_expect env expected_mode sarg (mk_expected ty_arg) in
51485159
if is_optional lbl then
51495160
unify_exp env arg (type_option(newvar()));
51505161
(lbl, Arg arg)
51515162
| Arg (Known_arg { sarg; ty_arg; ty_arg0; mode_arg; wrapped_in_some }) ->
51525163
let mode = Alloc_mode.newvar () in
51535164
Alloc_mode.submode_exn mode mode_arg;
5154-
let expected_mode = mode_argument ~position ~partial_app mode in
5165+
let expected_mode =
5166+
mode_argument ~funct ~index ~position ~partial_app mode in
51555167
let arg =
51565168
if wrapped_in_some then
51575169
option_some env
@@ -5186,7 +5198,8 @@ and type_application env app_loc expected_mode funct funct_mode sargs =
51865198
submode ~loc:app_loc ~env
51875199
(Value_mode.of_alloc mres) expected_mode;
51885200
let marg =
5189-
mode_argument ~position:expected_mode.position ~partial_app:false marg
5201+
mode_argument ~funct ~index:0 ~position:expected_mode.position
5202+
~partial_app:false marg
51905203
in
51915204
let exp = type_expect env marg sarg (mk_expected ty_arg) in
51925205
check_partial_application false exp;
@@ -5214,17 +5227,11 @@ and type_application env app_loc expected_mode funct funct_mode sargs =
52145227
collect_apply_args env funct ignore_labels ty (instance ty)
52155228
(Value_mode.regional_to_global_alloc funct_mode) sargs
52165229
in
5217-
let position =
5218-
match funct.exp_desc with
5219-
| Texp_ident (_, _, _, Id_prim _) ->
5220-
(* Primitives cannot be tail-called, so their arguments
5221-
need not be mode-restricted *)
5222-
Nontail
5223-
| _ -> expected_mode.position in
5230+
let position = expected_mode.position in
52245231
let partial_app = is_partial_apply args in
52255232
let args =
5226-
List.map
5227-
(fun arg -> type_apply_arg env ~position ~partial_app arg)
5233+
List.mapi (fun index arg ->
5234+
type_apply_arg env ~funct ~index ~position ~partial_app arg)
52285235
args
52295236
in
52305237
let ty_ret, mode_ret, args =

0 commit comments

Comments
 (0)