diff --git a/lib/Projectrans/DeclarationGenerator.py b/lib/Projectrans/DeclarationGenerator.py index 4777adf..189dd80 100644 --- a/lib/Projectrans/DeclarationGenerator.py +++ b/lib/Projectrans/DeclarationGenerator.py @@ -1274,6 +1274,11 @@ class DeclarationGenerator: return f'{sname}*' elif isinstance(annotation, ast.Attribute): attr_name: str = annotation.attr if hasattr(annotation, 'attr') else '' + # 值引用参数标记(CRef = `&T`、CTempRef = `&temp T`,非真实类):按 t.CPtr + # 同形的「一层指针 = i8*」参与联合合并(`T | t.CRef` → T*),保证跨模块 + # .vpi 声明端类型与定义端 _ctype_to_llvm 一致。 + if attr_name in ('CRef', 'CTempRef'): + return 'i8*' module_name: str = '' if hasattr(annotation, 'value'): if isinstance(annotation.value, ast.Name): diff --git a/lib/Projectrans/Phase1Generator.py b/lib/Projectrans/Phase1Generator.py index 35bbc4c..ea0e3db 100644 --- a/lib/Projectrans/Phase1Generator.py +++ b/lib/Projectrans/Phase1Generator.py @@ -152,6 +152,22 @@ class Phase1Generator: deps: set[str] = get_file_dependencies(src_path, self.src_root) imported_from_src.update(deps) + # 检测 assert 使用,自动添加 testcheck 依赖:assert 译为 + # testcheck.check(...),该 include 须纳入 .vpi/.stub 生成与后续编译链接, + # 否则断言所在模块解析不到 testcheck.check(跨模块符号缺失)。 + # 独立成一次 walk:下面的 dict/list 检测循环带 break,会漏掉其后的 assert。 + for src_path in reachable_source_files: + try: + _, tree = parse_python_file(src_path) + if tree is None: + continue + for node in ast.walk(tree): + if isinstance(node, ast.Assert): + imported_from_src.add('testcheck') + break + except Exception: + pass + # 检测 dict/list 容器使用,自动添加 _dict/_list/json 依赖 for src_path in reachable_source_files: try: diff --git a/lib/Projectrans/Phase2Translator.py b/lib/Projectrans/Phase2Translator.py index 3e99ba1..94197e4 100644 --- a/lib/Projectrans/Phase2Translator.py +++ b/lib/Projectrans/Phase2Translator.py @@ -1547,6 +1547,10 @@ class Phase2Translator: elif isinstance(node, ast.ImportFrom): if node.module: self.used_includes.add(node.module.split('.')[0]) + # 检测 assert 使用,自动添加 testcheck 到 used_includes: + # assert 译为 testcheck.check(...),该库须被发现、编译并链接。 + if isinstance(node, ast.Assert): + self.used_includes.add('testcheck') # 检测 list/dict 容器使用,自动添加 _list/_dict/json 到 used_includes # 以便 _scan_include_libraries 能发现并编译这些库文件 if isinstance(node, ast.Subscript) and isinstance(node.value, ast.Name): @@ -2144,11 +2148,14 @@ class Phase2Translator: def _inject_auto_imports(self, code: str, src_path: str) -> str: """检测源码中对 list/dict 容器的使用,自动注入 import _list / import _dict。 当使用 dict 时,同时注入 import json (用于 dict 的 JSON 处理功能)。 + 检测到 assert 语句时自动注入 import testcheck(assert 译为 + testcheck.check(...),需该模块的跨模块符号声明)。 检测模式: - list[T] / dict[V] 作为类型注解 (ast.Subscript, value=Name(id='list'/'dict')) - list[T](...) / dict[V](...) 作为构造函数调用 (ast.Call, func=Subscript) - list(...) / dict(...) 作为构造函数调用 (ast.Call, func=Name) + - assert 语句 (ast.Assert) """ try: # `.vp` 源码须先过 Viper 软关键字层(`enum X:` / `fn` 等)再解析: @@ -2158,10 +2165,11 @@ class Phase2Translator: except SyntaxError: return code - # 检查是否已经导入了 _list / _dict / json + # 检查是否已经导入了 _list / _dict / json / testcheck has_list_import: bool = False has_dict_import: bool = False has_json_import: bool = False + has_testcheck_import: bool = False for node in ast.iter_child_nodes(tree): if isinstance(node, ast.Import): for alias in node.names: @@ -2171,6 +2179,8 @@ class Phase2Translator: has_dict_import = True elif alias.name == 'json': has_json_import = True + elif alias.name == 'testcheck': + has_testcheck_import = True elif isinstance(node, ast.ImportFrom): if node.module == '_list': has_list_import = True @@ -2178,13 +2188,18 @@ class Phase2Translator: has_dict_import = True elif node.module == 'json': has_json_import = True - if has_list_import and has_dict_import and has_json_import: + elif node.module == 'testcheck': + has_testcheck_import = True + if has_list_import and has_dict_import and has_json_import and has_testcheck_import: break - # 检查是否使用了 list / dict 容器 + # 检查是否使用了 list / dict 容器,以及是否含 assert 语句 uses_list: bool = False uses_dict: bool = False + uses_assert: bool = False for node in ast.walk(tree): + if isinstance(node, ast.Assert): + uses_assert = True if isinstance(node, ast.Subscript): if isinstance(node.value, ast.Name): if node.value.id == 'list': @@ -2211,6 +2226,9 @@ class Phase2Translator: prefix += 'import _dict\n' if uses_dict and not has_json_import: prefix += 'import json\n' + # 含 assert 的模块等价于在文件顶部写下 import testcheck(已导入则不重复) + if uses_assert and not has_testcheck_import: + prefix += 'import testcheck\n' if prefix: return prefix + code return code diff --git a/lib/StubGen/Converter.py b/lib/StubGen/Converter.py index 03c1a9c..b2d1080 100644 --- a/lib/StubGen/Converter.py +++ b/lib/StubGen/Converter.py @@ -220,14 +220,14 @@ class PythonToStubConverter: StubLines.append('') StubLines.append(ImportStr) elif isinstance(node, ast.Expr): - # 处理模块级别的宏调用,如 c.CIf(...), c.CEndif() + # 处理模块级别的宏调用,如 c.CEndif() ExprStr: str = PythonToStubConverter._GetExprString(node.value) if ExprStr: CurrentType = 'macro' StubLines.append(ExprStr) StubLines.append('') # 宏后空行 elif isinstance(node, ast.If): - # 处理模块级别的 if 宏条件,如 if c.CIf(FF_MULTI_PARTITION): + # 处理模块级别的 cif 宏条件,如 `cif FF_MULTI_PARTITION:` IfStr: str = PythonToStubConverter._GetModuleIfMacroString(node) if IfStr: CurrentType = 'macro' @@ -440,12 +440,12 @@ class PythonToStubConverter: FuncStub: list[str] = PythonToStubConverter._ConvertFunction(item, indent=indent+1, SourceLines=SourceLines, class_name=node.name) lines.extend(FuncStub) elif isinstance(item, ast.Expr): - # 处理宏调用,如 c.CIf(...), c.CEndif(), c.CElif(), c.CElse() + # 处理宏调用,如 c.CEndif() ExprStr: str = PythonToStubConverter._GetExprString(item.value) if ExprStr: lines.append(f'{IndentStr} {ExprStr}') elif isinstance(item, ast.If): - # 处理 if c.CIf(...): 形式的宏条件 + # 处理 cif 宏条件(`cif ...:` / `celif ...:` / `celse:`) IfStr: str = PythonToStubConverter._get_if_macro_string(item, indent=indent+1) if IfStr: lines.append(IfStr) @@ -710,6 +710,21 @@ class PythonToStubConverter: left: ast.AST = annotation.left right: ast.AST = annotation.right + # 值引用参数标记:联合形态 `T | t.CRef`(ref)/ `T | t.CTempRef`(temp) + # 还原为源码写法 `&T` / `&temp T`,使跨模块 .vpi 往返保留取址模式。 + if isinstance(annotation.op, ast.BitOr): + _ref_kind: str | None = None + _type_side: ast.AST | None = None + if isinstance(right, ast.Attribute) and right.attr in ('CRef', 'CTempRef'): + _ref_kind = right.attr + _type_side = left + elif isinstance(left, ast.Attribute) and left.attr in ('CRef', 'CTempRef'): + _ref_kind = left.attr + _type_side = right + if _ref_kind is not None and _type_side is not None: + _inner: str = PythonToStubConverter._GetTypeString(_type_side) + return f'&temp {_inner}' if _ref_kind == 'CTempRef' else f'&{_inner}' + # 检查右侧是否是 __set_default__ 调用 if isinstance(right, ast.Call) and isinstance(right.func, ast.Attribute) and right.func.attr in ('__set_default__', '__set_default'): # 去除 __set_default__,保留左侧,右侧替换为函数名部分 @@ -973,81 +988,132 @@ class PythonToStubConverter: def _GetExprString(expr: ast.AST) -> str: """获取表达式字符串(用于宏调用)""" if isinstance(expr, ast.Call): - # 处理函数调用,如 c.CIf(...), c.CEndif(), c.CUndef(...), c.CPragma(...) + # 处理函数调用,如 c.CEndif(), c.CUndef(...), c.CPragma(...) + # (旧的 c.CIf/c.CElif/c.CElse 已删除:cif/celif/celse 由 _ProcessIfMacro 渲染) FuncStr: str = PythonToStubConverter._GetDecoratorString(expr.func) - if FuncStr and ('CIf' in FuncStr or 'CEndif' in FuncStr or 'CElif' in FuncStr or 'CElse' in FuncStr or 'CUndef' in FuncStr or 'CPragma' in FuncStr): + if FuncStr and ('CEndif' in FuncStr or 'CUndef' in FuncStr or 'CPragma' in FuncStr): ArgsStr: str = ', '.join([PythonToStubConverter._GetTypeString(arg) for arg in expr.args]) return f'{FuncStr}({ArgsStr})' if ArgsStr else f'{FuncStr}()' return '' + @staticmethod + def _RestoreDefinedMarkers(node: ast.AST) -> ast.AST: + """把内部标记 c.CIfdef(N) 递归还原为 defined(N) 调用(供 stub 文本渲染)。 + + cif/celif 条件的 desugar 会把 defined(NAME) 变成 c.@CIfdef(NAME) 标记调用; + 生成 .vpi 文本时必须还原为用户可写的 defined(NAME),否则存根不可再解析。 + """ + if isinstance(node, ast.Call): + F: str = PythonToStubConverter._GetDecoratorString(node.func) + short: str = F.rsplit('.', 1)[-1] if F else '' + if short in ('@CIfdef', '@CIfndef') and len(node.args) == 1: + inner: ast.AST = PythonToStubConverter._RestoreDefinedMarkers(node.args[0]) + call: ast.Call = ast.Call( + func=ast.Name(id='defined', ctx=ast.Load()), + args=[inner], keywords=[]) + if short == '@CIfndef': + return ast.UnaryOp(op=ast.Not(), operand=call) + return call + for field, value in ast.iter_fields(node): + if isinstance(value, list): + for i, item in enumerate(value): + if isinstance(item, ast.AST): + value[i] = PythonToStubConverter._RestoreDefinedMarkers(item) + elif isinstance(value, ast.AST): + setattr(node, field, PythonToStubConverter._RestoreDefinedMarkers(value)) + return node + + @staticmethod + def _CifConditionText(test: ast.AST) -> str | None: + """cif/celif 条件文本:内部标记 c.@CIf(EXPR) → EXPR;c.@CIfdef(N) → defined(N)。 + + 返回 None 表示 test 不是 cif 族标记(普通 if 的运行时条件)。 + """ + if isinstance(test, ast.Call): + F: str = PythonToStubConverter._GetDecoratorString(test.func) + short: str = F.rsplit('.', 1)[-1] if F else '' + if short == '@CIf' and len(test.args) == 1: + inner: ast.AST = PythonToStubConverter._RestoreDefinedMarkers(test.args[0]) + return PythonToStubConverter._GetTypeString(inner) + if short in ('@CIfdef', '@CIfndef') and len(test.args) == 1: + return PythonToStubConverter._GetTypeString( + PythonToStubConverter._RestoreDefinedMarkers(test)) + return None + @staticmethod def _get_if_macro_string(node: ast.If, indent: int = 1) -> str: - """获取类内部的 if 宏字符串(如 if c.CIf(...):)""" + """获取类内部的 cif 宏字符串(如 `cif :`,含 celif/celse 链)。""" IndentStr: str = ' ' * indent InnerIndent: str = ' ' * (indent + 1) - # 检查条件是否是宏调用 - if isinstance(node.test, ast.Call): - FuncStr: str = PythonToStubConverter._GetDecoratorString(node.test.func) - if FuncStr and 'CIf' in FuncStr: - ArgsStr: str = ', '.join([PythonToStubConverter._GetTypeString(arg) for arg in node.test.args]) - result: list[str] = [f'{IndentStr}if {FuncStr}({ArgsStr}):' if ArgsStr else f'{IndentStr}if {FuncStr}():'] - # 处理 if 体内的语句 - for item in node.body: - if isinstance(item, ast.AnnAssign): - TypeStr: str = PythonToStubConverter._GetTypeString(item.annotation) - result.append(f'{InnerIndent}{item.target.id}: {TypeStr}') - elif isinstance(item, ast.Expr): - ExprStr: str = PythonToStubConverter._GetExprString(item.value) - if ExprStr: - result.append(f'{InnerIndent}{ExprStr}') - return '\n'.join(result) - return '' + cond: str | None = PythonToStubConverter._CifConditionText(node.test) + if cond is None: + return '' + + def _body_lines(body: list, ind: str) -> list[str]: + out: list[str] = [] + for item in body: + if isinstance(item, ast.AnnAssign): + TypeStr: str = PythonToStubConverter._GetTypeString(item.annotation) + out.append(f'{ind}{item.target.id}: {TypeStr}') + elif isinstance(item, ast.Expr): + ExprStr: str = PythonToStubConverter._GetExprString(item.value) + if ExprStr: + out.append(f'{ind}{ExprStr}') + return out + + result: list[str] = [f'{IndentStr}cif {cond}:'] + result.extend(_body_lines(node.body, InnerIndent)) + current: ast.If = node + while current.orelse: + if len(current.orelse) == 1 and isinstance(current.orelse[0], ast.If): + elif_node: ast.If = current.orelse[0] + econd: str | None = PythonToStubConverter._CifConditionText(elif_node.test) + if econd is None: + break + result.append(f'{IndentStr}celif {econd}:') + result.extend(_body_lines(elif_node.body, InnerIndent)) + current = elif_node + continue + result.append(f'{IndentStr}celse:') + result.extend(_body_lines(current.orelse, InnerIndent)) + break + return '\n'.join(result) @staticmethod def _GetModuleIfMacroString(node: ast.If) -> str: - """获取模块级别的 if 宏字符串(如 if c.CIf(FF_MULTI_PARTITION):)""" + """获取模块级别的 cif 宏字符串(如 `cif FF_MULTI_PARTITION:`)。""" return PythonToStubConverter._ProcessIfMacro(node, indent=0) @staticmethod def _ProcessIfMacro(node: ast.If, indent: int = 0) -> str: - """处理 if 宏节点,支持 elif 和 else""" + """处理 cif 宏节点,支持 celif / celse 链。""" IndentStr: str = ' ' * indent + cond: str | None = PythonToStubConverter._CifConditionText(node.test) + if cond is None: + return '' + result: list[str] = [f'{IndentStr}cif {cond}:'] + for item in node.body: + result.extend(PythonToStubConverter._ProcessMacroBodyItem(item, indent + 1)) - # 检查条件是否是宏调用 - if isinstance(node.test, ast.Call): - FuncStr: str = PythonToStubConverter._GetDecoratorString(node.test.func) - if FuncStr and 'CIf' in FuncStr: - ArgsStr: str = ', '.join([PythonToStubConverter._GetTypeString(arg) for arg in node.test.args]) - result: list[str] = [f'{IndentStr}if {FuncStr}({ArgsStr}):' if ArgsStr else f'{IndentStr}if {FuncStr}():'] - # 处理 if 体内的语句 - for item in node.body: + current: ast.If = node + while current.orelse: + if len(current.orelse) == 1 and isinstance(current.orelse[0], ast.If): + # celif 情况 + elif_node: ast.If = current.orelse[0] + econd: str | None = PythonToStubConverter._CifConditionText(elif_node.test) + if econd is None: + break + result.append(f'{IndentStr}celif {econd}:') + for item in elif_node.body: result.extend(PythonToStubConverter._ProcessMacroBodyItem(item, indent + 1)) - - # 处理 elif 和 else - current: ast.If = node - while current.orelse: - if len(current.orelse) == 1 and isinstance(current.orelse[0], ast.If): - # elif 情况 - elif_node: ast.If = current.orelse[0] - if isinstance(elif_node.test, ast.Call): - ElifFuncStr: str = PythonToStubConverter._GetDecoratorString(elif_node.test.func) - if ElifFuncStr and 'CIf' in ElifFuncStr: - ElifArgsStr: str = ', '.join([PythonToStubConverter._GetTypeString(arg) for arg in elif_node.test.args]) - result.append(f'{IndentStr}elif {ElifFuncStr}({ElifArgsStr}):' if ElifArgsStr else f'{IndentStr}elif {ElifFuncStr}():') - for item in elif_node.body: - result.extend(PythonToStubConverter._ProcessMacroBodyItem(item, indent + 1)) - current = elif_node - continue - break - else: - # else 情况 - result.append(f'{IndentStr}else:') - for item in current.orelse: - result.extend(PythonToStubConverter._ProcessMacroBodyItem(item, indent + 1)) - break - - return '\n'.join(result) - return '' + current = elif_node + continue + # celse 情况 + result.append(f'{IndentStr}celse:') + for item in current.orelse: + result.extend(PythonToStubConverter._ProcessMacroBodyItem(item, indent + 1)) + break + return '\n'.join(result) @staticmethod def _ProcessMacroBodyItem(item: ast.AST, indent: int = 0) -> list[str]: diff --git a/lib/core/ConstEvaluator.py b/lib/core/ConstEvaluator.py index a2a8326..d1da49e 100644 --- a/lib/core/ConstEvaluator.py +++ b/lib/core/ConstEvaluator.py @@ -222,6 +222,28 @@ class ConstEvaluator: elif isinstance(func, ast.Name): func_name = func.id + # c.@CIfdef(NAME) / c.@CIfndef(NAME):defined(NAME) 的解析期 desugar 标记,编译期返回 0/1。 + # 仅出现在 cif/celif 条件内(解析器保证),故此处只做宏定义判定。 + if func_name == '@CIfdef': + if node.args: + macro_name: str | None = ConstEvaluator._resolve_macro_ref(node.args[0], ctx) + if macro_name is not None: + return 1 if ctx.is_macro_defined(macro_name) else 0 + return 0 + if func_name == '@CIfndef': + if node.args: + macro_name = ConstEvaluator._resolve_macro_ref(node.args[0], ctx) + if macro_name is not None: + return 0 if ctx.is_macro_defined(macro_name) else 1 + return 1 + # defined(NAME) 谓词(未 desugar 形态 / TPV 口径):编译期返回 0/1。 + if func_name == 'defined' and module_name is None: + if node.args: + macro_name = ConstEvaluator._resolve_macro_ref(node.args[0], ctx) + if macro_name is not None: + return 1 if ctx.is_macro_defined(macro_name) else 0 + return 0 + # ctraits.isptr(x) — 带模块前缀 # isptr(x) — 通过 from ctraits import isptr 导入 is_isptr: bool = False @@ -434,6 +456,17 @@ class ConstEvaluator: pass return None + @staticmethod + def _resolve_macro_ref(node: ast.AST, ctx: 'EvalContext') -> Optional[str]: + """从 defined(...) 实参节点解析宏名:Name→id;Constant(str)→值;Attribute→点分全名。""" + if isinstance(node, ast.Name): + return node.id + if isinstance(node, ast.Constant) and isinstance(node.value, str): + return node.value + if isinstance(node, ast.Attribute): + return EvalContext._get_attr_full_name(node) + return None + @staticmethod def _eval_attribute_define(node: ast.Attribute, symtab: Any) -> Optional[Any]: """从 SymbolTable 查找 Attribute 形式的 define 值""" @@ -532,7 +565,39 @@ class EvalContext: return None - def lookup_attribute(self, node: ast.Attribute) -> Optional[Any]: + def is_macro_defined(self, name: str) -> bool: + """判定宏 NAME 是否已定义(供 defined(NAME) / c.CIfdef 标记使用)。 + + 来源与 HandlesIf._is_macro_defined 同口径:Gen._define_constants / + _DefineConstants / SymbolTable.is_define / 平台宏。 + """ + if not name: + return False + if self.Gen and name in getattr(self.Gen, '_define_constants', {}): + return True + if self.Gen and hasattr(self.Gen, '_DefineConstants') and name in self.Gen._DefineConstants: + return True + if self.symtab: + is_define = getattr(self.symtab, 'is_define', None) + if callable(is_define) and is_define(name): + return True + if hasattr(self.symtab, '__contains__') and name in self.symtab: + info: CTypeInfo = self.symtab[name] + if getattr(info, 'IsDefine', False): + return True + macros: dict = self._get_platform_macros() + if name in macros: + return True + # Attribute 形式(如 platmacro.IS_WINDOWS):退化为短名匹配。 + if '.' in name: + short: str = name.rsplit('.', 1)[-1] + if short in macros: + return True + if self.Gen and short in getattr(self.Gen, '_define_constants', {}): + return True + return False + + def lookup_attribute(self, node: ast.Attribute) -> Optional[Any]: """查找 Attribute 节点的值""" module_name: str | None = node.value.id if isinstance(node.value, ast.Name) else None attr_name: str = node.attr diff --git a/lib/core/Handles/HandlesAnnAssign.py b/lib/core/Handles/HandlesAnnAssign.py index 0adf5b9..bb682e3 100644 --- a/lib/core/Handles/HandlesAnnAssign.py +++ b/lib/core/Handles/HandlesAnnAssign.py @@ -12,6 +12,7 @@ from lib.includes import t from lib.core.SymbolUtils import ( IsListAnnotation, ParseListAnnotation, FindStructNameInAnnotationWithContext, AnnotationContainsName, IsCPtrNode, RequireDefineConstStorable, + FindAutoAddrMode, ) @@ -38,7 +39,8 @@ def _IsGenericTpName(name: str) -> bool: _PTR_MARKER_CLASS_NAMES: frozenset[str] = frozenset({ 'rawptr', 't.rawptr', 'c.rawptr', 'CPtr', 't.CPtr', 'c.CPtr', - 'CNeedPtr', 't.CNeedPtr', 'CAutoPtr', 't.CAutoPtr', + 'CAutoPtr', 't.CAutoPtr', + 'CRef', 't.CRef', 'CTempRef', 't.CTempRef', }) @@ -501,6 +503,14 @@ class AnnAssignHandle(BaseHandle): def _HandleAnnAssignLlvmInner(self, Node: ast.AnnAssign) -> None: Gen: LlvmCodeGenerator = self.Trans.LlvmGen + # 值引用标记(`&T` / `&temp T`)仅用于函数参数位:局部/全局变量注解出现即 fail-fast。 + _ref_mode: str | None = FindAutoAddrMode(Node.annotation) + if _ref_mode is not None: + from lib.core.LLVMCG.FuncGen import FailFastError + _ann_text: str = ast.unparse(Node.annotation) + raise FailFastError( + f"变量注解不支持值引用(`&T` / `&temp T`):'{_ann_text}'。" + f"值引用仅用于函数参数位;局部变量请改用显式指针类型(如 `T*`)。") # 内建定长数组构造子 array 的注解校验(误用即 fail-fast,含文件:行:列): # 局部变量/self 字段注解不经过 ResolveAnnotationType,故在此显式校验。 self._CheckFixedArrayKeyword(Node.annotation) diff --git a/lib/core/Handles/HandlesAssert.py b/lib/core/Handles/HandlesAssert.py index 6cf2266..cf59f6a 100644 --- a/lib/core/Handles/HandlesAssert.py +++ b/lib/core/Handles/HandlesAssert.py @@ -1,60 +1,79 @@ -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 -import llvmlite.ir as ir - -class AssertHandle(BaseHandle): - def _HandleAssertLlvm(self, Node: ast.Assert) -> None: - Gen: "Translator.LlvmGen" = self.Trans.LlvmGen - cond_val: ir.Value | None = self.HandleExprLlvm(Node.test) - if not cond_val: - return - if not isinstance(cond_val.type, ir.IntType) or cond_val.type.width != 1: - zero: ir.Constant = ir.Constant(cond_val.type, 0) - cond_val = Gen.builder.icmp_signed('!=', cond_val, zero, name="assert_cond") - AssertFailBB: ir.Block = Gen.func.append_basic_block(name="assert.fail") - AssertOkBB: ir.Block = Gen.func.append_basic_block(name="assert.ok") - Gen.builder.cbranch(cond_val, AssertOkBB, AssertFailBB) - Gen.builder.position_at_start(AssertFailBB) - exc_code: int = 9 - exc_msg: ir.Value | None = None - if Node.msg: - if isinstance(Node.msg, ast.Constant) and isinstance(Node.msg.value, str): - exc_msg = self.HandleExprLlvm(Node.msg) - elif isinstance(Node.msg, ast.Call) and isinstance(Node.msg.func, ast.Name): - exc_code = EXCEPTION_CODE_MAP.get(Node.msg.func.id, 99) - if Node.msg.args: - first_arg: ast.expr = Node.msg.args[0] - if isinstance(first_arg, ast.Constant) and isinstance(first_arg.value, str): - exc_msg = self.HandleExprLlvm(first_arg) - elif isinstance(Node.msg, ast.Name): - exc_code = EXCEPTION_CODE_MAP.get(Node.msg.id, 99) - else: - exc_msg = self.HandleExprLlvm(Node.msg) - if Gen.eh_except_block_stack: - ExceptBB: ir.Block = Gen.eh_except_block_stack[-1][0] - EndBB: ir.Block | None = Gen.eh_except_block_stack[-1][1] - exception_code: ir.AllocaInstr = Gen.eh_except_block_stack[-1][2] - eh_message: ir.AllocaInstr = Gen.eh_except_block_stack[-1][3] - Gen._store(ir.Constant(ir.IntType(32), exc_code), exception_code) - if exc_msg: - Gen._store(exc_msg, eh_message) - else: - null_ptr: ir.Constant = ir.Constant(ir.PointerType(ir.IntType(8)), None) - Gen._store(null_ptr, eh_message) - Gen.builder.branch(ExceptBB) - else: - printf_func: ir.Function = Gen.get_or_declare_c_func('printf', ir.FunctionType(ir.IntType(32), [ir.PointerType(ir.IntType(8))], var_arg=True)) - if exc_msg: - fmt: ir.Value = Gen.emit_constant("AssertionError: %s\n", 'string') - Gen.builder.call(printf_func, [fmt, exc_msg], name="assert_fail_print") - else: - fmt: ir.Value = Gen.emit_constant("AssertionError\n", 'string') - Gen.builder.call(printf_func, [fmt], name="assert_fail_print") - exit_func: ir.Function = Gen.get_or_declare_c_func('exit', ir.FunctionType(ir.VoidType(), [ir.IntType(32)])) - Gen.builder.call(exit_func, [ir.Constant(ir.IntType(32), 1)], name="call_exit") - Gen.builder.unreachable() - Gen.builder.position_at_start(AssertOkBB) \ No newline at end of file +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 '')) diff --git a/lib/core/Handles/HandlesBase.py b/lib/core/Handles/HandlesBase.py index 46f8ba7..b219f78 100644 --- a/lib/core/Handles/HandlesBase.py +++ b/lib/core/Handles/HandlesBase.py @@ -16,7 +16,7 @@ from lib.core.TypeSpec import TypeSpec, SymbolMeta, FIELD_ROUTES from lib.core.SymbolUtils import ( IsPtrSubscriptNode, IsListAnnotation, IsCPtrMarkerNode, FlattenUnionOperands, IsI8DoublePtrType, IsClassRefPtrAnnotation, - FixedArrayKeywordAnnotationError) + FixedArrayKeywordAnnotationError, FindAutoAddrMode) from lib.core.VLogger import get_logger as _vlog @@ -1247,6 +1247,11 @@ class BaseHandle: return None VarName: str = item.target.id AnnotationText: str = ast.unparse(item.annotation) + # 值引用标记(`&T` / `&temp T`)仅用于函数参数位:结构体字段/成员注解出现即 fail-fast。 + if FindAutoAddrMode(item.annotation) is not None: + raise ValueError( + f"[FATAL] 成员 '{VarName}' 的注解 '{AnnotationText}':结构体字段/成员不支持" + f"值引用(`&T` / `&temp T`)——值引用仅用于函数参数位,请改用显式指针类型(如 `T*`)") try: TypeInfo: CTypeInfo | None = self.ResolveAnnotationType(item.annotation) except RecursionError: diff --git a/lib/core/Handles/HandlesClassDef.py b/lib/core/Handles/HandlesClassDef.py index 5110f77..f2e7907 100644 --- a/lib/core/Handles/HandlesClassDef.py +++ b/lib/core/Handles/HandlesClassDef.py @@ -19,11 +19,12 @@ from lib.constants.config import mode as _config_mode from lib.includes import t from lib.includes.t import CTypeRegistry -# 枚举成员不允许的指针形态名:t.CPtr 家族(CPtr/CNeedPtr/CAutoPtr,后两者继承 -# CPtr)、内建 rawptr(= t.CPtr)、str/bytes(= i8*)。枚举值是整数常量, -# 指针成员语义不成立,命中即明确报错(不静默当 int)。 +# 枚举成员不允许的指针形态名:t.CPtr 家族(CPtr/CAutoPtr,后者继承 +# CPtr)、值引用标记(CRef/CTempRef,`&T`/`&temp T`)、内建 rawptr(= t.CPtr)、 +# str/bytes(= i8*)。枚举值是整数常量,指针成员语义不成立,命中即明确报错 +# (不静默当 int)。 _ENUM_PTR_ANNOTATION_NAMES: frozenset[str] = frozenset({ - 'CPtr', 'CNeedPtr', 'CAutoPtr', 'rawptr', 'str', 'bytes', + 'CPtr', 'CAutoPtr', 'CRef', 'CTempRef', 'rawptr', 'str', 'bytes', }) diff --git a/lib/core/Handles/HandlesExprCall.py b/lib/core/Handles/HandlesExprCall.py index 2e30612..43d8c46 100644 --- a/lib/core/Handles/HandlesExprCall.py +++ b/lib/core/Handles/HandlesExprCall.py @@ -27,6 +27,14 @@ _LEN_SUPPORTED_HINT: str = ( "或改用 t.CPtr[元素] 并显式维护元素计数" ) +# 已删除的用户可见 CIf 族(c.CIf / c.CIfdef / c.CIfndef / c.CElif / c.CElse / c.CEndif): +# 用户再写即在调用点 FailFast,指向原生 cif/celif/celse 与 defined(NAME)(英文报文, +# 与 TransPyV CallGenDispatch.vp 同口径)。内部标记 c.@CIf 族不受影响。 +_REMOVED_CIF_CALL_MSG: str = ( + "c.CIf / c.CIfdef / c.CIfndef were removed; use the cif / celif / celse statements " + "together with the defined(NAME) predicate instead " + "(e.g. `cif defined(NAME):` ... `celif ...:` ... `celse:` ...)") + # PROF 热点:_EmitLLVMIRInstruction 对每条 IR 模板都要做多次 re.match / re.split, # 未预编译的模块级入口每次都要「模式串哈希 → re 缓存查找 → 未命中就重新编译」, @@ -938,8 +946,9 @@ class ExprCallHandle(BaseHandle): return self._HandleCDerefAsLlvm(Node) elif FuncAttr == 'PtrToInt': return self.Trans.CSpecialCallHandle._HandleCPtrToIntLlvm(Node) - elif FuncAttr in ('CIf', 'CElif'): - return self.Trans.CSpecialCallHandle._HandleCIfLlvm(Node) + elif FuncAttr in ('CIf', 'CIfdef', 'CIfndef', 'CElif', 'CElse', 'CEndif'): + from lib.core.LLVMCG.FuncGen import FailFastError + raise FailFastError(_REMOVED_CIF_CALL_MSG) elif FuncAttr == 'CError': msg = "compile-time error" if Node.args: @@ -2003,8 +2012,9 @@ class ExprCallHandle(BaseHandle): return self._HandleCDerefAsLlvm(Node) elif func_name == 'PtrToInt': return self.Trans.CSpecialCallHandle._HandleCPtrToIntLlvm(Node) - elif func_name in ('CIf', 'CElif'): - return self.Trans.CSpecialCallHandle._HandleCIfLlvm(Node) + elif func_name in ('CIf', 'CIfdef', 'CIfndef', 'CElif', 'CElse', 'CEndif'): + from lib.core.LLVMCG.FuncGen import FailFastError + raise FailFastError(_REMOVED_CIF_CALL_MSG) elif func_name == 'CError': msg = "compile-time error" if Node.args: @@ -3355,19 +3365,9 @@ class ExprCallHandle(BaseHandle): return self._HandleCDerefAsLlvm(Node) elif MethodName == 'PtrToInt': return self.Trans.CSpecialCallHandle._HandleCPtrToIntLlvm(Node) - elif MethodName in ('CIf', 'CElif'): - if Node.args: - ArgVal = self.HandleExprLlvm(Node.args[0]) - if ArgVal: - if isinstance(ArgVal, ir.Constant) and isinstance(ArgVal.type, ir.IntType): - return ir.Constant(ir.IntType(1), 1 if ArgVal.constant != 0 else 0) - if isinstance(ArgVal.type, ir.IntType): - return Gen.builder.icmp_signed('!=', ArgVal, Gen._ZeroConst(ArgVal.type), name="cif_cond") - if isinstance(ArgVal.type, ir.PointerType): - return Gen.builder.icmp_signed('!=', ArgVal, Gen._ZeroConst(ArgVal.type), name="cif_cond") - return ir.Constant(ir.IntType(1), 0) - elif MethodName in ('CIfdef', 'CIfndef'): - return ir.Constant(ir.IntType(1), 1) + elif MethodName in ('CIf', 'CIfdef', 'CIfndef', 'CElif'): + from lib.core.LLVMCG.FuncGen import FailFastError + raise FailFastError(_REMOVED_CIF_CALL_MSG) elif MethodName == 'CError': msg = "compile-time error" if Node.args: @@ -3389,7 +3389,10 @@ class ExprCallHandle(BaseHandle): if _line_offset > 0 and lineno > _line_offset: lineno -= _line_offset raise Exception(f"#error: {msg} (line {lineno})") - elif MethodName in ('CElse', 'CEndif', 'CPragma', 'CUndef'): + elif MethodName in ('CElse', 'CEndif'): + from lib.core.LLVMCG.FuncGen import FailFastError + raise FailFastError(_REMOVED_CIF_CALL_MSG) + elif MethodName in ('CPragma', 'CUndef'): return ir.Constant(ir.IntType(1), 1) elif MethodName in ('LLVMIR', 'LInp', 'LOut'): return None diff --git a/lib/core/Handles/HandlesFunctions.py b/lib/core/Handles/HandlesFunctions.py index ae1c400..a5caef7 100644 --- a/lib/core/Handles/HandlesFunctions.py +++ b/lib/core/Handles/HandlesFunctions.py @@ -10,7 +10,7 @@ import llvmlite.ir as ir from lib.core.Handles.HandlesBase import BaseHandle, CTypeInfo, FuncMeta from lib.core.SymbolUtils import ( IsTModule, FindStructNameInAnnotationWithContext, AnnotationContainsName, - IsPtrSubscriptNode, DesugarPtrSubscriptToUnion) + IsPtrSubscriptNode, DesugarPtrSubscriptToUnion, FindAutoAddrMode) from lib.core.VLogger import get_logger as _vlog from lib.constants.config import mode as _config_mode from lib.includes import t @@ -35,6 +35,21 @@ def _RejectCReturnDecorator(node: ast.AST, decorators: list[ast.AST]) -> None: raise SyntaxError(f"函数 '{func_name}':{_CRETURN_REMOVED_HINT}") +def _RejectRefReturnAnnotation(node: ast.AST) -> None: + """返回注解出现值引用标记(`&T` / `&temp T`)即 fail-fast。 + + 值引用仅用于函数参数位;返回位一律拒绝,禁止静默按指针降级。 + """ + if getattr(node, 'returns', None) is None: + return + if FindAutoAddrMode(node.returns) is not None: + from lib.core.LLVMCG.FuncGen import FailFastError + func_name: str = getattr(node, 'name', '') + raise FailFastError( + f"返回注解不支持值引用 `&T` / `&temp T`(函数 '{func_name}'):" + f"值引用仅用于函数参数位;返回位请改用显式指针类型(如 `T*`)。") + + def _IsVoidOrDeclAnnotation(Annot: ast.AST) -> bool: """判断函数返回注解是否为 void 或 extern/export 声明型辅助符号。 @@ -505,6 +520,7 @@ class FunctionHandle(BaseHandle): FuncName, RawFuncName, func_meta = self._compute_func_name_meta(Node, ClassName) if Node.decorator_list: _RejectCReturnDecorator(Node, Node.decorator_list) + _RejectRefReturnAnnotation(Node) MultiRetTypes: list[ast.AST] = [] if Node.returns: llvm_attrs: list[str] = self._ExtractLLVMAttrs(Node.returns) @@ -762,16 +778,14 @@ class FunctionHandle(BaseHandle): ParamTypes.append(ParamType) ParamNames.append(Arg.arg) ParamTypeStrs.append(ParamTypeStr) - # 检测 t.CNeedPtr / t.CAutoPtr 参数注解 - auto_mode: str | None = None - if ParamTypeStr is not None and hasattr(ParamTypeStr, 'BaseType') and ParamTypeStr.BaseType is not None: - if isinstance(ParamTypeStr.BaseType, t.CAutoPtr): - auto_mode = 'always' - elif isinstance(ParamTypeStr.BaseType, t.CNeedPtr): - auto_mode = 'need' + # 自动取址模式(由注解读出,不依赖 BaseType 类型): + # 'ref' = &T、'temp' = &temp T(合成联合 T | t.CRef / t.CTempRef), + # 'always' = t.CAutoPtr。经 Gen._auto_addr_params 供调用点分流。 + auto_mode: str | None = FindAutoAddrMode(Arg.annotation) auto_addr_modes.append(auto_mode) if self._apply_struct_self_cls_types(ParamTypes, ParamNames, IsMethod, IsClassMethod, ResolvedClassName, Gen): ParamTypeStrs.insert(0, f"struct {ResolvedClassName}") + auto_addr_modes.insert(0, None) IsVariadic: bool = Node.args.vararg is not None if extra_params: for var_name, ptr_type in extra_params: @@ -1366,6 +1380,7 @@ class FunctionHandle(BaseHandle): if self._is_generic_function(Node): return FuncName, RawFuncName, func_meta = self._compute_func_name_meta(Node, ClassName) + _RejectRefReturnAnnotation(Node) # 保存父级 _CurrentCpythonObjectClass,防止嵌套函数发射污染父方法类上下文 # (例如 HandleImport body 内触发 list[ast.ASTptr] 方法发射,结束后会清空 _CurrentCpythonObjectClass) saved_cpython_class: str | None = self.Trans._CurrentCpythonObjectClass @@ -1667,13 +1682,8 @@ class FunctionHandle(BaseHandle): ParamTypes.append(ParamType) ParamNames.append(Arg.arg) ParamTypeStrs.append(ParamTypeInfo) - # 检测 t.CNeedPtr / t.CAutoPtr 参数注解 - auto_mode_emit: str | None = None - if ParamTypeInfo is not None and hasattr(ParamTypeInfo, 'BaseType') and ParamTypeInfo.BaseType is not None: - if isinstance(ParamTypeInfo.BaseType, t.CAutoPtr): - auto_mode_emit = 'always' - elif isinstance(ParamTypeInfo.BaseType, t.CNeedPtr): - auto_mode_emit = 'need' + # 自动取址模式(由注解读出):'ref' = &T、'temp' = &temp T、'always' = t.CAutoPtr + auto_mode_emit: str | None = FindAutoAddrMode(Arg.annotation) auto_addr_modes_emit.append(auto_mode_emit) Gen._record_var_signedness(Arg.arg, ParamIsUnsigned) self.Trans.VarScopes[-1][Arg.arg] = ParamTypeInfo @@ -1706,7 +1716,8 @@ class FunctionHandle(BaseHandle): self.Trans.CurrentMultiRetTypes = MultiRetTypes else: self.Trans.CurrentMultiRetTypes = None - self._apply_struct_self_cls_types(ParamTypes, ParamNames, IsMethod, IsClassMethod, ResolvedClassName, Gen) + if self._apply_struct_self_cls_types(ParamTypes, ParamNames, IsMethod, IsClassMethod, ResolvedClassName, Gen): + auto_addr_modes_emit.insert(0, None) IsVariadic = Node.args.vararg is not None if extra_params: for var_name, ptr_type in extra_params: diff --git a/lib/core/Handles/HandlesIf.py b/lib/core/Handles/HandlesIf.py index 0158434..ff2798a 100644 --- a/lib/core/Handles/HandlesIf.py +++ b/lib/core/Handles/HandlesIf.py @@ -23,12 +23,16 @@ class IfHandle(BaseHandle): return None def _is_cif_call(self, node: ast.expr) -> bool: + """判定 If.test 是否为 cif/celif 的内部标记调用(c.@CIf 族,解析期合成)。 + + 标记属性名带前导 '@'(词法器不可产出),故用户写的 c.CIf(...) 不会命中。 + """ if isinstance(node, ast.Call): if isinstance(node.func, ast.Attribute): if isinstance(node.func.value, ast.Name) and node.func.value.id == 'c': - return node.func.attr in ('CIf', 'CIfdef', 'CIfndef') + return node.func.attr in ('@CIf', '@CIfdef', '@CIfndef') if isinstance(node.func, ast.Name): - return node.func.id in ('CIf', 'CIfdef', 'CIfndef') + return node.func.id in ('@CIf', '@CIfdef', '@CIfndef') return False def _resolve_macro_name(self, arg: ast.expr) -> str | None: @@ -55,17 +59,17 @@ class IfHandle(BaseHandle): kind = node.func.id else: return None - if kind == 'CIfdef': + if kind == '@CIfdef': name: str | None = self._resolve_macro_name(args[0]) if name is None: return 0 return 1 if self._is_macro_defined(name) else 0 - elif kind == 'CIfndef': + elif kind == '@CIfndef': name: str | None = self._resolve_macro_name(args[0]) if name is None: return 1 return 0 if self._is_macro_defined(name) else 1 - elif kind == 'CIf': + elif kind == '@CIf': return self._eval_const_expr(args[0]) return None @@ -154,19 +158,36 @@ class IfHandle(BaseHandle): return 1 if result != 0.0 else 0 return result + def _CifExprText(self, node: ast.expr) -> str: + """cif/celif 标记调用内层条件表达式的源码文本(供 FailFast 报文,失败取占位)。""" + try: + if isinstance(node, ast.Call) and node.args: + return ast.unparse(node.args[0]) + return ast.unparse(node) + except Exception: + return '' + def _HandleIfLlvm(self, Node: ast.If) -> None: Gen: "Translator.LlvmGen" = self.Trans.LlvmGen if self._is_cif_call(Node.test): compile_time_val: int | None = self._evaluate_cif_condition(Node.test) - if compile_time_val is not None: - if compile_time_val: - self.Trans.BodyHandler.HandleBodyLlvm(Node.body) - elif Node.orelse: - if isinstance(Node.orelse, list) and len(Node.orelse) == 1 and isinstance(Node.orelse[0], ast.If): - self._HandleIfLlvm(Node.orelse[0]) - else: - self.Trans.BodyHandler.HandleBodyLlvm(Node.orelse) - return + if compile_time_val is None: + # cif/celif 条件必须是编译期常量:求不出 ⇒ FailFast(不再回退运行时分支)。 + from lib.core.LLVMCG.FuncGen import FailFastError + expr_text: str = self._CifExprText(Node.test) + Message: str = ( + f"'cif' / 'celif' condition is not a compile-time constant: {expr_text}. " + f"Only CDefine constants, literals, their arithmetic/comparison/boolean/" + f"bitwise operators and the 'defined(NAME)' predicate are allowed.") + raise FailFastError(Message + (Gen._get_node_info() if Gen else '')) + if compile_time_val: + self.Trans.BodyHandler.HandleBodyLlvm(Node.body) + elif Node.orelse: + if isinstance(Node.orelse, list) and len(Node.orelse) == 1 and isinstance(Node.orelse[0], ast.If): + self._HandleIfLlvm(Node.orelse[0]) + else: + self.Trans.BodyHandler.HandleBodyLlvm(Node.orelse) + return Cond: ir.Value | None = self.Trans.ExprHandler.HandleExprLlvm(Node.test) if not Cond: return diff --git a/lib/core/Handles/HandlesImports.py b/lib/core/Handles/HandlesImports.py index b3668c7..2a9dd41 100644 --- a/lib/core/Handles/HandlesImports.py +++ b/lib/core/Handles/HandlesImports.py @@ -14,7 +14,7 @@ from lib.core.VLogger import get_logger as _vlog from lib.constants.config import mode as _config_mode from lib.includes import t from lib.includes.t import CTypeRegistry -from lib.core.SymbolUtils import ParseListAnnotation, ExtractTypeNameFromBinOp, AnnotationContainsName, FindStructNameInAnnotationWithContext +from lib.core.SymbolUtils import ParseListAnnotation, ExtractTypeNameFromBinOp, AnnotationContainsName, FindStructNameInAnnotationWithContext, FindAutoAddrMode from lib.core.SymbolUtils import EnumClassIntWidth, ExpandSoftKeywords from lib.core.SymbolUtils import ClassHasFuncLevelCVTable, IsVtableSlotMethod @@ -1870,6 +1870,13 @@ class ImportHandle(BaseHandle): func = Gen._get_or_declare_function(MangledName, FuncType) Gen.functions[MangledName] = func Gen.functions[FuncName] = func + # 跨模块自动取址模式登记(ref/temp/always):调用点 Gen._apply_auto_addr 按函数名 + # 查表,命中才做取址适配。ParamTypes 与 Node.args.args 逐位对齐(含方法 self)。 + _auto_modes: list[str | None] = [ + FindAutoAddrMode(a.annotation) for a in Node.args.args] + if any(m is not None for m in _auto_modes): + Gen._auto_addr_params[MangledName] = _auto_modes + Gen._auto_addr_params[FuncName] = _auto_modes func_meta = FuncMeta.NONE if Node.decorator_list: for d in Node.decorator_list: diff --git a/lib/core/Handles/HandlesTypeMerge.py b/lib/core/Handles/HandlesTypeMerge.py index e1008a2..f40a737 100644 --- a/lib/core/Handles/HandlesTypeMerge.py +++ b/lib/core/Handles/HandlesTypeMerge.py @@ -207,6 +207,13 @@ class HandlesTypeMerge(BaseHandle): return Info if ModulePath == 't' or (ModulePath and ModulePath.startswith('t.')): + # 值引用参数标记(CRef = `&T`、CTempRef = `&temp T`,非真实类):与 + # t.CPtr 同形的「一层指针」,使 `T | t.CRef` 合并为 T*(pointer 层 +1)。 + if TypeName in ('CRef', 'CTempRef'): + Info = CTypeInfo() + Info.PtrCount = 1 + Info.BaseType = t.CVoid + return Info if self._IsTModuleType(TypeName): TypeClass = CTypeHelper.GetTModuleCType(TypeName) or getattr(t, TypeName, None) if TypeClass == t.CArrayPtr: diff --git a/lib/core/LLVMCG/BaseGen.py b/lib/core/LLVMCG/BaseGen.py index 05e8b95..c0c45d2 100644 --- a/lib/core/LLVMCG/BaseGen.py +++ b/lib/core/LLVMCG/BaseGen.py @@ -378,8 +378,9 @@ class BaseGenMixin: # 编译期 with 上下文栈:记录当前嵌套 with 链中所有可用的 provides 字段名 # 用于 __require_must__ 的纯编译期静态可达性检查 self._with_provides_stack: list[list[str]] = [] - # 函数参数自动取地址模式: func_name -> [None, None, 'need', 'always', ...] - # 'need': t.CNeedPtr — 仅对非指针值取地址 + # 函数参数自动取地址模式: func_name -> [None, None, 'ref', 'temp', 'always', ...] + # 'ref': &T — 实参非指针左值取址,右值 fail-fast + # 'temp': &temp T — 实参非指针时在调用方建同型临时再取址 # 'always': t.CAutoPtr — 无条件取地址 self._auto_addr_params: dict[str, list[str | None]] = {} self.global_struct_class: dict[str, str] = {} diff --git a/lib/core/LLVMCG/FuncGen.py b/lib/core/LLVMCG/FuncGen.py index 8d176ce..6e01e10 100644 --- a/lib/core/LLVMCG/FuncGen.py +++ b/lib/core/LLVMCG/FuncGen.py @@ -94,11 +94,17 @@ class FuncGenMixin: self.class_methods[ClassName].append(MethodName) def _apply_auto_addr(self, func_name: str, args: list[ir.Value]) -> list[ir.Value]: - """对函数参数应用自动取地址逻辑(t.CNeedPtr / t.CAutoPtr)。 - - 在调用前对标记为 auto-addr 的参数自动取地址: - - 'need' (t.CNeedPtr): 仅对非指针值取地址(alloca + store + get pointer) - - 'always' (t.CAutoPtr): 无条件取地址 + """对函数参数应用自动取址逻辑(ref / temp / always 三种模式)。 + + 在调用前对标记为 auto-addr 的实参做地址适配(模式由 HandlesFunctions 从形参注解 + 读出并写入 Gen._auto_addr_params[func_name]): + - 'ref'(&T):实参已是指针 → 原样传;非指针左值 → 传该左值地址; + 非指针且非左值(字面量/表达式结果)→ fail-fast(禁止静默降级)。 + - 'temp'(&temp T):实参已是指针 → 原样传;否则在 entry 块建同型临时 + store + 传地址。 + - 'always'(t.CAutoPtr):无条件取址(保持既有语义)。 + + 非指针左值的地址取自实参 load 指令的指针操作数(`load(p)` ⇒ p 即左值地址), + 因为调用点实参已按 rvalue 求值(标量变量的 load 结果)。 """ modes: list[str | None] | None = self._auto_addr_params.get(func_name) if not modes: @@ -109,8 +115,23 @@ class FuncGenMixin: continue arg: ir.Value = new_args[i] is_ptr: bool = isinstance(arg.type, ir.PointerType) - if mode == 'always' or (mode == 'need' and not is_ptr): - # alloca + store + get pointer + if mode == 'ref': + if is_ptr: + continue + if isinstance(arg, ir.LoadInstr) and len(arg.operands) == 1: + # load(p) 的 p 即该左值的地址(该实参是变量/字段/下标的取值) + new_args[i] = arg.operands[0] + continue + raise FailFastError( + f"&T 形参只接受左值,右值请用具名临时变量或改用 &temp T" + f"(调用 {func_name} 第 {i} 个实参类型 {arg.type})") + if mode == 'temp': + if is_ptr: + continue + slot: ir.Value = self._allocaEntry(arg.type, name=f"autoaddr_tmp_{i}") + self.builder.store(arg, slot) + new_args[i] = slot + elif mode == 'always': slot: ir.Value = self.builder.alloca(arg.type) self.builder.store(arg, slot) new_args[i] = slot diff --git a/lib/core/SymbolUtils.py b/lib/core/SymbolUtils.py index 2261037..eb9cc8a 100644 --- a/lib/core/SymbolUtils.py +++ b/lib/core/SymbolUtils.py @@ -2386,6 +2386,38 @@ def IsCPtrNode(node: ast.AST) -> bool: and node.attr == 'CPtr') +# 值引用参数标记名 → 自动取址模式(仅注解 AST 内按名字识别,非真实类): +# 'CRef' → 'ref' (&T :实参非指针左值取址,右值 fail-fast) +# 'CTempRef' → 'temp' (&temp T :实参非指针时在调用方建同型临时再取址) +# 'CAutoPtr' → 'always' (t.CAutoPtr:无条件取址,保持既有语义) +_AUTO_ADDR_MARKER_MODES: dict[str, str] = { + 'CRef': 'ref', 'CTempRef': 'temp', 'CAutoPtr': 'always', +} + + +def FindAutoAddrMode(node: ast.AST | None) -> str | None: + """注解 AST 内查找自动取址模式标记,返回 'ref' / 'temp' / 'always';无则 None。 + + 只按名字识别(Attribute.attr 或 Name.id),递归联合/嵌套注解;CRef/CTempRef + 非真实类,不出现在 lib/includes/t.py。 + """ + if node is None: + return None + if isinstance(node, ast.Attribute): + mode = _AUTO_ADDR_MARKER_MODES.get(node.attr) + if mode is not None: + return mode + elif isinstance(node, ast.Name): + mode = _AUTO_ADDR_MARKER_MODES.get(node.id) + if mode is not None: + return mode + for child in ast.iter_child_nodes(node): + mode = FindAutoAddrMode(child) + if mode is not None: + return mode + return None + + def IsCPtrSubscriptNode(node: ast.AST) -> bool: """检查节点是否为 t.CPtr[T] 下标指针类型(非裸标记,裸标记见 IsCPtrNode)。 diff --git a/lib/core/TypeAnnotationResolver.py b/lib/core/TypeAnnotationResolver.py index 0a8e227..db97b30 100644 --- a/lib/core/TypeAnnotationResolver.py +++ b/lib/core/TypeAnnotationResolver.py @@ -1131,6 +1131,14 @@ class TypeAnnotationResolver: ModulePath = resolved_name if ModulePath and IsTModulePath(ModulePath): + # 值引用参数标记(CRef = `&T`、CTempRef = `&temp T`):非真实类,仅按名字 + # 识别为「一层指针」(与 t.CPtr 同形),使联合形态 `T | t.CRef` 解析为 T*、 + # `void | t.CRef` 解析为 void*(i8*)。模式语义(ref/temp)由注解侧识别。 + if TypeName in ('CRef', 'CTempRef'): + Info: CTypeInfo = CTypeInfo() + Info.PtrCount = 1 + Info.BaseType = t.CVoid + return Info TypeClass: type | None = CTypeHelper.GetTModuleCType(TypeName) if TypeClass is None: TypeClass = getattr(t, TypeName, None) diff --git a/lib/includes/c.py b/lib/includes/c.py index 5f9b11a..d2c86ec 100644 --- a/lib/includes/c.py +++ b/lib/includes/c.py @@ -454,32 +454,6 @@ class CDefine: return [] -class CIfndef: - """#ifndef 条件编译""" - def __init__(self, condition: Any) -> None: - self.condition: Any = condition - - @staticmethod - def HandleCall(translator: Any, args: Any, keywords: Any) -> list[str]: - """处理 c.CIfndef() 调用""" - if args: - condition: Any = translator.HandleExpr(args[0])[0] - return ['#ifndef ' + _to_str(condition)] - return ['#if 0'] - -class CIfdef: - """#ifdef 条件编译""" - def __init__(self, condition: Any) -> None: - self.condition: Any = condition - - @staticmethod - def HandleCall(translator: Any, args: Any, keywords: Any) -> list[str]: - """处理 c.CIfdef() 调用""" - if args: - condition: Any = translator.HandleExpr(args[0])[0] - return ['#ifdef ' + _to_str(condition)] - return ['#if 0'] - class CError: """#error 错误信息""" def __init__(self, condition: Any) -> None: @@ -536,20 +510,6 @@ class TokenPast: return [''] -class CIf: - """#if 条件编译""" - def __init__(self, condition: Any) -> None: - self.condition: Any = condition - - @staticmethod - def HandleCall(translator: Any, args: Any, keywords: Any) -> list[str]: - """处理 c.CIf() 调用""" - if args: - condition: Any = translator.HandleExpr(args[0])[0] - return ['#if ' + _to_str(condition)] - return ['#if 0'] - - class CElif: """#elif 条件编译""" def __init__(self, condition: Any) -> None: @@ -688,9 +648,6 @@ Library_C = { 'AsmInp': AsmInp, 'AsmOut': AsmOut, 'CDefine': CDefine, - 'CIfndef': CIfndef, - 'CIfdef': CIfdef, - 'CIf': CIf, 'CElif': CElif, 'CElse': CElse, 'CEndif': CEndif, diff --git a/lib/includes/t.py b/lib/includes/t.py index 690c97b..69afa18 100644 --- a/lib/includes/t.py +++ b/lib/includes/t.py @@ -530,15 +530,6 @@ class CPtr(CType, Generic[T]): def __init__(self, value: Any = None) -> None: super().__init__(value) -class CNeedPtr(CPtr): - """函数签名注解:自动取地址(仅对非指针值)。 - 用作参数类型时,编译器在调用处自动将非指针值取地址为指针。 - 若参数已经是指针,则不做操作。""" - position: ClassVar[frozenset[str]] = frozenset({CType.POINTER}) - auto_addr_mode: ClassVar[str] = 'need' - def __init__(self, value: Any = None) -> None: - super().__init__(value) - class CAutoPtr(CPtr): """函数签名注解:无条件取地址。 用作参数类型时,编译器在调用处无条件将值取地址为指针, diff --git a/lib/vast/ExprParser.py b/lib/vast/ExprParser.py index 07c3935..d13caac 100644 --- a/lib/vast/ExprParser.py +++ b/lib/vast/ExprParser.py @@ -40,6 +40,12 @@ def _JoinConstParts(parts: list[object]) -> str | bytes: _HEX_ALPHABET: str = '0123456789abcdefABCDEF' +# 已删除的用户可见 CIf 族名:用户再写 c.CIf / c.CIfdef / c.CIfndef ⇒ FailFast, +# 提示改用原生 cif/celif/celse 与 defined() 谓词(内部标记由解析期合成,不走此处)。 +_REMOVED_CIF_NAMES: frozenset[str] = frozenset({ + 'CIf', 'CIfdef', 'CIfndef', 'c.CIf', 'c.CIfdef', 'c.CIfndef', +}) + def _HexValOf(ch: str) -> int: """单个字符的十六进制位值;非十六进制字符返回 0(与 C 解析器 vast_c.HexVal 同口径)。""" @@ -581,6 +587,44 @@ class ExprParserMixin(TokenStream): self._ExpectOp(')') node: N.Call = N.Call(func=func, args=args, keywords=keywords) self._FixLoc(node, start) + return self._DesugarSpecialCall(node, start) + + def _CallCalleeName(self, func: N.expr) -> str: + """被调用者的限定名:Name('X')→'X';Attribute(c,'X')→'c.X';否则空串。""" + if isinstance(func, N.Name): + return func.id + if isinstance(func, N.Attribute) and isinstance(func.value, N.Name): + return f'{func.value.id}.{func.attr}' + return '' + + def _DesugarSpecialCall(self, node: N.Call, start: Token) -> N.expr: + """调用点的特殊 desugar / fail-fast。 + + - `defined(NAME)`:仅 cif/celif 条件内合法(`self._InCIfCond`), + desugar 为内部标记调用 `c.CIfdef(NAME)`;其余位置 FailFast。 + - `c.CIf` / `c.CIfdef` / `c.CIfndef`(及裸名):已删除,FailFast 指向新语法。 + """ + callee: str = self._CallCalleeName(node.func) + if callee in _REMOVED_CIF_NAMES: + raise SyntaxError( + f"'{callee}(...)' has been removed; use the native 'cif' / 'celif' / " + f"'celse' statements together with the 'defined(NAME)' predicate " + f"instead, at line {start.Line}") + if callee == 'defined': + if not self._InCIfCond: + raise SyntaxError( + "'defined(NAME)' is only allowed inside a 'cif' / 'celif' " + f"condition, at line {start.Line}") + if len(node.args) != 1 or node.keywords: + raise SyntaxError( + f"'defined' expects exactly one macro name (e.g. defined(MAX_SIZE)), " + f"at line {start.Line}") + marker: N.Call = N.Call( + func=N.Attribute(value=N.Name(id='c', ctx=N.Load()), + attr='@CIfdef', ctx=N.Load()), + args=[node.args[0]], keywords=[]) + self._FixLoc(marker, start) + return marker return node def _ParseSubscriptTail(self, value: N.expr, start: Token) -> N.Subscript: @@ -1567,7 +1611,33 @@ class ExprParserMixin(TokenStream): 正确写法示例),绝不静默丢弃:注解末尾若仍遗留未被 `_AtPtrSuffix` 接受的 '*'/'**'(如 `a: int * 2` / `a: int* | t.CPtr`),判据拒绝消费即终止编译 (历史事故:静默退化会产出错误的指针层数)。 + + 值引用参数注解(仅形参位有效):前导 `&` 为 ref 模式、`&temp` 为 temp 模式, + 合成联合形态 `T | t.CRef` / `T | t.CTempRef` 承载(CRef/CTempRef 非真实类, + 仅按名字识别;语义侧见 HandlesFunctions / FuncGen)。表达式位的 `&x` 不受影响。 """ + ref_mode: str | None = None + ref_tok: Token = self._Peek() + if self._AtOp('&'): + self._Next() + if self._AtName('temp'): + self._Next() + ref_mode = 'temp' + else: + ref_mode = 'ref' + # `&` / `&temp` 之后必须紧跟类型;缺失即响亮失败(禁止静默丢弃) + end_tok: Token = self._Peek() + if end_tok.Type in (NEWLINE, NL, ENDMARKER) or ( + end_tok.Type == OP and end_tok.Value in (')', ',', ':', '=', ']', '}', ';')): + snippet: str = '' + try: + snippet = (self.Source.splitlines()[end_tok.Line - 1] or '').strip() + except Exception: + snippet = '' + raise SyntaxError( + f"[vast] 值引用参数注解缺少类型:第 {end_tok.Line} 行第 {end_tok.Col} 列的 " + f"`&` 后必须紧跟类型 T(ref:`&T`;temp:`&temp T`)。" + f"源码片段 {snippet!r}。正确示例:`def f(p: &int)` / `def f(p: &temp void)`。") saved_flag: bool = self._InTypeAnnotation self._InTypeAnnotation = True try: @@ -1575,9 +1645,13 @@ class ExprParserMixin(TokenStream): finally: self._InTypeAnnotation = saved_flag if expr is None: + if ref_mode is not None: + raise SyntaxError( + "[vast] 值引用参数注解缺少类型:`&` 后必须紧跟类型 T" + "(ref:`&T`;temp:`&temp T`)。") return None if self._AtPtrSuffix(expr): - return self._ConsumePtrSuffix(expr) + expr = self._ConsumePtrSuffix(expr) tok: Token = self._Peek() if tok.Type == OP and tok.Value in ('*', '**'): snippet: str = '' @@ -1595,8 +1669,23 @@ class ExprParserMixin(TokenStream): f"正确示例:`a: t.CPtr*` / `a: rawptr*` / `a: t.X**` / " f"`a: rawptr[ast.AST*]` / `def f(p: int*) -> int**`。" ) + if ref_mode is not None: + expr = self._MakeRefMarker(expr, ref_mode, ref_tok) return expr + def _MakeRefMarker(self, base: N.expr, mode: str, tok: Token) -> N.expr: + """构造值引用标记联合:`base | t.CRef`(ref)/ `base | t.CTempRef`(temp)。 + + CRef / CTempRef 非真实类,仅作为注解 AST 内的名字标记(语义侧按名字识别)。 + """ + marker: N.expr = N.Attribute( + value=N.Name(id='t', ctx=N.Load()), + attr='CRef' if mode == 'ref' else 'CTempRef', + ctx=N.Load()) + node: N.BinOp = N.BinOp(left=base, op=N.BitOr(), right=marker) + self._FixLoc(node, tok) + return node + # 类型结束后缀:'_AtPtrSuffix' 的判据表 # - 全局(任意表达式上下文):'*'/'**' 串后紧跟这些 operator 时,乘法/幂本就缺右操作数 # (即原语法错误),故按指针后缀处理不会改变任何合法表达式的语义; diff --git a/lib/vast/Parser.py b/lib/vast/Parser.py index be0533e..1d5be0a 100644 --- a/lib/vast/Parser.py +++ b/lib/vast/Parser.py @@ -91,6 +91,32 @@ _SWITCH_KEYWORD: str = 'switch' # 标记调用(switch 生成侧据此让 case 体不自动跳出 switch,落到下一个 case)。 _FALLTHROUGH_KEYWORD: str = 'fallthrough' +# ====================================================================== +# cif / celif / celse 编译期条件编译(软关键字) +# ====================================================================== +# 语法:`cif <编译期常量表达式>:` / `celif <...>:` / `celse:` +# 语义:只有 cif 能开链;celif/celse 必须属于同一条 cif 链。 +# 解析期 desugar 为既有「If + CIf 族标记调用」形状(与 Viper 侧口径一致): +# cif E: → If(test=c.@CIf(E), body, orelse) +# celif E: → 同链下一个 If(test=c.@CIf(E), ...)(挂在上一支 orelse) +# celse: → 普通 orelse +# defined(N) → c.@CIfdef(N) 标记调用 +# 标记(c.@CIf / c.@CIfdef)是内部表示:用户写 c.CIf(...) 在 ExprParser 处报错。 +_CIF_KEYWORD: str = 'cif' +_CELIF_KEYWORD: str = 'celif' +_CELSE_KEYWORD: str = 'celse' +# 内部 CIf 族标记:desugar 产出的调用名(用户不可写)。 +# 与 Viper 侧解析器(includes/ast/parser.vp 的 _MakeCifMarker)同拼写:属性名带前导 '@', +# 词法器无法产出该拼写,故用户写的 c.CIf(...) 永远不会命中此标记(会被 ExprParser 拒绝)。 +_CIF_MARKER: str = '@CIf' +_CIFDEF_MARKER: str = '@CIfdef' +# cif 语句起始判定:语句起始 NAME('cif') 后紧跟这些运算符时按普通语句(赋值/注解) +# 处理,避免 `cif = 5` / `cif: int = 5` 被误判为 cif 语句。 +_CIF_NON_STATEMENT_OPS: frozenset[str] = frozenset({ + '=', ':', '+=', '-=', '*=', '/=', '//=', '%=', '**=', + '<<=', '>>=', '&=', '|=', '^=', '@=', +}) + class Parser(ExprParserMixin): """Viper/Python 完整语法分析器。""" @@ -101,6 +127,10 @@ class Parser(ExprParserMixin): # "virtual def / novirtual def 只能直接写在类体内"。 _InClassBody: bool = False + # _InCIfCond:当前是否正在解析 cif/celif 的条件表达式。用于判定 `defined(NAME)` + # 谓词的合法位置(仅 cif/celif 条件内合法,其余位置 FailFast)。 + _InCIfCond: bool = False + # ================================================================== # 模块级入口 # ================================================================== @@ -177,6 +207,15 @@ class Parser(ExprParserMixin): # struct 纯数据类型定义(硬关键字):语句起始一律按 struct 定义解析。 if v == _STRUCT_KEYWORD: return self._ParseStructDef() + # cif 编译期条件编译(软关键字):语句起始 + 非赋值/注解形态。 + if v == _CIF_KEYWORD and self._IsCIfStatementStart(): + return self._ParseCIf() + # celif / celse 必须属于某条 cif 链(由 _ParseCIf 直接消费); + # 语句起始独立出现 ⇒ FailFast。 + if v == _CELIF_KEYWORD or v == _CELSE_KEYWORD: + raise SyntaxError( + f"'{v}' must belong to a 'cif' chain (no matching 'cif') " + f"at line {tok.Line}") if v == 'if': return self._ParseIf() if v == 'while': @@ -1215,6 +1254,88 @@ class Parser(ExprParserMixin): self._FixLoc(node, start) return node + # ================================================================== + # cif / celif / celse 编译期条件编译(软关键字,desugar 到 c.CIf 标记) + # ================================================================== + def _IsCIfStatementStart(self) -> bool: + """语句起始 NAME('cif') 是否按 cif 语句解析。 + + 排除赋值/注解形态(`cif = 5` / `cif: int = 5` / `cif += 1`): + 第二种 token 为 '=' ':' 或增强赋值运算符时按普通语句处理。 + """ + nxt: Token = self._Peek(1) + if nxt.Type == OP and nxt.Value in _CIF_NON_STATEMENT_OPS: + return False + return True + + def _MakeCIfMarkerCall(self, attr: str, expr: N.expr, start: Token) -> N.Call: + """构造内部标记调用 `c.(expr)`(用户不可写;cif/defined 的 desugar)。""" + func: N.Attribute = N.Attribute( + value=N.Name(id='c', ctx=N.Load()), attr=attr, ctx=N.Load()) + self._FixLoc(func, start) + call: N.Call = N.Call(func=func, args=[expr], keywords=[]) + self._FixLoc(call, start) + return call + + def _ParseCIf(self) -> N.If: + """`cif :`(+ 可选 celif/celse 链)。""" + start: Token = self._Next() # cif + test: N.expr = self._ParseCIfCondition() + self._ExpectOp(':') + body: list[N.stmt] = self._ParseSuite() + orelse: list[N.stmt] = self._ParseCIfChainTail() + node: N.If = N.If(test=self._MakeCIfMarkerCall(_CIF_MARKER, test, start), + body=body, orelse=orelse) + self._FixLoc(node, start) + return node + + def _ParseCIfCondition(self) -> N.expr: + """解析 cif/celif 条件表达式,期间允许 `defined(NAME)` 谓词。""" + prev: bool = self._InCIfCond + self._InCIfCond = True + try: + return self.ParseExpr() + finally: + self._InCIfCond = prev + + def _ParseCIfChainTail(self) -> list[N.stmt]: + """解析 cif 链尾部(celif/celse),返回 orelse 列表。 + + - 无 celif/celse:返回空(cif 链正常收尾)。 + - 出现普通 elif/else:FailFast(cif 链必须以 celse 收尾)。 + """ + if self._AtName(_CELIF_KEYWORD): + return [self._ParseCelif()] + if self._AtName(_CELSE_KEYWORD): + self._Next() # celse + self._ExpectOp(':') + body: list[N.stmt] = self._ParseSuite() + # celse 之后不得再出现 celif / celse(同缩进层级会落在本方法可见处)。 + if self._AtName(_CELIF_KEYWORD) or self._AtName(_CELSE_KEYWORD): + bad_tok: Token = self._Peek() + raise SyntaxError( + f"'{bad_tok.Value}' cannot follow 'celse' (the 'celse' branch must be " + f"the last of the 'cif' chain) at line {bad_tok.Line}") + return body + if self._AtName('elif') or self._AtName('else'): + kind: str = self._Peek().Value + raise SyntaxError( + f"a 'cif' chain must be closed with 'celse', not '{kind}', " + f"at line {self._Peek().Line}") + return [] + + def _ParseCelif(self) -> N.If: + """`celif :`(cif 链的后续分支)。""" + start: Token = self._Next() # celif + test: N.expr = self._ParseCIfCondition() + self._ExpectOp(':') + body: list[N.stmt] = self._ParseSuite() + orelse: list[N.stmt] = self._ParseCIfChainTail() + node: N.If = N.If(test=self._MakeCIfMarkerCall(_CIF_MARKER, test, start), + body=body, orelse=orelse) + self._FixLoc(node, start) + return node + def _ParseWhile(self) -> N.While: start: Token = self._Next() # while test: N.expr = self.ParseExpr() diff --git a/lib/vast_c/_parser.pyd b/lib/vast_c/_parser.pyd index 9e5184d..1cdbe58 100644 Binary files a/lib/vast_c/_parser.pyd and b/lib/vast_c/_parser.pyd differ diff --git a/lib/vast_c/vast_c.c b/lib/vast_c/vast_c.c index 46e47fb..8df093e 100644 --- a/lib/vast_c/vast_c.c +++ b/lib/vast_c/vast_c.c @@ -101,6 +101,7 @@ typedef struct { int FStringExprEnd; /* f-string 表达式段结束位置(body 内偏移) */ int InTypeAnnotation; /* 类型注解模式:'*'/'**' 允许按 C 风格指针后缀判定 */ int InClassBody; /* 当前套件的直接拥有者是否为 ClassDef(virtual def 判据) */ + int InCIfCond; /* 是否正在解析 cif/celif 条件(defined(NAME) 合法位置判据) */ Token *PtrSuffixTail; /* 最近消费的 C 指针后缀尾 token(识别 (T*)x 无括号强转) */ } Parser; @@ -2652,6 +2653,8 @@ static PyObject *ConsumePtrSuffix(Parser *P, PyObject *Node) { } static PyObject *ParseCallTail(Parser *P, PyObject *Func, Token *Start); +static PyObject *DesugarSpecialCall(Parser *P, PyObject *Node, Token *Start); +static PyObject *MakeCifMarkerCall(Parser *P, const char *AttrName, PyObject *Expr, Token *Start); /* 判定当前位置的 '*'/'**' 串是否为 `T*(x)` 形式的指针后缀**强转调用** * (对齐 Python ExprParser._AtPtrSuffixCall): @@ -2709,13 +2712,64 @@ static PyObject *ConsumePtrSuffixCall(Parser *P, PyObject *Base, Token *Start) { return Call; } -/* 类型注解入口:置注解模式标志(对齐 Python ExprParser._ParseTypeAnnotation)。 */ +/* 类型注解入口:置注解模式标志(对齐 Python ExprParser._ParseTypeAnnotation)。 + * + * 值引用参数注解(仅形参位有效,对齐纯 Python 端): + * 前导 '&' + 可选名 'temp',合成联合形态 base | t.CRef(ref)/ base | t.CTempRef(temp)。 + * CRef/CTempRef 非真实类,仅作注解 AST 内的名字标记(语义侧按名字识别)。 + * '&'/'&temp' 之后缺类型(行尾 / ')' / ',' / ':' / '=' / ']' / '}' / ';')时响亮失败。 */ static PyObject *ParseTypeAnnotation(Parser *P) { + int RefTemp = 0; /* 0=非值引用 1=ref 2=temp */ + Token *RefTok = PeekTok(P, 0); + if (AtOp(P, "&")) { + NextTok(P); + if (AtName(P, "temp")) { + NextTok(P); + RefTemp = 2; + } else { + RefTemp = 1; + } + Token *End = PeekTok(P, 0); + if (End->Kind == TK_NEWLINE || End->Kind == TK_NL + || End->Kind == TK_ENDMARKER + || TokIsOp(End, ")") || TokIsOp(End, ",") || TokIsOp(End, ":") + || TokIsOp(End, "=") || TokIsOp(End, "]") || TokIsOp(End, "}") + || TokIsOp(End, ";")) { + SyntaxErrorAt(P, End, + "值引用参数注解缺少类型:`&` 后必须紧跟类型 T(ref:`&T`;temp:`&temp T`)"); + return NULL; + } + } int Saved = P->InTypeAnnotation; P->InTypeAnnotation = 1; PyObject *Node = ParseExpr(P); P->InTypeAnnotation = Saved; - return Node; + if (Node == NULL) { + return NULL; + } + if (RefTemp == 0) { + return Node; + } + PyObject *Marker = MakeTAttrNode(P, RefTemp == 2 ? "CTempRef" : "CRef"); + if (Marker == NULL) { + Py_DECREF(Node); + return NULL; + } + PyObject *Op = MakeSingleton(P, "BitOr"); + if (Op == NULL) { + Py_DECREF(Node); + Py_DECREF(Marker); + return NULL; + } + PyObject *Argv[3] = { Node, Op, Marker }; + PyObject *Wrap = NewNode(P, "BinOp", Argv, 3); + Py_DECREF(Op); + Py_DECREF(Marker); + Py_DECREF(Node); + if (Wrap != NULL) { + FixLoc(P, Wrap, RefTok); + } + return Wrap; } /* term:factor (*|/|//|%|@ factor)* */ @@ -3008,10 +3062,127 @@ static PyObject *ParseCallTail(Parser *P, PyObject *Func, Token *Start) { Py_DECREF(Keywords); if (Node != NULL) { FixLoc(P, Node, Start); + Node = DesugarSpecialCall(P, Node, Start); } return Node; } +/* 被调用者限定名:Name('X')→'X';Attribute(Name('c'),'X')→'c.X';否则 NULL(无异常)。 */ +static PyObject *MakeCifMarkerCall(Parser *P, const char *AttrName, PyObject *Expr, Token *Start); +static PyObject *CalleeQualName(PyObject *Func) { + if (NodeIs(Func, "Name")) { + return PyObject_GetAttrString(Func, "id"); + } + if (NodeIs(Func, "Attribute")) { + PyObject *Value = PyObject_GetAttrString(Func, "value"); + PyObject *Attr = PyObject_GetAttrString(Func, "attr"); + PyObject *Result = NULL; + if (Value != NULL && Attr != NULL && NodeIs(Value, "Name")) { + PyObject *Id = PyObject_GetAttrString(Value, "id"); + if (Id != NULL) { + Result = PyUnicode_FromFormat("%U.%U", Id, Attr); + Py_DECREF(Id); + } + } + Py_XDECREF(Value); + Py_XDECREF(Attr); + return Result; + } + return NULL; +} + +static int StrIsOneOf(PyObject *S, const char *const *Names) { + for (int I = 0; Names[I] != NULL; I++) { + if (PyUnicode_CompareWithASCIIString(S, Names[I]) == 0) { + return 1; + } + } + return 0; +} + +/* 调用点特殊 desugar / fail-fast:接管 Node 的引用,返回新引用(失败返回 NULL)。 + * - defined(NAME):仅 cif/celif 条件内合法(P->InCIfCond)⇒ desugar 为 c.@CIfdef(NAME); + * 其余位置 ⇒ FailFast。 + * - c.CIf / c.CIfdef / c.CIfndef(及裸名):已删除 ⇒ FailFast,指向原生新语法。 */ +static PyObject *DesugarSpecialCall(Parser *P, PyObject *Node, Token *Start) { + PyObject *Func = PyObject_GetAttrString(Node, "func"); + if (Func == NULL) { + PyErr_Clear(); + return Node; + } + /* 廉价预筛:只看标识符短名(Name.id / Attribute.attr),与 cif 无关的调用一律 + * 立即返回,避免在解析热路径上为每个调用构造限定名并分配字符串。 */ + PyObject *Short = NULL; + if (NodeIs(Func, "Name")) { + Short = PyObject_GetAttrString(Func, "id"); + } else if (NodeIs(Func, "Attribute")) { + Short = PyObject_GetAttrString(Func, "attr"); + } + if (Short == NULL) { + PyErr_Clear(); + Py_DECREF(Func); + return Node; + } + static const char *Interesting[] = {"CIf", "CIfdef", "CIfndef", "defined", NULL}; + int Hit = StrIsOneOf(Short, Interesting); + Py_DECREF(Short); + if (!Hit) { + Py_DECREF(Func); + return Node; + } + PyObject *Callee = CalleeQualName(Func); + Py_DECREF(Func); + if (Callee == NULL) { + PyErr_Clear(); + return Node; + } + static const char *Removed[] = { + "CIf", "CIfdef", "CIfndef", "c.CIf", "c.CIfdef", "c.CIfndef", NULL + }; + if (StrIsOneOf(Callee, Removed)) { + PyErr_Format(PyExc_SyntaxError, + "'%U(...)' has been removed; use the native 'cif' / 'celif' / 'celse' " + "statements together with the 'defined(NAME)' predicate instead, at line %d", + Callee, Start->Line); + Py_DECREF(Callee); + Py_DECREF(Node); + return NULL; + } + if (PyUnicode_CompareWithASCIIString(Callee, "defined") == 0) { + Py_DECREF(Callee); + if (!P->InCIfCond) { + PyErr_Format(PyExc_SyntaxError, + "'defined(NAME)' is only allowed inside a 'cif' / 'celif' condition, at line %d", + Start->Line); + Py_DECREF(Node); + return NULL; + } + PyObject *Args = PyObject_GetAttrString(Node, "args"); + PyObject *Kw = PyObject_GetAttrString(Node, "keywords"); + PyObject *Arg0 = NULL; + if (Args != NULL && Kw != NULL && PyList_Check(Args) && PyList_Check(Kw) + && PyList_GET_SIZE(Args) == 1 && PyList_GET_SIZE(Kw) == 0) { + Arg0 = PyList_GetItem(Args, 0); /* borrowed */ + } + if (Arg0 == NULL) { + PyErr_Format(PyExc_SyntaxError, + "'defined' expects exactly one macro name (e.g. defined(MAX_SIZE)), at line %d", + Start->Line); + Py_XDECREF(Args); + Py_XDECREF(Kw); + Py_DECREF(Node); + return NULL; + } + PyObject *Marker = MakeCifMarkerCall(P, "@CIfdef", Arg0, Start); + Py_XDECREF(Args); + Py_XDECREF(Kw); + Py_DECREF(Node); + return Marker; + } + Py_DECREF(Callee); + return Node; +} + static PyObject *ParseSliceList(Parser *P); static PyObject *ParseSliceItem(Parser *P); @@ -4324,6 +4495,12 @@ static PyObject *ParseSimpleStmt(Parser *P); static PyObject *ParseSuite(Parser *P); static PyObject *ParseSuiteOwned(Parser *P, int OwnerIsClass); static PyObject *ParseIf(Parser *P); +static PyObject *ParseCIf(Parser *P); +static PyObject *ParseCelif(Parser *P); +static PyObject *ParseCIfChainTail(Parser *P); +static PyObject *MakeCifMarkerCall(Parser *P, const char *AttrName, PyObject *Expr, Token *Start); +static int IsCIfStatementStart(Parser *P); +static PyObject *DesugarSpecialCall(Parser *P, PyObject *Node, Token *Start); static PyObject *ParseWhile(Parser *P); static PyObject *ParseFor(Parser *P, int IsAsync); static PyObject *ParseTry(Parser *P); @@ -4437,6 +4614,22 @@ static PyObject *ParseStatement(Parser *P) { if (TokIsName(T, "struct")) { return ParseStructDef(P, NULL); } + /* cif 编译期条件编译(软关键字):语句起始 + 非赋值/注解形态 */ + if (TokIsName(T, "cif") && IsCIfStatementStart(P)) { + return ParseCIf(P); + } + /* celif / celse 必须属于某条 cif 链(由 ParseCIf 直接消费); + * 语句起始独立出现 ⇒ FailFast */ + if (TokIsName(T, "celif")) { + SyntaxErrorAt(P, T, + "'celif' must belong to a 'cif' chain (no matching 'cif')"); + return NULL; + } + if (TokIsName(T, "celse")) { + SyntaxErrorAt(P, T, + "'celse' must belong to a 'cif' chain (no matching 'cif')"); + return NULL; + } if (TokIsName(T, "if")) { return ParseIf(P); } @@ -6023,6 +6216,227 @@ static PyObject *ParseIf(Parser *P) { return Node; } +/* ===================== cif / celif / celse 编译期条件编译 ===================== + * 软关键字:语句起始 NAME('cif') 后非赋值/注解形态 ⇒ cif 语句。 + * 解析期 desugar 为既有「If + c.@CIf 标记调用」形状(与纯 Python / Viper 侧一致): + * cif E: → If(test=c.@CIf(E), body, orelse) + * celif E: → 同链下一个 If(test=c.@CIf(E), ...)(挂在上一支 orelse) + * celse: → 普通 orelse + * 标记 c.@CIf / c.@CIfdef 由本解析器合成(属性名前导 '@' 词法器不可产出), + * 用户写 c.CIf 在表达式处 FailFast。 */ + +/* cif 语句起始判定:第二 token 为 '=' ':' 或增强赋值 ⇒ 非 cif 语句(cif = 5)。 */ +static int IsCIfStatementStart(Parser *P) { + Token *N = PeekTok(P, 1); + if (N->Kind != TK_OP) { + return 1; + } + static const char *Ops[] = { + "=", ":", "+=", "-=", "*=", "/=", "//=", "%=", "**=", + "<<=", ">>=", "&=", "|=", "^=", "@=", NULL + }; + for (int I = 0; Ops[I] != NULL; I++) { + if (TokIsOp(N, Ops[I])) { + return 0; + } + } + return 1; +} + +/* 构造内部标记调用 `c.(Expr)`(用户不可写)。 */ +static PyObject *MakeCifMarkerCall(Parser *P, const char *AttrName, PyObject *Expr, Token *Start) { + PyObject *Load1 = MakeCtx(P, "Load"); + PyObject *IdC = Load1 ? PyUnicode_FromString("c") : NULL; + if (Load1 == NULL || IdC == NULL) { + Py_XDECREF(Load1); + Py_XDECREF(IdC); + return NULL; + } + PyObject *NameArgv[2] = { IdC, Load1 }; + PyObject *NameNode = NewNode(P, "Name", NameArgv, 2); + Py_DECREF(IdC); + Py_DECREF(Load1); + if (NameNode == NULL) { + return NULL; + } + FixLoc(P, NameNode, Start); + PyObject *Load2 = MakeCtx(P, "Load"); + PyObject *AttrStr = Load2 ? PyUnicode_FromString(AttrName) : NULL; + if (Load2 == NULL || AttrStr == NULL) { + Py_XDECREF(Load2); + Py_XDECREF(AttrStr); + Py_DECREF(NameNode); + return NULL; + } + PyObject *AttrArgv[3] = { NameNode, AttrStr, Load2 }; + PyObject *AttrNode = NewNode(P, "Attribute", AttrArgv, 3); + Py_DECREF(AttrStr); + Py_DECREF(Load2); + Py_DECREF(NameNode); + if (AttrNode == NULL) { + return NULL; + } + FixLoc(P, AttrNode, Start); + PyObject *Args = NewList(); + PyObject *Kw = NewList(); + if (Args == NULL || Kw == NULL) { + Py_XDECREF(Args); + Py_XDECREF(Kw); + Py_DECREF(AttrNode); + return NULL; + } + PyList_Append(Args, Expr); + PyObject *CallArgv[3] = { AttrNode, Args, Kw }; + PyObject *CallNode = NewNode(P, "Call", CallArgv, 3); + Py_DECREF(Args); + Py_DECREF(Kw); + Py_DECREF(AttrNode); + if (CallNode == NULL) { + return NULL; + } + FixLoc(P, CallNode, Start); + return CallNode; +} + +/* 解析 cif/celif 条件表达式:期间允许 defined(NAME) 谓词。 */ +static PyObject *ParseCIfCondition(Parser *P) { + int Prev = P->InCIfCond; + P->InCIfCond = 1; + PyObject *E = ParseExpr(P); + P->InCIfCond = Prev; + return E; +} + +/* `cif :` + 可选 celif/celse 链 */ +static PyObject *ParseCIf(Parser *P) { + Token *Start = NextTok(P); /* cif */ + PyObject *Test = ParseCIfCondition(P); + if (Test == NULL) { + return NULL; + } + if (!ExpectOp(P, ":")) { + Py_DECREF(Test); + return NULL; + } + PyObject *Body = ParseSuite(P); + if (Body == NULL) { + Py_DECREF(Test); + return NULL; + } + PyObject *Marker = MakeCifMarkerCall(P, "@CIf", Test, Start); + Py_DECREF(Test); + if (Marker == NULL) { + Py_DECREF(Body); + return NULL; + } + PyObject *Orelse = ParseCIfChainTail(P); + if (Orelse == NULL) { + Py_DECREF(Marker); + Py_DECREF(Body); + return NULL; + } + PyObject *Argv[3] = { Marker, Body, Orelse }; + PyObject *Node = NewNode(P, "If", Argv, 3); + Py_DECREF(Marker); + Py_DECREF(Body); + Py_DECREF(Orelse); + if (Node != NULL) { + FixLoc(P, Node, Start); + } + return Node; +} + +/* `celif :`(cif 链的后续分支) */ +static PyObject *ParseCelif(Parser *P) { + Token *Start = NextTok(P); /* celif */ + PyObject *Test = ParseCIfCondition(P); + if (Test == NULL) { + return NULL; + } + if (!ExpectOp(P, ":")) { + Py_DECREF(Test); + return NULL; + } + PyObject *Body = ParseSuite(P); + if (Body == NULL) { + Py_DECREF(Test); + return NULL; + } + PyObject *Marker = MakeCifMarkerCall(P, "@CIf", Test, Start); + Py_DECREF(Test); + if (Marker == NULL) { + Py_DECREF(Body); + return NULL; + } + PyObject *Orelse = ParseCIfChainTail(P); + if (Orelse == NULL) { + Py_DECREF(Marker); + Py_DECREF(Body); + return NULL; + } + PyObject *Argv[3] = { Marker, Body, Orelse }; + PyObject *Node = NewNode(P, "If", Argv, 3); + Py_DECREF(Marker); + Py_DECREF(Body); + Py_DECREF(Orelse); + if (Node != NULL) { + FixLoc(P, Node, Start); + } + return Node; +} + +/* cif 链尾部(celif/celse)→ orelse 列表;错配 ⇒ FailFast(静态报文)。 */ +static PyObject *ParseCIfChainTail(Parser *P) { + if (AtName(P, "celif")) { + PyObject *Nested = ParseCelif(P); + if (Nested == NULL) { + return NULL; + } + PyObject *L = NewList(); + if (L == NULL) { + Py_DECREF(Nested); + return NULL; + } + PyList_Append(L, Nested); + Py_DECREF(Nested); + return L; + } + if (AtName(P, "celse")) { + NextTok(P); + if (!ExpectOp(P, ":")) { + return NULL; + } + PyObject *Body = ParseSuite(P); + if (Body == NULL) { + return NULL; + } + if (AtName(P, "celif")) { + SyntaxErrorAt(P, PeekTok(P, 0), + "'celif' cannot follow 'celse' (the 'celse' branch must be the last of the 'cif' chain)"); + Py_DECREF(Body); + return NULL; + } + if (AtName(P, "celse")) { + SyntaxErrorAt(P, PeekTok(P, 0), + "'celse' cannot follow 'celse' (the 'celse' branch must be the last of the 'cif' chain)"); + Py_DECREF(Body); + return NULL; + } + return Body; + } + if (AtName(P, "elif")) { + SyntaxErrorAt(P, PeekTok(P, 0), + "a 'cif' chain must be closed with 'celse', not 'elif'"); + return NULL; + } + if (AtName(P, "else")) { + SyntaxErrorAt(P, PeekTok(P, 0), + "a 'cif' chain must be closed with 'celse', not 'else'"); + return NULL; + } + return NewList(); +} + /* while / else */ static PyObject *ParseWhile(Parser *P) { Token *Start = NextTok(P); /* while */ @@ -7265,15 +7679,16 @@ static PyObject *VastC_Parse(PyObject *Self, PyObject *Args, PyObject *Kwargs) { P.FStringExprEnd = 0; P.InTypeAnnotation = 0; P.InClassBody = 0; + P.InCIfCond = 0; P.PtrSuffixTail = NULL; PyObject *Tree = ParseModule(&P); - PyMem_Free(Tokens); - PyMem_Free(LineStarts); if (Tree == NULL) { if (P.Error) { /* 报错必须自足可定位:带上 类型名 + token 原文 + 行号,措辞与 * Python ExprParser 的 `unexpected token OP('*') at line N` 一致, - * 这样 `[vast] C 解析器失败,回退纯 Python:<文件>: …` 一行即可定位。 */ + * 这样 `[vast] C 解析器失败,回退纯 Python:<文件>: …` 一行即可定位。 + * 注意:P.ErrTok 指向 Tokens 数组内部,必须在 PyMem_Free 之前读取 + * (否则读到 free 链表指针,首个 token 会被写成 ENDMARKER(''))。 */ char TokBuf[64]; const char *TokName = "ENDMARKER"; TokBuf[0] = '\0'; @@ -7296,8 +7711,12 @@ static PyObject *VastC_Parse(PyObject *Self, PyObject *Args, PyObject *Kwargs) { P.ErrMsg ? P.ErrMsg : "vast_c: syntax error", TokName, TokBuf, P.ErrLine); } + PyMem_Free(Tokens); + PyMem_Free(LineStarts); return NULL; } + PyMem_Free(Tokens); + PyMem_Free(LineStarts); if (strcmp(Mode, "exec") == 0) { return Tree; }