feat(imports): 支持模块级 cif 条件 import
- ImportsGenMixin 新增 translate_cif_imports / _collect_cif_imports_in: 用 cif 求值只收集选中分支的 Import/ImportFrom,celif 的 orelse 递归求值, celse 按普通语句收集;普通模块级 if 原样忽略 - Translator._translate_module_level 的 import 预扫描新增 If 分支(mode 0) - TranslatorGenMixin.translate_children 在 declare/import-scan 模式补收集(mode != 0), 避免与预扫描重复写入 from_imports - GenApi 增加 translate_cif_imports 转发 未选中分支的模块不进入依赖闭包,因此不会被编译/链接, 可用于在平台专有模块之间二选一(如 win32.fileio / posix.fileio)。
This commit is contained in:
1 parent
a20bc312ab
commit
a2829182b8
4 files changed
+75
-11
No files matched your search
@@ -899,6 +899,13 @@ def translate_import(trans: HT.Translator*, node: ast.AST*) -> int:
|
||||
return 0
|
||||
|
||||
|
||||
# ---------------- translate_cif_imports (delegates to ImportsGenMixin.translate_cif_imports) ----------------
|
||||
def translate_cif_imports(trans: HT.Translator*, node: ast.AST*) -> int:
|
||||
"""Delegate to gen.translate_cif_imports (module-level cif conditional imports)"""
|
||||
if _g_gen is not None: return _g_gen.ImportsGen.translate_cif_imports(trans, node)
|
||||
return 0
|
||||
|
||||
|
||||
# ---------------- translate_import_from_module (delegates to ImportsGenMixin.translate_import_from_module) ----------------
|
||||
def translate_import_from_module(trans: HT.Translator*, node: ast.AST*) -> int:
|
||||
"""Delegate to gen.translate_import_from_module"""
|
||||
|
||||
@@ -690,6 +690,43 @@ novirtual class ImportsGenMixin:
|
||||
if trans.ModuleSha1 is not None: self.register_reexport(trans.ModuleSha1, local_name, fi_target)
|
||||
return 0
|
||||
|
||||
# translate_cif_imports - 模块级 cif 条件 import 的预扫描。
|
||||
# cif/celif/celse 在编译期求值,只有**选中分支**里的 Import/ImportFrom 会进入
|
||||
# _imported_modules/_from_imports;未选中分支的模块不进入依赖闭包,也不会被编译/链接,
|
||||
# 因此可以安全地用 cif 在平台专有模块之间二选一。普通模块级 if 不受理(原样忽略)。
|
||||
def translate_cif_imports(self, trans: HT.Translator*, node: ast.AST*) -> int:
|
||||
"""Collect the taken branch's imports from a module-level cif; returns how many were registered."""
|
||||
if trans is None or node is None or node.kind() != ast.ASTKind.If: return 0
|
||||
ifn: ast.If* = ast.If*(node)
|
||||
if ifn is None or ifn.test is None: return 0
|
||||
if self.Gen.StmtGenCif._is_cif_call(ifn.test) == 0: return 0
|
||||
taken: int = self.Gen.StmtGenCif._prune_cif_test(trans.Pool, trans, ifn.test)
|
||||
if taken != 0: return self._collect_cif_imports_in(trans, ifn.children)
|
||||
return self._collect_cif_imports_in(trans, ifn.orelse)
|
||||
|
||||
# _collect_cif_imports_in - 递归收集一个分支体内的 import。
|
||||
# celif 的 orelse 是嵌套 If(条件仍需按 cif 求值),celse 的 orelse 是普通语句列表;
|
||||
# 其余语句(define/函数/赋值)在模块级 import 预扫描阶段一律跳过。
|
||||
def _collect_cif_imports_in(self, trans: HT.Translator*, stmts: list[ast.AST*]*) -> int:
|
||||
"""Walk one cif branch body: register imports, recurse into nested celif chains."""
|
||||
if stmts is None: return 0
|
||||
n: int = 0
|
||||
cnt: sizet = len(stmts)
|
||||
for i in range(int(cnt)):
|
||||
st: ast.AST* = stmts.get(i)
|
||||
if st is None: continue
|
||||
k: int = st.kind()
|
||||
if k == ast.ASTKind.Import:
|
||||
self.translate_import(trans, st)
|
||||
n += 1
|
||||
elif k == ast.ASTKind.ImportFrom:
|
||||
self.translate_import_from_module(trans, st)
|
||||
self.translate_import_from_names(trans, st)
|
||||
n += 1
|
||||
elif k == ast.ASTKind.If:
|
||||
n += self.translate_cif_imports(trans, st)
|
||||
return n
|
||||
|
||||
def InitImportsGen(self) -> int:
|
||||
"""Initialize the ImportsGen fields, called by Generator at construction."""
|
||||
if self is None: return 1
|
||||
|
||||
@@ -30,7 +30,7 @@ define MAX_VARS = 1024
|
||||
# Function-table capacity (single source of truth). Allocation happens in _init_state;
|
||||
# the registration-side max_count passed to add_func_to_table must be <= this capacity.
|
||||
define MAX_FUNCS = 1024
|
||||
# Reference convention: cross-module t.CDefine is readable only inside functions, so write
|
||||
# Reference convention: cross-module t.DefineT is readable only inside functions, so write
|
||||
# qualified names there; the global/nonlocal name-table capacity must match on both guard sides.
|
||||
define MAX_GLOBAL_NAMES = 128
|
||||
# Coroutine table capacity (VPC-M1): async function count / coroutine var and local-name count
|
||||
@@ -208,6 +208,11 @@ novirtual class Translator:
|
||||
# Chain-nonlocal heap built flag (built once per function): reset to 0 on entering a function,
|
||||
# set to 1 by _prepare_chain_nonlocals. Shared by nested definitions to avoid double malloc.
|
||||
_nl_chain_done: int
|
||||
# 每个 chain nonlocal 名字是否**已经**迁移到堆(0/1,与 _nl_chain_names 同下标)。
|
||||
# 迁移不止发生一次:函数体开头先整体扫一遍(必须早于任何语句,否则先交出去的
|
||||
# `&local` 会指向栈副本),之后再遇到嵌套 def 时只补扫新名字 —— 靠这张表跳过的已迁移项,
|
||||
# 避免同一个对象被二次搬走(那会让上一次交出去的地址再次失效)。
|
||||
_nl_migrated: rawptr
|
||||
|
||||
# Generic specialization context: type parameter and argument name lists of the class
|
||||
# currently being specialized. Set by _specialize_generic_class; None when not specializing.
|
||||
@@ -217,6 +222,11 @@ novirtual class Translator:
|
||||
# Phase 1a declare mode: 1=register struct/enum/union only (no body), 0=full translation
|
||||
_declare_only: int
|
||||
|
||||
# Expression-statement context: 1 = the surrounding statement discards the call result
|
||||
# (e.g. a bare `VLogger.error(...)` line). A placeholder return type is then harmless,
|
||||
# so cross-module return-type resolution must not fail-fast in this context.
|
||||
_discard_result: int
|
||||
|
||||
# App pre-registration (Phase A-pre) switch: when 1, imports and LoadDepVpiIntoModule run even
|
||||
# if _declare_only != 0, keeping class field annotations in phase A's context. App-only.
|
||||
_pre_allow_dep_inject: int
|
||||
@@ -264,6 +274,7 @@ novirtual class Translator:
|
||||
self._nl_chain_names = None
|
||||
self._nl_chain_count = 0
|
||||
self._nl_chain_done = 0
|
||||
self._nl_migrated = None
|
||||
self.GenericTypeParamNames = None
|
||||
self.GenericTypeArgs = None
|
||||
self._declare_only = 0
|
||||
@@ -522,13 +533,17 @@ novirtual class Translator:
|
||||
cur_imp_lineno = child.lineno
|
||||
GenApi.translate_import_from_module(self, child)
|
||||
GenApi.translate_import_from_names(self, child)
|
||||
elif kd == ast.ASTKind.If:
|
||||
# 模块级 cif 条件 import:按平台常量只收集选中分支(未选中分支的模块
|
||||
# 不进依赖闭包,因此不会被编译/链接);普通 if 由 collect 函数自行忽略。
|
||||
GenApi.translate_cif_imports(self, child)
|
||||
# Mirror TPC _inject_auto_imports: detect list/dict container usage (bare annotations,
|
||||
# constructors, subscripts) and mark the container classes visible so bare names resolve.
|
||||
self._inject_auto_container_visibility(pool, tree)
|
||||
# A module using `assert` behaves as if it had `import testcheck`: inject the module
|
||||
# before dependency-vpi loading so testcheck.check resolves across modules.
|
||||
self._inject_auto_assert_import(pool, tree)
|
||||
# CTypedef declarations are registered by Phase1 text scanning (AST string tokens are unstable).
|
||||
# TypedefT declarations are registered by Phase1 text scanning (AST string tokens are unstable).
|
||||
# Cross-module .vpi injection loads dep .vpi declares when full-translating or during App pre-registration.
|
||||
if self._imported_modules is not None:
|
||||
# Error-context frame: import site that triggered dependency stub loading
|
||||
@@ -598,6 +613,15 @@ novirtual class Translator:
|
||||
if tk == ast.ASTKind.Attribute:
|
||||
at: ast.Attribute* = ast.Attribute*(target)
|
||||
if at is None or at.value is None: return 0
|
||||
# Nested form `proj.advanced.<field> = ...`: erase the whole statement as well.
|
||||
if at.value.kind() == ast.ASTKind.Attribute:
|
||||
mid: ast.Attribute* = ast.Attribute*(at.value)
|
||||
if mid is None or mid.value is None: return 0
|
||||
if mid.value.kind() != ast.ASTKind.Name: return 0
|
||||
mn: ast.Name* = ast.Name*(mid.value)
|
||||
if mn is None or mn.id is None: return 0
|
||||
if self._vb_in_proj_vars(mn.id, proj_vars) != 0: return 1
|
||||
return 0
|
||||
if at.value.kind() != ast.ASTKind.Name: return 0
|
||||
bn: ast.Name* = ast.Name*(at.value)
|
||||
if bn is None or bn.id is None: return 0
|
||||
@@ -609,15 +633,6 @@ novirtual class Translator:
|
||||
if value is None or value.kind() != ast.ASTKind.Call: return 0
|
||||
cl: ast.Call* = ast.Call*(value)
|
||||
if cl is None or cl.func is None: return 0
|
||||
# Nested form `proj.advanced.<field> = ...`: erase the whole statement as well.
|
||||
if at.value.kind() == ast.ASTKind.Attribute:
|
||||
mid: ast.Attribute* = ast.Attribute*(at.value)
|
||||
if mid is None or mid.value is None: return 0
|
||||
if mid.value.kind() != ast.ASTKind.Name: return 0
|
||||
mn: ast.Name* = ast.Name*(mid.value)
|
||||
if mn is None or mn.id is None: return 0
|
||||
if self._vb_in_proj_vars(mn.id, proj_vars) != 0: return 1
|
||||
return 0
|
||||
if cl.func.kind() != ast.ASTKind.Attribute: return 0
|
||||
at: ast.Attribute* = ast.Attribute*(cl.func)
|
||||
if at is None or at.attr is None or at.value is None: return 0
|
||||
|
||||
@@ -60,6 +60,11 @@ novirtual class TranslatorGenMixin:
|
||||
elif kd == ast.ASTKind.ImportFrom:
|
||||
self.Gen.ImportsGen.translate_import_from_module(trans, child)
|
||||
self.Gen.ImportsGen.translate_import_from_names(trans, child)
|
||||
elif kd == ast.ASTKind.If:
|
||||
# 模块级 cif 条件 import:只在 import-scan / declare 模式下补收集
|
||||
# (_declare_only == 0 时 _translate_module_level 的预扫描已经收过,重复会写出
|
||||
# 重复的 from_imports 条目)。
|
||||
if trans._declare_only != 0: self.Gen.ImportsGen.translate_cif_imports(trans, child)
|
||||
elif kd == ast.ASTKind.FunctionDef:
|
||||
# PEP 695 generic functions (def f[T]): skip at the definition site (the
|
||||
# template is registered by Phase1Scan/Phase2) and monomorphize at call sites.
|
||||
|
||||
Reference in new issue
Block a user