Skip to content

Commit 5f13dbf

Browse files
Merge branch 'master' into weak-proxy
2 parents c58f827 + fb41108 commit 5f13dbf

File tree

19 files changed

+439
-101
lines changed

19 files changed

+439
-101
lines changed

mypy/messages.py

Lines changed: 3 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -2432,13 +2432,13 @@ def format_long_tuple_type(self, typ: TupleType) -> str:
24322432
"""Format very long tuple type using an ellipsis notation"""
24332433
item_cnt = len(typ.items)
24342434
if item_cnt > MAX_TUPLE_ITEMS:
2435-
return "tuple[{}, {}, ... <{} more items>]".format(
2435+
return '"tuple[{}, {}, ... <{} more items>]"'.format(
24362436
format_type_bare(typ.items[0], self.options),
24372437
format_type_bare(typ.items[1], self.options),
24382438
str(item_cnt - 2),
24392439
)
24402440
else:
2441-
return format_type_bare(typ, self.options)
2441+
return format_type(typ, self.options)
24422442

24432443
def generate_incompatible_tuple_error(
24442444
self,
@@ -2517,15 +2517,12 @@ def iteration_dependent_errors(self, iter_errors: IterationDependentErrors) -> N
25172517

25182518
def quote_type_string(type_string: str) -> str:
25192519
"""Quotes a type representation for use in messages."""
2520-
no_quote_regex = r"^<(tuple|union): \d+ items>$"
25212520
if (
25222521
type_string in ["Module", "overloaded function", "<deleted>"]
25232522
or type_string.startswith("Module ")
2524-
or re.match(no_quote_regex, type_string) is not None
25252523
or type_string.endswith("?")
25262524
):
2527-
# Messages are easier to read if these aren't quoted. We use a
2528-
# regex to match strings with variable contents.
2525+
# These messages are easier to read if these aren't quoted.
25292526
return type_string
25302527
return f'"{type_string}"'
25312528

mypy/types.py

Lines changed: 20 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -5,18 +5,7 @@
55
import sys
66
from abc import abstractmethod
77
from collections.abc import Iterable, Sequence
8-
from typing import (
9-
TYPE_CHECKING,
10-
Any,
11-
ClassVar,
12-
Final,
13-
NamedTuple,
14-
NewType,
15-
TypeVar,
16-
Union,
17-
cast,
18-
overload,
19-
)
8+
from typing import TYPE_CHECKING, Any, ClassVar, Final, NewType, TypeVar, Union, cast, overload
209
from typing_extensions import Self, TypeAlias as _TypeAlias, TypeGuard
2110

2211
import mypy.nodes
@@ -1607,11 +1596,25 @@ def bound(self) -> bool:
16071596
return bool(self.items) and self.items[0].is_bound
16081597

16091598

1610-
class FormalArgument(NamedTuple):
1611-
name: str | None
1612-
pos: int | None
1613-
typ: Type
1614-
required: bool
1599+
class FormalArgument:
1600+
def __init__(self, name: str | None, pos: int | None, typ: Type, required: bool) -> None:
1601+
self.name = name
1602+
self.pos = pos
1603+
self.typ = typ
1604+
self.required = required
1605+
1606+
def __eq__(self, other: object) -> bool:
1607+
if not isinstance(other, FormalArgument):
1608+
return NotImplemented
1609+
return (
1610+
self.name == other.name
1611+
and self.pos == other.pos
1612+
and self.typ == other.typ
1613+
and self.required == other.required
1614+
)
1615+
1616+
def __hash__(self) -> int:
1617+
return hash((self.name, self.pos, self.typ, self.required))
16151618

16161619

16171620
class Parameters(ProperType):

mypyc/codegen/emitfunc.py

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
from mypyc.codegen.cstring import c_string_initializer
99
from mypyc.codegen.emit import DEBUG_ERRORS, Emitter, TracebackAndGotoHandler, c_array_initializer
1010
from mypyc.common import (
11+
GENERATOR_ATTRIBUTE_PREFIX,
1112
HAVE_IMMORTAL,
1213
MODULE_PREFIX,
1314
NATIVE_PREFIX,
@@ -436,7 +437,9 @@ def visit_get_attr(self, op: GetAttr) -> None:
436437
exc_class = "PyExc_AttributeError"
437438
self.emitter.emit_line(
438439
'PyErr_SetString({}, "attribute {} of {} undefined");'.format(
439-
exc_class, repr(op.attr), repr(cl.name)
440+
exc_class,
441+
repr(op.attr.removeprefix(GENERATOR_ATTRIBUTE_PREFIX)),
442+
repr(cl.name),
440443
)
441444
)
442445

@@ -938,7 +941,7 @@ def emit_attribute_error(self, op: Branch, class_name: str, attr: str) -> None:
938941
self.source_path.replace("\\", "\\\\"),
939942
op.traceback_entry[0],
940943
class_name,
941-
attr,
944+
attr.removeprefix(GENERATOR_ATTRIBUTE_PREFIX),
942945
op.traceback_entry[1],
943946
globals_static,
944947
)

mypyc/common.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,13 +15,15 @@
1515
MODULE_PREFIX: Final = "CPyModule_" # Cached modules
1616
TYPE_VAR_PREFIX: Final = "CPyTypeVar_" # Type variables when using new-style Python 3.12 syntax
1717
ATTR_PREFIX: Final = "_" # Attributes
18+
FAST_PREFIX: Final = "__mypyc_fast_" # Optimized methods in non-extension classes
1819

1920
ENV_ATTR_NAME: Final = "__mypyc_env__"
2021
NEXT_LABEL_ATTR_NAME: Final = "__mypyc_next_label__"
2122
TEMP_ATTR_NAME: Final = "__mypyc_temp__"
2223
LAMBDA_NAME: Final = "__mypyc_lambda__"
2324
PROPSET_PREFIX: Final = "__mypyc_setter__"
2425
SELF_NAME: Final = "__mypyc_self__"
26+
GENERATOR_ATTRIBUTE_PREFIX: Final = "__mypyc_generator_attribute__"
2527

2628
# Max short int we accept as a literal is based on 32-bit platforms,
2729
# so that we can just always emit the same code.

mypyc/ir/class_ir.py

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -210,6 +210,9 @@ def __init__(
210210
# per-type free "list" of up to length 1.
211211
self.reuse_freed_instance = False
212212

213+
# Is this a class inheriting from enum.Enum? Such classes can be special-cased.
214+
self.is_enum = False
215+
213216
def __repr__(self) -> str:
214217
return (
215218
"ClassIR("
@@ -410,6 +413,7 @@ def serialize(self) -> JsonDict:
410413
"init_self_leak": self.init_self_leak,
411414
"env_user_function": self.env_user_function.id if self.env_user_function else None,
412415
"reuse_freed_instance": self.reuse_freed_instance,
416+
"is_enum": self.is_enum,
413417
}
414418

415419
@classmethod
@@ -466,6 +470,7 @@ def deserialize(cls, data: JsonDict, ctx: DeserMaps) -> ClassIR:
466470
ctx.functions[data["env_user_function"]] if data["env_user_function"] else None
467471
)
468472
ir.reuse_freed_instance = data["reuse_freed_instance"]
473+
ir.is_enum = data["is_enum"]
469474

470475
return ir
471476

mypyc/irbuild/builder.py

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -59,7 +59,7 @@
5959
)
6060
from mypy.util import module_prefix, split_target
6161
from mypy.visitor import ExpressionVisitor, StatementVisitor
62-
from mypyc.common import BITMAP_BITS, SELF_NAME, TEMP_ATTR_NAME
62+
from mypyc.common import BITMAP_BITS, GENERATOR_ATTRIBUTE_PREFIX, SELF_NAME, TEMP_ATTR_NAME
6363
from mypyc.crash import catch_errors
6464
from mypyc.errors import Errors
6565
from mypyc.ir.class_ir import ClassIR, NonExtClassInfo
@@ -651,7 +651,11 @@ def get_assignment_target(
651651
# current environment.
652652
if self.fn_info.is_generator:
653653
return self.add_var_to_env_class(
654-
symbol, reg_type, self.fn_info.generator_class, reassign=False
654+
symbol,
655+
reg_type,
656+
self.fn_info.generator_class,
657+
reassign=False,
658+
prefix=GENERATOR_ATTRIBUTE_PREFIX,
655659
)
656660

657661
# Otherwise define a new local variable.
@@ -1333,10 +1337,11 @@ def add_var_to_env_class(
13331337
base: FuncInfo | ImplicitClass,
13341338
reassign: bool = False,
13351339
always_defined: bool = False,
1340+
prefix: str = "",
13361341
) -> AssignmentTarget:
13371342
# First, define the variable name as an attribute of the environment class, and then
13381343
# construct a target for that attribute.
1339-
name = remangle_redefinition_name(var.name)
1344+
name = prefix + remangle_redefinition_name(var.name)
13401345
self.fn_info.env_class.attributes[name] = rtype
13411346
if always_defined:
13421347
self.fn_info.env_class.attrs_with_defaults.add(name)

mypyc/irbuild/env_class.py

Lines changed: 35 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,13 @@ def g() -> int:
1818
from __future__ import annotations
1919

2020
from mypy.nodes import Argument, FuncDef, SymbolNode, Var
21-
from mypyc.common import BITMAP_BITS, ENV_ATTR_NAME, SELF_NAME, bitmap_name
21+
from mypyc.common import (
22+
BITMAP_BITS,
23+
ENV_ATTR_NAME,
24+
GENERATOR_ATTRIBUTE_PREFIX,
25+
SELF_NAME,
26+
bitmap_name,
27+
)
2228
from mypyc.ir.class_ir import ClassIR
2329
from mypyc.ir.ops import Call, GetAttr, SetAttr, Value
2430
from mypyc.ir.rtypes import RInstance, bitmap_rprimitive, object_rprimitive
@@ -60,7 +66,7 @@ class is generated, the function environment has not yet been
6066
return env_class
6167

6268

63-
def finalize_env_class(builder: IRBuilder) -> None:
69+
def finalize_env_class(builder: IRBuilder, prefix: str = "") -> None:
6470
"""Generate, instantiate, and set up the environment of an environment class."""
6571
if not builder.fn_info.can_merge_generator_and_env_classes():
6672
instantiate_env_class(builder)
@@ -69,9 +75,9 @@ def finalize_env_class(builder: IRBuilder) -> None:
6975
# that were previously added to the environment with references to the function's
7076
# environment class.
7177
if builder.fn_info.is_nested:
72-
add_args_to_env(builder, local=False, base=builder.fn_info.callable_class)
78+
add_args_to_env(builder, local=False, base=builder.fn_info.callable_class, prefix=prefix)
7379
else:
74-
add_args_to_env(builder, local=False, base=builder.fn_info)
80+
add_args_to_env(builder, local=False, base=builder.fn_info, prefix=prefix)
7581

7682

7783
def instantiate_env_class(builder: IRBuilder) -> Value:
@@ -96,15 +102,15 @@ def instantiate_env_class(builder: IRBuilder) -> Value:
96102
return curr_env_reg
97103

98104

99-
def load_env_registers(builder: IRBuilder) -> None:
105+
def load_env_registers(builder: IRBuilder, prefix: str = "") -> None:
100106
"""Load the registers for the current FuncItem being visited.
101107
102108
Adds the arguments of the FuncItem to the environment. If the
103109
FuncItem is nested inside of another function, then this also
104110
loads all of the outer environments of the FuncItem into registers
105111
so that they can be used when accessing free variables.
106112
"""
107-
add_args_to_env(builder, local=True)
113+
add_args_to_env(builder, local=True, prefix=prefix)
108114

109115
fn_info = builder.fn_info
110116
fitem = fn_info.fitem
@@ -113,7 +119,7 @@ def load_env_registers(builder: IRBuilder) -> None:
113119
# If this is a FuncDef, then make sure to load the FuncDef into its own environment
114120
# class so that the function can be called recursively.
115121
if isinstance(fitem, FuncDef) and fn_info.add_nested_funcs_to_env:
116-
setup_func_for_recursive_call(builder, fitem, fn_info.callable_class)
122+
setup_func_for_recursive_call(builder, fitem, fn_info.callable_class, prefix=prefix)
117123

118124

119125
def load_outer_env(
@@ -134,8 +140,11 @@ def load_outer_env(
134140
assert isinstance(env.type, RInstance), f"{env} must be of type RInstance"
135141

136142
for symbol, target in outer_env.items():
137-
env.type.class_ir.attributes[symbol.name] = target.type
138-
symbol_target = AssignmentTargetAttr(env, symbol.name)
143+
attr_name = symbol.name
144+
if isinstance(target, AssignmentTargetAttr):
145+
attr_name = target.attr
146+
env.type.class_ir.attributes[attr_name] = target.type
147+
symbol_target = AssignmentTargetAttr(env, attr_name)
139148
builder.add_target(symbol, symbol_target)
140149

141150
return env
@@ -178,6 +187,7 @@ def add_args_to_env(
178187
local: bool = True,
179188
base: FuncInfo | ImplicitClass | None = None,
180189
reassign: bool = True,
190+
prefix: str = "",
181191
) -> None:
182192
fn_info = builder.fn_info
183193
args = fn_info.fitem.arguments
@@ -193,10 +203,12 @@ def add_args_to_env(
193203
if is_free_variable(builder, arg.variable) or fn_info.is_generator:
194204
rtype = builder.type_to_rtype(arg.variable.type)
195205
assert base is not None, "base cannot be None for adding nonlocal args"
196-
builder.add_var_to_env_class(arg.variable, rtype, base, reassign=reassign)
206+
builder.add_var_to_env_class(
207+
arg.variable, rtype, base, reassign=reassign, prefix=prefix
208+
)
197209

198210

199-
def add_vars_to_env(builder: IRBuilder) -> None:
211+
def add_vars_to_env(builder: IRBuilder, prefix: str = "") -> None:
200212
"""Add relevant local variables and nested functions to the environment class.
201213
202214
Add all variables and functions that are declared/defined within current
@@ -216,7 +228,9 @@ def add_vars_to_env(builder: IRBuilder) -> None:
216228
for var in sorted(builder.free_variables[builder.fn_info.fitem], key=lambda x: x.name):
217229
if isinstance(var, Var):
218230
rtype = builder.type_to_rtype(var.type)
219-
builder.add_var_to_env_class(var, rtype, env_for_func, reassign=False)
231+
builder.add_var_to_env_class(
232+
var, rtype, env_for_func, reassign=False, prefix=prefix
233+
)
220234

221235
if builder.fn_info.fitem in builder.encapsulating_funcs:
222236
for nested_fn in builder.encapsulating_funcs[builder.fn_info.fitem]:
@@ -226,12 +240,16 @@ def add_vars_to_env(builder: IRBuilder) -> None:
226240
# the same name and signature across conditional blocks
227241
# will generate different callable classes, so the callable
228242
# class that gets instantiated must be generic.
243+
if nested_fn.is_generator:
244+
prefix = GENERATOR_ATTRIBUTE_PREFIX
229245
builder.add_var_to_env_class(
230-
nested_fn, object_rprimitive, env_for_func, reassign=False
246+
nested_fn, object_rprimitive, env_for_func, reassign=False, prefix=prefix
231247
)
232248

233249

234-
def setup_func_for_recursive_call(builder: IRBuilder, fdef: FuncDef, base: ImplicitClass) -> None:
250+
def setup_func_for_recursive_call(
251+
builder: IRBuilder, fdef: FuncDef, base: ImplicitClass, prefix: str = ""
252+
) -> None:
235253
"""Enable calling a nested function (with a callable class) recursively.
236254
237255
Adds the instance of the callable class representing the given
@@ -241,7 +259,8 @@ def setup_func_for_recursive_call(builder: IRBuilder, fdef: FuncDef, base: Impli
241259
"""
242260
# First, set the attribute of the environment class so that GetAttr can be called on it.
243261
prev_env = builder.fn_infos[-2].env_class
244-
prev_env.attributes[fdef.name] = builder.type_to_rtype(fdef.type)
262+
attr_name = prefix + fdef.name
263+
prev_env.attributes[attr_name] = builder.type_to_rtype(fdef.type)
245264

246265
if isinstance(base, GeneratorClass):
247266
# If we are dealing with a generator class, then we need to first get the register
@@ -253,7 +272,7 @@ def setup_func_for_recursive_call(builder: IRBuilder, fdef: FuncDef, base: Impli
253272

254273
# Obtain the instance of the callable class representing the FuncDef, and add it to the
255274
# current environment.
256-
val = builder.add(GetAttr(prev_env_reg, fdef.name, -1))
275+
val = builder.add(GetAttr(prev_env_reg, attr_name, -1))
257276
target = builder.add_local_reg(fdef, object_rprimitive)
258277
builder.assign(target, val, -1)
259278

mypyc/irbuild/function.py

Lines changed: 15 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@
2929
Var,
3030
)
3131
from mypy.types import CallableType, Type, UnboundType, get_proper_type
32-
from mypyc.common import LAMBDA_NAME, PROPSET_PREFIX, SELF_NAME
32+
from mypyc.common import FAST_PREFIX, LAMBDA_NAME, PROPSET_PREFIX, SELF_NAME
3333
from mypyc.ir.class_ir import ClassIR, NonExtClassInfo
3434
from mypyc.ir.func_ir import (
3535
FUNC_CLASSMETHOD,
@@ -166,6 +166,7 @@ def gen_func_item(
166166
name: str,
167167
sig: FuncSignature,
168168
cdef: ClassDef | None = None,
169+
make_ext_method: bool = False,
169170
) -> tuple[FuncIR, Value | None]:
170171
"""Generate and return the FuncIR for a given FuncDef.
171172
@@ -217,7 +218,7 @@ def c() -> None:
217218
class_name = None
218219
if cdef:
219220
ir = builder.mapper.type_to_ir[cdef.info]
220-
in_non_ext = not ir.is_ext_class
221+
in_non_ext = not ir.is_ext_class and not make_ext_method
221222
class_name = cdef.name
222223

223224
if is_singledispatch:
@@ -339,6 +340,9 @@ def gen_func_ir(
339340
fitem = fn_info.fitem
340341
assert isinstance(fitem, FuncDef), fitem
341342
func_decl = builder.mapper.func_to_decl[fitem]
343+
if cdef and fn_info.name == FAST_PREFIX + func_decl.name:
344+
# Special-cased version of a method has a separate FuncDecl, use that one.
345+
func_decl = builder.mapper.type_to_ir[cdef.info].method_decls[fn_info.name]
342346
if fn_info.is_decorated or is_singledispatch_main_func:
343347
class_name = None if cdef is None else cdef.name
344348
func_decl = FuncDecl(
@@ -453,6 +457,15 @@ def handle_non_ext_method(
453457

454458
builder.add_to_non_ext_dict(non_ext, name, func_reg, fdef.line)
455459

460+
# If we identified that this non-extension class method can be special-cased for
461+
# direct access during prepare phase, generate a "static" version of it.
462+
class_ir = builder.mapper.type_to_ir[cdef.info]
463+
name = FAST_PREFIX + fdef.name
464+
if name in class_ir.method_decls:
465+
func_ir, func_reg = gen_func_item(builder, fdef, name, sig, cdef, make_ext_method=True)
466+
class_ir.methods[name] = func_ir
467+
builder.functions.append(func_ir)
468+
456469

457470
def gen_func_ns(builder: IRBuilder) -> str:
458471
"""Generate a namespace for a nested function using its outer function names."""

0 commit comments

Comments
 (0)