Permalink
Browse files

Don't add redundant bounds to tvenv

Summary: As with `tpenv`, it's sensible to avoid adding redundant upper and lower bounds, by passing the `intersect` and `union` parameters into `add_tyvar_upper_bound` etc.

Reviewed By: manzyuk

Differential Revision: D13213945

fbshipit-source-id: 60a6121367812dfa57197850b7a7c05b9d943138
  • Loading branch information...
andrewjkennedy authored and hhvm-bot committed Nov 27, 2018
1 parent b3b676e commit cdfe901964306e907c01c70643719890a81d0d5e
@@ -1550,7 +1550,7 @@ and props_to_env env remain props =
[Log_type ("var", ty'); Log_type ("ty", ty)])]));
let tyl = Typing_set.elements (Env.get_tyvar_lower_bounds env var) in
let env = List.fold_left ~f:(fun env ty_sub -> sub_type env ty_sub ty) ~init:env tyl in
props_to_env (Env.add_tyvar_upper_bound env var ty) remain props
props_to_env (Env.add_tyvar_upper_bound ~intersect:(try_intersect env) env var ty) remain props
| TL.IsSubtype (ty, ((r, Tvar var) as ty')) :: props ->
(* Add a new lower bound ty on var. Apply transitivity of sutyping, so if we
* already have var <: tyl then check that for each ty_super in tyl we
@@ -1562,7 +1562,7 @@ and props_to_env env remain props =
[Log_type ("var", ty'); Log_type ("ty", ty)])]));
let tyl = Typing_set.elements (Env.get_tyvar_upper_bounds env var) in
let env = List.fold_left ~f:(fun env ty_super -> sub_type env ty ty_super) ~init:env tyl in
props_to_env (Env.add_tyvar_lower_bound env var ty) remain props
props_to_env (Env.add_tyvar_lower_bound ~union:(try_union env) env var ty) remain props
| TL.Conj props' :: props ->
props_to_env env remain (props' @ props)
| prop :: props ->
@@ -1853,7 +1853,7 @@ and try_intersect env ty tyl =
* 3. It can be assumed that the original list contains no redundancy.
* TODO: there are many more unions to implement yet.
*)
let rec try_union env ty tyl =
and try_union env ty tyl =
match tyl with
| [] -> [ty]
| ty'::tyl' ->
@@ -0,0 +1,20 @@
<?hh // strict
// Copyright 2004-present Facebook. All Rights Reserved.
class B { }
class D extends B { }
class Cov<+T> { }
function expectCovB(Cov<B> $cb): void { }
function expectCovD(Cov<D> $cd): void { }
function testit():void {
$x = new Cov();
expectCovB($x);
expectCovD($x);
$y = new Cov();
expectCovD($y);
expectCovB($y);
// hh_show_env(); should show D as upper bound for both type variables
}
@@ -0,0 +1,35 @@
<?hh // strict
// Copyright 2004-present Facebook. All Rights Reserved.
class Inv<Tinv> {
public function __construct(public Tinv $item):void { }
}
class B { }
class C extends B {
public function foo():void { }
}
class D extends B {
}
function make_inv<T>(T $x):Inv<T> {
return new Inv($x);
}
function expectD(D $d):void {
}
function expectB(B $b):void {
}
function testit(C $c): void {
// So C <: v
// $z : Inv<v>
$z = make_inv($c);
$a = $z->item;
// This is benign; we should now have C <: v <: B
expectB($a);
// Now we will require v <: D
// Even though we haven't "solved" for v, transitivity will require that C <: D
expectD($a);
}
@@ -0,0 +1,6 @@
File "transitive_error2.php", line 34, characters 11-12:
Invalid argument (Typing[4110])
File "transitive_error2.php", line 20, characters 18-18:
This is an object of type D
File "transitive_error2.php", line 25, characters 17-17:
It is incompatible with an object of type C

0 comments on commit cdfe901

Please sign in to comment.