refactor(app): win32.fileio 全部切换到跨平台 io
20 个文件把 'import win32.fileio as fileio' 改为 'import io as fileio', 文件 IO 统一走 includes/io.vp(编译期 cif 选择 win32.fileio / posix.fileio)。 依赖编译器新增的模块级 cif 条件 import(a282918)。 注:这些文件中存在此前未提交的既有改动,按整文件一并提交。
This commit is contained in:
1 parent
a2829182b8
commit
94515ddf12
20 files changed
+2184
-859
No files matched your search
@@ -6,7 +6,7 @@ import stdlib
|
||||
import memhub
|
||||
import viperlib
|
||||
import vthreading
|
||||
import win32.fileio as fileio
|
||||
import io as fileio
|
||||
import win32.win32file
|
||||
import win32.win32base
|
||||
import win32.win32process as win32process
|
||||
@@ -151,7 +151,9 @@ def TranslateFileGetTrans(mb: memhub.MemBuddy*, file_path: str, sha1_val: str, c
|
||||
"""Translate one file and return a Translator (caller dumps IR); None on failure."""
|
||||
if file_path is None: return None
|
||||
f: fileio.File* = fileio.File(file_path, fileio.MODE.R)
|
||||
if f.closed: return None
|
||||
# Fail-fast: a path that cannot be opened (e.g. an App source resolved against the wrong root)
|
||||
# used to return None silently, and the caller only reported "cannot be translated".
|
||||
if f.closed: VLogger.error(f"cannot open source file: {file_path}", "Translate")
|
||||
# read_all's capacity includes the terminator (reads cap-1 bytes max) and silently
|
||||
# truncates longer files; reject by size first, else parsing gets confusing syntax errors.
|
||||
if f.size() >= SRC_BUF_SIZE:
|
||||
@@ -161,12 +163,12 @@ def TranslateFileGetTrans(mb: memhub.MemBuddy*, file_path: str, sha1_val: str, c
|
||||
src_buf: bytes = stdlib.malloc(SRC_BUF_SIZE)
|
||||
if src_buf is None:
|
||||
f.close()
|
||||
return None
|
||||
VLogger.error(f"source buffer allocation failed (OOM): {file_path}", "Translate")
|
||||
bytes_read: LONG = f.read_all(src_buf, SRC_BUF_SIZE)
|
||||
f.close()
|
||||
if bytes_read <= 0:
|
||||
stdlib.free(src_buf)
|
||||
return None
|
||||
VLogger.error(f"source file read failed or empty: {file_path}", "Translate")
|
||||
if bytes_read < SRC_BUF_SIZE: src_buf[bytes_read] = 0
|
||||
lx: ast.Lexer* = ast.new_lexer(mb)
|
||||
ast._lexer_init(lx, src_buf, mb)
|
||||
@@ -183,7 +185,7 @@ def TranslateFileGetTrans(mb: memhub.MemBuddy*, file_path: str, sha1_val: str, c
|
||||
VLogger.error(f"parse failed: {file_path} (line {err_line} col {err_col})", "PARSE")
|
||||
return None
|
||||
# Generate the .vpi stub from the AST before tr.translate (same as Phase1); it lets
|
||||
# other modules query function signatures and CTypedef definitions.
|
||||
# other modules query function signatures and TypedefT definitions.
|
||||
if temp_dir is not None:
|
||||
pyi_buf: bytes = stdlib.malloc(PYI_BUF_SIZE)
|
||||
if pyi_buf is not None:
|
||||
@@ -235,9 +237,9 @@ def TranslateFileGetTrans(mb: memhub.MemBuddy*, file_path: str, sha1_val: str, c
|
||||
GenApi.set_current_file_imports(file_path)
|
||||
# clear the call stack (reset before each file translation)
|
||||
GenApi.clear_trace()
|
||||
# module switch: clear the CDefine constant table to isolate each module's constants
|
||||
# module switch: clear the DefineT constant table to isolate each module's constants
|
||||
GenApi.clear_cdefine_constants()
|
||||
# module switch: clear the CTypedef table to isolate each module's typedefs
|
||||
# module switch: clear the TypedefT table to isolate each module's typedefs
|
||||
GenApi.clear_ctypedefs()
|
||||
# namespace isolation: includes loose (all visible), user files strict (local + imports only)
|
||||
strict_mode: int = 1
|
||||
|
||||
@@ -3,7 +3,7 @@ import string
|
||||
import stdlib
|
||||
import memhub
|
||||
import viperlib
|
||||
import win32.fileio as fileio
|
||||
import io as fileio
|
||||
import win32.win32base
|
||||
import ast
|
||||
import mlirlite
|
||||
@@ -256,7 +256,7 @@ def RunPhase1(mb: memhub.MemBuddy*, includes_dir: str, temp_dir: str, log: VLogg
|
||||
VLogger.error("includes_dir or temp_dir is empty; cannot run Phase1", "Phase1")
|
||||
return 1
|
||||
|
||||
# Set the global temp_dir (used for cross-module CDefine lookup)
|
||||
# Set the global temp_dir (used for cross-module DefineT lookup)
|
||||
GenApi.set_temp_dir(temp_dir)
|
||||
# Also set HandlesExprCall's temp_dir (for the on-demand vpi return-annotation cache);
|
||||
# must run before includes translation since Phase2's SetTempDir is too late.
|
||||
@@ -362,7 +362,7 @@ def RunPhase1(mb: memhub.MemBuddy*, includes_dir: str, temp_dir: str, log: VLogg
|
||||
VLogger.info(f"[EVIDENCE] order(class_names {p1_sz_cn}B+16)={p1_o_cn} fl={p1_fl_cn} " f"order(def_sha1s {p1_sz_ds}B+16)={p1_o_ds} fl={p1_fl_ds} " f"order(topo {p1_sz_tp}B+16)={p1_o_tp} order(box {p1_sz_bx}B+16)={p1_o_bx} " f"(fl==0 only means that order is empty and can still be split by a higher order; if alloc fails while dump_stats shows free>0 that is the definitive proof of metadata corruption; " f"any order>max_order={mb.max_order} means an abnormal size)", "Phase1")
|
||||
|
||||
# Pre-scan class inheritance and build the topo order (flat byte arrays avoid str-pointer NULL crashes)
|
||||
# 取消整数提升后,模块属性 CDefine 按最小位宽物化为 i16/i8;512*64=32768 恰好越出 i16 有符号上限而回绕成负值,
|
||||
# 取消整数提升后,模块属性 DefineT 按最小位宽物化为 i16/i8;512*64=32768 恰好越出 i16 有符号上限而回绕成负值,
|
||||
# 首乘数显式 sizet() 加宽到 64 位。下面 def_sha1s_buf 的 512*17=8704 未溢出,无需改。
|
||||
class_names_buf: bytes = mb.alloc(sizet(Phase1.MAX_CLASSES) * Phase1.CLASS_NAME_LEN)
|
||||
parent_names_buf: bytes = mb.alloc(sizet(Phase1.MAX_CLASSES) * Phase1.CLASS_NAME_LEN)
|
||||
@@ -739,7 +739,13 @@ def RunPhase1(mb: memhub.MemBuddy*, includes_dir: str, temp_dir: str, log: VLogg
|
||||
if vpos_1b < vtotal_1b: vpos_1b += 1
|
||||
vi2_1b: sizet = vls_1b
|
||||
while vi2_1b < vls_1b + vll_1b and vbuf_1b[vi2_1b] == ' ': vi2_1b += 1
|
||||
if vll_1b >= 6 and string.strncmp(vbuf_1b + vi2_1b, "class ", 6) == 0:
|
||||
vcls_1b: int = 0
|
||||
if vll_1b >= 14 and string.strncmp(vbuf_1b + vi2_1b, "virtual class ", 14) == 0: vcls_1b = 1
|
||||
elif vll_1b >= 16 and string.strncmp(vbuf_1b + vi2_1b, "novirtual class ", 16) == 0: vcls_1b = 1
|
||||
elif vll_1b >= 6 and (string.strncmp(vbuf_1b + vi2_1b, "class ", 6) == 0 or string.strncmp(vbuf_1b + vi2_1b, "union ", 6) == 0 or string.strncmp(vbuf_1b + vi2_1b, "renum ", 6) == 0): vcls_1b = 1
|
||||
elif vll_1b >= 5 and string.strncmp(vbuf_1b + vi2_1b, "enum ", 5) == 0: vcls_1b = 1
|
||||
elif vll_1b >= 7 and string.strncmp(vbuf_1b + vi2_1b, "struct ", 7) == 0: vcls_1b = 1
|
||||
if vcls_1b != 0:
|
||||
force_translate = 1
|
||||
break
|
||||
stdlib.free(vbuf_1b)
|
||||
@@ -850,8 +856,8 @@ def RunPhase1(mb: memhub.MemBuddy*, includes_dir: str, temp_dir: str, log: VLogg
|
||||
# Timer 1: read + lexer/tokenize/parse done
|
||||
t_f1: uint64 = win32.win32base.GetTickCount64()
|
||||
|
||||
# Register CTypedef by scanning source text: AST string fields (attr/id) point into src_buf
|
||||
# and dangle across phases (reading attr faults), so match "NAME: t.CTypedef = TYPE" lines.
|
||||
# Register TypedefT by scanning source text: AST string fields (attr/id) point into src_buf
|
||||
# and dangle across phases (reading attr faults), so match "NAME: t.TypedefT = TYPE" lines.
|
||||
GenApi.RegisterCTypedefFromSource(mb, src_buf)
|
||||
|
||||
# Generate the .vpi stub (walk the AST directly). 1b-pre already generated one, so this
|
||||
|
||||
@@ -2,7 +2,7 @@ from stdint import *
|
||||
import string
|
||||
import stdlib
|
||||
import memhub
|
||||
import win32.fileio as fileio
|
||||
import io as fileio
|
||||
import ast
|
||||
import lib.core.VLogger as VLogger
|
||||
import lib.core.IncludesScanner as IncludesScanner
|
||||
|
||||
@@ -5,7 +5,7 @@ import string
|
||||
import stdlib
|
||||
import memhub
|
||||
import viperlib
|
||||
import win32.fileio as fileio
|
||||
import io as fileio
|
||||
import win32.win32file
|
||||
import win32.win32base
|
||||
import sys
|
||||
@@ -28,6 +28,7 @@ import lib.Stub.StubMerger.StubMergerIR as StubMergerIR
|
||||
import lib.Stub.StubMerger.StubMergerStore as StubMergerStore
|
||||
import lib.Stub.StubMerger.StubMergerTopo as StubMergerTopo
|
||||
import lib.Stub.StubGen.Converter as StubConverter
|
||||
import lib.Stub.StubGen.VpiReader as VpiReader
|
||||
|
||||
# Global mbuddy pointer
|
||||
_mbuddy: memhub.MemBuddy*
|
||||
@@ -564,12 +565,11 @@ def RunMultiFileProject(mb: memhub.MemBuddy*, source_dir: str, temp_dir: str, ou
|
||||
stdlib.free(rs_05)
|
||||
|
||||
# Append App source SHA1s to _sha1_map.txt (human-readable only; format {sha1}:{rel_path}) and to the
|
||||
# in-memory store (AppendToSha1MapStore) for cross-module CDefine lookups.
|
||||
td_len_am: sizet = string.strlen(temp_dir)
|
||||
# in-memory store (AppendToSha1MapStore) for cross-module DefineT lookups.
|
||||
src_dir_len_am: sizet = string.strlen(source_dir)
|
||||
map_path_am: bytes = stdlib.malloc(td_len_am + 32)
|
||||
# _sha1_map.txt 落在构建目录(temp 的上一级),方便直接按 SHA1 反查源文件
|
||||
map_path_am: bytes = StubMergerCollect.Sha1MapPath(mb, temp_dir)
|
||||
if map_path_am is not None:
|
||||
viperlib.snprintf(map_path_am, td_len_am + 32, "%s/_sha1_map.txt", temp_dir)
|
||||
mf: fileio.File* = fileio.File(map_path_am, fileio.MODE.A)
|
||||
line_am: bytes = stdlib.malloc(512)
|
||||
# First fill the in-memory store (no file I/O), then write _sha1_map.txt
|
||||
@@ -584,7 +584,7 @@ def RunMultiFileProject(mb: memhub.MemBuddy*, source_dir: str, temp_dir: str, ou
|
||||
rel_path_am: str = p_str
|
||||
if p_len > src_dir_len_am + 1:
|
||||
if string.strncmp(p_str, source_dir, src_dir_len_am) == 0: rel_path_am = p_str + src_dir_len_am + 1
|
||||
# Also append to the in-memory store (for cross-module CDefine lookups)
|
||||
# Also append to the in-memory store (for cross-module DefineT lookups)
|
||||
# rel_path_am looks like "lib/core/StubMerger.vp", matching _sha1_map.txt
|
||||
if StubMergerStore.AppendToSha1MapStore(ent_am.Sha1, rel_path_am) != 0: VLogger.error(f"AppendToSha1MapStore failed: {rel_path_am}", "project")
|
||||
app_filled += 1
|
||||
@@ -606,8 +606,13 @@ def RunMultiFileProject(mb: memhub.MemBuddy*, source_dir: str, temp_dir: str, ou
|
||||
# === Phase A-pre-vpi: pre-generate .vpi stubs for all App sources ===
|
||||
# Deps' .vpi must exist before their dependents translate; pre-generating avoids missing dep declarations.
|
||||
td_len_vpa: sizet = string.strlen(temp_dir)
|
||||
PYI_BUF_SIZE_P2: sizet = 262144
|
||||
# Reuse BuildPipeline's DefineT (proven to be non-zero: the same buffer size generates ViperBuild.vp's
|
||||
# vpi in Phase A). A large literal stored in a local used to come through as 0 here, and
|
||||
# _GeneratePyiFromAst then returns immediately (buf_size == 0) -> every App .vpi silently missing.
|
||||
PYI_BUF_SIZE_P2: sizet = BuildPipeline.PYI_BUF_SIZE
|
||||
vpi_failed_p2: int = 0
|
||||
vpi_written_p2: int = 0
|
||||
vpi_skipped_p2: int = 0
|
||||
for i_vp2 in range(int(file_count)):
|
||||
ea_vp2: uint64 = uint64(entries) + i_vp2 * entry_size
|
||||
ent_vp2: SrcFileEntry* = (SrcFileEntry*)(ea_vp2)
|
||||
@@ -624,6 +629,7 @@ def RunMultiFileProject(mb: memhub.MemBuddy*, source_dir: str, temp_dir: str, ou
|
||||
vf_vp2: fileio.File* = fileio.File(vpi_path_vp2, fileio.MODE.R)
|
||||
if not vf_vp2.closed:
|
||||
vf_vp2.close()
|
||||
vpi_skipped_p2 += 1
|
||||
stdlib.free(vpi_path_vp2)
|
||||
continue
|
||||
stdlib.free(vpi_path_vp2)
|
||||
@@ -676,13 +682,24 @@ def RunMultiFileProject(mb: memhub.MemBuddy*, source_dir: str, temp_dir: str, ou
|
||||
# 文件级名字窗口(FileFuncNames 等)由 _GeneratePyiFromAst 入口统一 reset+重建,
|
||||
# 不再在此处重复调用;契约见 StructGenMixin.reset_file_name_window。
|
||||
pyi_pos_vp2: sizet = StubConverter._GeneratePyiFromAst(mb, tree_vp2, ent_vp2.Path, pyi_buf_vp2, PYI_BUF_SIZE_P2)
|
||||
if pyi_pos_vp2 == 0:
|
||||
# The generator always emits at least a newline once buf_size > 1; 0 means the
|
||||
# buffer size collapsed (or the generator failed) and no .vpi would be produced —
|
||||
# every later cross-module lookup for this module would then fail.
|
||||
vpi_failed_p2 += 1
|
||||
VLogger.error(f"[Phase A-pre-vpi] {ent_vp2.Path} | vpi generation produced nothing (buf_size={PYI_BUF_SIZE_P2})", "PhaseA")
|
||||
if pyi_pos_vp2 > 0:
|
||||
pyi_path_vp2: str = StubMergerCollect._sliced_path(temp_dir, td_len_vpa, ent_vp2.Sha1, "vpi")
|
||||
if pyi_path_vp2 is not None:
|
||||
pf_vp2: fileio.File* = fileio.File(pyi_path_vp2, fileio.MODE.W)
|
||||
if not pf_vp2.closed:
|
||||
pf_vp2.write(pyi_buf_vp2, pyi_pos_vp2)
|
||||
pf_vp2.close()
|
||||
if pf_vp2.closed:
|
||||
# Silent here used to mean "no App .vpi at all" and every cross-module
|
||||
# return-type lookup later failed. Report the exact path instead.
|
||||
vpi_failed_p2 += 1
|
||||
VLogger.error(f"[Phase A-pre-vpi] {ent_vp2.Path} | cannot open the vpi for writing: {pyi_path_vp2}", "PhaseA")
|
||||
pf_vp2.write(pyi_buf_vp2, pyi_pos_vp2)
|
||||
pf_vp2.close()
|
||||
vpi_written_p2 += 1
|
||||
stdlib.free(pyi_path_vp2)
|
||||
stdlib.free(pyi_buf_vp2)
|
||||
else:
|
||||
@@ -691,6 +708,7 @@ def RunMultiFileProject(mb: memhub.MemBuddy*, source_dir: str, temp_dir: str, ou
|
||||
stdlib.free(src_buf_vp2)
|
||||
# OOM cleanup: free this file's AST (the vpi is already on disk; persistent data is stdlib-allocated)
|
||||
mb.reset_to(mark2)
|
||||
VLogger.info(f"[Phase A-pre-vpi] generated={vpi_written_p2} skipped_existing={vpi_skipped_p2} failed={vpi_failed_p2} total={file_count}", "PhaseA")
|
||||
if vpi_failed_p2 > 0:
|
||||
VLogger.error(f"Phase A-pre-vpi: {vpi_failed_p2} files failed vpi pre-generation (see the per-file [Phase A-pre-vpi] logs above for failing files and causes)", "PhaseA")
|
||||
return 1
|
||||
@@ -706,6 +724,14 @@ def RunMultiFileProject(mb: memhub.MemBuddy*, source_dir: str, temp_dir: str, ou
|
||||
# Initialize the Phase A convergence-failure diagnostic table (stdlib, survives reset)
|
||||
_PhaseADiagInit(sizet(file_count) + StubMerger.MAX_INCLUDES)
|
||||
|
||||
# 枚举全量预扫(最早的一次):所有已落盘 .vpi 里的 `enum X:` 先登记进 EnumTable,
|
||||
# 再进入任何预注册/翻译。否则依赖 stub 里的 `types.FRESULT` 会在枚举模块的 vpi
|
||||
# 被注入之前被当成类名 ensure_forward_class,占位结构体常驻进程级 StructTable,
|
||||
# 之后所有模块的 `FRESULT` 都解析成空结构体(llvm.call 返回
|
||||
# !llvm.struct<"...FRESULT", ()>,函数本体却是 i32 -> mlir-opt: llvm.return 类型不匹配)。
|
||||
preenum_early: int = VpiReader.PreRegisterVpiEnums(mb, temp_dir)
|
||||
VLogger.info(f"enum pre-scan (early): {preenum_early} enums registered", "PhaseA")
|
||||
|
||||
# === Phase A-pre-inc: pre-register includes structs + fill deps ===
|
||||
# Must run before App pre-registration (multi-pass resolves cross-file inheritance) and fills PopulateIncludesDeps.
|
||||
if includes_dir is not None and includes_binary_dir is not None:
|
||||
@@ -738,15 +764,24 @@ def RunMultiFileProject(mb: memhub.MemBuddy*, source_dir: str, temp_dir: str, ou
|
||||
inc_fail_flags[si_inc] = 0
|
||||
continue
|
||||
rel_len_pre: sizet = string.strlen(inc_rel_pre)
|
||||
# The includes root dir (per-entry from the IncDir array; empty falls back to includes_dir)
|
||||
# Empty IncDir marks an App source (AppendToSha1MapStore / _BuildReachableSha1Set):
|
||||
# its rel path is source_dir-relative, so resolving it against includes_dir opens a
|
||||
# non-existent file and the translation fails with no diagnostic. App declarations
|
||||
# are pre-registered by Phase A-pre below (against source_dir) — skip them here.
|
||||
incdir_pre: str = store_incdir_inc_pre + sizet(si_inc) * StubMerger.MAX_INC_DIR_LEN
|
||||
if incdir_pre is None or incdir_pre[0] == '\0': incdir_pre = includes_dir
|
||||
if incdir_pre is None or incdir_pre[0] == '\0':
|
||||
if inc_fail_flags is not None: inc_fail_flags[si_inc] = 0
|
||||
continue
|
||||
incdir_len_pre: sizet = string.strlen(incdir_pre)
|
||||
full_path_pre: bytes = stdlib.malloc(incdir_len_pre + rel_len_pre + 2)
|
||||
if full_path_pre is None: continue
|
||||
viperlib.snprintf(full_path_pre, incdir_len_pre + rel_len_pre + 2, "%s/%s", incdir_pre, inc_rel_pre)
|
||||
pkg_inc_pre: str = GenApi.compute_package_from_relpath(mb, inc_rel_pre)
|
||||
tr_inc_pre: HandlesTranslator.Translator* = BuildPipeline.TranslateFileGetTrans(mb, full_path_pre, inc_sha1_pre, pkg_inc_pre, 1)
|
||||
# Pass temp_dir so the include's .vpi is materialized here: Phase A resolves
|
||||
# cross-module method return types (and escape contracts) from the dependency's
|
||||
# .vpi, and in a build without Phase1 artifacts none exists yet — the lookup then
|
||||
# fails hard ("method return type cannot be obtained precisely").
|
||||
tr_inc_pre: HandlesTranslator.Translator* = BuildPipeline.TranslateFileGetTrans(mb, full_path_pre, inc_sha1_pre, pkg_inc_pre, 1, temp_dir)
|
||||
stdlib.free(full_path_pre)
|
||||
if tr_inc_pre is not None:
|
||||
# Record this file's deps (for convergence diagnostics; stdlib table survives reset)
|
||||
@@ -789,7 +824,10 @@ def RunMultiFileProject(mb: memhub.MemBuddy*, source_dir: str, temp_dir: str, ou
|
||||
pkg_pre = GenApi.compute_package_from_relpath(mb, rel_path_pre)
|
||||
# App pre-registration passes pre_allow_dep_inject=1: imports and dep vpi injection happen at
|
||||
# declaration time so field annotations share Phase A's context (else struct layouts diverge).
|
||||
tr_pre: HandlesTranslator.Translator* = BuildPipeline.TranslateFileGetTrans(mb, ent_pre.Path, ent_pre.Sha1, pkg_pre, 1, None, 1)
|
||||
# temp_dir is passed so every App module's .vpi is materialized here (right after its AST is
|
||||
# parsed), before Phase A translates anything: a module referenced by an earlier file must
|
||||
# have its declarations on disk, else the call site cannot resolve its return type.
|
||||
tr_pre: HandlesTranslator.Translator* = BuildPipeline.TranslateFileGetTrans(mb, ent_pre.Path, ent_pre.Sha1, pkg_pre, 1, temp_dir, 1)
|
||||
if tr_pre is None: VLogger.error(f"pre-registration failed: {ent_pre.Path}", "PhaseA")
|
||||
# OOM cleanup: free this App file's transient pre-registration data
|
||||
mb.reset_to(mark2)
|
||||
@@ -798,6 +836,22 @@ def RunMultiFileProject(mb: memhub.MemBuddy*, source_dir: str, temp_dir: str, ou
|
||||
if struct_count_after == struct_count_before: break
|
||||
VLogger.info("pre-registration complete", "PhaseA")
|
||||
|
||||
# 缺省参数全量预扫(TPC 式):所有模块 .vpi 里带缺省值的函数/方法一次性登记进全局
|
||||
# defaults 表,避免“谁先翻译谁登记”的时序依赖。stub-only 依赖(如包 mlirlite 的
|
||||
# __builder.vp,只产 .vpi 不产 .ll)走不到 FuncGenDef,缺了这一步跨模块少传实参时
|
||||
# 会补不出缺省值,直接发出操作数不足的 llvm.call(mlir-opt: incorrect number of
|
||||
# operands;现场 build_load / build_store)。
|
||||
predef_count: int = VpiReader.PreRegisterVpiDefaults(mb, temp_dir)
|
||||
VLogger.info(f"default-argument pre-scan: {predef_count} functions registered", "PhaseA")
|
||||
|
||||
# 枚举全量预扫(与缺省值同理):所有模块 .vpi 里的 `enum X:` 一次性登记进 EnumTable,
|
||||
# 消除"谁先注入谁登记"的时序依赖。否则依赖 stub 里的 `types.FRESULT` 会在枚举模块
|
||||
# 的 vpi 被注入之前解析,被当成类名 ensure_forward_class;占位结构体常驻进程级
|
||||
# StructTable,之后所有模块的 `FRESULT` 都变成空结构体(llvm.call 返回
|
||||
# !llvm.struct<"...FRESULT", ()>,而函数本体是 i32 -> mlir-opt: llvm.return 类型不匹配)。
|
||||
preenum_count: int = VpiReader.PreRegisterVpiEnums(mb, temp_dir)
|
||||
VLogger.info(f"enum pre-scan: {preenum_count} enums registered", "PhaseA")
|
||||
|
||||
# Phase A IR buffer: 1MB is not enough, ast/parser.vp's text.ll exceeds 1MB and got truncated ->
|
||||
# merged IR lost the tail (opt: "end of file in global variable name").
|
||||
PHASE_A_IR_SIZE: sizet = 8388608
|
||||
@@ -1104,7 +1158,7 @@ def RunMultiFileProject(mb: memhub.MemBuddy*, source_dir: str, temp_dir: str, ou
|
||||
ti += 1
|
||||
stdlib.free(llc_tasks)
|
||||
|
||||
# Assemble .s files: llvm-mc -> .obj (isr.s etc., bare symbols matching the .vp CExport/State stubs).
|
||||
# Assemble .s files: llvm-mc -> .obj (isr.s etc., bare symbols matching the .vp ExportT/State stubs).
|
||||
# The obj is added to obj_paths for Phase C linking.
|
||||
od_len_as: sizet = string.strlen(output_dir)
|
||||
asm_compiled: int = 0
|
||||
@@ -1179,12 +1233,20 @@ def RunMultiFileProject(mb: memhub.MemBuddy*, source_dir: str, temp_dir: str, ou
|
||||
# issues from passing the store's union return (bytes*) across functions; rel/incdir keep pointers.
|
||||
td_len_pb: sizet = string.strlen(temp_dir)
|
||||
store_sha1_arr_mi: bytes = stdlib.malloc(StubMerger.MAX_INCLUDES * 17)
|
||||
store_mod_arr_mi: bytes = stdlib.malloc(StubMerger.MAX_INCLUDES * 64)
|
||||
store_mod_arr_mi: bytes = stdlib.malloc(sizet(StubMerger.MAX_INCLUDES) * 64)
|
||||
store_count_mi: int = 0
|
||||
if store_sha1_arr_mi is not None and store_mod_arr_mi is not None:
|
||||
store_count_mi = StubMergerIR._BuildIncludesSha1Map(temp_dir, td_len_pb, store_sha1_arr_mi, store_mod_arr_mi)
|
||||
store_rel_arr_mi: bytes = StubMergerStore.GetSha1StoreRelArrPtr()
|
||||
store_incdir_arr_mi: bytes = StubMergerStore.GetSha1StoreIncdirArrPtr()
|
||||
# 诊断/失败前置:store 为空或拓扑为空时,下面的 include 编译循环会整体跳过,
|
||||
# 但 PhaseC 仍会把这些 include 的 .obj 塞进链接命令 → 只看到一串
|
||||
# "no such file or directory: includes.binary/xx/xxxx.obj"。这里直接给出根因。
|
||||
if store_count_mi <= 0:
|
||||
VLogger.error(f"PhaseB+: Sha1Store is empty (store_count=0) but {reachable_count} include(s) are reachable; the include compile loop would be skipped and every include .obj would be missing at link time (check the Phase1 dependency scan / cache flags)", "PhaseB+")
|
||||
if store_sha1_arr_mi is not None: stdlib.free(store_sha1_arr_mi)
|
||||
if store_mod_arr_mi is not None: stdlib.free(store_mod_arr_mi)
|
||||
return 1
|
||||
if store_count_mi > 0:
|
||||
|
||||
# Topologically sort reachable includes: dependencies are fully translated first. Cross-module
|
||||
@@ -1196,6 +1258,11 @@ def RunMultiFileProject(mb: memhub.MemBuddy*, source_dir: str, temp_dir: str, ou
|
||||
topo_count = 0
|
||||
else:
|
||||
topo_count = StubMergerTopo.BuildTopoSortedIndices(store_count_mi, store_sha1_arr_mi, store_mod_arr_mi, reachable_set, reachable_count, topo_idx)
|
||||
# 拓扑为空 ⇒ 下面的编译循环整体不执行,但 PhaseC 仍会引用这些 .obj → 链接必然失败。
|
||||
# 在这里给根因,而不是让人去看一串 "no such file or directory"。
|
||||
if topo_count <= 0:
|
||||
VLogger.error(f"PhaseB+: topological sort produced 0 entries (store_count={store_count_mi}, reachable={reachable_count}); include .obj files would be missing at link time", "PhaseB+")
|
||||
return 1
|
||||
|
||||
for si_mi in range(int(topo_count)):
|
||||
# Current entry (topological index; reachable modules only)
|
||||
@@ -1321,6 +1388,8 @@ def RunMultiFileProject(mb: memhub.MemBuddy*, source_dir: str, temp_dir: str, ou
|
||||
stdlib.free(vpcall_text_mi)
|
||||
# Retry reading full.ll
|
||||
inc_combined_len_mi = StubMergerBuild.LoadFullIR(temp_dir, inc_sha1_mi, inc_combined_mi, COMBINED_IR_SIZE)
|
||||
if tr_mi is None:
|
||||
VLogger.error(f"include translation returned no module: {src_fp_mi} (sha1={inc_sha1_mi})", "PhaseB+")
|
||||
# OOM cleanup: stub/text/meta/full are on disk, free this includes file's transient
|
||||
# translation data (AST/tokens/lexer/IR/Handler); reads only touch files.
|
||||
mb.reset_to(mark2)
|
||||
@@ -1351,12 +1420,14 @@ def RunMultiFileProject(mb: memhub.MemBuddy*, source_dir: str, temp_dir: str, ou
|
||||
diag_text_ex = 1
|
||||
df_diag_t.close()
|
||||
stdlib.free(diag_text)
|
||||
VLogger.error(f"failed to read full.ll: {src_fp_mi} (full={diag_full_ex} stub={diag_stub_ex} text={diag_text_ex} is_decl={is_decl_mi})", "PhaseB+")
|
||||
# fail-fast:该 include 既没有 full.ll、翻译也没产出 → 它的 .obj 必然缺失,
|
||||
# 继续下去只会得到一串链接器 "no such file or directory"。这里直接停下并指出文件。
|
||||
VLogger.error(f"failed to read full.ll: {src_fp_mi} (full={diag_full_ex} stub={diag_stub_ex} text={diag_text_ex} is_decl={is_decl_mi}); aborting so the include .obj is not silently missing at link time", "PhaseB+")
|
||||
if inc_combined_mi is not None: stdlib.free(inc_combined_mi)
|
||||
stdlib.free(check_pat_mi)
|
||||
if check_fd_mi is not None: stdlib.free(check_fd_mi)
|
||||
stdlib.free(src_fp_mi)
|
||||
continue
|
||||
return 1
|
||||
|
||||
# Compile to .obj (output to includes_binary_dir, consistent with the existence check and link collection)
|
||||
inc_cret_mi: int = BuildPipeline.compile_module_to_obj(inc_combined_mi, inc_combined_len_mi, temp_dir, includes_binary_dir, inc_sha1_mi, cc_cmd, cc_flags)
|
||||
@@ -1475,18 +1546,12 @@ def RunMultiFileProject(mb: memhub.MemBuddy*, source_dir: str, temp_dir: str, ou
|
||||
fop_pos += op_len_pc
|
||||
final_obj_paths[fop_pos] = '\0'
|
||||
else:
|
||||
# .obj missing: a declaration-only module (no define lines) needs no .obj, so skip silently;
|
||||
# under phase2command mode the missing .obj is expected and the path is still added.
|
||||
if BuildPipeline._phase2_cmd_path is not None:
|
||||
op_len_p2: sizet = string.strlen(obj_path_pc)
|
||||
if fop_pos + op_len_p2 + 2 < OBJ_PATHS_SIZE + 512:
|
||||
if fop_pos > 0:
|
||||
final_obj_paths[fop_pos] = ' '
|
||||
fop_pos += 1
|
||||
string.strcpy(final_obj_paths + fop_pos, obj_path_pc)
|
||||
fop_pos += op_len_p2
|
||||
final_obj_paths[fop_pos] = '\0'
|
||||
elif StubMergerIR.IsDeclOnlyModule(temp_dir, td_len_pc, rsha1_pc) != 1: VLogger.error(f"reachable module .obj missing: {obj_path_pc}", "PhaseC")
|
||||
# .obj 缺失:声明-only 模块本来就不需要 .obj,静默跳过;其余情况无论是否
|
||||
# --phase2command,链接都会因缺文件失败 —— 直接报根因并中止,不要拖到链接器。
|
||||
if StubMergerIR.IsDeclOnlyModule(temp_dir, td_len_pc, rsha1_pc) != 1:
|
||||
VLogger.error(f"reachable module .obj missing: {obj_path_pc} (not declaration-only); aborting before link", "PhaseC")
|
||||
stdlib.free(obj_path_pc)
|
||||
return 1
|
||||
stdlib.free(obj_path_pc)
|
||||
|
||||
final_obj_paths[fop_pos] = '\0'
|
||||
|
||||
@@ -6,7 +6,7 @@ import viperlib
|
||||
import lib.core.VLogger as VLogger
|
||||
from json.__parser import parse as json_parse
|
||||
from json import JsonValue, JSON_OBJECT, JSON_INT
|
||||
import win32.fileio as fileio
|
||||
import io as fileio
|
||||
|
||||
|
||||
# Config - project configuration
|
||||
|
||||
@@ -6,7 +6,7 @@ import viperlib
|
||||
import hashlib
|
||||
import win32.win32file
|
||||
import win32.win32base
|
||||
import win32.fileio as fileio
|
||||
import io as fileio
|
||||
|
||||
|
||||
# Utils - project utility functions (SHA1 hashing, directory cleanup)
|
||||
|
||||
+1205
-274
File diff suppressed because it is too large.
Load diff
@@ -6,7 +6,7 @@ import stdlib
|
||||
import mlirlite
|
||||
import win32.win32file as win32file
|
||||
import win32.win32base as win32base
|
||||
import win32.fileio as fileio
|
||||
import io as fileio
|
||||
import viperlib
|
||||
import sys
|
||||
import ast
|
||||
@@ -187,7 +187,7 @@ def _BuildReachableSha1Set(mb: memhub.MemBuddy*, source_dir: str, temp_dir: str,
|
||||
wl_len -= 1
|
||||
|
||||
# 2. Pre-add base modules (builtins): builtins is always compiled in; _list/_dict are injected only for
|
||||
# explicit list/dict annotations/ctors (list literals to t.CArray are array init and do not trigger _list).
|
||||
# explicit list/dict annotations/ctors (list literals to t.ArrayT are array init and do not trigger _list).
|
||||
wl_len = _AppendDepsToWorklist(worklist, wl_len, "builtins", None)
|
||||
if wl_len < 0:
|
||||
stdlib.free(worklist)
|
||||
@@ -524,7 +524,7 @@ def LoadDepVpiIntoModule(pool: memhub.MemBuddy*, mod: mlirlite.LLVMModule*, impo
|
||||
if pool is None or mod is None or temp_dir is None or trans is None: return 0
|
||||
if imported_modules is None: return 0
|
||||
|
||||
# Reset the typedef recursion guard before injecting each module's stub: the CTypedef table is
|
||||
# Reset the typedef recursion guard before injecting each module's stub: the TypedefT table is
|
||||
# per-file, so stale visited state registers stdint typedefs only once and degrades param types (FuncTest root cause).
|
||||
VpiReader.ResetVpiTypedefVisited()
|
||||
# Reset the on-demand injection reentry table too: the target mod differs per module, so carrying it
|
||||
|
||||
@@ -5,7 +5,7 @@ import stdlib
|
||||
import mlirlite
|
||||
import win32.win32file as win32file
|
||||
import win32.win32base as win32base
|
||||
import win32.fileio as fileio
|
||||
import io as fileio
|
||||
import viperlib
|
||||
import sys
|
||||
import ast
|
||||
@@ -84,20 +84,39 @@ def _is_in_sha1_set(sha1: str, sha1_set: str, set_count: int) -> int:
|
||||
for i in range(int(set_count)):
|
||||
if string.strcmp(sha1_set + i * 17, sha1) == 0: return 1
|
||||
return 0
|
||||
# Sha1MapPath - _sha1_map.txt 的落盘路径:放在**构建目录**(temp 的上一级),
|
||||
# 便于直接按 SHA1 反查源文件;temp 内的 sliced 产物(.ll/.vpi/.meta)位置不变。
|
||||
# 返回 stdlib 缓冲区(调用方 free);temp_dir 无上一级(相对路径)时退回 temp 目录。
|
||||
def Sha1MapPath(pool: memhub.MemBuddy*, temp_dir: str) -> str:
|
||||
"""返回 {temp_dir 的上一级}/_sha1_map.txt"""
|
||||
if temp_dir is None: return None
|
||||
td: sizet = string.strlen(temp_dir)
|
||||
end: sizet = td
|
||||
while end > 0 and (temp_dir[end - 1] == '/' or temp_dir[end - 1] == '\\'): end -= 1
|
||||
cut: sizet = end
|
||||
while cut > 0 and temp_dir[cut - 1] != '/' and temp_dir[cut - 1] != '\\': cut -= 1
|
||||
parent_len: sizet = 0
|
||||
if cut > 0: parent_len = cut - 1
|
||||
else: parent_len = end # 没有上一级(相对路径):退回 temp 目录本身
|
||||
buf: bytes = stdlib.malloc(parent_len + 32)
|
||||
if buf is None: return None
|
||||
if parent_len > 0: string.strncpy(buf, temp_dir, parent_len)
|
||||
buf[parent_len] = '\0'
|
||||
viperlib.snprintf(buf + parent_len, 32, "/_sha1_map.txt")
|
||||
return buf
|
||||
# WriteIncludesSha1Map - write includes scan results to _sha1_map.txt (human-readable debug output only).
|
||||
# Internally the in-memory store filled by PopulateSha1MapStore is used; returns 0 on success, non-zero on failure.
|
||||
def WriteIncludesSha1Map(mb: memhub.MemBuddy*, temp_dir: str, scan_result: IncludesScanner.ScanResult*, filter_set: str, filter_count: int) -> int:
|
||||
"""Write the includes scan result to _sha1_map.txt (human-readable debug output)."""
|
||||
if temp_dir is None or scan_result is None: return 1
|
||||
|
||||
# Build the path temp_dir/_sha1_map.txt
|
||||
dir_len: sizet = string.strlen(temp_dir)
|
||||
map_path: bytes = stdlib.malloc(dir_len + 32)
|
||||
# Build the path {temp_dir 的上一级}/_sha1_map.txt
|
||||
map_path: bytes = Sha1MapPath(mb, temp_dir)
|
||||
if map_path is None: return 1
|
||||
viperlib.snprintf(map_path, dir_len + 32, "%s/_sha1_map.txt", temp_dir)
|
||||
|
||||
# Open the file for writing (CREATE_ALWAYS)
|
||||
f: fileio.File* = fileio.File(map_path, fileio.MODE.W)
|
||||
stdlib.free(map_path)
|
||||
if f.closed: return 1
|
||||
|
||||
# Write each include entry: {sha1}:includes/{rel_path}\n
|
||||
|
||||
@@ -6,7 +6,7 @@ import stdlib
|
||||
import mlirlite
|
||||
import win32.win32file as win32file
|
||||
import win32.win32base as win32base
|
||||
import win32.fileio as fileio
|
||||
import io as fileio
|
||||
import viperlib
|
||||
import sys
|
||||
import ast
|
||||
@@ -38,7 +38,9 @@ def WriteModuleMeta(mod: mlirlite.LLVMModule*, temp_dir: str, td_len: sizet, sha
|
||||
if mpath is None:
|
||||
VLogger.error("meta write: path construction failed", "Meta")
|
||||
return 1
|
||||
mf: fileio.File = fileio.File(mpath, fileio.MODE.W)
|
||||
# 与其他模块一致用指针形式:File 是类(构造函数返回实例指针),值形式在部分编译器
|
||||
# 上会读不到真实句柄状态(closed 恒为默认值)。
|
||||
mf: fileio.File* = fileio.File(mpath, fileio.MODE.W)
|
||||
if mf.closed:
|
||||
VLogger.error(f"meta write: cannot open {mpath}", "Meta")
|
||||
return 1
|
||||
@@ -122,7 +124,9 @@ def _MetaHasDef(temp_dir: str, td_len: sizet, sha1: str) -> int:
|
||||
if temp_dir is None or sha1 is None: return -1
|
||||
mpath: str = StubMergerCollect._sliced_path(temp_dir, td_len, sha1, "meta")
|
||||
if mpath is None: return -1
|
||||
mf: fileio.File = fileio.File(mpath, fileio.MODE.R)
|
||||
# 指针形式(见 WriteModuleMeta 注释):值形式读不到真实的 closed/handle 状态,
|
||||
# 文件不存在时 mf.closed 仍为默认 false,会把"读不到 meta"误判成"可读且无 DEF"。
|
||||
mf: fileio.File* = fileio.File(mpath, fileio.MODE.R)
|
||||
stdlib.free(mpath)
|
||||
if mf.closed: return -1
|
||||
mbuf: bytes = stdlib.malloc(StubMerger.STUB_READ_BUF_SIZE)
|
||||
@@ -131,9 +135,10 @@ def _MetaHasDef(temp_dir: str, td_len: sizet, sha1: str) -> int:
|
||||
mf.close()
|
||||
if mbuf is None: return -1
|
||||
if mbr <= 0:
|
||||
# Empty meta: no DEF, decl-only
|
||||
# 读不到/空 meta 一律视为"未知",绝不能当作 decl-only:那会让 include 被整段跳过、
|
||||
# 不产生 .obj,最后只在链接阶段报一堆 "no such file or directory"。
|
||||
stdlib.free(mbuf)
|
||||
return 0
|
||||
return -1
|
||||
if mbr < StubMerger.STUB_READ_BUF_SIZE: mbuf[mbr] = '\0'
|
||||
# Scan line by line: a trailing " DEF" marks an impl file
|
||||
mpos: sizet = 0
|
||||
|
||||
@@ -5,7 +5,7 @@ import stdlib
|
||||
import mlirlite
|
||||
import win32.win32file as win32file
|
||||
import win32.win32base as win32base
|
||||
import win32.fileio as fileio
|
||||
import io as fileio
|
||||
import viperlib
|
||||
import sys
|
||||
import ast
|
||||
@@ -134,14 +134,14 @@ def PopulateSha1MapStore(scan_result: IncludesScanner.ScanResult*) -> int:
|
||||
if StubMerger._sha1_store_rel is not None: stdlib.free(StubMerger._sha1_store_rel)
|
||||
if StubMerger._sha1_store_incdir is not None: stdlib.free(StubMerger._sha1_store_incdir)
|
||||
StubMerger._sha1_store_arr = stdlib.malloc(StubMerger.MAX_INCLUDES * 17)
|
||||
StubMerger._sha1_store_mod = stdlib.malloc(StubMerger.MAX_INCLUDES * 64)
|
||||
# 取消 C 整数提升后:MAX_INCLUDES/MAX_REL_PATH_LEN/MAX_INC_DIR_LEN 这些模块属性 CDefine 按最小位宽物化为 i16,
|
||||
StubMerger._sha1_store_mod = stdlib.malloc(sizet(StubMerger.MAX_INCLUDES) * 64)
|
||||
# 取消 C 整数提升后:MAX_INCLUDES/MAX_REL_PATH_LEN/MAX_INC_DIR_LEN 这些模块属性 DefineT 按最小位宽物化为 i16,
|
||||
# 两侧都 i16 的乘积(1024*256 / 1024*512)会静默回绕成 0;显式 sizet() 把乘数加宽到 64 位,结果与旧「提升到 int」一致。
|
||||
StubMerger._sha1_store_rel = stdlib.malloc(sizet(StubMerger.MAX_INCLUDES) * StubMerger.MAX_REL_PATH_LEN)
|
||||
StubMerger._sha1_store_incdir = stdlib.malloc(sizet(StubMerger.MAX_INCLUDES) * StubMerger.MAX_INC_DIR_LEN)
|
||||
if StubMerger._sha1_store_arr is None or StubMerger._sha1_store_mod is None or StubMerger._sha1_store_rel is None or StubMerger._sha1_store_incdir is None: return -1
|
||||
string.memset(StubMerger._sha1_store_arr, 0, StubMerger.MAX_INCLUDES * 17)
|
||||
string.memset(StubMerger._sha1_store_mod, 0, StubMerger.MAX_INCLUDES * 64)
|
||||
string.memset(StubMerger._sha1_store_mod, 0, sizet(StubMerger.MAX_INCLUDES) * 64)
|
||||
string.memset(StubMerger._sha1_store_rel, 0, sizet(StubMerger.MAX_INCLUDES) * StubMerger.MAX_REL_PATH_LEN)
|
||||
string.memset(StubMerger._sha1_store_incdir, 0, sizet(StubMerger.MAX_INCLUDES) * StubMerger.MAX_INC_DIR_LEN)
|
||||
StubMerger._sha1_store_count = 0
|
||||
@@ -160,7 +160,14 @@ def PopulateSha1MapStore(scan_result: IncludesScanner.ScanResult*) -> int:
|
||||
if full_path_buf is None:
|
||||
p_bad += 1
|
||||
continue
|
||||
viperlib.snprintf(full_path_buf, rp_len + 10, "includes/%s", ent.RelPath)
|
||||
# The scanner may already hand back a workspace-relative path ("includes/ast/lexer.vp")
|
||||
# when the scan root is the workspace; prepending again yields "includes/includes/..."
|
||||
# and PathToModuleName (which strips a single prefix) produces "includes.ast.lexer",
|
||||
# so exact module lookups ("ast.lexer") miss and re-export resolution breaks.
|
||||
if rp_len >= 9 and string.strncmp(ent.RelPath, "includes/", 9) == 0:
|
||||
string.strcpy(full_path_buf, ent.RelPath)
|
||||
else:
|
||||
viperlib.snprintf(full_path_buf, rp_len + 10, "includes/%s", ent.RelPath)
|
||||
mod_name: str = Utils.PathToModuleName(full_path_buf, 1)
|
||||
stdlib.free(full_path_buf)
|
||||
if mod_name is not None:
|
||||
@@ -198,13 +205,13 @@ def AppendToSha1MapStore(sha1: str, rel_path: str) -> int:
|
||||
# init store if empty (Phase2 may run before Phase1)
|
||||
if StubMerger._sha1_store_arr is None:
|
||||
StubMerger._sha1_store_arr = stdlib.malloc(StubMerger.MAX_INCLUDES * 17)
|
||||
StubMerger._sha1_store_mod = stdlib.malloc(StubMerger.MAX_INCLUDES * 64)
|
||||
StubMerger._sha1_store_mod = stdlib.malloc(sizet(StubMerger.MAX_INCLUDES) * 64)
|
||||
# sizet(): 取消整数提升后 i16(MAX_INCLUDES)*i16(长度) 会回绕,显式加宽到 64 位
|
||||
StubMerger._sha1_store_rel = stdlib.malloc(sizet(StubMerger.MAX_INCLUDES) * StubMerger.MAX_REL_PATH_LEN)
|
||||
StubMerger._sha1_store_incdir = stdlib.malloc(sizet(StubMerger.MAX_INCLUDES) * StubMerger.MAX_INC_DIR_LEN)
|
||||
if StubMerger._sha1_store_arr is None or StubMerger._sha1_store_mod is None or StubMerger._sha1_store_rel is None or StubMerger._sha1_store_incdir is None: return 1
|
||||
string.memset(StubMerger._sha1_store_arr, 0, StubMerger.MAX_INCLUDES * 17)
|
||||
string.memset(StubMerger._sha1_store_mod, 0, StubMerger.MAX_INCLUDES * 64)
|
||||
string.memset(StubMerger._sha1_store_mod, 0, sizet(StubMerger.MAX_INCLUDES) * 64)
|
||||
string.memset(StubMerger._sha1_store_rel, 0, sizet(StubMerger.MAX_INCLUDES) * StubMerger.MAX_REL_PATH_LEN)
|
||||
string.memset(StubMerger._sha1_store_incdir, 0, sizet(StubMerger.MAX_INCLUDES) * StubMerger.MAX_INC_DIR_LEN)
|
||||
StubMerger._sha1_store_count = 0
|
||||
@@ -256,13 +263,13 @@ def AddIncludeToSha1MapStore(sha1: str, rel_path: str, inc_dir: str, mod_name: s
|
||||
# init store if empty
|
||||
if StubMerger._sha1_store_arr is None:
|
||||
StubMerger._sha1_store_arr = stdlib.malloc(StubMerger.MAX_INCLUDES * 17)
|
||||
StubMerger._sha1_store_mod = stdlib.malloc(StubMerger.MAX_INCLUDES * 64)
|
||||
StubMerger._sha1_store_mod = stdlib.malloc(sizet(StubMerger.MAX_INCLUDES) * 64)
|
||||
# sizet(): 取消整数提升后 i16(MAX_INCLUDES)*i16(长度) 会回绕,显式加宽到 64 位
|
||||
StubMerger._sha1_store_rel = stdlib.malloc(sizet(StubMerger.MAX_INCLUDES) * StubMerger.MAX_REL_PATH_LEN)
|
||||
StubMerger._sha1_store_incdir = stdlib.malloc(sizet(StubMerger.MAX_INCLUDES) * StubMerger.MAX_INC_DIR_LEN)
|
||||
if StubMerger._sha1_store_arr is None or StubMerger._sha1_store_mod is None or StubMerger._sha1_store_rel is None or StubMerger._sha1_store_incdir is None: return 1
|
||||
string.memset(StubMerger._sha1_store_arr, 0, StubMerger.MAX_INCLUDES * 17)
|
||||
string.memset(StubMerger._sha1_store_mod, 0, StubMerger.MAX_INCLUDES * 64)
|
||||
string.memset(StubMerger._sha1_store_mod, 0, sizet(StubMerger.MAX_INCLUDES) * 64)
|
||||
string.memset(StubMerger._sha1_store_rel, 0, sizet(StubMerger.MAX_INCLUDES) * StubMerger.MAX_REL_PATH_LEN)
|
||||
string.memset(StubMerger._sha1_store_incdir, 0, sizet(StubMerger.MAX_INCLUDES) * StubMerger.MAX_INC_DIR_LEN)
|
||||
StubMerger._sha1_store_count = 0
|
||||
@@ -536,15 +543,15 @@ def GetSha1StoreIncdirArrPtr() -> bytes:
|
||||
return StubMerger._sha1_store_incdir
|
||||
# ============================================================
|
||||
# GetSha1StoreArr - return the store's sha1/mod/rel array pointers and entry count; do not free.
|
||||
def GetSha1StoreArr(out_sha1_arr: rawptr[t.CPtr], out_mod_arr: rawptr[t.CPtr], out_rel_arr: rawptr[t.CPtr]) -> int:
|
||||
def GetSha1StoreArr(out_sha1_arr: void**, out_mod_arr: void**, out_rel_arr: void**) -> int:
|
||||
"""Get the global store array pointers and entry count; returns -1 on failure"""
|
||||
if out_sha1_arr is None or out_mod_arr is None or out_rel_arr is None: return -1
|
||||
if StubMerger._sha1_store_arr is None or StubMerger._sha1_store_mod is None or StubMerger._sha1_store_rel is None: return -1
|
||||
sp: rawptr[t.CPtr] = out_sha1_arr
|
||||
sp: void** = out_sha1_arr
|
||||
if sp is not None: sp[0] = StubMerger._sha1_store_arr
|
||||
mp: rawptr[t.CPtr] = out_mod_arr
|
||||
mp: void** = out_mod_arr
|
||||
if mp is not None: mp[0] = StubMerger._sha1_store_mod
|
||||
rp: rawptr[t.CPtr] = out_rel_arr
|
||||
rp: void** = out_rel_arr
|
||||
if rp is not None: rp[0] = StubMerger._sha1_store_rel
|
||||
return StubMerger._sha1_store_count
|
||||
|
||||
|
||||
@@ -2,7 +2,7 @@ from stdint import *
|
||||
import string
|
||||
import stdio
|
||||
import stdlib
|
||||
import win32.fileio as fileio
|
||||
import io as fileio
|
||||
import lib.core.VLogger as VLogger
|
||||
|
||||
|
||||
|
||||
+198
-183
@@ -6,7 +6,7 @@ import string
|
||||
import stdio
|
||||
import stdlib
|
||||
import viperlib
|
||||
import win32.fileio as fileio
|
||||
import io as fileio
|
||||
import lib.core.VLogger as VLogger
|
||||
import lib.core.Gen.Translator as HT
|
||||
import lib.Stub.StubMerger.StubMergerStore as StubMergerStore
|
||||
@@ -23,20 +23,20 @@ novirtual class AssignGenMixin:
|
||||
|
||||
RecoverFound: int
|
||||
|
||||
# Check whether an annotation is the CDefine marker (module-level function)
|
||||
# Check whether an annotation is the DefineT marker (module-level function)
|
||||
def is_cdefine_annotation(self, annot: ast.AST*) -> int:
|
||||
"""Return 1 if the annotation is the CDefine marker (bare or CDefine[T]), else 0."""
|
||||
"""Return 1 if the annotation is the DefineT marker (bare or DefineT[T]), else 0."""
|
||||
if annot is None: return 0
|
||||
k: int = annot.kind()
|
||||
if k == ast.ASTKind.Name:
|
||||
nm: ast.Name* = ast.Name*(annot)
|
||||
if nm.id is not None and string.strcmp(nm.id, "CDefine") == 0: return 1
|
||||
if nm.id is not None and string.strcmp(nm.id, "DefineT") == 0: return 1
|
||||
return 0
|
||||
if k == ast.ASTKind.Subscript:
|
||||
sub_cd: ast.Subscript* = ast.Subscript*(annot)
|
||||
if sub_cd.value is not None and sub_cd.value.kind() == ast.ASTKind.Name:
|
||||
mk_cd: ast.Name* = ast.Name*(sub_cd.value)
|
||||
if mk_cd.id is not None and string.strcmp(mk_cd.id, "CDefine") == 0: return 1
|
||||
if mk_cd.id is not None and string.strcmp(mk_cd.id, "DefineT") == 0: return 1
|
||||
return 0
|
||||
|
||||
# extract_cdefine_int_value - extract an integer constant from AnnAssign.value
|
||||
@@ -67,11 +67,11 @@ novirtual class AssignGenMixin:
|
||||
# Case 1: Constant(INT) - e.g. 0x0002
|
||||
if k == ast.ASTKind.Constant:
|
||||
cn: ast.Constant* = ast.Constant*(val_node)
|
||||
# True/False boolean constant -> 1/0 (bool treated as int in CDefine context)
|
||||
# True/False boolean constant -> 1/0 (bool treated as int in DefineT context)
|
||||
if cn.const_kind == ast.CONST_BOOL: return 1 if cn.int_val != 0 else 0
|
||||
if cn.const_kind != ast.CONST_INT:
|
||||
# Non-INT constant: fail fast
|
||||
self.Gen.TypeGenErr.fatal_error(val_node, "CDefine value extraction failed: cannot parse a compile-time constant from the expression", "TypeError")
|
||||
self.Gen.TypeGenErr.fatal_error(val_node, "DefineT value extraction failed: cannot parse a compile-time constant from the expression", "TypeError")
|
||||
return cn.int_val
|
||||
|
||||
# Case 2: BinOp - e.g. FOREGROUND_RED | FOREGROUND_GREEN, (1 << N)
|
||||
@@ -108,13 +108,13 @@ novirtual class AssignGenMixin:
|
||||
else: res = left_val // right_val
|
||||
else:
|
||||
# Unknown BinOp operator: fail fast(不能静默返回 0)
|
||||
self.Gen.TypeGenErr.fatal_error(val_node, "CDefine value extraction failed: cannot parse a compile-time constant from the expression", "TypeError")
|
||||
self.Gen.TypeGenErr.fatal_error(val_node, "DefineT value extraction failed: cannot parse a compile-time constant from the expression", "TypeError")
|
||||
if bits_out is not None:
|
||||
# 静态计算**自行拓宽**:字面量/define 的声明宽度仍保持最小(不提升),但 `+ - * <<`
|
||||
# 的精确结果可能比操作数宽 —— 若照操作数宽度物化就会静默回绕
|
||||
# (现场:define A=8192(最小 16 位)的 `A*24+512` 被按 16 位物化成 512)。
|
||||
# 规则:以操作数的“较宽”为基准,再用**结果值**算精确可容纳位宽,只有装不下才加宽
|
||||
# (不无故提升,避免把 `CDefine + 1` 这类算式整体抬成 i64 引出新的“隐式窄化”报错);
|
||||
# (不无故提升,避免把 `DefineT + 1` 这类算式整体抬成 i64 引出新的“隐式窄化”报错);
|
||||
# 基准未知(wbase==0,如纯字面量算式)时保持“未知”,仍由物化端按值推断。
|
||||
w: int = wbase
|
||||
if w > 0 and res != 0:
|
||||
@@ -136,11 +136,11 @@ novirtual class AssignGenMixin:
|
||||
else: bits_out[0] = 64
|
||||
return res
|
||||
|
||||
# Case 3: Name - a registered CDefine constant or True/False literal
|
||||
# Case 3: Name - a registered DefineT constant or True/False literal
|
||||
if k == ast.ASTKind.Name:
|
||||
nm: ast.Name* = ast.Name*(val_node)
|
||||
if nm.id is not None:
|
||||
# True/False literal -> 1/0 (bool treated as int in CDefine context)
|
||||
# True/False literal -> 1/0 (bool treated as int in DefineT context)
|
||||
if string.strcmp(nm.id, "True") == 0: return 1
|
||||
if string.strcmp(nm.id, "False") == 0: return 0
|
||||
looked_up: int64 = self.Gen.TypeGenCDef.lookup_cdefine_constant(nm.id)
|
||||
@@ -149,7 +149,7 @@ novirtual class AssignGenMixin:
|
||||
if bits_out is not None: bits_out[0] = self.Gen.TypeGenCDef.lookup_cdefine_bits(nm.id)
|
||||
return looked_up
|
||||
# Unregistered reference: fail fast
|
||||
self.Gen.TypeGenErr.fatal_error(val_node, "CDefine value extraction failed: cannot parse a compile-time constant from the expression", "TypeError")
|
||||
self.Gen.TypeGenErr.fatal_error(val_node, "DefineT value extraction failed: cannot parse a compile-time constant from the expression", "TypeError")
|
||||
|
||||
# Case 4: Call - 整型/typedef 强转(如 WORD(0x0080) / t.CUnsignedLong(-11)):
|
||||
# 取实参值,并按**目标类型的位宽**规范化(这赋予常量"已知宽度")。
|
||||
@@ -169,7 +169,7 @@ novirtual class AssignGenMixin:
|
||||
if bits_out is not None: bits_out[0] = ib
|
||||
return arg_val
|
||||
# Call with no args: fail fast
|
||||
self.Gen.TypeGenErr.fatal_error(val_node, "CDefine value extraction failed: cannot parse a compile-time constant from the expression", "TypeError")
|
||||
self.Gen.TypeGenErr.fatal_error(val_node, "DefineT value extraction failed: cannot parse a compile-time constant from the expression", "TypeError")
|
||||
|
||||
# Case 5: UnaryOp - e.g. -11
|
||||
if k == ast.ASTKind.UnaryOp:
|
||||
@@ -181,10 +181,10 @@ novirtual class AssignGenMixin:
|
||||
if uop.op == ast.OpKind.UAdd: return operand_val
|
||||
if uop.op == ast.OpKind.Invert: return ~operand_val
|
||||
# Unknown UnaryOp operator: fail fast
|
||||
self.Gen.TypeGenErr.fatal_error(val_node, "CDefine value extraction failed: cannot parse a compile-time constant from the expression", "TypeError")
|
||||
self.Gen.TypeGenErr.fatal_error(val_node, "DefineT value extraction failed: cannot parse a compile-time constant from the expression", "TypeError")
|
||||
|
||||
# Unknown node kind: fail fast
|
||||
self.Gen.TypeGenErr.fatal_error(val_node, "CDefine value extraction failed: cannot parse a compile-time constant from the expression", "TypeError")
|
||||
self.Gen.TypeGenErr.fatal_error(val_node, "DefineT value extraction failed: cannot parse a compile-time constant from the expression", "TypeError")
|
||||
# After fatal_error add an explicit return so the block is properly terminated
|
||||
return 0
|
||||
|
||||
@@ -229,7 +229,7 @@ novirtual class AssignGenMixin:
|
||||
if rhs_bits != 0: return rhs_bits
|
||||
return self.define_min_width(val)
|
||||
|
||||
# define / t.CDefine integer constant storable and width checks
|
||||
# define / t.DefineT integer constant storable and width checks
|
||||
# Auto-derived => 64-bit pattern; explicit type T => T's width range.
|
||||
def define_min_width(self, value: int64) -> int:
|
||||
"""Return the minimal storable width (8/16/32/64) for an integer value."""
|
||||
@@ -263,7 +263,7 @@ novirtual class AssignGenMixin:
|
||||
"""1=通用求值器(extract_cdefine_int_value_bits)一定能求出整数;0=不确定,**不得调用求值器**。
|
||||
|
||||
判定范围与求值器严格对齐(否则求值器会 fail-fast,变成误报):
|
||||
整型/布尔字面量、True/False、**已注册的** CDefine 名、其 +/-/~、求值器支持的整型二元运算、
|
||||
整型/布尔字面量、True/False、**已注册的** DefineT 名、其 +/-/~、求值器支持的整型二元运算、
|
||||
以及带实参的强转调用。变量、普通函数调用、求值器不支持的算子一律判 0 —— 即"求不出来就不检查"。
|
||||
"""
|
||||
if node is None: return 0
|
||||
@@ -326,27 +326,27 @@ novirtual class AssignGenMixin:
|
||||
self.Gen.TypeGenErr.fatal_error(node, f"[VPC] shift count out of range: the count {v} is >= the left operand width {lhs_bits} bits; " f"the result would be 0 or undefined (not a value the compiler may guess). " f"Fix: use a count < {lhs_bits}, or widen the left operand.", "ValueError")
|
||||
|
||||
def _is_cdefine_marker_node(self, node: ast.AST*) -> int:
|
||||
"""Return 1 if the node is the CDefine marker node (bare or CDefine[T])."""
|
||||
"""Return 1 if the node is the DefineT marker node (bare or DefineT[T])."""
|
||||
if node is None: return 0
|
||||
k: int = node.kind()
|
||||
if k == ast.ASTKind.Name:
|
||||
nm: ast.Name* = ast.Name*(node)
|
||||
if nm.id is not None and string.strcmp(nm.id, "CDefine") == 0: return 1
|
||||
if nm.id is not None and string.strcmp(nm.id, "DefineT") == 0: return 1
|
||||
if k == ast.ASTKind.Subscript:
|
||||
sub_cm: ast.Subscript* = ast.Subscript*(node)
|
||||
if sub_cm.value is not None and sub_cm.value.kind() == ast.ASTKind.Name:
|
||||
mk_cm: ast.Name* = ast.Name*(sub_cm.value)
|
||||
if mk_cm.id is not None and string.strcmp(mk_cm.id, "CDefine") == 0: return 1
|
||||
if mk_cm.id is not None and string.strcmp(mk_cm.id, "DefineT") == 0: return 1
|
||||
return 0
|
||||
|
||||
def _define_explicit_annot(self, annot: ast.AST*) -> ast.AST*:
|
||||
"""Return the explicit type T of a `CDefine[T]` annotation, or None."""
|
||||
"""Return the explicit type T of a `DefineT[T]` annotation, or None."""
|
||||
if annot is None: return None
|
||||
if annot.kind() != ast.ASTKind.Subscript: return None
|
||||
sub_d: ast.Subscript* = ast.Subscript*(annot)
|
||||
if sub_d.value is None or sub_d.value.kind() != ast.ASTKind.Name: return None
|
||||
mk_d: ast.Name* = ast.Name*(sub_d.value)
|
||||
if mk_d.id is None or string.strcmp(mk_d.id, "CDefine") != 0: return None
|
||||
if mk_d.id is None or string.strcmp(mk_d.id, "DefineT") != 0: return None
|
||||
return sub_d.slice
|
||||
|
||||
def _annot_int_bits(self, pool: memhub.MemBuddy*, annot: ast.AST*) -> int:
|
||||
@@ -361,7 +361,7 @@ novirtual class AssignGenMixin:
|
||||
n2: ast.Name* = ast.Name*(annot)
|
||||
nm = n2.id
|
||||
elif k == ast.ASTKind.Subscript:
|
||||
# Descriptor wrapper (CConst[T] etc.): the width comes from the wrapped type.
|
||||
# Descriptor wrapper (ConstT[T] etc.): the width comes from the wrapped type.
|
||||
sub_ib: ast.Subscript* = ast.Subscript*(annot)
|
||||
return self._annot_int_bits(pool, sub_ib.slice)
|
||||
if nm is None: return 0
|
||||
@@ -371,7 +371,7 @@ novirtual class AssignGenMixin:
|
||||
"""类型名 → 整数位宽(0=未知/非整数)。depth 防 typedef 环。
|
||||
|
||||
覆盖:内建名(int/bool/int8../uint64/sizet)、`t.CXxx`(map_builtin_type)、
|
||||
以及**用户 typedef**(如 stdint 的 `WORD: t.CTypedef = uint16` —— 经 lookup_ctypedef
|
||||
以及**用户 typedef**(如 stdint 的 `WORD: t.TypedefT = uint16` —— 经 lookup_ctypedef
|
||||
取其底层类型文本再递归定宽)。此前用户 typedef 一律返回 0,导致
|
||||
`define X: WORD = ...` 的声明宽度不参与校验/物化。
|
||||
"""
|
||||
@@ -396,7 +396,7 @@ novirtual class AssignGenMixin:
|
||||
return 0
|
||||
case _:
|
||||
return 0
|
||||
# 用户/include typedef(如 stdint.vp 的 `WORD: t.CTypedef = uint16`):
|
||||
# 用户/include typedef(如 stdint.vp 的 `WORD: t.TypedefT = uint16`):
|
||||
# 表内值可能是模块限定 ref(stdint.uint16),先用 _expand_typedef_ref 展开;
|
||||
# 本地表未命中再走 builtins 源扫描兜底。
|
||||
underlying: str = self.Gen.TypeGenCType.lookup_ctypedef(nm)
|
||||
@@ -410,7 +410,7 @@ novirtual class AssignGenMixin:
|
||||
return self._int_bits_of_type_name(pool, underlying, depth + 1)
|
||||
|
||||
def _define_annot_bits(self, pool: memhub.MemBuddy*, annot: ast.AST*) -> int:
|
||||
"""Return the bit width of a CDefine declaration's explicit type T."""
|
||||
"""Return the bit width of a DefineT declaration's explicit type T."""
|
||||
return self._annot_int_bits(pool, self._define_explicit_annot(annot))
|
||||
|
||||
# cdefine_decl_float_bits - define 声明的**浮点**宽度裁决(定义方与跨模块使用方共用同一套)
|
||||
@@ -459,7 +459,7 @@ novirtual class AssignGenMixin:
|
||||
return 0
|
||||
case _:
|
||||
return 0
|
||||
# 用户/include typedef(如 `MYF: t.CTypedef = float`):展开后再递归判定
|
||||
# 用户/include typedef(如 `MYF: t.TypedefT = float`):展开后再递归判定
|
||||
underlying: str = self.Gen.TypeGenCType.lookup_ctypedef(nm)
|
||||
if underlying is None:
|
||||
underlying = self.Gen.TypeGenResolve._lookup_builtins_typedef(pool, nm)
|
||||
@@ -471,7 +471,7 @@ novirtual class AssignGenMixin:
|
||||
return self._float_kind_of_type_name(pool, underlying, depth + 1)
|
||||
|
||||
def _cdefine_int_literal_text(self, name: str) -> str:
|
||||
"""Return the raw value text of a CDefine line, or None if not found."""
|
||||
"""Return the raw value text of a DefineT line, or None if not found."""
|
||||
if name is None: return None
|
||||
fp: str = self.Gen.TypeGenResolve.get_current_file()
|
||||
if fp is None: return None
|
||||
@@ -588,7 +588,7 @@ novirtual class AssignGenMixin:
|
||||
return 0
|
||||
|
||||
def require_cdefine_storable(self, pool: memhub.MemBuddy*, node: ast.AST*, name: str, value: int64, annot: ast.AST*, rhs_bits: int = 0) -> None:
|
||||
"""Fail fast if a CDefine value is not storable in its declared type.
|
||||
"""Fail fast if a DefineT value is not storable in its declared type.
|
||||
|
||||
rhs_bits: 右侧表达式自身的位宽(0=未定宽)。右侧带显式强转(如 WORD(...))且不宽于声明注解时,
|
||||
截断是**显式**的(左侧只看到截断后的数据),此时不再按"存不下"报错——这正是
|
||||
@@ -640,10 +640,10 @@ novirtual class AssignGenMixin:
|
||||
if bits >= dw: return
|
||||
self.Gen.TypeGenErr.fatal_error(value_node, f"[VPC] define constant use-site width insufficient: constant '{rn.id}' is {dw} bits wide " f"(declared width; actual value {cdef_val}), but the use site (variable declaration annotation) provides only {bits} bits. " f"Silent truncation/downgrade is forbidden; compilation aborted. " f"Fix: widen the variable declaration annotation to at least {dw} bits (such as int{dw}T / CUInt{dw}T), " f"or narrow the constant at its define with an explicit cast.", "TypeError")
|
||||
|
||||
# eval_cdefine_float_expr - 编译期**浮点**常量表达式求值(CDefine 值为浮点表达式时用)。
|
||||
# eval_cdefine_float_expr - 编译期**浮点**常量表达式求值(DefineT 值为浮点表达式时用)。
|
||||
# 支持 Constant(float/int)、UnaryOp(+/-)、Call 强转包裹(float(x)/double(x)/t.CDouble(x))、
|
||||
# BinOp(+ - * /)。成功写 out[0] 并返回 1;不是纯浮点常量表达式时返回 0(交回整型路径)。
|
||||
# 与 extract_cdefine_int_value_bits 的分工:后者只服务整型 CDefine,遇到 float 字面量会 fail-fast。
|
||||
# 与 extract_cdefine_int_value_bits 的分工:后者只服务整型 DefineT,遇到 float 字面量会 fail-fast。
|
||||
def eval_cdefine_float_expr(self, node: ast.AST*, out: double*, pool: memhub.MemBuddy*) -> int:
|
||||
if node is None: return 0
|
||||
k: int = node.kind()
|
||||
@@ -690,14 +690,14 @@ novirtual class AssignGenMixin:
|
||||
return 1
|
||||
if bop.op == ast.OpKind.Div:
|
||||
if rv == 0.0:
|
||||
self.Gen.TypeGenErr.fatal_error(node, "constant expression division by zero: the right operand of '/' evaluates to 0 at compile time (float CDefine).", "ValueError")
|
||||
self.Gen.TypeGenErr.fatal_error(node, "constant expression division by zero: the right operand of '/' evaluates to 0 at compile time (float DefineT).", "ValueError")
|
||||
out[0] = lv / rv
|
||||
return 1
|
||||
return 0
|
||||
return 0
|
||||
|
||||
# expr_has_float_literal - 表达式里是否**含浮点字面量**(递归)。
|
||||
# 用于把"浮点 CDefine"与"整型 CDefine"分开:只有含浮点字面量时才允许走浮点求值,
|
||||
# 用于把"浮点 DefineT"与"整型 DefineT"分开:只有含浮点字面量时才允许走浮点求值,
|
||||
# 否则纯整型表达式会被登记成 double,破坏整型位宽与整型语义。
|
||||
def expr_has_float_literal(self, node: ast.AST*) -> int:
|
||||
if node is None: return 0
|
||||
@@ -808,12 +808,12 @@ novirtual class AssignGenMixin:
|
||||
return prev
|
||||
|
||||
def _register_cdefine_from_value(self, pool: memhub.MemBuddy*, name: str, val_node: ast.AST*, annot: ast.AST* = None) -> None:
|
||||
"""Register a CDefine constant (double for floats, else integer)."""
|
||||
"""Register a DefineT constant (double for floats, else integer)."""
|
||||
if pool is None or name is None: return
|
||||
# 浮点宽度种类(f32 与否)必须**在拆掉右侧包裹前**判定:
|
||||
# ① 注解有显式类型(`define PI: float = ...` / `PI: float | t.CDefine = ...`)→ 注解说了算
|
||||
# ① 注解有显式类型(`define PI: float = ...` / `PI: float | t.DefineT = ...`)→ 注解说了算
|
||||
# (`define X: double = float(1.5)` 按 double 走);
|
||||
# ② 注解没有显式类型(`PI: t.CDefine = float(3.14)`)→ 看右侧强转包裹 float(...) / double(...)。
|
||||
# ② 注解没有显式类型(`PI: t.DefineT = float(3.14)`)→ 看右侧强转包裹 float(...) / double(...)。
|
||||
f32_kind: int = 0
|
||||
if self._define_explicit_annot(annot) is not None:
|
||||
f32_kind = self.cdefine_decl_float_bits(pool, annot)
|
||||
@@ -850,7 +850,7 @@ novirtual class AssignGenMixin:
|
||||
if val_node is not None and val_node.kind() == ast.ASTKind.Constant:
|
||||
cn_fp: ast.Constant* = ast.Constant*(val_node)
|
||||
if cn_fp.const_kind == ast.CONST_STR:
|
||||
# String CDefine: register as a string constant (reader returns a global string)
|
||||
# String DefineT: register as a string constant (reader returns a global string)
|
||||
self.Gen.TypeGenCDef.register_cdefine_string(pool, name, cn_fp.str_val)
|
||||
return
|
||||
if cn_fp.const_kind == ast.CONST_FLOAT:
|
||||
@@ -860,13 +860,13 @@ novirtual class AssignGenMixin:
|
||||
if self._recover_cdefine_float(name, &rec_d) != 0: use_val = rec_d
|
||||
else:
|
||||
# Source recovery failed: fail fast to avoid registering a corrupted float
|
||||
self.Gen.TypeGenErr.fatal_error(val_node, "CDefine float value source-file recovery failed", "InternalCompilerError")
|
||||
self.Gen.TypeGenErr.fatal_error(val_node, "DefineT float value source-file recovery failed", "InternalCompilerError")
|
||||
return
|
||||
self.Gen.TypeGenCDef.register_cdefine_double(pool, name, use_val, f32_kind)
|
||||
return
|
||||
if val_node is None:
|
||||
# CDefine declaration without a value node: fail fast
|
||||
self.Gen.TypeGenErr.fatal_error(None, f"CDefine value extraction failed: '{name}' is missing an initial value", "TypeError")
|
||||
# DefineT declaration without a value node: fail fast
|
||||
self.Gen.TypeGenErr.fatal_error(None, f"DefineT value extraction failed: '{name}' is missing an initial value", "TypeError")
|
||||
return
|
||||
# 浮点常量表达式(如 `define GW_F = 160.0 * 4`):整型提取器只认整型,遇到其中的 float
|
||||
# 字面量会 fail-fast("cannot parse a compile-time constant")。这里先按浮点求值;
|
||||
@@ -877,7 +877,7 @@ novirtual class AssignGenMixin:
|
||||
self.Gen.TypeGenCDef.register_cdefine_double(pool, name, fv_try, f32_kind)
|
||||
return
|
||||
# 含浮点字面量但不是编译期常量(含变量/函数调用)⇒ fail-fast,不静默降级
|
||||
self.Gen.TypeGenErr.fatal_error(val_node, f"CDefine float expression is not a compile-time constant: '{name}'", "TypeError")
|
||||
self.Gen.TypeGenErr.fatal_error(val_node, f"DefineT float expression is not a compile-time constant: '{name}'", "TypeError")
|
||||
return
|
||||
# 位宽裁决:与跨模块使用方共用 cdefine_decl_bits(注解 → 右侧显式强转 → 值的最小可存宽度)。
|
||||
# 有注解而存不下时:右侧带显式强转截断则不报错(左侧只看到截断值),否则由
|
||||
@@ -889,7 +889,7 @@ novirtual class AssignGenMixin:
|
||||
self.require_cdefine_storable(pool, val_node, name, val_fp, annot, rhs_bits)
|
||||
self.Gen.TypeGenCDef.register_cdefine_constant(pool, name, val_fp, vb)
|
||||
|
||||
# Recover a CDefine float value by re-reading its source line with atof
|
||||
# Recover a DefineT float value by re-reading its source line with atof
|
||||
|
||||
def _recover_found(self) -> int:
|
||||
"""Return whether the last _recover_cdefine_float succeeded."""
|
||||
@@ -948,7 +948,7 @@ novirtual class AssignGenMixin:
|
||||
if sep >= line_start + line_len: continue
|
||||
if buf[sep] != ':' and buf[sep] != '=': continue
|
||||
|
||||
# CDefine path requires t.CDefine in the line, or the define keyword form
|
||||
# DefineT path requires t.DefineT in the line, or the define keyword form
|
||||
if require_cdefine != 0 and is_define_line == 0:
|
||||
has_cd: int = 0
|
||||
ci: sizet = line_start + name_len + 1
|
||||
@@ -998,7 +998,7 @@ novirtual class AssignGenMixin:
|
||||
return 0
|
||||
|
||||
def _recover_cdefine_float(self, name: str, out: double*) -> int:
|
||||
"""Recover a CDefine float value (requiring a t.CDefine line)."""
|
||||
"""Recover a DefineT float value (requiring a t.DefineT line)."""
|
||||
return self._recover_float_literal(name, out, 1)
|
||||
|
||||
# _annotation_contains_name - recursively check whether an annotation contains a name
|
||||
@@ -1020,13 +1020,13 @@ novirtual class AssignGenMixin:
|
||||
return 0
|
||||
|
||||
def _annotation_has_cptr_marker(self, annot: ast.AST*) -> int:
|
||||
"""Return 1 if the annotation is a CPtr pointer wrapper (the `T*` form)."""
|
||||
"""Return 1 if the annotation is a PtrT pointer wrapper (the `T*` form)."""
|
||||
if annot is None: return 0
|
||||
if annot.kind() != ast.ASTKind.Subscript: return 0
|
||||
sub_cp: ast.Subscript* = ast.Subscript*(annot)
|
||||
if sub_cp.value is not None and sub_cp.value.kind() == ast.ASTKind.Name:
|
||||
nm_cp: ast.Name* = ast.Name*(sub_cp.value)
|
||||
if nm_cp.id is not None and (string.strcmp(nm_cp.id, "CPtr") == 0 or string.strcmp(nm_cp.id, "rawptr") == 0): return 1
|
||||
if nm_cp.id is not None and (string.strcmp(nm_cp.id, "PtrT") == 0 or string.strcmp(nm_cp.id, "rawptr") == 0): return 1
|
||||
if sub_cp.slice is not None: return self._annotation_has_cptr_marker(sub_cp.slice)
|
||||
return 0
|
||||
|
||||
@@ -1041,14 +1041,14 @@ novirtual class AssignGenMixin:
|
||||
pe_nm: ast.Name* = ast.Name*(sub_pe.value)
|
||||
if pe_nm.id is None: return 0
|
||||
# Descriptor / storage / ref wrapper: the pointer form lives in the wrapped type.
|
||||
if string.strcmp(pe_nm.id, "CConst") == 0 or string.strcmp(pe_nm.id, "CVolatile") == 0 \
|
||||
if string.strcmp(pe_nm.id, "ConstT") == 0 or string.strcmp(pe_nm.id, "VolatileT") == 0 \
|
||||
or string.strcmp(pe_nm.id, "BigEndian") == 0 or string.strcmp(pe_nm.id, "LittleEndian") == 0 \
|
||||
or string.strcmp(pe_nm.id, "CStatic") == 0 or string.strcmp(pe_nm.id, "CExport") == 0 \
|
||||
or string.strcmp(pe_nm.id, "CExtern") == 0 or string.strcmp(pe_nm.id, "State") == 0 \
|
||||
or string.strcmp(pe_nm.id, "CDefine") == 0 or string.strcmp(pe_nm.id, "CRef") == 0 \
|
||||
or string.strcmp(pe_nm.id, "CTempRef") == 0 or string.strcmp(pe_nm.id, "CAutoPtr") == 0:
|
||||
or string.strcmp(pe_nm.id, "StaticT") == 0 or string.strcmp(pe_nm.id, "ExportT") == 0 \
|
||||
or string.strcmp(pe_nm.id, "ExternT") == 0 or string.strcmp(pe_nm.id, "State") == 0 \
|
||||
or string.strcmp(pe_nm.id, "DefineT") == 0 or string.strcmp(pe_nm.id, "RefT") == 0 \
|
||||
or string.strcmp(pe_nm.id, "TempRefT") == 0 or string.strcmp(pe_nm.id, "AutoPtrT") == 0:
|
||||
return self._is_ptr_element_annotation(sub_pe.slice)
|
||||
if string.strcmp(pe_nm.id, "CPtr") != 0 and string.strcmp(pe_nm.id, "rawptr") != 0: return 0
|
||||
if string.strcmp(pe_nm.id, "PtrT") != 0 and string.strcmp(pe_nm.id, "rawptr") != 0: return 0
|
||||
inner_pe: ast.AST* = sub_pe.slice
|
||||
if inner_pe is None: return 0
|
||||
# str/bytes are already i8*; the pointer layer makes them i8** and needs 8-byte slots.
|
||||
@@ -1060,9 +1060,15 @@ novirtual class AssignGenMixin:
|
||||
gp_nm: str = inner_nm_pe.id
|
||||
if gp_nm is not None:
|
||||
gp_resolved: str = self.Gen.TypeGenBase.resolve_generic_type_name(gp_nm)
|
||||
# resolve_generic_type_name 对非泛型名原样返回入参,所以"是否为泛型形参"只能用
|
||||
# 解析结果与入参名是否相同来判定。此前 return 1 落在 if 外,任何具名 pointee
|
||||
# (如 `uint8*`)都会被误判成 ptr-element,下标按 8 字节步长 GEP 并 load 指针:
|
||||
# 现场 includes/hashlib/__sha1.vp 的 `data: uint8* = block` 被编成
|
||||
# `getelementptr !llvm.ptr, ptr %block, i32 idx` + `load !llvm.ptr` + ptrtoint,
|
||||
# 越界读堆 → VPC2 的 SHA1 全部算错(每次运行值都不同)。
|
||||
if gp_resolved is not None and string.strcmp(gp_resolved, gp_nm) != 0:
|
||||
if self._is_scalar_ty_name(gp_resolved) != 0: return 0
|
||||
if gp_resolved is not None: return 1
|
||||
return 1
|
||||
# 64-bit scalar pointee: element is 8 bytes (else GEP truncation).
|
||||
if self._is_64bit_scalar_ty(inner_pe) != 0: return 1
|
||||
return 0
|
||||
@@ -1198,29 +1204,6 @@ novirtual class AssignGenMixin:
|
||||
# life_of_expr - 求表达式当前值的生命期标签。
|
||||
# target_ty 参与判定:裸本帧数组名在**指针**目标位置 = 数组退让(交出本帧栈地址),
|
||||
# 在同型聚合目标位置 = 按值拷贝(LIFE_VALUE)。
|
||||
# [TEMP-DIAG] life_of_expr 的 Name 分支判定输入快照(定位完删除)。
|
||||
# hit 语义:1=已登记 HEAP/STATIC 直接返回 / 2=裸数组名+指针目标(FRAME) / 3=裸数组名+值目标(VALUE)
|
||||
# 4=值类型变量+值目标(VALUE,本次新增分支) / 5=指针或异型落到登记值 / 6=变量非指针落到登记值
|
||||
def _life_diag_name(self, trans: HT.Translator*, nm: str, rg: int, v: mlirlite.Value*, target_ty: mlirlite.LLVMType*, hit: int) -> int:
|
||||
vptr: int = -1
|
||||
iptr: int = -1
|
||||
ikind: int = -1
|
||||
tptr: int = -1
|
||||
if v is not None and v.Ty is not None:
|
||||
vptr = self.Gen.ExprGenBase.is_ptr_type(v.Ty)
|
||||
if vptr != 0 and v.Ty.Pointee is not None:
|
||||
iptr = self.Gen.ExprGenBase.is_ptr_type(v.Ty.Pointee)
|
||||
ikind = int(v.Ty.Pointee.kind())
|
||||
if target_ty is not None: tptr = self.Gen.ExprGenBase.is_ptr_type(target_ty)
|
||||
fn_s: str = None
|
||||
if trans._cur_func is not None: fn_s = mlirlite.function_get_name(trans._cur_func)
|
||||
if fn_s is None: fn_s = "?"
|
||||
nm_s: str = nm
|
||||
if nm_s is None: nm_s = "?"
|
||||
stdio.printf("[LIFE-DIAG] fn=%s name=%s rg=%d hit=%d vptr=%d iptr=%d ikind=%d tptr=%d\n",
|
||||
fn_s, nm_s, rg, hit, vptr, iptr, ikind, tptr)
|
||||
return 0
|
||||
|
||||
def life_of_expr(self, trans: HT.Translator*, node: ast.AST*, target_ty: mlirlite.LLVMType*) -> int:
|
||||
"""表达式的 LifeTag;无法判定时 LIFE_UNKNOWN(保守放行)"""
|
||||
if node is None or trans is None: return GenTypes.LIFE_VALUE
|
||||
@@ -1276,9 +1259,6 @@ novirtual class AssignGenMixin:
|
||||
hit = 4
|
||||
else:
|
||||
hit = 5
|
||||
# [TEMP-DIAG] 只打"结果不是值拷贝"或"新增分支命中"的情况,避免刷屏(定位完删除)
|
||||
if res != GenTypes.LIFE_VALUE or hit == 4:
|
||||
self._life_diag_name(trans, nm.id, rg, v, target_ty, hit)
|
||||
return res
|
||||
if k == ast.ASTKind.UnaryOp:
|
||||
uo: ast.UnaryOp* = ast.UnaryOp*(node)
|
||||
@@ -1335,27 +1315,6 @@ novirtual class AssignGenMixin:
|
||||
# 目标位置是地址(`&T(...)`、指针目标、指针槽)才交出本帧地址 ⇒ LIFE_FRAME。
|
||||
if target_ty is not None and self.Gen.ExprGenBase.is_ptr_type(target_ty) == 0:
|
||||
return GenTypes.LIFE_VALUE
|
||||
# [TEMP-DIAG] 打印构造类型与目标类型的**真实 LLVM 文本**(定位完删除):
|
||||
# ct_ty 是 `T(...)` 自身的类型(`%"..T"` = 值本体 / `ptr` = 已被解析成指针)
|
||||
# tgt 是目标位置(模块级全局/值槽)的类型文本
|
||||
ctb: str = "?"
|
||||
bt1: str = trans.Pool.alloc_tagged(512, memhub.DETSITE_ASSIGNGEN_578)
|
||||
if bt1 is not None:
|
||||
mlirlite.TypePrint(bt1, 512, ct_ty, trans.Pool)
|
||||
ctb = bt1
|
||||
ttb: str = "(none)"
|
||||
if target_ty is not None:
|
||||
bt2: str = trans.Pool.alloc_tagged(512, memhub.DETSITE_ASSIGNGEN_579)
|
||||
if bt2 is not None:
|
||||
mlirlite.TypePrint(bt2, 512, target_ty, trans.Pool)
|
||||
ttb = bt2
|
||||
hv: int = -1
|
||||
if ct_en is not None: hv = ct_en.HasNew
|
||||
fn_c: str = None
|
||||
if trans._cur_func is not None: fn_c = mlirlite.function_get_name(trans._cur_func)
|
||||
if fn_c is None: fn_c = "?"
|
||||
stdio.printf("[CTOR-DIAG] fn=%s ctor=%s hasnew=%d ct_ty=%s tgt=%s\n",
|
||||
fn_c, ctor_nm, hv, ctb, ttb)
|
||||
return GenTypes.LIFE_FRAME
|
||||
return GenTypes.LIFE_UNKNOWN
|
||||
if k == ast.ASTKind.New:
|
||||
@@ -1499,16 +1458,6 @@ novirtual class AssignGenMixin:
|
||||
if life_r != GenTypes.LIFE_FRAME: return 0
|
||||
rk: int = GenApi.get_ret_kind_by_func(fd)
|
||||
if rk == GenApi.RET_KIND_FRAME: return 0
|
||||
# [TEMP-DIAG] 定义端拦截点输入(只在"判为帧生命期且未被 @life(ret(frame)) 放行"时打,定位完删除)
|
||||
fn_d: str = None
|
||||
if trans._cur_func is not None: fn_d = mlirlite.function_get_name(trans._cur_func)
|
||||
if fn_d is None: fn_d = "?"
|
||||
rvp: int = -1
|
||||
rvk: int = -1
|
||||
if rv_ty is not None:
|
||||
rvp = self.Gen.ExprGenBase.is_ptr_type(rv_ty)
|
||||
rvk = int(rv_ty.kind())
|
||||
stdio.printf("[RT-DIAG] fn=%s life=%d rk=%d rvptr=%d rvkind=%d\n", fn_d, life_r, rk, rvp, rvk)
|
||||
is_borrow_kind: int = 0
|
||||
pi: int = -1
|
||||
if rk >= GenApi.RET_KIND_BORROW_BASE:
|
||||
@@ -1672,7 +1621,7 @@ novirtual class AssignGenMixin:
|
||||
if string.strchr(ty_name, '*') is not None: return 0
|
||||
if string.strcmp(ty_name, "str") == 0: return 0
|
||||
if string.strcmp(ty_name, "bytes") == 0: return 0
|
||||
if string.strcmp(ty_name, "CPtr") == 0: return 0
|
||||
if string.strcmp(ty_name, "PtrT") == 0: return 0
|
||||
if string.strstr(ty_name, "Ptr") is not None: return 0
|
||||
# builtin short type names
|
||||
if string.strcmp(ty_name, "sizet") == 0: return 1
|
||||
@@ -1790,17 +1739,21 @@ novirtual class AssignGenMixin:
|
||||
return 0
|
||||
|
||||
# _init_global_array_from_list - initialize a global array from a list literal
|
||||
def _init_global_array_from_list(self, builder: mlirlite.IRBuilder*, pool: memhub.MemBuddy*, mod: mlirlite.LLVMModule*, array_alloca: mlirlite.Value*, list_node: ast.AST*, trans: HT.Translator*, owner_ctx: str = None, byte_order: int = 0) -> int:
|
||||
def _init_global_array_from_list(self, builder: mlirlite.IRBuilder*, pool: memhub.MemBuddy*, mod: mlirlite.LLVMModule*, array_alloca: mlirlite.Value*, list_node: ast.AST*, trans: HT.Translator*, owner_ctx: str = None, byte_order: int = 0, arr_ty_opt: mlirlite.LLVMType* = None) -> int:
|
||||
"""Initialize a global array from a list literal via per-element GEP+store.
|
||||
|
||||
byte_order (0=native / 1=BigEndian / 2=LittleEndian): when the array variable is big-endian
|
||||
every element is stored byte-swapped, so a later element read (which un-swaps) sees the same
|
||||
value; left at 0 for module globals / struct-field defaults (not registered)."""
|
||||
value; left at 0 for module globals / struct-field defaults (not registered).
|
||||
arr_ty_opt: the declared array type, used when the destination value carries no pointee
|
||||
type (struct-field defaults go through a GEP whose Value.Ty may be absent, which used to
|
||||
bail out silently and leave the field zero-initialized — 现场: sha1.state)."""
|
||||
if builder is None or array_alloca is None or list_node is None: return 1
|
||||
|
||||
# Get the array type (pointee of [N x elem_ty]*)
|
||||
arr_ty: mlirlite.LLVMType* = None
|
||||
if array_alloca.Ty is not None: arr_ty = array_alloca.Ty.Pointee
|
||||
if arr_ty is None: arr_ty = arr_ty_opt
|
||||
if arr_ty is None: return 1
|
||||
|
||||
# Match the array type to get the element type and declared length
|
||||
@@ -2031,9 +1984,9 @@ novirtual class AssignGenMixin:
|
||||
# translate_augassign - translate augmented assignment statements
|
||||
def const_assign_msg(self, kind: str, name: str, src_name: str) -> str:
|
||||
"""Build the const-assignment error message used by const_assign_fail."""
|
||||
decl: str = "(annotation contains t.CConst; the source name at the declaration was not recorded)"
|
||||
if src_name is not None: decl = f"CConst | {src_name}"
|
||||
return GenApi.escape_fstring_copy(f"cannot assign to const variable '{name}' ({kind}); " f"declaration type (including const): {decl}" f"; fix: remove the const descriptor / t.CConst from the declaration, or assign once at the declaration " f"and afterwards use only non-const copies/temporaries; a const object forbids assignment, " f"augmented assignment and element assignment within its scope, and this never degrades to a warning")
|
||||
decl: str = "(annotation contains t.ConstT; the source name at the declaration was not recorded)"
|
||||
if src_name is not None: decl = f"ConstT | {src_name}"
|
||||
return GenApi.escape_fstring_copy(f"cannot assign to const variable '{name}' ({kind}); " f"declaration type (including const): {decl}" f"; fix: remove the const descriptor / t.ConstT from the declaration, or assign once at the declaration " f"and afterwards use only non-const copies/temporaries; a const object forbids assignment, " f"augmented assignment and element assignment within its scope, and this never degrades to a warning")
|
||||
|
||||
def const_assign_fail(self, node: ast.AST*, kind: str, name: str, src_name: str) -> int:
|
||||
"""Fail fast when a const target is written."""
|
||||
@@ -2076,12 +2029,12 @@ novirtual class AssignGenMixin:
|
||||
if target_ty is None:
|
||||
# fail-fast: field type missing, no i32 fallback
|
||||
self.Gen.TypeGenErr.fatal_error(target, "field type information missing", "TypeError")
|
||||
# t.CVolatile field: both load and store carry the volatile qualifier
|
||||
# t.VolatileT field: both load and store carry the volatile qualifier
|
||||
vol_fa: int = self.Gen.ExprGenAttr.attr_field_is_volatile(pool, trans, target)
|
||||
# BigEndian field: the memory slot holds the byte-swapped pattern, so the current value must
|
||||
# be un-swapped before it takes part in the arithmetic, and the result re-swapped on store.
|
||||
bo_fa: int = self.Gen.ExprGenAttr.attr_field_byte_order(pool, trans, target)
|
||||
# rawptr[t.CVolatile|T] base: field load/store also carry volatile
|
||||
# rawptr[t.VolatileT|T] base: field load/store also carry volatile
|
||||
at_aug: ast.Attribute* = ast.Attribute*(target)
|
||||
if at_aug is not None:
|
||||
if self.Gen.ExprGenAttr.base_pointee_is_volatile(pool, trans, at_aug.value) != 0: vol_fa = 1
|
||||
@@ -2092,7 +2045,7 @@ novirtual class AssignGenMixin:
|
||||
cur_val = self.Gen.ExprGenBase.apply_byte_order_bswap(pool, builder, cur_val, bo_fa)
|
||||
|
||||
# 3. Translate the RHS value
|
||||
# 与 `x = x op y` 同口径:RHS 在左值类型(target_ty)上下文中翻译,嵌套 binop / CDefine 的
|
||||
# 与 `x = x op y` 同口径:RHS 在左值类型(target_ty)上下文中翻译,嵌套 binop / DefineT 的
|
||||
# 目标感知与常量适配据此生效(旧行为 target_ty=None 使 RHS 口径与二元形态不一致)。
|
||||
rhs_val: mlirlite.Value* = self.Gen.ExprGen.translate_value(builder, pool, mod, aug.value, None, 0, trans, target_ty)
|
||||
if rhs_val is None:
|
||||
@@ -2149,7 +2102,7 @@ novirtual class AssignGenMixin:
|
||||
if target_ty is None:
|
||||
# fail-fast: element type missing, no i32 fallback
|
||||
self.Gen.TypeGenErr.fatal_error(target, "element type information missing", "TypeError")
|
||||
# rawptr[t.CVolatile|T] base: p[i] load/store carry volatile when the base is resolvable
|
||||
# rawptr[t.VolatileT|T] base: p[i] load/store carry volatile when the base is resolvable
|
||||
vol_sa: int = 0
|
||||
sub_aug: ast.Subscript* = ast.Subscript*(target)
|
||||
if sub_aug is not None: vol_sa = self.Gen.ExprGenAttr.base_pointee_is_volatile(pool, trans, sub_aug.value)
|
||||
@@ -2167,7 +2120,7 @@ novirtual class AssignGenMixin:
|
||||
cur_val = self.Gen.ExprGenBase.apply_byte_order_bswap(pool, builder, cur_val, bo_sa)
|
||||
|
||||
# 3. Translate the RHS value
|
||||
# 与 `x = x op y` 同口径:RHS 在左值类型(target_ty)上下文中翻译,嵌套 binop / CDefine 的
|
||||
# 与 `x = x op y` 同口径:RHS 在左值类型(target_ty)上下文中翻译,嵌套 binop / DefineT 的
|
||||
# 目标感知与常量适配据此生效(旧行为 target_ty=None 使 RHS 口径与二元形态不一致)。
|
||||
rhs_val: mlirlite.Value* = self.Gen.ExprGen.translate_value(builder, pool, mod, aug.value, None, 0, trans, target_ty)
|
||||
if rhs_val is None:
|
||||
@@ -2242,7 +2195,7 @@ novirtual class AssignGenMixin:
|
||||
if GenApi.is_global_name(trans, nm.id) != 0: scope_type = 1
|
||||
elif GenApi.is_nonlocal_name(trans, nm.id) != 0: scope_type = 2
|
||||
|
||||
# t.CVolatile variable: both load and store carry the volatile qualifier
|
||||
# t.VolatileT variable: both load and store carry the volatile qualifier
|
||||
vol_aa: int = self.Gen.VarGen.var_is_volatile(self.Gen.VarGen.lookup_var_entry(trans.SymTab, nm.id))
|
||||
# BigEndian local variable: un-swap on load / re-swap on store so the arithmetic sees the native
|
||||
# value (local scope only; global/nonlocal are not registered in the byte-order table, like TPC).
|
||||
@@ -2350,23 +2303,23 @@ novirtual class AssignGenMixin:
|
||||
|
||||
return 0
|
||||
|
||||
# is_ctypedef_annotation - check whether an annotation is t.CTypedef
|
||||
# is_ctypedef_annotation - check whether an annotation is t.TypedefT
|
||||
def is_ctypedef_annotation(self, annot: ast.AST*) -> int:
|
||||
"""Return 1 if the annotation is t.CTypedef, else 0."""
|
||||
"""Return 1 if the annotation is t.TypedefT, else 0."""
|
||||
return self.Gen.TypeGenCType._is_ctypedef_annotation(annot)
|
||||
|
||||
# _is_single_param_carray_annot - check for single-param t.CArray[elem_ty]
|
||||
# _is_single_param_carray_annot - check for single-param t.ArrayT[elem_ty]
|
||||
def _is_single_param_carray_annot(self, annot: ast.AST*) -> int:
|
||||
"""Return 1 for a single-param or None-length t.CArray annotation."""
|
||||
"""Return 1 for a single-param or None-length t.ArrayT annotation."""
|
||||
if annot is None or annot.kind() != ast.ASTKind.Subscript: return 0
|
||||
sub: ast.Subscript* = ast.Subscript*(annot)
|
||||
if sub.value is None or sub.slice is None: return 0
|
||||
if sub.value.kind() == ast.ASTKind.Name:
|
||||
# Bare CArray / builtin rawarray / builtin array are all equivalent.
|
||||
# Bare ArrayT / builtin rawarray / builtin array are all equivalent.
|
||||
bn: ast.Name* = ast.Name*(sub.value)
|
||||
if bn.id is None or (string.strcmp(bn.id, "CArray") != 0 and string.strcmp(bn.id, "rawarray") != 0 and string.strcmp(bn.id, "array") != 0): return 0
|
||||
if bn.id is None or (string.strcmp(bn.id, "ArrayT") != 0 and string.strcmp(bn.id, "rawarray") != 0 and string.strcmp(bn.id, "array") != 0): return 0
|
||||
else: return 0
|
||||
# Single-param form: slice is not a Tuple (e.g. CArray[u64])
|
||||
# Single-param form: slice is not a Tuple (e.g. ArrayT[u64])
|
||||
if sub.slice.kind() != ast.ASTKind.Tuple: return 1
|
||||
# Two-param form with count=None is equivalent to single-param
|
||||
tup: ast.Tuple* = ast.Tuple*(sub.slice)
|
||||
@@ -2377,9 +2330,9 @@ novirtual class AssignGenMixin:
|
||||
if cn0.const_kind == ast.CONST_NONE: return 1
|
||||
return 0
|
||||
|
||||
# _infer_static_array_ty_from_literal - single-param CArray + list literal -> [len x elem_ty]
|
||||
# _infer_static_array_ty_from_literal - single-param ArrayT + list literal -> [len x elem_ty]
|
||||
def _infer_static_array_ty_from_literal(self, pool: memhub.MemBuddy*, annot: ast.AST*, value: ast.AST*, resolved_ty: mlirlite.LLVMType*) -> mlirlite.LLVMType*:
|
||||
"""Return Array(elem_ty, len) for a single-param CArray with a list literal."""
|
||||
"""Return Array(elem_ty, len) for a single-param ArrayT with a list literal."""
|
||||
if value is not None and value.kind() == ast.ASTKind.List and resolved_ty is not None:
|
||||
if self._is_single_param_carray_annot(annot) != 0:
|
||||
elem_ty_arr: mlirlite.LLVMType* = resolved_ty.Pointee
|
||||
@@ -2428,13 +2381,13 @@ novirtual class AssignGenMixin:
|
||||
nm: ast.Name* = ast.Name*(target)
|
||||
if nm.id is None: return 0
|
||||
|
||||
# CDefine annotation: register the constant instead of creating an alloca
|
||||
# DefineT annotation: register the constant instead of creating an alloca
|
||||
if self.is_cdefine_annotation(aa.annotation) != 0:
|
||||
pool_ps: memhub.MemBuddy* = trans.Pool
|
||||
self._register_cdefine_from_value(pool_ps, nm.id, aa.value)
|
||||
return 0
|
||||
|
||||
# CTypedef annotation: register the alias only, no storage (avoids duplicate symbols)
|
||||
# TypedefT annotation: register the alias only, no storage (avoids duplicate symbols)
|
||||
if self.is_ctypedef_annotation(aa.annotation) != 0: return 0
|
||||
|
||||
# REPL fragment entry: AnnAssign goes to the session slot; skip pre-creating a local alloca
|
||||
@@ -2466,7 +2419,7 @@ novirtual class AssignGenMixin:
|
||||
resolved: mlirlite.LLVMType* = self.Gen.TypeGenResolve.resolve_annotation_type(pool, annot, trans._imported_modules, trans._from_imports, trans, 0)
|
||||
if resolved is not None:
|
||||
var_ty = resolved
|
||||
# Single-param CArray + list literal -> static array; keep PreScan and Handle types in sync
|
||||
# Single-param ArrayT + list literal -> static array; keep PreScan and Handle types in sync
|
||||
var_ty = self._infer_static_array_ty_from_literal(pool, annot, aa.value, var_ty)
|
||||
# [D31 fail-fast] generic annotation degraded to a bare base class
|
||||
self._fail_on_degenerated_generic_annotation(aa, nm.id, annot, var_ty)
|
||||
@@ -2480,23 +2433,6 @@ novirtual class AssignGenMixin:
|
||||
# Ensure the full cross-module struct definition is available (for alloca)
|
||||
self.Gen.StructGen.ensure_struct_def_in_module(pool, trans.Module, var_ty)
|
||||
alloca: mlirlite.Value* = self.Gen.VarGen._alloca_at_entry(builder, var_ty)
|
||||
# [TEMP-DIAG] 变量声明的槽类型 + **alloca 地址**(定位完删除):
|
||||
# `npos = pos + Δ` 之后 pos.y 变化 ⇒ 若 pos/npos 的 slot 地址相同,说明两个变量
|
||||
# 共用了同一块存储(槽复用/RVO 回填错槽),值语义被破坏。
|
||||
vtb: str = "?"
|
||||
bt0: str = pool.alloc_tagged(512, memhub.DETSITE_ASSIGNGEN_578)
|
||||
if bt0 is not None:
|
||||
mlirlite.TypePrint(bt0, 512, var_ty, pool)
|
||||
vtb = bt0
|
||||
vptr0: int = -1
|
||||
if var_ty is not None: vptr0 = self.Gen.ExprGenBase.is_ptr_type(var_ty)
|
||||
nm0: str = nm.id
|
||||
if nm0 is None: nm0 = "?"
|
||||
fn0: str = None
|
||||
if trans._cur_func is not None: fn0 = mlirlite.function_get_name(trans._cur_func)
|
||||
if fn0 is None: fn0 = "?"
|
||||
stdio.printf("[VAR-DIAG] fn=%s name=%s vptr=%d slot=0x%x var_ty=%s\n",
|
||||
fn0, nm0, vptr0, uint64(alloca) if alloca is not None else uint64(0), vtb)
|
||||
if alloca is not None:
|
||||
if self.Gen.VarGen.define_var(trans.SymTab, nm.id, alloca) == 0:
|
||||
# Store the annotation's class name; pass pool so generic Subscript yields the mangled name
|
||||
@@ -2505,11 +2441,11 @@ novirtual class AssignGenMixin:
|
||||
if cls_nm_ps is not None: self.Gen.VarGen.set_var_annot_class_name(trans.SymTab, nm.id, cls_nm_ps)
|
||||
# Check for bytes* or str* (i8** semantics, 8-byte stride)
|
||||
if self._is_ptr_element_annotation(annot) != 0: self.Gen.VarGen.set_var_ptr_element(trans.SymTab, nm.id)
|
||||
# const object (annotation has t.CConst and the type is not a pointer): no writes in scope
|
||||
# const object (annotation has t.ConstT and the type is not a pointer): no writes in scope
|
||||
if self.Gen.TypeGenErr.is_const_object_marker(annot, var_ty) != 0: self.Gen.VarGen.set_var_const(trans.SymTab, nm.id)
|
||||
# t.CVolatile: mark the variable at declaration; its load/store carry volatile
|
||||
if self.Gen.TypeGenErr.has_decorator_marker(annot, "CVolatile", 0) != 0: self.Gen.VarGen.set_var_volatile(trans.SymTab, nm.id)
|
||||
# rawptr[t.CVolatile | T]: dereference load/store carry volatile
|
||||
# t.VolatileT: mark the variable at declaration; its load/store carry volatile
|
||||
if self.Gen.TypeGenErr.has_decorator_marker(annot, "VolatileT", 0) != 0: self.Gen.VarGen.set_var_volatile(trans.SymTab, nm.id)
|
||||
# rawptr[t.VolatileT | T]: dereference load/store carry volatile
|
||||
if self.Gen.TypeGenErr.has_pointee_volatile_marker(annot) != 0: self.Gen.VarGen.set_var_pointee_volatile(trans.SymTab, nm.id)
|
||||
return 0
|
||||
return 0
|
||||
@@ -2518,13 +2454,13 @@ novirtual class AssignGenMixin:
|
||||
def _prespecialize_generic_annotation(self, trans: HT.Translator*, pool: memhub.MemBuddy*, annot: ast.AST*) -> int:
|
||||
"""Pre-specialize a generic annotation; return 1 if attempted."""
|
||||
if annot is None or trans is None: return 0
|
||||
# Take the generic Subscript side: the Subscript itself or the Subscript wrapped by CPtr[T].
|
||||
# Take the generic Subscript side: the Subscript itself or the Subscript wrapped by PtrT[T].
|
||||
ann_ps: ast.AST* = annot
|
||||
if ann_ps.kind() == ast.ASTKind.Subscript:
|
||||
sub_wrap_ps: ast.Subscript* = ast.Subscript*(ann_ps)
|
||||
if sub_wrap_ps.value is not None and sub_wrap_ps.value.kind() == ast.ASTKind.Name:
|
||||
wk_nm_ps: ast.Name* = ast.Name*(sub_wrap_ps.value)
|
||||
if wk_nm_ps.id is not None and (string.strcmp(wk_nm_ps.id, "CPtr") == 0 or string.strcmp(wk_nm_ps.id, "rawptr") == 0):
|
||||
if wk_nm_ps.id is not None and (string.strcmp(wk_nm_ps.id, "PtrT") == 0 or string.strcmp(wk_nm_ps.id, "rawptr") == 0):
|
||||
if sub_wrap_ps.slice is not None: ann_ps = sub_wrap_ps.slice
|
||||
spec_annot: ast.Subscript* = None
|
||||
if ann_ps.kind() == ast.ASTKind.Subscript: spec_annot = ast.Subscript*(ann_ps)
|
||||
@@ -2544,13 +2480,13 @@ novirtual class AssignGenMixin:
|
||||
def _fail_on_degenerated_generic_annotation(self, aa: ast.AnnAssign*, var_name: str, annot: ast.AST*, var_ty: mlirlite.LLVMType*) -> int:
|
||||
"""Fail fast when a generic annotation degrades to a bare base pointer."""
|
||||
if aa is None or annot is None or var_ty is None: return 0
|
||||
# Take the generic Subscript side: the Subscript itself or the Subscript wrapped by CPtr[T].
|
||||
# Take the generic Subscript side: the Subscript itself or the Subscript wrapped by PtrT[T].
|
||||
ann_ff: ast.AST* = annot
|
||||
if ann_ff.kind() == ast.ASTKind.Subscript:
|
||||
sub_wrap_ff: ast.Subscript* = ast.Subscript*(ann_ff)
|
||||
if sub_wrap_ff.value is not None and sub_wrap_ff.value.kind() == ast.ASTKind.Name:
|
||||
wk_nm_ff: ast.Name* = ast.Name*(sub_wrap_ff.value)
|
||||
if wk_nm_ff.id is not None and (string.strcmp(wk_nm_ff.id, "CPtr") == 0 or string.strcmp(wk_nm_ff.id, "rawptr") == 0):
|
||||
if wk_nm_ff.id is not None and (string.strcmp(wk_nm_ff.id, "PtrT") == 0 or string.strcmp(wk_nm_ff.id, "rawptr") == 0):
|
||||
if sub_wrap_ff.slice is not None: ann_ff = sub_wrap_ff.slice
|
||||
ann_sub: ast.Subscript* = None
|
||||
if ann_ff.kind() == ast.ASTKind.Subscript: ann_sub = ast.Subscript*(ann_ff)
|
||||
@@ -2583,20 +2519,20 @@ novirtual class AssignGenMixin:
|
||||
nm: ast.Name* = ast.Name*(target)
|
||||
if nm.id is None: return 0
|
||||
|
||||
# CDefine annotation: compile-time constant, no runtime code; register it (idempotent)
|
||||
# DefineT annotation: compile-time constant, no runtime code; register it (idempotent)
|
||||
if self.is_cdefine_annotation(aa.annotation) != 0:
|
||||
pool_cd: memhub.MemBuddy* = trans.Pool
|
||||
self._register_cdefine_from_value(pool_cd, nm.id, aa.value, aa.annotation)
|
||||
return 0
|
||||
|
||||
# CTypedef annotation: register the alias on demand; no storage (avoids duplicate symbols)
|
||||
# TypedefT annotation: register the alias on demand; no storage (avoids duplicate symbols)
|
||||
if self.is_ctypedef_annotation(aa.annotation) != 0:
|
||||
pool_ct: memhub.MemBuddy* = trans.Pool
|
||||
ctd_str: str = self.Gen.TypeGenResolve._get_type_name_from_annotation(pool_ct, aa.value)
|
||||
if ctd_str is not None: self.Gen.TypeGenCType.register_ctypedef(pool_ct, nm.id, ctd_str)
|
||||
return 0
|
||||
|
||||
# Use-site width check for define/CDefine constants (a: T = C): T must be >= W_min
|
||||
# Use-site width check for define/DefineT constants (a: T = C): T must be >= W_min
|
||||
self.require_cdefine_use_width(trans.Pool, trans, aa.annotation, aa.value)
|
||||
|
||||
# REPL session variable: annotated declaration at fragment entry goes to the session slot
|
||||
@@ -2720,7 +2656,7 @@ novirtual class AssignGenMixin:
|
||||
# str/bytes is not a generic pointer: bare str/bytes annotation with an aggregate-pointer RHS
|
||||
self.Gen.ExprGenBase.fail_if_str_annot_ast_agg_ptr(rhs_val, aa.annotation, node)
|
||||
rhs_val = self.Gen.ExprGenBase.coerce_to_type(builder, rhs_val, target_ty, node, trans)
|
||||
# t.CVolatile global: the initializing store carries the volatile qualifier
|
||||
# t.VolatileT global: the initializing store carries the volatile qualifier
|
||||
vol_gaa: int = self.Gen.VarGen.var_is_volatile(self.Gen.VarGen.lookup_var_entry(trans.SymTab, nm.id))
|
||||
mlirlite.build_store(builder, rhs_val, mod_alloca, vol_gaa)
|
||||
# Clear the assignment context (all four branches must stay in sync)
|
||||
@@ -2802,7 +2738,7 @@ novirtual class AssignGenMixin:
|
||||
# str/bytes is not a generic pointer: bare str/bytes annotation with an aggregate-pointer RHS
|
||||
self.Gen.ExprGenBase.fail_if_str_annot_ast_agg_ptr(rhs_val_mg, aa.annotation, node)
|
||||
rhs_val_mg = self.Gen.ExprGenBase.coerce_to_type(builder, rhs_val_mg, target_ty_mg, node)
|
||||
# t.CVolatile module-level global: the initializing store carries volatile
|
||||
# t.VolatileT module-level global: the initializing store carries volatile
|
||||
vol_mga: int = self.Gen.VarGen.var_is_volatile(self.Gen.VarGen.lookup_var_entry(trans.SymTab, nm.id))
|
||||
mlirlite.build_store(builder, rhs_val_mg, mod_glob, vol_mga)
|
||||
# Clear the assignment context (all four branches must stay in sync)
|
||||
@@ -2810,7 +2746,7 @@ novirtual class AssignGenMixin:
|
||||
trans.CurAssignNode = None
|
||||
return 0
|
||||
|
||||
# Single-param CArray + list literal -> static array; match PreScan so the alloca is reused
|
||||
# Single-param ArrayT + list literal -> static array; match PreScan so the alloca is reused
|
||||
var_ty = self._infer_static_array_ty_from_literal(pool, annot, aa.value, var_ty)
|
||||
# [D31 fail-fast] generic annotation degraded to a bare base class
|
||||
self._fail_on_degenerated_generic_annotation(aa, nm.id, annot, var_ty)
|
||||
@@ -2911,7 +2847,16 @@ novirtual class AssignGenMixin:
|
||||
# P1-A: use the local alloca pointee as the target type so integer literals materialize at target width
|
||||
lv_target_ty: mlirlite.LLVMType* = None
|
||||
if alloca.Ty is not None: lv_target_ty = alloca.Ty.Pointee
|
||||
# `x: T = T(...)`:把目标存储交给构造序列,让构造直接落在 x 上(原地构造)。
|
||||
ip_slot: mlirlite.Value* = self._inplace_ctor_slot(pool, trans, aa.value, alloca)
|
||||
ip_prev_st: mlirlite.Value* = None
|
||||
ip_prev_life: int = -1
|
||||
if ip_slot is not None:
|
||||
ip_prev_st = self.Gen.CallGenCtor.CurCtorStorage()
|
||||
ip_prev_life = self.Gen.CallGenCtor.CurCtorLife()
|
||||
self.Gen.CallGenCtor.SetCtorStorage(ip_slot, GenTypes.LIFE_FRAME)
|
||||
rhs_val: mlirlite.Value* = self.Gen.ExprGen.translate_value(builder, pool, mod, aa.value, None, 0, trans, lv_target_ty, "local variable annotation")
|
||||
if ip_slot is not None: self.Gen.CallGenCtor.SetCtorStorage(ip_prev_st, ip_prev_life)
|
||||
if rhs_val is None:
|
||||
# fail-fast: 右值翻译失败时绝不能静默跳过整条声明(此前直接落到 `if rhs_val is
|
||||
# not None` 之外,变量永远保持未初始化,运行期读到栈垃圾后断言崩溃)。
|
||||
@@ -2935,12 +2880,12 @@ novirtual class AssignGenMixin:
|
||||
ann_gen_base: str = None
|
||||
if annot is not None:
|
||||
ag_side: ast.AST* = annot
|
||||
# Unwrap a CPtr[T] pointer layer to inspect the generic base.
|
||||
# Unwrap a PtrT[T] pointer layer to inspect the generic base.
|
||||
if ag_side.kind() == ast.ASTKind.Subscript:
|
||||
ag_wrap: ast.Subscript* = ast.Subscript*(ag_side)
|
||||
if ag_wrap.value is not None and ag_wrap.value.kind() == ast.ASTKind.Name:
|
||||
ag_wnm: ast.Name* = ast.Name*(ag_wrap.value)
|
||||
if ag_wnm.id is not None and (string.strcmp(ag_wnm.id, "CPtr") == 0 or string.strcmp(ag_wnm.id, "rawptr") == 0):
|
||||
if ag_wnm.id is not None and (string.strcmp(ag_wnm.id, "PtrT") == 0 or string.strcmp(ag_wnm.id, "rawptr") == 0):
|
||||
if ag_wrap.slice is not None: ag_side = ag_wrap.slice
|
||||
if ag_side.kind() == ast.ASTKind.Subscript:
|
||||
ag_sub: ast.Subscript* = ast.Subscript*(ag_side)
|
||||
@@ -2982,7 +2927,13 @@ novirtual class AssignGenMixin:
|
||||
av_entry: GenTypes.StructEntry* = self.Gen.StructGen.find_struct_by_type(alloca_pt)
|
||||
if av_entry is None or av_entry.HasVTable == 0: ann_agg_value = 1
|
||||
elif ak_av == mlirlite.LLVMType.Array: ann_agg_value = 1
|
||||
if alloca_pt is not None and uint64(alloca_pt) != uint64(precise_rhs_ty) and rb_is_bare_generic == 0 and rb_is_upcast == 0 and ann_agg_value == 0:
|
||||
# 只在**槽本身已是指针**时才允许按右值重建(`x: T* = <degraded>` 的修正)。
|
||||
# 注解解析成非指针(int/enum/无 vtable 聚合)却拿到 Ptr(T) 右值时,重建会
|
||||
# 把变量悄悄改成指针类型:后续 `f = x` 才在别处报"指针不能赋给整数",真正的
|
||||
# 错误(右值类型不对)被掩盖。这里不重建,让下面的 coerce 在本行给出精确诊断。
|
||||
rb_slot_is_ptr: int = 0
|
||||
if alloca_pt is not None and self.Gen.ExprGenBase.is_ptr_type(alloca_pt) != 0: rb_slot_is_ptr = 1
|
||||
if alloca_pt is not None and uint64(alloca_pt) != uint64(precise_rhs_ty) and rb_is_bare_generic == 0 and rb_is_upcast == 0 and ann_agg_value == 0 and rb_slot_is_ptr != 0:
|
||||
new_alloca: mlirlite.Value* = self.Gen.VarGen._alloca_at_entry(builder, precise_rhs_ty)
|
||||
if new_alloca is not None:
|
||||
v_entry_rb: GenTypes.VarEntry* = self.Gen.VarGen.lookup_var_entry(trans.SymTab, nm.id)
|
||||
@@ -2996,15 +2947,15 @@ novirtual class AssignGenMixin:
|
||||
# str/bytes is not a generic pointer: bare str/bytes annotation with an aggregate-pointer RHS
|
||||
self.Gen.ExprGenBase.fail_if_str_annot_ast_agg_ptr(rhs_val, aa.annotation, node)
|
||||
rhs_val = self.Gen.ExprGenBase.coerce_to_type(builder, rhs_val, target_ty, node, trans)
|
||||
# t.CVolatile local variable: mark the VarEntry and store with volatile
|
||||
# t.VolatileT local variable: mark the VarEntry and store with volatile
|
||||
vol_laa: int = 0
|
||||
if aa.annotation is not None and nm.id is not None:
|
||||
# const object local variable: mark VarEntry.IsConst (idempotent with prescan)
|
||||
if self.Gen.TypeGenErr.is_const_object_marker(aa.annotation, target_ty) != 0: self.Gen.VarGen.set_var_const(trans.SymTab, nm.id)
|
||||
if self.Gen.TypeGenErr.has_decorator_marker(aa.annotation, "CVolatile", 0) != 0:
|
||||
if self.Gen.TypeGenErr.has_decorator_marker(aa.annotation, "VolatileT", 0) != 0:
|
||||
self.Gen.VarGen.set_var_volatile(trans.SymTab, nm.id)
|
||||
vol_laa = 1
|
||||
# rawptr[t.CVolatile | T]: mark PointeeVolatile (this store writes the pointer itself)
|
||||
# rawptr[t.VolatileT | T]: mark PointeeVolatile (this store writes the pointer itself)
|
||||
if self.Gen.TypeGenErr.has_pointee_volatile_marker(aa.annotation) != 0: self.Gen.VarGen.set_var_pointee_volatile(trans.SymTab, nm.id)
|
||||
# Value-position volatile call prefix: the result slot is accessed with volatile
|
||||
if aa.value is not None and aa.value.kind() == ast.ASTKind.Call and (ast.Call*(aa.value)).IsVolatile != 0:
|
||||
@@ -3120,7 +3071,7 @@ novirtual class AssignGenMixin:
|
||||
sub_nm_lv: ast.Name* = ast.Name*(sub_lv_b.value)
|
||||
if sub_nm_lv.id is not None: bo_lv = self.Gen.VarGen.var_byte_order(self.Gen.VarGen.lookup_var_entry(trans.SymTab, sub_nm_lv.id))
|
||||
store_val = self.Gen.ExprGenBase.apply_byte_order_bswap(pool, builder, store_val, bo_lv)
|
||||
# t.CVolatile 字段 / rawptr[t.CVolatile|T] 基址:写出同样带 volatile。
|
||||
# t.VolatileT 字段 / rawptr[t.VolatileT|T] 基址:写出同样带 volatile。
|
||||
vol_lv: int = 0
|
||||
if is_attr != 0:
|
||||
vol_lv = self.Gen.ExprGenAttr.attr_field_is_volatile(pool, trans, target)
|
||||
@@ -3130,6 +3081,54 @@ novirtual class AssignGenMixin:
|
||||
mlirlite.build_store(builder, store_val, ptr, vol_lv)
|
||||
return 0
|
||||
|
||||
# _inplace_ctor_slot - `x: T = T(...)` 的原地构造成立吗?成立则返回 x 的存储,否则 None。
|
||||
# 只在"目标存储地址已固定、且 RHS 就是同一类的构造调用"时成立:
|
||||
# · 目标是本地变量(入口块 alloca,地址稳定且支配构造点);
|
||||
# · RHS 的调用名就是该类本身:`T(...)`(Name)或 `mod.T(...)`(Attribute,左侧必须是
|
||||
# 已导入模块),排除同名普通函数与 `obj.method()`;
|
||||
# · 目标存储的 pointee 是结构体,其短名(剥掉 sha1 前缀)与构造名逐字相同,
|
||||
# 且该结构体在 StructTable 里确实登记过。
|
||||
# 为什么必须原地:`__init__` 可能把 `self` 存进字段(vpui 的 `Tk.tk = self`)。若先在临时
|
||||
# alloca 上构造再整块拷贝,字段里的自指针仍指向那个死栈槽,之后经它写数据会写坏别的局部
|
||||
# 变量(现场:Tk.widget_count 永远读到 0,所有控件都没注册,窗口内容只剩一层底色)。
|
||||
def _inplace_ctor_slot(self, pool: memhub.MemBuddy*, trans: HT.Translator*, value: ast.AST*, slot: mlirlite.Value*) -> mlirlite.Value*:
|
||||
"""返回可原地构造的目标存储;不满足条件返回 None(保持原有"临时对象 + 按值拷贝"语义)"""
|
||||
if value is None or slot is None or trans is None: return None
|
||||
if slot.Ty is None: return None
|
||||
if value.kind() != ast.ASTKind.Call: return None
|
||||
cl: ast.Call* = ast.Call*(value)
|
||||
if cl.func is None: return None
|
||||
# 取构造名:`T(...)` 直接取 Name;`mod.T(...)` 取 Attribute 的末段,但左侧必须是
|
||||
# 已导入模块(否则是 `obj.method()`,不能把 obj 的槽当成构造存储)。
|
||||
ctor_name: str = None
|
||||
if cl.func.kind() == ast.ASTKind.Name:
|
||||
cnm: ast.Name* = ast.Name*(cl.func)
|
||||
if cnm.id is not None: ctor_name = cnm.id
|
||||
elif cl.func.kind() == ast.ASTKind.Attribute:
|
||||
cat: ast.Attribute* = ast.Attribute*(cl.func)
|
||||
if cat.attr is not None and cat.value is not None and cat.value.kind() == ast.ASTKind.Name:
|
||||
mnm: ast.Name* = ast.Name*(cat.value)
|
||||
if mnm.id is not None and self.Gen.StructGen._resolve_module_sha1(mnm.id, trans._from_imports) is not None:
|
||||
ctor_name = cat.attr
|
||||
if ctor_name is None: return None
|
||||
st: mlirlite.LLVMType* = slot.Ty.Pointee
|
||||
if st is None: return None
|
||||
sname: str = None
|
||||
match st:
|
||||
case mlirlite.LLVMType.Struct(_, _, _): sname = mlirlite.get_struct_name(st)
|
||||
case _: return None
|
||||
if sname is None: return None
|
||||
# 结构体名形如 "sha1.ClassName":剥掉 sha1 前缀后与构造名比较
|
||||
pf: int = GenApi.sha1_prefix_len(sname)
|
||||
short: str = sname + pf if pf != 0 else sname
|
||||
if string.strcmp(short, ctor_name) != 0: return None
|
||||
# 只有确实登记过的类才走构造序列;否则 `T(...)` 可能是同名普通函数调用。
|
||||
# 先按类型指针精确匹配,落空再按全名查 —— 条目里的 Ty 会被深拷贝改写成新指针
|
||||
# (见 StructGenMixin.find_struct_by_type 的注释),只靠指针比较会假阴性。
|
||||
if self.Gen.StructGen.find_struct_by_type(st) is None:
|
||||
if self.Gen.StructGen.find_struct(sname) is None: return None
|
||||
return slot
|
||||
|
||||
# translate_assign - handle an Assign statement; returns the number of new variables
|
||||
def translate_assign(self, trans: HT.Translator*, node: ast.AST*) -> int:
|
||||
"""Translate an Assign statement; return the number of new variables."""
|
||||
@@ -3214,7 +3213,23 @@ novirtual class AssignGenMixin:
|
||||
# REPL: a typed session variable also uses its slot type as the target type
|
||||
asg_re: GenTypes.ReplVarEntry* = self.Gen.VarGen.repl_find_entry(trans.ReplCtx, asg_nm0.id)
|
||||
if asg_re is not None and asg_re.Kind != GenTypes.REPL_KIND_UNTYPED: asg_target_ty = self.Gen.VarGen.repl_slot_ty(pool, asg_re)
|
||||
# `x = T(...)`(x 已有本地存储):与 `x: T = T(...)` 同口径原地构造 —— 自指针必须
|
||||
# 指向 x 本身,不能指向一个随后被按值拷贝走的临时对象。
|
||||
ip_slot: mlirlite.Value* = None
|
||||
if len(targets) == 1:
|
||||
ip_t0: ast.AST* = targets.get(0)
|
||||
if ip_t0 is not None and ip_t0.kind() == ast.ASTKind.Name:
|
||||
ip_nm: ast.Name* = ast.Name*(ip_t0)
|
||||
if ip_nm.id is not None:
|
||||
ip_slot = self._inplace_ctor_slot(pool, trans, asgn.value, self.Gen.VarGen.lookup_var(trans.SymTab, ip_nm.id))
|
||||
ip_prev_st: mlirlite.Value* = None
|
||||
ip_prev_life: int = -1
|
||||
if ip_slot is not None:
|
||||
ip_prev_st = self.Gen.CallGenCtor.CurCtorStorage()
|
||||
ip_prev_life = self.Gen.CallGenCtor.CurCtorLife()
|
||||
self.Gen.CallGenCtor.SetCtorStorage(ip_slot, GenTypes.LIFE_FRAME)
|
||||
rhs_val: mlirlite.Value* = self.Gen.ExprGen.translate_value(builder, pool, mod, asgn.value, None, 0, trans, asg_target_ty)
|
||||
if ip_slot is not None: self.Gen.CallGenCtor.SetCtorStorage(ip_prev_st, ip_prev_life)
|
||||
if rhs_val is None:
|
||||
# RHS is None usually means allocation failure; dump both pool statistics
|
||||
mb_diag: memhub.MemBuddy* = CompilerFactory.factory.Pool
|
||||
@@ -3345,7 +3360,7 @@ novirtual class AssignGenMixin:
|
||||
sub_nm_sw: ast.Name* = ast.Name*(sub_wr.value)
|
||||
if sub_nm_sw.id is not None: bo_sw = self.Gen.VarGen.var_byte_order(self.Gen.VarGen.lookup_var_entry(trans.SymTab, sub_nm_sw.id))
|
||||
store_val = self.Gen.ExprGenBase.apply_byte_order_bswap(pool, builder, store_val, bo_sw)
|
||||
# rawptr[t.CVolatile|T] base: p[i] = v stores with volatile when the base is resolvable
|
||||
# rawptr[t.VolatileT|T] base: p[i] = v stores with volatile when the base is resolvable
|
||||
vol_sw: int = 0
|
||||
if sub_wr is not None: vol_sw = self.Gen.ExprGenAttr.base_pointee_is_volatile(pool, trans, sub_wr.value)
|
||||
mlirlite.build_store(builder, store_val, elem_ptr, vol_sw)
|
||||
@@ -3522,9 +3537,9 @@ novirtual class AssignGenMixin:
|
||||
# (pairs the un-swap done by ExprGenAttr.translate_attribute on the load path).
|
||||
bo_fw: int = self.Gen.ExprGenAttr.attr_field_byte_order(pool, trans, target)
|
||||
store_val = self.Gen.ExprGenBase.apply_byte_order_bswap(pool, builder, store_val, bo_fw)
|
||||
# t.CVolatile field: store with the volatile qualifier
|
||||
# t.VolatileT field: store with the volatile qualifier
|
||||
vol_fw: int = self.Gen.ExprGenAttr.attr_field_is_volatile(pool, trans, target)
|
||||
# rawptr[t.CVolatile|T] base: p.field = v also stores with volatile
|
||||
# rawptr[t.VolatileT|T] base: p.field = v also stores with volatile
|
||||
at_wr: ast.Attribute* = ast.Attribute*(target)
|
||||
if at_wr is not None:
|
||||
if self.Gen.ExprGenAttr.base_pointee_is_volatile(pool, trans, at_wr.value) != 0: vol_fw = 1
|
||||
@@ -3582,7 +3597,7 @@ novirtual class AssignGenMixin:
|
||||
target_ty: mlirlite.LLVMType* = None
|
||||
if mod_alloca.Ty is not None: target_ty = mod_alloca.Ty.Pointee
|
||||
if target_ty is not None: rhs_val = self.Gen.ExprGenBase.coerce_to_type(builder, rhs_val, target_ty, node, trans)
|
||||
# t.CVolatile global: store with the volatile qualifier
|
||||
# t.VolatileT global: store with the volatile qualifier
|
||||
vol_gas: int = 0
|
||||
if nm.id is not None: vol_gas = self.Gen.VarGen.var_is_volatile(self.Gen.VarGen.lookup_var_entry(trans.SymTab, nm.id))
|
||||
mlirlite.build_store(builder, rhs_val, mod_alloca, vol_gas)
|
||||
@@ -3635,7 +3650,7 @@ novirtual class AssignGenMixin:
|
||||
# memory (pairs the un-swap done by ExprGenName.translate_name_value on the read path).
|
||||
bo_las: int = self.Gen.VarGen.var_byte_order(self.Gen.VarGen.lookup_var_entry(trans.SymTab, nm.id))
|
||||
store_val = self.Gen.ExprGenBase.apply_byte_order_bswap(pool, builder, store_val, bo_las)
|
||||
# t.CVolatile local variable: store with the volatile qualifier
|
||||
# t.VolatileT local variable: store with the volatile qualifier
|
||||
vol_las: int = self.Gen.VarGen.var_is_volatile(self.Gen.VarGen.lookup_var_entry(trans.SymTab, nm.id))
|
||||
# M3 所有权记账:必须先记账再写值(覆盖释放要读到旧值);
|
||||
# `p = new heap slife …` → 释放旧对象 + 影子置 1;`q = p` / 外来指针 → 交还所有权
|
||||
@@ -3679,7 +3694,7 @@ novirtual class AssignGenMixin:
|
||||
if rhs_inner_wo.kind() != mlirlite.LLVMType.Struct: return
|
||||
# The pointee type must match the element type
|
||||
if self._stg_ty_equal(pool, elem_ty, rhs_inner_wo) == 0: return
|
||||
# Array slots (t.CArray[T,N]) are legal; only block when the base is a pointer store
|
||||
# Array slots (t.ArrayT[T,N]) are legal; only block when the base is a pointer store
|
||||
if self.Gen.ExprGenName.subscript_base_store_kind(pool, trans, target) != 0: return
|
||||
elem_nm_wo: str = mlirlite.get_struct_name(elem_ty)
|
||||
if elem_nm_wo is None: elem_nm_wo = "<anonymous struct>"
|
||||
@@ -3694,7 +3709,7 @@ novirtual class AssignGenMixin:
|
||||
if target is not None:
|
||||
col_s_wo = target.col_offset + 1
|
||||
col_e_wo = target.end_col_offset + 1
|
||||
self.Gen.TypeGenErr.fatal_error(target, f"the lvalue element of the subscript assignment is object '{elem_nm_wo}' ({elem_sz_wo} bytes), " f"but the rhs is a pointer to '{rhs_nm_wo}': the compiler would silently emit a whole-object copy " f"(load {elem_nm_wo} + store {elem_nm_wo}, writing {elem_sz_wo} bytes), " f"instead of writing {ptr_sz_wo} bytes as a pointer. If that address actually stores pointers in 8-byte slots " f"(such as a rawptr[T] buffer/free list), every element written overflows by {over_wo} bytes, " f"writing through adjacent blocks. Fix: declare the base type of the pointer slot in pointer-element form " f"(`rawptr[T]*` or `rawptr[rawptr[T]]`) before assigning; " f"or explicitly cast the base to t.CPtr (untyped pointer) and write with 8-byte pointer semantics.", "TypeError", col_s_wo, col_e_wo)
|
||||
self.Gen.TypeGenErr.fatal_error(target, f"the lvalue element of the subscript assignment is object '{elem_nm_wo}' ({elem_sz_wo} bytes), " f"but the rhs is a pointer to '{rhs_nm_wo}': the compiler would silently emit a whole-object copy " f"(load {elem_nm_wo} + store {elem_nm_wo}, writing {elem_sz_wo} bytes), " f"instead of writing {ptr_sz_wo} bytes as a pointer. If that address actually stores pointers in 8-byte slots " f"(such as a rawptr[T] buffer/free list), every element written overflows by {over_wo} bytes, " f"writing through adjacent blocks. Fix: declare the base type of the pointer slot in pointer-element form " f"(`rawptr[T]*` or `rawptr[rawptr[T]]`) before assigning; " f"or explicitly cast the base to rawptr (untyped pointer) and write with 8-byte pointer semantics.", "TypeError", col_s_wo, col_e_wo)
|
||||
|
||||
# _infer_rhs_annot - infer a variable's annotation class name from the RHS expression
|
||||
def _infer_rhs_annot(self, pool: memhub.MemBuddy*, mod: mlirlite.LLVMModule*, node: ast.AST*, trans: HT.Translator*, rhs_val: mlirlite.Value* = None) -> str:
|
||||
@@ -3796,8 +3811,8 @@ novirtual class AssignGenMixin:
|
||||
if seglen > 0 and seglen < 256:
|
||||
string.strncpy(seg, annot + s, seglen)
|
||||
seg[seglen] = '\0'
|
||||
# Unwrap the CPtr[T] pointer marker: the class name is inside.
|
||||
while seglen > 6 and string.strncmp(seg, "CPtr[", 5) == 0 and seg[seglen - 1] == ']':
|
||||
# Unwrap the PtrT[T] pointer marker: the class name is inside.
|
||||
while seglen > 6 and string.strncmp(seg, "PtrT[", 5) == 0 and seg[seglen - 1] == ']':
|
||||
seg = seg + 5
|
||||
seglen -= 6
|
||||
seg[seglen] = '\0'
|
||||
@@ -3814,8 +3829,8 @@ novirtual class AssignGenMixin:
|
||||
if seglen > 0 and seglen < 256:
|
||||
string.strncpy(seg, annot + s, seglen)
|
||||
seg[seglen] = '\0'
|
||||
# Unwrap the CPtr[T] pointer marker: the class name is inside.
|
||||
while seglen > 6 and string.strncmp(seg, "CPtr[", 5) == 0 and seg[seglen - 1] == ']':
|
||||
# Unwrap the PtrT[T] pointer marker: the class name is inside.
|
||||
while seglen > 6 and string.strncmp(seg, "PtrT[", 5) == 0 and seg[seglen - 1] == ']':
|
||||
seg = seg + 5
|
||||
seglen -= 6
|
||||
seg[seglen] = '\0'
|
||||
|
||||
@@ -11,7 +11,7 @@ import string
|
||||
import stdio
|
||||
import stdlib
|
||||
import viperlib
|
||||
import win32.fileio as fileio
|
||||
import io as fileio
|
||||
import win32.win32file as win32file
|
||||
import lib.core.VLogger as VLogger
|
||||
import lib.Stub.StubMerger as StubMerger
|
||||
@@ -56,7 +56,7 @@ novirtual class CallGenBase:
|
||||
CexportFuncArr: bytes = None
|
||||
CexportCount: int = 0
|
||||
|
||||
# --- static symbol registry (static / @t.CStatic -> defining module SHA1),
|
||||
# --- static symbol registry (static / @t.StaticT -> defining module SHA1),
|
||||
# meaning no cross-module use (C internal linkage); PermPool fixed slots.
|
||||
StaticSha1Arr: bytes = None
|
||||
StaticNameArr: bytes = None
|
||||
@@ -149,6 +149,9 @@ novirtual class CallGenBase:
|
||||
# 报错,可据此把报错精确定位到这个实参表达式(如 `&_on_submit`),而不是退回函数定义行。
|
||||
if arg_node is not None:
|
||||
self.Gen.TypeGenBase.set_cur_arg(self.Gen.TypeGenResolve.get_current_file(), arg_node.lineno, arg_node.col_offset + 1)
|
||||
# 强类型(2026-10-22):值语义变量传给声明为 `T*` 的形参必须显式 &a —— 否则会被拷进
|
||||
# 临时槽再传地址,被调者改的是副本(跨模块按 vpi 形参注解判定,见 helper)。
|
||||
self._fail_on_value_arg_to_ptr_param(trans, callee_name, arg_idx, arg_node, fpty)
|
||||
mode: int = self._auto_addr_mode(callee_name, arg_idx)
|
||||
if mode == 0:
|
||||
return self.Gen.ExprGen.translate_value(builder, pool, mod, arg_node, None, 0, trans, fpty, use_ctx)
|
||||
@@ -188,9 +191,19 @@ novirtual class CallGenBase:
|
||||
def _vpi_auto_addr_mode(self, call_name: str, cur_sha1: str, idx: int) -> int:
|
||||
"""Cross-module ({sha1}.cls.method / {sha1}.func) value-reference mode from the vpi cache; 0 if none/unknown."""
|
||||
if call_name is None or idx < 0: return 0
|
||||
if GenApi.sha1_prefix_len(call_name) == 0: return 0
|
||||
prm_annots: str = self._vpi_param_annots_for(call_name, cur_sha1)
|
||||
if prm_annots is None: return 0
|
||||
return VpiReader._vpi_param_annot_seg_mode(prm_annots, idx)
|
||||
|
||||
# _vpi_param_annots_for - cross-module callee 的 vpi 形参注解串;None = 同模块/不可用。
|
||||
# 解析只在 {sha1}.{cls}.{method} / {sha1}.{func} 的拆分这一处实现,_vpi_auto_addr_mode 与
|
||||
# _fail_on_value_arg_to_ptr_param 共用。
|
||||
def _vpi_param_annots_for(self, call_name: str, cur_sha1: str) -> str:
|
||||
"""Return the callee's vpi param-annotation string (None if same-module or unavailable)."""
|
||||
if call_name is None: return None
|
||||
if GenApi.sha1_prefix_len(call_name) == 0: return None
|
||||
# Same-module callee: AutoAddrModes (registration during this compilation) is authoritative.
|
||||
if cur_sha1 is not None and string.strncmp(call_name, cur_sha1, 16) == 0: return 0
|
||||
if cur_sha1 is not None and string.strncmp(call_name, cur_sha1, 16) == 0: return None
|
||||
# split {sha1}.{rest}: if rest has '.', it is {cls}.{method}, else {func}
|
||||
rest: str = call_name + 17
|
||||
cls_nm: str = None
|
||||
@@ -207,20 +220,49 @@ novirtual class CallGenBase:
|
||||
meth_nm = dot_r + 1
|
||||
if meth_nm is None or meth_nm[0] == '\0':
|
||||
if cls_nm is not None: stdlib.free(cls_nm)
|
||||
return 0
|
||||
return None
|
||||
sha1_buf: bytes = stdlib.malloc(17)
|
||||
if sha1_buf is None:
|
||||
if cls_nm is not None: stdlib.free(cls_nm)
|
||||
return 0
|
||||
return None
|
||||
string.strncpy(sha1_buf, call_name, 16)
|
||||
sha1_buf[16] = '\0'
|
||||
if self.TempDir is not None and self.TempDirLen > 0: VpiReader._ensure_vpi_param_annot_loaded(sha1_buf, self.TempDir)
|
||||
prm_annots: str = VpiReader._lookup_vpi_param_annots(sha1_buf, cls_nm, meth_nm)
|
||||
mode: int = 0
|
||||
if prm_annots is not None: mode = VpiReader._vpi_param_annot_seg_mode(prm_annots, idx)
|
||||
stdlib.free(sha1_buf)
|
||||
if cls_nm is not None: stdlib.free(cls_nm)
|
||||
return mode
|
||||
return prm_annots
|
||||
|
||||
# _fail_on_value_arg_to_ptr_param - 强类型(2026-10-22):值语义变量不能隐式取地址传给
|
||||
# 声明为 `T*` 的形参,必须写 `&a`。判定依据是被调者 vpi 里该形参的源码注解(跨模块可用);
|
||||
# 同模块形参的源码写法无法从 declare 反推,保持放行。
|
||||
def _fail_on_value_arg_to_ptr_param(self, trans: HT.Translator*, callee_name: str, idx: int,
|
||||
arg_node: ast.AST*, fpty: mlirlite.LLVMType*) -> int:
|
||||
"""Fail fast when a value-semantics variable is implicitly addressed into a `T*` parameter."""
|
||||
if trans is None or callee_name is None or arg_node is None or fpty is None: return 0
|
||||
if self.Gen.ExprGenBase.is_ptr_type(fpty) == 0: return 0
|
||||
# 只判定"变量引用"实参(字段/下标/调用结果的类型来源更复杂,交由 coerce 的通用规则)
|
||||
if arg_node.kind() != ast.ASTKind.Name: return 0
|
||||
nm_va: str = (ast.Name*(arg_node)).id
|
||||
if nm_va is None: return 0
|
||||
ve_va: GenTypes.VarEntry* = self.Gen.VarGen.lookup_var_entry(trans.SymTab, nm_va)
|
||||
if ve_va is None or ve_va.Alloca is None or ve_va.Alloca.Ty is None: return 0
|
||||
# 实参本身必须是"聚合值语义"的**结构体**槽;指针变量与数组都不受影响
|
||||
# (数组退让成元素指针是 C 语义,传的就是它自己的地址,不存在拷贝副本)
|
||||
if self.Gen.ExprGenBase.is_ptr_type(ve_va.Alloca.Ty) != 0: return 0
|
||||
ak_va: int = ve_va.Alloca.Ty.kind()
|
||||
if ak_va != mlirlite.LLVMType.Struct: return 0
|
||||
prm_annots: str = self._vpi_param_annots_for(callee_name, trans.ModuleSha1)
|
||||
if prm_annots is None: return 0
|
||||
# vpi 的形参注解段**含 self**(方法第 0 段为空注解),故方法调用的实参下标要 +1
|
||||
rest_va: str = callee_name + 17
|
||||
seg_idx: int = idx
|
||||
if string.strchr(rest_va, '.') is not None: seg_idx = idx + 1
|
||||
if VpiReader._vpi_param_annot_seg_is_ptr(prm_annots, seg_idx) != 1: return 0
|
||||
pf_va: int = GenApi.sha1_prefix_len(callee_name)
|
||||
short_va: str = callee_name + pf_va if pf_va != 0 else callee_name
|
||||
self.Gen.TypeGenErr.fatal_error(arg_node, f"parameter {idx + 1} of '{short_va}' is declared as a pointer (T*), but the argument '{nm_va}' is a value-semantics variable; it would be copied into a temporary and the callee would modify the copy — pass '&{nm_va}' explicitly, or declare the parameter as a value type (T)", "CastError")
|
||||
return 1
|
||||
|
||||
# _apply_auto_addr_to_call_args - auto-address the user arguments of a call whose definition params
|
||||
# are value references (&T -> take the lvalue address / &temp T -> copy into a temp slot). The
|
||||
@@ -283,6 +325,13 @@ novirtual class CallGenBase:
|
||||
if string.strcmp(sha1_arr_ptr + sidx, mod_sha1) != 0: continue
|
||||
ridx: sizet = sizet(i) * StubMerger.MAX_REL_PATH_LEN
|
||||
rel: str = rel_arr_ptr + ridx
|
||||
# Includes are registered with a workspace-relative path ("includes/ast/__init__.vp")
|
||||
# because the scan root is the workspace, while the module name drops that prefix
|
||||
# ("ast", "ast.lexer"). Dropping it here too keeps re-export resolution (`from .lexer
|
||||
# import ...` -> "ast.lexer") from building "includes.ast.lexer", which resolves nowhere.
|
||||
rel_off: sizet = 0
|
||||
if string.strncmp(rel, "includes/", 9) == 0: rel_off = 9
|
||||
rel = rel + rel_off
|
||||
last_slash: str = string.strrchr(rel, 47) # 47 = ord('/')
|
||||
if last_slash is None: return None
|
||||
pkg_len: sizet = sizet(uint64(last_slash) - uint64(rel))
|
||||
@@ -468,7 +517,7 @@ novirtual class CallGenBase:
|
||||
|
||||
|
||||
def register_cexport_func(self, sha1: str, func_name: str) -> int:
|
||||
"""Register a CExport function into the global table (single source of truth in Gen.CallGenBase)"""
|
||||
"""Register a ExportT function into the global table (single source of truth in Gen.CallGenBase)"""
|
||||
if self.Gen is None or sha1 is None or func_name is None: return 0
|
||||
if self.Gen.CallGenBase.CexportSha1Arr is None:
|
||||
self.Gen.CallGenBase.CexportSha1Arr = CompilerFactory.factory.PermPool.alloc(_CEXPORT_MAX * 17)
|
||||
@@ -490,7 +539,7 @@ novirtual class CallGenBase:
|
||||
|
||||
|
||||
def is_cexport_func(self, sha1: str, func_name: str) -> int:
|
||||
"""Whether (sha1, func_name) is a CExport function (reads the Gen.CallGenBase source of truth), falling back to the vpi return annotation on a registry miss."""
|
||||
"""Whether (sha1, func_name) is a ExportT function (reads the Gen.CallGenBase source of truth), falling back to the vpi return annotation on a registry miss."""
|
||||
if self.Gen is None or sha1 is None or func_name is None: return 0
|
||||
if self.Gen.CallGenBase.CexportSha1Arr is not None:
|
||||
for i in range(int(self.Gen.CallGenBase.CexportCount)):
|
||||
@@ -502,7 +551,7 @@ novirtual class CallGenBase:
|
||||
|
||||
|
||||
# RegisterStaticSymbol / IsStaticSymbol - static symbol registry: the four
|
||||
# syntaxes all normalize to t.CStatic; meaning = no cross-module use.
|
||||
# syntaxes all normalize to t.StaticT; meaning = no cross-module use.
|
||||
def RegisterStaticSymbol(self, sha1: str, name: str) -> int:
|
||||
"""Register a static symbol (defining module SHA1 + symbol name); 1=registered (idempotent) / 0=failure"""
|
||||
if self.Gen is None or sha1 is None or name is None: return 0
|
||||
@@ -544,20 +593,25 @@ novirtual class CallGenBase:
|
||||
|
||||
|
||||
def _is_cexport_func_from_vpi(self, sha1: str, func_name: str) -> int:
|
||||
"""The vpi return annotation contains t.CExport, so the defining side uses a bare name (no sha1 prefix cross-module)."""
|
||||
"""Whether the vpi declares a bare-name function (`export def` / `extern def` / legacy ExportT)."""
|
||||
if sha1 is None or func_name is None: return 0
|
||||
if self.Gen is None or self.Gen.CallGenBase.TempDir is None or self.Gen.CallGenBase.TempDirLen <= 0: return 0
|
||||
VpiReader._ensure_vpi_ret_annot_loaded(sha1, self.Gen.CallGenBase.TempDir)
|
||||
# New vpi carries the storage marker as the `export`/`extern` prefix, not in the
|
||||
# return annotation; without this, cross-module calls mangle the name and link fails.
|
||||
if VpiReader._lookup_vpi_bare(sha1, None, func_name) != 0: return 1
|
||||
cexp_annot: str = VpiReader._lookup_vpi_ret_annot(sha1, None, func_name)
|
||||
if cexp_annot is None: return 0
|
||||
if string.strstr(cexp_annot, "CExport") is not None: return 1
|
||||
if string.strstr(cexp_annot, "ExportT") is not None: return 1
|
||||
return 0
|
||||
|
||||
|
||||
def _is_state_func_from_vpi(self, pool: memhub.MemBuddy*, sha1: str, func_name: str) -> int:
|
||||
"""The vpi return annotation contains t.State, an external C function marker, so bare names are used cross-module to avoid undefined symbols."""
|
||||
"""Whether the vpi declares a bare-name external C function (`extern def` / legacy State)."""
|
||||
if pool is None or sha1 is None or func_name is None: return 0
|
||||
if self.Gen.CallGenBase.TempDir is not None and self.Gen.CallGenBase.TempDirLen > 0: VpiReader._ensure_vpi_ret_annot_loaded(sha1, self.Gen.CallGenBase.TempDir)
|
||||
# `extern def` prefix (current vpi) => bare C symbol name.
|
||||
if VpiReader._lookup_vpi_bare(sha1, None, func_name) != 0: return 1
|
||||
state_annot: str = VpiReader._lookup_vpi_ret_annot(sha1, None, func_name)
|
||||
if state_annot is None: return 0
|
||||
# ret annotation contains "State" -> external C function
|
||||
@@ -783,8 +837,118 @@ novirtual class CallGenBase:
|
||||
|
||||
# _lookup_method_ret_ty_via_vpi: resolve method return type from the vpi annotation cache
|
||||
|
||||
# ============================================================
|
||||
def _lookup_method_ret_ty_via_vpi(self, pool: memhub.MemBuddy*, cls_sha1: str, cls_name: str, method_name: str) -> mlirlite.LLVMType*:
|
||||
# ============================================================
|
||||
# _resolve_vpi_annot_direct: 直接解析 vpi 注解里的常见写法
|
||||
# `X*` / `X**` / `module.Class` / 裸类名 / 内建名 / typedef。
|
||||
# 只用最基础的字符串操作,不依赖 VpiReader 的多分支分段解析器:自举时该解析器可能
|
||||
# 误编译,让一个已注册的类解析失败。无法判定时返回 None,由后续完整解析器继续处理。
|
||||
def _resolve_vpi_annot_direct(self, pool: memhub.MemBuddy*, annot: str) -> mlirlite.LLVMType*:
|
||||
"""解析 vpi 注解里的指针写法:`X*` / `X**` / `const X*` / `module.Class`。
|
||||
不递归:先把 `const` / `volatile` 前缀剥掉,再统计尾部 `*` 的层数,
|
||||
最后只做一次类/枚举/typedef 查找并按层数包 Ptr。"""
|
||||
if annot is None or pool is None: return None
|
||||
a_len: sizet = string.strlen(annot)
|
||||
st_i: sizet = 0
|
||||
end_i: sizet = a_len
|
||||
while st_i < end_i and annot[st_i] == ' ': st_i += 1
|
||||
while end_i > st_i and annot[end_i - 1] == ' ': end_i -= 1
|
||||
# 存储修饰符不改变 LLVM 类型
|
||||
while True:
|
||||
if end_i - st_i >= 6 and string.strncmp(annot + st_i, "const ", 6) == 0:
|
||||
st_i += 6
|
||||
elif end_i - st_i >= 9 and string.strncmp(annot + st_i, "volatile ", 9) == 0:
|
||||
st_i += 9
|
||||
else: break
|
||||
while st_i < end_i and annot[st_i] == ' ': st_i += 1
|
||||
ptr_layers: int = 0
|
||||
# 尾部 '*' 逐层计入(`X**` = 两层)
|
||||
while end_i > st_i and annot[end_i - 1] == '*':
|
||||
ptr_layers += 1
|
||||
end_i -= 1
|
||||
while end_i > st_i and annot[end_i - 1] == ' ': end_i -= 1
|
||||
if end_i <= st_i: return None
|
||||
nm_buf: bytes = stdlib.malloc(end_i - st_i + 1)
|
||||
if nm_buf is None: return None
|
||||
string.strncpy(nm_buf, annot + st_i, end_i - st_i)
|
||||
nm_buf[end_i - st_i] = '\0'
|
||||
dot_n: str = string.strrchr(nm_buf, '.')
|
||||
cls_n: str = nm_buf
|
||||
if dot_n is not None: cls_n = dot_n + 1
|
||||
base_ty: mlirlite.LLVMType* = GenApi.get_struct_type(cls_n)
|
||||
if base_ty is None: base_ty = GenApi.get_enum_base_type(cls_n)
|
||||
if base_ty is None: base_ty = GenApi.resolve_typedef_or_map(pool, cls_n, None, None)
|
||||
stdlib.free(nm_buf)
|
||||
if base_ty is None: return None
|
||||
for _pi in range(ptr_layers): base_ty = mlirlite.Ptr(pool, base_ty)
|
||||
return base_ty
|
||||
|
||||
# _resolve_generic_annot_str - resolve a vpi annotation that instantiates a generic class
|
||||
# (`GSList[Param]*`, `dict[str, X]*`) by specializing it on demand. VpiReader's string
|
||||
# resolver handles markers/named types only, so without this a generic return type falls
|
||||
# through to the i32 placeholder (现场: mlirlite.function_get_params -> GSList[Param]*).
|
||||
def _resolve_generic_annot_str(self, trans: HT.Translator*, pool: memhub.MemBuddy*, annot: str) -> mlirlite.LLVMType*:
|
||||
"""Specialize `Base[Arg, ...]` (+ trailing '*') from an annotation string; None if not applicable."""
|
||||
if trans is None or pool is None or annot is None: return None
|
||||
a_len: sizet = string.strlen(annot)
|
||||
st_i: sizet = 0
|
||||
end_i: sizet = a_len
|
||||
while st_i < end_i and annot[st_i] == ' ': st_i += 1
|
||||
while end_i > st_i and annot[end_i - 1] == ' ': end_i -= 1
|
||||
while True:
|
||||
if end_i - st_i >= 6 and string.strncmp(annot + st_i, "const ", 6) == 0: st_i += 6
|
||||
elif end_i - st_i >= 9 and string.strncmp(annot + st_i, "volatile ", 9) == 0: st_i += 9
|
||||
else: break
|
||||
while st_i < end_i and annot[st_i] == ' ': st_i += 1
|
||||
ptr_layers: int = 0
|
||||
while end_i > st_i and annot[end_i - 1] == '*':
|
||||
ptr_layers += 1
|
||||
end_i -= 1
|
||||
while end_i > st_i and annot[end_i - 1] == ' ': end_i -= 1
|
||||
if end_i <= st_i + 2: return None
|
||||
if annot[end_i - 1] != ']': return None
|
||||
lb: sizet = st_i
|
||||
while lb < end_i and annot[lb] != '[': lb += 1
|
||||
if lb >= end_i - 1: return None
|
||||
base_len: sizet = lb - st_i
|
||||
if base_len == 0: return None
|
||||
base_buf: str = pool.alloc(base_len + 1)
|
||||
if base_buf is None: return None
|
||||
string.strncpy(base_buf, annot + st_i, base_len)
|
||||
base_buf[base_len] = '\0'
|
||||
ta_list: list[str]* = list[str](pool, 4)
|
||||
if ta_list is None: return None
|
||||
arg_lim: sizet = end_i - 1
|
||||
ai: sizet = lb + 1
|
||||
depth_g: int = 0
|
||||
seg_s: sizet = ai
|
||||
while ai <= arg_lim:
|
||||
if ai == arg_lim or (annot[ai] == ',' and depth_g == 0):
|
||||
seg_e: sizet = ai
|
||||
while seg_s < seg_e and annot[seg_s] == ' ': seg_s += 1
|
||||
while seg_e > seg_s and annot[seg_e - 1] == ' ': seg_e -= 1
|
||||
if seg_e > seg_s:
|
||||
ab: str = pool.alloc(seg_e - seg_s + 1)
|
||||
if ab is None: return None
|
||||
string.strncpy(ab, annot + seg_s, seg_e - seg_s)
|
||||
ab[seg_e - seg_s] = '\0'
|
||||
ta_list.append(ab)
|
||||
seg_s = ai + 1
|
||||
elif annot[ai] == '[': depth_g += 1
|
||||
elif annot[ai] == ']': depth_g -= 1
|
||||
ai += 1
|
||||
if len(ta_list) == 0: return None
|
||||
spec: str = self.Gen.ClassGenGeneric._specialize_generic_class(trans, base_buf, ta_list)
|
||||
if spec is None: return None
|
||||
ty: mlirlite.LLVMType* = GenApi.get_struct_type(spec)
|
||||
if ty is None: ty = GenApi.get_enum_base_type(spec)
|
||||
if ty is None: return None
|
||||
k_l: int = 0
|
||||
while k_l < ptr_layers:
|
||||
ty = mlirlite.Ptr(pool, ty)
|
||||
k_l += 1
|
||||
return ty
|
||||
|
||||
def _lookup_method_ret_ty_via_vpi(self, pool: memhub.MemBuddy*, cls_sha1: str, cls_name: str, method_name: str, trans: HT.Translator* = None) -> mlirlite.LLVMType*:
|
||||
"""Get a method return type from the vpi stub annotation cache; None on miss"""
|
||||
if pool is None or cls_sha1 is None or method_name is None: return None
|
||||
# lazy import inside the function: VpiReader imports HandlesExprCall, a top-level import would cycle
|
||||
@@ -795,7 +959,21 @@ novirtual class CallGenBase:
|
||||
if self.Gen.CallGenBase.TempDir is not None and self.Gen.CallGenBase.TempDirLen > 0: VpiReader._ensure_vpi_ret_annot_loaded(cls_sha1, self.Gen.CallGenBase.TempDir)
|
||||
annot: str = VpiReader._lookup_vpi_ret_annot(cls_sha1, cls_name, method_name)
|
||||
if annot is None: return None
|
||||
return VpiReader._resolve_vpi_ret_annot_str(pool, annot, i32_ty, i8_ptr_ty)
|
||||
# Fast path for pointer spellings (`X*` / `X**` / `const X*`): plain character ops, no
|
||||
# recursive subscript unwrapping, so a registered class is always found for these forms.
|
||||
# Restricted to pointer forms; bare / sha1-prefixed names keep the full parser's
|
||||
# cross-module disambiguation.
|
||||
a_l0: sizet = string.strlen(annot)
|
||||
is_ptr_form: int = 0
|
||||
if a_l0 > 0 and annot[a_l0 - 1] == '*': is_ptr_form = 1
|
||||
if is_ptr_form != 0:
|
||||
direct_ty: mlirlite.LLVMType* = self._resolve_vpi_annot_direct(pool, annot)
|
||||
if direct_ty is not None: return direct_ty
|
||||
# Typedef-aware: the vpi keeps the source spelling, so a return annotation spelled with
|
||||
# a module-local alias (`posix.getpid -> pid_t`) must be expanded before resolving.
|
||||
ret_ty: mlirlite.LLVMType* = VpiReader._resolve_vpi_annot_with_typedefs(pool, cls_sha1, annot, i32_ty, i8_ptr_ty)
|
||||
if ret_ty is None: ret_ty = self._resolve_generic_annot_str(trans, pool, annot)
|
||||
return ret_ty
|
||||
|
||||
def _vpi_escape_packed(self, sha1: str, cls_name: str, method_name: str) -> int:
|
||||
"""跨模块逃逸摘要查询(v3):按 (定义模块 sha1, Class?, method) 查 .vpi 缓存;0=无摘要。
|
||||
@@ -910,12 +1088,13 @@ novirtual class CallGenBase:
|
||||
return VpiReader._lookup_vpi_param_annots(cls_sha1, cls_name, method_name)
|
||||
|
||||
# _vpi_resolve_param_annot_ty: parse a vpi param annotation segment into LLVMType
|
||||
def _vpi_resolve_param_annot_ty(self, pool: memhub.MemBuddy*, seg: str) -> mlirlite.LLVMType*:
|
||||
def _vpi_resolve_param_annot_ty(self, pool: memhub.MemBuddy*, seg: str, src_sha1: str = None) -> mlirlite.LLVMType*:
|
||||
"""Parse a single vpi parameter annotation segment into an LLVMType; None on failure"""
|
||||
if pool is None or seg is None: return None
|
||||
i32_ty: mlirlite.LLVMType* = mlirlite.Int32(pool)
|
||||
i8_ptr_ty: mlirlite.LLVMType* = mlirlite.Ptr(pool, mlirlite.Int8(pool))
|
||||
return VpiReader._resolve_vpi_ret_annot_str(pool, seg, i32_ty, i8_ptr_ty)
|
||||
# Typedef-aware: params may be spelled with the defining module's alias (`waitpid(pid: pid_t)`).
|
||||
return VpiReader._resolve_vpi_annot_with_typedefs(pool, src_sha1, seg, i32_ty, i8_ptr_ty)
|
||||
|
||||
# _special_method_ret_ty: fixed return types for compiler built-in special methods
|
||||
|
||||
@@ -934,7 +1113,7 @@ novirtual class CallGenBase:
|
||||
# _fail_method_ret_ty: ERROR + abort when the method return type cannot be resolved
|
||||
|
||||
# ============================================================
|
||||
def _fail_method_ret_ty(self, method_name: str, node: ast.AST* = None, cls: str = None, anc: str = None, sha1: str = None) -> mlirlite.LLVMType*:
|
||||
def _fail_method_ret_ty(self, method_name: str, node: ast.AST* = None, cls: str = None, anc: str = None, sha1: str = None, pool: memhub.MemBuddy* = None) -> mlirlite.LLVMType*:
|
||||
"""Fail fast when a method return type cannot be resolved precisely (module function, text.ll define, then vpi cache); never default to i32."""
|
||||
# NOTE: VLogger.error terminates the process in batch mode (sys.exit(1)), so the whole
|
||||
# report must be a SINGLE call -- anything after it is dead code (this is why a separate
|
||||
@@ -960,12 +1139,41 @@ novirtual class CallGenBase:
|
||||
if st_sha_d is not None: c_st_sha = st_sha_d
|
||||
c_tll: int = 0
|
||||
c_vpi: str = "-"
|
||||
c_tail_ty: str = "-"
|
||||
c_annot_res: str = "-"
|
||||
if probe_sha is not None:
|
||||
c_tll = self._dep_text_ll_exists(None, probe_sha)
|
||||
if self.Gen.CallGenBase.TempDir is not None and self.Gen.CallGenBase.TempDirLen > 0:
|
||||
VpiReader._ensure_vpi_ret_annot_loaded(probe_sha, self.Gen.CallGenBase.TempDir)
|
||||
annot_d: str = VpiReader._lookup_vpi_ret_annot(probe_sha, cls, method_name)
|
||||
if annot_d is not None: c_vpi = annot_d
|
||||
if annot_d is not None:
|
||||
c_vpi = annot_d
|
||||
# Probe the string resolver here (caller's pool when available, same annotation)
|
||||
# so the report separates "class missing" from "resolver returned nothing".
|
||||
pb_d: memhub.MemBuddy* = pool
|
||||
if pb_d is None: pb_d = CompilerFactory.factory.PermPool
|
||||
i32_p: mlirlite.LLVMType* = mlirlite.Int32(pb_d)
|
||||
i8_p: mlirlite.LLVMType* = mlirlite.Int8(pb_d)
|
||||
i8p_p: mlirlite.LLVMType* = mlirlite.Ptr(pb_d, i8_p)
|
||||
rv_p: mlirlite.LLVMType* = VpiReader._resolve_vpi_ret_annot_str(pb_d, annot_d, i32_p, i8p_p)
|
||||
if rv_p is None: c_annot_res = "none"
|
||||
else: c_annot_res = "ok"
|
||||
# Annotate the tail class of the raw annotation: "missing" = the class was never
|
||||
# registered (upstream bug); "struct"/"enum" = registered but the string resolver
|
||||
# still failed (resolver bug). Distinguishes the two without guessing.
|
||||
dot_d: str = string.strrchr(annot_d, '.')
|
||||
if dot_d is not None:
|
||||
tail_len_d: sizet = string.strlen(dot_d + 1)
|
||||
tb_d: bytes = stdlib.malloc(tail_len_d + 1)
|
||||
if tb_d is not None:
|
||||
rb_d: str = string.strchr(dot_d + 1, ']')
|
||||
if rb_d is not None: tail_len_d = sizet(uint64(rb_d) - uint64(dot_d + 1))
|
||||
string.strncpy(tb_d, dot_d + 1, tail_len_d)
|
||||
tb_d[tail_len_d] = '\0'
|
||||
if GenApi.get_struct_type(tb_d) is not None: c_tail_ty = "struct"
|
||||
elif GenApi.get_enum_base_type(tb_d) is not None: c_tail_ty = "enum"
|
||||
else: c_tail_ty = "missing"
|
||||
stdlib.free(tb_d)
|
||||
# Same-named struct entries: find_struct returns the FIRST exact match, so an earlier bogus
|
||||
# registration (alias/forward/stale, different ModuleSha1) silently shadows the real one.
|
||||
dup_n_d: int = 0
|
||||
@@ -993,7 +1201,7 @@ novirtual class CallGenBase:
|
||||
if cur_file_d is None: cur_file_d = "-"
|
||||
cur_def_d: str = self.Gen.StructGen.CurDefModuleSha1
|
||||
if cur_def_d is None: cur_def_d = "-"
|
||||
VLogger.error(f"method return type cannot be obtained precisely (function/text.ll/vpi stub all missed): {method_name} | [diag] cls={c_cls} struct_sha1={c_st_sha} lookup_anc={c_anc} lookup_sha1={c_sha} text_ll={c_tll} vpi_annot={c_vpi} same_name_entries={dup_n_d} last_same_sha1={dup_last_d} entry_ty={dup_ty_nm_d} cur_file_sha1={cur_file_d} cur_def_sha1={cur_def_d}", "HandlesExprCall")
|
||||
VLogger.error(f"method return type cannot be obtained precisely (function/text.ll/vpi stub all missed): {method_name} | [diag] cls={c_cls} struct_sha1={c_st_sha} lookup_anc={c_anc} lookup_sha1={c_sha} text_ll={c_tll} vpi_annot={c_vpi} tail_ty={c_tail_ty} annot_res={c_annot_res} same_name_entries={dup_n_d} last_same_sha1={dup_last_d} entry_ty={dup_ty_nm_d} cur_file_sha1={cur_file_d} cur_def_sha1={cur_def_d}", "HandlesExprCall")
|
||||
self.Gen.TypeGenErr.fatal_error(node, f"method return type cannot be obtained precisely; defaulting to i32 would truncate pointers/i64: {method_name}", "TypeError")
|
||||
return None
|
||||
|
||||
|
||||
@@ -10,7 +10,7 @@ import string
|
||||
import stdlib
|
||||
import sys
|
||||
import viperlib
|
||||
import win32.fileio as fileio
|
||||
import io as fileio
|
||||
import lib.core.VLogger as VLogger
|
||||
import lib.core.Gen.Translator as HT
|
||||
import lib.core.Gen.Type.TypeSpec as TSpec
|
||||
@@ -221,11 +221,25 @@ novirtual class CallGenDispatch(CallGenBase.CallGenBase):
|
||||
return None
|
||||
|
||||
def _tc_h2_c_special(self, pool: memhub.MemBuddy*, builder: mlirlite.IRBuilder*, mod: mlirlite.LLVMModule*, cl: ast.Call*, funcs_ptr: rawptr, func_count: int, trans: HT.Translator*, func_name: str, cargs: list[ast.AST*]*, can: sizet, hit_box: rawptr = None) -> mlirlite.Value*:
|
||||
"""Special c.Deref/Asm/LLVMIR calls; marks hit_box for void calls, else returns None."""
|
||||
"""Special __asm__/__llvmir__ calls; marks hit_box for void calls, else returns None."""
|
||||
# Retired c.* pointer builtins: fail fast with the replacement syntax.
|
||||
if cl.func is not None and cl.func.kind() == ast.ASTKind.Attribute:
|
||||
ra_c: ast.Attribute* = ast.Attribute*(cl.func)
|
||||
if ra_c.value is not None and ra_c.value.kind() == ast.ASTKind.Name and ra_c.attr is not None:
|
||||
rn_c: ast.Name* = ast.Name*(ra_c.value)
|
||||
if rn_c.id is not None and string.strcmp(rn_c.id, "c") == 0:
|
||||
if string.strcmp(ra_c.attr, "Deref") == 0 or string.strcmp(ra_c.attr, "DerefAs") == 0 \
|
||||
or string.strcmp(ra_c.attr, "Set") == 0 or string.strcmp(ra_c.attr, "Addr") == 0 \
|
||||
or string.strcmp(ra_c.attr, "Load") == 0:
|
||||
self.Gen.TypeGenErr.fatal_error(cl, f"c.{ra_c.attr} was removed; use `*p` (read), `*p = v` (write) or `&v` (address-of)", "NameError")
|
||||
return None
|
||||
# __asm__(...) / __llvmir__(...) sugar: bare calls equal c.Asm(...) / c.LLVMIR(...). Must
|
||||
# intercept before the c.* attribute branch, since the bare cl.func is a Name.
|
||||
if func_name is not None and cl.func is not None and cl.func.kind() == ast.ASTKind.Name:
|
||||
bare_nm: ast.Name* = ast.Name*(cl.func)
|
||||
if bare_nm.id is not None and (string.strcmp(bare_nm.id, "Deref") == 0 or string.strcmp(bare_nm.id, "DerefAs") == 0):
|
||||
self.Gen.TypeGenErr.fatal_error(cl, f"{bare_nm.id} was removed; use `*p` (read), `*p = v` (write) or `&v` (address-of)", "NameError")
|
||||
return None
|
||||
if bare_nm.id is not None and string.strcmp(bare_nm.id, "defined") == 0:
|
||||
# defined(NAME) is a compile-time predicate valid only inside a cif/celif condition;
|
||||
# it is evaluated there and never lowered as a runtime call.
|
||||
@@ -377,7 +391,7 @@ novirtual class CallGenDispatch(CallGenBase.CallGenBase):
|
||||
mt_arg_val: mlirlite.Value* = self.Gen.ExprGen.translate_value(builder, pool, mod, cargs.get(0), funcs_ptr, func_count, trans, mod_td_ty)
|
||||
if mt_arg_val is not None: return self.Gen.CallGenCtor._translate_t_type_cast(pool, builder, mt_arg_val, mod_td_ty, cargs.get(0), trans)
|
||||
|
||||
# Bare typedef casts (BYTEPTR, HANDLE, CHARPTR, ...): all CTypedef names and builtin primitives
|
||||
# Bare typedef casts (BYTEPTR, HANDLE, CHARPTR, ...): all TypedefT names and builtin primitives
|
||||
# are castable. In generic contexts T(x) first resolves T, then goes through resolve_typedef_or_map.
|
||||
if cl.func is not None and cl.func.kind() == ast.ASTKind.Name:
|
||||
bare_fn_name: str = self.Gen.TypeGenBase.resolve_generic_type_name(func_name)
|
||||
@@ -387,7 +401,7 @@ novirtual class CallGenDispatch(CallGenBase.CallGenBase):
|
||||
if string.strcmp(bare_fn_name, "double") == 0: bare_ty = mlirlite.Double(pool)
|
||||
elif string.strcmp(bare_fn_name, "float") == 0: bare_ty = mlirlite.Float(pool)
|
||||
else:
|
||||
# Prefer the CTypedef dynamic table (library typedef casts); on miss fall back to
|
||||
# Prefer the TypedefT dynamic table (library typedef casts); on miss fall back to
|
||||
# map_builtin_type.
|
||||
bare_imp_c: str = None
|
||||
bare_fi_c: str = None
|
||||
@@ -412,10 +426,10 @@ novirtual class CallGenDispatch(CallGenBase.CallGenBase):
|
||||
bare_arg2_node: ast.AST* = cargs.get(1)
|
||||
bare_arg2_is_cptr: int = 0
|
||||
if bare_arg2_node is not None:
|
||||
# Builtin pointer marker: CPtr / rawptr
|
||||
# Builtin pointer marker: PtrT / rawptr
|
||||
if bare_arg2_node.kind() == ast.ASTKind.Name:
|
||||
bare_arg2_bn: ast.Name* = ast.Name*(bare_arg2_node)
|
||||
if bare_arg2_bn.id is not None and (string.strcmp(bare_arg2_bn.id, "CPtr") == 0 or string.strcmp(bare_arg2_bn.id, "rawptr") == 0): bare_arg2_is_cptr = 1
|
||||
if bare_arg2_bn.id is not None and (string.strcmp(bare_arg2_bn.id, "PtrT") == 0 or string.strcmp(bare_arg2_bn.id, "rawptr") == 0): bare_arg2_is_cptr = 1
|
||||
if bare_arg2_is_cptr != 0:
|
||||
# CVoid* -> i8* (void* is represented as i8* in LLVM)
|
||||
match bare_ty:
|
||||
@@ -531,7 +545,7 @@ novirtual class CallGenDispatch(CallGenBase.CallGenBase):
|
||||
elif string.strcmp(sz_cls_name, "CChar") == 0: sz_builtin = 1
|
||||
elif string.strcmp(sz_cls_name, "CDouble") == 0: sz_builtin = 8
|
||||
elif string.strcmp(sz_cls_name, "CFloat") == 0: sz_builtin = 4
|
||||
elif string.strcmp(sz_cls_name, "CPtr") == 0: sz_builtin = 8
|
||||
elif string.strcmp(sz_cls_name, "PtrT") == 0: sz_builtin = 8
|
||||
elif string.strcmp(sz_cls_name, "CSizeT") == 0: sz_builtin = 8
|
||||
elif string.strcmp(sz_cls_name, "CBool") == 0: sz_builtin = 1
|
||||
elif string.strcmp(sz_cls_name, "bool") == 0: sz_builtin = 1
|
||||
@@ -583,7 +597,7 @@ novirtual class CallGenDispatch(CallGenBase.CallGenBase):
|
||||
sz_resolved_name = spec_sz
|
||||
sz_resolved_ty = self.Gen.StructGen.get_struct_type(spec_sz)
|
||||
# Fallback 2: instance receiver self.__sizeof__() / obj.__sizeof__() uses the actual
|
||||
# value type; array receivers (t.CArray[T,N]) size as N*elem_size.
|
||||
# value type; array receivers (t.ArrayT[T,N]) size as N*elem_size.
|
||||
if sz_resolved_ty is None:
|
||||
inst_sz_val: mlirlite.Value* = self.Gen.ExprGen.translate_value(builder, pool, mod, sz_at.value, None, 0, trans)
|
||||
if inst_sz_val is not None and inst_sz_val.Ty is not None:
|
||||
@@ -622,7 +636,7 @@ novirtual class CallGenDispatch(CallGenBase.CallGenBase):
|
||||
obj_val: mlirlite.Value* = self.Gen.ExprGen.translate_value(builder, pool, mod, len_at.value, funcs_ptr, func_count, trans)
|
||||
if obj_val is not None and obj_val.Ty is not None:
|
||||
# Defensive check: obj_val must be a pointer for GEP. If translate_value returns
|
||||
# i32 (misparsed CDefine constant, module-attr fallback), llc reports a non-pointer GEP base.
|
||||
# i32 (misparsed DefineT constant, module-attr fallback), llc reports a non-pointer GEP base.
|
||||
if self.Gen.ExprGenBase.is_ptr_type(obj_val.Ty) == 0:
|
||||
# Type inference failed (not a pointer/struct): fail fast rather than return 0
|
||||
self.Gen.TypeGenErr.fatal_error(len_at.value, "len() argument type inference failed (not a pointer/struct)", "TypeError")
|
||||
@@ -641,7 +655,7 @@ novirtual class CallGenDispatch(CallGenBase.CallGenBase):
|
||||
if len_arg_node is not None:
|
||||
len_val: mlirlite.Value* = self.Gen.ExprGen.translate_value(builder, pool, mod, len_arg_node, None, 0, trans)
|
||||
if len_val is not None:
|
||||
# len(array): t.CArray[T, N] -> constant N (TPC _HandleLenLlvm ArrayType.count).
|
||||
# len(array): t.ArrayT[T, N] -> constant N (TPC _HandleLenLlvm ArrayType.count).
|
||||
# Array variables come back as Ptr([N x T]) or [N x T]; recognize both.
|
||||
len_is_arr: int = 0
|
||||
len_arr_n: int = 0
|
||||
@@ -714,7 +728,7 @@ novirtual class CallGenDispatch(CallGenBase.CallGenBase):
|
||||
return mlirlite.build_call(builder, len_mangled_x, len_self_x, 1, len_ret_x, 0)
|
||||
# fail-fast: the class has no __len__ (compile-time error, matching TPC
|
||||
# HandlesExprBuiltin._FailFastLenUnsupported wording).
|
||||
self.Gen.TypeGenErr.fatal_error(len_arg_node, f"len() argument type '{len_cls_nm}' has no __len__ method. len() only supports fixed-size arrays/array pointers (t.CArray[T, N]), i8* (str/bytes), or structs with __len__; non-i8 bare pointers/scalar pointers have no length semantics: use str/bytes for strings, record the length yourself when the element count is needed, or use rawptr[Element] and maintain the element count explicitly", "TypeError")
|
||||
self.Gen.TypeGenErr.fatal_error(len_arg_node, f"len() argument type '{len_cls_nm}' has no __len__ method. len() only supports fixed-size arrays/array pointers (t.ArrayT[T, N]), i8* (str/bytes), or structs with __len__; non-i8 bare pointers/scalar pointers have no length semantics: use str/bytes for strings, record the length yourself when the element count is needed, or use rawptr[Element] and maintain the element count explicitly", "TypeError")
|
||||
# Non-struct: str (Ptr(Int8)) uses strlen; other types fail fast
|
||||
len_arg_ty: mlirlite.LLVMType* = mlirlite.value_get_ty(len_val)
|
||||
len_is_str: int = 0
|
||||
@@ -747,7 +761,7 @@ novirtual class CallGenDispatch(CallGenBase.CallGenBase):
|
||||
return mlirlite.build_call(builder, "strlen", len_coerced, 1, i64_ty_ln2, 0)
|
||||
# fail-fast: not a string/array/object, so length is unavailable (matching TPC
|
||||
# HandlesExprBuiltin._FailFastLenUnsupported wording).
|
||||
self.Gen.TypeGenErr.fatal_error(len_arg_node, "len() argument type not supported (not a string/array/object). len() only supports fixed-size arrays/array pointers (t.CArray[T, N]), i8* (str/bytes), or structs with __len__; non-i8 bare pointers/scalar pointers have no length semantics: use str/bytes for strings, record the length yourself when the element count is needed, or use rawptr[Element] and maintain the element count explicitly", "TypeError")
|
||||
self.Gen.TypeGenErr.fatal_error(len_arg_node, "len() argument type not supported (not a string/array/object). len() only supports fixed-size arrays/array pointers (t.ArrayT[T, N]), i8* (str/bytes), or structs with __len__; non-i8 bare pointers/scalar pointers have no length semantics: use str/bytes for strings, record the length yourself when the element count is needed, or use rawptr[Element] and maintain the element count explicitly", "TypeError")
|
||||
|
||||
# Detect isinstance(): runtime vtable subclass-set comparison. obj's head vtable pointer must
|
||||
# be in {Cls and all subclass vtables}; NoVTable targets fall back to a compile-time check.
|
||||
@@ -983,6 +997,16 @@ novirtual class CallGenDispatch(CallGenBase.CallGenBase):
|
||||
if nl_obj is not None and nl_obj.Ty is not None:
|
||||
nl_deref: mlirlite.Value* = self.Gen.ExprGenBase._deref_if_ptr_ptr(builder, nl_obj)
|
||||
if nl_deref is None: nl_deref = nl_obj
|
||||
# 值语义捕获(`entry: vpui.Entry`):闭包槽里存的是结构体本身,
|
||||
# load 出来是**值**。方法接收者必须是槽地址(可写、可作指针),
|
||||
# 把结构体值直接当接收者会让虚调用 build_bitcast(struct -> ptr),
|
||||
# 那是非法 IR(mlir-opt: operand #0 must be LLVM-compatible
|
||||
# non-aggregate type)。与本地结构体变量返回 alloca 的规则对齐。
|
||||
if nl_deref.Ty is not None:
|
||||
nlk: int = nl_deref.Ty.kind()
|
||||
if nlk == mlirlite.LLVMType.Struct or nlk == mlirlite.LLVMType.Array:
|
||||
nl_slot: mlirlite.Value* = self.Gen.StmtGenDecl.get_nonlocal_var_ptr(trans, method_obj_nm.id)
|
||||
if nl_slot is not None: nl_deref = nl_slot
|
||||
nl_struct: mlirlite.LLVMType* = self.Gen.StructGen.get_struct_type_from_value(nl_deref)
|
||||
if nl_struct is not None:
|
||||
nl_cls: str = self.Gen.StructGen.get_class_name_by_type(pool, nl_struct)
|
||||
|
||||
@@ -6,7 +6,7 @@ import string
|
||||
import stdio
|
||||
import stdlib
|
||||
import viperlib
|
||||
import win32.fileio as fileio
|
||||
import io as fileio
|
||||
import lib.Stub.StubMerger as StubMerger
|
||||
import lib.core.VLogger as VLogger
|
||||
import lib.Projectrans.Config as Config
|
||||
@@ -17,9 +17,9 @@ import lib.Stub.StubMerger.StubMergerStore as StubMergerStore
|
||||
|
||||
|
||||
# ============================================================
|
||||
# ExprGenCdef - ExprGenMixin split (stage D): cross-module CDefine lookup
|
||||
# ExprGenCdef - ExprGenMixin split (stage D): cross-module DefineT lookup
|
||||
novirtual class ExprGenCdef(ExprGenBase.ExprGenBase):
|
||||
"""Cross-module CDefine lookup"""
|
||||
"""Cross-module DefineT lookup"""
|
||||
|
||||
# _build_source_path_from_sha1: build a source .vp/.py path from SHA1 via the StubMerger
|
||||
# global store (.vpi missing -> source .py); "lib/..." -> SourceDir, else IncludesDir.
|
||||
@@ -43,7 +43,7 @@ novirtual class ExprGenCdef(ExprGenBase.ExprGenBase):
|
||||
idx_s = i
|
||||
break
|
||||
if idx_s < 0: return None
|
||||
# get the rel path (MAX_REL_PATH_LEN=256, avoids cross-module CDefine issues)
|
||||
# get the rel path (MAX_REL_PATH_LEN=256, avoids cross-module DefineT issues)
|
||||
MAX_RPL: sizet = 256
|
||||
ridx: sizet = sizet(idx_s) * MAX_RPL
|
||||
rel_path: str = rel_arr_ptr + ridx
|
||||
@@ -112,18 +112,18 @@ novirtual class ExprGenCdef(ExprGenBase.ExprGenBase):
|
||||
if ok_before != 0 and ok_after != 0: return 1
|
||||
i += 1
|
||||
return 0
|
||||
# _lookup_cross_module_cdefine - cross-module CDefine lookup: resolve the source module
|
||||
# from from_imports, parse its pyi "NAME: t.CDefine = value", then cache in the current module.
|
||||
# _lookup_cross_module_cdefine - cross-module DefineT lookup: resolve the source module
|
||||
# from from_imports, parse its pyi "NAME: t.DefineT = value", then cache in the current module.
|
||||
def _lookup_cross_module_cdefine(self, pool: memhub.MemBuddy*, name: str, from_imports: str, imported_modules: str = None, depth: int = 0, node: ast.AST* = None) -> int64:
|
||||
"""Look up a CDefine constant across modules, returning its 64-bit value or -1"""
|
||||
"""Look up a DefineT constant across modules, returning its 64-bit value or -1"""
|
||||
# recursion-depth guard: re-export chains + imported_modules walk can loop and OOM;
|
||||
# cap at 32 to fail fast on cyclic imports (error carries node line + module chain).
|
||||
if depth > 32:
|
||||
err_buf: str = pool.alloc(256)
|
||||
if err_buf is not None:
|
||||
viperlib.snprintf(err_buf, 256, "cross-module CDefine lookup recursion too deep (suspected module import/re-export cycle): %s (depth=%d fi=%s)", name, depth, from_imports if from_imports is not None else "(nil)")
|
||||
viperlib.snprintf(err_buf, 256, "cross-module DefineT lookup recursion too deep (suspected module import/re-export cycle): %s (depth=%d fi=%s)", name, depth, from_imports if from_imports is not None else "(nil)")
|
||||
self.Gen.TypeGenErr.fatal_error(node, err_buf, "InternalCompilerError")
|
||||
self.Gen.TypeGenErr.fatal_error(node, "cross-module CDefine lookup recursion too deep (suspected module import/re-export cycle)", "InternalCompilerError")
|
||||
self.Gen.TypeGenErr.fatal_error(node, "cross-module DefineT lookup recursion too deep (suspected module import/re-export cycle)", "InternalCompilerError")
|
||||
# clear the found flag at entry so every early return reports "not found",
|
||||
# avoiding a stale success flag left over from a previous recursive call.
|
||||
self.Gen.TypeGenCDef.set_cdefine_found(0)
|
||||
@@ -131,7 +131,7 @@ novirtual class ExprGenCdef(ExprGenBase.ExprGenBase):
|
||||
if from_imports is None: from_imports = ""
|
||||
|
||||
# 1. resolve the module name for `name` from from_imports; try exact match first
|
||||
# (allow_star_fallback=0) so a star import cannot mislead CDefine constants.
|
||||
# (allow_star_fallback=0) so a star import cannot mislead DefineT constants.
|
||||
mod_name_raw: str = self.Gen.ImportsGen.lookup_from_import(from_imports, name, 0)
|
||||
if mod_name_raw is None:
|
||||
# exact match failed: try the star import fallback
|
||||
@@ -151,7 +151,7 @@ novirtual class ExprGenCdef(ExprGenBase.ExprGenBase):
|
||||
base_mod = mb_buf
|
||||
while base_mod is not None and base_mod[0] == '.': base_mod = base_mod + 1
|
||||
|
||||
# cross-module CDefine cache (perm_pool, reused across files): key = "name@srcModule"
|
||||
# cross-module DefineT cache (perm_pool, reused across files): key = "name@srcModule"
|
||||
# keeps modules isolated so same-name constants from different modules do not cross.
|
||||
xkey_buf: str = None
|
||||
if base_mod is not None:
|
||||
@@ -240,36 +240,64 @@ novirtual class ExprGenCdef(ExprGenBase.ExprGenBase):
|
||||
if bytes_read < 10: need_py_fallback = 1
|
||||
else:
|
||||
if bytes_read < PYI_READ_BUF_SIZE: pyi_buf[bytes_read] = 0
|
||||
# vpi stubs may declare a CDefine name without a value; the real value lives
|
||||
# vpi stubs may declare a DefineT name without a value; the real value lives
|
||||
# in the source .vp/.py, so trust vpi only when the target name's line has a value.
|
||||
cd_sig: int = 0
|
||||
name_has_val: int = 0
|
||||
cs_name_len: sizet = string.strlen(name)
|
||||
cs_total: sizet = string.strlen(pyi_buf)
|
||||
cs_i: sizet = 0
|
||||
while cs_i + 9 < cs_total:
|
||||
if (pyi_buf[cs_i] == 't' and pyi_buf[cs_i + 1] == '.' and pyi_buf[cs_i + 2] == 'C' and pyi_buf[cs_i + 3] == 'D' and pyi_buf[cs_i + 4] == 'e' and pyi_buf[cs_i + 5] == 'f' and pyi_buf[cs_i + 6] == 'i' and pyi_buf[cs_i + 7] == 'n' and pyi_buf[cs_i + 8] == 'e'):
|
||||
# check whether the rest of the line has '=' (only valued decls match in vpi)
|
||||
cd_eq: int = 0
|
||||
cs_j: sizet = cs_i + 9
|
||||
while cs_j < cs_total and pyi_buf[cs_j] != '\n':
|
||||
if pyi_buf[cs_j] == '=':
|
||||
cd_eq = 1
|
||||
while cs_i < cs_total:
|
||||
cs_ls: sizet = cs_i
|
||||
while cs_ls > 0 and pyi_buf[cs_ls - 1] != '\n': cs_ls -= 1
|
||||
cs_le: sizet = cs_i
|
||||
while cs_le < cs_total and pyi_buf[cs_le] != '\n': cs_le += 1
|
||||
cs_off: sizet = cs_ls
|
||||
while cs_off < cs_le and pyi_buf[cs_off] == ' ': cs_off += 1
|
||||
# New source-style line: `define NAME[: T] = value`
|
||||
if cs_le - cs_off >= 7 and string.strncmp(pyi_buf + cs_off, "define ", 7) == 0:
|
||||
cs_ns: sizet = cs_off + 7
|
||||
while cs_ns < cs_le and pyi_buf[cs_ns] == ' ': cs_ns += 1
|
||||
if cs_le - cs_ns >= cs_name_len + 1:
|
||||
cs_nm_ok: int = 1
|
||||
for ki2 in range(int(cs_name_len)):
|
||||
if pyi_buf[cs_ns + ki2] != name[ki2]:
|
||||
cs_nm_ok = 0
|
||||
break
|
||||
if cs_nm_ok != 0:
|
||||
cs_next: char = pyi_buf[cs_ns + cs_name_len]
|
||||
if cs_next == ':' or cs_next == ' ' or cs_next == '=':
|
||||
cs_eq: int = 0
|
||||
cs_j: sizet = cs_ns + cs_name_len
|
||||
while cs_j < cs_le:
|
||||
if pyi_buf[cs_j] == '=':
|
||||
cs_eq = 1
|
||||
break
|
||||
cs_j += 1
|
||||
if cs_eq != 0:
|
||||
cd_sig = 1
|
||||
name_has_val = 1
|
||||
# Legacy marker line: `NAME: ... DefineT ... = value`
|
||||
elif cs_le - cs_off >= cs_name_len + 1:
|
||||
cs_nm_ok2: int = 1
|
||||
for ki2b in range(int(cs_name_len)):
|
||||
if pyi_buf[cs_off + ki2b] != name[ki2b]:
|
||||
cs_nm_ok2 = 0
|
||||
break
|
||||
cs_j += 1
|
||||
if cd_eq != 0:
|
||||
cd_sig = 1
|
||||
# locate line start; check it begins with "name:" (target has a value)
|
||||
cs_line_start: sizet = cs_i
|
||||
while cs_line_start > 0 and pyi_buf[cs_line_start - 1] != '\n': cs_line_start -= 1
|
||||
if cs_i - cs_line_start >= cs_name_len + 1:
|
||||
nm_ok: int = 1
|
||||
for ki2 in range(int(cs_name_len)):
|
||||
if pyi_buf[cs_line_start + ki2] != name[ki2]:
|
||||
nm_ok = 0
|
||||
break
|
||||
if nm_ok == 1 and pyi_buf[cs_line_start + cs_name_len] == ':': name_has_val = 1
|
||||
cs_i += 1
|
||||
if cs_nm_ok2 != 0 and pyi_buf[cs_off + cs_name_len] == ':':
|
||||
cs_marker: int = 0
|
||||
cs_eq2: int = 0
|
||||
cs_j2: sizet = cs_off + cs_name_len + 1
|
||||
while cs_j2 < cs_le:
|
||||
if pyi_buf[cs_j2] == '=':
|
||||
cs_eq2 = 1
|
||||
break
|
||||
if cs_j2 + 9 <= cs_le and string.strncmp(pyi_buf + cs_j2, "t.DefineT", 9) == 0: cs_marker = 1
|
||||
cs_j2 += 1
|
||||
if cs_marker != 0 and cs_eq2 != 0:
|
||||
cd_sig = 1
|
||||
name_has_val = 1
|
||||
cs_i = cs_le + 1
|
||||
if cd_sig == 0 or name_has_val == 0: need_py_fallback = 1
|
||||
stdlib.free(pyi_path)
|
||||
|
||||
@@ -281,7 +309,7 @@ novirtual class ExprGenCdef(ExprGenBase.ExprGenBase):
|
||||
# report and abort instead of silently returning -1 (avoids bad @UNDEFINED refs).
|
||||
err_buf: str = pool.alloc_tagged(4096, memhub.DETSITE_EXPRGENCDEF_263)
|
||||
if err_buf is not None:
|
||||
viperlib.snprintf(err_buf, 512, "cross-module CDefine lookup failed: SHA1 %s is not in the store (base_mod=%s name=%s). " "Phase1/Phase2 did not correctly populate PopulateSha1MapStore/AppendToSha1MapStore", sha1, base_mod, name)
|
||||
viperlib.snprintf(err_buf, 512, "cross-module DefineT lookup failed: SHA1 %s is not in the store (base_mod=%s name=%s). " "Phase1/Phase2 did not correctly populate PopulateSha1MapStore/AppendToSha1MapStore", sha1, base_mod, name)
|
||||
VLogger.error(err_buf, "XMOD-CD")
|
||||
stdlib.free(pyi_buf)
|
||||
return -1
|
||||
@@ -297,8 +325,8 @@ novirtual class ExprGenCdef(ExprGenBase.ExprGenBase):
|
||||
return -1
|
||||
if bytes_read < PYI_READ_BUF_SIZE: pyi_buf[bytes_read] = 0
|
||||
|
||||
# 8. search pyi_buf for the "name: t.CDefine = value" line
|
||||
# format: NAME: t.CDefine = value
|
||||
# 8. search pyi_buf for the "name: t.DefineT = value" line
|
||||
# format: NAME: t.DefineT = value
|
||||
name_len: sizet = string.strlen(name)
|
||||
total_len: sizet = string.strlen(pyi_buf)
|
||||
pos: sizet = 0
|
||||
@@ -318,32 +346,50 @@ novirtual class ExprGenCdef(ExprGenBase.ExprGenBase):
|
||||
if pos < total_len:
|
||||
pos += 1 # skip '\n'
|
||||
|
||||
# check whether the line starts with "name:"
|
||||
if line_len < name_len + 1: continue
|
||||
match_ok: int = 1
|
||||
for ki in range(int(name_len)):
|
||||
if pyi_buf[line_start + ki] != name[ki]:
|
||||
match_ok = 0
|
||||
break
|
||||
# Match `define NAME[: T] = value` or legacy `NAME: ... DefineT ... = value`.
|
||||
match_ok: int = 0
|
||||
eq_scan_start: sizet = 0
|
||||
line_off: sizet = line_start
|
||||
while line_off < line_start + line_len and pyi_buf[line_off] == ' ': line_off += 1
|
||||
if line_len >= 7 and string.strncmp(pyi_buf + line_off, "define ", 7) == 0:
|
||||
ns: sizet = line_off + 7
|
||||
while ns < line_start + line_len and pyi_buf[ns] == ' ': ns += 1
|
||||
if line_start + line_len >= ns + name_len + 1:
|
||||
match_ok = 1
|
||||
for ki in range(int(name_len)):
|
||||
if pyi_buf[ns + ki] != name[ki]:
|
||||
match_ok = 0
|
||||
break
|
||||
if match_ok != 0:
|
||||
nx: char = pyi_buf[ns + name_len]
|
||||
if nx != ':' and nx != ' ' and nx != '=': match_ok = 0
|
||||
if match_ok != 0: eq_scan_start = ns + name_len
|
||||
elif line_len >= name_len + 1:
|
||||
match_ok = 1
|
||||
for ki_ld in range(int(name_len)):
|
||||
if pyi_buf[line_start + ki_ld] != name[ki_ld]:
|
||||
match_ok = 0
|
||||
break
|
||||
if match_ok != 0 and pyi_buf[line_start + name_len] == ':':
|
||||
has_cdefine: int = 0
|
||||
ci: sizet = line_start + name_len + 1
|
||||
while ci + 7 <= line_start + line_len:
|
||||
if (pyi_buf[ci] == 'C' and pyi_buf[ci + 1] == 'D' and pyi_buf[ci + 2] == 'e' and pyi_buf[ci + 3] == 'f' and pyi_buf[ci + 4] == 'i' and pyi_buf[ci + 5] == 'n' and pyi_buf[ci + 6] == 'e'):
|
||||
has_cdefine = 1
|
||||
break
|
||||
ci += 1
|
||||
if has_cdefine != 0: eq_scan_start = line_start + name_len + 1
|
||||
else: match_ok = 0
|
||||
else: match_ok = 0
|
||||
if match_ok == 0: continue
|
||||
if pyi_buf[line_start + name_len] != ':': continue
|
||||
|
||||
# check the line contains the CDefine marker
|
||||
has_cdefine: int = 0
|
||||
ci: sizet = line_start + name_len + 1
|
||||
while ci + 7 <= line_start + line_len:
|
||||
if (pyi_buf[ci] == 'C' and pyi_buf[ci + 1] == 'D' and pyi_buf[ci + 2] == 'e' and pyi_buf[ci + 3] == 'f' and pyi_buf[ci + 4] == 'i' and pyi_buf[ci + 5] == 'n' and pyi_buf[ci + 6] == 'e'):
|
||||
has_cdefine = 1
|
||||
break
|
||||
ci += 1
|
||||
if has_cdefine == 0: continue
|
||||
|
||||
# find the value after "="
|
||||
eq_pos: sizet = line_start + name_len + 1
|
||||
eq_pos: sizet = eq_scan_start
|
||||
while eq_pos < line_start + line_len and pyi_buf[eq_pos] != '=': eq_pos += 1
|
||||
if eq_pos >= line_start + line_len: continue
|
||||
# 注解段 = [名字冒号后, '='),用于判定声明的浮点宽度(如 `PI: float | t.CDefine`)
|
||||
annot_start: sizet = line_start + name_len + 1
|
||||
# Annotation segment between the name and '=', used for the declared float width.
|
||||
annot_start: sizet = eq_scan_start
|
||||
while annot_start < eq_pos and (pyi_buf[annot_start] == ':' or pyi_buf[annot_start] == ' '): annot_start += 1
|
||||
annot_end: sizet = eq_pos
|
||||
eq_pos += 1 # skip '='
|
||||
|
||||
@@ -384,7 +430,7 @@ novirtual class ExprGenCdef(ExprGenBase.ExprGenBase):
|
||||
is_name_val: int = 0
|
||||
is_str_val: int = 0
|
||||
# string value (e.g. ZLIB_VERSION = "1.3.2"): read inside the quotes; without this,
|
||||
# cross-module read-back skipped it and reported "CDefine not found".
|
||||
# cross-module read-back skipped it and reported "DefineT not found".
|
||||
if eq_pos < line_start + line_len and pyi_buf[eq_pos] == '"':
|
||||
is_str_val = 1
|
||||
eq_pos += 1 # skip opening quote
|
||||
@@ -439,18 +485,18 @@ novirtual class ExprGenCdef(ExprGenBase.ExprGenBase):
|
||||
l_is_double: int = 0
|
||||
ldbl: double = 0.0
|
||||
if is_str_val != 0:
|
||||
# string CDefine (e.g. ZLIB_VERSION = "1.3.2"): register the string value
|
||||
# string DefineT (e.g. ZLIB_VERSION = "1.3.2"): register the string value
|
||||
# (including ""), mark result_found + res_is_str so the tail skips integers.
|
||||
self.Gen.TypeGenCDef.register_cdefine_string(pool, name, val_buf)
|
||||
result_val = 0
|
||||
result_found = 1
|
||||
res_is_str = 1
|
||||
elif is_name_val != 0:
|
||||
# True/False literal -> 1/0 (TPC treats bool as integer in CDefine context)
|
||||
# True/False literal -> 1/0 (TPC treats bool as integer in DefineT context)
|
||||
if string.strcmp(val_buf, "True") == 0: lval = 1
|
||||
elif string.strcmp(val_buf, "False") == 0: lval = 0
|
||||
else:
|
||||
# name reference: look up the current CDefine table (e.g. FOREGROUND_COMBO)
|
||||
# name reference: look up the current DefineT table (e.g. FOREGROUND_COMBO)
|
||||
lval = self.Gen.TypeGenCDef.lookup_cdefine_constant(val_buf)
|
||||
if self.Gen.TypeGenCDef.is_cdefine_found() == 0:
|
||||
# name not registered: cannot evaluate, skip the line
|
||||
@@ -600,7 +646,7 @@ novirtual class ExprGenCdef(ExprGenBase.ExprGenBase):
|
||||
while eq_pos < line_start + line_len and pyi_buf[eq_pos] == ' ': eq_pos += 1
|
||||
if eq_pos < line_start + line_len and pyi_buf[eq_pos] == ')': eq_pos += 1
|
||||
if is_float_val != 0:
|
||||
# float CDefine (e.g. U_M_PI = 3.14159...): cache as double for double
|
||||
# float DefineT (e.g. U_M_PI = 3.14159...): cache as double for double
|
||||
# constant generation; int part serves the int path (is_cdefine_double flag).
|
||||
# 注解声明为 float 时带上 f32 种类,使用方才能直接出 f32 常量。
|
||||
res_is_f32 = self._pyi_annot_float_kind(pyi_buf, annot_start, annot_end)
|
||||
@@ -789,11 +835,11 @@ novirtual class ExprGenCdef(ExprGenBase.ExprGenBase):
|
||||
result_val = sub_val2
|
||||
result_found = 1
|
||||
|
||||
# 10. cache into the current module's CDefine table and the cross-module perm_pool;
|
||||
# 10. cache into the current module's DefineT table and the cross-module perm_pool;
|
||||
# float CDefines are already cached as double, so only integers are written here.
|
||||
if result_found != 0:
|
||||
if res_is_str != 0:
|
||||
# string CDefine: the value was already registered in the match loop (val_buf is
|
||||
# string DefineT: the value was already registered in the match loop (val_buf is
|
||||
# the hit line's content, valid across the function); only write the cross cache.
|
||||
if base_mod is not None:
|
||||
ck_len_s: sizet = string.strlen(name) + string.strlen(base_mod) + 2
|
||||
@@ -832,10 +878,10 @@ novirtual class ExprGenCdef(ExprGenBase.ExprGenBase):
|
||||
else: self.Gen.TypeGenCDef.set_cdefine_found(0)
|
||||
|
||||
return result_val
|
||||
# _is_cdefine_name_pattern - CDefine names follow ALL_CAPS (A-Z, 0-9, _ with >=1 uppercase);
|
||||
# _is_cdefine_name_pattern - DefineT names follow ALL_CAPS (A-Z, 0-9, _ with >=1 uppercase);
|
||||
# lowercase keywords/variables won't match, avoiding fail-fast false positives. 1=match, 0=no.
|
||||
def _is_cdefine_name_pattern(self, name: str) -> int:
|
||||
"""Return 1 if name follows the CDefine convention (uppercase letters, digits, underscore)"""
|
||||
"""Return 1 if name follows the DefineT convention (uppercase letters, digits, underscore)"""
|
||||
if name is None: return 0
|
||||
has_upper: int = 0
|
||||
i: sizet = 0
|
||||
@@ -848,7 +894,7 @@ novirtual class ExprGenCdef(ExprGenBase.ExprGenBase):
|
||||
# ============================================================
|
||||
# Translate a variable reference (Name node) -> load
|
||||
def _const_int_for_cdefine(self, pool: memhub.MemBuddy*, name: str, value: int64) -> mlirlite.Value*:
|
||||
"""Materialize a CDefine integer constant as an LLVM constant (TPC TypeConvert aligned)
|
||||
"""Materialize a DefineT integer constant as an LLVM constant (TPC TypeConvert aligned)
|
||||
|
||||
位宽优先用 define 的**已知位宽**(右侧显式强转或声明注解,见 register_cdefine_constant 的 bits);
|
||||
没有位宽记录时按**推断位宽**物化(值的最小可存宽度;0x80000000 → 32)。
|
||||
@@ -867,7 +913,7 @@ novirtual class ExprGenCdef(ExprGenBase.ExprGenBase):
|
||||
return mlirlite.const_int(pool, wmin, self._cdefine_bits_value(value, wmin))
|
||||
|
||||
# ============================================================
|
||||
# cdefine_use_width_value - materialize CDefine constants at the use-site width (TPC-aligned)
|
||||
# cdefine_use_width_value - materialize DefineT constants at the use-site width (TPC-aligned)
|
||||
def _cdefine_bits_value(self, value: int64, bits: int) -> int64:
|
||||
"""把值按位宽处理:**只有在当前位宽的两种解释下都装不下时才截断**,否则原样保留。
|
||||
|
||||
@@ -893,7 +939,7 @@ novirtual class ExprGenCdef(ExprGenBase.ExprGenBase):
|
||||
return value
|
||||
|
||||
def cdefine_use_width_value(self, pool: memhub.MemBuddy*, node: ast.AST*, name: str, value: int64, target_ty: mlirlite.LLVMType*, use_ctx: str = None) -> mlirlite.Value*:
|
||||
"""Materialize a CDefine integer constant at a known use-site width, or None on fallback"""
|
||||
"""Materialize a DefineT integer constant at a known use-site width, or None on fallback"""
|
||||
bits: int = self.Gen.ExprGenBase.get_llvm_type_bits(target_ty)
|
||||
if bits != 8 and bits != 16 and bits != 32 and bits != 64: return None
|
||||
wmin: int = self.Gen.AssignGen.define_min_width(value)
|
||||
|
||||
@@ -8,7 +8,7 @@ import stdlib
|
||||
import viperlib
|
||||
import stdio
|
||||
import sys
|
||||
import win32.fileio as fileio
|
||||
import io as fileio
|
||||
import lib.Stub.StubMerger as StubMerger
|
||||
import lib.core.CompilerFactory as CompilerFactory
|
||||
import lib.core.Gen.Translator as HT
|
||||
|
||||
@@ -17,7 +17,7 @@ import lib.core.CompilerFactory as CompilerFactory
|
||||
import lib.Projectrans.Config as Config
|
||||
|
||||
import lib.core.Gen.Type.TypeGenBase as TypeGenBase
|
||||
import win32.fileio as fileio
|
||||
import io as fileio
|
||||
|
||||
|
||||
# ============================================================
|
||||
@@ -60,7 +60,7 @@ novirtual class TypeGenResolve(TypeGenBase.TypeGenBase):
|
||||
return buf
|
||||
return at.attr
|
||||
# Subscript: build "value[slice]" (recursive slice, List/Tuple kept fully).
|
||||
# CTypedef extraction rebuilds the expression, so the slice must not be dropped.
|
||||
# TypedefT extraction rebuilds the expression, so the slice must not be dropped.
|
||||
if k == ast.ASTKind.Subscript:
|
||||
sub_s: ast.Subscript* = ast.Subscript*(node)
|
||||
val_s: str = self._get_type_name_from_annotation(pool, sub_s.value)
|
||||
@@ -87,7 +87,7 @@ novirtual class TypeGenResolve(TypeGenBase.TypeGenBase):
|
||||
k2: int = node.kind()
|
||||
# Name / Attribute / BinOp / nested Subscript: reuse type-name extraction
|
||||
if k2 == ast.ASTKind.Name or k2 == ast.ASTKind.Attribute or k2 == ast.ASTKind.BinOp or k2 == ast.ASTKind.Subscript: return self._get_type_name_from_annotation(pool, node)
|
||||
# Constant: numeric/string literal (e.g. the count in t.CArray[elem, 104])
|
||||
# Constant: numeric/string literal (e.g. the count in t.ArrayT[elem, 104])
|
||||
if k2 == ast.ASTKind.Constant:
|
||||
cn2: ast.Constant* = ast.Constant*(node)
|
||||
if cn2.const_kind == ast.CONST_INT:
|
||||
@@ -101,7 +101,7 @@ novirtual class TypeGenResolve(TypeGenBase.TypeGenBase):
|
||||
if k2 == ast.ASTKind.List:
|
||||
lst: ast.List* = ast.List*(node)
|
||||
return self._list_elts_to_type_str(pool, lst.elts, "[", "]")
|
||||
# Tuple: two-arg forms like t.CArray[elem, N]. Nested comma slices are packed as
|
||||
# Tuple: two-arg forms like t.ArrayT[elem, N]. Nested comma slices are packed as
|
||||
# Tuple, so output the slice without parens ("x, y") to keep ast.parse_expression valid.
|
||||
if k2 == ast.ASTKind.Tuple:
|
||||
tup: ast.Tuple* = ast.Tuple*(node)
|
||||
@@ -237,12 +237,12 @@ novirtual class TypeGenResolve(TypeGenBase.TypeGenBase):
|
||||
_cname: str = container
|
||||
_c_dot: str = string.strrchr(container, '.')
|
||||
if _c_dot is not None: _cname = _c_dot + 1
|
||||
if string.strcmp(_cname, "CArray") == 0: return 1
|
||||
if string.strcmp(_cname, "ArrayT") == 0: return 1
|
||||
if string.strcmp(_cname, "list") == 0: return 1
|
||||
return 0
|
||||
|
||||
def _is_non_class_type_name(self, pool: memhub.MemBuddy*, name: str) -> int:
|
||||
"""Whether a type name has no class ownership (CTypedef/builtin/generic param)."""
|
||||
"""Whether a type name has no class ownership (TypedefT/builtin/generic param)."""
|
||||
if name is None or name[0] == '\0': return 0
|
||||
if self.Gen.TypeGenCType is not None:
|
||||
_ts_nc: str = self.Gen.TypeGenCType.lookup_ctypedef(name)
|
||||
@@ -266,17 +266,17 @@ novirtual class TypeGenResolve(TypeGenBase.TypeGenBase):
|
||||
if name[0] >= 'A' and name[0] <= 'Z': return 1
|
||||
return 0
|
||||
|
||||
# _is_ptr_marker_text - whether text is the builtin pointer marker (CPtr).
|
||||
# _is_ptr_marker_text - whether text is the builtin pointer marker (PtrT).
|
||||
def _is_ptr_marker_text(self, txt: str) -> int:
|
||||
"""Whether txt is a bare pointer marker (one pointer layer): 1=yes / 0=no."""
|
||||
if txt is None: return 0
|
||||
if string.strcmp(txt, "CPtr") == 0: return 1
|
||||
if string.strcmp(txt, "PtrT") == 0: return 1
|
||||
return 0
|
||||
|
||||
# _is_ptr_subscript_container_text - whether text is a pointer subscript container name
|
||||
# (rawptr / CPtr); subscript forms then fold to `T*`.
|
||||
# (rawptr / PtrT); subscript forms then fold to `T*`.
|
||||
def _is_ptr_subscript_container_text(self, txt: str) -> int:
|
||||
"""Whether txt names a pointer subscript container (rawptr/CPtr): 1=yes / 0=no."""
|
||||
"""Whether txt names a pointer subscript container (rawptr/PtrT): 1=yes / 0=no."""
|
||||
if txt is None: return 0
|
||||
if self._is_ptr_marker_text(txt) != 0: return 1
|
||||
if string.strcmp(txt, "rawptr") == 0: return 1
|
||||
@@ -312,7 +312,7 @@ novirtual class TypeGenResolve(TypeGenBase.TypeGenBase):
|
||||
if seg_len == 0: return type_name
|
||||
if string.strchr(type_name, '|') is not None: return None
|
||||
# ---- 1.5 nested generic args "Name[A,B]...": split inner args on top-level ',',
|
||||
# normalize each; t.CArray / list keep their count arg verbatim.
|
||||
# normalize each; t.ArrayT / list keep their count arg verbatim.
|
||||
lb_idx: sizet = seg_len
|
||||
ld: int = 0
|
||||
for li2 in range(int(seg_len)):
|
||||
@@ -347,7 +347,7 @@ novirtual class TypeGenResolve(TypeGenBase.TypeGenBase):
|
||||
if arg_list_g is not None:
|
||||
arg_start_g: sizet = 0
|
||||
cur_dep_g = 0
|
||||
# The second arg of a fixed-size container is an element count (CDefine
|
||||
# The second arg of a fixed-size container is an element count (DefineT
|
||||
# name / literal / const expr), not a type name, so keep it verbatim.
|
||||
_cntc_g: int = self._is_count_arg_container(container_g)
|
||||
_argi_g: int = 0
|
||||
@@ -374,8 +374,6 @@ novirtual class TypeGenResolve(TypeGenBase.TypeGenBase):
|
||||
else: arg_list_g.append(abuf_g)
|
||||
_argi_g += 1
|
||||
arg_start_g = ci_g + 1
|
||||
# [pointer-marker container normalization] Single-arg `rawptr[T]` /
|
||||
# `t.CPtr[T]` / `CPtr[T]` yields "<arg>*" so it folds with `T*`.
|
||||
if self._is_ptr_subscript_container_text(container_g) != 0 and len(arg_list_g) == 1:
|
||||
_only_arg_g: str = arg_list_g.get(0)
|
||||
_star_cg: str = self._ptr_star_text(pool, _only_arg_g)
|
||||
@@ -514,7 +512,7 @@ novirtual class TypeGenResolve(TypeGenBase.TypeGenBase):
|
||||
if _def_mod is not None and _w_cls_cur is not None:
|
||||
if string.strcmp(_w_cls_cur, type_name) != 0: _w_need_log = 1
|
||||
# Gate: only class names can split into the "{module SHA1}.Class" form, so
|
||||
# exempt CTypedef / builtin scalars / generic params to avoid noise ERRORs.
|
||||
# exempt TypedefT / builtin scalars / generic params to avoid noise ERRORs.
|
||||
if _w_need_log != 0 and _bare_entry is None and self._is_non_class_type_name(pool, type_name) != 0: _w_need_log = 0
|
||||
if _w_need_log != 0:
|
||||
_w_mod: str = "None"
|
||||
@@ -568,6 +566,17 @@ novirtual class TypeGenResolve(TypeGenBase.TypeGenBase):
|
||||
"""Parse a type-name string back into an AST node, trimming spaces around '|' and '.'."""
|
||||
if type_str is None or pool is None: return None
|
||||
|
||||
# Type strings from line scans may keep an inline comment (`rawptr # opaque handle`):
|
||||
# cut at '#' before parsing so the comment can never become part of a type name.
|
||||
hash_p: str = string.strchr(type_str, '#')
|
||||
if hash_p is not None:
|
||||
cut_l: sizet = sizet(uint64(hash_p) - uint64(type_str))
|
||||
cut_buf: str = pool.alloc(cut_l + 1)
|
||||
if cut_buf is not None:
|
||||
string.strncpy(cut_buf, type_str, cut_l)
|
||||
cut_buf[cut_l] = '\0'
|
||||
type_str = cut_buf
|
||||
|
||||
# Skip leading spaces (the right segment after '|' may have them)
|
||||
while type_str[0] == ' ': type_str += 1
|
||||
|
||||
@@ -701,7 +710,7 @@ novirtual class TypeGenResolve(TypeGenBase.TypeGenBase):
|
||||
"""Get the file currently being compiled (used for error attribution)."""
|
||||
return self.CurrentFile
|
||||
def resolve_typedef_or_map(self, pool: memhub.MemBuddy*, name: str, imported_modules: str, from_imports: str, trans: HT.Translator* = None) -> mlirlite.LLVMType*:
|
||||
"""Resolve a name as a CTypedef first, falling back to map_builtin_type."""
|
||||
"""Resolve a name as a TypedefT first, falling back to map_builtin_type."""
|
||||
if name is None: return None
|
||||
if self.Gen.TypeGenCType.CTypedefResolveDepth < TypeGenMixin.MAX_CTYPEDEF_DEPTH:
|
||||
ts_rt: str = self.Gen.TypeGenCType.lookup_ctypedef(name)
|
||||
@@ -714,8 +723,6 @@ novirtual class TypeGenResolve(TypeGenBase.TypeGenBase):
|
||||
self.Gen.TypeGenCType.CTypedefResolveDepth -= 1
|
||||
self.Gen.TypeGenCType._ctypedef_pop_visiting()
|
||||
if ity_rt is not None: return ity_rt
|
||||
# [builtins last match] After the typedef table and map_builtin_type both miss, check
|
||||
# includes/builtins.vp (e.g. rawptr: t.CTypedef = t.CPtr) and parse recursively.
|
||||
btd_r: str = self._lookup_builtins_typedef(pool, name)
|
||||
if btd_r is not None:
|
||||
btd_node_r: ast.AST* = self._parse_type_string(pool, btd_r, ast.ASTCtx.Load)
|
||||
@@ -725,8 +732,6 @@ novirtual class TypeGenResolve(TypeGenBase.TypeGenBase):
|
||||
btd_ty_r: mlirlite.LLVMType* = self.resolve_annotation_type(pool, btd_node_r, imported_modules, from_imports, trans, 1)
|
||||
self.Gen.TypeGenCType.CTypedefResolveDepth -= 1
|
||||
if btd_ty_r is not None: return btd_ty_r
|
||||
# Library typedef fallback: read library sources (e.g. stdint UINT/DWORD) and
|
||||
# register them when _vpi_register_typedef_closure did not inject them in time.
|
||||
lib_ts: str = self._lookup_library_typedef(pool, "stdint", name)
|
||||
if lib_ts is None: lib_ts = self._lookup_library_typedef(pool, "win32.win32base", name)
|
||||
if lib_ts is not None:
|
||||
@@ -737,9 +742,6 @@ novirtual class TypeGenResolve(TypeGenBase.TypeGenBase):
|
||||
lib_ty: mlirlite.LLVMType* = self.resolve_annotation_type(pool, lib_node, imported_modules, from_imports, trans, 1)
|
||||
self.Gen.TypeGenCType.CTypedefResolveDepth -= 1
|
||||
if lib_ty is not None: return lib_ty
|
||||
# Bare Python/C builtin aliases: map_builtin_type only knows the t.CXxx spelling, so
|
||||
# resolve them here to match resolve_annotation_type's bare-name branch. Without
|
||||
# this the two-arg form int(x, t.CPtr) misses _tc_h4 and fails fast as "int".
|
||||
if string.strcmp(name, "str") == 0: return mlirlite.Ptr(pool, mlirlite.Int8(pool))
|
||||
if string.strcmp(name, "bytes") == 0: return mlirlite.Ptr(pool, mlirlite.Int8(pool))
|
||||
if string.strcmp(name, "int") == 0: return mlirlite.Int32(pool)
|
||||
@@ -872,12 +874,17 @@ novirtual class TypeGenResolve(TypeGenBase.TypeGenBase):
|
||||
while name_l > 0 and buf[line_start + name_l - 1] == ' ': name_l -= 1
|
||||
if name_l == 0 or name_l >= 128: return 0
|
||||
rest: str = buf + line_start + j + 1
|
||||
if string.strstr(rest, "CTypedef") is None: return 0
|
||||
if string.strstr(rest, "TypedefT") is None: return 0
|
||||
eq: str = string.strstr(rest, "=")
|
||||
if eq is None: return 0
|
||||
vs = sizet(uint64(eq) - uint64(buf)) - line_start + 1
|
||||
while vs < line_len and (buf[line_start + vs] == ' ' or buf[line_start + vs] == '\t'): vs += 1
|
||||
ve: sizet = line_len
|
||||
# Strip an inline comment first: `typedef DIR = rawptr # opaque handle`.
|
||||
for ci_cm in range(int(vs), int(ve)):
|
||||
if buf[line_start + ci_cm] == '#':
|
||||
ve = ci_cm
|
||||
break
|
||||
while ve > vs and (buf[line_start + ve - 1] == ' ' or buf[line_start + ve - 1] == '\t' or buf[line_start + ve - 1] == '\r'): ve -= 1
|
||||
if ve <= vs: return 0
|
||||
vlen: sizet = ve - vs
|
||||
@@ -888,19 +895,19 @@ novirtual class TypeGenResolve(TypeGenBase.TypeGenBase):
|
||||
ci += 1
|
||||
out_buf[name_l] = '\0'
|
||||
# Normalize a C-style typedef RHS (`const T` / `T*`) into marker-wrapper text:
|
||||
# leading const/volatile become `CConst[` / `CVolatile[`, trailing `*` runs become `CPtr[`,
|
||||
# leading const/volatile become `ConstT[` / `VolatileT[`, trailing `*` runs become `PtrT[`,
|
||||
# and the matching closing brackets are appended after the type text.
|
||||
pos: sizet = name_l + 1
|
||||
k: sizet = vs
|
||||
descr: sizet = 0
|
||||
while True:
|
||||
if k + 6 <= ve and string.strncmp(buf + line_start + k, "const", 5) == 0 and (buf[line_start + k + 5] == ' ' or buf[line_start + k + 5] == '\t'):
|
||||
string.memcpy(out_buf + pos, "CConst[", 7)
|
||||
string.memcpy(out_buf + pos, "ConstT[", 7)
|
||||
pos += 7
|
||||
descr += 1
|
||||
k += 6
|
||||
elif k + 9 <= ve and string.strncmp(buf + line_start + k, "volatile", 8) == 0 and (buf[line_start + k + 8] == ' ' or buf[line_start + k + 8] == '\t'):
|
||||
string.memcpy(out_buf + pos, "CVolatile[", 10)
|
||||
string.memcpy(out_buf + pos, "VolatileT[", 10)
|
||||
pos += 10
|
||||
descr += 1
|
||||
k += 9
|
||||
@@ -913,9 +920,9 @@ novirtual class TypeGenResolve(TypeGenBase.TypeGenBase):
|
||||
ptrs += 1
|
||||
ve2 -= 1
|
||||
while ve2 > k and (buf[line_start + ve2 - 1] == ' ' or buf[line_start + ve2 - 1] == '\t'): ve2 -= 1
|
||||
# Pointer prefixes wrap the value: CPtr[ repeatedly.
|
||||
# Pointer prefixes wrap the value: PtrT[ repeatedly.
|
||||
for pi in range(int(ptrs)):
|
||||
string.memcpy(out_buf + pos, "CPtr[", 5)
|
||||
string.memcpy(out_buf + pos, "PtrT[", 5)
|
||||
pos += 5
|
||||
vlen2: sizet = ve2 - k
|
||||
if vlen2 > 190: vlen2 = 190
|
||||
@@ -936,17 +943,17 @@ novirtual class TypeGenResolve(TypeGenBase.TypeGenBase):
|
||||
out_buf[pos] = '\0'
|
||||
return 1
|
||||
def _lookup_builtins_typedef(self, pool: memhub.MemBuddy*, name: str) -> str:
|
||||
"""Last-match fallback: look up a CTypedef declared in includes/builtins.vp."""
|
||||
"""Last-match fallback: look up a TypedefT declared in includes/builtins.vp."""
|
||||
if name is None: return None
|
||||
# Record the builtins last-match request/result. name is often a caller's
|
||||
# temporary buffer, so copy it via store_builtins_diag to avoid a dangling pointer.
|
||||
self.Gen.TypeGenCType.LastBuiltinsTypedefName = self.Gen.TypeGenCType.store_builtins_diag(0, name)
|
||||
self.Gen.TypeGenCType.LastBuiltinsTypedefResult = None
|
||||
# rawptr is built into the compiler (a reserved word on both TPC/VPC) and takes
|
||||
# priority over any external declaration; always return CPtr.
|
||||
# priority over any external declaration; always return PtrT.
|
||||
if string.strcmp(name, "rawptr") == 0:
|
||||
self.Gen.TypeGenCType.LastBuiltinsTypedefResult = "CPtr"
|
||||
return "CPtr"
|
||||
self.Gen.TypeGenCType.LastBuiltinsTypedefResult = "PtrT"
|
||||
return "PtrT"
|
||||
ts_b: str = self.Gen.TypeGenCType.lookup_ctypedef(name)
|
||||
if ts_b is not None and self.Gen.TypeGenCType.CTypedefFound != 0:
|
||||
# The table value may still be a module-qualified ref (size_t -> stdint.size_t); expand it
|
||||
@@ -976,7 +983,7 @@ novirtual class TypeGenResolve(TypeGenBase.TypeGenBase):
|
||||
stdlib.free(b_buf)
|
||||
return None
|
||||
if b_read < LONG(BUF_B): b_buf[sizet(b_read)] = 0
|
||||
# Scan line-by-line and register all builtins "NAME: t.CTypedef = TYPE" entries at
|
||||
# Scan line-by-line and register all builtins "NAME: t.TypedefT = TYPE" entries at
|
||||
# once; existing entries (module/import-injected) take priority and are not overwritten.
|
||||
b_total: sizet = string.strlen(b_buf)
|
||||
# Pre-scan import lines to register aliases (import stdint as __stdint), so later
|
||||
@@ -1029,7 +1036,7 @@ novirtual class TypeGenResolve(TypeGenBase.TypeGenBase):
|
||||
if b_i == b_total or b_buf[b_i] == '\n':
|
||||
b_line_len: sizet = b_i - b_line_start
|
||||
if b_line_len > 0 and b_td_buf is not None:
|
||||
# Parse both equivalent forms (`typedef N = V` and `N: t.CTypedef = V`);
|
||||
# Parse both equivalent forms (`typedef N = V` and `N: t.TypedefT = V`);
|
||||
# builtins.vp now uses the sugar form, so accept both.
|
||||
if self._parse_typedef_line(b_td_buf, b_buf, b_line_start, b_line_len) != 0:
|
||||
b_nb: str = b_td_buf
|
||||
@@ -1116,14 +1123,14 @@ novirtual class TypeGenResolve(TypeGenBase.TypeGenBase):
|
||||
if v is None: return None
|
||||
return rawptr(v)
|
||||
def _lookup_library_typedef(self, pool: memhub.MemBuddy*, mod: str, attr: str) -> str:
|
||||
"""Look up "attr: t.CTypedef = TYPE" in a library module source, returning TYPE or None."""
|
||||
"""Look up "attr: t.TypedefT = TYPE" in a library module source, returning TYPE or None."""
|
||||
if mod is None or attr is None: return None
|
||||
# Restore the real module name from a builtins import alias (__stdint -> stdint);
|
||||
# without this, SHA1 lookup and path resolution all miss.
|
||||
m_real: str = self._resolve_import_alias(mod)
|
||||
if m_real is None: m_real = mod
|
||||
# [gate hit] This library module was already scanned: query the persistent cache
|
||||
# only, but still register hits into the current file's CTypedef table.
|
||||
# only, but still register hits into the current file's TypedefT table.
|
||||
if self.LibTypedefScannedMods is not None:
|
||||
if self.LibTypedefScannedMods.__contains__(m_real) != 0:
|
||||
ts_hit: str = self._lookup_lib_typedef_cache(m_real, attr)
|
||||
@@ -1160,7 +1167,7 @@ novirtual class TypeGenResolve(TypeGenBase.TypeGenBase):
|
||||
if m_i == m_total or m_buf[m_i] == '\n':
|
||||
m_line_len: sizet = m_i - m_ls
|
||||
if m_line_len > 0 and m_td_buf is not None:
|
||||
# Parse both equivalent forms (`typedef N = V` and `N: t.CTypedef = V`),
|
||||
# Parse both equivalent forms (`typedef N = V` and `N: t.TypedefT = V`),
|
||||
# both of which occur in library module sources.
|
||||
if self._parse_typedef_line(m_td_buf, m_buf, m_ls, m_line_len) != 0:
|
||||
m_nb: str = m_td_buf
|
||||
@@ -1294,6 +1301,10 @@ novirtual class TypeGenResolve(TypeGenBase.TypeGenBase):
|
||||
"""Map a builtin type name to its LLVMType. Note: str = i8* = bytes."""
|
||||
if type_name is None: return None
|
||||
|
||||
# Bare pointer marker: `rawptr` (source) and `PtrT` (internal carrier for `T*`)
|
||||
# both mean one opaque pointer layer, i.e. i8*.
|
||||
if string.strcmp(type_name, "rawptr") == 0 or string.strcmp(type_name, "PtrT") == 0: return mlirlite.Ptr(pool, mlirlite.Int8(pool))
|
||||
|
||||
# Builtin short names (no import / typedef required)
|
||||
if string.strcmp(type_name, "sizet") == 0 or string.strcmp(type_name, "size_t") == 0: return mlirlite.Int64(pool)
|
||||
elif string.strcmp(type_name, "int8") == 0 or string.strcmp(type_name, "intfast8") == 0 or string.strcmp(type_name, "intleast8") == 0: return mlirlite.Int8(pool)
|
||||
@@ -1322,85 +1333,20 @@ novirtual class TypeGenResolve(TypeGenBase.TypeGenBase):
|
||||
elif string.strcmp(type_name, "char8") == 0: return mlirlite.Int8(pool)
|
||||
elif string.strcmp(type_name, "char16") == 0 or string.strcmp(type_name, "wchar") == 0: return mlirlite.Int16(pool)
|
||||
elif string.strcmp(type_name, "char32") == 0: return mlirlite.Int32(pool)
|
||||
# Basic integer types
|
||||
if string.strcmp(type_name, "CInt") == 0: return mlirlite.Int32(pool)
|
||||
elif string.strcmp(type_name, "CChar") == 0: return mlirlite.Int8(pool)
|
||||
elif string.strcmp(type_name, "CShort") == 0: return mlirlite.Int16(pool)
|
||||
elif string.strcmp(type_name, "CLong") == 0:
|
||||
# long width depends on target: Windows LLP64 = 32-bit (as TPC), LP64 = 64-bit
|
||||
return self._long_llvm_ty(pool)
|
||||
elif string.strcmp(type_name, "CLongLong") == 0: return mlirlite.Int64(pool)
|
||||
# Exact-width signed types
|
||||
elif string.strcmp(type_name, "CInt8T") == 0: return mlirlite.Int8(pool)
|
||||
elif string.strcmp(type_name, "CInt16T") == 0: return mlirlite.Int16(pool)
|
||||
elif string.strcmp(type_name, "CInt32T") == 0: return mlirlite.Int32(pool)
|
||||
elif string.strcmp(type_name, "CInt64T") == 0: return mlirlite.Int64(pool)
|
||||
# Exact-width unsigned types
|
||||
elif string.strcmp(type_name, "CUInt8T") == 0: return mlirlite.Int8(pool)
|
||||
elif string.strcmp(type_name, "CUInt16T") == 0: return mlirlite.Int16(pool)
|
||||
elif string.strcmp(type_name, "CUInt32T") == 0: return mlirlite.Int32(pool)
|
||||
elif string.strcmp(type_name, "CUInt64T") == 0: return mlirlite.Int64(pool)
|
||||
# Floating-point types
|
||||
elif string.strcmp(type_name, "CFloat") == 0: return mlirlite.Float(pool)
|
||||
elif string.strcmp(type_name, "CDouble") == 0: return mlirlite.Double(pool)
|
||||
# Exact-width float types (t.py float_map: 8/16->half, 32->float, 64->double, 128->fp128)
|
||||
elif string.strcmp(type_name, "CFloat8T") == 0: return mlirlite.Half(pool)
|
||||
elif string.strcmp(type_name, "CFloat16T") == 0: return mlirlite.Half(pool)
|
||||
elif string.strcmp(type_name, "CFloat32T") == 0: return mlirlite.Float(pool)
|
||||
elif string.strcmp(type_name, "CFloat64T") == 0: return mlirlite.Double(pool)
|
||||
elif string.strcmp(type_name, "CFloat128T") == 0: return mlirlite.FP128(pool)
|
||||
# Exact-width char types (C11 char8_t/char16_t/char32_t, Windows wchar_t = 16-bit)
|
||||
elif string.strcmp(type_name, "CChar8T") == 0: return mlirlite.Int8(pool)
|
||||
elif string.strcmp(type_name, "CChar16T") == 0 or string.strcmp(type_name, "CWCharT") == 0: return mlirlite.Int16(pool)
|
||||
elif string.strcmp(type_name, "CChar32T") == 0: return mlirlite.Int32(pool)
|
||||
# Pointer / void: t.CPtr = i8*, str = i8* = str = bytes
|
||||
elif string.strcmp(type_name, "CPtr") == 0: return mlirlite.Ptr(pool, mlirlite.Int8(pool))
|
||||
# Unsigned function pointer: bare t.Callable -> i8* (same LLVM repr as the full
|
||||
# t.Callable[[A, B], Ret]); no signature means it is only for taking addresses.
|
||||
elif string.strcmp(type_name, "Callable") == 0: return mlirlite.Ptr(pool, mlirlite.Int8(pool))
|
||||
|
||||
# 存疑
|
||||
|
||||
# Lowercase alias callable: equivalent to t.Callable in annotations.
|
||||
elif string.strcmp(type_name, "callable") == 0: return mlirlite.Ptr(pool, mlirlite.Int8(pool))
|
||||
# Bare void is an alias of t.CVoid: alone it is LLVM void; `void*` is merged to i8*
|
||||
# by the BinOp branch below.
|
||||
elif string.strcmp(type_name, "CVoid") == 0 or string.strcmp(type_name, "void") == 0: return mlirlite.Void(pool)
|
||||
# CDefine: compile-time constant marker, backed by i32 (TPC-aligned). Global/field
|
||||
# CDefine annotations are short-circuited earlier, so only return-position reaches here.
|
||||
elif string.strcmp(type_name, "CDefine") == 0: return mlirlite.Int32(pool)
|
||||
# CAutoPtr: unconditional-address-of parameter marker; maps to i8* at the LLVM level
|
||||
# (t.CNeedPtr was removed in favor of the `&T` / `&temp T` parameter annotation).
|
||||
elif string.strcmp(type_name, "CAutoPtr") == 0: return mlirlite.Ptr(pool, mlirlite.Int8(pool))
|
||||
# Unsigned modifiers
|
||||
elif string.strcmp(type_name, "CUnsigned") == 0: return mlirlite.Int32(pool)
|
||||
elif string.strcmp(type_name, "CUnsignedInt") == 0: return mlirlite.Int32(pool)
|
||||
elif string.strcmp(type_name, "CUnsignedChar") == 0: return mlirlite.Int8(pool)
|
||||
elif string.strcmp(type_name, "CUnsignedShort") == 0: return mlirlite.Int16(pool)
|
||||
elif string.strcmp(type_name, "CUnsignedLong") == 0:
|
||||
# unsigned long as above: Windows LLP64 = 32-bit (ULONG/DWORD width), LP64 = 64-bit
|
||||
return self._long_llvm_ty(pool)
|
||||
elif string.strcmp(type_name, "CUnsignedLongLong") == 0: return mlirlite.Int64(pool)
|
||||
# size_t / ptr types
|
||||
elif string.strcmp(type_name, "CSizeT") == 0: return mlirlite.Int64(pool)
|
||||
elif string.strcmp(type_name, "CIntPtrT") == 0: return mlirlite.Int64(pool)
|
||||
elif string.strcmp(type_name, "CUIntPtrT") == 0: return mlirlite.Int64(pool)
|
||||
# bool
|
||||
elif string.strcmp(type_name, "CBool") == 0:
|
||||
# bool = t.CBool = i1 (1-bit LLVM integer, same as C _Bool)
|
||||
return mlirlite.Int1(pool)
|
||||
# ptrdiff_t
|
||||
elif string.strcmp(type_name, "CPtrDiffT") == 0: return mlirlite.Int64(pool)
|
||||
# Byte-order markers written alone (no real type on the other side) degrade to CInt,
|
||||
# matching TPC: `t.BigEndian` alone -> BaseType=CInt() + ByteOrder='big'.
|
||||
elif string.strcmp(type_name, "BigEndian") == 0: return mlirlite.Int32(pool)
|
||||
elif string.strcmp(type_name, "LittleEndian") == 0: return mlirlite.Int32(pool)
|
||||
|
||||
# stdint.vp aliases (INT/BYTE/DWORD/int8_t/size_t/_Bool ...) now come from its
|
||||
# CTypedef declarations; lowercase bare char/float/double stay here, int/bool use Name.
|
||||
# TypedefT declarations; lowercase bare char/float/double stay here, int/bool use Name.
|
||||
elif string.strcmp(type_name, "char") == 0: return mlirlite.Int8(pool)
|
||||
elif string.strcmp(type_name, "float") == 0: return mlirlite.Float(pool)
|
||||
elif string.strcmp(type_name, "double") == 0: return mlirlite.Double(pool)
|
||||
return None
|
||||
def _is_builtin_marker(self, node: ast.AST*, attr_name: str, imported_modules: str) -> int:
|
||||
"""Whether a node is the bare builtin marker name (e.g. CPtr / CRef)."""
|
||||
"""Whether a node is the bare builtin marker name (e.g. PtrT / RefT)."""
|
||||
if node is None or attr_name is None: return 0
|
||||
if node.kind() != ast.ASTKind.Name: return 0
|
||||
nm: ast.Name* = ast.Name*(node)
|
||||
@@ -1408,16 +1354,16 @@ novirtual class TypeGenResolve(TypeGenBase.TypeGenBase):
|
||||
if string.strcmp(nm.id, attr_name) != 0: return 0
|
||||
return 1
|
||||
def _is_cptr_marker(self, node: ast.AST*, imported_modules: str) -> int:
|
||||
"""Whether a node is a CPtr pointer marker: 1=yes / 0=no."""
|
||||
if self._is_builtin_marker(node, "CPtr", imported_modules) != 0: return 1
|
||||
"""Whether a node is a PtrT pointer marker: 1=yes / 0=no."""
|
||||
if self._is_builtin_marker(node, "PtrT", imported_modules) != 0: return 1
|
||||
if node is not None and node.kind() == ast.ASTKind.Name:
|
||||
nm: ast.Name* = ast.Name*(node)
|
||||
if nm.id is not None and string.strcmp(nm.id, "CPtr") == 0: return 1
|
||||
if nm.id is not None and string.strcmp(nm.id, "PtrT") == 0: return 1
|
||||
return 0
|
||||
def _is_ref_marker(self, node: ast.AST*, imported_modules: str) -> int:
|
||||
"""Whether a node is a CRef/CTempRef value-reference marker: 1=yes / 0=no."""
|
||||
if self._is_builtin_marker(node, "CRef", imported_modules) != 0: return 1
|
||||
if self._is_builtin_marker(node, "CTempRef", imported_modules) != 0: return 1
|
||||
"""Whether a node is a RefT/TempRefT value-reference marker: 1=yes / 0=no."""
|
||||
if self._is_builtin_marker(node, "RefT", imported_modules) != 0: return 1
|
||||
if self._is_builtin_marker(node, "TempRefT", imported_modules) != 0: return 1
|
||||
return 0
|
||||
def _is_byteorder_marker(self, node: ast.AST*, imported_modules: str) -> int:
|
||||
"""Whether a node is a byte-order marker: 0=none / 1=BigEndian / 2=LittleEndian."""
|
||||
@@ -1443,7 +1389,7 @@ novirtual class TypeGenResolve(TypeGenBase.TypeGenBase):
|
||||
if ei >= 3 and tr[ei - 3] == '_' and tr[ei - 2] == 'b' and tr[ei - 1] == 'e': return 1
|
||||
return 0
|
||||
def _ref_marker_name_mode(self, node: ast.AST*) -> int:
|
||||
"""Syntactic value-reference marker test (no import context): 0=none / 1=CRef / 2=CTempRef."""
|
||||
"""Syntactic value-reference marker test (no import context): 0=none / 1=RefT / 2=TempRefT."""
|
||||
if node is None: return 0
|
||||
if node.kind() != ast.ASTKind.Subscript: return 0
|
||||
sub_r: ast.Subscript* = ast.Subscript*(node)
|
||||
@@ -1451,8 +1397,8 @@ novirtual class TypeGenResolve(TypeGenBase.TypeGenBase):
|
||||
nm_node_r: ast.Name* = ast.Name*(sub_r.value)
|
||||
nm: str = nm_node_r.id
|
||||
if nm is None: return 0
|
||||
if string.strcmp(nm, "CRef") == 0: return 1
|
||||
if string.strcmp(nm, "CTempRef") == 0: return 2
|
||||
if string.strcmp(nm, "RefT") == 0: return 1
|
||||
if string.strcmp(nm, "TempRefT") == 0: return 2
|
||||
return 0
|
||||
def ref_param_mode(self, node: ast.AST*, imported_modules: str) -> int:
|
||||
"""Value-reference mode of a parameter annotation: 0=none / 1=ref (&T) / 2=temp (&temp T)."""
|
||||
@@ -1463,12 +1409,12 @@ novirtual class TypeGenResolve(TypeGenBase.TypeGenBase):
|
||||
self.Gen.TypeGenErr.fatal_error(node, f"value-reference parameter annotation '&T' / '&temp T' is only allowed in function parameter position, not for {where}", "TypeError")
|
||||
return 1
|
||||
def _is_cptr_subscript(self, node: ast.AST*) -> int:
|
||||
"""Whether a node is a CPtr[T] subscript pointer type (e.g. CPtr[CPtr])."""
|
||||
"""Whether a node is a PtrT[T] subscript pointer type (e.g. PtrT[PtrT])."""
|
||||
if node is None or node.kind() != ast.ASTKind.Subscript: return 0
|
||||
sub: ast.Subscript* = ast.Subscript*(node)
|
||||
if sub.value is None or sub.value.kind() != ast.ASTKind.Name: return 0
|
||||
nm: ast.Name* = ast.Name*(sub.value)
|
||||
if nm.id is None or string.strcmp(nm.id, "CPtr") != 0: return 0
|
||||
if nm.id is None or string.strcmp(nm.id, "PtrT") != 0: return 0
|
||||
return 1
|
||||
def _is_cattr_marker(self, node: ast.AST*) -> int:
|
||||
"""Whether a node is an `attr.<name>` builtin marker (including the call form)."""
|
||||
@@ -1497,12 +1443,12 @@ novirtual class TypeGenResolve(TypeGenBase.TypeGenBase):
|
||||
sub_v: ast.Subscript* = ast.Subscript*(node)
|
||||
if sub_v.value is not None and sub_v.value.kind() == ast.ASTKind.Name:
|
||||
sw_nm: ast.Name* = ast.Name*(sub_v.value)
|
||||
if sw_nm.id is not None and (string.strcmp(sw_nm.id, "CConst") == 0 or string.strcmp(sw_nm.id, "CVolatile") == 0 \
|
||||
if sw_nm.id is not None and (string.strcmp(sw_nm.id, "ConstT") == 0 or string.strcmp(sw_nm.id, "VolatileT") == 0 \
|
||||
or string.strcmp(sw_nm.id, "BigEndian") == 0 or string.strcmp(sw_nm.id, "LittleEndian") == 0 \
|
||||
or string.strcmp(sw_nm.id, "CStatic") == 0 or string.strcmp(sw_nm.id, "CExport") == 0 \
|
||||
or string.strcmp(sw_nm.id, "CExtern") == 0 or string.strcmp(sw_nm.id, "State") == 0 \
|
||||
or string.strcmp(sw_nm.id, "CDefine") == 0 or string.strcmp(sw_nm.id, "CRef") == 0 \
|
||||
or string.strcmp(sw_nm.id, "CTempRef") == 0 or string.strcmp(sw_nm.id, "CAutoPtr") == 0):
|
||||
or string.strcmp(sw_nm.id, "StaticT") == 0 or string.strcmp(sw_nm.id, "ExportT") == 0 \
|
||||
or string.strcmp(sw_nm.id, "ExternT") == 0 or string.strcmp(sw_nm.id, "State") == 0 \
|
||||
or string.strcmp(sw_nm.id, "DefineT") == 0 or string.strcmp(sw_nm.id, "RefT") == 0 \
|
||||
or string.strcmp(sw_nm.id, "TempRefT") == 0 or string.strcmp(sw_nm.id, "AutoPtrT") == 0):
|
||||
if sub_v.slice is not None: return self._is_cvoid_marker(sub_v.slice, imported_modules)
|
||||
return 0
|
||||
def annotation_to_str(self, annot: ast.AST*) -> str:
|
||||
@@ -1535,14 +1481,14 @@ novirtual class TypeGenResolve(TypeGenBase.TypeGenBase):
|
||||
return buf
|
||||
return at.attr
|
||||
|
||||
# Subscript node: list[...], CArray[...], CPtr[...] (rendered as T*), marker wrappers.
|
||||
# Subscript node: list[...], ArrayT[...], PtrT[...] (rendered as T*), marker wrappers.
|
||||
if k == ast.ASTKind.Subscript:
|
||||
sub: ast.Subscript* = ast.Subscript*(annot)
|
||||
if self._is_cattr_marker(sub.value) != 0: return self.annotation_to_str(sub.slice)
|
||||
if sub.value.kind() == ast.ASTKind.Name:
|
||||
sv_nm: ast.Name* = ast.Name*(sub.value)
|
||||
if sv_nm.id is not None:
|
||||
if string.strcmp(sv_nm.id, "CPtr") == 0:
|
||||
if string.strcmp(sv_nm.id, "PtrT") == 0:
|
||||
inner_cp: str = self.annotation_to_str(sub.slice)
|
||||
if inner_cp is not None:
|
||||
cp_buf: str = stdlib.malloc(string.strlen(inner_cp) + 2)
|
||||
@@ -1550,20 +1496,20 @@ novirtual class TypeGenResolve(TypeGenBase.TypeGenBase):
|
||||
viperlib.snprintf(cp_buf, string.strlen(inner_cp) + 2, "%s*", inner_cp)
|
||||
return cp_buf
|
||||
return "<subscript>"
|
||||
if string.strcmp(sv_nm.id, "CRef") == 0 or string.strcmp(sv_nm.id, "CTempRef") == 0:
|
||||
if string.strcmp(sv_nm.id, "RefT") == 0 or string.strcmp(sv_nm.id, "TempRefT") == 0:
|
||||
inner_rf: str = self.annotation_to_str(sub.slice)
|
||||
if inner_rf is not None:
|
||||
rf_prefix: str = "&temp " if string.strcmp(sv_nm.id, "CTempRef") == 0 else "&"
|
||||
rf_prefix: str = "&temp " if string.strcmp(sv_nm.id, "TempRefT") == 0 else "&"
|
||||
rf_buf: str = stdlib.malloc(string.strlen(inner_rf) + string.strlen(rf_prefix) + 1)
|
||||
if rf_buf is not None:
|
||||
viperlib.snprintf(rf_buf, string.strlen(inner_rf) + string.strlen(rf_prefix) + 1, "%s%s", rf_prefix, inner_rf)
|
||||
return rf_buf
|
||||
return "<subscript>"
|
||||
if string.strcmp(sv_nm.id, "CConst") == 0 or string.strcmp(sv_nm.id, "CVolatile") == 0 \
|
||||
if string.strcmp(sv_nm.id, "ConstT") == 0 or string.strcmp(sv_nm.id, "VolatileT") == 0 \
|
||||
or string.strcmp(sv_nm.id, "BigEndian") == 0 or string.strcmp(sv_nm.id, "LittleEndian") == 0 \
|
||||
or string.strcmp(sv_nm.id, "CStatic") == 0 or string.strcmp(sv_nm.id, "CExport") == 0 \
|
||||
or string.strcmp(sv_nm.id, "CExtern") == 0 or string.strcmp(sv_nm.id, "State") == 0 \
|
||||
or string.strcmp(sv_nm.id, "CDefine") == 0 or string.strcmp(sv_nm.id, "CAutoPtr") == 0:
|
||||
or string.strcmp(sv_nm.id, "StaticT") == 0 or string.strcmp(sv_nm.id, "ExportT") == 0 \
|
||||
or string.strcmp(sv_nm.id, "ExternT") == 0 or string.strcmp(sv_nm.id, "State") == 0 \
|
||||
or string.strcmp(sv_nm.id, "DefineT") == 0 or string.strcmp(sv_nm.id, "AutoPtrT") == 0:
|
||||
return self.annotation_to_str(sub.slice)
|
||||
base: str = self.annotation_to_str(sub.value)
|
||||
if base is not None:
|
||||
@@ -1578,11 +1524,11 @@ novirtual class TypeGenResolve(TypeGenBase.TypeGenBase):
|
||||
def _is_storage_class_qualifier(self, name: str) -> int:
|
||||
"""Whether a name is a storage-class qualifier macro (not a type): 1=yes / 0=no."""
|
||||
if name is None: return 0
|
||||
if string.strcmp(name, "CStatic") == 0: return 1
|
||||
if string.strcmp(name, "CVolatile") == 0: return 1
|
||||
if string.strcmp(name, "CExport") == 0: return 1
|
||||
if string.strcmp(name, "CExtern") == 0: return 1
|
||||
if string.strcmp(name, "CInline") == 0: return 1
|
||||
if string.strcmp(name, "StaticT") == 0: return 1
|
||||
if string.strcmp(name, "VolatileT") == 0: return 1
|
||||
if string.strcmp(name, "ExportT") == 0: return 1
|
||||
if string.strcmp(name, "ExternT") == 0: return 1
|
||||
if string.strcmp(name, "InlineT") == 0: return 1
|
||||
# Byte-order markers are decorators, not types: never let them be extracted as a
|
||||
# class name (which would make the field degrade to an unknown struct lookup).
|
||||
if string.strcmp(name, "BigEndian") == 0: return 1
|
||||
@@ -1667,17 +1613,17 @@ novirtual class TypeGenResolve(TypeGenBase.TypeGenBase):
|
||||
if sub.value.kind() == ast.ASTKind.Name:
|
||||
rp_nm: ast.Name* = ast.Name*(sub.value)
|
||||
if rp_nm.id is not None:
|
||||
# rawptr[T] / CPtr[T]: AnnotClassName takes the pointee class T.
|
||||
if string.strcmp(rp_nm.id, "rawptr") == 0 or string.strcmp(rp_nm.id, "CPtr") == 0:
|
||||
# rawptr[T] / PtrT[T]: AnnotClassName takes the pointee class T.
|
||||
if string.strcmp(rp_nm.id, "rawptr") == 0 or string.strcmp(rp_nm.id, "PtrT") == 0:
|
||||
if sub.slice is not None: return self.extract_class_name_from_annotation(sub.slice, imported_modules, pool, from_imports)
|
||||
return None
|
||||
# Descriptor / storage / value-reference marker wrappers: unwrap to the type.
|
||||
if string.strcmp(rp_nm.id, "CConst") == 0 or string.strcmp(rp_nm.id, "CVolatile") == 0 \
|
||||
if string.strcmp(rp_nm.id, "ConstT") == 0 or string.strcmp(rp_nm.id, "VolatileT") == 0 \
|
||||
or string.strcmp(rp_nm.id, "BigEndian") == 0 or string.strcmp(rp_nm.id, "LittleEndian") == 0 \
|
||||
or string.strcmp(rp_nm.id, "CStatic") == 0 or string.strcmp(rp_nm.id, "CExport") == 0 \
|
||||
or string.strcmp(rp_nm.id, "CExtern") == 0 or string.strcmp(rp_nm.id, "State") == 0 \
|
||||
or string.strcmp(rp_nm.id, "CDefine") == 0 or string.strcmp(rp_nm.id, "CRef") == 0 \
|
||||
or string.strcmp(rp_nm.id, "CTempRef") == 0 or string.strcmp(rp_nm.id, "CAutoPtr") == 0:
|
||||
or string.strcmp(rp_nm.id, "StaticT") == 0 or string.strcmp(rp_nm.id, "ExportT") == 0 \
|
||||
or string.strcmp(rp_nm.id, "ExternT") == 0 or string.strcmp(rp_nm.id, "State") == 0 \
|
||||
or string.strcmp(rp_nm.id, "DefineT") == 0 or string.strcmp(rp_nm.id, "RefT") == 0 \
|
||||
or string.strcmp(rp_nm.id, "TempRefT") == 0 or string.strcmp(rp_nm.id, "AutoPtrT") == 0:
|
||||
if sub.slice is not None: return self.extract_class_name_from_annotation(sub.slice, imported_modules, pool, from_imports)
|
||||
return None
|
||||
# When recursing into base, from_imports must be passed down too, else the mangled
|
||||
@@ -1695,7 +1641,7 @@ novirtual class TypeGenResolve(TypeGenBase.TypeGenBase):
|
||||
if sub_tup.elts is not None:
|
||||
tup_elts_sn: list[ast.AST*]* = sub_tup.elts
|
||||
tn_sn: sizet = len(tup_elts_sn)
|
||||
# The 2nd arg of a fixed-size container (t.CArray / list) is an element count,
|
||||
# The 2nd arg of a fixed-size container (t.ArrayT / list) is an element count,
|
||||
# not a type name, so keep it verbatim (this is a separate entry from rule 1.5).
|
||||
_cntc_sn: int = self._is_count_arg_container(base_name)
|
||||
# Compute the total length
|
||||
@@ -1899,6 +1845,18 @@ novirtual class TypeGenResolve(TypeGenBase.TypeGenBase):
|
||||
if self.Gen.ClassGenGeneric._find_generic_template(base_nm) is None: return 0
|
||||
self.Gen.TypeGenErr.fatal_error(node, f"The annotation of generic class '{base_nm}' must carry type arguments (e.g. {base_nm}[T]); " f"a bare name is treated as a bare generic base class without arguments, and once the element type is lost, method calls specialize on the wrong element " f"type and truncate pointers. Correct examples: vector.Vector[CInt], GSList[G5Node].", "TypeError")
|
||||
return 0
|
||||
|
||||
# _resolve_enum_base_ty - 解析枚举基类型(EnumTable 的键是 (定义模块 SHA1, 名字))
|
||||
def _resolve_enum_base_ty(self, name: str, mod_sha1: str, trans: HT.Translator*) -> mlirlite.LLVMType*:
|
||||
"""按 (名字, 模块 SHA1) 解析枚举基类型;模块未知时退化为"同名唯一 / 同名基类型一致"。"""
|
||||
if name is None: return None
|
||||
if mod_sha1 is not None:
|
||||
ty_m: mlirlite.LLVMType* = self.Gen.StmtGenDecl.get_enum_base_type_in_module(name, mod_sha1)
|
||||
if ty_m is not None: return ty_m
|
||||
ctx_sha1: str = None
|
||||
if trans is not None: ctx_sha1 = trans.ModuleSha1
|
||||
return self.Gen.StmtGenDecl.get_enum_base_type_ctx(name, ctx_sha1)
|
||||
|
||||
def resolve_annotation_type(self, pool: memhub.MemBuddy*, node: ast.AST*, imported_modules: str, from_imports: str, trans: HT.Translator* = None, depth: int = 0) -> mlirlite.LLVMType*:
|
||||
"""Resolve a type-annotation node into an LLVMType."""
|
||||
if node is None: return None
|
||||
@@ -1951,7 +1909,7 @@ novirtual class TypeGenResolve(TypeGenBase.TypeGenBase):
|
||||
for gp_j in range(int(gp_n)):
|
||||
gp_pn: str = tpn_g.get(gp_j)
|
||||
# Token-boundary match (not strstr): the param name must be an
|
||||
# independent identifier, else chars inside AST/CPtr misjudge as a param.
|
||||
# independent identifier, else chars inside AST/PtrT misjudge as a param.
|
||||
if gp_pn is not None and self._str_contains_ident(gp_ta, gp_pn) != 0:
|
||||
polluted = 1
|
||||
break
|
||||
@@ -1985,6 +1943,7 @@ novirtual class TypeGenResolve(TypeGenBase.TypeGenBase):
|
||||
# str = bytes = str = i8*
|
||||
if string.strcmp(nm.id, "str") == 0: return mlirlite.Ptr(pool, mlirlite.Int8(pool))
|
||||
elif string.strcmp(nm.id, "None") == 0: return mlirlite.Void(pool)
|
||||
elif string.strcmp(nm.id, "void") == 0: return mlirlite.Void(pool)
|
||||
elif string.strcmp(nm.id, "int") == 0: return mlirlite.Int32(pool)
|
||||
elif string.strcmp(nm.id, "char") == 0:
|
||||
# char = i8 (consistent with CChar/char in map_builtin_type)
|
||||
@@ -1996,11 +1955,24 @@ novirtual class TypeGenResolve(TypeGenBase.TypeGenBase):
|
||||
# Bare float types: float = t.CFloat = f32, double = t.CDouble = f64
|
||||
elif string.strcmp(nm.id, "float") == 0: return mlirlite.Float(pool)
|
||||
elif string.strcmp(nm.id, "double") == 0: return mlirlite.Double(pool)
|
||||
# Enum base type, needed by the struct branches below (forward-placeholder guard).
|
||||
# EnumTable 的键是 (定义模块 SHA1, 名字):先按本文件模块精确解析,再退化到
|
||||
# "同名唯一 / 同名基类型一致";同名枚举(win32.fileio 与 fat32_types 都有 FRESULT)
|
||||
# 因此不会互相覆盖成员表。
|
||||
enum_base_rn: mlirlite.LLVMType* = self._resolve_enum_base_ty(nm.id, None, trans)
|
||||
# class name: return the registered StructType itself (no Ptr wrapping); prefer the
|
||||
# current module's class before falling back to a global bare-name lookup.
|
||||
if trans is not None and trans.ModuleSha1 is not None:
|
||||
own_entry: GenTypes.StructEntry* = self.Gen.StructGen.find_struct_by_module(nm.id, trans.ModuleSha1)
|
||||
if own_entry is not None and own_entry.Ty is not None:
|
||||
# StructTable 是**进程级**的(init_struct_table 只建一次,reset_visible_structs
|
||||
# 不清 StructCount):别的文件在本模块的 `enum FRESULT:` 被翻译之前引用了
|
||||
# `types.FRESULT`,会在本模块 SHA1 名下留下一个 opaque 的**前向**占位结构体。
|
||||
# 若这里返回它,槽位就成了结构体,而结构体右值取的是自身地址(指针),
|
||||
# `res: FRESULT` 于是静默变成指针,`self.last_error = res` 报
|
||||
# "implicit pointer-to-integer conversion"。故:已注册的枚举压过前向占位;
|
||||
# 真正的定义(IsForwardDecl == 0)仍然压过枚举。
|
||||
if own_entry.IsForwardDecl != 0 and enum_base_rn is not None: return enum_base_rn
|
||||
# A bare class name is always the prototype struct (type/layout), not
|
||||
# auto-promoted to Ptr(struct) by HasNew; heap refs must be written as `T*`.
|
||||
return own_entry.Ty
|
||||
@@ -2025,22 +1997,29 @@ novirtual class TypeGenResolve(TypeGenBase.TypeGenBase):
|
||||
if fi_sha1 is not None:
|
||||
fi_entry: GenTypes.StructEntry* = self.Gen.StructGen.find_struct_by_module(nm.id, fi_sha1)
|
||||
if fi_entry is not None and fi_entry.Ty is not None:
|
||||
# Same forward-placeholder guard: a registered enum wins over a stub
|
||||
# placeholder created before the enum's module was translated.
|
||||
if fi_entry.IsForwardDecl != 0 and enum_base_rn is not None: return enum_base_rn
|
||||
# Bare class name is always the prototype (see above)
|
||||
return fi_entry.Ty
|
||||
if self.Gen.StructGen.is_struct_visible(nm.id) != 0:
|
||||
# Same guard as above: a forward placeholder (possibly keyed by another module's SHA1
|
||||
# via the bare-name scan in get_struct_type) must not shadow a registered enum.
|
||||
vis_e_rn: GenTypes.StructEntry* = self.Gen.StructGen.find_struct(nm.id)
|
||||
if vis_e_rn is not None and vis_e_rn.IsForwardDecl != 0 and enum_base_rn is not None: return enum_base_rn
|
||||
struct_ty: mlirlite.LLVMType* = self.Gen.StructGen.get_struct_type(nm.id)
|
||||
if struct_ty is not None:
|
||||
# Bare class name is always the prototype (see above): no HasNew promotion to Ptr
|
||||
return struct_ty
|
||||
# Enum class (t.CEnum) bare-name annotation -> underlying integer type
|
||||
# Enum class (t.EnumBase) bare-name annotation -> underlying integer type
|
||||
# (e.g. `res: FRESULT = ...` where FRESULT is an enum, not a struct).
|
||||
if self.Gen.StmtGenDecl.is_enum_class(nm.id) == 1: return self.Gen.StmtGenDecl.get_enum_base_type(nm.id)
|
||||
# CTypedef dynamic lookup: resolve the expression recursively; depth is capped by
|
||||
if enum_base_rn is not None: return enum_base_rn
|
||||
# TypedefT dynamic lookup: resolve the expression recursively; depth is capped by
|
||||
# MAX_CTYPEDEF_DEPTH and cycle detection prevents A->B->A.
|
||||
if self.Gen.TypeGenCType.CTypedefResolveDepth < TypeGenMixin.MAX_CTYPEDEF_DEPTH:
|
||||
ctypedef_str: str = self.Gen.TypeGenCType.lookup_ctypedef(nm.id)
|
||||
if ctypedef_str is not None and self.Gen.TypeGenCType.CTypedefFound != 0:
|
||||
# Cycle detection: add the current CTypedef name to the in-progress list
|
||||
# Cycle detection: add the current TypedefT name to the in-progress list
|
||||
if self.Gen.TypeGenCType._ctypedef_push_visiting(pool, nm.id) == 0:
|
||||
# Convert the type string back to an AST with _parse_type_string, then recurse
|
||||
inner_node: ast.AST* = self._parse_type_string(pool, ctypedef_str, nm.ctx)
|
||||
@@ -2074,16 +2053,12 @@ novirtual class TypeGenResolve(TypeGenBase.TypeGenBase):
|
||||
if orig_node_a is not None:
|
||||
alias_ty: mlirlite.LLVMType* = self.resolve_annotation_type(pool, orig_node_a, imported_modules, from_imports, trans, depth + 1)
|
||||
if alias_ty is not None: return alias_ty
|
||||
# [builtins last match] Final fallback after all normal paths miss: check
|
||||
# includes/builtins.vp CTypedefs (e.g. rawptr: t.CTypedef = t.CPtr).
|
||||
btd_n: str = self._lookup_builtins_typedef(pool, nm.id)
|
||||
if btd_n is not None:
|
||||
btd_node_n: ast.AST* = self._parse_type_string(pool, btd_n, nm.ctx)
|
||||
if btd_node_n is not None:
|
||||
btd_ty_n: mlirlite.LLVMType* = self.resolve_annotation_type(pool, btd_node_n, imported_modules, from_imports, trans, depth + 1)
|
||||
if btd_ty_n is not None: return btd_ty_n
|
||||
# Library typedef fallback: read library sources (stdint / win32.win32base bare
|
||||
# typedefs) when _vpi_register_typedef_closure did not inject them, same as above.
|
||||
lib_ts_n: str = self._lookup_library_typedef(pool, "stdint", nm.id)
|
||||
if lib_ts_n is None: lib_ts_n = self._lookup_library_typedef(pool, "win32.win32base", nm.id)
|
||||
if lib_ts_n is not None:
|
||||
@@ -2091,12 +2066,8 @@ novirtual class TypeGenResolve(TypeGenBase.TypeGenBase):
|
||||
if lib_node_n is not None:
|
||||
lib_ty_n: mlirlite.LLVMType* = self.resolve_annotation_type(pool, lib_node_n, imported_modules, from_imports, trans, depth + 1)
|
||||
if lib_ty_n is not None: return lib_ty_n
|
||||
# [REPL session] The owner module is known but the class is not in the session
|
||||
# StructTable, so use an opaque placeholder (no StructTable write); batch mode skips.
|
||||
if trans is not None and trans.ReplCtx is not None:
|
||||
own_sha_rn: str = fi_sha1_rn
|
||||
# This-module bare names need trans.ModuleSha1 to be the stub's source sha1; the
|
||||
# session sha1 must not be used as owner, so exclude it and keep the fail-fast.
|
||||
if own_sha_rn is None:
|
||||
if trans.ModuleSha1 is not None and string.strcmp(trans.ModuleSha1, GenTypes.REPL_SESSION_SHA1) != 0: own_sha_rn = trans.ModuleSha1
|
||||
if own_sha_rn is not None:
|
||||
@@ -2104,17 +2075,13 @@ novirtual class TypeGenResolve(TypeGenBase.TypeGenBase):
|
||||
if ty_repl_n is not None: return ty_repl_n
|
||||
return None
|
||||
|
||||
# Attribute node: int, uint64, namespace_defs.PlainStruct, etc.
|
||||
if k == ast.ASTKind.Attribute:
|
||||
at: ast.Attribute* = ast.Attribute*(node)
|
||||
if at.attr is None: return None
|
||||
if at.value is not None and at.value.kind() == ast.ASTKind.Name:
|
||||
val_name: ast.Name* = ast.Name*(at.value)
|
||||
if val_name.id is not None:
|
||||
# `attr.<name>` is a builtin attribute marker, never a type.
|
||||
if string.strcmp(val_name.id, "attr") == 0: return None
|
||||
# SHA1 prefix ("{16hex}.Class"): the module segment is a SHA1 (already
|
||||
# compiled), so skip the imported_modules check via GenApi.is_sha1_hex16.
|
||||
sha1_attr: int = GenApi.is_sha1_hex16(val_name.id)
|
||||
if sha1_attr == 0:
|
||||
mod_imp: int = self.Gen.ImportsGen.is_module_imported(imported_modules, val_name.id)
|
||||
@@ -2147,6 +2114,15 @@ novirtual class TypeGenResolve(TypeGenBase.TypeGenBase):
|
||||
self.Gen.TypeGenCType.CTypedefResolveDepth -= 1
|
||||
self.Gen.TypeGenCType._ctypedef_pop_visiting()
|
||||
if inner_ty_attr is not None: return inner_ty_attr
|
||||
# Module-qualified typedef fallback: read the library module source even when
|
||||
# the module is explicitly imported (its typedefs may not be injected into this
|
||||
# file's table yet, e.g. `import posix` + `posix.DIR`).
|
||||
lib_td_q: str = self._lookup_library_typedef(pool, val_name.id, at.attr)
|
||||
if lib_td_q is not None:
|
||||
lib_td_node_q: ast.AST* = self._parse_type_string(pool, lib_td_q, at.ctx)
|
||||
if lib_td_node_q is not None:
|
||||
lib_td_ty_q: mlirlite.LLVMType* = self.resolve_annotation_type(pool, lib_td_node_q, imported_modules, from_imports, trans, depth + 1)
|
||||
if lib_td_ty_q is not None: return lib_td_ty_q
|
||||
# First try the builtin type mapping (int etc.)
|
||||
mapped_ty: mlirlite.LLVMType* = self.map_builtin_type(pool, at.attr)
|
||||
if mapped_ty is not None: return mapped_ty
|
||||
@@ -2184,14 +2160,22 @@ novirtual class TypeGenResolve(TypeGenBase.TypeGenBase):
|
||||
if nm_eq_a == 0 or seg_eq_a == 0: mod_sha1_v = inj_mod_a
|
||||
mod_sha1_rv = mod_sha1_v
|
||||
if mod_sha1_v is not None:
|
||||
# 枚举按 (名字, 目标模块 SHA1) 精确解析:`types.FRESULT` 只认 fat32_types 的
|
||||
# 那一条,不会和 win32.fileio 的同名 FRESULT 混用。
|
||||
enum_mod_ty: mlirlite.LLVMType* = self._resolve_enum_base_ty(at.attr, mod_sha1_v, trans)
|
||||
full_q_v: str = self._concat_sha1_class(pool, mod_sha1_v, at.attr)
|
||||
if full_q_v is not None:
|
||||
full_ty_v: mlirlite.LLVMType* = self.Gen.StructGen.get_struct_type(full_q_v)
|
||||
if full_ty_v is not None: return full_ty_v
|
||||
if full_ty_v is not None:
|
||||
# 同 Name 分支:opaque 前向占位不得压过已注册的枚举
|
||||
if enum_mod_ty is None: return full_ty_v
|
||||
fq_e_v: GenTypes.StructEntry* = self.Gen.StructGen.find_struct(full_q_v)
|
||||
if fq_e_v is None or fq_e_v.IsForwardDecl == 0: return full_ty_v
|
||||
mod_entry_v: GenTypes.StructEntry* = self.Gen.StructGen.find_struct_by_module(at.attr, mod_sha1_v)
|
||||
if mod_entry_v is not None and mod_entry_v.Ty is not None:
|
||||
# Bare class names (incl. mod.Class) are always the prototype (see above)
|
||||
return mod_entry_v.Ty
|
||||
if mod_entry_v.IsForwardDecl == 0 or enum_mod_ty is None:
|
||||
# Bare class names (incl. mod.Class) are always the prototype (see above)
|
||||
return mod_entry_v.Ty
|
||||
# On miss, the module may just re-export the class; correct to the true
|
||||
# defining module via _class_true_def_sha1 and look up once more.
|
||||
mod_sha1_td: str = self._class_true_def_sha1(pool, at.attr, mod_sha1_v)
|
||||
@@ -2202,6 +2186,16 @@ novirtual class TypeGenResolve(TypeGenBase.TypeGenBase):
|
||||
if full_ty_td is not None: return full_ty_td
|
||||
mod_entry_td: GenTypes.StructEntry* = self.Gen.StructGen.find_struct_by_module(at.attr, mod_sha1_td)
|
||||
if mod_entry_td is not None and mod_entry_td.Ty is not None: return mod_entry_td.Ty
|
||||
# Enum in a resolvable module: never forward-declare a class for it. The
|
||||
# StructTable is process-global, so the placeholder would survive into
|
||||
# every later file (in particular the enum's own defining module, where
|
||||
# it would shadow the real enum -- see the Name branch guard above).
|
||||
if enum_mod_ty is not None: return enum_mod_ty
|
||||
# Cyclic imports: the dependency's vpi may not be injected yet, so the
|
||||
# class is absent from StructTable. Forward-declare it; the real
|
||||
# definition later fills the same Ty (ensure_forward_class is idempotent).
|
||||
fwd_e_v: GenTypes.StructEntry* = self.Gen.StructGen.ensure_forward_class(pool, mod_sha1_td, at.attr)
|
||||
if fwd_e_v is not None and fwd_e_v.Ty is not None: return fwd_e_v.Ty
|
||||
# Module resolvable but class unregistered, or module unregistered:
|
||||
# do not fall back to bare names; fall through to enum/typedef attempts.
|
||||
else:
|
||||
@@ -2210,8 +2204,10 @@ novirtual class TypeGenResolve(TypeGenBase.TypeGenBase):
|
||||
if mod_struct_ty is not None: return mod_struct_ty
|
||||
# Module-qualified enum type: Module.EnumClass (e.g. fat32_types.FRESULT);
|
||||
# an enum class is not a struct, so get_struct_type misses it.
|
||||
if self.Gen.StmtGenDecl.is_enum_class(at.attr) == 1: return self.Gen.StmtGenDecl.get_enum_base_type(at.attr)
|
||||
# Module-qualified typedef (posix.DIR = t.CTypedef = t.CVoid* etc.): same
|
||||
# 模块段无法解析时退回"同名唯一 / 同名基类型一致"。
|
||||
enum_fb_ty: mlirlite.LLVMType* = self._resolve_enum_base_ty(at.attr, None, trans)
|
||||
if enum_fb_ty is not None: return enum_fb_ty
|
||||
# Module-qualified typedef (posix.DIR = t.TypedefT = t.CVoid* etc.): same
|
||||
# lookup_ctypedef + recursive resolution as the Name branch.
|
||||
if self.Gen.TypeGenCType.CTypedefResolveDepth < TypeGenMixin.MAX_CTYPEDEF_DEPTH:
|
||||
mod_td_str: str = self.Gen.TypeGenCType.lookup_ctypedef(at.attr)
|
||||
@@ -2237,7 +2233,8 @@ novirtual class TypeGenResolve(TypeGenBase.TypeGenBase):
|
||||
nested_struct_ty: mlirlite.LLVMType* = self.Gen.StructGen.get_struct_type(at.attr)
|
||||
if nested_struct_ty is not None: return nested_struct_ty
|
||||
# Nested module-qualified enum class (three-level name): look up the enum base type
|
||||
if self.Gen.StmtGenDecl.is_enum_class(at.attr) == 1: return self.Gen.StmtGenDecl.get_enum_base_type(at.attr)
|
||||
nested_enum_ty: mlirlite.LLVMType* = self._resolve_enum_base_ty(at.attr, None, trans)
|
||||
if nested_enum_ty is not None: return nested_enum_ty
|
||||
# Nested module-qualified typedef (win32.win32base.HANDLE = VOIDPTR): same
|
||||
# lookup_ctypedef + recursion + map_builtin_type fallback as the Name branch.
|
||||
if self.Gen.TypeGenCType.CTypedefResolveDepth < TypeGenMixin.MAX_CTYPEDEF_DEPTH:
|
||||
@@ -2272,8 +2269,8 @@ novirtual class TypeGenResolve(TypeGenBase.TypeGenBase):
|
||||
self.Gen.TypeGenErr.fatal_type_error(node, "type union `T1 | T2` was removed; use `T*`, a prefix descriptor (const/volatile/bigend/littlend), `&T` / `&temp T`, or export/extern/static instead")
|
||||
return None
|
||||
|
||||
# Subscript node: CArray[elem, count] -> [count x elem]; CArray[elem] -> Ptr(elem);
|
||||
# list[int] -> specialized struct type; marker wrappers (CPtr[T], CConst[T], ...) -> unwrap.
|
||||
# Subscript node: ArrayT[elem, count] -> [count x elem]; ArrayT[elem] -> Ptr(elem);
|
||||
# list[int] -> specialized struct type; marker wrappers (PtrT[T], ConstT[T], ...) -> unwrap.
|
||||
if k == ast.ASTKind.Subscript:
|
||||
sub: ast.Subscript* = ast.Subscript*(node)
|
||||
if sub.value is None: self.Gen.TypeGenErr.fatal_type_error(node, "Subscript node missing value")
|
||||
@@ -2281,7 +2278,7 @@ novirtual class TypeGenResolve(TypeGenBase.TypeGenBase):
|
||||
# pointers (handled as i8* below); other types still require a slice.
|
||||
if sub.slice is None and self._IsCallableSubscriptValue(sub.value) == 0: self.Gen.TypeGenErr.fatal_type_error(node, "Subscript node missing value or slice")
|
||||
# Function pointers (bare callable / Callable) equal t.Callable point-for-point and
|
||||
# map to i8*; must be checked before the Name path (generic/CArray/builtins fallback).
|
||||
# map to i8*; must be checked before the Name path (generic/ArrayT/builtins fallback).
|
||||
if sub.value.kind() == ast.ASTKind.Name and self._IsCallableSubscriptValue(sub.value) != 0: return mlirlite.Ptr(pool, mlirlite.Int8(pool))
|
||||
# Generic class annotation (list[int]): look up the specialized struct for a Name value.
|
||||
if sub.value.kind() == ast.ASTKind.Name:
|
||||
@@ -2289,18 +2286,18 @@ novirtual class TypeGenResolve(TypeGenBase.TypeGenBase):
|
||||
if gen_nm.id is not None:
|
||||
# Builtin marker wrappers: descriptor / storage / value-reference markers carry
|
||||
# no LLVM type of their own, so resolve the wrapped type.
|
||||
if string.strcmp(gen_nm.id, "CConst") == 0 or string.strcmp(gen_nm.id, "CVolatile") == 0 \
|
||||
if string.strcmp(gen_nm.id, "ConstT") == 0 or string.strcmp(gen_nm.id, "VolatileT") == 0 \
|
||||
or string.strcmp(gen_nm.id, "BigEndian") == 0 or string.strcmp(gen_nm.id, "LittleEndian") == 0 \
|
||||
or string.strcmp(gen_nm.id, "CStatic") == 0 or string.strcmp(gen_nm.id, "CExport") == 0 \
|
||||
or string.strcmp(gen_nm.id, "CExtern") == 0 or string.strcmp(gen_nm.id, "State") == 0 \
|
||||
or string.strcmp(gen_nm.id, "CDefine") == 0 or string.strcmp(gen_nm.id, "CAutoPtr") == 0:
|
||||
or string.strcmp(gen_nm.id, "StaticT") == 0 or string.strcmp(gen_nm.id, "ExportT") == 0 \
|
||||
or string.strcmp(gen_nm.id, "ExternT") == 0 or string.strcmp(gen_nm.id, "State") == 0 \
|
||||
or string.strcmp(gen_nm.id, "DefineT") == 0 or string.strcmp(gen_nm.id, "AutoPtrT") == 0:
|
||||
return self.resolve_annotation_type(pool, sub.slice, imported_modules, from_imports, trans, depth + 1)
|
||||
# Value-reference parameters use the pointer ABI: &T / &temp T -> T*.
|
||||
if string.strcmp(gen_nm.id, "CRef") == 0 or string.strcmp(gen_nm.id, "CTempRef") == 0:
|
||||
if string.strcmp(gen_nm.id, "RefT") == 0 or string.strcmp(gen_nm.id, "TempRefT") == 0:
|
||||
ref_inner: mlirlite.LLVMType* = self.resolve_annotation_type(pool, sub.slice, imported_modules, from_imports, trans, depth + 1)
|
||||
if ref_inner is None: return None
|
||||
return mlirlite.Ptr(pool, ref_inner)
|
||||
# Builtin fixed-array constructor array[E, N] (soft keyword, like t.CArray[E, N]):
|
||||
# Builtin fixed-array constructor array[E, N] (soft keyword, like t.ArrayT[E, N]):
|
||||
# active only in type positions; only exactly two args with a positive length.
|
||||
if string.strcmp(gen_nm.id, "array") == 0:
|
||||
if sub.slice.kind() != ast.ASTKind.Tuple:
|
||||
@@ -2313,23 +2310,23 @@ novirtual class TypeGenResolve(TypeGenBase.TypeGenBase):
|
||||
if count_node_arr.kind() == ast.ASTKind.Constant:
|
||||
cnt_arr0: ast.Constant* = ast.Constant*(count_node_arr)
|
||||
if cnt_arr0.const_kind == ast.CONST_NONE:
|
||||
self.Gen.TypeGenErr.fatal_type_error(count_node_arr, "Built-in fixed-size array constructor 'array' length must be a positive integer constant (>0); None is not allowed (use t.CArray[E] for automatic length inference). Correct example: state: array[uint32, 4]")
|
||||
self.Gen.TypeGenErr.fatal_type_error(count_node_arr, "Built-in fixed-size array constructor 'array' length must be a positive integer constant (>0); None is not allowed (use t.ArrayT[E] for automatic length inference). Correct example: state: array[uint32, 4]")
|
||||
elem_ty_arr: mlirlite.LLVMType* = self.resolve_annotation_type(pool, elem_node_arr, imported_modules, from_imports, trans, depth + 1)
|
||||
if elem_ty_arr is None: return None
|
||||
count_arr: int = self.Gen.TypeGenCType._eval_const_int_expr(count_node_arr, pool, from_imports, imported_modules)
|
||||
if self.Gen.TypeGenCType.ConstEvalOk == 0:
|
||||
self.Gen.TypeGenErr.fatal_type_error(count_node_arr, "Built-in fixed-size array constructor 'array' length must be an integer constant expression (literal / CDefine constant name / constant arithmetic expression). Correct example: state: array[uint8, MAX_LEN]")
|
||||
self.Gen.TypeGenErr.fatal_type_error(count_node_arr, "Built-in fixed-size array constructor 'array' length must be an integer constant expression (literal / DefineT constant name / constant arithmetic expression). Correct example: state: array[uint8, MAX_LEN]")
|
||||
if count_arr <= 0: self.Gen.TypeGenErr.fatal_type_error(count_node_arr, "Built-in fixed-size array constructor 'array' length must be positive. Correct example: state: array[uint32, 4]")
|
||||
return mlirlite.Array(pool, elem_ty_arr, count_arr)
|
||||
# Bare CArray (from t import *): equivalent to t.CArray; otherwise generic
|
||||
# Bare ArrayT (from t import *): equivalent to t.ArrayT; otherwise generic
|
||||
# lookup fails, the field type degrades to Ptr(i8), and linking fails.
|
||||
if string.strcmp(gen_nm.id, "CArray") == 0 or string.strcmp(gen_nm.id, "rawarray") == 0:
|
||||
# Single arg: CArray[elem_ty] -> Ptr(elem_ty) (statically inferred length array)
|
||||
if string.strcmp(gen_nm.id, "ArrayT") == 0 or string.strcmp(gen_nm.id, "rawarray") == 0:
|
||||
# Single arg: ArrayT[elem_ty] -> Ptr(elem_ty) (statically inferred length array)
|
||||
if sub.slice.kind() != ast.ASTKind.Tuple:
|
||||
elem_n: mlirlite.LLVMType* = self.resolve_annotation_type(pool, sub.slice, imported_modules, from_imports, trans, depth + 1)
|
||||
if elem_n is None: return None
|
||||
return mlirlite.Ptr(pool, elem_n)
|
||||
# Two args: CArray[elem_ty, count] -> [count x elem_ty]
|
||||
# Two args: ArrayT[elem_ty, count] -> [count x elem_ty]
|
||||
tup_n: ast.Tuple* = ast.Tuple*(sub.slice)
|
||||
if tup_n.elts is None or len(tup_n.elts) < 2: return None
|
||||
elem_node_n: ast.AST* = tup_n.elts.get(0)
|
||||
@@ -2344,9 +2341,9 @@ novirtual class TypeGenResolve(TypeGenBase.TypeGenBase):
|
||||
count_n: int = self.Gen.TypeGenCType._eval_const_int_expr(count_node_n, pool, from_imports, imported_modules)
|
||||
if self.Gen.TypeGenCType.ConstEvalOk == 0 or count_n <= 0: return None
|
||||
return mlirlite.Array(pool, elem_ty_n, count_n)
|
||||
# rawptr[T] / CPtr[T] builtin bare pointer: equivalent to `T*` -> Ptr(T),
|
||||
# rawptr[T] / PtrT[T] builtin bare pointer: equivalent to `T*` -> Ptr(T),
|
||||
# always appending exactly 1 layer. The old "absorb a layer" compensation is removed.
|
||||
if string.strcmp(gen_nm.id, "rawptr") == 0 or string.strcmp(gen_nm.id, "CPtr") == 0:
|
||||
if string.strcmp(gen_nm.id, "rawptr") == 0 or string.strcmp(gen_nm.id, "PtrT") == 0:
|
||||
if sub.slice.kind() != ast.ASTKind.Tuple:
|
||||
rp_elem: mlirlite.LLVMType* = self.resolve_annotation_type(pool, sub.slice, imported_modules, from_imports, trans, depth + 1)
|
||||
if rp_elem is None: return None
|
||||
@@ -2381,7 +2378,7 @@ novirtual class TypeGenResolve(TypeGenBase.TypeGenBase):
|
||||
if self._is_cattr_marker(sub.value) != 0:
|
||||
return self.resolve_annotation_type(pool, sub.slice, imported_modules, from_imports, trans, depth + 1)
|
||||
# Check whether value is an Attribute (module-qualified generic like numpy.ndarray[T])
|
||||
if sub.value.kind() != ast.ASTKind.Attribute: self.Gen.TypeGenErr.fatal_type_error(node, "Unsupported Subscript type annotation (only CArray / Callable are supported)")
|
||||
if sub.value.kind() != ast.ASTKind.Attribute: self.Gen.TypeGenErr.fatal_type_error(node, "Unsupported Subscript type annotation (only ArrayT / Callable are supported)")
|
||||
arr_at: ast.Attribute* = ast.Attribute*(sub.value)
|
||||
if arr_at.attr is None: self.Gen.TypeGenErr.fatal_type_error(node, "Subscript Attribute missing attr")
|
||||
# Module-qualified generic class: numpy.ndarray[CDouble] -> use the bare class name
|
||||
@@ -2389,17 +2386,17 @@ novirtual class TypeGenResolve(TypeGenBase.TypeGenBase):
|
||||
if trans is not None:
|
||||
if self.Gen.ClassGenGeneric._find_generic_template(arr_at.attr) is not None:
|
||||
return self._resolve_generic_subscript_ty(pool, sub, arr_at.attr, trans, imported_modules, from_imports, depth + 1)
|
||||
if arr_at.value is None or arr_at.value.kind() != ast.ASTKind.Name: self.Gen.TypeGenErr.fatal_type_error(node, "CArray / Callable must be qualified by the module name")
|
||||
if arr_at.value is None or arr_at.value.kind() != ast.ASTKind.Name: self.Gen.TypeGenErr.fatal_type_error(node, "ArrayT / Callable must be qualified by the module name")
|
||||
arr_val_name: ast.Name* = ast.Name*(arr_at.value)
|
||||
if arr_val_name.id is None: self.Gen.TypeGenErr.fatal_type_error(node, "Subscript module name is None")
|
||||
# Function pointer type: Callable[[A, B], Ret] -> i8* (function values are bitcast to
|
||||
# i8* for storage and the signature is rebuilt at calls, matching VPC convention).
|
||||
if string.strcmp(arr_at.attr, "Callable") == 0:
|
||||
return mlirlite.Ptr(pool, mlirlite.Int8(pool))
|
||||
# Pointer type: CPtr[T] -> Ptr(T); always appends exactly 1 layer, no exceptions.
|
||||
if string.strcmp(arr_at.attr, "CPtr") == 0:
|
||||
# Pointer type: PtrT[T] -> Ptr(T); always appends exactly 1 layer, no exceptions.
|
||||
if string.strcmp(arr_at.attr, "PtrT") == 0:
|
||||
if self.Gen.ImportsGen.is_module_imported(imported_modules, arr_val_name.id) == 0:
|
||||
self.Gen.TypeGenErr.fatal_type_error(node, "CPtr module not imported")
|
||||
self.Gen.TypeGenErr.fatal_type_error(node, "PtrT module not imported")
|
||||
inner_cptr_ty: mlirlite.LLVMType* = self.resolve_annotation_type(pool, sub.slice, imported_modules, from_imports, trans, depth + 1)
|
||||
if inner_cptr_ty is None:
|
||||
# Element type not ready (class not yet registered): prefer a forward declaration
|
||||
@@ -2408,43 +2405,43 @@ novirtual class TypeGenResolve(TypeGenBase.TypeGenBase):
|
||||
if _fwd_cptr_ty is not None: return _fwd_cptr_ty
|
||||
return None
|
||||
return mlirlite.Ptr(pool, inner_cptr_ty)
|
||||
if string.strcmp(arr_at.attr, "CArray") != 0: self.Gen.TypeGenErr.fatal_type_error(node, "Unsupported Subscript type annotation (only CArray / Callable are supported)")
|
||||
if string.strcmp(arr_at.attr, "ArrayT") != 0: self.Gen.TypeGenErr.fatal_type_error(node, "Unsupported Subscript type annotation (only ArrayT / Callable are supported)")
|
||||
if self.Gen.ImportsGen.is_module_imported(imported_modules, arr_val_name.id) == 0:
|
||||
self.Gen.TypeGenErr.fatal_type_error(node, "CArray module not imported")
|
||||
# Single-arg form: t.CArray[elem_ty] -> Ptr(elem_ty), equivalent to
|
||||
# t.CArray[elem_ty, None] (statically inferred length array).
|
||||
self.Gen.TypeGenErr.fatal_type_error(node, "ArrayT module not imported")
|
||||
# Single-arg form: t.ArrayT[elem_ty] -> Ptr(elem_ty), equivalent to
|
||||
# t.ArrayT[elem_ty, None] (statically inferred length array).
|
||||
if sub.slice.kind() != ast.ASTKind.Tuple:
|
||||
elem_ty_single: mlirlite.LLVMType* = self.resolve_annotation_type(pool, sub.slice, imported_modules, from_imports, trans, depth + 1)
|
||||
if elem_ty_single is None:
|
||||
# Element type not ready: return None for multi-pass convergence
|
||||
return None
|
||||
return mlirlite.Ptr(pool, elem_ty_single)
|
||||
# Two-arg form: t.CArray[elem_ty, count] -> [count x elem_ty]
|
||||
# Two-arg form: t.ArrayT[elem_ty, count] -> [count x elem_ty]
|
||||
tup: ast.Tuple* = ast.Tuple*(sub.slice)
|
||||
if tup.elts is None: self.Gen.TypeGenErr.fatal_type_error(node, "t.CArray Tuple missing elts")
|
||||
if tup.elts is None: self.Gen.TypeGenErr.fatal_type_error(node, "t.ArrayT Tuple missing elts")
|
||||
elts_list: list[ast.AST*]* = tup.elts
|
||||
elts_n: sizet = len(elts_list)
|
||||
if elts_n < 2: self.Gen.TypeGenErr.fatal_type_error(node, "t.CArray Tuple has too few elements")
|
||||
if elts_n < 2: self.Gen.TypeGenErr.fatal_type_error(node, "t.ArrayT Tuple has too few elements")
|
||||
elem_node: ast.AST* = elts_list.get(0)
|
||||
count_node: ast.AST* = elts_list.get(1)
|
||||
if elem_node is None or count_node is None: self.Gen.TypeGenErr.fatal_type_error(node, "t.CArray Tuple element is None")
|
||||
if elem_node is None or count_node is None: self.Gen.TypeGenErr.fatal_type_error(node, "t.ArrayT Tuple element is None")
|
||||
# Recursively resolve the element type
|
||||
elem_ty: mlirlite.LLVMType* = self.resolve_annotation_type(pool, elem_node, imported_modules, from_imports, trans, depth + 1)
|
||||
if elem_ty is None:
|
||||
# Element type not ready (t.CArray[class, N] with the class unregistered): return
|
||||
# Element type not ready (t.ArrayT[class, N] with the class unregistered): return
|
||||
# None for multi-pass convergence rather than fatal erroring in Phase 1a.
|
||||
return None
|
||||
# Resolve count (integer constant, None, CDefine name, or const expression like 2*MAX):
|
||||
# Resolve count (integer constant, None, DefineT name, or const expression like 2*MAX):
|
||||
# None equals the single-arg form -> Ptr(elem); a name/expression yields [count x elem].
|
||||
count_val: int = 0
|
||||
if count_node.kind() == ast.ASTKind.Constant:
|
||||
cnt_cn: ast.Constant* = ast.Constant*(count_node)
|
||||
if cnt_cn.const_kind == ast.CONST_NONE: return mlirlite.Ptr(pool, elem_ty)
|
||||
# Use _eval_const_int_expr for the constant integer expression (Constant/Name/BinOp);
|
||||
# pool/from_imports/imported_modules enable cross-module CDefine lookup.
|
||||
# pool/from_imports/imported_modules enable cross-module DefineT lookup.
|
||||
count_val = self.Gen.TypeGenCType._eval_const_int_expr(count_node, pool, from_imports, imported_modules)
|
||||
if self.Gen.TypeGenCType.ConstEvalOk == 0: self.Gen.TypeGenErr.fatal_type_error(count_node, "t.CArray count must be an integer constant, None, a CDefine constant name, or a constant expression")
|
||||
if count_val <= 0: self.Gen.TypeGenErr.fatal_type_error(count_node, "t.CArray count must be positive")
|
||||
if self.Gen.TypeGenCType.ConstEvalOk == 0: self.Gen.TypeGenErr.fatal_type_error(count_node, "t.ArrayT count must be an integer constant, None, a DefineT constant name, or a constant expression")
|
||||
if count_val <= 0: self.Gen.TypeGenErr.fatal_type_error(count_node, "t.ArrayT count must be positive")
|
||||
# Create the ArrayType (no Ptr wrapping), so build_alloca allocates the array
|
||||
# and alloca returns [N x elem_ty]*.
|
||||
return mlirlite.Array(pool, elem_ty, count_val)
|
||||
|
||||
@@ -4,7 +4,7 @@ import string
|
||||
import stdlib
|
||||
import win32.win32file as win32file
|
||||
import win32.win32base as win32base
|
||||
import win32.fileio as fileio
|
||||
import io as fileio
|
||||
import hashlib
|
||||
import viperlib
|
||||
import lib.Projectrans.Utils as Utils
|
||||
|
||||
+19
-19
@@ -6,7 +6,7 @@ import viperlib
|
||||
import mlirlite
|
||||
import memhub
|
||||
import ast
|
||||
import win32.fileio as fileio
|
||||
import io as fileio
|
||||
import win32.win32file as win32file
|
||||
import win32.win32base as win32base
|
||||
import win32.win32process as win32process
|
||||
@@ -78,14 +78,14 @@ define _REPL_CMD_CAP = 16384
|
||||
define _REPL_SRC_BUF_CAP = 1048576
|
||||
# .vpi stub buffer (matches BuildPipeline.PYI_BUF_SIZE).
|
||||
define _REPL_VPI_BUF_CAP = 262144
|
||||
# ---- M4: session sha1 / fragment import lib / session baseline (CDefine + CTypedef) ----
|
||||
# ---- M4: session sha1 / fragment import lib / session baseline (DefineT + TypedefT) ----
|
||||
# Max prior fragment import lib slots (a fragment takes one if its IR mentions a session symbol).
|
||||
define _REPL_FRAG_IMPLIB_MAX = 128
|
||||
# Fragment import lib record array capacity (slots x 8-byte pointers).
|
||||
define _REPL_FRAG_IMPLIB_ARR_CAP = 1024
|
||||
# Session baseline (CDefine) entry limit; replayed when each fragment rebuilds its table.
|
||||
# Session baseline (DefineT) entry limit; replayed when each fragment rebuilds its table.
|
||||
define _REPL_BASE_CDEFINE_MAX = 512
|
||||
# Session baseline (CTypedef) entry limit.
|
||||
# Session baseline (TypedefT) entry limit.
|
||||
define _REPL_BASE_CTYPEDEF_MAX = 512
|
||||
# includes class registration (done once at session init): max modules injected via vpi.
|
||||
define _REPL_INC_CLS_MOD_MAX = 512
|
||||
@@ -293,7 +293,7 @@ ReplSessionSha1: bytes = None
|
||||
ReplFragImplibs: sizet* = None
|
||||
ReplFragImplibCount: int = 0
|
||||
|
||||
# ==== M4: session baseline (CDefine/CTypedef survive across fragments) ====
|
||||
# ==== M4: session baseline (DefineT/TypedefT survive across fragments) ====
|
||||
# Replayed each fragment after clear_*; only successful fragments append; failures truncate.
|
||||
ReplBaseCDefineNames: sizet* = None
|
||||
ReplBaseCDefineValues: sizet* = None
|
||||
@@ -514,10 +514,10 @@ def ReplInitSessionSha1() -> int:
|
||||
return 0
|
||||
|
||||
|
||||
# ---- Session baseline (CDefine/CTypedef): find / restore / append / truncate ----
|
||||
# ---- Session baseline (DefineT/TypedefT): find / restore / append / truncate ----
|
||||
|
||||
def _repl_base_cdefine_find(name: str) -> int:
|
||||
"""Find a baseline CDefine entry index by name; -1 when not found."""
|
||||
"""Find a baseline DefineT entry index by name; -1 when not found."""
|
||||
if name is None or ReplBaseCDefineNames is None: return -1
|
||||
i: int
|
||||
for i in range(int(ReplBaseCDefineCount)):
|
||||
@@ -527,7 +527,7 @@ def _repl_base_cdefine_find(name: str) -> int:
|
||||
|
||||
|
||||
def ReplBaselineRestore(pool: memhub.MemBuddy*) -> int:
|
||||
"""Restore the session baseline (CDefine + CTypedef) into the just-cleared tables; 0=success."""
|
||||
"""Restore the session baseline (DefineT + TypedefT) into the just-cleared tables; 0=success."""
|
||||
if pool is None or ReplBaseCDefineNames is None: return 1
|
||||
if GenApi._g_gen is None: return 1
|
||||
i: int
|
||||
@@ -539,7 +539,7 @@ def ReplBaselineRestore(pool: memhub.MemBuddy*) -> int:
|
||||
if kind == 0: GenApi.register_cdefine_constant(pool, nm, int64(string.atoll(val)))
|
||||
elif kind == 1: GenApi.register_cdefine_double(pool, nm, string.atof(val))
|
||||
else:
|
||||
# String CDefine: GenApi has no wrapper, so call gen directly (same shape as
|
||||
# String DefineT: GenApi has no wrapper, so call gen directly (same shape as
|
||||
# the clear_cdefine_constants wrapper).
|
||||
GenApi._g_gen.TypeGenCDef.register_cdefine_string(pool, nm, val)
|
||||
for i in range(int(ReplBaseCTypedefCount)):
|
||||
@@ -551,7 +551,7 @@ def ReplBaselineRestore(pool: memhub.MemBuddy*) -> int:
|
||||
|
||||
|
||||
def ReplBaselineCapture() -> int:
|
||||
"""Append baseline-missing CDefine/CTypedef entries from the current tables into the session baseline; 0=success."""
|
||||
"""Append baseline-missing DefineT/TypedefT entries from the current tables into the session baseline; 0=success."""
|
||||
global ReplBaseCDefineCount, ReplBaseCTypedefCount
|
||||
if _perm_pool is None or GenApi._g_gen is None: return 1
|
||||
cdef_names: list[str]* = GenApi._g_gen.TypeGenCDef.CDefineNames
|
||||
@@ -882,7 +882,7 @@ def ReplInitSession() -> int:
|
||||
head.B = 0
|
||||
head.VarCount = 0
|
||||
head.LastFrag = 0
|
||||
# 8) M4 permanent state: session sha1 + fragment import lib table + session baseline (CDefine/CTypedef).
|
||||
# 8) M4 permanent state: session sha1 + fragment import lib table + session baseline (DefineT/TypedefT).
|
||||
if ReplInitSessionSha1() != 0: return 1
|
||||
ReplFragImplibs = _perm_pool.alloc(_REPL_FRAG_IMPLIB_ARR_CAP)
|
||||
ReplBaseCDefineNames = _perm_pool.alloc(8 * _REPL_BASE_CDEFINE_MAX)
|
||||
@@ -1678,7 +1678,7 @@ def _exec_repl_src(mb: memhub.MemBuddy*, src: str) -> int:
|
||||
if ReplCtxObj is not None:
|
||||
ReplCtxObj.Count = ReplSavedVarCount
|
||||
ReplCtxObj.FuncCount = ReplSavedFuncCount
|
||||
# M4: roll back the session baseline (CDefine/CTypedef) watermarks; a no-op on the normal
|
||||
# M4: roll back the session baseline (DefineT/TypedefT) watermarks; a no-op on the normal
|
||||
# path, it keeps the "failed fragment never changes the baseline" invariant.
|
||||
ReplBaselineTruncate(ReplSavedCDefineCount, ReplSavedCTypedefCount)
|
||||
return 0
|
||||
@@ -1706,7 +1706,7 @@ def ReplExecFragment(mb: memhub.MemBuddy*, src: str) -> int:
|
||||
VLogger.set_repl_jmpbuf(ReplJmpBuf)
|
||||
ReplSavedVarCount = ReplCtxObj.Count
|
||||
ReplSavedFuncCount = ReplCtxObj.FuncCount
|
||||
# M4: session baseline watermarks (CDefine/CTypedef); a failed fragment must not add
|
||||
# M4: session baseline watermarks (DefineT/TypedefT); a failed fragment must not add
|
||||
# increments, same idea as the var/func watermarks (only success calls ReplBaselineCapture).
|
||||
ReplSavedCDefineCount = ReplBaseCDefineCount
|
||||
ReplSavedCTypedefCount = ReplBaseCTypedefCount
|
||||
@@ -1745,7 +1745,7 @@ def ReplExecFragment(mb: memhub.MemBuddy*, src: str) -> int:
|
||||
vpi_dir: str = Config.TempDir
|
||||
if vpi_dir is None: vpi_dir = ReplTmpDir
|
||||
GenApi.set_temp_dir(vpi_dir)
|
||||
# CallGenBase.TempDir/TempDirLen (cross-module .vpi/.text.ll lookup, CExport checks) is only
|
||||
# CallGenBase.TempDir/TempDirLen (cross-module .vpi/.text.ll lookup, ExportT checks) is only
|
||||
# set by SetTempDir; set it here once per fragment, same value as set_temp_dir.
|
||||
GenApi.SetTempDir(vpi_dir)
|
||||
GenApi.set_current_file_type(ReplSrcPath)
|
||||
@@ -1759,7 +1759,7 @@ def ReplExecFragment(mb: memhub.MemBuddy*, src: str) -> int:
|
||||
# Struct registration uses permanent allocation (like Phase1b/Phase2 StdlibAlloc=1): table,
|
||||
# entry names and field types go into PermPool, else later fragments read freed memory.
|
||||
GenApi.set_stdlib_alloc(1)
|
||||
# M4: replay the CDefine/CTypedef session baseline; the clear_* above emptied the tables, and
|
||||
# M4: replay the DefineT/TypedefT session baseline; the clear_* above emptied the tables, and
|
||||
# this fragment's additions are captured on success by ReplBaselineCapture.
|
||||
ReplBaselineRestore(mb)
|
||||
|
||||
@@ -1875,7 +1875,7 @@ def ReplExecFragment(mb: memhub.MemBuddy*, src: str) -> int:
|
||||
if ReplHostImplib is None:
|
||||
stdio.printf(" Hint: host import lib (<exe name>.dll.a) not found -- " "cross-module symbols (of the form <16-hex>.<func name>) cannot be resolved; " "build the host with ViperC/project.vpj (including -Wl,--export-all-symbols), " "and keep <exe name>.dll.a in the same dir as the exe\n")
|
||||
else:
|
||||
stdio.printf(" Hint: if the output contains undefined symbol of the form <16-hex>.<name>, " "that symbol is not defined/exported by the host (the corresponding includes module was not linked when building the host); " "if the function is `export def` in source (CExport, bare-name definition) yet still carries a prefix, " "the include stub .vpi is probably missing (see the includes scan line at session start)\n")
|
||||
stdio.printf(" Hint: if the output contains undefined symbol of the form <16-hex>.<name>, " "that symbol is not defined/exported by the host (the corresponding includes module was not linked when building the host); " "if the function is `export def` in source (ExportT, bare-name definition) yet still carries a prefix, " "the include stub .vpi is probably missing (see the includes scan line at session start)\n")
|
||||
return 1
|
||||
|
||||
# 11') M4: record this fragment's import lib (if it defined session-prefixed symbols) so later
|
||||
@@ -1920,7 +1920,7 @@ def ReplExecFragment(mb: memhub.MemBuddy*, src: str) -> int:
|
||||
stdio.printf("fragment %s: failed to read the result area\n", ReplNameBuf)
|
||||
return 1
|
||||
if r_rc != 0: stdio.printf("fragment %s: fragment returned non-zero status %d\n", ReplNameBuf, r_rc)
|
||||
# 14') M4: on success, merge this fragment's new CDefine/CTypedef entries into the session
|
||||
# 14') M4: on success, merge this fragment's new DefineT/TypedefT entries into the session
|
||||
# baseline; failure paths skip this, so the baseline never holds a failed fragment's leftovers.
|
||||
ReplBaselineCapture()
|
||||
return 0
|
||||
@@ -1950,7 +1950,7 @@ def run(mb: memhub.MemBuddy*) -> int:
|
||||
ReplSavedFuncCount = 0
|
||||
VLogger.set_repl_jmpbuf(jb)
|
||||
# jit_flag is set True by main before calling run (see main.vp --repl); run resets it to False.
|
||||
lg: VLogger.Logger = VLogger.logger
|
||||
lg: VLogger.Logger* = VLogger.logger
|
||||
|
||||
# Arm the recovery point before use: session init may longjmp via VLogger.error, and an
|
||||
# un-armed jmp_buf would be UB; init is the only stage that must succeed, so exit on failure.
|
||||
@@ -2056,7 +2056,7 @@ def run(mb: memhub.MemBuddy*) -> int:
|
||||
if ReplCtxObj is not None:
|
||||
ReplCtxObj.Count = ReplSavedVarCount
|
||||
ReplCtxObj.FuncCount = ReplSavedFuncCount
|
||||
# M4: roll back the session baseline (CDefine/CTypedef) watermarks (same idea as above).
|
||||
# M4: roll back the session baseline (DefineT/TypedefT) watermarks (same idea as above).
|
||||
ReplBaselineTruncate(ReplSavedCDefineCount, ReplSavedCTypedefCount)
|
||||
mb.reset_to(mark)
|
||||
if eof_hit != 0: break
|
||||
|
||||
Reference in new issue
Block a user