vast 双路径(Python Parser/ExprParser 与 C 扩展 vast_c.c)新增 cif-celif-celse 语句族与 &T / &temp T 参数注解解析,_parser.pyd 已同步重编。HandlesIf 改认 @CIf 标记并做编译期常量求值,含运行时变量一律 fail-fast,同时删除 c.CIf 家族(c.py)。HandlesAssert 合成 testcheck.check(cond, None, msg) 调用,非字符串字面量消息报 TypeError。FuncGen 调用点按形参模式自动取址(ref 仅接受左值,右值 fail-fast;temp 物化临时栈),SymbolUtils/TypeAnnotationResolver 提供模式识别;t.py 删除 CNeedPtr。
80 lines
3.9 KiB
Python
80 lines
3.9 KiB
Python
from __future__ import annotations
|
||
from typing import TYPE_CHECKING
|
||
if TYPE_CHECKING:
|
||
from lib.core.translator import Translator
|
||
from lib.core.Handles.HandlesBase import BaseHandle, EXCEPTION_CODE_MAP
|
||
import vast as ast
|
||
|
||
# 无信息 assert 使用的默认 fail_msg;须与 testcheck.check 的 fail_msg: str 形参一致
|
||
_ASSERT_DEFAULT_FAIL_MSG: str = "assertion failed"
|
||
# assert 自动依赖的检查库模块名与入口函数(见 includes/testcheck.vp)
|
||
_ASSERT_CHECK_MODULE: str = "testcheck"
|
||
_ASSERT_CHECK_FUNC: str = "check"
|
||
|
||
class AssertHandle(BaseHandle):
|
||
def _HandleAssertLlvm(self, Node: ast.Assert) -> None:
|
||
"""把 `assert 条件[, 信息]` 译为一次普通函数调用 testcheck.check(条件, None, 信息)。
|
||
|
||
语义:条件为真 → ok_msg 传 None(静默,不打印 PASS);条件为假 → fail(信息)。
|
||
不再中止程序(旧实现的 printf AssertionError + exit(1) 已移除),也不再走
|
||
try/except 抛异常码的路径。
|
||
"""
|
||
FailMsgNode: ast.expr = self._ResolveAssertFailMsgNode(Node)
|
||
CallNode: ast.Call = ast.Call(
|
||
func=ast.Attribute(
|
||
value=ast.Name(id=_ASSERT_CHECK_MODULE, ctx=ast.Load()),
|
||
attr=_ASSERT_CHECK_FUNC,
|
||
ctx=ast.Load()),
|
||
args=[Node.test, ast.Constant(value=None, kind=None), FailMsgNode],
|
||
keywords=[],
|
||
)
|
||
ast.copy_location(CallNode, Node)
|
||
# 复用既有跨模块调用生成路径(含符号解析、实参按形参类型收窄/扩充与 fail-fast 校验)
|
||
self.HandleExprLlvm(CallNode)
|
||
|
||
def _ResolveAssertFailMsgNode(self, Node: ast.Assert) -> ast.expr:
|
||
"""解析 assert 的信息表达式,并强制其为 str(fail-fast,禁止静默降级)。
|
||
|
||
- 无信息 → 字符串字面量 "assertion failed"
|
||
- 有信息 → 原节点(须为 str 形态:字面量 / f-string / str 变量或属性)
|
||
"""
|
||
MsgNode: ast.expr | None = Node.msg
|
||
if MsgNode is None:
|
||
DefaultMsg: ast.Constant = ast.Constant(value=_ASSERT_DEFAULT_FAIL_MSG, kind=None)
|
||
ast.copy_location(DefaultMsg, Node)
|
||
return DefaultMsg
|
||
# 旧实现允许把异常类名当信息(映射为异常码、不打印)。check() 的 fail_msg 形参
|
||
# 是 str,这类写法已无对应语义:显式报错,不静默降级。
|
||
_ExcName: str | None = None
|
||
if isinstance(MsgNode, ast.Name):
|
||
_ExcName = MsgNode.id
|
||
elif isinstance(MsgNode, ast.Call) and isinstance(MsgNode.func, ast.Name):
|
||
_ExcName = MsgNode.func.id
|
||
if _ExcName is not None and _ExcName in EXCEPTION_CODE_MAP:
|
||
self._RaiseAssertMsgTypeError(
|
||
f"assert 的信息必须是字符串(str),不能是异常类 '{_ExcName}'")
|
||
if not self._IsStrAssertMsg(MsgNode):
|
||
self._RaiseAssertMsgTypeError(
|
||
"assert 的信息必须是字符串(str):仅支持字符串字面量、f-string 或 str 变量/属性"
|
||
"(如需拼接请先写入 str 变量)")
|
||
return MsgNode
|
||
|
||
def _IsStrAssertMsg(self, MsgNode: ast.expr) -> bool:
|
||
"""静态判定 assert 信息是否为 str 形态。
|
||
|
||
变量/属性(Name/Attribute)无法在此静态证伪为 str,放行并由调用点的
|
||
`fail_msg: str` 形参做类型收口;其余非常量、非 f-string 的形态一律拒绝。
|
||
"""
|
||
if isinstance(MsgNode, ast.Constant):
|
||
return isinstance(MsgNode.value, str)
|
||
if isinstance(MsgNode, ast.JoinedStr):
|
||
return True
|
||
if isinstance(MsgNode, (ast.Name, ast.Attribute)):
|
||
return True
|
||
return False
|
||
|
||
def _RaiseAssertMsgTypeError(self, Message: str) -> None:
|
||
from lib.core.LLVMCG.FuncGen import FailFastError
|
||
Gen = self.Trans.LlvmGen
|
||
raise FailFastError(Message + (Gen._get_node_info() if Gen else ''))
|