def class_type_is_not_subtype_of_structural_type_if_it_is_missing_attrs(self): cls = types.class_type("Person") structural_type = types.structural_type("HasName", [ types.attr("name", types.str_type), ]) assert not types.is_sub_type(structural_type, cls) assert not types.is_sub_type(cls, structural_type)
def class_type_is_not_subtype_of_structural_type_if_attr_is_strict_supertype_of_attr_on_structural_type(self): cls = types.class_type("Person", [ types.attr("name", types.object_type), ]) structural_type = types.structural_type("HasName", [ types.attr("name", types.str_type), ]) assert not types.is_sub_type(structural_type, cls) assert not types.is_sub_type(cls, structural_type)
def class_type_is_subtype_of_structural_type_if_it_has_subset_of_attrs(self): # TODO: how to handle sub-typing of mutable attrs cls = types.class_type("Person", [ types.attr("name", types.str_type), types.attr("number_of_hats", types.int_type), ]) structural_type = types.structural_type("HasName", [ types.attr("name", types.str_type), ]) assert types.is_sub_type(structural_type, cls) assert not types.is_sub_type(cls, structural_type)
def recursive_instantiated_generic_structural_type_is_sub_type_of_same_instantiated_generic_structural_type_if_it_has_matching_attributes(self): recursive = types.generic_structural_type("recursive", [types.covariant("T")], lambda T: [ types.attr("__iter__", types.func([], recursive(T))), ]) assert types.is_sub_type( recursive(types.int_type), recursive(types.int_type), )
def rescursive_structural_types_do_not_cause_stack_overflow(self): recursive1 = types.structural_type("recursive1") recursive1.attrs.add("uh_oh", types.func([], recursive1)) recursive2 = types.structural_type("recursive2") recursive2.attrs.add("uh_oh", types.func([], recursive2)) assert not types.is_sub_type( recursive1, recursive2, )
def invariant_type_parameter_can_be_unified_when_part_of_recursive_structural_type(self): invariant_type_param = types.invariant("T") recursive = types.generic_structural_type("recursive", [types.covariant("T")], lambda T: [ types.attr("__iter__", types.func([], recursive(T))), ]) assert types.is_sub_type( recursive(invariant_type_param), recursive(types.int_type), unify=[invariant_type_param] )
def invariant_type_parameter_cannot_have_different_values_in_same_is_sub_type_relation(self): invariant_type_param = types.invariant("T") first_class_type = types.class_type("User") second_class_type = types.class_type("Role") generic_class = types.generic_class("Pair", [invariant_type_param, invariant_type_param]) assert not types.is_sub_type( # TODO: need a reliable way of getting the underlying type (but as an instantiated type) generic_class(invariant_type_param, invariant_type_param), generic_class(first_class_type, second_class_type), unify=[invariant_type_param] )
def contravariant_type_parameter_is_substituted_with_common_sub_type_of_actual_type_params(self): contravariant_type_param = types.contravariant("T") first_class_type = types.class_type("User") second_class_type = types.class_type("Role") generic_class = types.generic_class("Pair", [contravariant_type_param, contravariant_type_param]) type_map = types.is_sub_type( # TODO: need a reliable way of getting the underlying type (but as an instantiated type) generic_class(contravariant_type_param, contravariant_type_param), generic_class(first_class_type, second_class_type), unify=[contravariant_type_param] ) assert_equal(types.bottom_type, type_map[contravariant_type_param])
def instantiated_generic_structural_type_is_sub_type_of_other_instantiated_generic_structural_type_if_it_has_matching_attributes(self): iterator = types.generic_structural_type("iterator", [types.covariant("T")], lambda T: [ types.attr("__iter__", types.func([], iterator(T))), types.attr("__next__", types.func([], T)), ]) iterable = types.generic_structural_type("iterable", [types.covariant("T")], lambda T: [ types.attr("__iter__", types.func([], iterator(T))), ]) assert types.is_sub_type( iterable(types.int_type), iterator(types.int_type), )
def functions_are_covariant_in_return_type(self): super_type = types.func([int_type], types.object_type) sub_type = types.func([int_type], str_type) assert types.is_sub_type(super_type, sub_type) assert not types.is_sub_type(sub_type, super_type)
def functions_are_contravariant_in_their_arguments(self): super_type = types.func([int_type], str_type) sub_type = types.func([types.object_type], str_type) assert types.is_sub_type(super_type, sub_type) assert not types.is_sub_type(sub_type, super_type)
def func_type_is_sub_type_if_argument_of_super_type_has_no_name_and_sub_type_has_name(self): first_func_type = types.func([types.func_arg(None, int_type)], str_type) second_func_type = types.func([types.func_arg("y", int_type)], str_type) assert types.is_sub_type(first_func_type, second_func_type) assert not types.is_sub_type(second_func_type, first_func_type)
def func_type_is_not_sub_type_if_argument_has_different_name(self): first_func_type = types.func([types.func_arg("x", int_type)], str_type) second_func_type = types.func([types.func_arg("y", int_type)], str_type) assert not types.is_sub_type(first_func_type, second_func_type) assert not types.is_sub_type(second_func_type, first_func_type)
def instantiated_class_is_sub_type_of_other_instantiated_class_if_formal_param_is_contravariant_and_type_params_are_supertypes(self): generic_class = types.generic_class("iterator", [types.contravariant("T")]) assert not types.is_sub_type(generic_class(types.object_type), generic_class(types.int_type)) assert types.is_sub_type(generic_class(types.int_type), generic_class(types.object_type))
def class_type_is_subtype_of_base_class_of_base_class(self): super_super_type = types.class_type("GrandParent") super_type = types.class_type("Parent", base_classes=[super_super_type]) cls = types.class_type("Blah", base_classes=[super_type]) assert types.is_sub_type(super_super_type, cls) assert not types.is_sub_type(cls, super_super_type)
def class_type_is_subtype_of_itself(self): cls = types.class_type("Blah") assert types.is_sub_type(cls, cls)
def union_type_is_subtype_of_other_union_type_if_its_types_are_a_subset(self): smaller_union_type = types.union(types.int_type, types.none_type) larger_union_type = types.union(types.int_type, types.none_type, types.str_type) assert types.is_sub_type(larger_union_type, smaller_union_type) assert not types.is_sub_type(smaller_union_type, larger_union_type)
def instantiated_union_type_is_subtype_of_other_instantiated_union_type_if_its_types_are_a_subset(self): smaller_union_type = types.unnamed_generic(["T"], lambda T: types.union(T, types.none_type)) larger_union_type = types.unnamed_generic(["T"], lambda T: types.union(T, types.none_type, types.str_type)) assert types.is_sub_type(larger_union_type(types.int_type), smaller_union_type(types.int_type)) assert not types.is_sub_type(smaller_union_type(types.int_type), larger_union_type(types.int_type))
def unification_occurs_before_reification(self): instantiated_type = types.generic_class("blah", ["T"])(int_type) type_map = types.is_sub_type(_formal_param, instantiated_type, unify=[_formal_param]) assert_equal(instantiated_type, type_map[_formal_param])
def formal_type_parameter_is_mapped_to_itself_if_unified_with_itself(self): type_map = types.is_sub_type(_formal_param, _formal_param, unify=[_formal_param]) assert_equal(_formal_param, type_map[_formal_param])
def type_map_is_returned_by_super_type_unification(self): type_map = types.is_sub_type(_class_type, _formal_param, unify=[_formal_param]) assert_equal(_class_type, type_map[_formal_param])
def formal_type_can_be_sub_type_of_formal_param_if_formal_param_can_be_unified(self): assert types.is_sub_type(_class_type, _formal_param, unify=[_formal_param])
def class_type_is_not_super_type_of_formal_type_parameter(self): assert not types.is_sub_type(_class_type, _formal_param)
def any_meta_type_is_super_type_of_meta_types(self): assert types.is_sub_type(types.any_meta_type, types.meta_type(types.int_type)) assert not types.is_sub_type(types.meta_type(types.int_type), types.any_meta_type)
def any_meta_type_is_not_super_type_of_non_meta_types(self): assert not types.is_sub_type(types.any_meta_type, types.int_type)
def type_is_subtype_of_union_type_if_it_appears_in_union_type(self): class_type = types.class_type("User") union_type = types.union(class_type, types.none_type) assert types.is_sub_type(union_type, class_type) assert not types.is_sub_type(class_type, union_type)
def class_type_is_subtype_of_object_type(self): cls = types.class_type("Blah") assert types.is_sub_type(types.object_type, cls)
def func_type_is_sub_type_of_itself(self): func_type = lambda: types.func([int_type], str_type) assert types.is_sub_type(func_type(), func_type())
def func_type_is_not_sub_type_if_it_has_different_number_of_arguments(self): short_func_type = types.func([], str_type) long_func_type = types.func([int_type], str_type) assert not types.is_sub_type(short_func_type, long_func_type) assert not types.is_sub_type(long_func_type, short_func_type)
def union_of_union_is_treated_the_same_as_flat_union(self): nested_union_type = types.union(types.int_type, types.union(types.none_type, types.str_type)) flat_union_type = types.union(types.int_type, types.none_type, types.str_type) assert types.is_sub_type(nested_union_type, flat_union_type) assert types.is_sub_type(flat_union_type, nested_union_type)