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:
TermiNexus committed 2026-10-25 12:28:29 +08:00
1 parent a20bc312ab
commit a2829182b8
4 files changed
+75 -11

No files matched your search

+7
View File
@@ -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"""
+37
View File
@@ -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
+26 -11
View File
@@ -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
+5
View File
@@ -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.