Skip to content

Commit

Permalink
Consider type parameter defaults in TypeAppVarianceCheckT
Browse files Browse the repository at this point in the history
Summary:
When we compare type typeapps with the same root, we take a shortcut and only
compare the type arguments according to the polarity of the type parameter.

This shortcut did not properly account for type parameter defaults. This diff
handles the default case by ensuring that all type arguments are constrained.

Reviewed By: jbrown215

Differential Revision: D15675738

fbshipit-source-id: 35cee8b2789ecc48a89717b557fea3bd177f3fa4
  • Loading branch information
samwgoldman authored and facebook-github-bot committed Jun 5, 2019
1 parent e943b69 commit dc2cfac
Show file tree
Hide file tree
Showing 3 changed files with 49 additions and 14 deletions.
34 changes: 21 additions & 13 deletions src/typing/flow_js.ml
Original file line number Diff line number Diff line change
Expand Up @@ -3579,28 +3579,36 @@ let rec __flow cx ((l: Type.t), (u: Type.use_t)) trace =
add_output cx ~trace
(Error_message.ETooManyTypeArgs (reason_tapp, reason_arity, maximum_arity));
) else (
let unused_targs = Nel.fold_left (fun targs { name; default; polarity; reason; _ } ->
match default, targs with
| None, [] ->
(* fewer arguments than params but no default *)
add_output cx ~trace (Error_message.ETooFewTypeArgs
(reason_tapp, reason_arity, minimum_arity));
[]
| _, [] -> []
| _, (t1, t2)::targs ->
let unused_targs, _, _ = Nel.fold_left (fun (targs, map1, map2) tparam->
let { name; default; polarity; reason; _ } = tparam in
let flow_targs t1 t2 =
let use_op = Frame (TypeArgCompatibility {
name;
targ = reason;
lower = reason_op;
upper = reason_tapp;
polarity;
}, use_op) in
(match polarity with
match polarity with
| Positive -> rec_flow cx trace (t1, UseT (use_op, t2))
| Negative -> rec_flow cx trace (t2, UseT (use_op, t1))
| Neutral -> rec_unify cx trace ~use_op t1 t2);
targs
) targs tparams in
| Neutral -> rec_unify cx trace ~use_op t1 t2;
in
match default, targs with
| None, [] ->
(* fewer arguments than params but no default *)
add_output cx ~trace (Error_message.ETooFewTypeArgs
(reason_tapp, reason_arity, minimum_arity));
[], map1, map2
| Some default, [] ->
let t1 = subst cx ~use_op map1 default in
let t2 = subst cx ~use_op map2 default in
flow_targs t1 t2;
[], SMap.add name t1 map1, SMap.add name t2 map2
| _, (t1, t2)::targs ->
flow_targs t1 t2;
targs, SMap.add name t1 map1, SMap.add name t2 map2
) (targs, SMap.empty, SMap.empty) tparams in
assert (unused_targs = []);
)

Expand Down
7 changes: 7 additions & 0 deletions tests/poly/inout.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
declare opaque type A;
declare opaque type B: A;

class C<+Out, -In: Out = Out> {}

declare var x: C<B>;
(x: C<A>); // error: A ~> B in default-expanded type
22 changes: 21 additions & 1 deletion tests/poly/poly.exp
Original file line number Diff line number Diff line change
Expand Up @@ -173,6 +173,26 @@ References:
^^^^^^ [2]


Error ----------------------------------------------------------------------------------------------------- inout.js:7:2

Cannot cast `x` to `C` because `B` [1] is incompatible with `A` [2] in type argument `In` [3].

inout.js:7:2
7| (x: C<A>); // error: A ~> B in default-expanded type
^

References:
inout.js:6:18
6| declare var x: C<B>;
^ [1]
inout.js:7:7
7| (x: C<A>); // error: A ~> B in default-expanded type
^ [2]
inout.js:4:16
4| class C<+Out, -In: Out = Out> {}
^^ [3]


Error --------------------------------------------------------------------------------------------------- phantom.js:7:2

Cannot cast `a` to `B` because number [1] is incompatible with string [2] in type argument `Phantom` [3].
Expand Down Expand Up @@ -290,4 +310,4 @@ References:



Found 19 errors
Found 20 errors

0 comments on commit dc2cfac

Please sign in to comment.