diff --git a/Test/NoVTableCrossMixinTest/App/main.vp b/Test/NoVTableCrossMixinTest/App/main.vp new file mode 100644 index 0000000..f1ce924 --- /dev/null +++ b/Test/NoVTableCrossMixinTest/App/main.vp @@ -0,0 +1,50 @@ +import t +import stdio +import testcheck + + +# ============================================================ +# 复现:NoVTable 多继承下跨 Mixin self. 方法调用 +# +# 缺陷:BaseB.caller 内 `self.helper()`(helper 定义在兄弟 Mixin BaseA, +# 经 Combined 多继承组合)被 mangle 成 `.BaseB.helper` +# (用调用者自身/直接父类做前缀),而定义符号是 `.BaseA.helper` +# → ld.lld undefined symbol(TPV 侧 StmtGenMixin.translate_stmt 调 +# self.translate_return 同根因,符号成 StmtGenDeclMixin.translate_return)。 +# 期望:按实际定义类(BaseA)做前缀,或按最终组合类(Combined)。 +# ============================================================ +@t.NoVTable +class BaseA: + a: t.CInt + + def helper(self) -> int: + return self.a + + +@t.NoVTable +class BaseB: + b: t.CInt + + def caller(self) -> int: + return self.helper() + + +@t.NoVTable +class Combined(BaseA, BaseB): + pass + + +def test_cross_mixin_call(): + testcheck.section("Test 1: cross-mixin self. call") + c: Combined = Combined() + c.a = 42 + c.b = 7 + testcheck.check(c.caller() == 42, + "caller()==42 (BaseB->BaseA)", + "cross-mixin call fail") + + +def main() -> t.CInt: + testcheck.begin("NoVTableCrossMixinTest") + test_cross_mixin_call() + return testcheck.end() diff --git a/Test/NoVTableCrossMixinTest/project.json b/Test/NoVTableCrossMixinTest/project.json new file mode 100644 index 0000000..073341c --- /dev/null +++ b/Test/NoVTableCrossMixinTest/project.json @@ -0,0 +1,29 @@ +{ + "$schema": "https://raw.githubusercontent.com/TermiNexus/TransPyC/main/schemas/project-schema.json", + "name": "NoVTableCrossMixinTest", + "version": "1.0.0", + "source_dir": "./App", + "build_dir": "./.tpv_build", + "compiler": { + "cmd": "llc", + "flags": ["-filetype=obj", "-relocation-model=pic"] + }, + "linker": { + "cmd": "clang++", + "flags": ["-lmsvcrt", "-lucrt", "-lpthread", "-lmingwex", "-lkernel32", "-Wl,--allow-multiple-definition"], + "output": "NoVTableCrossMixinTest.exe" + }, + "includes": [ + "../../includes" + ], + "target": { + "triple": "x86_64-pc-windows-gnu", + "datalayout": "e-m:w-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128" + }, + "options": { + "slice_level": 3, + "sha1_slice_level": 1, + "target": "llvm", + "strict_mode": true + } +} diff --git a/Test/TestsOverview.md b/Test/TestsOverview.md index b7b3d71..6fd8a3c 100644 --- a/Test/TestsOverview.md +++ b/Test/TestsOverview.md @@ -100,6 +100,7 @@ | # | 项目 | 功能概述 | 关键特性 | |---|------|---------|---------| | 68 | [WebTest](WebTest/App/main.vp) | 网络栈:socket OOP、DNS 解析、requests.get HTTP 请求(依赖真实网络) | socket/requests、真实网络 I/O | +| 69 | [NoVTableCrossMixinTest](NoVTableCrossMixinTest/App/main.vp) | 回归:@t.NoVTable 多继承下跨 Mixin `self.` 方法调用——BaseB 内 `self.helper()` 调兄弟 Mixin BaseA.helper,须按定义类解析 + this 前移 sizeof(定义类) | 跨 Mixin 调用、NoVTable 多继承、this 调整 | --- @@ -190,6 +191,7 @@ 66. **ZlibTest** — zlib/Huffman(代码量最大) 67. **CPythonTest** — CPython C API 嵌入(依赖 Python 运行时) 68. **WebTest** — socket/requests(依赖真实网络,最后跑) +69. **NoVTableCrossMixinTest** — @t.NoVTable 跨 Mixin self. 调用(编译器缺陷回归) --- diff --git a/lib/core/Handles/HandlesExprCall.py b/lib/core/Handles/HandlesExprCall.py index 64817e6..c9d5e8b 100644 --- a/lib/core/Handles/HandlesExprCall.py +++ b/lib/core/Handles/HandlesExprCall.py @@ -2529,7 +2529,23 @@ class ExprCallHandle(BaseHandle): return ObjVal sub_off: int | None = self._compute_field_byte_offset(Gen, ClassName, first_field_name) if sub_off is None: - return ObjVal + # 兄弟 Mixin 场景:调用类(ClassName)不含定义类字段——定义类在 + # 组合类 MRO 中位于调用类之前(如 Combined(BaseA, BaseB),BaseB 内 + # self.helper() 调 BaseA.helper)。此时 BaseB.this 指向组合类中 + # BaseB 字段区起点,BaseA 区在它前面,this 前移 sizeof(定义类 struct)。 + anc_st: Any = Gen.structs.get(method_anc) or Gen.structs.get(anc_lookup) + if not anc_st or not isinstance(anc_st, ir.IdentifiedStructType) or anc_st.elements is None: + return ObjVal + sibling_delta: int = -Gen._get_struct_size(anc_st) + if sibling_delta >= 0: + return ObjVal + obj_i8_s: ir.Value = Gen.builder.bitcast( + ObjVal, ir.PointerType(ir.IntType(8)), name=f"this_sibling_i8_{anc_lookup}") + adj_s: ir.Value = Gen.builder.gep( + obj_i8_s, [ir.Constant(ir.IntType(64), sibling_delta)], + name=f"this_sibling_{anc_lookup}") + return Gen.builder.bitcast(adj_s, ir.PointerType(anc_st), + name=f"this_sibling_cast_{anc_lookup}") anc_off: int | None = self._compute_field_byte_offset(Gen, method_anc, first_field_name) if anc_off is None: return ObjVal @@ -3148,6 +3164,25 @@ class ExprCallHandle(BaseHandle): if func: method_anc = anc break + if not func or len(list(func.blocks)) == 0: + # 兄弟 Mixin 兜底:方法不在本类 MRO 链(如 BaseB 内 self.helper() + # 调兄弟 BaseA.helper,BaseB 自身无 BaseA 的继承信息)。全局扫描 + # 已注册类找定义类,this 由 _adjust_this_for_inherited_method 按 + # 组合布局前移 sizeof(定义类 struct)。修复 TPC 编译 TPV 的 + # undefined symbol: .BaseB.helper / StmtGenDeclMixin.translate_*。 + for _cand_cls, _cand_methods in list(Gen.class_methods.items()): + if _cand_cls == ClassName: + continue + if not any(_cm.endswith(f'.{MethodName}') or _cm == MethodName for _cm in _cand_methods): + continue + _cand_full = f'{_cand_cls}.{MethodName}' + _cand_func = Gen._find_function(_cand_full) + if not _cand_func: + _cand_func = Gen._find_function(f'{_cand_full}__') + if _cand_func: + func = _cand_func + method_anc = _cand_cls + break if func: # 多继承 this 调整:继承方法定义在无 vtable 父类且字段区偏移 > 0 时, # 把 self 定位到父类第一个用户字段在子类中的位置并 bitcast 成父类指针。