Skip to content

Commit

Permalink
Valid redefinition rework (#11657)
Browse files Browse the repository at this point in the history
* create type hole to see which tests fail

* pair up ttps during definition unification, then check constraints

* invert mapping to allow 1:N relationships
  • Loading branch information
Simn authored May 6, 2024
1 parent 0adc110 commit 49fe159
Show file tree
Hide file tree
Showing 23 changed files with 221 additions and 35 deletions.
31 changes: 31 additions & 0 deletions src/core/tUnification.ml
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,14 @@ type eq_kind =
| EqDoNotFollowNull (* like EqStrict, but does not follow Null<T> *)
| EqStricter

type type_param_unification_context = {
mutable type_param_pairs : (typed_type_param * typed_type_param) list;
}

type type_param_mode =
| TpDefault
| TpDefinition of type_param_unification_context

type unification_context = {
allow_transitive_cast : bool;
allow_abstract_cast : bool; (* allows a non-transitive abstract cast (from,to,@:from,@:to) *)
Expand All @@ -39,6 +47,7 @@ type unification_context = {
equality_kind : eq_kind;
equality_underlying : bool;
strict_field_kind : bool;
type_param_mode : type_param_mode;
}

type unify_min_result =
Expand All @@ -64,6 +73,7 @@ let default_unification_context = {
equality_kind = EqStrict;
equality_underlying = false;
strict_field_kind = false;
type_param_mode = TpDefault;
}

(* Unify like targets (e.g. Java) probably would. *)
Expand All @@ -75,6 +85,7 @@ let native_unification_context = {
equality_underlying = false;
allow_arg_name_mismatch = true;
strict_field_kind = false;
type_param_mode = TpDefault;
}

module Monomorph = struct
Expand Down Expand Up @@ -519,6 +530,7 @@ let rec_stack stack value fcheck frun ferror =
let rec_stack_default stack value fcheck frun def =
if not (rec_stack_exists fcheck stack) then rec_stack_loop stack value frun () else def


let rec type_eq uctx a b =
let param = uctx.equality_kind in
let can_follow_null = match param with
Expand Down Expand Up @@ -575,6 +587,9 @@ let rec type_eq uctx a b =
| TEnum (e1,tl1) , TEnum (e2,tl2) ->
if e1 != e2 && not (param = EqCoreType && e1.e_path = e2.e_path) then error [cannot_unify a b];
type_eq_params uctx a b tl1 tl2
| TInst ({cl_kind = KTypeParameter ttp1},tl1) , TInst ({cl_kind = KTypeParameter ttp2},tl2) when param <> EqCoreType ->
assign_type_params uctx ttp1 ttp2;
type_eq_params uctx a b tl1 tl2
| TInst (c1,tl1) , TInst (c2,tl2) ->
if c1 != c2 && not (param = EqCoreType && c1.cl_path = c2.cl_path) && (match c1.cl_kind, c2.cl_kind with KExpr _, KExpr _ -> false | _ -> true) then error [cannot_unify a b];
type_eq_params uctx a b tl1 tl2
Expand Down Expand Up @@ -636,6 +651,19 @@ let rec type_eq uctx a b =
| _ , _ ->
error [cannot_unify a b]

and assign_type_params uctx ttp1 ttp2 =
if ttp1 != ttp2 then begin match uctx.type_param_mode with
| TpDefault ->
error []
| TpDefinition tctx ->
begin try
let ttp3 = List.assq ttp2 tctx.type_param_pairs in
if ttp1 != ttp3 then error []
with Not_found ->
tctx.type_param_pairs <- (ttp2,ttp1) :: tctx.type_param_pairs
end
end

and type_eq_params uctx a b tl1 tl2 =
let i = ref 0 in
List.iter2 (fun t1 t2 ->
Expand Down Expand Up @@ -732,6 +760,9 @@ let rec unify (uctx : unification_context) a b =
unify_to {uctx with allow_transitive_cast = false} a b ab tl
| TAbstract (a1,tl1) , TAbstract (a2,tl2) ->
unify_abstracts uctx a b a1 tl1 a2 tl2
| TInst ({cl_kind = KTypeParameter ttp1},tl1) , TInst ({cl_kind = KTypeParameter ttp2},tl2) when uctx.type_param_mode != TpDefault ->
assign_type_params uctx ttp1 ttp2;
unify_type_params uctx a b tl1 tl2;
| TInst (c1,tl1) , TInst (c2,tl2) ->
let rec loop c tl =
if c == c2 then begin
Expand Down
1 change: 1 addition & 0 deletions src/typing/tanon_identification.ml
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,7 @@ object(self)
equality_kind = EqStricter;
equality_underlying = false;
strict_field_kind = true;
type_param_mode = TpDefault;
} else {default_unification_context with equality_kind = EqDoNotFollowNull} in

let check () =
Expand Down
60 changes: 25 additions & 35 deletions src/typing/typeloadCheck.ml
Original file line number Diff line number Diff line change
Expand Up @@ -48,49 +48,20 @@ let is_generic_parameter ctx c =
false

let valid_redefinition map1 map2 f1 t1 f2 t2 = (* child, parent *)
let tctx = {
type_param_pairs = [];
} in
let uctx = {default_unification_context with type_param_mode = TpDefinition tctx} in
let valid t1 t2 =
Type.unify t1 t2;
unify_custom uctx t1 t2;
if is_null t1 <> is_null t2 || ((follow t1) == t_dynamic && (follow t2) != t_dynamic) then raise (Unify_error [Cannot_unify (t1,t2)]);
in
begin match PurityState.get_purity_from_meta f2.cf_meta,PurityState.get_purity_from_meta f1.cf_meta with
| PurityState.Pure,PurityState.MaybePure -> f1.cf_meta <- (Meta.Pure,[EConst(Ident "expect"),f2.cf_pos],null_pos) :: f1.cf_meta
| PurityState.ExpectPure p,PurityState.MaybePure -> f1.cf_meta <- (Meta.Pure,[EConst(Ident "expect"),p],null_pos) :: f1.cf_meta
| _ -> ()
end;
let t1, t2 = (match f1.cf_params, f2.cf_params with
| [], [] -> t1, t2
| l1, l2 when List.length l1 = List.length l2 ->
let to_check = ref [] in
(* TPTODO: defaults *)
let monos = List.map2 (fun ttp1 ttp2 ->
let ct1 = get_constraints ttp1 in
let ct2 = get_constraints ttp2 in
(match ct1, ct2 with
| [], [] -> ()
| _, _ when List.length ct1 = List.length ct2 ->
(* if same constraints, they are the same type *)
let check monos =
List.iter2 (fun t1 t2 ->
try
let t1 = apply_params l1 monos (map2 t1) in
let t2 = apply_params l2 monos (map1 t2) in
type_eq EqStrict t1 t2
with Unify_error l ->
raise (Unify_error (Unify_custom "Constraints differ" :: l))
) ct1 ct2
in
to_check := check :: !to_check;
| _ ->
raise (Unify_error [Unify_custom "Different number of constraints"]));
TInst (mk_class null_module ([],ttp1.ttp_name) null_pos null_pos,[])
) l1 l2 in
List.iter (fun f -> f monos) !to_check;
apply_params l1 monos t1, apply_params l2 monos t2
| _ ->
(* ignore type params, will create other errors later *)
t1, t2
) in
match f1.cf_kind,f2.cf_kind with
begin match f1.cf_kind,f2.cf_kind with
| Method m1, Method m2 when not (m1 = MethDynamic) && not (m2 = MethDynamic) ->
begin match follow t1, follow t2 with
| TFun (args1,r1) , TFun (args2,r2) -> (
Expand Down Expand Up @@ -122,6 +93,25 @@ let valid_redefinition map1 map2 f1 t1 f2 t2 = (* child, parent *)
(* in case args differs, or if an interface var *)
type_eq EqStrict t1 t2;
if is_null t1 <> is_null t2 then raise (Unify_error [Cannot_unify (t1,t2)])
end;
let assign_ttp ttp1 ttp2 =
let ct1 = get_constraints ttp1 in
let ct2 = get_constraints ttp2 in
match ct1,ct2 with
| _,[] ->
()
| [],(t2 :: _) ->
raise (Unify_error ([Unify_custom (Printf.sprintf "Constraint unsatisfied for type parameter %s: %s" ttp2.ttp_name (s_type (print_context()) t2))]))
| ct1,ct2 ->
List.iter (fun t2 ->
let t2 = map2 t2 in
if not (List.exists (fun t1 -> does_unify (map1 t1) t2) ct1) then
raise (Unify_error ([Unify_custom (Printf.sprintf "Constraint unsatisfied for type parameter %s: %s" ttp2.ttp_name (s_type (print_context()) t2))]))
) ct2
in
List.iter (fun (ttp1,ttp2) ->
assign_ttp ttp2 ttp1
) tctx.type_param_pairs

let copy_meta meta_src meta_target sl =
let meta = ref meta_target in
Expand Down
14 changes: 14 additions & 0 deletions tests/misc/projects/Issue11411/MainArgumentVarianceBad.hx
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
class Parent {}
class Child extends Parent {}

interface I {
public function test<T:Parent>(t:T):Void;
}

class C implements I {
public function test<T:Child>(t:T) { }
}

function main() {

}
14 changes: 14 additions & 0 deletions tests/misc/projects/Issue11411/MainArgumentVarianceGood.hx
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
class Parent {}
class Child extends Parent {}

interface I {
public function test<T:Child>(t:T):Void;
}

class C implements I {
public function test<T:Parent>(t:T) { }
}

function main() {

}
14 changes: 14 additions & 0 deletions tests/misc/projects/Issue11411/MainArgumentVarianceMissingBad.hx
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
class Parent {}
class Child extends Parent {}

interface I {
public function test<T>(t:T):Void;
}

class C implements I {
public function test<T:Child>(t:T) { }
}

function main() {

}
14 changes: 14 additions & 0 deletions tests/misc/projects/Issue11411/MainArgumentVarianceMissingGood.hx
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
class Parent {}
class Child extends Parent {}

interface I {
public function test<T:Child>(t:T):Void;
}

class C implements I {
public function test<T>(t:T) { }
}

function main() {

}
13 changes: 13 additions & 0 deletions tests/misc/projects/Issue11411/MainInterfaceUnion.hx
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
interface A {}
interface B {}
class C implements A implements B {}

class Parent {
function f<TC:C>(a:TC, b:TC) {}
}

class Child extends Parent {
override function f<TA:A, TB:B>(a:TA, b:TB) {}
}

function main() {}
11 changes: 11 additions & 0 deletions tests/misc/projects/Issue11411/MainNoVariance.hx
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
interface I {
public function test<T>():Void;
}

class C implements I {
public function test<T:String>() { }
}

function main() {

}
16 changes: 16 additions & 0 deletions tests/misc/projects/Issue11411/MainReturnVarianceBad.hx
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
class Parent {}
class Child extends Parent {}

interface I {
public function test<T:Child>():T;
}

class C implements I {
public function test<T:Parent>():T {
return null;
}
}

function main() {

}
16 changes: 16 additions & 0 deletions tests/misc/projects/Issue11411/MainReturnVarianceGood.hx
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
class Parent {}
class Child extends Parent {}

interface I {
public function test<T:Parent>():T;
}

class C implements I {
public function test<T:Child>():T {
return null;
}
}

function main() {

}
16 changes: 16 additions & 0 deletions tests/misc/projects/Issue11411/MainReturnVarianceMissingBad.hx
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
class Parent {}
class Child extends Parent {}

interface I {
public function test<T:Child>():T;
}

class C implements I {
public function test<T>():T {
return null;
}
}

function main() {

}
16 changes: 16 additions & 0 deletions tests/misc/projects/Issue11411/MainReturnVarianceMissingGood.hx
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
class Parent {}
class Child extends Parent {}

interface I {
public function test<T>():T;
}

class C implements I {
public function test<T:Child>():T {
return null;
}
}

function main() {

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
--main MainArgumentVarianceBad
--interp
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
--main MainArgumentVarianceGood
--interp
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
--main MainArgumentVarianceMissingBad
--interp
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
--main MainArgumentVarianceMissingGood
--interp
2 changes: 2 additions & 0 deletions tests/misc/projects/Issue11411/compile-interface-union.hxml
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
--main MainInterfaceUnion
--interp
2 changes: 2 additions & 0 deletions tests/misc/projects/Issue11411/compile-no-variance.hxml
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
--main MainNoVariance
--interp
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
--main MainReturnVarianceBad
--interp
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
--main MainReturnVarianceGood
--interp
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
--main MainReturnVarianceMissingBad
--interp
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
--main MainReturnVarianceMissingGood
--interp

0 comments on commit 49fe159

Please sign in to comment.