From 6e17716a6949eefd5b715ab2d3ab24f3d7f7ff6a Mon Sep 17 00:00:00 2001 From: TermiNexus Date: Sat, 17 Oct 2026 19:22:50 +0800 Subject: [PATCH] =?UTF-8?q?revert(vtable):=20=E6=81=A2=E5=A4=8D=E6=8C=89?= =?UTF-8?q?=E7=B1=BB=E5=AE=9E=E9=99=85=E8=A7=84=E6=A8=A1=E5=8A=A8=E6=80=81?= =?UTF-8?q?=E5=88=86=E9=85=8D?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit bf0bbcc 已经把固定上限去掉:收集前按「各父类 VTableMethodCount 之和 + 本类 FunctionDef 数 + 1」一次性分配三张表,越界检查保留为 InternalCompilerError。eff88dd 在并行开发中误把这段覆盖回固定 1024,这里恢复 bf0bbcc 的实现——无任意上限,且符合「能动态就动态」的既定规则。 --- App/lib/core/Gen/Class/ClassGenVTable.vp | 56 +++++++++++++++--------- 1 file changed, 36 insertions(+), 20 deletions(-) diff --git a/App/lib/core/Gen/Class/ClassGenVTable.vp b/App/lib/core/Gen/Class/ClassGenVTable.vp index 993f36a..6f4102e 100644 --- a/App/lib/core/Gen/Class/ClassGenVTable.vp +++ b/App/lib/core/Gen/Class/ClassGenVTable.vp @@ -18,10 +18,8 @@ import lib.core.Gen.DebugInfoGen as DebugInfoGen # Method metadata flag: TLS (@t.TLS decorator; local copy avoids a cross-module CDefine lookup). define META_TLS = 32 -# Max number of vtable methods (per class). 32 was too small: large utility classes such as -# memhub.MemBuddy declare well over 32 methods. The three slot arrays are PermPool-allocated -# (8 * VTBL_METHOD_MAX each), so 1024 costs 24 KiB per class and survives pool resets. -define VTBL_METHOD_MAX = 1024 +# Vtable method tables are sized per class at collection time (parents' count + own methods); +# there is deliberately no fixed VTBL_METHOD_MAX. define SCOPE_FUNCTION = 1 # ============================================================ @@ -68,15 +66,35 @@ novirtual class ClassGenVTable(ClassGenBase.ClassGenBase): if children is None: return 0 cn: sizet = len(children) + # Capacity comes from the class itself: the parents' already-collected vtable methods + # (an upper bound — dedupe/override only shrink it) plus the number of own method + # definitions. A fixed 32 silently dropped every later method, after which a call missed + # the slot and fell back to a static call of the base implementation. + parent_count: int = self.Gen.StructGen.get_parent_count(class_name) + vt_cap: int = 0 + if parent_count > 0: + for pi0 in range(int(parent_count)): + pname0: str = self.Gen.StructGen.get_parent_name_at(class_name, pi0) + if pname0 is None: continue + pe0: GenTypes.StructEntry* = self.Gen.StructGen.find_struct_by_module(pname0, trans.ModuleSha1) + if pe0 is None: pe0 = self.Gen.StructGen.find_struct(pname0) + if pe0 is not None and pe0.VTableMethodCount > 0: vt_cap = vt_cap + pe0.VTableMethodCount + for ci0 in range(int(cn)): + stmt0: ast.AST* = children.get(ci0) + if stmt0 is not None and stmt0.kind() == ast.ASTKind.FunctionDef: vt_cap = vt_cap + 1 + vt_cap = vt_cap + 1 # one spare slot + if vt_cap < 1: vt_cap = 1 + vt_bytes: sizet = sizet(8 * vt_cap) + # Allocate with PermPool: VTableMethods must survive per-file pool resets. - full_names_buf: sizet* = CompilerFactory.factory.PermPool.alloc(8 * VTBL_METHOD_MAX) - method_names_buf: sizet* = CompilerFactory.factory.PermPool.alloc(8 * VTBL_METHOD_MAX) + full_names_buf: sizet* = CompilerFactory.factory.PermPool.alloc(vt_bytes) + method_names_buf: sizet* = CompilerFactory.factory.PermPool.alloc(vt_bytes) # Record each method's parent SHA1 (non-zero = inherited, mangle with parent module SHA1). - method_parent_sha1s: sizet* = CompilerFactory.factory.PermPool.alloc(8 * VTBL_METHOD_MAX) + method_parent_sha1s: sizet* = CompilerFactory.factory.PermPool.alloc(vt_bytes) if full_names_buf is None or method_names_buf is None or method_parent_sha1s is None: return 0 - string.memset(full_names_buf, 0, 8 * VTBL_METHOD_MAX) - string.memset(method_names_buf, 0, 8 * VTBL_METHOD_MAX) - string.memset(method_parent_sha1s, 0, 8 * VTBL_METHOD_MAX) + string.memset(full_names_buf, 0, vt_bytes) + string.memset(method_names_buf, 0, vt_bytes) + string.memset(method_parent_sha1s, 0, vt_bytes) method_count: int = 0 @@ -84,7 +102,6 @@ novirtual class ClassGenVTable(ClassGenBase.ClassGenBase): # subclass, others the parent; SHA1-aware lookup avoids cross-module same-name mismatches. vt_self_entry: GenTypes.StructEntry* = self.Gen.StructGen.find_struct_by_module(class_name, trans.ModuleSha1) # Multiple inheritance: walk parents in declaration order, dedupe by short name (first wins). - parent_count: int = self.Gen.StructGen.get_parent_count(class_name) if parent_count > 0: for pi in range(int(parent_count)): pname: str = self.Gen.StructGen.get_parent_name_at(class_name, pi) @@ -98,11 +115,10 @@ novirtual class ClassGenVTable(ClassGenBase.ClassGenBase): if vt_parent_entry is not None: parent_vt_count = vt_parent_entry.VTableMethodCount if parent_vt_count > 0: for pvi in range(int(parent_vt_count)): - if method_count >= VTBL_METHOD_MAX: - # fail-fast: the VTableMethods arrays are fixed at VTBL_METHOD_MAX; - # dropping the excess makes call sites miss the slot and silently fall - # back to a static call of the base implementation. - self.Gen.TypeGenErr.fatal_error(cd, f"class '{class_name}' inherits more than {VTBL_METHOD_MAX} virtual methods (compiler limit); raise VTBL_METHOD_MAX", "OverflowError") + if method_count >= vt_cap: + # Unreachable with the upper-bound sizing above; stay loud rather than + # writing past the PermPool array. + self.Gen.TypeGenErr.fatal_error(cd, f"internal error: vtable of '{class_name}' exceeded its computed capacity {vt_cap}", "InternalCompilerError") pmethod_name: str = None if vt_parent_entry is not None: pmethod_name = self.Gen.StructGen.get_vtable_method_name(pname, pvi) if pmethod_name is None: continue @@ -170,10 +186,10 @@ novirtual class ClassGenVTable(ClassGenBase.ClassGenBase): if stmt.kind() != ast.ASTKind.FunctionDef: continue fd: ast.FunctionDef* = ast.FunctionDef*(stmt) if fd is None or fd.name is None: continue - if method_count >= VTBL_METHOD_MAX: - # fail-fast: see the inheritance branch above; a dropped method silently loses - # its vtable slot (subclass override stops taking effect). - self.Gen.TypeGenErr.fatal_error(cd, f"class '{class_name}' declares more than {VTBL_METHOD_MAX} virtual methods (compiler limit); raise VTBL_METHOD_MAX", "OverflowError") + if method_count >= vt_cap: + # Unreachable with the upper-bound sizing above (own methods were counted); + # stay loud rather than writing past the PermPool array. + self.Gen.TypeGenErr.fatal_error(cd, f"internal error: vtable of '{class_name}' exceeded its computed capacity {vt_cap}", "InternalCompilerError") mname: str = fd.name # Skip constructors and special methods (they must not enter the vtable).