Implement conditionally-dynamic classes
commitce890f08169b3a48501c897c68c8eeeac938be07
authorAndrew Kennedy <akenn@fb.com>
Wed, 5 May 2021 16:55:16 +0000 (5 09:55 -0700)
committerFacebook GitHub Bot <facebook-github-bot@users.noreply.github.com>
Wed, 5 May 2021 16:56:50 +0000 (5 09:56 -0700)
treeb867efa3bcb4afe8e863a2607d921abf2b849204
parentf7a32cef1e4e92ad50be3907ccd23fa5dfdd6146
Implement conditionally-dynamic classes

Summary:
Writing `<<__SupportDynamicType>>` on a type imposes stringent conditions on its methods: in particular, most generic types cannot be marked this way because the generic parameter is neither an enforced type nor a subtype of `dynamic`. Often we would like the generic type to be support dynamic whenever its generic parameters do, in other words, it is *conditionally-dynamic*. Container classes are a classic example. Here is our favourite `Box<T>` example:
```
<<__SupportDynamicType>>
class Box<T> {
  public function __construct(private T $item) { }
  public function get(): T {
    return $this->item;
  }
  public function set(T $val): void {
    $this->item = $val;
  }
}
```
This diff introduces conditional-dynamic as the default on generic parameters, unless the parameter is marked with `__NoRequireDynamic` attribute. There are four major changes:

(1) Subtyping. For the above example, `Box<dynamic> <D: dynamic`, i.e. `Box<dynamic>` is a dynamic-aware subtype of `dynamic`. More generally, an instantiation of a generic type that is marked `__SupportDynamicType` is a subtype of `dynamic` if any of its parameters not marked `__NoRequireDynamic` are instantiated with `dynamic & t1 & ... & tn` where `t1` to `tn` are the upper bounds on the generic parameter.

(2) Checking of members. When checking the *signature* of a member to see it if satisfies constraints for dynamic calling, any parameters not marked `__NoRequireDynamic` are assumed to be equal to `dynamic`. When checking the *body* of a member under dynamic assumptions (if the signature check fails), any generic parameters not marked `__NoRequireDynamic` are assumed to be equal to `dynamic`.

(3) Checking of inheritance. We currently require any class that extends or implements a type that is declared `__SupportDynamicType` to itself also declare `__SupportDynamicType`. In the presence of conditional-dynamic generics, a further check is required. Suppose an class that has an invariant type parameter supports dynamic e.g. `Vector<T>`. Consider what happens when it extends (or implements) a covariant class that also supports dynamic, e.g. `Container<+T>`. We want `Vector<t> <: dynamic` only if `t` is *exactly* `dynamic`. Unfortunately we have `Container<int> <: dynamic` (by covariance), and so `Vector<int> <: dynamic` (by inheritance and transitivity). To prevent this, we require that for *any* instantiation of generic parameters, the parent supports dynamic *implies* the child also supports dynamic.

(4) We add `__SupportDynamicType` to many standard types in `hhi` files, including Hack arrays (`vec`, `dict`) and collection classes. Special casing in subtyping for collections is no longer required.

A subsequent diff will deal with coercion from `dynamic` to instantiations of types whose generic parameters are conditional-dynamic. This will simplify the special casing of collections during coercion checking.

Differential Revision: D27823442

fbshipit-source-id: 7bdb85251ede544110ba7251414ad4b4b12a693a
89 files changed:
hphp/hack/hhi/classes.hhi
hphp/hack/hhi/collections/ImmMap.hhi
hphp/hack/hhi/collections/ImmSet.hhi
hphp/hack/hhi/collections/ImmVector.hhi
hphp/hack/hhi/collections/Map.hhi
hphp/hack/hhi/collections/Pair.hhi
hphp/hack/hhi/collections/Set.hhi
hphp/hack/hhi/collections/Vector.hhi
hphp/hack/hhi/collections/interfaces.hhi
hphp/hack/hhi/hackarray.hhi
hphp/hack/hhi/rx/interfaces.hhi
hphp/hack/src/errors/error_codes.ml
hphp/hack/src/errors/errors.ml
hphp/hack/src/errors/errors.mli
hphp/hack/src/naming/naming_special_names.ml
hphp/hack/src/oxidized/gen/error_codes.rs
hphp/hack/src/oxidized_by_ref/gen/typing_kinding_defs.rs
hphp/hack/src/typing/type_parameter_env.ml
hphp/hack/src/typing/type_parameter_env.mli
hphp/hack/src/typing/typing_dynamic.ml
hphp/hack/src/typing/typing_dynamic.mli
hphp/hack/src/typing/typing_env.ml
hphp/hack/src/typing/typing_env.mli
hphp/hack/src/typing/typing_kinding_defs.ml
hphp/hack/src/typing/typing_kinding_defs.mli
hphp/hack/src/typing/typing_log.ml
hphp/hack/src/typing/typing_subtype.ml
hphp/hack/src/typing/typing_toplevel.ml
hphp/hack/test/sound_dynamic/typing/as.good.php
hphp/hack/test/sound_dynamic/typing/box_generics.bad.php
hphp/hack/test/sound_dynamic/typing/box_generics.bad.php.exp
hphp/hack/test/sound_dynamic/typing/coerce_to_dyn.good.php
hphp/hack/test/sound_dynamic/typing/generic_params.bad.php [new file with mode: 0644]
hphp/hack/test/sound_dynamic/typing/generic_params.bad.php.exp [new file with mode: 0644]
hphp/hack/test/sound_dynamic/typing/generic_params.good.php [new file with mode: 0644]
hphp/hack/test/sound_dynamic/typing/generic_params.good.php.exp [new file with mode: 0644]
hphp/hack/test/sound_dynamic/typing/generic_params_2.bad.php [new file with mode: 0644]
hphp/hack/test/sound_dynamic/typing/generic_params_2.bad.php.exp [new file with mode: 0644]
hphp/hack/test/sound_dynamic/typing/reify.bad.php
hphp/hack/test/sound_dynamic/typing/reify.good.php
hphp/hack/test/typecheck/aktuple/foreach_error.php.exp
hphp/hack/test/typecheck/aktuple/subtyping_interfaces.php.exp
hphp/hack/test/typecheck/argument_unpacking/unpack_call12_infer.php.exp
hphp/hack/test/typecheck/array/allow_returning_darray_of_subtype.php.exp
hphp/hack/test/typecheck/array/safe_vector_array/disallow_darray_with_non_arraykey_keys.php.exp
hphp/hack/test/typecheck/array/safe_vector_array/disallow_darray_with_wrong_key_type_where_varray_or_darray_is_required_in_non_strict_mode.php.exp
hphp/hack/test/typecheck/array/safe_vector_array/disallows_array_of_int_when_array_of_tk_to_tv_is_required.php.exp
hphp/hack/test/typecheck/array_get_generic_lvalue_array_get.php.exp
hphp/hack/test/typecheck/array_get_newtype.php.exp
hphp/hack/test/typecheck/array_get_tconst.php.exp
hphp/hack/test/typecheck/array_map_filter/array_filter2.php.exp
hphp/hack/test/typecheck/array_sub.php.exp
hphp/hack/test/typecheck/array_sub2.php.exp
hphp/hack/test/typecheck/as_expression/dict.php.exp
hphp/hack/test/typecheck/as_expression/dict2.php.exp
hphp/hack/test/typecheck/as_expression/keyset.php.exp
hphp/hack/test/typecheck/as_expression/keyset2.php.exp
hphp/hack/test/typecheck/as_expression/vec.php.exp
hphp/hack/test/typecheck/as_expression/vec2.php.exp
hphp/hack/test/typecheck/assignment1.php.exp
hphp/hack/test/typecheck/coeffects/varray_darray_const_ctx_bad.php.exp
hphp/hack/test/typecheck/collection_mixed.php.exp
hphp/hack/test/typecheck/const_type_violated_constraint.php.exp
hphp/hack/test/typecheck/ffp/function_async_bad_arity.php.exp
hphp/hack/test/typecheck/ffp/function_async_partial.php.exp
hphp/hack/test/typecheck/invalid_arraykey_constraint/invalid_arraykey_constraint.php.exp
hphp/hack/test/typecheck/is_expression/dict.php.exp
hphp/hack/test/typecheck/is_expression/keyset.php.exp
hphp/hack/test/typecheck/is_expression/vec.php.exp
hphp/hack/test/typecheck/lambda/lambda_bad_pos.php.exp
hphp/hack/test/typecheck/lambda/variadics/variadic_not_indexed.php.exp
hphp/hack/test/typecheck/memoize/array_of_bad_class.php.exp
hphp/hack/test/typecheck/memoize/array_of_bad_class2.php.exp
hphp/hack/test/typecheck/memoize/opaque_alias_container_of_bad_class.php.exp
hphp/hack/test/typecheck/memoize/opaque_alias_container_of_bad_class2.php.exp
hphp/hack/test/typecheck/must_be_arraykey.php.exp
hphp/hack/test/typecheck/new_inference/eager_solve/shapes_toarray.php.exp
hphp/hack/test/typecheck/new_inference/index_array_bad.php.exp
hphp/hack/test/typecheck/nonnull/array_is_subtype_of_nonnull.php.exp
hphp/hack/test/typecheck/reified_generics/funciton_call_missing_reified_generics1.php.exp
hphp/hack/test/typecheck/reified_generics/funciton_call_missing_reified_generics2.php.exp
hphp/hack/test/typecheck/subtype_array3.php.exp
hphp/hack/test/typecheck/subtype_array4.php.exp
hphp/hack/test/typecheck/subtype_collection_interfaces_1.php.exp
hphp/hack/test/typecheck/subtype_collection_interfaces_2.php.exp
hphp/hack/test/typecheck/test_array4.php.exp
hphp/hack/test/typecheck/variadic_args1.php.exp
hphp/hack/test/typecheck/variadic_args1.php.like_types.exp
hphp/hack/test/typecheck/variadic_args8.php.exp