feat(len): len() 对「非 i8 裸指针 / 无 __len__ 的 struct」改为 fail-fast(对齐 TransPyV)
旧实现:非 i8 裸指针(如 []T 形参实为 T*)静默返回常量 0;struct 无 __len__ 时回落 strlen —— 两者都用假长度掩盖错误。 新口径(两侧逐字一致):定长数组/数组指针 → 常量 N;i8*(str/bytes)→ strlen;带 __len__ 的结构体 → 调 __len__;其余一律编译期 TypeError。 新增 _LEN_SUPPORTED_HINT 共享提示语 + _FailFastLenUnsupported 出口(含 行:列 + 源码片段 + 正确做法)。HandlesExprCall.py 为同源死代码副本,一并同步语义未删。
This commit is contained in:
1 parent
97c196bfc7
commit
809f9d08fa
2 files changed
+164
-48
No files matched your search
@@ -13,6 +13,15 @@ from lib.includes import t as _t
|
||||
from lib.core.SymbolUtils import IsTModule
|
||||
|
||||
|
||||
# len() 参数形态不支持时的共享提示语(与 TransPyV App/lib/core/Gen/Call/
|
||||
# CallGenDispatch.vp 的 len() 分支同口径,措辞逐字一致,便于用户对照两侧报文)。
|
||||
_LEN_SUPPORTED_HINT: str = (
|
||||
"len() 只支持定长数组/数组指针(t.CArray[T, N])、i8*(str/bytes)或带 __len__ 的结构体;"
|
||||
"非 i8 裸指针/标量指针没有长度语义:字符串请用 str/bytes,需要元素个数请自行记录长度,"
|
||||
"或改用 t.CPtr[元素] 并显式维护元素计数"
|
||||
)
|
||||
|
||||
|
||||
class ExprBuiltinHandle(BaseHandle):
|
||||
_C_LIB_FUNCS: dict[str, object] = {
|
||||
'strlen': lambda: (ir.IntType(64), [ir.PointerType(ir.IntType(8))]),
|
||||
@@ -571,6 +580,41 @@ class ExprBuiltinHandle(BaseHandle):
|
||||
Gen.functions[func_name] = func
|
||||
return func
|
||||
|
||||
def _FailFastLenUnsupported(self, arg_node: ast.AST, Gen: LlvmGeneratorMixin,
|
||||
val: ir.Value | None, reason: str) -> None:
|
||||
"""len(x) 参数形态不支持时的编译期 fail-fast 出口(TPC 与 TransPyV 同口径)。
|
||||
|
||||
规则(对齐 TransPyV App/lib/core/Gen/Call/CallGenDispatch.vp 的 len() 分支):
|
||||
定长数组/数组指针 → 常量 N;i8*(str/bytes)→ strlen;
|
||||
带 __len__ 的结构体 → 调用 __len__;其余一律编译期报错。
|
||||
非 i8 裸指针(如 `[]T` 形参实为 `T*`)与标量指针**没有长度语义**,旧实现会
|
||||
静默返回常量 0(或回落 strlen)从而掩盖错误,这里改为响亮失败。
|
||||
报文含 行:列 + 源码片段 + 期望形态 + 正确做法提示。
|
||||
"""
|
||||
raw_lineno: int = getattr(arg_node, 'lineno', 0) or 0
|
||||
lineno: int = raw_lineno or getattr(Gen, '_current_lineno', 0) or 0
|
||||
col: int = getattr(arg_node, 'col_offset', -1)
|
||||
src_file: str = getattr(Gen, '_current_source_file', '') or ''
|
||||
_line_offset: int = int(getattr(self.Trans, '_line_offset', 0) or 0)
|
||||
if _line_offset > 0 and lineno > _line_offset:
|
||||
lineno -= _line_offset
|
||||
code_line: str = ''
|
||||
if lineno > 0 and hasattr(Gen, '_get_source_line'):
|
||||
try:
|
||||
code_line = (Gen._get_source_line(lineno) or '').strip()
|
||||
except Exception:
|
||||
code_line = ''
|
||||
loc: str = src_file
|
||||
if lineno > 0:
|
||||
loc += f":{lineno}"
|
||||
if col >= 0:
|
||||
loc += f":{col}"
|
||||
actual: str = str(val.type) if val is not None else '<未知>'
|
||||
msg: str = (f"[fast-fail] {reason}。{_LEN_SUPPORTED_HINT}。"
|
||||
f"实际类型 '{actual}'。位置 {loc}"
|
||||
+ (f",源码: {code_line}" if code_line else ''))
|
||||
raise TypeError(msg)
|
||||
|
||||
def _HandleLenLlvm(self, arg_node: ast.AST) -> ir.Value | None:
|
||||
Gen: LlvmGeneratorMixin = self.Trans.LlvmGen
|
||||
# len(obj.attr):obj 可为 self 或任意局部变量(VpiReader 的
|
||||
@@ -657,37 +701,51 @@ class ExprBuiltinHandle(BaseHandle):
|
||||
val = Gen._load(val, name="len_deref")
|
||||
pointee = val.type.pointee
|
||||
if isinstance(pointee, (ir.IdentifiedStructType, ir.LiteralStructType)):
|
||||
# struct(或任何非基本类型)→ 仍先走既有 __len__ 分派(保持不变);
|
||||
# 没有 __len__ 时由旧实现的「回落 strlen」改为 fail-fast(与 TPV 同口径)。
|
||||
_matched_cn: str | None = None
|
||||
for CN, ST in Gen.structs.items():
|
||||
if pointee == ST:
|
||||
result = self.Trans.ExprUtils._try_operator_overLoad(CN, '__len__', val, Gen)
|
||||
if result is not None:
|
||||
return result
|
||||
_matched_cn = CN
|
||||
break
|
||||
if _matched_cn is not None:
|
||||
result = self.Trans.ExprUtils._try_operator_overLoad(_matched_cn, '__len__', val, Gen)
|
||||
if result is not None:
|
||||
return result
|
||||
self._FailFastLenUnsupported(arg_node, Gen, val, f"len() 参数类型 '{_matched_cn}' 没有 __len__ 方法")
|
||||
else:
|
||||
# 未在 Gen.structs 登记的 struct(opaque/匿名)→ fail-fast。
|
||||
self._FailFastLenUnsupported(arg_node, Gen, val, "len() 参数类型不支持(非字符串/数组/对象)")
|
||||
if isinstance(pointee, ir.ArrayType):
|
||||
return ir.Constant(ir.IntType(64), pointee.count)
|
||||
if isinstance(pointee, (ir.FloatType, ir.DoubleType, ir.IntType)) and pointee.width != 8:
|
||||
return ir.Constant(ir.IntType(64), 0)
|
||||
if val.type != ir.PointerType(ir.IntType(8)):
|
||||
val = Gen.builder.bitcast(val, ir.PointerType(ir.IntType(8)), name="str_ptr_cast")
|
||||
correct_strlen_type = ir.FunctionType(ir.IntType(64), [ir.PointerType(ir.IntType(8))])
|
||||
strlen_func = None
|
||||
if 'strlen' in Gen.functions:
|
||||
existing = Gen.functions['strlen']
|
||||
if existing.ftype == correct_strlen_type:
|
||||
strlen_func = existing
|
||||
if not strlen_func:
|
||||
try:
|
||||
strlen_func = ir.Function(Gen.module, correct_strlen_type, name='strlen')
|
||||
Gen.functions['strlen'] = strlen_func
|
||||
except Exception as _e:
|
||||
if _config_mode == "strict":
|
||||
self.Trans.LogWarning(f"异常被忽略: {_e}")
|
||||
if strlen_func:
|
||||
result = Gen.builder.call(strlen_func, [val], name="strlen_call")
|
||||
return result
|
||||
# i8*(含 str/bytes 指针形态)→ strlen(保持不变)。
|
||||
if isinstance(pointee, ir.IntType) and pointee.width == 8:
|
||||
if val.type != ir.PointerType(ir.IntType(8)):
|
||||
val = Gen.builder.bitcast(val, ir.PointerType(ir.IntType(8)), name="str_ptr_cast")
|
||||
correct_strlen_type = ir.FunctionType(ir.IntType(64), [ir.PointerType(ir.IntType(8))])
|
||||
strlen_func = None
|
||||
if 'strlen' in Gen.functions:
|
||||
existing = Gen.functions['strlen']
|
||||
if existing.ftype == correct_strlen_type:
|
||||
strlen_func = existing
|
||||
if not strlen_func:
|
||||
try:
|
||||
strlen_func = ir.Function(Gen.module, correct_strlen_type, name='strlen')
|
||||
Gen.functions['strlen'] = strlen_func
|
||||
except Exception as _e:
|
||||
if _config_mode == "strict":
|
||||
self.Trans.LogWarning(f"异常被忽略: {_e}")
|
||||
if strlen_func:
|
||||
result = Gen.builder.call(strlen_func, [val], name="strlen_call")
|
||||
return result
|
||||
return None
|
||||
# 非 i8 裸指针 / 标量指针(i32*/i64*/float*/double*)→ fail-fast
|
||||
# (旧实现静默返回常量 0 或回落 strlen,与 TPV 口径不符)。
|
||||
self._FailFastLenUnsupported(arg_node, Gen, val, "len() 参数类型不支持(非字符串/数组/对象)")
|
||||
elif isinstance(val.type, ir.ArrayType):
|
||||
return ir.Constant(ir.IntType(64), val.type.count)
|
||||
return ir.Constant(ir.IntType(64), 0)
|
||||
# 其它不支持形态(标量值 / 结构体值等)→ fail-fast(旧实现静默返回常量 0)。
|
||||
self._FailFastLenUnsupported(arg_node, Gen, val, "len() 参数类型不支持(非字符串/数组/对象)")
|
||||
|
||||
def _resolve_nested_opaque_structs(self, struct_type: ir.Type, Gen: LlvmGeneratorMixin, visited: set | None = None) -> None:
|
||||
"""递归解析嵌套的 opaque struct,从 stub 文件中加载其定义"""
|
||||
|
||||
@@ -19,6 +19,15 @@ from lib.core.SymbolUtils import (
|
||||
from lib.core.Handles.HandlesExpr import DefineUseWidthFromLLVMType
|
||||
|
||||
|
||||
# len() 参数形态不支持时的共享提示语(与 TransPyV App/lib/core/Gen/Call/
|
||||
# CallGenDispatch.vp 的 len() 分支同口径,措辞逐字一致,便于用户对照两侧报文)。
|
||||
_LEN_SUPPORTED_HINT: str = (
|
||||
"len() 只支持定长数组/数组指针(t.CArray[T, N])、i8*(str/bytes)或带 __len__ 的结构体;"
|
||||
"非 i8 裸指针/标量指针没有长度语义:字符串请用 str/bytes,需要元素个数请自行记录长度,"
|
||||
"或改用 t.CPtr[元素] 并显式维护元素计数"
|
||||
)
|
||||
|
||||
|
||||
def _safe_ptr_type(ty: ir.Type) -> ir.Type:
|
||||
"""LLVM IR 中不允许 void* 指针,Ptr(VoidType) 统一替换为 i8*(对应 C 的 void* 语义)"""
|
||||
if isinstance(ty, ir.PointerType):
|
||||
@@ -3888,6 +3897,41 @@ class ExprCallHandle(BaseHandle):
|
||||
Gen.functions[func_name] = func
|
||||
return func
|
||||
|
||||
def _FailFastLenUnsupported(self, arg_node: ast.AST, Gen: LlvmGeneratorMixin,
|
||||
val: ir.Value | None, reason: str) -> None:
|
||||
"""len(x) 参数形态不支持时的编译期 fail-fast 出口(TPC 与 TransPyV 同口径)。
|
||||
|
||||
规则(对齐 TransPyV App/lib/core/Gen/Call/CallGenDispatch.vp 的 len() 分支):
|
||||
定长数组/数组指针 → 常量 N;i8*(str/bytes)→ strlen;
|
||||
带 __len__ 的结构体 → 调用 __len__;其余一律编译期报错。
|
||||
非 i8 裸指针(如 `[]T` 形参实为 `T*`)与标量指针**没有长度语义**,旧实现会
|
||||
静默返回常量 0(或回落 strlen)从而掩盖错误,这里改为响亮失败。
|
||||
报文含 行:列 + 源码片段 + 期望形态 + 正确做法提示。
|
||||
"""
|
||||
raw_lineno: int = getattr(arg_node, 'lineno', 0) or 0
|
||||
lineno: int = raw_lineno or getattr(Gen, '_current_lineno', 0) or 0
|
||||
col: int = getattr(arg_node, 'col_offset', -1)
|
||||
src_file: str = getattr(Gen, '_current_source_file', '') or ''
|
||||
_line_offset: int = int(getattr(self.Trans, '_line_offset', 0) or 0)
|
||||
if _line_offset > 0 and lineno > _line_offset:
|
||||
lineno -= _line_offset
|
||||
code_line: str = ''
|
||||
if lineno > 0 and hasattr(Gen, '_get_source_line'):
|
||||
try:
|
||||
code_line = (Gen._get_source_line(lineno) or '').strip()
|
||||
except Exception:
|
||||
code_line = ''
|
||||
loc: str = src_file
|
||||
if lineno > 0:
|
||||
loc += f":{lineno}"
|
||||
if col >= 0:
|
||||
loc += f":{col}"
|
||||
actual: str = str(val.type) if val is not None else '<未知>'
|
||||
msg: str = (f"[fast-fail] {reason}。{_LEN_SUPPORTED_HINT}。"
|
||||
f"实际类型 '{actual}'。位置 {loc}"
|
||||
+ (f",源码: {code_line}" if code_line else ''))
|
||||
raise TypeError(msg)
|
||||
|
||||
def _HandleLenLlvm(self, arg_node: ast.AST) -> ir.Value | None:
|
||||
Gen = self.Trans.LlvmGen
|
||||
if isinstance(arg_node, ast.Attribute) and isinstance(arg_node.value, ast.Name) and arg_node.value.id == 'self':
|
||||
@@ -3949,37 +3993,51 @@ class ExprCallHandle(BaseHandle):
|
||||
val = Gen._load(val, name="len_deref")
|
||||
pointee = val.type.pointee
|
||||
if isinstance(pointee, (ir.IdentifiedStructType, ir.LiteralStructType)):
|
||||
# struct(或任何非基本类型)→ 仍先走既有 __len__ 分派(保持不变);
|
||||
# 没有 __len__ 时由旧实现的「回落 strlen」改为 fail-fast(与 TPV 同口径)。
|
||||
_matched_cn: str | None = None
|
||||
for CN, ST in Gen.structs.items():
|
||||
if pointee == ST:
|
||||
result = self.Trans.ExprUtils._try_operator_overLoad(CN, '__len__', val, Gen)
|
||||
if result is not None:
|
||||
return result
|
||||
_matched_cn = CN
|
||||
break
|
||||
if _matched_cn is not None:
|
||||
result = self.Trans.ExprUtils._try_operator_overLoad(_matched_cn, '__len__', val, Gen)
|
||||
if result is not None:
|
||||
return result
|
||||
self._FailFastLenUnsupported(arg_node, Gen, val, f"len() 参数类型 '{_matched_cn}' 没有 __len__ 方法")
|
||||
else:
|
||||
# 未在 Gen.structs 登记的 struct(opaque/匿名)→ fail-fast。
|
||||
self._FailFastLenUnsupported(arg_node, Gen, val, "len() 参数类型不支持(非字符串/数组/对象)")
|
||||
if isinstance(pointee, ir.ArrayType):
|
||||
return ir.Constant(ir.IntType(64), pointee.count)
|
||||
if isinstance(pointee, (ir.FloatType, ir.DoubleType, ir.IntType)) and pointee.width != 8:
|
||||
return ir.Constant(ir.IntType(64), 0)
|
||||
if val.type != ir.PointerType(ir.IntType(8)):
|
||||
val = Gen.builder.bitcast(val, ir.PointerType(ir.IntType(8)), name="str_ptr_cast")
|
||||
correct_strlen_type = ir.FunctionType(ir.IntType(64), [ir.PointerType(ir.IntType(8))])
|
||||
strlen_func = None
|
||||
if 'strlen' in Gen.functions:
|
||||
existing = Gen.functions['strlen']
|
||||
if existing.ftype == correct_strlen_type:
|
||||
strlen_func = existing
|
||||
if not strlen_func:
|
||||
try:
|
||||
strlen_func = ir.Function(Gen.module, correct_strlen_type, name='strlen')
|
||||
Gen.functions['strlen'] = strlen_func
|
||||
except Exception as _e:
|
||||
if _config_mode == "strict":
|
||||
self.Trans.LogWarning(f"异常被忽略: {_e}")
|
||||
if strlen_func:
|
||||
result = Gen.builder.call(strlen_func, [val], name="strlen_call")
|
||||
return result
|
||||
# i8*(含 str/bytes 指针形态)→ strlen(保持不变)。
|
||||
if isinstance(pointee, ir.IntType) and pointee.width == 8:
|
||||
if val.type != ir.PointerType(ir.IntType(8)):
|
||||
val = Gen.builder.bitcast(val, ir.PointerType(ir.IntType(8)), name="str_ptr_cast")
|
||||
correct_strlen_type = ir.FunctionType(ir.IntType(64), [ir.PointerType(ir.IntType(8))])
|
||||
strlen_func = None
|
||||
if 'strlen' in Gen.functions:
|
||||
existing = Gen.functions['strlen']
|
||||
if existing.ftype == correct_strlen_type:
|
||||
strlen_func = existing
|
||||
if not strlen_func:
|
||||
try:
|
||||
strlen_func = ir.Function(Gen.module, correct_strlen_type, name='strlen')
|
||||
Gen.functions['strlen'] = strlen_func
|
||||
except Exception as _e:
|
||||
if _config_mode == "strict":
|
||||
self.Trans.LogWarning(f"异常被忽略: {_e}")
|
||||
if strlen_func:
|
||||
result = Gen.builder.call(strlen_func, [val], name="strlen_call")
|
||||
return result
|
||||
return None
|
||||
# 非 i8 裸指针 / 标量指针(i32*/i64*/float*/double*)→ fail-fast
|
||||
# (旧实现静默返回常量 0 或回落 strlen,与 TPV 口径不符)。
|
||||
self._FailFastLenUnsupported(arg_node, Gen, val, "len() 参数类型不支持(非字符串/数组/对象)")
|
||||
elif isinstance(val.type, ir.ArrayType):
|
||||
return ir.Constant(ir.IntType(64), val.type.count)
|
||||
return ir.Constant(ir.IntType(64), 0)
|
||||
# 其它不支持形态(标量值 / 结构体值等)→ fail-fast(旧实现静默返回常量 0)。
|
||||
self._FailFastLenUnsupported(arg_node, Gen, val, "len() 参数类型不支持(非字符串/数组/对象)")
|
||||
|
||||
def _resolve_opaque_struct_members(self, struct_type: ir.Type, Gen: LlvmGeneratorMixin, _visited: set | None = None) -> None:
|
||||
"""Recursively resolve opaque struct members by loading from stub files."""
|
||||
|
||||
Reference in new issue
Block a user