refactor(viperc): 退役 t/c 模块标记,内建类型改由编译器直接识别

This commit is contained in:
TermiNexus committed 2026-10-18 12:56:57 +08:00
1 parent 3242560d02
commit 42b094a575
25 files changed
+437 -660

No files matched your search

+2 -7
View File
@@ -440,8 +440,6 @@ def _PhaseADumpNode(temp_dir: str, td_len: sizet, sha1: str, depth: int, prefix:
nm[tl] = 0 nm[tl] = 0
is_last: int = 0 is_last: int = 0
if idx + 1 >= token_count: is_last = 1 if idx + 1 >= token_count: is_last = 1
builtin: int = 0
if string.strcmp(nm, "t") == 0 or string.strcmp(nm, "c") == 0: builtin = 1
dep_sha1: str = _PhaseAResolveDepSha1(nm) dep_sha1: str = _PhaseAResolveDepSha1(nm)
txt_ok: int = 0 txt_ok: int = 0
if dep_sha1 is not None: txt_ok = _PhaseATextLLExists(temp_dir, td_len, dep_sha1) if dep_sha1 is not None: txt_ok = _PhaseATextLLExists(temp_dir, td_len, dep_sha1)
@@ -449,10 +447,7 @@ def _PhaseADumpNode(temp_dir: str, td_len: sizet, sha1: str, depth: int, prefix:
# stack pointer for a literal -> printf %s crashes and the graph output is swallowed) # stack pointer for a literal -> printf %s crashes and the graph output is swallowed)
br_1: str = "|-- " br_1: str = "|-- "
br_2: str = "`-- " br_2: str = "`-- "
if builtin != 0: if dep_sha1 is None:
if is_last != 0: stdio.printf("%s%s%s (builtin)\n", prefix, br_2, nm)
else: stdio.printf("%s%s%s (builtin)\n", prefix, br_1, nm)
elif dep_sha1 is None:
if is_last != 0: stdio.printf("%s%s%s (unresolved)\n", prefix, br_2, nm) if is_last != 0: stdio.printf("%s%s%s (unresolved)\n", prefix, br_2, nm)
else: stdio.printf("%s%s%s (unresolved)\n", prefix, br_1, nm) else: stdio.printf("%s%s%s (unresolved)\n", prefix, br_1, nm)
elif txt_ok != 0: elif txt_ok != 0:
@@ -463,7 +458,7 @@ def _PhaseADumpNode(temp_dir: str, td_len: sizet, sha1: str, depth: int, prefix:
else: stdio.printf("%s%s%s [MISS] text.ll=%s\n", prefix, br_1, nm, dep_sha1) else: stdio.printf("%s%s%s [MISS] text.ll=%s\n", prefix, br_1, nm, dep_sha1)
# Drill down: MISS follows the whole chain (to PA_GRAPH_DEPTH_MAX); OK probes 1 level only # Drill down: MISS follows the whole chain (to PA_GRAPH_DEPTH_MAX); OK probes 1 level only
can_expand: int = 0 can_expand: int = 0
if dep_sha1 is not None and builtin == 0: if dep_sha1 is not None:
if txt_ok == 0: if txt_ok == 0:
if depth + 1 < PA_GRAPH_DEPTH_MAX: can_expand = 1 if depth + 1 < PA_GRAPH_DEPTH_MAX: can_expand = 1
else: else:
+5 -27
View File
@@ -776,27 +776,8 @@ def _GeneratePyiFromAst(mb: memhub.MemBuddy*, tree: ast.AST*, rel_path: str, buf
tree_children: list[ast.AST*]* = tree.children tree_children: list[ast.AST*]* = tree.children
n: sizet = len(tree_children) n: sizet = len(tree_children)
# Pass 1: detect import t/c # t / c are retired compiler-internal modules: the stub never injects their imports.
has_import_t: int = 0 pos = _PyiEmit(buf, buf_size, pos, "\n")
has_import_c: int = 0
for i in range(int(n)):
stmt: ast.AST* = tree_children.get(i)
if stmt is not None and stmt.kind() == ast.ASTKind.Import:
imp: ast.Import* = ast.Import*(stmt)
if imp.names is not None:
imp_names: list[ast.AST*]* = imp.names
an: sizet = len(imp_names)
for ai in range(int(an)):
al: ast.Alias* = ast.Alias*(imp_names.get(ai))
if al is not None and al.name is not None:
if string.strcmp(al.name, "t") == 0: has_import_t = 1
elif string.strcmp(al.name, "c") == 0: has_import_c = 1
# Add missing import t/c
if has_import_t == 0 and has_import_c == 0: pos = _PyiEmit(buf, buf_size, pos, "import t, c\n\n")
elif has_import_t == 0: pos = _PyiEmit(buf, buf_size, pos, "import t\n\n")
elif has_import_c == 0: pos = _PyiEmit(buf, buf_size, pos, "import c\n\n")
else: pos = _PyiEmit(buf, buf_size, pos, "\n")
# Pass 2: emit declarations # Pass 2: emit declarations
for i in range(int(n)): for i in range(int(n)):
@@ -886,12 +867,9 @@ def _GeneratePyiFromAst(mb: memhub.MemBuddy*, tree: ast.AST*, rel_path: str, buf
# If there is a value (e.g. a CDefine constant) emit = value. # If there is a value (e.g. a CDefine constant) emit = value.
# CTypedef is special: its value is a type expression, rendered by _PyiTypeStr. # CTypedef is special: its value is a type expression, rendered by _PyiTypeStr.
is_ctypedef_ann: int = 0 is_ctypedef_ann: int = 0
if aa.annotation is not None and aa.annotation.kind() == ast.ASTKind.Attribute: if aa.annotation is not None and aa.annotation.kind() == ast.ASTKind.Name:
ctd_at: ast.Attribute* = ast.Attribute*(aa.annotation) ctd_nm: ast.Name* = ast.Name*(aa.annotation)
if ctd_at.attr is not None and string.strcmp(ctd_at.attr, "CTypedef") == 0: if ctd_nm.id is not None and string.strcmp(ctd_nm.id, "CTypedef") == 0: is_ctypedef_ann = 1
if ctd_at.value is not None and ctd_at.value.kind() == ast.ASTKind.Name:
ctd_mod: ast.Name* = ast.Name*(ctd_at.value)
if ctd_mod.id is not None and string.strcmp(ctd_mod.id, "t") == 0: is_ctypedef_ann = 1
if is_ctypedef_ann != 0 and aa.value is not None: if is_ctypedef_ann != 0 and aa.value is not None:
pos = _PyiEmit(buf, buf_size, pos, " = ") pos = _PyiEmit(buf, buf_size, pos, " = ")
pos = _PyiTypeStr(buf, buf_size, pos, aa.value) pos = _PyiTypeStr(buf, buf_size, pos, aa.value)
+18 -26
View File
@@ -1645,10 +1645,10 @@ def _resolve_single_vpi_type_str(pool: memhub.MemBuddy*, seg: str) -> mlirlite.L
stdlib.free(inner_buf_rp) stdlib.free(inner_buf_rp)
if inner_ty_rp is not None: return mlirlite.Ptr(pool, inner_ty_rp) if inner_ty_rp is not None: return mlirlite.Ptr(pool, inner_ty_rp)
return None return None
# t.CPtr[T] / c.CPtr[T] subscript pointers (nesting allowed, e.g. t.CPtr[t.CPtr] # CPtr[T] subscript pointers (nesting allowed, e.g. CPtr[CPtr] -> Ptr(Ptr(i8)));
# -> Ptr(Ptr(i8))); same as TypeGenResolve's Subscript(CPtr) branch: Ptr(resolve(T)). # same as TypeGenResolve's Subscript(CPtr) branch: Ptr(resolve(T)).
cptr_sub_pfx: sizet = 0 cptr_sub_pfx: sizet = 0
if string.strncmp(seg, "t.CPtr[", 7) == 0 or string.strncmp(seg, "c.CPtr[", 7) == 0: cptr_sub_pfx = 7 if string.strncmp(seg, "CPtr[", 6) == 0: cptr_sub_pfx = 6
if cptr_sub_pfx > 0: if cptr_sub_pfx > 0:
seg_len_cp: sizet = string.strlen(seg) seg_len_cp: sizet = string.strlen(seg)
if seg_len_cp > cptr_sub_pfx + 1 and seg[seg_len_cp - 1] == ']': if seg_len_cp > cptr_sub_pfx + 1 and seg[seg_len_cp - 1] == ']':
@@ -1661,15 +1661,12 @@ def _resolve_single_vpi_type_str(pool: memhub.MemBuddy*, seg: str) -> mlirlite.L
stdlib.free(inner_buf_cp) stdlib.free(inner_buf_cp)
if inner_ty_cp is not None: return mlirlite.Ptr(pool, inner_ty_cp) if inner_ty_cp is not None: return mlirlite.Ptr(pool, inner_ty_cp)
return None return None
# t.XXX / c.XXX built-in type mapping # Bare builtin type names (int8 / uint64 / sizet / CPtr / CExport ...)
if string.strncmp(seg, "t.", 2) == 0 or string.strncmp(seg, "c.", 2) == 0: if string.strchr(seg, '.') is None:
attr_nm: str = seg + 2 # CExport is an export marker, not a real type; FuncGenDef maps it to Void,
# t.CExport is an export marker, not a real type; FuncGenDef maps it to Void,
# so normalize it here too or cross-module return types fail fast. # so normalize it here too or cross-module return types fail fast.
if string.strcmp(attr_nm, "CExport") == 0: return mlirlite.Void(pool) if string.strcmp(seg, "CExport") == 0: return mlirlite.Void(pool)
# [stage 2] unified: CTypedef dynamic table first (library typedefs analyzed normally), mapped_ty: mlirlite.LLVMType* = GenApi.resolve_typedef_or_map(pool, seg, None, None)
# on miss fall back to map_t_type (t/c library internal special-casing).
mapped_ty: mlirlite.LLVMType* = GenApi.resolve_typedef_or_map(pool, attr_nm, None, None)
if mapped_ty is not None: return mapped_ty if mapped_ty is not None: return mapped_ty
return None return None
# module-qualified class name "memhub.MemBuddy" -> use last segment to look up struct # module-qualified class name "memhub.MemBuddy" -> use last segment to look up struct
@@ -1689,7 +1686,7 @@ def _resolve_single_vpi_type_str(pool: memhub.MemBuddy*, seg: str) -> mlirlite.L
# bare enum name -> EnumTable fallback (as above: enums are not in StructTable) # bare enum name -> EnumTable fallback (as above: enums are not in StructTable)
bare_enum_ty: mlirlite.LLVMType* = GenApi.get_enum_base_type(seg) bare_enum_ty: mlirlite.LLVMType* = GenApi.get_enum_base_type(seg)
if bare_enum_ty is not None: return bare_enum_ty if bare_enum_ty is not None: return bare_enum_ty
# bare typedef name (e.g. VOIDPTR): CTypedef dynamic table first, on miss fall back to map_t_type # bare typedef name (e.g. VOIDPTR): CTypedef dynamic table first, on miss fall back to map_builtin_type
mapped_bare: mlirlite.LLVMType* = GenApi.resolve_typedef_or_map(pool, seg, None, None) mapped_bare: mlirlite.LLVMType* = GenApi.resolve_typedef_or_map(pool, seg, None, None)
if mapped_bare is not None: return mapped_bare if mapped_bare is not None: return mapped_bare
return None return None
@@ -1793,7 +1790,7 @@ def _resolve_vpi_ret_annot_str(pool: memhub.MemBuddy*, ret_annot: str, i32_ty: m
seg_buf[seg_len2] = '\0' seg_buf[seg_len2] = '\0'
# CPtr subscript segments must be recognized first: the definition side treats # CPtr subscript segments must be recognized first: the definition side treats
# `X*[T]` as "CPtr side contributes N pointer levels, the other side the base". # `X*[T]` as "CPtr side contributes N pointer levels, the other side the base".
if string.strncmp(seg_buf, "t.CPtr[", 7) == 0 or string.strncmp(seg_buf, "c.CPtr[", 7) == 0: if string.strncmp(seg_buf, "CPtr[", 6) == 0:
seg_ty_cs: mlirlite.LLVMType* = _resolve_single_vpi_type_str(pool, seg_buf) seg_ty_cs: mlirlite.LLVMType* = _resolve_single_vpi_type_str(pool, seg_buf)
if seg_ty_cs is not None: if seg_ty_cs is not None:
# Count outer Ptr nesting (same as the definition side). Do not update the # Count outer Ptr nesting (same as the definition side). Do not update the
@@ -1813,10 +1810,10 @@ def _resolve_vpi_ret_annot_str(pool: memhub.MemBuddy*, ret_annot: str, i32_ty: m
if lvl_cs > cptr_sub_lvl: if lvl_cs > cptr_sub_lvl:
cptr_sub_lvl = lvl_cs cptr_sub_lvl = lvl_cs
cptr_sub_ty = seg_ty_cs cptr_sub_ty = seg_ty_cs
elif string.strcmp(seg_buf, "t.CPtr") == 0 or string.strcmp(seg_buf, "CPtr") == 0: has_cptr = 1 elif string.strcmp(seg_buf, "CPtr") == 0: has_cptr = 1
elif string.strcmp(seg_buf, "t.CRef") == 0 or string.strcmp(seg_buf, "CRef") == 0: has_cref = 1 elif string.strcmp(seg_buf, "CRef") == 0: has_cref = 1
elif string.strcmp(seg_buf, "t.CTempRef") == 0 or string.strcmp(seg_buf, "CTempRef") == 0: has_cref = 1 elif string.strcmp(seg_buf, "CTempRef") == 0: has_cref = 1
elif string.strcmp(seg_buf, "void") == 0 or string.strcmp(seg_buf, "CVoid") == 0 or string.strcmp(seg_buf, "void") == 0: elif string.strcmp(seg_buf, "void") == 0 or string.strcmp(seg_buf, "CVoid") == 0:
# Bare void is an alias of t.CVoid and must be handled the same, else the # Bare void is an alias of t.CVoid and must be handled the same, else the
# else branch sets first_td and produces the illegal Ptr(Void) (void* = i8*). # else branch sets first_td and produces the illegal Ptr(Void) (void* = i8*).
has_cvoid = 1 has_cvoid = 1
@@ -2614,9 +2611,9 @@ def _vpi_class_has_packed(decorator_list: list[ast.AST*]*) -> int:
if deco is None: continue if deco is None: continue
if deco.kind() == ast.ASTKind.Call: if deco.kind() == ast.ASTKind.Call:
cl: ast.Call* = ast.Call*(deco) cl: ast.Call* = ast.Call*(deco)
if cl.func is None or cl.func.kind() != ast.ASTKind.Attribute: continue if cl.func is None or cl.func.kind() != ast.ASTKind.Name: continue
at0: ast.Attribute* = ast.Attribute*(cl.func) at0: ast.Name* = ast.Name*(cl.func)
if at0.attr is None or string.strcmp(at0.attr, "Attribute") != 0: continue if at0.id is None or string.strcmp(at0.id, "Attribute") != 0: continue
if cl.args is None: continue if cl.args is None: continue
an: sizet = len(cl.args) an: sizet = len(cl.args)
for ai in range(int(an)): for ai in range(int(an)):
@@ -3527,12 +3524,7 @@ def _repl_stub_deco_flags(deco_list: list[ast.AST*]*) -> int:
if deco is None: continue if deco is None: continue
nm: str = None nm: str = None
k: int = deco.kind() k: int = deco.kind()
if k == ast.ASTKind.Attribute: if k == ast.ASTKind.Name:
at: ast.Attribute* = ast.Attribute*(deco)
if at.attr is not None and at.value is not None and at.value.kind() == ast.ASTKind.Name:
vn: ast.Name* = ast.Name*(at.value)
if vn.id is not None and string.strcmp(vn.id, "t") == 0: nm = at.attr
elif k == ast.ASTKind.Name:
nn: ast.Name* = ast.Name*(deco) nn: ast.Name* = ast.Name*(deco)
nm = nn.id nm = nn.id
if nm is None: continue if nm is None: continue
+68 -53
View File
@@ -23,14 +23,14 @@ novirtual class AssignGenMixin:
RecoverFound: int RecoverFound: int
# Check whether an annotation is t.CDefine (module-level function) # Check whether an annotation is the CDefine marker (module-level function)
def is_cdefine_annotation(self, annot: ast.AST*) -> int: def is_cdefine_annotation(self, annot: ast.AST*) -> int:
"""Return 1 if the annotation is t.CDefine, else 0.""" """Return 1 if the annotation is the CDefine marker, else 0."""
if annot is None: return 0 if annot is None: return 0
k: int = annot.kind() k: int = annot.kind()
if k == ast.ASTKind.Attribute: if k == ast.ASTKind.Name:
at: ast.Attribute* = ast.Attribute*(annot) nm: ast.Name* = ast.Name*(annot)
if at.attr is not None and string.strcmp(at.attr, "CDefine") == 0: return 1 if nm.id is not None and string.strcmp(nm.id, "CDefine") == 0: return 1
return 0 return 0
if k == ast.ASTKind.BinOp: if k == ast.ASTKind.BinOp:
bop: ast.BinOp* = ast.BinOp*(annot) bop: ast.BinOp* = ast.BinOp*(annot)
@@ -325,13 +325,9 @@ novirtual class AssignGenMixin:
self.Gen.TypeGenErr.fatal_error(node, f"[VPC] shift count out of range: the count {v} is >= the left operand width {lhs_bits} bits; " f"the result would be 0 or undefined (not a value the compiler may guess). " f"Fix: use a count < {lhs_bits}, or widen the left operand.", "ValueError") self.Gen.TypeGenErr.fatal_error(node, f"[VPC] shift count out of range: the count {v} is >= the left operand width {lhs_bits} bits; " f"the result would be 0 or undefined (not a value the compiler may guess). " f"Fix: use a count < {lhs_bits}, or widen the left operand.", "ValueError")
def _is_cdefine_marker_node(self, node: ast.AST*) -> int: def _is_cdefine_marker_node(self, node: ast.AST*) -> int:
"""Return 1 if the node is a t.CDefine marker node.""" """Return 1 if the node is the CDefine marker node."""
if node is None: return 0 if node is None: return 0
k: int = node.kind() k: int = node.kind()
if k == ast.ASTKind.Attribute:
at: ast.Attribute* = ast.Attribute*(node)
if at.attr is not None and string.strcmp(at.attr, "CDefine") == 0: return 1
return 0
if k == ast.ASTKind.Name: if k == ast.ASTKind.Name:
nm: ast.Name* = ast.Name*(node) nm: ast.Name* = ast.Name*(node)
if nm.id is not None and string.strcmp(nm.id, "CDefine") == 0: return 1 if nm.id is not None and string.strcmp(nm.id, "CDefine") == 0: return 1
@@ -363,7 +359,7 @@ novirtual class AssignGenMixin:
def _int_bits_of_type_name(self, pool: memhub.MemBuddy*, nm: str, depth: int) -> int: def _int_bits_of_type_name(self, pool: memhub.MemBuddy*, nm: str, depth: int) -> int:
"""类型名 → 整数位宽(0=未知/非整数)。depth 防 typedef 环。 """类型名 → 整数位宽(0=未知/非整数)。depth 防 typedef 环。
覆盖:内建名(int/bool/int8../uint64/sizet)、`t.CXxx`(map_t_type)、 覆盖:内建名(int/bool/int8../uint64/sizet)、`t.CXxx`(map_builtin_type)、
以及**用户 typedef**(如 stdint 的 `WORD: t.CTypedef = uint16` —— 经 lookup_ctypedef 以及**用户 typedef**(如 stdint 的 `WORD: t.CTypedef = uint16` —— 经 lookup_ctypedef
取其底层类型文本再递归定宽)。此前用户 typedef 一律返回 0,导致 取其底层类型文本再递归定宽)。此前用户 typedef 一律返回 0,导致
`define X: WORD = ...` 的声明宽度不参与校验/物化。 `define X: WORD = ...` 的声明宽度不参与校验/物化。
@@ -380,7 +376,7 @@ novirtual class AssignGenMixin:
if string.strcmp(nm, "int32") == 0 or string.strcmp(nm, "uint32") == 0: return 32 if string.strcmp(nm, "int32") == 0 or string.strcmp(nm, "uint32") == 0: return 32
if string.strcmp(nm, "int64") == 0 or string.strcmp(nm, "uint64") == 0: return 64 if string.strcmp(nm, "int64") == 0 or string.strcmp(nm, "uint64") == 0: return 64
if string.strcmp(nm, "sizet") == 0 or string.strcmp(nm, "size_t") == 0: return 64 if string.strcmp(nm, "sizet") == 0 or string.strcmp(nm, "size_t") == 0: return 64
ty: mlirlite.LLVMType* = self.Gen.TypeGenResolve.map_t_type(pool, nm) ty: mlirlite.LLVMType* = self.Gen.TypeGenResolve.map_builtin_type(pool, nm)
if ty is not None: if ty is not None:
match ty: match ty:
case mlirlite.LLVMType.Int(bits): case mlirlite.LLVMType.Int(bits):
@@ -432,7 +428,7 @@ novirtual class AssignGenMixin:
def _float_kind_of_type_name(self, pool: memhub.MemBuddy*, nm: str, depth: int) -> int: def _float_kind_of_type_name(self, pool: memhub.MemBuddy*, nm: str, depth: int) -> int:
"""类型名 → 1 表示它是 32 位 float(f32),0 表示 double 或非浮点/未知。depth 防 typedef 环。 """类型名 → 1 表示它是 32 位 float(f32),0 表示 double 或非浮点/未知。depth 防 typedef 环。
覆盖:内建名 float、`t.CFloat` / `t.CFloat32T`(map_t_type)、用户/include typedef(递归展开)。 覆盖:内建名 float、`t.CFloat` / `t.CFloat32T`(map_builtin_type)、用户/include typedef(递归展开)。
""" """
if nm is None or depth > 10: return 0 if nm is None or depth > 10: return 0
# 去掉模块/类限定前缀:`t.CFloat` → `CFloat`;`stdint.float32` → `float32` # 去掉模块/类限定前缀:`t.CFloat` → `CFloat`;`stdint.float32` → `float32`
@@ -441,7 +437,7 @@ novirtual class AssignGenMixin:
return self._float_kind_of_type_name(pool, dot + 1, depth + 1) return self._float_kind_of_type_name(pool, dot + 1, depth + 1)
if string.strcmp(nm, "float") == 0: return 1 if string.strcmp(nm, "float") == 0: return 1
if string.strcmp(nm, "float32") == 0: return 1 if string.strcmp(nm, "float32") == 0: return 1
ty: mlirlite.LLVMType* = self.Gen.TypeGenResolve.map_t_type(pool, nm) ty: mlirlite.LLVMType* = self.Gen.TypeGenResolve.map_builtin_type(pool, nm)
if ty is not None: if ty is not None:
match ty: match ty:
case mlirlite.LLVMType.Float(bits): case mlirlite.LLVMType.Float(bits):
@@ -1502,34 +1498,22 @@ novirtual class AssignGenMixin:
return 0 return 0
def _is_addrlike_call(self, node: ast.AST*) -> int: def _is_addrlike_call(self, node: ast.AST*) -> int:
"""Return 1 if the rhs is an explicit pointer expression (c.Addr/c.Deref/c.Cast/c.Ptr call, or new-syntax `&x` / `*p`).""" """Return 1 if the rhs is an explicit pointer expression (`&x` / `*p`).
"""
if node is None: return 0 if node is None: return 0
# New syntax (R3/R4): `&x` and `*p` parse as UnaryOp(AddrOf) / UnaryOp(Deref) and replace the # `&x` and `*p` parse as UnaryOp(AddrOf) / UnaryOp(Deref). A pointer-valued RHS must not
# c.Addr / c.Deref call forms. A pointer-valued RHS must not take the module-global # take the module-global "object with static storage -> copy the object" branch.
# "object with static storage -> copy the object" branch, so both forms count as addrlike.
if node.kind() == ast.ASTKind.UnaryOp: if node.kind() == ast.ASTKind.UnaryOp:
uo_al: ast.UnaryOp* = ast.UnaryOp*(node) uo_al: ast.UnaryOp* = ast.UnaryOp*(node)
if uo_al.op == ast.OpKind.AddrOf: return 1 if uo_al.op == ast.OpKind.AddrOf: return 1
if uo_al.op == ast.OpKind.Deref: return 1 if uo_al.op == ast.OpKind.Deref: return 1
return 0 return 0
if node.kind() != ast.ASTKind.Call: return 0
cl: ast.Call* = ast.Call*(node)
if cl.func is None or cl.func.kind() != ast.ASTKind.Attribute: return 0
at: ast.Attribute* = ast.Attribute*(cl.func)
if at.value is None or at.value.kind() != ast.ASTKind.Name: return 0
vn: ast.Name* = ast.Name*(at.value)
if vn.id is None or string.strcmp(vn.id, "c") != 0: return 0
if at.attr is None: return 0
if string.strcmp(at.attr, "Addr") == 0: return 1
if string.strcmp(at.attr, "Deref") == 0: return 1
if string.strcmp(at.attr, "Cast") == 0: return 1
if string.strcmp(at.attr, "Ptr") == 0: return 1
return 0 return 0
def _is_addr_of_expr(self, node: ast.AST*) -> int: def _is_addr_of_expr(self, node: ast.AST*) -> int:
"""Return 1 only for an explicit address-of expression (`&x` / `c.Addr(x)`). """Return 1 only for an explicit address-of expression (`&x`).
Distinct from _is_addrlike_call, which also counts deref/cast forms: assigning a *deref* Distinct from _is_addrlike_call, which also counts deref forms: assigning a *deref*
result (`*p`) to a value-typed variable is a legitimate value copy, so only `&x` indicates result (`*p`) to a value-typed variable is a legitimate value copy, so only `&x` indicates
a missing `*` in the annotation. a missing `*` in the annotation.
""" """
@@ -1538,14 +1522,6 @@ novirtual class AssignGenMixin:
uo_ao: ast.UnaryOp* = ast.UnaryOp*(node) uo_ao: ast.UnaryOp* = ast.UnaryOp*(node)
if uo_ao.op == ast.OpKind.AddrOf: return 1 if uo_ao.op == ast.OpKind.AddrOf: return 1
return 0 return 0
if node.kind() != ast.ASTKind.Call: return 0
cl_ao: ast.Call* = ast.Call*(node)
if cl_ao.func is None or cl_ao.func.kind() != ast.ASTKind.Attribute: return 0
at_ao: ast.Attribute* = ast.Attribute*(cl_ao.func)
if at_ao.value is None or at_ao.value.kind() != ast.ASTKind.Name: return 0
vn_ao: ast.Name* = ast.Name*(at_ao.value)
if vn_ao.id is None or string.strcmp(vn_ao.id, "c") != 0: return 0
if at_ao.attr is not None and string.strcmp(at_ao.attr, "Addr") == 0: return 1
return 0 return 0
def _DerefPunBits(self, pool: memhub.MemBuddy*, ty: mlirlite.LLVMType*) -> int: def _DerefPunBits(self, pool: memhub.MemBuddy*, ty: mlirlite.LLVMType*) -> int:
@@ -1577,20 +1553,65 @@ novirtual class AssignGenMixin:
if string.strcmp(ty_nm, "CUInt64T") == 0: return 1 if string.strcmp(ty_nm, "CUInt64T") == 0: return 1
if string.strcmp(ty_nm, "CInt64T") == 0: return 1 if string.strcmp(ty_nm, "CInt64T") == 0: return 1
if string.strcmp(ty_nm, "CSizeT") == 0: return 1 if string.strcmp(ty_nm, "CSizeT") == 0: return 1
# builtin 64-bit short names
if string.strcmp(ty_nm, "sizet") == 0: return 1
if string.strcmp(ty_nm, "size_t") == 0: return 1
if string.strcmp(ty_nm, "int64") == 0: return 1
if string.strcmp(ty_nm, "uint64") == 0: return 1
if string.strcmp(ty_nm, "intfast64") == 0: return 1
if string.strcmp(ty_nm, "uintfast64") == 0: return 1
if string.strcmp(ty_nm, "intleast64") == 0: return 1
if string.strcmp(ty_nm, "uintleast64") == 0: return 1
if string.strcmp(ty_nm, "longlong") == 0: return 1
if string.strcmp(ty_nm, "ulonglong") == 0: return 1
if string.strcmp(ty_nm, "ptrdifft") == 0: return 1
return 0 return 0
def _is_scalar_ty_name(self, ty_name: str) -> int: def _is_scalar_ty_name(self, ty_name: str) -> int:
"""Return 1 if the type name is a known pure scalar (whitelist).""" """Return 1 if the type name is a known pure scalar (whitelist)."""
if ty_name is None: return 0 if ty_name is None: return 0
# Strip the t./c. namespace prefix so int and bare aliases are judged the same
if ty_name[0] == 't' and ty_name[1] == '.': ty_name += 2
elif ty_name[0] == 'c' and ty_name[1] == '.': ty_name += 2
if string.strchr(ty_name, '|') is not None: return 0 if string.strchr(ty_name, '|') is not None: return 0
if string.strchr(ty_name, '*') is not None: return 0 if string.strchr(ty_name, '*') is not None: return 0
if string.strcmp(ty_name, "str") == 0: return 0 if string.strcmp(ty_name, "str") == 0: return 0
if string.strcmp(ty_name, "bytes") == 0: return 0 if string.strcmp(ty_name, "bytes") == 0: return 0
if string.strcmp(ty_name, "CPtr") == 0: return 0 if string.strcmp(ty_name, "CPtr") == 0: return 0
if string.strstr(ty_name, "Ptr") is not None: return 0 if string.strstr(ty_name, "Ptr") is not None: return 0
# builtin short type names
if string.strcmp(ty_name, "sizet") == 0: return 1
if string.strcmp(ty_name, "size_t") == 0: return 1
if string.strcmp(ty_name, "int8") == 0: return 1
if string.strcmp(ty_name, "int16") == 0: return 1
if string.strcmp(ty_name, "int32") == 0: return 1
if string.strcmp(ty_name, "int64") == 0: return 1
if string.strcmp(ty_name, "uint8") == 0: return 1
if string.strcmp(ty_name, "uint16") == 0: return 1
if string.strcmp(ty_name, "uint32") == 0: return 1
if string.strcmp(ty_name, "uint64") == 0: return 1
if string.strcmp(ty_name, "intfast8") == 0: return 1
if string.strcmp(ty_name, "intfast16") == 0: return 1
if string.strcmp(ty_name, "intfast32") == 0: return 1
if string.strcmp(ty_name, "intfast64") == 0: return 1
if string.strcmp(ty_name, "uintfast8") == 0: return 1
if string.strcmp(ty_name, "uintfast16") == 0: return 1
if string.strcmp(ty_name, "uintfast32") == 0: return 1
if string.strcmp(ty_name, "uintfast64") == 0: return 1
if string.strcmp(ty_name, "intleast8") == 0: return 1
if string.strcmp(ty_name, "intleast16") == 0: return 1
if string.strcmp(ty_name, "intleast32") == 0: return 1
if string.strcmp(ty_name, "intleast64") == 0: return 1
if string.strcmp(ty_name, "uintleast8") == 0: return 1
if string.strcmp(ty_name, "uintleast16") == 0: return 1
if string.strcmp(ty_name, "uintleast32") == 0: return 1
if string.strcmp(ty_name, "uintleast64") == 0: return 1
if string.strcmp(ty_name, "uchar") == 0: return 1
if string.strcmp(ty_name, "short") == 0: return 1
if string.strcmp(ty_name, "ushort") == 0: return 1
if string.strcmp(ty_name, "uint") == 0: return 1
if string.strcmp(ty_name, "long") == 0: return 1
if string.strcmp(ty_name, "ulong") == 0: return 1
if string.strcmp(ty_name, "longlong") == 0: return 1
if string.strcmp(ty_name, "ulonglong") == 0: return 1
if string.strcmp(ty_name, "ptrdifft") == 0: return 1
# Whitelist: known pure scalars (base names + stdint uppercase aliases) # Whitelist: known pure scalars (base names + stdint uppercase aliases)
if string.strcmp(ty_name, "int") == 0: return 1 if string.strcmp(ty_name, "int") == 0: return 1
if string.strcmp(ty_name, "char") == 0: return 1 if string.strcmp(ty_name, "char") == 0: return 1
@@ -1913,7 +1934,7 @@ novirtual class AssignGenMixin:
def const_assign_msg(self, kind: str, name: str, src_name: str) -> str: def const_assign_msg(self, kind: str, name: str, src_name: str) -> str:
"""Build the const-assignment error message used by const_assign_fail.""" """Build the const-assignment error message used by const_assign_fail."""
decl: str = "(annotation contains t.CConst; the source name at the declaration was not recorded)" decl: str = "(annotation contains t.CConst; the source name at the declaration was not recorded)"
if src_name is not None: decl = f"t.CConst | {src_name}" if src_name is not None: decl = f"CConst | {src_name}"
return GenApi.escape_fstring_copy(f"cannot assign to const variable '{name}' ({kind}); " f"declaration type (including const): {decl}" f"; fix: remove the const descriptor / t.CConst from the declaration, or assign once at the declaration " f"and afterwards use only non-const copies/temporaries; a const object forbids assignment, " f"augmented assignment and element assignment within its scope, and this never degrades to a warning") return GenApi.escape_fstring_copy(f"cannot assign to const variable '{name}' ({kind}); " f"declaration type (including const): {decl}" f"; fix: remove the const descriptor / t.CConst from the declaration, or assign once at the declaration " f"and afterwards use only non-const copies/temporaries; a const object forbids assignment, " f"augmented assignment and element assignment within its scope, and this never degrades to a warning")
def const_assign_fail(self, node: ast.AST*, kind: str, name: str, src_name: str) -> int: def const_assign_fail(self, node: ast.AST*, kind: str, name: str, src_name: str) -> int:
@@ -2242,18 +2263,12 @@ novirtual class AssignGenMixin:
if annot is None or annot.kind() != ast.ASTKind.Subscript: return 0 if annot is None or annot.kind() != ast.ASTKind.Subscript: return 0
sub: ast.Subscript* = ast.Subscript*(annot) sub: ast.Subscript* = ast.Subscript*(annot)
if sub.value is None or sub.slice is None: return 0 if sub.value is None or sub.slice is None: return 0
if sub.value.kind() == ast.ASTKind.Attribute: if sub.value.kind() == ast.ASTKind.Name:
at: ast.Attribute* = ast.Attribute*(sub.value) # Bare CArray / builtin rawarray / builtin array are all equivalent.
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* = ast.Name*(at.value)
if mv.id is None or string.strcmp(mv.id, "t") != 0: return 0
elif sub.value.kind() == ast.ASTKind.Name:
# Bare CArray / builtin rawarray / builtin array are all equivalent to t.CArray
bn: ast.Name* = ast.Name*(sub.value) bn: ast.Name* = ast.Name*(sub.value)
if bn.id is None or (string.strcmp(bn.id, "CArray") != 0 and string.strcmp(bn.id, "rawarray") != 0 and string.strcmp(bn.id, "array") != 0): return 0 if bn.id is None or (string.strcmp(bn.id, "CArray") != 0 and string.strcmp(bn.id, "rawarray") != 0 and string.strcmp(bn.id, "array") != 0): return 0
else: return 0 else: return 0
# Single-param form: slice is not a Tuple (e.g. t.CArray[u64]) # Single-param form: slice is not a Tuple (e.g. CArray[u64])
if sub.slice.kind() != ast.ASTKind.Tuple: return 1 if sub.slice.kind() != ast.ASTKind.Tuple: return 1
# Two-param form with count=None is equivalent to single-param # Two-param form with count=None is equivalent to single-param
tup: ast.Tuple* = ast.Tuple*(sub.slice) tup: ast.Tuple* = ast.Tuple*(sub.slice)
+2 -17
View File
@@ -669,21 +669,11 @@ novirtual class CallGenCBuiltin(CallGenBase.CallGenBase):
return buf return buf
return "" return ""
# Attribute: t.ASM_DESCR.XXX or t.XXX # Attribute: ASM_DESCR.XXX
if k == ast.ASTKind.Attribute: if k == ast.ASTKind.Attribute:
at: ast.Attribute* = ast.Attribute*(node) at: ast.Attribute* = ast.Attribute*(node)
# t.ASM_DESCR.XXX
if at.value is not None and at.value.kind() == ast.ASTKind.Attribute:
inner: ast.Attribute* = ast.Attribute*(at.value)
if inner.value is not None and inner.value.kind() == ast.ASTKind.Name:
mod_nm: ast.Name* = ast.Name*(inner.value)
if mod_nm.id is not None and string.strcmp(mod_nm.id, "t") == 0:
if inner.attr is not None and string.strcmp(inner.attr, "ASM_DESCR") == 0: return self._resolve_register(at.attr)
# t.XXX shorthand form
if at.value is not None and at.value.kind() == ast.ASTKind.Name: if at.value is not None and at.value.kind() == ast.ASTKind.Name:
mod_nm2: ast.Name* = ast.Name*(at.value) mod_nm2: ast.Name* = ast.Name*(at.value)
if mod_nm2.id is not None and string.strcmp(mod_nm2.id, "t") == 0: return self._resolve_register(at.attr)
# Bare ASM_DESCR.XXX: equivalent form without the t. prefix
if mod_nm2.id is not None and string.strcmp(mod_nm2.id, "ASM_DESCR") == 0: return self._resolve_register(at.attr) if mod_nm2.id is not None and string.strcmp(mod_nm2.id, "ASM_DESCR") == 0: return self._resolve_register(at.attr)
return "" return ""
@@ -724,12 +714,7 @@ novirtual class CallGenCBuiltin(CallGenBase.CallGenBase):
if string.strcmp(nm.id, "irinp") == 0: return "LInp" if string.strcmp(nm.id, "irinp") == 0: return "LInp"
if string.strcmp(nm.id, "irout") == 0: return "LOut" if string.strcmp(nm.id, "irout") == 0: return "LOut"
return None return None
if node.func.kind() != ast.ASTKind.Attribute: return None return None
at: ast.Attribute* = ast.Attribute*(node.func)
if at.value is None or at.value.kind() != ast.ASTKind.Name: return None
mod_nm: ast.Name* = ast.Name*(at.value)
if mod_nm.id is None or string.strcmp(mod_nm.id, "c") != 0: return None
return at.attr
def _asm_name_is_mangled(self, name: str) -> int: def _asm_name_is_mangled(self, name: str) -> int:
+23 -154
View File
@@ -248,54 +248,28 @@ novirtual class CallGenDispatch(CallGenBase.CallGenBase):
# irinp/irout standalone calls return 0 (meaningful only inside __llvmir__ f-strings) # irinp/irout standalone calls return 0 (meaningful only inside __llvmir__ f-strings)
return mlirlite.ConstInt(pool, mlirlite.Int32(pool), 0, "0") return mlirlite.ConstInt(pool, mlirlite.Int32(pool), 0, "0")
# Detect c.Deref / c.DerefAs / c.Addr special calls (inline IR, no call emitted) # Detect coro.* coroutine interception (inline translation, VPC-M1).
# and coro.* coroutine interception (inline translation, VPC-M1).
if cl.func is not None and cl.func.kind() == ast.ASTKind.Attribute: if cl.func is not None and cl.func.kind() == ast.ASTKind.Attribute:
at_node: ast.Attribute* = ast.Attribute*(cl.func) at_node: ast.Attribute* = ast.Attribute*(cl.func)
if at_node.value is not None and at_node.value.kind() == ast.ASTKind.Name: if at_node.value is not None and at_node.value.kind() == ast.ASTKind.Name:
mod_nm: ast.Name* = ast.Name*(at_node.value) mod_nm: ast.Name* = ast.Name*(at_node.value)
is_coro_mod: int = 0 is_coro_mod: int = 0
if mod_nm.id is not None and string.strcmp(mod_nm.id, "coro") == 0: is_coro_mod = 1 if mod_nm.id is not None and string.strcmp(mod_nm.id, "coro") == 0: is_coro_mod = 1
if mod_nm.id is not None and (string.strcmp(mod_nm.id, "c") == 0 or is_coro_mod != 0): if is_coro_mod != 0:
if string.strcmp(func_name, "Deref") == 0: return self.Gen.CallGenCBuiltin.translate_c_deref(pool, builder, mod, cl, trans) if string.strcmp(func_name, "coro_run") == 0:
elif string.strcmp(func_name, "DerefAs") == 0: return self.Gen.CallGenCBuiltin.translate_c_derefas(pool, builder, mod, cl, trans)
elif string.strcmp(func_name, "Addr") == 0: return self.Gen.CallGenCBuiltin.translate_c_addr(pool, builder, mod, cl, trans)
elif string.strcmp(func_name, "Set") == 0:
# c.Set(a, b) = a = b (assignment, not pointer deref). Treating it as a plain
# call would emit @Set and fail to link (undefined reference to `Set').
return self.Gen.CallGenCBuiltin.translate_c_set(pool, builder, mod, cl, trans)
elif string.strcmp(func_name, "Load") == 0: return self.Gen.CallGenCBuiltin.translate_c_load(pool, builder, mod, cl, trans)
elif string.strcmp(func_name, "Asm") == 0:
# translate_c_asm returns None for void: set the hit flag so translate_call
# does not fall through to the external function path.
if hit_box is not None: hit_box[0] = 1
return self.Gen.CallGenCBuiltin.translate_c_asm(pool, builder, mod, cl, trans)
elif string.strcmp(func_name, "LLVMIR") == 0:
# c.LLVMIR always returns void (None): set the hit flag
if hit_box is not None: hit_box[0] = 1
return self.Gen.CallGenCBuiltin.translate_c_llvmir(pool, builder, mod, cl, trans)
elif string.strcmp(func_name, "LInp") == 0 or string.strcmp(func_name, "LOut") == 0:
# c.LInp/c.LOut standalone calls return 0 (only meaningful in c.LLVMIR f-strings)
return mlirlite.ConstInt(pool, mlirlite.Int32(pool), 0, "0")
elif string.strcmp(func_name, "CIf") == 0 or string.strcmp(func_name, "CIfdef") == 0 or string.strcmp(func_name, "CIfndef") == 0 or string.strcmp(func_name, "CElif") == 0 or string.strcmp(func_name, "CElse") == 0 or string.strcmp(func_name, "CEndif") == 0:
# The c.CIf-family spellings were removed: report the replacement.
# (There is no runtime fallback: compile-time conditions use cif/celif/celse.)
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
elif is_coro_mod != 0 and string.strcmp(func_name, "coro_run") == 0:
# coro.coro_run(frame): inline translation (resume loop until done, returns the ret slot). # coro.coro_run(frame): inline translation (resume loop until done, returns the ret slot).
# Note: translate_coro_* take (builder, pool, mod, cl, trans), unlike the c.* branches. # Note: translate_coro_* take (builder, pool, mod, cl, trans).
return self.Gen.AsyncGen.translate_coro_run(builder, pool, mod, cl, trans) return self.Gen.AsyncGen.translate_coro_run(builder, pool, mod, cl, trans)
elif is_coro_mod != 0 and string.strcmp(func_name, "coro_destroy") == 0: elif string.strcmp(func_name, "coro_destroy") == 0:
# coro.coro_destroy(frame): return the frame to the pool (M7 free-list) # coro.coro_destroy(frame): return the frame to the pool (M7 free-list)
return self.Gen.AsyncGen.translate_coro_destroy(builder, pool, mod, cl, trans) return self.Gen.AsyncGen.translate_coro_destroy(builder, pool, mod, cl, trans)
elif is_coro_mod != 0 and string.strcmp(func_name, "coro_exception") == 0: elif string.strcmp(func_name, "coro_exception") == 0:
# coro.coro_exception(frame): return the frame exc slot (M4) # coro.coro_exception(frame): return the frame exc slot (M4)
return self.Gen.AsyncGen.translate_coro_exception(builder, pool, mod, cl, trans) return self.Gen.AsyncGen.translate_coro_exception(builder, pool, mod, cl, trans)
elif is_coro_mod != 0 and string.strcmp(func_name, "coro_exception_msg") == 0: elif string.strcmp(func_name, "coro_exception_msg") == 0:
# coro.coro_exception_msg(frame): return the frame msg slot (M7) # coro.coro_exception_msg(frame): return the frame msg slot (M7)
return self.Gen.AsyncGen.translate_coro_exception_msg(builder, pool, mod, cl, trans) return self.Gen.AsyncGen.translate_coro_exception_msg(builder, pool, mod, cl, trans)
elif is_coro_mod != 0 and string.strcmp(func_name, "coro_gather") == 0: elif string.strcmp(func_name, "coro_gather") == 0:
# coro.coro_gather(frames...): advance all coroutines concurrently (M4) # coro.coro_gather(frames...): advance all coroutines concurrently (M4)
return self.Gen.AsyncGen.translate_coro_gather(builder, pool, mod, cl, trans) return self.Gen.AsyncGen.translate_coro_gather(builder, pool, mod, cl, trans)
return None return None
@@ -362,112 +336,15 @@ novirtual class CallGenDispatch(CallGenBase.CallGenBase):
return None return None
def _tc_h4_type_casts(self, pool: memhub.MemBuddy*, builder: mlirlite.IRBuilder*, mod: mlirlite.LLVMModule*, cl: ast.Call*, funcs_ptr: rawptr, func_count: int, trans: HT.Translator*, func_name: str, cargs: list[ast.AST*]*, can: sizet) -> mlirlite.Value*: def _tc_h4_type_casts(self, pool: memhub.MemBuddy*, builder: mlirlite.IRBuilder*, mod: mlirlite.LLVMModule*, cl: ast.Call*, funcs_ptr: rawptr, func_count: int, trans: HT.Translator*, func_name: str, cargs: list[ast.AST*]*, can: sizet) -> mlirlite.Value*:
"""t.XXX / typedef type casts; returns None when unmatched.""" """Builtin / typedef type casts; returns None when unmatched."""
# Detect t.XXX type casts (uint64(ptr), rawptr(val), int(val), ...): when func is t.XXX and
# XXX is a known type, treat it as a cast; once confirmed, never fall through to cross-module calls.
if cl.func is not None and cl.func.kind() == ast.ASTKind.Attribute:
tc_at: ast.Attribute* = ast.Attribute*(cl.func)
if tc_at.value is not None and tc_at.value.kind() == ast.ASTKind.Name:
tc_mod_nm: ast.Name* = ast.Name*(tc_at.value)
if tc_mod_nm.id is not None and string.strcmp(tc_mod_nm.id, "t") == 0:
# t.CType(value, TypeName, rawptr) casts value to TypeName*; TypeName is a type
# name (not a value), so it must be handled explicitly or it falls through to a FATAL.
if string.strcmp(func_name, "CType") == 0 and can >= 2:
cty_node: ast.AST* = cargs.get(1)
ctype_target: mlirlite.LLVMType* = None
if cty_node is not None:
cti_imp: str = None
cti_fi: str = None
if trans is not None:
cti_imp = trans._imported_modules
cti_fi = trans._from_imports
ctype_target = self.Gen.TypeGenResolve.resolve_annotation_type(pool, cty_node, cti_imp, cti_fi, trans, 0)
if ctype_target is not None:
if can == 3:
cty_arg3: ast.AST* = cargs.get(2)
cty_is_cptr: int = 0
if cty_arg3 is not None:
# Form 1: rawptr (Attribute)
if cty_arg3.kind() == ast.ASTKind.Attribute:
cty_at3: ast.Attribute* = ast.Attribute*(cty_arg3)
if cty_at3.attr is not None and string.strcmp(cty_at3.attr, "CPtr") == 0:
if cty_at3.value is not None and cty_at3.value.kind() == ast.ASTKind.Name:
cty_nm3: ast.Name* = ast.Name*(cty_at3.value)
if cty_nm3.id is not None and string.strcmp(cty_nm3.id, "t") == 0: cty_is_cptr = 1
# Form 2: bare CPtr
elif cty_arg3.kind() == ast.ASTKind.Name:
cty_bn3: ast.Name* = ast.Name*(cty_arg3)
if cty_bn3.id is not None and string.strcmp(cty_bn3.id, "CPtr") == 0: cty_is_cptr = 1
if cty_is_cptr != 0:
# CVoid* -> i8* (void* is represented as i8* in LLVM)
match ctype_target:
case mlirlite.LLVMType.Void():
ctype_target = mlirlite.Int8(pool)
case _:
pass
ctype_target = mlirlite.Ptr(pool, ctype_target)
cty_arg0: ast.AST* = cargs.get(0)
if cty_arg0 is not None:
cty_val: mlirlite.Value* = self.Gen.ExprGen.translate_value(builder, pool, mod, cty_arg0, funcs_ptr, func_count, trans)
if cty_val is not None: return self.Gen.CallGenCtor._translate_t_type_cast(pool, builder, cty_val, ctype_target, cty_arg0, trans)
# t.CType confirmed: even if type resolution fails, do not fall through to a cross-module FATAL
return None
# CConst is a const marker: CConst(val, rawptr) -> Ptr(i8) (const void*). Without
# this it emits an undefined @CConst; single-arg CConst(x) returns the value unchanged.
if string.strcmp(func_name, "CConst") == 0:
if can >= 2:
cconst_ptr_ty: mlirlite.LLVMType* = mlirlite.Ptr(pool, mlirlite.Int8(pool))
cconst_arg: mlirlite.Value* = self.Gen.ExprGen.translate_value(builder, pool, mod, cargs.get(0), funcs_ptr, func_count, trans)
if cconst_arg is not None: return self.Gen.CallGenCtor._translate_t_type_cast(pool, builder, cconst_arg, cconst_ptr_ty, cargs.get(0), trans)
elif can == 1: return self.Gen.ExprGen.translate_value(builder, pool, mod, cargs.get(0), funcs_ptr, func_count, trans)
return None
target_ty: mlirlite.LLVMType* = self.Gen.TypeGenResolve.map_t_type(pool, func_name)
if target_ty is not None:
tc_effective_can: sizet = can
# Two-arg form: int(val, rawptr) -> Ptr(target_ty). void* is illegal in LLVM,
# so CVoid* must be represented as i8*.
if can == 2:
tc_arg2_node: ast.AST* = cargs.get(1)
tc_arg2_is_cptr: int = 0
if tc_arg2_node is not None:
# Form 1: rawptr (Attribute form)
if tc_arg2_node.kind() == ast.ASTKind.Attribute:
tc_arg2_at: ast.Attribute* = ast.Attribute*(tc_arg2_node)
if tc_arg2_at.attr is not None and string.strcmp(tc_arg2_at.attr, "CPtr") == 0:
if tc_arg2_at.value is not None and tc_arg2_at.value.kind() == ast.ASTKind.Name:
tc_arg2_nm: ast.Name* = ast.Name*(tc_arg2_at.value)
if tc_arg2_nm.id is not None and string.strcmp(tc_arg2_nm.id, "t") == 0: tc_arg2_is_cptr = 1
# Form 2: bare CPtr (usable after `from t import CPtr`, e.g. t.CXXX(x, CPtr))
elif tc_arg2_node.kind() == ast.ASTKind.Name:
tc_arg2_bn: ast.Name* = ast.Name*(tc_arg2_node)
if tc_arg2_bn.id is not None and string.strcmp(tc_arg2_bn.id, "CPtr") == 0: tc_arg2_is_cptr = 1
if tc_arg2_is_cptr != 0:
# CVoid* -> i8* (void* is represented as i8* in LLVM)
match target_ty:
case mlirlite.LLVMType.Void():
target_ty = mlirlite.Int8(pool)
case _:
pass
target_ty = mlirlite.Ptr(pool, target_ty)
tc_effective_can = 1
if tc_effective_can == 1:
tc_arg_node: ast.AST* = cargs.get(0)
if tc_arg_node is not None:
# Pass target_ty as the 8th arg: otherwise constants pick their width from
# the i32 range and uint64(-1) materializes as i32 -1 then zext (N1).
tc_arg_val: mlirlite.Value* = self.Gen.ExprGen.translate_value(builder, pool, mod, tc_arg_node, None, 0, trans, target_ty)
if tc_arg_val is not None: return self.Gen.CallGenCtor._translate_t_type_cast(pool, builder, tc_arg_val, target_ty, tc_arg_node, trans)
# t.XXX cast confirmed but arg shape unsupported: return None, do not fall through to FATAL
return None
# Module-qualified typedef cast (posix.mode_t(mode), win32.win32base.HANDLE(v), ...): func is # Module-qualified typedef cast (posix.mode_t(mode), win32.win32base.HANDLE(v), ...): func is
# Attribute(Name(module), typedef); non-typedef names still go to the module function call. # Attribute(Name(module), typedef); non-typedef names still go to the module function call.
if cl.func is not None and cl.func.kind() == ast.ASTKind.Attribute: if cl.func is not None and cl.func.kind() == ast.ASTKind.Attribute:
mt_at: ast.Attribute* = ast.Attribute*(cl.func) mt_at: ast.Attribute* = ast.Attribute*(cl.func)
if mt_at.value is not None and mt_at.value.kind() == ast.ASTKind.Name: if mt_at.value is not None and mt_at.value.kind() == ast.ASTKind.Name:
mt_mod_nm: ast.Name* = ast.Name*(mt_at.value) mt_mod_nm: ast.Name* = ast.Name*(mt_at.value)
if mt_mod_nm.id is not None and string.strcmp(mt_mod_nm.id, "t") != 0 and string.strcmp(mt_mod_nm.id, "c") != 0: if mt_mod_nm.id is not None:
mod_td_ty: mlirlite.LLVMType* = self.Gen.TypeGenResolve.map_t_type(pool, func_name) mod_td_ty: mlirlite.LLVMType* = self.Gen.TypeGenResolve.map_builtin_type(pool, func_name)
if mod_td_ty is None: if mod_td_ty is None:
mod_td_str: str = self.Gen.TypeGenCType.lookup_ctypedef(func_name) mod_td_str: str = self.Gen.TypeGenCType.lookup_ctypedef(func_name)
if mod_td_str is not None and self.Gen.TypeGenCType.is_ctypedef_found() != 0: if mod_td_str is not None and self.Gen.TypeGenCType.is_ctypedef_found() != 0:
@@ -483,26 +360,26 @@ novirtual class CallGenDispatch(CallGenBase.CallGenBase):
mt_arg_val: mlirlite.Value* = self.Gen.ExprGen.translate_value(builder, pool, mod, cargs.get(0), funcs_ptr, func_count, trans, mod_td_ty) mt_arg_val: mlirlite.Value* = self.Gen.ExprGen.translate_value(builder, pool, mod, cargs.get(0), funcs_ptr, func_count, trans, mod_td_ty)
if mt_arg_val is not None: return self.Gen.CallGenCtor._translate_t_type_cast(pool, builder, mt_arg_val, mod_td_ty, cargs.get(0), trans) if mt_arg_val is not None: return self.Gen.CallGenCtor._translate_t_type_cast(pool, builder, mt_arg_val, mod_td_ty, cargs.get(0), trans)
# Bare typedef casts (BYTEPTR, HANDLE, CHARPTR, ...): all CTypedef names and t/c primitives # Bare typedef casts (BYTEPTR, HANDLE, CHARPTR, ...): all CTypedef names and builtin primitives
# are castable. In generic contexts T(x) first resolves T, then goes through resolve_typedef_or_map. # are castable. In generic contexts T(x) first resolves T, then goes through resolve_typedef_or_map.
if cl.func is not None and cl.func.kind() == ast.ASTKind.Name: if cl.func is not None and cl.func.kind() == ast.ASTKind.Name:
bare_fn_name: str = self.Gen.TypeGenBase.resolve_generic_type_name(func_name) bare_fn_name: str = self.Gen.TypeGenBase.resolve_generic_type_name(func_name)
bare_ty: mlirlite.LLVMType* = None bare_ty: mlirlite.LLVMType* = None
# Builtin names resolved from generic context (T->"double"/"float"): map_t_type only knows # Builtin names resolved from generic context (T->"double"/"float"): float must map to
# t.CDouble, and float must map to t.CFloat (f32), not f64. # f32, not f64.
if string.strcmp(bare_fn_name, "double") == 0: bare_ty = mlirlite.Double(pool) if string.strcmp(bare_fn_name, "double") == 0: bare_ty = mlirlite.Double(pool)
elif string.strcmp(bare_fn_name, "float") == 0: bare_ty = mlirlite.Float(pool) elif string.strcmp(bare_fn_name, "float") == 0: bare_ty = mlirlite.Float(pool)
else: else:
# Prefer the CTypedef dynamic table (library typedef casts); on miss fall back to # Prefer the CTypedef dynamic table (library typedef casts); on miss fall back to
# map_t_type, which (after phase-2 convergence) no longer contains stdint aliases. # map_builtin_type.
bare_imp_c: str = None bare_imp_c: str = None
bare_fi_c: str = None bare_fi_c: str = None
if trans is not None: if trans is not None:
bare_imp_c = trans._imported_modules bare_imp_c = trans._imported_modules
bare_fi_c = trans._from_imports bare_fi_c = trans._from_imports
bare_ty = self.Gen.TypeGenResolve.resolve_typedef_or_map(pool, bare_fn_name, bare_imp_c, bare_fi_c) bare_ty = self.Gen.TypeGenResolve.resolve_typedef_or_map(pool, bare_fn_name, bare_imp_c, bare_fi_c)
# Names substituted from generic context (T->"int") are unknown to map_t_type, so fall # Names substituted from generic context (T->"int") are unknown to map_builtin_type, so fall
# back to resolve_annotation_type (supports t.CXxx / struct class names / aliases). # back to resolve_annotation_type (supports builtin names / struct class names / aliases).
if bare_ty is None and string.strcmp(bare_fn_name, func_name) != 0: if bare_ty is None and string.strcmp(bare_fn_name, func_name) != 0:
gp_ast: ast.AST* = self.Gen.TypeGenResolve._parse_type_string(pool, bare_fn_name, 0) gp_ast: ast.AST* = self.Gen.TypeGenResolve._parse_type_string(pool, bare_fn_name, 0)
if gp_ast is not None: if gp_ast is not None:
@@ -518,17 +395,10 @@ novirtual class CallGenDispatch(CallGenBase.CallGenBase):
bare_arg2_node: ast.AST* = cargs.get(1) bare_arg2_node: ast.AST* = cargs.get(1)
bare_arg2_is_cptr: int = 0 bare_arg2_is_cptr: int = 0
if bare_arg2_node is not None: if bare_arg2_node is not None:
# Form 1: rawptr (Attribute form) # Builtin pointer marker: CPtr / rawptr
if bare_arg2_node.kind() == ast.ASTKind.Attribute: if bare_arg2_node.kind() == ast.ASTKind.Name:
bare_arg2_at: ast.Attribute* = ast.Attribute*(bare_arg2_node)
if bare_arg2_at.attr is not None and string.strcmp(bare_arg2_at.attr, "CPtr") == 0:
if bare_arg2_at.value is not None and bare_arg2_at.value.kind() == ast.ASTKind.Name:
bare_arg2_nm: ast.Name* = ast.Name*(bare_arg2_at.value)
if bare_arg2_nm.id is not None and string.strcmp(bare_arg2_nm.id, "t") == 0: bare_arg2_is_cptr = 1
# Form 2: bare CPtr (usable after `from t import CPtr`)
elif bare_arg2_node.kind() == ast.ASTKind.Name:
bare_arg2_bn: ast.Name* = ast.Name*(bare_arg2_node) 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, "CPtr") == 0: bare_arg2_is_cptr = 1 if bare_arg2_bn.id is not None and (string.strcmp(bare_arg2_bn.id, "CPtr") == 0 or string.strcmp(bare_arg2_bn.id, "rawptr") == 0): bare_arg2_is_cptr = 1
if bare_arg2_is_cptr != 0: if bare_arg2_is_cptr != 0:
# CVoid* -> i8* (void* is represented as i8* in LLVM) # CVoid* -> i8* (void* is represented as i8* in LLVM)
match bare_ty: match bare_ty:
@@ -668,8 +538,8 @@ novirtual class CallGenDispatch(CallGenBase.CallGenBase):
if szf_mod is not None: if szf_mod is not None:
string.strncpy(szf_mod, sz_full_name, szf_mod_len) string.strncpy(szf_mod, sz_full_name, szf_mod_len)
szf_mod[szf_mod_len] = '\0' szf_mod[szf_mod_len] = '\0'
# Exempt t/c builtins (int etc. are already handled by the builtin/size table) # Module-qualified builtin names are already handled by the builtin/size table.
if string.strcmp(szf_mod, "t") != 0 and string.strcmp(szf_mod, "c") != 0: if szf_mod is not None:
szf_sha1: str = self.Gen.StructGen._resolve_module_sha1(szf_mod, trans._from_imports if trans is not None else None) szf_sha1: str = self.Gen.StructGen._resolve_module_sha1(szf_mod, trans._from_imports if trans is not None else None)
if szf_sha1 is not None: if szf_sha1 is not None:
szf_entry: GenTypes.StructEntry* = self.Gen.StructGen.find_struct_by_module(sz_cls_name, szf_sha1) szf_entry: GenTypes.StructEntry* = self.Gen.StructGen.find_struct_by_module(sz_cls_name, szf_sha1)
@@ -1247,8 +1117,7 @@ novirtual class CallGenDispatch(CallGenBase.CallGenBase):
if expr_root is not None and expr_root.kind() == ast.ASTKind.Name: if expr_root is not None and expr_root.kind() == ast.ASTKind.Name:
expr_root_nm: str = ast.Name*(expr_root).id expr_root_nm: str = ast.Name*(expr_root).id
if expr_root_nm is not None: if expr_root_nm is not None:
if string.strcmp(expr_root_nm, "t") == 0 or string.strcmp(expr_root_nm, "c") == 0: is_mod_path_expr = 1 if trans._imported_modules is not None:
elif trans._imported_modules is not None:
if self.Gen.ImportsGen.is_module_imported(trans._imported_modules, expr_root_nm) != 0: is_mod_path_expr = 1 if self.Gen.ImportsGen.is_module_imported(trans._imported_modules, expr_root_nm) != 0: is_mod_path_expr = 1
if evk != ast.ASTKind.Name and is_mod_path_expr == 0: if evk != ast.ASTKind.Name and is_mod_path_expr == 0:
# Translate the receiver expression (the result should be Ptr(Struct)) # Translate the receiver expression (the result should be Ptr(Struct))
+2 -2
View File
@@ -1291,8 +1291,8 @@ novirtual class CallGenMixin(CallGenBase.CallGenBase):
viperlib.snprintf(mangled, mangled_len, "%s.%s", mod_sha1, func_name) viperlib.snprintf(mangled, mangled_len, "%s.%s", mod_sha1, func_name)
call_name = mangled call_name = mangled
else: else:
# t and c are internal libraries exempt from SHA1 lookup: use bare-name calls. # Only real modules reach this path; a missing module segment falls back to a bare name.
if string.strcmp(xmod_path, "t") == 0 or string.strcmp(xmod_path, "c") == 0: call_name = func_name if xmod_path is None: call_name = func_name
else: else:
# Check whether it is an imported module name (e.g. BuildPipeline.run_pipeline): if imported # Check whether it is an imported module name (e.g. BuildPipeline.run_pipeline): if imported
# but its SHA1 is unregistered the module was not compiled, so error out instead of a misleading from_imports lookup. # but its SHA1 is unregistered the module was not compiled, so error out instead of a misleading from_imports lookup.
+13 -37
View File
@@ -147,11 +147,7 @@ novirtual class ClassGenBase:
while alias_orig[og_len_ce] != ' ' and alias_orig[og_len_ce] != '\0': og_len_ce += 1 while alias_orig[og_len_ce] != ' ' and alias_orig[og_len_ce] != '\0': og_len_ce += 1
if og_len_ce == 5 and string.strncmp(alias_orig, "CEnum", 5) == 0: return 1 if og_len_ce == 5 and string.strncmp(alias_orig, "CEnum", 5) == 0: return 1
return 0 return 0
# Attribute(t.CEnum) form. # Attribute form was retired together with the t module prefix.
if k == ast.ASTKind.Attribute:
at: ast.Attribute* = ast.Attribute*(base_node)
if at.attr is not None and string.strcmp(at.attr, "CEnum") == 0: return 1
return 0
return 0 return 0
def _is_enum_class(self, cd: ast.ClassDef*, trans: HT.Translator* = None) -> int: def _is_enum_class(self, cd: ast.ClassDef*, trans: HT.Translator* = None) -> int:
"""Return 1 if the ClassDef is a CEnum (subclasses t.CEnum or uses @t.CEnum).""" """Return 1 if the ClassDef is a CEnum (subclasses t.CEnum or uses @t.CEnum)."""
@@ -185,11 +181,7 @@ novirtual class ClassGenBase:
while alias_orig[og_len_cu] != ' ' and alias_orig[og_len_cu] != '\0': og_len_cu += 1 while alias_orig[og_len_cu] != ' ' and alias_orig[og_len_cu] != '\0': og_len_cu += 1
if og_len_cu == 6 and string.strncmp(alias_orig, "CUnion", 6) == 0: return 1 if og_len_cu == 6 and string.strncmp(alias_orig, "CUnion", 6) == 0: return 1
return 0 return 0
# Attribute(t.CUnion) form. # Attribute form was retired together with the t module prefix.
if k == ast.ASTKind.Attribute:
at: ast.Attribute* = ast.Attribute*(base_node)
if at.attr is not None and string.strcmp(at.attr, "CUnion") == 0: return 1
return 0
return 0 return 0
def _is_union_class(self, cd: ast.ClassDef*, trans: HT.Translator* = None) -> int: def _is_union_class(self, cd: ast.ClassDef*, trans: HT.Translator* = None) -> int:
"""Return 1 if the ClassDef is a CUnion (subclasses t.CUnion or uses @t.CUnion).""" """Return 1 if the ClassDef is a CUnion (subclasses t.CUnion or uses @t.CUnion)."""
@@ -223,11 +215,7 @@ novirtual class ClassGenBase:
while alias_orig[og_len_cr] != ' ' and alias_orig[og_len_cr] != '\0': og_len_cr += 1 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 if og_len_cr == 5 and string.strncmp(alias_orig, "REnum", 5) == 0: return 1
return 0 return 0
# Attribute(t.REnum) form. # Attribute form was retired together with the t module prefix.
if k == ast.ASTKind.Attribute:
at: ast.Attribute* = ast.Attribute*(base_node)
if at.attr is not None and string.strcmp(at.attr, "REnum") == 0: return 1
return 0
return 0 return 0
def _is_renum_class(self, cd: ast.ClassDef*, trans: HT.Translator* = None) -> int: 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).""" """Return 1 if the ClassDef is a REnum (subclasses t.REnum or uses @t.REnum)."""
@@ -245,7 +233,7 @@ novirtual class ClassGenBase:
if self._is_crenum_base(base_node, trans) == 1: return 1 if self._is_crenum_base(base_node, trans) == 1: return 1
return 0 return 0
def _has_decorator(self, decorator_list: list[ast.AST*]*, deco_name: str) -> int: def _has_decorator(self, decorator_list: list[ast.AST*]*, deco_name: str) -> int:
"""Return 1 if decorator_list contains the given decorator name.""" """Return 1 if decorator_list contains the given bare builtin decorator name."""
if decorator_list is None: return 0 if decorator_list is None: return 0
dn: sizet = len(decorator_list) dn: sizet = len(decorator_list)
if dn == 0: return 0 if dn == 0: return 0
@@ -253,16 +241,7 @@ novirtual class ClassGenBase:
deco: ast.AST* = decorator_list.get(di) deco: ast.AST* = decorator_list.get(di)
if deco is None: continue if deco is None: continue
k: int = deco.kind() k: int = deco.kind()
# @t.NoVTable form (Attribute). # @NoVTable / @CVTable / @TLS / @CStatic form (bare builtin Name).
if k == ast.ASTKind.Attribute:
at: ast.Attribute* = ast.Attribute*(deco)
if at.attr is not None and string.strcmp(at.attr, deco_name) == 0:
# Check that value is Name('t').
if at.value is not None and at.value.kind() == ast.ASTKind.Name:
vn: ast.Name* = ast.Name*(at.value)
if vn.id is not None and string.strcmp(vn.id, "t") == 0: return 1
return 0
# @NoVTable form (Name; from t import NoVTable).
if k == ast.ASTKind.Name: if k == ast.ASTKind.Name:
nm: ast.Name* = ast.Name*(deco) nm: ast.Name* = ast.Name*(deco)
if nm.id is not None and string.strcmp(nm.id, deco_name) == 0: return 1 if nm.id is not None and string.strcmp(nm.id, deco_name) == 0: return 1
@@ -290,10 +269,10 @@ novirtual class ClassGenBase:
k: int = deco.kind() k: int = deco.kind()
if k == ast.ASTKind.Call: if k == ast.ASTKind.Call:
cl: ast.Call* = ast.Call*(deco) cl: ast.Call* = ast.Call*(deco)
# @c.Attribute(t.attr.xxx) form: func is Attribute(c, "Attribute"). # @Attribute(attr.xxx) form: func is the bare builtin Name("Attribute").
if cl.func is None or cl.func.kind() != ast.ASTKind.Attribute: continue if cl.func is None or cl.func.kind() != ast.ASTKind.Name: continue
at0: ast.Attribute* = ast.Attribute*(cl.func) at0: ast.Name* = ast.Name*(cl.func)
if at0.attr is None or string.strcmp(at0.attr, "Attribute") != 0: continue if at0.id is None or string.strcmp(at0.id, "Attribute") != 0: continue
if cl.args is None: continue if cl.args is None: continue
an: sizet = len(cl.args) an: sizet = len(cl.args)
for ai in range(int(an)): for ai in range(int(an)):
@@ -319,7 +298,7 @@ novirtual class ClassGenBase:
return int(cn.int_val) return int(cn.int_val)
return 0 return 0
def _class_aligned_value(self, decorator_list: list[ast.AST*]*) -> int: def _class_aligned_value(self, decorator_list: list[ast.AST*]*) -> int:
"""Return the alignment bytes from @c.Attribute(t.attr.aligned(N)); 0 when absent.""" """Return the alignment bytes from @Attribute(attr.aligned(N)); 0 when absent."""
if decorator_list is None: return 0 if decorator_list is None: return 0
dn: sizet = len(decorator_list) dn: sizet = len(decorator_list)
if dn == 0: return 0 if dn == 0: return 0
@@ -328,9 +307,9 @@ novirtual class ClassGenBase:
if deco is None: continue if deco is None: continue
if deco.kind() == ast.ASTKind.Call: if deco.kind() == ast.ASTKind.Call:
cl: ast.Call* = ast.Call*(deco) cl: ast.Call* = ast.Call*(deco)
if cl.func is None or cl.func.kind() != ast.ASTKind.Attribute: continue if cl.func is None or cl.func.kind() != ast.ASTKind.Name: continue
at0: ast.Attribute* = ast.Attribute*(cl.func) at0: ast.Name* = ast.Name*(cl.func)
if at0.attr is None or string.strcmp(at0.attr, "Attribute") != 0: continue if at0.id is None or string.strcmp(at0.id, "Attribute") != 0: continue
if cl.args is None: continue if cl.args is None: continue
an: sizet = len(cl.args) an: sizet = len(cl.args)
for ai in range(int(an)): for ai in range(int(an)):
@@ -366,9 +345,6 @@ novirtual class ClassGenBase:
vn: ast.Name* = ast.Name*(at.value) vn: ast.Name* = ast.Name*(at.value)
is_prop_name: int = 0 is_prop_name: int = 0
if vn.id is not None and string.strcmp(vn.id, "property") == 0: is_prop_name = 1 if vn.id is not None and string.strcmp(vn.id, "property") == 0: is_prop_name = 1
# @t.TLS one-time init marker (mirrors TPC HandlesFunctions):
# value must be Name('t'); bare @TLS is handled by the Name branch.
if vn.id is not None and string.strcmp(vn.id, "t") == 0 and string.strcmp(at.attr, "TLS") == 0: meta |= META_TLS
# Recognize @property.setter and @x.setter/getter/deleter. # Recognize @property.setter and @x.setter/getter/deleter.
if is_prop_name != 0 or string.strcmp(at.attr, "setter") == 0 or string.strcmp(at.attr, "getter") == 0 or string.strcmp(at.attr, "deleter") == 0: if is_prop_name != 0 or string.strcmp(at.attr, "setter") == 0 or string.strcmp(at.attr, "getter") == 0 or string.strcmp(at.attr, "deleter") == 0:
if string.strcmp(at.attr, "setter") == 0: meta |= GenTypes.META_PROP_SETTER if string.strcmp(at.attr, "setter") == 0: meta |= GenTypes.META_PROP_SETTER
+15 -3
View File
@@ -42,7 +42,7 @@ novirtual class ClassGenEnum(ClassGenBase.ClassGenBase):
# [enum bool rule] `t.CBool` and bare `bool` both materialize as 8 bits in enum member annotations, # [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). # 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) if string.strcmp(at.attr, "CBool") == 0: return mlirlite.Int8(pool)
# Other t.CXxx types: unified (CTypedef dynamic table first, fall back to map_t_type) # Other t.CXxx types: unified (CTypedef dynamic table first, fall back to map_builtin_type)
ty: mlirlite.LLVMType* = self.Gen.TypeGenResolve.resolve_typedef_or_map(pool, at.attr, imported_modules, from_imports) ty: mlirlite.LLVMType* = self.Gen.TypeGenResolve.resolve_typedef_or_map(pool, at.attr, imported_modules, from_imports)
if ty is not None: return ty if ty is not None: return ty
# [S4 fail-fast] annotated but unresolvable -> no silent i32 fallback # [S4 fail-fast] annotated but unresolvable -> no silent i32 fallback
@@ -266,7 +266,7 @@ novirtual class ClassGenEnum(ClassGenBase.ClassGenBase):
tgt: ast.Name* = ast.Name*(aa.target) tgt: ast.Name* = ast.Name*(aa.target)
if tgt.id is None: continue if tgt.id is None: continue
# [enum spec fail-fast] Members may not be pointer types: t.CPtr / CRef / # [enum spec fail-fast] Members may not be pointer types: rawptr / CRef /
# CTempRef / CAutoPtr / rawptr[...] / str / bytes and unions carrying a pointer. Checked before type resolution. # CTempRef / CAutoPtr / rawptr[...] / str / bytes and unions carrying a pointer. Checked before type resolution.
if self._enum_member_annotation_is_ptr(aa.annotation) == 1: if self._enum_member_annotation_is_ptr(aa.annotation) == 1:
self.Gen.TypeGenErr.fatal_error(aa, f"enum member '{tgt.id}' cannot be a pointer type (enum values only support integer member types)", "TypeError") self.Gen.TypeGenErr.fatal_error(aa, f"enum member '{tgt.id}' cannot be a pointer type (enum values only support integer member types)", "TypeError")
@@ -601,6 +601,18 @@ novirtual class ClassGenEnum(ClassGenBase.ClassGenBase):
# analysis; the hardcoded size table below is only a fallback for t/c core primitives. # analysis; the hardcoded size table below is only a fallback for t/c core primitives.
dyn_sz: int = self.Gen.TypeGenResolve.get_type_size_bytes(pool, type_name, None, None) dyn_sz: int = self.Gen.TypeGenResolve.get_type_size_bytes(pool, type_name, None, None)
if dyn_sz > 0: return dyn_sz if dyn_sz > 0: return dyn_sz
# Builtin short names
if string.strcmp(type_name, "sizet") == 0 or string.strcmp(type_name, "size_t") == 0: return 8
if string.strcmp(type_name, "int8") == 0 or string.strcmp(type_name, "uint8") == 0: return 1
if string.strcmp(type_name, "uchar") == 0: return 1
if string.strcmp(type_name, "int16") == 0 or string.strcmp(type_name, "uint16") == 0: return 2
if string.strcmp(type_name, "short") == 0 or string.strcmp(type_name, "ushort") == 0: return 2
if string.strcmp(type_name, "int32") == 0 or string.strcmp(type_name, "uint32") == 0: return 4
if string.strcmp(type_name, "int") == 0 or string.strcmp(type_name, "uint") == 0: return 4
if string.strcmp(type_name, "long") == 0 or string.strcmp(type_name, "ulong") == 0: return 4
if string.strcmp(type_name, "int64") == 0 or string.strcmp(type_name, "uint64") == 0: return 8
if string.strcmp(type_name, "longlong") == 0 or string.strcmp(type_name, "ulonglong") == 0: return 8
if string.strcmp(type_name, "ptrdifft") == 0: return 8
# Builtin types # Builtin types
if string.strcmp(type_name, "int") == 0: return 4 if string.strcmp(type_name, "int") == 0: return 4
if string.strcmp(type_name, "CInt") == 0: return 4 if string.strcmp(type_name, "CInt") == 0: return 4
@@ -793,7 +805,7 @@ novirtual class ClassGenEnum(ClassGenBase.ClassGenBase):
# Register into HandlesStruct. # Register into HandlesStruct.
# A forward-declared placeholder for THIS class may have been created *during the variant walk* # A forward-declared placeholder for THIS class may have been created *during the variant walk*
# above: a variant field like `Pointee: LLVMType*` is `BinOp(LLVMType | t.CPtr)` (parser's # above: a variant field like `Pointee: LLVMType*` is `BinOp(LLVMType | rawptr)` (parser's
# _make_ptr_type expands the C-style suffix into a union, NOT into `rawptr[...]`), so resolving it # _make_ptr_type expands the C-style suffix into a union, NOT into `rawptr[...]`), so resolving it
# resolves the class name and calls ensure_forward_class. register_struct de-duplicates by Ty # resolves the class name and calls ensure_forward_class. register_struct de-duplicates by Ty
# pointer, so it would add a SECOND same-named entry and get_struct_type/find_struct (first match # pointer, so it would add a SECOND same-named entry and get_struct_type/find_struct (first match
+2 -2
View File
@@ -318,10 +318,10 @@ novirtual class ClassGenGeneric(ClassGenBase.ClassGenBase):
if sign == TSpec.TS_SIGN_SIGNED: return "int64" if sign == TSpec.TS_SIGN_SIGNED: return "int64"
return None return None
# ---- Floats: exact bit width (no signedness concept, unambiguous) ---- # ---- Floats: exact bit width (no signedness concept, unambiguous) ----
if string.strcmp(buf, "half") == 0: return "t.CFloat16T" if string.strcmp(buf, "half") == 0: return "float16"
if string.strcmp(buf, "float") == 0: return "float" if string.strcmp(buf, "float") == 0: return "float"
if string.strcmp(buf, "double") == 0: return "double" if string.strcmp(buf, "double") == 0: return "double"
if string.strcmp(buf, "fp128") == 0: return "t.CFloat128T" if string.strcmp(buf, "fp128") == 0: return "float128"
return None return None
def infer_scalar_type_arg_from_llvm_ty(self, pool: memhub.MemBuddy*, ty: mlirlite.LLVMType*, sign: int) -> str: def infer_scalar_type_arg_from_llvm_ty(self, pool: memhub.MemBuddy*, ty: mlirlite.LLVMType*, sign: int) -> str:
+1 -1
View File
@@ -694,7 +694,7 @@ novirtual class ClassGenVTable(ClassGenBase.ClassGenBase):
arg_m: ast.Arg* = ast.Arg*(alist.get(ai_m)) arg_m: ast.Arg* = ast.Arg*(alist.get(ai_m))
mm: int = 0 mm: int = 0
if arg_m is not None and arg_m.annotation is not None: if arg_m is not None and arg_m.annotation is not None:
if self.Gen.TypeGenResolve._is_t_attr(arg_m.annotation, "CVoid", imported_modules) == 0: if self.Gen.TypeGenResolve._is_builtin_marker(arg_m.annotation, "CVoid", imported_modules) == 0:
mm = self.Gen.TypeGenResolve.ref_param_mode(arg_m.annotation, imported_modules) mm = self.Gen.TypeGenResolve.ref_param_mode(arg_m.annotation, imported_modules)
if mm == 1: mode_buf[mode_cnt] = char(49) if mm == 1: mode_buf[mode_cnt] = char(49)
elif mm == 2: mode_buf[mode_cnt] = char(50) elif mm == 2: mode_buf[mode_cnt] = char(50)
+48 -45
View File
@@ -65,6 +65,42 @@ novirtual class ExprGenAttr(ExprGenBase.ExprGenBase):
def _is_t_type_object_name(self, name: str) -> int: def _is_t_type_object_name(self, name: str) -> int:
"""Check whether name is a t.py type-object name (CPtr/CInt/CDouble, ...).""" """Check whether name is a t.py type-object name (CPtr/CInt/CDouble, ...)."""
if name is None: return 0 if name is None: return 0
# builtin short type names (int8/uint64/sizet/...)
if string.strcmp(name, "sizet") == 0: return 1
if string.strcmp(name, "size_t") == 0: return 1
if string.strcmp(name, "int8") == 0: return 1
if string.strcmp(name, "int16") == 0: return 1
if string.strcmp(name, "int32") == 0: return 1
if string.strcmp(name, "int64") == 0: return 1
if string.strcmp(name, "uint8") == 0: return 1
if string.strcmp(name, "uint16") == 0: return 1
if string.strcmp(name, "uint32") == 0: return 1
if string.strcmp(name, "uint64") == 0: return 1
if string.strcmp(name, "intfast8") == 0: return 1
if string.strcmp(name, "intfast16") == 0: return 1
if string.strcmp(name, "intfast32") == 0: return 1
if string.strcmp(name, "intfast64") == 0: return 1
if string.strcmp(name, "uintfast8") == 0: return 1
if string.strcmp(name, "uintfast16") == 0: return 1
if string.strcmp(name, "uintfast32") == 0: return 1
if string.strcmp(name, "uintfast64") == 0: return 1
if string.strcmp(name, "intleast8") == 0: return 1
if string.strcmp(name, "intleast16") == 0: return 1
if string.strcmp(name, "intleast32") == 0: return 1
if string.strcmp(name, "intleast64") == 0: return 1
if string.strcmp(name, "uintleast8") == 0: return 1
if string.strcmp(name, "uintleast16") == 0: return 1
if string.strcmp(name, "uintleast32") == 0: return 1
if string.strcmp(name, "uintleast64") == 0: return 1
if string.strcmp(name, "uchar") == 0: return 1
if string.strcmp(name, "short") == 0: return 1
if string.strcmp(name, "ushort") == 0: return 1
if string.strcmp(name, "uint") == 0: return 1
if string.strcmp(name, "long") == 0: return 1
if string.strcmp(name, "ulong") == 0: return 1
if string.strcmp(name, "longlong") == 0: return 1
if string.strcmp(name, "ulonglong") == 0: return 1
if string.strcmp(name, "ptrdifft") == 0: return 1
# type object names in t.py (PascalCase, initial uppercase) # type object names in t.py (PascalCase, initial uppercase)
if string.strcmp(name, "CPtr") == 0: return 1 if string.strcmp(name, "CPtr") == 0: return 1
if string.strcmp(name, "CInt") == 0: return 1 if string.strcmp(name, "CInt") == 0: return 1
@@ -217,36 +253,16 @@ novirtual class ExprGenAttr(ExprGenBase.ExprGenBase):
if use_ty is None: use_ty = mlirlite.Int8(pool) if use_ty is None: use_ty = mlirlite.Int8(pool)
# ensure cross-module struct types in use_ty have an opaque declaration # ensure cross-module struct types in use_ty have an opaque declaration
mlirlite.module_ensure_opaque_for_type(mod, pool, use_ty) mlirlite.module_ensure_opaque_for_type(mod, pool, use_ty)
# special case: type objects in t.py (CPtr/CInt/CDouble etc.). t.py is not compiled but
# these objects are referenced at runtime, so emit a linkonce_odr definition with null init.
is_t_type_obj: int = 0
if string.strcmp(mod_path_w, "t") == 0: is_t_type_obj = self._is_t_type_object_name(at.attr)
# a class name is not a module-level global: registered classes/structs/REnums are always # a class name is not a module-level global: registered classes/structs/REnums are always
# handled by the class/REnum paths, never manufactured into an external global here. # handled by the class/REnum paths, never manufactured into an external global here.
if is_t_type_obj == 0 and self.Gen.StructGen.find_struct(at.attr) is not None: return None if self.Gen.StructGen.find_struct(at.attr) is not None: return None
# fallback declaration name: t type objects (t.py uncompiled, no sha1) keep the bare # fallback declaration name: other module attributes carry the target module's SHA1 prefix.
# linkonce_odr name; other module attributes carry the target module's SHA1 prefix. gv_name_ba: str = self.Gen.FuncGenBase._mangle_name_with_sha1(self._lookup_module_sha1(mod_path_w), at.attr)
gv_name_ba: str = at.attr
if is_t_type_obj == 0: gv_name_ba = self.Gen.FuncGenBase._mangle_name_with_sha1(self._lookup_module_sha1(mod_path_w), at.attr)
ext_gv: mlirlite.GlobalVariable* = mlirlite.new_global_variable(pool, gv_name_ba, use_ty) ext_gv: mlirlite.GlobalVariable* = mlirlite.new_global_variable(pool, gv_name_ba, use_ty)
if ext_gv is not None: if ext_gv is not None:
if is_t_type_obj != 0: # regular module attribute: external declaration (definition provided elsewhere)
# t.CPtr etc.: create a definition with a null initializer (linkonce_odr) ext_gv.Linkage = "external"
ext_gv.Linkage = "linkonce_odr" ext_gv.Initializer = None
# initializer text for i8* null
null_init: bytes = pool.alloc(8)
if null_init is not None:
null_init[0] = 'n'
null_init[1] = 'u'
null_init[2] = 'l'
null_init[3] = 'l'
null_init[4] = '\0'
ext_gv.Initializer = null_init
else: ext_gv.Initializer = None
else:
# regular module attribute: external declaration (definition provided elsewhere)
ext_gv.Linkage = "external"
ext_gv.Initializer = None
mlirlite.module_add_global(mod, ext_gv) mlirlite.module_add_global(mod, ext_gv)
return self.make_global_ref(pool, gv_name_ba, use_ty) return self.make_global_ref(pool, gv_name_ba, use_ty)
def _lookup_module_sha1(self, mod_name: str) -> str: def _lookup_module_sha1(self, mod_name: str) -> str:
@@ -256,32 +272,19 @@ novirtual class ExprGenAttr(ExprGenBase.ExprGenBase):
# module attribute (sys._mbuddy → global), then struct field (GEP + load). Name bases skip the struct load. # module attribute (sys._mbuddy → global), then struct field (GEP + load). Name bases skip the struct load.
# _get_cderef_attr_base - base pointer of a c.Deref(ptr).attr access (like *p.a → p->a): return the ptr # _get_cderef_attr_base - base pointer of a `(*ptr).attr` access (like p->a): return the ptr
# itself, not the loaded struct value; None for non-c.Deref forms (_deref_if_ptr_ptr resolves the level). # itself, not the loaded struct value; None for non-deref forms (_deref_if_ptr_ptr resolves the level).
def _get_cderef_attr_base(self, builder: mlirlite.IRBuilder*, pool: memhub.MemBuddy*, mod: mlirlite.LLVMModule*, at_value: ast.AST*, trans: HT.Translator* = None) -> mlirlite.Value*: def _get_cderef_attr_base(self, builder: mlirlite.IRBuilder*, pool: memhub.MemBuddy*, mod: mlirlite.LLVMModule*, at_value: ast.AST*, trans: HT.Translator* = None) -> mlirlite.Value*:
"""Return the ptr itself for a dereferenced attribute base (`c.Deref(ptr).attr` / `(*ptr).attr`), else None (no load).""" """Return the ptr itself for a dereferenced attribute base (`(*ptr).attr`), else None (no load)."""
if at_value is None: return None if at_value is None: return None
# New syntax form: `(*ptr).attr` is AST UnaryOp(OpKind.Deref) — identical semantics to the legacy # `(*ptr).attr` is AST UnaryOp(OpKind.Deref). Without this branch the attribute write path
# `c.Deref(ptr).attr`. Without this branch the attribute write path sees a *loaded struct value* # sees a *loaded struct value* as the base; both paths must GEP the pointer itself.
# as the base and fails with "base=非指针类型,无法做属性写入"; the read path silently does an
# extra struct load. Both must GEP the pointer itself (R4 migration of `c.Deref` -> `*`).
if at_value.kind() == ast.ASTKind.UnaryOp: if at_value.kind() == ast.ASTKind.UnaryOp:
uo_cd: ast.UnaryOp* = ast.UnaryOp*(at_value) uo_cd: ast.UnaryOp* = ast.UnaryOp*(at_value)
if uo_cd.op != ast.OpKind.Deref or uo_cd.operand is None: return None if uo_cd.op != ast.OpKind.Deref or uo_cd.operand is None: return None
inner_uo: mlirlite.Value* = self.Gen.ExprGen.translate_value(builder, pool, mod, uo_cd.operand, None, 0, trans) inner_uo: mlirlite.Value* = self.Gen.ExprGen.translate_value(builder, pool, mod, uo_cd.operand, None, 0, trans)
if inner_uo is not None and self.is_ptr_type(inner_uo.Ty) != 0: return inner_uo if inner_uo is not None and self.is_ptr_type(inner_uo.Ty) != 0: return inner_uo
return None return None
if at_value.kind() != ast.ASTKind.Call: return None
cl_cd: ast.Call* = ast.Call*(at_value)
if cl_cd.func is None or cl_cd.func.kind() != ast.ASTKind.Attribute: return None
f_cd: ast.Attribute* = ast.Attribute*(cl_cd.func)
if f_cd.attr is None or string.strcmp(f_cd.attr, "Deref") != 0: return None
if f_cd.value is None or f_cd.value.kind() != ast.ASTKind.Name: return None
nm_cd: ast.Name* = ast.Name*(f_cd.value)
if nm_cd.id is None or string.strcmp(nm_cd.id, "c") != 0: return None
if cl_cd.args is None or len(cl_cd.args) < 1: return None
inner_cd: mlirlite.Value* = self.Gen.ExprGen.translate_value(builder, pool, mod, cl_cd.args.get(0), None, 0, trans)
if inner_cd is not None and self.is_ptr_type(inner_cd.Ty) != 0: return inner_cd
return None return None
def translate_attribute(self, builder: mlirlite.IRBuilder*, pool: memhub.MemBuddy*, mod: mlirlite.LLVMModule*, node: ast.AST*, trans: HT.Translator* = None) -> mlirlite.Value*: def translate_attribute(self, builder: mlirlite.IRBuilder*, pool: memhub.MemBuddy*, mod: mlirlite.LLVMModule*, node: ast.AST*, trans: HT.Translator* = None) -> mlirlite.Value*:
"""Translate an attribute access and return the loaded field value.""" """Translate an attribute access and return the loaded field value."""
@@ -482,7 +485,7 @@ novirtual class ExprGenAttr(ExprGenBase.ExprGenBase):
return None # it is a submodule, hand it to the nested attribute path return None # it is a submodule, hand it to the nested attribute path
# special case: t.ASM_DESCR is the inline-asm constraint namespace, not a CDefine; its # special case: t.ASM_DESCR is the inline-asm constraint namespace, not a CDefine; its
# .XXX is handled by _asm_resolve_constraint, so return None for the nested path to handle. # .XXX is handled by _asm_resolve_constraint, so return None for the nested path to handle.
if string.strcmp(nm_ma.id, "t") == 0 and string.strcmp(at.attr, "ASM_DESCR") == 0: return None if string.strcmp(nm_ma.id, "ASM_DESCR") == 0 and string.strcmp(at.attr, "ASM_DESCR") == 0: return None
# ALL_CAPS module attribute that is not a CDefine: load it as a cross-module global when the # ALL_CAPS module attribute that is not a CDefine: load it as a cross-module global when the
# module defines a same-named global (e.g. PA_DEPS_MAX_STR); else fail-fast (undefined value). # module defines a same-named global (e.g. PA_DEPS_MAX_STR); else fail-fast (undefined value).
if self.Gen.ExprGenCdef._is_cdefine_name_pattern(at.attr) != 0: if self.Gen.ExprGenCdef._is_cdefine_name_pattern(at.attr) != 0:
+3 -4
View File
@@ -328,12 +328,11 @@ novirtual class ExprGenCdef(ExprGenBase.ExprGenBase):
if match_ok == 0: continue if match_ok == 0: continue
if pyi_buf[line_start + name_len] != ':': continue if pyi_buf[line_start + name_len] != ':': continue
# check the line contains "t.CDefine" # check the line contains the CDefine marker
# scan the line for "t.CDefine"
has_cdefine: int = 0 has_cdefine: int = 0
ci: sizet = line_start + name_len + 1 ci: sizet = line_start + name_len + 1
while ci + 8 <= line_start + line_len: while ci + 7 <= line_start + line_len:
if (pyi_buf[ci] == 't' and pyi_buf[ci + 1] == '.' and pyi_buf[ci + 2] == 'C' and pyi_buf[ci + 3] == 'D' and pyi_buf[ci + 4] == 'e' and pyi_buf[ci + 5] == 'f' and pyi_buf[ci + 6] == 'i' and pyi_buf[ci + 7] == 'n' and pyi_buf[ci + 8] == 'e'): if (pyi_buf[ci] == 'C' and pyi_buf[ci + 1] == 'D' and pyi_buf[ci + 2] == 'e' and pyi_buf[ci + 3] == 'f' and pyi_buf[ci + 4] == 'i' and pyi_buf[ci + 5] == 'n' and pyi_buf[ci + 6] == 'e'):
has_cdefine = 1 has_cdefine = 1
break break
ci += 1 ci += 1
+2 -14
View File
@@ -201,13 +201,7 @@ novirtual class ExprGenOps(ExprGenBase.ExprGenBase):
k: int = node.kind() k: int = node.kind()
if k == ast.ASTKind.Call: if k == ast.ASTKind.Call:
cl: ast.Call* = ast.Call*(node) cl: ast.Call* = ast.Call*(node)
if cl.func is not None and cl.func.kind() == ast.ASTKind.Attribute: if cl.func is not None and cl.func.kind() == ast.ASTKind.Attribute: return 0
at: ast.Attribute* = ast.Attribute*(cl.func)
if at.value is not None and at.value.kind() == ast.ASTKind.Name:
mod_nm: ast.Name* = ast.Name*(at.value)
if mod_nm.id is not None and string.strcmp(mod_nm.id, "t") == 0 and at.attr is not None:
if self._type_name_unsigned(at.attr) != 0: return 1
return 0
# Plain function call: widen by the callee's **declared return type**. Without this, # Plain function call: widen by the callee's **declared return type**. Without this,
# `uint64(f())` with `def f() -> uint32` emitted `sext i32 -> i64` (verified in the # `uint64(f())` with `def f() -> uint32` emitted `sext i32 -> i64` (verified in the
# generated IR), so a u32 with bit 31 set grew 0xFFFFFFFF in the high half — a real # generated IR), so a u32 with bit 31 set grew 0xFFFFFFFF in the high half — a real
@@ -359,13 +353,7 @@ novirtual class ExprGenOps(ExprGenBase.ExprGenBase):
k: int = node.kind() k: int = node.kind()
if k == ast.ASTKind.Call: if k == ast.ASTKind.Call:
cl: ast.Call* = ast.Call*(node) cl: ast.Call* = ast.Call*(node)
if cl.func is not None and cl.func.kind() == ast.ASTKind.Attribute: if cl.func is not None and cl.func.kind() == ast.ASTKind.Attribute: return 0
at: ast.Attribute* = ast.Attribute*(cl.func)
if at.value is not None and at.value.kind() == ast.ASTKind.Name:
mod_nm: ast.Name* = ast.Name*(at.value)
if mod_nm.id is not None and string.strcmp(mod_nm.id, "t") == 0 and at.attr is not None:
if self._spec_sign_from_annot(at.attr) == TSpec.TS_SIGN_SIGNED: return 1
return 0
if k == ast.ASTKind.Name: if k == ast.ASTKind.Name:
nm: ast.Name* = ast.Name*(node) nm: ast.Name* = ast.Name*(node)
if nm.id is not None: if nm.id is not None:
+13 -24
View File
@@ -42,23 +42,12 @@ novirtual class FuncGenBase(CallGenBase.CallGenBase):
call_node: ast.Call* = ast.Call*(deco) call_node: ast.Call* = ast.Call*(deco)
if call_node.func is None: continue if call_node.func is None: continue
cfk: int = call_node.func.kind() cfk: int = call_node.func.kind()
if cfk != ast.ASTKind.Attribute: continue
# Detect c.Attribute # Detect the builtin Attribute(...) wrapper emitted by @__attribute__(...).
func_attr: ast.Attribute* = ast.Attribute*(call_node.func) if cfk != ast.ASTKind.Name: continue
if func_attr.attr is None: continue func_nm: ast.Name* = ast.Name*(call_node.func)
if string.strcmp(func_attr.attr, "Attribute") != 0: continue if func_nm.id is None: continue
if func_attr.value is None: continue if string.strcmp(func_nm.id, "Attribute") != 0: continue
fvk: int = func_attr.value.kind()
if fvk != ast.ASTKind.Name: continue
c_name: ast.Name* = ast.Name*(func_attr.value)
if c_name.id is None: continue
if string.strcmp(c_name.id, "c") != 0: continue
# 不要求 `import c`:`@__attribute__(...)` 会被 ast/parser.vp(_DesugarMagicAttribute)
# 脱糖成 `@c.Attribute(...)`,这里的 `c.` 前缀是编译器自己产生的(用户写的是
# `@__attribute__`,并非 `@c.Attribute`)。若在此要求 `import c`,用户明明没引入 c
# 就会把整条装饰器静默丢弃(`stackrealign`/`always_inline` 等函数属性全失效)。
# extract_func_section 本就没有这个守卫,二者保持一致。
# Iterate args and extract attrs # Iterate args and extract attrs
if call_node.args is None: continue if call_node.args is None: continue
@@ -80,7 +69,7 @@ novirtual class FuncGenBase(CallGenBase.CallGenBase):
if found_any == 0: return None if found_any == 0: return None
return attrs_buf return attrs_buf
def extract_func_section(self, pool: memhub.MemBuddy*, decorator_list: list[ast.AST*]*, imported_modules: str) -> str: def extract_func_section(self, pool: memhub.MemBuddy*, decorator_list: list[ast.AST*]*, imported_modules: str) -> str:
"""Extract @c.Attribute(t.attr.section(...)) as the attr-group text.""" """Extract @Attribute(attr.section(...)) as the attr-group text."""
if pool is None or decorator_list is None: return None if pool is None or decorator_list is None: return None
dn: sizet = len(decorator_list) dn: sizet = len(decorator_list)
if dn == 0: return None if dn == 0: return None
@@ -88,9 +77,9 @@ novirtual class FuncGenBase(CallGenBase.CallGenBase):
deco: ast.AST* = decorator_list.get(di) deco: ast.AST* = decorator_list.get(di)
if deco is None or deco.kind() != ast.ASTKind.Call: continue if deco is None or deco.kind() != ast.ASTKind.Call: continue
call_node: ast.Call* = ast.Call*(deco) call_node: ast.Call* = ast.Call*(deco)
if call_node.func is None or call_node.func.kind() != ast.ASTKind.Attribute: continue if call_node.func is None or call_node.func.kind() != ast.ASTKind.Name: continue
func_attr: ast.Attribute* = ast.Attribute*(call_node.func) func_nm: ast.Name* = ast.Name*(call_node.func)
if func_attr.attr is None or string.strcmp(func_attr.attr, "Attribute") != 0: continue if func_nm.id is None or string.strcmp(func_nm.id, "Attribute") != 0: continue
if call_node.args is None: continue if call_node.args is None: continue
args_list: list[ast.AST*]* = call_node.args args_list: list[ast.AST*]* = call_node.args
an: sizet = len(args_list) an: sizet = len(args_list)
@@ -272,11 +261,11 @@ novirtual class FuncGenBase(CallGenBase.CallGenBase):
for ai in range(int(an)): for ai in range(int(an)):
arg: ast.Arg* = ast.Arg*(alist.get(ai)) arg: ast.Arg* = ast.Arg*(alist.get(ai))
if arg is not None and arg.arg is not None: if arg is not None and arg.arg is not None:
# t.CVoid means no args: skip # CVoid / void means no args: skip
if arg.annotation is not None: if arg.annotation is not None:
if arg.annotation.kind() == ast.ASTKind.Attribute: if arg.annotation.kind() == ast.ASTKind.Name:
at_cf: ast.Attribute* = ast.Attribute*(arg.annotation) at_cn: ast.Name* = ast.Name*(arg.annotation)
if at_cf.attr is not None and string.strcmp(at_cf.attr, "CVoid") == 0: continue if at_cn.id is not None and (string.strcmp(at_cn.id, "CVoid") == 0 or string.strcmp(at_cn.id, "void") == 0): continue
param_ty: mlirlite.LLVMType* = i32_ty param_ty: mlirlite.LLVMType* = i32_ty
if arg.annotation is not None: if arg.annotation is not None:
resolved: mlirlite.LLVMType* = self.Gen.TypeGenResolve.resolve_annotation_type(pool, arg.annotation, None, None, None, 0) resolved: mlirlite.LLVMType* = self.Gen.TypeGenResolve.resolve_annotation_type(pool, arg.annotation, None, None, None, 0)
+14 -20
View File
@@ -38,7 +38,7 @@ novirtual class FuncGenDef(CallGenBase.CallGenBase):
arg: ast.Arg* = ast.Arg*(alist.get(i)) arg: ast.Arg* = ast.Arg*(alist.get(i))
if arg is None or arg.arg is None: continue if arg is None or arg.arg is None: continue
# CVoid params are not emitted into the signature, so they must not occupy a mode slot. # CVoid params are not emitted into the signature, so they must not occupy a mode slot.
if arg.annotation is not None and self.Gen.TypeGenResolve._is_t_attr(arg.annotation, "CVoid", imp) != 0: continue if arg.annotation is not None and self.Gen.TypeGenResolve._is_builtin_marker(arg.annotation, "CVoid", imp) != 0: continue
m: int = 0 m: int = 0
if arg.annotation is not None: m = self.Gen.TypeGenResolve.ref_param_mode(arg.annotation, imp) if arg.annotation is not None: m = self.Gen.TypeGenResolve.ref_param_mode(arg.annotation, imp)
if m == 1: buf[cnt] = char(49) if m == 1: buf[cnt] = char(49)
@@ -110,14 +110,11 @@ novirtual class FuncGenDef(CallGenBase.CallGenBase):
# Value-reference markers are parameter-only: `-> &T` fails fast (never a silent Ptr(T) return). # Value-reference markers are parameter-only: `-> &T` fails fast (never a silent Ptr(T) return).
self.Gen.TypeGenResolve.reject_ref_marker_position(fd.returns, imported_modules, "a function return type") self.Gen.TypeGenResolve.reject_ref_marker_position(fd.returns, imported_modules, "a function return type")
ret_ty = self.Gen.TypeGenResolve.resolve_annotation_type(pool, fd.returns, imported_modules, from_imports, trans, 0) ret_ty = self.Gen.TypeGenResolve.resolve_annotation_type(pool, fd.returns, imported_modules, from_imports, trans, 0)
# A lone t.CExport return annotation is an export modifier with no real type -> Void # A lone CExport return annotation is an export modifier with no real type -> Void
# (CExport fell back to i32, forcing the tail check to return i32: mm.vp free root cause). # (CExport fell back to i32, forcing the tail check to return i32: mm.vp free root cause).
if fd.returns.kind() == ast.ASTKind.Attribute: if fd.returns.kind() == ast.ASTKind.Name:
ce_at: ast.Attribute* = ast.Attribute*(fd.returns) ce_at: ast.Name* = ast.Name*(fd.returns)
if ce_at.attr is not None and string.strcmp(ce_at.attr, "CExport") == 0: if ce_at.id is not None and string.strcmp(ce_at.id, "CExport") == 0: ret_ty = mlirlite.Void(pool)
if ce_at.value is not None and ce_at.value.kind() == ast.ASTKind.Name:
ce_nm: ast.Name* = ast.Name*(ce_at.value)
if ce_nm.id is not None and string.strcmp(ce_nm.id, "t") == 0: ret_ty = mlirlite.Void(pool)
if ret_ty is None and fd.returns is not None: if ret_ty is None and fd.returns is not None:
if self.Gen.TypeGenErr.has_decorator_marker(fd.returns, "State", 0) != 0: ret_ty = mlirlite.Void(pool) if self.Gen.TypeGenErr.has_decorator_marker(fd.returns, "State", 0) != 0: ret_ty = mlirlite.Void(pool)
# @c.CReturn decorator removed: encountering it is a compile-time error (use -> tuple[...] for multiple returns) # @c.CReturn decorator removed: encountering it is a compile-time error (use -> tuple[...] for multiple returns)
@@ -206,7 +203,7 @@ novirtual class FuncGenDef(CallGenBase.CallGenBase):
if arg is None or arg.arg is None: continue if arg is None or arg.arg is None: continue
# CVoid means empty param: skip # CVoid means empty param: skip
if arg.annotation is not None: if arg.annotation is not None:
if self.Gen.TypeGenResolve._is_t_attr(arg.annotation, "CVoid", imported_modules) != 0: continue if self.Gen.TypeGenResolve._is_builtin_marker(arg.annotation, "CVoid", imported_modules) != 0: continue
param_ty: mlirlite.LLVMType* = i32_ty param_ty: mlirlite.LLVMType* = i32_ty
if arg.annotation is not None: if arg.annotation is not None:
resolved: mlirlite.LLVMType* = self.Gen.TypeGenResolve.resolve_annotation_type(pool, arg.annotation, imported_modules, from_imports, trans, 0) resolved: mlirlite.LLVMType* = self.Gen.TypeGenResolve.resolve_annotation_type(pool, arg.annotation, imported_modules, from_imports, trans, 0)
@@ -280,14 +277,11 @@ novirtual class FuncGenDef(CallGenBase.CallGenBase):
# Value-reference markers are parameter-only: `-> &T` fails fast (never a silent Ptr(T) return). # Value-reference markers are parameter-only: `-> &T` fails fast (never a silent Ptr(T) return).
self.Gen.TypeGenResolve.reject_ref_marker_position(fd.returns, imported_modules, "a function return type") self.Gen.TypeGenResolve.reject_ref_marker_position(fd.returns, imported_modules, "a function return type")
ret_ty = self.Gen.TypeGenResolve.resolve_annotation_type(pool, fd.returns, imported_modules, from_imports, trans, 0) ret_ty = self.Gen.TypeGenResolve.resolve_annotation_type(pool, fd.returns, imported_modules, from_imports, trans, 0)
# A lone t.CExport return annotation is an export modifier with no real type -> Void # A lone CExport return annotation is an export modifier with no real type -> Void
# (CExport fell back to i32, forcing return i32: mm.vp free root cause). Mixed types keep the resolved result. # (CExport fell back to i32, forcing return i32: mm.vp free root cause). Mixed types keep the resolved result.
if fd.returns.kind() == ast.ASTKind.Attribute: if fd.returns.kind() == ast.ASTKind.Name:
ce_at2: ast.Attribute* = ast.Attribute*(fd.returns) ce_at2: ast.Name* = ast.Name*(fd.returns)
if ce_at2.attr is not None and string.strcmp(ce_at2.attr, "CExport") == 0: if ce_at2.id is not None and string.strcmp(ce_at2.id, "CExport") == 0: ret_ty = mlirlite.Void(pool)
if ce_at2.value is not None and ce_at2.value.kind() == ast.ASTKind.Name:
ce_nm2: ast.Name* = ast.Name*(ce_at2.value)
if ce_nm2.id is not None and string.strcmp(ce_nm2.id, "t") == 0: ret_ty = mlirlite.Void(pool)
# If the return annotation is purely a decorator marker (e.g. t.State, no real type), use void. # If the return annotation is purely a decorator marker (e.g. t.State, no real type), use void.
# Must be checked before infer_return_type, which would return at least i32. # Must be checked before infer_return_type, which would return at least i32.
@@ -412,7 +406,7 @@ novirtual class FuncGenDef(CallGenBase.CallGenBase):
arg_e: ast.Arg* = ast.Arg*(alist_e.get(ai_e)) arg_e: ast.Arg* = ast.Arg*(alist_e.get(ai_e))
if arg_e is not None and arg_e.arg is not None: if arg_e is not None and arg_e.arg is not None:
# CVoid means empty param: skip, do not add to the function signature # CVoid means empty param: skip, do not add to the function signature
if arg_e.annotation is not None and self.Gen.TypeGenResolve._is_t_attr(arg_e.annotation, "CVoid", imported_modules) != 0: continue if arg_e.annotation is not None and self.Gen.TypeGenResolve._is_builtin_marker(arg_e.annotation, "CVoid", imported_modules) != 0: continue
param_ty_e: mlirlite.LLVMType* = i32_ty param_ty_e: mlirlite.LLVMType* = i32_ty
if arg_e.annotation is not None: if arg_e.annotation is not None:
resolved_e: mlirlite.LLVMType* = self.Gen.TypeGenResolve.resolve_annotation_type(pool, arg_e.annotation, imported_modules, from_imports, trans, 0) resolved_e: mlirlite.LLVMType* = self.Gen.TypeGenResolve.resolve_annotation_type(pool, arg_e.annotation, imported_modules, from_imports, trans, 0)
@@ -479,7 +473,7 @@ novirtual class FuncGenDef(CallGenBase.CallGenBase):
rearg: ast.Arg* = ast.Arg*(reuse_list.get(reuse_i)) rearg: ast.Arg* = ast.Arg*(reuse_list.get(reuse_i))
if rearg is None or rearg.arg is None: continue if rearg is None or rearg.arg is None: continue
# CVoid means empty param: not part of the signature (mirrors the add_param loops above) # CVoid means empty param: not part of the signature (mirrors the add_param loops above)
if rearg.annotation is not None and self.Gen.TypeGenResolve._is_t_attr(rearg.annotation, "CVoid", imported_modules) != 0: continue if rearg.annotation is not None and self.Gen.TypeGenResolve._is_builtin_marker(rearg.annotation, "CVoid", imported_modules) != 0: continue
if reused_p is None: if reused_p is None:
# 复用声明形参少于定义:签名与函数体必然错配,禁止静默,fail-fast # 复用声明形参少于定义:签名与函数体必然错配,禁止静默,fail-fast
self.Gen.TypeGenErr.fatal_error(rearg, f"reused declaration for '{fd.name}' has fewer parameters than the definition", "TypeError") self.Gen.TypeGenErr.fatal_error(rearg, f"reused declaration for '{fd.name}' has fewer parameters than the definition", "TypeError")
@@ -537,7 +531,7 @@ novirtual class FuncGenDef(CallGenBase.CallGenBase):
arg: ast.Arg* = ast.Arg*(alist.get(ai)) arg: ast.Arg* = ast.Arg*(alist.get(ai))
if arg is not None and arg.arg is not None: if arg is not None and arg.arg is not None:
# CVoid means empty param: skip, do not add to the function signature # CVoid means empty param: skip, do not add to the function signature
if arg.annotation is not None and self.Gen.TypeGenResolve._is_t_attr(arg.annotation, "CVoid", imported_modules) != 0: continue if arg.annotation is not None and self.Gen.TypeGenResolve._is_builtin_marker(arg.annotation, "CVoid", imported_modules) != 0: continue
param_ty: mlirlite.LLVMType* = i32_ty param_ty: mlirlite.LLVMType* = i32_ty
if arg.annotation is not None: if arg.annotation is not None:
resolved: mlirlite.LLVMType* = self.Gen.TypeGenResolve.resolve_annotation_type(pool, arg.annotation, imported_modules, from_imports, trans, 0) resolved: mlirlite.LLVMType* = self.Gen.TypeGenResolve.resolve_annotation_type(pool, arg.annotation, imported_modules, from_imports, trans, 0)
@@ -678,7 +672,7 @@ novirtual class FuncGenDef(CallGenBase.CallGenBase):
if arg2 is not None and arg2.arg is not None: if arg2 is not None and arg2.arg is not None:
# CVoid means empty param: skip, do not create an alloca # CVoid means empty param: skip, do not create an alloca
if arg2.annotation is not None: if arg2.annotation is not None:
if self.Gen.TypeGenResolve._is_t_attr(arg2.annotation, "CVoid", imported_modules) != 0: continue if self.Gen.TypeGenResolve._is_builtin_marker(arg2.annotation, "CVoid", imported_modules) != 0: continue
param_ty2: mlirlite.LLVMType* = i32_ty param_ty2: mlirlite.LLVMType* = i32_ty
if arg2.annotation is not None: if arg2.annotation is not None:
resolved2: mlirlite.LLVMType* = self.Gen.TypeGenResolve.resolve_annotation_type(pool, arg2.annotation, imported_modules, from_imports, trans, 0) resolved2: mlirlite.LLVMType* = self.Gen.TypeGenResolve.resolve_annotation_type(pool, arg2.annotation, imported_modules, from_imports, trans, 0)
+2 -2
View File
@@ -94,7 +94,7 @@ novirtual class FuncGenMixin(CallGenBase.CallGenBase):
arg_n: ast.Arg* = ast.Arg*(alist_n.get(ai_n)) 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 is None or arg_n.arg is None: continue
if arg_n.annotation is not None: if arg_n.annotation is not None:
if self.Gen.TypeGenResolve._is_t_attr(arg_n.annotation, "CVoid", imported_modules) != 0: continue if self.Gen.TypeGenResolve._is_builtin_marker(arg_n.annotation, "CVoid", imported_modules) != 0: continue
pty_n: mlirlite.LLVMType* = i32_ty pty_n: mlirlite.LLVMType* = i32_ty
if arg_n.annotation is not None: 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) 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)) 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 is None or arg_p.arg is None: continue
if arg_p.annotation is not None: if arg_p.annotation is not None:
if self.Gen.TypeGenResolve._is_t_attr(arg_p.annotation, "CVoid", imported_modules) != 0: continue if self.Gen.TypeGenResolve._is_builtin_marker(arg_p.annotation, "CVoid", imported_modules) != 0: continue
if idx_p >= param_count: break if idx_p >= param_count: break
pty2_n: mlirlite.LLVMType* = i32_ty pty2_n: mlirlite.LLVMType* = i32_ty
if param_tys is not None: if param_tys is not None:
+5 -5
View File
@@ -86,16 +86,16 @@ def fatal_error(node: ast.AST*, msg: str, err_type: str = "SyntaxError") -> None
if gg is not None: gg.TypeGenErr.fatal_error(node, msg, err_type) if gg is not None: gg.TypeGenErr.fatal_error(node, msg, err_type)
# ---------------- map_t_type (delegates to TypeGenResolve.map_t_type) ---------------- # ---------------- map_builtin_type (delegates to TypeGenResolve.map_builtin_type) ----------------
def map_t_type(pool: memhub.MemBuddy*, type_name: str) -> mlirlite.LLVMType*: def map_builtin_type(pool: memhub.MemBuddy*, type_name: str) -> mlirlite.LLVMType*:
"""Delegate to gen.map_t_type""" """Delegate to gen.map_builtin_type"""
if _g_gen is not None: _g_gen.TypeGenResolve.map_t_type(pool, type_name) if _g_gen is not None: _g_gen.TypeGenResolve.map_builtin_type(pool, type_name)
return None return None
# ---------------- resolve_typedef_or_map (delegates to TypeGenResolve.resolve_typedef_or_map) ---------------- # ---------------- resolve_typedef_or_map (delegates to TypeGenResolve.resolve_typedef_or_map) ----------------
def resolve_typedef_or_map(pool: memhub.MemBuddy*, name: str, imported_modules: str, from_imports: str, trans: HT.Translator* = None) -> mlirlite.LLVMType*: def resolve_typedef_or_map(pool: memhub.MemBuddy*, name: str, imported_modules: str, from_imports: str, trans: HT.Translator* = None) -> mlirlite.LLVMType*:
"""Delegate to gen.resolve_typedef_or_map: CTypedef table first, then map_t_type.""" """Delegate to gen.resolve_typedef_or_map: CTypedef table first, then map_builtin_type."""
if _g_gen is not None: return _g_gen.TypeGenResolve.resolve_typedef_or_map(pool, name, imported_modules, from_imports, trans) if _g_gen is not None: return _g_gen.TypeGenResolve.resolve_typedef_or_map(pool, name, imported_modules, from_imports, trans)
return None return None
+6 -3
View File
@@ -546,9 +546,12 @@ novirtual class ImportsGenMixin:
def _check_import_sha1(self, node: ast.AST*, mod_name: str) -> int: def _check_import_sha1(self, node: ast.AST*, mod_name: str) -> int:
"""Fail-fast when an imported module's SHA1 is not registered.""" """Fail-fast when an imported module's SHA1 is not registered."""
if mod_name is None: return 1 if mod_name is None: return 1
# t/c are internal libraries exempt from includes scanning; so are ctraits (a .vpi # t / c were retired: their types are builtin and their C helpers now have language syntax.
# compile-time macro lib) and viperbuild (a compiler builtin macro lib). if string.strcmp(mod_name, "t") == 0 or string.strcmp(mod_name, "c") == 0:
if string.strcmp(mod_name, "t") == 0 or string.strcmp(mod_name, "c") == 0 or string.strcmp(mod_name, "ctraits") == 0: return 1 self._report_import_error(node, "`import t` / `import c` is retired; builtin types and C helpers no longer live in modules")
return 0
# ctraits (a .vpi compile-time macro lib) and viperbuild (a compiler builtin macro lib) stay exempt.
if string.strcmp(mod_name, "ctraits") == 0: return 1
if string.strcmp(mod_name, "viperbuild") == 0: return 1 if string.strcmp(mod_name, "viperbuild") == 0: return 1
mod_sha1: str = StubMergerStore.LookupSha1ByModName(mod_name) mod_sha1: str = StubMergerStore.LookupSha1ByModName(mod_name)
if mod_sha1 is None: if mod_sha1 is None:
+1 -8
View File
@@ -53,18 +53,11 @@ novirtual class StmtGenCifMixin:
return 0 return 0
def _is_cif_call(self, node: ast.AST*) -> int: def _is_cif_call(self, node: ast.AST*) -> int:
"""Check whether test is a cif marker call (c.@CIf family, or a bare @CIf-family name).""" """Check whether test is a cif marker call (bare @CIf-family name)."""
if node is None or node.kind() != ast.ASTKind.Call: return 0 if node is None or node.kind() != ast.ASTKind.Call: return 0
cl: ast.Call* = ast.Call*(node) cl: ast.Call* = ast.Call*(node)
if cl.func is None: return 0 if cl.func is None: return 0
fk: int = cl.func.kind() fk: int = cl.func.kind()
if fk == ast.ASTKind.Attribute:
at: ast.Attribute* = ast.Attribute*(cl.func)
if at.attr is None: return 0
if at.value is not None and at.value.kind() == ast.ASTKind.Name:
vn: ast.Name* = ast.Name*(at.value)
if vn.id is not None and string.strcmp(vn.id, "c") == 0: return self._is_cif_attr(at.attr)
return 0
if fk == ast.ASTKind.Name: if fk == ast.ASTKind.Name:
nm: ast.Name* = ast.Name*(cl.func) nm: ast.Name* = ast.Name*(cl.func)
if nm.id is not None and self._is_cif_attr(nm.id) != 0: return 1 if nm.id is not None and self._is_cif_attr(nm.id) != 0: return 1
+2 -2
View File
@@ -2652,7 +2652,7 @@ novirtual class StructGenMixin:
if mod_buf_f is not None: if mod_buf_f is not None:
string.strncpy(mod_buf_f, name, mod_len_f) string.strncpy(mod_buf_f, name, mod_len_f)
mod_buf_f[mod_len_f] = '\0' mod_buf_f[mod_len_f] = '\0'
if string.strcmp(mod_buf_f, "t") != 0 and string.strcmp(mod_buf_f, "c") != 0: if mod_buf_f is not None:
# A 16-hex module segment is a SHA1 literal (e.g. "{9aab}.Scene3D", TypeGenResolve L1397-1402's sha1-prefix form): skip the store module-name lookup (store keys are module names, not sha1) and match name+sha1 exactly. # A 16-hex module segment is a SHA1 literal (e.g. "{9aab}.Scene3D", TypeGenResolve L1397-1402's sha1-prefix form): skip the store module-name lookup (store keys are module names, not sha1) and match name+sha1 exactly.
# Otherwise _resolve_module_sha1("9aab") returns None and a bare-name fallback returns the first-registered wrong cross-module Scene3D entry (VKernel gfx / App gfx both define Scene3D -> wrong ownership -> ret_sha1=0ae -> Phase A _deps_pending deadlock). [C10 convergence] The module segment is exactly 16 lowercase hex = SHA1 literal: go through GenApi.is_sha1_hex16 (single source of truth). # Otherwise _resolve_module_sha1("9aab") returns None and a bare-name fallback returns the first-registered wrong cross-module Scene3D entry (VKernel gfx / App gfx both define Scene3D -> wrong ownership -> ret_sha1=0ae -> Phase A _deps_pending deadlock). [C10 convergence] The module segment is exactly 16 lowercase hex = SHA1 literal: go through GenApi.is_sha1_hex16 (single source of truth).
mod_hex_ok: int = GenApi.is_sha1_hex16(mod_buf_f) mod_hex_ok: int = GenApi.is_sha1_hex16(mod_buf_f)
@@ -2905,7 +2905,7 @@ novirtual class StructGenMixin:
if mod_buf_q is not None: if mod_buf_q is not None:
string.strncpy(mod_buf_q, class_name, mod_len_q) string.strncpy(mod_buf_q, class_name, mod_len_q)
mod_buf_q[mod_len_q] = '\0' mod_buf_q[mod_len_q] = '\0'
if string.strcmp(mod_buf_q, "t") != 0 and string.strcmp(mod_buf_q, "c") != 0: if mod_buf_q is not None:
# A 16-hex module segment ("{16hex}.Class", the normalized generic type-arg form) is the module SHA1; a registered class proves that module compiled, so match name+sha1 exactly without _resolve_module_sha1 (store keys are module names, so querying the sha1 is always None and would fail-fast wrongly). # A 16-hex module segment ("{16hex}.Class", the normalized generic type-arg form) is the module SHA1; a registered class proves that module compiled, so match name+sha1 exactly without _resolve_module_sha1 (store keys are module names, so querying the sha1 is always None and would fail-fast wrongly).
# [C10 convergence] The module segment is exactly 16 lowercase hex = SHA1 literal: go through GenApi.is_sha1_hex16; generic subscript dots are already skipped by _last_dot_outside_brackets, so only top-level a.b forms remain here. # [C10 convergence] The module segment is exactly 16 lowercase hex = SHA1 literal: go through GenApi.is_sha1_hex16; generic subscript dots are already skipped by _last_dot_outside_brackets, so only top-level a.b forms remain here.
mod_hex_ok_q: int = GenApi.is_sha1_hex16(mod_buf_q) mod_hex_ok_q: int = GenApi.is_sha1_hex16(mod_buf_q)
+8 -20
View File
@@ -351,29 +351,17 @@ class TypeGenCType(TypeGenBase.TypeGenBase):
vn: sizet = len(self.CTypedefVisiting) vn: sizet = len(self.CTypedefVisiting)
if vn > 0: self.CTypedefVisiting.pop() if vn > 0: self.CTypedefVisiting.pop()
def _is_ctypedef_annotation(self, annotation: ast.AST*) -> int: def _is_ctypedef_annotation(self, annotation: ast.AST*) -> int:
"""Return 1 if the annotation is t.CTypedef, otherwise 0.""" """Return 1 if the annotation is the CTypedef marker, otherwise 0."""
if annotation is None: return 0 if annotation is None: return 0
k_v: int = annotation.kind() if annotation.kind() != ast.ASTKind.Name: return 0
if k_v != ast.ASTKind.Attribute: return 0 nm: ast.Name* = ast.Name*(annotation)
at: ast.Attribute* = ast.Attribute*(annotation) nm_id_v: str = ast.name_get_id(nm)
# Use ast module accessors for field access: a direct at.attr read may emit a if nm_id_v is None: return 0
# wrong GEP from the stub type and crash strcmp.
at_attr_v: str = ast.attribute_get_attr(at)
if at_attr_v is None: return 0
# Length-bounded compare instead of strcmp: the token may lack a NUL and strcmp # Length-bounded compare instead of strcmp: the token may lack a NUL and strcmp
# reads past the page; check byte 9 is a terminator/separator. # reads past the page; check byte 8 is a terminator/separator.
if string.strncmp(at_attr_v, "CTypedef", 8) != 0: return 0 if string.strncmp(nm_id_v, "CTypedef", 8) != 0: return 0
sep_ch_d: char = at_attr_v[8] sep_ch_d: char = nm_id_v[8]
if sep_ch_d != '\0' and sep_ch_d != ' ' and sep_ch_d != '\n' and sep_ch_d != '\r' and sep_ch_d != '\t' and sep_ch_d != '|': return 0 if sep_ch_d != '\0' and sep_ch_d != ' ' and sep_ch_d != '\n' and sep_ch_d != '\r' and sep_ch_d != '\t' and sep_ch_d != '|': return 0
at_val_v: ast.AST* = ast.attribute_get_value(at)
if at_val_v is None or at_val_v.kind() != ast.ASTKind.Name: return 0
mod_nm: ast.Name* = ast.Name*(at_val_v)
mod_id_v: str = ast.name_get_id(mod_nm)
if mod_id_v is None: return 0
# same length-bounded compare ("t" may lack a NUL)
if string.strncmp(mod_id_v, "t", 1) != 0: return 0
sep_ch_m: char = mod_id_v[1]
if sep_ch_m != '\0' and sep_ch_m != ' ' and sep_ch_m != '\n' and sep_ch_m != '\r' and sep_ch_m != '\t' and sep_ch_m != '|': return 0
return 1 return 1
def ScanCTypedefDeclarations(self, pool: memhub.MemBuddy*, tree: ast.AST*) -> int: def ScanCTypedefDeclarations(self, pool: memhub.MemBuddy*, tree: ast.AST*) -> int:
"""Scan top-level AST nodes and register all NAME: t.CTypedef = Type declarations.""" """Scan top-level AST nodes and register all NAME: t.CTypedef = Type declarations."""
+6 -7
View File
@@ -140,14 +140,13 @@ novirtual class TypeGenErr(TypeGenBase.TypeGenBase):
k: int = node.kind() k: int = node.kind()
# Attribute node: t.CExtern, t.State, t.CExport, t.CInline # Name node: builtin markers CExtern / State / CExport / CInline / CStatic / CConst / CVolatile
if k == ast.ASTKind.Attribute: if k == ast.ASTKind.Name:
at: ast.Attribute* = ast.Attribute*(node) nm: ast.Name* = ast.Name*(node)
if at.attr is not None: if nm.id is not None and string.strcmp(nm.id, marker_name) == 0: return 1
if string.strcmp(at.attr, marker_name) == 0: return 1
return 0 return 0
# BinOp node: int | t.CExtern (recurse into both children) # BinOp node: int | CExtern (recurse into both children)
if k == ast.ASTKind.BinOp: if k == ast.ASTKind.BinOp:
bop: ast.BinOp* = ast.BinOp*(node) bop: ast.BinOp* = ast.BinOp*(node)
if self.has_decorator_marker(bop.left, marker_name, depth + 1) != 0: return 1 if self.has_decorator_marker(bop.left, marker_name, depth + 1) != 0: return 1
@@ -298,7 +297,7 @@ novirtual class TypeGenErr(TypeGenBase.TypeGenBase):
if arg is None or arg.arg is None: continue if arg is None or arg.arg is None: continue
param_ty: mlirlite.LLVMType* = i32_ty param_ty: mlirlite.LLVMType* = i32_ty
if arg.annotation is not None: if arg.annotation is not None:
# Unified: prefer the CTypedef dynamic table, fall back to map_t_type # Unified: prefer the CTypedef dynamic table, fall back to map_builtin_type
# (stage 2 removed hardcoded aliases; BYTE etc. now rely on library typedefs) # (stage 2 removed hardcoded aliases; BYTE etc. now rely on library typedefs)
if arg.annotation.kind() == ast.ASTKind.Attribute: if arg.annotation.kind() == ast.ASTKind.Attribute:
at: ast.Attribute* = ast.Attribute*(arg.annotation) at: ast.Attribute* = ast.Attribute*(arg.annotation)
+138 -177
View File
@@ -266,6 +266,7 @@ novirtual class TypeGenResolve(TypeGenBase.TypeGenBase):
# TypeGenCDef.lookup_cdefine_constant), since -1 is a valid constant. # TypeGenCDef.lookup_cdefine_constant), since -1 is a valid constant.
self.Gen.TypeGenCDef.lookup_cdefine_constant(name) self.Gen.TypeGenCDef.lookup_cdefine_constant(name)
if self.Gen.TypeGenCDef.is_cdefine_found() != 0: return 1 if self.Gen.TypeGenCDef.is_cdefine_found() != 0: return 1
if self.map_builtin_type(pool, name) is not None: return 1
if string.strcmp(name, "int") == 0: return 1 if string.strcmp(name, "int") == 0: return 1
if string.strcmp(name, "char") == 0: return 1 if string.strcmp(name, "char") == 0: return 1
if string.strcmp(name, "bool") == 0: return 1 if string.strcmp(name, "bool") == 0: return 1
@@ -278,25 +279,20 @@ novirtual class TypeGenResolve(TypeGenBase.TypeGenBase):
if name[0] >= 'A' and name[0] <= 'Z': return 1 if name[0] >= 'A' and name[0] <= 'Z': return 1
return 0 return 0
# _is_ptr_marker_text - whether text is a bare pointer marker (t.CPtr / c.CPtr / CPtr); # _is_ptr_marker_text - whether text is the builtin pointer marker (CPtr).
# only the CPtr family counts, consistent with _is_cptr_marker.
def _is_ptr_marker_text(self, txt: str) -> int: def _is_ptr_marker_text(self, txt: str) -> int:
"""Whether txt is a bare pointer marker (one pointer layer): 1=yes / 0=no.""" """Whether txt is a bare pointer marker (one pointer layer): 1=yes / 0=no."""
if txt is None: return 0 if txt is None: return 0
if string.strcmp(txt, "CPtr") == 0: return 1 if string.strcmp(txt, "CPtr") == 0: return 1
if string.strcmp(txt, "t.CPtr") == 0: return 1
if string.strcmp(txt, "c.CPtr") == 0: return 1
return 0 return 0
# _is_ptr_subscript_container_text - whether text is a pointer subscript container name # _is_ptr_subscript_container_text - whether text is a pointer subscript container name
# (rawptr / t.rawptr / c.rawptr / CPtr); subscript forms then fold to `T*`. # (rawptr / CPtr); subscript forms then fold to `T*`.
def _is_ptr_subscript_container_text(self, txt: str) -> int: def _is_ptr_subscript_container_text(self, txt: str) -> int:
"""Whether txt names a pointer subscript container (rawptr/CPtr): 1=yes / 0=no.""" """Whether txt names a pointer subscript container (rawptr/CPtr): 1=yes / 0=no."""
if txt is None: return 0 if txt is None: return 0
if self._is_ptr_marker_text(txt) != 0: return 1 if self._is_ptr_marker_text(txt) != 0: return 1
if string.strcmp(txt, "rawptr") == 0: return 1 if string.strcmp(txt, "rawptr") == 0: return 1
if string.strcmp(txt, "t.rawptr") == 0: return 1
if string.strcmp(txt, "c.rawptr") == 0: return 1
return 0 return 0
# _ptr_star_text - append one pointer layer to a type-name text (base + "*"); # _ptr_star_text - append one pointer layer to a type-name text (base + "*");
@@ -517,20 +513,6 @@ novirtual class TypeGenResolve(TypeGenBase.TypeGenBase):
sha1c = self._class_true_def_sha1(pool, type_name, sha1c) sha1c = self._class_true_def_sha1(pool, type_name, sha1c)
res_c: str = self._concat_sha1_class(pool, sha1c, type_name) res_c: str = self._concat_sha1_class(pool, sha1c, type_name)
if res_c is not None: return res_c if res_c is not None: return res_c
# ---- 5.2 bare name from builtin t/c -> normalize to "t.X" / "c.X"; t/c have no
# module SHA1, so a bare import would otherwise split from `t.X`.
if from_imports is not None:
_bi_mod: str = self.Gen.ImportsGen.lookup_from_import(from_imports, type_name, 0)
if _bi_mod is not None:
if (string.strcmp(_bi_mod, "t") == 0 or string.strcmp(_bi_mod, "c") == 0) and self.map_t_type(pool, type_name) is not None:
_bi_len: sizet = string.strlen(_bi_mod) + 1 + string.strlen(type_name) + 1
_bi_buf: str = pool.alloc(_bi_len)
if _bi_buf is not None:
_bi_buf[0] = '\0'
string.strcat(_bi_buf, _bi_mod)
string.strcat(_bi_buf, ".")
string.strcat(_bi_buf, type_name)
return _bi_buf
# ---- 5.5 bare name that is a registered class: prefix with its defining module # ---- 5.5 bare name that is a registered class: prefix with its defining module
# SHA1; rule 5 only checks from_imports and would split specialization names. # SHA1; rule 5 only checks from_imports and would split specialization names.
if string.strchr(type_name, '[') is None and string.strchr(type_name, '.') is None: if string.strchr(type_name, '[') is None and string.strchr(type_name, '.') is None:
@@ -791,7 +773,7 @@ novirtual class TypeGenResolve(TypeGenBase.TypeGenBase):
"""Get the file currently being compiled (used for error attribution).""" """Get the file currently being compiled (used for error attribution)."""
return self.CurrentFile return self.CurrentFile
def resolve_typedef_or_map(self, pool: memhub.MemBuddy*, name: str, imported_modules: str, from_imports: str, trans: HT.Translator* = None) -> mlirlite.LLVMType*: def resolve_typedef_or_map(self, pool: memhub.MemBuddy*, name: str, imported_modules: str, from_imports: str, trans: HT.Translator* = None) -> mlirlite.LLVMType*:
"""Resolve a name as a CTypedef first, falling back to map_t_type.""" """Resolve a name as a CTypedef first, falling back to map_builtin_type."""
if name is None: return None if name is None: return None
if self.Gen.TypeGenCType.CTypedefResolveDepth < TypeGenMixin.MAX_CTYPEDEF_DEPTH: if self.Gen.TypeGenCType.CTypedefResolveDepth < TypeGenMixin.MAX_CTYPEDEF_DEPTH:
ts_rt: str = self.Gen.TypeGenCType.lookup_ctypedef(name) ts_rt: str = self.Gen.TypeGenCType.lookup_ctypedef(name)
@@ -804,7 +786,7 @@ novirtual class TypeGenResolve(TypeGenBase.TypeGenBase):
self.Gen.TypeGenCType.CTypedefResolveDepth -= 1 self.Gen.TypeGenCType.CTypedefResolveDepth -= 1
self.Gen.TypeGenCType._ctypedef_pop_visiting() self.Gen.TypeGenCType._ctypedef_pop_visiting()
if ity_rt is not None: return ity_rt if ity_rt is not None: return ity_rt
# [builtins last match] After the typedef table and map_t_type both miss, check # [builtins last match] After the typedef table and map_builtin_type both miss, check
# includes/builtins.vp (e.g. rawptr: t.CTypedef = t.CPtr) and parse recursively. # includes/builtins.vp (e.g. rawptr: t.CTypedef = t.CPtr) and parse recursively.
btd_r: str = self._lookup_builtins_typedef(pool, name) btd_r: str = self._lookup_builtins_typedef(pool, name)
if btd_r is not None: if btd_r is not None:
@@ -827,7 +809,7 @@ novirtual class TypeGenResolve(TypeGenBase.TypeGenBase):
lib_ty: mlirlite.LLVMType* = self.resolve_annotation_type(pool, lib_node, imported_modules, from_imports, trans, 1) lib_ty: mlirlite.LLVMType* = self.resolve_annotation_type(pool, lib_node, imported_modules, from_imports, trans, 1)
self.Gen.TypeGenCType.CTypedefResolveDepth -= 1 self.Gen.TypeGenCType.CTypedefResolveDepth -= 1
if lib_ty is not None: return lib_ty if lib_ty is not None: return lib_ty
# Bare Python/C builtin aliases: map_t_type only knows the t.CXxx spelling, so # Bare Python/C builtin aliases: map_builtin_type only knows the t.CXxx spelling, so
# resolve them here to match resolve_annotation_type's bare-name branch. Without # resolve them here to match resolve_annotation_type's bare-name branch. Without
# this the two-arg form int(x, t.CPtr) misses _tc_h4 and fails fast as "int". # this the two-arg form int(x, t.CPtr) misses _tc_h4 and fails fast as "int".
if string.strcmp(name, "str") == 0: return mlirlite.Ptr(pool, mlirlite.Int8(pool)) if string.strcmp(name, "str") == 0: return mlirlite.Ptr(pool, mlirlite.Int8(pool))
@@ -838,7 +820,7 @@ novirtual class TypeGenResolve(TypeGenBase.TypeGenBase):
if string.strcmp(name, "float") == 0: return mlirlite.Float(pool) if string.strcmp(name, "float") == 0: return mlirlite.Float(pool)
if string.strcmp(name, "double") == 0: return mlirlite.Double(pool) if string.strcmp(name, "double") == 0: return mlirlite.Double(pool)
if string.strcmp(name, "void") == 0: return mlirlite.Void(pool) if string.strcmp(name, "void") == 0: return mlirlite.Void(pool)
return self.map_t_type(pool, name) return self.map_builtin_type(pool, name)
def get_type_size_bytes(self, pool: memhub.MemBuddy*, name: str, imported_modules: str, from_imports: str) -> int: def get_type_size_bytes(self, pool: memhub.MemBuddy*, name: str, imported_modules: str, from_imports: str) -> int:
"""Return the type size in bytes via resolve_typedef_or_map, or 0 on failure.""" """Return the type size in bytes via resolve_typedef_or_map, or 0 on failure."""
if name is None: return 0 if name is None: return 0
@@ -962,7 +944,7 @@ novirtual class TypeGenResolve(TypeGenBase.TypeGenBase):
while name_l > 0 and buf[line_start + name_l - 1] == ' ': name_l -= 1 while name_l > 0 and buf[line_start + name_l - 1] == ' ': name_l -= 1
if name_l == 0 or name_l >= 128: return 0 if name_l == 0 or name_l >= 128: return 0
rest: str = buf + line_start + j + 1 rest: str = buf + line_start + j + 1
if string.strstr(rest, "t.CTypedef") is None: return 0 if string.strstr(rest, "CTypedef") is None: return 0
eq: str = string.strstr(rest, "=") eq: str = string.strstr(rest, "=")
if eq is None: return 0 if eq is None: return 0
vs = sizet(uint64(eq) - uint64(buf)) - line_start + 1 vs = sizet(uint64(eq) - uint64(buf)) - line_start + 1
@@ -979,18 +961,18 @@ novirtual class TypeGenResolve(TypeGenBase.TypeGenBase):
out_buf[name_l] = '\0' out_buf[name_l] = '\0'
# Normalize a C-style typedef RHS (`const T` / `T*`) into the canonical `|` union text, # Normalize a C-style typedef RHS (`const T` / `T*`) into the canonical `|` union text,
# mirroring TPC's TypeAnnotationResolver._NormalizeTypedefRhsText: leading const/volatile # mirroring TPC's TypeAnnotationResolver._NormalizeTypedefRhsText: leading const/volatile
# become `t.CConst |` / `t.CVolatile |`, trailing `*` runs become ` | t.CPtr`. Text without # become `CConst |` / `CVolatile |`, trailing `*` runs become ` | CPtr`. Text without
# these forms is copied verbatim (byte-identical to the pre-normalization behavior). # these forms is copied verbatim (byte-identical to the pre-normalization behavior).
pos: sizet = name_l + 1 pos: sizet = name_l + 1
k: sizet = vs k: sizet = vs
while True: while True:
if k + 6 <= ve and string.strncmp(buf + line_start + k, "const", 5) == 0 and (buf[line_start + k + 5] == ' ' or buf[line_start + k + 5] == '\t'): if k + 6 <= ve and string.strncmp(buf + line_start + k, "const", 5) == 0 and (buf[line_start + k + 5] == ' ' or buf[line_start + k + 5] == '\t'):
string.memcpy(out_buf + pos, "t.CConst | ", 11) string.memcpy(out_buf + pos, "CConst | ", 9)
pos += 11 pos += 9
k += 6 k += 6
elif k + 9 <= ve and string.strncmp(buf + line_start + k, "volatile", 8) == 0 and (buf[line_start + k + 8] == ' ' or buf[line_start + k + 8] == '\t'): elif k + 9 <= ve and string.strncmp(buf + line_start + k, "volatile", 8) == 0 and (buf[line_start + k + 8] == ' ' or buf[line_start + k + 8] == '\t'):
string.memcpy(out_buf + pos, "t.CVolatile | ", 14) string.memcpy(out_buf + pos, "CVolatile | ", 12)
pos += 14 pos += 12
k += 9 k += 9
else: else:
break break
@@ -1004,16 +986,16 @@ novirtual class TypeGenResolve(TypeGenBase.TypeGenBase):
vlen2: sizet = ve2 - k vlen2: sizet = ve2 - k
if vlen2 > 190: vlen2 = 190 if vlen2 > 190: vlen2 = 190
# Output capacity guard (register_ctypedef_from_source allocates 512 bytes for NAME\0VALUE\0). # Output capacity guard (register_ctypedef_from_source allocates 512 bytes for NAME\0VALUE\0).
if pos + vlen2 + ptrs * 11 > 500: if pos + vlen2 + ptrs * 7 > 500:
if pos + ptrs * 11 >= 500: ptrs = 0 if pos + ptrs * 7 >= 500: ptrs = 0
if pos + ptrs * 11 >= 500: vlen2 = 0 if pos + ptrs * 7 >= 500: vlen2 = 0
else: vlen2 = 500 - pos - ptrs * 11 else: vlen2 = 500 - pos - ptrs * 7
for ci2 in range(int(vlen2)): for ci2 in range(int(vlen2)):
out_buf[pos + ci2] = buf[line_start + k + ci2] out_buf[pos + ci2] = buf[line_start + k + ci2]
pos += vlen2 pos += vlen2
for pi in range(int(ptrs)): for pi in range(int(ptrs)):
string.memcpy(out_buf + pos, " | t.CPtr", 9) string.memcpy(out_buf + pos, " | CPtr", 7)
pos += 9 pos += 7
out_buf[pos] = '\0' out_buf[pos] = '\0'
return 1 return 1
def _lookup_builtins_typedef(self, pool: memhub.MemBuddy*, name: str) -> str: def _lookup_builtins_typedef(self, pool: memhub.MemBuddy*, name: str) -> str:
@@ -1024,10 +1006,10 @@ novirtual class TypeGenResolve(TypeGenBase.TypeGenBase):
self.Gen.TypeGenCType.LastBuiltinsTypedefName = self.Gen.TypeGenCType.store_builtins_diag(0, name) self.Gen.TypeGenCType.LastBuiltinsTypedefName = self.Gen.TypeGenCType.store_builtins_diag(0, name)
self.Gen.TypeGenCType.LastBuiltinsTypedefResult = None self.Gen.TypeGenCType.LastBuiltinsTypedefResult = None
# rawptr is built into the compiler (a reserved word on both TPC/VPC) and takes # rawptr is built into the compiler (a reserved word on both TPC/VPC) and takes
# priority over any external declaration; always return t.CPtr. # priority over any external declaration; always return CPtr.
if string.strcmp(name, "rawptr") == 0: if string.strcmp(name, "rawptr") == 0:
self.Gen.TypeGenCType.LastBuiltinsTypedefResult = "t.CPtr" self.Gen.TypeGenCType.LastBuiltinsTypedefResult = "CPtr"
return "t.CPtr" return "CPtr"
ts_b: str = self.Gen.TypeGenCType.lookup_ctypedef(name) ts_b: str = self.Gen.TypeGenCType.lookup_ctypedef(name)
if ts_b is not None and self.Gen.TypeGenCType.CTypedefFound != 0: if ts_b is not None and self.Gen.TypeGenCType.CTypedefFound != 0:
# The table value may still be a module-qualified ref (size_t -> stdint.size_t); expand it # The table value may still be a module-qualified ref (size_t -> stdint.size_t); expand it
@@ -1371,10 +1353,38 @@ novirtual class TypeGenResolve(TypeGenBase.TypeGenBase):
if entry is None or entry.Ty is None: return None if entry is None or entry.Ty is None: return None
return mlirlite.Ptr(pool, entry.Ty) return mlirlite.Ptr(pool, entry.Ty)
def map_t_type(self, pool: memhub.MemBuddy*, type_name: str) -> mlirlite.LLVMType*: def map_builtin_type(self, pool: memhub.MemBuddy*, type_name: str) -> mlirlite.LLVMType*:
"""Map a t.CXxx / c.CXxx type name to its LLVMType. Note: str = i8* = bytes.""" """Map a builtin type name to its LLVMType. Note: str = i8* = bytes."""
if type_name is None: return None if type_name is None: return None
# Builtin short names (no import / typedef required)
if string.strcmp(type_name, "sizet") == 0 or string.strcmp(type_name, "size_t") == 0: return mlirlite.Int64(pool)
elif string.strcmp(type_name, "int8") == 0 or string.strcmp(type_name, "intfast8") == 0 or string.strcmp(type_name, "intleast8") == 0: return mlirlite.Int8(pool)
elif string.strcmp(type_name, "int16") == 0 or string.strcmp(type_name, "intfast16") == 0 or string.strcmp(type_name, "intleast16") == 0: return mlirlite.Int16(pool)
elif string.strcmp(type_name, "int32") == 0 or string.strcmp(type_name, "intfast32") == 0 or string.strcmp(type_name, "intleast32") == 0: return mlirlite.Int32(pool)
elif string.strcmp(type_name, "int64") == 0 or string.strcmp(type_name, "intfast64") == 0 or string.strcmp(type_name, "intleast64") == 0: return mlirlite.Int64(pool)
elif string.strcmp(type_name, "uint8") == 0 or string.strcmp(type_name, "uintfast8") == 0 or string.strcmp(type_name, "uintleast8") == 0: return mlirlite.Int8(pool)
elif string.strcmp(type_name, "uint16") == 0 or string.strcmp(type_name, "uintfast16") == 0 or string.strcmp(type_name, "uintleast16") == 0: return mlirlite.Int16(pool)
elif string.strcmp(type_name, "uint32") == 0 or string.strcmp(type_name, "uintfast32") == 0 or string.strcmp(type_name, "uintleast32") == 0: return mlirlite.Int32(pool)
elif string.strcmp(type_name, "uint64") == 0 or string.strcmp(type_name, "uintfast64") == 0 or string.strcmp(type_name, "uintleast64") == 0: return mlirlite.Int64(pool)
elif string.strcmp(type_name, "uchar") == 0: return mlirlite.Int8(pool)
elif string.strcmp(type_name, "ushort") == 0: return mlirlite.Int16(pool)
elif string.strcmp(type_name, "uint") == 0: return mlirlite.Int32(pool)
elif string.strcmp(type_name, "short") == 0: return mlirlite.Int16(pool)
elif string.strcmp(type_name, "long") == 0: return mlirlite.Int32(pool)
elif string.strcmp(type_name, "ulong") == 0: return mlirlite.Int32(pool)
elif string.strcmp(type_name, "longlong") == 0: return mlirlite.Int64(pool)
elif string.strcmp(type_name, "ulonglong") == 0: return mlirlite.Int64(pool)
elif string.strcmp(type_name, "ptrdifft") == 0: return mlirlite.Int64(pool)
elif string.strcmp(type_name, "intptr") == 0 or string.strcmp(type_name, "uintptr") == 0: return mlirlite.Int64(pool)
# Exact-width float / char short names (stdint aliases use these directly)
elif string.strcmp(type_name, "float8") == 0 or string.strcmp(type_name, "float16") == 0: return mlirlite.Half(pool)
elif string.strcmp(type_name, "float32") == 0: return mlirlite.Float(pool)
elif string.strcmp(type_name, "float64") == 0: return mlirlite.Double(pool)
elif string.strcmp(type_name, "float128") == 0: return mlirlite.FP128(pool)
elif string.strcmp(type_name, "char8") == 0: return mlirlite.Int8(pool)
elif string.strcmp(type_name, "char16") == 0 or string.strcmp(type_name, "wchar") == 0: return mlirlite.Int16(pool)
elif string.strcmp(type_name, "char32") == 0: return mlirlite.Int32(pool)
# Basic integer types # Basic integer types
if string.strcmp(type_name, "CInt") == 0: return mlirlite.Int32(pool) if string.strcmp(type_name, "CInt") == 0: return mlirlite.Int32(pool)
elif string.strcmp(type_name, "CChar") == 0: return mlirlite.Int8(pool) elif string.strcmp(type_name, "CChar") == 0: return mlirlite.Int8(pool)
@@ -1402,6 +1412,10 @@ novirtual class TypeGenResolve(TypeGenBase.TypeGenBase):
elif string.strcmp(type_name, "CFloat32T") == 0: return mlirlite.Float(pool) elif string.strcmp(type_name, "CFloat32T") == 0: return mlirlite.Float(pool)
elif string.strcmp(type_name, "CFloat64T") == 0: return mlirlite.Double(pool) elif string.strcmp(type_name, "CFloat64T") == 0: return mlirlite.Double(pool)
elif string.strcmp(type_name, "CFloat128T") == 0: return mlirlite.FP128(pool) elif string.strcmp(type_name, "CFloat128T") == 0: return mlirlite.FP128(pool)
# Exact-width char types (C11 char8_t/char16_t/char32_t, Windows wchar_t = 16-bit)
elif string.strcmp(type_name, "CChar8T") == 0: return mlirlite.Int8(pool)
elif string.strcmp(type_name, "CChar16T") == 0 or string.strcmp(type_name, "CWCharT") == 0: return mlirlite.Int16(pool)
elif string.strcmp(type_name, "CChar32T") == 0: return mlirlite.Int32(pool)
# Pointer / void: t.CPtr = i8*, str = i8* = str = bytes # Pointer / void: t.CPtr = i8*, str = i8* = str = bytes
elif string.strcmp(type_name, "CPtr") == 0: return mlirlite.Ptr(pool, mlirlite.Int8(pool)) elif string.strcmp(type_name, "CPtr") == 0: return mlirlite.Ptr(pool, mlirlite.Int8(pool))
# Unsigned function pointer: bare t.Callable -> i8* (same LLVM repr as the full # Unsigned function pointer: bare t.Callable -> i8* (same LLVM repr as the full
@@ -1448,46 +1462,30 @@ novirtual class TypeGenResolve(TypeGenBase.TypeGenBase):
elif string.strcmp(type_name, "float") == 0: return mlirlite.Float(pool) elif string.strcmp(type_name, "float") == 0: return mlirlite.Float(pool)
elif string.strcmp(type_name, "double") == 0: return mlirlite.Double(pool) elif string.strcmp(type_name, "double") == 0: return mlirlite.Double(pool)
return None return None
def _is_t_attr(self, node: ast.AST*, attr_name: str, imported_modules: str) -> int: def _is_builtin_marker(self, node: ast.AST*, attr_name: str, imported_modules: str) -> int:
"""Whether a node is `t.attr_name` or a bare `attr_name` imported from t.""" """Whether a node is the bare builtin marker name (e.g. CPtr / CRef)."""
if node is None or attr_name is None: return 0 if node is None or attr_name is None: return 0
if node.kind() == ast.ASTKind.Attribute: if node.kind() != ast.ASTKind.Name: return 0
at: ast.Attribute* = ast.Attribute*(node) nm: ast.Name* = ast.Name*(node)
if at.attr is None: return 0 if nm.id is None: return 0
if string.strcmp(at.attr, attr_name) != 0: return 0 if string.strcmp(nm.id, attr_name) != 0: return 0
if at.value is None or at.value.kind() != ast.ASTKind.Name: return 0 return 1
val_nm: ast.Name* = ast.Name*(at.value)
if val_nm.id is None: return 0
if string.strcmp(val_nm.id, "t") == 0:
# t is a builtin library (SHA1-exempt) and may be absent from _imported_modules;
# an explicit t.CPtr / t.CVoid unambiguously means module t.
return 1
return 0
# Bare form: from t import attr_name -> Name(id == attr_name)
if node.kind() == ast.ASTKind.Name:
nm: ast.Name* = ast.Name*(node)
if nm.id is None: return 0
if string.strcmp(nm.id, attr_name) != 0: return 0
# A bare name requires module t to be imported (from t import X registers it)
if self.Gen.ImportsGen.is_module_imported(imported_modules, "t") == 0: return 0
return 1
return 0
def _is_cptr_marker(self, node: ast.AST*, imported_modules: str) -> int: def _is_cptr_marker(self, node: ast.AST*, imported_modules: str) -> int:
"""Whether a node is a CPtr pointer marker: 1=yes / 0=no.""" """Whether a node is a CPtr pointer marker: 1=yes / 0=no."""
if self._is_t_attr(node, "CPtr", imported_modules) != 0: return 1 if self._is_builtin_marker(node, "CPtr", imported_modules) != 0: return 1
if node is not None and node.kind() == ast.ASTKind.Name: if node is not None and node.kind() == ast.ASTKind.Name:
nm: ast.Name* = ast.Name*(node) nm: ast.Name* = ast.Name*(node)
if nm.id is not None and string.strcmp(nm.id, "CPtr") == 0: return 1 if nm.id is not None and string.strcmp(nm.id, "CPtr") == 0: return 1
return 0 return 0
def _is_ref_marker(self, node: ast.AST*, imported_modules: str) -> int: def _is_ref_marker(self, node: ast.AST*, imported_modules: str) -> int:
"""Whether a node is a CRef/CTempRef value-reference marker: 1=yes / 0=no.""" """Whether a node is a CRef/CTempRef value-reference marker: 1=yes / 0=no."""
if self._is_t_attr(node, "CRef", imported_modules) != 0: return 1 if self._is_builtin_marker(node, "CRef", imported_modules) != 0: return 1
if self._is_t_attr(node, "CTempRef", imported_modules) != 0: return 1 if self._is_builtin_marker(node, "CTempRef", imported_modules) != 0: return 1
return 0 return 0
def _is_byteorder_marker(self, node: ast.AST*, imported_modules: str) -> int: def _is_byteorder_marker(self, node: ast.AST*, imported_modules: str) -> int:
"""Whether a node is a byte-order marker: 0=none / 1=BigEndian / 2=LittleEndian.""" """Whether a node is a byte-order marker: 0=none / 1=BigEndian / 2=LittleEndian."""
if self._is_t_attr(node, "BigEndian", imported_modules) != 0: return 1 if self._is_builtin_marker(node, "BigEndian", imported_modules) != 0: return 1
if self._is_t_attr(node, "LittleEndian", imported_modules) != 0: return 2 if self._is_builtin_marker(node, "LittleEndian", imported_modules) != 0: return 2
return 0 return 0
def target_is_big_endian(self) -> int: def target_is_big_endian(self) -> int:
"""Whether the compilation target is big-endian (1) or little-endian (0). """Whether the compilation target is big-endian (1) or little-endian (0).
@@ -1511,12 +1509,7 @@ novirtual class TypeGenResolve(TypeGenBase.TypeGenBase):
"""Syntactic value-reference marker test (no import context): 0=none / 1=CRef / 2=CTempRef.""" """Syntactic value-reference marker test (no import context): 0=none / 1=CRef / 2=CTempRef."""
if node is None: return 0 if node is None: return 0
nm: str = None nm: str = None
if node.kind() == ast.ASTKind.Attribute: if node.kind() == ast.ASTKind.Name:
at: ast.Attribute* = ast.Attribute*(node)
if at.value is not None and at.value.kind() == ast.ASTKind.Name:
at_vn: ast.Name* = ast.Name*(at.value)
if at_vn.id is not None and string.strcmp(at_vn.id, "t") == 0: nm = at.attr
elif node.kind() == ast.ASTKind.Name:
nm = (ast.Name*(node)).id nm = (ast.Name*(node)).id
if nm is None: return 0 if nm is None: return 0
if string.strcmp(nm, "CRef") == 0: return 1 if string.strcmp(nm, "CRef") == 0: return 1
@@ -1527,10 +1520,10 @@ novirtual class TypeGenResolve(TypeGenBase.TypeGenBase):
if node is None or node.kind() != ast.ASTKind.BinOp: return 0 if node is None or node.kind() != ast.ASTKind.BinOp: return 0
bo: ast.BinOp* = ast.BinOp*(node) bo: ast.BinOp* = ast.BinOp*(node)
if bo.op != ast.OpKind.BitOr: return 0 if bo.op != ast.OpKind.BitOr: return 0
if self._is_t_attr(bo.left, "CTempRef", imported_modules) != 0: return 2 if self._is_builtin_marker(bo.left, "CTempRef", imported_modules) != 0: return 2
if self._is_t_attr(bo.right, "CTempRef", imported_modules) != 0: return 2 if self._is_builtin_marker(bo.right, "CTempRef", imported_modules) != 0: return 2
if self._is_t_attr(bo.left, "CRef", imported_modules) != 0: return 1 if self._is_builtin_marker(bo.left, "CRef", imported_modules) != 0: return 1
if self._is_t_attr(bo.right, "CRef", imported_modules) != 0: return 1 if self._is_builtin_marker(bo.right, "CRef", imported_modules) != 0: return 1
return 0 return 0
def reject_ref_marker_position(self, node: ast.AST*, imported_modules: str, where: str) -> int: def reject_ref_marker_position(self, node: ast.AST*, imported_modules: str, where: str) -> int:
"""Fail fast when a value-reference marker (&T / &temp T) is used outside a parameter position.""" """Fail fast when a value-reference marker (&T / &temp T) is used outside a parameter position."""
@@ -1538,18 +1531,15 @@ novirtual class TypeGenResolve(TypeGenBase.TypeGenBase):
self.Gen.TypeGenErr.fatal_error(node, f"value-reference parameter annotation '&T' / '&temp T' is only allowed in function parameter position, not for {where}", "TypeError") self.Gen.TypeGenErr.fatal_error(node, f"value-reference parameter annotation '&T' / '&temp T' is only allowed in function parameter position, not for {where}", "TypeError")
return 1 return 1
def _is_cptr_subscript(self, node: ast.AST*) -> int: def _is_cptr_subscript(self, node: ast.AST*) -> int:
"""Whether a node is a t.CPtr[T] subscript pointer type (e.g. t.CPtr[t.CPtr]).""" """Whether a node is a CPtr[T] subscript pointer type (e.g. CPtr[CPtr])."""
if node is None or node.kind() != ast.ASTKind.Subscript: return 0 if node is None or node.kind() != ast.ASTKind.Subscript: return 0
sub: ast.Subscript* = ast.Subscript*(node) sub: ast.Subscript* = ast.Subscript*(node)
if sub.value is None or sub.value.kind() != ast.ASTKind.Attribute: return 0 if sub.value is None or sub.value.kind() != ast.ASTKind.Name: return 0
at: ast.Attribute* = ast.Attribute*(sub.value) nm: ast.Name* = ast.Name*(sub.value)
if at.attr is None or string.strcmp(at.attr, "CPtr") != 0: return 0 if nm.id is None or string.strcmp(nm.id, "CPtr") != 0: return 0
if at.value is None or at.value.kind() != ast.ASTKind.Name: return 0
nm: ast.Name* = ast.Name*(at.value)
if nm.id is None or string.strcmp(nm.id, "t") != 0: return 0
return 1 return 1
def _is_tattr_marker(self, node: ast.AST*) -> int: def _is_cattr_marker(self, node: ast.AST*) -> int:
"""Whether a node is a `t.attr.<name>` marker (including the call form).""" """Whether a node is an `attr.<name>` builtin marker (including the call form)."""
if node is None: return 0 if node is None: return 0
inner: ast.AST* = node inner: ast.AST* = node
if inner.kind() == ast.ASTKind.Call: if inner.kind() == ast.ASTKind.Call:
@@ -1559,22 +1549,17 @@ novirtual class TypeGenResolve(TypeGenBase.TypeGenBase):
if inner.kind() != ast.ASTKind.Attribute: return 0 if inner.kind() != ast.ASTKind.Attribute: return 0
at: ast.Attribute* = ast.Attribute*(inner) at: ast.Attribute* = ast.Attribute*(inner)
if at.attr is None or at.value is None: return 0 if at.attr is None or at.value is None: return 0
if at.value.kind() != ast.ASTKind.Attribute: return 0 if at.value.kind() != ast.ASTKind.Name: return 0
root: ast.Attribute* = ast.Attribute*(at.value) rn: ast.Name* = ast.Name*(at.value)
if root.attr is None or root.value is None: return 0 if rn.id is None or string.strcmp(rn.id, "attr") != 0: return 0
if root.value.kind() != ast.ASTKind.Name: return 0
rn: ast.Name* = ast.Name*(root.value)
if rn.id is None or string.strcmp(rn.id, "t") != 0: return 0
if string.strcmp(root.attr, "attr") != 0: return 0
return 1 return 1
def _is_cvoid_marker(self, node: ast.AST*, imported_modules: str) -> int: def _is_cvoid_marker(self, node: ast.AST*, imported_modules: str) -> int:
"""Whether a node is a CVoid pointer marker: 1=yes / 0=no.""" """Whether a node is a CVoid pointer marker: 1=yes / 0=no."""
if self._is_t_attr(node, "CVoid", imported_modules) != 0: return 1 if self._is_builtin_marker(node, "CVoid", imported_modules) != 0: return 1
if node is not None and node.kind() == ast.ASTKind.Name: if node is not None and node.kind() == ast.ASTKind.Name:
nm: ast.Name* = ast.Name*(node) nm: ast.Name* = ast.Name*(node)
# Bare CVoid (from t import CVoid) is synonymous with the builtin alias void # Bare CVoid is synonymous with the builtin alias void
if nm.id is not None: if nm.id is not None and string.strcmp(nm.id, "void") == 0: return 1
if string.strcmp(nm.id, "CVoid") == 0 or string.strcmp(nm.id, "void") == 0: return 1
return 0 return 0
def annotation_to_str(self, annot: ast.AST*) -> str: def annotation_to_str(self, annot: ast.AST*) -> str:
"""Render a type-annotation AST to a readable string; None means not extractable.""" """Render a type-annotation AST to a readable string; None means not extractable."""
@@ -1713,7 +1698,7 @@ novirtual class TypeGenResolve(TypeGenBase.TypeGenBase):
# can record it; consumers degrade gracefully for non-struct names. # can record it; consumers degrade gracefully for non-struct names.
if at.value is not None and at.value.kind() == ast.ASTKind.Name: if at.value is not None and at.value.kind() == ast.ASTKind.Name:
val_nm2: ast.Name* = ast.Name*(at.value) val_nm2: ast.Name* = ast.Name*(at.value)
if val_nm2.id is not None and string.strcmp(val_nm2.id, "t") == 0: return at.attr if val_nm2.id is not None and string.strcmp(val_nm2.id, "attr") == 0: return at.attr
# Restore import aliases: val_nm2.id may be an alias like Y for # Restore import aliases: val_nm2.id may be an alias like Y for
# "import X as Y"; storing the real qualified name enables cross-module lookup. # "import X as Y"; storing the real qualified name enables cross-module lookup.
real_mod2: str = val_nm2.id real_mod2: str = val_nm2.id
@@ -2069,11 +2054,11 @@ novirtual class TypeGenResolve(TypeGenBase.TypeGenBase):
elif string.strcmp(nm.id, "None") == 0: return mlirlite.Void(pool) elif string.strcmp(nm.id, "None") == 0: return mlirlite.Void(pool)
elif string.strcmp(nm.id, "int") == 0: return mlirlite.Int32(pool) elif string.strcmp(nm.id, "int") == 0: return mlirlite.Int32(pool)
elif string.strcmp(nm.id, "char") == 0: elif string.strcmp(nm.id, "char") == 0:
# char = i8 (consistent with CChar/char in map_t_type) # char = i8 (consistent with CChar/char in map_builtin_type)
return mlirlite.Int8(pool) return mlirlite.Int8(pool)
elif string.strcmp(nm.id, "bytes") == 0: return mlirlite.Ptr(pool, mlirlite.Int8(pool)) elif string.strcmp(nm.id, "bytes") == 0: return mlirlite.Ptr(pool, mlirlite.Int8(pool))
elif string.strcmp(nm.id, "bool") == 0: elif string.strcmp(nm.id, "bool") == 0:
# bool = t.CBool = i1 (consistent with CBool in map_t_type) # bool = t.CBool = i1 (consistent with CBool in map_builtin_type)
return mlirlite.Int1(pool) return mlirlite.Int1(pool)
# Bare float types: float = t.CFloat = f32, double = t.CDouble = f64 # Bare float types: float = t.CFloat = f32, double = t.CDouble = f64
elif string.strcmp(nm.id, "float") == 0: return mlirlite.Float(pool) elif string.strcmp(nm.id, "float") == 0: return mlirlite.Float(pool)
@@ -2133,9 +2118,9 @@ novirtual class TypeGenResolve(TypeGenBase.TypeGenBase):
self.Gen.TypeGenCType._ctypedef_pop_visiting() self.Gen.TypeGenCType._ctypedef_pop_visiting()
if inner_ty is not None: return inner_ty if inner_ty is not None: return inner_ty
else: self.Gen.TypeGenCType._ctypedef_pop_visiting() else: self.Gen.TypeGenCType._ctypedef_pop_visiting()
# Fallback: map_t_type hardcoded typedefs (VOIDPTR/HANDLE/ULONG etc.) resolve even # Fallback: map_builtin_type hardcoded typedefs (VOIDPTR/HANDLE/ULONG etc.) resolve even
# when from-import lookup fails; struct names return None and are deferred by declare_only. # when from-import lookup fails; struct names return None and are deferred by declare_only.
mapped_ty_fallback: mlirlite.LLVMType* = self.map_t_type(pool, nm.id) mapped_ty_fallback: mlirlite.LLVMType* = self.map_builtin_type(pool, nm.id)
if mapped_ty_fallback is not None: return mapped_ty_fallback if mapped_ty_fallback is not None: return mapped_ty_fallback
# [stage 1a] from-import alias reverse lookup: a bare alias XXX is in no table, so # [stage 1a] from-import alias reverse lookup: a bare alias XXX is in no table, so
# reverse it to the original name and recurse (supports typedef alias chains). # reverse it to the original name and recurse (supports typedef alias chains).
@@ -2190,38 +2175,34 @@ novirtual class TypeGenResolve(TypeGenBase.TypeGenBase):
if k == ast.ASTKind.Attribute: if k == ast.ASTKind.Attribute:
at: ast.Attribute* = ast.Attribute*(node) at: ast.Attribute* = ast.Attribute*(node)
if at.attr is None: return None if at.attr is None: return None
# Check whether value is Name('t') or Name('c')
if at.value is not None and at.value.kind() == ast.ASTKind.Name: if at.value is not None and at.value.kind() == ast.ASTKind.Name:
val_name: ast.Name* = ast.Name*(at.value) val_name: ast.Name* = ast.Name*(at.value)
if val_name.id is not None: if val_name.id is not None:
# t/c are builtin libraries (SHA1-exempt); an explicit t.XXX unambiguously # `attr.<name>` is a builtin attribute marker, never a type.
# means the builtin module and does not rely on imported_modules. if string.strcmp(val_name.id, "attr") == 0: return None
is_builtin_attr: int = 0 # SHA1 prefix ("{16hex}.Class"): the module segment is a SHA1 (already
if string.strcmp(val_name.id, "t") == 0 or string.strcmp(val_name.id, "c") == 0: is_builtin_attr = 1 # compiled), so skip the imported_modules check via GenApi.is_sha1_hex16.
if is_builtin_attr == 0: sha1_attr: int = GenApi.is_sha1_hex16(val_name.id)
# SHA1 prefix ("{16hex}.Class"): the module segment is a SHA1 (already if sha1_attr == 0:
# compiled), so skip the imported_modules check via GenApi.is_sha1_hex16. mod_imp: int = self.Gen.ImportsGen.is_module_imported(imported_modules, val_name.id)
sha1_attr: int = GenApi.is_sha1_hex16(val_name.id) if mod_imp == 0:
if sha1_attr == 0: # [builtins last match] Library-module typedef exemption: a typedef
mod_imp: int = self.Gen.ImportsGen.is_module_imported(imported_modules, val_name.id) # in a registered library module resolves without an explicit import.
if mod_imp == 0: td_known: int = 0
# [builtins last match] Library-module typedef exemption: a typedef # First restore the module name via the builtins import alias (__stdint -> stdint)
# in a registered library module resolves without an explicit import. v_real: str = self._resolve_import_alias(val_name.id)
td_known: int = 0 if v_real is None: v_real = val_name.id
# First restore the module name via the builtins import alias (__stdint -> stdint) if StubMergerStore.LookupSha1ByModName(v_real) is not None:
v_real: str = self._resolve_import_alias(val_name.id) lib_td_v: str = self._lookup_library_typedef(pool, val_name.id, at.attr)
if v_real is None: v_real = val_name.id if lib_td_v is not None: td_known = 1
if StubMergerStore.LookupSha1ByModName(v_real) is not None: if td_known == 0:
lib_td_v: str = self._lookup_library_typedef(pool, val_name.id, at.attr) # Nested class/struct: `Outer.Inner` (base is a class name); a
if lib_td_v is not None: td_known = 1 # flat-name `Outer_Inner` lookup cannot shadow any existing success.
if td_known == 0: nested_ty_q: mlirlite.LLVMType* = self._resolve_nested_class_type(pool, val_name.id, at.attr, trans)
# Nested class/struct: `Outer.Inner` (base is a class name); a if nested_ty_q is not None: return nested_ty_q
# flat-name `Outer_Inner` lookup cannot shadow any existing success. return None
nested_ty_q: mlirlite.LLVMType* = self._resolve_nested_class_type(pool, val_name.id, at.attr, trans)
if nested_ty_q is not None: return nested_ty_q
return None
# [stage 1b] Prefer the library typedef dynamic table (single source of # [stage 1b] Prefer the library typedef dynamic table (single source of
# truth); on miss fall back to map_t_type (t/c internal special cases). # truth); on miss fall back to map_builtin_type (t/c internal special cases).
if self.Gen.TypeGenCType.CTypedefResolveDepth < TypeGenMixin.MAX_CTYPEDEF_DEPTH: if self.Gen.TypeGenCType.CTypedefResolveDepth < TypeGenMixin.MAX_CTYPEDEF_DEPTH:
ctypedef_attr: str = self.Gen.TypeGenCType.lookup_ctypedef(at.attr) ctypedef_attr: str = self.Gen.TypeGenCType.lookup_ctypedef(at.attr)
if ctypedef_attr is not None and self.Gen.TypeGenCType.CTypedefFound != 0: if ctypedef_attr is not None and self.Gen.TypeGenCType.CTypedefFound != 0:
@@ -2234,7 +2215,7 @@ novirtual class TypeGenResolve(TypeGenBase.TypeGenBase):
self.Gen.TypeGenCType._ctypedef_pop_visiting() self.Gen.TypeGenCType._ctypedef_pop_visiting()
if inner_ty_attr is not None: return inner_ty_attr if inner_ty_attr is not None: return inner_ty_attr
# First try the builtin type mapping (int etc.) # First try the builtin type mapping (int etc.)
mapped_ty: mlirlite.LLVMType* = self.map_t_type(pool, at.attr) mapped_ty: mlirlite.LLVMType* = self.map_builtin_type(pool, at.attr)
if mapped_ty is not None: return mapped_ty if mapped_ty is not None: return mapped_ty
# Module-qualified class type: Module.ClassName (namespace isolation). # Module-qualified class type: Module.ClassName (namespace isolation).
# The SHA1-prefixed form should use the full name to hit the defining struct. # The SHA1-prefixed form should use the full name to hit the defining struct.
@@ -2244,14 +2225,14 @@ novirtual class TypeGenResolve(TypeGenBase.TypeGenBase):
mod_sha1_rv: str = None mod_sha1_rv: str = None
# val_name.id is the module segment: it is a SHA1 alias only if it is exactly # val_name.id is the module segment: it is a SHA1 alias only if it is exactly
# 16 lowercase hex (via GenApi.is_sha1_hex16, the single source of truth). # 16 lowercase hex (via GenApi.is_sha1_hex16, the single source of truth).
if is_builtin_attr == 0 and GenApi.is_sha1_hex16(val_name.id) != 0: if GenApi.is_sha1_hex16(val_name.id) != 0:
mod_sha1_rv = val_name.id mod_sha1_rv = val_name.id
full_q: str = self._concat_sha1_class(pool, val_name.id, at.attr) full_q: str = self._concat_sha1_class(pool, val_name.id, at.attr)
if full_q is not None: full_attr_ty = self.Gen.StructGen.get_struct_type(full_q) if full_q is not None: full_attr_ty = self.Gen.StructGen.get_struct_type(full_q)
if full_attr_ty is not None: return full_attr_ty if full_attr_ty is not None: return full_attr_ty
# Cross-module qualified name (Module.Class): resolve by module ownership, not # Cross-module qualified name (Module.Class): resolve by module ownership, not
# bare-name fallback; module name -> sha1 then hit "{sha1}.{Class}" exactly. # bare-name fallback; module name -> sha1 then hit "{sha1}.{Class}" exactly.
if is_builtin_attr == 0: if val_name.id is not None:
# Module name -> sha1 (exact/suffix/alias restoration via # Module name -> sha1 (exact/suffix/alias restoration via
# StructGenMixin._resolve_module_sha1). # StructGenMixin._resolve_module_sha1).
mod_sha1_v: str = self.Gen.StructGen._resolve_module_sha1(val_name.id, from_imports) mod_sha1_v: str = self.Gen.StructGen._resolve_module_sha1(val_name.id, from_imports)
@@ -2325,7 +2306,7 @@ novirtual class TypeGenResolve(TypeGenBase.TypeGenBase):
# Nested module-qualified enum class (three-level name): look up the enum base type # Nested module-qualified enum class (three-level name): look up the enum base type
if self.Gen.StmtGenDecl.is_enum_class(at.attr) == 1: return self.Gen.StmtGenDecl.get_enum_base_type(at.attr) if self.Gen.StmtGenDecl.is_enum_class(at.attr) == 1: return self.Gen.StmtGenDecl.get_enum_base_type(at.attr)
# Nested module-qualified typedef (win32.win32base.HANDLE = VOIDPTR): same # Nested module-qualified typedef (win32.win32base.HANDLE = VOIDPTR): same
# lookup_ctypedef + recursion + map_t_type fallback as the Name branch. # lookup_ctypedef + recursion + map_builtin_type fallback as the Name branch.
if self.Gen.TypeGenCType.CTypedefResolveDepth < TypeGenMixin.MAX_CTYPEDEF_DEPTH: if self.Gen.TypeGenCType.CTypedefResolveDepth < TypeGenMixin.MAX_CTYPEDEF_DEPTH:
nested_td_str: str = self.Gen.TypeGenCType.lookup_ctypedef(at.attr) nested_td_str: str = self.Gen.TypeGenCType.lookup_ctypedef(at.attr)
if nested_td_str is not None and self.Gen.TypeGenCType.CTypedefFound != 0: if nested_td_str is not None and self.Gen.TypeGenCType.CTypedefFound != 0:
@@ -2338,7 +2319,7 @@ novirtual class TypeGenResolve(TypeGenBase.TypeGenBase):
self.Gen.TypeGenCType._ctypedef_pop_visiting() self.Gen.TypeGenCType._ctypedef_pop_visiting()
if nested_ty is not None: return nested_ty if nested_ty is not None: return nested_ty
else: self.Gen.TypeGenCType._ctypedef_pop_visiting() else: self.Gen.TypeGenCType._ctypedef_pop_visiting()
mapped_ty_na: mlirlite.LLVMType* = self.map_t_type(pool, at.attr) mapped_ty_na: mlirlite.LLVMType* = self.map_builtin_type(pool, at.attr)
if mapped_ty_na is not None: return mapped_ty_na if mapped_ty_na is not None: return mapped_ty_na
# Nested module-qualified typedef fallback (win32.win32base.HANDLE = VOIDPTR): # Nested module-qualified typedef fallback (win32.win32base.HANDLE = VOIDPTR):
# build the full module path from the nested Attribute, then scan the library source. # build the full module path from the nested Attribute, then scan the library source.
@@ -2382,24 +2363,8 @@ novirtual class TypeGenResolve(TypeGenBase.TypeGenBase):
# (left marker -> other is right; right marker -> other is left). # (left marker -> other is right; right marker -> other is left).
other_node: ast.AST* = bop.left other_node: ast.AST* = bop.left
if left_is_cptr != 0 or left_is_ref != 0: other_node = bop.right if left_is_cptr != 0 or left_is_ref != 0: other_node = bop.right
# Detect whether the other side is t.CVoid (void* = i8*), strictly validating # Detect whether the other side is CVoid / void (void* = i8*).
# the full form (Attribute with value Name("t"), or bare CVoid with t imported). other_is_cvoid: int = self._is_cvoid_marker(other_node, imported_modules)
other_is_cvoid: int = 0
if other_node is not None:
if other_node.kind() == ast.ASTKind.Attribute:
oat2: ast.Attribute* = ast.Attribute*(other_node)
if oat2.attr is not None and string.strcmp(oat2.attr, "CVoid") == 0:
if oat2.value is not None and oat2.value.kind() == ast.ASTKind.Name:
ov2: ast.Name* = ast.Name*(oat2.value)
if ov2.id is not None and string.strcmp(ov2.id, "t") == 0: other_is_cvoid = 1
elif other_node.kind() == ast.ASTKind.Name:
onm2: ast.Name* = ast.Name*(other_node)
if onm2.id is not None:
if string.strcmp(onm2.id, "CVoid") == 0:
if self.Gen.ImportsGen.is_module_imported(imported_modules, "t") != 0: other_is_cvoid = 1
elif string.strcmp(onm2.id, "void") == 0:
# Builtin alias void: synonymous with t.CVoid, no import t needed
other_is_cvoid = 1
if other_is_cvoid != 0: return mlirlite.Ptr(pool, mlirlite.Int8(pool)) if other_is_cvoid != 0: return mlirlite.Ptr(pool, mlirlite.Int8(pool))
other_ty: mlirlite.LLVMType* = self.resolve_annotation_type(pool, other_node, imported_modules, from_imports, trans, depth + 1) other_ty: mlirlite.LLVMType* = self.resolve_annotation_type(pool, other_node, imported_modules, from_imports, trans, depth + 1)
# If the result is void, merging with CPtr gives void* = i8*; must guard None # If the result is void, merging with CPtr gives void* = i8*; must guard None
@@ -2508,10 +2473,10 @@ novirtual class TypeGenResolve(TypeGenBase.TypeGenBase):
# No CPtr marker: return the first non-attribute-marker side. Attribute markers # No CPtr marker: return the first non-attribute-marker side. Attribute markers
# (t.attr.<name>) are not types and are prepended left, so skip them. # (t.attr.<name>) are not types and are prepended left, so skip them.
if self._is_tattr_marker(bop.left) == 0: if self._is_cattr_marker(bop.left) == 0:
left_ty: mlirlite.LLVMType* = self.resolve_annotation_type(pool, bop.left, imported_modules, from_imports, trans, depth + 1) left_ty: mlirlite.LLVMType* = self.resolve_annotation_type(pool, bop.left, imported_modules, from_imports, trans, depth + 1)
if left_ty is not None: return left_ty if left_ty is not None: return left_ty
if self._is_tattr_marker(bop.right) == 0: if self._is_cattr_marker(bop.right) == 0:
right_ty: mlirlite.LLVMType* = self.resolve_annotation_type(pool, bop.right, imported_modules, from_imports, trans, depth + 1) right_ty: mlirlite.LLVMType* = self.resolve_annotation_type(pool, bop.right, imported_modules, from_imports, trans, depth + 1)
if right_ty is not None: return right_ty if right_ty is not None: return right_ty
return None return None
@@ -2575,9 +2540,9 @@ novirtual class TypeGenResolve(TypeGenBase.TypeGenBase):
count_n: int = self.Gen.TypeGenCType._eval_const_int_expr(count_node_n, pool, from_imports, imported_modules) count_n: int = self.Gen.TypeGenCType._eval_const_int_expr(count_node_n, pool, from_imports, imported_modules)
if self.Gen.TypeGenCType.ConstEvalOk == 0 or count_n <= 0: return None if self.Gen.TypeGenCType.ConstEvalOk == 0 or count_n <= 0: return None
return mlirlite.Array(pool, elem_ty_n, count_n) return mlirlite.Array(pool, elem_ty_n, count_n)
# rawptr[T] builtin bare pointer: equivalent to t.CPtr[T] -> Ptr(T), always # rawptr[T] / CPtr[T] builtin bare pointer: equivalent to `T*` -> Ptr(T),
# appending exactly 1 layer. The old "absorb a layer" compensation is removed. # always appending exactly 1 layer. The old "absorb a layer" compensation is removed.
if string.strcmp(gen_nm.id, "rawptr") == 0: if string.strcmp(gen_nm.id, "rawptr") == 0 or string.strcmp(gen_nm.id, "CPtr") == 0:
if sub.slice.kind() != ast.ASTKind.Tuple: if sub.slice.kind() != ast.ASTKind.Tuple:
rp_elem: mlirlite.LLVMType* = self.resolve_annotation_type(pool, sub.slice, imported_modules, from_imports, trans, depth + 1) rp_elem: mlirlite.LLVMType* = self.resolve_annotation_type(pool, sub.slice, imported_modules, from_imports, trans, depth + 1)
if rp_elem is None: return None if rp_elem is None: return None
@@ -2607,30 +2572,26 @@ novirtual class TypeGenResolve(TypeGenBase.TypeGenBase):
fb_ty: mlirlite.LLVMType* = self.resolve_annotation_type(pool, fb_node, imported_modules, from_imports, trans, depth + 1) fb_ty: mlirlite.LLVMType* = self.resolve_annotation_type(pool, fb_node, imported_modules, from_imports, trans, depth + 1)
if fb_ty is not None: return fb_ty if fb_ty is not None: return fb_ty
return None return None
# Check whether value is an Attribute (t.CArray / t.Callable / module-qualified generic like numpy.ndarray[T]) # Check whether value is an Attribute (module-qualified generic like numpy.ndarray[T])
if sub.value.kind() != ast.ASTKind.Attribute: self.Gen.TypeGenErr.fatal_type_error(node, "Unsupported Subscript type annotation (only t.CArray / t.Callable are supported)") if sub.value.kind() != ast.ASTKind.Attribute: self.Gen.TypeGenErr.fatal_type_error(node, "Unsupported Subscript type annotation (only CArray / Callable are supported)")
arr_at: ast.Attribute* = ast.Attribute*(sub.value) arr_at: ast.Attribute* = ast.Attribute*(sub.value)
if arr_at.attr is None: self.Gen.TypeGenErr.fatal_type_error(node, "Subscript Attribute missing attr") if arr_at.attr is None: self.Gen.TypeGenErr.fatal_type_error(node, "Subscript Attribute missing attr")
# Module-qualified generic class: numpy.ndarray[t.CDouble] -> use the bare class name # Module-qualified generic class: numpy.ndarray[CDouble] -> use the bare class name
# ndarray for specialization (before the t.CArray / t.Callable checks). # ndarray for specialization.
if trans is not None: if trans is not None:
if self.Gen.ClassGenGeneric._find_generic_template(arr_at.attr) is not None: if self.Gen.ClassGenGeneric._find_generic_template(arr_at.attr) is not None:
return self._resolve_generic_subscript_ty(pool, sub, arr_at.attr, trans, imported_modules, from_imports, depth + 1) return self._resolve_generic_subscript_ty(pool, sub, arr_at.attr, trans, imported_modules, from_imports, depth + 1)
if arr_at.value is None or arr_at.value.kind() != ast.ASTKind.Name: self.Gen.TypeGenErr.fatal_type_error(node, "t.CArray / t.Callable must be qualified by the module name") if arr_at.value is None or arr_at.value.kind() != ast.ASTKind.Name: self.Gen.TypeGenErr.fatal_type_error(node, "CArray / Callable must be qualified by the module name")
arr_val_name: ast.Name* = ast.Name*(arr_at.value) arr_val_name: ast.Name* = ast.Name*(arr_at.value)
if arr_val_name.id is None: self.Gen.TypeGenErr.fatal_type_error(node, "Subscript module name is None") if arr_val_name.id is None: self.Gen.TypeGenErr.fatal_type_error(node, "Subscript module name is None")
# Function pointer type: t.Callable[[A, B], Ret] -> i8* (function values are bitcast to # Function pointer type: Callable[[A, B], Ret] -> i8* (function values are bitcast to
# i8* for storage and the signature is rebuilt at calls, matching VPC convention). # i8* for storage and the signature is rebuilt at calls, matching VPC convention).
if string.strcmp(arr_at.attr, "Callable") == 0: if string.strcmp(arr_at.attr, "Callable") == 0:
# t/c are builtin namespaces (not compiled, not in imported_modules); no import check needed
if string.strcmp(arr_val_name.id, "t") != 0 and string.strcmp(arr_val_name.id, "c") != 0 and self.Gen.ImportsGen.is_module_imported(imported_modules, arr_val_name.id) == 0:
self.Gen.TypeGenErr.fatal_type_error(node, "t.Callable module not imported")
return mlirlite.Ptr(pool, mlirlite.Int8(pool)) return mlirlite.Ptr(pool, mlirlite.Int8(pool))
# Pointer type: t.CPtr[T] -> Ptr(T); always appends exactly 1 layer, no exceptions # Pointer type: CPtr[T] -> Ptr(T); always appends exactly 1 layer, no exceptions.
# (`t.CPtr[T]` == `T*`). The old "absorb a layer" compensation is removed.
if string.strcmp(arr_at.attr, "CPtr") == 0: if string.strcmp(arr_at.attr, "CPtr") == 0:
if string.strcmp(arr_val_name.id, "t") != 0 and string.strcmp(arr_val_name.id, "c") != 0 and self.Gen.ImportsGen.is_module_imported(imported_modules, arr_val_name.id) == 0: if self.Gen.ImportsGen.is_module_imported(imported_modules, arr_val_name.id) == 0:
self.Gen.TypeGenErr.fatal_type_error(node, "t.CPtr module not imported") self.Gen.TypeGenErr.fatal_type_error(node, "CPtr module not imported")
inner_cptr_ty: mlirlite.LLVMType* = self.resolve_annotation_type(pool, sub.slice, imported_modules, from_imports, trans, depth + 1) inner_cptr_ty: mlirlite.LLVMType* = self.resolve_annotation_type(pool, sub.slice, imported_modules, from_imports, trans, depth + 1)
if inner_cptr_ty is None: if inner_cptr_ty is None:
# Element type not ready (class not yet registered): prefer a forward declaration # Element type not ready (class not yet registered): prefer a forward declaration
@@ -2639,9 +2600,9 @@ novirtual class TypeGenResolve(TypeGenBase.TypeGenBase):
if _fwd_cptr_ty is not None: return _fwd_cptr_ty if _fwd_cptr_ty is not None: return _fwd_cptr_ty
return None return None
return mlirlite.Ptr(pool, inner_cptr_ty) return mlirlite.Ptr(pool, inner_cptr_ty)
if string.strcmp(arr_at.attr, "CArray") != 0: self.Gen.TypeGenErr.fatal_type_error(node, "Unsupported Subscript type annotation (only t.CArray / t.Callable are supported)") if string.strcmp(arr_at.attr, "CArray") != 0: self.Gen.TypeGenErr.fatal_type_error(node, "Unsupported Subscript type annotation (only CArray / Callable are supported)")
if string.strcmp(arr_val_name.id, "t") != 0 and string.strcmp(arr_val_name.id, "c") != 0 and self.Gen.ImportsGen.is_module_imported(imported_modules, arr_val_name.id) == 0: if self.Gen.ImportsGen.is_module_imported(imported_modules, arr_val_name.id) == 0:
self.Gen.TypeGenErr.fatal_type_error(node, "t.CArray module not imported") self.Gen.TypeGenErr.fatal_type_error(node, "CArray module not imported")
# Single-arg form: t.CArray[elem_ty] -> Ptr(elem_ty), equivalent to # Single-arg form: t.CArray[elem_ty] -> Ptr(elem_ty), equivalent to
# t.CArray[elem_ty, None] (statically inferred length array). # t.CArray[elem_ty, None] (statically inferred length array).
if sub.slice.kind() != ast.ASTKind.Tuple: if sub.slice.kind() != ast.ASTKind.Tuple:
+38
View File
@@ -86,6 +86,44 @@ def SpecSignFromTypeNameSpan(text: str, start: sizet, end: sizet) -> int:
if _spec_name_eq(text, start, end, "CSizeT") != 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, "CUIntPtrT") != 0: return TS_SIGN_UNSIGNED
if _spec_name_eq(text, start, end, "CBool") != 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
if _spec_name_eq(text, start, end, "uint8") != 0: return TS_SIGN_UNSIGNED
if _spec_name_eq(text, start, end, "uint16") != 0: return TS_SIGN_UNSIGNED
if _spec_name_eq(text, start, end, "uint32") != 0: return TS_SIGN_UNSIGNED
if _spec_name_eq(text, start, end, "uint64") != 0: return TS_SIGN_UNSIGNED
if _spec_name_eq(text, start, end, "uintfast8") != 0: return TS_SIGN_UNSIGNED
if _spec_name_eq(text, start, end, "uintfast16") != 0: return TS_SIGN_UNSIGNED
if _spec_name_eq(text, start, end, "uintfast32") != 0: return TS_SIGN_UNSIGNED
if _spec_name_eq(text, start, end, "uintfast64") != 0: return TS_SIGN_UNSIGNED
if _spec_name_eq(text, start, end, "uintleast8") != 0: return TS_SIGN_UNSIGNED
if _spec_name_eq(text, start, end, "uintleast16") != 0: return TS_SIGN_UNSIGNED
if _spec_name_eq(text, start, end, "uintleast32") != 0: return TS_SIGN_UNSIGNED
if _spec_name_eq(text, start, end, "uintleast64") != 0: return TS_SIGN_UNSIGNED
if _spec_name_eq(text, start, end, "uchar") != 0: return TS_SIGN_UNSIGNED
if _spec_name_eq(text, start, end, "ushort") != 0: return TS_SIGN_UNSIGNED
if _spec_name_eq(text, start, end, "uint") != 0: return TS_SIGN_UNSIGNED
if _spec_name_eq(text, start, end, "ulong") != 0: return TS_SIGN_UNSIGNED
if _spec_name_eq(text, start, end, "ulonglong") != 0: return TS_SIGN_UNSIGNED
if _spec_name_eq(text, start, end, "uintptr") != 0: return TS_SIGN_UNSIGNED
if _spec_name_eq(text, start, end, "int8") != 0: return TS_SIGN_SIGNED
if _spec_name_eq(text, start, end, "int16") != 0: return TS_SIGN_SIGNED
if _spec_name_eq(text, start, end, "int32") != 0: return TS_SIGN_SIGNED
if _spec_name_eq(text, start, end, "int64") != 0: return TS_SIGN_SIGNED
if _spec_name_eq(text, start, end, "intfast8") != 0: return TS_SIGN_SIGNED
if _spec_name_eq(text, start, end, "intfast16") != 0: return TS_SIGN_SIGNED
if _spec_name_eq(text, start, end, "intfast32") != 0: return TS_SIGN_SIGNED
if _spec_name_eq(text, start, end, "intfast64") != 0: return TS_SIGN_SIGNED
if _spec_name_eq(text, start, end, "intleast8") != 0: return TS_SIGN_SIGNED
if _spec_name_eq(text, start, end, "intleast16") != 0: return TS_SIGN_SIGNED
if _spec_name_eq(text, start, end, "intleast32") != 0: return TS_SIGN_SIGNED
if _spec_name_eq(text, start, end, "intleast64") != 0: return TS_SIGN_SIGNED
if _spec_name_eq(text, start, end, "short") != 0: return TS_SIGN_SIGNED
if _spec_name_eq(text, start, end, "long") != 0: return TS_SIGN_SIGNED
if _spec_name_eq(text, start, end, "longlong") != 0: return TS_SIGN_SIGNED
if _spec_name_eq(text, start, end, "ptrdifft") != 0: return TS_SIGN_SIGNED
if _spec_name_eq(text, start, end, "intptr") != 0: return TS_SIGN_SIGNED
# --- builtin lowercase scalars --- # --- builtin lowercase scalars ---
if _spec_name_eq(text, start, end, "bool") != 0: return TS_SIGN_UNSIGNED if _spec_name_eq(text, start, end, "bool") != 0: return TS_SIGN_UNSIGNED
# --- t/c core signed primitives --- # --- t/c core signed primitives ---