diff --git a/src/core/tUnification.ml b/src/core/tUnification.ml index 0cac5763cc6..6736102db29 100644 --- a/src/core/tUnification.ml +++ b/src/core/tUnification.ml @@ -31,11 +31,13 @@ type eq_kind = | EqDoNotFollowNull (* like EqStrict, but does not follow Null<T> *) type unification_context = { - allow_transitive_cast : bool; - allow_abstract_cast : bool; (* allows a non-transitive abstract cast (from,to,@:from,@:to) *) - allow_dynamic_to_cast : bool; (* allows a cast from dynamic to non-dynamic *) - equality_kind : eq_kind; - equality_underlying : bool; + allow_transitive_cast : bool; + allow_abstract_cast : bool; (* allows a non-transitive abstract cast (from,to,@:from,@:to) *) + allow_dynamic_to_cast : bool; (* allows a cast from dynamic to non-dynamic *) + allow_arg_name_mismatch : bool; + equality_kind : eq_kind; + equality_underlying : bool; + strict_field_kind : bool; } type unify_min_result = @@ -53,12 +55,50 @@ let check_constraint name f = let unify_ref : (unification_context -> t -> t -> unit) ref = ref (fun _ _ _ -> ()) let unify_min_ref : (unification_context -> t -> t list -> unify_min_result) ref = ref (fun _ _ _ -> assert false) +(* + we can restrict access as soon as both are runtime-compatible +*) +let unify_access a1 a2 = + a1 = a2 || match a1, a2 with + | _, AccNo | _, AccNever -> true + | AccInline, AccNormal -> true + | _ -> false + +let direct_access = function + | AccNo | AccNever | AccNormal | AccInline | AccRequire _ | AccCtor -> true + | AccCall -> false + +let unify_kind ?(strict:bool = false) k1 k2 = + k1 = k2 || match k1, k2 with + | Var v1, Var v2 -> unify_access v1.v_read v2.v_read && unify_access v1.v_write v2.v_write + | Method m1, Method m2 -> + (match m1,m2 with + | MethInline, MethNormal + | MethDynamic, MethNormal -> true + | _ -> false) + | Var v, Method m when not strict -> + (match v.v_read, v.v_write, m with + | AccNormal, _, MethNormal -> true + | AccNormal, AccNormal, MethDynamic -> true + | _ -> false) + | Method m, Var v when not strict -> + (match m with + | MethDynamic -> direct_access v.v_read && direct_access v.v_write + | MethMacro -> false + | MethNormal | MethInline -> + match v.v_read,v.v_write with + | AccNormal,(AccNo | AccNever) -> true + | _ -> false) + | _ -> false + let default_unification_context = { - allow_transitive_cast = true; - allow_abstract_cast = true; - allow_dynamic_to_cast = true; - equality_kind = EqStrict; - equality_underlying = false; + allow_transitive_cast = true; + allow_abstract_cast = true; + allow_dynamic_to_cast = true; + allow_arg_name_mismatch = true; + equality_kind = EqStrict; + equality_underlying = false; + strict_field_kind = false; } module Monomorph = struct @@ -410,45 +450,6 @@ let invalid_visibility n = Invalid_visibility n let has_no_field t n = Has_no_field (t,n) let has_extra_field t n = Has_extra_field (t,n) -(* - we can restrict access as soon as both are runtime-compatible -*) -let unify_access a1 a2 = - a1 = a2 || match a1, a2 with - | _, AccNo | _, AccNever -> true - | AccInline, AccNormal -> true - | _ -> false - -let direct_access = function - | AccNo | AccNever | AccNormal | AccInline | AccRequire _ | AccCtor -> true - | AccCall -> false - -let unify_kind k1 k2 = - k1 = k2 || match k1, k2 with - | Var v1, Var v2 -> unify_access v1.v_read v2.v_read && unify_access v1.v_write v2.v_write - | Var v, Method m -> - (match v.v_read, v.v_write, m with - | AccNormal, _, MethNormal -> true - | AccNormal, AccNormal, MethDynamic -> true - | _ -> false) - | Method m, Var v -> - (match m with - | MethDynamic -> direct_access v.v_read && direct_access v.v_write - | MethMacro -> false - | MethNormal | MethInline -> - match v.v_read,v.v_write with - | AccNormal,(AccNo | AccNever) -> true - | _ -> false) - | Method m1, Method m2 -> - match m1,m2 with - | MethInline, MethNormal - | MethDynamic, MethNormal -> true - | _ -> false - -let unify_kind_strict cfk1 cfk2 = cfk1 = cfk2 || match cfk1, cfk2 with - | Var _, Var _ | Method _, Method _ -> unify_kind cfk1 cfk2 - | _ -> false - type 'a rec_stack = { mutable rec_stack : 'a list; } @@ -545,9 +546,10 @@ let rec type_eq uctx a b = let i = ref 0 in (try type_eq uctx r1 r2; - List.iter2 (fun (n,o1,t1) (_,o2,t2) -> + List.iter2 (fun (n1,o1,t1) (n2,o2,t2) -> incr i; - if o1 <> o2 then error [Not_matching_optional n]; + if not uctx.allow_arg_name_mismatch && n1 <> n2 then error [Unify_custom (Printf.sprintf "Arg name mismatch: %s should be %s" n2 n1)]; + if o1 <> o2 then error [Not_matching_optional n1]; type_eq uctx t1 t2 ) l1 l2 with @@ -572,8 +574,7 @@ let rec type_eq uctx a b = PMap.iter (fun n f1 -> try let f2 = PMap.find n a2.a_fields in - (* if f1.cf_kind <> f2.cf_kind && (param = EqStrict || param = EqCoreType || not (unify_kind f1.cf_kind f2.cf_kind)) then error [invalid_kind n f1.cf_kind f2.cf_kind]; *) - if f1.cf_kind <> f2.cf_kind && (param = EqStrict || param = EqCoreType || param = EqDoNotFollowNull || not (unify_kind f1.cf_kind f2.cf_kind)) then error [invalid_kind n f1.cf_kind f2.cf_kind]; + if f1.cf_kind <> f2.cf_kind && (param = EqStrict || param = EqCoreType || param = EqDoNotFollowNull || not (unify_kind ~strict:uctx.strict_field_kind f1.cf_kind f2.cf_kind)) then error [invalid_kind n f1.cf_kind f2.cf_kind]; let a = f1.cf_type and b = f2.cf_type in (try type_eq uctx a b with Unify_error l -> error (invalid_field n :: l)); if (has_class_field_flag f1 CfPublic) != (has_class_field_flag f2 CfPublic) then error [invalid_visibility n]; @@ -750,7 +751,7 @@ let rec unify (uctx : unification_context) a b = in let _, ft, f1 = (try raw_class_field make_type c tl n with Not_found -> error [has_no_field a n]) in let ft = apply_params c.cl_params tl ft in - if not (unify_kind f1.cf_kind f2.cf_kind) then error [invalid_kind n f1.cf_kind f2.cf_kind]; + if not (unify_kind ~strict:uctx.strict_field_kind f1.cf_kind f2.cf_kind) then error [invalid_kind n f1.cf_kind f2.cf_kind]; if (has_class_field_flag f2 CfPublic) && not (has_class_field_flag f1 CfPublic) then error [invalid_visibility n]; (match f2.cf_kind with @@ -906,7 +907,7 @@ and unify_anons uctx a b a1 a2 = PMap.iter (fun n f2 -> try let f1 = PMap.find n a1.a_fields in - if not (unify_kind f1.cf_kind f2.cf_kind) then + if not (unify_kind ~strict:uctx.strict_field_kind f1.cf_kind f2.cf_kind) then error [invalid_kind n f1.cf_kind f2.cf_kind]; if (has_class_field_flag f2 CfPublic) && not (has_class_field_flag f1 CfPublic) then error [invalid_visibility n]; try diff --git a/src/typing/tanon_identification.ml b/src/typing/tanon_identification.ml index 16bd7018f75..f5ab7653963 100644 --- a/src/typing/tanon_identification.ml +++ b/src/typing/tanon_identification.ml @@ -59,7 +59,17 @@ object(self) DynArray.add (DynArray.get pfm_by_arity pfm.pfm_arity) pfm; Hashtbl.replace pfms path pfm - method unify ?(unify_kind = TUnification.unify_kind) ?(strict:bool = false) (tc : Type.t) (pfm : 'a path_field_mapping) = + method unify ?(strict:bool = false) (tc : Type.t) (pfm : 'a path_field_mapping) = + let uctx = if strict then { + allow_transitive_cast = false; + allow_abstract_cast = false; + allow_dynamic_to_cast = false; + allow_arg_name_mismatch = false; + equality_kind = EqDoNotFollowNull; + equality_underlying = true; + strict_field_kind = true; + } else {default_unification_context with equality_kind = EqDoNotFollowNull} in + let check () = let pair_up fields = PMap.fold (fun cf acc -> @@ -73,7 +83,7 @@ object(self) let monos = List.map (fun _ -> mk_mono()) pfm.pfm_params in let map = apply_params pfm.pfm_params monos in List.iter (fun (cf,cf') -> - if not (unify_kind cf'.cf_kind cf.cf_kind) then raise (Unify_error [Unify_custom "kind mismatch"]); + if not (unify_kind ~strict:uctx.strict_field_kind cf'.cf_kind cf.cf_kind) then raise (Unify_error [Unify_custom "kind mismatch"]); Type.unify (apply_params c.cl_params tl (monomorphs cf'.cf_params cf'.cf_type)) (map (monomorphs cf.cf_params cf.cf_type)) ) pairs; monos @@ -83,15 +93,9 @@ object(self) let monos = List.map (fun _ -> mk_mono()) pfm.pfm_params in let map = apply_params pfm.pfm_params monos in List.iter (fun (cf,cf') -> - if not (unify_kind cf'.cf_kind cf.cf_kind) then raise (Unify_error [Unify_custom "kind mismatch"]); + if strict && (Meta.has Meta.Optional cf.cf_meta) != (Meta.has Meta.Optional cf'.cf_meta) then raise (Unify_error [Unify_custom "optional mismatch"]); + if not (unify_kind ~strict:uctx.strict_field_kind cf'.cf_kind cf.cf_kind) then raise (Unify_error [Unify_custom "kind mismatch"]); fields := PMap.remove cf.cf_name !fields; - let uctx = if strict then { - allow_transitive_cast = false; - allow_abstract_cast = false; - allow_dynamic_to_cast = false; - equality_kind = EqDoNotFollowNull; - equality_underlying = true; - } else {default_unification_context with equality_kind = EqDoNotFollowNull} in type_eq_custom uctx cf'.cf_type (map (monomorphs cf.cf_params cf.cf_type)) ) pairs; if not (PMap.is_empty !fields) then raise (Unify_error [Unify_custom "not enough fields"]); @@ -123,7 +127,7 @@ object(self) raise Not_found; let pfm = DynArray.unsafe_get d i in try - if strict then self#unify ~unify_kind:unify_kind_strict ~strict tc pfm else self#unify tc pfm; + if strict then self#unify ~strict tc pfm else self#unify tc pfm; pfm with Unify_error _ -> loop (i + 1)