feat(gen,err): CastError 定位到出错表达式;vpi 生成 fail-fast;函数属性接受直接形态
- ExprGenBase:新增 _cast_fail_where,指针/整数隐式转换失败时打印出错表达式与列号(多实参调用可定位到具体实参,如 Name 'toggle_ptr') - Converter:import / from-import 别名列表为空、Alias 指针为空、name 为空分别 fail-fast,不再写出 import , 这类残缺 vpi 行(该残缺会让包重导出注册失效,报出与根因无关的 NameError) - FuncGenBase.extract_func_attrs:接受直接 @t.attr.<name> 装饰器形态(与类侧 _has_decorator 同口径),使 inline def 在无 import c 的文件里也生效 - 附:CallGenDispatch 内保留一处临时 [MDISP] 探针(MpoolOopTest pool.alloc 定位用)
This commit is contained in:
1 parent
f084da7c01
commit
7d28c9649e
4 files changed
+178
-12
No files matched your search
@@ -890,6 +890,12 @@ def _GeneratePyiFromAst(mb: memhub.MemBuddy*, tree: ast.AST*,
|
||||
if k == ast.ASTKind.Import:
|
||||
imp2: ast.Import* = ast.Import*(stmt)
|
||||
pos = _PyiEmit(buf, buf_size, pos, "import ")
|
||||
# fail-fast:别名列表为空 ⇒ 写出 `import ` 这种残缺行,import 名字
|
||||
# 全丢,包重导出注册随之失效(与下面逐个别名为空的检查互补)。
|
||||
if imp2.names is None or len(imp2.names) == 0:
|
||||
GenApi.fatal_error(stmt,
|
||||
"vpi 生成:import 语句别名列表为空(应为 ≥1 个别名)——AST 构造/解析异常",
|
||||
"NameError")
|
||||
if imp2.names is not None:
|
||||
imp2_names: list[ast.AST*]* = imp2.names
|
||||
an2: sizet = len(imp2_names)
|
||||
@@ -898,11 +904,29 @@ def _GeneratePyiFromAst(mb: memhub.MemBuddy*, tree: ast.AST*,
|
||||
if ai2 > 0:
|
||||
pos = _PyiEmit(buf, buf_size, pos, ", ")
|
||||
al2: ast.Alias* = ast.Alias*(imp2_names.get(ai2))
|
||||
if al2 is not None and al2.name is not None:
|
||||
pos = _PyiEmit(buf, buf_size, pos, al2.name)
|
||||
if al2.asname is not None and string.strlen(al2.asname) > 0:
|
||||
pos = _PyiEmit(buf, buf_size, pos, " as ")
|
||||
pos = _PyiEmit(buf, buf_size, pos, al2.asname)
|
||||
# fail-fast:别名节点或其名字为空 ⇒ 绝不静默写出 "import , "。
|
||||
# vpi 里 import 行的名字是**包重导出注册的唯一输入**
|
||||
# (VpiReader._inject_reexport_class_declare 靠它把子模块里的类以
|
||||
# 包 __init__ 的 sha1 注册别名 entry),名字静默丢失会让 `包.类`
|
||||
# 形式的模块限定注解解析落空,下游报出与根因无关的 NameError
|
||||
# (实测:ast/__init__.vpi 的 `from .base import , , ,` 名字全空
|
||||
# ⇒ ast.AST 报"方法 'Result.__init__' 形参 'elem' 类型注解解析失败")。
|
||||
# 两种成因分开报,便于一次定位:节点指针为空 = Alias 构造失败;
|
||||
# 节点在而 name 为空 = _copy_str/__init__ 未生效。
|
||||
if al2 is None:
|
||||
GenApi.fatal_error(stmt,
|
||||
"vpi 生成:import 别名**节点指针为空**(Alias(...) 构造未产出对象:"
|
||||
"pool.alloc 失败或 __init__ 未执行)——import 名字丢失会破坏包重导出注册",
|
||||
"NameError")
|
||||
if al2.name is None or al2.name[0] == '\0':
|
||||
GenApi.fatal_error(stmt,
|
||||
"vpi 生成:import 别名节点存在但 **name 为空**(_copy_str 返回 None / "
|
||||
"__init__ 未赋值 name)——import 名字丢失会破坏包重导出注册",
|
||||
"NameError")
|
||||
pos = _PyiEmit(buf, buf_size, pos, al2.name)
|
||||
if al2.asname is not None and string.strlen(al2.asname) > 0:
|
||||
pos = _PyiEmit(buf, buf_size, pos, " as ")
|
||||
pos = _PyiEmit(buf, buf_size, pos, al2.asname)
|
||||
ai2 += 1
|
||||
pos = _PyiEmit(buf, buf_size, pos, "\n")
|
||||
|
||||
@@ -917,6 +941,14 @@ def _GeneratePyiFromAst(mb: memhub.MemBuddy*, tree: ast.AST*,
|
||||
if ifr.module is not None:
|
||||
pos = _PyiEmit(buf, buf_size, pos, ifr.module)
|
||||
pos = _PyiEmit(buf, buf_size, pos, " import ")
|
||||
# fail-fast:同 Import 分支——`from X import *` 走的是 Alias("*"),
|
||||
# 列表为空或名字为空都说明 AST 坏;静默写出 `from X import ` 会让
|
||||
# 包重导出注册失效(star 也用于 re-export 判断)。
|
||||
if ifr.names is None or len(ifr.names) == 0:
|
||||
GenApi.fatal_error(stmt,
|
||||
"vpi 生成:from-import 语句别名列表为空(应为 ≥1 个别名,含 '*')"
|
||||
"——AST 构造/解析异常",
|
||||
"NameError")
|
||||
if ifr.names is not None:
|
||||
ifr_names: list[ast.AST*]* = ifr.names
|
||||
an3: sizet = len(ifr_names)
|
||||
@@ -925,11 +957,21 @@ def _GeneratePyiFromAst(mb: memhub.MemBuddy*, tree: ast.AST*,
|
||||
if ai3 > 0:
|
||||
pos = _PyiEmit(buf, buf_size, pos, ", ")
|
||||
al3: ast.Alias* = ast.Alias*(ifr_names.get(ai3))
|
||||
if al3 is not None and al3.name is not None:
|
||||
pos = _PyiEmit(buf, buf_size, pos, al3.name)
|
||||
if al3.asname is not None and string.strlen(al3.asname) > 0:
|
||||
pos = _PyiEmit(buf, buf_size, pos, " as ")
|
||||
pos = _PyiEmit(buf, buf_size, pos, al3.asname)
|
||||
# fail-fast:同 Import 分支(两种成因分开报,一次定位)。
|
||||
if al3 is None:
|
||||
GenApi.fatal_error(stmt,
|
||||
"vpi 生成:from-import 别名**节点指针为空**(Alias(...) 构造未产出对象:"
|
||||
"pool.alloc 失败或 __init__ 未执行)——包重导出注册会因此失效",
|
||||
"NameError")
|
||||
if al3.name is None or al3.name[0] == '\0':
|
||||
GenApi.fatal_error(stmt,
|
||||
"vpi 生成:from-import 别名节点存在但 **name 为空**(_copy_str 返回 None / "
|
||||
"__init__ 未赋值 name)——包重导出注册会因此失效",
|
||||
"NameError")
|
||||
pos = _PyiEmit(buf, buf_size, pos, al3.name)
|
||||
if al3.asname is not None and string.strlen(al3.asname) > 0:
|
||||
pos = _PyiEmit(buf, buf_size, pos, " as ")
|
||||
pos = _PyiEmit(buf, buf_size, pos, al3.asname)
|
||||
ai3 += 1
|
||||
pos = _PyiEmit(buf, buf_size, pos, "\n")
|
||||
|
||||
|
||||
@@ -8,6 +8,7 @@ import ast
|
||||
import llvmlite
|
||||
import memhub
|
||||
import string
|
||||
import stdio
|
||||
import stdlib
|
||||
import sys
|
||||
import viperlib
|
||||
@@ -1241,6 +1242,80 @@ novirtual class CallGenDispatch(CallGenBase.CallGenBase):
|
||||
if method_at.value.kind() == ast.ASTKind.Name and trans is not None:
|
||||
method_obj_nm: ast.Name* = ast.Name*(method_at.value)
|
||||
if method_obj_nm.id is not None:
|
||||
# [DIAG] 并排取证「已知成功」与「刚落空」的形的方法调用,看 H6 各级
|
||||
# 判据的命中差异(临时探针,定位后连同 import stdio 一起删除):
|
||||
# A) recv=__with_ctx m=__enter__ :with 绑定 asname 之前执行,已知成功
|
||||
# B) recv=pool m=alloc :与 A 同一注释类名(memhub.MemPool),
|
||||
# 但接收者 LLVM 类型来自 __enter__ 的**返回值**(A 是构造结果)
|
||||
# 只有这一对能解释 H6 为何对 B 全线落空 —— 落空且不置 hit_box 时,
|
||||
# translate_call 会把接收者名当模块名,报「跨模块调用 'pool.alloc'
|
||||
# 找不到模块 SHA1」(MpoolOopTest/App/mpooltest.vp:14)。
|
||||
dg_hit: int = 0
|
||||
if string.strcmp(func_name, "alloc") == 0 \
|
||||
and string.strcmp(method_obj_nm.id, "pool") == 0:
|
||||
dg_hit = 1
|
||||
if string.strcmp(func_name, "__enter__") == 0 \
|
||||
and string.strcmp(method_obj_nm.id, "__with_ctx") == 0:
|
||||
dg_hit = 1
|
||||
if dg_hit != 0:
|
||||
dg_obj: llvmlite.Value* = self.Gen.VarGen.lookup_var(trans.SymTab, method_obj_nm.id)
|
||||
dg_found: int = 0
|
||||
dg_ty: str = "-"
|
||||
dg_c1: str = "-"
|
||||
dg_c2: str = "-"
|
||||
dg_an: str = "-"
|
||||
dg_struct_ok: int = -1
|
||||
dg_sha1_ok: int = -1
|
||||
dg_spc_ok: int = -1
|
||||
if dg_obj is not None:
|
||||
dg_found = 1
|
||||
if dg_obj.Ty is not None:
|
||||
dg_ty = pool.alloc(256)
|
||||
if dg_ty is not None:
|
||||
llvmlite.TypePrint(dg_ty, 256, dg_obj.Ty, pool)
|
||||
dg_s1: llvmlite.LLVMType* = None
|
||||
if self.Gen.ExprGenBase.is_ptr_type(dg_obj.Ty) != 0:
|
||||
dg_s1 = dg_obj.Ty.Pointee
|
||||
if dg_s1 is not None:
|
||||
dg_x1: str = self.Gen.StructGen.get_class_name_by_type(pool, dg_s1)
|
||||
if dg_x1 is not None:
|
||||
dg_c1 = dg_x1
|
||||
if self.Gen.ExprGenBase.is_ptr_type(dg_s1) != 0:
|
||||
dg_x2: str = self.Gen.StructGen.get_class_name_by_type(pool, dg_s1.Pointee)
|
||||
if dg_x2 is not None:
|
||||
dg_c2 = dg_x2
|
||||
dg_ve: GenTypes.VarEntry* = self.Gen.VarGen.lookup_var_entry(trans.SymTab, method_obj_nm.id)
|
||||
dg_anr: str = self.Gen.VarGen.var_annot_class_name(dg_ve)
|
||||
if dg_anr is not None:
|
||||
dg_an = dg_anr
|
||||
dg_bare: str = dg_anr
|
||||
dg_dot: str = string.strrchr(dg_anr, '.')
|
||||
if dg_dot is not None:
|
||||
dg_bare = dg_dot + 1
|
||||
if self.Gen.StructGen.get_struct_type(dg_bare) is not None:
|
||||
dg_struct_ok = 1
|
||||
else:
|
||||
dg_struct_ok = 0
|
||||
if dg_dot is not None:
|
||||
dg_ml: sizet = string.strlen(dg_anr) - string.strlen(dg_bare) - 1
|
||||
dg_mb: str = pool.alloc(dg_ml + 1)
|
||||
if dg_mb is not None:
|
||||
string.strncpy(dg_mb, dg_anr, dg_ml)
|
||||
dg_mb[dg_ml] = '\0'
|
||||
if self._lookup_module_sha1(dg_mb) is not None:
|
||||
dg_sha1_ok = 1
|
||||
else:
|
||||
dg_sha1_ok = 0
|
||||
if self._special_method_ret_ty(pool, dg_bare, func_name) is not None:
|
||||
dg_spc_ok = 1
|
||||
else:
|
||||
dg_spc_ok = 0
|
||||
dg_nl: int = GenApi.is_nonlocal_name(trans, method_obj_nm.id)
|
||||
stdio.printf("[MDISP] mod=%s line=%d recv=%s m=%s found=%d nonlocal=%d recv_ty=%s cls1=%s cls2=%s annot=%s struct_ok=%d sha1_ok=%d spc_ok=%d\n",
|
||||
trans.ModuleSha1, cl.lineno, method_obj_nm.id, func_name,
|
||||
dg_found, dg_nl, dg_ty, dg_c1, dg_c2, dg_an,
|
||||
dg_struct_ok, dg_sha1_ok, dg_spc_ok)
|
||||
stdio.fflush(None)
|
||||
# nonlocal 对象:提升函数内从 env 读对象指针(load_nonlocal_var),
|
||||
# 不能 lookup_var 跨函数命中父函数 alloca(SSA 编号错乱 →
|
||||
# llc "use of undefined value")。entry.get() 等方法调用的
|
||||
|
||||
@@ -317,6 +317,42 @@ novirtual class ExprGenBase:
|
||||
if loaded is None: return obj_ptr
|
||||
return loaded
|
||||
# ============================================================
|
||||
# _cast_fail_where - 隐式转换失败时报出**出错的表达式**(→ 定位到具体实参)
|
||||
#
|
||||
# 调用点只传 AST 节点,报错原本只有"调用所在行",多实参时看不出是哪一个
|
||||
# (如 `_td_set_token(td, TD_TOKEN_IN, dev_addr, endpoint, toggle_ptr, max_pkt)`
|
||||
# 报指针→整数,无法判断是哪个实参)。此处按节点形态给出可读名:
|
||||
# Name→'id'、Attribute→'.attr'、Call/Subscript→形态名,并附列号(源码行已
|
||||
# 由 traceback 打出,列号即可指到具体实参)。
|
||||
# ============================================================
|
||||
def _cast_fail_where(self, node: ast.AST*) -> str:
|
||||
"""取隐式转换失败的定位文本(出错表达式 + 列号)"""
|
||||
if node is None:
|
||||
return "节点未知(无 AST 信息)"
|
||||
k: int = node.kind()
|
||||
pos: int = -1
|
||||
if k == ast.ASTKind.Name:
|
||||
nm: ast.Name* = ast.Name*(node)
|
||||
if nm.id is not None:
|
||||
pos = nm.col_offset
|
||||
return f"Name '{nm.id}'(列 {pos})"
|
||||
elif k == ast.ASTKind.Attribute:
|
||||
at: ast.Attribute* = ast.Attribute*(node)
|
||||
if at.attr is not None:
|
||||
pos = at.col_offset
|
||||
return f"属性 '.{at.attr}'(列 {pos})"
|
||||
elif k == ast.ASTKind.Call:
|
||||
pos = (ast.Call*(node)).col_offset
|
||||
return f"调用结果 Call(列 {pos})"
|
||||
elif k == ast.ASTKind.Subscript:
|
||||
pos = (ast.Subscript*(node)).col_offset
|
||||
return f"下标表达式 Subscript(列 {pos})"
|
||||
elif k == ast.ASTKind.Constant:
|
||||
pos = (ast.Constant*(node)).col_offset
|
||||
return f"常量 Constant(列 {pos})"
|
||||
return f"表达式 kind={k}(列 {pos})"
|
||||
|
||||
# ============================================================
|
||||
# 类型强制转换:整数 sext/trunc,浮点 fpext/fptrunc,int↔float si2fp/fp2si
|
||||
# ============================================================
|
||||
def coerce_to_type(self, builder: llvmlite.IRBuilder*,
|
||||
@@ -361,14 +397,16 @@ novirtual class ExprGenBase:
|
||||
# (inttoptr 要求同宽,对齐 MatchGenMixin 模式),再 inttoptr 成空指针。
|
||||
if val.IsConst == 1 and val.IntVal == 0:
|
||||
return llvmlite.build_inttoptr(builder, llvmlite.const_int64(builder.Pool, 0), target_ty)
|
||||
self.Gen.TypeGenErr.fatal_error(node, "整数隐式转指针已禁止:必须显式强转为指针(如 t.CPtr(x))才能把整数赋给指针类型", "CastError")
|
||||
m_i2p: str = "整数隐式转指针已禁止:必须显式强转为指针(如 t.CPtr(x))才能把整数赋给指针类型"
|
||||
self.Gen.TypeGenErr.fatal_error(node, f"{m_i2p};出错表达式: {self._cast_fail_where(node)}", "CastError")
|
||||
return val
|
||||
# Pointer -> integer: rejected (symmetric with the integer -> pointer rule above).
|
||||
# No implicit pointer <-> integer conversion is allowed; an explicit cast is
|
||||
# required. (The old code silently did ptrtoint, and for 8-bit targets fell
|
||||
# through to the build_load branch below, copying the pointee's first byte.)
|
||||
if self.is_ptr_type(val.Ty) != 0 and target_bits != 0 and self.is_ptr_type(target_ty) == 0:
|
||||
self.Gen.TypeGenErr.fatal_error(node, "指针隐式转整数已禁止:必须显式强转(如 uint64(p) / t.CInt64T(p) / int(p))才能把指针赋给整数类型", "CastError")
|
||||
m_p2i: str = "指针隐式转整数已禁止:必须显式强转(如 uint64(p) / t.CInt64T(p) / int(p))才能把指针赋给整数类型"
|
||||
self.Gen.TypeGenErr.fatal_error(node, f"{m_p2i};出错表达式: {self._cast_fail_where(node)}", "CastError")
|
||||
return val
|
||||
if val_bits != 0 and target_bits != 0:
|
||||
if val_bits == target_bits:
|
||||
|
||||
@@ -43,6 +43,17 @@ novirtual class FuncGenBase(CallGenBase.CallGenBase):
|
||||
deco: ast.AST* = decorator_list.get(di)
|
||||
if deco is None: continue
|
||||
dk: int = deco.kind()
|
||||
# 直接形式 `@t.attr.<name>`(与类侧 ClassGenBase._has_decorator 同口径,
|
||||
# 类定义早就接受两种写法):`inline def f(...)` 合成的就是这一形态,
|
||||
# 且**不要求** import c。此前只认 c.Attribute(...) ⇒ 没有 import c 的文件
|
||||
# 会静默不内联。不支持的属性名由 _append_llvm_attr 返回 0 自然忽略
|
||||
# (如 @t.CVTable / @t.NoVTable / packed / aligned 对函数无意义)。
|
||||
if dk == ast.ASTKind.Attribute:
|
||||
dan: str = self._get_attr_name_from_node(deco)
|
||||
if dan is not None and string.strcmp(dan, "section") != 0:
|
||||
if self._append_llvm_attr(attrs_buf, dan) != 0:
|
||||
found_any = 1
|
||||
continue
|
||||
if dk != ast.ASTKind.Call: continue
|
||||
call_node: ast.Call* = ast.Call*(deco)
|
||||
if call_node.func is None: continue
|
||||
|
||||
Reference in new issue
Block a user