Skip to content

Commit

Permalink
Relax variance restrictions on constraints on class type parameters
Browse files Browse the repository at this point in the history
Summary:
Covariant and contravariant parameters can safely appear anywhere in "as" and "super" constraints on
class type parameters. Currently an "as" constraint is treated as a negative (contravariant) position,
and a "super" constraint as a positive (covariant) position. This is necessary for constraints on *method*
type parameters but there is no soundness requirement for this check on class type parameters.

(See http://research.microsoft.com/pubs/64042/ecoop06.pdf for a soundness theorem that proves it.)

Reviewed By: dlreeves

Differential Revision: D3235459

fb-gh-sync-id: 483a61d9ddc1201c31a9ef870daaa4a366cc5693
fbshipit-source-id: 483a61d9ddc1201c31a9ef870daaa4a366cc5693
  • Loading branch information
andrewjkennedy authored and Hhvm Bot committed Apr 28, 2016
1 parent 784ea81 commit 7f14eab
Show file tree
Hide file tree
Showing 5 changed files with 27 additions and 10 deletions.
22 changes: 14 additions & 8 deletions hphp/hack/src/typing/typing_variance.ml
Expand Up @@ -140,9 +140,9 @@ let reason_to_string ~sign (_, descr, variance) =
(variance_to_string variance)
name
| Rconstraint_super ->
"`super` constraints are covariant"
"`super` constraints on method type parameters are covariant"
| Rconstraint_as ->
"`as` constraints are contravariant"
"`as` constraints on method type parameters are contravariant"

let detailed_message variance pos stack =
match stack with
Expand Down Expand Up @@ -384,6 +384,8 @@ and class_method tcopt root _method_name method_ env =
end ~init:env in
let env =
List.fold_left ~f:(fun_param tcopt root) ~init:env ft_params in
let env =
List.fold_left ~f:(fun_tparam tcopt root) ~init:env ft_tparams in
let env = fun_ret tcopt root env ft_ret in
env
| _ -> assert false
Expand All @@ -394,6 +396,12 @@ and fun_param tcopt root env (_, (reason, _ as ty)) =
let variance = Vcontravariant [reason_contravariant] in
type_ tcopt root variance env ty

and fun_tparam tcopt root env (_, _, cstr_opt) =
begin match cstr_opt with
| None -> env
| Some cstr -> constraint_ tcopt root env cstr
end

and fun_ret tcopt root env (reason, _ as ty) =
let pos = Reason.to_pos reason in
let reason_covariant = pos, Rfun_return, Pcovariant in
Expand All @@ -419,7 +427,7 @@ and type_ tcopt root variance env (reason, ty) =
(* `this` constraints are bivariant (otherwise any class that used the
* `this` type would not be able to use covariant type params) *)
env
| Tgeneric (name, cstr_opt) ->
| Tgeneric (name, _) ->
let pos = Reason.to_pos reason in
(* This section makes the position more precise.
* Say we find a return type that is a tuple (int, int, T).
Expand All @@ -438,10 +446,7 @@ and type_ tcopt root variance env (reason, ty) =
| x -> x
in
let env = add_variance env name variance in
begin match cstr_opt with
| None -> env
| Some cstr -> constraint_ tcopt root env cstr
end
env
| Toption ty ->
type_ tcopt root variance env ty
| Tprim _ -> env
Expand Down Expand Up @@ -481,7 +486,8 @@ and type_ tcopt root variance env (reason, ty) =
type_ tcopt root variance env ty
end ty_map env

(* `as` constraints must be contravariant and `super` constraints covariant. To
(* `as` constraints on method type parameters must be contravariant
* and `super` constraints on method type parameters are covariant. To
* see why, suppose that we allow the wrong variance:
*
* class Foo<+T> {
Expand Down
Expand Up @@ -3,4 +3,4 @@ Illegal usage of a covariant type parameter (Typing[4120])
File "covariance_as_constraint_1.php", line 3, characters 12-12:
This is where the parameter was declared as covariant (+)
File "covariance_as_constraint_1.php", line 4, characters 29-29:
`as` constraints are contravariant
`as` constraints on method type parameters are contravariant
10 changes: 10 additions & 0 deletions hphp/hack/test/typecheck/covariance_as_constraint_3.php
@@ -0,0 +1,10 @@
<?hh // strict
// Copyright 2004-present Facebook. All Rights Reserved.

interface Box<+T> {
public function get(): T;
}

interface BoxFactory<+T, +Tbox as Box<T>> {
public function make(): Tbox;
}
@@ -0,0 +1 @@
No errors
Expand Up @@ -3,4 +3,4 @@ Illegal usage of a contravariant type parameter (Typing[4121])
File "covariance_super_constraint.php", line 3, characters 12-12:
This is where the parameter was declared as contravariant (-)
File "covariance_super_constraint.php", line 4, characters 32-32:
`super` constraints are covariant
`super` constraints on method type parameters are covariant

0 comments on commit 7f14eab

Please sign in to comment.