Skip to content

Commit 520dda0

Browse files
Michael Thomasmeta-codesync[bot]
authored andcommitted
Extend rank check
Summary: The current check looks for type parameters of higher rank when adding a bound to a type variable but not higher rank type variables. This omission allows higher rank generics to escape their scope. This diff extends the rank check to cover both cases and modifies the error message for the second case Reviewed By: andrewjkennedy Differential Revision: D86223152 fbshipit-source-id: eff5b85810db51f37d8953c0a5c9dc9d7a42c8f0
1 parent dcfe2ae commit 520dda0

File tree

6 files changed

+97
-20
lines changed

6 files changed

+97
-20
lines changed

hphp/hack/src/typing/typing_error.ml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1893,7 +1893,7 @@ and Secondary : sig
18931893
tvar_pos: Pos_or_decl.t;
18941894
pos_with_generic: Pos_or_decl.t;
18951895
generic_reason: Typing_reason.t;
1896-
generic_name: string;
1896+
generic_name: string option;
18971897
}
18981898
[@@deriving show]
18991899
end = struct
@@ -2212,7 +2212,7 @@ end = struct
22122212
tvar_pos: Pos_or_decl.t;
22132213
pos_with_generic: Pos_or_decl.t;
22142214
generic_reason: Typing_reason.t;
2215-
generic_name: string;
2215+
generic_name: string option;
22162216
}
22172217
[@@deriving show]
22182218
end

hphp/hack/src/typing/typing_error.mli

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1732,7 +1732,7 @@ and Secondary : sig
17321732
tvar_pos: Pos_or_decl.t;
17331733
pos_with_generic: Pos_or_decl.t;
17341734
generic_reason: Typing_reason.t;
1735-
generic_name: string;
1735+
generic_name: string option;
17361736
}
17371737
[@@deriving show]
17381738
end

hphp/hack/src/typing/typing_error_utils.ml

Lines changed: 17 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -6604,16 +6604,24 @@ end = struct
66046604
create ~code:Error_code.EnumClassLabelUnknown ~reasons ()
66056605

66066606
let higher_rank_tparam_escape
6607-
tvar_pos pos_with_generic generic_reason generic_name =
6607+
tvar_pos pos_with_generic generic_reason generic_name_opt =
66086608
let reasons =
6609-
lazy
6610-
((pos_with_generic, "Expected a first-class polymorphic function type.")
6611-
:: Typing_reason.to_string
6612-
(Format.sprintf
6613-
"A higher rank generic `%s` will escape its scope"
6614-
generic_name)
6615-
generic_reason
6616-
@ [(tvar_pos, "Please make this type explicit")])
6609+
match generic_name_opt with
6610+
| Some generic_name ->
6611+
let msg = "Expected a first-class polymorphic function type." in
6612+
let prefix =
6613+
Format.sprintf
6614+
"A higher rank generic `%s` will escape its scope"
6615+
generic_name
6616+
in
6617+
lazy
6618+
((pos_with_generic, msg)
6619+
:: Typing_reason.to_string prefix generic_reason
6620+
@ [(tvar_pos, "Please make this type explicit")])
6621+
| _ ->
6622+
let msg = "A higher rank generic will escape its scope" in
6623+
let pos = Typing_reason.to_pos generic_reason in
6624+
lazy [(pos, msg); (tvar_pos, "Please make this type explicit")]
66176625
in
66186626
create ~code:Error_code.RigidTVarEscape ~reasons ()
66196627

hphp/hack/src/typing/typing_subtype.ml

Lines changed: 10 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -845,23 +845,25 @@ end = struct
845845
end
846846

847847
(* Find the first generic occurring in [ty] with a rank higher than [rank] *)
848-
let find_higher_rank_generic env subtype_env ty rank =
848+
let check_rank env subtype_env ty rank =
849849
(* To avoid this potentially expensive check we record when the sub- or
850850
supertype _may_ contain a higher rank type parameter. *)
851851
if Subtype_env.get_check_rank subtype_env then
852852
let p ty =
853853
match get_node ty with
854854
| Tgeneric name -> Env.rank_of_tparam env name > rank
855+
| Tvar tv -> Env.rank_of_tvar env tv > rank
855856
| _ -> false
856857
in
857858
let ty_opt = Typing_defs_core.find_locl_ty ty ~p in
858859
Option.map ty_opt ~f:(fun ty ->
859860
match deref ty with
860-
| (r, Tgeneric name) -> (r, name)
861+
| (r, Tgeneric name) -> (r, Some name)
862+
| (r, Tvar _) -> (r, None)
861863
| _ ->
862864
(* We are guaranteed that if the result if [Some(...)] then the type
863865
is a [Tgeneric] so something is seriously wrong here *)
864-
failwith "find_higher_rank_generic: unexpected type")
866+
failwith "check_rank: unexpected type")
865867
else
866868
None
867869

@@ -1100,7 +1102,7 @@ end = struct
11001102
| (r_sub, Tvar id) -> begin
11011103
(* Ensure that higher-ranked type parameters don't escape their scope *)
11021104
let tvar_rank = Env.rank_of_tvar env id in
1103-
match find_higher_rank_generic env subtype_env ty_super tvar_rank with
1105+
match check_rank env subtype_env ty_super tvar_rank with
11041106
| Some (generic_reason, generic_name) ->
11051107
let error =
11061108
Typing_error.Secondary.Higher_rank_tparam_escape
@@ -1195,7 +1197,7 @@ end = struct
11951197
tvar_pos = Typing_reason.to_pos r_super;
11961198
pos_with_generic = Typing_reason.to_pos r_generic;
11971199
generic_reason = r_generic;
1198-
generic_name = name_sub;
1200+
generic_name = Some name_sub;
11991201
}
12001202
in
12011203
let fail = Subtype_env.fail_with_secondary_error subtype_env error in
@@ -3403,7 +3405,7 @@ end = struct
34033405
(r_super, Tvar var_super_id) ) -> begin
34043406
(* Ensure that higher-ranked type parameters don't escape their scope *)
34053407
let tvar_rank = Env.rank_of_tvar env var_super_id in
3406-
match find_higher_rank_generic env subtype_env ty_sub tvar_rank with
3408+
match check_rank env subtype_env ty_sub tvar_rank with
34073409
| Some (generic_reason, generic_name) ->
34083410
let error =
34093411
Typing_error.Secondary.Higher_rank_tparam_escape
@@ -4060,7 +4062,7 @@ end = struct
40604062
tvar_pos = Typing_reason.to_pos r_sub;
40614063
pos_with_generic = Typing_reason.to_pos r_super;
40624064
generic_reason = r_super;
4063-
generic_name = tp_sup;
4065+
generic_name = Some tp_sup;
40644066
}
40654067
in
40664068
let fail = Subtype_env.fail_with_secondary_error subtype_env error in
@@ -9034,7 +9036,7 @@ end = struct
90349036
tvar_pos = Typing_reason.to_pos r_super;
90359037
pos_with_generic = Typing_reason.to_pos r_generic;
90369038
generic_reason = r_generic;
9037-
generic_name = nm;
9039+
generic_name = Some nm;
90389040
}
90399041
in
90409042
let fail = Subtype_env.fail_with_secondary_error subtype_env error in
Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
<?hh
2+
3+
class SecretResource {
4+
public function __construct(private string $secret_path): void {}
5+
6+
public async function doSomething(): Awaitable<string> {
7+
return "lolololol";
8+
}
9+
10+
public function close(): void {}
11+
}
12+
13+
class Handle<TScope> {
14+
public function __construct(private SecretResource $resource) {}
15+
16+
public async function doSomething(): Awaitable<string> {
17+
return await $this->resource->doSomething();
18+
}
19+
20+
public function close(): void {
21+
$this->resource->close();
22+
}
23+
}
24+
25+
function withSecretResourceUnsafe<TScope, T>(
26+
string $path,
27+
(function(Handle<TScope>): T) $f,
28+
): T {
29+
$handle = new Handle(new SecretResource($path));
30+
try {
31+
return $f($handle);
32+
} finally {
33+
$handle->close();
34+
}
35+
}
36+
37+
function withSecretResourceSafe<T>(
38+
string $path,
39+
(function<TScope>(Handle<TScope>): T) $f,
40+
): T {
41+
$handle = new Handle(new SecretResource($path));
42+
try {
43+
return $f($handle);
44+
} finally {
45+
$handle->close();
46+
}
47+
}
48+
49+
function tryAndGetTheHandle(): void {
50+
$escaping_handle =
51+
withSecretResourceUnsafe("tell_nobody.txt", $handle ==> $handle);
52+
53+
$nope = withSecretResourceSafe(
54+
"tell_nobody.txt",
55+
function<TScope>(Handle<TScope> $handle): Handle<TScope> ==> $handle,
56+
);
57+
}
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
ERROR: File "secret_resource.php", line 39, characters 3-39:
2+
Cannot use unstable feature: `polymorphic_function_hints` (Parsing[1002])
3+
ERROR: File "secret_resource.php", line 55, characters 5-60:
4+
Cannot use unstable feature: `polymorphic_lambda` (Parsing[1002])
5+
ERROR: File "secret_resource.php", line 55, characters 5-72:
6+
Invalid argument (Typing[4110])
7+
File "secret_resource.php", line 55, characters 54-59:
8+
A higher rank generic will escape its scope
9+
File "secret_resource.php", line 53, characters 11-32:
10+
Please make this type explicit

0 commit comments

Comments
 (0)