def matches_signature(self, arg_types, is_var_arg, callee): """Determine whether argument types match the signature. If is_var_arg is True, the caller uses varargs. """ if not is_valid_argc(len(arg_types), False, callee): return False if is_var_arg: if not self.is_valid_var_arg(arg_types[-1]): return False arg_types, rest = expand_caller_var_args(arg_types, callee.max_fixed_args()) # Fixed function arguments. func_fixed = callee.max_fixed_args() for i in range(min(len(arg_types), func_fixed)): if not is_subtype(self.erase(arg_types[i]), self.erase( callee.arg_types[i])): return False # Function varargs. if callee.is_var_arg: for i in range(func_fixed, len(arg_types)): if not is_subtype(self.erase(arg_types[i]), self.erase(callee.arg_types[func_fixed])): return False return True
def join_instances(t, s, allow_interfaces, basic): """Calculate the join of two instance types. If allow_interfaces is True, also consider interface-type results for non-interface types. Return ErrorType if the result is ambiguous. """ if t.typ == s.typ: # Simplest case: join two types with the same base type (but # potentially different arguments). if is_subtype(t, s): # Compatible; combine type arguments. args = [] for i in range(len(t.args)): args.append(join_types(t.args[i], s.args[i], basic)) return Instance(t.typ, args) else: # Incompatible; return trivial result object. return basic.object elif t.typ.is_interface != s.typ.is_interface: return join_instances_as_interface(t, s, basic) elif t.typ.base is not None and is_subtype(t, s): return join_instances_via_supertype(t, s, allow_interfaces, basic) elif s.typ.base is not None: return join_instances_via_supertype(s, t, allow_interfaces, basic) elif allow_interfaces and not t.typ.is_interface: return join_instances_as_interface(t, s, basic) else: return basic.object
def is_valid_cast(self, source_type, target_type): """Is a cast from source_type to target_type valid (i.e. can succeed at runtime)? """ return (is_subtype(target_type, source_type) or is_subtype(source_type, target_type) or (isinstance(target_type, Instance) and (target_type).typ.is_interface) or (isinstance(source_type, Instance) and (source_type).typ.is_interface))
def assert_simple_join(self, s, t, join): result = join_types(s, t, self.fx.basic) actual = str(result) expected = str(join) assert_equal(actual, expected, 'join({}, {}) == {{}} ({{}} expected)'.format(s, t)) if not isinstance(s, ErrorType) and not isinstance(result, ErrorType): assert_true(is_subtype(s, result), '{} not subtype of {}'.format(s, result)) if not isinstance(t, ErrorType) and not isinstance(result, ErrorType): assert_true(is_subtype(t, result), '{} not subtype of {}'.format(t, result))
def assert_simple_meet(self, s, t, meet): result = meet_types(s, t, self.fx.basic) actual = str(result) expected = str(meet) assert_equal(actual, expected, 'meet({}, {}) == {{}} ({{}} expected)'.format(s, t)) if not isinstance(s, ErrorType) and not isinstance(result, ErrorType): assert_true(is_subtype(result, s), '{} not subtype of {}'.format(result, s)) if not isinstance(t, ErrorType) and not isinstance(result, ErrorType): assert_true(is_subtype(result, t), '{} not subtype of {}'.format(result, t))
def check_exception_type(self, info, context): t = Instance(info, []) if is_subtype(t, self.named_type('builtins.BaseException')): return t else: self.fail(messages.INVALID_EXCEPTION_TYPE, context) return Any()
def visit_instance(self, t): if isinstance(self.s, Instance): return join_instances(t, self.s, True, self.basic) elif t.typ == self.basic.std_type.typ and is_subtype(self.s, t): return t else: return self.default(self.s)
def check_arg(self, caller_type, original_caller_type, callee_type, n, callee, context): """Check the type of a single argument in a call.""" if isinstance(caller_type, Void): self.msg.does_not_return_value(caller_type, context) elif not is_subtype(caller_type, callee_type): self.msg.incompatible_argument(n, callee, original_caller_type, context)
def solve_constraints( vars, constraints, basic): """Solve type constraints. Return lower bound for each type variable or None if the variable could not be solved. """ # Collect a list of constraints for each type variable. cmap = {} for con in constraints: a = cmap.get(con.type_var, []) a.append(con) cmap[con.type_var] = a res = [] # Solve each type variable separately. for tvar in vars: bottom = None top = None # Process each contraint separely, and calculate the lower and upper # bounds based on constraints. Note that we assume that the contraint # targets do not have contraint references. for c in cmap.get(tvar, []): if c.op == SUPERTYPE_OF: if bottom is None: bottom = c.target else: bottom = join_types(bottom, c.target, basic) else: if top is None: top = c.target else: top = meet_types(top, c.target, basic) if top is None: if isinstance(bottom, Void): top = Void() else: top = basic.object if bottom is None: if isinstance(top, Void): bottom = Void() else: bottom = NoneTyp() if isinstance(top, Any) or isinstance(bottom, Any): top = Any() bottom = Any() # Pick the most specific type if it satisfies the constraints. if (not top or not bottom or is_subtype(bottom, top)) and ( not isinstance(top, ErrorType) and not isinstance(bottom, ErrorType)): res.append(bottom) else: res.append(None) return res
def check_subtype(self, subtype, supertype, context, msg=messages.INCOMPATIBLE_TYPES): """Generate an error if the subtype is not compatible with supertype.""" if not is_subtype(subtype, supertype): if isinstance(subtype, Void): self.msg.does_not_return_value(subtype, context) else: self.fail(msg, context)
def visit_callable(self, t): if isinstance(self.s, Callable) and is_similar_callables( t, self.s): return combine_similar_callables(t, self.s, self.basic) elif t.is_type_obj() and is_subtype(self.s, self.basic.std_type): return self.basic.std_type elif (isinstance(self.s, Instance) and (self.s).typ == self.basic.std_type.typ and t.is_type_obj()): return self.basic.std_type else: return self.default(self.s)
def visit_operator_assignment_stmt(self, s): """Type check an operator assignment statement, e.g. x += 1.""" lvalue_type = self.accept(s.lvalue) rvalue_type = self.expr_checker.check_op(op_methods[s.op], lvalue_type, s.rvalue, s) if isinstance(s.lvalue, IndexExpr): lv = s.lvalue self.check_single_assignment(None, (self.accept(lv.base), lv.index), s.rvalue, s.rvalue) else: if not is_subtype(rvalue_type, lvalue_type): self.msg.incompatible_operator_assignment(s.op, s)
def visit_instance(self, t): if isinstance(self.s, Instance): si = self.s if t.typ == si.typ: if is_subtype(t, self.s): # Combine type arguments. We could have used join below # equivalently. args = [] for i in range(len(t.args)): args.append(self.meet(t.args[i], si.args[i])) return Instance(t.typ, args) else: return NoneTyp() else: if is_subtype(t, self.s): return t elif is_subtype(self.s, t): # See also above comment. return self.s else: return NoneTyp() else: return self.default(self.s)
def check_override(self, override, original, name, supertype, node): """Check a method override with given signatures. Arguments: override: The signature of the overriding method. original: The signature of the original supertype method. name: The name of the subtype. This and the next argument are only used for generating error messages. supertype: The name of the supertype. """ if (isinstance(override, Overloaded) or isinstance(original, Overloaded) or len((override).arg_types) != len((original).arg_types) or (override).min_args != (original).min_args): if not is_subtype(override, original): self.msg.signature_incompatible_with_supertype( name, supertype, node) return else: # Give more detailed messages for the common case of both # signatures having the same number of arguments and no # intersection types. coverride = override coriginal = original for i in range(len(coverride.arg_types)): if not is_equivalent(coriginal.arg_types[i], coverride.arg_types[i]): self.msg.argument_incompatible_with_supertype( i + 1, name, supertype, node) if not is_subtype(coverride.ret_type, coriginal.ret_type): self.msg.return_type_incompatible_with_supertype( name, supertype, node)
return ErrorType() Typ visit_erased_type(self, ErasedType t): return self.s Typ visit_type_var(self, TypeVar t): if isinstance(self.s, TypeVar) and ((TypeVar)self.s).id == t.id: return self.s else: return self.default(self.s) Typ visit_instance(self, Instance t): if isinstance(self.s, Instance): si = (Instance)self.s if t.typ == si.typ: if is_subtype(t, self.s): # Combine type arguments. We could have used join below # equivalently. Typ[] args = [] for i in range(len(t.args)): args.append(self.meet(t.args[i], si.args[i])) return Instance(t.typ, args) else: return NoneTyp() else: if is_subtype(t, self.s): return t elif is_subtype(self.s, t): # See also above comment. return self.s else:
def assert_not_subtype(self, s, t): assert_true(not is_subtype(s, t), '{} subtype of {}'.format(s, t))
def assert_subtype(self, s, t): assert_true(is_subtype(s, t), '{} not subtype of {}'.format(s, t))
tuplet = (TupleType)arg_types[actual] while tuple_counter[0] < len(tuplet.items): actual_type = get_actual_type(arg_type, arg_kinds[actual], tuple_counter) self.check_arg(actual_type, arg_type, callee.arg_types[i], actual + 1, callee, context) void check_arg(self, Typ caller_type, Typ original_caller_type, Typ callee_type, int n, Callable callee, Context context): """Check the type of a single argument in a call.""" if isinstance(caller_type, Void): self.msg.does_not_return_value(caller_type, context) elif not is_subtype(caller_type, callee_type): self.msg.incompatible_argument(n, callee, original_caller_type, context) Typ overload_call_target(self, Typ[] arg_types, bool is_var_arg, Overloaded overload, Context context): """Infer the correct overload item to call with given argument types. The return value may be Callable or any (if an unique item could not be determined). If is_var_arg is True, the caller uses varargs. """ # TODO for overlapping signatures we should try to get a more precise # result than 'any' Typ match = None # Callable, Any or None for typ in overload.items():
def is_valid_keyword_var_arg(self, typ): """Is a type valid as a **kwargs argument?""" return is_subtype(typ, self.chk.named_generic_type( 'builtins.dict', [self.named_type('builtins.str'), Any()]))
def is_boolean(self, typ): """Is type compatible with bool?""" return is_subtype(typ, self.chk.bool_type())
else: return self.default(self.s) Typ visit_erased_type(self, ErasedType t): return self.s Typ visit_type_var(self, TypeVar t): if isinstance(self.s, TypeVar) and ((TypeVar)self.s).id == t.id: return self.s else: return self.default(self.s) Typ visit_instance(self, Instance t): if isinstance(self.s, Instance): return join_instances(t, (Instance)self.s, True, self.basic) elif t.typ == self.basic.std_type.typ and is_subtype(self.s, t): return t else: return self.default(self.s) Typ visit_callable(self, Callable t): if isinstance(self.s, Callable) and is_similar_callables( t, (Callable)self.s): return combine_similar_callables(t, (Callable)self.s, self.basic) elif t.is_type_obj() and is_subtype(self.s, self.basic.std_type): return self.basic.std_type elif (isinstance(self.s, Instance) and ((Instance)self.s).typ == self.basic.std_type.typ and t.is_type_obj()): return self.basic.std_type else:
if top is None: top = c.target else: top = meet_types(top, c.target, basic) if top is None: if isinstance(bottom, Void): top = Void() else: top = basic.object if bottom is None: if isinstance(top, Void): bottom = Void() else: bottom = NoneTyp() if isinstance(top, Any) or isinstance(bottom, Any): top = Any() bottom = Any() # Pick the most specific type if it satisfies the constraints. if (not top or not bottom or is_subtype(bottom, top)) and ( not isinstance(top, ErrorType) and not isinstance(bottom, ErrorType)): res.append(bottom) else: res.append(None) return res