diff --git a/Test/TpvIsinstanceTest/App/main.vp b/Test/TpvIsinstanceTest/App/main.vp new file mode 100644 index 0000000..8de2865 --- /dev/null +++ b/Test/TpvIsinstanceTest/App/main.vp @@ -0,0 +1,125 @@ +from stdint import * +import t, c +from t import CPtr, CInt, CExport +import testcheck + + +# ============================================================ +# TPV 侧 isinstance 内建函数测试(由 TransPyV.exe 编译) +# +# 验证 TPV 编译器生成的 isinstance IR: +# 1. CVTable 自身 / 子类→基类 +# 2. 运行时多态(基类指针指向子类对象) +# 3. 元组 (A, B) +# 4. 多继承 +# 5. NoVTable 编译时静态判断 +# ============================================================ + + +# ========== CVTable 继承 ========== + +@t.CVTable +class Animal: + Name: str + + def __init__(self, name: str): + self.Name = name + + def speak(self) -> str: + return "..." + + +@t.CVTable +class Dog(Animal): + def speak(self) -> str: + return "Woof" + + +@t.CVTable +class Cat(Animal): + def speak(self) -> str: + return "Meow" + + +# ========== 多继承 ========== + +@t.CVTable +class A: + a: CInt + + def get_a(self) -> CInt: + return self.a + + +@t.CVTable +class B: + b: CInt + + def get_b(self) -> CInt: + return self.b + + +@t.CVTable +class AB(A, B): + def __init__(self, a: CInt, b: CInt): + self.a = a + self.b = b + + +# ========== NoVTable(编译时静态判断) ========== + +@t.NoVTable +class Plain: + v: CInt + + +def main() -> CInt | CExport: + testcheck.begin("TpvIsinstanceTest: TPV 侧 isinstance 测试") + + # --- 1. 自身类型 + 子类→基类 --- + d: Dog | CPtr = Dog("dog") + testcheck.check(isinstance(d, Dog) == 1, "isinstance(d, Dog) 自身 OK", + "isinstance(d, Dog) FAILED") + testcheck.check(isinstance(d, Animal) == 1, "isinstance(d, Animal) 子类→基类 OK", + "isinstance(d, Animal) FAILED") + testcheck.check(isinstance(d, Cat) == 0, "isinstance(d, Cat) 不匹配 OK", + "isinstance(d, Cat) FAILED") + + # --- 2. 运行时多态:基类指针指向子类对象 --- + a: Animal | CPtr = (Animal | CPtr)(d) + testcheck.check(isinstance(a, Dog) == 1, + "isinstance(Animal*, Dog 对象) 运行时多态 OK", + "isinstance(Animal*, Dog) FAILED") + testcheck.check(isinstance(a, Animal) == 1, + "isinstance(Animal*, Animal) 基类自身 OK", + "isinstance(Animal*, Animal) FAILED") + testcheck.check(isinstance(a, Cat) == 0, + "isinstance(Animal*, Cat) 运行时 False OK", + "isinstance(Animal*, Cat) FAILED") + + # --- 3. 元组 --- + testcheck.check(isinstance(d, (Cat, Dog)) == 1, "isinstance 元组命中 OK", + "isinstance 元组 FAILED") + testcheck.check(isinstance(d, (Cat, A)) == 0, "isinstance 元组未命中 OK", + "isinstance 元组未命中 FAILED") + + # --- 4. 多继承 --- + ab: AB | CPtr = AB(1, 2) + testcheck.check(isinstance(ab, A) == 1, "isinstance(AB, A) 多继承 OK", + "isinstance(AB, A) FAILED") + testcheck.check(isinstance(ab, B) == 1, "isinstance(AB, B) 多继承 OK", + "isinstance(AB, B) FAILED") + testcheck.check(isinstance(ab, Animal) == 0, "isinstance(AB, Animal) False OK", + "isinstance(AB, Animal) FAILED") + testcheck.check(ab.get_a() == 1 and ab.get_b() == 2, "AB 字段/方法正常 OK", + "AB 字段/方法 FAILED") + + # --- 5. NoVTable(编译时静态判断) --- + p: Plain | CPtr = Plain() + p.v = 42 + testcheck.check(isinstance(p, Plain) == 1, "isinstance NoVTable 编译时 OK", + "isinstance NoVTable FAILED") + testcheck.check(p.v == 42, "NoVTable 字段正常 OK", "NoVTable 字段 FAILED") + + testcheck.ok("TPV 侧 isinstance 全部测试完成") + return testcheck.end() diff --git a/Test/TpvIsinstanceTest/project.json b/Test/TpvIsinstanceTest/project.json new file mode 100644 index 0000000..70b06ad --- /dev/null +++ b/Test/TpvIsinstanceTest/project.json @@ -0,0 +1,29 @@ +{ + "$schema": "https://raw.githubusercontent.com/TermiNexus/TransPyC/main/schemas/project-schema.json", + "name": "TpvIsinstanceTest", + "version": "1.0.0", + "source_dir": "./App", + "build_dir": "./.tpv_build", + "compiler": { + "cmd": "llc", + "flags": ["-filetype=obj", "-relocation-model=pic"] + }, + "linker": { + "cmd": "clang++", + "flags": ["-Wl,--allow-multiple-definition", "-Wl,--unresolved-symbols=ignore-in-object-files", "-lmsvcrt", "-lucrt", "-lpthread", "-lmingwex", "-lkernel32", "-luser32", "-latomic"], + "output": "TpvIsinstanceTest.exe" + }, + "includes": [ + "../../includes" + ], + "target": { + "triple": "x86_64-pc-windows-gnu", + "datalayout": "e-m:w-p270:32:32-p271:32:32-p272:64:64-f80:128-n8:16:32:64-S128" + }, + "options": { + "slice_level": 3, + "sha1_slice_level": 1, + "target": "llvm", + "strict_mode": true + } +} diff --git a/lib/core/Translator/LlvmGenerator.py b/lib/core/Translator/LlvmGenerator.py index 6b0cac5..9b3658d 100644 --- a/lib/core/Translator/LlvmGenerator.py +++ b/lib/core/Translator/LlvmGenerator.py @@ -74,30 +74,44 @@ class LlvmGeneratorMixin: # 使用 ConstEvaluator 处理 BinOp/UnaryOp/Name/Attribute/Call return ConstEvaluator.eval_with_symtab(node, self.SymbolTable) + def _is_cdefine_type(TypeInfo: CTypeInfo | None) -> bool: + """判断 CTypeInfo 是否对应 t.CDefine 注解""" + return bool(TypeInfo and TypeInfo.BaseType and + ((isinstance(TypeInfo.BaseType, type) and issubclass(TypeInfo.BaseType, t.CDefine)) + or isinstance(TypeInfo.BaseType, t.CDefine) or TypeInfo.IsDefine)) + + def _register_cdefine_constant(NameNode: ast.Name, Annotation: ast.expr, Value: ast.expr) -> None: + """注册 CDefine 编译期常量(_define_constants + SymbolTable)""" + TypeInfo: CTypeInfo | None = self.TypeMergeHandler.GetCTypeInfo(Annotation) + if not _is_cdefine_type(TypeInfo): + return + val: int | str | float | None = _extract_const_value(Value) + if val is not None: + if not hasattr(Gen, '_define_constants'): + Gen._define_constants: dict[str, int | str | float | bool] = {} + Gen._define_constants[NameNode.id] = val + existing = self.SymbolTable.lookup(NameNode.id) + if not (existing and existing.IsTypedef and existing.OriginalType): + sym_info: _CTypeInfo = _CTypeInfo() + sym_info.IsDefine = True + sym_info.DefineValue = val + self.SymbolTable.insert(NameNode.id, sym_info) + for Node in ast.iter_child_nodes(Tree): if isinstance(Node, ast.AnnAssign) and isinstance(Node.target, ast.Name) and Node.value: TypeInfo: CTypeInfo | None = self.TypeMergeHandler.GetCTypeInfo(Node.annotation) # 治本修复:检查 BaseType 是否为 t.CDefine 实例/子类,或 IsDefine 标志 # IsDefine 由 GetCTypeInfo 对 t.CDefine 注解自动设置 - if TypeInfo and TypeInfo.BaseType and ((isinstance(TypeInfo.BaseType, type) and issubclass(TypeInfo.BaseType, t.CDefine)) or isinstance(TypeInfo.BaseType, t.CDefine) or TypeInfo.IsDefine): - val: int | str | float | None = _extract_const_value(Node.value) - import sys as _sys - _nm = Node.target.id - # [CD] 仅输出关键常量(FOREGROUND_*, STD_OUTPUT_HANDLE, BACKGROUND_*) - if isinstance(_nm, str) and (_nm.startswith('FOREGROUND_') or _nm.startswith('BACKGROUND_') or 'STD_' in _nm or 'HANDLE' in _nm): - print(f"[CD] extract: '{_nm}' IsDefine={TypeInfo.IsDefine} val={val}", file=_sys.stderr, flush=True) - if val is not None: - if not hasattr(Gen, '_define_constants'): - Gen._define_constants: dict[str, int | str | float | bool] = {} - Gen._define_constants[Node.target.id] = val - existing = self.SymbolTable.lookup(Node.target.id) - if not (existing and existing.IsTypedef and existing.OriginalType): - sym_info: _CTypeInfo = _CTypeInfo() - sym_info.IsDefine = True - sym_info.DefineValue = val - self.SymbolTable.insert(Node.target.id, sym_info) - else: - print(f"[CD] extract: WARN val is None for '{_nm}'", file=_sys.stderr, flush=True) + if _is_cdefine_type(TypeInfo): + _register_cdefine_constant(Node.target, Node.annotation, Node.value) + elif isinstance(Node, ast.ClassDef): + # 类体 CDefine 常量(2026-08-10):供方法内裸名(如 Mixin 的 + # ISINSTANCE_CLASS_MAX/MRO_MAX: t.CDefine = N)编译期解析为常量值。 + # 原预扫描只用 iter_child_nodes 扫顶层,类体常量不注册 → 方法内 + # 裸名解析为 0 → pool.alloc(8*0) 失败(TPV 编译测试 SUBS-NONE)。 + for _citem in Node.body: + if isinstance(_citem, ast.AnnAssign) and isinstance(_citem.target, ast.Name) and _citem.value: + _register_cdefine_constant(_citem.target, _citem.annotation, _citem.value) elif isinstance(Node, ast.Assign): for target in Node.targets: if isinstance(target, ast.Name): diff --git a/lib/includes/t.py b/lib/includes/t.py index bf1f894..690c97b 100644 --- a/lib/includes/t.py +++ b/lib/includes/t.py @@ -579,9 +579,19 @@ class CArray(CType): return cls class CDefine(CType): + """编译期常量定义标记(X: t.CDefine = 64)。 + + Size/IsSigned 覆盖为 32 位有符号整数(2026-08-10 修复):基类 __init__ + 将 Size/IsSigned 置 None,导致 CTypeToLLVM(CDefine) 推导为 'void', + 类字段注解 t.CDefine(如 Mixin 的常量声明)在 stub 生成时变成 void, + LLVM struct 含 void 报 "void type only allowed for function results"。 + 语义上 CDefine 常量即为 int;超大值由 TypeConvert 按 DefineValue 升 i64。 + """ position: ClassVar[frozenset[str]] = frozenset({CType.BASE}) def __init__(self, value: Any = None) -> None: super().__init__(value) + self.Size = 32 + self.IsSigned = True class CPass(CType): position: ClassVar[frozenset[str]] = frozenset({CType.BASE})