fix(gen): 泛型类构造复用 new 描述符存储,并支持泛型下标 callee

- _translate_generic_ctor_call 之前一律 _alloca_at_entry:new memgr(pool) hlife list[int](...) 会忽略描述符给的内存、把对象构造到栈上、返回栈地址(静默悬垂)。改为与 _translate_struct_ctor 同款:tmp = _g_new_storage,为空才回落 alloca。

- _translate_new_expr 支持泛型下标 callee(Box[int]):新增 _specialize_new_generic_class 先特化模板拿具体类名,再走同一条取存储路径。
This commit is contained in:
TermiNexus committed 2026-10-08 21:04:41 +08:00
1 parent b6a9b50f93
commit 9ff5a3a03e
1 file changed
+24 -2
+24 -2
View File
@@ -325,6 +325,21 @@ novirtual class CallGenCtor(CallGenBase.CallGenBase):
if new_node.Source == ast.NEW_SRC_HEAP or new_node.Source == ast.NEW_SRC_DEFAULT: return 1
return 0
# _specialize_new_generic_class - `new` 的 callee 是泛型下标(list[int])时先特化,返回具体类名。
# §7.2/M4:`list`/`dict`/`hashtable` 从"类内 __new__ 从池取本体"迁到"调用点 new memgr(pool) …"
# 之后,构造点的 callee 就是下标形式,必须先特化才能拿到 struct 布局去取存储。
def _specialize_new_generic_class(self, pool: memhub.MemBuddy*, func_node: ast.AST*, trans: HT.Translator*) -> str:
"""`new … list[int](...)` → 特化模板并返回具体类名;不是已知泛型模板 → None"""
if pool is None or func_node is None or trans is None: return None
sub_node: ast.Subscript* = ast.Subscript*(func_node)
if sub_node.value is None or sub_node.value.kind() != ast.ASTKind.Name: return None
gen_nm: ast.Name* = ast.Name*(sub_node.value)
if gen_nm.id is None: return None
if self.Gen.ClassGenGeneric._find_generic_template(gen_nm.id) is None: return None
gen_type_args: list[str]* = self._extract_type_args_from_slice(pool, sub_node.slice, trans._from_imports)
if gen_type_args is None or len(gen_type_args) == 0: return None
return self.Gen.ClassGenGeneric._specialize_generic_class(trans, gen_nm.id, gen_type_args)
# _translate_new_expr - M2.4 `new [hlife|slife] [stack|heap|memgr(x)] T(args)`。
# 与既有值构造(T(args))唯一差别是"取存储"一步;其余序列(__before_init__ → __init__ /
# 字段赋值)用合成 Call 走同一条路径,存储经 _g_new_storage 覆盖。结果恒为 T*。
@@ -351,6 +366,10 @@ novirtual class CallGenCtor(CallGenBase.CallGenBase):
self.Gen.TypeGenErr.fatal_error(new_node, "`new stack hlife` is contradictory: a stack allocation can never outlive its frame; use `new stack slife` (the default for stack), or `heap`/`memgr(x)` for a longer lifetime", "LifetimeError")
return None
class_name: str = self.Gen.ExprGenBase.get_func_name(new_node.func)
# §7.2/M4:泛型容器 callee(`new memgr(pool) hlife list[int](...)`)——先特化模板拿具体类名,
# 再按普通类走同一条取存储路径(存储复用见 _translate_generic_ctor_call)。
if class_name is None and new_node.func is not None and new_node.func.kind() == ast.ASTKind.Subscript:
class_name = self._specialize_new_generic_class(pool, new_node.func, trans)
if class_name is None:
self.Gen.TypeGenErr.fatal_error(new_node, "`new` requires a class type to construct (expected `new … T(args)`)", "TypeError")
return None
@@ -901,8 +920,11 @@ novirtual class CallGenCtor(CallGenBase.CallGenBase):
# ensure the struct definition is available in this module (for alloca)
self.Gen.StructGen.ensure_struct_def_in_module(pool, mod, struct_ty)
# alloca tmp (self pointer)
tmp: mlirlite.Value* = self.Gen.VarGen._alloca_at_entry(builder, struct_ty)
# 取存储:`new` 描述符已给出存储(memgr(pool)/heap/stack)就复用它,否则回落到入口块
# alloca(普通 `T(...)` 构造的既有行为)。**这是 §7.2/M4 迁移的前提件**:没有它,
# `new memgr(pool) hlife list[int](...)` 会忽略描述符存储、构造到栈上并返回栈地址。
tmp: mlirlite.Value* = _g_new_storage
if tmp is None: tmp = self.Gen.VarGen._alloca_at_entry(builder, struct_ty)
if tmp is None: return None
# Translate args into a pointer array (extra_args[slot]): positional args -> slots