refactor(types): drop retired t/c module type spellings

The `t`/`c` modules are gone, so their C type aliases (CInt*, CUnsigned*, CSizeT, CBool, CVoid, CFloat, CDouble, CChar, t.State, t.DefineT, t.VolatileT, the c.CIf family, @c.CReturn) are no longer recognised or reported anywhere: sign inference, enum widths, stub generation/reading, expression lookups and the LSP type-alias check. `import t` / `import c` no longer gets a special message.
This commit is contained in:
TermiNexus committed 2026-11-04 22:49:57 +08:00
1 parent a746952997
commit e7ef23b432
28 files changed
+161 -326

No files matched your search

+1 -1
View File
@@ -789,7 +789,7 @@ def RunPhase1(mb: memhub.MemBuddy*, includes_dir: str, temp_dir: str, log: VLogg
t_f1: uint64 = win32.win32base.GetTickCount64()
# Register TypedefT by scanning source text: AST string fields (attr/id) point into src_buf
# and dangle across phases (reading attr faults), so match "NAME: t.TypedefT = TYPE" lines.
# and dangle across phases (reading attr faults), so match "NAME: TypedefT = TYPE" lines.
GenApi.RegisterCTypedefFromSource(mb, src_buf)
# Generate the .vpi stub (walk the AST directly). 1b-pre already generated one, so this
+8 -38
View File
@@ -576,9 +576,8 @@ def _PyiTypeStr(buf: bytes, buf_size: sizet, pos: sizet, annot: ast.AST*, depth:
if k == ast.ASTKind.Name:
nm: ast.Name* = ast.Name*(annot)
if nm.id is not None:
# Retired bare markers map to their source spellings.
# Bare markers map to their source spellings.
if string.strcmp(nm.id, "PtrT") == 0: return _PyiEmit(buf, buf_size, pos, "rawptr")
if string.strcmp(nm.id, "CVoid") == 0: return _PyiEmit(buf, buf_size, pos, "void")
return _PyiEmit(buf, buf_size, pos, nm.id)
return _PyiEmit(buf, buf_size, pos, "int")
@@ -586,7 +585,6 @@ def _PyiTypeStr(buf: bytes, buf_size: sizet, pos: sizet, annot: ast.AST*, depth:
if k == ast.ASTKind.Attribute:
at: ast.Attribute* = ast.Attribute*(annot)
if at.attr is not None and string.strcmp(at.attr, "PtrT") == 0: return _PyiEmit(buf, buf_size, pos, "rawptr")
if at.attr is not None and string.strcmp(at.attr, "CVoid") == 0: return _PyiEmit(buf, buf_size, pos, "void")
pos = _PyiTypeStr(buf, buf_size, pos, at.value, depth + 1)
pos = _PyiEmit(buf, buf_size, pos, ".")
if at.attr is not None: pos = _PyiEmit(buf, buf_size, pos, at.attr)
@@ -608,8 +606,8 @@ def _PyiTypeStr(buf: bytes, buf_size: sizet, pos: sizet, annot: ast.AST*, depth:
# Subscript: subscripted type (e.g. list[int], ArrayT[elem, count])
if k == ast.ASTKind.Subscript:
sub: ast.Subscript* = ast.Subscript*(annot)
# Builtin markers carry source syntax in a stub: `T*` / `[N]T` / `const T` /
# `volatile T` / `&T` / `&temp T`. The retired t.* C-names must never be written.
# Builtin markers are written as source syntax: `T*` / `[N]T` / `const T` /
# `volatile T` / `&T` / `&temp T`.
if sub.value is not None and sub.value.kind() == ast.ASTKind.Name:
mk_n: ast.Name* = ast.Name*(sub.value)
if mk_n.id is not None:
@@ -842,30 +840,6 @@ def _PyiDefaultStr(buf: bytes, buf_size: sizet, pos: sizet, node: ast.AST*, dept
return pos
# _PyiRejectCReturnDecorator - fail if @c.CReturn appears in the decorator list.
# The decorator was removed; a stale stub must be regenerated. 0 = none found.
def _PyiRejectCReturnDecorator(node: ast.AST*, decorators: list[ast.AST*]*) -> int:
"""Fail fast on a @c.CReturn decorator (returns 0 when absent)"""
if decorators is None: return 0
dn: sizet = len(decorators)
di: sizet = 0
while di < dn:
dnode: ast.AST* = decorators.get(di)
di += 1
if dnode is None: continue
# Handles both @c.CReturn(...) (Call with func=Attribute) and bare @c.CReturn
dattr: ast.Attribute* = None
if dnode.kind() == ast.ASTKind.Call:
dcall: ast.Call* = ast.Call*(dnode)
if dcall.func is not None and dcall.func.kind() == ast.ASTKind.Attribute: dattr = ast.Attribute*(dcall.func)
elif dnode.kind() == ast.ASTKind.Attribute: dattr = ast.Attribute*(dnode)
if dattr is None or dattr.attr is None: continue
if string.strcmp(dattr.attr, "CReturn") == 0:
GenApi.fatal_error(node, "vpi still contains `@c.CReturn` (removed): regenerate the .vpi using a return annotation `-> tuple[ty1, ty2, ...]` (you may add --clean)", "TypeError")
return 1
return 0
# ============================================================
# 逃逸摘要计算用的复用临时区(74 槽:gset[0]=global 计数、gset[1..65)=名哈希、box=gset+65)。
# 只分配一次,避免每个 def 都分配;布局与 StructGen 的 EscScratch 完全一致。
@@ -1024,9 +998,6 @@ def _PyiFunction(buf: bytes, buf_size: sizet, pos: sizet, fn: ast.FunctionDef*,
if ret_node is not None:
pos = _PyiEmit(buf, buf_size, pos, " -> ")
pos = _PyiTypeStr(buf, buf_size, pos, ret_node)
else:
# Without a `->` annotation, @c.CReturn is removed: fail fast to regenerate vpi
_PyiRejectCReturnDecorator(fn, fn.decorator_list)
pos = _PyiEmit(buf, buf_size, pos, ": pass")
# v3 跨模块逃逸摘要:把形参逃逸掩码与返回借用位写成**同一行**的行尾注释,供 VpiReader
@@ -1322,7 +1293,7 @@ def _GeneratePyiFromAst(mb: memhub.MemBuddy*, tree: ast.AST*, rel_path: str, buf
if tree.children is not None:
pre_children: list[ast.AST*]* = tree.children
pcn: sizet = len(pre_children)
# Pass 1: count t.DefineT declarations (C arrays need a fixed size first)
# Pass 1: count DefineT declarations (C arrays need a fixed size first)
_g_pyi_cdef_count = 0
for pci0 in range(int(pcn)):
stmt_pc0: ast.AST* = pre_children.get(pci0)
@@ -1373,7 +1344,6 @@ def _GeneratePyiFromAst(mb: memhub.MemBuddy*, tree: ast.AST*, rel_path: str, buf
tree_children: list[ast.AST*]* = tree.children
n: sizet = len(tree_children)
# t / c are retired compiler-internal modules: the stub never injects their imports.
pos = _PyiEmit(buf, buf_size, pos, "\n")
# Pass 2: emit declarations
@@ -1461,8 +1431,8 @@ def _GeneratePyiFromAst(mb: memhub.MemBuddy*, tree: ast.AST*, rel_path: str, buf
if aa.target is not None and aa.target.kind() == ast.ASTKind.Name:
nm: ast.Name* = ast.Name*(aa.target)
if nm.id is not None:
# Retired t.TypedefT / t.DefineT markers are written as their source
# syntax (`typedef NAME = T` / `define NAME[: T] = value`).
# TypedefT / DefineT markers are written as source syntax:
# `typedef NAME = T` / `define NAME[: T] = value`.
is_ctypedef_ann: int = 0
if aa.annotation is not None and aa.annotation.kind() == ast.ASTKind.Name:
ctd_nm0: ast.Name* = ast.Name*(aa.annotation)
@@ -1560,7 +1530,7 @@ def _GeneratePyiFromAst(mb: memhub.MemBuddy*, tree: ast.AST*, rel_path: str, buf
viperlib.snprintf(num_buf_u, 32, " = %lld", neg_val)
pos = _PyiEmit(buf, buf_size, pos, num_buf_u)
elif aa.value is not None and aa.value.kind() == ast.ASTKind.Call:
# Type-constructor constant (e.g. t.CUnsignedLong(-11)):
# Type-constructor constant (e.g. uint32(-11)):
# extract and emit the integer value of the first argument.
call_cv: ast.Call* = ast.Call*(aa.value)
if call_cv.args is not None:
@@ -1600,7 +1570,7 @@ def _GeneratePyiFromAst(mb: memhub.MemBuddy*, tree: ast.AST*, rel_path: str, buf
viperlib.snprintf(fbuf_cu, 64, " = %.17g", neg_cf)
pos = _PyiEmit(buf, buf_size, pos, fbuf_cu)
elif arg_kind == ast.ASTKind.BinOp:
# BinOp inside a Call (e.g. t.CUnsignedLong(A | B)):
# BinOp inside a Call (e.g. uint32(A | B)):
# recursively evaluate the BinOp value.
binop_val_bo: int64 = _eval_binop_int(first_arg)
num_buf_bo: bytes = mb.alloc(32)
+19 -24
View File
@@ -2124,21 +2124,18 @@ def _resolve_vpi_ret_annot_str(pool: memhub.MemBuddy*, ret_annot: str, i32_ty: m
if tup_head_t is None: return None
return mlirlite.Struct(pool, tup_head_t, tup_count_t, None)
# Pure marker annotations (t.State / t.ExternT / t.ExportT alone, optional t./c. prefix)
# -> void: decorator markers, not real return types, matching _inject_module_declare.
# Pure marker annotations (State / ExternT / ExportT alone) mean void.
marker_len: sizet = string.strlen(ret_annot)
mk_s: sizet = 0
while mk_s < marker_len and ret_annot[mk_s] == ' ': mk_s += 1
mk_e: sizet = marker_len
while mk_e > mk_s and ret_annot[mk_e - 1] == ' ': mk_e -= 1
mk_off_m: sizet = 0
if mk_e - mk_s >= 2 and (ret_annot[mk_s] == 't' or ret_annot[mk_s] == 'c') and ret_annot[mk_s + 1] == '.': mk_off_m = 2
mk_nlen: sizet = mk_e - mk_s - mk_off_m
if (mk_nlen == 5 and string.strncmp(ret_annot + mk_s + mk_off_m, "State", 5) == 0) or (mk_nlen == 7 and string.strncmp(ret_annot + mk_s + mk_off_m, "ExternT", 7) == 0) or (mk_nlen == 7 and string.strncmp(ret_annot + mk_s + mk_off_m, "ExportT", 7) == 0):
mk_nlen: sizet = mk_e - mk_s
if (mk_nlen == 5 and string.strncmp(ret_annot + mk_s, "State", 5) == 0) or (mk_nlen == 7 and string.strncmp(ret_annot + mk_s, "ExternT", 7) == 0) or (mk_nlen == 7 and string.strncmp(ret_annot + mk_s, "ExportT", 7) == 0):
return mlirlite.Void(pool)
# Unions were retired, so a stub return annotation is a single type. `T*` / `T**` and
# `const` / `volatile` prefixes are handled by the shared single-segment resolver.
# A stub return annotation is a single type. `T*` / `T**` and `const` / `volatile`
# prefixes are handled by the shared single-segment resolver.
return _resolve_single_vpi_type_str(pool, ret_annot)
@@ -2372,8 +2369,6 @@ def _vpi_register_typedef_closure(pool: memhub.MemBuddy*, import_mods: str, temp
while pos < total_len and import_mods[pos] != ' ': pos += 1
m_len: sizet = pos - m_start
if m_len == 0: continue
# t/c built-in libs are not compiled, so they have no vpi stub
if m_len == 1 and (import_mods[m_start] == 't' or import_mods[m_start] == 'c'): continue
mod_buf: bytes = stdlib.malloc(m_len + 1)
if mod_buf is None: continue
string.strncpy(mod_buf, import_mods + m_start, m_len)
@@ -3268,7 +3263,7 @@ def _ParseVpiIntoModuleImpl(pool: memhub.MemBuddy*, mod: mlirlite.LLVMModule*, v
n: sizet = len(ch)
# Collect the read module's own imports from the stub to build an isolated annotation
# context, replacing the current module's imports (e.g. t.ArrayT[str] needs 't' imported).
# context that replaces the current module's imports.
stub_imported_modules: str = None
stub_from_imports: str = None
# Bookkeeping goes through StubMergerStore (same as ImportsGenMixin.translate_import):
@@ -3508,7 +3503,7 @@ def _ParseVpiIntoModuleImpl(pool: memhub.MemBuddy*, mod: mlirlite.LLVMModule*, v
_inject_function_declare(pool, mod, fd, trans, src_sha1, imported_modules, from_imports, i32_ty, i8_ptr_ty, funcs_ptr, max_funcs)
continue
# AnnAssign with t.TypedefT -> register TypedefT;
# AnnAssign with TypedefT -> register TypedefT;
# plain module-level globals (e.g. tokens._op_head) -> external global declare
if kd == ast.ASTKind.AnnAssign:
aa: ast.AnnAssign* = ast.AnnAssign*(child)
@@ -3561,7 +3556,7 @@ def _ParseVpiIntoModuleImpl(pool: memhub.MemBuddy*, mod: mlirlite.LLVMModule*, v
# `define X = None`: a named None (see _register_cdefine_from_value).
GenApi.register_cdefine_string(pool, cdef_tgt.id, "None", def_mod)
elif cdef_is_str == 0 and cdef_is_float != 0:
# Float DefineT (e.g. FRAGMENTATION_THRESHOLD: t.DefineT = 0.7): register via
# Float DefineT (e.g. FRAGMENTATION_THRESHOLD: DefineT = 0.7): register via
# register_cdefine_double (integer extraction would fail fast).
# 注解声明 float 时带上 f32 种类(与定义方同一套裁决)。
GenApi.register_cdefine_double(pool, cdef_tgt.id, cdef_float_val, GenApi.cdefine_decl_float_bits(pool, aa.annotation), def_mod)
@@ -3695,7 +3690,7 @@ def _vpi_tattr_name(node: ast.AST*, attr_name: str) -> int:
def _vpi_class_has_packed(decorator_list: list[ast.AST*]*) -> int:
"""Check whether a vpi ClassDef decorator list contains t.attr.packed."""
"""Check whether a vpi ClassDef decorator list contains attr.packed."""
if decorator_list is None: return 0
dn: sizet = len(decorator_list)
if dn == 0: return 0
@@ -3718,7 +3713,7 @@ def _vpi_class_has_packed(decorator_list: list[ast.AST*]*) -> int:
def _vpi_class_is_renum(class_cd: ast.ClassDef*) -> int:
"""Check whether a vpi ClassDef is a REnum (base class contains t.REnum); 1=yes / 0=no."""
"""Check whether a vpi ClassDef is a REnum (base class contains REnum); 1=yes / 0=no."""
if class_cd is None or class_cd.bases is None: return 0
bases: list[ast.AST*]* = class_cd.bases
bn: sizet = len(bases)
@@ -3935,7 +3930,7 @@ def _inject_class_struct_declare(pool: memhub.MemBuddy*, mod: mlirlite.LLVMModul
field_types_buf: sizet* = pool.alloc(8 * GenTypes.FIELD_MAX)
# Per-field marker buffers, index-aligned with field_names_buf / add_field order. The .vpi text
# keeps each field's raw annotation (Converter._PyiTypeStr renders BinOp/Attribute verbatim, so
# `X | t.BigEndian` / `t.VolatileT | int` survive), so the same markers ClassGenDef extracts on
# `X | bigend` / `volatile | int` survive), so the same markers ClassGenDef extracts on
# the definition side must be re-extracted here; otherwise a struct referenced across a module
# boundary loses its volatile/const/byte-order semantics and the same field compiles differently
# per module (the bug this fixes).
@@ -3980,8 +3975,8 @@ def _inject_class_struct_declare(pool: memhub.MemBuddy*, mod: mlirlite.LLVMModul
if GenApi.has_decorator_marker(annot_fc, "VolatileT", 0) != 0: field_volatile_buf[field_count] = 1
if GenApi.has_pointee_volatile_marker(annot_fc) != 0: field_pointee_volatile_buf[field_count] = 1
if GenApi.is_const_object_marker(annot_fc, field_ty_c) != 0: field_const_buf[field_count] = 1
# Byte order: 1 = t.BigEndian / 2 = t.LittleEndian (0 stays native);
# annotation `|` means "and", so `X | t.BigEndian` is a BigEndian X.
# Byte order: 1 = bigend / 2 = littlend (0 stays native);
# annotation `|` means "and", so `X | bigend` is a BigEndian X.
if GenApi.has_decorator_marker(annot_fc, "BigEndian", 0) != 0: field_byteorder_buf[field_count] = 1
elif GenApi.has_decorator_marker(annot_fc, "LittleEndian", 0) != 0: field_byteorder_buf[field_count] = 2
field_count += 1
@@ -4203,17 +4198,17 @@ def _inject_function_declare(pool: memhub.MemBuddy*, mod: mlirlite.LLVMModule*,
has_extern: int = 0
has_state: int = 0
has_export: int = 0
# `extern def` / `export def` recorded as the vpi keyword are the same storage markers as the
# legacy t.ExternT / t.ExportT return annotations: the defining side emits the bare C name, so
# the injected declare must be bare as well (a SHA1 prefix leaves the symbol undefined).
# `extern def` / `export def` keywords and ExternT / ExportT return annotations are the same
# storage markers: the defining side emits the bare C name, so the injected declare must be
# bare as well (a SHA1 prefix leaves the symbol undefined).
if (fd.flags & ast.FLAG_IS_EXTERN) != 0: has_extern = 1
if (fd.flags & ast.FLAG_IS_EXPORT) != 0: has_export = 1
if fd.returns is not None:
if GenApi.has_decorator_marker(fd.returns, "ExternT") != 0: has_extern = 1
if GenApi.has_decorator_marker(fd.returns, "State") != 0: has_state = 1
if GenApi.has_decorator_marker(fd.returns, "ExportT") != 0: has_export = 1
# Bare-struct param rule (see _AggregateParamTy): C ABI externs (t.ExternT/t.State) by value;
# the rest (incl. t.ExportT) keep Ptr(struct). Same source as FuncGenDef.is_extern_decl.
# Bare-struct param rule (see _AggregateParamTy): C ABI externs (ExternT/State) by value;
# the rest (including ExportT) keep Ptr(struct). Same source as FuncGenDef.is_extern_decl.
agg_is_c_abi: int = 0
if has_extern != 0 or has_state != 0: agg_is_c_abi = 1
@@ -4527,7 +4522,7 @@ def _inject_method_declare(pool: memhub.MemBuddy*, mod: mlirlite.LLVMModule*, m_
# _register_ctypedef_from_annassign - register a TypedefT from an AnnAssign node.
# Extracts the NAME and type expression from `NAME: t.TypedefT = Type` and registers it.
# Extracts the NAME and type expression from `NAME: TypedefT = Type` and registers it.
def _register_ctypedef_from_annassign(pool: memhub.MemBuddy*, aa: ast.AnnAssign*) -> None:
"""Extract and register a TypedefT from an AnnAssign node."""
if aa is None or pool is None: return
+2 -2
View File
@@ -922,7 +922,7 @@ novirtual class CallGenCtor(CallGenBase.CallGenBase):
if self.Gen.ExprGenBase._zext_or_sext_val(pool, val, src_node, trans) != 0: return mlirlite.build_zext(builder, val, target_ty)
return mlirlite.build_sext(builder, val, target_ty)
else:
# Target i1 (bool / t.CBool) via an EXPLICIT cast `t.CBool(x)` / `(bool)(x)`:
# Target i1 (bool / bool) via an EXPLICIT cast `bool(x)` / `(bool)(x)`:
# normalise truth as `x != 0 -> 1/0` (not trunc, which would make x=2 false while
# `if 2:` is true). Implicit wide-int -> bool stores no longer normalise here; they
# fail fast in coerce_to_type (rule 2026-10-13), so only explicit casts reach this.
@@ -1018,7 +1018,7 @@ novirtual class CallGenCtor(CallGenBase.CallGenBase):
if cn.const_kind == ast.CONST_CHAR: return "char"
if cn.const_kind == ast.CONST_STR: return "str"
return "int"
# type-cast call: int(...)/float(...)/t.CDouble(...)
# type-cast call: int(...)/float(...)/double(...)
if k == ast.ASTKind.Call:
cl_f: ast.Call* = ast.Call*(arg_node)
if cl_f.func is not None:
+4 -33
View File
@@ -233,33 +233,14 @@ novirtual class CallGenDispatch(CallGenBase.CallGenBase):
def _tc_h2_c_special(self, pool: memhub.MemBuddy*, builder: mlirlite.IRBuilder*, mod: mlirlite.LLVMModule*, cl: ast.Call*, funcs_ptr: rawptr, func_count: int, trans: HT.Translator*, func_name: char*, cargs: list[ast.AST*]*, can: sizet, hit_box: rawptr = None) -> mlirlite.Value*:
"""Special __asm__/__llvmir__ calls; marks hit_box for void calls, else returns None."""
# Retired c.* pointer builtins: fail fast with the replacement syntax.
if cl.func is not None and cl.func.kind() == ast.ASTKind.Attribute:
ra_c: ast.Attribute* = ast.Attribute*(cl.func)
if ra_c.value is not None and ra_c.value.kind() == ast.ASTKind.Name and ra_c.attr is not None:
rn_c: ast.Name* = ast.Name*(ra_c.value)
if rn_c.id is not None and string.strcmp(rn_c.id, "c") == 0:
if string.strcmp(ra_c.attr, "Deref") == 0 or string.strcmp(ra_c.attr, "DerefAs") == 0 \
or string.strcmp(ra_c.attr, "Set") == 0 or string.strcmp(ra_c.attr, "Addr") == 0 \
or string.strcmp(ra_c.attr, "Load") == 0:
self.Gen.TypeGenErr.fatal_error(cl, f"c.{ra_c.attr} was removed; use `*p` (read), `*p = v` (write) or `&v` (address-of)", "NameError")
return None
# __asm__(...) / __llvmir__(...) sugar: bare calls equal c.Asm(...) / c.LLVMIR(...). Must
# intercept before the c.* attribute branch, since the bare cl.func is a Name.
# __asm__(...) / __llvmir__(...) sugar: intercept bare calls before the module path.
if func_name is not None and cl.func is not None and cl.func.kind() == ast.ASTKind.Name:
bare_nm: ast.Name* = ast.Name*(cl.func)
if bare_nm.id is not None and (string.strcmp(bare_nm.id, "Deref") == 0 or string.strcmp(bare_nm.id, "DerefAs") == 0):
self.Gen.TypeGenErr.fatal_error(cl, f"{bare_nm.id} was removed; use `*p` (read), `*p = v` (write) or `&v` (address-of)", "NameError")
return None
if bare_nm.id is not None and string.strcmp(bare_nm.id, "defined") == 0:
# defined(NAME) is a compile-time predicate valid only inside a cif/celif condition;
# it is evaluated there and never lowered as a runtime call.
self.Gen.TypeGenErr.fatal_error(cl, "defined(NAME) is only valid inside a cif/celif condition; it cannot be used in a normal expression", "NameError")
return None
if bare_nm.id is not None and (string.strcmp(bare_nm.id, "CIf") == 0 or string.strcmp(bare_nm.id, "CIfdef") == 0 or string.strcmp(bare_nm.id, "CIfndef") == 0 or string.strcmp(bare_nm.id, "CElif") == 0 or string.strcmp(bare_nm.id, "CElse") == 0 or string.strcmp(bare_nm.id, "CEndif") == 0):
# removed spellings (also covered for the bare-name form of the c.CIf family)
self.Gen.TypeGenErr.fatal_error(cl, "c.CIf/c.CIfdef/c.CIfndef/c.CElif/c.CElse/c.CEndif were removed; use the cif/celif/celse statements and the defined(NAME) predicate instead (e.g. `cif defined(NAME):` ... `celse:` ...)", "NameError")
return None
if bare_nm.id is not None and string.strcmp(bare_nm.id, "__asm__") == 0:
# translate_c_asm returns None for void output: set the hit flag so translate_call
# does not misread it as a miss and fall through to the external function path.
@@ -447,7 +428,7 @@ novirtual class CallGenDispatch(CallGenBase.CallGenBase):
bare_arg2_bn: ast.Name* = ast.Name*(bare_arg2_node)
if bare_arg2_bn.id is not None and (string.strcmp(bare_arg2_bn.id, "PtrT") == 0 or string.strcmp(bare_arg2_bn.id, "rawptr") == 0): bare_arg2_is_cptr = 1
if bare_arg2_is_cptr != 0:
# CVoid* -> i8* (void* is represented as i8* in LLVM)
# void* -> i8* (void* is represented as i8* in LLVM)
match bare_ty:
case mlirlite.LLVMType.Void():
bare_ty = mlirlite.Int8(pool)
@@ -553,17 +534,7 @@ novirtual class CallGenDispatch(CallGenBase.CallGenBase):
if string.strcmp(sz_cls_name, "int") == 0: sz_builtin = 4
elif string.strcmp(sz_cls_name, "str") == 0: sz_builtin = 8
elif string.strcmp(sz_cls_name, "bytes") == 0: sz_builtin = 8
elif string.strcmp(sz_cls_name, "CInt") == 0: sz_builtin = 4
elif string.strcmp(sz_cls_name, "CInt8T") == 0: sz_builtin = 1
elif string.strcmp(sz_cls_name, "CInt16T") == 0: sz_builtin = 2
elif string.strcmp(sz_cls_name, "CInt32T") == 0: sz_builtin = 4
elif string.strcmp(sz_cls_name, "CInt64T") == 0: sz_builtin = 8
elif string.strcmp(sz_cls_name, "CChar") == 0: sz_builtin = 1
elif string.strcmp(sz_cls_name, "CDouble") == 0: sz_builtin = 8
elif string.strcmp(sz_cls_name, "CFloat") == 0: sz_builtin = 4
elif string.strcmp(sz_cls_name, "PtrT") == 0: sz_builtin = 8
elif string.strcmp(sz_cls_name, "CSizeT") == 0: sz_builtin = 8
elif string.strcmp(sz_cls_name, "CBool") == 0: sz_builtin = 1
elif string.strcmp(sz_cls_name, "bool") == 0: sz_builtin = 1
# Bare builtin names resolved from generic T (e.g. T->"double"/"float"): without
# them sz_builtin stays -1 and Name(T) is translated as a value, causing NameError.
@@ -748,7 +719,7 @@ novirtual class CallGenDispatch(CallGenBase.CallGenBase):
return mlirlite.build_call(builder, len_mangled_x, len_self_x, 1, len_ret_x, 0)
# fail-fast: the class has no __len__ (compile-time error, matching TPC
# HandlesExprBuiltin._FailFastLenUnsupported wording).
self.Gen.TypeGenErr.fatal_error(len_arg_node, f"len() argument type '{len_cls_nm}' has no __len__ method. len() only supports fixed-size arrays/array pointers (t.ArrayT[T, N]), i8* (str/bytes), or structs with __len__; non-i8 bare pointers/scalar pointers have no length semantics: use str/bytes for strings, record the length yourself when the element count is needed, or use rawptr[Element] and maintain the element count explicitly", "TypeError")
self.Gen.TypeGenErr.fatal_error(len_arg_node, f"len() argument type '{len_cls_nm}' has no __len__ method. len() only supports fixed-size arrays/array pointers ([N]T), i8* (str/bytes), or structs with __len__; non-i8 bare pointers/scalar pointers have no length semantics: use str/bytes for strings, record the length yourself when the element count is needed, or use rawptr[Element] and maintain the element count explicitly", "TypeError")
# Non-struct: char* (Ptr(Int8)) uses strlen; other types fail fast
len_arg_ty: mlirlite.LLVMType* = mlirlite.value_get_ty(len_val)
len_is_str: int = 0
@@ -781,7 +752,7 @@ novirtual class CallGenDispatch(CallGenBase.CallGenBase):
return mlirlite.build_call(builder, "strlen", len_coerced, 1, i64_ty_ln2, 0)
# fail-fast: not a string/array/object, so length is unavailable (matching TPC
# HandlesExprBuiltin._FailFastLenUnsupported wording).
self.Gen.TypeGenErr.fatal_error(len_arg_node, "len() argument type not supported (not a string/array/object). len() only supports fixed-size arrays/array pointers (t.ArrayT[T, N]), i8* (str/bytes), or structs with __len__; non-i8 bare pointers/scalar pointers have no length semantics: use str/bytes for strings, record the length yourself when the element count is needed, or use rawptr[Element] and maintain the element count explicitly", "TypeError")
self.Gen.TypeGenErr.fatal_error(len_arg_node, "len() argument type not supported (not a string/array/object). len() only supports fixed-size arrays/array pointers ([N]T), i8* (str/bytes), or structs with __len__; non-i8 bare pointers/scalar pointers have no length semantics: use str/bytes for strings, record the length yourself when the element count is needed, or use rawptr[Element] and maintain the element count explicitly", "TypeError")
# Detect isinstance(): runtime vtable subclass-set comparison. obj's head vtable pointer must
# be in {Cls and all subclass vtables}; NoVTable targets fall back to a compile-time check.
+1 -1
View File
@@ -642,7 +642,7 @@ novirtual class CallGenLeafKit(CallGenCBuiltin.CallGenCBuiltin):
# 3. build the signature from arg types + registered ret type (missing = fail fast)
ret_ty: mlirlite.LLVMType* = self.lookup_func_ptr_ret(func_name)
if ret_ty is None:
self.Gen.TypeGenErr.fatal_error(None, f"Return type of function pointer variable '{func_name}' is not registered (t.Callable annotation missing)", "TypeError")
self.Gen.TypeGenErr.fatal_error(None, f"Return type of function pointer variable '{func_name}' is not registered (Callable annotation missing)", "TypeError")
ret_ty = i32_ty
fp_first: mlirlite.ParamNode* = None
fp_prev: mlirlite.ParamNode* = None
+3 -3
View File
@@ -1168,15 +1168,15 @@ novirtual class CallGenMixin(CallGenBase.CallGenBase):
# Closure variable (nested function hoisted and registered): extract fn_ptr/env_ptr and call indirectly.
if self.Gen.CallGenLeafKit.is_closure_name(func_name) != 0:
return self.Gen.CallGenLeafKit.translate_closure_call(pool, builder, mod, fp_alloca, cargs, can, trans)
# Function-pointer variable (t.Callable annotation -> i8** alloca): build the signature from args and call indirectly.
# Function-pointer variable (Callable annotation -> i8** alloca): build the signature from args and call indirectly.
if self.Gen.CallGenLeafKit._is_func_ptr_alloca(fp_alloca) != 0:
# Distinguish t.Callable (true function pointer) from rawptr (closure pointer): a rawptr
# Distinguish Callable (true function pointer) from rawptr (closure pointer): a rawptr
# holds {fn_ptr, env_ptr} and must pass env, else a direct call jumps into closure data and crashes.
ve_fp: GenTypes.VarEntry* = self.Gen.VarGen.lookup_var_entry(trans.SymTab, func_name)
is_cptr_annot_fp: int = 0
fp_annot: char* = self.Gen.VarGen.var_annot_class_name(ve_fp)
if fp_annot is not None:
# [S4] PtrT marker = annotation base name exactly "PtrT": t.Callable[[A],R] also resolves to
# [S4] PtrT marker = annotation base name exactly "PtrT": Callable[[A],R] also resolves to
# Ptr(i8) (same Spec), so only the annotation name can carry the marker; substring matching would misfire.
if TSpec.SpecTypeNameBaseIs(fp_annot, "PtrT") != 0: is_cptr_annot_fp = 1
if is_cptr_annot_fp == 0:
+17 -54
View File
@@ -127,25 +127,12 @@ novirtual class ClassGenBase:
# Methods below migrated in P5-2 from HandlesClassDef.vp; intra-module calls use self.
def _is_cenum_base(self, base_node: ast.AST*, trans: HT.Translator* = None) -> int:
"""Return 1 if the base node is t.EnumBase, else 0."""
"""Return 1 if the base node is EnumBase, else 0."""
if base_node is None: return 0
k: int = base_node.kind()
# Name('EnumBase') form (from t import EnumBase).
if k == ast.ASTKind.Name:
nm: ast.Name* = ast.Name*(base_node)
if nm.id is None: return 0
if string.strcmp(nm.id, "EnumBase") == 0: return 1
# Alias form: from t import EnumBase as en; class Color(en).
# Resolve the original name (en -> EnumBase) before comparing.
if trans is not None and trans._from_imports is not None:
alias_orig: str = self.Gen.ImportsGen.lookup_from_import_original(trans._from_imports, nm.id)
if alias_orig is not None:
og_len_ce: sizet = 0
while alias_orig[og_len_ce] != ' ' and alias_orig[og_len_ce] != '\0': og_len_ce += 1
if og_len_ce == 8 and string.strncmp(alias_orig, "EnumBase", 8) == 0: return 1
return 0
# Attribute form was retired together with the t module prefix.
return 0
if base_node.kind() != ast.ASTKind.Name: return 0
nm: ast.Name* = ast.Name*(base_node)
if nm.id is None: return 0
return 1 if string.strcmp(nm.id, "EnumBase") == 0 else 0
def _is_enum_class(self, cd: ast.ClassDef*, trans: HT.Translator* = None) -> int:
"""Return 1 if the ClassDef is a EnumBase (subclasses t.EnumBase or uses @t.EnumBase)."""
if cd is None: return 0
@@ -162,24 +149,12 @@ novirtual class ClassGenBase:
if self._is_cenum_base(base_node, trans) == 1: return 1
return 0
def _is_cunion_base(self, base_node: ast.AST*, trans: HT.Translator* = None) -> int:
"""Return 1 if the base node is t.UnionBase, else 0."""
"""Return 1 if the base node is UnionBase, else 0."""
if base_node is None: return 0
k: int = base_node.kind()
# Name('UnionBase') form (from t import UnionBase).
if k == ast.ASTKind.Name:
nm: ast.Name* = ast.Name*(base_node)
if nm.id is None: return 0
if string.strcmp(nm.id, "UnionBase") == 0: return 1
# Alias form: from t import UnionBase as un; class Data(un).
if trans is not None and trans._from_imports is not None:
alias_orig: str = self.Gen.ImportsGen.lookup_from_import_original(trans._from_imports, nm.id)
if alias_orig is not None:
og_len_cu: sizet = 0
while alias_orig[og_len_cu] != ' ' and alias_orig[og_len_cu] != '\0': og_len_cu += 1
if og_len_cu == 9 and string.strncmp(alias_orig, "UnionBase", 9) == 0: return 1
return 0
# Attribute form was retired together with the t module prefix.
return 0
if base_node.kind() != ast.ASTKind.Name: return 0
nm: ast.Name* = ast.Name*(base_node)
if nm.id is None: return 0
return 1 if string.strcmp(nm.id, "UnionBase") == 0 else 0
def _is_union_class(self, cd: ast.ClassDef*, trans: HT.Translator* = None) -> int:
"""Return 1 if the ClassDef is a UnionBase (subclasses t.UnionBase or uses @t.UnionBase)."""
if cd is None: return 0
@@ -196,24 +171,12 @@ novirtual class ClassGenBase:
if self._is_cunion_base(base_node, trans) == 1: return 1
return 0
def _is_crenum_base(self, base_node: ast.AST*, trans: HT.Translator* = None) -> int:
"""Return 1 if the base node is t.REnum, else 0."""
"""Return 1 if the base node is REnum, else 0."""
if base_node is None: return 0
k: int = base_node.kind()
# Name('REnum') form (from t import REnum).
if k == ast.ASTKind.Name:
nm: ast.Name* = ast.Name*(base_node)
if nm.id is None: return 0
if string.strcmp(nm.id, "REnum") == 0: return 1
# Alias form: from t import REnum as ren; class Opt(ren).
if trans is not None and trans._from_imports is not None:
alias_orig: str = self.Gen.ImportsGen.lookup_from_import_original(trans._from_imports, nm.id)
if alias_orig is not None:
og_len_cr: sizet = 0
while alias_orig[og_len_cr] != ' ' and alias_orig[og_len_cr] != '\0': og_len_cr += 1
if og_len_cr == 5 and string.strncmp(alias_orig, "REnum", 5) == 0: return 1
return 0
# Attribute form was retired together with the t module prefix.
return 0
if base_node.kind() != ast.ASTKind.Name: return 0
nm: ast.Name* = ast.Name*(base_node)
if nm.id is None: return 0
return 1 if string.strcmp(nm.id, "REnum") == 0 else 0
def _is_renum_class(self, cd: ast.ClassDef*, trans: HT.Translator* = None) -> int:
"""Return 1 if the ClassDef is a REnum (subclasses t.REnum or uses @t.REnum)."""
if cd is None: return 0
@@ -692,13 +655,13 @@ novirtual class ClassGenBase:
if trans is not None: _fi_pc = trans._from_imports
bname: str = self._get_base_name(base_node, pool, _fi_pc)
if bname is not None:
# Resolve base aliases (from t import StructBase as cs -> cs is StructBase).
# Resolve base aliases (from X import StructBase as cs -> cs is StructBase).
# Otherwise find_struct("cs") fails and the class is deferred forever.
bname = self._resolve_base_alias(bname, trans)
if self._is_marker_base(bname) == 0: return bname
return None
def _resolve_base_alias(self, base_name: str, trans: HT.Translator* = None) -> str:
"""Resolve a base alias (from t import StructBase as cs -> base cs is actually StructBase)."""
"""Resolve a base alias (from X import StructBase as cs -> base cs is actually StructBase)."""
if base_name is None or trans is None or trans._from_imports is None: return base_name
orig: str = self.Gen.ImportsGen.lookup_from_import_original(trans._from_imports, base_name)
if orig is not None:
+2 -15
View File
@@ -37,16 +37,12 @@ novirtual class ClassGenEnum(ClassGenBase.ClassGenBase):
# [S4 fail-fast] Attribute missing the member name (corrupt AST); no silent i32 fallback
self.Gen.TypeGenErr.fatal_error(ann_node, "enum member type annotation Attribute is missing the member name", "TypeError")
return mlirlite.Int32(pool) # unreachable (fatal_error already exited)
# t.State -> default i32
if string.strcmp(at.attr, "State") == 0: return mlirlite.Int32(pool)
# [enum bool rule] `t.CBool` and bare `bool` both materialize as 8 bits in enum member annotations,
# else the same member would split into i1 / i8; the global `t.CBool` stays i1 (only enums differ).
if string.strcmp(at.attr, "CBool") == 0: return mlirlite.Int8(pool)
# Other t.CXxx types: unified (TypedefT dynamic table first, fall back to map_builtin_type)
# Other qualified types: resolve through the TypedefT table first.
ty: mlirlite.LLVMType* = self.Gen.TypeGenResolve.resolve_typedef_or_map(pool, at.attr, imported_modules, from_imports)
if ty is not None: return ty
# [S4 fail-fast] annotated but unresolvable -> no silent i32 fallback
self.Gen.TypeGenErr.fatal_error(ann_node, f"failed to resolve enum member type annotation: t.{at.attr}", "NameError")
self.Gen.TypeGenErr.fatal_error(ann_node, f"failed to resolve enum member type annotation: {at.attr}", "NameError")
return mlirlite.Int32(pool) # unreachable (fatal_error already exited)
# Name node: from-import form (State / CInt)
@@ -598,16 +594,7 @@ novirtual class ClassGenEnum(ClassGenBase.ClassGenBase):
if string.strcmp(type_name, "ptrdifft") == 0: return 8
# Builtin types
if string.strcmp(type_name, "int") == 0: return 4
if string.strcmp(type_name, "CInt") == 0: return 4
if string.strcmp(type_name, "CInt8T") == 0: return 1
if string.strcmp(type_name, "CInt16T") == 0: return 2
if string.strcmp(type_name, "CInt32T") == 0: return 4
if string.strcmp(type_name, "CInt64T") == 0: return 8
if string.strcmp(type_name, "CChar") == 0: return 1
if string.strcmp(type_name, "CDouble") == 0: return 8
if string.strcmp(type_name, "CFloat") == 0: return 4
if string.strcmp(type_name, "PtrT") == 0: return 8
if string.strcmp(type_name, "CSizeT") == 0: return 8
if string.strcmp(type_name, "str") == 0: return 8
if string.strcmp(type_name, "bytes") == 0: return 8
if string.strcmp(type_name, "bool") == 0: return 1
+4 -4
View File
@@ -608,7 +608,7 @@ novirtual class ExprGenAttr(ExprGenBase.ExprGenBase):
# if obj_ptr is Ptr(Ptr(Struct)) (the alloca of an X* variable), load to deref and get Ptr(Struct)
obj_ptr = self._deref_if_ptr_ptr(builder, obj_ptr)
# rawptr[t.VolatileT | T] base (volatile pointee): loads of fields (p.field) through this pointer carry
# rawptr[volatile | T] base (volatile pointee): loads of fields (p.field) through this pointer carry
# volatile. It is 1 only when the base resolves to a VarEntry (Name) / FieldEntry (Attribute), else 0.
vol_ba_rd: int = self.base_pointee_is_volatile(pool, trans, at.value)
@@ -702,9 +702,9 @@ novirtual class ExprGenAttr(ExprGenBase.ExprGenBase):
opt_fp: mlirlite.Value* = mlirlite.build_gep_struct(builder, struct_ty, field_ty, obj_ptr, field_info.Index)
if opt_fp is None: return None
return self._optional_field_result(pool, builder, trans, obj_ptr, opt_fp, field_ty, 0)
# t.VolatileT field: loads through the object carry the volatile qualifier
# volatile field: loads through the object carry the volatile qualifier
field_vol_rd: int = self.Gen.StructGen.field_is_volatile(field_info)
# merge "the field's pointee is volatile": when the base is rawptr[t.VolatileT|T],
# merge "the field's pointee is volatile": when the base is rawptr[volatile|T],
# field loads through it must also be volatile (OR of both flags)
if vol_ba_rd != 0: field_vol_rd = 1
# union: bitcast obj_ptr to field_ty* then load
@@ -913,7 +913,7 @@ novirtual class ExprGenAttr(ExprGenBase.ExprGenBase):
fe: GenTypes.FieldEntry* = self.Gen.StructGen.lookup_field_by_class(parent_class, at.attr, trans.ModuleSha1, at, trans._from_imports)
return self.Gen.StructGen.field_is_volatile(fe)
# pointee volatile (rawptr[t.VolatileT | T], i.e. C's volatile T*): resolves a base to a
# pointee volatile (rawptr[volatile | T], i.e. C's volatile T*): resolves a base to a
# VarEntry/FieldEntry so that loads/stores through p[i] / p.field / *p carry the volatile qualifier.
def _resolve_attr_field_entry(self, pool: memhub.MemBuddy*, trans: HT.Translator*, attr_node: ast.AST*) -> GenTypes.FieldEntry*:
"""Return the FieldEntry for an obj.field access via a non-fatal read-only resolution."""
+11 -11
View File
@@ -577,7 +577,7 @@ novirtual class ExprGenBase:
return self.Gen.AssignGen._stg_ty_equal(pool, a, b)
# _agg_value_slot_ty - 值语义槽:源表达式的变量槽**自身**持有聚合物(`x: Foo`,Spec.PtrLevel == 0)。
# 真·指针值(`p: Foo*`,以及**裸类形参** `node: T` —— 其 ABI 即 T*,见 ClassGenVTable "objects are
# pointers")的 Spec.PtrLevel >= 1,返回 None,仍必须显式解引用(c.deref(p) / *p)。
# pointers")的 Spec.PtrLevel >= 1,返回 None,仍必须显式解引用(*p / *p)。
# 返回该聚合类型;不适用时 None。
def _agg_value_slot_ty(self, node: ast.AST*, val: mlirlite.Value*, trans: HT.Translator*) -> mlirlite.LLVMType*:
"""Aggregate type when the source is a variable slot holding the aggregate by value; else None."""
@@ -779,9 +779,9 @@ novirtual class ExprGenBase:
m_p2i: str = "implicit pointer-to-integer conversion is forbidden: an explicit cast (e.g. uint64(p) / int64(p) / int(p)) is required to assign a pointer to an integer type"
# 带上两端类型与被赋值的变量名:否则只剩一句泛泛的禁令,无法判断是指针变量、
# 被改型的槽,还是上游把函数返回类型解析成了指针。
p2i_txt: str = GenApi.escape_fstring_copy(f"; source type {self._type_text(builder.Pool, val.Ty)} -> target type {self._type_text(builder.Pool, target_ty)}")
# 不透明指针打印出来都是 !llvm.ptr,必须补 pointee 才能看出到底指向哪个结构体
if self.get_ptr_level(val.Ty) > 0 and val.Ty.Pointee is not None:
p2i_txt: str = GenApi.escape_fstring_copy(f"; source type {self._type_text(builder.Pool, val.Ty)} -> target type {self._type_text(builder.Pool, target_ty)}")
# 不透明指针打印出来都是 !llvm.ptr,必须补 pointee 才能看出到底指向哪个结构体
if self.get_ptr_level(val.Ty) > 0 and val.Ty.Pointee is not None:
p2i_txt = f"{p2i_txt}; source pointee {self._type_text(builder.Pool, val.Ty.Pointee)}"
p2i_var: str = ""
vn_p2i: ast.AST* = self._value_node_of(node)
@@ -820,16 +820,16 @@ novirtual class ExprGenBase:
if iv < lo_c or iv > hi_c:
self.Gen.TypeGenErr.fatal_error(node, f"narrowing truncation overflow: constant {iv} is outside the {target_bits}-bit representable range [{lo_c}, {hi_c}]; use an explicit cast or widen the target type", "OverflowError")
return mlirlite.build_trunc(builder, val, target_ty)
# 规则 2026-10-13:目标 i1(bool / t.CBool)**不再**对宽整型做隐式真值归一化
# 规则 2026-10-13:目标 i1(bool / bool)**不再**对宽整型做隐式真值归一化
# (旧的 `x != 0 -> 1/0`)。真值归一化会掩盖真实的位宽不匹配(如 `b: bool = some_i32`),
# 属静默转换,必须 fail-fast;需要显式 `bool(x)` / `t.CBool(x)`。
# 属静默转换,必须 fail-fast;需要显式 `bool(x)` / `bool(x)`。
# 合法写法不受影响:比较(!=/==/…)、`and` / `or`、`not` 本身即产出 i1
# (translate_compare / translate_boolop / translate_unaryop),i1 源走上面的
# `val_bits == target_bits` 直接放行;True/False 常量走上面的常量豁免。
if target_bits == 1:
self.Gen.TypeGenErr.fatal_error(node, f"implicit narrowing is forbidden: a {val_bits}-bit value cannot be implicitly assigned to bool (i1) (it would be silently reduced to its truthiness); use an explicit cast (e.g. bool(x) / t.CBool(x))", "CastError")
self.Gen.TypeGenErr.fatal_error(node, f"implicit narrowing is forbidden: a {val_bits}-bit value cannot be implicitly assigned to bool (i1) (it would be silently reduced to its truthiness); use an explicit cast (e.g. bool(x))", "CastError")
return val
self.Gen.TypeGenErr.fatal_error(node, f"implicit narrowing is forbidden: a {val_bits}-bit value cannot be implicitly assigned to a {target_bits}-bit type (the high bits would be lost); use an explicit cast (e.g. int32(x) / t.CInt32T(x)) or widen the target type", "CastError")
self.Gen.TypeGenErr.fatal_error(node, f"implicit narrowing is forbidden: a {val_bits}-bit value cannot be implicitly assigned to a {target_bits}-bit type (the high bits would be lost); use an explicit cast (e.g. int32(x)) or widen the target type", "CastError")
return val
# Float conversion.
val_fbits: int = self.get_llvm_float_bits(val.Ty)
@@ -869,7 +869,7 @@ novirtual class ExprGenBase:
# 整个调用期间有效(生命周期覆盖调用),故合法用例不会悬垂。
# 唯有被调者**保存**此地址(如容器 append 挂链)才会悬垂 —— 这类写法必须由
# 源码显式声明为指针形参(`node: T*`)并传入既有对象的地址(此时实参本就是
# 指针,不会走到此处隐式拷贝)。显式 T* / &v / c.addr(...) 的正常用法不受影响。
# 指针,不会走到此处隐式拷贝)。显式 T* / &v / &v 的正常用法不受影响。
# 注意(2026-10-22):豁免 (b) 与"裸类形参 `f(a: T)` 的 ABI 是 T*"共用同一条
# 路径,coerce 这里只能看到 LLVM 的 `T*`,无法区分形参源码上写的是 `T` 还是
# `T*`。因此"值传给 T* 形参须显式 &a"这一条尚未在此处收紧,需在调用侧按
@@ -883,7 +883,7 @@ novirtual class ExprGenBase:
if same_v2p == 0:
ctor_v2p: mlirlite.LLVMType* = self._ctor_call_agg_ty(node, trans)
if self._same_agg_ty(builder.Pool, ctor_v2p, val.Ty) == 0:
m_v2p: str = "implicit value/pointer conversion for non-basic types is forbidden (except construction materialization): a non-basic value cannot be implicitly converted to a pointer (Foo -> Foo*); to take its address use c.addr(...) / &v explicitly, or cast explicitly"
m_v2p: str = "implicit value/pointer conversion for non-basic types is forbidden (except construction materialization): a non-basic value cannot be implicitly converted to a pointer (Foo -> Foo*); to take its address use &v / &v explicitly, or cast explicitly"
v2p_txt: str = GenApi.escape_fstring_copy(f"; source value type {self._type_text(builder.Pool, val.Ty)} -> target type {self._type_text(builder.Pool, target_ty)}")
self.Gen.TypeGenErr.fatal_error(self._value_node_of(node), f"{m_v2p}{v2p_txt}", "CastError")
return val
@@ -903,7 +903,7 @@ novirtual class ExprGenBase:
# `T*`(对象即指针),把它赋给同型值字段/变量时按值拷贝;`x: T` 这类值语义槽指针同理。
# 拷贝后目标独立、不会悬垂 —— 与「传参用指针」的 ABI 选择互不冲突。
# ② 构造物化(见 _ctor_call_agg_ty)。
# 其余(不同型聚合 / 多级指针)仍 fail-fast,须显式 *p / c.deref(p) 或显式 cast。
# 其余(不同型聚合 / 多级指针)仍 fail-fast,须显式 *p / *p 或显式 cast。
if self.is_ptr_type(val.Ty) != 0 and self.is_ptr_type(target_ty) == 0:
target_kind: int = target_ty.kind()
if target_kind == mlirlite.LLVMType.Struct or target_kind == mlirlite.LLVMType.Array:
+5 -19
View File
@@ -113,7 +113,7 @@ novirtual class ExprGenCdef(ExprGenBase.ExprGenBase):
i += 1
return 0
# _lookup_cross_module_cdefine - cross-module DefineT lookup: resolve the source module
# from from_imports, parse its pyi "NAME: t.DefineT = value", then cache in the current module.
# from from_imports, parse its pyi "NAME: DefineT = value", then cache in the current module.
def _lookup_cross_module_cdefine(self, pool: memhub.MemBuddy*, name: str, from_imports: str, imported_modules: str = None, depth: int = 0, node: ast.AST* = None) -> int64:
"""Look up a DefineT constant across modules, returning its 64-bit value or -1"""
# recursion-depth guard: re-export chains + imported_modules walk can loop and OOM;
@@ -266,7 +266,7 @@ novirtual class ExprGenCdef(ExprGenBase.ExprGenBase):
if pyi_buf[cs_j2] == '=':
cs_eq2 = 1
break
if cs_j2 + 9 <= cs_le and string.strncmp(pyi_buf + cs_j2, "t.DefineT", 9) == 0: cs_marker = 1
if cs_j2 + 9 <= cs_le and string.strncmp(pyi_buf + cs_j2, "DefineT", 9) == 0: cs_marker = 1
cs_j2 += 1
if cs_marker != 0 and cs_eq2 != 0:
cd_sig = 1
@@ -299,8 +299,8 @@ novirtual class ExprGenCdef(ExprGenBase.ExprGenBase):
return -1
if bytes_read < PYI_READ_BUF_SIZE: pyi_buf[bytes_read] = 0
# 8. search pyi_buf for the "name: t.DefineT = value" line
# format: NAME: t.DefineT = value
# 8. search pyi_buf for the "name: DefineT = value" line
# format: NAME: DefineT = value
name_len: sizet = string.strlen(name)
total_len: sizet = string.strlen(pyi_buf)
pos: sizet = 0
@@ -370,21 +370,7 @@ novirtual class ExprGenCdef(ExprGenBase.ExprGenBase):
# skip spaces
while eq_pos < line_start + line_len and pyi_buf[eq_pos] == ' ': eq_pos += 1
# check for type-ctor forms like t.CUnsignedLong(...) / t.CLong(...) / int(...);
# if so, skip the "t.CXxx(" prefix, parse inside the parens, ignore trailing ')'.
is_type_ctor: int = 0
if eq_pos + 2 < line_start + line_len:
if pyi_buf[eq_pos] == 't' and pyi_buf[eq_pos + 1] == '.':
# find the '(' position
paren_pos: sizet = eq_pos + 2
while paren_pos < line_start + line_len and pyi_buf[paren_pos] != '(': paren_pos += 1
if paren_pos < line_start + line_len and pyi_buf[paren_pos] == '(':
is_type_ctor = 1
eq_pos = paren_pos + 1 # skip '('
# skip possible leading spaces inside the parens
while eq_pos < line_start + line_len and pyi_buf[eq_pos] == ' ': eq_pos += 1
# paren-wrapped flag: (1 << 23) / (512) / (A | B)
# paren-wrapped flag: (1 << 23) / (512)
is_paren_expr: int = 0
if eq_pos < line_start + line_len and pyi_buf[eq_pos] == '(':
is_paren_expr = 1
+5 -5
View File
@@ -143,15 +143,15 @@ novirtual class ExprGenConst(ExprGenBase.ExprGenBase):
bits: int = self.get_llvm_type_bits(ty)
if bits != 0:
if bits == 32: return "int"
if bits == 64: return "CSizeT"
if bits == 8: return "CInt8T"
if bits == 16: return "CInt16T"
if bits == 64: return "sizet"
if bits == 8: return "int8"
if bits == 16: return "int16"
return None
if self.is_ptr_type(ty) != 0: return "str"
fbits: int = self.get_llvm_float_bits(ty)
if fbits != 0:
if fbits == 64: return "CDouble"
if fbits == 32: return "CFloat"
if fbits == 64: return "double"
if fbits == 32: return "float"
return None
# _find_pool_var - find the pool variable in the context.
# Tries "pool", "_mbuddy", "mbuddy", "mb" in order.
+8 -13
View File
@@ -49,7 +49,7 @@ novirtual class ExprGenName(ExprGenBase.ExprGenBase):
if self.Gen.TypeGenCDef.is_cdefine_found() != 0:
if trans is None or self.Gen.VarGen.lookup_var(trans.SymTab, nm_id) is None:
if self.Gen.TypeGenCDef.is_cdefine_str() != 0:
# String DefineT (e.g. ZLIB_VERSION: t.DefineT = "1.3.2"): return a global
# String DefineT (e.g. ZLIB_VERSION: DefineT = "1.3.2"): return a global
# string (i8*) for `return ZLIB_VERSION`.
cdef_sv: str = self.Gen.TypeGenCDef.lookup_cdefine_string(nm_id)
if cdef_sv is not None:
@@ -139,7 +139,7 @@ novirtual class ExprGenName(ExprGenBase.ExprGenBase):
return mod_alloca
case _:
pass
# t.VolatileT global: load carries volatile (prevents optimizing away repeated reads)
# volatile global: load carries volatile (prevents optimizing away repeated reads)
vol_g: int = 0
if trans is not None:
ve_g: GenTypes.VarEntry* = self.Gen.VarGen.lookup_var_entry(trans.SymTab, nm_id)
@@ -156,7 +156,7 @@ novirtual class ExprGenName(ExprGenBase.ExprGenBase):
if trans._va_list_ptr is not None: return trans._va_list_ptr
# Local variable lookup must precede the module-alias check, or a local with the same
# name as a module (e.g. local `c` after `import t, c`) would be misread and not found.
# name as a module would be misread and not found.
alloca: mlirlite.Value* = self.Gen.VarGen.lookup_var(trans.SymTab, nm_id)
if alloca is not None:
load_ty: mlirlite.LLVMType* = None
@@ -182,7 +182,7 @@ novirtual class ExprGenName(ExprGenBase.ExprGenBase):
# Array variables decay to a pointer (base address) per C semantics; loading the
# whole array value into "%s" reads wrong data (ClassArrayTest crash root cause).
return alloca
# t.VolatileT local: load carries volatile
# volatile local: load carries volatile
vol_l: int = 0
bo_l: int = 0
if trans is not None:
@@ -289,10 +289,6 @@ novirtual class ExprGenName(ExprGenBase.ExprGenBase):
# All lookup paths failed: fail-fast (NameError) instead of returning None and crashing
# downstream. Exempt keywords/builtins (self/pool/None/_) whose absence is normal.
if nm_id is None: return None
if string.strcmp(nm_id, "CIf") == 0 or string.strcmp(nm_id, "CIfdef") == 0 or string.strcmp(nm_id, "CIfndef") == 0 or string.strcmp(nm_id, "CElif") == 0 or string.strcmp(nm_id, "CElse") == 0 or string.strcmp(nm_id, "CEndif") == 0:
# The c.CIf-family spellings were removed; report the replacement.
self.Gen.TypeGenErr.fatal_error(node, "c.CIf/c.CIfdef/c.CIfndef/c.CElif/c.CElse/c.CEndif were removed; use the cif/celif/celse statements and the defined(NAME) predicate instead (e.g. `cif defined(NAME):` ... `celse:` ...)", "NameError")
return None
if string.strcmp(nm_id, "self") == 0 or string.strcmp(nm_id, "pool") == 0 or string.strcmp(nm_id, "true") == 0 or string.strcmp(nm_id, "false") == 0 or string.strcmp(nm_id, "None") == 0 or string.strcmp(nm_id, "match") == 0 or string.strcmp(nm_id, "_") == 0:
return None
# Diagnostics: report the generic context, since "undefined name 'T'" is often far from the
@@ -523,7 +519,7 @@ novirtual class ExprGenName(ExprGenBase.ExprGenBase):
if sub.value is not None and sub.value.kind() == ast.ASTKind.Name:
nm: ast.Name* = ast.Name*(sub.value)
if nm.id is not None and trans is not None:
# rawptr[t.VolatileT | T] variable (volatile pointee): element loads (p[i] dereference)
# rawptr[volatile | T] variable (volatile pointee): element loads (p[i] dereference)
# carry volatile; loading the pointer value itself does not.
vol_sub_rd: int = self.Gen.VarGen.var_pointee_is_volatile(self.Gen.VarGen.lookup_var_entry(trans.SymTab, nm.id))
# nonlocal array/pointer: read the shared heap address from env (the outer alloca
@@ -738,7 +734,7 @@ novirtual class ExprGenName(ExprGenBase.ExprGenBase):
# Check whether it is a pointer type
if self.is_ptr_type(ptr_val.Ty) != 0:
# Generic-path base (e.g. self.ptr / a complex expression): element loads carry volatile only
# when the base resolves to a VarEntry/FieldEntry marked rawptr[t.VolatileT|T]; otherwise 0.
# when the base resolves to a VarEntry/FieldEntry marked rawptr[volatile|T]; otherwise 0.
vol_sub_gen: int = self.Gen.ExprGenAttr.base_pointee_is_volatile(pool, trans, sub.value)
elem_ty2: mlirlite.LLVMType* = ptr_val.Ty.Pointee
if elem_ty2 is not None:
@@ -775,9 +771,8 @@ novirtual class ExprGenName(ExprGenBase.ExprGenBase):
def _annot_is_64bit_scalar(self, annot: str) -> int:
"""Return 1 when the annotation is a 64-bit scalar | rawptr union (element type Int64)."""
if annot is None: return 0
if string.strstr(annot, "CInt64T") is not None: return 1
if string.strstr(annot, "CUInt64T") is not None: return 1
if string.strstr(annot, "CSizeT") is not None: return 1
if string.strstr(annot, "int64") is not None: return 1
if string.strstr(annot, "sizet") is not None: return 1
return 0
# subscript_base_store_kind - read-only base storage classification: 1 = array slot (whole-object
+2 -2
View File
@@ -274,7 +274,7 @@ novirtual class ExprGenOps(ExprGenBase.ExprGenBase):
if fe_fn_u.RetSign == GenTypes.FUNC_RET_SIGN_UNSIGNED: return 1
if fe_fn_u.Spec is not None and TSpec.SpecIsUnsigned(fe_fn_u.Spec.Ret) != 0: return 1
elif fk_u == ast.ASTKind.Attribute:
# Qualified type cast: t.CUInt16T(x); then method / module-qualified calls.
# Qualified type cast: uint16(x); then method / module-qualified calls.
at_u: ast.Attribute* = ast.Attribute*(cl.func)
if at_u.attr is not None and self._type_name_unsigned(at_u.attr) != 0: return 1
fe_at_u: GenTypes.FuncEntry* = self._attr_call_ret_entry(pool, at_u, trans)
@@ -433,7 +433,7 @@ novirtual class ExprGenOps(ExprGenBase.ExprGenBase):
fe_fn_s: GenTypes.FuncEntry* = self.Gen.CallGenLeafKit.find_func_entry_global(nm_c.id)
if fe_fn_s is not None and fe_fn_s.RetSign == GenTypes.FUNC_RET_SIGN_SIGNED: return 1
elif fk_s == ast.ASTKind.Attribute:
# Qualified signed cast (t.CInt16T(x)) or method / module-qualified call.
# Qualified signed cast (int16(x)) or method / module-qualified call.
at_c: ast.Attribute* = ast.Attribute*(cl.func)
if at_c.attr is not None:
if self._spec_sign_from_annot(at_c.attr) == TSpec.TS_SIGN_SIGNED: return 1
+8 -8
View File
@@ -52,7 +52,7 @@ novirtual class FuncGenMixin(CallGenBase.CallGenBase):
# → 撞 coerce_to_type 的 CastError(现场:EscapeMethodTest 的 (C) 用例)。那是"嵌套 def
# 只支持 int 返回"这个预先存在的缺口,此前被同文件更早的用例报错掩盖。
# 约定(最小改动的边界):
# · `fd.returns is None` 或注解是 `None` / `t.CVoid` → **保持 i32**(不改既有 ABI,
# · `fd.returns is None` 或注解是 `None` / `t.void` → **保持 i32**(不改既有 ABI,
# 避免打断现有"无返回值/无注解"的嵌套 def;这是已知缺口,不在本次范围内);
# · 其余注解采用 resolve_annotation_type 的结果;解析不出 → fail-fast,不静默兜底。
ret_ty_n: mlirlite.LLVMType* = i32_ty
@@ -61,7 +61,7 @@ novirtual class FuncGenMixin(CallGenBase.CallGenBase):
if fd.returns.kind() == ast.ASTKind.Constant:
rc_n: ast.Constant* = ast.Constant*(fd.returns)
if rc_n is not None and rc_n.const_kind == ast.CONST_NONE: keep_i32_n = 1
if self.Gen.TypeGenResolve._is_cvoid_marker(fd.returns, trans._imported_modules) != 0: keep_i32_n = 1
if self.Gen.TypeGenResolve._is_void_marker(fd.returns) != 0: keep_i32_n = 1
if keep_i32_n == 0:
rt_res_n: mlirlite.LLVMType* = self.Gen.TypeGenResolve.resolve_annotation_type(pool, fd.returns, trans._imported_modules, trans._from_imports, trans, 0)
if rt_res_n is None:
@@ -79,7 +79,7 @@ novirtual class FuncGenMixin(CallGenBase.CallGenBase):
mlirlite.add_param(pool, func, i8_ptr_ty, "%env")
# 2a. Nested function params join the promoted signature (i8* %env, args...);
# types go into the param_tys buffer (CVoid skipped, aligning with alloca indices).
# types go into the param_tys buffer (void skipped, aligning with alloca indices).
imported_modules: str = trans._imported_modules
from_imports: str = trans._from_imports
param_tys: rawptr = pool.alloc(8 * 64)
@@ -94,7 +94,7 @@ novirtual class FuncGenMixin(CallGenBase.CallGenBase):
arg_n: ast.Arg* = ast.Arg*(alist_n.get(ai_n))
if arg_n is None or arg_n.arg is None: continue
if arg_n.annotation is not None:
if self.Gen.TypeGenResolve._is_builtin_marker(arg_n.annotation, "CVoid", imported_modules) != 0: continue
if self.Gen.TypeGenResolve._is_bare_void(arg_n.annotation) != 0: continue
pty_n: mlirlite.LLVMType* = i32_ty
if arg_n.annotation is not None:
resolved_n: mlirlite.LLVMType* = self.Gen.TypeGenResolve.resolve_annotation_type(pool, arg_n.annotation, imported_modules, from_imports, trans, 0)
@@ -147,7 +147,7 @@ novirtual class FuncGenMixin(CallGenBase.CallGenBase):
arg_p: ast.Arg* = ast.Arg*(alist_p.get(api))
if arg_p is None or arg_p.arg is None: continue
if arg_p.annotation is not None:
if self.Gen.TypeGenResolve._is_builtin_marker(arg_p.annotation, "CVoid", imported_modules) != 0: continue
if self.Gen.TypeGenResolve._is_bare_void(arg_p.annotation) != 0: continue
if idx_p >= param_count: break
pty2_n: mlirlite.LLVMType* = i32_ty
if param_tys is not None:
@@ -165,12 +165,12 @@ novirtual class FuncGenMixin(CallGenBase.CallGenBase):
cls_nm_p: str = self.Gen.TypeGenResolve.extract_class_name_from_annotation(arg_p.annotation, imported_modules, pool, trans._from_imports)
if cls_nm_p is not None: self.Gen.VarGen.set_var_annot_class_name(trans.SymTab, arg_p.arg, cls_nm_p)
# const param: keep TPC's historical test (only whether the annotation contains
# t.ConstT, not the pointer depth); both sides align per type-descriptors.md.
# const, not the pointer depth); both sides align per type-descriptors.md.
if self.Gen.TypeGenErr.has_decorator_marker(arg_p.annotation, "ConstT", 0) != 0: self.Gen.VarGen.set_var_const(trans.SymTab, arg_p.arg)
# t.VolatileT param: mark at declaration (same as the FuncGenDef main path) so
# volatile param: mark at declaration (same as the FuncGenDef main path) so
# load/store of this param name in the body carry volatile.
if self.Gen.TypeGenErr.has_decorator_marker(arg_p.annotation, "VolatileT", 0) != 0: self.Gen.VarGen.set_var_volatile(trans.SymTab, arg_p.arg)
# rawptr[t.VolatileT | T] param (volatile pointee): dereferences in the body carry
# rawptr[volatile | T] param (volatile pointee): dereferences in the body carry
# volatile (same as the FuncGenDef main path).
if self.Gen.TypeGenErr.has_pointee_volatile_marker(arg_p.annotation) != 0: self.Gen.VarGen.set_var_pointee_volatile(trans.SymTab, arg_p.arg)
pname2_n: str = pool.alloc(32)
+2 -2
View File
@@ -269,14 +269,14 @@ def has_decorator_marker(node: ast.AST*, marker_name: str, depth: int = 0) -> in
# ---------------- has_pointee_volatile_marker (delegates to TypeGenErr.has_pointee_volatile_marker) ----------------
def has_pointee_volatile_marker(node: ast.AST*) -> int:
"""Delegate to gen.has_pointee_volatile_marker (rawptr[t.VolatileT | T] field/var pointee)"""
"""Delegate to gen.has_pointee_volatile_marker (rawptr[volatile | T] field/var pointee)"""
if _g_gen is not None: return _g_gen.TypeGenErr.has_pointee_volatile_marker(node)
return 0
# ---------------- is_const_object_marker (delegates to TypeGenErr.is_const_object_marker) ----------------
def is_const_object_marker(node: ast.AST*, var_ty: mlirlite.LLVMType*) -> int:
"""Delegate to gen.is_const_object_marker (t.ConstT non-pointer object field/var)"""
"""Delegate to gen.is_const_object_marker (const non-pointer object field/var)"""
if _g_gen is not None: return _g_gen.TypeGenErr.is_const_object_marker(node, var_ty)
return 0
-4
View File
@@ -618,10 +618,6 @@ novirtual class ImportsGenMixin:
def _check_import_sha1(self, node: ast.AST*, mod_name: str) -> int:
"""Fail-fast when an imported module's SHA1 is not registered."""
if mod_name is None: return 1
# t / c were retired: their types are builtin and their C helpers now have language syntax.
if string.strcmp(mod_name, "t") == 0 or string.strcmp(mod_name, "c") == 0:
self._report_import_error(node, "`import t` / `import c` is retired; builtin types and C helpers no longer live in modules")
return 0
# ctraits (.vpi compile-time macro lib), viperbuild (compiler builtin macro lib) and
# ffilib (compiler builtin FFI-declaration macro lib) stay exempt: they have no module
# SHA1 because they emit no code at all (Pass 0 reads them, translation erases them).
+2 -2
View File
@@ -2573,7 +2573,7 @@ novirtual class StructGenMixin:
if entry is not None and entry.Name is not None:
if string.strcmp(entry.Name, name) == 0: return entry
# Module-qualified name (module.Class): when the module resolves, match by module ownership exactly, to stop the bare-name fallback from grabbing a cross-module same-named class; if the module misses but the bare name is unique, fall back.
# Take the dot outside generic brackets (numpy.ndarray[t.CDouble] -> numpy + ndarray[t.CDouble]), else strrchr lands inside the subscript and truncates the module name (same root cause as lookup_field_by_class).
# Take the dot outside generic brackets (numpy.ndarray[double] -> numpy + ndarray[double]), else strrchr lands inside the subscript and truncates the module name (same root cause as lookup_field_by_class).
dot_pos_q: str = self._last_dot_outside_brackets(name)
if dot_pos_q is not None and dot_pos_q[1] != '\0':
bare_f: str = dot_pos_q + 1
@@ -2780,7 +2780,7 @@ novirtual class StructGenMixin:
# Supports module-qualified names: class_name may be "HT.Translator" while entry.Name is "Translator"; try exact match first, then fall back to the strrchr-extracted class name.
if sha1 is not None:
sha1_matched: int = 0
# Compute the class-name fallback ("HT.Translator" -> "Translator"; "numpy.ndarray[t.CDouble]" -> "ndarray[t.CDouble]").
# Compute the class-name fallback ("HT.Translator" -> "Translator"; "numpy.ndarray[double]" -> "ndarray[double]").
bare_name: str = class_name
dot_pos_lfbc: str = self._last_dot_outside_brackets(class_name)
if dot_pos_lfbc is not None:
+1 -1
View File
@@ -30,7 +30,7 @@ define MAX_VARS = 1024
# Function-table capacity (single source of truth). Allocation happens in _init_state;
# the registration-side max_count passed to add_func_to_table must be <= this capacity.
define MAX_FUNCS = 1024
# Reference convention: cross-module t.DefineT is readable only inside functions, so write
# Reference convention: cross-module DefineT is readable only inside functions, so write
# qualified names there; the global/nonlocal name-table capacity must match on both guard sides.
define MAX_GLOBAL_NAMES = 128
# Coroutine table capacity (VPC-M1): async function count / coroutine var and local-name count
+5 -5
View File
@@ -559,7 +559,7 @@ novirtual class TranslatorGenMixin:
# t.StaticT vars (no cross-module use) are registered as (name, def module SHA1);
# the logic stays inside _RegisterStaticModuleVar to avoid disturbing type inference.
self._RegisterStaticModuleVar(trans, annot_node, var_name)
# Module-level t.Callable function pointers register (name -> return type) here so
# Module-level Callable function pointers register (name -> return type) here so
# indirect calls resolve; done before any body translation, same as in-function AnnAssign.
if annot_node is not None:
ml_callable_ret: mlirlite.LLVMType* = self.Gen.TypeGenErr.try_extract_callable_ret(pool, annot_node, trans._imported_modules,trans._from_imports, trans)
@@ -984,15 +984,15 @@ novirtual class TranslatorGenMixin:
# Register in the module scope
if self.Gen.VarGen.define_module_var(trans.SymTab, var_name, gv_ref) == 0:
# t.VolatileT storage marker (union annotation): mark the global volatile so all
# volatile storage marker (union annotation): mark the global volatile so all
# loads/stores carry LLVM volatile and busy-wait loops are not optimized away.
if annot_node is not None:
# const object global: a non-pointer annotation with t.ConstT registers IsConst,
# const object global: a non-pointer annotation with const registers IsConst,
# making later assignment/aug-assign/element-assign to the name fail-fast.
if self.Gen.TypeGenErr.is_const_object_marker(annot_node, var_ty) != 0: self.Gen.VarGen.set_var_const(trans.SymTab, var_name)
if self.Gen.TypeGenErr.has_decorator_marker(annot_node, "VolatileT", 0) != 0: self.Gen.VarGen.set_var_volatile(trans.SymTab, var_name)
# rawptr[t.VolatileT | T] (volatile pointee): loads/stores through p[i]/p.field/
# c.Deref(p) carry volatile; distinct from the variable's own volatile qualifier.
# rawptr[volatile | T] (volatile pointee): loads/stores through p[i]/p.field/
# *p carry volatile; distinct from the variable's own volatile qualifier.
if self.Gen.TypeGenErr.has_pointee_volatile_marker(annot_node) != 0: self.Gen.VarGen.set_var_pointee_volatile(trans.SymTab, var_name)
# Set AnnotClassName so attribute access can fall back to the class name when a union
# type like Logger* is simplified to Ptr(i8).
+1 -1
View File
@@ -48,7 +48,7 @@ novirtual class TypeGenBase:
# 判定"是不是浮点常量"一律用 `!= 0`(既有调用处口径不变),只有浮点常量物化处区分 64/32:
# `define PI: float = 3.14159...` 必须直接出 f32 常量,否则与 f32 字面量混算会多出 fpext。
CDefineDoubleFlags: list[int]*
CDefineStrFlags: list[int]* # 1=string value (e.g. ZLIB_VERSION: t.DefineT = "1.3.2")
CDefineStrFlags: list[int]* # 1=string value (e.g. ZLIB_VERSION: DefineT = "1.3.2")
# 值表达式的**位宽**(bit);0=未知(按值推断)。来源:右侧显式强转(如 WORD(0x0080))优先,
# 否则声明注解(define X: WORD = ... 的 T)。物化时据此定宽,避免小位宽常量被提升为 i32。
CDefineBits: list[int]*
+3 -3
View File
@@ -32,8 +32,8 @@ def _ctypedef_idx_hash(name: str) -> uint64:
# TypeGenCType - TypedefT table (register/lookup/constant evaluation);
# inheritance chain is one level; @t.NoVTable static binding.
@t.NoVTable
# inheritance chain is one level; @NoVTable static binding.
@NoVTable
class TypeGenCType(TypeGenBase.TypeGenBase):
"""TypedefT table: register, lookup, scan declarations, eval const ints."""
@@ -381,7 +381,7 @@ class TypeGenCType(TypeGenBase.TypeGenBase):
line_len: sizet = i - line_start
if line_len > 0 and td_buf is not None:
# Parse both equivalent forms: the sugar "typedef N = V" and the
# annotation "N: t.TypedefT = V".
# annotation "N: TypedefT = V".
if self.Gen.TypeGenResolve._parse_typedef_line(td_buf, src, line_start, line_len) != 0:
name_buf: str = td_buf
ty_buf: str = td_buf + string.strlen(td_buf) + 1
+4 -4
View File
@@ -100,10 +100,10 @@ novirtual class TypeGenErr(TypeGenBase.TypeGenBase):
"""Report an error in Python style and exit (TypeError variant)"""
return self.fatal_error(node, msg, "TypeError", col_start, col_end)
def try_extract_callable_ret(self, pool: memhub.MemBuddy*, annot: ast.AST*, imported_modules: str, from_imports: str, trans: HT.Translator*) -> mlirlite.LLVMType*:
"""Extract the return type of a function-pointer annotation (t.Callable[[A,B], R]); None for non-Callable or signature-less forms."""
"""Extract the return type of a function-pointer annotation (Callable[[A,B], R]); None for non-Callable or signature-less forms."""
if annot is None or annot.kind() != ast.ASTKind.Subscript: return None
sub_cr: ast.Subscript* = ast.Subscript*(annot)
# The subscript test uses TypeGenResolve as the single source of truth (the t.Callable
# The subscript test uses TypeGenResolve as the single source of truth (the Callable
# Attribute form is equivalent to the bare-name callable / Callable forms)
if self.Gen.TypeGenResolve._IsCallableSubscriptValue(sub_cr.value) == 0: return None
# slice must be Tuple(elts=[args_list, ret_node]); the one-element form
@@ -151,7 +151,7 @@ novirtual class TypeGenErr(TypeGenBase.TypeGenBase):
return 0
def has_pointee_volatile_marker(self, node: ast.AST*) -> int:
"""Whether a rawptr[...] subscript argument carries the t.VolatileT marker (pointee-side volatile), 1=yes / 0=no."""
"""Whether a rawptr[...] subscript argument carries the volatile marker (pointee-side volatile), 1=yes / 0=no."""
if node is None or node.kind() != ast.ASTKind.Subscript: return 0
sub: ast.Subscript* = ast.Subscript*(node)
if sub.value is None or sub.value.kind() != ast.ASTKind.Name: return 0
@@ -162,7 +162,7 @@ novirtual class TypeGenErr(TypeGenBase.TypeGenBase):
return self.has_decorator_marker(sub.slice, "VolatileT", 0)
def is_const_object_marker(self, node: ast.AST*, var_ty: mlirlite.LLVMType*) -> int:
"""Whether a variable/field is itself a const object (1=yes / 0=no): annotation carries t.ConstT and the declared LLVM type is not a pointer."""
"""Whether a variable/field is itself a const object (1=yes / 0=no): annotation carries const and the declared LLVM type is not a pointer."""
if self.has_decorator_marker(node, "ConstT", 0) == 0: return 0
if var_ty is None: return 0
if var_ty.kind() == mlirlite.LLVMType.Ptr: return 0
+34 -59
View File
@@ -82,19 +82,6 @@ def SpecTypeNameBaseIs(name: str, base: str) -> int:
def SpecSignFromTypeNameSpan(text: str, start: sizet, end: sizet) -> int:
"""Exact signedness of text[start:end]; TS_SIGN_UNKNOWN if not covered"""
if text is None: return TS_SIGN_UNKNOWN
# --- t/c core unsigned primitives ---
if _spec_name_eq(text, start, end, "CUInt8T") != 0: return TS_SIGN_UNSIGNED
if _spec_name_eq(text, start, end, "CUInt16T") != 0: return TS_SIGN_UNSIGNED
if _spec_name_eq(text, start, end, "CUInt32T") != 0: return TS_SIGN_UNSIGNED
if _spec_name_eq(text, start, end, "CUInt64T") != 0: return TS_SIGN_UNSIGNED
if _spec_name_eq(text, start, end, "CUnsignedChar") != 0: return TS_SIGN_UNSIGNED
if _spec_name_eq(text, start, end, "CUnsignedShort") != 0: return TS_SIGN_UNSIGNED
if _spec_name_eq(text, start, end, "CUnsignedInt") != 0: return TS_SIGN_UNSIGNED
if _spec_name_eq(text, start, end, "CUnsignedLong") != 0: return TS_SIGN_UNSIGNED
if _spec_name_eq(text, start, end, "CUnsignedLongLong") != 0: return TS_SIGN_UNSIGNED
if _spec_name_eq(text, start, end, "CSizeT") != 0: return TS_SIGN_UNSIGNED
if _spec_name_eq(text, start, end, "CUIntPtrT") != 0: return TS_SIGN_UNSIGNED
if _spec_name_eq(text, start, end, "CBool") != 0: return TS_SIGN_UNSIGNED
# --- builtin short names ---
if _spec_name_eq(text, start, end, "sizet") != 0: return TS_SIGN_UNSIGNED
if _spec_name_eq(text, start, end, "size_t") != 0: return TS_SIGN_UNSIGNED
@@ -135,18 +122,6 @@ def SpecSignFromTypeNameSpan(text: str, start: sizet, end: sizet) -> int:
if _spec_name_eq(text, start, end, "intptr") != 0: return TS_SIGN_SIGNED
# --- builtin lowercase scalars ---
if _spec_name_eq(text, start, end, "bool") != 0: return TS_SIGN_UNSIGNED
# --- t/c core signed primitives ---
if _spec_name_eq(text, start, end, "CInt8T") != 0: return TS_SIGN_SIGNED
if _spec_name_eq(text, start, end, "CInt16T") != 0: return TS_SIGN_SIGNED
if _spec_name_eq(text, start, end, "CInt32T") != 0: return TS_SIGN_SIGNED
if _spec_name_eq(text, start, end, "CInt64T") != 0: return TS_SIGN_SIGNED
if _spec_name_eq(text, start, end, "CSignedChar") != 0: return TS_SIGN_SIGNED
if _spec_name_eq(text, start, end, "CShort") != 0: return TS_SIGN_SIGNED
if _spec_name_eq(text, start, end, "CInt") != 0: return TS_SIGN_SIGNED
if _spec_name_eq(text, start, end, "CLong") != 0: return TS_SIGN_SIGNED
if _spec_name_eq(text, start, end, "CLongLong") != 0: return TS_SIGN_SIGNED
if _spec_name_eq(text, start, end, "CPtrDiffT") != 0: return TS_SIGN_SIGNED
if _spec_name_eq(text, start, end, "CChar") != 0: return TS_SIGN_SIGNED
if _spec_name_eq(text, start, end, "int") != 0: return TS_SIGN_SIGNED
if _spec_name_eq(text, start, end, "char") != 0: return TS_SIGN_SIGNED
return TS_SIGN_UNKNOWN
@@ -450,40 +425,40 @@ def SpecToText(sp: TypeSpec*, buf: str, buf_size: sizet) -> int:
return r
# _sig_hex_char - lower-case hex digit for 0..15
def _sig_hex_char(n: int) -> char:
"""Lower-case hex digit for a 0..15 nibble"""
if n < 10: return char('0' + n)
return char('a' + (n - 10))
# SpecSigHash16 - 16-hex SHA1 of a canonical parameter-signature text.
# This is the overload symbol suffix: "{module sha1}.{name}_v{hash}". The text is built from
# SpecToText, which already expands aliases to iN/uN/fN, writes pointers as p:, arrays as
# arr[N]: and structs as st:{module sha1}.Name -- so `uint` and `int` never collide, and a
# cross-module class reference carries its owning module's sha1.
def SpecSigHash16(text: str) -> str:
"""Return the 16-hex signature hash of a canonical text (PermPool string); None on failure"""
if text is None: return None
ctx: hashlib.sha1* = hashlib.sha1()
if ctx is None: return None
ctx.update(text)
digest: bytes = stdlib.malloc(hashlib.SHA1_DIGEST_LEN)
if digest is None: return None
ctx.final(digest)
out: str = CompilerFactory.factory.PermPool.alloc(17)
if out is None:
stdlib.free(digest)
return None
for i in range(8):
b: int = int(digest[i])
out[i * 2] = _sig_hex_char((b >> 4) & 0xF)
out[i * 2 + 1] = _sig_hex_char(b & 0xF)
out[16] = '\0'
stdlib.free(digest)
return out
# _sig_hex_char - lower-case hex digit for 0..15
def _sig_hex_char(n: int) -> char:
"""Lower-case hex digit for a 0..15 nibble"""
if n < 10: return char('0' + n)
return char('a' + (n - 10))
# SpecSigHash16 - 16-hex SHA1 of a canonical parameter-signature text.
# This is the overload symbol suffix: "{module sha1}.{name}_v{hash}". The text is built from
# SpecToText, which already expands aliases to iN/uN/fN, writes pointers as p:, arrays as
# arr[N]: and structs as st:{module sha1}.Name -- so `uint` and `int` never collide, and a
# cross-module class reference carries its owning module's sha1.
def SpecSigHash16(text: str) -> str:
"""Return the 16-hex signature hash of a canonical text (PermPool string); None on failure"""
if text is None: return None
ctx: hashlib.sha1* = hashlib.sha1()
if ctx is None: return None
ctx.update(text)
digest: bytes = stdlib.malloc(hashlib.SHA1_DIGEST_LEN)
if digest is None: return None
ctx.final(digest)
out: str = CompilerFactory.factory.PermPool.alloc(17)
if out is None:
stdlib.free(digest)
return None
for i in range(8):
b: int = int(digest[i])
out[i * 2] = _sig_hex_char((b >> 4) & 0xF)
out[i * 2 + 1] = _sig_hex_char(b & 0xF)
out[16] = '\0'
stdlib.free(digest)
return out
def _spec_to_text(sp: TypeSpec*, buf: str, buf_size: sizet, depth: int, scratch: str) -> int:
"""Recursive implementation of SpecToText (depth limit SPEC_DEPTH_MAX)"""
if sp is None or buf is None or buf_size == 0 or scratch is None or depth > SPEC_DEPTH_MAX: return 1
+7 -7
View File
@@ -173,7 +173,7 @@ novirtual class VarGenMixin:
if sp.ElemBits == 64: return 1
return 0
# set_var_volatile - mark a variable volatile (annotation union contains t.VolatileT).
# set_var_volatile - mark a variable volatile (annotation union contains volatile).
# Every load/store of it carries LLVM volatile, preventing hoisting/removal of busy-wait loads.
def set_var_volatile(self, symtab: GenTypes.SymbolTable*, name: str) -> int:
"""Mark a variable as volatile (VarEntry.IsVolatile=1), return 0=success / 1=failure"""
@@ -189,8 +189,8 @@ novirtual class VarGenMixin:
if entry is None: return 0
return entry.IsVolatile
# set_var_pointee_volatile - mark the pointed-to object volatile (rawptr[t.VolatileT | T]).
# Dereferences (p[i] / p.field / c.Deref(p)) then use LLVM volatile load/store on the pointee.
# set_var_pointee_volatile - mark the pointed-to object volatile (rawptr[volatile | T]).
# Dereferences (p[i] / p.field / *p) then use LLVM volatile load/store on the pointee.
def set_var_pointee_volatile(self, symtab: GenTypes.SymbolTable*, name: str) -> int:
"""Mark the variable's pointee volatile (VarEntry.PointeeVolatile=1), return 0=success / 1=failure"""
if symtab is None or name is None: return 1
@@ -205,7 +205,7 @@ novirtual class VarGenMixin:
if entry is None: return 0
return entry.PointeeVolatile
# set_var_const - mark a variable as a const object (annotation has t.ConstT and the type is not a pointer).
# set_var_const - mark a variable as a const object (annotation has const and the type is not a pointer).
# Assignment / augmented assignment / element assignment to it are then rejected (same rule as TPC).
def set_var_const(self, symtab: GenTypes.SymbolTable*, name: str) -> int:
"""Mark a variable as a const object (VarEntry.IsConst=1), return 0=success / 1=failure"""
@@ -222,7 +222,7 @@ novirtual class VarGenMixin:
return entry.IsConst
# set_var_byte_order - register a local variable's byte order (VarEntry.ByteOrder) from the
# `T | t.BigEndian` / `T | t.LittleEndian` annotation union: 1=BigEndian / 2=LittleEndian.
# `T | bigend` / `T | littlend` annotation union: 1=BigEndian / 2=LittleEndian.
# The value is only 1/2 when a marker is present, so the local load/store paths bswap only then.
def set_var_byte_order(self, symtab: GenTypes.SymbolTable*, name: str, byte_order: int) -> int:
"""Set a local variable's byte order (VarEntry.ByteOrder), return 0=success / 1=failure"""
@@ -1522,7 +1522,7 @@ novirtual class VarGenMixin:
return None
return mlirlite.build_load(trans._cur_builder, slot_ty, slot)
# ---- repl_try_addr - session variable address-of (c.Addr(x) -> ctx slot address) ----
# ---- repl_try_addr - session variable address-of (&x -> ctx slot address) ----
def repl_try_addr(self, trans: HT.Translator*, name: str, node: ast.AST*) -> mlirlite.Value*:
"""Session variable address-of: return the slot address (Ptr(value type)); None when unregistered, untyped, or outside the fragment body."""
if trans is None or trans.ReplCtx is None or name is None: return None
@@ -1530,7 +1530,7 @@ novirtual class VarGenMixin:
if entry is None: return None
if self.repl_in_frag_scope(trans.SymTab) == 0: return None
if entry.Kind == GenTypes.REPL_KIND_UNTYPED:
self.Gen.TypeGenErr.fatal_error(node, f"REPL: session variable '{name}' has not been assigned yet (untyped), cannot take its address (c.Addr)", "NameError")
self.Gen.TypeGenErr.fatal_error(node, f"REPL: session variable '{name}' has not been assigned yet (untyped), cannot take its address (&x)", "NameError")
return None
pool: memhub.MemBuddy* = trans.Pool
slot_ty: mlirlite.LLVMType* = self.repl_slot_ty(pool, entry)
+1 -1
View File
@@ -206,7 +206,7 @@ def ReplFindProject(Out: bytes, Cap: int, Diag: bytes, DiagCap: int) -> int:
# ============================================================
# Session state: permanent buffers in PermPool; per-fragment strings in mb; t.Callable slots.
# Session state: permanent buffers in PermPool; per-fragment strings in mb; function-pointer slots.
ReplFragFn: callable[[rawptr], int] = None
# Fragment sequence number: module-level and monotonic, so names/files are never reused.
ReplFragNo: int = 1
+1 -4
View File
@@ -715,7 +715,7 @@ def _NameOnLine(pool: memhub.MemManager*, a: LspAnalysis*, line1: int,
def _IsTypeAliasDecl(decl: ast.AST*) -> int:
"""1 when decl is `name: t.TypedefT = type` (the canonical typedef form)."""
"""1 when decl is `name: TypedefT = type` (the canonical typedef form)."""
if decl is None or decl.kind() != ast.ASTKind.AnnAssign:
return 0
ann: ast.AST* = ((ast.AnnAssign*)(decl)).annotation
@@ -724,9 +724,6 @@ def _IsTypeAliasDecl(decl: ast.AST*) -> int:
if ann.kind() == ast.ASTKind.Name:
nm: ast.Name* = (ast.Name*)(ann)
return 1 if nm.id is not None and string.strcmp(nm.id, "TypedefT") == 0 else 0
if ann.kind() == ast.ASTKind.Attribute:
at: ast.Attribute* = (ast.Attribute*)(ann)
return 1 if at.attr is not None and string.strcmp(at.attr, "CTypedef") == 0 else 0
return 0