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 solve_constraints(vars: List[TypeVarId], constraints: List[Constraint], strict: bool =True) -> List[Optional[Type]]: """Solve type constraints. Return the best type(s) for type variables; each type can be None if the value of the variable could not be solved. If a variable has no constraints, if strict=True then arbitrarily pick NoneTyp as the value of the type variable. If strict=False, pick AnyType. """ # Collect a list of constraints for each type variable. cmap = defaultdict(list) # type: Dict[TypeVarId, List[Constraint]] for con in constraints: cmap[con.type_var].append(con) res = [] # type: List[Optional[Type]] # Solve each type variable separately. for tvar in vars: bottom = None # type: Optional[Type] top = None # type: Optional[Type] candidate = None # type: Optional[Type] # Process each constraint separately, and calculate the lower and upper # bounds based on constraints. Note that we assume that the constraint # targets do not have constraint 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) else: if top is None: top = c.target else: top = meet_types(top, c.target) if isinstance(top, AnyType) or isinstance(bottom, AnyType): res.append(AnyType()) continue elif bottom is None: if top: candidate = top else: # No constraints for type variable -- 'UninhabitedType' is the most specific type. if strict: candidate = UninhabitedType() else: candidate = AnyType() elif top is None: candidate = bottom elif is_subtype(bottom, top): candidate = bottom else: candidate = None res.append(candidate) return res
def test_mixed_truth_restricted_type(self) -> None: # join_types against differently restricted truthiness types drops restrictions. true_any = true_only(AnyType(TypeOfAny.special_form)) false_o = false_only(self.fx.o) j = join_types(true_any, false_o) assert_true(j.can_be_true) assert_true(j.can_be_false)
def assert_simple_join(self, s: Type, t: Type, join: Type) -> None: result = join_types(s, t) actual = str(result) expected = str(join) assert_equal(actual, expected, 'join({}, {}) == {{}} ({{}} expected)'.format(s, t)) assert is_subtype(s, result), '{} not subtype of {}'.format(s, result) assert is_subtype(t, result), '{} not subtype of {}'.format(t, result)
def solve_constraints(vars: List[int], constraints: List[Constraint], basic: BasicTypes) -> List[Type]: """Solve type constraints. Return the best type(s) for type variables; each type can be None if the value of the variable could not be solved. If a variable has no constraints, arbitrarily pick NoneTyp as the value of the type variable. """ # Collect a list of constraints for each type variable. cmap = Dict[int, List[Constraint]]() for con in constraints: a = cmap.get(con.type_var, []) a.append(con) cmap[con.type_var] = a res = [] # type: List[Type] # Solve each type variable separately. for tvar in vars: bottom = None # type: Type top = None # type: Type # Process each contraint separely, and calculate the lower and upper # bounds based on constraints. Note that we assume that the constraint # targets do not have constraint 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 isinstance(top, AnyType) or isinstance(bottom, AnyType): res.append(AnyType()) continue elif bottom is None: if top: candidate = top else: # No constraints for type variable -- type 'None' is the most specific type. candidate = NoneTyp() elif top is None: candidate = bottom elif is_subtype(bottom, top): candidate = bottom else: candidate = None if isinstance(candidate, ErrorType): res.append(None) else: res.append(candidate) return res
def test_simple_type_objects(self): t1 = self.type_callable(self.fx.a, self.fx.a) t2 = self.type_callable(self.fx.b, self.fx.b) self.assert_join(t1, t1, t1) assert_true(join_types(t1, t1, self.fx.basic).is_type_obj()) self.assert_join(t1, t2, self.fx.std_type) self.assert_join(t1, self.fx.std_type, self.fx.std_type) self.assert_join(self.fx.std_type, self.fx.std_type, self.fx.std_type)
def assert_simple_join(self, s: Type, t: Type, join: Type) -> None: result = join_types(s, t) actual = str(result) expected = str(join) assert_equal(actual, expected, 'join({}, {}) == {{}} ({{}} expected)'.format(s, t)) assert_true(is_subtype(s, result), '{} not subtype of {}'.format(s, result)) assert_true(is_subtype(t, result), '{} not subtype of {}'.format(t, result))
def test_simple_type_objects(self): t1 = self.type_callable(self.fx.a, self.fx.a) t2 = self.type_callable(self.fx.b, self.fx.b) self.assert_join(t1, t1, t1) assert_true(join_types(t1, t1).is_type_obj()) self.assert_join(t1, t2, self.fx.type_type) self.assert_join(t1, self.fx.type_type, self.fx.type_type) self.assert_join(self.fx.type_type, self.fx.type_type, self.fx.type_type)
def assert_simple_join(self, s, t, join): result = join_types(s, t) 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 test_simple_type_objects(self) -> None: t1 = self.type_callable(self.fx.a, self.fx.a) t2 = self.type_callable(self.fx.b, self.fx.b) self.assert_join(t1, t1, t1) j = join_types(t1, t1) assert isinstance(j, CallableType) assert_true(j.is_type_obj()) self.assert_join(t1, t2, self.fx.type_type) self.assert_join(t1, self.fx.type_type, self.fx.type_type) self.assert_join(self.fx.type_type, self.fx.type_type, self.fx.type_type)
def visit_or_pattern(self, o: OrPattern) -> PatternType: current_type = self.type_context[-1] # # Check all the subpatterns # pattern_types = [] for pattern in o.patterns: pattern_type = self.accept(pattern, current_type) pattern_types.append(pattern_type) current_type = pattern_type.rest_type # # Collect the final type # types = [] for pattern_type in pattern_types: if not is_uninhabited(pattern_type.type): types.append(pattern_type.type) # # Check the capture types # capture_types: Dict[Var, List[Tuple[Expression, Type]]] = defaultdict(list) # Collect captures from the first subpattern for expr, typ in pattern_types[0].captures.items(): node = get_var(expr) capture_types[node].append((expr, typ)) # Check if other subpatterns capture the same names for i, pattern_type in enumerate(pattern_types[1:]): vars = {get_var(expr) for expr, _ in pattern_type.captures.items()} if capture_types.keys() != vars: self.msg.fail(message_registry.OR_PATTERN_ALTERNATIVE_NAMES, o.patterns[i]) for expr, typ in pattern_type.captures.items(): node = get_var(expr) capture_types[node].append((expr, typ)) captures: Dict[Expression, Type] = {} for var, capture_list in capture_types.items(): typ = UninhabitedType() for _, other in capture_list: typ = join_types(typ, other) captures[capture_list[0][0]] = typ union_type = make_simplified_union(types) return PatternType(union_type, current_type, captures)
def meet_similar_callables(t: CallableType, s: CallableType) -> CallableType: from mypy.join import join_types arg_types = [] # type: List[Type] for i in range(len(t.arg_types)): arg_types.append(join_types(t.arg_types[i], s.arg_types[i])) # TODO in combine_similar_callables also applies here (names and kinds) # The fallback type can be either 'function' or 'type'. The result should have 'function' as # fallback only if both operands have it as 'function'. if t.fallback.type.fullname() != 'builtins.function': fallback = t.fallback else: fallback = s.fallback return t.copy_modified(arg_types=arg_types, ret_type=meet_types(t.ret_type, s.ret_type), fallback=fallback, name=None)
def _analyze_iterable_item_type(self, expr: Expression) -> Type: """Return the item type given by 'expr' in an iterable context.""" # This logic is copied from mypy's TypeChecker.analyze_iterable_item_type. iterable = get_proper_type(self.types[expr]) echk = self.graph[self.module_name].type_checker().expr_checker iterator = echk.check_method_call_by_name('__iter__', iterable, [], [], expr)[0] from mypy.join import join_types if isinstance(iterable, TupleType): joined = UninhabitedType() # type: Type for item in iterable.items: joined = join_types(joined, item) return joined else: # Non-tuple iterable. return echk.check_method_call_by_name('__next__', iterator, [], [], expr)[0]
def solve_constraints(vars: List[TypeVarId], constraints: List[Constraint], strict: bool =True) -> List[Optional[Type]]: """Solve type constraints. Return the best type(s) for type variables; each type can be None if the value of the variable could not be solved. If a variable has no constraints, if strict=True then arbitrarily pick NoneTyp as the value of the type variable. If strict=False, pick AnyType. """ # Collect a list of constraints for each type variable. cmap = defaultdict(list) # type: Dict[TypeVarId, List[Constraint]] for con in constraints: cmap[con.type_var].append(con) res = [] # type: List[Optional[Type]] # Solve each type variable separately. for tvar in vars: bottom = None # type: Optional[Type] top = None # type: Optional[Type] candidate = None # type: Optional[Type] # Process each constraint separately, and calculate the lower and upper # bounds based on constraints. Note that we assume that the constraint # targets do not have constraint 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) else: if top is None: top = c.target else: top = meet_types(top, c.target) if isinstance(top, AnyType) or isinstance(bottom, AnyType): source_any = top if isinstance(top, AnyType) else bottom assert isinstance(source_any, AnyType) res.append(AnyType(TypeOfAny.from_another_any, source_any=source_any)) continue elif bottom is None: if top: candidate = top else: # No constraints for type variable -- 'UninhabitedType' is the most specific type. if strict: candidate = UninhabitedType() candidate.ambiguous = True else: candidate = AnyType(TypeOfAny.special_form) elif top is None: candidate = bottom elif is_subtype(bottom, top): candidate = bottom else: candidate = None res.append(candidate) return res
def solve_constraints(vars: List[int], constraints: List[Constraint], basic: BasicTypes) -> List[Type]: """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 = Dict[int, List[Constraint]]() for con in constraints: a = cmap.get(con.type_var, []) a.append(con) cmap[con.type_var] = a res = [] # type: List[Type] # Solve each type variable separately. for tvar in vars: bottom = None # type: Type top = None # type: Type # 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, AnyType) or isinstance(bottom, AnyType): top = AnyType() bottom = AnyType() # 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
Type[] res = [] # Solve each type variable separately. for tvar in vars: Type bottom = None Type 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()
def visit_class_pattern(self, o: ClassPattern) -> PatternType: current_type = get_proper_type(self.type_context[-1]) # # Check class type # type_info = o.class_ref.node assert type_info is not None if isinstance(type_info, TypeAlias) and not type_info.no_args: self.msg.fail(message_registry.CLASS_PATTERN_GENERIC_TYPE_ALIAS, o) return self.early_non_match() if isinstance(type_info, TypeInfo): any_type = AnyType(TypeOfAny.implementation_artifact) typ: Type = Instance(type_info, [any_type] * len(type_info.defn.type_vars)) elif isinstance(type_info, TypeAlias): typ = type_info.target else: if isinstance(type_info, Var): name = str(type_info.type) else: name = type_info.name self.msg.fail( message_registry.CLASS_PATTERN_TYPE_REQUIRED.format(name), o.class_ref) return self.early_non_match() new_type, rest_type = self.chk.conditional_types_with_intersection( current_type, [get_type_range(typ)], o, default=current_type) if is_uninhabited(new_type): return self.early_non_match() # TODO: Do I need this? narrowed_type = narrow_declared_type(current_type, new_type) # # Convert positional to keyword patterns # keyword_pairs: List[Tuple[Optional[str], Pattern]] = [] match_arg_set: Set[str] = set() captures: Dict[Expression, Type] = {} if len(o.positionals) != 0: if self.should_self_match(typ): if len(o.positionals) > 1: self.msg.fail( message_registry. CLASS_PATTERN_TOO_MANY_POSITIONAL_ARGS, o) pattern_type = self.accept(o.positionals[0], narrowed_type) if not is_uninhabited(pattern_type.type): return PatternType( pattern_type.type, join_types(rest_type, pattern_type.rest_type), pattern_type.captures) captures = pattern_type.captures else: local_errors = self.msg.clean_copy() match_args_type = analyze_member_access("__match_args__", typ, o, False, False, False, local_errors, original_type=typ, chk=self.chk) if local_errors.is_errors(): self.msg.fail( message_registry.MISSING_MATCH_ARGS.format(typ), o) return self.early_non_match() proper_match_args_type = get_proper_type(match_args_type) if isinstance(proper_match_args_type, TupleType): match_arg_names = get_match_arg_names( proper_match_args_type) if len(o.positionals) > len(match_arg_names): self.msg.fail( message_registry. CLASS_PATTERN_TOO_MANY_POSITIONAL_ARGS, o) return self.early_non_match() else: match_arg_names = [None] * len(o.positionals) for arg_name, pos in zip(match_arg_names, o.positionals): keyword_pairs.append((arg_name, pos)) if arg_name is not None: match_arg_set.add(arg_name) # # Check for duplicate patterns # keyword_arg_set = set() has_duplicates = False for key, value in zip(o.keyword_keys, o.keyword_values): keyword_pairs.append((key, value)) if key in match_arg_set: self.msg.fail( message_registry.CLASS_PATTERN_KEYWORD_MATCHES_POSITIONAL. format(key), value) has_duplicates = True elif key in keyword_arg_set: self.msg.fail( message_registry.CLASS_PATTERN_DUPLICATE_KEYWORD_PATTERN. format(key), value) has_duplicates = True keyword_arg_set.add(key) if has_duplicates: return self.early_non_match() # # Check keyword patterns # can_match = True for keyword, pattern in keyword_pairs: key_type: Optional[Type] = None local_errors = self.msg.clean_copy() if keyword is not None: key_type = analyze_member_access(keyword, narrowed_type, pattern, False, False, False, local_errors, original_type=new_type, chk=self.chk) else: key_type = AnyType(TypeOfAny.from_error) if local_errors.is_errors() or key_type is None: key_type = AnyType(TypeOfAny.from_error) self.msg.fail( message_registry.CLASS_PATTERN_UNKNOWN_KEYWORD.format( typ, keyword), value) inner_type, inner_rest_type, inner_captures = self.accept( pattern, key_type) if is_uninhabited(inner_type): can_match = False else: self.update_type_map(captures, inner_captures) if not is_uninhabited(inner_rest_type): rest_type = current_type if not can_match: new_type = UninhabitedType() return PatternType(new_type, rest_type, captures)
def visit_sequence_pattern(self, o: SequencePattern) -> PatternType: # # check for existence of a starred pattern # current_type = get_proper_type(self.type_context[-1]) if not self.can_match_sequence(current_type): return self.early_non_match() star_positions = [ i for i, p in enumerate(o.patterns) if isinstance(p, StarredPattern) ] star_position: Optional[int] = None if len(star_positions) == 1: star_position = star_positions[0] elif len(star_positions) >= 2: assert False, "Parser should prevent multiple starred patterns" required_patterns = len(o.patterns) if star_position is not None: required_patterns -= 1 # # get inner types of original type # if isinstance(current_type, TupleType): inner_types = current_type.items size_diff = len(inner_types) - required_patterns if size_diff < 0: return self.early_non_match() elif size_diff > 0 and star_position is None: return self.early_non_match() else: inner_type = self.get_sequence_type(current_type) if inner_type is None: inner_type = self.chk.named_type("builtins.object") inner_types = [inner_type] * len(o.patterns) # # match inner patterns # contracted_new_inner_types: List[Type] = [] contracted_rest_inner_types: List[Type] = [] captures: Dict[Expression, Type] = {} contracted_inner_types = self.contract_starred_pattern_types( inner_types, star_position, required_patterns) can_match = True for p, t in zip(o.patterns, contracted_inner_types): pattern_type = self.accept(p, t) typ, rest, type_map = pattern_type if is_uninhabited(typ): can_match = False else: contracted_new_inner_types.append(typ) contracted_rest_inner_types.append(rest) self.update_type_map(captures, type_map) new_inner_types = self.expand_starred_pattern_types( contracted_new_inner_types, star_position, len(inner_types)) # # Calculate new type # new_type: Type rest_type: Type = current_type if not can_match: new_type = UninhabitedType() elif isinstance(current_type, TupleType): narrowed_inner_types = [] inner_rest_types = [] for inner_type, new_inner_type in zip(inner_types, new_inner_types): narrowed_inner_type, inner_rest_type = \ self.chk.conditional_types_with_intersection( new_inner_type, [get_type_range(inner_type)], o, default=new_inner_type ) narrowed_inner_types.append(narrowed_inner_type) inner_rest_types.append(inner_rest_type) if all(not is_uninhabited(typ) for typ in narrowed_inner_types): new_type = TupleType(narrowed_inner_types, current_type.partial_fallback) else: new_type = UninhabitedType() if all(is_uninhabited(typ) for typ in inner_rest_types): # All subpatterns always match, so we can apply negative narrowing new_type, rest_type = self.chk.conditional_types_with_intersection( current_type, [get_type_range(new_type)], o, default=current_type) else: new_inner_type = UninhabitedType() for typ in new_inner_types: new_inner_type = join_types(new_inner_type, typ) new_type = self.construct_sequence_child(current_type, new_inner_type) if not is_subtype(new_type, current_type): new_type = current_type return PatternType(new_type, rest_type, captures)