Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Typecheck x|>f as (f x) #10081

Merged
merged 10 commits into from
Mar 3, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
5 changes: 5 additions & 0 deletions Changes
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,11 @@ Working version
- #10244: Optimise Int32.unsigned_to_int
(Fabian Hemmer, review by Stephen Dolan and Xavier Leroy)

### Type system:

* #10081: Typecheck `x |> f` and `f @@ x` as `(f x)`
(Alain Frisch, review by Jacques Garrigue, Josh Berdine and Thomas Refis)

### Standard library:

- #9533: Added String.starts_with and String.ends_with.
Expand Down
5 changes: 5 additions & 0 deletions testsuite/tests/prim-revapply/apply.ml
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
(* TEST
flags="-w +48"
*)

external ( @@ ) : ('a -> 'b) -> 'a -> 'b = "%apply"
Expand Down Expand Up @@ -37,3 +38,7 @@ let _ =
h @@ g @@ f @@ 3; (* 37 *)
add 4 @@ g @@ f @@ add 3 @@ add 2 @@ 3; (* 260 *)
]

(* PR#10081 *)
let bump ?(cap = 100) x = min cap (x + 1)
let _f x = bump @@ x (* no warning 48 *)
11 changes: 11 additions & 0 deletions testsuite/tests/prim-revapply/revapply.ml
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
(* TEST
flags="-w +48"
*)

external ( |> ) : 'a -> ('a -> 'b) -> 'b = "%revapply"
Expand All @@ -19,3 +20,13 @@ let _ =
3 |> f |> g |> h; (* 37 *)
3 |> add 2 |> add 3 |> f |> g |> add 4; (* 260 *)
]


(* PR#10081 *)
let bump ?(cap = 100) x = min cap (x + 1)
let _f x = x |> bump (* no warning 48 *)

(* PR#10081 *)
type t = A | B
type s = A | B
let _f (x : t) = x |> function A -> 0 | B -> 1
4 changes: 2 additions & 2 deletions testsuite/tests/typing-misc/printing.ml
Original file line number Diff line number Diff line change
Expand Up @@ -113,6 +113,6 @@ and bar () =
Line 4, characters 7-29:
4 | x |> List.fold_left max 0 x
^^^^^^^^^^^^^^^^^^^^^^
Error: This expression has type int but an expression was expected of type
int list -> 'a
Error: This expression has type int
This is not a function; it cannot be applied.
|}]
66 changes: 43 additions & 23 deletions typing/typecore.ml
Original file line number Diff line number Diff line change
Expand Up @@ -2646,6 +2646,17 @@ let unify_exp env exp expected_ty =
with Error(loc, env, Expr_type_clash(trace, tfc, None)) ->
raise (Error(loc, env, Expr_type_clash(trace, tfc, Some exp.exp_desc)))

(* If [is_inferred e] is true, [e] will be typechecked without using
the "expected type" provided by the context. *)

let rec is_inferred sexp =
match sexp.pexp_desc with
| Pexp_ident _ | Pexp_apply _ | Pexp_field _ | Pexp_constraint _
| Pexp_coerce _ | Pexp_send _ | Pexp_new _ -> true
| Pexp_sequence (_, e) | Pexp_open (_, e) -> is_inferred e
| Pexp_ifthenelse (_, e1, Some e2) -> is_inferred e1 && is_inferred e2
| _ -> false

let rec type_exp ?recarg env sexp =
(* We now delegate everything to type_expect *)
type_expect ?recarg env sexp (mk_expected (newvar ()))
Expand Down Expand Up @@ -2823,25 +2834,42 @@ and type_expect_
loc sexp.pexp_attributes env ty_expected_explained Nolabel caselist
| Pexp_apply(sfunct, sargs) ->
assert (sargs <> []);
begin_def (); (* one more level for non-returning functions *)
if !Clflags.principal then begin_def ();
let funct = type_exp env sfunct in
alainfrisch marked this conversation as resolved.
Show resolved Hide resolved
if !Clflags.principal then begin
end_def ();
generalize_structure funct.exp_type
end;
let rec lower_args seen ty_fun =
let ty = expand_head env ty_fun in
if List.memq ty seen then () else
match ty.desc with
Tarrow (_l, ty_arg, ty_fun, _com) ->
(try unify_var env (newvar()) ty_arg with Unify _ -> assert false);
lower_args (ty::seen) ty_fun
| _ -> ()
match ty.desc with
Tarrow (_l, ty_arg, ty_fun, _com) ->
(try unify_var env (newvar()) ty_arg
with Unify _ -> assert false);
lower_args (ty::seen) ty_fun
| _ -> ()
in
let type_sfunct sfunct =
begin_def (); (* one more level for non-returning functions *)
if !Clflags.principal then begin_def ();
let funct = type_exp env sfunct in
if !Clflags.principal then begin
end_def ();
generalize_structure funct.exp_type
end;
let ty = instance funct.exp_type in
end_def ();
wrap_trace_gadt_instances env (lower_args []) ty;
funct
in
let funct, sargs =
let funct = type_sfunct sfunct in
match funct.exp_desc, sargs with
| Texp_ident (_, _, {val_kind = Val_prim {prim_name = "%revapply"}}),
[Nolabel, sarg; Nolabel, actual_sfunct]
when is_inferred actual_sfunct ->
type_sfunct actual_sfunct, [Nolabel, sarg]
| Texp_ident (_, _, {val_kind = Val_prim {prim_name = "%apply"}}),
[Nolabel, actual_sfunct; Nolabel, sarg] ->
type_sfunct actual_sfunct, [Nolabel, sarg]
| _ ->
funct, sargs
in
let ty = instance funct.exp_type in
end_def ();
wrap_trace_gadt_instances env (lower_args []) ty;
begin_def ();
let (args, ty_res) = type_application env funct sargs in
end_def ();
Expand Down Expand Up @@ -4210,14 +4238,6 @@ and type_argument ?explanation ?recarg env sarg ty_expected' ty_expected =
let ls, tvar = list_labels env ty in
not tvar && List.for_all ((=) Nolabel) ls
in
let rec is_inferred sexp =
match sexp.pexp_desc with
Pexp_ident _ | Pexp_apply _ | Pexp_field _ | Pexp_constraint _
| Pexp_coerce _ | Pexp_send _ | Pexp_new _ -> true
| Pexp_sequence (_, e) | Pexp_open (_, e) -> is_inferred e
| Pexp_ifthenelse (_, e1, Some e2) -> is_inferred e1 && is_inferred e2
| _ -> false
in
match expand_head env ty_expected' with
{desc = Tarrow(Nolabel,ty_arg,ty_res,_); level = lv}
when is_inferred sarg ->
Expand Down