|
98 | 98 | )
|
99 | 99 | from mypy.semanal_enum import ENUM_BASES
|
100 | 100 | from mypy.state import state
|
101 |
| -from mypy.subtypes import is_equivalent, is_same_type, is_subtype, non_method_protocol_members |
| 101 | +from mypy.subtypes import ( |
| 102 | + is_equivalent, |
| 103 | + is_proper_subtype, |
| 104 | + is_same_type, |
| 105 | + is_subtype, |
| 106 | + non_method_protocol_members, |
| 107 | +) |
102 | 108 | from mypy.traverser import has_await_expression
|
103 | 109 | from mypy.typeanal import (
|
104 | 110 | check_for_explicit_any,
|
@@ -259,7 +265,7 @@ class ExpressionChecker(ExpressionVisitor[Type]):
|
259 | 265 | type_context: list[Type | None]
|
260 | 266 |
|
261 | 267 | # cache resolved types in some cases
|
262 |
| - resolved_type: dict[Expression, ProperType] |
| 268 | + resolved_type: dict[tuple[Expression, Type | None], ProperType] |
263 | 269 |
|
264 | 270 | strfrm_checker: StringFormatterChecker
|
265 | 271 | plugin: Plugin
|
@@ -3994,34 +4000,59 @@ def fast_container_type(
|
3994 | 4000 | self, e: ListExpr | SetExpr | TupleExpr, container_fullname: str
|
3995 | 4001 | ) -> Type | None:
|
3996 | 4002 | """
|
3997 |
| - Fast path to determine the type of a list or set literal, |
3998 |
| - based on the list of entries. This mostly impacts large |
3999 |
| - module-level constant definitions. |
| 4003 | + Fast path to determine the type of a list or set literal, based on the list of entries. |
| 4004 | + This mostly impacts large constant definitions. |
4000 | 4005 |
|
4001 | 4006 | Limitations:
|
4002 | 4007 | - no active type context
|
4003 | 4008 | - no star expressions
|
4004 | 4009 | - the joined type of all entries must be an Instance or Tuple type
|
4005 | 4010 | """
|
4006 |
| - ctx = self.type_context[-1] |
4007 |
| - if ctx: |
| 4011 | + ctx = get_proper_type(self.type_context[-1]) |
| 4012 | + # TODO: can we safely allow TypeVarType (with appropriate upper_bound or values) ? |
| 4013 | + if not ctx or isinstance(ctx, AnyType): |
| 4014 | + return self._fast_container_type(e, container_fullname) |
| 4015 | + |
| 4016 | + # TODO: support relevant typing base classes (Sequence, AbstractSet, ...) ? |
| 4017 | + if not (isinstance(ctx, Instance) and ctx.type.fullname == container_fullname): |
4008 | 4018 | return None
|
4009 |
| - rt = self.resolved_type.get(e, None) |
| 4019 | + |
| 4020 | + vt = get_proper_type(ctx.args[0]) |
| 4021 | + if not (isinstance(vt, Instance) and allow_fast_container_literal(vt)): |
| 4022 | + return None |
| 4023 | + |
| 4024 | + with self.msg.filter_errors() as w: |
| 4025 | + rt = self._fast_container_type(e, container_fullname) |
| 4026 | + if w.has_new_errors(): |
| 4027 | + # fallback to slow path if we run into any errors here |
| 4028 | + return None |
| 4029 | + if rt and is_proper_subtype(rt.args[0], vt): |
| 4030 | + # let the type context win if we inferred a more precise type (List is invariant...) |
| 4031 | + return ctx |
| 4032 | + return None |
| 4033 | + |
| 4034 | + def _fast_container_type( |
| 4035 | + self, |
| 4036 | + e: ListExpr | SetExpr | TupleExpr, |
| 4037 | + container_fullname: str, |
| 4038 | + ctx: Optional[Instance] = None, |
| 4039 | + ) -> Instance | None: |
| 4040 | + rt = self.resolved_type.get((e, ctx), None) |
4010 | 4041 | if rt is not None:
|
4011 | 4042 | return rt if isinstance(rt, Instance) else None
|
4012 | 4043 | values: list[Type] = []
|
4013 | 4044 | for item in e.items:
|
4014 | 4045 | if isinstance(item, StarExpr):
|
4015 | 4046 | # fallback to slow path
|
4016 |
| - self.resolved_type[e] = NoneType() |
| 4047 | + self.resolved_type[(e, ctx)] = NoneType() |
4017 | 4048 | return None
|
4018 |
| - values.append(self.accept(item)) |
| 4049 | + values.append(self.accept(item, type_context=ctx.args[0] if ctx else None)) |
4019 | 4050 | vt = join.join_type_list(values)
|
4020 | 4051 | if not allow_fast_container_literal(vt):
|
4021 |
| - self.resolved_type[e] = NoneType() |
| 4052 | + self.resolved_type[(e, ctx)] = NoneType() |
4022 | 4053 | return None
|
4023 | 4054 | ct = self.chk.named_generic_type(container_fullname, [vt])
|
4024 |
| - self.resolved_type[e] = ct |
| 4055 | + self.resolved_type[(e, ctx)] = ct |
4025 | 4056 | return ct
|
4026 | 4057 |
|
4027 | 4058 | def check_lst_expr(self, e: ListExpr | SetExpr | TupleExpr, fullname: str, tag: str) -> Type:
|
@@ -4116,49 +4147,83 @@ def visit_tuple_expr(self, e: TupleExpr) -> Type:
|
4116 | 4147 |
|
4117 | 4148 | def fast_dict_type(self, e: DictExpr) -> Type | None:
|
4118 | 4149 | """
|
4119 |
| - Fast path to determine the type of a dict literal, |
4120 |
| - based on the list of entries. This mostly impacts large |
4121 |
| - module-level constant definitions. |
| 4150 | + Fast path to determine the type of a dict literal, based on the list of entries. |
| 4151 | + This mostly impacts large module-level constant definitions. |
| 4152 | +
|
| 4153 | + Will only trigger for the following type contexts: |
| 4154 | + - None (i.e no type context) |
| 4155 | + - AnyType |
| 4156 | + - Dict[K, V] where K and V are both Instance or Tuple types |
| 4157 | + """ |
| 4158 | + ctx = get_proper_type(self.type_context[-1]) |
| 4159 | + # TODO: can we safely allow TypeVarType (with appropriate upper_bound or values) ? |
| 4160 | + if not ctx or isinstance(ctx, AnyType): |
| 4161 | + return self._fast_dict_type(e) |
| 4162 | + |
| 4163 | + # TODO: support relevant typing base classes (Mapping, MutableMapping, ...) ? |
| 4164 | + if not (isinstance(ctx, Instance) and ctx.type.fullname == "builtins.dict"): |
| 4165 | + return None |
| 4166 | + |
| 4167 | + kt = get_proper_type(ctx.args[0]) |
| 4168 | + vt = get_proper_type(ctx.args[1]) |
| 4169 | + if not ( |
| 4170 | + isinstance(kt, Instance) |
| 4171 | + and isinstance(vt, Instance) |
| 4172 | + and allow_fast_container_literal(kt) |
| 4173 | + and allow_fast_container_literal(vt) |
| 4174 | + ): |
| 4175 | + return None |
| 4176 | + |
| 4177 | + with self.msg.filter_errors() as w: |
| 4178 | + rt = self._fast_dict_type(e, ctx) |
| 4179 | + if w.has_new_errors(): |
| 4180 | + # fallback to slow path if we run into any errors here |
| 4181 | + return None |
| 4182 | + if rt and is_proper_subtype(rt.args[0], kt) and is_proper_subtype(rt.args[1], vt): |
| 4183 | + # let the type context win if we inferred a more precise type (Dict is invariant...) |
| 4184 | + return ctx |
| 4185 | + return None |
| 4186 | + |
| 4187 | + def _fast_dict_type(self, e: DictExpr, ctx: Optional[Instance] = None) -> Instance | None: |
| 4188 | + """ |
| 4189 | + Fast path to determine the type of a dict literal, based on the list of entries. |
| 4190 | + This mostly impacts large constant definitions. |
4122 | 4191 |
|
4123 | 4192 | Limitations:
|
4124 |
| - - no active type context |
4125 | 4193 | - only supported star expressions are other dict instances
|
4126 | 4194 | - the joined types of all keys and values must be Instance or Tuple types
|
4127 | 4195 | """
|
4128 |
| - ctx = self.type_context[-1] |
4129 |
| - if ctx: |
4130 |
| - return None |
4131 |
| - rt = self.resolved_type.get(e, None) |
| 4196 | + rt = self.resolved_type.get((e, ctx), None) |
4132 | 4197 | if rt is not None:
|
4133 | 4198 | return rt if isinstance(rt, Instance) else None
|
4134 | 4199 | keys: list[Type] = []
|
4135 | 4200 | values: list[Type] = []
|
4136 | 4201 | stargs: tuple[Type, Type] | None = None
|
4137 | 4202 | for key, value in e.items:
|
4138 | 4203 | if key is None:
|
4139 |
| - st = get_proper_type(self.accept(value)) |
| 4204 | + st = get_proper_type(self.accept(value, type_context=ctx.args[1] if ctx else None)) |
4140 | 4205 | if (
|
4141 | 4206 | isinstance(st, Instance)
|
4142 | 4207 | and st.type.fullname == "builtins.dict"
|
4143 | 4208 | and len(st.args) == 2
|
4144 | 4209 | ):
|
4145 | 4210 | stargs = (st.args[0], st.args[1])
|
4146 | 4211 | else:
|
4147 |
| - self.resolved_type[e] = NoneType() |
| 4212 | + self.resolved_type[(e, ctx)] = NoneType() |
4148 | 4213 | return None
|
4149 | 4214 | else:
|
4150 |
| - keys.append(self.accept(key)) |
4151 |
| - values.append(self.accept(value)) |
| 4215 | + keys.append(self.accept(key, type_context=ctx.args[0] if ctx else None)) |
| 4216 | + values.append(self.accept(value, type_context=ctx.args[1] if ctx else None)) |
4152 | 4217 | kt = join.join_type_list(keys)
|
4153 | 4218 | vt = join.join_type_list(values)
|
4154 | 4219 | if not (allow_fast_container_literal(kt) and allow_fast_container_literal(vt)):
|
4155 |
| - self.resolved_type[e] = NoneType() |
| 4220 | + self.resolved_type[(e, ctx)] = NoneType() |
4156 | 4221 | return None
|
4157 | 4222 | if stargs and (stargs[0] != kt or stargs[1] != vt):
|
4158 |
| - self.resolved_type[e] = NoneType() |
| 4223 | + self.resolved_type[(e, ctx)] = NoneType() |
4159 | 4224 | return None
|
4160 | 4225 | dt = self.chk.named_generic_type("builtins.dict", [kt, vt])
|
4161 |
| - self.resolved_type[e] = dt |
| 4226 | + self.resolved_type[(e, ctx)] = dt |
4162 | 4227 | return dt
|
4163 | 4228 |
|
4164 | 4229 | def visit_dict_expr(self, e: DictExpr) -> Type:
|
|
0 commit comments