Skip to content

Commit

Permalink
Add projection reason for rewriting class<T> to classname<T>
Browse files Browse the repository at this point in the history
Summary: Closes the TODOs in typing_subtype around rewriting -- this allows us to trace exposure of `class<C>` types to `classname` and other string-coercing points.

Reviewed By: enetsee

Differential Revision: D66132800

fbshipit-source-id: cb09bb31616bf89f18de86dda286d1bf0d647913
  • Loading branch information
vassilmladenov authored and facebook-github-bot committed Nov 21, 2024
1 parent 9593ec4 commit 9591b86
Show file tree
Hide file tree
Showing 11 changed files with 440 additions and 38 deletions.
4 changes: 3 additions & 1 deletion hphp/hack/src/oxidized/gen/typing_reason.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
// This source code is licensed under the MIT license found in the
// LICENSE file in the "hack" directory of this source tree.
//
// @generated SignedSource<<2337a3a78f206543dedb8ac5ada2a89d>>
// @generated SignedSource<<d4d3ae5e8bc4721b490313db730102fd>>
//
// To regenerate this file, run:
// hphp/hack/src/oxidized_regen.sh
Expand Down Expand Up @@ -310,6 +310,8 @@ pub enum PrjAsymm {
PrjAsymmArraykey,
#[rust_to_ocaml(name = "Prj_asymm_num")]
PrjAsymmNum,
#[rust_to_ocaml(name = "Prj_rewrite_classname")]
PrjRewriteClassname,
}
impl TrivialDrop for PrjAsymm {}
arena_deserializer::impl_deserialize_in_arena!(PrjAsymm);
Expand Down
3 changes: 2 additions & 1 deletion hphp/hack/src/oxidized_by_ref/decl_visitor/node_impl_gen.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
// This source code is licensed under the MIT license found in the
// LICENSE file in the "hack" directory of this source tree.
//
// @generated SignedSource<<697117f438c1d0dc72305e8d9a2862ca>>
// @generated SignedSource<<32e6ae4375299e5a185a8ebc19c7a465>>
//
// To regenerate this file, run:
// hphp/hack/src/oxidized_regen.sh
Expand Down Expand Up @@ -1630,6 +1630,7 @@ impl<'a> Node<'a> for PrjAsymm {
PrjAsymm::PrjAsymmNullable => {}
PrjAsymm::PrjAsymmArraykey => {}
PrjAsymm::PrjAsymmNum => {}
PrjAsymm::PrjRewriteClassname => {}
}
}
}
Expand Down
9 changes: 9 additions & 0 deletions hphp/hack/src/typing/typing_reason.ml
Original file line number Diff line number Diff line change
Expand Up @@ -255,6 +255,7 @@ type prj_asymm =
| Prj_asymm_nullable
| Prj_asymm_arraykey
| Prj_asymm_num
| Prj_rewrite_classname
[@@deriving hash]

let prj_asymm_to_json = function
Expand All @@ -264,6 +265,7 @@ let prj_asymm_to_json = function
| Prj_asymm_nullable -> Hh_json.JSON_String "Prj_asymm_nullable"
| Prj_asymm_arraykey -> Hh_json.JSON_String "Prj_asymm_arraykey"
| Prj_asymm_num -> Hh_json.JSON_String "Prj_asymm_num"
| Prj_rewrite_classname -> Hh_json.JSON_String "Prj_rewrite_classname"

(* ~~ Flow kinds ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ *)

Expand Down Expand Up @@ -2745,6 +2747,9 @@ module Constructors = struct
let prj_arraykey_super ~super ~super_prj =
prj_asymm_super ~super ~super_prj Prj_asymm_arraykey

let prj_rewrite_classname ~sub ~sub_prj =
prj_asymm_sub ~sub ~sub_prj Prj_rewrite_classname

let missing_field = Missing_field

let pessimised_this p = from_witness_decl @@ Pessimised_this p
Expand Down Expand Up @@ -3262,6 +3267,8 @@ module Derivation = struct
"The subtype is an arraykey type so next I checked the subtype constraint is satisfied for both the int and string parts."
| Prj_asymm_neg ->
"The subtype is a negated type so next I checked the inner type."
| Prj_rewrite_classname ->
"The subtype is a class<T> type and the supertype might be classname<T> or one of its supertypes, so next I checked the constraint with the subtype rewritten to classname<T>."

let explain_prj_asymm_super prj =
match prj with
Expand All @@ -3277,6 +3284,8 @@ module Derivation = struct
"The supertype is an arraykey type so next I checked the subtype constraint is satisfied for either the int or string part."
| Prj_asymm_neg ->
"The supertype is a negated type so I checked the inner type."
| Prj_rewrite_classname ->
failwith "The rewrites only happen for the subtype"

let explain = function
| Using_prj prj -> explain_prj prj
Expand Down
6 changes: 6 additions & 0 deletions hphp/hack/src/typing/typing_reason.mli
Original file line number Diff line number Diff line change
Expand Up @@ -573,6 +573,12 @@ val prj_arraykey_sub :
val prj_arraykey_super :
super:locl_phase t_ -> super_prj:locl_phase t_ -> locl_phase t_

(** Record the rewrite of a subtype constraint between a class<T> type in the
subtype position and a string-ish type in the supertype position to
classname<T> <: Tsuper *)
val prj_rewrite_classname :
sub:locl_phase t_ -> sub_prj:locl_phase t_ -> locl_phase t_

val missing_field : t

val pessimised_this : Pos_or_decl.t -> 'phase t_
Expand Down
24 changes: 12 additions & 12 deletions hphp/hack/src/typing/typing_subtype.ml
Original file line number Diff line number Diff line change
Expand Up @@ -3171,32 +3171,32 @@ end = struct
&& is_nonexact exact_super
&& (String.equal class_nm_super SN.Classes.cXHPChild
|| String.equal class_nm_super SN.Classes.cStringish) ->
(* TODO(T199610905) replace reason with upcoming rewrite reasons *)
let ty_classname = MakeType.classname r_sub [ty_sub] in
let t =
Typing_env.update_reason env ty_classname ~f:(fun r_sub_prj ->
Typing_reason.prj_rewrite_classname ~sub:r_sub ~sub_prj:r_sub_prj)
in
simplify
~subtype_env
~this_ty
~lhs:{ sub_supportdyn; ty_sub = MakeType.classname r_sub [ty_sub] }
~lhs:{ sub_supportdyn; ty_sub = t }
~rhs:{ super_like; super_supportdyn; ty_super }
env
(* -- Rewrite: class<T> <: N ---> classname<T> <: N
instead of e.g. considering class<T> as an element in a case type *)
| ((r_sub, Tclass_ptr ty_sub), (_r_super, Tnewtype _))
when TypecheckerOptions.class_sub_classname env.genv.tcopt ->
(* TODO(T199610905) replace reason with upcoming rewrite reasons *)
simplify
~subtype_env
~this_ty
~lhs:{ sub_supportdyn; ty_sub = MakeType.classname r_sub [ty_sub] }
~rhs:{ super_like; super_supportdyn; ty_super }
env
(* -- Rewrite: class<T> <: prim ---> classname<T> <: prim *)
| ((r_sub, Tclass_ptr ty_sub), (_r_super, Tprim _))
when TypecheckerOptions.class_sub_classname env.genv.tcopt ->
(* TODO(T199610905) replace reason with upcoming rewrite reasons *)
let ty_classname = MakeType.classname r_sub [ty_sub] in
let t =
Typing_env.update_reason env ty_classname ~f:(fun r_sub_prj ->
Typing_reason.prj_rewrite_classname ~sub:r_sub ~sub_prj:r_sub_prj)
in
simplify
~subtype_env
~this_ty
~lhs:{ sub_supportdyn; ty_sub = MakeType.classname r_sub [ty_sub] }
~lhs:{ sub_supportdyn; ty_sub = t }
~rhs:{ super_like; super_supportdyn; ty_super }
env
(* -- C-Var-R ----------------------------------------------------------- *)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ File class_expr_classname.php, line 6, character 58 - line 6, character 58:
Here's why:


Step 1 of 4
Step 1 of 5

I started by checking this subtype relationship.

Expand Down Expand Up @@ -122,7 +122,7 @@ File class_expr_classname.php, line 8, character 29 - line 8, character 31:
11 | }


Step 2 of 4
Step 2 of 5

The subtype is a union type so next I checked the subtype constraint is satisfied for all its elements.

Expand Down Expand Up @@ -184,7 +184,69 @@ File class_expr_classname.php, line 8, character 29 - line 8, character 31:
11 | }


Step 3 of 4
Step 3 of 5

The subtype is a class<T> type and the supertype might be classname<T> or one of its supertypes, so next I checked the constraint with the subtype rewritten to classname<T>.

The subtype comes from this generic parameter T of generic_classname

File class_expr_classname.php, line 9, character 9 - line 9, character 25:

6 | function generic_classname<T as classname<mixed>>(T $t): T { return $t; }
7 |
8 | function test(class<C> $c): int {
9 | $gc = »generic_classname«($c);
10 | return $gc;
11 | }

and flows into this hint

File class_expr_classname.php, line 6, character 58 - line 6, character 58:

5 | class C {}
6 | function generic_classname<T as classname<mixed>>(T $t): »T« { return $t; }
7 |
8 | function test(class<C> $c): int {
9 | $gc = generic_classname($c);

as the instantiation of the generic T

which I solved to this generic parameter T of generic_classname

File class_expr_classname.php, line 9, character 9 - line 9, character 25:

6 | function generic_classname<T as classname<mixed>>(T $t): T { return $t; }
7 |
8 | function test(class<C> $c): int {
9 | $gc = »generic_classname«($c);
10 | return $gc;
11 | }

and flows into this hint

File class_expr_classname.php, line 6, character 51 - line 6, character 51:

5 | class C {}
6 | function generic_classname<T as classname<mixed>>(»T« $t): T { return $t; }
7 |
8 | function test(class<C> $c): int {
9 | $gc = generic_classname($c);

as the instantiation of the generic T

The supertype is the same as before.

File class_expr_classname.php, line 8, character 29 - line 8, character 31:

6 | function generic_classname<T as classname<mixed>>(T $t): T { return $t; }
7 |
8 | function test(class<C> $c): »int« {
9 | $gc = generic_classname($c);
10 | return $gc;
11 | }


Step 4 of 5

The subtype declares an upper bound so next I checked that was a subtype of the supertype.

Expand Down Expand Up @@ -246,7 +308,7 @@ File class_expr_classname.php, line 8, character 29 - line 8, character 31:
11 | }


Step 4 of 4 (here is where the error occurred)
Step 5 of 5 (here is where the error occurred)

The subtype declares an upper bound so next I checked that was a subtype of the supertype.

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ File class_expr_string.php, line 6, character 45 - line 6, character 45:
Here's why:


Step 1 of 4
Step 1 of 5

I started by checking this subtype relationship.

Expand Down Expand Up @@ -127,7 +127,7 @@ File class_expr_string.php, line 8, character 18 - line 8, character 20:
11 | }


Step 2 of 4
Step 2 of 5

The subtype is a union type so next I checked the subtype constraint is satisfied for all its elements.

Expand Down Expand Up @@ -193,7 +193,73 @@ File class_expr_string.php, line 8, character 18 - line 8, character 20:
11 | }


Step 3 of 4
Step 3 of 5

The subtype is a class<T> type and the supertype might be classname<T> or one of its supertypes, so next I checked the constraint with the subtype rewritten to classname<T>.

The subtype comes from this generic parameter T of generic_string

File class_expr_string.php, line 9, character 9 - line 9, character 22:

6 | function generic_string<T as string>(T $t): T { return $t; }
7 |
8 | function test(): int {
9 | $gc = »generic_string«(C::class);
10 | return $gc;
11 | }

and flows into this hint

File class_expr_string.php, line 6, character 45 - line 6, character 45:

3 | <<file:__EnableUnstableFeatures('class_type')>>
4 |
5 | class C {}
6 | function generic_string<T as string>(T $t): »T« { return $t; }
7 |
8 | function test(): int {
9 | $gc = generic_string(C::class);

as the instantiation of the generic T

which I solved to this generic parameter T of generic_string

File class_expr_string.php, line 9, character 9 - line 9, character 22:

6 | function generic_string<T as string>(T $t): T { return $t; }
7 |
8 | function test(): int {
9 | $gc = »generic_string«(C::class);
10 | return $gc;
11 | }

and flows into this hint

File class_expr_string.php, line 6, character 38 - line 6, character 38:

3 | <<file:__EnableUnstableFeatures('class_type')>>
4 |
5 | class C {}
6 | function generic_string<T as string>(»T« $t): T { return $t; }
7 |
8 | function test(): int {
9 | $gc = generic_string(C::class);

as the instantiation of the generic T

The supertype is the same as before.

File class_expr_string.php, line 8, character 18 - line 8, character 20:

6 | function generic_string<T as string>(T $t): T { return $t; }
7 |
8 | function test(): »int« {
9 | $gc = generic_string(C::class);
10 | return $gc;
11 | }


Step 4 of 5

The subtype declares an upper bound so next I checked that was a subtype of the supertype.

Expand Down Expand Up @@ -259,7 +325,7 @@ File class_expr_string.php, line 8, character 18 - line 8, character 20:
11 | }


Step 4 of 4 (here is where the error occurred)
Step 5 of 5 (here is where the error occurred)

The subtype declares an upper bound so next I checked that was a subtype of the supertype.

Expand Down
Loading

0 comments on commit 9591b86

Please sign in to comment.