cinderx 2026.1.16.2__cp314-cp314-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (68) hide show
  1. __static__/__init__.py +641 -0
  2. __static__/compiler_flags.py +8 -0
  3. __static__/enum.py +160 -0
  4. __static__/native_utils.py +77 -0
  5. __static__/type_code.py +48 -0
  6. __strict__/__init__.py +39 -0
  7. _cinderx.so +0 -0
  8. cinderx/__init__.py +577 -0
  9. cinderx/__pycache__/__init__.cpython-314.pyc +0 -0
  10. cinderx/_asyncio.py +156 -0
  11. cinderx/compileall.py +710 -0
  12. cinderx/compiler/__init__.py +40 -0
  13. cinderx/compiler/__main__.py +137 -0
  14. cinderx/compiler/config.py +7 -0
  15. cinderx/compiler/consts.py +72 -0
  16. cinderx/compiler/debug.py +70 -0
  17. cinderx/compiler/dis_stable.py +283 -0
  18. cinderx/compiler/errors.py +151 -0
  19. cinderx/compiler/flow_graph_optimizer.py +1287 -0
  20. cinderx/compiler/future.py +91 -0
  21. cinderx/compiler/misc.py +32 -0
  22. cinderx/compiler/opcode_cinder.py +18 -0
  23. cinderx/compiler/opcode_static.py +100 -0
  24. cinderx/compiler/opcodebase.py +158 -0
  25. cinderx/compiler/opcodes.py +991 -0
  26. cinderx/compiler/optimizer.py +547 -0
  27. cinderx/compiler/pyassem.py +3711 -0
  28. cinderx/compiler/pycodegen.py +7660 -0
  29. cinderx/compiler/pysourceloader.py +62 -0
  30. cinderx/compiler/static/__init__.py +1404 -0
  31. cinderx/compiler/static/compiler.py +629 -0
  32. cinderx/compiler/static/declaration_visitor.py +335 -0
  33. cinderx/compiler/static/definite_assignment_checker.py +280 -0
  34. cinderx/compiler/static/effects.py +160 -0
  35. cinderx/compiler/static/module_table.py +666 -0
  36. cinderx/compiler/static/type_binder.py +2176 -0
  37. cinderx/compiler/static/types.py +10580 -0
  38. cinderx/compiler/static/util.py +81 -0
  39. cinderx/compiler/static/visitor.py +91 -0
  40. cinderx/compiler/strict/__init__.py +69 -0
  41. cinderx/compiler/strict/class_conflict_checker.py +249 -0
  42. cinderx/compiler/strict/code_gen_base.py +409 -0
  43. cinderx/compiler/strict/common.py +507 -0
  44. cinderx/compiler/strict/compiler.py +352 -0
  45. cinderx/compiler/strict/feature_extractor.py +130 -0
  46. cinderx/compiler/strict/flag_extractor.py +97 -0
  47. cinderx/compiler/strict/loader.py +827 -0
  48. cinderx/compiler/strict/preprocessor.py +11 -0
  49. cinderx/compiler/strict/rewriter/__init__.py +5 -0
  50. cinderx/compiler/strict/rewriter/remove_annotations.py +84 -0
  51. cinderx/compiler/strict/rewriter/rewriter.py +975 -0
  52. cinderx/compiler/strict/runtime.py +77 -0
  53. cinderx/compiler/symbols.py +1754 -0
  54. cinderx/compiler/unparse.py +414 -0
  55. cinderx/compiler/visitor.py +194 -0
  56. cinderx/jit.py +230 -0
  57. cinderx/opcode.py +202 -0
  58. cinderx/static.py +113 -0
  59. cinderx/strictmodule.py +6 -0
  60. cinderx/test_support.py +341 -0
  61. cinderx-2026.1.16.2.dist-info/METADATA +15 -0
  62. cinderx-2026.1.16.2.dist-info/RECORD +68 -0
  63. cinderx-2026.1.16.2.dist-info/WHEEL +6 -0
  64. cinderx-2026.1.16.2.dist-info/licenses/LICENSE +21 -0
  65. cinderx-2026.1.16.2.dist-info/top_level.txt +5 -0
  66. opcodes/__init__.py +0 -0
  67. opcodes/assign_opcode_numbers.py +272 -0
  68. opcodes/cinderx_opcodes.py +121 -0
@@ -0,0 +1,1404 @@
1
+ # Copyright (c) Meta Platforms, Inc. and affiliates.
2
+
3
+ # pyre-strict
4
+
5
+ from __future__ import annotations
6
+
7
+ import ast
8
+ import builtins
9
+ import sys
10
+ from ast import (
11
+ Assign,
12
+ AST,
13
+ AsyncFunctionDef,
14
+ Attribute,
15
+ BinOp,
16
+ BoolOp,
17
+ Call,
18
+ ClassDef,
19
+ cmpop,
20
+ Compare,
21
+ Constant,
22
+ DictComp,
23
+ expr,
24
+ FunctionDef,
25
+ Module,
26
+ Name,
27
+ UnaryOp,
28
+ )
29
+ from collections.abc import Callable as typingCallable, Generator
30
+ from contextlib import contextmanager
31
+ from types import CodeType
32
+ from typing import Any, cast
33
+
34
+ from .. import consts, opcode_static
35
+ from ..opcodebase import Opcode
36
+ from ..pyassem import (
37
+ Block,
38
+ IndexedSet,
39
+ PyFlowGraph,
40
+ PyFlowGraph312,
41
+ PyFlowGraph314,
42
+ PyFlowGraph315,
43
+ PyFlowGraphCinder310,
44
+ )
45
+ from ..pycodegen import (
46
+ CinderCodeGenerator310,
47
+ CinderCodeGenerator312,
48
+ CinderCodeGenerator314,
49
+ CinderCodeGenerator315,
50
+ CodeGenerator,
51
+ CodeGenTree,
52
+ compile_code,
53
+ CompNode,
54
+ FuncOrLambda,
55
+ PatternContext,
56
+ )
57
+ from ..strict import (
58
+ StrictCodeGenerator310,
59
+ StrictCodeGenerator312,
60
+ StrictCodeGenerator314,
61
+ )
62
+ from ..strict.code_gen_base import StrictCodeGenBase
63
+ from ..strict.common import FIXED_MODULES
64
+ from ..symbols import BaseSymbolVisitor, ClassScope, ModuleScope, Scope
65
+ from .compiler import Compiler
66
+ from .definite_assignment_checker import DefiniteAssignmentVisitor
67
+ from .effects import NarrowingEffect, TypeState
68
+ from .module_table import ModuleFlag, ModuleTable
69
+ from .type_binder import UsedRefinementField
70
+ from .types import (
71
+ ASYNC_CACHED_PROPERTY_IMPL_PREFIX,
72
+ AsyncCachedPropertyMethod,
73
+ AwaitableType,
74
+ CACHED_PROPERTY_IMPL_PREFIX,
75
+ CachedPropertyMethod,
76
+ CInstance,
77
+ Class,
78
+ CType,
79
+ Dataclass,
80
+ DataclassDecorator,
81
+ DataclassField,
82
+ DecoratedMethod,
83
+ DynamicInstance,
84
+ EAGER_IMPORT_NAME,
85
+ Function,
86
+ FunctionContainer,
87
+ GenericClass,
88
+ KnownBoolean,
89
+ Slot,
90
+ TMP_VAR_PREFIX,
91
+ TType,
92
+ TypeDescr,
93
+ Value,
94
+ )
95
+
96
+ try:
97
+ from cinder import _set_qualname
98
+ except ImportError:
99
+
100
+ def _set_qualname(code: CodeType, qualname: str) -> None:
101
+ pass
102
+
103
+
104
+ def exec_static(
105
+ source: str,
106
+ locals: dict[str, object],
107
+ globals: dict[str, object],
108
+ modname: str = "<module>",
109
+ ) -> None:
110
+ code = compile_code(
111
+ source, "<module>", "exec", compiler=StaticCodeGenerator, modname=modname
112
+ )
113
+ if "<fixed-modules>" not in globals:
114
+ globals["<fixed-modules>"] = FIXED_MODULES
115
+ if "<builtins>" not in globals:
116
+ globals["<builtins>"] = builtins.__dict__
117
+
118
+ exec(code, locals, globals)
119
+
120
+
121
+ class StaticPatternContext(PatternContext):
122
+ def __init__(self) -> None:
123
+ super().__init__()
124
+ self.type_dict: dict[str, Class] = {}
125
+
126
+ def clone(self) -> StaticPatternContext:
127
+ pc = StaticPatternContext()
128
+ pc.stores = list(self.stores)
129
+ pc.allow_irrefutable = self.allow_irrefutable
130
+ pc.fail_pop = list(self.fail_pop)
131
+ pc.on_top = self.on_top
132
+ pc.type_dict = self.type_dict.copy()
133
+ return pc
134
+
135
+
136
+ class PyFlowGraphStatic310(PyFlowGraphCinder310):
137
+ opcode: Opcode = opcode_static.opcode
138
+
139
+
140
+ class PyFlowGraphStatic312(PyFlowGraph312):
141
+ opcode: Opcode = opcode_static.opcode
142
+
143
+
144
+ class InitSubClassGenerator:
145
+ def __init__(self, flow_graph: PyFlowGraph, qualname: str) -> None:
146
+ self.flow_graph = flow_graph
147
+ self.qualname = qualname
148
+
149
+ @property
150
+ def scope(self) -> Scope:
151
+ raise NotImplementedError()
152
+
153
+ @property
154
+ def name(self) -> str:
155
+ return self.qualname
156
+
157
+ def getCode(self) -> CodeType:
158
+ code = self.flow_graph.getCode()
159
+ _set_qualname(code, self.qualname)
160
+ return code
161
+
162
+
163
+ class StaticCodeGenBase(StrictCodeGenBase):
164
+ flow_graph = PyFlowGraph
165
+ _default_cache: dict[type[ast.AST], typingCallable[..., None]] = {}
166
+ pattern_context = StaticPatternContext
167
+ # Defined in subclasses; this is an explicit receiver class for
168
+ # self.defaultCall() and self.make_child_codegen to dispatch to
169
+ parent_impl: type[CodeGenerator] = CodeGenerator
170
+
171
+ def __init__(
172
+ self,
173
+ parent: CodeGenerator | None,
174
+ node: AST,
175
+ symbols: BaseSymbolVisitor,
176
+ graph: PyFlowGraph,
177
+ compiler: Compiler,
178
+ modname: str,
179
+ flags: int = 0,
180
+ optimization_lvl: int = 0,
181
+ enable_patching: bool = False,
182
+ builtins: dict[str, Any] = builtins.__dict__,
183
+ future_flags: int | None = None,
184
+ ) -> None:
185
+ super().__init__(
186
+ parent,
187
+ node,
188
+ symbols,
189
+ graph,
190
+ flags=flags,
191
+ optimization_lvl=optimization_lvl,
192
+ builtins=builtins,
193
+ future_flags=future_flags,
194
+ )
195
+ self.compiler = compiler
196
+ self.modname = modname
197
+ # Use this counter to allocate temporaries for loop indices
198
+ self._tmpvar_loopidx_count = 0
199
+ self.cur_mod: ModuleTable = self.compiler.modules[modname]
200
+ self.enable_patching = enable_patching
201
+ self.scope_to_node: dict[Scope, AST] = {
202
+ scope: node for node, scope in self.scopes.items()
203
+ }
204
+
205
+ def _is_static_compiler_disabled(self, node: AST) -> bool:
206
+ if not isinstance(node, (AsyncFunctionDef, FunctionDef, ClassDef)):
207
+ # Static compilation can only be disabled for functions and classes.
208
+ return False
209
+ if node in self.cur_mod.compile_non_static:
210
+ return True
211
+ scope_node = self.scope_to_node[self.scope]
212
+ fn = None
213
+ if isinstance(scope_node, ClassDef):
214
+ klass = self.get_type(scope_node)
215
+ if klass:
216
+ assert isinstance(klass, Class)
217
+ if klass.donotcompile:
218
+ # If static compilation is disabled on the entire class, it's skipped for all contained
219
+ # methods too.
220
+ return True
221
+ fn = klass.get_own_member(node.name)
222
+
223
+ if fn is None:
224
+ # Wasn't a method, let's check if it's a module level function
225
+ fn = self.get_type(node)
226
+
227
+ if isinstance(fn, (Function, DecoratedMethod)):
228
+ return fn.donotcompile
229
+
230
+ return False
231
+
232
+ def _is_type_checked(self, t: Class) -> bool:
233
+ return t is not self.compiler.type_env.object and t.is_nominal_type
234
+
235
+ def _get_arg_types(
236
+ self,
237
+ func: FuncOrLambda | CompNode,
238
+ args: ast.arguments,
239
+ graph: PyFlowGraph,
240
+ ) -> tuple[object, ...]:
241
+ arg_checks = []
242
+ cellvars = graph.cellvars
243
+ is_comprehension = not isinstance(
244
+ func, (ast.AsyncFunctionDef, ast.FunctionDef, ast.Lambda)
245
+ )
246
+
247
+ for i, arg in enumerate(args.posonlyargs):
248
+ t = self.get_type(arg)
249
+ if self._is_type_checked(t.klass):
250
+ arg_checks.append(self._calculate_idx(arg.arg, i, cellvars))
251
+ arg_checks.append(t.klass.type_descr)
252
+
253
+ for i, arg in enumerate(args.args):
254
+ # Comprehension nodes don't have arguments when they're typed; make
255
+ # up for that here.
256
+ t = (
257
+ self.compiler.type_env.DYNAMIC
258
+ if is_comprehension
259
+ else self.get_type(arg)
260
+ )
261
+ if self._is_type_checked(t.klass):
262
+ arg_checks.append(
263
+ self._calculate_idx(arg.arg, i + len(args.posonlyargs), cellvars)
264
+ )
265
+ arg_checks.append(t.klass.type_descr)
266
+
267
+ for i, arg in enumerate(args.kwonlyargs):
268
+ t = self.get_type(arg)
269
+ if self._is_type_checked(t.klass):
270
+ arg_checks.append(
271
+ self._calculate_idx(
272
+ arg.arg,
273
+ i + len(args.posonlyargs) + len(args.args),
274
+ cellvars,
275
+ )
276
+ )
277
+ arg_checks.append(t.klass.type_descr)
278
+
279
+ # we should never emit arg checks for object
280
+ assert not any(td == ("builtins", "object") for td in arg_checks[1::2])
281
+
282
+ return tuple(arg_checks)
283
+
284
+ def get_type(self, node: AST) -> Value:
285
+ return self.cur_mod.types[node]
286
+
287
+ def get_node_data(self, key: AST, data_type: type[TType]) -> TType:
288
+ return self.cur_mod.get_node_data(key, data_type)
289
+
290
+ def get_opt_node_data(self, key: AST, data_type: type[TType]) -> TType | None:
291
+ return self.cur_mod.get_opt_node_data(key, data_type)
292
+
293
+ def set_node_data(self, key: AST, data_type: type[TType], value: TType) -> None:
294
+ self.cur_mod.set_node_data(key, data_type, value)
295
+
296
+ @classmethod
297
+ def make_code_gen(
298
+ cls,
299
+ module_name: str,
300
+ tree: AST,
301
+ filename: str,
302
+ source: str | bytes | ast.Module | ast.Expression | ast.Interactive,
303
+ flags: int,
304
+ optimize: int,
305
+ ast_optimizer_enabled: bool = True,
306
+ enable_patching: bool = False,
307
+ builtins: dict[str, Any] = builtins.__dict__,
308
+ ) -> StaticCodeGenBase:
309
+ assert ast_optimizer_enabled
310
+
311
+ compiler = Compiler(cls)
312
+ code_gen = compiler.code_gen(
313
+ module_name, filename, tree, source, optimize, enable_patching, builtins
314
+ )
315
+ return code_gen
316
+
317
+ def make_child_codegen(
318
+ self,
319
+ tree: CodeGenTree,
320
+ graph: PyFlowGraph,
321
+ name: str | None = None,
322
+ ) -> CodeGenerator:
323
+ if self._is_static_compiler_disabled(tree):
324
+ return self.parent_impl(
325
+ self,
326
+ tree,
327
+ self.symbols,
328
+ graph,
329
+ flags=self.flags,
330
+ optimization_lvl=self.optimization_lvl,
331
+ )
332
+ return self.__class__(
333
+ self,
334
+ tree,
335
+ self.symbols,
336
+ graph,
337
+ compiler=self.compiler,
338
+ modname=self.modname,
339
+ optimization_lvl=self.optimization_lvl,
340
+ enable_patching=self.enable_patching,
341
+ )
342
+
343
+ def make_function_graph(
344
+ self,
345
+ func: FuncOrLambda | CompNode,
346
+ func_args: ast.arguments,
347
+ filename: str,
348
+ scopes: dict[AST, Scope],
349
+ class_name: str | None,
350
+ name: str,
351
+ first_lineno: int,
352
+ ) -> PyFlowGraph:
353
+ graph = super().make_function_graph(
354
+ func, func_args, filename, scopes, class_name, name, first_lineno
355
+ )
356
+
357
+ if self._is_static_compiler_disabled(func):
358
+ return graph
359
+
360
+ graph.setFlag(consts.CI_CO_STATICALLY_COMPILED)
361
+ arg_types = self._get_arg_types(func, func_args, graph)
362
+ ret_type = self._get_return_type(func)
363
+ graph.extra_consts.append((arg_types, ret_type.type_descr))
364
+ return graph
365
+
366
+ def _get_return_type(self, func: FuncOrLambda | CompNode) -> Class:
367
+ if isinstance(func, (FunctionDef, AsyncFunctionDef)):
368
+ function = self.get_func_container(func)
369
+ klass = function.return_type.resolved()
370
+ if not self._is_type_checked(klass):
371
+ return self.compiler.type_env.object
372
+ else:
373
+ klass = self.get_type(func).klass
374
+ if isinstance(klass, AwaitableType):
375
+ klass = klass.type_args[0]
376
+ return klass
377
+
378
+ @contextmanager
379
+ def new_loopidx(self) -> Generator[str, None, None]:
380
+ self._tmpvar_loopidx_count += 1
381
+ try:
382
+ yield f"{TMP_VAR_PREFIX}.{self._tmpvar_loopidx_count}"
383
+ finally:
384
+ self._tmpvar_loopidx_count -= 1
385
+
386
+ def _resolve_class(self, node: ClassDef) -> Class | None:
387
+ klass = self.get_type(node)
388
+ if not isinstance(klass, Class) or klass is self.compiler.type_env.dynamic:
389
+ return
390
+ return klass
391
+
392
+ def emit_build_class(
393
+ self,
394
+ node: ast.ClassDef,
395
+ class_body: CodeGenerator,
396
+ outer_scope: CodeGenerator | None = None,
397
+ ) -> None:
398
+ if (outer_scope is not None and outer_scope is not self) or getattr(
399
+ node, "type_params", None
400
+ ):
401
+ # TASK(T210915794): We don't support type params, we need to pass the
402
+ # .generic_base as part of the bases
403
+ raise NotImplementedError("Type params not supported in static Python yet")
404
+
405
+ klass = self._resolve_class(node)
406
+ if not isinstance(self.scope, ModuleScope) or klass is None:
407
+ # If a class isn't top-level then it's not going to be using static
408
+ # features (we could relax this in the future for classes nested in classes).
409
+ return super().emit_build_class(node, class_body)
410
+
411
+ self.emit_closure(class_body, 0)
412
+ self.emit("LOAD_CONST", node.name)
413
+
414
+ if node.keywords:
415
+ for keyword in node.keywords:
416
+ self.emit("LOAD_CONST", keyword.arg)
417
+ self.visit(keyword.value)
418
+
419
+ self.emit("BUILD_MAP", len(node.keywords))
420
+ else:
421
+ self.emit("LOAD_CONST", None)
422
+
423
+ class_scope = class_body.scope
424
+ assert isinstance(class_scope, ClassScope)
425
+ self.emit("LOAD_CONST", class_scope.needs_class_closure)
426
+
427
+ assert klass is not None
428
+ final_methods: list[str] = []
429
+ for class_or_subclass in klass.mro:
430
+ final_methods.extend(class_or_subclass.get_own_final_method_names())
431
+ self.emit("LOAD_CONST", tuple(sorted(final_methods)))
432
+
433
+ if klass.is_final:
434
+ self.emit("LOAD_CONST", True)
435
+ else:
436
+ self.emit("LOAD_CONST", False)
437
+
438
+ count = 0
439
+ for name, value in klass.members.items():
440
+ if isinstance(value, CachedPropertyMethod):
441
+ self.emit("LOAD_CONST", CACHED_PROPERTY_IMPL_PREFIX + name)
442
+ count += 1
443
+ elif isinstance(value, AsyncCachedPropertyMethod):
444
+ self.emit("LOAD_CONST", ASYNC_CACHED_PROPERTY_IMPL_PREFIX + name)
445
+ count += 1
446
+
447
+ self.emit("BUILD_TUPLE", count)
448
+
449
+ for base in node.bases:
450
+ self.visit(base)
451
+
452
+ self.emit(
453
+ "INVOKE_FUNCTION",
454
+ ((("_static",), "__build_cinder_class__"), 7 + len(node.bases)),
455
+ )
456
+
457
+ def processBody(
458
+ self, node: FuncOrLambda, body: list[ast.stmt], gen: CodeGenerator
459
+ ) -> None:
460
+ if isinstance(node, (ast.FunctionDef | ast.AsyncFunctionDef)):
461
+ # check for any unassigned primitive values and force them to be
462
+ # assigned.
463
+ visitor = DefiniteAssignmentVisitor(self.symbols.scopes[node])
464
+ visitor.analyzeFunction(node)
465
+ for unassigned in visitor.unassigned:
466
+ node_type = self.get_type(unassigned)
467
+ if isinstance(node_type, CInstance):
468
+ assert isinstance(gen, StaticCodeGenBase)
469
+ node_type.emit_init(unassigned, gen)
470
+
471
+ super().processBody(node, body, gen)
472
+
473
+ def visitFunctionOrLambda(
474
+ self, node: ast.FunctionDef | ast.AsyncFunctionDef | ast.Lambda
475
+ ) -> None:
476
+ if isinstance(node, ast.Lambda):
477
+ return super().visitFunctionOrLambda(node)
478
+
479
+ function = self.get_func_container(node)
480
+ name = function.emit_function(node, self)
481
+ self.storeName(name)
482
+
483
+ def get_func_container(self, node: ast.AST) -> FunctionContainer:
484
+ function = self.get_type(node)
485
+ if not isinstance(function, FunctionContainer):
486
+ raise RuntimeError("bad value for function")
487
+
488
+ return function
489
+
490
+ def walkClassBody(self, node: ClassDef, gen: CodeGenerator) -> None:
491
+ klass = self._resolve_class(node)
492
+ super().walkClassBody(node, gen)
493
+ if not klass:
494
+ return
495
+
496
+ if not klass.has_init_subclass:
497
+ gen_scope = gen.scope
498
+ assert isinstance(gen_scope, ClassScope)
499
+
500
+ # we define a custom override of __init_subclass__
501
+ if not gen_scope.needs_class_closure:
502
+ # we need to call super(), which will need to have
503
+ # __class__ available if it's not already...
504
+ gen.graph.cellvars.get_index("__class__")
505
+ gen_scope.cells["__class__"] = 1
506
+ gen_scope.needs_class_closure = True
507
+
508
+ init_graph = self.flow_graph(
509
+ "__init_subclass__",
510
+ self.graph.filename,
511
+ None,
512
+ 0,
513
+ ("cls",),
514
+ (),
515
+ (),
516
+ optimized=1,
517
+ docstring=None,
518
+ )
519
+ init_graph.freevars.get_index("__class__")
520
+ init_graph.emit_prologue()
521
+ init_graph.emit("LOAD_GLOBAL", "super")
522
+ init_graph.emit("LOAD_DEREF", "__class__")
523
+ init_graph.emit("LOAD_FAST", "cls")
524
+ init_graph.emit_super_call("__init_subclass__", True)
525
+ init_graph.emit_call_method(0)
526
+ init_graph.emit("POP_TOP")
527
+
528
+ init_graph.emit("LOAD_FAST", "cls")
529
+ init_graph.emit("INVOKE_FUNCTION", ((("_static",), "init_subclass"), 1))
530
+ init_graph.emit("RETURN_VALUE")
531
+
532
+ gen.emit("LOAD_CLOSURE", "__class__")
533
+ gen.emit("BUILD_TUPLE", 1)
534
+ init_gen = InitSubClassGenerator(
535
+ init_graph, self.get_qual_prefix(gen) + ".__init_subclass__"
536
+ )
537
+ gen.emit_make_function(
538
+ init_gen, self.get_qual_prefix(gen) + ".__init_subclass__", 8
539
+ )
540
+ gen.emit("STORE_NAME", "__init_subclass__")
541
+
542
+ assert isinstance(gen, StaticCodeGenBase)
543
+ klass.emit_extra_members(node, gen)
544
+
545
+ class_mems_with_overrides = [
546
+ name
547
+ for name, value in klass.members.items()
548
+ if (isinstance(value, Slot) and not value.is_classvar)
549
+ or isinstance(value, CachedPropertyMethod)
550
+ or isinstance(value, AsyncCachedPropertyMethod)
551
+ ]
552
+
553
+ class_mems = [
554
+ name
555
+ for name in class_mems_with_overrides
556
+ if not isinstance(mem := klass.members[name], Slot) or not mem.override
557
+ ]
558
+
559
+ if klass.allow_weakrefs:
560
+ class_mems.append("__weakref__")
561
+
562
+ # In the future we may want a compatibility mode where we add
563
+ # __dict__ and __weakref__
564
+ gen.emit("LOAD_CONST", tuple(class_mems))
565
+ gen.emit("STORE_NAME", "__slots__")
566
+
567
+ slots_with_default = [
568
+ name
569
+ for name in class_mems_with_overrides
570
+ if name in klass.members
571
+ and isinstance(klass.members[name], Slot)
572
+ and cast(
573
+ Slot[Class], klass.members[name]
574
+ ).is_typed_descriptor_with_default_value()
575
+ ]
576
+
577
+ for name in slots_with_default:
578
+ gen.emit("LOAD_CONST", name)
579
+ gen.emit("LOAD_NAME", name)
580
+ gen.emit("DELETE_NAME", name)
581
+
582
+ gen.emit("BUILD_MAP", len(slots_with_default))
583
+ gen.emit("STORE_NAME", "__slots_with_default__")
584
+
585
+ count = 0
586
+ for name, value in klass.members.items():
587
+ if not isinstance(value, Slot):
588
+ continue
589
+
590
+ if value.is_classvar:
591
+ continue
592
+
593
+ value_type = value.decl_type
594
+ if value.decl_type is self.compiler.type_env.dynamic:
595
+ if value.is_typed_descriptor_with_default_value():
596
+ value_type = self.compiler.type_env.object
597
+ else:
598
+ continue
599
+
600
+ gen.emit("LOAD_CONST", name)
601
+ gen.emit("LOAD_CONST", value_type.type_descr)
602
+ count += 1
603
+
604
+ if count:
605
+ gen.emit("BUILD_MAP", count)
606
+ gen.emit("STORE_NAME", "__slot_types__")
607
+
608
+ def visit_decorator(self, decorator: AST, class_def: ClassDef) -> None:
609
+ d = self.get_type(decorator)
610
+ if (
611
+ isinstance(d, DataclassDecorator)
612
+ and self.get_type(class_def) is not self.compiler.type_env.dynamic
613
+ ):
614
+ return
615
+
616
+ super().visit_decorator(decorator, class_def)
617
+
618
+ def emit_decorator_call(self, decorator: AST, class_def: ClassDef) -> None:
619
+ self.get_type(decorator).emit_decorator_call(class_def, self)
620
+
621
+ def emit_load_builtin(self, name: str) -> None:
622
+ is_checked_dict = (
623
+ name == "dict" and ModuleFlag.CHECKED_DICTS in self.cur_mod.flags
624
+ )
625
+ is_checked_list = (
626
+ name == "list" and ModuleFlag.CHECKED_LISTS in self.cur_mod.flags
627
+ )
628
+ if is_checked_dict or is_checked_list:
629
+ chkname = f"chk{name}"
630
+ self.emit("LOAD_CONST", 0)
631
+ self.emit("LOAD_CONST", (chkname,))
632
+ self.emit(EAGER_IMPORT_NAME, "_static")
633
+ self.emit("IMPORT_FROM", chkname)
634
+ self.emit_rotate_stack(2)
635
+ self.emit("POP_TOP")
636
+
637
+ def visitModule(self, node: Module) -> None:
638
+ if ModuleFlag.CHECKED_DICTS in self.cur_mod.flags:
639
+ self.emit_load_builtin("dict")
640
+ self.storeName("dict")
641
+
642
+ if ModuleFlag.CHECKED_LISTS in self.cur_mod.flags:
643
+ self.emit_load_builtin("list")
644
+ self.storeName("list")
645
+
646
+ super().visitModule(node)
647
+
648
+ def emit_module_return(self, node: ast.Module) -> None:
649
+ self.emit("LOAD_CONST", tuple(self.cur_mod.named_finals.keys()))
650
+ self.emit("STORE_NAME", "__final_constants__")
651
+ super().emit_module_return(node)
652
+
653
+ def visitAssert(self, node: ast.Assert) -> None:
654
+ super().visitAssert(node)
655
+ # Only add casts when the assert is optimized out.
656
+ if not self.optimization_lvl:
657
+ return
658
+ # Since we're narrowing types in asserts, we need to ensure we cast
659
+ # all narrowed locals when asserts are optimized away.
660
+ effect = self.get_node_data(node, NarrowingEffect)
661
+ # As all of our effects store the final type, we can apply the effects on
662
+ # an empty dictionary to get an overapproximation of what we need to cast.
663
+ type_state = TypeState()
664
+ effect_nodes: dict[str, ast.AST] = {}
665
+ effect.apply(type_state, effect_nodes)
666
+ for key, value in type_state.local_types.items():
667
+ if value.klass.is_nominal_type:
668
+ self.visit(effect_nodes[key])
669
+ self.emit("CAST", value.klass.type_descr)
670
+ self.emit("POP_TOP")
671
+ for base, refinement_dict in type_state.refined_fields.items():
672
+ for attr, (value, _, _) in refinement_dict.items():
673
+ if value.klass.is_nominal_type:
674
+ key = f"{base}.{attr}"
675
+ self.visit(effect_nodes[key])
676
+ self.emit("CAST", value.klass.type_descr)
677
+ self.emit("POP_TOP")
678
+
679
+ def visitAttribute(self, node: Attribute) -> None:
680
+ self.set_pos(node)
681
+ data = self.get_opt_node_data(node, UsedRefinementField)
682
+ if data is not None and not data.is_source:
683
+ self.emit("LOAD_FAST", data.name)
684
+ return
685
+
686
+ if isinstance(node.ctx, ast.Store) and data is not None and data.is_used:
687
+ self.emit_dup()
688
+ self.emit("STORE_FAST", data.name)
689
+
690
+ self.get_type(node.value).emit_attr(node, self)
691
+
692
+ if (
693
+ isinstance(node.ctx, ast.Load)
694
+ and data is not None
695
+ and data.is_source
696
+ and data.is_used
697
+ ):
698
+ self.emit_dup()
699
+ self.emit("STORE_FAST", data.name)
700
+
701
+ def visitAssignTarget(
702
+ self, elt: expr, stmt: AST, value: expr | None = None
703
+ ) -> None:
704
+ if isinstance(elt, (ast.Tuple, ast.List)):
705
+ self._visitUnpack(elt)
706
+ if isinstance(value, ast.Tuple) and len(value.elts) == len(elt.elts):
707
+ for target, inner_value in zip(elt.elts, value.elts):
708
+ self.visitAssignTarget(target, stmt, inner_value)
709
+ else:
710
+ for target in elt.elts:
711
+ self.visitAssignTarget(target, stmt, None)
712
+ else:
713
+ elt_type = self.get_type(elt).klass
714
+ value_type = (
715
+ self.compiler.type_env.dynamic
716
+ if value is None
717
+ else self.get_type(value).klass
718
+ )
719
+ elt_type.emit_type_check(value_type, self)
720
+ self.visit(elt)
721
+
722
+ def visitAssign(self, node: Assign) -> None:
723
+ self.set_pos(node)
724
+ self.visit(node.value)
725
+ dups = len(node.targets) - 1
726
+ for i in range(len(node.targets)):
727
+ elt = node.targets[i]
728
+ if i < dups:
729
+ self.emit_dup()
730
+ if isinstance(elt, ast.AST):
731
+ self.visitAssignTarget(elt, node, node.value)
732
+
733
+ def visitAnnAssign(self, node: ast.AnnAssign) -> None:
734
+ self.set_pos(node)
735
+ value = node.value
736
+ if value:
737
+ value_type = self.get_type(value)
738
+ if (
739
+ isinstance(value_type, DataclassField)
740
+ and isinstance(self.tree, ast.ClassDef)
741
+ and isinstance(self.get_type(self.tree), Dataclass)
742
+ ):
743
+ value_type.emit_field(node.target, self)
744
+ else:
745
+ self.visit(value)
746
+ self.get_type(node.target).klass.emit_type_check(value_type.klass, self)
747
+ self.visit(node.target)
748
+ target = node.target
749
+ if isinstance(target, ast.Name):
750
+ self.emit_simple_ann_assign(node)
751
+ elif isinstance(target, ast.Attribute):
752
+ if not node.value:
753
+ self.checkAnnExpr(target.value)
754
+ elif isinstance(target, ast.Subscript):
755
+ if not node.value:
756
+ self.checkAnnExpr(target.value)
757
+ self.checkAnnSubscr(target.slice)
758
+ else:
759
+ raise SystemError(
760
+ f"invalid node type {type(node).__name__} for annotated assignment"
761
+ )
762
+
763
+ if not node.simple:
764
+ self.checkAnnotation(node)
765
+
766
+ def visitConstant(self, node: Constant) -> None:
767
+ self.get_type(node).emit_constant(node, self)
768
+
769
+ def visitDefault(self, node: expr) -> None:
770
+ self.visit(node)
771
+ node_type = self.get_type(node)
772
+ if isinstance(node_type, CInstance):
773
+ node_type.emit_box(self)
774
+
775
+ def get_final_literal(self, node: AST) -> ast.Constant | None:
776
+ return self.cur_mod.get_final_literal(node, self.scope)
777
+
778
+ def visitName(self, node: Name) -> None:
779
+ final_val = self.get_final_literal(node)
780
+ if final_val is not None:
781
+ # visit the constant directly
782
+ return self.defaultVisit(final_val)
783
+ self.get_type(node).emit_name(node, self)
784
+
785
+ def emitAugAttribute(self, node: ast.AugAssign) -> None:
786
+ target = node.target
787
+ assert isinstance(target, ast.Attribute)
788
+ self.visit(target.value)
789
+ typ = self.get_type(target.value)
790
+ self.emit_dup()
791
+ typ.emit_load_attr(target, self)
792
+ self.emitAugRHS(node)
793
+ self.emit_rotate_stack(2)
794
+ typ.emit_store_attr(target, self)
795
+
796
+ def emitAugName(self, node: ast.AugAssign) -> None:
797
+ target = node.target
798
+ assert isinstance(target, ast.Name)
799
+ typ = self.get_type(target)
800
+ typ.emit_load_name(target, self)
801
+ self.emitAugRHS(node)
802
+ typ.emit_store_name(target, self)
803
+
804
+ def emitAugSubscript(self, node: ast.AugAssign) -> None:
805
+ target = node.target
806
+ assert isinstance(target, ast.Subscript)
807
+ self.visit(target.value)
808
+ self.visit(target.slice)
809
+ typ = self.get_type(target.value)
810
+ self.emit_dup(2)
811
+ typ.emit_load_subscr(target, self)
812
+ self.emitAugRHS(node)
813
+ self.emit_rotate_stack(3)
814
+ typ.emit_store_subscr(target, self)
815
+
816
+ def emitAugRHS(self, node: ast.AugAssign) -> None:
817
+ self.get_type(node.target).emit_aug_rhs(node, self)
818
+
819
+ def visitBinOp(self, node: BinOp) -> None:
820
+ self.get_type(node).emit_binop(node, self)
821
+
822
+ def visitUnaryOp(self, node: UnaryOp, type_ctx: Class | None = None) -> None:
823
+ self.get_type(node).emit_unaryop(node, self)
824
+
825
+ def visitCall(self, node: Call) -> None:
826
+ self.strictPreVisitCall(node)
827
+ self.get_type(node.func).emit_call(node, self)
828
+
829
+ def visitSubscript(self, node: ast.Subscript, aug_flag: bool = False) -> None:
830
+ # aug_flag is unused in static compiler; we have our own
831
+ # emitAugSubscript that doesn't call visitSubscript
832
+ self.get_type(node.value).emit_subscr(node, self)
833
+
834
+ def _visitReturnValue(self, value: ast.AST, expected: Class) -> None:
835
+ self.visit(value)
836
+ expected.emit_type_check(self.get_type(value).klass, self)
837
+
838
+ def visitReturn(self, node: ast.Return) -> None:
839
+ self.checkReturn(node)
840
+ function = self.get_func_container(self.tree)
841
+
842
+ expected = function.return_type.resolved()
843
+ if isinstance(self.tree, AsyncFunctionDef):
844
+ assert isinstance(expected, AwaitableType)
845
+ expected = expected.type_args[0]
846
+ self.set_pos(node)
847
+ value = node.value
848
+ is_return_constant = isinstance(value, ast.Constant)
849
+ opcode = "RETURN_VALUE"
850
+ oparg = 0
851
+ if value:
852
+ if not is_return_constant:
853
+ self._visitReturnValue(value, expected)
854
+ self.unwind_setup_entries(preserve_tos=True)
855
+ else:
856
+ self.unwind_setup_entries(preserve_tos=False)
857
+ self._visitReturnValue(value, expected)
858
+ if isinstance(expected, CType):
859
+ opcode = "RETURN_PRIMITIVE"
860
+ oparg = expected.instance.as_oparg()
861
+ else:
862
+ self.unwind_setup_entries(preserve_tos=False)
863
+ self.emit("LOAD_CONST", None)
864
+
865
+ self.emit(opcode, oparg)
866
+ self.nextBlock()
867
+
868
+ def visitDictComp(self, node: DictComp) -> None:
869
+ dict_type = self.get_type(node)
870
+ if dict_type in (
871
+ self.compiler.type_env.dict.instance,
872
+ self.compiler.type_env.dict.exact_type().instance,
873
+ ):
874
+ return super().visitDictComp(node)
875
+ klass = dict_type.klass
876
+
877
+ assert (
878
+ isinstance(klass, GenericClass)
879
+ and klass.type_def is self.compiler.type_env.checked_dict
880
+ ), dict_type
881
+ self.compile_comprehension(
882
+ node,
883
+ sys.intern("<dictcomp>"),
884
+ node.key,
885
+ node.value,
886
+ "BUILD_CHECKED_MAP",
887
+ (dict_type.klass.type_descr, 0),
888
+ )
889
+
890
+ def compile_subgendict(
891
+ self, node: ast.Dict, begin: int, end: int, dict_descr: TypeDescr
892
+ ) -> None:
893
+ n = end - begin
894
+ for i in range(begin, end):
895
+ k = node.keys[i]
896
+ assert k is not None
897
+ self.visit(k)
898
+ self.visit(node.values[i])
899
+
900
+ self.emit("BUILD_CHECKED_MAP", (dict_descr, n))
901
+
902
+ def visitDict(self, node: ast.Dict) -> None:
903
+ dict_type = self.get_type(node)
904
+ if dict_type in (
905
+ self.compiler.type_env.dict.instance,
906
+ self.compiler.type_env.dict.exact_type().instance,
907
+ ):
908
+ return super().visitDict(node)
909
+ klass = dict_type.klass
910
+
911
+ assert (
912
+ isinstance(klass, GenericClass)
913
+ and klass.type_def is self.compiler.type_env.checked_dict
914
+ ), dict_type
915
+
916
+ self.set_pos(node)
917
+ elements = 0
918
+ is_unpacking = False
919
+ built_final_dict = False
920
+
921
+ # This is similar to the normal dict code generation, but instead of relying
922
+ # upon an opcode for BUILD_MAP_UNPACK we invoke the update method on the
923
+ # underlying dict type. Therefore the first dict that we create becomes
924
+ # the final dict. This allows us to not introduce a new opcode, but we should
925
+ # also be able to dispatch the INVOKE_METHOD rather efficiently.
926
+ dict_descr = dict_type.klass.type_descr
927
+ update_descr = dict_descr + ("update",)
928
+ for i, (k, v) in enumerate(zip(node.keys, node.values)):
929
+ is_unpacking = k is None
930
+ if elements == 0xFFFF or (elements and is_unpacking):
931
+ self.compile_subgendict(node, i - elements, i, dict_descr)
932
+ built_final_dict = True
933
+ elements = 0
934
+
935
+ if is_unpacking:
936
+ if not built_final_dict:
937
+ # {**foo, ...}, we need to generate the empty dict
938
+ self.emit("BUILD_CHECKED_MAP", (dict_descr, 0))
939
+ built_final_dict = True
940
+ self.emit_dup()
941
+ self.visit(v)
942
+
943
+ self.emit_invoke_method(update_descr, 1)
944
+ self.emit("POP_TOP")
945
+ else:
946
+ elements += 1
947
+
948
+ if elements or not built_final_dict:
949
+ if built_final_dict:
950
+ self.emit_dup()
951
+ self.compile_subgendict(
952
+ node, len(node.keys) - elements, len(node.keys), dict_descr
953
+ )
954
+ if built_final_dict:
955
+ self.emit_invoke_method(update_descr, 1)
956
+ self.emit("POP_TOP")
957
+
958
+ def compile_sub_checked_list(
959
+ self, node: ast.List, begin: int, end: int, type_descr: TypeDescr
960
+ ) -> None:
961
+ n = end - begin
962
+ for i in range(begin, end):
963
+ elt = node.elts[i]
964
+ assert not isinstance(elt, ast.Starred)
965
+ self.visit(elt)
966
+
967
+ self.emit("BUILD_CHECKED_LIST", (type_descr, n))
968
+
969
+ def visitListComp(self, node: ast.ListComp) -> None:
970
+ list_type = self.get_type(node)
971
+ if list_type in (
972
+ self.compiler.type_env.list.instance,
973
+ self.compiler.type_env.list.exact_type().instance,
974
+ ):
975
+ return super().visitListComp(node)
976
+ klass = list_type.klass
977
+
978
+ assert (
979
+ isinstance(klass, GenericClass)
980
+ and klass.type_def is self.compiler.type_env.checked_list
981
+ ), list_type
982
+ self.compile_comprehension(
983
+ node,
984
+ sys.intern("<listcomp>"),
985
+ node.elt,
986
+ None,
987
+ "BUILD_CHECKED_LIST",
988
+ (list_type.klass.type_descr, 0),
989
+ )
990
+
991
+ def visitList(self, node: ast.List) -> None:
992
+ list_type = self.get_type(node)
993
+ if list_type in (
994
+ self.compiler.type_env.list.instance,
995
+ self.compiler.type_env.list.exact_type().instance,
996
+ ):
997
+ return super().visitList(node)
998
+ klass = list_type.klass
999
+
1000
+ assert (
1001
+ isinstance(klass, GenericClass)
1002
+ and klass.type_def is self.compiler.type_env.checked_list
1003
+ ), list_type
1004
+
1005
+ self.set_pos(node)
1006
+ list_descr = list_type.klass.type_descr
1007
+ extend_descr = (list_descr, "extend")
1008
+ built_final_list = False
1009
+ elements = 0
1010
+
1011
+ for i, elt in enumerate(node.elts):
1012
+ if isinstance(elt, ast.Starred):
1013
+ if elements:
1014
+ self.compile_sub_checked_list(node, i - elements, i, list_descr)
1015
+ built_final_list = True
1016
+ elements = 0
1017
+ if not built_final_list:
1018
+ # We need to generate the empty list to extend in the case of [*foo, ...].
1019
+ self.emit("BUILD_CHECKED_LIST", (list_descr, 0))
1020
+ built_final_list = True
1021
+ self.emit_dup()
1022
+ self.emit_load_static_method(extend_descr)
1023
+ self.visit(elt.value)
1024
+ self.emit_invoke_method(extend_descr, 1)
1025
+ self.emit("POP_TOP")
1026
+ else:
1027
+ elements += 1
1028
+
1029
+ if elements or not built_final_list:
1030
+ if built_final_list:
1031
+ self.emit_dup()
1032
+ self.compile_sub_checked_list(
1033
+ node, len(node.elts) - elements, len(node.elts), list_descr
1034
+ )
1035
+ if built_final_list:
1036
+ self.emit_invoke_method(extend_descr, 1)
1037
+ self.emit("POP_TOP")
1038
+
1039
+ def visitContinue(self, node: ast.Continue) -> None:
1040
+ loop_node = self.get_opt_node_data(node, AST)
1041
+ if isinstance(loop_node, ast.For):
1042
+ # Since we support primitive int for loops with crange()
1043
+ # we have some special handling to deal with the "continue"
1044
+ # statement.
1045
+ iter_type = self.get_type(loop_node.iter)
1046
+ iter_type.emit_continue(node, self)
1047
+ else:
1048
+ super().visitContinue(node)
1049
+
1050
+ def visitFor(self, node: ast.For) -> None:
1051
+ self.strictPreVisitFor(node)
1052
+ iter_type = self.get_type(node.iter)
1053
+ iter_type.emit_forloop(node, self)
1054
+ self.strictPostVisitFor(node)
1055
+
1056
+ def emit_invoke_function(self, descr: TypeDescr, arg_count: int) -> None:
1057
+ # Emit a zero EXTENDED_ARG before so that we can optimize and insert the
1058
+ # arg count
1059
+ self.emit("EXTENDED_ARG", 0)
1060
+ self.emit("INVOKE_FUNCTION", (descr, arg_count))
1061
+
1062
+ def emit_invoke_method(
1063
+ self, descr: TypeDescr, arg_count: int, is_classmethod: bool = False
1064
+ ) -> None:
1065
+ # Emit a zero EXTENDED_ARG before so that we can optimize and insert the
1066
+ # arg count
1067
+ self.emit("EXTENDED_ARG", 0)
1068
+ self.emit(
1069
+ "INVOKE_METHOD",
1070
+ (descr, arg_count, True) if is_classmethod else (descr, arg_count),
1071
+ )
1072
+
1073
+ def emit_load_static_method(
1074
+ self, descr: TypeDescr, is_classmethod: bool = False
1075
+ ) -> None:
1076
+ # Emit a zero EXTENDED_ARG before so that we can optimize and insert the
1077
+ # arg count
1078
+ self.emit("EXTENDED_ARG", 0)
1079
+ self.emit(
1080
+ "LOAD_METHOD_STATIC",
1081
+ (descr, True) if is_classmethod else (descr,),
1082
+ )
1083
+
1084
+ def defaultCall(self, node: object, name: str, *args: object) -> None:
1085
+ meth = getattr(self.parent_impl, name)
1086
+ return meth(self, node, *args)
1087
+
1088
+ def defaultVisit(self, node: AST, *args: object) -> None:
1089
+ klass = node.__class__
1090
+ meth = self._default_cache.get(klass, None)
1091
+ if meth is None:
1092
+ className = klass.__name__
1093
+ meth = getattr(
1094
+ super(StaticCodeGenBase, StaticCodeGenBase),
1095
+ "visit" + className,
1096
+ StaticCodeGenBase.generic_visit,
1097
+ )
1098
+ self._default_cache[klass] = meth
1099
+ return meth(self, node, *args)
1100
+
1101
+ def get_bool_const(self, node: AST) -> bool | None:
1102
+ if isinstance(node, ast.Constant):
1103
+ return bool(node.value)
1104
+
1105
+ kb = self.get_opt_node_data(node, KnownBoolean)
1106
+ if kb is not None:
1107
+ return True if kb == KnownBoolean.TRUE else False
1108
+
1109
+ def visitIf(self, node: ast.If) -> None:
1110
+ self.get_type(node.test)
1111
+
1112
+ test_const = self.get_bool_const(node.test)
1113
+
1114
+ end = self.newBlock("if_end")
1115
+ orelse = None
1116
+ if node.orelse:
1117
+ orelse = self.newBlock("if_else")
1118
+
1119
+ self.compileJumpIf(node.test, orelse or end, False)
1120
+ if test_const is not False:
1121
+ self.visitStatements(node.body)
1122
+
1123
+ if node.orelse:
1124
+ self.emit_jump_forward_noline(end)
1125
+ self.nextBlock(orelse)
1126
+ if test_const is not True:
1127
+ self.visitStatements(node.orelse)
1128
+
1129
+ self.nextBlock(end)
1130
+
1131
+ def compileJumpIf(self, test: AST, next: Block, is_if_true: bool) -> None:
1132
+ if isinstance(test, ast.UnaryOp):
1133
+ if isinstance(test.op, ast.Not):
1134
+ # Compile to remove not operation
1135
+ return self.compileJumpIf(test.operand, next, not is_if_true)
1136
+ elif isinstance(test, ast.BoolOp):
1137
+ is_or = isinstance(test.op, ast.Or)
1138
+ skip_jump = next
1139
+ if is_if_true != is_or:
1140
+ skip_jump = self.newBlock()
1141
+
1142
+ for node in test.values[:-1]:
1143
+ self.get_type(node).emit_jumpif(node, skip_jump, is_or, self)
1144
+ self.nextBlock()
1145
+
1146
+ self.get_type(test.values[-1]).emit_jumpif(
1147
+ test.values[-1], next, is_if_true, self
1148
+ )
1149
+ self.nextBlock()
1150
+
1151
+ if skip_jump is not next:
1152
+ self.nextBlock(skip_jump)
1153
+ return
1154
+ elif isinstance(test, ast.IfExp):
1155
+ end = self.newBlock("end")
1156
+ orelse = self.newBlock("orelse")
1157
+ # Jump directly to orelse if test matches
1158
+ self.get_type(test.test).emit_jumpif(test.test, orelse, False, self)
1159
+ # Jump directly to target if test is true and body is matches
1160
+ self.get_type(test.body).emit_jumpif(test.body, next, is_if_true, self)
1161
+ self.emit_jump_forward_noline(end)
1162
+ # Jump directly to target if test is true and orelse matches
1163
+ self.nextBlock(orelse)
1164
+ self.get_type(test.orelse).emit_jumpif(test.orelse, next, is_if_true, self)
1165
+ self.nextBlock(end)
1166
+ return
1167
+
1168
+ self.get_type(test).emit_jumpif(test, next, is_if_true, self)
1169
+ self.nextBlock()
1170
+
1171
+ def _calculate_idx(
1172
+ self, arg_name: str, non_cellvar_pos: int, cellvars: IndexedSet
1173
+ ) -> int:
1174
+ if sys.version_info >= (3, 12):
1175
+ return non_cellvar_pos
1176
+ try:
1177
+ offset = cellvars.index(arg_name)
1178
+ except ValueError:
1179
+ return non_cellvar_pos
1180
+ else:
1181
+ # the negative sign indicates to the runtime/JIT that this is a cellvar
1182
+ return -(offset + 1)
1183
+
1184
+ def perf_warning(self, msg: str, node: AST) -> None:
1185
+ return self.compiler.error_sink.perf_warning(msg, self.graph.filename, node)
1186
+
1187
+ def storePatternName(self, name: str, pc: PatternContext) -> None:
1188
+ pc = cast(StaticPatternContext, pc)
1189
+ pc.type_dict[name].emit_type_check(self.compiler.type_env.DYNAMIC.klass, self)
1190
+ self.storeName(name)
1191
+
1192
+ def _pattern_helper_store_name(
1193
+ self, name: str | None, pc: PatternContext, loc: ast.AST
1194
+ ) -> None:
1195
+ super()._pattern_helper_store_name(name, pc, loc)
1196
+ pc = cast(StaticPatternContext, pc)
1197
+ if name is not None:
1198
+ pc.type_dict[name] = self.get_type(loc).klass
1199
+
1200
+ def emit_convert_compare_arg(self, op: AST, optype: Value, ltype: Value) -> None:
1201
+ """Convert `ltype` to be compatible with `optype`."""
1202
+ # Helper function for visitCompare; checks if ltype needs to be boxed.
1203
+ optype.emit_convert(ltype, self)
1204
+ if (
1205
+ isinstance(ltype, CInstance)
1206
+ and isinstance(optype, DynamicInstance)
1207
+ and isinstance(op, ast.In)
1208
+ ):
1209
+ # We don't support static containers yet, so for something
1210
+ # like `x: int64 in y: list` we set the optype to dynamic
1211
+ # in static.types.CIntInstance.bind_compare, and we need to
1212
+ # box `x` here before the COMPARE opcode is emitted.
1213
+ ltype.emit_box(self)
1214
+
1215
+
1216
+ class Static310CodeGenerator(StaticCodeGenBase, CinderCodeGenerator310):
1217
+ flow_graph = PyFlowGraphStatic310
1218
+ parent_impl = StrictCodeGenerator310
1219
+
1220
+ def visitBoolOp(self, node: BoolOp) -> None:
1221
+ end = self.newBlock()
1222
+ for child in node.values[:-1]:
1223
+ self.get_type(child).emit_jumpif_pop(
1224
+ child, end, type(node.op) is ast.Or, self
1225
+ )
1226
+ self.nextBlock()
1227
+ self.visit(node.values[-1])
1228
+ self.nextBlock(end)
1229
+
1230
+ def visitCompare(self, node: Compare) -> None:
1231
+ self.set_pos(node)
1232
+ self.visit(node.left)
1233
+ cleanup = self.newBlock("cleanup")
1234
+ left = node.left
1235
+ for op, code in zip(node.ops[:-1], node.comparators[:-1]):
1236
+ optype = self.get_type(op)
1237
+ ltype = self.get_type(left)
1238
+ if ltype != optype:
1239
+ optype.emit_convert(ltype, self)
1240
+ self.emitChainedCompareStep(op, code, cleanup)
1241
+ left = code
1242
+ # now do the last comparison
1243
+ if node.ops:
1244
+ op = node.ops[-1]
1245
+ optype = self.get_type(op)
1246
+ ltype = self.get_type(left)
1247
+ if ltype != optype:
1248
+ self.emit_convert_compare_arg(op, optype, ltype)
1249
+ code = node.comparators[-1]
1250
+ self.visit(code)
1251
+ rtype = self.get_type(code)
1252
+ if rtype != optype:
1253
+ optype.emit_convert(rtype, self)
1254
+ optype.emit_compare(op, self)
1255
+ if len(node.ops) > 1:
1256
+ end = self.newBlock("end")
1257
+ self.emit_jump_forward(end)
1258
+ self.nextBlock(cleanup)
1259
+ self.emit_rotate_stack(2)
1260
+ self.emit("POP_TOP")
1261
+ self.nextBlock(end)
1262
+
1263
+ def emitChainedCompareStep(
1264
+ self, op: cmpop, value: AST, cleanup: Block, always_pop: bool = False
1265
+ ) -> None:
1266
+ optype = self.get_type(op)
1267
+ self.visit(value)
1268
+ rtype = self.get_type(value)
1269
+ if rtype != optype:
1270
+ optype.emit_convert(rtype, self)
1271
+ self.emit_dup()
1272
+ self.emit_rotate_stack(3)
1273
+ optype.emit_compare(op, self)
1274
+ method = optype.emit_jumpif_only if always_pop else optype.emit_jumpif_pop_only
1275
+ method(cleanup, False, self)
1276
+ self.nextBlock(label="compare_or_cleanup")
1277
+
1278
+
1279
+ class Static312CodeGenerator(StaticCodeGenBase, CinderCodeGenerator312):
1280
+ flow_graph = PyFlowGraph312
1281
+ parent_impl = StrictCodeGenerator312
1282
+
1283
+ def visitBoolOp(self, node: ast.BoolOp) -> None:
1284
+ is_if_true = type(node.op) is not ast.And
1285
+ end = self.newBlock()
1286
+ for value in node.values[:-1]:
1287
+ self.visit(value)
1288
+ self.emit("COPY", 1)
1289
+ self.get_type(value).emit_jumpif_only(end, is_if_true, self)
1290
+ self.nextBlock()
1291
+ self.emit("POP_TOP")
1292
+ self.visit(node.values[-1])
1293
+ self.nextBlock(end)
1294
+
1295
+ def visitCompare(self, node: Compare) -> None:
1296
+ self.set_pos(node)
1297
+ left = node.left
1298
+ self.visit(left)
1299
+ ltype = self.get_type(left)
1300
+
1301
+ if len(node.comparators) == 1:
1302
+ op = node.ops[0]
1303
+ optype = self.get_type(op)
1304
+ if ltype != optype:
1305
+ self.emit_convert_compare_arg(op, optype, ltype)
1306
+
1307
+ self.visit(node.comparators[0])
1308
+ rtype = self.get_type(node.comparators[0])
1309
+ if rtype != optype:
1310
+ optype.emit_convert(rtype, self)
1311
+
1312
+ optype.emit_compare(op, self)
1313
+ return
1314
+
1315
+ cleanup = self.newBlock("cleanup")
1316
+ for i in range(len(node.comparators) - 1):
1317
+ self.set_pos(node)
1318
+ op = node.ops[i]
1319
+ optype = self.get_type(op)
1320
+ if ltype != optype:
1321
+ self.emit_convert_compare_arg(op, optype, ltype)
1322
+
1323
+ self.emitChainedCompareStep(
1324
+ node.ops[i], node.comparators[i], cleanup, False
1325
+ )
1326
+ left = node.comparators[i]
1327
+ self.emit("COPY", 1)
1328
+ optype = self.get_type(node.ops[i])
1329
+ optype.emit_jumpif_only(cleanup, False, self)
1330
+ self.nextBlock(label="compare_or_cleanup")
1331
+ self.emit("POP_TOP")
1332
+
1333
+ op = node.ops[-1]
1334
+ optype = self.get_type(op)
1335
+ ltype = self.get_type(left)
1336
+ if ltype != optype:
1337
+ optype.emit_convert(ltype, self)
1338
+
1339
+ self.visit(node.comparators[-1])
1340
+
1341
+ rtype = self.get_type(node.comparators[-1])
1342
+ optype.emit_compare(op, self)
1343
+ end = self.newBlock("end")
1344
+ self.emit_jump_forward(end)
1345
+
1346
+ self.nextBlock(cleanup)
1347
+ self.emit("SWAP", 2)
1348
+ self.emit("POP_TOP")
1349
+ self.nextBlock(end)
1350
+
1351
+ def emitChainedCompareStep(
1352
+ self,
1353
+ op: ast.cmpop,
1354
+ value: ast.expr,
1355
+ cleanup: Block,
1356
+ always_pop: bool = False,
1357
+ ) -> None:
1358
+ optype = self.get_type(op)
1359
+ self.visit(value)
1360
+ rtype = self.get_type(value)
1361
+ if rtype != optype:
1362
+ optype.emit_convert(rtype, self)
1363
+ self.emit("SWAP", 2)
1364
+ self.emit("COPY", 2)
1365
+ optype = self.get_type(op)
1366
+ optype.emit_compare(op, self)
1367
+
1368
+
1369
+ class Static314CodeGenerator(Static312CodeGenerator, CinderCodeGenerator314):
1370
+ flow_graph = PyFlowGraph314
1371
+ parent_impl = StrictCodeGenerator314
1372
+
1373
+ def emit_invoke_function(self, descr: TypeDescr, arg_count: int) -> None:
1374
+ self.emit("INVOKE_FUNCTION", (descr, arg_count))
1375
+
1376
+ def emit_invoke_method(
1377
+ self, descr: TypeDescr, arg_count: int, is_classmethod: bool = False
1378
+ ) -> None:
1379
+ self.emit(
1380
+ "INVOKE_METHOD",
1381
+ (descr, arg_count, True) if is_classmethod else (descr, arg_count),
1382
+ )
1383
+
1384
+ def emit_load_static_method(
1385
+ self, descr: TypeDescr, is_classmethod: bool = False
1386
+ ) -> None:
1387
+ self.emit(
1388
+ "LOAD_METHOD_STATIC",
1389
+ (descr, True) if is_classmethod else (descr,),
1390
+ )
1391
+
1392
+
1393
+ class Static315CodeGenerator(Static314CodeGenerator, CinderCodeGenerator315):
1394
+ pass
1395
+
1396
+
1397
+ if sys.version_info >= (3, 15):
1398
+ StaticCodeGenerator = Static315CodeGenerator
1399
+ elif sys.version_info >= (3, 14):
1400
+ StaticCodeGenerator = Static314CodeGenerator
1401
+ elif sys.version_info >= (3, 12):
1402
+ StaticCodeGenerator = Static312CodeGenerator
1403
+ else:
1404
+ StaticCodeGenerator = Static310CodeGenerator