Skip to content

Commit 5e09390

Browse files
Merge branch 'master' into patch-3
2 parents 5300f51 + 1091321 commit 5e09390

File tree

12 files changed

+280
-24
lines changed

12 files changed

+280
-24
lines changed

.github/workflows/mypy_primer.yml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ jobs:
2828
runs-on: ubuntu-latest
2929
strategy:
3030
matrix:
31-
shard-index: [0, 1, 2, 3, 4]
31+
shard-index: [0, 1, 2, 3, 4, 5]
3232
fail-fast: false
3333
timeout-minutes: 60
3434
steps:
@@ -63,7 +63,7 @@ jobs:
6363
mypy_primer \
6464
--repo mypy_to_test \
6565
--new $GITHUB_SHA --old base_commit \
66-
--num-shards 5 --shard-index ${{ matrix.shard-index }} \
66+
--num-shards 6 --shard-index ${{ matrix.shard-index }} \
6767
--debug \
6868
--additional-flags="--debug-serialize" \
6969
--output concise \

mypy/checker.py

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3138,7 +3138,7 @@ def check_assignment(
31383138
else:
31393139
self.check_getattr_method(signature, lvalue, name)
31403140

3141-
if name == "__slots__":
3141+
if name == "__slots__" and self.scope.active_class() is not None:
31423142
typ = lvalue_type or self.expr_checker.accept(rvalue)
31433143
self.check_slots_definition(typ, lvalue)
31443144
if name == "__match_args__" and inferred is not None:
@@ -3317,6 +3317,12 @@ def get_variable_type_context(self, inferred: Var, rvalue: Expression) -> Type |
33173317
type_contexts.append(base_type)
33183318
# Use most derived supertype as type context if available.
33193319
if not type_contexts:
3320+
if inferred.name == "__slots__" and self.scope.active_class() is not None:
3321+
str_type = self.named_type("builtins.str")
3322+
return self.named_generic_type("typing.Iterable", [str_type])
3323+
if inferred.name == "__all__" and self.scope.is_top_level():
3324+
str_type = self.named_type("builtins.str")
3325+
return self.named_generic_type("typing.Sequence", [str_type])
33203326
return None
33213327
candidate = type_contexts[0]
33223328
for other in type_contexts:

mypy/checker_shared.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -334,6 +334,10 @@ def current_self_type(self) -> Instance | TupleType | None:
334334
return fill_typevars(item)
335335
return None
336336

337+
def is_top_level(self) -> bool:
338+
"""Is current scope top-level (no classes or functions)?"""
339+
return len(self.stack) == 1
340+
337341
@contextmanager
338342
def push_function(self, item: FuncItem) -> Iterator[None]:
339343
self.stack.append(item)

mypy/plugins/enums.py

Lines changed: 21 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,10 +17,12 @@
1717
from typing import TypeVar, cast
1818

1919
import mypy.plugin # To avoid circular imports.
20-
from mypy.nodes import TypeInfo
20+
from mypy.checker import TypeChecker
21+
from mypy.nodes import TypeInfo, Var
2122
from mypy.subtypes import is_equivalent
2223
from mypy.typeops import fixup_partial_type, make_simplified_union
2324
from mypy.types import (
25+
ELLIPSIS_TYPE_NAMES,
2426
CallableType,
2527
Instance,
2628
LiteralType,
@@ -79,6 +81,19 @@ def _infer_value_type_with_auto_fallback(
7981
if proper_type is None:
8082
return None
8183
proper_type = get_proper_type(fixup_partial_type(proper_type))
84+
# Enums in stubs may have ... instead of actual values. If `_value_` is annotated
85+
# (manually or inherited from IntEnum, for example), it is a more reasonable guess
86+
# than literal ellipsis type.
87+
if (
88+
_is_defined_in_stub(ctx)
89+
and isinstance(proper_type, Instance)
90+
and proper_type.type.fullname in ELLIPSIS_TYPE_NAMES
91+
and isinstance(ctx.type, Instance)
92+
):
93+
value_type = ctx.type.type.get("_value_")
94+
if value_type is not None and isinstance(var := value_type.node, Var):
95+
return var.type
96+
return proper_type
8297
if not (isinstance(proper_type, Instance) and proper_type.type.fullname == "enum.auto"):
8398
if is_named_instance(proper_type, "enum.member") and proper_type.args:
8499
return proper_type.args[0]
@@ -106,6 +121,11 @@ def _infer_value_type_with_auto_fallback(
106121
return ctx.default_attr_type
107122

108123

124+
def _is_defined_in_stub(ctx: mypy.plugin.AttributeContext) -> bool:
125+
assert isinstance(ctx.api, TypeChecker)
126+
return isinstance(ctx.type, Instance) and ctx.api.modules[ctx.type.type.module_name].is_stub
127+
128+
109129
def _implements_new(info: TypeInfo) -> bool:
110130
"""Check whether __new__ comes from enum.Enum or was implemented in a
111131
subclass. In the latter case, we must infer Any as long as mypy can't infer

mypy/stubtest.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1149,7 +1149,7 @@ def verify_var(
11491149
proper_type = mypy.types.get_proper_type(stub.type)
11501150
if (
11511151
isinstance(proper_type, mypy.types.Instance)
1152-
and proper_type.type.fullname == "builtins.ellipsis"
1152+
and proper_type.type.fullname in mypy.types.ELLIPSIS_TYPE_NAMES
11531153
):
11541154
should_error = False
11551155

mypy/types.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -181,6 +181,8 @@
181181
# Supported @override decorator names.
182182
OVERRIDE_DECORATOR_NAMES: Final = ("typing.override", "typing_extensions.override")
183183

184+
ELLIPSIS_TYPE_NAMES: Final = ("builtins.ellipsis", "types.EllipsisType")
185+
184186
# A placeholder used for Bogus[...] parameters
185187
_dummy: Final[Any] = object()
186188

@@ -1574,7 +1576,7 @@ def is_singleton_type(self) -> bool:
15741576
return (
15751577
self.type.is_enum
15761578
and len(self.type.enum_members) == 1
1577-
or self.type.fullname in {"builtins.ellipsis", "types.EllipsisType"}
1579+
or self.type.fullname in ELLIPSIS_TYPE_NAMES
15781580
)
15791581

15801582

mypyc/irbuild/for_helpers.py

Lines changed: 74 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -24,19 +24,23 @@
2424
TypeAlias,
2525
)
2626
from mypyc.ir.ops import (
27+
ERR_NEVER,
2728
BasicBlock,
2829
Branch,
2930
Integer,
3031
IntOp,
3132
LoadAddress,
33+
LoadErrorValue,
3234
LoadMem,
35+
MethodCall,
3336
RaiseStandardError,
3437
Register,
3538
TupleGet,
3639
TupleSet,
3740
Value,
3841
)
3942
from mypyc.ir.rtypes import (
43+
RInstance,
4044
RTuple,
4145
RType,
4246
bool_rprimitive,
@@ -48,10 +52,13 @@
4852
is_short_int_rprimitive,
4953
is_str_rprimitive,
5054
is_tuple_rprimitive,
55+
object_pointer_rprimitive,
56+
object_rprimitive,
5157
pointer_rprimitive,
5258
short_int_rprimitive,
5359
)
5460
from mypyc.irbuild.builder import IRBuilder
61+
from mypyc.irbuild.prepare import GENERATOR_HELPER_NAME
5562
from mypyc.irbuild.targets import AssignmentTarget, AssignmentTargetTuple
5663
from mypyc.primitives.dict_ops import (
5764
dict_check_size_op,
@@ -62,7 +69,7 @@
6269
dict_next_value_op,
6370
dict_value_iter_op,
6471
)
65-
from mypyc.primitives.exc_ops import no_err_occurred_op
72+
from mypyc.primitives.exc_ops import no_err_occurred_op, propagate_if_error_op
6673
from mypyc.primitives.generic_ops import aiter_op, anext_op, iter_op, next_op
6774
from mypyc.primitives.list_ops import list_append_op, list_get_item_unsafe_op, new_list_set_item_op
6875
from mypyc.primitives.misc_ops import stop_async_iteration_op
@@ -511,7 +518,15 @@ def make_for_loop_generator(
511518
# Default to a generic for loop.
512519
if iterable_expr_reg is None:
513520
iterable_expr_reg = builder.accept(expr)
514-
for_obj = ForIterable(builder, index, body_block, loop_exit, line, nested)
521+
522+
it = iterable_expr_reg.type
523+
for_obj: ForNativeGenerator | ForIterable
524+
if isinstance(it, RInstance) and it.class_ir.has_method(GENERATOR_HELPER_NAME):
525+
# Directly call generator object methods if iterating over a native generator.
526+
for_obj = ForNativeGenerator(builder, index, body_block, loop_exit, line, nested)
527+
else:
528+
# Generic implementation that works of arbitrary iterables.
529+
for_obj = ForIterable(builder, index, body_block, loop_exit, line, nested)
515530
item_type = builder._analyze_iterable_item_type(expr)
516531
item_rtype = builder.type_to_rtype(item_type)
517532
for_obj.init(iterable_expr_reg, item_rtype)
@@ -623,6 +638,63 @@ def gen_cleanup(self) -> None:
623638
self.builder.call_c(no_err_occurred_op, [], self.line)
624639

625640

641+
class ForNativeGenerator(ForGenerator):
642+
"""Generate IR for a for loop over a native generator."""
643+
644+
def need_cleanup(self) -> bool:
645+
# Create a new cleanup block for when the loop is finished.
646+
return True
647+
648+
def init(self, expr_reg: Value, target_type: RType) -> None:
649+
# Define target to contains the generator expression. It's also the iterator.
650+
# If we are inside a generator function, spill these into the environment class.
651+
builder = self.builder
652+
self.iter_target = builder.maybe_spill(expr_reg)
653+
self.target_type = target_type
654+
655+
def gen_condition(self) -> None:
656+
builder = self.builder
657+
line = self.line
658+
self.return_value = Register(object_rprimitive)
659+
err = builder.add(LoadErrorValue(object_rprimitive, undefines=True))
660+
builder.assign(self.return_value, err, line)
661+
662+
# Call generated generator helper method, passing a PyObject ** as the final
663+
# argument that will be used to store the return value in the return value
664+
# register. We ignore the return value but the presence of a return value
665+
# indicates that the generator has finished. This is faster than raising
666+
# and catching StopIteration, which is the non-native way of doing this.
667+
ptr = builder.add(LoadAddress(object_pointer_rprimitive, self.return_value))
668+
nn = builder.none_object()
669+
helper_call = MethodCall(
670+
builder.read(self.iter_target), GENERATOR_HELPER_NAME, [nn, nn, nn, nn, ptr], line
671+
)
672+
# We provide custom handling for error values.
673+
helper_call.error_kind = ERR_NEVER
674+
675+
self.next_reg = builder.add(helper_call)
676+
builder.add(Branch(self.next_reg, self.loop_exit, self.body_block, Branch.IS_ERROR))
677+
678+
def begin_body(self) -> None:
679+
# Assign the value obtained from the generator helper method to the
680+
# lvalue so that it can be referenced by code in the body of the loop.
681+
builder = self.builder
682+
line = self.line
683+
# We unbox here so that iterating with tuple unpacking generates a tuple based
684+
# unpack instead of an iterator based one.
685+
next_reg = builder.coerce(self.next_reg, self.target_type, line)
686+
builder.assign(builder.get_assignment_target(self.index), next_reg, line)
687+
688+
def gen_step(self) -> None:
689+
# Nothing to do here, since we get the next item as part of gen_condition().
690+
pass
691+
692+
def gen_cleanup(self) -> None:
693+
# If return value is NULL (it wasn't assigned to by the generator helper method),
694+
# an exception was raised that we need to propagate.
695+
self.builder.primitive_op(propagate_if_error_op, [self.return_value], self.line)
696+
697+
626698
class ForAsyncIterable(ForGenerator):
627699
"""Generate IR for an async for loop."""
628700

mypyc/irbuild/prepare.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@
1515

1616
from collections import defaultdict
1717
from collections.abc import Iterable
18-
from typing import NamedTuple
18+
from typing import Final, NamedTuple
1919

2020
from mypy.build import Graph
2121
from mypy.nodes import (
@@ -71,7 +71,7 @@
7171
from mypyc.options import CompilerOptions
7272
from mypyc.sametype import is_same_type
7373

74-
GENERATOR_HELPER_NAME = "__mypyc_generator_helper__"
74+
GENERATOR_HELPER_NAME: Final = "__mypyc_generator_helper__"
7575

7676

7777
def build_type_map(

0 commit comments

Comments
 (0)