Skip to content

Commit

Permalink
[flow] increase precedence of IntersectionT ~> _ quick check
Browse files Browse the repository at this point in the history
Summary:
The comment in the code that moved explains why we need this case in the checker.

This diff allows this check to be used earlier before TypeAppTs have been expanded in the RHS. This is to support check
```
P<T> & {} <: P<T>
```
Types like the one on the left are not very common, but can appear when applying type guard refinements.

Changelog: [fix] fixed a limitation when checking intersections of polymorphic types (e.g. [tryFlow](https://flow.org/try/#1N4Igxg9gdgZglgcxALlAIwIZoKYBsD6uEEAztvhgE6UYCe+JADpdhgCYowa5kA0I2KAFcAtiRQAXSkOz9sADwxgJ+NPTbYuQ3BMnTZA+Y2yU4IwRO4A6SFBIrGVDGM7c+h46fNRLuKxJIGWh8MeT0ZfhYlCStpHzNsFBAMIQkIEQwJODAQfiEyfBE4eWw2fDgofDBMsAALfAA3KjgsXGxxZC4eAw0G-GhcWn9aY3wWZldu-g1mbGqJUoBaCRHEzrcDEgBrbAk62kXhXFxJ923d-cPRHEpTgyEoMDaqZdW7vKgoOfaSKgOKpqmDA+d4gB5fMA-P6LCCMLLQbiLOoYCqgh6-GDYRYIXYLSgkRZkCR4jpddwPfJLZjpOBkO4AX34kA0SRWxgABABBdkAXnZwHZlgQyHZAEZ2fSANwAHR8q3ZACFefzBRhheyAEwSmVQWVs7DsgAKAB4ACoAPmV3IAPoqdRonlQDYD2fIRSbgbRLQAyflS2XydkYEhG42e82S9kAeij7IA7nBjuyoBAJOycEGoOyTJQIJRkxA47kQA0TCQ4NAkg0AAxWDUAVnrVmrIHpQA))

Reviewed By: SamChou19815

Differential Revision: D66558959

fbshipit-source-id: bae559d502a2acfef0b82655680fc3c87aae20a3
  • Loading branch information
panagosg7 authored and facebook-github-bot committed Dec 3, 2024
1 parent efec76e commit bf006da
Show file tree
Hide file tree
Showing 2 changed files with 27 additions and 21 deletions.
42 changes: 21 additions & 21 deletions src/typing/subtyping_kit.ml
Original file line number Diff line number Diff line change
Expand Up @@ -737,6 +737,27 @@ module Make (Flow : INPUT) : OUTPUT = struct
| (_, ThisTypeAppT (reason_tapp, c, this, ts)) ->
let reason_op = reason_of_t l in
instantiate_this_class cx trace ~reason_op ~reason_tapp c ts this (Lower (use_op, l))
(*
* When a subtyping question involves a union appearing on the right or an
* intersection appearing on the left, the simplification rules are
* imprecise: we split the union / intersection into cases and try to prove
* that the subtyping question holds for one of the cases, but each of those
* cases may be unprovable, which might lead to spurious errors. In
* particular, obvious assertions such as (A | B) & C is a subtype of A | B
* cannot be proved if we choose to split the union first (discharging
* unprovable subgoals of (A | B) & C being a subtype of either A or B);
* dually, obvious assertions such as A & B is a subtype of (A & B) | C
* cannot be proved if we choose to simplify the intersection first
* (discharging unprovable subgoals of either A or B being a subtype of (A &
* B) | C). So instead, we try inclusion rules to handle such cases.
*
* An orthogonal benefit is that for large unions or intersections, checking
* inclusion is significantly faster that splitting for proving simple
* inequalities (O(n) instead of O(n^2) for n cases).
*)
| (IntersectionT (_, rep), u)
when Base.List.mem ~equal:(Concrete_type_eq.eq cx) (InterRep.members rep) u ->
()
(* If we have a TypeAppT (c, ts) ~> TypeAppT (c, ts) then we want to
* concretize both cs to PolyTs so that we may referentially compare them.
* We cannot compare the non-concretized versions since they may have been
Expand Down Expand Up @@ -1096,27 +1117,6 @@ module Make (Flow : INPUT) : OUTPUT = struct
| _ -> ()
);
InterRep.members rep |> List.iter (fun t -> rec_flow cx trace (l, UseT (use_op, t)))
(*
* When a subtyping question involves a union appearing on the right or an
* intersection appearing on the left, the simplification rules are
* imprecise: we split the union / intersection into cases and try to prove
* that the subtyping question holds for one of the cases, but each of those
* cases may be unprovable, which might lead to spurious errors. In
* particular, obvious assertions such as (A | B) & C is a subtype of A | B
* cannot be proved if we choose to split the union first (discharging
* unprovable subgoals of (A | B) & C being a subtype of either A or B);
* dually, obvious assertions such as A & B is a subtype of (A & B) | C
* cannot be proved if we choose to simplify the intersection first
* (discharging unprovable subgoals of either A or B being a subtype of (A &
* B) | C). So instead, we try inclusion rules to handle such cases.
*
* An orthogonal benefit is that for large unions or intersections, checking
* inclusion is significantly faster that splitting for proving simple
* inequalities (O(n) instead of O(n^2) for n cases).
*)
| (IntersectionT (_, rep), u)
when Base.List.mem ~equal:(Concrete_type_eq.eq cx) (InterRep.members rep) u ->
()
(* String enum sets can be handled in logarithmic time by just
* checking for membership in the set.
*)
Expand Down
6 changes: 6 additions & 0 deletions tests/intersection/typeapp.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
type A = { tag: 1 };
type B = { tag: 2 };

type P<T> = A | B;
declare var x: P<any> & {};
x as P<any>;

0 comments on commit bf006da

Please sign in to comment.