From 2514cee4c91269f7522e399c6d6156d55aeed2e8 Mon Sep 17 00:00:00 2001 From: TermiNexus Date: Wed, 7 Oct 2026 19:03:29 +0800 Subject: [PATCH] =?UTF-8?q?feat(lang):=20=E5=90=8C=E6=AD=A5=20cif/celif/ce?= =?UTF-8?q?lse=E3=80=81=E5=80=BC=E5=BC=95=E7=94=A8=E6=B3=A8=E8=A7=A3?= =?UTF-8?q?=E4=B8=8E=20assert=20=E6=94=B9=E8=AF=91=20testcheck.check?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 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。 --- lib/Projectrans/DeclarationGenerator.py | 5 + lib/Projectrans/Phase1Generator.py | 16 + lib/Projectrans/Phase2Translator.py | 24 +- lib/StubGen/Converter.py | 186 ++++++---- lib/core/ConstEvaluator.py | 67 +++- lib/core/Handles/HandlesAnnAssign.py | 12 +- lib/core/Handles/HandlesAssert.py | 139 ++++---- lib/core/Handles/HandlesBase.py | 7 +- lib/core/Handles/HandlesClassDef.py | 9 +- lib/core/Handles/HandlesExprCall.py | 39 ++- lib/core/Handles/HandlesFunctions.py | 43 ++- lib/core/Handles/HandlesIf.py | 49 ++- lib/core/Handles/HandlesImports.py | 9 +- lib/core/Handles/HandlesTypeMerge.py | 7 + lib/core/LLVMCG/BaseGen.py | 5 +- lib/core/LLVMCG/FuncGen.py | 35 +- lib/core/SymbolUtils.py | 32 ++ lib/core/TypeAnnotationResolver.py | 8 + lib/includes/c.py | 43 --- lib/includes/t.py | 9 - lib/vast/ExprParser.py | 91 ++++- lib/vast/Parser.py | 121 +++++++ lib/vast_c/_parser.pyd | Bin 221753 -> 227354 bytes lib/vast_c/vast_c.c | 429 +++++++++++++++++++++++- 24 files changed, 1139 insertions(+), 246 deletions(-) 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 9e5184d2dce523e7522920cc96cec00a4b8636c4..1cdbe58b9396c52a47faf6f28f1ddee7af4716de 100644 GIT binary patch delta 71657 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 == 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; }