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,666 @@
1
+ # Copyright (c) Meta Platforms, Inc. and affiliates.
2
+
3
+ # pyre-strict
4
+
5
+ from __future__ import annotations
6
+
7
+ import ast
8
+ from ast import AST, Attribute, BinOp, Call, ClassDef, Constant, Name, Subscript
9
+ from contextlib import nullcontext
10
+ from enum import Enum
11
+ from typing import cast, ContextManager, Optional, TYPE_CHECKING
12
+
13
+ from ..errors import TypedSyntaxError
14
+ from ..symbols import ModuleScope, Scope
15
+ from .types import (
16
+ Callable,
17
+ Class,
18
+ ClassVar,
19
+ CType,
20
+ DataclassDecorator,
21
+ DynamicClass,
22
+ ExactClass,
23
+ FinalClass,
24
+ Function,
25
+ FunctionGroup,
26
+ InitVar,
27
+ KnownBoolean,
28
+ MethodType,
29
+ ModuleInstance,
30
+ NativeDecorator,
31
+ TType,
32
+ TypeDescr,
33
+ UnionType,
34
+ UnknownDecoratedMethod,
35
+ Value,
36
+ )
37
+ from .visitor import GenericVisitor
38
+
39
+ if TYPE_CHECKING:
40
+ from .compiler import Compiler
41
+
42
+
43
+ # When set to True, allow type alises of the form
44
+ # A = Foo
45
+ # as well as the explicit 3.12+
46
+ # type A = Foo
47
+ # When set to False, only the latter form will be supported.
48
+ #
49
+ # TASK(T214139735): This is a placeholder for a feature flag that will let
50
+ # users opt in to implicit type aliases on a per-module basis.
51
+ ENABLE_IMPLICIT_TYPE_ALIASES = False
52
+
53
+
54
+ class ModuleFlag(Enum):
55
+ CHECKED_DICTS = 1
56
+ CHECKED_LISTS = 3
57
+
58
+
59
+ class ModuleTableException(Exception):
60
+ pass
61
+
62
+
63
+ class ReferenceVisitor(GenericVisitor[Optional[Value]]):
64
+ def __init__(self, module: ModuleTable) -> None:
65
+ super().__init__(module)
66
+ self.subscr_nesting = 0
67
+ self.local_names: dict[str, Value] = {}
68
+
69
+ def visitName(self, node: Name) -> Value | None:
70
+ if node.id in self.local_names:
71
+ return self.local_names[node.id]
72
+ return self.module.get_child(
73
+ node.id, self.context_qualname, force_decl=self.force_decl_deps
74
+ ) or self.module.compiler.builtins.get_child_intrinsic(node.id)
75
+
76
+ def visitAttribute(self, node: Attribute) -> Value | None:
77
+ val = self.visit(node.value)
78
+ if val is not None:
79
+ return val.resolve_attr(node, self)
80
+
81
+ def add_local_name(self, name: str, value: Value) -> None:
82
+ self.local_names[name] = value
83
+
84
+ def clear_local_names(self) -> None:
85
+ self.local_names = {}
86
+
87
+
88
+ class AnnotationVisitor(ReferenceVisitor):
89
+ def resolve_annotation(
90
+ self,
91
+ node: ast.AST,
92
+ *,
93
+ is_declaration: bool = False,
94
+ ) -> Class | None:
95
+ with self.error_context(node):
96
+ klass = self.visit(node)
97
+ if not isinstance(klass, Class):
98
+ return None
99
+
100
+ if self.subscr_nesting or not is_declaration:
101
+ if isinstance(klass, FinalClass):
102
+ raise TypedSyntaxError(
103
+ "Final annotation is only valid in initial declaration "
104
+ "of attribute or module-level constant",
105
+ )
106
+ if isinstance(klass, ClassVar):
107
+ raise TypedSyntaxError(
108
+ "ClassVar is allowed only in class attribute annotations. "
109
+ "Class Finals are inferred ClassVar; do not nest with Final."
110
+ )
111
+ if isinstance(klass, InitVar):
112
+ raise TypedSyntaxError(
113
+ "InitVar is allowed only in class attribute annotations."
114
+ )
115
+
116
+ if isinstance(klass, ExactClass):
117
+ klass = klass.unwrap().exact_type()
118
+ elif isinstance(klass, FinalClass):
119
+ pass
120
+ else:
121
+ klass = klass.inexact_type()
122
+ # PEP-484 specifies that ints should be treated as a subclass of floats,
123
+ # even though they differ in the runtime. We need to maintain the distinction
124
+ # between the two internally, so we should view user-specified `float` annotations
125
+ # as `float | int`. This widening of the type prevents us from applying
126
+ # optimizations to user-specified floats, but does not affect ints. Since we
127
+ # don't optimize Python floats anyway, we accept this to maintain PEP-484 compatibility.
128
+
129
+ if klass.unwrap() is self.type_env.float:
130
+ klass = self.compiler.type_env.get_union(
131
+ (self.type_env.float, self.type_env.int)
132
+ )
133
+
134
+ # TODO until we support runtime checking of unions, we must for
135
+ # safety resolve union annotations to dynamic (except for
136
+ # optionals, which we can check at runtime)
137
+ if self.is_unsupported_union_type(klass):
138
+ return None
139
+
140
+ # Types like typing.NamedTuple or typing.Protocol should not be
141
+ # used as annotations; if they are, fall back to dynamic rather
142
+ # than raise an error (see T186572841 for context).
143
+
144
+ # TASK(T186572841): Extend to Protocol as well
145
+ special_types = ("NamedTuple", "TypedDict")
146
+ if klass.type_name.module == "typing":
147
+ if klass.type_name.qualname in special_types:
148
+ return self.type_env.dynamic
149
+
150
+ return klass
151
+
152
+ def is_unsupported_union_type(self, klass: Value) -> bool:
153
+ """Returns True if klass is an unsupported union type."""
154
+ return (
155
+ isinstance(klass, UnionType)
156
+ and klass is not self.type_env.union
157
+ and klass is not self.type_env.optional
158
+ and klass.opt_type is None
159
+ )
160
+
161
+ def visitSubscript(self, node: Subscript) -> Value | None:
162
+ target = self.resolve_annotation(node.value, is_declaration=True)
163
+ if target is None:
164
+ return None
165
+
166
+ self.subscr_nesting += 1
167
+ slice = self.visit(node.slice) or self.type_env.DYNAMIC
168
+ self.subscr_nesting -= 1
169
+ return target.resolve_subscr(node, slice, self) or target
170
+
171
+ def visitBinOp(self, node: BinOp) -> Value | None:
172
+ if isinstance(node.op, ast.BitOr):
173
+ ltype = self.resolve_annotation(node.left)
174
+ rtype = self.resolve_annotation(node.right)
175
+ if ltype is None or rtype is None:
176
+ return None
177
+ return self.module.compiler.type_env.get_union((ltype, rtype))
178
+
179
+ def visitConstant(self, node: Constant) -> Value | None:
180
+ sval = node.value
181
+ if sval is None:
182
+ return self.type_env.none
183
+ elif isinstance(sval, str):
184
+ n = ast.parse(node.value, "", "eval").body
185
+ return self.visit(n)
186
+
187
+
188
+ class DepTrackingOptOut:
189
+ """A reason for opting out of module dependency tracking.
190
+
191
+ This helps ensure our dependency tracking is complete; we can't request type
192
+ information from a module via ModuleTable.get_child(...) without either
193
+ providing a `requester` (to track the dependency) or providing an opt-out
194
+ reason.
195
+ """
196
+
197
+ def __init__(self, reason: str) -> None:
198
+ self.reason = reason
199
+
200
+
201
+ INTRINSIC_OPT_OUT = DepTrackingOptOut(
202
+ "We don't need to track dependencies to intrinsic modules; "
203
+ "they won't change during development and require pyc updates. "
204
+ "If they change, we should bump the static pyc magic number."
205
+ )
206
+ DEFERRED_IMPORT_OPT_OUT = DepTrackingOptOut("Tracked in ModuleTable.declare_import()")
207
+
208
+
209
+ class DeferredImport:
210
+ def __init__(
211
+ self,
212
+ in_module: ModuleTable,
213
+ mod_to_import: str,
214
+ asname: str,
215
+ optimize: int,
216
+ compiler: Compiler,
217
+ name: str | None = None,
218
+ mod_to_return: str | None = None,
219
+ ) -> None:
220
+ self.in_module = in_module
221
+ self.mod_to_import = mod_to_import
222
+ self.asname = asname
223
+ self.name = name
224
+ self.mod_to_return = mod_to_return
225
+ self.compiler = compiler
226
+ self.optimize = optimize
227
+
228
+ def import_mod(self, mod_name: str) -> ModuleTable | None:
229
+ return self.compiler.import_module(mod_name, optimize=self.optimize)
230
+
231
+ def resolve(self) -> Value:
232
+ if self.mod_to_import not in self.compiler.modules:
233
+ self.import_mod(self.mod_to_import)
234
+ mod = self.compiler.modules.get(self.mod_to_import)
235
+ if mod is not None:
236
+ if self.name is None:
237
+ return ModuleInstance(
238
+ self.mod_to_return or self.mod_to_import, self.compiler
239
+ )
240
+ val = mod.get_child(self.name, DEFERRED_IMPORT_OPT_OUT)
241
+ if val is not None:
242
+ return val
243
+ try_mod = f"{self.mod_to_import}.{self.name}"
244
+ self.import_mod(try_mod)
245
+ if try_mod in self.compiler.modules:
246
+ return ModuleInstance(try_mod, self.compiler)
247
+ if not mod.first_pass_done:
248
+ raise ModuleTableException(
249
+ f"Cannot find {self.mod_to_import}.{self.name} due to cyclic reference"
250
+ )
251
+ # For unknown modules, we don't need to record each individual object
252
+ # used from them; it doesn't matter, as we won't track transitive deps
253
+ # across them. We just need to record a dependency to the module in
254
+ # general.
255
+ self.in_module.record_dependency(self.asname, (self.mod_to_import, "<any>"))
256
+ return self.compiler.type_env.DYNAMIC
257
+
258
+
259
+ class UnknownModule:
260
+ def __init__(self, name: str) -> None:
261
+ self.name = name
262
+
263
+
264
+ class ModuleTable:
265
+ def __init__(
266
+ self,
267
+ name: str,
268
+ filename: str,
269
+ compiler: Compiler,
270
+ members: dict[str, Value] | None = None,
271
+ first_pass_done: bool = True,
272
+ ) -> None:
273
+ self.name = name
274
+ self.filename = filename
275
+ self._children: dict[str, Value | DeferredImport] = {}
276
+ if members is not None:
277
+ self._children.update(members)
278
+ self.compiler = compiler
279
+ self.types: dict[AST, Value] = {}
280
+ self.node_data: dict[tuple[AST, object], object] = {}
281
+ self.flags: set[ModuleFlag] = set()
282
+ self.decls: list[tuple[AST, str | None, Value | None]] = []
283
+ self.implicit_decl_names: set[str] = set()
284
+ self.type_alias_names: set[str] = set()
285
+ self.compile_non_static: set[AST] = set()
286
+ # {local-name: {(mod, qualname)}} for decl-time deps
287
+ self.decl_deps: dict[str, set[tuple[str, str]]] = {}
288
+ # {local-name: {(mod, qualname)}} for bind-time deps
289
+ self.bind_deps: dict[str, set[tuple[str, str]]] = {}
290
+ # (source module, source name) for every name this module imports-from
291
+ # another static module at top level
292
+ self.imported_from: dict[str, tuple[str, str]] = {}
293
+ # TODO: final constants should be typed to literals, and
294
+ # this should be removed in the future
295
+ self.named_finals: dict[str, ast.Constant] = {}
296
+ # Have we completed our first pass through the module, populating
297
+ # imports and types defined in the module? Until we have, resolving
298
+ # type annotations is not safe.
299
+ self.first_pass_done = first_pass_done
300
+ self.finish_bind_done = False
301
+ self.ann_visitor = AnnotationVisitor(self)
302
+ self.ref_visitor = ReferenceVisitor(self)
303
+ # the prefix children will get on their qualname; always None for
304
+ # modules (the qualname of a class or function is within-module, it
305
+ # doesn't include the module name)
306
+ self.qualname: None = None
307
+
308
+ def __repr__(self) -> str:
309
+ return f"<ModuleTable {self.name}>"
310
+
311
+ def get_dependencies(self) -> set[ModuleTable | UnknownModule]:
312
+ """Return all modules this module depends on."""
313
+ # We only include bind deps for the current module; for transitive deps
314
+ # we follow only decl_deps
315
+ immediate_deps = {**self.bind_deps, **self.decl_deps}
316
+ all_modules_deps = {
317
+ name: mod.decl_deps for name, mod in self.compiler.modules.items()
318
+ }
319
+ all_modules_deps[self.name] = immediate_deps
320
+ dep_names = find_transitive_deps(self.name, all_modules_deps)
321
+ return {
322
+ self.compiler.modules.get(name) or UnknownModule(name) for name in dep_names
323
+ }
324
+
325
+ def record_dependency(
326
+ self, name: str, target: tuple[str, str], force_decl: bool = False
327
+ ) -> None:
328
+ """Record a dependency from a local name to a name in another module.
329
+
330
+ `name` is the name in this module's namespace. `target` is a (str, str)
331
+ tuple of (source_module, source_name).
332
+
333
+ """
334
+ deps = (
335
+ self.bind_deps
336
+ if (self.finish_bind_done and not force_decl)
337
+ else self.decl_deps
338
+ )
339
+ deps.setdefault(name, set()).add(target)
340
+
341
+ def get_child(
342
+ self, name: str, requester: str | DepTrackingOptOut, force_decl: bool = False
343
+ ) -> Value | None:
344
+ if not isinstance(requester, DepTrackingOptOut):
345
+ # Using imported_from here is just an optimization that lets us skip
346
+ # one level of transitive-dependency-following later. If modA has
347
+ # "from modB import B", we already record `modA.B -> modB.B`, so
348
+ # technically if `modA.f -> modA.B`, we could just record that
349
+ # dependency, and following the transitive closure would later give
350
+ # us `modA.f -> modB.B`. But since we have the `imported_from` data
351
+ # available, we use it to just record `modA.f -> modB.B` initially.
352
+ # We don't need `modA.f -> modA.B` recorded as a dependency in that
353
+ # case, since intra-module dependencies have no direct impact on
354
+ # recompilation, they are only needed for following the transitive
355
+ # chain across modules.
356
+ source = self.imported_from.get(name, (self.name, name))
357
+ self.record_dependency(requester, source, force_decl)
358
+ res = self._children.get(name)
359
+ if isinstance(res, DeferredImport):
360
+ self._children[name] = res = res.resolve()
361
+ return res
362
+
363
+ def syntax_error(self, msg: str, node: AST) -> None:
364
+ return self.compiler.error_sink.syntax_error(msg, self.filename, node)
365
+
366
+ def error_context(self, node: AST | None) -> ContextManager[None]:
367
+ if node is None:
368
+ return nullcontext()
369
+ return self.compiler.error_sink.error_context(self.filename, node)
370
+
371
+ def maybe_set_type_alias(
372
+ self,
373
+ # pyre-ignore[11]: Annotation `ast.TypeAlias` is not defined as a type
374
+ node: ast.Assign | ast.TypeAlias,
375
+ name: str,
376
+ *,
377
+ require_type: bool = False,
378
+ ) -> None:
379
+ """
380
+ Check if we are assigning a Class or Union value to a variable at
381
+ module scope, and if so, store it as a type alias.
382
+ """
383
+ value = self.resolve_type(node.value, name)
384
+ if isinstance(value, Class):
385
+ # This is a type, treat it similarly to a class declaration
386
+ self.decls.append((node, name, value))
387
+ self._children[name] = value
388
+ self.type_alias_names.add(name)
389
+ else:
390
+ # Treat the type as dynamic if it is an assignment,
391
+ # raise an error if it is an explicit type alias.
392
+ if require_type:
393
+ rhs = ast.unparse(node.value)
394
+ raise TypedSyntaxError(f"RHS of type alias {name} is not a type: {rhs}")
395
+ self.implicit_decl_names.add(name)
396
+
397
+ # pyre-ignore[11]: Annotation `ast.TypeAlias` is not defined as a type
398
+ def declare_type_alias(self, node: ast.TypeAlias) -> None:
399
+ self.maybe_set_type_alias(node, node.name.id, require_type=True)
400
+
401
+ def declare_class(self, node: ClassDef, klass: Class) -> None:
402
+ if self.first_pass_done:
403
+ raise ModuleTableException(
404
+ "Attempted to declare a class after the declaration visit"
405
+ )
406
+ self.decls.append((node, node.name, klass))
407
+ self._children[node.name] = klass
408
+
409
+ def declare_function(self, func: Function) -> None:
410
+ if self.first_pass_done:
411
+ raise ModuleTableException(
412
+ "Attempted to declare a function after the declaration visit"
413
+ )
414
+ existing = self._children.get(func.func_name)
415
+ new_member = func
416
+ if existing is not None:
417
+ if isinstance(existing, Function):
418
+ new_member = FunctionGroup([existing, new_member], func.klass.type_env)
419
+ elif isinstance(existing, FunctionGroup):
420
+ existing.functions.append(new_member)
421
+ new_member = existing
422
+ else:
423
+ raise TypedSyntaxError(
424
+ f"function conflicts with other member {func.func_name} in {self.name}"
425
+ )
426
+
427
+ self.decls.append((func.node, func.func_name, new_member))
428
+ self._children[func.func_name] = new_member
429
+
430
+ def _get_inferred_type(self, value: ast.expr, requester: str) -> Value | None:
431
+ if not isinstance(value, ast.Name):
432
+ return None
433
+ return self.get_child(value.id, requester)
434
+
435
+ def finish_bind(self) -> None:
436
+ self.first_pass_done = True
437
+ for node, name, value in self.decls:
438
+ with self.error_context(node):
439
+ if value is not None:
440
+ assert name is not None
441
+ new_value = value.finish_bind(self, None)
442
+ if new_value is None:
443
+ # We still need to maintain a name for unknown decorated methods since they
444
+ # will bind to some name, and we want to avoid throwing unknown name errors.
445
+ if isinstance(self.types[node], UnknownDecoratedMethod):
446
+ self._children[name] = self.compiler.type_env.DYNAMIC
447
+ else:
448
+ del self._children[name]
449
+ elif new_value is not value:
450
+ self._children[name] = new_value
451
+
452
+ if isinstance(node, ast.AnnAssign) and isinstance(
453
+ node.target, ast.Name
454
+ ):
455
+ # isinstance(target := node.target, ast.Name) above would be
456
+ # nicer, but pyre doesn't narrow the type of target :/
457
+ target = node.target
458
+ assert isinstance(target, ast.Name)
459
+ typ = self.resolve_annotation(
460
+ node.annotation, target.id, is_declaration=True
461
+ )
462
+ if typ is not None:
463
+ # Special case Final[dynamic] to use inferred type.
464
+ instance = typ.instance
465
+ value = node.value
466
+ if isinstance(typ, FinalClass):
467
+ # We keep track of annotated finals in the
468
+ # named_finals field - we can safely remove that
469
+ # information here to ensure that the rest of the
470
+ # type system can safely ignore it.
471
+ unwrapped = typ.unwrap()
472
+ if value is not None and isinstance(
473
+ unwrapped, DynamicClass
474
+ ):
475
+ instance = (
476
+ self._get_inferred_type(value, target.id)
477
+ or unwrapped.instance
478
+ )
479
+ else:
480
+ instance = unwrapped.instance
481
+
482
+ self._children[target.id] = instance
483
+
484
+ if isinstance(typ, FinalClass):
485
+ value = node.value
486
+ if not value:
487
+ raise TypedSyntaxError(
488
+ "Must assign a value when declaring a Final"
489
+ )
490
+ elif not isinstance(typ, CType) and isinstance(
491
+ value, ast.Constant
492
+ ):
493
+ self.named_finals[target.id] = value
494
+
495
+ for name in self.implicit_decl_names:
496
+ if name not in self._children:
497
+ self._children[name] = self.compiler.type_env.DYNAMIC
498
+ # We don't need these anymore...
499
+ self.implicit_decl_names.clear()
500
+ self.finish_bind_done = True
501
+
502
+ def validate_overrides(self) -> None:
503
+ for _node, name, _value in self.decls:
504
+ if name is None or name in self.type_alias_names:
505
+ continue
506
+
507
+ child = self._children.get(name, None)
508
+ if isinstance(child, Value):
509
+ child.validate_overrides(self, None)
510
+
511
+ self.decls.clear()
512
+
513
+ def resolve_type(self, node: ast.AST, requester: str) -> Class | None:
514
+ with self.ann_visitor.temporary_context_qualname(requester):
515
+ try:
516
+ typ = self.ann_visitor.visit(node)
517
+ except SyntaxError:
518
+ # We resolve types via the AnnotationVisitor, which expects
519
+ # string literals to parse as well-formed python type
520
+ # expressions. In the more general case here (where we are
521
+ # resolving a node that is not necessarily an annotation in the
522
+ # source code) the visitor will try to parse arbitrary string
523
+ # literals as python code, and will raise a SyntaxError.
524
+ return None
525
+ if isinstance(typ, Class):
526
+ return typ
527
+
528
+ def resolve_decorator(self, node: ast.AST) -> Value | None:
529
+ if isinstance(node, Call):
530
+ func = self.ref_visitor.visit(node.func)
531
+ if isinstance(func, Class):
532
+ return func.instance
533
+ elif isinstance(func, DataclassDecorator):
534
+ return func
535
+ elif isinstance(func, NativeDecorator):
536
+ return func
537
+ elif isinstance(func, Callable):
538
+ return func.return_type.resolved().instance
539
+ elif isinstance(func, MethodType):
540
+ return func.function.return_type.resolved().instance
541
+
542
+ return self.ref_visitor.visit(node)
543
+
544
+ def resolve_annotation(
545
+ self,
546
+ node: ast.AST,
547
+ requester: str,
548
+ *,
549
+ # annotation on a variable or attribute declaration (could be inside a
550
+ # function, thus not public)
551
+ is_declaration: bool = False,
552
+ # annotation on public API (also includes e.g. function arg/return
553
+ # annotations, does not include function-internal declarations)
554
+ is_decl_dep: bool = False,
555
+ ) -> Class | None:
556
+ with self.ann_visitor.temporary_context_qualname(requester, is_decl_dep):
557
+ return self.ann_visitor.resolve_annotation(
558
+ node, is_declaration=is_declaration
559
+ )
560
+
561
+ def resolve_name_with_descr(
562
+ self, name: str, requester: str
563
+ ) -> tuple[Value | None, TypeDescr | None]:
564
+ if val := self.get_child(name, requester):
565
+ return val, ((self.name,), name)
566
+ elif val := self.compiler.builtins.get_child_intrinsic(name):
567
+ return val, None
568
+ return None, None
569
+
570
+ def get_final_literal(self, node: AST, scope: Scope) -> ast.Constant | None:
571
+ if not isinstance(node, Name):
572
+ return None
573
+
574
+ final_val = self.named_finals.get(node.id, None)
575
+ if (
576
+ final_val is not None
577
+ and isinstance(node.ctx, ast.Load)
578
+ and (
579
+ # Ensure the name is not shadowed in the local scope
580
+ isinstance(scope, ModuleScope) or node.id not in scope.defs
581
+ )
582
+ ):
583
+ return final_val
584
+
585
+ def declare_import(
586
+ self, name: str, target: tuple[str, str] | None, val: Value | DeferredImport
587
+ ) -> None:
588
+ """Declare a name imported into this module.
589
+
590
+ `name` is the name in this module's namespace. `target` is a (str, str)
591
+ tuple of (source_module, source_name) for an `import from`. For a
592
+ top-level module import, `target` should be `None`.
593
+
594
+ """
595
+ if self.first_pass_done:
596
+ raise ModuleTableException(
597
+ "Attempted to declare an import after the declaration visit"
598
+ )
599
+ self._children[name] = val
600
+ if target is not None:
601
+ self.imported_from[name] = target
602
+ self.record_dependency(name, target)
603
+
604
+ def declare_variable(self, node: ast.AnnAssign, module: ModuleTable) -> None:
605
+ self.decls.append((node, None, None))
606
+
607
+ def declare_variables(self, node: ast.Assign, module: ModuleTable) -> None:
608
+ targets = node.targets
609
+ if (
610
+ ENABLE_IMPLICIT_TYPE_ALIASES
611
+ and len(targets) == 1
612
+ and isinstance(targets[0], ast.Name)
613
+ ):
614
+ # pyre-ignore[16]: `ast.expr` has no attribute `id`
615
+ return self.maybe_set_type_alias(node, targets[0].id)
616
+
617
+ for target in targets:
618
+ if isinstance(target, ast.Name):
619
+ self.implicit_decl_names.add(target.id)
620
+
621
+ def get_node_data(self, key: AST, data_type: type[TType]) -> TType:
622
+ return cast(TType, self.node_data[key, data_type])
623
+
624
+ def get_opt_node_data(self, key: AST, data_type: type[TType]) -> TType | None:
625
+ return cast(Optional[TType], self.node_data.get((key, data_type)))
626
+
627
+ def set_node_data(self, key: AST, data_type: type[TType], value: TType) -> None:
628
+ self.node_data[key, data_type] = value
629
+
630
+ def mark_known_boolean_test(self, node: ast.expr, *, value: bool) -> None:
631
+ """
632
+ For boolean tests that can be determined during decl-visit, we note the AST nodes
633
+ and the boolean value. This helps us avoid visiting dead code in later passes.
634
+ """
635
+ self.set_node_data(
636
+ node, KnownBoolean, KnownBoolean.TRUE if value else KnownBoolean.FALSE
637
+ )
638
+
639
+
640
+ class IntrinsicModuleTable(ModuleTable):
641
+ """A ModuleTable for modules that are intrinsic in the compiler."""
642
+
643
+ def get_child_intrinsic(self, name: str) -> Value | None:
644
+ return self.get_child(name, INTRINSIC_OPT_OUT)
645
+
646
+
647
+ def find_transitive_deps(
648
+ modname: str, all_deps: dict[str, dict[str, set[tuple[str, str]]]]
649
+ ) -> set[str]:
650
+ """Find all transitive dependency modules of `modname`.
651
+
652
+ Given an `alldeps` dictionary of {modname: {name: {(module, name)}}}, return
653
+ the transitive closure of module names depended on by `modname` (not
654
+ including `modname` itself).
655
+ """
656
+ worklist = {dep for deps in all_deps.get(modname, {}).values() for dep in deps}
657
+ ret = set()
658
+ seen = set()
659
+ while worklist:
660
+ dep = worklist.pop()
661
+ seen.add(dep)
662
+ mod, name = dep
663
+ ret.add(mod)
664
+ worklist.update(all_deps.get(mod, {}).get(name, set()).difference(seen))
665
+ ret.discard(modname)
666
+ return ret