diff --git a/mypy/build.py b/mypy/build.py index e3b51e5dd0fc..f69488a943b9 100644 --- a/mypy/build.py +++ b/mypy/build.py @@ -31,7 +31,7 @@ from mypy.nodes import MypyFile, ImportBase, Import, ImportFrom, ImportAll, SymbolTable from mypy.semanal_pass1 import SemanticAnalyzerPreAnalysis from mypy.semanal import SemanticAnalyzer -from mypy.semanal_main import semantic_analysis_for_scc +import mypy.semanal_main from mypy.checker import TypeChecker from mypy.indirection import TypeIndirectionVisitor from mypy.errors import Errors, CompileError, ErrorInfo, report_internal_error @@ -2961,7 +2961,7 @@ def process_stale_scc(graph: Graph, scc: List[str], manager: BuildManager) -> No # SemanticAnalyzerPass2.add_builtin_aliases for details. typing_mod = graph['typing'].tree assert typing_mod, "The typing module was not parsed" - semantic_analysis_for_scc(graph, scc, manager.errors) + mypy.semanal_main.semantic_analysis_for_scc(graph, scc, manager.errors) # Track what modules aren't yet done so we can finish them as soon # as possible, saving memory. diff --git a/mypy/checker.py b/mypy/checker.py index 561e5fcd2439..e3f7e066e5c2 100644 --- a/mypy/checker.py +++ b/mypy/checker.py @@ -34,7 +34,7 @@ Type, AnyType, CallableType, FunctionLike, Overloaded, TupleType, TypedDictType, Instance, NoneType, strip_type, TypeType, TypeOfAny, UnionType, TypeVarId, TypeVarType, PartialType, DeletedType, UninhabitedType, TypeVarDef, - true_only, false_only, function_type, is_named_instance, union_items, TypeQuery, LiteralType, + function_type, is_named_instance, union_items, TypeQuery, LiteralType, is_optional, remove_optional, TypeTranslator, StarType, get_proper_type, ProperType, get_proper_types, is_literal_type ) @@ -45,8 +45,12 @@ ) import mypy.checkexpr from mypy.checkmember import ( - map_type_from_supertype, bind_self, erase_to_bound, type_object_type, - analyze_descriptor_access, + analyze_descriptor_access, type_object_type, +) +from mypy.typeops import ( + map_type_from_supertype, bind_self, erase_to_bound, make_simplified_union, + erase_def_to_union_or_bound, erase_to_union_or_bound, + true_only, false_only, ) from mypy import message_registry from mypy.subtypes import ( @@ -1539,7 +1543,7 @@ def get_op_other_domain(self, tp: FunctionLike) -> Optional[Type]: raw_items = [self.get_op_other_domain(it) for it in tp.items()] items = [it for it in raw_items if it] if items: - return UnionType.make_simplified_union(items) + return make_simplified_union(items) return None else: assert False, "Need to check all FunctionLike subtypes here" @@ -1973,7 +1977,7 @@ def check_assignment(self, lvalue: Lvalue, rvalue: Expression, infer_lvalue_type if not self.current_node_deferred: # Partial type can't be final, so strip any literal values. rvalue_type = remove_instance_last_known_values(rvalue_type) - inferred_type = UnionType.make_simplified_union( + inferred_type = make_simplified_union( [rvalue_type, NoneType()]) self.set_inferred_type(var, lvalue, inferred_type) else: @@ -2417,7 +2421,7 @@ def check_multi_assignment_from_union(self, lvalues: List[Expression], rvalue: E rv_type=item, undefined_rvalue=True) for t, lv in zip(transposed, self.flatten_lvalues(lvalues)): t.append(self.type_map.pop(lv, AnyType(TypeOfAny.special_form))) - union_types = tuple(UnionType.make_simplified_union(col) for col in transposed) + union_types = tuple(make_simplified_union(col) for col in transposed) for expr, items in assignments.items(): # Bind a union of types collected in 'assignments' to every expression. if isinstance(expr, StarExpr): @@ -2432,8 +2436,8 @@ def check_multi_assignment_from_union(self, lvalues: List[Expression], rvalue: E types, declared_types = zip(*clean_items) self.binder.assign_type(expr, - UnionType.make_simplified_union(list(types)), - UnionType.make_simplified_union(list(declared_types)), + make_simplified_union(list(types)), + make_simplified_union(list(declared_types)), False) for union, lv in zip(union_types, self.flatten_lvalues(lvalues)): # Properly store the inferred types. @@ -2856,9 +2860,9 @@ def try_infer_partial_type_from_indexed_assignment( # TODO: Don't infer things twice. key_type = self.expr_checker.accept(lvalue.index) value_type = self.expr_checker.accept(rvalue) - full_key_type = UnionType.make_simplified_union( + full_key_type = make_simplified_union( [key_type, var.type.inner_types[0]]) - full_value_type = UnionType.make_simplified_union( + full_value_type = make_simplified_union( [value_type, var.type.inner_types[1]]) if (is_valid_inferred_type(full_key_type) and is_valid_inferred_type(full_value_type)): @@ -3176,7 +3180,7 @@ def check_except_handler_test(self, n: Expression) -> Type: all_types.append(exc_type) - return UnionType.make_simplified_union(all_types) + return make_simplified_union(all_types) def get_types_from_except_handler(self, typ: Type, n: Expression) -> List[Type]: """Helper for check_except_handler_test to retrieve handler types.""" @@ -3524,7 +3528,7 @@ def partition_by_callable(self, typ: Type, # do better. # If it is possible for the false branch to execute, return the original # type to avoid losing type information. - callables, uncallables = self.partition_by_callable(typ.erase_to_union_or_bound(), + callables, uncallables = self.partition_by_callable(erase_to_union_or_bound(typ), unsound_partition) uncallables = [typ] if len(uncallables) else [] return callables, uncallables @@ -4087,7 +4091,7 @@ def conditional_type_map(expr: Expression, if it was not the proposed type, if any. None means bot, {} means top""" if proposed_type_ranges: proposed_items = [type_range.item for type_range in proposed_type_ranges] - proposed_type = UnionType.make_simplified_union(proposed_items) + proposed_type = make_simplified_union(proposed_items) if current_type: if isinstance(proposed_type, AnyType): # We don't really know much about the proposed type, so we shouldn't @@ -4170,7 +4174,7 @@ def builtin_item_type(tp: Type) -> Optional[Type]: return tp.args[0] elif isinstance(tp, TupleType) and all(not isinstance(it, AnyType) for it in get_proper_types(tp.items)): - return UnionType.make_simplified_union(tp.items) # this type is not externally visible + return make_simplified_union(tp.items) # this type is not externally visible elif isinstance(tp, TypedDictType): # TypedDict always has non-optional string keys. Find the key type from the Mapping # base class. @@ -4221,7 +4225,7 @@ def or_conditional_maps(m1: TypeMap, m2: TypeMap) -> TypeMap: for n1 in m1: for n2 in m2: if literal_hash(n1) == literal_hash(n2): - result[n1] = UnionType.make_simplified_union([m1[n1], m2[n2]]) + result[n1] = make_simplified_union([m1[n1], m2[n2]]) return result @@ -4435,7 +4439,7 @@ def overload_can_never_match(signature: CallableType, other: CallableType) -> bo # the below subtype check and (surprisingly?) `is_proper_subtype(Any, Any)` # returns `True`. # TODO: find a cleaner solution instead of this ad-hoc erasure. - exp_signature = expand_type(signature, {tvar.id: tvar.erase_to_union_or_bound() + exp_signature = expand_type(signature, {tvar.id: erase_def_to_union_or_bound(tvar) for tvar in signature.variables}) assert isinstance(exp_signature, CallableType) return is_callable_compatible(exp_signature, other, @@ -4689,7 +4693,7 @@ class Status(Enum): if isinstance(typ, UnionType): items = [try_expanding_enum_to_union(item, target_fullname) for item in typ.items] - return UnionType.make_simplified_union(items) + return make_simplified_union(items) elif isinstance(typ, Instance) and typ.type.is_enum and typ.type.fullname() == target_fullname: new_items = [] for name, symbol in typ.type.names.items(): @@ -4704,7 +4708,7 @@ class Status(Enum): # only using CPython, but we might as well for the sake of full correctness. if sys.version_info < (3, 7): new_items.sort(key=lambda lit: lit.value) - return UnionType.make_simplified_union(new_items) + return make_simplified_union(new_items) else: return typ @@ -4716,7 +4720,7 @@ def coerce_to_literal(typ: Type) -> ProperType: typ = get_proper_type(typ) if isinstance(typ, UnionType): new_items = [coerce_to_literal(item) for item in typ.items] - return UnionType.make_simplified_union(new_items) + return make_simplified_union(new_items) elif isinstance(typ, Instance) and typ.last_known_value: return typ.last_known_value else: diff --git a/mypy/checkexpr.py b/mypy/checkexpr.py index 135f796d479e..b49c73bed846 100644 --- a/mypy/checkexpr.py +++ b/mypy/checkexpr.py @@ -17,7 +17,7 @@ Type, AnyType, CallableType, Overloaded, NoneType, TypeVarDef, TupleType, TypedDictType, Instance, TypeVarType, ErasedType, UnionType, PartialType, DeletedType, UninhabitedType, TypeType, TypeOfAny, LiteralType, LiteralValue, - true_only, false_only, is_named_instance, function_type, callable_type, FunctionLike, + is_named_instance, function_type, callable_type, FunctionLike, StarType, is_optional, remove_optional, is_generic_instance, get_proper_type, ProperType, get_proper_types ) @@ -59,7 +59,9 @@ from mypy.typevars import fill_typevars from mypy.visitor import ExpressionVisitor from mypy.plugin import Plugin, MethodContext, MethodSigContext, FunctionContext -from mypy.typeops import tuple_fallback +from mypy.typeops import ( + tuple_fallback, make_simplified_union, true_only, false_only, erase_to_union_or_bound, +) import mypy.errorcodes as codes # Type of callback user for checking individual function arguments. See @@ -528,7 +530,7 @@ def try_infer_partial_type(self, e: CallExpr) -> None: if (typename in self.item_args and methodname in self.item_args[typename] and e.arg_kinds == [ARG_POS]): item_type = self.accept(e.args[0]) - full_item_type = UnionType.make_simplified_union( + full_item_type = make_simplified_union( [item_type, partial_type.inner_types[0]]) if mypy.checker.is_valid_inferred_type(full_item_type): var.type = self.chk.named_generic_type(typename, [full_item_type]) @@ -541,7 +543,7 @@ def try_infer_partial_type(self, e: CallExpr) -> None: arg_typename = arg_type.type.fullname() if arg_typename in self.container_args[typename][methodname]: full_item_types = [ - UnionType.make_simplified_union([item_type, prev_type]) + make_simplified_union([item_type, prev_type]) for item_type, prev_type in zip(arg_type.args, partial_type.inner_types) ] @@ -713,7 +715,7 @@ def check_union_call_expr(self, e: CallExpr, object_type: UnionType, member: str item_object_type = typ if callable_name else None res.append(self.check_call_expr_with_callee_type(narrowed, e, callable_name, item_object_type)) - return UnionType.make_simplified_union(res) + return make_simplified_union(res) def check_call(self, callee: Type, @@ -1377,9 +1379,9 @@ def check_overload_call(self, # a union of inferred callables because for example a call # Union[int -> int, str -> str](Union[int, str]) is invalid and # we don't want to introduce internal inconsistencies. - unioned_result = (UnionType.make_simplified_union(list(returns), - context.line, - context.column), + unioned_result = (make_simplified_union(list(returns), + context.line, + context.column), self.combine_function_signatures(inferred_types)) # Step 3: We try checking each branch one-by-one. @@ -1736,7 +1738,7 @@ def combine_function_signatures(self, types: Sequence[Type]) -> Union[AnyType, C new_args[i].append(arg) new_returns.append(target.ret_type) - union_return = UnionType.make_simplified_union(new_returns) + union_return = make_simplified_union(new_returns) if too_complex: any = AnyType(TypeOfAny.special_form) return callables[0].copy_modified( @@ -1749,7 +1751,7 @@ def combine_function_signatures(self, types: Sequence[Type]) -> Union[AnyType, C final_args = [] for args_list in new_args: - new_type = UnionType.make_simplified_union(args_list) + new_type = make_simplified_union(args_list) final_args.append(new_type) return callables[0].copy_modified( @@ -1818,7 +1820,7 @@ def check_union_call(self, arg_messages=arg_messages) for subtype in callee.relevant_items()] self.msg.disable_type_names -= 1 - return (UnionType.make_simplified_union([res[0] for res in results]), + return (make_simplified_union([res[0] for res in results]), callee) def visit_member_expr(self, e: MemberExpr, is_lvalue: bool = False) -> Type: @@ -2168,7 +2170,7 @@ def check_union_method_call_by_name(self, local_errors.disable_type_names -= 1 res.append(item) meth_res.append(meth_item) - return UnionType.make_simplified_union(res), UnionType.make_simplified_union(meth_res) + return make_simplified_union(res), make_simplified_union(meth_res) def check_method_call(self, method_name: str, @@ -2425,8 +2427,8 @@ def check_op(self, method: str, base_type: Type, all_inferred.append(inferred) if not msg.is_errors(): - results_final = UnionType.make_simplified_union(all_results) - inferred_final = UnionType.make_simplified_union(all_inferred) + results_final = make_simplified_union(all_results) + inferred_final = make_simplified_union(all_inferred) return results_final, inferred_final # Step 2: If that fails, we try again but also destructure the right argument. @@ -2475,7 +2477,7 @@ def check_op(self, method: str, base_type: Type, # See the comment in 'check_overload_call' for more details on why # we call 'combine_function_signature' instead of just unioning the inferred # callable types. - results_final = UnionType.make_simplified_union(all_results) + results_final = make_simplified_union(all_results) inferred_final = self.combine_function_signatures(all_inferred) return results_final, inferred_final else: @@ -2560,7 +2562,7 @@ def check_boolean_op(self, e: OpExpr, context: Context) -> Type: # The left operand is always the result return left_type else: - return UnionType.make_simplified_union([restricted_left_type, right_type]) + return make_simplified_union([restricted_left_type, right_type]) def check_list_multiply(self, e: OpExpr) -> Type: """Type check an expression of form '[...] * e'. @@ -2626,9 +2628,9 @@ def visit_index_with_type(self, left_type: Type, e: IndexExpr, if isinstance(left_type, UnionType): original_type = original_type or left_type - return UnionType.make_simplified_union([self.visit_index_with_type(typ, e, - original_type) - for typ in left_type.relevant_items()]) + return make_simplified_union([self.visit_index_with_type(typ, e, + original_type) + for typ in left_type.relevant_items()]) elif isinstance(left_type, TupleType) and self.chk.in_checked_function(): # Special case for tuples. They return a more specific type when # indexed by an integer literal. @@ -2646,7 +2648,7 @@ def visit_index_with_type(self, left_type: Type, e: IndexExpr, else: self.chk.fail(message_registry.TUPLE_INDEX_OUT_OF_RANGE, e) return AnyType(TypeOfAny.from_error) - return UnionType.make_simplified_union(out) + return make_simplified_union(out) else: return self.nonliteral_tuple_index_helper(left_type, index) elif isinstance(left_type, TypedDictType): @@ -2687,7 +2689,7 @@ def visit_tuple_slice_helper(self, left_type: TupleType, slic: SliceExpr) -> Typ items = [] # type: List[Type] for b, e, s in itertools.product(begin, end, stride): items.append(left_type.slice(b, e, s)) - return UnionType.make_simplified_union(items) + return make_simplified_union(items) def try_getting_int_literals(self, index: Expression) -> Optional[List[int]]: """If the given expression or type corresponds to an int literal @@ -2732,7 +2734,7 @@ def nonliteral_tuple_index_helper(self, left_type: TupleType, index: Expression) 'actual type', 'expected type'): return AnyType(TypeOfAny.from_error) else: - union = UnionType.make_simplified_union(left_type.items) + union = make_simplified_union(left_type.items) if isinstance(index, SliceExpr): return self.chk.named_generic_type('builtins.tuple', [union]) else: @@ -2767,7 +2769,7 @@ def visit_typeddict_index_expr(self, td_type: TypedDictType, index: Expression) return AnyType(TypeOfAny.from_error) else: value_types.append(value_type) - return UnionType.make_simplified_union(value_types) + return make_simplified_union(value_types) def visit_enum_index_expr(self, enum_type: TypeInfo, index: Expression, context: Context) -> Type: @@ -3471,7 +3473,7 @@ def visit_conditional_expr(self, e: ConditionalExpr) -> Type: # # TODO: Always create a union or at least in more cases? if isinstance(get_proper_type(self.type_context[-1]), UnionType): - res = UnionType.make_simplified_union([if_type, else_type]) + res = make_simplified_union([if_type, else_type]) else: res = join.join_types(if_type, else_type) @@ -3919,9 +3921,9 @@ def arg_approximate_similarity(actual: Type, formal: Type) -> bool: # Erase typevars: we'll consider them all to have the same "shape". if isinstance(actual, TypeVarType): - actual = actual.erase_to_union_or_bound() + actual = erase_to_union_or_bound(actual) if isinstance(formal, TypeVarType): - formal = formal.erase_to_union_or_bound() + formal = erase_to_union_or_bound(formal) # Callable or Type[...]-ish types def is_typetype_like(typ: ProperType) -> bool: diff --git a/mypy/checkmember.py b/mypy/checkmember.py index e0b22d12827d..f42eaa0d87db 100644 --- a/mypy/checkmember.py +++ b/mypy/checkmember.py @@ -1,11 +1,11 @@ """Type checking of attribute access""" -from typing import cast, Callable, List, Optional, TypeVar, Union +from typing import cast, Callable, Optional, Union from typing_extensions import TYPE_CHECKING from mypy.types import ( Type, Instance, AnyType, TupleType, TypedDictType, CallableType, FunctionLike, TypeVarDef, - Overloaded, TypeVarType, UnionType, PartialType, UninhabitedType, TypeOfAny, LiteralType, + Overloaded, TypeVarType, UnionType, PartialType, TypeOfAny, LiteralType, DeletedType, NoneType, TypeType, function_type, get_type_vars, get_proper_type, ProperType ) from mypy.nodes import ( @@ -15,16 +15,17 @@ ) from mypy.messages import MessageBuilder from mypy.maptype import map_instance_to_supertype -from mypy.expandtype import expand_type_by_instance, expand_type, freshen_function_type_vars +from mypy.expandtype import expand_type_by_instance, freshen_function_type_vars from mypy.erasetype import erase_typevars -from mypy.infer import infer_type_arguments -from mypy.typevars import fill_typevars from mypy.plugin import AttributeContext from mypy.typeanal import set_any_tvars from mypy import message_registry from mypy import subtypes from mypy import meet -from mypy.typeops import tuple_fallback +from mypy.typeops import ( + tuple_fallback, bind_self, erase_to_bound, class_callable, type_object_type_from_function, + make_simplified_union, +) if TYPE_CHECKING: # import for forward declaration only import mypy.checker @@ -290,7 +291,7 @@ def analyze_union_member_access(name: str, typ: UnionType, mx: MemberContext) -> item_mx = mx.copy_modified(self_type=subtype) results.append(_analyze_member_access(name, subtype, item_mx)) mx.msg.disable_type_names -= 1 - return UnionType.make_simplified_union(results) + return make_simplified_union(results) def analyze_none_member_access(name: str, typ: NoneType, mx: MemberContext) -> Type: @@ -433,7 +434,7 @@ def analyze_descriptor_access(instance_type: Type, if isinstance(descriptor_type, UnionType): # Map the access over union types - return UnionType.make_simplified_union([ + return make_simplified_union([ analyze_descriptor_access(instance_type, typ, builtin_type, msg, context, chk=chk) for typ in descriptor_type.items @@ -869,181 +870,3 @@ def is_valid_constructor(n: Optional[SymbolNode]) -> bool: if isinstance(n, Decorator): return isinstance(get_proper_type(n.type), FunctionLike) return False - - -def type_object_type_from_function(signature: FunctionLike, - info: TypeInfo, - def_info: TypeInfo, - fallback: Instance, - is_new: bool) -> FunctionLike: - # The __init__ method might come from a generic superclass - # (init_or_new.info) with type variables that do not map - # identically to the type variables of the class being constructed - # (info). For example - # - # class A(Generic[T]): def __init__(self, x: T) -> None: pass - # class B(A[List[T]], Generic[T]): pass - # - # We need to first map B's __init__ to the type (List[T]) -> None. - signature = bind_self(signature, original_type=fill_typevars(info), is_classmethod=is_new) - signature = cast(FunctionLike, - map_type_from_supertype(signature, info, def_info)) - special_sig = None # type: Optional[str] - if def_info.fullname() == 'builtins.dict': - # Special signature! - special_sig = 'dict' - - if isinstance(signature, CallableType): - return class_callable(signature, info, fallback, special_sig, is_new) - else: - # Overloaded __init__/__new__. - assert isinstance(signature, Overloaded) - items = [] # type: List[CallableType] - for item in signature.items(): - items.append(class_callable(item, info, fallback, special_sig, is_new)) - return Overloaded(items) - - -def class_callable(init_type: CallableType, info: TypeInfo, type_type: Instance, - special_sig: Optional[str], - is_new: bool) -> CallableType: - """Create a type object type based on the signature of __init__.""" - variables = [] # type: List[TypeVarDef] - variables.extend(info.defn.type_vars) - variables.extend(init_type.variables) - - init_ret_type = get_proper_type(init_type.ret_type) - if is_new and isinstance(init_ret_type, (Instance, TupleType)): - ret_type = init_type.ret_type # type: Type - else: - ret_type = fill_typevars(info) - - callable_type = init_type.copy_modified( - ret_type=ret_type, fallback=type_type, name=None, variables=variables, - special_sig=special_sig) - c = callable_type.with_name(info.name()) - return c - - -def map_type_from_supertype(typ: Type, - sub_info: TypeInfo, - super_info: TypeInfo) -> Type: - """Map type variables in a type defined in a supertype context to be valid - in the subtype context. Assume that the result is unique; if more than - one type is possible, return one of the alternatives. - - For example, assume - - . class D(Generic[S]) ... - . class C(D[E[T]], Generic[T]) ... - - Now S in the context of D would be mapped to E[T] in the context of C. - """ - # Create the type of self in subtype, of form t[a1, ...]. - inst_type = fill_typevars(sub_info) - if isinstance(inst_type, TupleType): - inst_type = tuple_fallback(inst_type) - # Map the type of self to supertype. This gets us a description of the - # supertype type variables in terms of subtype variables, i.e. t[t1, ...] - # so that any type variables in tN are to be interpreted in subtype - # context. - inst_type = map_instance_to_supertype(inst_type, super_info) - # Finally expand the type variables in type with those in the previously - # constructed type. Note that both type and inst_type may have type - # variables, but in type they are interpreted in supertype context while - # in inst_type they are interpreted in subtype context. This works even if - # the names of type variables in supertype and subtype overlap. - return expand_type_by_instance(typ, inst_type) - - -F = TypeVar('F', bound=FunctionLike) - - -def bind_self(method: F, original_type: Optional[Type] = None, is_classmethod: bool = False) -> F: - """Return a copy of `method`, with the type of its first parameter (usually - self or cls) bound to original_type. - - If the type of `self` is a generic type (T, or Type[T] for classmethods), - instantiate every occurrence of type with original_type in the rest of the - signature and in the return type. - - original_type is the type of E in the expression E.copy(). It is None in - compatibility checks. In this case we treat it as the erasure of the - declared type of self. - - This way we can express "the type of self". For example: - - T = TypeVar('T', bound='A') - class A: - def copy(self: T) -> T: ... - - class B(A): pass - - b = B().copy() # type: B - - """ - if isinstance(method, Overloaded): - return cast(F, Overloaded([bind_self(c, original_type) for c in method.items()])) - assert isinstance(method, CallableType) - func = method - if not func.arg_types: - # invalid method. return something - return cast(F, func) - if func.arg_kinds[0] == ARG_STAR: - # The signature is of the form 'def foo(*args, ...)'. - # In this case we shouldn't drop the first arg, - # since func will be absorbed by the *args. - - # TODO: infer bounds on the type of *args? - return cast(F, func) - self_param_type = get_proper_type(func.arg_types[0]) - if func.variables and (isinstance(self_param_type, TypeVarType) or - (isinstance(self_param_type, TypeType) and - isinstance(self_param_type.item, TypeVarType))): - if original_type is None: - # Type check method override - # XXX value restriction as union? - original_type = erase_to_bound(self_param_type) - original_type = get_proper_type(original_type) - - ids = [x.id for x in func.variables] - typearg = get_proper_type(infer_type_arguments(ids, self_param_type, original_type)[0]) - if (is_classmethod and isinstance(typearg, UninhabitedType) - and isinstance(original_type, (Instance, TypeVarType, TupleType))): - # In case we call a classmethod through an instance x, fallback to type(x) - # TODO: handle Union - typearg = get_proper_type(infer_type_arguments(ids, self_param_type, - TypeType(original_type))[0]) - - def expand(target: Type) -> Type: - assert typearg is not None - return expand_type(target, {func.variables[0].id: typearg}) - - arg_types = [expand(x) for x in func.arg_types[1:]] - ret_type = expand(func.ret_type) - variables = func.variables[1:] - else: - arg_types = func.arg_types[1:] - ret_type = func.ret_type - variables = func.variables - - original_type = get_proper_type(original_type) - if isinstance(original_type, CallableType) and original_type.is_type_obj(): - original_type = TypeType.make_normalized(original_type.ret_type) - res = func.copy_modified(arg_types=arg_types, - arg_kinds=func.arg_kinds[1:], - arg_names=func.arg_names[1:], - variables=variables, - ret_type=ret_type, - bound_args=[original_type]) - return cast(F, res) - - -def erase_to_bound(t: Type) -> Type: - t = get_proper_type(t) - if isinstance(t, TypeVarType): - return t.upper_bound - if isinstance(t, TypeType): - if isinstance(t.item, TypeVarType): - return TypeType.make_normalized(t.item.upper_bound) - return t diff --git a/mypy/erasetype.py b/mypy/erasetype.py index ee4a2d9f5bdc..62580dfb0f12 100644 --- a/mypy/erasetype.py +++ b/mypy/erasetype.py @@ -87,7 +87,8 @@ def visit_literal_type(self, t: LiteralType) -> ProperType: def visit_union_type(self, t: UnionType) -> ProperType: erased_items = [erase_type(item) for item in t.items] - return UnionType.make_simplified_union(erased_items) + from mypy.typeops import make_simplified_union # asdf + return make_simplified_union(erased_items) def visit_type_type(self, t: TypeType) -> ProperType: return TypeType.make_normalized(t.item.accept(self), line=t.line) diff --git a/mypy/expandtype.py b/mypy/expandtype.py index 0f04fe43f1c0..d92275b684bf 100644 --- a/mypy/expandtype.py +++ b/mypy/expandtype.py @@ -119,7 +119,8 @@ def visit_literal_type(self, t: LiteralType) -> ProperType: def visit_union_type(self, t: UnionType) -> ProperType: # After substituting for type variables in t.items, # some of the resulting types might be subtypes of others. - return UnionType.make_simplified_union(self.expand_types(t.items), t.line, t.column) + from mypy.typeops import make_simplified_union # asdf + return make_simplified_union(self.expand_types(t.items), t.line, t.column) def visit_partial_type(self, t: PartialType) -> ProperType: return t diff --git a/mypy/join.py b/mypy/join.py index a64ed62cbdb4..20d18d96d16b 100644 --- a/mypy/join.py +++ b/mypy/join.py @@ -6,7 +6,7 @@ from mypy.types import ( Type, AnyType, NoneType, TypeVisitor, Instance, UnboundType, TypeVarType, CallableType, TupleType, TypedDictType, ErasedType, UnionType, FunctionLike, Overloaded, LiteralType, - PartialType, DeletedType, UninhabitedType, TypeType, true_or_false, TypeOfAny, get_proper_type, + PartialType, DeletedType, UninhabitedType, TypeType, TypeOfAny, get_proper_type, ProperType, get_proper_types ) from mypy.maptype import map_instance_to_supertype @@ -27,8 +27,8 @@ def join_simple(declaration: Optional[Type], s: Type, t: Type) -> ProperType: if (s.can_be_true, s.can_be_false) != (t.can_be_true, t.can_be_false): # if types are restricted in different ways, use the more general versions - s = true_or_false(s) - t = true_or_false(t) + s = mypy.typeops.true_or_false(s) + t = mypy.typeops.true_or_false(t) if isinstance(s, AnyType): return s @@ -43,7 +43,7 @@ def join_simple(declaration: Optional[Type], s: Type, t: Type) -> ProperType: return s if isinstance(declaration, UnionType): - return UnionType.make_simplified_union([s, t]) + return mypy.typeops.make_simplified_union([s, t]) if isinstance(s, NoneType) and not isinstance(t, NoneType): s, t = t, s @@ -68,8 +68,8 @@ def join_types(s: Type, t: Type) -> ProperType: if (s.can_be_true, s.can_be_false) != (t.can_be_true, t.can_be_false): # if types are restricted in different ways, use the more general versions - s = true_or_false(s) - t = true_or_false(t) + s = mypy.typeops.true_or_false(s) + t = mypy.typeops.true_or_false(t) if isinstance(s, AnyType): return s @@ -107,7 +107,7 @@ def visit_union_type(self, t: UnionType) -> ProperType: if is_subtype(self.s, t): return t else: - return UnionType.make_simplified_union([self.s, t]) + return mypy.typeops.make_simplified_union([self.s, t]) def visit_any(self, t: AnyType) -> ProperType: return t @@ -119,7 +119,7 @@ def visit_none_type(self, t: NoneType) -> ProperType: elif isinstance(self.s, UnboundType): return AnyType(TypeOfAny.special_form) else: - return UnionType.make_simplified_union([self.s, t]) + return mypy.typeops.make_simplified_union([self.s, t]) else: return self.s diff --git a/mypy/meet.py b/mypy/meet.py index b15dbd0ef3a8..c5d25652d7b3 100644 --- a/mypy/meet.py +++ b/mypy/meet.py @@ -13,7 +13,7 @@ from mypy.subtypes import is_equivalent, is_subtype, is_callable_compatible, is_proper_subtype from mypy.erasetype import erase_type from mypy.maptype import map_instance_to_supertype -from mypy.typeops import tuple_fallback +from mypy.typeops import tuple_fallback, make_simplified_union from mypy import state # TODO Describe this module. @@ -41,8 +41,8 @@ def narrow_declared_type(declared: Type, narrowed: Type) -> Type: if declared == narrowed: return declared if isinstance(declared, UnionType): - return UnionType.make_simplified_union([narrow_declared_type(x, narrowed) - for x in declared.relevant_items()]) + return make_simplified_union([narrow_declared_type(x, narrowed) + for x in declared.relevant_items()]) elif not is_overlapping_types(declared, narrowed, prohibit_none_typevar_overlap=True): if state.strict_optional: @@ -50,8 +50,8 @@ def narrow_declared_type(declared: Type, narrowed: Type) -> Type: else: return NoneType() elif isinstance(narrowed, UnionType): - return UnionType.make_simplified_union([narrow_declared_type(declared, x) - for x in narrowed.relevant_items()]) + return make_simplified_union([narrow_declared_type(declared, x) + for x in narrowed.relevant_items()]) elif isinstance(narrowed, AnyType): return narrowed elif isinstance(declared, TypeType) and isinstance(narrowed, TypeType): @@ -425,7 +425,7 @@ def visit_union_type(self, t: UnionType) -> ProperType: else: meets = [meet_types(x, self.s) for x in t.items] - return UnionType.make_simplified_union(meets) + return make_simplified_union(meets) def visit_none_type(self, t: NoneType) -> ProperType: if state.strict_optional: diff --git a/mypy/plugins/attrs.py b/mypy/plugins/attrs.py index 093bcf0edc15..305b7592e562 100644 --- a/mypy/plugins/attrs.py +++ b/mypy/plugins/attrs.py @@ -22,6 +22,7 @@ Type, AnyType, TypeOfAny, CallableType, NoneType, TypeVarDef, TypeVarType, Overloaded, UnionType, FunctionLike, get_proper_type ) +from mypy.typeops import make_simplified_union from mypy.typevars import fill_typevars from mypy.util import unmangle from mypy.server.trigger import make_wildcard_trigger @@ -109,7 +110,7 @@ def argument(self, ctx: 'mypy.plugin.ClassDefContext') -> Argument: types.append(item.arg_types[0]) # Make a union of all the valid types. if types: - args = UnionType.make_simplified_union(types) + args = make_simplified_union(types) init_type = ctx.api.anal_type(args) if self.converter.is_attr_converters_optional and init_type: diff --git a/mypy/plugins/ctypes.py b/mypy/plugins/ctypes.py index 446fb2cac8ad..ce943cec8850 100644 --- a/mypy/plugins/ctypes.py +++ b/mypy/plugins/ctypes.py @@ -11,6 +11,7 @@ AnyType, CallableType, Instance, NoneType, Type, TypeOfAny, UnionType, union_items, ProperType, get_proper_type ) +from mypy.typeops import make_simplified_union def _get_bytes_type(api: 'mypy.plugin.CheckerPluginInterface') -> Instance: @@ -76,7 +77,7 @@ def _autoconvertible_to_cdata(tp: Type, api: 'mypy.plugin.CheckerPluginInterface allowed_types.append(api.named_generic_type('builtins.int', [])) allowed_types.append(NoneType()) - return UnionType.make_simplified_union(allowed_types) + return make_simplified_union(allowed_types) def _autounboxed_cdata(tp: Type) -> ProperType: @@ -89,7 +90,7 @@ def _autounboxed_cdata(tp: Type) -> ProperType: tp = get_proper_type(tp) if isinstance(tp, UnionType): - return UnionType.make_simplified_union([_autounboxed_cdata(t) for t in tp.items]) + return make_simplified_union([_autounboxed_cdata(t) for t in tp.items]) elif isinstance(tp, Instance): for base in tp.type.bases: if base.type.fullname() == 'ctypes._SimpleCData': @@ -207,7 +208,7 @@ def array_value_callback(ctx: 'mypy.plugin.AttributeContext') -> Type: ' with element type c_char or c_wchar, not "{}"' .format(et), ctx.context) - return UnionType.make_simplified_union(types) + return make_simplified_union(types) return ctx.default_attr_type @@ -226,5 +227,5 @@ def array_raw_callback(ctx: 'mypy.plugin.AttributeContext') -> Type: ' with element type c_char, not "{}"' .format(et), ctx.context) - return UnionType.make_simplified_union(types) + return make_simplified_union(types) return ctx.default_attr_type diff --git a/mypy/plugins/default.py b/mypy/plugins/default.py index e67cd1fa3aa0..3476b930307b 100644 --- a/mypy/plugins/default.py +++ b/mypy/plugins/default.py @@ -8,10 +8,11 @@ ) from mypy.plugins.common import try_getting_str_literals from mypy.types import ( - Type, Instance, AnyType, TypeOfAny, CallableType, NoneType, UnionType, TypedDictType, + Type, Instance, AnyType, TypeOfAny, CallableType, NoneType, TypedDictType, TypeVarType, TPDICT_FB_NAMES, get_proper_type ) from mypy.subtypes import is_subtype +from mypy.typeops import make_simplified_union class DefaultPlugin(Plugin): @@ -168,7 +169,7 @@ def typed_dict_get_signature_callback(ctx: MethodSigContext) -> CallableType: tv = TypeVarType(signature.variables[0]) return signature.copy_modified( arg_types=[signature.arg_types[0], - UnionType.make_simplified_union([value_type, tv])], + make_simplified_union([value_type, tv])], ret_type=ret_type) return signature @@ -205,7 +206,7 @@ def typed_dict_get_callback(ctx: MethodContext) -> Type: if len(ctx.arg_types) == 1: output_types.append(NoneType()) - return UnionType.make_simplified_union(output_types) + return make_simplified_union(output_types) return ctx.default_return_type @@ -231,7 +232,7 @@ def typed_dict_pop_signature_callback(ctx: MethodSigContext) -> CallableType: # only needed for type inference since there's a union with a type # variable that accepts everything. tv = TypeVarType(signature.variables[0]) - typ = UnionType.make_simplified_union([value_type, tv]) + typ = make_simplified_union([value_type, tv]) return signature.copy_modified( arg_types=[str_type, typ], ret_type=typ) @@ -261,10 +262,10 @@ def typed_dict_pop_callback(ctx: MethodContext) -> Type: return AnyType(TypeOfAny.from_error) if len(ctx.args[1]) == 0: - return UnionType.make_simplified_union(value_types) + return make_simplified_union(value_types) elif (len(ctx.arg_types) == 2 and len(ctx.arg_types[1]) == 1 and len(ctx.args[1]) == 1): - return UnionType.make_simplified_union([*value_types, ctx.arg_types[1][0]]) + return make_simplified_union([*value_types, ctx.arg_types[1][0]]) return ctx.default_return_type @@ -321,7 +322,7 @@ def typed_dict_setdefault_callback(ctx: MethodContext) -> Type: value_types.append(value_type) - return UnionType.make_simplified_union(value_types) + return make_simplified_union(value_types) return ctx.default_return_type diff --git a/mypy/sametypes.py b/mypy/sametypes.py index cc0884ac42e7..f09de9c18e15 100644 --- a/mypy/sametypes.py +++ b/mypy/sametypes.py @@ -6,7 +6,7 @@ Overloaded, PartialType, DeletedType, UninhabitedType, TypeType, LiteralType, ProperType, get_proper_type ) -from mypy.typeops import tuple_fallback +from mypy.typeops import tuple_fallback, make_simplified_union def is_same_type(left: Type, right: Type) -> bool: @@ -35,7 +35,7 @@ def is_same_type(left: Type, right: Type) -> bool: def simplify_union(t: Type) -> ProperType: t = get_proper_type(t) if isinstance(t, UnionType): - return UnionType.make_simplified_union(t.items) + return make_simplified_union(t.items) return t diff --git a/mypy/subtypes.py b/mypy/subtypes.py index 0a40b09fff32..13276f0d4f60 100644 --- a/mypy/subtypes.py +++ b/mypy/subtypes.py @@ -241,7 +241,8 @@ def visit_type_var(self, left: TypeVarType) -> bool: right = self.right if isinstance(right, TypeVarType) and left.id == right.id: return True - if left.values and self._is_subtype(UnionType.make_simplified_union(left.values), right): + if left.values and self._is_subtype( + mypy.typeops.make_simplified_union(left.values), right): return True return self._is_subtype(left.upper_bound, self.right) @@ -609,7 +610,8 @@ def find_node_type(node: Union[Var, FuncBase], itype: Instance, subtype: Type) - """Find type of a variable or method 'node' (maybe also a decorated method). Apply type arguments from 'itype', and bind 'self' to 'subtype'. """ - from mypy.checkmember import bind_self + from mypy.typeops import bind_self + if isinstance(node, FuncBase): typ = function_type(node, fallback=Instance(itype.type.mro[-1], [])) # type: Optional[Type] @@ -841,7 +843,7 @@ def _incompatible(left_arg: Optional[FormalArgument], # also accept. The only exception is if we are allowing partial # partial overlaps: in that case, we ignore optional args on the right. for right_arg in right.formal_arguments(): - left_arg = left.corresponding_argument(right_arg) + left_arg = mypy.typeops.callable_corresponding_argument(left, right_arg) if left_arg is None: if allow_partial_overlap and not right_arg.required: continue @@ -981,13 +983,17 @@ def new_is_compat(left: Type, right: Type) -> bool: def unify_generic_callable(type: CallableType, target: CallableType, ignore_return: bool, - return_constraint_direction: int = mypy.constraints.SUBTYPE_OF, + return_constraint_direction: Optional[int] = None, ) -> Optional[CallableType]: """Try to unify a generic callable type with another callable type. Return unified CallableType if successful; otherwise, return None. """ import mypy.solve + + if return_constraint_direction is None: + return_constraint_direction = mypy.constraints.SUBTYPE_OF + constraints = [] # type: List[mypy.constraints.Constraint] for arg_type, target_arg_type in zip(type.arg_types, target.arg_types): c = mypy.constraints.infer_constraints( @@ -1166,8 +1172,8 @@ def check_argument(leftarg: Type, rightarg: Type, variance: int) -> bool: def visit_type_var(self, left: TypeVarType) -> bool: if isinstance(self.right, TypeVarType) and left.id == self.right.id: return True - if left.values and self._is_proper_subtype(UnionType.make_simplified_union(left.values), - self.right): + if left.values and self._is_proper_subtype( + mypy.typeops.make_simplified_union(left.values), self.right): return True return self._is_proper_subtype(left.upper_bound, self.right) diff --git a/mypy/suggestions.py b/mypy/suggestions.py index c0b5606a1089..1b100d50e949 100644 --- a/mypy/suggestions.py +++ b/mypy/suggestions.py @@ -48,6 +48,7 @@ from mypy.join import join_type_list from mypy.sametypes import is_same_type +from mypy.typeops import make_simplified_union from contextlib import contextmanager @@ -598,7 +599,7 @@ def generate_type_combinations(types: List[Type]) -> List[Type]: and unioning the types. We try both. """ joined_type = join_type_list(types) - union_type = UnionType.make_simplified_union(types) + union_type = make_simplified_union(types) if is_same_type(joined_type, union_type): return [joined_type] else: diff --git a/mypy/test/testtypes.py b/mypy/test/testtypes.py index fbf9e1601d63..604ce941e5e8 100644 --- a/mypy/test/testtypes.py +++ b/mypy/test/testtypes.py @@ -10,13 +10,14 @@ from mypy.sametypes import is_same_type from mypy.types import ( UnboundType, AnyType, CallableType, TupleType, TypeVarDef, Type, Instance, NoneType, - Overloaded, TypeType, UnionType, UninhabitedType, true_only, false_only, TypeVarId, TypeOfAny, + Overloaded, TypeType, UnionType, UninhabitedType, TypeVarId, TypeOfAny, LiteralType, ) from mypy.nodes import ARG_POS, ARG_OPT, ARG_STAR, ARG_STAR2, CONTRAVARIANT, INVARIANT, COVARIANT from mypy.subtypes import is_subtype, is_more_precise, is_proper_subtype from mypy.test.typefixture import TypeFixture, InterfaceTypeFixture from mypy.state import strict_optional_set +from mypy.typeops import true_only, false_only class TypesSuite(Suite): diff --git a/mypy/typeops.py b/mypy/typeops.py index 67135af550e5..91548fb77608 100644 --- a/mypy/typeops.py +++ b/mypy/typeops.py @@ -1,13 +1,26 @@ -"""Miscellaneuus type operations and helpers for use during type checking. +"""Miscellaneous type operations and helpers for use during type checking. NOTE: These must not be accessed from mypy.nodes or mypy.types to avoid import cycles. These must not be called from the semantic analysis main pass since these may assume that MROs are ready. """ -# TODO: Move more type operations here +from typing import cast, Optional, List, Sequence, Set -from mypy.types import TupleType, Instance +from mypy.types import ( + TupleType, Instance, FunctionLike, Type, CallableType, TypeVarDef, Overloaded, + TypeVarType, TypeType, UninhabitedType, FormalArgument, UnionType, NoneType, + ProperType, get_proper_type, get_proper_types, copy_type +) +from mypy.nodes import ( + TypeInfo, TypeVar, ARG_STAR, +) +from mypy.maptype import map_instance_to_supertype +from mypy.expandtype import expand_type_by_instance, expand_type + +from mypy.typevars import fill_typevars + +from mypy import state def tuple_fallback(typ: TupleType) -> Instance: @@ -18,3 +31,341 @@ def tuple_fallback(typ: TupleType) -> Instance: if info.fullname() != 'builtins.tuple': return typ.partial_fallback return Instance(info, [join_type_list(typ.items)]) + + +def type_object_type_from_function(signature: FunctionLike, + info: TypeInfo, + def_info: TypeInfo, + fallback: Instance, + is_new: bool) -> FunctionLike: + # The __init__ method might come from a generic superclass + # (init_or_new.info) with type variables that do not map + # identically to the type variables of the class being constructed + # (info). For example + # + # class A(Generic[T]): def __init__(self, x: T) -> None: pass + # class B(A[List[T]], Generic[T]): pass + # + # We need to first map B's __init__ to the type (List[T]) -> None. + signature = bind_self(signature, original_type=fill_typevars(info), is_classmethod=is_new) + signature = cast(FunctionLike, + map_type_from_supertype(signature, info, def_info)) + special_sig = None # type: Optional[str] + if def_info.fullname() == 'builtins.dict': + # Special signature! + special_sig = 'dict' + + if isinstance(signature, CallableType): + return class_callable(signature, info, fallback, special_sig, is_new) + else: + # Overloaded __init__/__new__. + assert isinstance(signature, Overloaded) + items = [] # type: List[CallableType] + for item in signature.items(): + items.append(class_callable(item, info, fallback, special_sig, is_new)) + return Overloaded(items) + + +def class_callable(init_type: CallableType, info: TypeInfo, type_type: Instance, + special_sig: Optional[str], + is_new: bool) -> CallableType: + """Create a type object type based on the signature of __init__.""" + variables = [] # type: List[TypeVarDef] + variables.extend(info.defn.type_vars) + variables.extend(init_type.variables) + + init_ret_type = get_proper_type(init_type.ret_type) + if is_new and isinstance(init_ret_type, (Instance, TupleType)): + ret_type = init_type.ret_type # type: Type + else: + ret_type = fill_typevars(info) + + callable_type = init_type.copy_modified( + ret_type=ret_type, fallback=type_type, name=None, variables=variables, + special_sig=special_sig) + c = callable_type.with_name(info.name()) + return c + + +def map_type_from_supertype(typ: Type, + sub_info: TypeInfo, + super_info: TypeInfo) -> Type: + """Map type variables in a type defined in a supertype context to be valid + in the subtype context. Assume that the result is unique; if more than + one type is possible, return one of the alternatives. + + For example, assume + + . class D(Generic[S]) ... + . class C(D[E[T]], Generic[T]) ... + + Now S in the context of D would be mapped to E[T] in the context of C. + """ + # Create the type of self in subtype, of form t[a1, ...]. + inst_type = fill_typevars(sub_info) + if isinstance(inst_type, TupleType): + inst_type = tuple_fallback(inst_type) + # Map the type of self to supertype. This gets us a description of the + # supertype type variables in terms of subtype variables, i.e. t[t1, ...] + # so that any type variables in tN are to be interpreted in subtype + # context. + inst_type = map_instance_to_supertype(inst_type, super_info) + # Finally expand the type variables in type with those in the previously + # constructed type. Note that both type and inst_type may have type + # variables, but in type they are interpreted in supertype context while + # in inst_type they are interpreted in subtype context. This works even if + # the names of type variables in supertype and subtype overlap. + return expand_type_by_instance(typ, inst_type) + + +F = TypeVar('F', bound=FunctionLike) + + +def bind_self(method: F, original_type: Optional[Type] = None, is_classmethod: bool = False) -> F: + """Return a copy of `method`, with the type of its first parameter (usually + self or cls) bound to original_type. + + If the type of `self` is a generic type (T, or Type[T] for classmethods), + instantiate every occurrence of type with original_type in the rest of the + signature and in the return type. + + original_type is the type of E in the expression E.copy(). It is None in + compatibility checks. In this case we treat it as the erasure of the + declared type of self. + + This way we can express "the type of self". For example: + + T = TypeVar('T', bound='A') + class A: + def copy(self: T) -> T: ... + + class B(A): pass + + b = B().copy() # type: B + + """ + from mypy.infer import infer_type_arguments + + if isinstance(method, Overloaded): + return cast(F, Overloaded([bind_self(c, original_type) for c in method.items()])) + assert isinstance(method, CallableType) + func = method + if not func.arg_types: + # invalid method. return something + return cast(F, func) + if func.arg_kinds[0] == ARG_STAR: + # The signature is of the form 'def foo(*args, ...)'. + # In this case we shouldn't drop the first arg, + # since func will be absorbed by the *args. + + # TODO: infer bounds on the type of *args? + return cast(F, func) + self_param_type = get_proper_type(func.arg_types[0]) + if func.variables and (isinstance(self_param_type, TypeVarType) or + (isinstance(self_param_type, TypeType) and + isinstance(self_param_type.item, TypeVarType))): + if original_type is None: + # Type check method override + # XXX value restriction as union? + original_type = erase_to_bound(self_param_type) + original_type = get_proper_type(original_type) + + ids = [x.id for x in func.variables] + typearg = get_proper_type(infer_type_arguments(ids, self_param_type, original_type)[0]) + if (is_classmethod and isinstance(typearg, UninhabitedType) + and isinstance(original_type, (Instance, TypeVarType, TupleType))): + # In case we call a classmethod through an instance x, fallback to type(x) + # TODO: handle Union + typearg = get_proper_type(infer_type_arguments(ids, self_param_type, + TypeType(original_type))[0]) + + def expand(target: Type) -> Type: + assert typearg is not None + return expand_type(target, {func.variables[0].id: typearg}) + + arg_types = [expand(x) for x in func.arg_types[1:]] + ret_type = expand(func.ret_type) + variables = func.variables[1:] + else: + arg_types = func.arg_types[1:] + ret_type = func.ret_type + variables = func.variables + + original_type = get_proper_type(original_type) + if isinstance(original_type, CallableType) and original_type.is_type_obj(): + original_type = TypeType.make_normalized(original_type.ret_type) + res = func.copy_modified(arg_types=arg_types, + arg_kinds=func.arg_kinds[1:], + arg_names=func.arg_names[1:], + variables=variables, + ret_type=ret_type, + bound_args=[original_type]) + return cast(F, res) + + +def erase_to_bound(t: Type) -> Type: + t = get_proper_type(t) + if isinstance(t, TypeVarType): + return t.upper_bound + if isinstance(t, TypeType): + if isinstance(t.item, TypeVarType): + return TypeType.make_normalized(t.item.upper_bound) + return t + + +def callable_corresponding_argument(typ: CallableType, + model: FormalArgument) -> Optional[FormalArgument]: + """Return the argument a function that corresponds to `model`""" + + by_name = typ.argument_by_name(model.name) + by_pos = typ.argument_by_position(model.pos) + if by_name is None and by_pos is None: + return None + if by_name is not None and by_pos is not None: + if by_name == by_pos: + return by_name + # If we're dealing with an optional pos-only and an optional + # name-only arg, merge them. This is the case for all functions + # taking both *args and **args, or a pair of functions like so: + + # def right(a: int = ...) -> None: ... + # def left(__a: int = ..., *, a: int = ...) -> None: ... + from mypy.subtypes import is_equivalent + + if (not (by_name.required or by_pos.required) + and by_pos.name is None + and by_name.pos is None + and is_equivalent(by_name.typ, by_pos.typ)): + return FormalArgument(by_name.name, by_pos.pos, by_name.typ, False) + return by_name if by_name is not None else by_pos + + +def make_simplified_union(items: Sequence[Type], + line: int = -1, column: int = -1) -> ProperType: + """Build union type with redundant union items removed. + + If only a single item remains, this may return a non-union type. + + Examples: + + * [int, str] -> Union[int, str] + * [int, object] -> object + * [int, int] -> int + * [int, Any] -> Union[int, Any] (Any types are not simplified away!) + * [Any, Any] -> Any + + Note: This must NOT be used during semantic analysis, since TypeInfos may not + be fully initialized. + """ + # TODO: Make this a function living somewhere outside mypy.types. Most other non-trivial + # type operations are not static methods, so this is inconsistent. + items = get_proper_types(items) + while any(isinstance(typ, UnionType) for typ in items): + all_items = [] # type: List[ProperType] + for typ in items: + if isinstance(typ, UnionType): + all_items.extend(typ.items) + else: + all_items.append(typ) + items = all_items + + from mypy.subtypes import is_proper_subtype + + removed = set() # type: Set[int] + for i, ti in enumerate(items): + if i in removed: continue + # Keep track of the truishness info for deleted subtypes which can be relevant + cbt = cbf = False + for j, tj in enumerate(items): + if i != j and is_proper_subtype(tj, ti): + # We found a redundant item in the union. + removed.add(j) + cbt = cbt or tj.can_be_true + cbf = cbf or tj.can_be_false + # if deleted subtypes had more general truthiness, use that + if not ti.can_be_true and cbt: + items[i] = true_or_false(ti) + elif not ti.can_be_false and cbf: + items[i] = true_or_false(ti) + + simplified_set = [items[i] for i in range(len(items)) if i not in removed] + return UnionType.make_union(simplified_set, line, column) + + +def true_only(t: Type) -> ProperType: + """ + Restricted version of t with only True-ish values + """ + t = get_proper_type(t) + + if not t.can_be_true: + # All values of t are False-ish, so there are no true values in it + return UninhabitedType(line=t.line, column=t.column) + elif not t.can_be_false: + # All values of t are already True-ish, so true_only is idempotent in this case + return t + elif isinstance(t, UnionType): + # The true version of a union type is the union of the true versions of its components + new_items = [true_only(item) for item in t.items] + return make_simplified_union(new_items, line=t.line, column=t.column) + else: + new_t = copy_type(t) + new_t.can_be_false = False + return new_t + + +def false_only(t: Type) -> ProperType: + """ + Restricted version of t with only False-ish values + """ + t = get_proper_type(t) + + if not t.can_be_false: + if state.strict_optional: + # All values of t are True-ish, so there are no false values in it + return UninhabitedType(line=t.line) + else: + # When strict optional checking is disabled, everything can be + # False-ish since anything can be None + return NoneType(line=t.line) + elif not t.can_be_true: + # All values of t are already False-ish, so false_only is idempotent in this case + return t + elif isinstance(t, UnionType): + # The false version of a union type is the union of the false versions of its components + new_items = [false_only(item) for item in t.items] + return make_simplified_union(new_items, line=t.line, column=t.column) + else: + new_t = copy_type(t) + new_t.can_be_true = False + return new_t + + +def true_or_false(t: Type) -> ProperType: + """ + Unrestricted version of t with both True-ish and False-ish values + """ + t = get_proper_type(t) + + if isinstance(t, UnionType): + new_items = [true_or_false(item) for item in t.items] + return make_simplified_union(new_items, line=t.line, column=t.column) + + new_t = copy_type(t) + new_t.can_be_true = new_t.can_be_true_default() + new_t.can_be_false = new_t.can_be_false_default() + return new_t + + +def erase_def_to_union_or_bound(tdef: TypeVarDef) -> Type: + if tdef.values: + return make_simplified_union(tdef.values) + else: + return tdef.upper_bound + + +def erase_to_union_or_bound(typ: TypeVarType) -> ProperType: + if typ.values: + return make_simplified_union(typ.values) + else: + return get_proper_type(typ.upper_bound) diff --git a/mypy/types.py b/mypy/types.py index bd96d539ef59..e4b5918d9c31 100644 --- a/mypy/types.py +++ b/mypy/types.py @@ -333,12 +333,6 @@ def new_unification_variable(old: 'TypeVarDef') -> 'TypeVarDef': return TypeVarDef(old.name, old.fullname, new_id, old.values, old.upper_bound, old.variance, old.line, old.column) - def erase_to_union_or_bound(self) -> Type: - if self.values: - return UnionType.make_simplified_union(self.values) - else: - return self.upper_bound - def __repr__(self) -> str: if self.values: return '{} in {}'.format(self.name, tuple(self.values)) @@ -855,12 +849,6 @@ def __init__(self, binder: TypeVarDef, line: int = -1, column: int = -1) -> None def accept(self, visitor: 'TypeVisitor[T]') -> T: return visitor.visit_type_var(self) - def erase_to_union_or_bound(self) -> ProperType: - if self.values: - return UnionType.make_simplified_union(self.values) - else: - return get_proper_type(self.upper_bound) - def __hash__(self) -> int: return hash(self.id) @@ -1123,30 +1111,6 @@ def formal_arguments(self, include_star_args: bool = False) -> Iterator[FormalAr self.arg_types[i], required) - def corresponding_argument(self, model: FormalArgument) -> Optional[FormalArgument]: - """Return the argument in this function that corresponds to `model`""" - - by_name = self.argument_by_name(model.name) - by_pos = self.argument_by_position(model.pos) - if by_name is None and by_pos is None: - return None - if by_name is not None and by_pos is not None: - if by_name == by_pos: - return by_name - # If we're dealing with an optional pos-only and an optional - # name-only arg, merge them. This is the case for all functions - # taking both *args and **args, or a pair of functions like so: - - # def right(a: int = ...) -> None: ... - # def left(__a: int = ..., *, a: int = ...) -> None: ... - from mypy.subtypes import is_equivalent - if (not (by_name.required or by_pos.required) - and by_pos.name is None - and by_name.pos is None - and is_equivalent(by_name.typ, by_pos.typ)): - return FormalArgument(by_name.name, by_pos.pos, by_name.typ, False) - return by_name if by_name is not None else by_pos - def argument_by_name(self, name: Optional[str]) -> Optional[FormalArgument]: if name is None: return None @@ -1719,58 +1683,6 @@ def make_union(items: Sequence[Type], line: int = -1, column: int = -1) -> Type: else: return UninhabitedType() - @staticmethod - def make_simplified_union(items: Sequence[Type], - line: int = -1, column: int = -1) -> ProperType: - """Build union type with redundant union items removed. - - If only a single item remains, this may return a non-union type. - - Examples: - - * [int, str] -> Union[int, str] - * [int, object] -> object - * [int, int] -> int - * [int, Any] -> Union[int, Any] (Any types are not simplified away!) - * [Any, Any] -> Any - - Note: This must NOT be used during semantic analysis, since TypeInfos may not - be fully initialized. - """ - # TODO: Make this a function living somewhere outside mypy.types. Most other non-trivial - # type operations are not static methods, so this is inconsistent. - items = get_proper_types(items) - while any(isinstance(typ, UnionType) for typ in items): - all_items = [] # type: List[ProperType] - for typ in items: - if isinstance(typ, UnionType): - all_items.extend(typ.items) - else: - all_items.append(typ) - items = all_items - - from mypy.subtypes import is_proper_subtype - - removed = set() # type: Set[int] - for i, ti in enumerate(items): - if i in removed: continue - # Keep track of the truishness info for deleted subtypes which can be relevant - cbt = cbf = False - for j, tj in enumerate(items): - if i != j and is_proper_subtype(tj, ti): - # We found a redundant item in the union. - removed.add(j) - cbt = cbt or tj.can_be_true - cbf = cbf or tj.can_be_false - # if deleted subtypes had more general truthiness, use that - if not ti.can_be_true and cbt: - items[i] = true_or_false(ti) - elif not ti.can_be_false and cbf: - items[i] = true_or_false(ti) - - simplified_set = [items[i] for i in range(len(items)) if i not in removed] - return UnionType.make_union(simplified_set, line, column) - def length(self) -> int: return len(self.items) @@ -2185,71 +2097,6 @@ def copy_type(t: TP) -> TP: return nt -def true_only(t: Type) -> ProperType: - """ - Restricted version of t with only True-ish values - """ - t = get_proper_type(t) - - if not t.can_be_true: - # All values of t are False-ish, so there are no true values in it - return UninhabitedType(line=t.line, column=t.column) - elif not t.can_be_false: - # All values of t are already True-ish, so true_only is idempotent in this case - return t - elif isinstance(t, UnionType): - # The true version of a union type is the union of the true versions of its components - new_items = [true_only(item) for item in t.items] - return UnionType.make_simplified_union(new_items, line=t.line, column=t.column) - else: - new_t = copy_type(t) - new_t.can_be_false = False - return new_t - - -def false_only(t: Type) -> ProperType: - """ - Restricted version of t with only False-ish values - """ - t = get_proper_type(t) - - if not t.can_be_false: - if state.strict_optional: - # All values of t are True-ish, so there are no false values in it - return UninhabitedType(line=t.line) - else: - # When strict optional checking is disabled, everything can be - # False-ish since anything can be None - return NoneType(line=t.line) - elif not t.can_be_true: - # All values of t are already False-ish, so false_only is idempotent in this case - return t - elif isinstance(t, UnionType): - # The false version of a union type is the union of the false versions of its components - new_items = [false_only(item) for item in t.items] - return UnionType.make_simplified_union(new_items, line=t.line, column=t.column) - else: - new_t = copy_type(t) - new_t.can_be_true = False - return new_t - - -def true_or_false(t: Type) -> ProperType: - """ - Unrestricted version of t with both True-ish and False-ish values - """ - t = get_proper_type(t) - - if isinstance(t, UnionType): - new_items = [true_or_false(item) for item in t.items] - return UnionType.make_simplified_union(new_items, line=t.line, column=t.column) - - new_t = copy_type(t) - new_t.can_be_true = new_t.can_be_true_default() - new_t.can_be_false = new_t.can_be_false_default() - return new_t - - def function_type(func: mypy.nodes.FuncBase, fallback: Instance) -> FunctionLike: if func.type: assert isinstance(func.type, FunctionLike) diff --git a/mypyc/genops_for.py b/mypyc/genops_for.py index 1aafc0a180a4..03d4094b960c 100644 --- a/mypyc/genops_for.py +++ b/mypyc/genops_for.py @@ -6,6 +6,7 @@ """ from typing import Union, List +from typing_extensions import TYPE_CHECKING from mypy.nodes import Lvalue, Expression from mypyc.ops import ( @@ -16,7 +17,9 @@ from mypyc.ops_list import list_len_op, list_get_item_unsafe_op from mypyc.ops_misc import iter_op, next_op from mypyc.ops_exc import no_err_occurred_op -import mypyc.genops + +if TYPE_CHECKING: + import mypyc.genops class ForGenerator: diff --git a/mypyc/ops.py b/mypyc/ops.py index b72c75bce236..3005d96a49cc 100644 --- a/mypyc/ops.py +++ b/mypyc/ops.py @@ -1866,10 +1866,5 @@ def all_concrete_classes(class_ir: ClassIR) -> List[ClassIR]: return concrete -# Import various modules that set up global state. -import mypyc.ops_int # noqa -import mypyc.ops_str # noqa -import mypyc.ops_list # noqa -import mypyc.ops_dict # noqa -import mypyc.ops_tuple # noqa -import mypyc.ops_misc # noqa +# Import ops_primitive that will set up set up global primitives tables. +import mypyc.ops_primitive # noqa diff --git a/mypyc/ops_primitive.py b/mypyc/ops_primitive.py index 497963d1496a..e2fe87554777 100644 --- a/mypyc/ops_primitive.py +++ b/mypyc/ops_primitive.py @@ -214,3 +214,12 @@ def custom_op(arg_types: List[RType], assert format_str is not None return OpDescription('', arg_types, result_type, is_var_arg, error_kind, format_str, emit, steals, is_borrowed, 0) + + +# Import various modules that set up global state. +import mypyc.ops_int # noqa +import mypyc.ops_str # noqa +import mypyc.ops_list # noqa +import mypyc.ops_dict # noqa +import mypyc.ops_tuple # noqa +import mypyc.ops_misc # noqa