与 includes 同步:<= 条件转 range(expr + 1),并放宽初始化行的紧贴要求(同块内最近一次写即为初始化即可)。其余保守守卫不变。 验证:bootstrap 三重重编 SUCCESS;全量 77/77 顺序回归通过。
1017 lines
59 KiB
Plaintext
1017 lines
59 KiB
Plaintext
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 |