增加 JIT REPL

This commit is contained in:
TermiNexus committed 2026-09-21 16:17:19 +08:00
1 parent 9910e05760
commit a5ceb3d4eb
56 files changed
+7621 -584

No files matched your search

+183 -51
View File
@@ -125,7 +125,7 @@ class AssignGenMixin:
cl: rawptr[ast.Call] = rawptr[ast.Call](val_node)
# 必须先把属性赋给显式类型为 list[...] | t.CPtr 的局部变量再调用 __len__(),
# 否则编译器无法识别属性返回的 list 类型,GEP base 会变成 i32 0 导致 llc 报错
cl_args_list: rawptr[list[rawptr[ast.AST]]] = cl.args
cl_args_list: list[ast.AST*]* = cl.args
if cl_args_list is not None and len(cl_args_list) > 0:
first_arg: rawptr[ast.AST] = cl_args_list.get(0)
arg_val: int64 = self.extract_cdefine_int_value(first_arg)
@@ -167,7 +167,7 @@ class AssignGenMixin:
if val_node is not None and val_node.kind() == ast.ASTKind.Call:
cl_cd: rawptr[ast.Call] = rawptr[ast.Call](val_node)
if cl_cd is not None and cl_cd.args is not None:
cl_args_cd: rawptr[list[rawptr[ast.AST]]] = cl_cd.args
cl_args_cd: list[ast.AST*]* = cl_cd.args
if len(cl_args_cd) > 0:
first_arg_cd: rawptr[ast.AST] = cl_args_cd.get(0)
if first_arg_cd is not None and first_arg_cd.kind() == ast.ASTKind.Constant:
@@ -640,7 +640,7 @@ class AssignGenMixin:
lst: rawptr[ast.List] = rawptr[ast.List](list_node)
if lst is None or lst.elts is None:
return 1
elts: rawptr[list[rawptr[ast.AST]]] = lst.elts
elts: list[ast.AST*]* = lst.elts
elts_count: sizet = len(elts)
# 特例:单个字符串字面量初始化字符数组([N x i8],N = strlen+1)
@@ -839,8 +839,17 @@ class AssignGenMixin:
if target_ty is None:
# fail-fast: 字段类型信息缺失,禁止兜底 i32
self.Gen.TypeGenErr.fatal_error(target, "字段类型信息缺失", "TypeError")
# t.CVolatile 字段:+= 的 load 与 store 均带 volatile 限定符
vol_fa: int = self.Gen.ExprGenAttr.attr_field_is_volatile(pool, trans, target)
# rawptr[t.CVolatile|T] 基址(volatile 指针指向物):经其访问的
# 字段 += 的 load/store 亦带 volatile(两来源取或)。
at_aug: rawptr[ast.Attribute] = rawptr[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
cur_val: rawptr[llvmlite.Value] = llvmlite.build_load(
builder, target_ty, field_ptr)
builder, target_ty, field_ptr, vol_fa)
if cur_val is None:
# fail-fast: 属性当前值加载失败
self.Gen.TypeGenErr.fatal_error(target, "无法加载 attribute 当前值", "TypeError")
@@ -864,7 +873,7 @@ class AssignGenMixin:
if result is None:
# fail-fast: 类型对齐失败
self.Gen.TypeGenErr.fatal_error(target, "类型对齐失败", "TypeError")
llvmlite.build_store(builder, result, field_ptr)
llvmlite.build_store(builder, result, field_ptr, vol_fa)
return 0
# Subscript 目标: self.state[i] += x / arr[i] += x
@@ -888,8 +897,15 @@ class AssignGenMixin:
if target_ty is None:
# fail-fast: 元素类型信息缺失,禁止兜底 i32
self.Gen.TypeGenErr.fatal_error(target, "元素类型信息缺失", "TypeError")
# rawptr[t.CVolatile|T] 基址(volatile 指针指向物):p[i] += 的
# load/store 均带 volatile。仅基址可解析到 VarEntry/FieldEntry 时为 1。
vol_sa: int = 0
sub_aug: rawptr[ast.Subscript] = rawptr[ast.Subscript](target)
if sub_aug is not None:
vol_sa = self.Gen.ExprGenAttr.base_pointee_is_volatile(
pool, trans, sub_aug.value)
cur_val: rawptr[llvmlite.Value] = llvmlite.build_load(
builder, target_ty, elem_ptr)
builder, target_ty, elem_ptr, vol_sa)
if cur_val is None:
# fail-fast: 元素当前值加载失败
self.Gen.TypeGenErr.fatal_error(target, "无法加载 subscript 元素当前值", "TypeError")
@@ -913,7 +929,7 @@ class AssignGenMixin:
if result is None:
# fail-fast: 类型对齐失败
self.Gen.TypeGenErr.fatal_error(target, "类型对齐失败", "TypeError")
llvmlite.build_store(builder, result, elem_ptr)
llvmlite.build_store(builder, result, elem_ptr, vol_sa)
return 0
if tk != ast.ASTKind.Name:
@@ -925,6 +941,17 @@ class AssignGenMixin:
if nm is None or nm.id is None:
return 0
# REPL 模式(M2/M3)会话变量增强赋值:读槽 → 二元运算 → 写槽 → 写结果区。
# 未登记的会话名返回 0(继续原路径 → 明确的"无法加载…当前值"报错)。
# 守卫 trans.ReplCtx:非 REPL 恒 None ⇒ 本分支整体不经过。
# 作用域用 repl_in_frag_entry_scope(M3):被提升的会话函数体内 `x += 1`
# 按普通局部变量路径处理(不写会话槽)。
if trans.ReplCtx is not None \
and self.Gen.VarGen.repl_in_frag_entry_scope(trans) != 0:
if self.Gen.VarGen.repl_try_augassign(
trans, nm.id, aug.op, aug.value, node) != 0:
return 0
pool: rawptr[memhub.MemBuddy] = trans.Pool
builder: rawptr[llvmlite.IRBuilder] = trans._cur_builder
mod: rawptr[llvmlite.LLVMModule] = trans.Module
@@ -936,6 +963,10 @@ class AssignGenMixin:
elif GenApi.is_nonlocal_name(trans, nm.id) != 0:
scope_type = 2
# t.CVolatile 变量:+= 的 load 与 store 均带 volatile 限定符
vol_aa: int = self.Gen.VarGen.var_is_volatile(
self.Gen.VarGen.lookup_var_entry(trans.SymTab, nm.id))
# 2. 加载当前值
cur_val: rawptr[llvmlite.Value] = None
target_alloca: rawptr[llvmlite.Value] = None
@@ -949,7 +980,7 @@ class AssignGenMixin:
# fail-fast: 变量类型信息缺失,禁止兜底 i32
self.Gen.TypeGenErr.fatal_error(target, "变量类型信息缺失", "TypeError")
target_ty = target_alloca.Ty.Pointee
cur_val = llvmlite.build_load(builder, target_ty, target_alloca)
cur_val = llvmlite.build_load(builder, target_ty, target_alloca, vol_aa)
elif scope_type == 2:
# nonlocal 变量(通过闭包 env)
cur_val = self.Gen.StmtGenDecl.load_nonlocal_var(trans, nm.id)
@@ -963,7 +994,7 @@ class AssignGenMixin:
# fail-fast: 变量类型信息缺失,禁止兜底 i32
self.Gen.TypeGenErr.fatal_error(target, "变量类型信息缺失", "TypeError")
target_ty = target_alloca.Ty.Pointee
cur_val = llvmlite.build_load(builder, target_ty, target_alloca)
cur_val = llvmlite.build_load(builder, target_ty, target_alloca, vol_aa)
if cur_val is None:
# fail-fast: 目标变量当前值加载失败
@@ -996,7 +1027,7 @@ class AssignGenMixin:
if scope_type == 1:
# global 变量
if target_alloca is not None:
llvmlite.build_store(builder, result, target_alloca)
llvmlite.build_store(builder, result, target_alloca, vol_aa)
elif scope_type == 2:
# nonlocal 变量
nl_ptr: rawptr[llvmlite.Value] = self.Gen.StmtGenDecl.get_nonlocal_var_ptr(
@@ -1006,7 +1037,7 @@ class AssignGenMixin:
else:
# 普通局部变量
if target_alloca is not None:
llvmlite.build_store(builder, result, target_alloca)
llvmlite.build_store(builder, result, target_alloca, vol_aa)
return 0
@@ -1176,6 +1207,15 @@ class AssignGenMixin:
if self.is_ctypedef_annotation(aa.annotation) != 0:
return 0
# REPL 模式(M2/M3):片段**入口**函数本体内的 AnnAssign 一律走会话槽(见
# translate_annassign 的会话分支),此处不得预建局部 alloca——否则局部名
# 会遮蔽同名会话变量(此后读 `x` 命中空局部槽,槽值丢失)。
# 被提升的会话函数体内不走该分支(那里按普通局部声明预建 alloca)。
# CDefine/CTypedef 两个分支在本次返回之前,其行为不变。
if trans.ReplCtx is not None \
and self.Gen.VarGen.repl_in_frag_entry_scope(trans) != 0:
return 0
# global/nonlocal 变量不需要局部 alloca
if GenApi.is_global_name(trans, nm.id) != 0:
return 0
@@ -1252,6 +1292,18 @@ class AssignGenMixin:
if self._is_ptr_element_annotation(annot) != 0:
self.Gen.VarGen.set_var_ptr_element(
trans.SymTab, nm.id)
# t.CVolatile:声明期即标记该变量的 load/store 带 volatile。
# 覆盖「无初值声明」`x: t.CVolatile | T`(有初值路径
# translate_annassign 亦会登记,幂等)。动机:volatile 禁优化器
# 把忙等循环的重复 load 提升/消除,不能依赖优化器保序。
if self.Gen.TypeGenErr.has_decorator_marker(
annot, "CVolatile", 0) != 0:
self.Gen.VarGen.set_var_volatile(trans.SymTab, nm.id)
# rawptr[t.CVolatile | T](volatile 指针指向物,等价 C 的
# volatile T*):解引用(p[i]/p.field/c.Deref(p))的 load/store
# 带 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
@@ -1401,6 +1453,20 @@ class AssignGenMixin:
self.Gen.TypeGenCType.register_ctypedef(pool_ct, nm.id, ctd_str)
return 0
# REPL 模式(M2/M3)会话变量:片段**入口**函数本体内的带注解声明/赋值
# (`x: int = 5` 定义并定型;`x: int` 仅定型)。注解先解析并定型条目,
# 定型后的槽类型再作为 RHS 的 target_ty(与普通 AnnAssign 的 P1-A 同口径)。
# 守卫 trans.ReplCtx:非 REPL 恒 None ⇒ 本分支整体不经过。
# 被提升的会话函数体内不走该分支(`x: int = 5` 是局部声明,Python 语义)。
if trans.ReplCtx is not None \
and self.Gen.VarGen.repl_in_frag_entry_scope(trans) != 0:
rp_e: rawptr[GenTypes.ReplVarEntry] = \
self.Gen.VarGen.repl_ensure_entry(trans, nm.id, aa)
if rp_e is not None:
self.Gen.VarGen.repl_try_annassign(
trans, nm.id, aa.annotation, aa.value, aa)
return 0
pool: rawptr[memhub.MemBuddy] = trans.Pool
builder: rawptr[llvmlite.IRBuilder] = trans._cur_builder
mod: rawptr[llvmlite.LLVMModule] = trans.Module
@@ -1434,7 +1500,12 @@ class AssignGenMixin:
callable_ret: rawptr[llvmlite.LLVMType] = self.Gen.TypeGenErr.try_extract_callable_ret(
pool, annot, trans._imported_modules, trans._from_imports, trans)
if callable_ret is not None:
self.Gen.CallGenLeafKit.register_func_ptr_sig(nm.id, callable_ret)
# 返回值:0=成功/同名同类型幂等命中;非 0 已在 register_func_ptr_sig
# 内部 fatal_error(表满/同名不同类型/表分配失败)终止编译。此处分支
# 仅为防御——报错通道异常时不带着未登记的签名继续翻译。
if self.Gen.CallGenLeafKit.register_func_ptr_sig(
pool, nm.id, callable_ret, aa) != 0:
return 0
# global 变量:写入模块作用域中的全局变量
if GenApi.is_global_name(trans, nm.id) != 0:
@@ -1447,12 +1518,12 @@ class AssignGenMixin:
aa_glob_ty: rawptr[llvmlite.LLVMType] = None
if mod_alloca.Ty is not None:
aa_glob_ty = mod_alloca.Ty.Pointee
# 供 c.Deref(i8*) 推断读取宽度 / 裸名泛型构造从注解推断类型实参:
# 与主分支(普通局部变量路径)同一口径——必须在翻译 RHS 之前写入
# CurAssignAnnot/CurAssignNode。四处(主分支 / 本 global 分支 /
# 下方 mod_glob 分支 / 下方 nonlocal 分支)必须同步,否则
# `x: int = c.Deref(p)` 的读取宽度与 `s: Stack[int] = Stack()`
# 的类型实参推断都会拿不到注解。
# 供裸名泛型构造从注解推断类型实参(消费者见 CallGenCtor.vp 的
# CurAssignNode 判定):与主分支(普通局部变量路径)同一口径——
# 必须在翻译 RHS 之前写入 CurAssignAnnot/CurAssignNode。四处
# (主分支 / 本 global 分支 / 下方 mod_glob 分支 / 下方 nonlocal
# 分支)必须同步,否则 `s: Stack[int] = Stack()` 的类型实参推断
# 会拿不到注解。
trans.CurAssignAnnot = aa.annotation
trans.CurAssignNode = aa.value
rhs_val: rawptr[llvmlite.Value] = self.Gen.ExprGen.translate_value(
@@ -1493,11 +1564,13 @@ class AssignGenMixin:
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)
llvmlite.build_store(builder, rhs_val, mod_alloca)
# t.CVolatile(global 声明)全局:初始化 store 带 volatile 限定符
vol_gaa: int = self.Gen.VarGen.var_is_volatile(
self.Gen.VarGen.lookup_var_entry(trans.SymTab, nm.id))
llvmlite.build_store(builder, rhs_val, mod_alloca, vol_gaa)
# 清除赋值上下文(防残留):与主分支清空时机同款。四处(主分支 /
# 本 global 分支 / 下方 mod_glob 分支 / 下方 nonlocal 分支)必须同步清除,
# 否则残留注解会让后续 while 条件/比较等处的 c.Deref 被误判读取宽度
# (i8* 按 i32* 读 4 字节),也会污染裸名泛型构造的类型实参推断
# 否则残留注解会污染后续语句里裸名泛型构造的类型实参推断
trans.CurAssignAnnot = None
trans.CurAssignNode = None
return 0
@@ -1515,12 +1588,11 @@ class AssignGenMixin:
nl_target_ty: rawptr[llvmlite.LLVMType] = None
if nl_ptr is not None and nl_ptr.Ty is not None:
nl_target_ty = nl_ptr.Ty.Pointee
# 供 c.Deref(i8*) 推断读取宽度 / 裸名泛型构造从注解推断类型实参:
# 与主分支、上方 global 分支、下方 mod_glob 分支同一口径——必须在
# 翻译 RHS 之前写入 CurAssignAnnot/CurAssignNode。四处(主分支 /
# global / mod_glob / 本 nonlocal 分支)必须同步,否则
# `nonlocal x: int = c.Deref(p)` 的读取宽度与
# `nonlocal s: Stack[int] = Stack()` 的类型实参推断都会拿不到注解。
# 供裸名泛型构造从注解推断类型实参:与主分支、上方 global 分支、
# 下方 mod_glob 分支同一口径——必须在翻译 RHS 之前写入
# CurAssignAnnot/CurAssignNode。四处(主分支 / global / mod_glob /
# 本 nonlocal 分支)必须同步,否则
# `nonlocal s: Stack[int] = Stack()` 的类型实参推断会拿不到注解。
trans.CurAssignAnnot = aa.annotation
trans.CurAssignNode = aa.value
rhs_val: rawptr[llvmlite.Value] = self.Gen.ExprGen.translate_value(
@@ -1542,9 +1614,8 @@ class AssignGenMixin:
builder, rhs_val, nl_target_ty, node)
llvmlite.build_store(builder, rhs_coerced, nl_ptr)
# 清除赋值上下文(防残留):与主分支清空时机同款。四处(主分支 /
# global / mod_glob / 本 nonlocal 分支)必须同步清除,否则残留注解会让
# 后续 while 条件/比较等处的 c.Deref 被误判读取宽度(i8* 按 i32* 读 4
# 字节),也会污染裸名泛型构造的类型实参推断
# global / mod_glob / 本 nonlocal 分支)必须同步清除,否则残留注解会
# 污染后续语句里裸名泛型构造的类型实参推断
trans.CurAssignAnnot = None
trans.CurAssignNode = None
return 0
@@ -1568,11 +1639,10 @@ class AssignGenMixin:
mg_val_ty: rawptr[llvmlite.LLVMType] = None
if mod_glob.Ty is not None:
mg_val_ty = mod_glob.Ty.Pointee
# 供 c.Deref(i8*) 推断读取宽度 / 裸名泛型构造从注解推断类型实参:
# 与主分支、上方 global 分支同一口径——翻译 RHS 前写入
# CurAssignAnnot/CurAssignNode。模块级 `s: Stack[int] = Stack()`
# 走的就是本分支,四处(主分支 / global / 本 mod_glob 分支 /
# 上方 nonlocal 分支)必须同步。
# 供裸名泛型构造从注解推断类型实参:与主分支、上方 global 分支
# 同一口径——翻译 RHS 前写入 CurAssignAnnot/CurAssignNode。
# 模块级 `s: Stack[int] = Stack()` 走的就是本分支,四处(主分支 /
# global / 本 mod_glob 分支 / 上方 nonlocal 分支)必须同步。
trans.CurAssignAnnot = aa.annotation
trans.CurAssignNode = aa.value
rhs_val_mg: rawptr[llvmlite.Value] = self.Gen.ExprGen.translate_value(
@@ -1613,11 +1683,13 @@ class AssignGenMixin:
target_ty_mg = mod_glob.Ty.Pointee
if target_ty_mg is not None:
rhs_val_mg = self.Gen.ExprGenBase.coerce_to_type(builder, rhs_val_mg, target_ty_mg, node)
llvmlite.build_store(builder, rhs_val_mg, mod_glob)
# t.CVolatile 模块级全局:初始化 store 带 volatile 限定符
vol_mga: int = self.Gen.VarGen.var_is_volatile(
self.Gen.VarGen.lookup_var_entry(trans.SymTab, nm.id))
llvmlite.build_store(builder, rhs_val_mg, mod_glob, vol_mga)
# 清除赋值上下文(防残留):与主分支清空时机同款。四处(主分支 /
# 上方 global 分支 / 本 mod_glob 分支 / 上方 nonlocal 分支)必须同步清除,
# 否则残留注解会让后续语句的 c.Deref 被误判读取宽度,并污染裸名泛型
# 构造的类型实参推断
# 否则残留注解会污染后续语句里裸名泛型构造的类型实参推断
trans.CurAssignAnnot = None
trans.CurAssignNode = None
return 0
@@ -1759,9 +1831,8 @@ class AssignGenMixin:
self._init_global_array_from_list(
builder, pool, mod, alloca, aa.value, trans)
else:
# 供 c.Deref(i8*) 推断读取宽度:v: int = c.Deref(p) → 读 i32
# CurAssignNode 记录 RHS 节点:translate_c_deref 据此判定 Deref
# 是否为赋值直接 RHS(对齐 TPC _infer_deref_target_type)。
# 供裸名泛型构造从注解推断类型实参(消费者见 CallGenCtor.vp 的
# CurAssignNode 判定:`s: Stack[int] = Stack()` 用注解里的 int 特化)。
# 注意:本主分支(普通局部变量)、上方 global 分支、mod_glob 分支、
# nonlocal 分支四处的 set/clear 必须同步(模块级 AnnAssign 走
# mod_glob;闭包内 AnnAssign 走 nonlocal)。
@@ -1854,7 +1925,19 @@ class AssignGenMixin:
target_ty = alloca.Ty.Pointee
if target_ty is not None:
rhs_val = self.Gen.ExprGenBase.coerce_to_type(builder, rhs_val, target_ty, node, trans)
llvmlite.build_store(builder, rhs_val, alloca)
# t.CVolatile 局部变量:标记 VarEntry 并让本次 store 带 volatile
vol_laa: int = 0
if aa.annotation is not None and nm.id is not None:
if self.Gen.TypeGenErr.has_decorator_marker(
aa.annotation, "CVolatile", 0) != 0:
self.Gen.VarGen.set_var_volatile(trans.SymTab, nm.id)
vol_laa = 1
# rawptr[t.CVolatile | T](volatile 指针指向物):登记
# PointeeVolatile;本 store 写的是指针变量自身,不影响。
if self.Gen.TypeGenErr.has_pointee_volatile_marker(
aa.annotation) != 0:
self.Gen.VarGen.set_var_pointee_volatile(trans.SymTab, nm.id)
llvmlite.build_store(builder, rhs_val, alloca, vol_laa)
# FakeDuck: 初始化影子结构体 __data__ 字段 = 初始字符串指针
# (对齐 TPC HandlesAnnAssign.py: 仅当有初值时写入 __data__)
if fd_str_ty is not None and fd_meta_alloca is not None:
@@ -1868,8 +1951,8 @@ class AssignGenMixin:
if fd_data_ptr is not None:
llvmlite.build_store(builder, rhs_val, fd_data_ptr)
# 清除赋值上下文(防残留):CurAssignAnnot/CurAssignNode 只在该
# AnnAssign 的 RHS 翻译期间有效,否则后续 while 条件/比较等处的
# c.Deref 会被残留注解误判读取宽度(i8* 被 bitcast i32* 读 4 字节)。
# AnnAssign 的 RHS 翻译期间有效,否则后续语句里裸名泛型构造的类型
# 实参推断会拿到残留注解。
# 四处(本主分支 / global / mod_glob / nonlocal)必须同步清除。
trans.CurAssignAnnot = None
trans.CurAssignNode = None
@@ -1911,7 +1994,7 @@ class AssignGenMixin:
VLogger.error("cast failed", "ASGN")
return 0
targets: rawptr[list[rawptr[ast.AST]]] = asgn.targets
targets: list[ast.AST*]* = asgn.targets
if targets is None:
VLogger.error("targets is None", "ASGN")
return 0
@@ -1923,7 +2006,7 @@ class AssignGenMixin:
# 翻译 RHS 值
# 无注解赋值无目标类型可推断,清空 CurAssignAnnot/CurAssignNode
# (防残留误判 c.Deref 宽度)
# (防残留污染裸名泛型构造的类型实参推断)
trans.CurAssignAnnot = None
trans.CurAssignNode = None
# P1-A(上下文位宽传播):单 Name 目标且其存储类型已知时,把存储类型作为
@@ -1943,6 +2026,16 @@ class AssignGenMixin:
trans.SymTab, asg_nm0.id)
if asg_slot is not None and asg_slot.Ty is not None:
asg_target_ty = asg_slot.Ty.Pointee
elif trans.ReplCtx is not None \
and self.Gen.VarGen.repl_in_frag_entry_scope(trans) != 0:
# REPL 模式(M2/M3):已定型的会话变量也用槽类型做 target_ty
# (与 P1-A 同口径;未定型则维持 None,由 RHS 类型定型)。
# 只在片段入口函数生效(被提升的会话函数体内同名赋值是局部变量)
asg_re: rawptr[GenTypes.ReplVarEntry] = \
self.Gen.VarGen.repl_find_entry(trans.ReplCtx, asg_nm0.id)
if asg_re is not None \
and asg_re.Kind != GenTypes.REPL_KIND_UNTYPED:
asg_target_ty = self.Gen.VarGen.repl_slot_ty(pool, asg_re)
rhs_val: rawptr[llvmlite.Value] = self.Gen.ExprGen.translate_value(
builder, pool, mod, asgn.value, None, 0, trans, asg_target_ty)
if rhs_val is None:
@@ -2008,7 +2101,7 @@ class AssignGenMixin:
# store + GEP 逐字段 load → store 到各 Name 目标。
if tk == ast.ASTKind.Tuple:
tgt_tup: rawptr[ast.Tuple] = rawptr[ast.Tuple](target)
tgt_elts: rawptr[list[rawptr[ast.AST]]] = tgt_tup.elts
tgt_elts: list[ast.AST*]* = tgt_tup.elts
# 守卫:rhs 不是 struct(tuple 打包)时跳过本 target
if tgt_elts is None or rhs_val is None or rhs_val.Ty is None:
continue
@@ -2084,7 +2177,14 @@ class AssignGenMixin:
if elem_ptr.Ty is not None and elem_ptr.Ty.Pointee is not None:
store_val = self.Gen.ExprGenBase.coerce_to_type(
builder, rhs_val, elem_ptr.Ty.Pointee, node, trans)
llvmlite.build_store(builder, store_val, elem_ptr)
# rawptr[t.CVolatile|T] 基址(volatile 指针指向物):p[i] = v
# 的 store 带 volatile。仅基址可解析到条目时为 1(否则文本不变)。
vol_sw: int = 0
sub_wr: rawptr[ast.Subscript] = rawptr[ast.Subscript](target)
if sub_wr is not None:
vol_sw = self.Gen.ExprGenAttr.base_pointee_is_volatile(
pool, trans, sub_wr.value)
llvmlite.build_store(builder, store_val, elem_ptr, vol_sw)
continue
# get_subscript_ptr 返回 None: 尝试 __setitem__ 运算符重载
# 适用于自定义类(如 hashtable[key]=val → hashtable.__setitem__(key, val))
@@ -2262,7 +2362,17 @@ class AssignGenMixin:
if field_ptr.Ty is not None and field_ptr.Ty.Pointee is not None:
store_val = self.Gen.ExprGenBase.coerce_to_type(
builder, rhs_val, field_ptr.Ty.Pointee, node, trans)
llvmlite.build_store(builder, store_val, field_ptr)
# t.CVolatile 字段:store 带 volatile 限定符
vol_fw: int = self.Gen.ExprGenAttr.attr_field_is_volatile(
pool, trans, target)
# rawptr[t.CVolatile|T] 基址(volatile 指针指向物):p.field = v
# 的 store 亦带 volatile(两来源取或)。
at_wr: rawptr[ast.Attribute] = rawptr[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
llvmlite.build_store(builder, store_val, field_ptr, vol_fw)
continue
# 守卫:field_ptr 为 None → 构造详细错误信息
attr_node: rawptr[ast.Attribute] = rawptr[ast.Attribute](target)
@@ -2333,7 +2443,12 @@ class AssignGenMixin:
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)
llvmlite.build_store(builder, rhs_val, mod_alloca)
# t.CVolatile 全局:store 带 volatile 限定符
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))
llvmlite.build_store(builder, rhs_val, mod_alloca, vol_gas)
continue
# nonlocal 变量:通过闭包 env 写入
@@ -2362,6 +2477,20 @@ class AssignGenMixin:
llvmlite.build_store(builder, rhs_coerced, nl_ptr)
continue
# REPL 模式(M2/M3)会话变量:片段**入口**函数本体内的 Name 目标即
# 会话变量(首次出现=定义:申请槽位 + 按 RHS 定型 + 写槽 + 写结果区)。
# 守卫 trans.ReplCtx(非 REPL 恒 None ⇒ 本分支整体不经过);
# repl_in_frag_entry_scope 排除被提升的会话函数体与片段内嵌套函数体
# (那些仍用普通局部 alloca:Python 局部语义 + 防形参同名串味)。
if trans.ReplCtx is not None \
and self.Gen.VarGen.repl_in_frag_entry_scope(trans) != 0:
rp_entry: rawptr[GenTypes.ReplVarEntry] = \
self.Gen.VarGen.repl_ensure_entry(trans, nm.id, target)
if rp_entry is not None:
self.Gen.VarGen.repl_try_assign(
trans, nm.id, rhs_val, asgn.value, node)
continue
# 拦截:函数内对模块级全局变量赋值但没 global 声明
# 否则静默创建局部变量遮蔽全局,全局保持 null 导致运行时崩溃
# (_mbuddy = mb 缺 global 声明即此类 bug:Int32 解引用 null 崩溃)
@@ -2387,7 +2516,10 @@ class AssignGenMixin:
if alloca.Ty is not None and alloca.Ty.Pointee is not None:
store_val = self.Gen.ExprGenBase.coerce_to_type(
builder, rhs_val, alloca.Ty.Pointee, node, trans)
llvmlite.build_store(builder, store_val, alloca)
# t.CVolatile 局部变量:store 带 volatile 限定符
vol_las: int = self.Gen.VarGen.var_is_volatile(
self.Gen.VarGen.lookup_var_entry(trans.SymTab, nm.id))
llvmlite.build_store(builder, store_val, alloca, vol_las)
# lambda 表达式赋给变量:注册为闭包变量名,
# 调用分派(is_closure_name)才走闭包间接调用路径;
# 否则 i8** alloca 被 _is_func_ptr_alloca 误判为