diff --git a/App/lib/Stub/StubGen/VpiReader.vp b/App/lib/Stub/StubGen/VpiReader.vp index 2a31489..85a2d84 100644 --- a/App/lib/Stub/StubGen/VpiReader.vp +++ b/App/lib/Stub/StubGen/VpiReader.vp @@ -2309,12 +2309,12 @@ def _vpi_register_typedef_closure(pool: memhub.MemBuddy*, import_mods: str, temp if neg_r != 0: fv_r = -fv_r # 浮点宽度同样由声明注解裁决(与定义方同一套), # 否则 `define PI: float = ...` 的使用方会把 f32 当 double - GenApi.register_cdefine_double(pool, cdef_t.id, fv_r, GenApi.cdefine_decl_float_bits(pool, aa_r.annotation)) + GenApi.register_cdefine_double(pool, cdef_t.id, fv_r, GenApi.cdefine_decl_float_bits(pool, aa_r.annotation), sha1_r) elif cn_v.const_kind == ast.CONST_INT: cdef_val: int64 = GenApi.extract_cdefine_int_value(vnode_r) # 位宽与定义方同一套裁决(注解 → 右侧强转 → 值的最小可存宽度), # 否则使用方拿 bits=0 只能兜底,跨模块位宽会与定义方不一致 - GenApi.register_cdefine_constant(pool, cdef_t.id, cdef_val, GenApi.cdefine_decl_bits(pool, aa_r.annotation, vnode_r)) + GenApi.register_cdefine_constant(pool, cdef_t.id, cdef_val, GenApi.cdefine_decl_bits(pool, aa_r.annotation, vnode_r), sha1_r) # recurse into child imports (cycle guarded by the visited marks) if sub_imports[0] != '\0': _vpi_register_typedef_closure(pool, sub_imports, temp_dir, depth + 1) stdlib.free(sub_imports) @@ -3241,6 +3241,11 @@ def _ParseVpiIntoModuleImpl(pool: memhub.MemBuddy*, mod: mlirlite.LLVMModule*, v if pre_skip_fn_declares == 0: _inject_method_declare(pool, mod, m_fd1, cls_name_v1, trans, src_sha1, imported_modules, from_imports, i32_ty, i8_ptr_ty, funcs_ptr, max_funcs) + def_mod: str = src_sha1 + if trans is not None and trans.ModuleSha1 is not None and string.strcmp(trans.ModuleSha1, src_sha1) == 0: + # This vpi is the current module's own: keep its defines reachable by bare name. + def_mod = None + i: sizet for i in range(int(n)): child: ast.AST* = ch.get(i) @@ -3308,11 +3313,11 @@ def _ParseVpiIntoModuleImpl(pool: memhub.MemBuddy*, mod: mlirlite.LLVMModule*, v # Float DefineT (e.g. FRAGMENTATION_THRESHOLD: t.DefineT = 0.7): register via # register_cdefine_double (integer extraction would fail fast). # 注解声明 float 时带上 f32 种类(与定义方同一套裁决)。 - GenApi.register_cdefine_double(pool, cdef_tgt.id, cdef_float_val, GenApi.cdefine_decl_float_bits(pool, aa.annotation)) + GenApi.register_cdefine_double(pool, cdef_tgt.id, cdef_float_val, GenApi.cdefine_decl_float_bits(pool, aa.annotation), def_mod) elif cdef_is_str == 0: cdef_val: int64 = GenApi.extract_cdefine_int_value(vnode) # 位宽与定义方同一套裁决(注解 → 右侧强转 → 值的最小可存宽度) - GenApi.register_cdefine_constant(pool, cdef_tgt.id, cdef_val, GenApi.cdefine_decl_bits(pool, aa.annotation, vnode)) + GenApi.register_cdefine_constant(pool, cdef_tgt.id, cdef_val, GenApi.cdefine_decl_bits(pool, aa.annotation, vnode), def_mod) else: _inject_global_variable_declare(pool, mod, aa, trans, stub_imported_modules, stub_from_imports, i32_ty, i8_ptr_ty, src_sha1) continue diff --git a/App/lib/core/Gen/Expr/ExprGenAttr.vp b/App/lib/core/Gen/Expr/ExprGenAttr.vp index 5e8f9a5..215900e 100644 --- a/App/lib/core/Gen/Expr/ExprGenAttr.vp +++ b/App/lib/core/Gen/Expr/ExprGenAttr.vp @@ -350,46 +350,50 @@ novirtual class ExprGenAttr(ExprGenBase.ExprGenBase): # first check whether it is an ordinary variable (higher priority than module attr) ma_is_var: mlirlite.Value* = self.Gen.VarGen.lookup_var(trans.SymTab, nm_ma.id) if ma_is_var is None: - # first check whether it is a DefineT compile-time constant (e.g. ast.CONST_INT / - # win32base.INVALID_HANDLE_VALUE); such constants require a cross-module .vp/.vpi search - cdef_ma_val: int64 = self.Gen.TypeGenCDef.lookup_cdefine_constant(at.attr) - cd_local: int = self.Gen.TypeGenCDef.is_cdefine_found() - if cd_local == 0: - # determine the real module name: prefer resolving the alias from _from_imports, - # whose entries look like "local_name:real_module" (e.g. win32base:win32.win32base) - real_mod_name: str = nm_ma.id - if trans._from_imports is not None: - resolved_mod: str = self.Gen.ImportsGen.lookup_from_import(trans._from_imports, nm_ma.id, 0) - if resolved_mod is not None: - # copy the module name into a new buffer (truncate at space, null, or ':') - rm_len: sizet = 0 - while resolved_mod[rm_len] != ' ' and resolved_mod[rm_len] != '\0' and resolved_mod[rm_len] != ':': rm_len += 1 - rm_buf: str = pool.alloc(rm_len + 1) - if rm_buf is not None: - for rm_i in range(int(rm_len)): rm_buf[rm_i] = resolved_mod[rm_i] - rm_buf[rm_len] = '\0' - real_mod_name = rm_buf - # even when _from_imports is None (e.g. a plain `import ast`), still attempt the - # cross-module DefineT lookup, else ast.CONST_INT is misread as @CONST_INT (undefined). - if real_mod_name is not None: - ma_fi_len: sizet = string.strlen(at.attr) + string.strlen(real_mod_name) + 2 - ma_fi: str = pool.alloc(ma_fi_len) - if ma_fi is not None: - viperlib.snprintf(ma_fi, ma_fi_len, "%s:%s", at.attr, real_mod_name) - cdef_ma_val = self.Gen.ExprGenCdef._lookup_cross_module_cdefine(pool, at.attr, ma_fi) + # Resolve the receiver module BEFORE any lookup. A module-qualified access + # must never fall back to the bare table: that is how `b.VALUE` used to + # return `a.VALUE` when both modules define VALUE. + real_mod_name: str = None + if trans._from_imports is not None: + resolved_mod: str = self.Gen.ImportsGen.lookup_from_import(trans._from_imports, nm_ma.id, 0) + if resolved_mod is not None: + rm_len: sizet = 0 + while resolved_mod[rm_len] != ' ' and resolved_mod[rm_len] != '\0' and resolved_mod[rm_len] != ':': rm_len += 1 + rm_buf: str = pool.alloc(rm_len + 1) + if rm_buf is not None: + for rm_i in range(int(rm_len)): rm_buf[rm_i] = resolved_mod[rm_i] + rm_buf[rm_len] = '\0' + real_mod_name = rm_buf + if real_mod_name is None and trans._imported_modules is not None: + if self.Gen.ImportsGen.is_module_imported(trans._imported_modules, nm_ma.id) != 0: real_mod_name = nm_ma.id + if real_mod_name is None and self._lookup_module_sha1(nm_ma.id) is not None: real_mod_name = nm_ma.id + + cd_mod: str = None + cdef_ma_val: int64 = 0 + if real_mod_name is not None: + # Qualified access: look up only this module's define. + ma_fi_len: sizet = string.strlen(at.attr) + string.strlen(real_mod_name) + 2 + ma_fi: str = pool.alloc(ma_fi_len) + if ma_fi is not None: + viperlib.snprintf(ma_fi, ma_fi_len, "%s:%s", at.attr, real_mod_name) + cdef_ma_val = self.Gen.ExprGenCdef._lookup_cross_module_cdefine(pool, at.attr, ma_fi) + if self.Gen.TypeGenCDef.is_cdefine_found() != 0: + cd_mod = self.Gen.TypeGenCDef.get_last_cdefine_module() + else: + # Non-module receiver (class/enum qualified field): bare table only. + cdef_ma_val = self.Gen.TypeGenCDef.lookup_cdefine_constant(at.attr) if self.Gen.TypeGenCDef.is_cdefine_found() != 0: # cross-module string DefineT (e.g. pyzlib.ZLIB_VERSION = "1.3.2") - # → string global constant (test_pyzlib.vp: ver = pyzlib.ZLIB_VERSION) if self.Gen.TypeGenCDef.is_cdefine_str() != 0: - cdef_str_v: str = self.Gen.TypeGenCDef.lookup_cdefine_string(at.attr) + cdef_str_v: str = self.Gen.TypeGenCDef.lookup_cdefine_string(at.attr, cd_mod) if cdef_str_v is not None: return self.Gen.ExprGenConst.create_global_string(builder, pool, mod, cdef_str_v, trans) # cross-module float DefineT (e.g. vipermath.U_M_PI) → float constant - dchk_ma: double = self.Gen.TypeGenCDef.lookup_cdefine_double(at.attr) + dchk_ma: double = self.Gen.TypeGenCDef.lookup_cdefine_double(at.attr, cd_mod) # f32 声明的常量直接出 f32 常量(否则 f32 字面量会被提升成 double) if self.Gen.TypeGenCDef.is_cdefine_double() != 0: if self.Gen.TypeGenCDef.is_cdefine_float32() != 0: return mlirlite.ConstFloat(pool, mlirlite.Float(pool), dchk_ma) return mlirlite.ConstFloat(pool, mlirlite.Double(pool), dchk_ma) - return self.Gen.ExprGenCdef._const_int_for_cdefine(pool, at.attr, cdef_ma_val) + return self.Gen.ExprGenCdef._const_int_for_cdefine(pool, at.attr, cdef_ma_val, cd_mod) # check whether nm_ma.id.at.attr is an imported submodule (e.g. win32.win32file); # for a submodule do not create a global, let the nested attribute path handle it is_submod: int = 0 @@ -493,23 +497,25 @@ novirtual class ExprGenAttr(ExprGenBase.ExprGenBase): case _: pass return mlirlite.build_load(builder, load_ty_nested, gv_ref_nested) - # global not found: try a DefineT constant lookup; for nested module attributes - # like win32.win32base.INVALID_HANDLE_VALUE, resolve the DefineT from the .vpi - cdef_val_nested: int64 = self.Gen.TypeGenCDef.lookup_cdefine_constant(at.attr) - if self.Gen.TypeGenCDef.is_cdefine_found() == 0: - # build a temporary from_imports "attr:module" for the cross-module lookup - tmp_fi_len: sizet = string.strlen(at.attr) + string.strlen(qual_buf) + 2 - tmp_fi: str = pool.alloc(tmp_fi_len) - if tmp_fi is not None: - viperlib.snprintf(tmp_fi, tmp_fi_len, "%s:%s", at.attr, qual_buf) - cdef_val_nested = self.Gen.ExprGenCdef._lookup_cross_module_cdefine(pool, at.attr, tmp_fi) + # DefineT lookup for a nested module attribute: the qualified + # module is known, so never consult the bare table here. + cdef_val_nested: int64 = 0 + tmp_fi_len: sizet = string.strlen(at.attr) + string.strlen(qual_buf) + 2 + tmp_fi: str = pool.alloc(tmp_fi_len) + if tmp_fi is not None: + viperlib.snprintf(tmp_fi, tmp_fi_len, "%s:%s", at.attr, qual_buf) + cdef_val_nested = self.Gen.ExprGenCdef._lookup_cross_module_cdefine(pool, at.attr, tmp_fi) if self.Gen.TypeGenCDef.is_cdefine_found() != 0: + nst_mod: str = self.Gen.TypeGenCDef.get_last_cdefine_module() # cross-module float DefineT → float constant (f32 声明直接出 f32) - dchk_nst: double = self.Gen.TypeGenCDef.lookup_cdefine_double(at.attr) + dchk_nst: double = self.Gen.TypeGenCDef.lookup_cdefine_double(at.attr, nst_mod) if self.Gen.TypeGenCDef.is_cdefine_double() != 0: if self.Gen.TypeGenCDef.is_cdefine_float32() != 0: return mlirlite.ConstFloat(pool, mlirlite.Float(pool), dchk_nst) return mlirlite.ConstFloat(pool, mlirlite.Double(pool), dchk_nst) - return self.Gen.ExprGenCdef._const_int_for_cdefine(pool, at.attr, cdef_val_nested) + if self.Gen.TypeGenCDef.is_cdefine_str() != 0: + nst_s: str = self.Gen.TypeGenCDef.lookup_cdefine_string(at.attr, nst_mod) + if nst_s is not None: return self.Gen.ExprGenConst.create_global_string(builder, pool, mod, nst_s, trans) + return self.Gen.ExprGenCdef._const_int_for_cdefine(pool, at.attr, cdef_val_nested, nst_mod) # DefineT not found: fail-fast and stop compiling (no more undefined global # references, which would make llc report "use of undefined value"). if self.Gen.ExprGenCdef._is_cdefine_name_pattern(at.attr) != 0: diff --git a/App/lib/core/Gen/Expr/ExprGenCdef.vp b/App/lib/core/Gen/Expr/ExprGenCdef.vp index f8ab2d4..6cb5954 100644 --- a/App/lib/core/Gen/Expr/ExprGenCdef.vp +++ b/App/lib/core/Gen/Expr/ExprGenCdef.vp @@ -127,15 +127,13 @@ novirtual class ExprGenCdef(ExprGenBase.ExprGenBase): # 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) + self.Gen.TypeGenCDef.set_last_cdefine_module(None) if name is None: return -1 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 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 - mod_name_raw = self.Gen.ImportsGen.lookup_from_import(from_imports, name, 1) + # 1. resolve the module name for `name` from from_imports. The LAST matching entry + # wins (Python name binding), so `from a import X; from b import X` resolves to b. + mod_name_raw: str = self.Gen.ImportsGen.lookup_from_import_last(from_imports, name) # truncate module name at NUL: lookup_from_import returns a pointer into from_imports # (not NUL-terminated); match _lookup_sha1_from_modname_raw (space/colon/NUL delimited). @@ -153,6 +151,10 @@ novirtual class ExprGenCdef(ExprGenBase.ExprGenBase): # 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. + # Local table entries are keyed by the source module SHA1 (stronger than a name alias). + sha1_pre: str = None + if mod_name_raw is not None: sha1_pre = self.Gen.CallGenBase._lookup_sha1_from_modname_raw(pool, mod_name_raw) + mod_key: str = sha1_pre if sha1_pre is not None else base_mod xkey_buf: str = None if base_mod is not None: xk_len: sizet = string.strlen(name) + string.strlen(base_mod) + 2 @@ -161,55 +163,27 @@ novirtual class ExprGenCdef(ExprGenBase.ExprGenBase): viperlib.snprintf(xkey_buf, xk_len, "%s@%s", name, base_mod) xval: str = self.Gen.TypeGenCDef.lookup_xmod_cdefine(xkey_buf) if self.Gen.TypeGenCDef.is_cdefine_found() != 0: - if self.Gen.TypeGenCDef.is_cdefine_str() != 0: self.Gen.TypeGenCDef.register_cdefine_string(pool, name, xval) + self.Gen.TypeGenCDef.set_last_cdefine_module(mod_key) + if self.Gen.TypeGenCDef.is_cdefine_str() != 0: self.Gen.TypeGenCDef.register_cdefine_string(pool, name, xval, mod_key) elif self.Gen.TypeGenCDef.is_cdefine_double() != 0: xd_v: double = string.atof(xval) # 缓存里带宽度种类(f32 声明 → 1),否则使用方会把 f32 常量当 double 物化 xd_f32: int = self.Gen.TypeGenCDef.is_cdefine_float32() - self.Gen.TypeGenCDef.register_cdefine_double(pool, name, xd_v, xd_f32) + self.Gen.TypeGenCDef.register_cdefine_double(pool, name, xd_v, xd_f32, mod_key) else: xv_i: int64 = string.atoll(xval) - self.Gen.TypeGenCDef.register_cdefine_constant(pool, name, xv_i) + self.Gen.TypeGenCDef.register_cdefine_constant(pool, name, xv_i, 0, mod_key) return string.atoll(xval) - # 2. from_imports failed: fall back to the imported_modules walk (step 9.6); - # a star fallback may name a wrong module, so keep scanning imported_modules. + # 2. No module could be resolved from from_imports. Do not guess by scanning every + # imported module: with same-name defines in several modules that picks an arbitrary + # value. Callers must qualify (a.X / b.X) or use an explicit from-import. if mod_name_raw is None: - # from_imports matched nothing (exact and star both failed): - # go straight to the imported_modules walk (step 9.6). - if imported_modules is None: return -1 - # skip reading the from_imports module; go directly to the step 9.6 walk. - # result_found=0 skips steps 3-9 and jumps to 9.6. - self.Gen.TypeGenCDef.set_cdefine_found(0) - # iterate imported_modules directly - im_len: sizet = string.strlen(imported_modules) - im_ci: sizet = 0 - while im_ci < im_len: - while im_ci < im_len and imported_modules[im_ci] == ' ': im_ci += 1 - if im_ci >= im_len: break - im_start: sizet = im_ci - while im_ci < im_len and imported_modules[im_ci] != ' ': im_ci += 1 - im_end: sizet = im_ci - im_nlen: sizet = im_end - im_start - if im_nlen > 0 and im_nlen < 256: - im_buf: str = pool.alloc(im_nlen + 1) - if im_buf is not None: - for mk in range(int(im_nlen)): - im_buf[mk] = imported_modules[im_start + mk] - im_buf[im_nlen] = '\0' - sub_fi: str = pool.alloc(im_nlen + 3) - if sub_fi is not None: - sub_fi[0] = '*' - sub_fi[1] = ':' - for mk2 in range(int(im_nlen)): - sub_fi[2 + mk2] = im_buf[mk2] - sub_fi[2 + im_nlen] = '\0' - sub_val: int64 = self._lookup_cross_module_cdefine(pool, name, sub_fi, None, depth + 1, node) - if self.Gen.TypeGenCDef.is_cdefine_found() != 0: return sub_val return -1 # truncate module name, strip leading dots, find SHA1 via exact+suffix (shared logic) - sha1: str = self.Gen.CallGenBase._lookup_sha1_from_modname_raw(pool, mod_name_raw) + sha1: str = sha1_pre + if sha1 is None: sha1 = self.Gen.CallGenBase._lookup_sha1_from_modname_raw(pool, mod_name_raw) if sha1 is None: return -1 # 5. get temp_dir @@ -487,7 +461,8 @@ novirtual class ExprGenCdef(ExprGenBase.ExprGenBase): if is_str_val != 0: # 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) + self.Gen.TypeGenCDef.register_cdefine_string(pool, name, val_buf, sha1) + self.Gen.TypeGenCDef.set_last_cdefine_module(sha1) result_val = 0 result_found = 1 res_is_str = 1 @@ -496,8 +471,10 @@ novirtual class ExprGenCdef(ExprGenBase.ExprGenBase): 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 DefineT table (e.g. FOREGROUND_COMBO) - lval = self.Gen.TypeGenCDef.lookup_cdefine_constant(val_buf) + # name reference: same source module first, then the current module + lval = self.Gen.TypeGenCDef.lookup_cdefine_constant(val_buf, sha1) + if self.Gen.TypeGenCDef.is_cdefine_found() == 0: + 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 continue @@ -608,7 +585,9 @@ novirtual class ExprGenCdef(ExprGenBase.ExprGenBase): if string.strcmp(r_buf, "True") == 0: rval = 1 elif string.strcmp(r_buf, "False") == 0: rval = 0 else: - rval = self.Gen.TypeGenCDef.lookup_cdefine_constant(r_buf) + rval = self.Gen.TypeGenCDef.lookup_cdefine_constant(r_buf, sha1) + if self.Gen.TypeGenCDef.is_cdefine_found() == 0: + rval = self.Gen.TypeGenCDef.lookup_cdefine_constant(r_buf) if self.Gen.TypeGenCDef.is_cdefine_found() == 0: # name not registered: cannot evaluate, skip the line continue @@ -650,7 +629,8 @@ novirtual class ExprGenCdef(ExprGenBase.ExprGenBase): # 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) - self.Gen.TypeGenCDef.register_cdefine_double(pool, name, ldbl, res_is_f32) + self.Gen.TypeGenCDef.register_cdefine_double(pool, name, ldbl, res_is_f32, sha1) + self.Gen.TypeGenCDef.set_last_cdefine_module(sha1) result_val = lval result_found = 1 res_is_double = 1 @@ -805,39 +785,14 @@ novirtual class ExprGenCdef(ExprGenBase.ExprGenBase): stdlib.free(pyi_buf) - # 9.6 star-fallback module had no match: fall back to walking imported_modules, - # e.g. `from stdint import *` names stdint but SHARE_READ lives in `import win32.fileio`. - if result_found == 0 and imported_modules is not None: - im2_len: sizet = string.strlen(imported_modules) - im2_ci: sizet = 0 - while im2_ci < im2_len and result_found == 0: - while im2_ci < im2_len and imported_modules[im2_ci] == ' ': im2_ci += 1 - if im2_ci >= im2_len: break - im2_start: sizet = im2_ci - while im2_ci < im2_len and imported_modules[im2_ci] != ' ': im2_ci += 1 - im2_end: sizet = im2_ci - im2_nlen: sizet = im2_end - im2_start - if im2_nlen > 0 and im2_nlen < 256: - im2_buf: str = pool.alloc(im2_nlen + 1) - if im2_buf is not None: - for mk3 in range(int(im2_nlen)): - im2_buf[mk3] = imported_modules[im2_start + mk3] - im2_buf[im2_nlen] = '\0' - sub_fi2: str = pool.alloc(im2_nlen + 3) - if sub_fi2 is not None: - sub_fi2[0] = '*' - sub_fi2[1] = ':' - for mk4 in range(int(im2_nlen)): - sub_fi2[2 + mk4] = im2_buf[mk4] - sub_fi2[2 + im2_nlen] = '\0' - sub_val2: int64 = self._lookup_cross_module_cdefine(pool, name, sub_fi2, None, depth + 1, node) - if self.Gen.TypeGenCDef.is_cdefine_found() != 0: - result_val = sub_val2 - result_found = 1 + # 9.6 removed: scanning every imported module for a bare define name is not isolation. + # An unqualified name must resolve through an explicit from-import; module-qualified + # access resolves the receiver module before lookup. # 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 self.Gen.TypeGenCDef.get_last_cdefine_module() is None: self.Gen.TypeGenCDef.set_last_cdefine_module(sha1) if res_is_str != 0: # 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. @@ -848,7 +803,8 @@ novirtual class ExprGenCdef(ExprGenBase.ExprGenBase): viperlib.snprintf(ckey_s, ck_len_s, "%s@%s", name, base_mod) self.Gen.TypeGenCDef.register_xmod_cdefine(ckey_s, val_buf, 0, 1) elif res_is_double == 0: - self.Gen.TypeGenCDef.register_cdefine_constant(pool, name, result_val) + self.Gen.TypeGenCDef.register_cdefine_constant(pool, name, result_val, 0, sha1) + self.Gen.TypeGenCDef.set_last_cdefine_module(sha1) # cross-module cache (perm_pool, reused across files), integers: key matches the # entry query "name@srcModule" (truncated base_mod, leading dots removed). if base_mod is not None: @@ -867,7 +823,7 @@ novirtual class ExprGenCdef(ExprGenBase.ExprGenBase): ckey2: str = pool.alloc(ck_len2) if ckey2 is not None: viperlib.snprintf(ckey2, ck_len2, "%s@%s", name, base_mod) - dval_cc: double = self.Gen.TypeGenCDef.lookup_cdefine_double(name) + dval_cc: double = self.Gen.TypeGenCDef.lookup_cdefine_double(name, sha1) if self.Gen.TypeGenCDef.is_cdefine_double() != 0: cbuf_d: str = pool.alloc(64) if cbuf_d is not None: @@ -893,7 +849,7 @@ novirtual class ExprGenCdef(ExprGenBase.ExprGenBase): return has_upper # ============================================================ # Translate a variable reference (Name node) -> load - def _const_int_for_cdefine(self, pool: memhub.MemBuddy*, name: str, value: int64) -> mlirlite.Value*: + def _const_int_for_cdefine(self, pool: memhub.MemBuddy*, name: str, value: int64, module: str = None) -> mlirlite.Value*: """Materialize a DefineT integer constant as an LLVM constant (TPC TypeConvert aligned) 位宽优先用 define 的**已知位宽**(右侧显式强转或声明注解,见 register_cdefine_constant 的 bits); @@ -903,7 +859,7 @@ novirtual class ExprGenCdef(ExprGenBase.ExprGenBase): 而小值 define 说成 8/16 也在算术提升下不会溢出。 """ dbits: int = 0 - if name is not None: dbits = self.Gen.TypeGenCDef.lookup_cdefine_bits(name) + if name is not None: dbits = self.Gen.TypeGenCDef.lookup_cdefine_bits(name, module) if dbits == 8 or dbits == 16 or dbits == 32 or dbits == 64: return mlirlite.const_int(pool, dbits, self._cdefine_bits_value(value, dbits)) # 无位宽记录(例如只从 .vpi 跨模块引入、未登记 bits)→ 按**推断位宽**物化: diff --git a/App/lib/core/Gen/Expr/ExprGenName.vp b/App/lib/core/Gen/Expr/ExprGenName.vp index 9f817b1..96fd69d 100644 --- a/App/lib/core/Gen/Expr/ExprGenName.vp +++ b/App/lib/core/Gen/Expr/ExprGenName.vp @@ -77,13 +77,14 @@ novirtual class ExprGenName(ExprGenBase.ExprGenBase): if self.Gen.VarGen.lookup_var(trans.SymTab, nm_id) is None: cdef_val: int64 = self.Gen.ExprGenCdef._lookup_cross_module_cdefine(pool, nm_id, trans._from_imports, trans._imported_modules, 0, node) if self.Gen.TypeGenCDef.is_cdefine_found() != 0: + xmod: str = self.Gen.TypeGenCDef.get_last_cdefine_module() if self.Gen.TypeGenCDef.is_cdefine_str() != 0: - cdef_sv2: str = self.Gen.TypeGenCDef.lookup_cdefine_string(nm_id) + cdef_sv2: str = self.Gen.TypeGenCDef.lookup_cdefine_string(nm_id, xmod) if cdef_sv2 is not None: return self.Gen.ExprGenConst.create_global_string(builder, pool, mod, cdef_sv2, trans) self.Gen.TypeGenErr.fatal_error(node, f"DefineT string value missing: {nm_id}", "InternalCompilerError") # Cross-module float DefineT is cached as double; return a float constant # rather than a truncated integer. - dchk_nm: double = self.Gen.TypeGenCDef.lookup_cdefine_double(nm_id) + dchk_nm: double = self.Gen.TypeGenCDef.lookup_cdefine_double(nm_id, xmod) # 同本模块路径:f32 声明的常量直接出 f32 常量(跨模块缓存已带宽度种类)。 if self.Gen.TypeGenCDef.is_cdefine_double() != 0: if self.Gen.TypeGenCDef.is_cdefine_float32() != 0: return mlirlite.ConstFloat(pool, mlirlite.Float(pool), dchk_nm) @@ -91,7 +92,7 @@ novirtual class ExprGenName(ExprGenBase.ExprGenBase): # Same use-site width rule (cross-module DefineT) uw_x: mlirlite.Value* = self.Gen.ExprGenCdef.cdefine_use_width_value(pool, node, nm_id, cdef_val, target_ty, use_ctx) if uw_x is not None: return uw_x - return self.Gen.ExprGenCdef._const_int_for_cdefine(pool, nm_id, cdef_val) + return self.Gen.ExprGenCdef._const_int_for_cdefine(pool, nm_id, cdef_val, xmod) # DefineT lookup failed: from-import module-level globals (e.g. `from Phase2 import # PA_DEPS_MAX_STR`, an i64 global) are located by suffix after confirming the source module. diff --git a/App/lib/core/Gen/Func/FuncGenDef.vp b/App/lib/core/Gen/Func/FuncGenDef.vp index c2cf954..5f9b8bd 100644 --- a/App/lib/core/Gen/Func/FuncGenDef.vp +++ b/App/lib/core/Gen/Func/FuncGenDef.vp @@ -448,6 +448,9 @@ novirtual class FuncGenDef(CallGenBase.CallGenBase): mod: mlirlite.LLVMModule* = trans.Module if pool is None or mod is None: return 0 + # Pre-register this module's own DefineT constants before any signature/body is + # translated (Translator._preregister_module_defines, before forward declarations). + imported_modules: str = trans._imported_modules from_imports: str = trans._from_imports funcs_ptr: GenTypes.FuncEntry* = trans._funcs diff --git a/App/lib/core/Gen/GenApi.vp b/App/lib/core/Gen/GenApi.vp index b607654..4d9f57c 100644 --- a/App/lib/core/Gen/GenApi.vp +++ b/App/lib/core/Gen/GenApi.vp @@ -177,9 +177,9 @@ def is_cdefine_found() -> int: # ---------------- register_cdefine_constant (delegates to TypeGenCDef.register_cdefine_constant) ---------------- -def register_cdefine_constant(pool: memhub.MemBuddy*, name: str, value: int64, bits: int = 0): - """Delegate to gen.register_cdefine_constant (bits: 0=未知;8/16/32/64=已知位宽)""" - if _g_gen is not None: _g_gen.TypeGenCDef.register_cdefine_constant(pool, name, value, bits) +def register_cdefine_constant(pool: memhub.MemBuddy*, name: str, value: int64, bits: int = 0, module: str = None): + """Delegate to gen.register_cdefine_constant (bits: 0=未知;8/16/32/64=已知位宽; module: source SHA1)""" + if _g_gen is not None: _g_gen.TypeGenCDef.register_cdefine_constant(pool, name, value, bits, module) # ---------------- cdefine_decl_bits (delegates to AssignGenMixin.cdefine_decl_bits) ---------------- @@ -191,9 +191,9 @@ def cdefine_decl_bits(pool: memhub.MemBuddy*, annot: ast.AST*, value_node: ast.A # ---------------- register_cdefine_double (delegates to TypeGenCDef.register_cdefine_double) ---------------- -def register_cdefine_double(pool: memhub.MemBuddy*, name: str, value: double, is_f32: int = 0): - """Delegate to gen.register_cdefine_double (is_f32: 1=float 声明,0=double)""" - if _g_gen is not None: _g_gen.TypeGenCDef.register_cdefine_double(pool, name, value, is_f32) +def register_cdefine_double(pool: memhub.MemBuddy*, name: str, value: double, is_f32: int = 0, module: str = None): + """Delegate to gen.register_cdefine_double (is_f32: 1=float 声明,0=double; module: source SHA1)""" + if _g_gen is not None: _g_gen.TypeGenCDef.register_cdefine_double(pool, name, value, is_f32, module) # ---------------- cdefine_decl_float_bits (delegates to AssignGenMixin.cdefine_decl_float_bits) ---------------- @@ -204,6 +204,12 @@ def cdefine_decl_float_bits(pool: memhub.MemBuddy*, annot: ast.AST*) -> int: return 0 +# ---------------- register_cdefine_from_ast (delegates to AssignGenMixin._register_cdefine_from_value) ---------------- +def register_cdefine_from_ast(pool: memhub.MemBuddy*, name: str, val_node: ast.AST*, annot: ast.AST* = None): + """Evaluate and register one module-level define from its AST value.""" + if _g_gen is not None: _g_gen.AssignGen._register_cdefine_from_value(pool, name, val_node, annot) + + # ---------------- is_cdefine_annotation (delegates to AssignGenMixin.is_cdefine_annotation) ---------------- def is_cdefine_annotation(annot: ast.AST*) -> int: """Delegate to gen.is_cdefine_annotation""" diff --git a/App/lib/core/Gen/ImportsGenMixin.vp b/App/lib/core/Gen/ImportsGenMixin.vp index 9f135e7..1f50584 100644 --- a/App/lib/core/Gen/ImportsGenMixin.vp +++ b/App/lib/core/Gen/ImportsGenMixin.vp @@ -264,6 +264,46 @@ novirtual class ImportsGenMixin: while ci < total_len and cur[ci] != ' ': ci += 1 return None + # lookup_from_import_last - like lookup_from_import, but the LAST matching entry wins + # (Python name binding: a later from-import shadows an earlier one). Used for DefineT + # resolution so `from a import X; from b import X` resolves to b, not a. + def lookup_from_import_last(self, from_imports: str, name: str) -> str: + """Return the module of the last exact (else last star) from-import for name.""" + if name is None or from_imports is None: return None + name_len: sizet = string.strlen(name) + cur: str = from_imports + ci: sizet = 0 + total_len: sizet = string.strlen(from_imports) + last_exact: str = None + last_star: str = None + while ci < total_len: + while ci < total_len and cur[ci] == ' ': ci += 1 + if ci >= total_len: break + colon_pos: sizet = ci + while colon_pos < total_len and cur[colon_pos] != ':' and cur[colon_pos] != ' ': colon_pos += 1 + if colon_pos >= total_len or cur[colon_pos] != ':': break + entry_name_len: sizet = colon_pos - ci + if entry_name_len == 1 and cur[ci] == '*': + mod_start: sizet = colon_pos + 1 + mod_end: sizet = mod_start + while mod_end < total_len and cur[mod_end] != ' ' and cur[mod_end] != '\0' and cur[mod_end] != ':': mod_end += 1 + if mod_end > mod_start: last_star = cur + mod_start + elif entry_name_len == name_len: + matched: int = 1 + for ei in range(int(name_len)): + if cur[ci + ei] != name[ei]: + matched = 0 + break + if matched != 0: + mod_start2: sizet = colon_pos + 1 + mod_end2: sizet = mod_start2 + while mod_end2 < total_len and cur[mod_end2] != ' ' and cur[mod_end2] != '\0' and cur[mod_end2] != ':': mod_end2 += 1 + if mod_end2 > mod_start2: last_exact = cur + mod_start2 + ci = colon_pos + while ci < total_len and cur[ci] != ' ': ci += 1 + if last_exact is not None: return last_exact + return last_star + # Look up a from-import name and return the module name, or None. # allow_star_fallback=1 permits a star-import fallback; pass 0 to disable it. def lookup_from_import(self, from_imports: str, name: str, allow_star_fallback: int = 1) -> str: diff --git a/App/lib/core/Gen/Translator.vp b/App/lib/core/Gen/Translator.vp index 010a2a4..c998451 100644 --- a/App/lib/core/Gen/Translator.vp +++ b/App/lib/core/Gen/Translator.vp @@ -559,6 +559,9 @@ novirtual class Translator: GenApi.push_trace(GenApi.get_current_file(), cur_imp_lineno) StubMergerBuild.LoadDepVpiIntoModule(pool, mod, self._imported_modules, self._from_imports, GenApi.get_temp_dir(), self) GenApi.pop_trace() + # Register the module's own DefineT constants before any class/function body is + # translated, so a define may be used above its declaration line. + self._preregister_module_defines(pool, tree) # File-level class pre-registration already ran at the top of this function. # Module var pre-registration + forward declarations run only in full translation. if self._declare_only == 0: @@ -720,6 +723,23 @@ novirtual class Translator: if erased != 0: tree.children = kept return erased + # _preregister_module_defines - evaluate/register all top-level DefineT constants early. + # Runs in declare-only and full translation, so signatures (array sizes) and bodies that + # appear above the define line can use it. Values stay in the current module's namespace. + def _preregister_module_defines(self, pool: memhub.MemBuddy*, tree: ast.AST*): + if tree is None or tree.children is None: return + ch: list[ast.AST*]* = tree.children + for ci in range(int(len(ch))): + child: ast.AST* = ch.get(ci) + if child is None or child.kind() != ast.ASTKind.AnnAssign: continue + aa: ast.AnnAssign* = ast.AnnAssign*(child) + if aa is None or aa.target is None or aa.value is None or aa.annotation is None: continue + if aa.target.kind() != ast.ASTKind.Name: continue + nm: ast.Name* = ast.Name*(aa.target) + if nm.id is None: continue + if GenApi.is_cdefine_annotation(aa.annotation) == 0: continue + GenApi.register_cdefine_from_ast(pool, nm.id, aa.value, aa.annotation) + # Auto container visibility injection (mirrors TPC _inject_auto_imports): mark list/dict classes # visible so bare annotations/constructors resolve. Detects AnnAssign annotation, Call.func, Subscript.value. def _inject_auto_container_visibility(self, pool: memhub.MemBuddy*, tree: ast.AST*): diff --git a/App/lib/core/Gen/Type/TypeGenBase.vp b/App/lib/core/Gen/Type/TypeGenBase.vp index b181a44..9202b52 100644 --- a/App/lib/core/Gen/Type/TypeGenBase.vp +++ b/App/lib/core/Gen/Type/TypeGenBase.vp @@ -40,6 +40,9 @@ novirtual class TypeGenBase: # --- Global DefineT compile-time constant table (was _g_cdefine_*) --- CDefineNames: list[str]* + # Source module of each entry ("" = declared in the current module). Cross-module values + # are only reachable through the (name, module) pair, so same-name defines stay isolated. + CDefineMods: list[str]* CDefineValues: list[str]* # 浮点常量的**宽度种类**:1=f64(double,默认),2=f32(float);0=非浮点(整型/字符串)。 # 判定"是不是浮点常量"一律用 `!= 0`(既有调用处口径不变),只有浮点常量物化处区分 64/32: @@ -61,6 +64,7 @@ novirtual class TypeGenBase: # 值与 CDefineValues 分开存:名字不变,值是 '\n' 连接的元素串(元素内不含换行), # 供 `*OTHER_LIST` 解包与后续编译期消费者读取。与 CDefineNames 同池同生命周期。 CDefineListNames: list[str]* + CDefineListMods: list[str]* CDefineListValues: list[str]* # --- Cross-module DefineT lookup cache (PermPool-allocated, reused across files) --- @@ -70,6 +74,8 @@ novirtual class TypeGenBase: # 同 CDefineDoubleFlags 的编码:1=f64,2=f32,0=非浮点。 XmodCdefDoubleFlags: list[int]* XmodCdefStrFlags: list[int]* + # Module that produced the last successful cross-module DefineT hit ("" = local/unknown). + CDefineLastModule: str # --- Current module SHA1 (was _g_current_module_sha1) --- CurrentModuleSha1: str @@ -125,6 +131,7 @@ novirtual class TypeGenBase: self.GenericCtxStack = None self.GenericCtxDepth = 0 self.CDefineNames = None + self.CDefineMods = None self.CDefineValues = None self.CDefineDoubleFlags = None self.CDefineStrFlags = None @@ -139,6 +146,7 @@ novirtual class TypeGenBase: self.XmodCdefValues = None self.XmodCdefDoubleFlags = None self.XmodCdefStrFlags = None + self.CDefineLastModule = None self.CurrentModuleSha1 = None self.CTypedefNames = None self.CTypedefTypeStrs = None diff --git a/App/lib/core/Gen/Type/TypeGenCDef.vp b/App/lib/core/Gen/Type/TypeGenCDef.vp index b2840a5..95a06ee 100644 --- a/App/lib/core/Gen/Type/TypeGenCDef.vp +++ b/App/lib/core/Gen/Type/TypeGenCDef.vp @@ -103,25 +103,51 @@ novirtual class TypeGenCDef(TypeGenBase.TypeGenBase): if CompilerFactory.factory is not None and CompilerFactory.factory.PermPool is not None: return CompilerFactory.factory.PermPool return pool - def register_cdefine_constant(self, pool: memhub.MemBuddy*, name: str, value: int64, bits: int = 0) -> None: + def _cdef_module_copy(self, pool: memhub.MemBuddy*, module: str) -> str: + """Deep-copy the source module for a table entry; None/"" -> empty (current module).""" + if module is None or module[0] == '\0': return "" + n: sizet = string.strlen(module) + c: str = pool.alloc(n + 1) + if c is None: return "" + string.strcpy(c, module) + return c + + def _cdef_module_match(self, index: sizet, module: str) -> int: + """1 when entry index belongs to module ("" / None = the current module).""" + if self.CDefineMods is None or index >= len(self.CDefineMods): return 0 + em: str = self.CDefineMods.get(index) + if module is None or module[0] == '\0': return 1 if (em is None or em[0] == '\0') else 0 + if em is None or em[0] == '\0': return 0 + return 1 if string.strcmp(em, module) == 0 else 0 + + def set_last_cdefine_module(self, module: str) -> None: + self.CDefineLastModule = module + + def get_last_cdefine_module(self) -> str: + return self.CDefineLastModule + + def register_cdefine_constant(self, pool: memhub.MemBuddy*, name: str, value: int64, bits: int = 0, module: str = None) -> None: """Register an integer DefineT constant; store the value as a decimal string. bits: 值表达式的位宽(0=未知,按值推断;8/16/32/64 = 显式强转或声明注解给出的位宽)。 + module: source module for a cross-module value; None/"" means the current module. """ if name is None: return if self.CDefineNames is None: tp: memhub.MemBuddy* = self._cdefine_table_pool(pool) tn: list[str]* = list[str](tp, 64) + tm: list[str]* = list[str](tp, 64) tv: list[str]* = list[str](tp, 64) td: list[int]* = list[int](tp, 64) tsf: list[int]* = list[int](tp, 64) tbits: list[int]* = list[int](tp, 64) # Table build failed (pool unusable/exhausted): report and return, never let the # append below hit None or an uninitialized address (would write NULL/0xCD). - if tn is None or tv is None or td is None or tsf is None or tbits is None: + if tn is None or tm is None or tv is None or td is None or tsf is None or tbits is None: self.Gen.TypeGenErr.fatal_error(None, "DefineT table allocation failed (table body pool unavailable or exhausted)", "MemoryError") return self.CDefineNames = tn + self.CDefineMods = tm self.CDefineValues = tv self.CDefineDoubleFlags = td self.CDefineStrFlags = tsf @@ -129,7 +155,7 @@ novirtual class TypeGenCDef(TypeGenBase.TypeGenBase): self._cdef_idx_init(tp) # Table-ready guard: missing/half-initialized body -> fail-fast, no garbage writes # (assert non-null before append). - if self.CDefineNames is None or self.CDefineValues is None or self.CDefineDoubleFlags is None or self.CDefineStrFlags is None or self.CDefineBits is None: + if self.CDefineNames is None or self.CDefineMods is None or self.CDefineValues is None or self.CDefineDoubleFlags is None or self.CDefineStrFlags is None or self.CDefineBits is None: self.Gen.TypeGenErr.fatal_error(None, "DefineT table not ready (CDefineNames/Values/Flags is empty)", "MemoryError") return name_copy: str = pool.alloc(string.strlen(name) + 1) @@ -138,6 +164,7 @@ novirtual class TypeGenCDef(TypeGenBase.TypeGenBase): return string.strcpy(name_copy, name) self.CDefineNames.append(name_copy) + self.CDefineMods.append(self._cdef_module_copy(pool, module)) self._cdef_idx_sync_append(name_copy) val_buf: str = pool.alloc(32) if val_buf is not None: @@ -150,31 +177,34 @@ novirtual class TypeGenCDef(TypeGenBase.TypeGenBase): if self.CDefineDoubleFlags is not None: self.CDefineDoubleFlags.append(0) if self.CDefineStrFlags is not None: self.CDefineStrFlags.append(0) if self.CDefineBits is not None: self.CDefineBits.append(bits) - def register_cdefine_double(self, pool: memhub.MemBuddy*, name: str, value: double, is_f32: int = 0) -> None: + def register_cdefine_double(self, pool: memhub.MemBuddy*, name: str, value: double, is_f32: int = 0, module: str = None) -> None: """Register a float DefineT constant; store the value as %f text and flag it. is_f32: 1 表示该常量声明为 32 位 float(`define PI: float = ...` / `float(...)`), 物化时直接出 f32 常量;0(默认)表示 double。 + module: source module for a cross-module value; None/"" means the current module. """ if name is None: return if self.CDefineNames is None: tp: memhub.MemBuddy* = self._cdefine_table_pool(pool) tn: list[str]* = list[str](tp, 64) + tm: list[str]* = list[str](tp, 64) tv: list[str]* = list[str](tp, 64) td: list[int]* = list[int](tp, 64) tsf: list[int]* = list[int](tp, 64) tbits: list[int]* = list[int](tp, 64) - if tn is None or tv is None or td is None or tsf is None or tbits is None: + if tn is None or tm is None or tv is None or td is None or tsf is None or tbits is None: self.Gen.TypeGenErr.fatal_error(None, "DefineT float table allocation failed (table body pool unavailable or exhausted)", "MemoryError") return self.CDefineNames = tn + self.CDefineMods = tm self.CDefineValues = tv self.CDefineDoubleFlags = td self.CDefineStrFlags = tsf self.CDefineBits = tbits self._cdef_idx_init(tp) # Table-ready guard (same as register_cdefine_constant): assert non-null before append - if self.CDefineNames is None or self.CDefineValues is None or self.CDefineDoubleFlags is None or self.CDefineStrFlags is None or self.CDefineBits is None: + if self.CDefineNames is None or self.CDefineMods is None or self.CDefineValues is None or self.CDefineDoubleFlags is None or self.CDefineStrFlags is None or self.CDefineBits is None: self.Gen.TypeGenErr.fatal_error(None, "DefineT float table not ready (CDefineNames/Values/Flags is empty)", "MemoryError") return name_copy_d: str = pool.alloc(string.strlen(name) + 1) @@ -183,6 +213,7 @@ novirtual class TypeGenCDef(TypeGenBase.TypeGenBase): return string.strcpy(name_copy_d, name) self.CDefineNames.append(name_copy_d) + self.CDefineMods.append(self._cdef_module_copy(pool, module)) self._cdef_idx_sync_append(name_copy_d) val_buf: str = pool.alloc(32) if val_buf is not None: @@ -201,8 +232,11 @@ novirtual class TypeGenCDef(TypeGenBase.TypeGenBase): if val_buf is None: VLogger.error("DefineT float value buffer allocation failed", "CDEF-DBG") self.Gen.TypeGenErr.fatal_error(None, "DefineT float value buffer allocation failed", "MemoryError") - def register_cdefine_string(self, pool: memhub.MemBuddy*, name: str, value: str) -> None: - """Register a string DefineT constant, deep-copying name and value into pool.""" + def register_cdefine_string(self, pool: memhub.MemBuddy*, name: str, value: str, module: str = None) -> None: + """Register a string DefineT constant, deep-copying name and value into pool. + + module: source module for a cross-module value; None/"" means the current module. + """ if pool is None or name is None: return if value is None: self.Gen.TypeGenErr.fatal_error(None, f"DefineT string value missing: {name}", "InternalCompilerError") @@ -210,21 +244,23 @@ novirtual class TypeGenCDef(TypeGenBase.TypeGenBase): if self.CDefineNames is None: tp: memhub.MemBuddy* = self._cdefine_table_pool(pool) tn: list[str]* = list[str](tp, 64) + tm: list[str]* = list[str](tp, 64) tv: list[str]* = list[str](tp, 64) td: list[int]* = list[int](tp, 64) tsf: list[int]* = list[int](tp, 64) tbits: list[int]* = list[int](tp, 64) - if tn is None or tv is None or td is None or tsf is None or tbits is None: + if tn is None or tm is None or tv is None or td is None or tsf is None or tbits is None: self.Gen.TypeGenErr.fatal_error(None, "DefineT string table allocation failed (table body pool unavailable or exhausted)", "MemoryError") return self.CDefineNames = tn + self.CDefineMods = tm self.CDefineValues = tv self.CDefineDoubleFlags = td self.CDefineStrFlags = tsf self.CDefineBits = tbits self._cdef_idx_init(tp) # Table-ready guard (same as register_cdefine_constant): assert non-null before append - if self.CDefineNames is None or self.CDefineValues is None or self.CDefineDoubleFlags is None or self.CDefineStrFlags is None or self.CDefineBits is None: + if self.CDefineNames is None or self.CDefineMods is None or self.CDefineValues is None or self.CDefineDoubleFlags is None or self.CDefineStrFlags is None or self.CDefineBits is None: self.Gen.TypeGenErr.fatal_error(None, "DefineT string table not ready (CDefineNames/Values/Flags is empty)", "MemoryError") return ncopy_s: str = pool.alloc(string.strlen(name) + 1) @@ -233,6 +269,7 @@ novirtual class TypeGenCDef(TypeGenBase.TypeGenBase): return string.strcpy(ncopy_s, name) self.CDefineNames.append(ncopy_s) + self.CDefineMods.append(self._cdef_module_copy(pool, module)) self._cdef_idx_sync_append(ncopy_s) vlen: sizet = string.strlen(value) vcopy: str = pool.alloc(vlen + 1) @@ -248,7 +285,7 @@ novirtual class TypeGenCDef(TypeGenBase.TypeGenBase): # Cross-module string DefineT reads call this + set_cdefine_found; missing the flag degrades to int 0 -> NULL crash. self.CDefineIsStr = 1 self.CDefineFound = 1 - def register_cdefine_string_list(self, pool: memhub.MemBuddy*, name: str, joined: str) -> None: + def register_cdefine_string_list(self, pool: memhub.MemBuddy*, name: str, joined: str, module: str = None) -> None: """Register a macro-list DefineT constant (`define NAME = [...]`, '\n'-joined value). The list is compile-time only: no LLVM global is created (an unannotated module-level @@ -260,13 +297,15 @@ novirtual class TypeGenCDef(TypeGenBase.TypeGenBase): if tp is None: self.Gen.TypeGenErr.fatal_error(None, "DefineT list table pool unavailable", "MemoryError") return - if self.CDefineListNames is None or self.CDefineListValues is None: + if self.CDefineListNames is None or self.CDefineListMods is None or self.CDefineListValues is None: ln_l: list[str]* = list[str](tp, 16) + lm_l: list[str]* = list[str](tp, 16) lv_l: list[str]* = list[str](tp, 16) - if ln_l is None or lv_l is None: + if ln_l is None or lm_l is None or lv_l is None: self.Gen.TypeGenErr.fatal_error(None, "DefineT list table allocation failed", "MemoryError") return self.CDefineListNames = ln_l + self.CDefineListMods = lm_l self.CDefineListValues = lv_l nlen_l: sizet = string.strlen(name) ncopy_l: str = tp.alloc(nlen_l + 1) @@ -281,16 +320,18 @@ novirtual class TypeGenCDef(TypeGenBase.TypeGenBase): return string.strcpy(vcopy_l, joined) self.CDefineListNames.append(ncopy_l) + self.CDefineListMods.append(self._cdef_module_copy(tp, module)) self.CDefineListValues.append(vcopy_l) - def lookup_cdefine_string_list(self, name: str) -> str: + def lookup_cdefine_string_list(self, name: str, module: str = None) -> str: """Return the '\n'-joined value of a macro-list DefineT constant; None if absent.""" - if name is None or self.CDefineListNames is None or self.CDefineListValues is None: return None + if name is None or self.CDefineListNames is None or self.CDefineListMods is None or self.CDefineListValues is None: return None n_l: sizet = len(self.CDefineListNames) for i in range(int(n_l)): nm_l: str = self.CDefineListNames.get(sizet(i)) if nm_l is not None and string.strcmp(nm_l, name) == 0: - return self.CDefineListValues.get(sizet(i)) + if self._cdef_module_match(i, module) != 0: + return self.CDefineListValues.get(sizet(i)) return None # Cross-module DefineT lookup cache (PermPool-allocated, reused across files) @@ -347,26 +388,34 @@ novirtual class TypeGenCDef(TypeGenBase.TypeGenBase): self.CDefineIsDouble = int(self.XmodCdefDoubleFlags.get(i)) return val_str return None - def lookup_cdefine_constant(self, name: str) -> int64: - """Look up an integer DefineT constant; 64-bit exact, found flag distinguishes -1.""" + def lookup_cdefine_constant(self, name: str, module: str = None) -> int64: + """Look up an integer DefineT constant; 64-bit exact, found flag distinguishes -1. + + module=None/"" matches only defines declared in the current module; a non-empty module + matches only that source module's entry (strong cross-module isolation). + """ self.CDefineFound = 0 self.CDefineIsDouble = 0 self.CDefineIsStr = 0 - if self.CDefineNames is None: return 0 + if self.CDefineNames is None or self.CDefineMods is None: return 0 n: sizet = len(self.CDefineNames) i: sizet - # Name-index fast path: when trusted (Built==n and name non-null) locate the single candidate - # (== first scan hit); if unavailable fall back to the full linear scan below. + # Name-index fast path: only for current-module (bare) lookups; a module-qualified + # lookup must scan because the same name may exist in several modules. cd_start: sizet = 0 cd_end: sizet = n - if name is not None and self.CDefineNameIdxBuilt == int(n) and self.CDefineNameIdx is not None and self.CDefineNameIdxCap > 0: - cd_hit: int = self._cdef_idx_find(name) - if cd_hit < 0: return 0 - cd_start = sizet(cd_hit) - cd_end = sizet(cd_hit + 1) + if module is None or module[0] == '\0': + if name is not None and self.CDefineNameIdxBuilt == int(n) and self.CDefineNameIdx is not None and self.CDefineNameIdxCap > 0: + cd_hit: int = self._cdef_idx_find(name) + if cd_hit < 0: return 0 + # The index points at the first same-name entry, which may belong to another + # module; only narrow when it is a current-module entry, else scan all. + if self._cdef_module_match(sizet(cd_hit), module) != 0: + cd_start = sizet(cd_hit) + cd_end = sizet(cd_hit + 1) for i in range(int(cd_start), int(cd_end)): nm: str = self.CDefineNames.get(i) - if nm is not None and string.strcmp(nm, name) == 0: + if nm is not None and string.strcmp(nm, name) == 0 and self._cdef_module_match(i, module) != 0: val_str: str = self.CDefineValues.get(i) if val_str is not None: self.CDefineFound = 1 @@ -381,14 +430,14 @@ novirtual class TypeGenCDef(TypeGenBase.TypeGenBase): return string.atoll(val_str) return string.atoll(val_str) return 0 - def lookup_cdefine_bits(self, name: str) -> int: + def lookup_cdefine_bits(self, name: str, module: str = None) -> int: """Look up a DefineT constant's value-expression bit width; 0 = unknown (width inferred by value). 同一名字可能被注册**多次**:本模块注册带位宽(如 WORD(...) 或声明注解),而 stub/.vpi 等路径以 bits=0 注册。若用「命中即返回」,哈希索引先命中的可能是那条 0 位宽记录, 于是已知位宽被丢掉、常量被物化成 i32。故这里**全表扫描并优先返回非 0 位宽**。 """ - if self.CDefineNames is None or self.CDefineBits is None or name is None: return 0 + if self.CDefineNames is None or self.CDefineMods is None or self.CDefineBits is None or name is None: return 0 n: sizet = len(self.CDefineNames) # 同一名字可能被注册多次:stub/.vpi 等早期路径先注册(位宽可能未知 → 兜底值), # 本模块自己的 define 翻译在后(注解/强转齐全,最权威)。故取**最后**一条非零记录, @@ -397,31 +446,32 @@ novirtual class TypeGenCDef(TypeGenBase.TypeGenBase): for i in range(int(n)): nm: str = self.CDefineNames.get(i) if nm is None or string.strcmp(nm, name) != 0: continue + if self._cdef_module_match(i, module) == 0: continue if sizet(i) >= len(self.CDefineBits): continue b: int = self.CDefineBits.get(i) if b != 0: found = b return found - def lookup_cdefine_double(self, name: str) -> double: + def lookup_cdefine_double(self, name: str, module: str = None) -> double: """Look up a DefineT constant and return its double value (float or int).""" self.CDefineFound = 0 self.CDefineIsDouble = 0 self.CDefineIsStr = 0 - if self.CDefineNames is None: return 0.0 + if self.CDefineNames is None or self.CDefineMods is None: return 0.0 n: sizet = len(self.CDefineNames) i: sizet - # Name-index fast path: when trusted (Built==n and name non-null) locate the single candidate - # (== first scan hit); if unavailable fall back to the full linear scan below. cd_start: sizet = 0 cd_end: sizet = n - if name is not None and self.CDefineNameIdxBuilt == int(n) and self.CDefineNameIdx is not None and self.CDefineNameIdxCap > 0: - cd_hit: int = self._cdef_idx_find(name) - if cd_hit < 0: return 0.0 - cd_start = sizet(cd_hit) - cd_end = sizet(cd_hit + 1) + if module is None or module[0] == '\0': + if name is not None and self.CDefineNameIdxBuilt == int(n) and self.CDefineNameIdx is not None and self.CDefineNameIdxCap > 0: + cd_hit: int = self._cdef_idx_find(name) + if cd_hit < 0: return 0.0 + if self._cdef_module_match(sizet(cd_hit), module) != 0: + cd_start = sizet(cd_hit) + cd_end = sizet(cd_hit + 1) for i in range(int(cd_start), int(cd_end)): nm: str = self.CDefineNames.get(i) # Fast path: strcmp equality implies equal first char, so skip strcmp when it differs. - if nm is not None and nm[0] == name[0] and string.strcmp(nm, name) == 0: + if nm is not None and nm[0] == name[0] and string.strcmp(nm, name) == 0 and self._cdef_module_match(i, module) != 0: val_str: str = self.CDefineValues.get(i) if val_str is not None: self.CDefineFound = 1 @@ -436,26 +486,26 @@ novirtual class TypeGenCDef(TypeGenBase.TypeGenBase): ret_i2: double = double(string.atoll(val_str)) return ret_i2 return 0.0 - def lookup_cdefine_string(self, name: str) -> str: + def lookup_cdefine_string(self, name: str, module: str = None) -> str: """Look up a string DefineT value (valid when found=1 and is_str=1).""" self.CDefineFound = 0 self.CDefineIsDouble = 0 self.CDefineIsStr = 0 - if self.CDefineNames is None: return None + if self.CDefineNames is None or self.CDefineMods is None: return None n: sizet = len(self.CDefineNames) i: sizet - # Name-index fast path: when trusted (Built==n and name non-null) locate the single candidate - # (== first scan hit); if unavailable fall back to the full linear scan below. cd_start: sizet = 0 cd_end: sizet = n - if name is not None and self.CDefineNameIdxBuilt == int(n) and self.CDefineNameIdx is not None and self.CDefineNameIdxCap > 0: - cd_hit: int = self._cdef_idx_find(name) - if cd_hit < 0: return None - cd_start = sizet(cd_hit) - cd_end = sizet(cd_hit + 1) + if module is None or module[0] == '\0': + if name is not None and self.CDefineNameIdxBuilt == int(n) and self.CDefineNameIdx is not None and self.CDefineNameIdxCap > 0: + cd_hit: int = self._cdef_idx_find(name) + if cd_hit < 0: return None + if self._cdef_module_match(sizet(cd_hit), module) != 0: + cd_start = sizet(cd_hit) + cd_end = sizet(cd_hit + 1) for i in range(int(cd_start), int(cd_end)): nm: str = self.CDefineNames.get(i) - if nm is not None and string.strcmp(nm, name) == 0: + if nm is not None and string.strcmp(nm, name) == 0 and self._cdef_module_match(i, module) != 0: val_str: str = self.CDefineValues.get(i) if val_str is not None: self.CDefineFound = 1 @@ -491,6 +541,7 @@ novirtual class TypeGenCDef(TypeGenBase.TypeGenBase): def clear_cdefine_constants(self) -> None: """Clear the DefineT table body in place, keeping the PermPool table pointers.""" if self.CDefineNames is not None: self.CDefineNames.clear() + if self.CDefineMods is not None: self.CDefineMods.clear() if self.CDefineValues is not None: self.CDefineValues.clear() if self.CDefineDoubleFlags is not None: self.CDefineDoubleFlags.clear() if self.CDefineStrFlags is not None: self.CDefineStrFlags.clear() @@ -498,7 +549,9 @@ novirtual class TypeGenCDef(TypeGenBase.TypeGenBase): # Macro-list table is per-file too: a stale entry would let `*NAME` resolve a list that # belongs to another file's compilation unit. if self.CDefineListNames is not None: self.CDefineListNames.clear() + if self.CDefineListMods is not None: self.CDefineListMods.clear() if self.CDefineListValues is not None: self.CDefineListValues.clear() + self.CDefineLastModule = None # Clear index slots (store index+1; 0=empty) and reset Built to 0 (empty table and index # in sync, matching a freshly built table before its first register). if self.CDefineNameIdx is not None and self.CDefineNameIdxCap > 0: