Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Statics subtyping (RFC: Newable<.> to indicate constructor subtyping) #1847

Closed
wants to merge 9 commits into from
58 changes: 51 additions & 7 deletions src/typing/flow_js.ml
Original file line number Diff line number Diff line change
Expand Up @@ -1848,6 +1848,24 @@ let rec __flow cx ((l: Type.t), (u: Type.use_t)) trace =
(List.hd ts,
LookupT (reason, strict, (List.tl ts) @ try_ts_on_failure, s, t))

| IntersectionT (r, rep), GetPropT (reason_op, (reason_prop, "constructor"), t) ->
Copy link
Contributor Author

@popham popham Jun 5, 2016

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Generalize with an is_set_op_distributive gate on u, and handle UnionT too.

let retarget element = mk_tvar_where cx reason_op (fun t ->
let u = GetPropT (reason_op, (reason_prop, "constructor"), t) in
rec_flow cx trace (element, u)
) in
let ts = List.map retarget (InterRep.members rep) in
rec_flow_t cx trace (IntersectionT (r, InterRep.make ts), t)

(** constructor calls **)
| IntersectionT (r, rep),
ConstructorT (reason_op, args, t) ->
let ts = InterRep.members rep |> List.map (fun element ->
mk_tvar_where cx reason_op (fun tvar ->
rec_flow cx trace (element, ConstructorT (reason_op, args, tvar))
)
) in
rec_flow_t cx trace (IntersectionT (r, InterRep.make ts), t)

(** extends **)
| IntersectionT (_, rep),
UseT (use_op, ExtendsT (try_ts_on_failure, l, u)) ->
Expand Down Expand Up @@ -2428,16 +2446,29 @@ let rec __flow cx ((l: Type.t), (u: Type.use_t)) trace =
(* Enable structural subtyping for upperbounds like interfaces *)
(***************************************************************)

| (ClassT _,
UseT (_, InstanceT (_, _, _, { structural = true; _; }))) ->
flow_err cx trace
"This class is incompatible with interface (did you forget 'Class<.>')"
l u;

| (_,
UseT (_, InstanceT (reason_inst, _, super, {
UseT (_, InstanceT (reason_inst, static, super, {
fields_tmap;
methods_tmap;
structural = true;
_;
})))
->
structural_subtype cx trace l reason_inst
(super, fields_tmap, methods_tmap)
(super, fields_tmap, methods_tmap);

(match l, static with
| InstanceT (_, l, _, _),
InstanceT (reason, _, super, { fields_tmap; methods_tmap; _; }) ->
structural_subtype cx trace l reason
(super, fields_tmap, methods_tmap)
| _ -> ())

(********************************************************)
(* runtime types derive static types through annotation *)
Expand Down Expand Up @@ -2475,8 +2506,8 @@ let rec __flow cx ((l: Type.t), (u: Type.use_t)) trace =
"Ineligible value used in/as type annotation (did you forget 'typeof'?)"
l u

| (ClassT(l), UseT (_, ClassT(u))) ->
rec_unify cx trace l u
| (ClassT(l), UseT (use_op, ClassT(u))) ->
rec_flow cx trace (l, UseT (use_op, u))

| FunT (_,static1,prototype,_),
UseT (_, ClassT (InstanceT (_,static2,_, _) as u_)) ->
Expand Down Expand Up @@ -3464,15 +3495,23 @@ let rec __flow cx ((l: Type.t), (u: Type.use_t)) trace =
rec_flow cx trace
(next, UseT (use_op, ExtendsT (try_ts_on_failure, l, u)))

| (MixedT _, UseT (_, ExtendsT ([], l, InstanceT (reason_inst, _, super, {
| (MixedT _,
UseT (_, ExtendsT ([], l, InstanceT (reason_inst, static, super, {
fields_tmap;
methods_tmap;
structural = true;
_;
}))))
->
structural_subtype cx trace l reason_inst
(super, fields_tmap, methods_tmap)
(super, fields_tmap, methods_tmap);

(match l, static with
| InstanceT (_, l, _, _),
InstanceT (reason, _, super, { fields_tmap; methods_tmap; _; }) ->
structural_subtype cx trace l reason
(super, fields_tmap, methods_tmap)
| _ -> ())

| (MixedT _, UseT (_, ExtendsT ([], t, tc))) ->
let msg = "This type is incompatible with" in
Expand Down Expand Up @@ -3693,6 +3732,7 @@ and ground_subtype = function
| (VoidT _, UseT (_, VoidT _))
| (EmptyT _, _)
| (_, UseT (_, MixedT _))
| (_, UseT (_, ClassT (MixedT _)))
| (_, UseT (_, FunProtoT _)) (* MixedT is used for object protos, this is for funcs *)
| (AnyT _, _)
| (_, UseT (_, AnyT _))
Expand Down Expand Up @@ -6091,6 +6131,7 @@ and become cx ?trace r t = match t with

(* set the position of the given def type from a reason *)
and reposition cx ?trace reason t =
let repos t = mod_reason_of_t (repos_reason (loc_of_reason reason)) t in
match t with
| OpenT (r, id) ->
let constraints = find_graph cx id in
Expand All @@ -6115,7 +6156,10 @@ and reposition cx ?trace reason t =
mk_tvar_where cx reason (fun tvar ->
flow_opt cx ?trace (t, ReposLowerT (reason, UseT (UnknownUse, tvar)))
)
| _ -> mod_reason_of_t (repos_reason (loc_of_reason reason)) t
| InstanceT (reason_inst, static, super, insttype) ->
let loc = loc_of_reason reason in
InstanceT (repos_reason loc reason_inst, repos static, super, insttype)
| _ -> repos t

(* given the type of a value v, return the type term
representing the `typeof v` annotation expression *)
Expand Down
8 changes: 1 addition & 7 deletions tests/class_statics/class_statics.exp
Original file line number Diff line number Diff line change
@@ -1,9 +1,3 @@
test.js:1
1: class A {
^ A. This type is incompatible with
48: (B: typeof A);
^ B

test.js:2
2: static x: number;
^^^^^^ number. This type is incompatible with
Expand Down Expand Up @@ -107,4 +101,4 @@ test.js:54
^^^^^^ string


Found 16 errors
Found 15 errors
14 changes: 13 additions & 1 deletion tests/class_subtyping/class_subtyping.exp
Original file line number Diff line number Diff line change
Expand Up @@ -32,5 +32,17 @@ test4.js:13
13: foo(new D, { f_: 0 });
^^^^^^^^^ object literal

test5.js:17
17: function gn(): Class<A> & Class<I> {
^ property `m1` of I. Property not found in
18: return class K extends A {}
^^^^^^^^^^^^^^^^^^^^ class expr `K`

Found 5 errors
test5.js:17
17: function gn(): Class<A> & Class<I> {
^ property `sm1` of statics of I. Property not found in
18: return class K extends A {}
^^^^^^^^^^^^^^^^^^^^ statics of class expr `K`


Found 7 errors
24 changes: 24 additions & 0 deletions tests/class_subtyping/test5.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
//@flow

class A {}

interface I {
m1(): string;
static sm1(): number;
}

function fn(): Class<A> & Class<I> {
return class K extends A {
m1() { return "a string"; }
static sm1() { return 1; }
}
}

function gn(): Class<A> & Class<I> {
return class K extends A {}
}

var X = fn();
var x = new X();

var Y = gn();
8 changes: 7 additions & 1 deletion tests/class_type/class_type.exp
Original file line number Diff line number Diff line change
@@ -1,2 +1,8 @@
test2.js:35
35: static gn(): Class<Y> { //NG
^ Y. This type is incompatible with
29: static gn(): Class<this> {
^^^^ some incompatible instantiation of `this`

Found 0 errors

Found 1 error
40 changes: 40 additions & 0 deletions tests/class_type/test2.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
class A {}
class B extends A {}

class K {
static gn() {
return this;
}
}

class L extends K {
static gn() { // NG Bug: False positive.
return L;
}
}

class W {
fn(): Class<A> {
return B;
}
static fn(): Class<A> {
return B;
}
static gn(): Class<W> {
return W;
}
}

class X extends W {
static gn(): Class<this> {
return this;
}
}

class Y extends X {
static gn(): Class<Y> { //NG
return Y;
}
}

var a: Class<W> = X.gn();
28 changes: 26 additions & 2 deletions tests/interface/interface.exp
Original file line number Diff line number Diff line change
Expand Up @@ -108,5 +108,29 @@ test3.js:6
6: (k.y: number); // error: y is string in I
^^^^^^ number


Found 19 errors
test4.js:8
8: var p: A & I = a; // NG
^ property `fn` of I. Property not found in
8: var p: A & I = a; // NG
^ A

test4.js:8
8: var p: A & I = a; // NG
^ property `gn` of statics of I. Property not found in
8: var p: A & I = a; // NG
^ statics of A

test4.js:9
9: var P: Class<A> & Class<I> = A; // NG
^ property `gn` of statics of I. Property not found in
6: class A {}
^ statics of A

test4.js:9
9: var P: Class<A> & Class<I> = A; // NG
^ property `fn` of I. Property not found in
9: var P: Class<A> & Class<I> = A; // NG
^ A


Found 23 errors
21 changes: 21 additions & 0 deletions tests/interface/test4.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
interface I {
fn(): number;
static gn(): number;
}

class A {}
var a = new A();
var p: A & I = a; // NG
var P: Class<A> & Class<I> = A; // NG

class B extends A {
fn(): number {
return 1;
}
static gn(): number {
return 2;
}
}
var b = new B();
var q: A & I = b; // OK
var Q: Class<A> & Class<I> = B; // OK
48 changes: 47 additions & 1 deletion tests/intersection/intersection.exp
Original file line number Diff line number Diff line change
@@ -1,3 +1,49 @@
intersection2.js:33
33: var a = new Ap();
^^^^^^^^ constructor call. Constructor cannot be called on
28: function gn(Ap: Class<I> & Class<A> & J): void { // Forgot `Class<.>` on `J`
^ J

intersection2.js:48
48: gn(JLess);
^^^^^^^^^ function call
48: gn(JLess);
^^^^^ class type: JLess. This class is incompatible with interface (did you forget 'Class<.>')
28: function gn(Ap: Class<I> & Class<A> & J): void { // Forgot `Class<.>` on `J`
^ J

intersection2.js:57
57: fn(full);
^^^^^^^^ function call
57: fn(full);
^^^^ Full. This type is incompatible with
11: function fn(a: I & A): void {
^ A

intersection2.js:58
58: gn(Full);
^^^^^^^^ function call
58: gn(Full);
^^^^ Full. This type is incompatible with
28: function gn(Ap: Class<I> & Class<A> & J): void { // Forgot `Class<.>` on `J`
^ A

intersection2.js:58
58: gn(Full);
^^^^^^^^ function call
58: gn(Full);
^^^^ class type: Full. This class is incompatible with interface (did you forget 'Class<.>')
28: function gn(Ap: Class<I> & Class<A> & J): void { // Forgot `Class<.>` on `J`
^ J

intersection2.js:68
68: gn(AlmostJ);
^^^^^^^^^^^ function call
68: gn(AlmostJ);
^^^^^^^ class type: AlmostJ. This class is incompatible with interface (did you forget 'Class<.>')
28: function gn(Ap: Class<I> & Class<A> & J): void { // Forgot `Class<.>` on `J`
^ J

test_obj.js:29
29: var e: E = c; // error
^ intersection. This type is incompatible with
Expand All @@ -21,4 +67,4 @@ test_obj.js:29
^^^^^^ number. See lib: lib/lib.js:33


Found 1 error
Found 7 errors
Loading