Files
ViperC/App/lib/core/Gen/Class/ClassGenGeneric.vp
T
TermiNexus aeb4e8e54c refactor(gen): R12 收尾同步——12 处计数 while 循环改 for range
与 includes 同步:<= 条件转 range(expr + 1),并放宽初始化行的紧贴要求(同块内最近一次写即为初始化即可)。其余保守守卫不变。

验证:bootstrap 三重重编 SUCCESS;全量 77/77 顺序回归通过。
2026-10-05 16:36:36 +08:00

1017 lines
59 KiB
Plaintext
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import lib.core.Gen.GenTypes as GenTypes
import t, c
from stdint import *
import ast
import mlirlite
import memhub
import string
import viperlib
import lib.core.VLogger as VLogger
import lib.core.Gen.Translator as HT
import lib.core.CompilerFactory as CompilerFactory
from . import ClassGenBase
import lib.Stub.StubMerger.StubMergerStore as StubMergerStore
import lib.core.Gen.Type.TypeSpec as TSpec
# Generic specialization recursion depth limit (guards against generic loops):
# normal nesting is <= 3 (e.g. list[list[str]]); 64 is far beyond sane, so hitting it means a loop.
define GENERIC_SPEC_MAX_DEPTH = 64
# Generic template table capacity (aligned with the template count the GenericClassNames pool tables can hold)
define GENERIC_TEMPLATE_MAX = 512
# Generic specialization cache table capacity
define SPEC_CACHE_MAX = 512
# Signedness sentinel meaning "take from Spec.IsSigned" (distinct from the three TSpec.TS_SIGN_* values)
define SIGN_FROM_SPEC = -2
# ClassGenGeneric - generic template registration / specialization / cloning
novirtual class ClassGenGeneric(ClassGenBase.ClassGenBase):
"""Generic template registration, specialization and cloning."""
# Cluster C: generic template registration/specialization (P5-2 phase 3 migration; bodies moved
# from HandlesClassDef.vp unchanged; _generic_class_* tables now live in self.GenericClassNames etc.).
def _is_generic_class(self, tp: list[str]*) -> int:
"""Return 1 when the class type_params mark a generic class; 0 otherwise."""
if tp is None: return 0
return 1
def _dup_perm_str(self, s: str) -> str:
"""Deep-copy a string into the permanent pool; return the input when unavailable."""
if s is None: return None
perm: memhub.MemBuddy* = CompilerFactory.factory.PermPool
if perm is None: return s
n: sizet = string.strlen(s)
b: str = perm.alloc(n + 1)
if b is None: return s
string.strcpy(b, s)
return b
def _register_generic_template(self, pool: memhub.MemBuddy*, cd: ast.ClassDef*, module_sha1: str) -> None:
"""Register a generic class template (name, ClassDef node, source module SHA1)."""
if cd is None or cd.name is None: return
perm: memhub.MemBuddy* = CompilerFactory.factory.PermPool
table_pool: memhub.MemBuddy* = perm
if table_pool is None: table_pool = pool
if self.GenericClassNames is None:
self.GenericClassNames = list[str](table_pool, 8)
self.GenericClassNodes = list[ast.ClassDef*](table_pool, 8)
self.GenericClassSha1s = list[str](table_pool, 8)
self.GenericClassFiles = list[str](table_pool, 8)
if self.GenericClassTpParams is None:
if perm is None: return
self.GenericClassTpParams = perm.alloc(GENERIC_TEMPLATE_MAX * 8)
if self.GenericClassTpParams is None: return
string.memset(self.GenericClassTpParams, 0, GENERIC_TEMPLATE_MAX * 8)
# Assign the list to a local to trigger type inference (avoids field __len__ failure)
names_local: list[str]* = self.GenericClassNames
cn: sizet = len(names_local)
for i in range(cn):
nm: str = names_local.get(i)
if nm is not None and string.strcmp(nm, cd.name) == 0:
# A duplicate name must not simply return: the first-registered node may lack
# type_params, so backfill the empty tp slot and replace the node with the richer one.
if self.GenericClassTpParams is not None and i < GENERIC_TEMPLATE_MAX and self.GenericClassTpParams[i] == 0:
back_buf: str = self._copy_type_params_stdlib(cd.type_params)
if back_buf is not None:
self.GenericClassTpParams[i] = sizet(back_buf)
if self.GenericClassNodes is not None: self.GenericClassNodes.set(i, cd)
return
# Deep-copy type_params to stdlib (the template node's list is pool-allocated and may be recycled)
tp_names_buf: str = self._copy_type_params_stdlib(cd.type_params)
self.GenericClassNames.append(self._dup_perm_str(cd.name))
self.GenericClassNodes.append(cd)
self.GenericClassSha1s.append(self._dup_perm_str(module_sha1))
# Also store the template source file path (for error reporting; see the field comment)
self.GenericClassFiles.append(self._dup_perm_str(self.Gen.TypeGenResolve.get_current_file()))
if tp_names_buf is not None and cn < GENERIC_TEMPLATE_MAX: self.GenericClassTpParams[cn] = sizet(tp_names_buf)
def _copy_type_params_stdlib(self, tp: list[str]*) -> str:
"""Deep-copy template type_params into a space-separated stdlib buffer; None on failure."""
if tp is None: return None
tp_n: sizet = len(tp)
if tp_n == 0: return None
# Compute total length: each name length + (n-1) spaces + NUL
total: sizet = 1
ti: sizet
for ti in range(tp_n):
tp_name: str = tp.get(ti)
if tp_name is not None: total += string.strlen(tp_name) + 1
buf: str = CompilerFactory.factory.PermPool.alloc(total)
if buf is None: return None
buf[0] = '\0'
for ti in range(tp_n):
tp_name = tp.get(ti)
if tp_name is None: continue
if ti > 0 and buf[0] != '\0': string.strcat(buf, " ")
string.strcat(buf, tp_name)
return buf
# Generic function templates (PEP 695 def f[T](...)): mirrors the generic class path; keys include
# the defining module SHA1, call sites infer args, and specialization emits a define (or a declare).
def _is_generic_function(self, tp: list[str]*) -> int:
"""Return 1 when the function type_params mark a generic function; 0 otherwise."""
if tp is None: return 0
return 1
def _register_generic_func_template(self, pool: memhub.MemBuddy*, fd: ast.FunctionDef*, module_sha1: str) -> None:
"""Register a generic function template keyed by "{module_sha1}.{name}"."""
if fd is None or fd.name is None or module_sha1 is None: return
perm: memhub.MemBuddy* = CompilerFactory.factory.PermPool
table_pool: memhub.MemBuddy* = perm
if table_pool is None: table_pool = pool
if self.GenericFuncKeys is None:
self.GenericFuncKeys = list[str](table_pool, 8)
self.GenericFuncNodes = list[ast.FunctionDef*](table_pool, 8)
self.GenericFuncSha1s = list[str](table_pool, 8)
self.GenericFuncFiles = list[str](table_pool, 8)
if self.GenericFuncTpParams is None:
if perm is None: return
self.GenericFuncTpParams = perm.alloc(GENERIC_TEMPLATE_MAX * 8)
if self.GenericFuncTpParams is None: return
string.memset(self.GenericFuncTpParams, 0, GENERIC_TEMPLATE_MAX * 8)
# Key = "{sha1}.{name}" (built in a new buffer to avoid in-place strcat overflow)
key_len: sizet = string.strlen(module_sha1) + string.strlen(fd.name) + 2
key_buf: str = table_pool.alloc(key_len)
if key_buf is None: return
viperlib.snprintf(key_buf, key_len, "%s.%s", module_sha1, fd.name)
keys_local: list[str]* = self.GenericFuncKeys
kn: sizet = len(keys_local)
ki: sizet
for ki in range(kn):
knm: str = keys_local.get(ki)
if knm is not None and string.strcmp(knm, key_buf) == 0: return
tp_names_buf: str = self._copy_type_params_stdlib(fd.type_params)
self.GenericFuncKeys.append(self._dup_perm_str(key_buf))
self.GenericFuncNodes.append(fd)
self.GenericFuncSha1s.append(self._dup_perm_str(module_sha1))
self.GenericFuncFiles.append(self._dup_perm_str(self.Gen.TypeGenResolve.get_current_file()))
if tp_names_buf is not None and kn < GENERIC_TEMPLATE_MAX: self.GenericFuncTpParams[kn] = sizet(tp_names_buf)
def _find_generic_func_template(self, key: str) -> ast.FunctionDef*:
"""Find a generic function template node by "{sha1}.{fname}" key; None when absent."""
if self.GenericFuncKeys is None or key is None: return None
keys_local: list[str]* = self.GenericFuncKeys
nodes_local: list[ast.FunctionDef*]* = self.GenericFuncNodes
kn: sizet = len(keys_local)
i: sizet
for i in range(kn):
knm: str = keys_local.get(i)
if knm is not None and string.strcmp(knm, key) == 0: return nodes_local.get(i)
return None
def _find_generic_func_template_sha1(self, key: str) -> str:
"""Find a generic function template's source module SHA1 by key; None when absent."""
if self.GenericFuncKeys is None or key is None: return None
keys_local: list[str]* = self.GenericFuncKeys
sha1s_local: list[str]* = self.GenericFuncSha1s
kn: sizet = len(keys_local)
i: sizet
for i in range(kn):
knm: str = keys_local.get(i)
if knm is not None and string.strcmp(knm, key) == 0: return sha1s_local.get(i)
return None
def _find_generic_func_template_file(self, key: str) -> str:
"""Find a generic function template's source file path by key; None when absent."""
if self.GenericFuncKeys is None or key is None: return None
keys_local: list[str]* = self.GenericFuncKeys
files_local: list[str]* = self.GenericFuncFiles
kn: sizet = len(keys_local)
i: sizet
for i in range(kn):
knm: str = keys_local.get(i)
if knm is not None and string.strcmp(knm, key) == 0: return files_local.get(i)
return None
def _find_generic_func_tp_params(self, pool: memhub.MemBuddy*, key: str) -> list[str]*:
"""Rebuild a generic function template's type-param name list; None when absent."""
if self.GenericFuncKeys is None or key is None or self.GenericFuncTpParams is None: return None
keys_local: list[str]* = self.GenericFuncKeys
kn: sizet = len(keys_local)
i: sizet
for i in range(kn):
knm: str = keys_local.get(i)
if knm is not None and string.strcmp(knm, key) == 0:
if i >= GENERIC_TEMPLATE_MAX: return None
buf_addr: sizet = self.GenericFuncTpParams[i]
if buf_addr == 0: return None
buf_tp: str = rawptr(buf_addr)
tp_list: list[str]* = list[str](pool, 8)
if tp_list is None: return None
seg_start: sizet = 0
blen: sizet = string.strlen(buf_tp)
for pos in range(blen + 1):
if buf_tp[pos] == ' ' or buf_tp[pos] == '\0':
seg_len: sizet = pos - seg_start
if seg_len > 0:
seg: str = pool.alloc(seg_len + 1)
if seg is not None:
string.strncpy(seg, buf_tp + seg_start, seg_len)
seg[seg_len] = '\0'
tp_list.append(seg)
seg_start = pos + 1
return tp_list
return None
def _find_generic_func_param_slot(self, fd: ast.FunctionDef*, tp_name: str) -> int:
"""Return the 0-based slot of the parameter annotated exactly as tp_name; -1 when absent."""
if fd is None or tp_name is None or fd.args is None: return -1
if fd.args.kind() != ast.ASTKind.Arguments: return -1
ags: ast.Arguments* = ast.Arguments*(fd.args)
if ags.args is None: return -1
alist: list[ast.AST*]* = ags.args
an: sizet = len(alist)
ai: sizet
for ai in range(an):
pa: ast.AST* = alist.get(ai)
if pa is None or pa.kind() != ast.ASTKind.Arg: continue
ag: ast.Arg* = ast.Arg*(pa)
if self.Gen.CallGenCtor._annotation_refers_type_param(ag.annotation, tp_name) != 0: return int(ai)
return -1
def _infer_generic_func_type_arg(self, pool: memhub.MemBuddy*, arg_node: ast.AST*, func_name: str, trans: HT.Translator*) -> str:
"""Infer a generic function's type argument name from a call argument AST; None when unknown."""
if arg_node is None: return None
k: int = arg_node.kind()
if k == ast.ASTKind.Constant:
cn: ast.Constant* = ast.Constant*(arg_node)
if cn.const_kind == ast.CONST_FLOAT: return "double"
if cn.const_kind == ast.CONST_CHAR: return "char"
if cn.const_kind == ast.CONST_STR: return "str"
return "int"
# Unary +/- constant (clamp(-3, ...)): the type is determined by the operand
if k == ast.ASTKind.UnaryOp:
uo: ast.UnaryOp* = ast.UnaryOp*(arg_node)
return self._infer_generic_func_type_arg(pool, uo.operand, func_name, trans)
if k == ast.ASTKind.Name:
nm: ast.Name* = ast.Name*(arg_node)
if nm.id is not None and trans is not None:
ve: GenTypes.VarEntry* = self.Gen.VarGen.lookup_var_entry(trans.SymTab, nm.id)
if ve is not None:
cn_nm: str = self.Gen.VarGen.var_annot_class_name(ve)
pl: int = 0
if ve.Spec is not None: pl = ve.Spec.PtrLevel
if cn_nm is not None: return self.Gen.CallGenCtor._ptr_layer_type_arg(pool, cn_nm, pl, trans._from_imports)
# Builtin scalars (skipped by extract_class_name_from_annotation, SourceName None):
# reverse-map the Spec's LLVM form to a parseable type name.
return self._infer_scalar_type_name_from_spec(pool, ve.Spec)
return None
if k == ast.ASTKind.Attribute:
cn_at: str = self.Gen.ExprGenAttr._resolve_attr_field_class(pool, trans, arg_node)
pl_at: int = 0
fe: GenTypes.FieldEntry* = self.Gen.ExprGenAttr._resolve_attr_field_entry(pool, trans, arg_node)
if fe is not None and fe.Spec is not None: pl_at = fe.Spec.PtrLevel
fi: str = None
if trans is not None: fi = trans._from_imports
if cn_at is not None: return self.Gen.CallGenCtor._ptr_layer_type_arg(pool, cn_at, pl_at, fi)
# Builtin scalar field: reverse-map from Spec, same as the Name branch
if fe is not None: return self._infer_scalar_type_name_from_spec(pool, fe.Spec)
return None
if k == ast.ASTKind.Call:
cl_f: ast.Call* = ast.Call*(arg_node)
if cl_f.func is not None:
fk_f: int = cl_f.func.kind()
# Only accept conversion calls whose callee resolves to a scalar type, mapping by exact
# bit width; non-scalar/unresolvable yields None. No name-substring guessing (never i64->i32).
if (fk_f == ast.ASTKind.Name or fk_f == ast.ASTKind.Attribute) and trans is not None:
cty_f: mlirlite.LLVMType* = self.Gen.TypeGenResolve.resolve_annotation_type(pool, cl_f.func, trans._imported_modules, trans._from_imports, trans, 0)
if cty_f is not None:
# Signedness can only come from the callee's source text (LLVM iN/uN look alike,
# SpecFromLLVMType always says SIGNED); reuse spec_sign_of_annot to pick the exact type.
callee_txt: str = None
if fk_f == ast.ASTKind.Name: callee_txt = ast.Name*(cl_f.func).id
else: callee_txt = ast.Attribute*(cl_f.func).attr
sign_f: int = TSpec.TS_SIGN_UNKNOWN
if callee_txt is not None: sign_f = self.Gen.TypeGenCType.spec_sign_of_annot(callee_txt, 0)
return self.infer_scalar_type_arg_from_llvm_ty(pool, cty_f, sign_f)
return None
return None
def _infer_scalar_type_name_from_spec(self, pool: memhub.MemBuddy*, sp: TSpec.TypeSpec*, sign_override: int = SIGN_FROM_SPEC) -> str:
"""Reverse-map a variable's TypeSpec to a resolvable builtin scalar type name; None otherwise."""
if pool is None or sp is None: return None
buf: str = pool.alloc(32)
if buf is None: return None
buf[0] = '\0'
# SpecToLLVMText returns 0=success / 1=failure; treating 0 as failure made this always return
# None for builtin scalars, so scalar-typed params could never be inferred.
if TSpec.SpecToLLVMText(sp, buf, 32) != 0: return None
sign: int = sign_override
if sign == SIGN_FROM_SPEC:
# SpecFromLLVMType always reports TS_SIGN_SIGNED (iN/uN are indistinguishable), so only an
# annotation-side Spec (via SpecWithSign) makes this field trustworthy.
sign = sp.IsSigned
# ---- Integers: exact bit width + consistent signedness (table in docstring) ----
if string.strcmp(buf, "i1") == 0: return "bool"
if string.strcmp(buf, "i8") == 0:
# Unsigned i8 (CUInt8T/CUnsignedChar) via the bare name char would silently change sign
if sign == TSpec.TS_SIGN_UNSIGNED: return "uint8"
return "char"
if string.strcmp(buf, "i16") == 0:
if sign == TSpec.TS_SIGN_UNSIGNED: return "uint16"
if sign == TSpec.TS_SIGN_SIGNED: return "int16"
return None
if string.strcmp(buf, "i32") == 0:
# Unsigned i32 (CUInt32T/CSizeT/CUIntPtrT) via the bare name int would silently change sign
if sign == TSpec.TS_SIGN_UNSIGNED: return "uint32"
return "int"
if string.strcmp(buf, "i64") == 0:
if sign == TSpec.TS_SIGN_UNSIGNED: return "uint64"
if sign == TSpec.TS_SIGN_SIGNED: return "int64"
return None
# ---- Floats: exact bit width (no signedness concept, unambiguous) ----
if string.strcmp(buf, "half") == 0: return "t.CFloat16T"
if string.strcmp(buf, "float") == 0: return "float"
if string.strcmp(buf, "double") == 0: return "double"
if string.strcmp(buf, "fp128") == 0: return "t.CFloat128T"
return None
def infer_scalar_type_arg_from_llvm_ty(self, pool: memhub.MemBuddy*, ty: mlirlite.LLVMType*, sign: int) -> str:
"""Map an LLVM scalar type to a generic type-argument name by exact bit width; None for non-scalars."""
if pool is None or ty is None: return None
arena: TSpec.TypeSpecArena* = self.Gen.TypeGenBase.ensure_spec_arena()
if arena is None: return None
return self._infer_scalar_type_name_from_spec(pool, TSpec.SpecFromLLVMType(arena, ty), sign)
def _clone_funcdef_for_spec(self, pool: memhub.MemBuddy*, fd: ast.FunctionDef*, spec_base: str, tp_names: list[str]*, type_args: list[str]*) -> ast.FunctionDef*:
"""Clone a FunctionDef for generic function specialization, substituting concrete types."""
if fd is None: return None
new_args: ast.AST* = self._clone_arguments(pool, fd.args, tp_names, type_args)
new_returns: ast.AST* = self._clone_annotation(pool, fd.returns, tp_names, type_args)
new_fd: ast.FunctionDef* = ast.FunctionDef(pool, spec_base, new_args, fd.decorator_list, new_returns, 0)
if fd.children is not None:
body: list[ast.AST*]* = fd.children
bn: sizet = len(body)
bi: sizet
for bi in range(bn):
stmt: ast.AST* = body.get(bi)
if stmt is not None: new_fd.append(stmt)
return new_fd
def _ensure_generic_func_declare(self, trans: HT.Translator*, pool: memhub.MemBuddy*, spec_name: str, template_key: str, tp_names: list[str]*, type_args: list[str]*) -> mlirlite.Function*:
"""Ensure the current module declares a specialized function; None on failure."""
if trans is None or pool is None or spec_name is None: return None
mod: mlirlite.LLVMModule* = trans.Module
if mod is None: return None
existing: mlirlite.Function* = self.Gen.CallGenBase.find_func_in_module(mod, spec_name)
if existing is not None: return existing
template_fd: ast.FunctionDef* = self._find_generic_func_template(template_key)
if template_fd is None: return None
template_sha1: str = self._find_generic_func_template_sha1(template_key)
if template_sha1 is None: return None
spec_base: str = self._mangle_generic_class_name(pool, template_fd.name, type_args, trans._from_imports)
if spec_base is None: return None
spec_fd: ast.FunctionDef* = self._clone_funcdef_for_spec(pool, template_fd, spec_base, tp_names, type_args)
if spec_fd is None: return None
i32_ty: mlirlite.LLVMType* = mlirlite.Int32(pool)
saved_ms: str = trans.ModuleSha1
saved_imp: str = trans._imported_modules
saved_gtpn: list[str]* = trans.GenericTypeParamNames
saved_gta: list[str]* = trans.GenericTypeArgs
trans.ModuleSha1 = template_sha1
self._merge_template_imports(trans, template_sha1)
trans.GenericTypeParamNames = tp_names
trans.GenericTypeArgs = type_args
self.Gen.TypeGenBase.set_generic_context(tp_names, type_args)
saved_cur: str = self.Gen.TypeGenResolve.get_current_file()
tpl_file: str = self._find_generic_func_template_file(template_key)
if tpl_file is not None: self.Gen.TypeGenResolve.set_current_file(tpl_file)
ret_ty: mlirlite.LLVMType* = None
if spec_fd.returns is not None: ret_ty = self.Gen.TypeGenResolve.resolve_annotation_type(pool, spec_fd.returns, trans._imported_modules, trans._from_imports, trans, 0)
if ret_ty is None: ret_ty = i32_ty
func: mlirlite.Function* = mlirlite.create_declare(pool, mod, spec_name, ret_ty)
if func is not None and spec_fd.args is not None and spec_fd.args.kind() == ast.ASTKind.Arguments:
ags: ast.Arguments* = ast.Arguments*(spec_fd.args)
if ags.args is not None:
alist: list[ast.AST*]* = ags.args
an: sizet = len(alist)
ai: sizet
for ai in range(an):
pa: ast.AST* = alist.get(ai)
if pa is None or pa.kind() != ast.ASTKind.Arg: continue
arg: ast.Arg* = ast.Arg*(pa)
if arg.arg is None: continue
param_ty: mlirlite.LLVMType* = i32_ty
if arg.annotation is not None:
resolved: mlirlite.LLVMType* = self.Gen.TypeGenResolve.resolve_annotation_type(pool, arg.annotation, trans._imported_modules, trans._from_imports, trans, 0)
if resolved is not None:
match resolved:
case mlirlite.LLVMType.Struct(_, _, _):
param_ty = mlirlite.Ptr(pool, resolved)
case mlirlite.LLVMType.Array(aelem_ty, _):
param_ty = mlirlite.Ptr(pool, aelem_ty)
case _:
param_ty = resolved
pname: str = pool.alloc(32)
if pname is not None: viperlib.snprintf(pname, 32, "%%%s", arg.arg)
mlirlite.add_param(pool, func, param_ty, pname)
if tpl_file is not None: self.Gen.TypeGenResolve.set_current_file(saved_cur)
trans.ModuleSha1 = saved_ms
trans._imported_modules = saved_imp
trans.GenericTypeParamNames = saved_gtpn
trans.GenericTypeArgs = saved_gta
self.Gen.TypeGenBase.clear_generic_context()
return func
def _specialize_generic_function(self, trans: HT.Translator*, template_key: str, type_args: list[str]*) -> str:
"""Specialize a generic function and return its mangled name."""
if trans is None or template_key is None or type_args is None: return None
self.GenericSpecDepth += 1
if self.GenericSpecDepth > GENERIC_SPEC_MAX_DEPTH: self.Gen.TypeGenErr.fatal_error(None, "泛型函数特化递归过深(疑似泛型循环套用)", "InternalCompilerError")
pool: memhub.MemBuddy* = trans.Pool
template_fd: ast.FunctionDef* = self._find_generic_func_template(template_key)
if template_fd is None:
self.GenericSpecDepth -= 1
return None
template_sha1: str = self._find_generic_func_template_sha1(template_key)
if template_sha1 is None:
self.GenericSpecDepth -= 1
return None
# Defensive: any unresolved type-parameter name (e.g. "T") left in the collected type_args
# is replaced with the concrete type name (nested specialization: generic fn calling generic fn).
ta_cnt_g: sizet = len(type_args)
ta_i_g: sizet
for ta_i_g in range(ta_cnt_g):
ta_raw_g: str = type_args.get(ta_i_g)
if ta_raw_g is not None:
ta_rsv_g: str = self.Gen.TypeGenBase.resolve_generic_type_name(ta_raw_g)
if ta_rsv_g is not None and string.strcmp(ta_raw_g, ta_rsv_g) != 0: type_args.set(ta_i_g, ta_rsv_g)
tp_names: list[str]* = self._find_generic_func_tp_params(pool, template_key)
if tp_names is None: tp_names = template_fd.type_params
if tp_names is None:
self.GenericSpecDepth -= 1
return None
if len(tp_names) != len(type_args):
err_tp: str = pool.alloc(256)
if err_tp is not None:
viperlib.snprintf(err_tp, 256, "泛型函数 '%s' 类型实参个数不匹配(需要 %d,给定 %d)", template_fd.name, len(tp_names), len(type_args))
self.Gen.TypeGenErr.fatal_error(None, err_tp, "TypeError")
self.GenericSpecDepth -= 1
return None
# Generic functions with CExport/CExtern/State markers are unsupported (specialized names bypass bare-name export)
if template_fd.returns is not None:
if self.Gen.TypeGenErr.has_decorator_marker(template_fd.returns, "CExport", 0) != 0 or self.Gen.TypeGenErr.has_decorator_marker(template_fd.returns, "CExtern", 0) != 0 or self.Gen.TypeGenErr.has_decorator_marker(template_fd.returns, "State", 0) != 0:
self.Gen.TypeGenErr.fatal_error(template_fd, "泛型函数暂不支持 t.CExport/t.CExtern/t.State 存储标记", "TypeError")
# Generate the specialized name (the single generator normalizes each arg so bare/prefixed forms converge)
spec_base: str = self._mangle_generic_class_name(pool, template_fd.name, type_args, trans._from_imports)
if spec_base is None:
self.GenericSpecDepth -= 1
return None
spec_name: str = self.Gen.FuncGenBase._mangle_name_with_sha1(template_sha1, spec_base)
# spec_key = template key + normalized spec base, matching spec_name one-to-one so bare/prefixed
# spellings cannot yield two cache keys; the template key (SHA1) keeps it distinct from class keys.
spec_key: str = pool.alloc(string.strlen(template_key) + string.strlen(spec_base) + 2)
if spec_key is None:
self.GenericSpecDepth -= 1
return None
spec_key[0] = '\0'
string.strcat(spec_key, template_key)
string.strcat(spec_key, "#")
string.strcat(spec_key, spec_base)
cached: str = self._find_cached_spec(spec_key)
if cached is not None:
self._ensure_generic_func_declare(trans, pool, spec_name, template_key, tp_names, type_args)
self.GenericSpecDepth -= 1
return cached
# An existing same-name define in this module (multi-pass/repeat) is reused, not re-cloned;
# a declare only continues to emit (translate_function_def reuses it and fills the body).
existing_fn: mlirlite.Function* = self.Gen.CallGenBase.find_func_in_module(trans.Module, spec_name)
if existing_fn is not None and existing_fn.IsDeclared == 0:
self._cache_spec(pool, spec_key, spec_name)
self.GenericSpecDepth -= 1
return spec_name
# Clone + translate with context to emit the define
spec_fd: ast.FunctionDef* = self._clone_funcdef_for_spec(pool, template_fd, spec_base, tp_names, type_args)
if spec_fd is None:
self.GenericSpecDepth -= 1
return None
# Pre-register in the cache to prevent recursive specialization
self._cache_spec(pool, spec_key, spec_name)
saved_ms: str = trans.ModuleSha1
saved_imp: str = trans._imported_modules
saved_gtpn: list[str]* = trans.GenericTypeParamNames
saved_gta: list[str]* = trans.GenericTypeArgs
# The class-definition window must be saved/restored together with ModuleSha1 (specialization
# translation refreshes it per template module sha1 + spec name; see _specialize_generic_class).
saved_def_mod: str = self.Gen.StructGen.CurDefModuleSha1
saved_def_cls: str = self.Gen.StructGen.CurDefClassName
# Switch ModuleSha1 to the template-defining module: specialized symbol names are {templateSHA1}.f[args],
# consistent cross-module; also merge the template module's imports so its cross-module calls resolve.
trans.ModuleSha1 = template_sha1
self._merge_template_imports(trans, template_sha1)
trans.GenericTypeParamNames = tp_names
trans.GenericTypeArgs = type_args
self.Gen.TypeGenBase.set_generic_context(tp_names, type_args)
# Error reporting: the specialized body's node.lineno is a template-file line, so temporarily
# switch CurrentFile to the template file and push a trace frame (restored after translation).
saved_cur: str = self.Gen.TypeGenResolve.get_current_file()
tpl_file: str = self._find_generic_func_template_file(template_key)
if tpl_file is not None:
self.Gen.TypeGenResolve.set_current_file(tpl_file)
self.Gen.TypeGenBase.push_trace(tpl_file, template_fd.lineno)
self.Gen.FuncGenDef.translate_function_def(trans, spec_fd)
if tpl_file is not None: self.Gen.TypeGenBase.pop_trace()
self.Gen.TypeGenResolve.set_current_file(saved_cur)
trans.ModuleSha1 = saved_ms
trans._imported_modules = saved_imp
self.Gen.StructGen.CurDefModuleSha1 = saved_def_mod
self.Gen.StructGen.CurDefClassName = saved_def_cls
trans.GenericTypeParamNames = saved_gtpn
trans.GenericTypeArgs = saved_gta
self.Gen.TypeGenBase.clear_generic_context()
self.GenericSpecDepth -= 1
return spec_name
def _find_generic_template_tp_params(self, pool: memhub.MemBuddy*, class_name: str) -> list[str]*:
"""Rebuild a generic class template's type-param name list; None when absent."""
if self.GenericClassNames is None or class_name is None or self.GenericClassTpParams is None: return None
names_local: list[str]* = self.GenericClassNames
cn: sizet = len(names_local)
i: sizet
for i in range(cn):
nm: str = names_local.get(i)
if nm is not None and string.strcmp(nm, class_name) == 0:
if i >= GENERIC_TEMPLATE_MAX: return None
buf_addr: sizet = self.GenericClassTpParams[i]
if buf_addr == 0: return None
buf_tp: str = rawptr(buf_addr)
# Split by spaces into list[str]. list[str](pool, 8)'s second arg is elem_size (pointer
# 8 bytes), not capacity; passing 4 corrupts get(i) offsets and append overwrites.
tp_list: list[str]* = list[str](pool, 8)
if tp_list is None: return None
seg_start: sizet = 0
blen: sizet = string.strlen(buf_tp)
for pos in range(blen + 1):
if buf_tp[pos] == ' ' or buf_tp[pos] == '\0':
seg_len: sizet = pos - seg_start
if seg_len > 0:
seg: str = pool.alloc(seg_len + 1)
if seg is not None:
string.strncpy(seg, buf_tp + seg_start, seg_len)
seg[seg_len] = '\0'
tp_list.append(seg)
seg_start = pos + 1
return tp_list
return None
def _find_generic_template(self, class_name: str) -> ast.ClassDef*:
"""Find a generic class template; return the ClassDef node or None."""
if self.GenericClassNames is None or class_name is None: return None
# Assign the list to a local to trigger type inference (avoids field __len__ failure)
names_local: list[str]* = self.GenericClassNames
nodes_local: list[ast.ClassDef*] | t.CPtr = self.GenericClassNodes
cn: sizet = len(names_local)
i: sizet
for i in range(cn):
nm: str = names_local.get(i)
if nm is not None and string.strcmp(nm, class_name) == 0:
node: ast.ClassDef* = nodes_local.get(i)
return node
return None
def _find_generic_template_sha1(self, class_name: str) -> str:
"""Find a generic class template's source module SHA1; None when absent."""
if self.GenericClassNames is None or class_name is None: return None
# Assign the list to a local to trigger type inference
names_local: list[str]* = self.GenericClassNames
sha1s_local: list[str]* = self.GenericClassSha1s
cn: sizet = len(names_local)
i: sizet
for i in range(cn):
nm: str = names_local.get(i)
if nm is not None and string.strcmp(nm, class_name) == 0:
sha1_val: str = sha1s_local.get(i)
return sha1_val
return None
def _find_generic_template_file(self, class_name: str) -> str:
"""Find a generic class template's source file path; None when absent."""
if self.GenericClassNames is None or class_name is None: return None
names_local: list[str]* = self.GenericClassNames
files_local: list[str]* = self.GenericClassFiles
cn: sizet = len(names_local)
i: sizet
for i in range(cn):
nm: str = names_local.get(i)
if nm is not None and string.strcmp(nm, class_name) == 0:
f_val: str = files_local.get(i)
return f_val
return None
def _mangle_generic_class_name(self, pool: memhub.MemBuddy*, class_name: str, type_args: list[str]*, from_imports: str = None) -> str:
"""Build the specialized class name (e.g. list + [int] -> list[int]) in a new buffer."""
if class_name is None or type_args is None or pool is None: return class_name
n: sizet = len(type_args)
if n == 0: return class_name
# Normalize each arg (idempotent): normalize into a new list first, then use it for length and concatenation
norm_args: list[str]* = list[str](pool, 8)
if norm_args is None: return class_name
i: sizet
for i in range(n):
ta_raw: str = type_args.get(i)
if ta_raw is None: continue
ta_norm: str = self.Gen.TypeGenResolve._norm_type_arg_name(pool, ta_raw, from_imports)
if ta_norm is None: ta_norm = ta_raw
norm_args.append(ta_norm)
n = len(norm_args)
if n == 0: return class_name
# Compute total length: class_name + "[" + arg0 + "," + arg1 + ... + "]" + NUL
total_len: sizet = string.strlen(class_name) + 2 # "[" and "]"
for i in range(n):
if i > 0:
total_len += 1 # ","
ta: str = norm_args.get(i)
if ta is not None: total_len += string.strlen(ta)
total_len += 1 # NUL
# Allocate a new buffer
mangled: str = pool.alloc(total_len)
if mangled is None: return class_name
mangled[0] = '\0'
string.strcat(mangled, class_name)
string.strcat(mangled, "[")
for i in range(n):
if i > 0: string.strcat(mangled, ",")
ta: str = norm_args.get(i)
if ta is not None: string.strcat(mangled, ta)
string.strcat(mangled, "]")
return mangled
def _clone_annotation(self, pool: memhub.MemBuddy*, node: ast.AST*, tp_names: list[str]*, type_args: list[str]*) -> ast.AST*:
"""Clone a type annotation expression, replacing type parameter names with concrete types."""
if node is None: return None
k: int = node.kind()
# Name: may be the type parameter T
if k == ast.ASTKind.Name:
nm: ast.Name* = ast.Name*(node)
if nm.id is not None:
n: sizet = len(tp_names) if tp_names is not None else 0
i: sizet
for i in range(n):
tpn: str = tp_names.get(i)
if tpn is not None and string.strcmp(nm.id, tpn) == 0:
ta: str = type_args.get(i)
# A type arg may be a composite type (e.g. "AST*"); parse it with _parse_type_string
parsed_node: ast.AST* = self.Gen.TypeGenResolve._parse_type_string(pool, ta, nm.ctx)
return parsed_node
return ast.Name(pool, nm.id, nm.ctx)
return None
# Attribute: t.CPtr etc.
if k == ast.ASTKind.Attribute:
at: ast.Attribute* = ast.Attribute*(node)
new_value: ast.AST* = self._clone_annotation(pool, at.value, tp_names, type_args)
return ast.Attribute(pool, new_value, at.attr, at.ctx)
# BinOp: T* etc.
if k == ast.ASTKind.BinOp:
bop: ast.BinOp* = ast.BinOp*(node)
new_left: ast.AST* = self._clone_annotation(pool, bop.left, tp_names, type_args)
new_right: ast.AST* = self._clone_annotation(pool, bop.right, tp_names, type_args)
return ast.BinOp(pool, new_left, bop.op, new_right)
# Subscript: list[int] etc.
if k == ast.ASTKind.Subscript:
sub: ast.Subscript* = ast.Subscript*(node)
new_value2: ast.AST* = self._clone_annotation(pool, sub.value, tp_names, type_args)
new_slice: ast.AST* = self._clone_annotation(pool, sub.slice, tp_names, type_args)
return ast.Subscript(pool, new_value2, new_slice, sub.ctx)
# Tuple: (T, int) etc.
if k == ast.ASTKind.Tuple:
tup: ast.Tuple* = ast.Tuple*(node)
new_elts: list[ast.AST*]* = list[ast.AST*](pool, 8)
if tup.elts is not None:
tup_elts: list[ast.AST*]* = tup.elts
en: sizet = len(tup_elts)
ei: sizet
for ei in range(en):
el: ast.AST* = tup_elts.get(ei)
new_el: ast.AST* = self._clone_annotation(pool, el, tp_names, type_args)
if new_el is not None: new_elts.append(new_el)
return ast.Tuple(pool, new_elts, tup.ctx)
# Constant: default values etc.
if k == ast.ASTKind.Constant:
cnst: ast.Constant* = ast.Constant*(node)
return ast.Constant(pool, cnst.const_kind, cnst.int_val, cnst.float_val, cnst.str_val, cnst.lineno, cnst.col_offset)
# Other: reference directly (no copy)
return node
def _clone_arguments(self, pool: memhub.MemBuddy*, args: ast.AST*, tp_names: list[str]*, type_args: list[str]*) -> ast.AST*:
"""Clone an Arguments node, replacing type parameters inside arg annotations."""
if args is None: return None
if args.kind() != ast.ASTKind.Arguments: return args
old_args: ast.Arguments* = ast.Arguments*(args)
# Copy the args list and replace each Arg's annotation
new_arg_list: list[ast.AST*]* = list[ast.AST*](pool, 8)
if old_args.args is not None:
old_arg_list: list[ast.AST*]* = old_args.args
an: sizet = len(old_arg_list)
ai: sizet
for ai in range(an):
arg_node: ast.AST* = old_arg_list.get(ai)
if arg_node is None: continue
if arg_node.kind() == ast.ASTKind.Arg:
ag: ast.Arg* = ast.Arg*(arg_node)
new_anno: ast.AST* = self._clone_annotation(pool, ag.annotation, tp_names, type_args)
new_arg: ast.Arg* = ast.Arg(pool, ag.arg, new_anno)
new_arg_list.append(new_arg)
else: new_arg_list.append(arg_node)
return ast.Arguments(pool, new_arg_list, old_args.vararg, old_args.kwarg, old_args.defaults, old_args.kw_defaults)
def _clone_classdef_for_spec(self, pool: memhub.MemBuddy*, cd: ast.ClassDef*, spec_name: str, tp_names: list[str]*, type_args: list[str]*) -> ast.ClassDef*:
"""Clone a ClassDef node for generic specialization, substituting concrete types."""
# Create the new ClassDef (type_params=None means non-generic)
new_cd: ast.ClassDef* = ast.ClassDef(pool, spec_name, cd.bases, cd.keywords, cd.decorator_list, None)
# Copy children
if cd.children is not None:
cd_children: list[ast.AST*]* = cd.children
cn: sizet = len(cd_children)
ci: sizet
for ci in range(cn):
child: ast.AST* = cd_children.get(ci)
if child is None: continue
ck: int = child.kind()
if ck == ast.ASTKind.AnnAssign:
aa: ast.AnnAssign* = ast.AnnAssign*(child)
new_target: ast.AST* = aa.target
new_anno2: ast.AST* = self._clone_annotation(pool, aa.annotation, tp_names, type_args)
new_value3: ast.AST* = aa.value
new_aa: ast.AnnAssign* = ast.AnnAssign(pool, new_target, new_anno2, new_value3, aa.simple)
new_cd.append(new_aa)
elif ck == ast.ASTKind.FunctionDef:
fd: ast.FunctionDef* = ast.FunctionDef*(child)
new_args2: ast.AST* = self._clone_arguments(pool, fd.args, tp_names, type_args)
new_returns2: ast.AST* = self._clone_annotation(pool, fd.returns, tp_names, type_args)
new_fd: ast.FunctionDef* = ast.FunctionDef(pool, fd.name, new_args2, fd.decorator_list, new_returns2, 0)
# The method body directly references the original nodes
if fd.children is not None:
fd_children: list[ast.AST*]* = fd.children
bcn: sizet = len(fd_children)
bci: sizet
for bci in range(bcn):
body_stmt: ast.AST* = fd_children.get(bci)
if body_stmt is not None: new_fd.append(body_stmt)
new_cd.append(new_fd)
else: new_cd.append(child)
return new_cd
def _find_cached_spec(self, spec_key: str) -> str:
"""Find a cached specialization name; None when absent."""
if self.SpecKeys is None or spec_key is None: return None
i: int
for i in range(self.SpecCount):
key_addr: sizet = self.SpecKeys[i]
if key_addr != 0:
k: str = rawptr(key_addr)
if k is not None and string.strcmp(k, spec_key) == 0:
name_addr: sizet = self.SpecNames[i]
if name_addr != 0: return rawptr(name_addr)
return None
def _cache_spec(self, pool: memhub.MemBuddy*, spec_key: str, spec_name: str):
"""Cache a specialization result as a cross-file-safe stdlib deep copy."""
if spec_key is None or spec_name is None: return
if self.SpecKeys is None:
self.SpecKeys = CompilerFactory.factory.PermPool.alloc(SPEC_CACHE_MAX * 8)
self.SpecNames = CompilerFactory.factory.PermPool.alloc(SPEC_CACHE_MAX * 8)
if self.SpecKeys is None or self.SpecNames is None: return
string.memset(self.SpecKeys, 0, SPEC_CACHE_MAX * 8)
string.memset(self.SpecNames, 0, SPEC_CACHE_MAX * 8)
self.SpecCount = 0
if self.SpecCount >= SPEC_CACHE_MAX: return
# Deep-copy strings into the stdlib region
key_len: sizet = string.strlen(spec_key)
name_len: sizet = string.strlen(spec_name)
key_copy: str = CompilerFactory.factory.PermPool.alloc(key_len + 1)
name_copy: str = CompilerFactory.factory.PermPool.alloc(name_len + 1)
if key_copy is None or name_copy is None: return
string.strcpy(key_copy, spec_key)
string.strcpy(name_copy, spec_name)
self.SpecKeys[self.SpecCount] = sizet(key_copy)
self.SpecNames[self.SpecCount] = sizet(name_copy)
self.SpecCount += 1
def _merge_template_imports(self, trans: HT.Translator*, template_sha1: str) -> None:
"""Merge the template module's imports into trans._imported_modules."""
if trans is None or template_sha1 is None: return
tdeps: str = StubMergerStore.LookupIncludesDeps(template_sha1)
if tdeps is None or tdeps[0] == '\0': return
cur: str = trans._imported_modules
if cur is None or cur[0] == '\0':
trans._imported_modules = tdeps
return
cur_len: sizet = string.strlen(cur)
td_len: sizet = string.strlen(tdeps)
merged: str = trans.Pool.alloc(cur_len + td_len + 2)
if merged is None: return
string.strcpy(merged, cur)
merged[cur_len] = ' '
string.strcpy(merged + cur_len + 1, tdeps)
trans._imported_modules = merged
def _record_spec_trigger(self, pool: memhub.MemBuddy*, spec_name: str, trigger_sha1: str) -> int:
"""Record the triggering file SHA1 of a specialized generic class for later reset."""
if spec_name is None or trigger_sha1 is None: return 0
entry_rt: GenTypes.StructEntry* = self.Gen.StructGen.find_struct(spec_name)
if entry_rt is None: return 0
if string.strcmp(entry_rt.Name, spec_name) != 0: return 0
ts_len_r: sizet = string.strlen(trigger_sha1)
ts_buf_r: str = CompilerFactory.factory.PermPool.alloc(ts_len_r + 1)
if ts_buf_r is None: return 0
string.strcpy(ts_buf_r, trigger_sha1)
entry_rt.SpecTriggerSha1 = ts_buf_r
return 1
def _tp_names_for_spec(self, pool: memhub.MemBuddy*, class_name: str, template_cd: ast.ClassDef*) -> list[str]*:
"""Return type-param names for specialization (stdlib table first, else template node)."""
tp: list[str]* = self._find_generic_template_tp_params(pool, class_name)
if tp is not None and len(tp) > 0: return tp
if template_cd is not None and template_cd.type_params is not None and len(template_cd.type_params) > 0: return template_cd.type_params
self.Gen.TypeGenErr.fatal_error(None, f"泛型类模板 '{class_name}' 的类型参数名缺失(GenericClassTpParams 对应槽为空," f"模板节点也无 type_params)——拒绝用空泛型上下文特化", "InternalCompilerError")
return None
def _specialize_generic_class(self, trans: HT.Translator*, class_name: str, type_args: list[str]*) -> str:
"""Specialize a generic class and return the specialized class name."""
if trans is None or class_name is None or type_args is None: return None
# Generic-loop guard: increment recursion depth and abort once the limit is exceeded (prevents
# F-bounded etc. from looping forever). Every return path must decrement the depth first.
self.GenericSpecDepth += 1
# Specialization trigger sha1: trans.ModuleSha1 is the triggering file on entry but is switched
# to the template sha1 below; used to reset the spec class's MethodsEmitted on invalidation.
spec_trigger_sha1: str = trans.ModuleSha1
if self.GenericSpecDepth > GENERIC_SPEC_MAX_DEPTH:
err_spec: str = trans.Pool.alloc(256)
if err_spec is not None:
viperlib.snprintf(err_spec, 256, "泛型特化递归过深(疑似泛型循环套用),类: %s 深度=%d", class_name, self.GenericSpecDepth)
self.Gen.TypeGenErr.fatal_error(None, err_spec, "InternalCompilerError")
self.Gen.TypeGenErr.fatal_error(None, "泛型特化递归过深(疑似泛型循环套用)", "InternalCompilerError")
# Defensive: unresolved type-parameter names (e.g. "T") left in type_args are replaced with the
# concrete type name to avoid ndarray[T] double-recursion crashes; in-place edits are side-effect free.
ta_cnt_g: sizet = len(type_args)
ta_i_g: sizet
for ta_i_g in range(ta_cnt_g):
ta_raw_g: str = type_args.get(ta_i_g)
if ta_raw_g is not None:
ta_rsv_g: str = self.Gen.TypeGenBase.resolve_generic_type_name(ta_raw_g)
if ta_rsv_g is not None and string.strcmp(ta_raw_g, ta_rsv_g) != 0: type_args.set(ta_i_g, ta_rsv_g)
pool: memhub.MemBuddy* = trans.Pool
# 1. Find the generic template
template_cd: ast.ClassDef* = self._find_generic_template(class_name)
if template_cd is None:
self.GenericSpecDepth -= 1
return None
# 2. Generate the specialized name (the single generator normalizes args: bare/prefixed converge)
_from_imports: str = None
if trans is not None: _from_imports = trans._from_imports
spec_name: str = self._mangle_generic_class_name(pool, class_name, type_args, _from_imports)
if spec_name is None:
self.GenericSpecDepth -= 1
return None
# Phase B back-fill of the specialized class's method bodies (unified entry, before cache/existing
# checks): Phase 1a only registered the struct, so Phase B re-entering emits the defines.
if trans._declare_only == 0:
pre_existing: GenTypes.StructEntry* = self.Gen.StructGen.find_struct(spec_name)
if pre_existing is not None and pre_existing.MethodsEmitted == 0:
# Re-entrancy guard: set MethodsEmitted=1 first so method/return-type annotations cannot
# re-trigger specialization (infinite recursion -> OOM); _translate_oop_methods must get reemit=1.
pre_existing.MethodsEmitted = 1
# Take type_params from the stdlib deep-copy table (the template node's pool list may be invalid across phases)
tp_names_pre: list[str]* = self._tp_names_for_spec(pool, class_name, template_cd)
spec_cd_pre: ast.ClassDef* = self._clone_classdef_for_spec(pool, template_cd, spec_name, tp_names_pre, type_args)
if spec_cd_pre is not None:
trans.GenericTypeParamNames = tp_names_pre
trans.GenericTypeArgs = type_args
self.Gen.TypeGenBase.set_generic_context(tp_names_pre, type_args)
saved_ms_pre: str = trans.ModuleSha1
saved_imp_pre: str = trans._imported_modules
template_sha1_pre: str = self._find_generic_template_sha1(class_name)
if template_sha1_pre is not None:
trans.ModuleSha1 = template_sha1_pre
self._merge_template_imports(trans, template_sha1_pre)
# Error reporting: the back-filled body is a cloned template, so switch CurrentFile to
# the template file and push a trace frame (restored after translation).
saved_cur_pre: str = self.Gen.TypeGenResolve.get_current_file()
tpl_file_pre: str = self._find_generic_template_file(class_name)
if tpl_file_pre is not None:
self.Gen.TypeGenResolve.set_current_file(tpl_file_pre)
self.Gen.TypeGenBase.push_trace(tpl_file_pre, template_cd.lineno)
self.Gen.ClassGenVTable._translate_oop_methods(trans, spec_cd_pre, pre_existing.Ty, spec_name, 0, 1)
if tpl_file_pre is not None: self.Gen.TypeGenBase.pop_trace()
self.Gen.TypeGenResolve.set_current_file(saved_cur_pre)
trans.ModuleSha1 = saved_ms_pre
trans._imported_modules = saved_imp_pre
trans.GenericTypeParamNames = None
trans.GenericTypeArgs = None
self.Gen.TypeGenBase.clear_generic_context()
# 3. Check the cache (spec_key includes all type args, avoiding multi-param cache collisions)
# Allocate a new buffer to avoid in-place strcat overflow
sk_n: sizet = len(type_args)
sk_total: sizet = string.strlen(class_name) + 2 # "<" and ">"
sk_i: sizet
for sk_i in range(sk_n):
if sk_i > 0:
sk_total += 1 # ","
sk_ta: str = type_args.get(sk_i)
if sk_ta is not None: sk_total += string.strlen(sk_ta)
sk_total += 1 # NUL
spec_key: str = pool.alloc(sk_total)
if spec_key is None:
self.GenericSpecDepth -= 1
return None
spec_key[0] = '\0'
string.strcat(spec_key, class_name)
string.strcat(spec_key, "<")
for sk_i in range(sk_n):
if sk_i > 0: string.strcat(spec_key, ",")
sk_ta: str = type_args.get(sk_i)
if sk_ta is not None: string.strcat(spec_key, sk_ta)
string.strcat(spec_key, ">")
cached: str = self._find_cached_spec(spec_key)
if cached is not None:
self._record_spec_trigger(pool, spec_name, spec_trigger_sha1)
self.GenericSpecDepth -= 1
return cached
# 4. Check whether it is already registered (Phase B duplicate specialization)
existing: GenTypes.StructEntry* = self.Gen.StructGen.find_struct(spec_name)
if existing is not None:
self._cache_spec(pool, spec_key, spec_name)
self._record_spec_trigger(pool, spec_name, spec_trigger_sha1)
self.GenericSpecDepth -= 1
return spec_name
# Pre-register in the cache (prevents recursion)
self._cache_spec(pool, spec_key, spec_name)
# 5. Clone the ClassDef node
# Take type_params from the stdlib deep-copy table (the template node's pool list may be invalid across phases)
tp_names: list[str]* = self._tp_names_for_spec(pool, class_name, template_cd)
spec_cd: ast.ClassDef* = self._clone_classdef_for_spec(pool, template_cd, spec_name, tp_names, type_args)
if spec_cd is None:
self.GenericSpecDepth -= 1
return None
# 6. Set the type_map context (used for T replacement in method bodies)
trans.GenericTypeParamNames = tp_names
trans.GenericTypeArgs = type_args
self.Gen.TypeGenBase.set_generic_context(tp_names, type_args)
# 6.5 Context self-check (fail-fast, right after setting): the type-param name list and type args
# must correspond one-to-one, each resolving to a concrete type; otherwise T fails with NameError.
tp_n_c: sizet = len(tp_names) if tp_names is not None else 0
ta_n_c: sizet = len(type_args) if type_args is not None else 0
if tp_n_c == 0 or ta_n_c != tp_n_c:
self.Gen.TypeGenErr.fatal_error(None, f"泛型类 '{class_name}' 的类型实参个数({ta_n_c})与模板类型形参个数({tp_n_c})不匹配," f"无法建立特化上下文(特化键 {spec_key})", "InternalCompilerError")
probe_tp: str = tp_names.get(0)
if probe_tp is not None:
if string.strcmp(self.Gen.TypeGenBase.resolve_generic_type_name(probe_tp), probe_tp) == 0:
self.Gen.TypeGenErr.fatal_error(None, f"泛型类 '{class_name}' 的特化上下文装不上:类型形参 '{probe_tp}' 解析不到具体类型" f"(特化键 {spec_key})——类型实参缺失/为空", "InternalCompilerError")
# 7. Translate the specialized class: switch trans.ModuleSha1 to the template-defining module so
# struct type names and method symbols use its SHA1 prefix; also merge its imports.
saved_module_sha1: str = trans.ModuleSha1
saved_imported: str = trans._imported_modules
# Save/restore the class-definition window (CurDefModuleSha1/CurDefClassName) alongside ModuleSha1:
# translate_class_def overwrites it, and restoring only ModuleSha1 splits one spec into two names.
saved_def_mod: str = self.Gen.StructGen.CurDefModuleSha1
saved_def_cls: str = self.Gen.StructGen.CurDefClassName
template_sha1: str = self._find_generic_template_sha1(class_name)
if template_sha1 is not None:
trans.ModuleSha1 = template_sha1
self._merge_template_imports(trans, template_sha1)
# Error reporting: spec_cd is cloned from the template, so method node.lineno lines belong to the
# template file; switch CurrentFile to it and push a trace frame (restored after translation).
saved_cur_full: str = self.Gen.TypeGenResolve.get_current_file()
tpl_file_full: str = self._find_generic_template_file(class_name)
if tpl_file_full is not None:
self.Gen.TypeGenResolve.set_current_file(tpl_file_full)
self.Gen.TypeGenBase.push_trace(tpl_file_full, template_cd.lineno)
self.Gen.ClassGenDef.translate_class_def(trans, spec_cd)
if tpl_file_full is not None: self.Gen.TypeGenBase.pop_trace()
self.Gen.TypeGenResolve.set_current_file(saved_cur_full)
# Restore the original ModuleSha1 / imports / class-definition window (the window does not shift; see above)
trans.ModuleSha1 = saved_module_sha1
trans._imported_modules = saved_imported
self.Gen.StructGen.CurDefModuleSha1 = saved_def_mod
self.Gen.StructGen.CurDefClassName = saved_def_cls
# 8. Clear the type_map context
trans.GenericTypeParamNames = None
trans.GenericTypeArgs = None
self.Gen.TypeGenBase.clear_generic_context()
# Specialization field-name integrity check (fail-fast: a missing field name means a corrupted
# field table, and silently continuing would make later GEPs use wrong indices).
if string.strcmp(class_name, "GSListNode") == 0:
result_entry: GenTypes.StructEntry* = self.Gen.StructGen.find_struct(spec_name)
if result_entry is not None:
for ri in range(result_entry.FieldCount):
rfe: GenTypes.FieldEntry* = self.Gen.StructGen._get_field_entry(result_entry, ri)
if rfe is not None:
rfn: str = self.Gen.StructGen.get_field_name_ptr(rfe)
if rfn is None:
VLogger.error("SPEC 字段名缺失(字段表损坏)", "SPEC")
self.Gen.TypeGenErr.fatal_error(None, "SPEC 字段名缺失(字段表损坏)", "TypeError")
self._record_spec_trigger(pool, spec_name, spec_trigger_sha1)
self.GenericSpecDepth -= 1
return spec_name