| FazBrowse GitHub Viewer | Trending | | Home |
| Tools: [Download Repo ZIP] [Original HTTPS Page] |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -1752,17 +1752,39 @@ def check_callable_call( | |||
| 1752 | 1752 | freeze_all_type_vars(fresh_ret_type) | |
| 1753 | 1753 | callee = callee.copy_modified(ret_type=fresh_ret_type) | |
| 1754 | 1754 | ||
| 1755 | + formal_to_actual: list[list[int]] | None = None | ||
| 1755 | 1756 | if callee.is_generic(): | |
| 1756 | 1757 | callee = freshen_function_type_vars(callee) | |
| 1758 | + callee_before_context = callee | ||
| 1757 | 1759 | callee = self.infer_function_type_arguments_using_context(callee, context) | |
| 1760 | + if callee != callee_before_context: | ||
| 1761 | + formal_to_actual = map_actuals_to_formals( | ||
| 1762 | + arg_kinds, | ||
| 1763 | + arg_names, | ||
| 1764 | + callee.arg_kinds, | ||
| 1765 | + callee.arg_names, | ||
| 1766 | + lambda i: self.accept(args[i]), | ||
| 1767 | + ) | ||
| 1768 | + if self.should_infer_args_without_context( | ||
| 1769 | + callee_before_context, | ||
| 1770 | + callee, | ||
| 1771 | + args, | ||
| 1772 | + arg_kinds, | ||
| 1773 | + arg_names, | ||
| 1774 | + formal_to_actual, | ||
| 1775 | + context, | ||
| 1776 | + ): | ||
| 1777 | + callee = callee_before_context | ||
| 1778 | + formal_to_actual = None | ||
| 1758 | 1779 | ||
| 1759 | - formal_to_actual = map_actuals_to_formals( | ||
| 1760 | - arg_kinds, | ||
| 1761 | - arg_names, | ||
| 1762 | - callee.arg_kinds, | ||
| 1763 | - callee.arg_names, | ||
| 1764 | - lambda i: self.accept(args[i]), | ||
| 1765 | - ) | ||
| 1780 | + if formal_to_actual is None: | ||
| 1781 | + formal_to_actual = map_actuals_to_formals( | ||
| 1782 | + arg_kinds, | ||
| 1783 | + arg_names, | ||
| 1784 | + callee.arg_kinds, | ||
| 1785 | + callee.arg_names, | ||
| 1786 | + lambda i: self.accept(args[i]), | ||
| 1787 | + ) | ||
| 1766 | 1788 | ||
| 1767 | 1789 | if callee.special_sig == "tuple" and len(args) == 1: | |
| 1768 | 1790 | with self.msg.filter_errors(): | |
@@ -2061,6 +2083,112 @@ def infer_arg_types_in_context( | |||
| 2061 | 2083 | res.append(self.accept(arg)) | |
| 2062 | 2084 | return res | |
| 2063 | 2085 | ||
| 2086 | + def should_infer_args_without_context( | ||
| 2087 | + self, | ||
| 2088 | + callee: CallableType, | ||
| 2089 | + contextual_callee: CallableType, | ||
| 2090 | + args: list[Expression], | ||
| 2091 | + arg_kinds: list[ArgKind], | ||
| 2092 | + arg_names: Sequence[str | None] | None, | ||
| 2093 | + formal_to_actual: list[list[int]], | ||
| 2094 | + context: Context, | ||
| 2095 | + ) -> bool: | ||
| 2096 | + """Should we abandon contextual type argument inference for this call? | ||
| 2097 | + | ||
| 2098 | + Context can infer a wider type argument than the actual argument supports when | ||
| 2099 | + the type variable also appears in an invariant or callback argument position. | ||
| 2100 | + In that case we can use regular argument inference instead, but only if it | ||
| 2101 | + produces a valid call whose return type still satisfies the outer context. | ||
| 2102 | + """ | ||
| 2103 | + if contextual_callee.special_sig is not None or contextual_callee.is_type_obj(): | ||
| 2104 | + # Keep the previous behavior for special synthetic signatures and type object | ||
| 2105 | + # calls, where context is often intentionally used to produce better errors. | ||
| 2106 | + return False | ||
| 2107 | + | ||
| 2108 | + valid = True | ||
| 2109 | + | ||
| 2110 | + def check_arg( | ||
| 2111 | + caller_type: Type, | ||
| 2112 | + original_caller_type: Type, | ||
| 2113 | + caller_kind: ArgKind, | ||
| 2114 | + callee_type: Type, | ||
| 2115 | + n: int, | ||
| 2116 | + m: int, | ||
| 2117 | + callee: CallableType, | ||
| 2118 | + object_type: Type | None, | ||
| 2119 | + context: Context, | ||
| 2120 | + outer_context: Context, | ||
| 2121 | + ) -> None: | ||
| 2122 | + nonlocal valid | ||
| 2123 | + if not is_subtype( | ||
| 2124 | + get_proper_type(caller_type), | ||
| 2125 | + get_proper_type(callee_type), | ||
| 2126 | + options=self.chk.options, | ||
| 2127 | + ): | ||
| 2128 | + valid = False | ||
| 2129 | + | ||
| 2130 | + validation_callee = contextual_callee | ||
| 2131 | + validation_formal_to_actual = formal_to_actual | ||
| 2132 | + with self.msg.filter_errors(), self.chk.local_type_map: | ||
| 2133 | + if validation_callee.is_generic(): | ||
| 2134 | + need_refresh = any( | ||
| 2135 | + isinstance(v, (ParamSpecType, TypeVarTupleType)) | ||
| 2136 | + for v in validation_callee.variables | ||
| 2137 | + ) | ||
| 2138 | + validation_callee = self.infer_function_type_arguments( | ||
| 2139 | + validation_callee, | ||
| 2140 | + args, | ||
| 2141 | + arg_kinds, | ||
| 2142 | + arg_names, | ||
| 2143 | + validation_formal_to_actual, | ||
| 2144 | + need_refresh, | ||
| 2145 | + context, | ||
| 2146 | + ) | ||
| 2147 | + if need_refresh: | ||
| 2148 | + validation_formal_to_actual = map_actuals_to_formals( | ||
| 2149 | + arg_kinds, | ||
| 2150 | + arg_names, | ||
| 2151 | + validation_callee.arg_kinds, | ||
| 2152 | + validation_callee.arg_names, | ||
| 2153 | + lambda i: self.accept(args[i]), | ||
| 2154 | + ) | ||
| 2155 | + arg_types = self.infer_arg_types_in_context( | ||
| 2156 | + validation_callee, args, arg_kinds, validation_formal_to_actual | ||
| 2157 | + ) | ||
| 2158 | + self.check_argument_types( | ||
| 2159 | + arg_types, | ||
| 2160 | + arg_kinds, | ||
| 2161 | + args, | ||
| 2162 | + validation_callee, | ||
| 2163 | + validation_formal_to_actual, | ||
| 2164 | + context, | ||
| 2165 | + check_arg=check_arg, | ||
| 2166 | + ) | ||
| 2167 | + if valid: | ||
| 2168 | + return False | ||
| 2169 | + | ||
| 2170 | + ctx = self.type_context[-1] | ||
| 2171 | + if not ctx: | ||
| 2172 | + return False | ||
| 2173 | + | ||
| 2174 | + with self.msg.filter_errors() as local_errors, self.chk.local_type_map: | ||
| 2175 | + formal_to_actual = map_actuals_to_formals( | ||
| 2176 | + arg_kinds, | ||
| 2177 | + arg_names, | ||
| 2178 | + callee.arg_kinds, | ||
| 2179 | + callee.arg_names, | ||
| 2180 | + lambda i: self.accept(args[i]), | ||
| 2181 | + ) | ||
| 2182 | + need_refresh = any( | ||
| 2183 | + isinstance(v, (ParamSpecType, TypeVarTupleType)) for v in callee.variables | ||
| 2184 | + ) | ||
| 2185 | + inferred_callee = self.infer_function_type_arguments( | ||
| 2186 | + callee, args, arg_kinds, arg_names, formal_to_actual, need_refresh, context | ||
| 2187 | + ) | ||
| 2188 | + return not local_errors.has_new_errors() and is_subtype( | ||
| 2189 | + inferred_callee.ret_type, ctx, options=self.chk.options | ||
| 2190 | + ) | ||
| 2191 | + | ||
| 2064 | 2192 | def infer_function_type_arguments_using_context( | |
| 2065 | 2193 | self, callable: CallableType, error_context: Context | |
| 2066 | 2194 | ) -> CallableType: | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -1542,6 +1542,41 @@ def check(paths: Iterable[str], key: Callable[[str], int]) -> Union[str, None]: | |||
| 1542 | 1542 | return mymin(paths, key=key, default=None) | |
| 1543 | 1543 | [builtins fixtures/tuple.pyi] | |
| 1544 | 1544 | ||
| 1545 | + [case testContextInferenceDoesNotWidenInvariantArgument] | ||
| 1546 | + from typing import Generic, Iterable, Iterator, TypeVar, Union | ||
| 1547 | + | ||
| 1548 | + T = TypeVar("T") | ||
| 1549 | + S = TypeVar("S") | ||
| 1550 | + | ||
| 1551 | + class Vec(Generic[T]): | ||
| 1552 | + def getitem(self, i: int) -> T: pass | ||
| 1553 | + def setitem(self, i: int, v: T) -> None: pass | ||
| 1554 | + def __init__(self, iterable: Iterable[T]) -> None: pass | ||
| 1555 | + def __iter__(self) -> Iterator[T]: pass | ||
| 1556 | + def __add__(self, value: "Vec[S]") -> "Vec[Union[S, T]]": pass | ||
| 1557 | + | ||
| 1558 | + def fmt(arg: Iterable[Union[int, str]]) -> None: pass | ||
| 1559 | + | ||
| 1560 | + l1: Vec[int] = Vec([1]) | ||
| 1561 | + l2: Vec[int] = Vec([1]) | ||
| 1562 | + fmt(l1 + l2) | ||
| 1563 | + | ||
| 1564 | + dummy = l1 + l2 | ||
| 1565 | + fmt(dummy) | ||
| 1566 | + | ||
| 1567 | + [case testContextInferenceDoesNotWidenKeyedMinItem] | ||
| 1568 | + from typing import Callable, Iterable, List, Tuple, TypeVar | ||
| 1569 | + | ||
| 1570 | + T = TypeVar("T") | ||
| 1571 | + | ||
| 1572 | + def my_min(values: Iterable[T], *, key: Callable[[T], int]) -> T: pass | ||
| 1573 | + def tuple_len(x: Tuple[str, ...]) -> int: pass | ||
| 1574 | + def dot_join(parts: Iterable[str]) -> str: pass | ||
| 1575 | + | ||
| 1576 | + def smallest_prefix(x: List[Tuple[str, ...]]) -> str: | ||
| 1577 | + return dot_join(my_min(x, key=tuple_len)) | ||
| 1578 | + [builtins fixtures/tuple.pyi] | ||
| 1579 | + | ||
| 1545 | 1580 | [case testBinaryOpInferenceContext] | |
| 1546 | 1581 | from typing import Literal, TypeVar | |
| 1547 | 1582 | ||
@@ -1617,6 +1652,34 @@ async def outer(c: Cls[T]) -> Optional[T]: | |||
| 1617 | 1652 | [builtins fixtures/async_await.pyi] | |
| 1618 | 1653 | [typing fixtures/typing-async.pyi] | |
| 1619 | 1654 | ||
| 1655 | + [case testContextInferenceDoesNotWidenAwaitedInferredAttribute] | ||
| 1656 | + from typing import Mapping, Optional, TypeVar | ||
| 1657 | + | ||
| 1658 | + K = TypeVar("K") | ||
| 1659 | + | ||
| 1660 | + async def select_one(options: Mapping[K, str]) -> Optional[K]: | ||
| 1661 | + return None | ||
| 1662 | + | ||
| 1663 | + class ViaInferredAttr: | ||
| 1664 | + async def m(self) -> None: | ||
| 1665 | + cases = {"a": "x"} | ||
| 1666 | + self._done = await select_one(cases) | ||
| 1667 | + | ||
| 1668 | + async def via_local() -> None: | ||
| 1669 | + cases = {"a": "x"} | ||
| 1670 | + done = await select_one(cases) | ||
| 1671 | + done = await select_one(cases) | ||
| 1672 | + done2: Optional[str] = await select_one(cases) | ||
| 1673 | + | ||
| 1674 | + class ViaAnnotatedAttr: | ||
| 1675 | + _done: Optional[str] | ||
| 1676 | + | ||
| 1677 | + async def m(self) -> None: | ||
| 1678 | + cases = {"a": "x"} | ||
| 1679 | + self._done = await select_one(cases) | ||
| 1680 | + [builtins fixtures/dict.pyi] | ||
| 1681 | + [typing fixtures/typing-async.pyi] | ||
| 1682 | + | ||
| 1620 | 1683 | [case testContextFallbackNarrowingTernaryRValue] | |
| 1621 | 1684 | from typing import Iterable, Union | |
| 1622 | 1685 | ||
| Back | FazBrowse Home | New Git URL |
0 commit comments