diff --git a/App/lib/core/Gen/CallGenMixin.vp b/App/lib/core/Gen/CallGenMixin.vp index 6f9ee1b..ea58e1e 100644 --- a/App/lib/core/Gen/CallGenMixin.vp +++ b/App/lib/core/Gen/CallGenMixin.vp @@ -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: 泛型类构造函数 diff --git a/App/lib/core/Gen/ClassGenMixin.vp b/App/lib/core/Gen/ClassGenMixin.vp index fdfb20f..e5c00eb 100644 --- a/App/lib/core/Gen/ClassGenMixin.vp +++ b/App/lib/core/Gen/ClassGenMixin.vp @@ -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) diff --git a/App/lib/core/Gen/ExprGenMixin.vp b/App/lib/core/Gen/ExprGenMixin.vp index c4520de..04e2291 100644 --- a/App/lib/core/Gen/ExprGenMixin.vp +++ b/App/lib/core/Gen/ExprGenMixin.vp @@ -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) # diff --git a/App/lib/core/Gen/ImportsGenMixin.vp b/App/lib/core/Gen/ImportsGenMixin.vp index f2bc14c..e0b1e58 100644 --- a/App/lib/core/Gen/ImportsGenMixin.vp +++ b/App/lib/core/Gen/ImportsGenMixin.vp @@ -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: diff --git a/App/lib/core/Gen/StmtGenMixin.vp b/App/lib/core/Gen/StmtGenMixin.vp index ae45af5..e0c2278 100644 --- a/App/lib/core/Gen/StmtGenMixin.vp +++ b/App/lib/core/Gen/StmtGenMixin.vp @@ -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: diff --git a/App/lib/core/Gen/StructGenMixin.vp b/App/lib/core/Gen/StructGenMixin.vp index f6b3ac3..8f142cb 100644 --- a/App/lib/core/Gen/StructGenMixin.vp +++ b/App/lib/core/Gen/StructGenMixin.vp @@ -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 diff --git a/App/lib/core/Gen/TypeGenMixin.vp b/App/lib/core/Gen/TypeGenMixin.vp index 45d3c79..952ed08 100644 --- a/App/lib/core/Gen/TypeGenMixin.vp +++ b/App/lib/core/Gen/TypeGenMixin.vp @@ -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: diff --git a/App/lib/core/Handles/HandlesAnnAssign.vp b/App/lib/core/Handles/HandlesAnnAssign.vp index 740bedd..86c7d8f 100644 --- a/App/lib/core/Handles/HandlesAnnAssign.vp +++ b/App/lib/core/Handles/HandlesAnnAssign.vp @@ -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( diff --git a/App/lib/core/Handles/HandlesType.vp b/App/lib/core/Handles/HandlesType.vp index 231d3bc..d73cee7 100644 --- a/App/lib/core/Handles/HandlesType.vp +++ b/App/lib/core/Handles/HandlesType.vp @@ -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