fix(call): 类名与模块同名时不再误报“模块未导入”
- _fail_on_unimported_module_call 先按文件级类名表(is_file_class)与 (name, sha1) 判定类静态调用,再走模块判定 - 修复 TypeSpec.vp 中 TypeSpec.__sizeof__() 被当模块调用而报 NameError - 注:本文件同时包含此前未提交的既有改动,按整文件提交
This commit is contained in:
1 parent
e2192db72d
commit
30e0a71cc6
1 file changed
+202
-18
@@ -1,4 +1,5 @@
|
||||
import lib.core.Gen.GenTypes as GenTypes
|
||||
import lib.core.CompilerFactory as CompilerFactory
|
||||
from stdint import *
|
||||
import ast
|
||||
import mlirlite
|
||||
@@ -9,6 +10,7 @@ import viperlib
|
||||
import lib.core.Gen.Translator as HT
|
||||
import lib.core.Gen.Type.TypeSpec as TSpec
|
||||
import lib.Stub.StubGen.VpiReader as VpiReader
|
||||
import lib.core.VLogger as VLogger
|
||||
|
||||
from . import CallGenBase
|
||||
import lib.core.Gen.GenApi as GenApi
|
||||
@@ -145,9 +147,117 @@ novirtual class CallGenMixin(CallGenBase.CallGenBase):
|
||||
cur_param = mlirlite.param_get_next(cur_param)
|
||||
return 0
|
||||
|
||||
# _defaults_entry_from_vpi - 全局表里没有该函数的缺省值时,直接按定义模块 .vpi 签名临时造一条
|
||||
# 快照条目(kinds/ints/strs 由文本解析得到,字符串已深拷贝进 PermPool),仅本次调用使用。
|
||||
def _defaults_entry_from_vpi(self, pool: memhub.MemBuddy*, sha1: str, name: str) -> GenTypes.FuncDefaultsEntry*:
|
||||
"""按 .vpi 签名临时构造缺省值条目;无缺省值或不可重建返回 None"""
|
||||
cap: int = 128
|
||||
# 快照数组放 PermPool:条目可能在后续 pool reset 之后才被消费,放 pool 会悬空。
|
||||
kinds: int* = CompilerFactory.factory.PermPool.alloc(sizet(cap) * 4)
|
||||
ints: int64* = CompilerFactory.factory.PermPool.alloc(sizet(cap) * 8)
|
||||
strs: sizet* = CompilerFactory.factory.PermPool.alloc(sizet(cap) * 8)
|
||||
if kinds is None or ints is None or strs is None: return None
|
||||
enc: int = VpiReader.LookupSignDefaultsByName(pool, sha1, name, kinds, ints, strs)
|
||||
if enc < 0: return None
|
||||
dc: int = enc & 0xFF
|
||||
pc: int = enc >> 8
|
||||
if dc <= 0: return None
|
||||
ent_mem: bytes = pool.alloc(sizet(GenTypes.FuncDefaultsEntry.__sizeof__()))
|
||||
if ent_mem is None: return None
|
||||
ent: GenTypes.FuncDefaultsEntry* = GenTypes.FuncDefaultsEntry*(uint64(ent_mem))
|
||||
ent.Name = None
|
||||
ent.ModuleSha1 = None
|
||||
ent.Defaults = None
|
||||
ent.DefaultCount = dc
|
||||
ent.ParamCount = pc
|
||||
ent.ConstKinds = kinds
|
||||
ent.ConstInts = ints
|
||||
ent.ConstStrs = strs
|
||||
return ent
|
||||
|
||||
# _fill_defaults_late - call_name 定型后按 {sha1 前缀}.{名字} 再补一次缺省值。
|
||||
# 早期补参用的是 dfx_callee_sha,链式接收者(`self.Gen.X.m(...)`)解析不出发起模块时它会
|
||||
# 退化成调用方 SHA1,表查不到;此时 call_name 已带定义模块 SHA1 前缀,是权威键,再补一次。
|
||||
# 返回补齐后的实参个数。
|
||||
def _fill_defaults_late(self, pool: memhub.MemBuddy*, mod: mlirlite.LLVMModule*, builder: mlirlite.IRBuilder*, trans: HT.Translator*, cl: ast.Call*, call_name: str, head: mlirlite.Value*, can: sizet) -> sizet:
|
||||
"""按 call_name 里的定义模块 SHA1 前缀补缺省参数;返回新的实参个数"""
|
||||
if head is None or can == 0 or call_name is None: return can
|
||||
if self.Gen.CallGenLib._is_sha1_mangled_call(call_name) == 0: return can
|
||||
sh_buf: str = pool.alloc(17)
|
||||
if sh_buf is None: return can
|
||||
string.strncpy(sh_buf, call_name, 16)
|
||||
sh_buf[16] = '\0'
|
||||
cname: str = call_name + 17
|
||||
if cname[0] == '\0': return can
|
||||
# 只做 SHA1 隔离的查找:表按 (名字, 定义模块 SHA1) 定位;表里没有就直接读该模块的 .vpi。
|
||||
# 绝不按裸名跨模块搜(`run` 这类名字在多个模块里都有,会拿错签名去补参/校验)。
|
||||
de: GenTypes.FuncDefaultsEntry* = self.Gen.CallGenLeafKit.find_func_defaults(cname, sh_buf)
|
||||
if de is None: de = self._defaults_entry_from_vpi(pool, sh_buf, cname)
|
||||
if de is None: return can
|
||||
n: int = int(can)
|
||||
if de.DefaultCount <= 0 or n >= de.ParamCount: return can
|
||||
df_off: int = de.ParamCount - de.DefaultCount
|
||||
df_i: int
|
||||
for df_i in range(int(de.DefaultCount)):
|
||||
df_pidx: int = df_off + df_i
|
||||
if df_pidx < n: continue
|
||||
df_val: mlirlite.Value* = self.Gen.CallGenLeafKit._build_const_from_snapshot(pool, mod, de, df_i, trans, builder)
|
||||
if df_val is None:
|
||||
self.Gen.TypeGenErr.fatal_error(cl, f"default argument {df_i + 1} of '{call_name}' cannot be rebuilt into a value; refusing to emit a call with fewer operands than the callee declares", "TypeError")
|
||||
df_val.Next = None
|
||||
df_tail: mlirlite.Value* = head
|
||||
while df_tail.Next is not None: df_tail = df_tail.Next
|
||||
df_tail.Next = df_val
|
||||
n += 1
|
||||
# 补完仍少于声明形参:说明签名里的缺省值不够用,直接给出可读的 VPC Diag,
|
||||
# 不要放行到 mlir-opt 变成 "incorrect number of operands"。
|
||||
if n < de.ParamCount:
|
||||
self.Gen.TypeGenErr.fatal_error(cl, f"call to '{call_name}' passes {n} argument(s) but the callee declares {de.ParamCount} parameter(s); only {de.DefaultCount} usable default value(s) are available", "TypeError")
|
||||
return sizet(n)
|
||||
|
||||
# _fail_on_unimported_module_call - 模块限定调用的模块必须已导入(否则报 NameError)
|
||||
# `mod.func(...)` 里的 mod 若确实是模块表里的模块、但本文件没 import,后续会把它当成
|
||||
# "外部 C 函数" 去猜返回类型,报出与真因无关的 TypeError
|
||||
# (现场:vpui.vp 用了 viperlib.snprintf 却漏写 `import viperlib`,报的是
|
||||
# "external function return type is unregistered ... snprintf")。
|
||||
def _fail_on_unimported_module_call(self, pool: memhub.MemBuddy*, trans: HT.Translator*, cl: ast.Call*) -> int:
|
||||
"""Fail with NameError when `mod.f(...)` names a known module this file never imported."""
|
||||
if cl is None or cl.func is None or trans is None: return 0
|
||||
if cl.func.kind() != ast.ASTKind.Attribute: return 0
|
||||
at: ast.Attribute* = ast.Attribute*(cl.func)
|
||||
if at.attr is None or at.value is None: return 0
|
||||
# 只处理一级模块段;多级路径(os._win32.exists)与表达式接收者交给别的路径
|
||||
if at.value.kind() != ast.ASTKind.Name: return 0
|
||||
nm: ast.Name* = ast.Name*(at.value)
|
||||
if nm.id is None: return 0
|
||||
# obj.method():接收者是局部/全局变量,不是模块
|
||||
if trans.SymTab is not None:
|
||||
if self.Gen.VarGen.lookup_var(trans.SymTab, nm.id) is not None: return 0
|
||||
# 已导入的模块
|
||||
if trans._imported_modules is not None:
|
||||
if self.Gen.ImportsGen.is_module_imported(trans._imported_modules, nm.id) != 0: return 0
|
||||
# import-as 别名 / from-import 名字
|
||||
if trans._from_imports is not None:
|
||||
if self.Gen.ImportsGen.lookup_from_import(trans._from_imports, nm.id, 0) is not None: return 0
|
||||
# REPL 会话宽松:会话里允许后补 import
|
||||
if trans.ReplCtx is not None: return 0
|
||||
# 类名优先:`TypeSpec.__sizeof__()` 是类静态调用,不是模块调用。类名与模块名同名时
|
||||
# (现场:TypeSpec.vp 里模块名 = 类名 = TypeSpec)不能因为"没 import 自己"而误报。
|
||||
# 先用文件级类名表(不依赖 StructTable 是否已注册),再补 (name, sha1) 精确查找。
|
||||
if pool is not None:
|
||||
if self.Gen.StructGen.is_file_class(pool, nm.id) != 0: return 0
|
||||
if trans.ModuleSha1 is not None:
|
||||
if self.Gen.StructGen.find_struct_by_module(nm.id, trans.ModuleSha1) is not None: return 0
|
||||
# 只有"确实是模块"才判;未定义名字交给其它路径
|
||||
if self.Gen.StructGen._resolve_module_sha1(nm.id, trans._from_imports) is None: return 0
|
||||
self.Gen.TypeGenErr.fatal_error(at, f"call '{nm.id}.{at.attr}(...)' targets module '{nm.id}', but this file does not import it; add `import {nm.id}`. Without the import the call degrades into an external C-function call and its return type cannot be resolved (pointer returns would be truncated to i32)", "NameError")
|
||||
return 0
|
||||
|
||||
def translate_call(self, pool: memhub.MemBuddy*, builder: mlirlite.IRBuilder*, mod: mlirlite.LLVMModule*, node: ast.AST*, funcs_ptr: rawptr, func_count: int, trans: HT.Translator* = None) -> mlirlite.Value*:
|
||||
"""Translate a Call node and return the result Value."""
|
||||
cl: ast.Call* = ast.Call*(node)
|
||||
# 模块限定调用先校验"模块已导入":不导入会被降级成外部 C 函数,报出与真因无关的 TypeError
|
||||
self._fail_on_unimported_module_call(pool, trans, cl)
|
||||
# Static symbols must not be used cross-module: fail fast on mod.fn(...) hitting another
|
||||
# module's static; the check lives in _CheckStaticCallCrossModule to keep this huge function inferable.
|
||||
self._CheckStaticCallCrossModule(pool, trans, cl)
|
||||
@@ -310,8 +420,8 @@ novirtual class CallGenMixin(CallGenBase.CallGenBase):
|
||||
# VPC compiles generic functions as plain functions, so drop [T] if a same-name function exists.
|
||||
if self.find_func_in_module(mod, gen_class_name) is not None: func_name = gen_class_name
|
||||
else:
|
||||
# Builtin pointer/type subscript cast: (rawptr[list[int]])(lst), (t.CPtr[T])(x),
|
||||
# (t.CArray[i8, N])(x); rawptr/CPtr/CArray are type subscripts, not generic templates.
|
||||
# Builtin pointer/type subscript cast: (rawptr[list[int]])(lst), (rawptr[T])(x),
|
||||
# (t.ArrayT[i8, N])(x); rawptr/PtrT/ArrayT are type subscripts, not generic templates.
|
||||
if cl.args is not None and len(cl.args) == 1:
|
||||
sub_cast_ty: mlirlite.LLVMType* = self.Gen.TypeGenResolve.resolve_annotation_type(pool, cl.func, trans._imported_modules, trans._from_imports, trans, 0)
|
||||
if sub_cast_ty is not None:
|
||||
@@ -685,7 +795,7 @@ novirtual class CallGenMixin(CallGenBase.CallGenBase):
|
||||
ff_nm2: str = mlirlite.function_get_name(found_func)
|
||||
if ff_nm2 is not None:
|
||||
if string.strncmp(ff_nm2, mod_sha1_chk, 16) != 0:
|
||||
# CExport / t.State 型跨模块函数**定义与声明都用裸名**(stdlib.free、
|
||||
# ExportT / t.State 型跨模块函数**定义与声明都用裸名**(stdlib.free、
|
||||
# CPython 的 PyList_Size 等):裸名不是"绑错了模块",必须放行。
|
||||
# 否则这里会被清成 None → 关键字实参路径整体跳过 → 静默生成
|
||||
# 零实参调用(现场:stdlib.free(p = p) → llvm.call @free() → mlir-opt
|
||||
@@ -755,6 +865,11 @@ novirtual class CallGenMixin(CallGenBase.CallGenBase):
|
||||
bi_ret = mlirlite.Double(pool)
|
||||
bi_p1 = mlirlite.Double(pool)
|
||||
bi_p2 = mlirlite.Double(pool)
|
||||
elif string.strcmp(func_name, "abs") == 0:
|
||||
# abs 也是语言内建(int -> int),由 builtins.vp 提供实现;不再落到 C 库的
|
||||
# `abs` 声明上,否则无 libc 的目标(VKernel)会 undefined symbol: abs。
|
||||
bi_ret = mlirlite.Int32(pool)
|
||||
bi_p1 = mlirlite.Int32(pool)
|
||||
if bi_ret is not None:
|
||||
bi_sym: str = func_name
|
||||
if self.Gen.CallGenBase.is_cexport_func(bi_sha1, func_name) == 0:
|
||||
@@ -824,17 +939,42 @@ novirtual class CallGenMixin(CallGenBase.CallGenBase):
|
||||
if rr_sha1 is not None:
|
||||
VpiReader._ensure_vpi_ret_annot_loaded(rr_sha1, self.Gen.CallGenBase.TempDir)
|
||||
ret_annot_fc = VpiReader._lookup_vpi_ret_annot(rr_sha1, None, func_name)
|
||||
if ret_annot_fc is not None: vpi_ret_ty = VpiReader._resolve_vpi_ret_annot_str(pool, ret_annot_fc, mlirlite.Int32(pool), mlirlite.Ptr(pool, mlirlite.Int8(pool)))
|
||||
if ret_annot_fc is not None:
|
||||
vpi_ret_ty = VpiReader._resolve_vpi_annot_with_typedefs(pool, mod_ret_sha1, ret_annot_fc, mlirlite.Int32(pool), mlirlite.Ptr(pool, mlirlite.Int8(pool)))
|
||||
# Generic instantiation (`mlirlite.function_get_params -> GSList[Param]*`)
|
||||
# needs on-demand specialization; the string resolver cannot build it.
|
||||
if vpi_ret_ty is None: vpi_ret_ty = self.Gen.CallGenBase._resolve_generic_annot_str(trans, pool, ret_annot_fc)
|
||||
if vpi_ret_ty is not None: call_ret_ty = vpi_ret_ty
|
||||
elif mod_ret_sha1 is not None:
|
||||
# Cross-module bare CExport function (e.g. mm.malloc -> @malloc): resolve the return type
|
||||
# Cross-module bare ExportT function (e.g. mm.malloc -> @malloc): resolve the return type
|
||||
# exactly from the dependency module, else the default i32 truncates the pointer (mm.malloc root cause).
|
||||
cross_ret: mlirlite.LLVMType* = self._lookup_method_ret_ty_via_vpi(pool, mod_ret_sha1, None, func_name)
|
||||
cross_ret: mlirlite.LLVMType* = self._lookup_method_ret_ty_via_vpi(pool, mod_ret_sha1, None, func_name, trans)
|
||||
if cross_ret is None:
|
||||
if trans is not None and trans._declare_only == 0: cross_ret = self._LookupMethodRetTyFromTextLL(pool, mod_ret_sha1, None, func_name, 1)
|
||||
if cross_ret is not None: call_ret_ty = cross_ret
|
||||
else:
|
||||
# Dependency text.ll not generated yet (Phase A middle pass): fatal_if_unknown=0, only a temporary placeholder.
|
||||
# 结果被丢弃(独立调用语句)时不 fail-fast:占位 i32 无害,没必要中断编译。
|
||||
if trans is not None and trans._declare_only == 0 and trans._discard_result == 0:
|
||||
vpi_rc_d: int = 0
|
||||
vpi_annot_d: str = None
|
||||
if self.Gen.CallGenBase.TempDir is not None and self.Gen.CallGenBase.TempDirLen > 0:
|
||||
vpi_rc_d = VpiReader._ensure_vpi_ret_annot_loaded(mod_ret_sha1, self.Gen.CallGenBase.TempDir)
|
||||
vpi_annot_d = VpiReader._lookup_vpi_ret_annot(mod_ret_sha1, None, func_name)
|
||||
annot_s_d: str = "-"
|
||||
if vpi_annot_d is not None: annot_s_d = vpi_annot_d
|
||||
# sha1 来源拆解:exact / suffix 各自返回什么,用来区分"存表里就是错的"
|
||||
# 与"后缀匹配撞到了别的模块"(两者修法完全不同)。
|
||||
ex_s_d: str = "-"
|
||||
if mod_path_ret is not None:
|
||||
_ex_d: str = self.Gen.CallGenBase._lookup_module_sha1(mod_path_ret)
|
||||
if _ex_d is not None: ex_s_d = _ex_d
|
||||
sf_s_d: str = "-"
|
||||
if mod_path_ret is not None:
|
||||
_sf_d: str = self.Gen.CallGenBase._lookup_module_sha1_suffix(mod_path_ret)
|
||||
if _sf_d is not None: sf_s_d = _sf_d
|
||||
# 走 VPC Diag(源码行 + 调用栈),而不是单行 VLogger.error:
|
||||
# 单行输出丢掉了位置与调用链,无法定位到底是谁在什么上下文里调用了它。
|
||||
self.Gen.TypeGenErr.fatal_error(node, f"cross-module function return type unresolved: {mod_path_ret}.{func_name} (sha1={mod_ret_sha1}, exact={ex_s_d}, suffix={sf_s_d}, tempdir_set={self.Gen.CallGenBase.TempDirLen}, vpi_load_rc={vpi_rc_d}, vpi_annot={annot_s_d}); a placeholder i32 would truncate a pointer return", "TypeError")
|
||||
call_ret_ty = self.Gen.CallGenLib._infer_external_func_ret_ty(pool, func_name, node, 0)
|
||||
else:
|
||||
# Bare call (cl.func is Name, mod_ret_sha1=None): from-import/closure/function-pointer targets are resolvable (placeholder, no fail-fast); pure external C functions fail-fast.
|
||||
@@ -885,15 +1025,15 @@ novirtual class CallGenMixin(CallGenBase.CallGenBase):
|
||||
return self.Gen.CallGenLeafKit.translate_closure_call(pool, builder, mod, fp_alloca, cargs, can, trans)
|
||||
# Function-pointer variable (t.Callable annotation -> i8** alloca): build the signature from args and call indirectly.
|
||||
if self.Gen.CallGenLeafKit._is_func_ptr_alloca(fp_alloca) != 0:
|
||||
# Distinguish t.Callable (true function pointer) from t.CPtr (closure pointer): a t.CPtr
|
||||
# Distinguish t.Callable (true function pointer) from rawptr (closure pointer): a rawptr
|
||||
# holds {fn_ptr, env_ptr} and must pass env, else a direct call jumps into closure data and crashes.
|
||||
ve_fp: GenTypes.VarEntry* = self.Gen.VarGen.lookup_var_entry(trans.SymTab, func_name)
|
||||
is_cptr_annot_fp: int = 0
|
||||
fp_annot: str = self.Gen.VarGen.var_annot_class_name(ve_fp)
|
||||
if fp_annot is not None:
|
||||
# [S4] CPtr marker = annotation base name exactly "CPtr": t.Callable[[A],R] also resolves to
|
||||
# [S4] PtrT marker = annotation base name exactly "PtrT": t.Callable[[A],R] also resolves to
|
||||
# Ptr(i8) (same Spec), so only the annotation name can carry the marker; substring matching would misfire.
|
||||
if TSpec.SpecTypeNameBaseIs(fp_annot, "CPtr") != 0: is_cptr_annot_fp = 1
|
||||
if TSpec.SpecTypeNameBaseIs(fp_annot, "PtrT") != 0: is_cptr_annot_fp = 1
|
||||
if is_cptr_annot_fp == 0:
|
||||
return self.Gen.CallGenLeafKit._translate_func_ptr_call(pool, builder, mod, fp_alloca, func_name, cargs, can, trans)
|
||||
return self.Gen.CallGenLeafKit.translate_closure_call(pool, builder, mod, fp_alloca, cargs, can, trans)
|
||||
@@ -1010,7 +1150,7 @@ novirtual class CallGenMixin(CallGenBase.CallGenBase):
|
||||
else: arg_val = self.Gen.ExprGenBase.coerce_to_type(builder, arg_val, ext_param_ty, arg)
|
||||
elif found_func is not None:
|
||||
# found_func present: coerce to the param type (e.g. stdlib.malloc(size: sizet)); a
|
||||
# CDefine/i32 literal passed to an i64 param would otherwise leave call(i32) vs define(i64) mismatched.
|
||||
# DefineT/i32 literal passed to an i64 param would otherwise leave call(i32) vs define(i64) mismatched.
|
||||
if fp is not None:
|
||||
if fpty is not None:
|
||||
# "str/bytes is not a generic pointer wildcard": a same-module bare function whose
|
||||
@@ -1101,6 +1241,9 @@ novirtual class CallGenMixin(CallGenBase.CallGenBase):
|
||||
if rr_src_dx is not None:
|
||||
rr_sha_dx: str = self._lookup_module_sha1(rr_src_dx)
|
||||
if rr_sha_dx is not None: gd_entry = self.Gen.CallGenLeafKit.find_func_defaults(func_name, rr_sha_dx)
|
||||
# 键失配兜底:链式接收者(`self.Gen.ExprGenMixin.translate_value(...)`)解析不出发起模块,
|
||||
# dfx_callee_sha 退化成调用方自己的 SHA1,限定查表落空 —— 由后面的 _fill_defaults_late
|
||||
# 用 call_name 里的定义模块 SHA1 前缀(权威键)再补一次,这里不做跨模块裸名猜测。
|
||||
# Override when df_default_count<=0 too: the caller table may hold a same-name entry with empty
|
||||
# Defaults (a VpiReader declare), so the real global entry must win or filling never triggers.
|
||||
if gd_entry is not None and (df_defaults is None or df_default_count <= 0):
|
||||
@@ -1176,7 +1319,7 @@ novirtual class CallGenMixin(CallGenBase.CallGenBase):
|
||||
fp_z = mlirlite.param_get_next(fp_z)
|
||||
fpi_z += 1
|
||||
|
||||
# Use the actual function name from the module (may carry a SHA1 prefix); CExtern/State/CExport
|
||||
# Use the actual function name from the module (may carry a SHA1 prefix); ExternT/State/ExportT
|
||||
# functions use the bare name. Both declare and define must use found_func.Name (mismatched names cause undefined references).
|
||||
call_name: str = func_name
|
||||
# Original function name (recorded for aliased imports, e.g. from X import parse as json_parse -> "parse"),
|
||||
@@ -1186,8 +1329,8 @@ novirtual class CallGenMixin(CallGenBase.CallGenBase):
|
||||
found_name: str = mlirlite.function_get_name(found_func)
|
||||
if found_name is not None:
|
||||
call_name = found_name
|
||||
# CExport bare-name fallback: a cross-module stub declare may carry a SHA1 prefix while the
|
||||
# definition uses @malloc, causing a link error; extract the sha1 and use the bare name if it is a CExport.
|
||||
# ExportT bare-name fallback: a cross-module stub declare may carry a SHA1 prefix while the
|
||||
# definition uses @malloc, causing a link error; extract the sha1 and use the bare name if it is a ExportT.
|
||||
if found_func.IsDeclared != 0:
|
||||
# Use the single source of truth GenApi.sha1_prefix_len (converged in C10).
|
||||
if GenApi.sha1_prefix_len(found_name) != 0:
|
||||
@@ -1258,7 +1401,7 @@ novirtual class CallGenMixin(CallGenBase.CallGenBase):
|
||||
rs_found = 1
|
||||
call_name = func_name
|
||||
else:
|
||||
# Original module has a plain def (not CExport/State): a vpi hit keeps the original
|
||||
# Original module has a plain def (not ExportT/State): a vpi hit keeps the original
|
||||
# module sha (@{mod_sha1}.func) instead of falling back to a star candidate that would hijack resolution.
|
||||
orig_has_def: int = 0
|
||||
if self.Gen.CallGenBase.TempDir is not None and self.Gen.CallGenBase.TempDirLen > 0:
|
||||
@@ -1268,7 +1411,7 @@ novirtual class CallGenMixin(CallGenBase.CallGenBase):
|
||||
# Original module does not define it: fall back to the first candidate (e.g. ast._lexer_init in ast.lexer).
|
||||
reexport_sha1: str = self._lookup_module_sha1(reexport_src)
|
||||
if reexport_sha1 is not None: mod_sha1 = reexport_sha1
|
||||
# CExport functions define with the bare name (@strlen), so the cross-module call must use the bare name too.
|
||||
# ExportT functions define with the bare name (@strlen), so the cross-module call must use the bare name too.
|
||||
if rs_found != 0:
|
||||
pass # Re-export defining module already verified and bare name set.
|
||||
elif self.Gen.CallGenBase.is_cexport_func(mod_sha1, func_name) != 0: call_name = func_name
|
||||
@@ -1319,7 +1462,7 @@ novirtual class CallGenMixin(CallGenBase.CallGenBase):
|
||||
from_sha1: str = None
|
||||
if trans is not None and trans._from_imports is not None: from_sha1 = self._lookup_mod_sha1(pool, trans._from_imports, xmod_path, 0)
|
||||
if from_sha1 is not None:
|
||||
# CExport functions define with the bare name, so the cross-module call must use the bare name.
|
||||
# ExportT functions define with the bare name, so the cross-module call must use the bare name.
|
||||
if self.Gen.CallGenBase.is_cexport_func(from_sha1, func_name) != 0: call_name = func_name
|
||||
else:
|
||||
fmangled_len: sizet = string.strlen(from_sha1) + string.strlen(func_name) + 2
|
||||
@@ -1328,6 +1471,22 @@ novirtual class CallGenMixin(CallGenBase.CallGenBase):
|
||||
viperlib.snprintf(fmangled, fmangled_len, "%s.%s", from_sha1, func_name)
|
||||
call_name = fmangled
|
||||
else:
|
||||
# The base name is neither a local/closure variable nor a class: the
|
||||
# attribute call can only be a module-qualified call, so an unresolved
|
||||
# module means the name itself is undefined (missing import). Reporting
|
||||
# "module SHA1 not found" here hid the real cause (现场: VLogger.error
|
||||
# used without `import lib.core.VLogger as VLogger`).
|
||||
base_defined: int = 0
|
||||
if trans is not None:
|
||||
if self.Gen.VarGen.lookup_var(trans.SymTab, xmod_path) is not None: base_defined = 1
|
||||
if base_defined == 0 and GenApi.is_nonlocal_name(trans, xmod_path) != 0: base_defined = 1
|
||||
if base_defined == 0 and self.Gen.StructGen.find_struct(xmod_path) is not None: base_defined = 1
|
||||
if base_defined == 0:
|
||||
err_msg_ne: str = pool.alloc(256)
|
||||
if err_msg_ne is not None:
|
||||
viperlib.snprintf(err_msg_ne, 256, "name '%s' is not defined (used as a module in '%s.%s'; missing an import?)", xmod_path, xmod_path, func_name)
|
||||
self.Gen.TypeGenErr.fatal_error(xmod_at, err_msg_ne, "NameError")
|
||||
self.Gen.TypeGenErr.fatal_error(xmod_at, f"name '{xmod_path}' is not defined", "NameError")
|
||||
err_msg2: str = pool.alloc(256)
|
||||
if err_msg2 is not None:
|
||||
viperlib.snprintf(err_msg2, 256, "Cross-module call '%s.%s': module SHA1 not found", xmod_path, func_name)
|
||||
@@ -1423,11 +1582,36 @@ novirtual class CallGenMixin(CallGenBase.CallGenBase):
|
||||
cf_ret: mlirlite.LLVMType* = self._LookupMethodRetTyFromTextLL(pool, cf_sha1, None, cf_bare, 1)
|
||||
if cf_ret is not None: call_ret_ty = cf_ret
|
||||
else:
|
||||
# 编译器内置(ord/chr/min/max):builtins.vp 只是注释桩,没有任何声明,
|
||||
# 签名由上面 "builtins auto-link" 按约定合成(ord(i8)->i32 等),
|
||||
# 因此不能按"跨模块缺 vpi/text.ll"判失败,否则任何 ord/chr 调用都会误报。
|
||||
bi_synth: int = 0
|
||||
if string.strcmp(cf_bare, "ord") == 0: bi_synth = 1
|
||||
elif string.strcmp(cf_bare, "chr") == 0: bi_synth = 1
|
||||
elif string.strcmp(cf_bare, "min") == 0: bi_synth = 1
|
||||
elif string.strcmp(cf_bare, "max") == 0: bi_synth = 1
|
||||
elif string.strcmp(cf_bare, "abs") == 0: bi_synth = 1
|
||||
# Dependency text.ll does not exist (module not yet translated): Phase A translates files
|
||||
# in order (main.vp before its imported mbuddytest.vp), so keep the default type without fail-fast.
|
||||
if self._dep_text_ll_exists(pool, cf_sha1) != 0: self.Gen.TypeGenErr.fatal_error(node, f"Cannot obtain the cross-module function return type precisely (neither vpi nor text.ll hit); the default i32 will truncate pointers: {call_name}", "TypeError")
|
||||
if bi_synth == 0 and self._dep_text_ll_exists(pool, cf_sha1) != 0: self.Gen.TypeGenErr.fatal_error(node, f"Cannot obtain the cross-module function return type precisely (neither vpi nor text.ll hit); the default i32 will truncate pointers: {call_name}", "TypeError")
|
||||
stdlib.free(cf_sha1)
|
||||
stdlib.free(cf_bare)
|
||||
# 迟到的补参:call_name 此时已带定义模块 SHA1 前缀(权威键),补上早期因链式接收者
|
||||
# 退化而成查不到的缺省值;必须在 _ensure_method_declare 之前完成,否则 declare 会按
|
||||
# 少参的实参个数被建短(同一函数出现两种 arity 声明)。
|
||||
can = self._fill_defaults_late(pool, mod, builder, trans, cl, call_name, head, can)
|
||||
# 静态实参个数校验(以定义模块 .vpi 签名为准,self 不计):declare 此时可能还没创建,
|
||||
# 而 _ensure_method_declare 又会按 can 建短 declare —— 旧逻辑会把这个错误一路藏到
|
||||
# mlir-opt 的 "incorrect number of operands"。含关键字实参或可变参数时跳过。
|
||||
if self.Gen.CallGenLib._is_sha1_mangled_call(call_name) != 0 and (cl.keywords is None or len(cl.keywords) == 0):
|
||||
ar_sha: str = pool.alloc(17)
|
||||
if ar_sha is not None:
|
||||
string.strncpy(ar_sha, call_name, 16)
|
||||
ar_sha[16] = '\0'
|
||||
ar_name: str = call_name + 17
|
||||
ar_n: int = VpiReader.LookupParamCountByName(pool, ar_sha, ar_name)
|
||||
if ar_n >= 0 and int(can) != ar_n:
|
||||
self.Gen.TypeGenErr.fatal_error(cl, f"call to '{ar_name}' passes {int(can)} argument(s) but the definition declares {ar_n} parameter(s) (self excluded)", "TypeError")
|
||||
# Bare C library function (string.memset -> @memset): promote args to the standard signature
|
||||
# (third arg i32 literal -> i64) before creating the declare, so call/declare/define widths match.
|
||||
head = self.Gen.CallGenLib._promote_clib_call_args(pool, builder, mod, call_name, head, can)
|
||||
|
||||
Reference in new issue
Block a user