feat(generic): 泛型实参推断为无裸名别名的整型位宽补精确票名

- _infer_scalar_type_name_from_spec 支持 i64/i16 -> t.CUInt64T/t.CInt64T、t.CUInt16T/t.CInt16T;符号性未知时不猜(返回 None)

- infer_scalar_type_arg_from_llvm_ty 增加 sign 参数:LLVM 类型层无法区分 iN/uN,符号性由被调名源码文本经 SpecSignFromTypeNameSpan 判定

- 泛型函数与泛型类构造两处 Call 推断同步传符号性,修复 GenericOopTest fxi2 第 3 个类型实参(CUInt64T)推断失败
This commit is contained in:
TermiNexus committed 2026-10-03 17:12:47 +08:00
1 parent ddb1e6cc2a
commit 21436b2690
2 files changed
+65 -12

No files matched your search

+13 -2
View File
@@ -9,7 +9,8 @@ import stdlib
import viperlib
import lib.core.VLogger as VLogger
import lib.core.Gen.Translator as HT
import lib.core.Gen.GenTypes as GenTypes
import lib.core.Gen.GenTypes as GenTypes
import lib.core.Gen.Type.TypeSpec as TSpec
from . import CallGenBase
@@ -858,7 +859,17 @@ novirtual class CallGenCtor(CallGenBase.CallGenBase):
cty_c: llvmlite.LLVMType* = self.Gen.TypeGenResolve.resolve_annotation_type(
pool, cl_f.func, trans._imported_modules, trans._from_imports, trans, 0)
if cty_c is not None:
ta_c: str = self.Gen.ClassGenGeneric.infer_scalar_type_arg_from_llvm_ty(pool, cty_c)
# 符号性从被调名源码文本取(LLVM 层 iN/uN 同形):决定
# CUInt64T/CInt16T 这类无裸名别名位宽发 t.CUInt64T / t.CInt64T。
callee_txt_c: str = None
if fk_c == ast.ASTKind.Name:
callee_txt_c = ast.Name*(cl_f.func).id
else:
callee_txt_c = ast.Attribute*(cl_f.func).attr
sign_c: int = TSpec.TS_SIGN_UNKNOWN
if callee_txt_c is not None:
sign_c = TSpec.SpecSignFromTypeNameSpan(callee_txt_c, 0, string.strlen(callee_txt_c))
ta_c: str = self.Gen.ClassGenGeneric.infer_scalar_type_arg_from_llvm_ty(pool, cty_c, sign_c)
if ta_c is not None:
return ta_c
# 类型转换调用(Call)但被调名无法解析为标量类型:明确的类型意图却无法
+52 -10
View File
@@ -22,6 +22,8 @@ define GENERIC_SPEC_MAX_DEPTH = 64
define GENERIC_TEMPLATE_MAX = 512
# 泛型特化缓存表容量
define SPEC_CACHE_MAX = 512
# 符号性哨兵:表示"取自 Spec.IsSigned"(区别于 TSpec.TS_SIGN_* 三值)
define SIGN_FROM_SPEC = -2
# ============================================================
# ClassGenGeneric - 泛型模板注册/特化/克隆
@@ -407,24 +409,44 @@ novirtual class ClassGenGeneric(ClassGenBase.ClassGenBase):
cty_f: llvmlite.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:
return self.infer_scalar_type_arg_from_llvm_ty(pool, cty_f)
# 符号性只能从**被调名源码文本**取(LLVM 层 iN/uN 同形,
# SpecFromLLVMType 恒给 SIGNED):CUInt64T/CInt16T 这类
# 无裸名别名的位宽必须靠它决定发 t.CUInt64T 还是 t.CInt64T。
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 = TSpec.SpecSignFromTypeNameSpan(callee_txt, 0, string.strlen(callee_txt))
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*) -> str:
sp: TSpec.TypeSpec*,
sign_override: int = SIGN_FROM_SPEC) -> str:
"""从变量的 TypeSpec 反推可解析的内建标量类型名
仅覆盖 extract_class_name_from_annotation 会跳过的内建标量
(str/char/bytes/bool/float/double 的 SourceName 为 None):按 LLVM
形态映射为源码可解析名(double/float/int/char/bool)。指针/结构体返回
None(交由注解来源名路径处理;此处不做猜测,避免错特化)。
形态映射为源码可解析名。指针/结构体返回 None(交由注解来源名路径处理;
此处不做猜测,避免错特化)。
**只映射"位宽精确对应到某个裸名"的形态**(i1→bool、i8→char、i32→int、
f32→float、f64→double)。i16/i64/f16 在 Viper 没有等位宽的裸名别名
(裸 int=int32、裸 float=float32),映射过去会静默窄化/扩宽位宽
(i64→int 即 i64→i32,直接改变 ABI 与数据),违反"禁止静默默认/兜底"
原则 ⇒ 一律返回 None,由调用方汇总后 fail-fast 提示显式标注。
f32→float、f64→double)。i16/i64 在 Viper 没有等位宽的裸名别名(裸
int=int32、裸 float=float32),映射到 int 会静默窄化/扩宽位宽(i64→int
即 i64→i32,直接改变 ABI 与数据)⇒ 改为发**精确位宽票名**
t.CUInt64T/t.CInt64T、t.CUInt16T/t.CInt16T(可被 _parse_type_string
解析),但**必须先知道符号性**:符号性未知时不猜(返回 None),由调用方
汇总后 fail-fast。
符号性来源(sign_override):
- SIGN_FROM_SPEC(默认):读 sp.IsSigned(注解侧 Spec 经
SpecWithSign 写入,可信);
- 显式 TS_SIGN_*:由调用方给出(LLVM 类型路径符号性在 LLVM 层已丢失,
只能由**被调名文本**经 TSpec.SpecSignFromTypeName 判定)。
"""
if pool is None or sp is None: return None
buf: str = pool.alloc(32)
@@ -437,15 +459,30 @@ novirtual class ClassGenGeneric(ClassGenBase.ClassGenBase):
# "无法从实参推断泛型函数 ... 禁止静默默认 int"。
if TSpec.SpecToLLVMText(sp, buf, 32) != 0:
return None
sign: int = sign_override
if sign == SIGN_FROM_SPEC:
# LLVM 层 SpecFromLLVMType 恒给 TS_SIGN_SIGNED(无法区分 iN/uN),
# 故仅注解侧 Spec(经 SpecWithSign)可信任此字段。
sign = sp.IsSigned
if string.strcmp(buf, "double") == 0: return "double"
if string.strcmp(buf, "float") == 0: return "float"
if string.strcmp(buf, "i1") == 0: return "bool"
if string.strcmp(buf, "i8") == 0: return "char"
if string.strcmp(buf, "i32") == 0: return "int"
# 无裸名别名的整型位宽 → 精确位宽票名(符号性未知则不猜)
if string.strcmp(buf, "i64") == 0:
if sign == TSpec.TS_SIGN_UNSIGNED: return "t.CUInt64T"
if sign == TSpec.TS_SIGN_SIGNED: return "t.CInt64T"
return None
if string.strcmp(buf, "i16") == 0:
if sign == TSpec.TS_SIGN_UNSIGNED: return "t.CUInt16T"
if sign == TSpec.TS_SIGN_SIGNED: return "t.CInt16T"
return None
return None
def infer_scalar_type_arg_from_llvm_ty(self, pool: memhub.MemBuddy*,
ty: llvmlite.LLVMType*) -> str:
ty: llvmlite.LLVMType*,
sign: int) -> str:
"""LLVM **标量**类型 → 泛型类型实参票名(精确位宽;非标量返回 None)
泛型函数(ClassGenGeneric._infer_generic_func_type_arg)与泛型类构造
@@ -453,13 +490,18 @@ novirtual class ClassGenGeneric(ClassGenBase.ClassGenBase):
"转换调用实参 → 类型实参" 两处同口径(唯一真源
_infer_scalar_type_name_from_spec)。非标量(结构体/指针/数组/函数)返回
None ⇒ 调用方不据此推断(宁缺毋错,不按名字子串猜)。
sign:LLVM 类型层无法区分 iN/uN(SpecFromLLVMType 恒给 TS_SIGN_SIGNED)
⇒ 无符号性必须由调用方从**被调名源码文本**经 TSpec.SpecSignFromTypeName
判定后传入;未知传 TSpec.TS_SIGN_UNKNOWN(此时 i16/i64 这类无裸名别名的
位宽推不出,本函数返回 None)。
"""
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))
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*,