Skip to content

Commit 628a0be

Browse files
Merge branch 'master' into special_case_typeddict_get
2 parents 16c97ba + 354bea6 commit 628a0be

File tree

9 files changed

+652
-37
lines changed

9 files changed

+652
-37
lines changed

mypy/build.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3488,7 +3488,7 @@ def process_stale_scc(graph: Graph, scc: list[str], manager: BuildManager) -> No
34883488

34893489

34903490
def sorted_components(
3491-
graph: Graph, vertices: AbstractSet[str] | None = None, pri_max: int = PRI_ALL
3491+
graph: Graph, vertices: AbstractSet[str] | None = None, pri_max: int = PRI_INDIRECT
34923492
) -> list[AbstractSet[str]]:
34933493
"""Return the graph's SCCs, topologically sorted by dependencies.
34943494

mypy/semanal.py

Lines changed: 10 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -5928,8 +5928,7 @@ def visit_member_expr(self, expr: MemberExpr) -> None:
59285928
if isinstance(sym.node, PlaceholderNode):
59295929
self.process_placeholder(expr.name, "attribute", expr)
59305930
return
5931-
if sym.node is not None:
5932-
self.record_imported_symbol(sym.node)
5931+
self.record_imported_symbol(sym)
59335932
expr.kind = sym.kind
59345933
expr.fullname = sym.fullname or ""
59355934
expr.node = sym.node
@@ -5960,7 +5959,7 @@ def visit_member_expr(self, expr: MemberExpr) -> None:
59605959
if type_info:
59615960
n = type_info.names.get(expr.name)
59625961
if n is not None and isinstance(n.node, (MypyFile, TypeInfo, TypeAlias)):
5963-
self.record_imported_symbol(n.node)
5962+
self.record_imported_symbol(n)
59645963
expr.kind = n.kind
59655964
expr.fullname = n.fullname or ""
59665965
expr.node = n.node
@@ -6282,14 +6281,17 @@ def lookup(
62826281
self, name: str, ctx: Context, suppress_errors: bool = False
62836282
) -> SymbolTableNode | None:
62846283
node = self._lookup(name, ctx, suppress_errors)
6285-
if node is not None and node.node is not None:
6284+
if node is not None:
62866285
# This call is unfortunate from performance point of view, but
62876286
# needed for rare cases like e.g. testIncrementalChangingAlias.
6288-
self.record_imported_symbol(node.node)
6287+
self.record_imported_symbol(node)
62896288
return node
62906289

6291-
def record_imported_symbol(self, node: SymbolNode) -> None:
6290+
def record_imported_symbol(self, sym: SymbolTableNode) -> None:
62926291
"""If the symbol was not defined in current module, add its module to module_refs."""
6292+
if sym.kind == LDEF or sym.node is None:
6293+
return
6294+
node = sym.node
62936295
if not node.fullname:
62946296
return
62956297
if isinstance(node, MypyFile):
@@ -6519,8 +6521,8 @@ def lookup_qualified(
65196521
self.name_not_defined(name, ctx, namespace=namespace)
65206522
return None
65216523
sym = nextsym
6522-
if sym is not None and sym.node is not None:
6523-
self.record_imported_symbol(sym.node)
6524+
if sym is not None:
6525+
self.record_imported_symbol(sym)
65246526
return sym
65256527

65266528
def lookup_type_node(self, expr: Expression) -> SymbolTableNode | None:

mypyc/irbuild/builder.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1437,6 +1437,10 @@ def add_function(self, func_ir: FuncIR, line: int) -> None:
14371437
self.function_names.add(name)
14381438
self.functions.append(func_ir)
14391439

1440+
def get_current_class_ir(self) -> ClassIR | None:
1441+
type_info = self.fn_info.fitem.info
1442+
return self.mapper.type_to_ir.get(type_info)
1443+
14401444

14411445
def gen_arg_defaults(builder: IRBuilder) -> None:
14421446
"""Generate blocks for arguments that have default values.

mypyc/irbuild/expression.py

Lines changed: 9 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -57,7 +57,6 @@
5757
from mypyc.ir.ops import (
5858
Assign,
5959
BasicBlock,
60-
Call,
6160
ComparisonOp,
6261
Integer,
6362
LoadAddress,
@@ -98,7 +97,11 @@
9897
join_formatted_strings,
9998
tokenizer_printf_style,
10099
)
101-
from mypyc.irbuild.specialize import apply_function_specialization, apply_method_specialization
100+
from mypyc.irbuild.specialize import (
101+
apply_function_specialization,
102+
apply_method_specialization,
103+
translate_object_new,
104+
)
102105
from mypyc.primitives.bytes_ops import bytes_slice_op
103106
from mypyc.primitives.dict_ops import dict_get_item_op, dict_new_op, exact_dict_set_item_op
104107
from mypyc.primitives.generic_ops import iter_op, name_op
@@ -473,35 +476,15 @@ def translate_super_method_call(builder: IRBuilder, expr: CallExpr, callee: Supe
473476
if callee.name in base.method_decls:
474477
break
475478
else:
479+
if callee.name == "__new__":
480+
result = translate_object_new(builder, expr, MemberExpr(callee.call, "__new__"))
481+
if result:
482+
return result
476483
if ir.is_ext_class and ir.builtin_base is None and not ir.inherits_python:
477484
if callee.name == "__init__" and len(expr.args) == 0:
478485
# Call translates to object.__init__(self), which is a
479486
# no-op, so omit the call.
480487
return builder.none()
481-
elif callee.name == "__new__":
482-
# object.__new__(cls)
483-
assert (
484-
len(expr.args) == 1
485-
), f"Expected object.__new__() call to have exactly 1 argument, got {len(expr.args)}"
486-
typ_arg = expr.args[0]
487-
method_args = builder.fn_info.fitem.arg_names
488-
if (
489-
isinstance(typ_arg, NameExpr)
490-
and len(method_args) > 0
491-
and method_args[0] == typ_arg.name
492-
):
493-
subtype = builder.accept(expr.args[0])
494-
return builder.add(Call(ir.setup, [subtype], expr.line))
495-
496-
if callee.name == "__new__":
497-
call = "super().__new__()"
498-
if not ir.is_ext_class:
499-
builder.error(f"{call} not supported for non-extension classes", expr.line)
500-
if ir.inherits_python:
501-
builder.error(
502-
f"{call} not supported for classes inheriting from non-native classes",
503-
expr.line,
504-
)
505488
return translate_call(builder, expr, callee)
506489

507490
decl = base.method_decl(callee.name)

mypyc/irbuild/specialize.py

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,12 +30,14 @@
3030
NameExpr,
3131
RefExpr,
3232
StrExpr,
33+
SuperExpr,
3334
TupleExpr,
3435
Var,
3536
)
3637
from mypy.types import AnyType, TypeOfAny
3738
from mypyc.ir.ops import (
3839
BasicBlock,
40+
Call,
3941
Extend,
4042
Integer,
4143
RaiseStandardError,
@@ -68,6 +70,7 @@
6870
is_list_rprimitive,
6971
is_uint8_rprimitive,
7072
list_rprimitive,
73+
object_rprimitive,
7174
set_rprimitive,
7275
str_rprimitive,
7376
uint8_rprimitive,
@@ -1002,3 +1005,44 @@ def translate_ord(builder: IRBuilder, expr: CallExpr, callee: RefExpr) -> Value
10021005
if isinstance(arg, (StrExpr, BytesExpr)) and len(arg.value) == 1:
10031006
return Integer(ord(arg.value))
10041007
return None
1008+
1009+
1010+
@specialize_function("__new__", object_rprimitive)
1011+
def translate_object_new(builder: IRBuilder, expr: CallExpr, callee: RefExpr) -> Value | None:
1012+
fn = builder.fn_info
1013+
if fn.name != "__new__":
1014+
return None
1015+
1016+
is_super_new = isinstance(expr.callee, SuperExpr)
1017+
is_object_new = (
1018+
isinstance(callee, MemberExpr)
1019+
and isinstance(callee.expr, NameExpr)
1020+
and callee.expr.fullname == "builtins.object"
1021+
)
1022+
if not (is_super_new or is_object_new):
1023+
return None
1024+
1025+
ir = builder.get_current_class_ir()
1026+
if ir is None:
1027+
return None
1028+
1029+
call = '"object.__new__()"'
1030+
if not ir.is_ext_class:
1031+
builder.error(f"{call} not supported for non-extension classes", expr.line)
1032+
return None
1033+
if ir.inherits_python:
1034+
builder.error(
1035+
f"{call} not supported for classes inheriting from non-native classes", expr.line
1036+
)
1037+
return None
1038+
if len(expr.args) != 1:
1039+
builder.error(f"{call} supported only with 1 argument, got {len(expr.args)}", expr.line)
1040+
return None
1041+
1042+
typ_arg = expr.args[0]
1043+
method_args = fn.fitem.arg_names
1044+
if isinstance(typ_arg, NameExpr) and len(method_args) > 0 and method_args[0] == typ_arg.name:
1045+
subtype = builder.accept(expr.args[0])
1046+
return builder.add(Call(ir.setup, [subtype], expr.line))
1047+
1048+
return None

mypyc/irbuild/statement.py

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@
3333
ListExpr,
3434
Lvalue,
3535
MatchStmt,
36+
NameExpr,
3637
OperatorAssignmentStmt,
3738
RaiseStmt,
3839
ReturnStmt,
@@ -170,10 +171,43 @@ def transform_return_stmt(builder: IRBuilder, stmt: ReturnStmt) -> None:
170171
builder.nonlocal_control[-1].gen_return(builder, retval, stmt.line)
171172

172173

174+
def check_unsupported_cls_assignment(builder: IRBuilder, stmt: AssignmentStmt) -> None:
175+
fn = builder.fn_info
176+
method_args = fn.fitem.arg_names
177+
if fn.name != "__new__" or len(method_args) == 0:
178+
return
179+
180+
ir = builder.get_current_class_ir()
181+
if ir is None or ir.inherits_python or not ir.is_ext_class:
182+
return
183+
184+
cls_arg = method_args[0]
185+
186+
def flatten(lvalues: list[Expression]) -> list[Expression]:
187+
flat = []
188+
for lvalue in lvalues:
189+
if isinstance(lvalue, (TupleExpr, ListExpr)):
190+
flat += flatten(lvalue.items)
191+
else:
192+
flat.append(lvalue)
193+
return flat
194+
195+
lvalues = flatten(stmt.lvalues)
196+
197+
for lvalue in lvalues:
198+
if isinstance(lvalue, NameExpr) and lvalue.name == cls_arg:
199+
# Disallowed because it could break the transformation of object.__new__ calls
200+
# inside __new__ methods.
201+
builder.error(
202+
f'Assignment to argument "{cls_arg}" in "__new__" method unsupported', stmt.line
203+
)
204+
205+
173206
def transform_assignment_stmt(builder: IRBuilder, stmt: AssignmentStmt) -> None:
174207
lvalues = stmt.lvalues
175208
assert lvalues
176209
builder.disallow_class_assignments(lvalues, stmt.line)
210+
check_unsupported_cls_assignment(builder, stmt)
177211
first_lvalue = lvalues[0]
178212
if stmt.type and isinstance(stmt.rvalue, TempNode):
179213
# This is actually a variable annotation without initializer. Don't generate

0 commit comments

Comments
 (0)