feat(obs): ownership for own params, returns and fields
`own` adds a pointer layer in the resolver; own parameters are released by the callee and callers move owned arguments (borrows are rejected); returning a local own binding transfers it; own fields are released when the containing object is deleted; reference-field assignments register a borrow so the owner cannot be del-ed while the object lives.
This commit is contained in:
1 parent
04aa27d8a0
commit
a746952997
12 files changed
+314
-196
No files matched your search
@@ -51,7 +51,7 @@ novirtual class AssignGenMixin:
|
||||
"""编译期常量表达式求值:返回值,并把**位宽**写入 bits_out[0](0=未知)。
|
||||
|
||||
位宽规则(供 define 物化用):
|
||||
- 显式强转(Call 的 callee 是整型/typedef,如 WORD / int / t.CUInt16T)→ 该位宽;
|
||||
- 显式强转(Call 的 callee 是整型/typedef,如 WORD / int / uint16)→ 该位宽;
|
||||
- Name 引用其它 define → 沿用其位宽;
|
||||
- BinOp:操作数的**声明宽度**保持最小(不提升),基准取较宽者;`+ - * <<` 的结果若装不进
|
||||
基准宽度,就**按结果值加宽**(向上取到 8/16/32/64)—— 静态计算不允许回绕;
|
||||
@@ -154,7 +154,7 @@ novirtual class AssignGenMixin:
|
||||
# Unregistered reference: fail fast
|
||||
self.Gen.TypeGenErr.fatal_error(val_node, "DefineT value extraction failed: cannot parse a compile-time constant from the expression", "TypeError")
|
||||
|
||||
# Case 4: Call - 整型/typedef 强转(如 WORD(0x0080) / t.CUnsignedLong(-11)):
|
||||
# Case 4: Call - 整型/typedef 强转(如 WORD(0x0080) / uint32(-11)):
|
||||
# 取实参值,并按**目标类型的位宽**规范化(这赋予常量"已知宽度")。
|
||||
if k == ast.ASTKind.Call:
|
||||
cl: ast.Call* = ast.Call*(val_node)
|
||||
@@ -208,7 +208,7 @@ novirtual class AssignGenMixin:
|
||||
if rhs_bits != 0: return rhs_bits
|
||||
return self.define_min_width(val)
|
||||
|
||||
# define / t.DefineT integer constant storable and width checks
|
||||
# define / DefineT integer constant storable and width checks
|
||||
# Auto-derived => 64-bit pattern; explicit type T => T's width range.
|
||||
def define_min_width(self, value: int64) -> int:
|
||||
"""Return the minimal storable width (8/16/32/64) for an integer value."""
|
||||
@@ -320,13 +320,13 @@ novirtual class AssignGenMixin:
|
||||
def _int_bits_of_type_name(self, pool: memhub.MemBuddy*, nm: str, depth: int) -> int:
|
||||
"""类型名 → 整数位宽(0=未知/非整数)。depth 防 typedef 环。
|
||||
|
||||
覆盖:内建名(int/bool/int8../uint64/sizet)、`t.CXxx`(map_builtin_type)、
|
||||
以及**用户 typedef**(如 stdint 的 `WORD: t.TypedefT = uint16` —— 经 lookup_ctypedef
|
||||
覆盖:内建名(int/bool/int8../uint64/sizet)、builtin names(map_builtin_type)、
|
||||
以及**用户 typedef**(如 stdint 的 `WORD: TypedefT = uint16` —— 经 lookup_ctypedef
|
||||
取其底层类型文本再递归定宽)。此前用户 typedef 一律返回 0,导致
|
||||
`define X: WORD = ...` 的声明宽度不参与校验/物化。
|
||||
"""
|
||||
if nm is None or depth > 10: return 0
|
||||
# 去掉模块/类限定前缀:`t.CUInt16T` → `CUInt16T`;`stdint.uint16` → `uint16`
|
||||
# 去掉模块/类限定前缀:`uint16` → `CUInt16T`;`stdint.uint16` → `uint16`
|
||||
dot: str = string.strrchr(nm, 46)
|
||||
if dot is not None and dot[1] != '\0':
|
||||
return self._int_bits_of_type_name(pool, dot + 1, depth + 1)
|
||||
@@ -346,7 +346,7 @@ novirtual class AssignGenMixin:
|
||||
return 0
|
||||
case _:
|
||||
return 0
|
||||
# 用户/include typedef(如 stdint.vp 的 `WORD: t.TypedefT = uint16`):
|
||||
# 用户/include typedef(如 stdint.vp 的 `WORD: TypedefT = uint16`):
|
||||
# 表内值可能是模块限定 ref(stdint.uint16),先用 _expand_typedef_ref 展开;
|
||||
# 本地表未命中再走 builtins 源扫描兜底。
|
||||
underlying: str = self.Gen.TypeGenCType.lookup_ctypedef(nm)
|
||||
@@ -392,10 +392,10 @@ novirtual class AssignGenMixin:
|
||||
def _float_kind_of_type_name(self, pool: memhub.MemBuddy*, nm: str, depth: int) -> int:
|
||||
"""类型名 → 1 表示它是 32 位 float(f32),0 表示 double 或非浮点/未知。depth 防 typedef 环。
|
||||
|
||||
覆盖:内建名 float、`t.CFloat` / `t.CFloat32T`(map_builtin_type)、用户/include typedef(递归展开)。
|
||||
覆盖:内建名 float、`float` / `float32`(map_builtin_type)、用户/include typedef(递归展开)。
|
||||
"""
|
||||
if nm is None or depth > 10: return 0
|
||||
# 去掉模块/类限定前缀:`t.CFloat` → `CFloat`;`stdint.float32` → `float32`
|
||||
# 去掉模块/类限定前缀:`float` → `CFloat`;`stdint.float32` → `float32`
|
||||
dot: str = string.strrchr(nm, 46)
|
||||
if dot is not None and dot[1] != '\0':
|
||||
return self._float_kind_of_type_name(pool, dot + 1, depth + 1)
|
||||
@@ -409,7 +409,7 @@ novirtual class AssignGenMixin:
|
||||
return 0
|
||||
case _:
|
||||
return 0
|
||||
# 用户/include typedef(如 `MYF: t.TypedefT = float`):展开后再递归判定
|
||||
# 用户/include typedef(如 `MYF: TypedefT = float`):展开后再递归判定
|
||||
underlying: str = self.Gen.TypeGenCType.lookup_ctypedef(nm)
|
||||
if underlying is None:
|
||||
underlying = self.Gen.TypeGenResolve._lookup_builtins_typedef(pool, nm)
|
||||
@@ -591,7 +591,7 @@ novirtual class AssignGenMixin:
|
||||
self.Gen.TypeGenErr.fatal_error(value_node, f"[VPC] define constant use-site width insufficient: constant '{rn.id}' is {dw} bits wide " f"(declared width; actual value {cdef_val}), but the use site (variable declaration annotation) provides only {bits} bits. " f"Silent truncation/downgrade is forbidden; compilation aborted. " f"Fix: widen the variable declaration annotation to at least {dw} bits (such as int{dw}T / CUInt{dw}T), " f"or narrow the constant at its define with an explicit cast.", "TypeError")
|
||||
|
||||
# eval_cdefine_float_expr - 编译期**浮点**常量表达式求值(DefineT 值为浮点表达式时用)。
|
||||
# 支持 Constant(float/int)、UnaryOp(+/-)、Call 强转包裹(float(x)/double(x)/t.CDouble(x))、
|
||||
# 支持 Constant(float/int)、UnaryOp(+/-)、Call 强转包裹(float(x)/double(x)/double(x))、
|
||||
# BinOp(+ - * /)。成功写 out[0] 并返回 1;不是纯浮点常量表达式时返回 0(交回整型路径)。
|
||||
# 与 extract_cdefine_int_value_bits 的分工:后者只服务整型 DefineT,遇到 float 字面量会 fail-fast。
|
||||
def eval_cdefine_float_expr(self, node: ast.AST*, out: double*, pool: memhub.MemBuddy*) -> int:
|
||||
@@ -618,7 +618,7 @@ novirtual class AssignGenMixin:
|
||||
return 1
|
||||
return 0
|
||||
if k == ast.ASTKind.Call:
|
||||
# 强转包裹(float(3.14) / double(x) / t.CDouble(x)):取首实参的值
|
||||
# 强转包裹(float(3.14) / double(x) / double(x)):取首实参的值
|
||||
cl: ast.Call* = ast.Call*(node)
|
||||
if cl.args is not None and len(cl.args) > 0:
|
||||
return self.eval_cdefine_float_expr(cl.args.get(0), out, pool)
|
||||
@@ -831,9 +831,9 @@ novirtual class AssignGenMixin:
|
||||
# which would hide the float cast when deciding the constant kind.
|
||||
float_src: ast.AST* = val_node
|
||||
# 浮点宽度种类(f32 与否)必须**在拆掉右侧包裹前**判定:
|
||||
# ① 注解有显式类型(`define PI: float = ...` / `PI: float | t.DefineT = ...`)→ 注解说了算
|
||||
# ① 注解有显式类型(`define PI: float = ...` / `PI: float | DefineT = ...`)→ 注解说了算
|
||||
# (`define X: double = float(1.5)` 按 double 走);
|
||||
# ② 注解没有显式类型(`PI: t.DefineT = float(3.14)`)→ 看右侧强转包裹 float(...) / double(...)。
|
||||
# ② 注解没有显式类型(`PI: DefineT = float(3.14)`)→ 看右侧强转包裹 float(...) / double(...)。
|
||||
f32_kind: int = 0
|
||||
if self._define_explicit_annot(annot) is not None:
|
||||
f32_kind = self.cdefine_decl_float_bits(pool, annot)
|
||||
@@ -974,16 +974,8 @@ novirtual class AssignGenMixin:
|
||||
if sep >= line_start + line_len: continue
|
||||
if buf[sep] != ':' and buf[sep] != '=': continue
|
||||
|
||||
# DefineT path requires t.DefineT in the line, or the define keyword form
|
||||
if require_cdefine != 0 and is_define_line == 0:
|
||||
has_cd: int = 0
|
||||
ci: sizet = line_start + name_len + 1
|
||||
while ci + 8 <= line_start + line_len:
|
||||
if (buf[ci] == 't' and buf[ci + 1] == '.' and buf[ci + 2] == 'C' and buf[ci + 3] == 'D' and buf[ci + 4] == 'e' and buf[ci + 5] == 'f' and buf[ci + 6] == 'i' and buf[ci + 7] == 'n' and buf[ci + 8] == 'e'):
|
||||
has_cd = 1
|
||||
break
|
||||
ci += 1
|
||||
if has_cd == 0: continue
|
||||
# DefineT path requires the define keyword form.
|
||||
if require_cdefine != 0 and is_define_line == 0: continue
|
||||
|
||||
# Find '='
|
||||
eq: sizet = line_start
|
||||
@@ -1023,7 +1015,7 @@ novirtual class AssignGenMixin:
|
||||
return 0
|
||||
|
||||
def _recover_cdefine_float(self, name: str, out: double*) -> int:
|
||||
"""Recover a DefineT float value (requiring a t.DefineT line)."""
|
||||
"""Recover a DefineT float value from its define line."""
|
||||
return self._recover_float_literal(name, out, 1)
|
||||
|
||||
# _annotation_contains_name - recursively check whether an annotation contains a name
|
||||
@@ -1854,9 +1846,6 @@ novirtual class AssignGenMixin:
|
||||
at64: ast.Attribute* = ast.Attribute*(node)
|
||||
ty_nm = at64.attr
|
||||
if ty_nm is None: return 0
|
||||
if string.strcmp(ty_nm, "CUInt64T") == 0: return 1
|
||||
if string.strcmp(ty_nm, "CInt64T") == 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
|
||||
@@ -1923,25 +1912,6 @@ novirtual class AssignGenMixin:
|
||||
if string.strcmp(ty_name, "float") == 0: return 1
|
||||
if string.strcmp(ty_name, "double") == 0: return 1
|
||||
if string.strcmp(ty_name, "bool") == 0: return 1
|
||||
if string.strcmp(ty_name, "CInt") == 0: return 1
|
||||
if string.strcmp(ty_name, "CUnsignedInt") == 0: return 1
|
||||
if string.strcmp(ty_name, "CUnsigned") == 0: return 1
|
||||
if string.strcmp(ty_name, "CChar") == 0: return 1
|
||||
if string.strcmp(ty_name, "CUnsignedChar") == 0: return 1
|
||||
if string.strcmp(ty_name, "CSignedChar") == 0: return 1
|
||||
if string.strcmp(ty_name, "CShort") == 0: return 1
|
||||
if string.strcmp(ty_name, "CUnsignedShort") == 0: return 1
|
||||
if string.strcmp(ty_name, "CLong") == 0: return 1
|
||||
if string.strcmp(ty_name, "CUnsignedLong") == 0: return 1
|
||||
if string.strcmp(ty_name, "CLongLong") == 0: return 1
|
||||
if string.strcmp(ty_name, "CUnsignedLongLong") == 0: return 1
|
||||
if string.strcmp(ty_name, "CSizeT") == 0: return 1
|
||||
if string.strcmp(ty_name, "CBool") == 0: return 1
|
||||
# Character width family: widths 8/16/32 are not 8-byte pointer slots
|
||||
if string.strcmp(ty_name, "CWCharT") == 0: return 1
|
||||
if string.strcmp(ty_name, "CChar8T") == 0: return 1
|
||||
if string.strcmp(ty_name, "CChar16T") == 0: return 1
|
||||
if string.strcmp(ty_name, "CChar32T") == 0: return 1
|
||||
if string.strcmp(ty_name, "BOOL") == 0: return 1
|
||||
if string.strcmp(ty_name, "BYTE") == 0: return 1
|
||||
if string.strcmp(ty_name, "UCHAR") == 0: return 1
|
||||
@@ -2241,9 +2211,9 @@ novirtual class AssignGenMixin:
|
||||
# translate_augassign - translate augmented assignment statements
|
||||
def const_assign_msg(self, kind: str, name: str, src_name: str) -> str:
|
||||
"""Build the const-assignment error message used by const_assign_fail."""
|
||||
decl: str = "(annotation contains t.ConstT; the source name at the declaration was not recorded)"
|
||||
decl: str = "(annotation contains const; the source name at the declaration was not recorded)"
|
||||
if src_name is not None: decl = f"ConstT | {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.ConstT 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 / const 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:
|
||||
"""Fail fast when a const target is written."""
|
||||
@@ -2286,12 +2256,12 @@ novirtual class AssignGenMixin:
|
||||
if target_ty is None:
|
||||
# fail-fast: field type missing, no i32 fallback
|
||||
self.Gen.TypeGenErr.fatal_error(target, "field type information missing", "TypeError")
|
||||
# t.VolatileT field: both load and store carry the volatile qualifier
|
||||
# volatile field: both load and store carry the volatile qualifier
|
||||
vol_fa: int = self.Gen.ExprGenAttr.attr_field_is_volatile(pool, trans, target)
|
||||
# BigEndian field: the memory slot holds the byte-swapped pattern, so the current value must
|
||||
# be un-swapped before it takes part in the arithmetic, and the result re-swapped on store.
|
||||
bo_fa: int = self.Gen.ExprGenAttr.attr_field_byte_order(pool, trans, target)
|
||||
# rawptr[t.VolatileT|T] base: field load/store also carry volatile
|
||||
# rawptr[volatile|T] base: field load/store also carry volatile
|
||||
at_aug: ast.Attribute* = ast.Attribute*(target)
|
||||
if at_aug is not None:
|
||||
if self.Gen.ExprGenAttr.base_pointee_is_volatile(pool, trans, at_aug.value) != 0: vol_fa = 1
|
||||
@@ -2359,7 +2329,7 @@ novirtual class AssignGenMixin:
|
||||
if target_ty is None:
|
||||
# fail-fast: element type missing, no i32 fallback
|
||||
self.Gen.TypeGenErr.fatal_error(target, "element type information missing", "TypeError")
|
||||
# rawptr[t.VolatileT|T] base: p[i] load/store carry volatile when the base is resolvable
|
||||
# rawptr[volatile|T] base: p[i] load/store carry volatile when the base is resolvable
|
||||
vol_sa: int = 0
|
||||
sub_aug: ast.Subscript* = ast.Subscript*(target)
|
||||
if sub_aug is not None: vol_sa = self.Gen.ExprGenAttr.base_pointee_is_volatile(pool, trans, sub_aug.value)
|
||||
@@ -2452,7 +2422,7 @@ novirtual class AssignGenMixin:
|
||||
if GenApi.is_global_name(trans, nm.id) != 0: scope_type = 1
|
||||
elif GenApi.is_nonlocal_name(trans, nm.id) != 0: scope_type = 2
|
||||
|
||||
# t.VolatileT variable: both load and store carry the volatile qualifier
|
||||
# volatile variable: both load and store carry the volatile qualifier
|
||||
vol_aa: int = self.Gen.VarGen.var_is_volatile(self.Gen.VarGen.lookup_var_entry(trans.SymTab, nm.id))
|
||||
# BigEndian local variable: un-swap on load / re-swap on store so the arithmetic sees the native
|
||||
# value (local scope only; global/nonlocal are not registered in the byte-order table, like TPC).
|
||||
@@ -2560,9 +2530,9 @@ novirtual class AssignGenMixin:
|
||||
|
||||
return 0
|
||||
|
||||
# is_ctypedef_annotation - check whether an annotation is t.TypedefT
|
||||
# is_ctypedef_annotation - check whether an annotation is TypedefT
|
||||
def is_ctypedef_annotation(self, annot: ast.AST*) -> int:
|
||||
"""Return 1 if the annotation is t.TypedefT, else 0."""
|
||||
"""Return 1 if the annotation is TypedefT, else 0."""
|
||||
return self.Gen.TypeGenCType._is_ctypedef_annotation(annot)
|
||||
|
||||
# _unwrap_value_type_markers - strip value-only markers around a declaration's type node.
|
||||
@@ -2769,17 +2739,22 @@ novirtual class AssignGenMixin:
|
||||
if o_ref is not None: owner_b = o_ref
|
||||
if owner_b is not None:
|
||||
self.Gen.StmtGen._BorrowAdd(trans, owner_b, nm.id)
|
||||
# A call returning `own T` transfers ownership to this binding.
|
||||
if GenApi.annotation_is_own(annot) != 0 and aa.value is not None \
|
||||
and aa.value.kind() == ast.ASTKind.Call:
|
||||
sh_c_own: mlirlite.Value* = self.Gen.VarGen._SlifeEnsureOwned(pool, builder, alloca)
|
||||
if sh_c_own is not None: self.Gen.VarGen._SlifeNoteStore(pool, builder, sh_c_own, 1)
|
||||
# Store the annotation's class name; pass pool so generic Subscript yields the mangled name
|
||||
if annot is not None:
|
||||
cls_nm_ps: str = self.Gen.TypeGenResolve.extract_class_name_from_annotation(annot, trans._imported_modules, pool, trans._from_imports)
|
||||
if cls_nm_ps is not None: self.Gen.VarGen.set_var_annot_class_name(trans.SymTab, nm.id, cls_nm_ps)
|
||||
# Check for bytes* or str* (i8** semantics, 8-byte stride)
|
||||
if self._is_ptr_element_annotation(annot) != 0: self.Gen.VarGen.set_var_ptr_element(trans.SymTab, nm.id)
|
||||
# const object (annotation has t.ConstT and the type is not a pointer): no writes in scope
|
||||
# const object (annotation has const and the type is not a pointer): no writes in scope
|
||||
if self.Gen.TypeGenErr.is_const_object_marker(annot, var_ty) != 0: self.Gen.VarGen.set_var_const(trans.SymTab, nm.id)
|
||||
# t.VolatileT: mark the variable at declaration; its load/store carry volatile
|
||||
# volatile: mark the variable at declaration; its load/store carry volatile
|
||||
if self.Gen.TypeGenErr.has_decorator_marker(annot, "VolatileT", 0) != 0: self.Gen.VarGen.set_var_volatile(trans.SymTab, nm.id)
|
||||
# rawptr[t.VolatileT | T]: dereference load/store carry volatile
|
||||
# rawptr[volatile | T]: dereference load/store carry volatile
|
||||
if self.Gen.TypeGenErr.has_pointee_volatile_marker(annot) != 0: self.Gen.VarGen.set_var_pointee_volatile(trans.SymTab, nm.id)
|
||||
return 0
|
||||
return 0
|
||||
@@ -2949,7 +2924,7 @@ novirtual class AssignGenMixin:
|
||||
if var_ty.kind() == mlirlite.LLVMType.Struct:
|
||||
self.Gen.TypeGenErr.fatal_error(aa, f"type annotation of '{nm.id}' is a class value type but the initializer is an address ('&...'): did you forget '*'? Write the pointer type ('T*') for a pointer target, or initialize with a value (e.g. 'T(...)')", "TypeError")
|
||||
|
||||
# t.Callable function pointer: register the return-type signature
|
||||
# Callable function pointer: register the return-type signature
|
||||
callable_ret: mlirlite.LLVMType* = self.Gen.TypeGenErr.try_extract_callable_ret(pool, annot, trans._imported_modules, trans._from_imports, trans)
|
||||
if callable_ret is not None:
|
||||
# Nonzero return already reported inside register_func_ptr_sig; this branch is defensive
|
||||
@@ -3016,7 +2991,7 @@ novirtual class AssignGenMixin:
|
||||
# str/bytes is not a generic pointer: bare str/bytes annotation with an aggregate-pointer RHS
|
||||
self.Gen.ExprGenBase.fail_if_str_annot_ast_agg_ptr(rhs_val, aa.annotation, node)
|
||||
rhs_val = self.Gen.ExprGenBase.coerce_to_type(builder, rhs_val, target_ty, node, trans)
|
||||
# t.VolatileT global: the initializing store carries the volatile qualifier
|
||||
# volatile global: the initializing store carries the volatile qualifier
|
||||
vol_gaa: int = self.Gen.VarGen.var_is_volatile(self.Gen.VarGen.lookup_var_entry(trans.SymTab, nm.id))
|
||||
mlirlite.build_store(builder, rhs_val, mod_alloca, vol_gaa)
|
||||
# Clear the assignment context (all four branches must stay in sync)
|
||||
@@ -3098,7 +3073,7 @@ novirtual class AssignGenMixin:
|
||||
# str/bytes is not a generic pointer: bare str/bytes annotation with an aggregate-pointer RHS
|
||||
self.Gen.ExprGenBase.fail_if_str_annot_ast_agg_ptr(rhs_val_mg, aa.annotation, node)
|
||||
rhs_val_mg = self.Gen.ExprGenBase.coerce_to_type(builder, rhs_val_mg, target_ty_mg, node, trans)
|
||||
# t.VolatileT module-level global: the initializing store carries volatile
|
||||
# volatile module-level global: the initializing store carries volatile
|
||||
vol_mga: int = self.Gen.VarGen.var_is_volatile(self.Gen.VarGen.lookup_var_entry(trans.SymTab, nm.id))
|
||||
mlirlite.build_store(builder, rhs_val_mg, mod_glob, vol_mga)
|
||||
# Clear the assignment context (all four branches must stay in sync)
|
||||
@@ -3158,9 +3133,9 @@ novirtual class AssignGenMixin:
|
||||
if cls_nm_hd is not None: self.Gen.VarGen.set_var_annot_class_name(trans.SymTab, nm.id, cls_nm_hd)
|
||||
# Check for bytes* or str* (i8** semantics, 8-byte stride)
|
||||
if self._is_ptr_element_annotation(annot) != 0: self.Gen.VarGen.set_var_ptr_element(trans.SymTab, nm.id)
|
||||
# Byte-order marker (`T | t.BigEndian` / `T | t.LittleEndian`): register the variable's byte
|
||||
# Byte-order marker (`T | bigend` / `T | littlend`): register the variable's byte
|
||||
# order so the local load/store paths bswap (mirrors ClassGenDef's field-level detection).
|
||||
# A bare `t.BigEndian` annotation resolves to i32 upstream, so registration is unconditional.
|
||||
# A bare `bigend` annotation resolves to i32 upstream, so registration is unconditional.
|
||||
if self.Gen.TypeGenErr.has_decorator_marker(annot, "BigEndian", 0) != 0: self.Gen.VarGen.set_var_byte_order(trans.SymTab, nm.id, 1)
|
||||
elif self.Gen.TypeGenErr.has_decorator_marker(annot, "LittleEndian", 0) != 0: self.Gen.VarGen.set_var_byte_order(trans.SymTab, nm.id, 2)
|
||||
|
||||
@@ -3177,7 +3152,7 @@ novirtual class AssignGenMixin:
|
||||
val_nm: ast.Name* = ast.Name*(val_cl.func)
|
||||
if val_nm.id is not None:
|
||||
if self.Gen.AsyncGen.is_async_func(trans, val_nm.id) != 0: self.Gen.AsyncGen.register_coro_var(trans, nm.id, val_nm.id)
|
||||
# c.Addr(async_call): interpret the tt: Coroutine = c.Addr(task()) form in place
|
||||
# &async_call: interpret the tt: Coroutine = &task() form in place
|
||||
elif val_cl.func is not None and val_cl.func.kind() == ast.ASTKind.Attribute:
|
||||
val_at: ast.Attribute* = ast.Attribute*(val_cl.func)
|
||||
if val_at.attr is not None and string.strcmp(val_at.attr, "Addr") == 0 and val_cl.args is not None and len(val_cl.args) >= 1:
|
||||
@@ -3319,7 +3294,7 @@ novirtual class AssignGenMixin:
|
||||
# str/bytes is not a generic pointer: bare str/bytes annotation with an aggregate-pointer RHS
|
||||
self.Gen.ExprGenBase.fail_if_str_annot_ast_agg_ptr(rhs_val, aa.annotation, node)
|
||||
rhs_val = self.Gen.ExprGenBase.coerce_to_type(builder, rhs_val, target_ty, node, trans)
|
||||
# t.VolatileT local variable: mark the VarEntry and store with volatile
|
||||
# volatile local variable: mark the VarEntry and store with volatile
|
||||
vol_laa: int = 0
|
||||
if aa.annotation is not None and nm.id is not None:
|
||||
# const object local variable: mark VarEntry.IsConst (idempotent with prescan)
|
||||
@@ -3327,7 +3302,7 @@ novirtual class AssignGenMixin:
|
||||
if self.Gen.TypeGenErr.has_decorator_marker(aa.annotation, "VolatileT", 0) != 0:
|
||||
self.Gen.VarGen.set_var_volatile(trans.SymTab, nm.id)
|
||||
vol_laa = 1
|
||||
# rawptr[t.VolatileT | T]: mark PointeeVolatile (this store writes the pointer itself)
|
||||
# rawptr[volatile | T]: mark PointeeVolatile (this store writes the pointer itself)
|
||||
if self.Gen.TypeGenErr.has_pointee_volatile_marker(aa.annotation) != 0: self.Gen.VarGen.set_var_pointee_volatile(trans.SymTab, nm.id)
|
||||
# `T?`: remember the declaration so later plain `a = None` is accepted
|
||||
self.Gen.VarGen.set_var_nullable(trans.SymTab, nm.id, GenApi.annotation_is_nullable(aa.annotation))
|
||||
@@ -3454,7 +3429,7 @@ novirtual class AssignGenMixin:
|
||||
sub_nm_lv: ast.Name* = ast.Name*(sub_lv_b.value)
|
||||
if sub_nm_lv.id is not None: bo_lv = self.Gen.VarGen.var_byte_order(self.Gen.VarGen.lookup_var_entry(trans.SymTab, sub_nm_lv.id))
|
||||
store_val = self.Gen.ExprGenBase.apply_byte_order_bswap(pool, builder, store_val, bo_lv)
|
||||
# t.VolatileT 字段 / rawptr[t.VolatileT|T] 基址:写出同样带 volatile。
|
||||
# volatile 字段 / rawptr[volatile|T] 基址:写出同样带 volatile。
|
||||
vol_lv: int = 0
|
||||
if is_attr != 0:
|
||||
vol_lv = self.Gen.ExprGenAttr.attr_field_is_volatile(pool, trans, target)
|
||||
@@ -3871,7 +3846,7 @@ novirtual class AssignGenMixin:
|
||||
sub_nm_sw: ast.Name* = ast.Name*(sub_wr.value)
|
||||
if sub_nm_sw.id is not None: bo_sw = self.Gen.VarGen.var_byte_order(self.Gen.VarGen.lookup_var_entry(trans.SymTab, sub_nm_sw.id))
|
||||
store_val = self.Gen.ExprGenBase.apply_byte_order_bswap(pool, builder, store_val, bo_sw)
|
||||
# rawptr[t.VolatileT|T] base: p[i] = v stores with volatile when the base is resolvable
|
||||
# rawptr[volatile|T] base: p[i] = v stores with volatile when the base is resolvable
|
||||
vol_sw: int = 0
|
||||
if sub_wr is not None: vol_sw = self.Gen.ExprGenAttr.base_pointee_is_volatile(pool, trans, sub_wr.value)
|
||||
mlirlite.build_store(builder, store_val, elem_ptr, vol_sw)
|
||||
@@ -3975,7 +3950,7 @@ novirtual class AssignGenMixin:
|
||||
pointee_w: mlirlite.LLVMType* = ptr_w.Ty.Pointee
|
||||
if pointee_w is None:
|
||||
self.Gen.TypeGenErr.fatal_error(target, "deref write target type missing (pointee is empty)", "TypeError")
|
||||
# `*a = *b` 是 c.Load 语义的**位拷贝**(type-pun):把源内存按**源自身类型**读出来,
|
||||
# `*a = *b` 是 `*` 语义的**位拷贝**(type-pun):把源内存按**源自身类型**读出来,
|
||||
# 再按位重解释(bitcast)成目的类型存回去 —— **绝不做数值转换**。缺了它
|
||||
# `*&u = *&xv`(double → uint64)会退化成 fptosi(现场 `cvttsd2si %xmm0, %rax`)
|
||||
# ⇒ IEEE 位型丢失(isnan/isinf、sin/cos 的 ef/neg 全错)。
|
||||
@@ -4050,9 +4025,9 @@ novirtual class AssignGenMixin:
|
||||
# (pairs the un-swap done by ExprGenAttr.translate_attribute on the load path).
|
||||
bo_fw: int = self.Gen.ExprGenAttr.attr_field_byte_order(pool, trans, target)
|
||||
store_val = self.Gen.ExprGenBase.apply_byte_order_bswap(pool, builder, store_val, bo_fw)
|
||||
# t.VolatileT field: store with the volatile qualifier
|
||||
# volatile field: store with the volatile qualifier
|
||||
vol_fw: int = self.Gen.ExprGenAttr.attr_field_is_volatile(pool, trans, target)
|
||||
# rawptr[t.VolatileT|T] base: p.field = v also stores with volatile
|
||||
# rawptr[volatile|T] base: p.field = v also stores with volatile
|
||||
at_wr: ast.Attribute* = ast.Attribute*(target)
|
||||
if at_wr is not None:
|
||||
if self.Gen.ExprGenAttr.base_pointee_is_volatile(pool, trans, at_wr.value) != 0: vol_fw = 1
|
||||
@@ -4070,6 +4045,15 @@ novirtual class AssignGenMixin:
|
||||
if nm_rf.id is not None and self.Gen.VarGen.var_ref_kind(trans.SymTab, nm_rf.id) != 0: ref_ok = 1
|
||||
if ref_ok == 0:
|
||||
self.Gen.TypeGenErr.fatal_error(asgn.value, f"field '.{at_tg.attr}' is a reference: assign a borrow (`&value`), not an owner", "TypeError")
|
||||
else:
|
||||
# Reference field: remember the borrow so the owner cannot be del-ed
|
||||
# while the owning object is in scope.
|
||||
own_bf: str = self._BorrowOwnerName(asgn.value)
|
||||
brw_bf: str = None
|
||||
if at_tg.value is not None and at_tg.value.kind() == ast.ASTKind.Name:
|
||||
brw_bf = (ast.Name*)(at_tg.value).id
|
||||
if own_bf is not None and brw_bf is not None:
|
||||
self.Gen.StmtGen._BorrowAdd(trans, own_bf, brw_bf)
|
||||
mlirlite.build_store(builder, store_val, field_ptr, vol_fw)
|
||||
self._move_out_assigned_source(trans, asgn.value, pool, builder)
|
||||
continue
|
||||
@@ -4125,7 +4109,7 @@ novirtual class AssignGenMixin:
|
||||
target_ty: mlirlite.LLVMType* = None
|
||||
if mod_alloca.Ty is not None: target_ty = mod_alloca.Ty.Pointee
|
||||
if target_ty is not None: rhs_val = self.Gen.ExprGenBase.coerce_to_type(builder, rhs_val, target_ty, node, trans)
|
||||
# t.VolatileT global: store with the volatile qualifier
|
||||
# volatile global: store with the volatile qualifier
|
||||
vol_gas: int = 0
|
||||
if nm.id is not None: vol_gas = self.Gen.VarGen.var_is_volatile(self.Gen.VarGen.lookup_var_entry(trans.SymTab, nm.id))
|
||||
mlirlite.build_store(builder, rhs_val, mod_alloca, vol_gas)
|
||||
@@ -4178,7 +4162,7 @@ novirtual class AssignGenMixin:
|
||||
# memory (pairs the un-swap done by ExprGenName.translate_name_value on the read path).
|
||||
bo_las: int = self.Gen.VarGen.var_byte_order(self.Gen.VarGen.lookup_var_entry(trans.SymTab, nm.id))
|
||||
store_val = self.Gen.ExprGenBase.apply_byte_order_bswap(pool, builder, store_val, bo_las)
|
||||
# t.VolatileT local variable: store with the volatile qualifier
|
||||
# volatile local variable: store with the volatile qualifier
|
||||
vol_las: int = self.Gen.VarGen.var_is_volatile(self.Gen.VarGen.lookup_var_entry(trans.SymTab, nm.id))
|
||||
# M3 所有权记账:必须先记账再写值(覆盖释放要读到旧值);
|
||||
# `p = new heap slife …` → 释放旧对象 + 影子置 1;`q = p` / 外来指针 → 交还所有权
|
||||
|
||||
@@ -106,6 +106,10 @@ novirtual class CallGenBase:
|
||||
# self included for methods. Registered when a callee definition is translated.
|
||||
NullableParams: hashtable.HashTable* = None
|
||||
|
||||
# --- owned parameter flags (explicit `own T` params / OBS move-in): key = callee symbol name
|
||||
# -> byte-per-parameter '0'/'1' (1 = the callee takes ownership and releases it).
|
||||
OwnParams: hashtable.HashTable* = None
|
||||
|
||||
def InitCallGen(self) -> int:
|
||||
"""Initialize the call mixin fields (inlined by subclass constructors since NoVTable does not forward inherited symbols)"""
|
||||
if self is None: return 1
|
||||
@@ -145,6 +149,42 @@ novirtual class CallGenBase:
|
||||
if code == 50: return 2 # '2' = temp
|
||||
return 0
|
||||
|
||||
# _register_own_params - store per-parameter `own` flags for a callee symbol name.
|
||||
def _register_own_params(self, name: char*, flags: char*, count: int) -> int:
|
||||
"""Store per-parameter '0'/'1' owned flags (PermPool-persistent) keyed by callee symbol name."""
|
||||
if name is None or flags is None or count <= 0: return 0
|
||||
perm: memhub.MemBuddy* = CompilerFactory.factory.PermPool
|
||||
if perm is None: return 0
|
||||
if self.OwnParams is None:
|
||||
self.OwnParams = hashtable.HashTable(perm)
|
||||
if self.OwnParams is None: return 0
|
||||
key: char* = perm.alloc(string.strlen(name) + 1)
|
||||
if key is None: return 0
|
||||
string.strcpy(key, name)
|
||||
val: char* = perm.alloc(sizet(count) + 1)
|
||||
if val is None: return 0
|
||||
string.memcpy(val, flags, sizet(count))
|
||||
val[count] = char(0)
|
||||
self.OwnParams[key] = val
|
||||
return 1
|
||||
|
||||
# _param_own_known - 1 when an `own` mask was registered for the callee symbol.
|
||||
def _param_own_known(self, name: char*) -> int:
|
||||
if self.OwnParams is None or name is None: return 0
|
||||
v: rawptr = self.OwnParams.__getitem__(name)
|
||||
return 0 if v is None else 1
|
||||
|
||||
# _param_is_own - 1 when parameter idx of the callee symbol takes ownership.
|
||||
def _param_is_own(self, name: char*, idx: int) -> int:
|
||||
if self.OwnParams is None or name is None or idx < 0: return 0
|
||||
v: rawptr = self.OwnParams.__getitem__(name)
|
||||
if v is None: return 0
|
||||
flags: char* = rawptr(v)
|
||||
i: int = 0
|
||||
while i < idx and flags[i] != char(0): i += 1
|
||||
if i != idx: return 0
|
||||
return 1 if int(flags[idx]) == 49 else 0
|
||||
|
||||
# _register_nullable_params - store per-parameter nullability flags for a callee symbol name.
|
||||
def _register_nullable_params(self, name: char*, flags: char*, count: int) -> int:
|
||||
"""Store per-parameter '0'/'1' nullable flags (PermPool-persistent) keyed by callee symbol name."""
|
||||
|
||||
@@ -179,6 +179,29 @@ novirtual class CallGenLib(CallGenBase.CallGenBase):
|
||||
# ensure-load + direct cache lookup (not _vpi_method_param_annot, which misses module funcs)
|
||||
if self.Gen.CallGenBase.TempDir is not None and self.Gen.CallGenBase.TempDirLen > 0: VpiReader._ensure_vpi_param_annot_loaded(sha1_buf, self.Gen.CallGenBase.TempDir)
|
||||
prm_annots: char* = VpiReader._lookup_vpi_param_annots(sha1_buf, cls_nm, meth_nm)
|
||||
# Ownership: an `own` parameter takes the argument's ownership. A local `own` binding is
|
||||
# moved in; `new` / calls are already owned; a borrowed value is rejected.
|
||||
if trans is not None and cargs is not None and self.Gen.CallGenBase._param_own_known(call_name) != 0:
|
||||
arg_n_o: int = int(len(cargs))
|
||||
ai_o: int = 0
|
||||
while ai_o < arg_n_o:
|
||||
an_o: ast.AST* = cargs.get(sizet(ai_o))
|
||||
ai_o += 1
|
||||
if an_o is None: continue
|
||||
pidx_o: int = ai_o - 1 + self_off
|
||||
if self.Gen.CallGenBase._param_is_own(call_name, pidx_o) == 0: continue
|
||||
if an_o.kind() == ast.ASTKind.Name:
|
||||
nm_o: ast.Name* = ast.Name*(an_o)
|
||||
if nm_o.id is None: continue
|
||||
src_o: mlirlite.Value* = self.Gen.VarGen.lookup_var(trans.SymTab, nm_o.id)
|
||||
if src_o is not None and self.Gen.VarGen._SlifeOwned(src_o) != 0:
|
||||
sh_o: mlirlite.Value* = self.Gen.VarGen._SlifeShadow(src_o)
|
||||
if sh_o is not None: self.Gen.VarGen._SlifeNoteStore(pool, builder, sh_o, 0)
|
||||
self.Gen.VarGen._FreedMark(src_o)
|
||||
continue
|
||||
self.Gen.TypeGenErr.fatal_error(an_o, f"cannot pass '{nm_o.id}' to an `own` parameter: it is not owned here; move an `own` binding or pass `new ...`", "LifetimeError")
|
||||
elif an_o.kind() == ast.ASTKind.UnaryOp:
|
||||
self.Gen.TypeGenErr.fatal_error(an_o, "cannot pass a borrow to an `own` parameter: the callee takes ownership", "LifetimeError")
|
||||
# Strict OBS: a nullable binding/field may be None and must not flow into a non-nullable
|
||||
# parameter. Same-module callees use the nullable registry, cross-module ones use their
|
||||
# vpi param annotations; an unknown callee signature is left alone.
|
||||
|
||||
@@ -8,6 +8,7 @@ import stdio
|
||||
import viperlib
|
||||
import lib.core.VLogger as VLogger
|
||||
import lib.core.Gen.Translator as HT
|
||||
import lib.core.Gen.GenApi as GenApi
|
||||
import lib.core.CompilerFactory as CompilerFactory
|
||||
|
||||
from . import ClassGenBase
|
||||
@@ -152,27 +153,29 @@ novirtual class ClassGenDef(ClassGenBase.ClassGenBase):
|
||||
field_types_buf: sizet* = pool.alloc(8 * GenTypes.FIELD_MAX)
|
||||
field_defaults_buf: sizet* = pool.alloc(8 * GenTypes.FIELD_MAX)
|
||||
field_annot_buf: sizet* = pool.alloc(8 * GenTypes.FIELD_MAX)
|
||||
# Field volatile flag buffer (1 = annotation union contains t.VolatileT), parallel
|
||||
# Field volatile flag buffer (1 = annotation union contains volatile), parallel
|
||||
# to field_annot_buf with values 0/1.
|
||||
field_volatile_buf: sizet* = pool.alloc(8 * GenTypes.FIELD_MAX)
|
||||
# Field pointee-volatile flag buffer (1 = rawptr[t.VolatileT | T], i.e. volatile T*),
|
||||
# Field pointee-volatile flag buffer (1 = rawptr[volatile | T], i.e. volatile T*),
|
||||
# parallel to field_volatile_buf with values 0/1.
|
||||
field_pointee_volatile_buf: sizet* = pool.alloc(8 * GenTypes.FIELD_MAX)
|
||||
# Field const-object flag buffer (1 = annotation has t.ConstT and the type is not
|
||||
# Field const-object flag buffer (1 = annotation has const and the type is not
|
||||
# a pointer), parallel to field_volatile_buf with values 0/1.
|
||||
field_const_buf: sizet* = pool.alloc(8 * GenTypes.FIELD_MAX)
|
||||
# Field byte-order buffer (0 = native, 1 = t.BigEndian, 2 = t.LittleEndian), parallel
|
||||
# to field_volatile_buf; from the `X | t.BigEndian` annotation union.
|
||||
# Field byte-order buffer (0 = native, 1 = bigend, 2 = littlend), parallel
|
||||
# to field_volatile_buf; from the `X | bigend` annotation union.
|
||||
field_byteorder_buf: sizet* = pool.alloc(8 * GenTypes.FIELD_MAX)
|
||||
# Field nullable flag buffer (1 = `own T?` / `T?`), parallel to field_volatile_buf.
|
||||
field_nullable_buf: sizet* = pool.alloc(8 * GenTypes.FIELD_MAX)
|
||||
# Field reference kind buffer (0=none, 1=`&T`, 2=`&mut T` in __obs__ files).
|
||||
field_refkind_buf: sizet* = pool.alloc(8 * GenTypes.FIELD_MAX)
|
||||
# Field `own T` flag buffer (1 = the containing object owns the pointee).
|
||||
field_own_buf: sizet* = pool.alloc(8 * GenTypes.FIELD_MAX)
|
||||
if field_names_buf is None or field_types_buf is None or field_defaults_buf is None: return 0
|
||||
if field_annot_buf is None or field_volatile_buf is None: return 0
|
||||
if field_pointee_volatile_buf is None or field_const_buf is None: return 0
|
||||
if field_byteorder_buf is None or field_nullable_buf is None: return 0
|
||||
if field_refkind_buf is None: return 0
|
||||
if field_refkind_buf is None or field_own_buf is None: return 0
|
||||
string.memset(field_names_buf, 0, 8 * GenTypes.FIELD_MAX)
|
||||
string.memset(field_types_buf, 0, 8 * GenTypes.FIELD_MAX)
|
||||
string.memset(field_defaults_buf, 0, 8 * GenTypes.FIELD_MAX)
|
||||
@@ -183,6 +186,7 @@ novirtual class ClassGenDef(ClassGenBase.ClassGenBase):
|
||||
string.memset(field_byteorder_buf, 0, 8 * GenTypes.FIELD_MAX)
|
||||
string.memset(field_nullable_buf, 0, 8 * GenTypes.FIELD_MAX)
|
||||
string.memset(field_refkind_buf, 0, 8 * GenTypes.FIELD_MAX)
|
||||
string.memset(field_own_buf, 0, 8 * GenTypes.FIELD_MAX)
|
||||
|
||||
field_count: int = 0
|
||||
|
||||
@@ -237,6 +241,7 @@ novirtual class ClassGenDef(ClassGenBase.ClassGenBase):
|
||||
# Inherit the nullable flag (unchanged after flattening)
|
||||
field_nullable_buf[field_count] = sizet(pfe.Nullable)
|
||||
field_refkind_buf[field_count] = sizet(pfe.RefKind)
|
||||
field_own_buf[field_count] = sizet(pfe.IsOwn)
|
||||
field_count += 1
|
||||
|
||||
children: list[ast.AST*]* = cd.children
|
||||
@@ -344,26 +349,27 @@ novirtual class ClassGenDef(ClassGenBase.ClassGenBase):
|
||||
diag_cn_val: sizet = sizet(annot_cn)
|
||||
field_annot_buf[field_count] = diag_cn_val
|
||||
else: field_annot_buf[field_count] = 0
|
||||
# t.VolatileT storage-class flag: loads/stores through this field are
|
||||
# volatile (e.g. Spinlock.locked: t.VolatileT | t.CInt).
|
||||
# volatile storage-class flag: loads/stores through this field are
|
||||
# volatile (e.g. Spinlock.locked: volatile | int).
|
||||
if self.Gen.TypeGenErr.has_decorator_marker(aa.annotation, "VolatileT", 0) != 0: field_volatile_buf[field_count] = 1
|
||||
else: field_volatile_buf[field_count] = 0
|
||||
# rawptr[t.VolatileT | T] (volatile pointee, i.e. C volatile T*): derefs
|
||||
# rawptr[volatile | T] (volatile pointee, i.e. C volatile T*): derefs
|
||||
# through this field (self.ptr[i]/self.ptr.field) are volatile.
|
||||
if self.Gen.TypeGenErr.has_pointee_volatile_marker(aa.annotation) != 0: field_pointee_volatile_buf[field_count] = 1
|
||||
else: field_pointee_volatile_buf[field_count] = 0
|
||||
# const object field (annotation has t.ConstT, non-pointer type): assigning
|
||||
# const object field (annotation has const, non-pointer type): assigning
|
||||
# through the object fail-fasts, matching TPC's Gen.field_const_flags.
|
||||
if self.Gen.TypeGenErr.is_const_object_marker(aa.annotation, field_ty) != 0: field_const_buf[field_count] = 1
|
||||
else: field_const_buf[field_count] = 0
|
||||
# Byte-order marker (t.BigEndian / t.LittleEndian) -> 1 / 2; annotation `|`
|
||||
# means "and", so `X | t.BigEndian` is a BigEndian-encoded X field.
|
||||
# Byte-order marker (bigend / littlend) -> 1 / 2; annotation `|`
|
||||
# means "and", so `X | bigend` is a BigEndian-encoded X field.
|
||||
if self.Gen.TypeGenErr.has_decorator_marker(aa.annotation, "BigEndian", 0) != 0: field_byteorder_buf[field_count] = 1
|
||||
elif self.Gen.TypeGenErr.has_decorator_marker(aa.annotation, "LittleEndian", 0) != 0: field_byteorder_buf[field_count] = 2
|
||||
else: field_byteorder_buf[field_count] = 0
|
||||
# `own T?` / `T?`: direct `.` access is rejected; use `?.` or a None check.
|
||||
field_nullable_buf[field_count] = sizet(self._ann_nullable(aa.annotation))
|
||||
field_refkind_buf[field_count] = sizet(self.Gen.TypeGenResolve.ref_kind(aa.annotation))
|
||||
field_own_buf[field_count] = sizet(GenApi.annotation_is_own(aa.annotation))
|
||||
else:
|
||||
field_annot_buf[field_count] = 0
|
||||
field_volatile_buf[field_count] = 0
|
||||
@@ -372,6 +378,7 @@ novirtual class ClassGenDef(ClassGenBase.ClassGenBase):
|
||||
field_byteorder_buf[field_count] = 0
|
||||
field_nullable_buf[field_count] = 0
|
||||
field_refkind_buf[field_count] = 0
|
||||
field_own_buf[field_count] = 0
|
||||
field_count += 1
|
||||
|
||||
if field_count == 0:
|
||||
@@ -536,11 +543,11 @@ novirtual class ClassGenDef(ClassGenBase.ClassGenBase):
|
||||
if fannot_addr != 0: fannot = str(fannot_addr)
|
||||
if fname is not None and fty2 is not None:
|
||||
fidx_add: int = self.Gen.StructGen.add_field(pool, entry, fname, fty2, fdef, fannot)
|
||||
# t.VolatileT field flag (field_volatile_buf[fi] corresponds 1:1 with add_field order)
|
||||
# volatile field flag (field_volatile_buf[fi] corresponds 1:1 with add_field order)
|
||||
if fidx_add >= 0 and field_volatile_buf[fi] != 0:
|
||||
fe_vol: GenTypes.FieldEntry* = self.Gen.StructGen._get_field_entry(entry, fidx_add)
|
||||
if fe_vol is not None: fe_vol.IsVolatile = 1
|
||||
# rawptr[t.VolatileT | T] field flag (field_pointee_volatile_buf[fi] corresponds
|
||||
# rawptr[volatile | T] field flag (field_pointee_volatile_buf[fi] corresponds
|
||||
# 1:1 with add_field): derefs through it load/store with volatile.
|
||||
if fidx_add >= 0 and field_pointee_volatile_buf[fi] != 0:
|
||||
fe_pv: GenTypes.FieldEntry* = self.Gen.StructGen._get_field_entry(entry, fidx_add)
|
||||
@@ -561,6 +568,9 @@ novirtual class ClassGenDef(ClassGenBase.ClassGenBase):
|
||||
if fidx_add >= 0 and field_refkind_buf[fi] != 0:
|
||||
fe_rk: GenTypes.FieldEntry* = self.Gen.StructGen._get_field_entry(entry, fidx_add)
|
||||
if fe_rk is not None: fe_rk.RefKind = int(field_refkind_buf[fi])
|
||||
if fidx_add >= 0 and field_own_buf[fi] != 0:
|
||||
fe_ow: GenTypes.FieldEntry* = self.Gen.StructGen._get_field_entry(entry, fidx_add)
|
||||
if fe_ow is not None: fe_ow.IsOwn = 1
|
||||
|
||||
# Phase 1a declaration mode: only register the struct and OOP flags, no method bodies
|
||||
if trans._declare_only == 1:
|
||||
|
||||
@@ -523,7 +523,7 @@ novirtual class ClassGenGeneric(ClassGenBase.ClassGenBase):
|
||||
if pa is None or pa.kind() != ast.ASTKind.Arg: continue
|
||||
arg: ast.Arg* = ast.Arg*(pa)
|
||||
if arg.arg is None: continue
|
||||
# One shared parameter mapping (CVoid skipped, class -> Ptr, array -> element pointer).
|
||||
# One shared parameter mapping (void skipped, class -> Ptr, array -> element pointer).
|
||||
param_ty: mlirlite.LLVMType* = self.Gen.FuncGenBase.param_llvm_ty(trans, arg, gg_ctx, 0)
|
||||
if param_ty is None: continue
|
||||
pname: str = pool.alloc(32)
|
||||
@@ -563,6 +563,20 @@ novirtual class ClassGenGeneric(ClassGenBase.ClassGenBase):
|
||||
nc_g += 1
|
||||
nul_buf_g_[nc_g] = char(0)
|
||||
self.Gen.CallGenBase._register_nullable_params(spec_name, nul_buf_g_, nc_g)
|
||||
own_buf_g_: str = pool.alloc(an + 1)
|
||||
if own_buf_g_ is not None:
|
||||
oc_g: int = 0
|
||||
for ai_og in range(int(an)):
|
||||
pa_og: ast.AST* = alist.get(ai_og)
|
||||
f_og: char = char(48)
|
||||
if pa_og is not None and pa_og.kind() == ast.ASTKind.Arg:
|
||||
arg_og: ast.Arg* = ast.Arg*(pa_og)
|
||||
if arg_og.annotation is not None and GenApi.annotation_is_own(arg_og.annotation) != 0:
|
||||
f_og = char(49)
|
||||
own_buf_g_[oc_g] = f_og
|
||||
oc_g += 1
|
||||
own_buf_g_[oc_g] = char(0)
|
||||
self.Gen.CallGenBase._register_own_params(spec_name, own_buf_g_, oc_g)
|
||||
if tpl_file is not None: self.Gen.TypeGenResolve.set_current_file(saved_cur)
|
||||
trans.ModuleSha1 = saved_ms
|
||||
trans._imported_modules = saved_imp
|
||||
|
||||
@@ -715,7 +715,7 @@ novirtual class ClassGenVTable(ClassGenBase.ClassGenBase):
|
||||
arg_m: ast.Arg* = ast.Arg*(alist.get(ai_m))
|
||||
mm: int = 0
|
||||
if arg_m is not None and arg_m.annotation is not None:
|
||||
if self.Gen.TypeGenResolve._is_builtin_marker(arg_m.annotation, "CVoid", imported_modules) == 0:
|
||||
if self.Gen.TypeGenResolve._is_bare_void(arg_m.annotation) == 0:
|
||||
mm = self.Gen.TypeGenResolve.ref_param_mode(arg_m.annotation, imported_modules)
|
||||
if mm == 1: mode_buf[mode_cnt] = char(49)
|
||||
elif mm == 2: mode_buf[mode_cnt] = char(50)
|
||||
@@ -741,6 +741,23 @@ novirtual class ClassGenVTable(ClassGenBase.ClassGenBase):
|
||||
nc_m += 1
|
||||
nul_buf_m[nc_m] = char(0)
|
||||
self.Gen.CallGenBase._register_nullable_params(mangled_name, nul_buf_m, nc_m)
|
||||
# Per-parameter ownership (`own T` params), same indexing.
|
||||
own_buf_m: str = pool.alloc(an + 1)
|
||||
if own_buf_m is not None:
|
||||
oc_m: int = 0
|
||||
if is_static_m == 0:
|
||||
own_buf_m[oc_m] = char(48)
|
||||
oc_m += 1
|
||||
for ai_o in range(int(start_idx), int(an)):
|
||||
arg_o: ast.Arg* = ast.Arg*(alist.get(ai_o))
|
||||
f_o: char = char(48)
|
||||
if arg_o is not None and arg_o.annotation is not None \
|
||||
and GenApi.annotation_is_own(arg_o.annotation) != 0:
|
||||
f_o = char(49)
|
||||
own_buf_m[oc_m] = f_o
|
||||
oc_m += 1
|
||||
own_buf_m[oc_m] = char(0)
|
||||
self.Gen.CallGenBase._register_own_params(mangled_name, own_buf_m, oc_m)
|
||||
|
||||
# *args variadic: set IsVarArg=1 so the signature carries the ... suffix (the body
|
||||
# pipeline allocates the va_list buffer, same as a module-level function).
|
||||
|
||||
@@ -38,7 +38,7 @@ novirtual class FuncGenBase(CallGenBase.CallGenBase):
|
||||
if deco is None: continue
|
||||
dk: int = deco.kind()
|
||||
# Direct form `@t.attr.<name>` (same as ClassGenBase._has_decorator): `inline def f`
|
||||
# synthesizes it and does NOT require `import c`; unsupported names are ignored.
|
||||
# synthesizes it and does NOT require an import; unsupported names are ignored.
|
||||
if dk == ast.ASTKind.Attribute:
|
||||
dan: str = self._get_attr_name_from_node(deco)
|
||||
if dan is not None and string.strcmp(dan, "section") != 0:
|
||||
@@ -230,26 +230,6 @@ novirtual class FuncGenBase(CallGenBase.CallGenBase):
|
||||
if ecn_f.id is not None and string.strcmp(ecn_f.id, "StopIteration") == 0: is_stop_f = 1
|
||||
if is_stop_f == 0: return 1
|
||||
return 0
|
||||
# RejectCReturnDecorator - @c.CReturn was removed; fail at compile time if seen.
|
||||
# Multi-return goes only through `-> tuple[...]`; any CReturn form is fail-fast, shared with FuncGenDef.
|
||||
def RejectCReturnDecorator(self, node: ast.AST*, decorators: list[ast.AST*]*) -> int:
|
||||
"""Fail if @c.CReturn appears in the decorators; 0=none found."""
|
||||
if decorators is None: return 0
|
||||
rcr_dn: sizet = len(decorators)
|
||||
for rcr_i in range(int(rcr_dn)):
|
||||
rcr_deco: ast.AST* = decorators.get(rcr_i)
|
||||
if rcr_deco is None: continue
|
||||
# Handle both @c.CReturn(...) (Call(func=Attribute)) and bare @c.CReturn (Attribute)
|
||||
rcr_attr: ast.Attribute* = None
|
||||
if rcr_deco.kind() == ast.ASTKind.Call:
|
||||
rcr_call: ast.Call* = ast.Call*(rcr_deco)
|
||||
if rcr_call.func is not None and rcr_call.func.kind() == ast.ASTKind.Attribute: rcr_attr = ast.Attribute*(rcr_call.func)
|
||||
elif rcr_deco.kind() == ast.ASTKind.Attribute: rcr_attr = ast.Attribute*(rcr_deco)
|
||||
if rcr_attr is None or rcr_attr.attr is None: continue
|
||||
if string.strcmp(rcr_attr.attr, "CReturn") == 0:
|
||||
self.Gen.TypeGenErr.fatal_error(node, "`@c.CReturn` removed: use a return annotation `-> tuple[ty1, ty2, ...]` instead (multiple return values are packed into a struct return)", "TypeError")
|
||||
return 1
|
||||
return 0
|
||||
# Create LLVM function (simple version, declaration only)
|
||||
def create_function(self, pool: memhub.MemBuddy*, mod: mlirlite.LLVMModule*, name: str, args_node: ast.AST*, ret_ty: mlirlite.LLVMType*) -> mlirlite.Function*:
|
||||
"""Create an LLVM function and add its parameters."""
|
||||
@@ -267,11 +247,11 @@ novirtual class FuncGenBase(CallGenBase.CallGenBase):
|
||||
for ai in range(int(an)):
|
||||
arg: ast.Arg* = ast.Arg*(alist.get(ai))
|
||||
if arg is not None and arg.arg is not None:
|
||||
# CVoid / void means no args: skip
|
||||
# void means no args: skip
|
||||
if arg.annotation is not None:
|
||||
if arg.annotation.kind() == ast.ASTKind.Name:
|
||||
at_cn: ast.Name* = ast.Name*(arg.annotation)
|
||||
if at_cn.id is not None and (string.strcmp(at_cn.id, "CVoid") == 0 or string.strcmp(at_cn.id, "void") == 0): continue
|
||||
if at_cn.id is not None and string.strcmp(at_cn.id, "void") == 0: continue
|
||||
param_ty: mlirlite.LLVMType* = i32_ty
|
||||
if arg.annotation is not None:
|
||||
resolved: mlirlite.LLVMType* = self.Gen.TypeGenResolve.resolve_annotation_type(pool, arg.annotation, None, None, None, 0)
|
||||
@@ -339,9 +319,6 @@ novirtual class FuncGenBase(CallGenBase.CallGenBase):
|
||||
elif self.Gen.TypeGenErr.has_decorator_marker(fd.returns, "ExportT", 0) != 0: ret_ty = mlirlite.Void(pool)
|
||||
elif self.Gen.TypeGenErr.has_decorator_marker(fd.returns, "ExternT", 0) != 0: ret_ty = mlirlite.Void(pool)
|
||||
|
||||
# @c.CReturn was removed: encountering it is a compile-time error (use -> tuple[...]).
|
||||
if ret_ty is None: self.RejectCReturnDecorator(fd, fd.decorator_list)
|
||||
|
||||
if ret_ty is not None: return ret_ty
|
||||
|
||||
if fd.returns is not None:
|
||||
@@ -362,7 +339,7 @@ novirtual class FuncGenBase(CallGenBase.CallGenBase):
|
||||
return mlirlite.Void(pool)
|
||||
|
||||
# param_llvm_ty - the single parameter-type mapping (signature and alloca must agree).
|
||||
# Returns None for a `CVoid` placeholder (the parameter is omitted), else the LLVM type:
|
||||
# Returns None for a `void` placeholder (the parameter is omitted), else the LLVM type:
|
||||
# bare class -> Ptr(struct), ArrayT -> element pointer (C array decay), others as resolved.
|
||||
def param_llvm_ty(self, trans: HT.Translator*, arg: ast.Arg*, ctx_label: str, pure_mode: int) -> mlirlite.LLVMType*:
|
||||
"""Map one parameter annotation to its LLVM type; None means the parameter is omitted."""
|
||||
@@ -373,8 +350,8 @@ novirtual class FuncGenBase(CallGenBase.CallGenBase):
|
||||
i32_ty: mlirlite.LLVMType* = mlirlite.Int32(pool)
|
||||
|
||||
if arg.annotation is None: return i32_ty
|
||||
# CVoid means empty param: skip, do not add to the signature or allocate it.
|
||||
if self.Gen.TypeGenResolve._is_builtin_marker(arg.annotation, "CVoid", imported_modules) != 0: return None
|
||||
# void means empty param: skip, do not add to the signature or allocate it.
|
||||
if self.Gen.TypeGenResolve._is_bare_void(arg.annotation) != 0: return None
|
||||
|
||||
resolved: mlirlite.LLVMType* = self.Gen.TypeGenResolve.resolve_annotation_type(pool, arg.annotation, imported_modules, from_imports, trans, 0)
|
||||
if resolved is None:
|
||||
@@ -436,6 +413,11 @@ novirtual class FuncGenBase(CallGenBase.CallGenBase):
|
||||
alloca: mlirlite.Value* = mlirlite.build_alloca(builder, param_ty2)
|
||||
if alloca is None: continue
|
||||
self.Gen.VarGen.define_var(trans.SymTab, arg2.arg, alloca)
|
||||
if GenApi.annotation_is_own(arg2.annotation) != 0:
|
||||
# An `own` parameter takes ownership: the callee releases it at exit.
|
||||
self.Gen.VarGen.set_var_own(trans.SymTab, arg2.arg, 1)
|
||||
sh_own_p: mlirlite.Value* = self.Gen.VarGen._SlifeEnsureOwned(pool, builder, alloca)
|
||||
if sh_own_p is not None: self.Gen.VarGen._SlifeNoteStore(pool, builder, sh_own_p, 1)
|
||||
if GenApi.strict_obs() != 0 and arg2.annotation is not None:
|
||||
rk_p: int = self.Gen.TypeGenResolve.ref_kind(arg2.annotation)
|
||||
if rk_p != 0: self.Gen.VarGen.set_var_ref_kind(trans.SymTab, arg2.arg, rk_p)
|
||||
@@ -448,7 +430,7 @@ novirtual class FuncGenBase(CallGenBase.CallGenBase):
|
||||
if pure_mode != 0:
|
||||
if string.strcmp(arg2.arg, "self") == 0: self.Gen.VarGen.set_var_life(trans.SymTab, arg2.arg, GenTypes.LIFE_FRAME)
|
||||
elif self.Gen.ExprGenBase._pure_str_struct_ty(pool, param_ty2) is not None: self.Gen.VarGen.set_var_life(trans.SymTab, arg2.arg, GenTypes.LIFE_FRAME)
|
||||
# t.ConstT / t.VolatileT / volatile pointee markers are per-parameter properties.
|
||||
# const / volatile / volatile pointee markers are per-parameter properties.
|
||||
if arg2.annotation is not None:
|
||||
if self.Gen.TypeGenErr.has_decorator_marker(arg2.annotation, "ConstT", 0) != 0: self.Gen.VarGen.set_var_const(trans.SymTab, arg2.arg)
|
||||
if self.Gen.TypeGenErr.has_decorator_marker(arg2.annotation, "VolatileT", 0) != 0: self.Gen.VarGen.set_var_volatile(trans.SymTab, arg2.arg)
|
||||
@@ -526,11 +508,11 @@ novirtual class FuncGenBase(CallGenBase.CallGenBase):
|
||||
if k3 == ast.ASTKind.If or k3 == ast.ASTKind.While or k3 == ast.ASTKind.For or k3 == ast.ASTKind.Try or k3 == ast.ASTKind.With or k3 == ast.ASTKind.Match: last_is_ctrl = 1
|
||||
|
||||
# A procedure never returns a value: explicit type, plus the annotation-level checks
|
||||
# (`-> None` / t.CVoid) for the case where the REnum class-pattern match degrades.
|
||||
# (`-> None` / `void`) for the case where the REnum class-pattern match degrades.
|
||||
is_void: int = self.ty_is_void(ret_ty)
|
||||
if is_void == 0 and unannotated_void != 0:
|
||||
if fd.returns is None: is_void = 1
|
||||
elif self.Gen.TypeGenResolve._is_cvoid_marker(fd.returns, trans._imported_modules) != 0: is_void = 1
|
||||
elif self.Gen.TypeGenResolve._is_void_marker(fd.returns) != 0: is_void = 1
|
||||
elif fd.returns.kind() == ast.ASTKind.Constant:
|
||||
ret_none_cn: ast.Constant* = (ast.Constant*)(fd.returns)
|
||||
if ret_none_cn.const_kind == ast.CONST_NONE: is_void = 1
|
||||
|
||||
@@ -44,8 +44,8 @@ novirtual class FuncGenDef(CallGenBase.CallGenBase):
|
||||
for i in range(int(an)):
|
||||
arg: ast.Arg* = ast.Arg*(alist.get(i))
|
||||
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.
|
||||
if arg.annotation is not None and self.Gen.TypeGenResolve._is_builtin_marker(arg.annotation, "CVoid", imp) != 0: continue
|
||||
# void 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_bare_void(arg.annotation) != 0: continue
|
||||
m: int = 0
|
||||
if arg.annotation is not None: m = self.Gen.TypeGenResolve.ref_param_mode(arg.annotation, imp)
|
||||
if m == 1: buf[cnt] = char(49)
|
||||
@@ -63,7 +63,7 @@ novirtual class FuncGenDef(CallGenBase.CallGenBase):
|
||||
for i2 in range(int(an)):
|
||||
arg2: ast.Arg* = ast.Arg*(alist.get(i2))
|
||||
if arg2 is None or arg2.arg is None: continue
|
||||
if arg2.annotation is not None and self.Gen.TypeGenResolve._is_builtin_marker(arg2.annotation, "CVoid", imp) != 0: continue
|
||||
if arg2.annotation is not None and self.Gen.TypeGenResolve._is_bare_void(arg2.annotation) != 0: continue
|
||||
f2: char = char(48)
|
||||
if arg2.annotation is not None and GenApi.annotation_is_nullable(arg2.annotation) != 0: f2 = char(49)
|
||||
nul_buf[nc] = f2
|
||||
@@ -71,6 +71,21 @@ novirtual class FuncGenDef(CallGenBase.CallGenBase):
|
||||
nul_buf[nc] = char(0)
|
||||
self.Gen.CallGenBase._register_nullable_params(name, nul_buf, nc)
|
||||
stdlib.free(nul_buf)
|
||||
# Per-parameter ownership (`own T` params).
|
||||
own_buf: bytes = stdlib.malloc(an + 1)
|
||||
if own_buf is not None:
|
||||
oc: int = 0
|
||||
for i3 in range(int(an)):
|
||||
arg3: ast.Arg* = ast.Arg*(alist.get(i3))
|
||||
if arg3 is None or arg3.arg is None: continue
|
||||
if arg3.annotation is not None and self.Gen.TypeGenResolve._is_bare_void(arg3.annotation) != 0: continue
|
||||
f3: char = char(48)
|
||||
if arg3.annotation is not None and GenApi.annotation_is_own(arg3.annotation) != 0: f3 = char(49)
|
||||
own_buf[oc] = f3
|
||||
oc += 1
|
||||
own_buf[oc] = char(0)
|
||||
self.Gen.CallGenBase._register_own_params(name, own_buf, oc)
|
||||
stdlib.free(own_buf)
|
||||
return 1
|
||||
|
||||
# ============================================================
|
||||
@@ -160,8 +175,8 @@ novirtual class FuncGenDef(CallGenBase.CallGenBase):
|
||||
if arg is None or arg.arg is None: continue
|
||||
ty: mlirlite.LLVMType* = None
|
||||
if arg.annotation is not None:
|
||||
# CVoid 是"空形参"占位,不参与签名
|
||||
if self.Gen.TypeGenResolve._is_builtin_marker(arg.annotation, "CVoid", imported_modules) != 0: continue
|
||||
# void 是"空形参"占位,不参与签名
|
||||
if self.Gen.TypeGenResolve._is_bare_void(arg.annotation) != 0: continue
|
||||
ty = self.Gen.TypeGenResolve.resolve_annotation_type(pool, arg.annotation, imported_modules, from_imports, trans, 0)
|
||||
if ty is None:
|
||||
stdlib.free(buf)
|
||||
@@ -331,7 +346,7 @@ novirtual class FuncGenDef(CallGenBase.CallGenBase):
|
||||
if arg is None or arg.arg is None: continue
|
||||
pty: mlirlite.LLVMType* = None
|
||||
if arg.annotation is not None:
|
||||
if self.Gen.TypeGenResolve._is_builtin_marker(arg.annotation, "CVoid", trans._imported_modules) != 0: continue
|
||||
if self.Gen.TypeGenResolve._is_bare_void(arg.annotation) != 0: continue
|
||||
pty = self.Gen.TypeGenResolve.resolve_annotation_type(pool, arg.annotation, trans._imported_modules, trans._from_imports, trans, 0)
|
||||
if pty is None:
|
||||
self.Gen.TypeGenErr.fatal_error(arg, f"runtime CDLL extern '{fd.name}' parameter '{arg.arg}' type annotation failed to resolve", "NameError")
|
||||
@@ -644,9 +659,9 @@ novirtual class FuncGenDef(CallGenBase.CallGenBase):
|
||||
for ai in range(int(an)):
|
||||
arg: ast.Arg* = ast.Arg*(alist.get(ai))
|
||||
if arg is None or arg.arg is None: continue
|
||||
# CVoid means empty param: skip
|
||||
# void means empty param: skip
|
||||
if arg.annotation is not None:
|
||||
if self.Gen.TypeGenResolve._is_builtin_marker(arg.annotation, "CVoid", imported_modules) != 0: continue
|
||||
if self.Gen.TypeGenResolve._is_bare_void(arg.annotation) != 0: continue
|
||||
param_ty: mlirlite.LLVMType* = i32_ty
|
||||
if arg.annotation is not None:
|
||||
resolved: mlirlite.LLVMType* = self.Gen.TypeGenResolve.resolve_annotation_type(pool, arg.annotation, imported_modules, from_imports, trans, 0)
|
||||
@@ -760,7 +775,7 @@ novirtual class FuncGenDef(CallGenBase.CallGenBase):
|
||||
for pai in range(int(len(pure_ags.args))):
|
||||
parg: ast.Arg* = ast.Arg*(pure_ags.args.get(pai))
|
||||
if parg is None or parg.arg is None or parg.annotation is None: continue
|
||||
if self.Gen.TypeGenResolve._is_builtin_marker(parg.annotation, "CVoid", imported_modules) != 0: continue
|
||||
if self.Gen.TypeGenResolve._is_bare_void(parg.annotation) != 0: continue
|
||||
pty_pa: mlirlite.LLVMType* = self.Gen.TypeGenResolve.resolve_annotation_type(pool, parg.annotation, imported_modules, from_imports, trans, 0)
|
||||
if pty_pa is not None and (self.Gen.ExprGenBase.is_ptr_type(pty_pa) != 0 or pty_pa.kind() == mlirlite.LLVMType.Array):
|
||||
self.Gen.TypeGenErr.fatal_error(parg, f"pure function '{fd.name}' parameter '{parg.arg}' cannot be a pointer or array: pure parameters are immutable value copies (use str/bytes for a pure_str value, or a pure class value)", "TypeError")
|
||||
@@ -796,7 +811,7 @@ novirtual class FuncGenDef(CallGenBase.CallGenBase):
|
||||
# Skip builtin decorators
|
||||
if string.strcmp(dname, "staticmethod") == 0 or string.strcmp(dname, "property") == 0 or string.strcmp(dname, "classmethod") == 0: continue
|
||||
# Skip t-module storage markers (@t.ExportT/@t.ExternT/@t.State/@t.TLS/@t.StaticT/@t.InlineT/@t.VTableT/
|
||||
# @t.NoVTable/@t.Object/@t.EnumBase/@t.UnionBase/@t.REnum/@t.DefineT). Attribute-form @t.xxx is not handled here.
|
||||
# @t.NoVTable/@t.Object/@t.EnumBase/@t.UnionBase/@t.REnum/@DefineT). Attribute-form @t.xxx is not handled here.
|
||||
# 另跳过**编译期生命期标注**(P2:规范形式 @life(willFree(...)) 与兼容叠加形式 @willFree(...)):
|
||||
# 它们是纯标注、没有运行时装饰器函数。漏在这里会让 VPC 生成 __decor_wrap_<name> wrapper,
|
||||
# 去调用并不存在的 @life/@willFree,并把被装饰的 extern def(如 stdlib.free)变成 internal 声明
|
||||
@@ -878,8 +893,8 @@ novirtual class FuncGenDef(CallGenBase.CallGenBase):
|
||||
for ai_e in range(int(an_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:
|
||||
# CVoid means empty param: skip, do not add to the function signature
|
||||
if arg_e.annotation is not None and self.Gen.TypeGenResolve._is_builtin_marker(arg_e.annotation, "CVoid", imported_modules) != 0: continue
|
||||
# void means empty param: skip, do not add to the function signature
|
||||
if arg_e.annotation is not None and self.Gen.TypeGenResolve._is_bare_void(arg_e.annotation) != 0: continue
|
||||
param_ty_e: mlirlite.LLVMType* = i32_ty
|
||||
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)
|
||||
@@ -945,8 +960,8 @@ novirtual class FuncGenDef(CallGenBase.CallGenBase):
|
||||
for reuse_i in range(int(reuse_n)):
|
||||
rearg: ast.Arg* = ast.Arg*(reuse_list.get(reuse_i))
|
||||
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)
|
||||
if rearg.annotation is not None and self.Gen.TypeGenResolve._is_builtin_marker(rearg.annotation, "CVoid", imported_modules) != 0: continue
|
||||
# void 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_bare_void(rearg.annotation) != 0: continue
|
||||
if reused_p is None:
|
||||
# 复用声明形参少于定义:签名与函数体必然错配,禁止静默,fail-fast
|
||||
self.Gen.TypeGenErr.fatal_error(rearg, f"reused declaration for '{fd.name}' has fewer parameters than the definition", "TypeError")
|
||||
|
||||
@@ -67,19 +67,19 @@ class VarEntry:
|
||||
# TypeSpec (sole type carrier since S3): filled by add_var via SpecFromLLVMType from
|
||||
# alloca.Ty's pointee (PermPool, cross-file safe); annotation side augments sign/source/elem.
|
||||
Spec: TSpec.TypeSpec*
|
||||
# 1=load/store of this variable carries LLVM volatile (annotation union has t.VolatileT).
|
||||
# 1=load/store of this variable carries LLVM volatile (annotation union has volatile).
|
||||
# Appended at the end so existing field offsets are unchanged.
|
||||
IsVolatile: int
|
||||
# 1=this variable is rawptr[t.VolatileT | T] (volatile pointee, like C's volatile T*):
|
||||
# 1=this variable is rawptr[volatile | T] (volatile pointee, like C's volatile T*):
|
||||
# its dereferences carry volatile. Unlike IsVolatile, which limits the pointer variable itself.
|
||||
PointeeVolatile: int
|
||||
# 1=this variable is a const object (annotation has t.ConstT and the type is not a
|
||||
# 1=this variable is a const object (annotation has const and the type is not a
|
||||
# pointer): assignment to it anywhere in scope fails fast. Pointer ConstT only limits the pointee.
|
||||
IsConst: int
|
||||
# 生命期标签(LifeTag,见文件头 define LIFE_*):该变量**当前值**的生命期。
|
||||
# 追加在末尾以保持既有字段偏移不变。默认 LIFE_UNKNOWN(未登记 = 保守放行)。
|
||||
Life: int
|
||||
# Byte order of this variable, from the `T | t.BigEndian` / `T | t.LittleEndian` annotation union:
|
||||
# Byte order of this variable, from the `T | bigend` / `T | littlend` annotation union:
|
||||
# 0=native (no marker), 1=BigEndian, 2=LittleEndian. Appended at the end so existing field offsets
|
||||
# are unchanged; local loads/stores bswap when 1 (16/32/64-bit ints only). Mirrors FieldEntry.ByteOrder.
|
||||
ByteOrder: int
|
||||
@@ -120,15 +120,15 @@ class FieldEntry:
|
||||
# field_ty (PermPool, safe across 1b reset); annotation side adds sign/source.
|
||||
Spec: TSpec.TypeSpec*
|
||||
# 1=load/store of this field via an object carries LLVM volatile (field annotation union
|
||||
# has t.VolatileT, e.g. Spinlock.locked). Appended at the end so offsets are unchanged.
|
||||
# has volatile, e.g. Spinlock.locked). Appended at the end so offsets are unchanged.
|
||||
IsVolatile: int
|
||||
# 1=this field is rawptr[t.VolatileT | T] (volatile pointee): dereferences via it
|
||||
# 1=this field is rawptr[volatile | T] (volatile pointee): dereferences via it
|
||||
# (self.ptr[i] / self.ptr.field) carry volatile. Same distinction as VarEntry.PointeeVolatile.
|
||||
PointeeVolatile: int
|
||||
# 1=this field is a const object (annotation has t.ConstT and the type is not a pointer):
|
||||
# 1=this field is a const object (annotation has const and the type is not a pointer):
|
||||
# assigning it via an object fails fast, same as VarEntry.IsConst.
|
||||
IsConst: int
|
||||
# Byte order of this field, from the `X | t.BigEndian` / `X | t.LittleEndian` annotation
|
||||
# Byte order of this field, from the `X | bigend` / `X | littlend` annotation
|
||||
# union: 0=native (no marker), 1=BigEndian, 2=LittleEndian. Appended at the end so offsets
|
||||
# are unchanged; consumers bswap on field load/store when 1 (16/32/64-bit ints only).
|
||||
ByteOrder: int
|
||||
@@ -137,6 +137,9 @@ class FieldEntry:
|
||||
Nullable: int
|
||||
# Reference kind in an `__obs__` file: 0=not a reference, 1=`&T` (read-only), 2=`&mut T`.
|
||||
RefKind: int
|
||||
# 1 when the field is declared `own T`: the containing object owns the pointee and releases
|
||||
# it when the object is deleted.
|
||||
IsOwn: int
|
||||
|
||||
|
||||
# ============================================================
|
||||
|
||||
@@ -44,7 +44,7 @@ novirtual class StmtGenCifMixin:
|
||||
"""Check the internal cif marker attribute name produced by the parser (c.@CIf family).
|
||||
|
||||
The '@' prefix is internal-only spelling (a leading '@' cannot be lexed as an identifier), so a
|
||||
user-written c.CIf/c.CIfdef/c.CIfndef never matches here and is reported as removed instead.
|
||||
user-written CIf spellings never match here.
|
||||
"""
|
||||
if attr is None: return 0
|
||||
if string.strcmp(attr, "@CIf") == 0: return 1
|
||||
@@ -448,6 +448,8 @@ novirtual class StmtGenCifMixin:
|
||||
self.Gen.TypeGenErr.fatal_error(rt.value, "the function's return type is not nullable: a nullable value may be None; return `T?` or unwrap it with `match` first", "TypeError")
|
||||
return 0
|
||||
|
||||
# Ownership: returning a local `own` binding transfers it to the caller (no release here).
|
||||
# Handled after the value is translated below.
|
||||
val: mlirlite.Value* = None
|
||||
if rt.value is not None:
|
||||
# f-string escape (return f"..."): its buffer lives on this frame's stack and dies on
|
||||
@@ -529,15 +531,15 @@ novirtual class StmtGenCifMixin:
|
||||
ret_val_ty: mlirlite.LLVMType* = None
|
||||
cur_func_rv: mlirlite.Function* = trans._cur_func
|
||||
if cur_func_rv is not None: ret_val_ty = mlirlite.function_get_ret_ty(cur_func_rv)
|
||||
# `return <闭包变量>` 且返回注解是 t.Callable:闭包对象是 {fn_ptr, env_ptr},
|
||||
# 不是裸函数指针。调用方把结果存进 t.Callable 变量后 `f()` 会按裸函数指针直调,
|
||||
# `return <闭包变量>` 且返回注解是 Callable:闭包对象是 {fn_ptr, env_ptr},
|
||||
# 不是裸函数指针。调用方把结果存进 Callable 变量后 `f()` 会按裸函数指针直调,
|
||||
# 跳到堆地址崩溃(ClosureCallTest 现场)。这里明确 fail-fast,要求用 rawptr。
|
||||
if rt.value.kind() == ast.ASTKind.Name and trans is not None and trans.CurFuncRetAnnot is not None:
|
||||
clo_nm_rv: ast.Name* = ast.Name*(rt.value)
|
||||
if clo_nm_rv.id is not None and self.Gen.CallGenLeafKit.is_closure_name(clo_nm_rv.id) != 0:
|
||||
clo_ret_rv: mlirlite.LLVMType* = self.Gen.TypeGenErr.try_extract_callable_ret(pool, trans.CurFuncRetAnnot, trans._imported_modules, trans._from_imports, trans)
|
||||
if clo_ret_rv is not None:
|
||||
self.Gen.TypeGenErr.fatal_error(rt, f"the return annotation is t.Callable (a bare function pointer) but the returned value is the closure variable '{clo_nm_rv.id}'. A closure is a (fn_ptr, env_ptr) object; storing it where a t.Callable is expected makes the caller jump into heap data. Annotate the return as rawptr instead", "TypeError")
|
||||
self.Gen.TypeGenErr.fatal_error(rt, f"the return annotation is Callable (a bare function pointer) but the returned value is the closure variable '{clo_nm_rv.id}'. A closure is a (fn_ptr, env_ptr) object; storing it where a Callable is expected makes the caller jump into heap data. Annotate the return as rawptr instead", "TypeError")
|
||||
# [C1] return-annotation context: record this return's direct value node so a bare
|
||||
# generic ctor (`return Stack()` in `-> Stack[int]`) specializes via CurFuncRetAnnot.
|
||||
trans.CurRetNode = rt.value
|
||||
@@ -599,6 +601,12 @@ novirtual class StmtGenCifMixin:
|
||||
if rt_slot_p is not None and self.Gen.AssignGen._pure_str_slot(trans.Pool, rt_slot_p) != 0:
|
||||
rt_sh_p: mlirlite.Value* = self.Gen.VarGen._SlifeShadow(rt_slot_p)
|
||||
if rt_sh_p is not None: self.Gen.VarGen._SlifeNoteStore(trans.Pool, builder, rt_sh_p, 0)
|
||||
# `return <own local>` in a `-> own T` function: ownership goes to the caller.
|
||||
fd_own: ast.FunctionDef* = ast.FunctionDef*(self.Gen.StructGen.NllCurFunc)
|
||||
if fd_own is not None and GenApi.annotation_is_own(fd_own.returns) != 0 \
|
||||
and rt_slot_p is not None and self.Gen.VarGen._SlifeOwned(rt_slot_p) != 0:
|
||||
rt_sh_o: mlirlite.Value* = self.Gen.VarGen._SlifeShadow(rt_slot_p)
|
||||
if rt_sh_o is not None: self.Gen.VarGen._SlifeNoteStore(trans.Pool, builder, rt_sh_o, 0)
|
||||
cur_func_rt: mlirlite.Function* = trans._cur_func
|
||||
ret_ty_rt: mlirlite.LLVMType* = None
|
||||
if cur_func_rt is not None and val is not None and val.Ty is not None: ret_ty_rt = mlirlite.function_get_ret_ty(cur_func_rt)
|
||||
@@ -1120,7 +1128,7 @@ novirtual class StmtGenCifMixin:
|
||||
cn_ik: ast.Constant* = ast.Constant*(iter_node)
|
||||
if cn_ik.const_kind != ast.CONST_STR: self.Gen.TypeGenErr.fatal_error(iter_node, "for iteration does not support this constant (only strings, arrays, list literals, range())")
|
||||
elif ik == ast.ASTKind.Dict or ik == ast.ASTKind.Set or ik == ast.ASTKind.Tuple or ik == ast.ASTKind.SetComp or ik == ast.ASTKind.DictComp:
|
||||
self.Gen.TypeGenErr.fatal_error(iter_node, "for iteration does not support dict/set/tuple literals; use range(), a t.ArrayT array, or a list literal")
|
||||
self.Gen.TypeGenErr.fatal_error(iter_node, "for iteration does not support dict/set/tuple literals; use range(), a fixed-size array, or a list literal")
|
||||
return self._handle_ptr_iter(trans, for_node, var_name)
|
||||
|
||||
call: ast.Call* = ast.Call*(iter_node)
|
||||
|
||||
@@ -244,6 +244,8 @@ novirtual class StmtGenMixin(StmtGenDeclMixin.StmtGenDeclMixin):
|
||||
self.Gen.TypeGenErr.fatal_error(tgt, f"cannot `del {nm_del.id}`: the pointer value could not be loaded", "TypeError")
|
||||
continue
|
||||
self._emit_del_dtor(pool, builder, mod, trans, del_cur, del_var_ty, del_struct_ty)
|
||||
# `own` fields belong to this object: release them before freeing it.
|
||||
self._release_own_fields(pool, builder, mod, trans, del_cur, del_struct_ty)
|
||||
if self.Gen.CallGenBase.find_func_in_module(mod, "free") is None:
|
||||
self.Gen.TypeGenErr.fatal_error(tgt, "cannot `del`: the module has no declaration of the C library function 'free'", "TypeError")
|
||||
continue
|
||||
@@ -330,6 +332,37 @@ novirtual class StmtGenMixin(StmtGenDeclMixin.StmtGenDeclMixin):
|
||||
# translate_assert - translate `assert cond[, msg]` into a single testcheck.check(...) call:
|
||||
# a true condition is silent (ok_msg=None), a false one reports FAIL via the standard path
|
||||
# (no printf/exit/exception; the checker prints nothing when the condition holds).
|
||||
# _release_own_fields - free the `own` fields of a heap object before the object itself is freed.
|
||||
def _release_own_fields(self, pool: memhub.MemBuddy*, builder: mlirlite.IRBuilder*,
|
||||
mod: mlirlite.LLVMModule*, trans: HT.Translator*,
|
||||
cur: mlirlite.Value*, struct_ty: mlirlite.LLVMType*, depth: int = 0) -> int:
|
||||
if pool is None or builder is None or mod is None or cur is None or struct_ty is None: return 0
|
||||
if depth > 8: return 0
|
||||
entry: GenTypes.StructEntry* = self.Gen.StructGen.find_struct_by_type(struct_ty)
|
||||
if entry is None: return 0
|
||||
if self.Gen.CallGenBase.find_func_in_module(mod, "free") is None: return 0
|
||||
i8p: mlirlite.LLVMType* = mlirlite.Ptr(pool, mlirlite.Int8(pool))
|
||||
fi: int = 0
|
||||
while fi < entry.FieldCount:
|
||||
fe: GenTypes.FieldEntry* = self.Gen.StructGen._get_field_entry(entry, fi)
|
||||
fi += 1
|
||||
if fe is None or fe.IsOwn == 0: continue
|
||||
if fe.Ty is None or fe.Ty.Pointee is None: continue
|
||||
if self.Gen.ExprGenBase.is_ptr_type(fe.Ty) == 0: continue
|
||||
fp: mlirlite.Value* = mlirlite.build_gep_struct(builder, struct_ty, fe.Ty, cur, fe.Index)
|
||||
if fp is None: continue
|
||||
fv: mlirlite.Value* = mlirlite.build_load(builder, fe.Ty, fp)
|
||||
if fv is None: continue
|
||||
inner: mlirlite.LLVMType* = fe.Ty.Pointee
|
||||
self._emit_del_dtor(pool, builder, mod, trans, fv, fe.Ty, inner)
|
||||
self._release_own_fields(pool, builder, mod, trans, fv, inner, depth + 1)
|
||||
arg_f: mlirlite.Value* = fv
|
||||
if fv.Ty is not None and uint64(fv.Ty) != uint64(i8p):
|
||||
arg_f = mlirlite.build_bitcast(builder, fv, i8p)
|
||||
if arg_f is not None:
|
||||
mlirlite.build_call(builder, "free", arg_f, 1, mlirlite.Void(pool), 0)
|
||||
return 1
|
||||
|
||||
def translate_assert(self, trans: HT.Translator*, node: ast.AST*) -> int:
|
||||
"""Translate an assert statement; returns 0"""
|
||||
if trans is None or node is None: return 0
|
||||
|
||||
@@ -418,9 +418,7 @@ novirtual class TypeGenResolve(TypeGenBase.TypeGenBase):
|
||||
if _out_s is not None: return _out_s
|
||||
# ---- 2. already normalized (SHA1 prefix) ----
|
||||
if self._is_sha1_prefixed(type_name) != 0: return type_name
|
||||
# ---- 3. t.XXX / c.XXX builtin namespace ----
|
||||
if (type_name[0] == 't' or type_name[0] == 'c') and type_name[1] == '.': return type_name
|
||||
# ---- 4. "mod.Class" Attribute ----
|
||||
# ---- 3. "mod.Class" Attribute ----
|
||||
dot_pos: str = string.strchr(type_name, '.')
|
||||
if dot_pos is not None:
|
||||
mod_len: sizet = sizet(uint64(dot_pos) - uint64(type_name))
|
||||
@@ -607,7 +605,7 @@ novirtual class TypeGenResolve(TypeGenBase.TypeGenBase):
|
||||
# `T1 | T2` unions were retired: an annotation string containing '|' is invalid.
|
||||
if string.strchr(type_str, '|') is not None: return None
|
||||
|
||||
# Check for '[' (Subscript) like t.Callable[[int], int]; must precede the '.'
|
||||
# Check for '[' (Subscript) like Callable[[int], int]; must precede the '.'
|
||||
# check since it is a Subscript. Parse fully via ast.parse_expression.
|
||||
bracket_idx: sizet = seg_len
|
||||
for bi in range(int(seg_len)):
|
||||
@@ -655,7 +653,7 @@ novirtual class TypeGenResolve(TypeGenBase.TypeGenBase):
|
||||
expr_body: ast.AST* = expr_node.body
|
||||
if expr_body is not None: return expr_body
|
||||
# Fallback: file-level ast.parse; the single-expression entry fails on complex
|
||||
# slices like "t.Callable[[int], int]" but the file-level one can parse them.
|
||||
# slices like "Callable[[int], int]" but the file-level one can parse them.
|
||||
tree_s: ast.AST* = ast.parse(type_str, pool)
|
||||
if tree_s is not None:
|
||||
ch_s: list[ast.AST*]* = tree_s.children
|
||||
@@ -984,7 +982,7 @@ novirtual class TypeGenResolve(TypeGenBase.TypeGenBase):
|
||||
stdlib.free(b_buf)
|
||||
return None
|
||||
if b_read < LONG(BUF_B): b_buf[sizet(b_read)] = 0
|
||||
# Scan line-by-line and register all builtins "NAME: t.TypedefT = TYPE" entries at
|
||||
# Scan line-by-line and register all builtins "NAME: TypedefT = TYPE" entries at
|
||||
# once; existing entries (module/import-injected) take priority and are not overwritten.
|
||||
b_total: sizet = string.strlen(b_buf)
|
||||
# Pre-scan import lines to register aliases (import stdint as __stdint), so later
|
||||
@@ -1037,7 +1035,7 @@ novirtual class TypeGenResolve(TypeGenBase.TypeGenBase):
|
||||
if b_i == b_total or b_buf[b_i] == '\n':
|
||||
b_line_len: sizet = b_i - b_line_start
|
||||
if b_line_len > 0 and b_td_buf is not None:
|
||||
# Parse both equivalent forms (`typedef N = V` and `N: t.TypedefT = V`);
|
||||
# Parse both equivalent forms (`typedef N = V` and `N: TypedefT = V`);
|
||||
# builtins.vp now uses the sugar form, so accept both.
|
||||
if self._parse_typedef_line(b_td_buf, b_buf, b_line_start, b_line_len) != 0:
|
||||
b_nb: str = b_td_buf
|
||||
@@ -1124,7 +1122,7 @@ novirtual class TypeGenResolve(TypeGenBase.TypeGenBase):
|
||||
if v is None: return None
|
||||
return rawptr(v)
|
||||
def _lookup_library_typedef(self, pool: memhub.MemBuddy*, mod: str, attr: str) -> str:
|
||||
"""Look up "attr: t.TypedefT = TYPE" in a library module source, returning TYPE or None."""
|
||||
"""Look up "attr: TypedefT = TYPE" in a library module source, returning TYPE or None."""
|
||||
if mod is None or attr is None: return None
|
||||
# Restore the real module name from a builtins import alias (__stdint -> stdint);
|
||||
# without this, SHA1 lookup and path resolution all miss.
|
||||
@@ -1168,7 +1166,7 @@ novirtual class TypeGenResolve(TypeGenBase.TypeGenBase):
|
||||
if m_i == m_total or m_buf[m_i] == '\n':
|
||||
m_line_len: sizet = m_i - m_ls
|
||||
if m_line_len > 0 and m_td_buf is not None:
|
||||
# Parse both equivalent forms (`typedef N = V` and `N: t.TypedefT = V`),
|
||||
# Parse both equivalent forms (`typedef N = V` and `N: TypedefT = V`),
|
||||
# both of which occur in library module sources.
|
||||
if self._parse_typedef_line(m_td_buf, m_buf, m_ls, m_line_len) != 0:
|
||||
m_nb: str = m_td_buf
|
||||
@@ -1198,7 +1196,6 @@ novirtual class TypeGenResolve(TypeGenBase.TypeGenBase):
|
||||
mod_len: sizet = sizet(uint64(dot_pos) - uint64(type_str))
|
||||
attr: str = dot_pos + 1
|
||||
if mod_len == 0 or attr is None or attr[0] == '\0': return type_str
|
||||
if mod_len == 1 and (type_str[0] == 't' or type_str[0] == 'c'): return type_str
|
||||
mod_buf: str = pool.alloc(mod_len + 1)
|
||||
if mod_buf is None: return type_str
|
||||
string.strncpy(mod_buf, type_str, mod_len)
|
||||
@@ -1210,7 +1207,7 @@ novirtual class TypeGenResolve(TypeGenBase.TypeGenBase):
|
||||
if ts_x is not None and self.Gen.TypeGenCType.CTypedefFound != 0 and string.strcmp(ts_x, type_str) != 0: return self._expand_typedef_ref(pool, ts_x, depth + 1)
|
||||
return type_str
|
||||
# _IsCallableSubscriptValue - whether a Subscript value is a function-pointer type
|
||||
# constructor (t.Callable / callable / Callable); used to allow the unsigned forms.
|
||||
# constructor (Callable / callable / Callable); used to allow the unsigned forms.
|
||||
def _IsCallableSubscriptValue(self, value_node: ast.AST*) -> int:
|
||||
if value_node is None: return 0
|
||||
vk: int = value_node.kind()
|
||||
@@ -1330,7 +1327,7 @@ novirtual class TypeGenResolve(TypeGenBase.TypeGenBase):
|
||||
|
||||
# 存疑
|
||||
|
||||
# Lowercase alias callable: equivalent to t.Callable in annotations.
|
||||
# Lowercase alias callable: equivalent to Callable in annotations.
|
||||
elif string.strcmp(type_name, "callable") == 0: return mlirlite.Ptr(pool, mlirlite.Int8(pool))
|
||||
|
||||
# stdint.vp aliases (INT/BYTE/DWORD/int8_t/size_t/_Bool ...) now come from its
|
||||
@@ -1339,21 +1336,13 @@ novirtual class TypeGenResolve(TypeGenBase.TypeGenBase):
|
||||
elif string.strcmp(type_name, "float") == 0: return mlirlite.Float(pool)
|
||||
elif string.strcmp(type_name, "double") == 0: return mlirlite.Double(pool)
|
||||
return None
|
||||
def _is_builtin_marker(self, node: ast.AST*, attr_name: str, imported_modules: str) -> int:
|
||||
"""Whether a node is the bare builtin marker name (e.g. PtrT / RefT)."""
|
||||
if node is None or attr_name is None: return 0
|
||||
if node.kind() != ast.ASTKind.Name: return 0
|
||||
nm: ast.Name* = ast.Name*(node)
|
||||
if nm.id is None: return 0
|
||||
if string.strcmp(nm.id, attr_name) != 0: return 0
|
||||
return 1
|
||||
def target_is_big_endian(self) -> int:
|
||||
"""Whether the compilation target is big-endian (1) or little-endian (0).
|
||||
The configured data layout wins: LLVM's first data-layout field is the endianness
|
||||
marker ('e' = little, 'E' = big; omitted means little). Without a data layout, fall
|
||||
back to the triple's `_be` arch suffix (e.g. aarch64_be-...); otherwise little, which
|
||||
is the default target. Both byte-order markers are judged against this: a `t.BigEndian`
|
||||
field on a big-endian target needs no swap, and a `t.LittleEndian` field on a
|
||||
is the default target. Both byte-order markers are judged against this: a `bigend`
|
||||
field on a big-endian target needs no swap, and a `littlend` field on a
|
||||
big-endian target does."""
|
||||
dl: str = Config.TargetDataLayout
|
||||
if dl is not None and dl[0] != '\0':
|
||||
@@ -1434,12 +1423,15 @@ novirtual class TypeGenResolve(TypeGenBase.TypeGenBase):
|
||||
rn: ast.Name* = ast.Name*(at.value)
|
||||
if rn.id is None or string.strcmp(rn.id, "attr") != 0: return 0
|
||||
return 1
|
||||
def _is_cvoid_marker(self, node: ast.AST*, imported_modules: str) -> int:
|
||||
"""Whether a node is a CVoid / void marker: 1=yes / 0=no."""
|
||||
if self._is_builtin_marker(node, "CVoid", imported_modules) != 0: return 1
|
||||
def _is_bare_void(self, node: ast.AST*) -> int:
|
||||
"""Whether a node is exactly the bare `void` name: 1=yes / 0=no."""
|
||||
if node is None or node.kind() != ast.ASTKind.Name: return 0
|
||||
nm: ast.Name* = ast.Name*(node)
|
||||
return 1 if nm.id is not None and string.strcmp(nm.id, "void") == 0 else 0
|
||||
def _is_void_marker(self, node: ast.AST*) -> int:
|
||||
"""Whether a node is a void marker: 1=yes / 0=no."""
|
||||
if node is not None and node.kind() == ast.ASTKind.Name:
|
||||
nm: ast.Name* = ast.Name*(node)
|
||||
# Bare CVoid is synonymous with the builtin alias void
|
||||
if nm.id is not None and string.strcmp(nm.id, "void") == 0: return 1
|
||||
# Storage / descriptor wrappers keep the wrapped type's void-ness (a pointer wrapper does not).
|
||||
if node is not None and node.kind() == ast.ASTKind.Subscript:
|
||||
@@ -1453,7 +1445,7 @@ novirtual class TypeGenResolve(TypeGenBase.TypeGenBase):
|
||||
or string.strcmp(sw_nm.id, "DefineT") == 0 or string.strcmp(sw_nm.id, "RefT") == 0 \
|
||||
or string.strcmp(sw_nm.id, "TempRefT") == 0 or string.strcmp(sw_nm.id, "MutRefT") == 0 \
|
||||
or string.strcmp(sw_nm.id, "AutoPtrT") == 0):
|
||||
if sub_v.slice is not None: return self._is_cvoid_marker(sub_v.slice, imported_modules)
|
||||
if sub_v.slice is not None: return self._is_void_marker(sub_v.slice)
|
||||
return 0
|
||||
def annotation_to_str(self, annot: ast.AST*) -> str:
|
||||
"""Render a type-annotation AST to a readable string; None means not extractable."""
|
||||
@@ -1472,7 +1464,7 @@ novirtual class TypeGenResolve(TypeGenBase.TypeGenBase):
|
||||
if nm.id is not None: return nm.id
|
||||
return "<name>"
|
||||
|
||||
# Attribute node: t.CBool, memhub.MemBuddy
|
||||
# Attribute node: bool, memhub.MemBuddy
|
||||
if k == ast.ASTKind.Attribute:
|
||||
at: ast.Attribute* = ast.Attribute*(annot)
|
||||
if at.attr is None: return "<attribute>"
|
||||
@@ -1593,8 +1585,8 @@ novirtual class TypeGenResolve(TypeGenBase.TypeGenBase):
|
||||
if k == ast.ASTKind.Attribute:
|
||||
at: ast.Attribute* = ast.Attribute*(node)
|
||||
if at.attr is None: return None
|
||||
# t.XXX builtin type: return XXX (e.g. uint32 -> "CUInt32T") so AnnotClassName
|
||||
# can record it; consumers degrade gracefully for non-struct names.
|
||||
# Qualified type: return "module.Type" so later resolution can record it;
|
||||
# consumers degrade gracefully for non-struct names.
|
||||
if at.value is not None and at.value.kind() == ast.ASTKind.Name:
|
||||
val_nm2: ast.Name* = ast.Name*(at.value)
|
||||
if val_nm2.id is not None and string.strcmp(val_nm2.id, "attr") == 0: return at.attr
|
||||
@@ -1716,7 +1708,7 @@ novirtual class TypeGenResolve(TypeGenBase.TypeGenBase):
|
||||
# named Option is never shadowed in files that do not use Option[...].
|
||||
if string.strcmp(gen_cls_name, "Option") == 0: self.Gen.ClassGenGeneric._ensure_builtin_option(None)
|
||||
# tuple[T1, T2, ...] -> anonymous struct {T1, T2, ...} (multi-value packing, matching
|
||||
# TPC LLVMTypeMapper._resolve_subscript), for `-> tuple[str, t.CUnsignedShort]` and unpacking.
|
||||
# TPC LLVMTypeMapper._resolve_subscript), for `-> tuple[str, ushort]` and unpacking.
|
||||
if string.strcmp(gen_cls_name, "tuple") == 0:
|
||||
tp_elts: list[ast.AST*]* = None
|
||||
tp_count: sizet = 0
|
||||
@@ -1981,9 +1973,9 @@ novirtual class TypeGenResolve(TypeGenBase.TypeGenBase):
|
||||
return mlirlite.Int8(pool)
|
||||
elif string.strcmp(nm.id, "bytes") == 0: return mlirlite.Ptr(pool, mlirlite.Int8(pool))
|
||||
elif string.strcmp(nm.id, "bool") == 0:
|
||||
# bool = t.CBool = i1 (consistent with CBool in map_builtin_type)
|
||||
# bool = bool = i1 (consistent with CBool in map_builtin_type)
|
||||
return mlirlite.Int1(pool)
|
||||
# Bare float types: float = t.CFloat = f32, double = t.CDouble = f64
|
||||
# Bare float types: float = float = f32, double = double = f64
|
||||
elif string.strcmp(nm.id, "float") == 0: return mlirlite.Float(pool)
|
||||
elif string.strcmp(nm.id, "double") == 0: return mlirlite.Double(pool)
|
||||
# Enum base type, needed by the struct branches below (forward-placeholder guard).
|
||||
@@ -2238,7 +2230,7 @@ novirtual class TypeGenResolve(TypeGenBase.TypeGenBase):
|
||||
# 模块段无法解析时退回"同名唯一 / 同名基类型一致"。
|
||||
enum_fb_ty: mlirlite.LLVMType* = self._resolve_enum_base_ty(at.attr, None, trans)
|
||||
if enum_fb_ty is not None: return enum_fb_ty
|
||||
# Module-qualified typedef (posix.DIR = t.TypedefT = t.CVoid* etc.): same
|
||||
# Module-qualified typedef (posix.DIR = TypedefT = void* etc.): same
|
||||
# lookup_ctypedef + recursive resolution as the Name branch.
|
||||
if self.Gen.TypeGenCType.CTypedefResolveDepth < TypeGenMixin.MAX_CTYPEDEF_DEPTH:
|
||||
mod_td_str: str = self.Gen.TypeGenCType.lookup_ctypedef(at.attr)
|
||||
@@ -2305,10 +2297,10 @@ novirtual class TypeGenResolve(TypeGenBase.TypeGenBase):
|
||||
if k == ast.ASTKind.Subscript:
|
||||
sub: ast.Subscript* = ast.Subscript*(node)
|
||||
if sub.value is None: self.Gen.TypeGenErr.fatal_type_error(node, "Subscript node missing value")
|
||||
# An empty subscript (t.Callable[]) has slice None: allowed only for unsigned function
|
||||
# An empty subscript (Callable[]) has slice None: allowed only for unsigned function
|
||||
# pointers (handled as i8* below); other types still require a slice.
|
||||
if sub.slice is None and self._IsCallableSubscriptValue(sub.value) == 0: self.Gen.TypeGenErr.fatal_type_error(node, "Subscript node missing value or slice")
|
||||
# Function pointers (bare callable / Callable) equal t.Callable point-for-point and
|
||||
# Function pointers (bare callable / Callable) equal Callable point-for-point and
|
||||
# map to i8*; must be checked before the Name path (generic/ArrayT/builtins fallback).
|
||||
if sub.value.kind() == ast.ASTKind.Name and self._IsCallableSubscriptValue(sub.value) != 0: return mlirlite.Ptr(pool, mlirlite.Int8(pool))
|
||||
# Generic class annotation (list[int]): look up the specialized struct for a Name value.
|
||||
@@ -2329,20 +2321,17 @@ novirtual class TypeGenResolve(TypeGenBase.TypeGenBase):
|
||||
and self.Gen.ClassGenGeneric._find_generic_template(gen_nm.id) is None:
|
||||
bx_inner: mlirlite.LLVMType* = self.resolve_annotation_type(pool, sub.slice, imported_modules, from_imports, trans, depth + 1)
|
||||
if bx_inner is None: return None
|
||||
match bx_inner:
|
||||
case mlirlite.LLVMType.Ptr(_bp): return bx_inner
|
||||
case _: return mlirlite.Ptr(pool, bx_inner)
|
||||
return mlirlite.Ptr(pool, bx_inner)
|
||||
# `RawT[T]`: the unboxed value type.
|
||||
if string.strcmp(gen_nm.id, "RawT") == 0 \
|
||||
and self.Gen.ClassGenGeneric._find_generic_template("RawT") is None:
|
||||
return self.resolve_annotation_type(pool, sub.slice, imported_modules, from_imports, trans, depth + 1)
|
||||
# `own T`: one owned pointer layer (exclusive ownership of a heap object).
|
||||
# Every `own` adds a layer, so `own own T` / `Box[Box[T]]` stay two pointers.
|
||||
if string.strcmp(gen_nm.id, "OwnT") == 0:
|
||||
ow_inner: mlirlite.LLVMType* = self.resolve_annotation_type(pool, sub.slice, imported_modules, from_imports, trans, depth + 1)
|
||||
if ow_inner is None: return None
|
||||
match ow_inner:
|
||||
case mlirlite.LLVMType.Ptr(_op): return ow_inner
|
||||
case _: return mlirlite.Ptr(pool, ow_inner)
|
||||
return mlirlite.Ptr(pool, ow_inner)
|
||||
# Builtin marker wrappers: descriptor / storage / value-reference markers carry
|
||||
# no LLVM type of their own, so resolve the wrapped type.
|
||||
if string.strcmp(gen_nm.id, "ConstT") == 0 or string.strcmp(gen_nm.id, "VolatileT") == 0 \
|
||||
@@ -2370,7 +2359,7 @@ novirtual class TypeGenResolve(TypeGenBase.TypeGenBase):
|
||||
if count_node_arr.kind() == ast.ASTKind.Constant:
|
||||
cnt_arr0: ast.Constant* = ast.Constant*(count_node_arr)
|
||||
if cnt_arr0.const_kind == ast.CONST_NONE:
|
||||
self.Gen.TypeGenErr.fatal_type_error(count_node_arr, "Built-in fixed-size array constructor 'array' length must be a positive integer constant (>0); None is not allowed (use t.ArrayT[E] for automatic length inference). Correct example: state: array[uint32, 4]")
|
||||
self.Gen.TypeGenErr.fatal_type_error(count_node_arr, "Built-in fixed-size array constructor 'array' length must be a positive integer constant (>0); None is not allowed (use [N]T for automatic length inference). Correct example: state: array[uint32, 4]")
|
||||
elem_ty_arr: mlirlite.LLVMType* = self.resolve_annotation_type(pool, elem_node_arr, imported_modules, from_imports, trans, depth + 1)
|
||||
if elem_ty_arr is None: return None
|
||||
count_arr: int = self.Gen.TypeGenCType._eval_const_int_expr(count_node_arr, pool, from_imports, imported_modules)
|
||||
@@ -2478,13 +2467,13 @@ novirtual class TypeGenResolve(TypeGenBase.TypeGenBase):
|
||||
return mlirlite.Ptr(pool, elem_ty_single)
|
||||
# Two-arg form: t.ArrayT[elem_ty, count] -> [count x elem_ty]
|
||||
tup: ast.Tuple* = ast.Tuple*(sub.slice)
|
||||
if tup.elts is None: self.Gen.TypeGenErr.fatal_type_error(node, "t.ArrayT Tuple missing elts")
|
||||
if tup.elts is None: self.Gen.TypeGenErr.fatal_type_error(node, "array tuple missing elements")
|
||||
elts_list: list[ast.AST*]* = tup.elts
|
||||
elts_n: sizet = len(elts_list)
|
||||
if elts_n < 2: self.Gen.TypeGenErr.fatal_type_error(node, "t.ArrayT Tuple has too few elements")
|
||||
if elts_n < 2: self.Gen.TypeGenErr.fatal_type_error(node, "array tuple has too few elements")
|
||||
elem_node: ast.AST* = elts_list.get(0)
|
||||
count_node: ast.AST* = elts_list.get(1)
|
||||
if elem_node is None or count_node is None: self.Gen.TypeGenErr.fatal_type_error(node, "t.ArrayT Tuple element is None")
|
||||
if elem_node is None or count_node is None: self.Gen.TypeGenErr.fatal_type_error(node, "array tuple element is None")
|
||||
# Recursively resolve the element type
|
||||
elem_ty: mlirlite.LLVMType* = self.resolve_annotation_type(pool, elem_node, imported_modules, from_imports, trans, depth + 1)
|
||||
if elem_ty is None:
|
||||
@@ -2500,8 +2489,8 @@ novirtual class TypeGenResolve(TypeGenBase.TypeGenBase):
|
||||
# Use _eval_const_int_expr for the constant integer expression (Constant/Name/BinOp);
|
||||
# pool/from_imports/imported_modules enable cross-module DefineT lookup.
|
||||
count_val = self.Gen.TypeGenCType._eval_const_int_expr(count_node, pool, from_imports, imported_modules)
|
||||
if self.Gen.TypeGenCType.ConstEvalOk == 0: self.Gen.TypeGenErr.fatal_type_error(count_node, "t.ArrayT count must be an integer constant, None, a DefineT constant name, or a constant expression")
|
||||
if count_val <= 0: self.Gen.TypeGenErr.fatal_type_error(count_node, "t.ArrayT count must be positive")
|
||||
if self.Gen.TypeGenCType.ConstEvalOk == 0: self.Gen.TypeGenErr.fatal_type_error(count_node, "array count must be an integer constant, None, a DefineT constant name, or a constant expression")
|
||||
if count_val <= 0: self.Gen.TypeGenErr.fatal_type_error(count_node, "array count must be positive")
|
||||
# Create the ArrayType (no Ptr wrapping), so build_alloca allocates the array
|
||||
# and alloca returns [N x elem_ty]*.
|
||||
return mlirlite.Array(pool, elem_ty, count_val)
|
||||
|
||||
Reference in new issue
Block a user