-
Notifications
You must be signed in to change notification settings - Fork 1.2k
Don't suggest a semicolon when the type is not unit #12116
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
Conversation
This hint message is printed when a function receives too many arguments. It doesn't make sense when the return type of the function is not unit: Line 2, characters 2-11: 2 | (+) 1 2 3 ^^^^^^^^^ Error: The function '(+)' has type int -> int -> int It is applied to too many arguments Line 2, characters 8-10: 2 | (+) 1 2 3 ^^ Hint: Did you forget a ';'? This removes the message when it is not applicable. The return type is computed again in the printing function by traversing the arrows to avoid passing more values and modifying the type checker.
typing/typecore.ml
Outdated
@@ -5657,6 +5657,11 @@ let report_this_function ppf funct = | |||
let report_too_many_arg_error ~funct ~func_ty ~previous_arg_loc | |||
~extra_arg_loc loc = | |||
let open Location in | |||
let rec res_type_is_unit ty = match get_desc ty with | |||
| Tarrow (_, _, rhs, _) -> res_type_is_unit rhs |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There is a small missing call to expand_head
to handle the cases where the ->
arrow is hidden behind a constructor:
type t = int -> int -> unit
let f (x:t) = x 0 1 2
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks, fixed.
I noticed that the main message could be improved too:
Line 2, characters 21-28:
2 | let f (x:int -> t) = x 0 1 2
^^^^^^^
Error: The function 'x' has type int -> t
It is applied to too many arguments
Line 2, characters 25-27:
2 | let f (x:int -> t) = x 0 1 2
^^
Hint: Did you forget a ';'?
Line 2, characters 27-28:
2 | let f (x:int -> t) = x 0 1 2
^
This extra argument is not expected.
I added a commit to fully expand arrows before the printing.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I am not sure if I agree with this change: we are erasing information providing by the user in form of type abbreviations, that might indicate that the root issue was earlier. For instance, I could have a combinator library, let's say for naive random variables:
type 'a rand = int -> 'a
let zero_or_one n = n mod 2
let add (x:'a rand) (y:'a rand): 'a rand = fun n -> x n + y n
Then, if I write
let t = add zero_or_one zero_or_one zero_or_one zero_or_one
the previous error message:
| let t = add zero_or_one zero_or_one zero_or_one zero_or_one
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
Error: The function 'add' has type int rand -> int rand -> int rand
It is applied to too many arguments
File "test.ml", line 4, characters 48-59:
4 | let t = add zero_or_one zero_or_one zero_or_one zero_or_one
^^^^^^^^^^^
This extra argument is not expected.
File "test.ml", line 4, characters 8-27:
carries the extra-information that add
morally only takes two arguments.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I agree with this point.
The printed type shows two arguments but the problem is due to a fourth argument. The "type a is not included in type b" message repeats with increasing amount of expansion, could we do the same here ?
Error: The function 'add' has type int rand -> int rand -> int rand
It is applied to 4 arguments but expect 3:
int rand -> int rand -> int -> int
I'll drop this commit, make this PR ready and eventually open a new one with more ideas.
typing/typecore.ml
Outdated
@@ -5742,8 +5737,14 @@ let report_error ~loc env = function | |||
| Apply_non_function { funct; func_ty; previous_arg_loc; extra_arg_loc } -> | |||
begin match get_desc func_ty with | |||
Tarrow _ -> | |||
let rec res_type_is_unit ty = match get_desc ty with |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The location of my comment was probably confusing but I would propose to move the expansion call just before the pattern match:
match get_desc (expand_head env rhs) with
rather than before the recursive call to res_type_is_unit
to avoid any question of the form "was the type expanded enough before pattern matching"?
On the very nitpicking side, the responsibility of computing the return type of the function could be left to report_too_many_arg_error
by transferring the current env as an argument, but the current location also makes sense.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I rewrote it with a different approach that doesn't have this problem.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I removed this assumption. It will be useful if this code is ever copy-pasted or if the code around is changed.
I like that it's not in report_too_many_arg_error
as it shows what information is needed and doesn't make assumptions.
9cbd943
to
488c8dc
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
In its current state, this PR is already a clear improvement: we remove a misleading hint with a handful of lines of code.
Thanks !
And I concur with the idea of keeping this PR simple (so I can merge it on Monday), while exploring further improvements in subsequent PRs.
I'm not fond of this approach. Looking at the place in typecore where the let type_unknown_arg (ty_fun, typed_args) (lbl, sarg) =
let (ty_arg, ty_res) =
let ty_fun = expand_head env ty_fun in
match get_desc ty_fun with
| Tvar _ ->
let t1 = newvar () and t2 = newvar () in
(* ... *)
unify env ty_fun (newty (Tarrow(lbl,t1,t2,commu_var ())));
(t1, t2)
| Tarrow (l,t1,t2,_) when l = lbl
|| !Clflags.classic && lbl = Nolabel && not (is_optional l) ->
(t1, t2)
| td ->
let ty_fun = match td with Tarrow _ -> newty td | _ -> ty_fun in
(* ... *)
raise(Error(funct.exp_loc, env, Apply_non_function {
funct;
func_ty = expand_head env funct.exp_type;
previous_arg_loc;
extra_arg_loc = sarg.pexp_loc; })) My impression is that including |
Good point, having the first non-functional return type available in |
@Julow are you planning to improve your PR as suggested? (Instead of suggesting new format features, hmmm... :-) |
It's on my list! Sorry for the slow reply. |
The expected result type of the function is already computed and can be passed through the `Apply_non_function` constructor.
The new code is indeed simpler this since we don't rely on the precondition that recursive functions types never raise the |
This was suggested by @Octachron in #11679 (review)
This hint message is printed when a function receives too many arguments. It doesn't make sense when the return type of the function is not unit:
This removes the message when it is not applicable.
The return type is computed again in the printing function by traversing the arrows to avoid passing more values and modifying the type checker.