fix(gen): 类字段单参CArray静态数组推断+列表默认值深拷贝;G3泛型循环防护与before_init去重;f-string/ctraits支持

This commit is contained in:
TermiNexus committed 2026-08-12 01:27:13 +08:00
1 parent a649e4807d
commit 4fd331dc08
9 files changed
+791 -2

No files matched your search

+36
View File
@@ -22,6 +22,7 @@ import lib.core.Handles.HandlesClassDef as HandlesClassDef
import lib.core.Handles.HandlesFunctions as HandlesFunctions
import lib.core.StubMerger as StubMerger
import lib.core.Handles.HandlesExprCall as HandlesExprCall
import lib.core.Handles.HandlesAnnAssign as HandlesAnnAssign
# ============================================================
@@ -1918,6 +1919,21 @@ class CallGenMixin:
continue
if fe.DefaultVal is None:
continue # 合理跳过:无默认值的字段
# 静态数组字段 + 列表字面量默认值 → 整体置零 + 逐元素 GEP+store
# (与 _generate_before_init 一致;translate_value 会把列表字面量
# 误译成动态 list 对象指针,coerce 到 [N x elem] 会失败)。
# Mixin 静态绑定:跨 Mixin 方法必须走 HandlesAnnAssign 模块级委托。
if fe.Ty is not None and HandlesExpr.is_array_type(fe.Ty) != 0 \
and fe.DefaultVal.kind() == ast.ASTKind.List:
field_ptr_arr2: llvmlite.Value | t.CPtr = llvmlite.build_gep_struct(
builder, struct_ty, fe.Ty, tmp, fe.Index)
if field_ptr_arr2 is not None:
zero_arr2: llvmlite.Value | t.CPtr = llvmlite.ConstZero(pool, fe.Ty)
if zero_arr2 is not None:
llvmlite.build_store(builder, zero_arr2, field_ptr_arr2)
HandlesAnnAssign._init_global_array_from_list(
builder, pool, mod, field_ptr_arr2, fe.DefaultVal, trans)
continue
default_val: llvmlite.Value | t.CPtr = HandlesExpr.translate_value(
builder, pool, mod, fe.DefaultVal, None, 0, trans)
if default_val is None:
@@ -4803,6 +4819,26 @@ class CallGenMixin:
if sup_nm.id is not None and string.strcmp(sup_nm.id, "super") == 0:
return self._translate_super_call(
pool, builder, mod, cl, func_name, trans)
# ctraits 宏库编译期谓词:ctraits.isptr(x) 不生成运行时调用,
# 直接按变量存储类型返回编译期常量(CIf 求值失败回退或非 CIf 上下文时兜底,
# 避免生成跨模块调用 ctraits.isptr 触发"模块未注册 SHA1"误导性报错)
if func_name is not None and cl.func is not None and cl.func.kind() == ast.ASTKind.Attribute:
ct_at: ast.Attribute | t.CPtr = (ast.Attribute | t.CPtr)(cl.func)
if ct_at.value is not None and ct_at.value.kind() == ast.ASTKind.Name:
ct_nm: ast.Name | t.CPtr = (ast.Name | t.CPtr)(ct_at.value)
if ct_nm.id is not None and string.strcmp(ct_nm.id, "ctraits") == 0 \
and string.strcmp(func_name, "isptr") == 0:
ct_args: list[ast.AST | t.CPtr] | t.CPtr = cl.args
if ct_args is not None and ct_args.__len__() >= 1:
ct_arg0: ast.AST | t.CPtr = ct_args.get(0)
if ct_arg0 is not None and ct_arg0.kind() == ast.ASTKind.Name:
ct_nm0: ast.Name | t.CPtr = (ast.Name | t.CPtr)(ct_arg0)
if ct_nm0.id is not None and trans is not None:
ct_r: int = HandlesType.eval_ctraits_isptr(trans.SymTab, ct_nm0.id)
if ct_r > 0:
return llvmlite.const_int32(pool, 1)
return llvmlite.const_int32(pool, 0)
if func_name is None:
# 检查是否是 list[int](pool) 形式的泛型实例化调用
# Subscript 节点作为 Call func: 泛型类构造函数
+63
View File
@@ -13,6 +13,7 @@ import lib.core.Handles.HandlesType as HandlesType
import lib.core.Handles.HandlesStruct as HandlesStruct
import lib.core.Handles.HandlesVar as HandlesVar
import lib.core.Handles.HandlesEnum as HandlesEnum
import lib.core.Handles.HandlesAnnAssign as HandlesAnnAssign
import lib.core.StubMerger as StubMerger
@@ -56,6 +57,10 @@ META_KEY_LEN: t.CDefine = 128
# 泛型特化缓存容量上限(与 HandlesClassDef.SPEC_CACHE_MAX 一致)
SPEC_CACHE_MAX: t.CDefine = 512
# 泛型特化递归深度上限(泛型循环套用防护)
# 正常嵌套特化深度 ≤ 3(如 list[list[str]]);64 远超合理值,命中即循环
GENERIC_SPEC_MAX_DEPTH: t.CDefine = 64
# vtable 方法最大数量(与 HandlesClassDef.VTBL_METHOD_MAX 一致)
VTBL_METHOD_MAX: t.CDefine = 32
@@ -92,6 +97,11 @@ class ClassGenMixin:
SpecNames: t.CSizeT | t.CPtr
SpecCount: int
# --- 泛型特化递归深度(泛型循环套用防护) ---
# _specialize_generic_class 进入时 +1、退出时 -1;超过上限立即报错终止
# (防止 F-bounded 等循环特化把编译变成死循环)
GenericSpecDepth: int
# --- 泛型模板表(原 HandlesClassDef._generic_class_names/
# _generic_class_nodes/_generic_class_sha1s) ---
GenericClassNames: list[str] | t.CPtr
@@ -2031,6 +2041,17 @@ class ClassGenMixin:
"""
if trans is None or class_name is None or type_args is None:
return None
# 泛型循环套用防护:递归深度 +1,超过上限立即报错终止(防 F-bounded
# 等循环特化把编译变死循环)。所有返回路径都必须 -1(见各 return 前)。
self.GenericSpecDepth += 1
if self.GenericSpecDepth > GENERIC_SPEC_MAX_DEPTH:
err_spec: t.CChar | t.CPtr = trans.Pool.alloc(256)
if err_spec is not None:
viperlib.snprintf(err_spec, 256,
"泛型特化递归过深(疑似泛型循环套用),类: %s 深度=%d",
class_name, self.GenericSpecDepth)
HandlesType.fatal_error(None, err_spec, "InternalCompilerError")
HandlesType.fatal_error(None, "泛型特化递归过深(疑似泛型循环套用)", "InternalCompilerError")
# 防御:泛型上下文内收集的 type_args 若残留未替换的类型参数名(如 "T"),
# 统一替换为具体类型名,避免 ndarray[T] 二次递归特化崩溃。
# 调用方传入的 type_args 均为临时新建列表,就地替换无共享副作用。
@@ -2069,11 +2090,13 @@ class ClassGenMixin:
if template_cd is None:
if string.strcmp(class_name, "GSListNode") == 0:
VLogger.debug("template NOT FOUND, return None", "SPEC")
self.GenericSpecDepth -= 1
return None
# 2. 生成特化名
spec_name: str = self._mangle_generic_class_name(pool, class_name, type_args)
if spec_name is None:
self.GenericSpecDepth -= 1
return None
# 治本修复:Phase B 补译泛型特化类方法体(统一入口,在 cache/existing 检查之前)
@@ -2122,6 +2145,7 @@ class ClassGenMixin:
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)
@@ -2140,6 +2164,7 @@ class ClassGenMixin:
if fb is not None:
viperlib.snprintf(fb, 1024, "CACHE HIT, return cached=%s", cached)
VLogger.debug(fb, "SPEC")
self.GenericSpecDepth -= 1
return cached
# 4. 检查是否已注册(Phase B 重复特化)
@@ -2152,6 +2177,7 @@ class ClassGenMixin:
spec_name, existing.FieldCount)
VLogger.debug(fb, "SPEC")
self._cache_spec(pool, spec_key, spec_name)
self.GenericSpecDepth -= 1
return spec_name
# 预注册缓存(防止递归)
@@ -2162,6 +2188,7 @@ class ClassGenMixin:
spec_cd: ast.ClassDef | t.CPtr = 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. 设置 type_map 上下文(用于方法体中的 T 替换)
@@ -2216,6 +2243,7 @@ class ClassGenMixin:
else:
VLogger.debug("DONE but result NOT FOUND in struct table!", "SPEC")
self.GenericSpecDepth -= 1
return spec_name
# ============================================================
@@ -2469,6 +2497,14 @@ class ClassGenMixin:
# 解析字段类型
field_ty: llvmlite.LLVMType | t.CPtr = HandlesType.resolve_annotation_type(
pool, aa.annotation, trans._imported_modules, trans._from_imports, trans)
# 单参 t.CArray[elem] + 列表字面量 → 静态数组 [N x elem]
# (与局部 AnnAssign 路径一致,对齐 TPC ParseListAnnotation 语义):
# 否则字段保持 Ptr(elem)(如 t.CArray[str] → i8**),
# for x in self.field 会误走指针迭代(元素非整数 → null 终止符
# 检查失败报错)。推断为静态数组后走数组迭代(按长度 N)。
if field_ty is not None:
field_ty = HandlesAnnAssign._infer_static_array_ty_from_literal(
pool, aa.annotation, aa.value, field_ty)
if field_ty is None:
# BUG 5 方案1修改点1:消除 i32 静默回退
# Phase A-pre(declare_only=1):依赖的类可能尚未注册,延迟到下一遍
@@ -3257,6 +3293,14 @@ class ClassGenMixin:
self._collect_vtable_methods(trans, cd, class_name)
return 0
# 防重复生成:MethodsEmitted==1 说明方法体/__before_init__ 已生成过。
# 场景:_specialize_generic_class 的 Phase B 补译分支(MethodsEmitted==0
# 时补译方法体并置 1)之后,同一特化类又经 translate_class_def 全量
# 翻译进入本方法 → 重复生成 __before_init__ → ld.lld duplicate symbol。
# 已生成过则直接跳过(方法+__before_init__ 都产出过,跳过安全)。
if oop_entry is not None and oop_entry.MethodsEmitted == 1:
return 0
# 防重入:方法体翻译前先标记 MethodsEmitted=1。
# 根因:若等全部方法译完再标记,翻译 __iter__(返回 list[T]|t.CPtr)时,
# 返回类型注解会再次触发 _specialize_generic_class,此时 MethodsEmitted
@@ -3775,6 +3819,25 @@ class ClassGenMixin:
bi_entry, fi)
if fe is None or fe.DefaultVal is None:
continue
# 静态数组字段 + 列表字面量默认值 → 整体置零 + 逐元素 GEP+store
# (与局部 AnnAssign 数组初始化一致;translate_value 会把列表字面量
# 误译成动态 list 对象指针,coerce 到 [N x elem] 会失败)。
# 单参 CArray 字段(如 t.CArray[str] = ["py", ...])经类体字段收集
# 推断为静态数组,此处保证 __before_init__ 填充真实字符串指针。
if fe.Ty is not None and HandlesExpr.is_array_type(fe.Ty) != 0 \
and fe.DefaultVal.kind() == ast.ASTKind.List:
field_ptr_arr: llvmlite.Value | t.CPtr = llvmlite.build_gep_struct(
func_builder, struct_ty, fe.Ty, self_val, fe.Index)
if field_ptr_arr is not None:
zero_arr_f: llvmlite.Value | t.CPtr = llvmlite.ConstZero(pool, fe.Ty)
if zero_arr_f is not None:
llvmlite.build_store(func_builder, zero_arr_f, field_ptr_arr)
# Mixin 静态绑定:self._xxx 只沿本类查找,跨 Mixin 方法
# (AssignGenMixin._init_global_array_from_list)会生成不存在
# 符号 → 必须走 HandlesAnnAssign 模块级委托(延迟 import Gen)。
HandlesAnnAssign._init_global_array_from_list(
func_builder, pool, mod, field_ptr_arr, fe.DefaultVal, trans)
continue
# 翻译默认值表达式
default_val: llvmlite.Value | t.CPtr = HandlesExpr.translate_value(
func_builder, pool, mod, fe.DefaultVal, None, 0, trans)
+476
View File
@@ -38,6 +38,17 @@ DOC_MAX: t.CDefine = 512
DOC_KEY_SZ: t.CDefine = 192
DOC_VAL_SZ: t.CDefine = 4096
# ============================================================
# f-string 翻译常量(对齐 TPC HandlesExprFormat)
#
# FSTR_BUF_SIZE: 运行时栈缓冲大小(snprintf 结果缓冲)
# FSTR_SPEC_MAX: 编译期 format_spec 解析缓冲宽度
# FSTR_FMT_MAX: 编译期 C printf 格式串缓冲宽度
# ============================================================
FSTR_BUF_SIZE: t.CDefine = 1024
FSTR_SPEC_MAX: t.CDefine = 64
FSTR_FMT_MAX: t.CDefine = 64
# ============================================================
# ExprGenMixin - 表达式处理 Mixin(对象化 P3,wiki/14-oo-refactor.md)
@@ -925,8 +936,473 @@ class ExprGenMixin:
# 模块级委托:translate_lambda 定义在 FuncGenMixin(跨 Mixin
# 方法不能 self. 直接调用,见 HandlesExpr.translate_lambda)
return HandlesExpr.translate_lambda(builder, pool, mod, node, trans)
elif k == ast.ASTKind.JoinedStr:
return self.translate_joined_str(builder, pool, mod, node, trans)
elif k == ast.ASTKind.FormattedValue:
# 解析器对 f"{expr:spec}"(单 FormattedValue 无字面量)返回裸 FormattedValue
# 而非 JoinedStr(见 ast/parser.vp _parse_fstring),需单独翻译
return self.translate_formatted_value(builder, pool, mod, node, trans)
return None
# ============================================================
# translate_joined_str - f-string 翻译(对齐 TPC _HandleJoinedStrLlvm)
#
# f"..."(JoinedStr)→ 拼接字面量与格式说明符 → snprintf 生成 i8*
# 栈缓冲。之前 f-string 未翻译导致 print/赋值拿到 NULL 指针
# ("(null)"输出 + printf("%s", 垃圾) 崩溃 0xC0000005)。
# ============================================================
def translate_joined_str(self, builder: llvmlite.IRBuilder | t.CPtr,
pool: memhub.MemBuddy | t.CPtr,
mod: llvmlite.LLVMModule | t.CPtr,
node: ast.AST | t.CPtr,
trans: HT.Translator | t.CPtr = None) -> llvmlite.Value | t.CPtr:
"""f-string → snprintf 生成 i8* 栈缓冲"""
if node is None or builder is None or trans is None:
return None
js: ast.JoinedStr | t.CPtr = (ast.JoinedStr | t.CPtr)(node)
if js is None or js.values is None:
return None
# 编译期格式串缓冲(pool 分配)
fmt_buf: t.CChar | t.CPtr = pool.alloc(FSTR_BUF_SIZE)
if fmt_buf is None:
return None
fmt_buf[0] = '\0'
# 运行时参数链表(Value.Next 链接,与 printf 调用一致)
arg_head: llvmlite.Value | t.CPtr = None
arg_tail: llvmlite.Value | t.CPtr = None
arg_count: int = 0
parts: list[ast.AST | t.CPtr] | t.CPtr = js.values
pn: t.CSizeT = parts.__len__()
for pi in range(pn):
part: ast.AST | t.CPtr = parts.get(pi)
if part is None:
continue
if part.kind() == ast.ASTKind.Constant:
pcn: ast.Constant | t.CPtr = (ast.Constant | t.CPtr)(part)
if pcn.const_kind == ast.CONST_STR and pcn.str_val is not None:
self._fstr_append_literal(fmt_buf, pcn.str_val)
continue
if part.kind() != ast.ASTKind.FormattedValue:
continue
fv: ast.FormattedValue | t.CPtr = (ast.FormattedValue | t.CPtr)(part)
if fv.value is None:
continue
val: llvmlite.Value | t.CPtr = self.translate_value(
builder, pool, mod, fv.value, None, 0, trans)
if val is None:
self._fstr_append_raw(fmt_buf, "(null)")
continue
# 确定 C 格式说明符(format_spec 优先,否则按值类型推断)
c_fmt: str = None
if fv.format_spec is not None:
c_fmt = self._fstr_parse_format_spec(pool, fv.format_spec, val, fv.value)
if c_fmt is None:
c_fmt = self._fstr_infer_c_fmt(pool, val, fv.value, trans)
if c_fmt is None:
c_fmt = "%d"
# 追加格式说明符
if string.strlen(fmt_buf) + string.strlen(c_fmt) < FSTR_BUF_SIZE - 1:
string.strcat(fmt_buf, c_fmt)
# 转换参数类型(i8/i16→i32、bool→i32、f32→f64)
cast_val: llvmlite.Value | t.CPtr = self._fstr_cast_value(
builder, pool, val, c_fmt, fv.value, trans)
if cast_val is None:
cast_val = val
cast_val.Next = None
if arg_head is None:
arg_head = cast_val
else:
arg_tail.Next = cast_val
arg_tail = cast_val
arg_count += 1
return self._fstr_finalize(builder, pool, mod, fmt_buf, arg_head, arg_tail, arg_count, trans)
def translate_formatted_value(self, builder: llvmlite.IRBuilder | t.CPtr,
pool: memhub.MemBuddy | t.CPtr,
mod: llvmlite.LLVMModule | t.CPtr,
node: ast.AST | t.CPtr,
trans: HT.Translator | t.CPtr = None) -> llvmlite.Value | t.CPtr:
"""翻译裸 FormattedValue(f"{expr:spec}" 无字面量时解析器返回 FormattedValue 而非 JoinedStr)"""
if node is None or builder is None or trans is None:
return None
fv: ast.FormattedValue | t.CPtr = (ast.FormattedValue | t.CPtr)(node)
if fv is None or fv.value is None:
return None
fmt_buf: t.CChar | t.CPtr = pool.alloc(FSTR_BUF_SIZE)
if fmt_buf is None:
return None
fmt_buf[0] = '\0'
arg_head: llvmlite.Value | t.CPtr = None
arg_tail: llvmlite.Value | t.CPtr = None
arg_count: int = 0
val: llvmlite.Value | t.CPtr = self.translate_value(
builder, pool, mod, fv.value, None, 0, trans)
if val is None:
self._fstr_append_raw(fmt_buf, "(null)")
else:
c_fmt: str = None
if fv.format_spec is not None:
c_fmt = self._fstr_parse_format_spec(pool, fv.format_spec, val, fv.value)
if c_fmt is None:
c_fmt = self._fstr_infer_c_fmt(pool, val, fv.value, trans)
if c_fmt is None:
c_fmt = "%d"
if string.strlen(fmt_buf) + string.strlen(c_fmt) < FSTR_BUF_SIZE - 1:
string.strcat(fmt_buf, c_fmt)
cast_val: llvmlite.Value | t.CPtr = self._fstr_cast_value(
builder, pool, val, c_fmt, fv.value, trans)
if cast_val is None:
cast_val = val
cast_val.Next = None
arg_head = cast_val
arg_tail = cast_val
arg_count = 1
return self._fstr_finalize(builder, pool, mod, fmt_buf, arg_head, arg_tail, arg_count, trans)
def _fstr_finalize(self, builder: llvmlite.IRBuilder | t.CPtr,
pool: memhub.MemBuddy | t.CPtr,
mod: llvmlite.LLVMModule | t.CPtr,
fmt_buf: t.CChar | t.CPtr,
arg_head: llvmlite.Value | t.CPtr,
arg_tail: llvmlite.Value | t.CPtr,
arg_count: int,
trans: HT.Translator | t.CPtr) -> llvmlite.Value | t.CPtr:
"""f-string 尾部:全局格式串 + 固定栈缓冲 + snprintf,返回 i8*"""
i32_ty: llvmlite.LLVMType | t.CPtr = llvmlite.Int32(pool)
i8_ty: llvmlite.LLVMType | t.CPtr = llvmlite.Int8(pool)
if fmt_buf is None or fmt_buf[0] == '\0':
# 空 f-string(f"")→ 空字符串常量
return HandlesExpr.create_global_string(builder, pool, mod, "", trans)
# 格式串 → 全局字符串常量(i8*)
fmt_ptr: llvmlite.Value | t.CPtr = HandlesExpr.create_global_string(
builder, pool, mod, fmt_buf, trans)
if fmt_ptr is None:
return None
# 固定栈缓冲 [FSTR_BUF_SIZE x i8],GEP(0,0) 取 i8*
buf_ty: llvmlite.LLVMType | t.CPtr = llvmlite.Array(pool, i8_ty, FSTR_BUF_SIZE)
buf_alloca: llvmlite.Value | t.CPtr = llvmlite.build_alloca(builder, buf_ty)
if buf_alloca is None:
return None
zero_i32: llvmlite.Value | t.CPtr = llvmlite.const_int32(pool, 0)
buf_ptr: llvmlite.Value | t.CPtr = llvmlite.build_gep_array(
builder, buf_ty, i8_ty, buf_alloca, zero_i32)
if buf_ptr is None:
return None
# snprintf(buf, FSTR_BUF_SIZE, fmt, args...)
size_val: llvmlite.Value | t.CPtr = llvmlite.const_int64(pool, FSTR_BUF_SIZE)
buf_ptr.Next = size_val
size_val.Next = fmt_ptr
fmt_ptr.Next = arg_head
if arg_tail is not None:
arg_tail.Next = None
llvmlite.build_call(builder, "snprintf", buf_ptr, 3 + arg_count, i32_ty, 1)
return buf_ptr
def _fstr_append_literal(self, fmt_buf: t.CChar | t.CPtr,
lit: str) -> None:
"""追加字面量到格式串(% 转义为 %%)"""
if fmt_buf is None or lit is None:
return
cur_len: t.CSizeT = string.strlen(fmt_buf)
rem: t.CSizeT = FSTR_BUF_SIZE - 1 - cur_len
if rem == 0:
return
src_len: t.CSizeT = string.strlen(lit)
si: t.CSizeT = 0
while si < src_len and rem > 0:
ch: t.CChar = lit[si]
if ch == '%':
if rem < 2:
break
fmt_buf[cur_len] = '%'
fmt_buf[cur_len + 1] = '%'
cur_len += 2
rem -= 2
else:
fmt_buf[cur_len] = ch
cur_len += 1
rem -= 1
si += 1
fmt_buf[cur_len] = '\0'
def _fstr_append_raw(self, fmt_buf: t.CChar | t.CPtr, s: str) -> None:
"""追加原始字符串(不做 % 转义)"""
if fmt_buf is None or s is None:
return
cur_len: t.CSizeT = string.strlen(fmt_buf)
src_len: t.CSizeT = string.strlen(s)
if cur_len + src_len < FSTR_BUF_SIZE:
string.strcpy(fmt_buf + cur_len, s)
def _fstr_parse_format_spec(self, pool: memhub.MemBuddy | t.CPtr,
spec_node: ast.AST | t.CPtr,
val: llvmlite.Value | t.CPtr,
arg_node: ast.AST | t.CPtr) -> str:
"""Python format_spec → C printf 格式串(pool 分配),无法解析返回 None"""
if spec_node is None or pool is None or val is None:
return None
# 1. 提取 spec 字符串(JoinedStr 仅常量拼接)
spec: t.CChar | t.CPtr = pool.alloc(FSTR_SPEC_MAX)
if spec is None:
return None
spec[0] = '\0'
sk: int = spec_node.kind()
if sk == ast.ASTKind.JoinedStr:
jss: ast.JoinedStr | t.CPtr = (ast.JoinedStr | t.CPtr)(spec_node)
if jss.values is None:
return None
vn: t.CSizeT = jss.values.__len__()
for vi in range(vn):
vpart: ast.AST | t.CPtr = jss.values.get(vi)
if vpart is None:
continue
if vpart.kind() != ast.ASTKind.Constant:
return None
vcn: ast.Constant | t.CPtr = (ast.Constant | t.CPtr)(vpart)
if vcn.const_kind != ast.CONST_STR or vcn.str_val is None:
return None
if string.strlen(spec) + string.strlen(vcn.str_val) < FSTR_SPEC_MAX - 1:
string.strcat(spec, vcn.str_val)
elif sk == ast.ASTKind.Constant:
cn: ast.Constant | t.CPtr = (ast.Constant | t.CPtr)(spec_node)
if cn.const_kind != ast.CONST_STR or cn.str_val is None:
return None
string.strncpy(spec, cn.str_val, FSTR_SPEC_MAX - 1)
else:
return None
slen: t.CSizeT = string.strlen(spec)
if slen == 0:
return None
# 2. 逐段解析: [fill]align / sign / # / 0 / width / , / .precision / type
pos: t.CSizeT = 0
align_char: t.CChar = 0
if pos < slen:
ch0: t.CChar = spec[pos]
if ch0 == '<' or ch0 == '>' or ch0 == '^' or ch0 == '=':
align_char = ch0
pos += 1
elif pos + 1 < slen:
ch1: t.CChar = spec[pos + 1]
if ch1 == '<' or ch1 == '>' or ch1 == '^' or ch1 == '=':
align_char = ch1
pos += 2
c_flags: t.CChar | t.CPtr = pool.alloc(FSTR_FMT_MAX)
c_width: t.CChar | t.CPtr = pool.alloc(32)
c_prec: t.CChar | t.CPtr = pool.alloc(32)
if c_flags is None or c_width is None or c_prec is None:
return None
c_flags[0] = '\0'
c_width[0] = '\0'
c_prec[0] = '\0'
# sign
if pos < slen and (spec[pos] == '+' or spec[pos] == '-' or spec[pos] == ' '):
if spec[pos] == '+':
string.strcat(c_flags, "+")
elif spec[pos] == ' ':
string.strcat(c_flags, " ")
pos += 1
# alt '#'
if pos < slen and spec[pos] == '#':
string.strcat(c_flags, "#")
pos += 1
# zero '0'
if pos < slen and spec[pos] == '0':
string.strcat(c_flags, "0")
pos += 1
# 左对齐 '<'/'^' → '-'(与 0 同时存在时 C 语义 '-' 优先)
if align_char == '<' or align_char == '^':
string.strcat(c_flags, "-")
# width digits
while pos < slen and spec[pos] >= '0' and spec[pos] <= '9':
wl: t.CSizeT = string.strlen(c_width)
if wl < 30:
c_width[wl] = spec[pos]
c_width[wl + 1] = '\0'
pos += 1
# comma ',' → C printf 不支持千分位,忽略
if pos < slen and spec[pos] == ',':
pos += 1
# .precision
if pos < slen and spec[pos] == '.':
string.strcat(c_prec, ".")
pos += 1
while pos < slen and spec[pos] >= '0' and spec[pos] <= '9':
pl: t.CSizeT = string.strlen(c_prec)
if pl < 30:
c_prec[pl] = spec[pos]
c_prec[pl + 1] = '\0'
pos += 1
if string.strlen(c_prec) == 1:
return None # 只有 '.' 无效
# type char
fmt_type: t.CChar = 0
if pos < slen:
fmt_type = spec[pos]
pos += 1
if pos != slen:
return None # 多余字符无法解析
# 3. 长度修饰(按值类型位宽)
len_mod: t.CChar | t.CPtr = pool.alloc(4)
if len_mod is None:
return None
len_mod[0] = '\0'
ibits: int = HandlesExpr.get_llvm_type_bits(val.Ty)
if ibits == 64:
string.strcpy(len_mod, "ll")
elif ibits == 32:
string.strcpy(len_mod, "l")
elif ibits == 16:
string.strcpy(len_mod, "h")
elif ibits == 8:
string.strcpy(len_mod, "hh")
# 4. 组装 C 格式串
out: t.CChar | t.CPtr = pool.alloc(FSTR_FMT_MAX)
if out is None:
return None
out[0] = '\0'
string.strcat(out, "%")
string.strcat(out, c_flags)
string.strcat(out, c_width)
string.strcat(out, c_prec)
if fmt_type == 0:
# 无 type:按值类型推断
fbits2: int = HandlesExpr.get_llvm_float_bits(val.Ty)
if ibits != 0:
if ibits == 8:
string.strcat(out, "c")
elif ibits == 1:
string.strcat(out, "d")
else:
string.strcat(out, len_mod)
string.strcat(out, "d")
elif fbits2 != 0:
string.strcat(out, "f")
elif HandlesExpr.is_ptr_type(val.Ty) != 0:
string.strcat(out, "s")
else:
string.strcat(out, "d")
elif fmt_type == 'c':
string.strcat(out, "c")
elif fmt_type == 'f' or fmt_type == 'F':
string.strcat(out, "f")
elif fmt_type == 'e' or fmt_type == 'E' or fmt_type == 'g' or fmt_type == 'G':
tmp_ch: t.CChar | t.CPtr = pool.alloc(2)
if tmp_ch is None:
return None
tmp_ch[0] = fmt_type
tmp_ch[1] = '\0'
string.strcat(out, tmp_ch)
elif fmt_type == 's':
string.strcat(out, "s")
elif fmt_type == '%':
string.strcat(out, "%")
elif fmt_type == 'b':
return None # 二进制不支持
elif fmt_type == 'd' or fmt_type == 'i':
string.strcat(out, len_mod)
string.strcat(out, "d")
elif fmt_type == 'x' or fmt_type == 'X' or fmt_type == 'o' or fmt_type == 'u':
string.strcat(out, len_mod)
tmp_ch2: t.CChar | t.CPtr = pool.alloc(2)
if tmp_ch2 is None:
return None
tmp_ch2[0] = fmt_type
tmp_ch2[1] = '\0'
string.strcat(out, tmp_ch2)
else:
return None
return out
def _fstr_infer_c_fmt(self, pool: memhub.MemBuddy | t.CPtr,
val: llvmlite.Value | t.CPtr,
arg_node: ast.AST | t.CPtr,
trans: HT.Translator | t.CPtr) -> str:
"""无 format_spec 时按值类型推断 C printf 格式(对齐 TPC 无 spec 分支,带 % 前缀)"""
if val is None or pool is None:
return None
out: t.CChar | t.CPtr = pool.alloc(16)
if out is None:
return None
out[0] = '\0'
ibits: int = HandlesExpr.get_llvm_type_bits(val.Ty)
fbits: int = HandlesExpr.get_llvm_float_bits(val.Ty)
if ibits == 1:
return "%d" # bool
if ibits == 8:
return "%c" # 字符
if ibits != 0:
if self._node_type_unsigned(pool, arg_node, trans) != 0:
if ibits == 64:
string.strcpy(out, "%llu")
else:
string.strcpy(out, "%u")
else:
if ibits == 64:
string.strcpy(out, "%lld")
else:
string.strcpy(out, "%d")
return out
if fbits != 0:
return "%f"
if HandlesExpr.is_ptr_type(val.Ty) != 0:
return "%s"
return "%d"
def _fstr_cast_value(self, builder: llvmlite.IRBuilder | t.CPtr,
pool: memhub.MemBuddy | t.CPtr,
val: llvmlite.Value | t.CPtr,
c_fmt: str,
arg_node: ast.AST | t.CPtr,
trans: HT.Translator | t.CPtr) -> llvmlite.Value | t.CPtr:
"""按 C 格式转换值类型(i8/i16→i32、bool→i32、f32→f64)"""
if val is None or c_fmt is None:
return None
# 找格式类型字符(最后一个字母,忽略 l/h 长度修饰)
fmt_char: t.CChar = 0
flen: t.CSizeT = string.strlen(c_fmt)
fi: t.CSizeT = flen
while fi > 0:
fi -= 1
ch: t.CChar = c_fmt[fi]
if ch == '%':
break
if (ch >= 'a' and ch <= 'z') or (ch >= 'A' and ch <= 'Z'):
if ch != 'l' and ch != 'h':
fmt_char = ch
break
i32_ty: llvmlite.LLVMType | t.CPtr = llvmlite.Int32(pool)
if fmt_char == 's':
return val
if fmt_char == 'c':
ibits: int = HandlesExpr.get_llvm_type_bits(val.Ty)
if ibits != 0 and ibits < 32:
return llvmlite.build_zext(builder, val, i32_ty)
return val
if fmt_char == 'd' or fmt_char == 'i' or fmt_char == 'x' \
or fmt_char == 'X' or fmt_char == 'o' or fmt_char == 'u':
ibits = HandlesExpr.get_llvm_type_bits(val.Ty)
if ibits == 1:
return llvmlite.build_zext(builder, val, i32_ty)
if ibits == 8 or ibits == 16:
if self._node_type_unsigned(pool, arg_node, trans) != 0:
return llvmlite.build_zext(builder, val, i32_ty)
return llvmlite.build_sext(builder, val, i32_ty)
return val
if fmt_char == 'f' or fmt_char == 'e' or fmt_char == 'E' \
or fmt_char == 'g' or fmt_char == 'G':
fbits: int = HandlesExpr.get_llvm_float_bits(val.Ty)
if fbits == 32:
return llvmlite.build_fpext(builder, val, llvmlite.Double(pool))
return val
return val
# ============================================================
# 翻译三元表达式 IfExp(test, body, orelse)
#
+2 -1
View File
@@ -602,7 +602,8 @@ class ImportsGenMixin:
if mod_name is None:
return 1
# t/c 是内部库,不参与 includes 扫描,豁免检查
if string.strcmp(mod_name, "t") == 0 or string.strcmp(mod_name, "c") == 0:
# ctraits 是纯编译期宏库(.vpi 后缀,IncludesScanner 不扫描 .vpi),豁免检查
if string.strcmp(mod_name, "t") == 0 or string.strcmp(mod_name, "c") == 0 or string.strcmp(mod_name, "ctraits") == 0:
return 1
mod_sha1: str = StubMerger.LookupSha1ByModName(mod_name)
if mod_sha1 is None:
+65
View File
@@ -474,6 +474,8 @@ class StmtGenMixin:
return cn.int_val
self.CifEvalOk = 0
return 0
if k == ast.ASTKind.Call:
return self._cif_eval_call(pool, trans, node)
if k == ast.ASTKind.Name:
nm: ast.Name | t.CPtr = (ast.Name | t.CPtr)(node)
if nm.id is None:
@@ -584,6 +586,69 @@ class StmtGenMixin:
self.CifEvalOk = 0
return 0
def _eval_ctraits_isptr(self, symtab: HandlesVar.SymbolTable | t.CPtr,
name: str) -> int:
"""ctraits.isptr(name) 编译期求值。
按变量实际存储类型判断是否指针:alloca 的 pointee 为 Ptr 即指针
(与 TPC ConstEvaluator._eval_compile_time_call 的 LLVM 类型判断一致)。
返回: 1=指针 0=非指针 -1=无法求值(变量不存在/无类型信息)。
"""
if name is None or symtab is None:
return -1
v: llvmlite.Value | t.CPtr = HandlesVar.lookup_var(symtab, name)
if v is None or v.Ty is None:
return -1
pointee: llvmlite.LLVMType | t.CPtr = v.Ty.Pointee
if pointee is not None and HandlesExpr.is_ptr_type(pointee) != 0:
return 1
return 0
def _cif_eval_call(self, pool: memhub.MemBuddy | t.CPtr,
trans: HT.Translator | t.CPtr,
node: ast.AST | t.CPtr) -> int:
"""编译期函数调用求值:ctraits.isptr(x)。
命中置 self.CifEvalOk=1 返回 1/0;未识别调用置 0 返回 0。
"""
self.CifEvalOk = 0
if node is None or trans is None:
return 0
cl: ast.Call | t.CPtr = (ast.Call | t.CPtr)(node)
if cl is None or cl.func is None:
return 0
fk: int = cl.func.kind()
if fk != ast.ASTKind.Attribute:
return 0
at: ast.Attribute | t.CPtr = (ast.Attribute | t.CPtr)(cl.func)
if at.value is None or at.attr is None:
return 0
if at.value.kind() != ast.ASTKind.Name:
return 0
vn: ast.Name | t.CPtr = (ast.Name | t.CPtr)(at.value)
if vn.id is None:
return 0
if string.strcmp(vn.id, "ctraits") != 0 or string.strcmp(at.attr, "isptr") != 0:
return 0
cargs: list[ast.AST | t.CPtr] | t.CPtr = cl.args
if cargs is None or cargs.__len__() < 1:
self.CifEvalOk = 0
return 0
arg0: ast.AST | t.CPtr = cargs.get(0)
if arg0 is None or arg0.kind() != ast.ASTKind.Name:
self.CifEvalOk = 0
return 0
nm0: ast.Name | t.CPtr = (ast.Name | t.CPtr)(arg0)
if nm0.id is None:
self.CifEvalOk = 0
return 0
r: int = self._eval_ctraits_isptr(trans.SymTab, nm0.id)
if r < 0:
self.CifEvalOk = 0
return 0
self.CifEvalOk = 1
return r
def _evaluate_cif_test(self, pool: memhub.MemBuddy | t.CPtr,
trans: HT.Translator | t.CPtr,
test: ast.AST | t.CPtr) -> int:
+55 -1
View File
@@ -317,12 +317,15 @@ class StructGenMixin:
字段默认值(fe.DefaultVal)除定义文件内 _generate_before_init 使用外,
跨文件结构体构造(_translate_struct_ctor → _apply_struct_defaults)会在
调用方文件翻译时读取——原 pool AST 已 reset 回收必须深拷贝。
非常量默认值(Name/BinOp 等引用表达式)无法安全深拷贝:返回 None
支持 Constant(标量)与 List(列表字面量,如 t.CArray[str] = ["py", "pyw"])。
其他非常量默认值(Name/BinOp 等引用表达式)无法安全深拷贝:返回 None
(调用方 fe.DefaultVal 保持 None,仅定义文件内 _generate_before_init
仍持有原始 pool 指针,不受影响)。
"""
if default_val is None:
return None
if default_val.kind() == ast.ASTKind.List:
return self._deep_copy_list_ast(default_val)
if default_val.kind() != ast.ASTKind.Constant:
return None
src_c: ast.Constant | t.CPtr = (ast.Constant | t.CPtr)(default_val)
@@ -352,6 +355,57 @@ class StructGenMixin:
dst_c.str_val = s_copy
return (ast.AST | t.CPtr)(dst_c)
def _deep_copy_list_ast(self, default_val: ast.AST | t.CPtr) -> ast.AST | t.CPtr:
"""深拷贝 List 节点(列表字面量默认值)到 stdlib,跨 1b reset 存活。
elts 容器手动在 stdlib 构造(list 的 __pool__ 置 None:深拷贝的 elts
只被只读访问 get/__len__/迭代,不 append,不会触发 pool 扩容)。
元素递归调用 _deep_copy_default_ast(支持 Constant 与嵌套 List)。
"""
src_l: ast.List | t.CPtr = (ast.List | t.CPtr)(default_val)
dst_l: ast.List | t.CPtr = (ast.List | t.CPtr)(
stdlib.malloc(ast.List.__sizeof__()))
if dst_l is None:
return None
string.memset(dst_l, 0, ast.List.__sizeof__())
# 拷贝 vtable 指针(AST 是 @t.CVTable,kind() 虚调用依赖首字段 vptr)
vt_l: t.CVoid | t.CPtr = t.CVoid(
c.Deref(t.CUInt64T(default_val, t.CPtr), t.CPtr), t.CPtr)
c.DerefAs(dst_l, vt_l)
# 拷贝位置字段与 ctx
dst_l.lineno = src_l.lineno
dst_l.col_offset = src_l.col_offset
dst_l.end_lineno = src_l.end_lineno
dst_l.end_col_offset = src_l.end_col_offset
dst_l.ctx = src_l.ctx
# 深拷贝 elts:stdlib 手动构造 list[AST | t.CPtr] 容器
src_elts_l: list[ast.AST | t.CPtr] | t.CPtr = src_l.elts
if src_elts_l is not None:
cnt_l: t.CSizeT = src_elts_l.__len__()
lst_copy_l: list[ast.AST | t.CPtr] | t.CPtr = (list[ast.AST | t.CPtr] | t.CPtr)(
stdlib.malloc(list[ast.AST | t.CPtr].__sizeof__()))
if lst_copy_l is not None:
string.memset(lst_copy_l, 0, list[ast.AST | t.CPtr].__sizeof__())
lst_copy_l.__count__ = cnt_l
lst_copy_l.__capacity__ = cnt_l
lst_copy_l.__elem_size__ = 8
lst_copy_l.__pool__ = None
data_l: t.CVoid | t.CPtr = stdlib.malloc(cnt_l * 8)
if data_l is not None:
string.memset(data_l, 0, cnt_l * 8)
lst_copy_l.__data__ = data_l
li_l: t.CSizeT
for li_l in range(cnt_l):
elem_src_l: ast.AST | t.CPtr = src_elts_l.get(li_l)
if elem_src_l is not None:
elem_dst_l: ast.AST | t.CPtr = self._deep_copy_default_ast(elem_src_l)
if elem_dst_l is not None:
slot_l: ast.AST | t.CPtr = (ast.AST | t.CPtr)(
t.CVoid(t.CUInt64T(data_l) + li_l * 8, t.CPtr))
slot_l[0] = elem_dst_l
dst_l.elts = lst_copy_l
return (ast.AST | t.CPtr)(dst_l)
def get_struct_count(self) -> int:
"""返回当前已注册的结构体数量"""
return self.StructCount
+5
View File
@@ -1608,6 +1608,11 @@ class TypeGenMixin:
if gp_tpn is not None and string.strcmp(nm.id, gp_tpn) == 0:
gp_ta: str = self.GenericTypeArgs.get(gp_i)
if gp_ta is not None:
# 自映射防护(泛型循环套用):实参与参数名相同(T→T)
# 时 _parse_type_string 会重新解析回同一个 Name → 无条件
# 自递归无退出条件 → 编译卡死。直接返回 None 让上层回落。
if string.strcmp(gp_ta, gp_tpn) == 0:
return None
# 用具体类型名递归解析(支持 BinOp/Attribute 类型实参)
gp_sub_node: ast.AST | t.CPtr = self._parse_type_string(pool, gp_ta, nm.ctx)
if gp_sub_node is not None:
+75
View File
@@ -162,6 +162,72 @@ def _is_ptr_element_annotation(annot: ast.AST | t.CPtr) -> int:
return 0
# ============================================================
# _is_single_param_carray_annot - 检查注解是否为单参 t.CArray[elem_ty]
#
# 单参形式(t.CArray[elem_ty])与 count=None 形式(t.CArray[elem_ty, None])
# 都解析为 Ptr(elem_ty)。当 RHS 是列表字面量时,按 TPC 语义应推断为
# 静态数组 [len x elem_ty](对齐 TPC ParseListAnnotation 的 is_pointer 分支:
# "单参数 list[type] → 指针,但如果有列表字面量初始化值则按数组 list[type, N] 处理")。
# ============================================================
def _is_single_param_carray_annot(annot: ast.AST | t.CPtr) -> int:
"""检查注解是否为单参/None 长度 t.CArray[...],返回 1=是 0=否"""
if annot is None or annot.kind() != ast.ASTKind.Subscript:
return 0
sub: ast.Subscript | t.CPtr = (ast.Subscript | t.CPtr)(annot)
if sub.value is None or sub.slice is None:
return 0
if sub.value.kind() != ast.ASTKind.Attribute:
return 0
at: ast.Attribute | t.CPtr = (ast.Attribute | t.CPtr)(sub.value)
if at.attr is None or string.strcmp(at.attr, "CArray") != 0:
return 0
if at.value is None or at.value.kind() != ast.ASTKind.Name:
return 0
mv: ast.Name | t.CPtr = (ast.Name | t.CPtr)(at.value)
if mv.id is None or string.strcmp(mv.id, "t") != 0:
return 0
# 单参形式:slice 非 Tuple(如 t.CArray[u64])
if sub.slice.kind() != ast.ASTKind.Tuple:
return 1
# 双参形式:count=None(t.CArray[u64, None])→ 等价单参
tup: ast.Tuple | t.CPtr = (ast.Tuple | t.CPtr)(sub.slice)
if tup.elts is None or tup.elts.__len__() < 2:
return 0
cnt_node: ast.AST | t.CPtr = tup.elts.get(1)
if cnt_node is not None and cnt_node.kind() == ast.ASTKind.Constant:
cn0: ast.Constant | t.CPtr = (ast.Constant | t.CPtr)(cnt_node)
if cn0.const_kind == ast.CONST_NONE:
return 1
return 0
# ============================================================
# _infer_static_array_ty_from_literal - 单参 CArray + 列表字面量 → 静态数组
#
# 单参 t.CArray[elem_ty] 解析为 Ptr(elem_ty),但当 RHS 是列表字面量时,
# 按 TPC 语义应推断为 [len x elem_ty](对齐 TPC ParseListAnnotation):
# 避免误走动态 list 构造(_find_pool_var 兜底引用外部 @_mbuddy → 链接期
# undefined)。PreScan 与 Handle 都调用(PreScan 预创建 alloca 的类型必须
# 与 Handle 一致,否则 get_or_create_sym 复用旧 alloca 使推断失效)。
# ============================================================
def _infer_static_array_ty_from_literal(pool: memhub.MemBuddy | t.CPtr,
annot: ast.AST | t.CPtr,
value: ast.AST | t.CPtr,
resolved_ty: llvmlite.LLVMType | t.CPtr) -> llvmlite.LLVMType | t.CPtr:
"""单参 CArray + 列表字面量 → Array(elem_ty, len);否则返回原类型"""
if value is not None and value.kind() == ast.ASTKind.List and resolved_ty is not None:
if _is_single_param_carray_annot(annot) != 0:
elem_ty_arr: llvmlite.LLVMType | t.CPtr = resolved_ty.Pointee
lst_arr: ast.List | t.CPtr = (ast.List | t.CPtr)(value)
cnt_arr: int = 0
if lst_arr.elts is not None:
cnt_arr = lst_arr.elts.__len__()
if elem_ty_arr is not None and cnt_arr > 0:
return llvmlite.Array(pool, elem_ty_arr, cnt_arr)
return resolved_ty
# ============================================================
# _init_global_array_from_list — 从列表字面量初始化全局数组
#
@@ -279,6 +345,10 @@ class AnnAssignHandle(HandlesBase.Mixin):
pool, annot, self.Trans._imported_modules, self.Trans._from_imports, self.Trans)
if resolved is not None:
var_ty = resolved
# 单参 t.CArray[elem_ty] + 列表字面量 → 静态数组(与 Handle 推断一致)。
# PreScan 预创建的 alloca 类型必须与 Handle 期望一致,否则 Handle 的
# get_or_create_sym 复用旧 alloca,数组推断失效仍走动态 list → _mbuddy。
var_ty = _infer_static_array_ty_from_literal(pool, annot, aa.value, var_ty)
else:
# fast-fail: 注解类型解析失败,Python traceback 立即终止(不静默回退 i32)
HandlesType.fatal_error(
@@ -440,6 +510,11 @@ class AnnAssignHandle(HandlesBase.Mixin):
llvmlite.build_store(builder, rhs_val_mg, mod_glob)
return 0
# 单参 t.CArray[elem_ty] + 列表字面量 → 按静态数组处理(count 从字面量长度推断)
# 与 PreScan 推断一致:get_or_create_sym 复用 PreScan 创建的 alloca,
# 类型一致才能命中局部数组快路径,避免 _find_pool_var 兜底引用 @_mbuddy。
var_ty = _infer_static_array_ty_from_literal(pool, annot, aa.value, var_ty)
# 普通局部变量
# 创建 alloca
alloca: llvmlite.Value | t.CPtr = HandlesVar.get_or_create_sym(
+14
View File
@@ -13,6 +13,7 @@ import lib.core.VLogger as VLogger
import lib.core.Handles.HandlesTranslator as HT
import lib.core.Handles.HandlesImports as HandlesImports
import lib.core.Handles.HandlesStruct as HandlesStruct
import lib.core.Handles.HandlesVar as HandlesVar
# ============================================================
@@ -174,6 +175,19 @@ def lookup_cdefine_double(name: str) -> double:
return 0.0
def eval_ctraits_isptr(symtab: HandlesVar.SymbolTable | t.CPtr,
name: str) -> int:
"""ctraits.isptr(name) 编译期求值(委托到 gen._eval_ctraits_isptr)。
返回: 1=指针 0=非指针 -1=无法求值。
"""
import lib.core.Gen.Generator as Generator
g: Generator.Generator | t.CPtr = (Generator.Generator | t.CPtr)(t.CVoid(_g_gen, t.CPtr))
if g is not None:
return g._eval_ctraits_isptr(symtab, name)
return -1
def is_cdefine_double() -> int:
"""检查最近一次 CDefine 查找命中的是否是浮点常量(委托到 gen.is_cdefine_double)"""
import lib.core.Gen.Generator as Generator