Skip to content

Commit

Permalink
Improve lambda parameter inference for mixed/dynamic
Browse files Browse the repository at this point in the history
Summary:
Adopts the following rule when dealing with expected type of a lambda is `mixed` or `dynamic`.
```
G, x : mixed/dynamic |- e : mixed/dynamic
-----------------------------------------
     G |- (\x -> e) : mixed/dynamic
```

Reviewed By: andrewjkennedy

Differential Revision: D26200919

fbshipit-source-id: 5942210d650c4389558478c7999396d6b89c1233
  • Loading branch information
madgen authored and facebook-github-bot committed Feb 4, 2021
1 parent efa6463 commit bfd338f
Show file tree
Hide file tree
Showing 12 changed files with 111 additions and 2 deletions.
32 changes: 32 additions & 0 deletions hphp/hack/src/typing/typing.ml
Expand Up @@ -2929,6 +2929,38 @@ and expr_
* Note: we should be using 'nothing' to type the arguments. *)
Typing_log.increment_feature_count env FL.Lambda.untyped_context;
check_body_under_known_params env declared_ft
| Some ExpectedTy.{ ty = { et_type; _ }; _ }
when TUtils.is_mixed env et_type || TUtils.is_dynamic env et_type ->
(* If the expected type of a lambda is mixed or dynamic, we
* decompose the expected type into a function type where the
* undeclared parameters and the return type are set to the expected
* type of lambda, i.e., mixed or dynamic.
*
* For an expected mixed type, one could argue that the lambda
* doesn't even need to be checked as it can't be called (there is
* no downcast to function type). Thus, we should be using nothing
* to type the arguments. But generally users are very confused by
* the use of nothing and would expect the lambda body to be
* checked as though it could be called.
*)
let replace_non_declared_type declared_ft_param =
let is_undeclared =
TUtils.is_any env declared_ft_param.fp_type.et_type
in
if is_undeclared then
let enforced_ty = { et_enforced = false; et_type } in
{ declared_ft_param with fp_type = enforced_ty }
else
declared_ft_param
in
let expected_ft =
let ft_params =
List.map ~f:replace_non_declared_type declared_ft.ft_params
in
{ declared_ft with ft_params }
in
let ret_ty = et_type in
check_body_under_known_params env ~ret_ty expected_ft
| Some _ ->
(* If the expected type is something concrete but not a function
* then we should reject in strict mode. Check body anyway.
Expand Down
11 changes: 11 additions & 0 deletions hphp/hack/test/typecheck/dynamic_lambda_ok.php
@@ -0,0 +1,11 @@
<?hh

class C {
public function __construct(
public dynamic $f,
) {}
}

function f(): void {
new C($x ==> $x + 1);
}
1 change: 1 addition & 0 deletions hphp/hack/test/typecheck/dynamic_lambda_ok.php.exp
@@ -0,0 +1 @@
No errors
@@ -1,2 +1,4 @@
File "untyped_lambda_strict_mode.php", line 5, characters 16-31:
Cannot determine types of lambda parameters in strict mode. Please add type hints on parameters. (Typing[4224])
File "untyped_lambda_strict_mode.php", line 5, characters 27-29:
You are trying to access the method `foo` but this is a mixed value. Use a **specific** class or interface name. (Typing[4064])
File "untyped_lambda_strict_mode.php", line 5, characters 16-17:
Definition is here
11 changes: 11 additions & 0 deletions hphp/hack/test/typecheck/mixed_lambda_explicit_return_ko.php
@@ -0,0 +1,11 @@
<?hh

class C {
public function __construct(
public mixed $f,
) {}
}

function f(): void {
new C(($x) : int ==> $x);
}
@@ -0,0 +1,6 @@
File "mixed_lambda_explicit_return_ko.php", line 10, characters 24-25:
Invalid return type (Typing[4110])
File "mixed_lambda_explicit_return_ko.php", line 10, characters 16-18:
Expected `int`
File "mixed_lambda_explicit_return_ko.php", line 5, characters 12-16:
But got `mixed`
11 changes: 11 additions & 0 deletions hphp/hack/test/typecheck/mixed_lambda_ko.php
@@ -0,0 +1,11 @@
<?hh

class C {
public function __construct(
public mixed $f,
) {}
}

function f(): void {
new C($x ==> $x + 1);
}
6 changes: 6 additions & 0 deletions hphp/hack/test/typecheck/mixed_lambda_ko.php.exp
@@ -0,0 +1,6 @@
File "mixed_lambda_ko.php", line 10, characters 16-21:
Typing error (Typing[4110])
File "mixed_lambda_ko.php", line 10, characters 16-17:
Expected `num` because this is used in an arithmetic operation
File "mixed_lambda_ko.php", line 5, characters 12-16:
But got `mixed`
11 changes: 11 additions & 0 deletions hphp/hack/test/typecheck/mixed_lambda_ok.php
@@ -0,0 +1,11 @@
<?hh

class C {
public function __construct(
public mixed $f,
) {}
}

function f(): void {
new C($x ==> $x);
}
1 change: 1 addition & 0 deletions hphp/hack/test/typecheck/mixed_lambda_ok.php.exp
@@ -0,0 +1 @@
No errors
11 changes: 11 additions & 0 deletions hphp/hack/test/typecheck/semi_mixed_lambda_ko.php
@@ -0,0 +1,11 @@
<?hh

class C {
public function __construct(
public mixed $f,
) {}
}

function f(): void {
new C(($x, string $y) ==> $y + 1);
}
6 changes: 6 additions & 0 deletions hphp/hack/test/typecheck/semi_mixed_lambda_ko.php.exp
@@ -0,0 +1,6 @@
File "semi_mixed_lambda_ko.php", line 10, characters 29-34:
Typing error (Typing[4110])
File "semi_mixed_lambda_ko.php", line 10, characters 29-30:
Expected `num` because this is used in an arithmetic operation
File "semi_mixed_lambda_ko.php", line 10, characters 14-19:
But got `string`

0 comments on commit bfd338f

Please sign in to comment.