fix(compiler): for x in ptr/数组 实现解引用为空终止(指针/浮点元素支持 + 数组长度上限+NULL终止)

This commit is contained in:
TermiNexus committed 2026-08-30 08:37:39 +08:00
1 parent d14415a4e6
commit 9b46e1da78
1 file changed
+55 -11
+55 -11
View File
@@ -1781,10 +1781,13 @@ class StmtGenCifMixin:
if arr_v2 is not None:
return self._handle_array_iter(trans, for_node, var_name, arr_v2)
# 元素类型必须是整数(用于 icmp eq 0 检查 null 终止符)
# 元素类型决定"空值"判定(用户语义:解引用后为空即终止):
# 整数 → ==0;指针(如 i8** 遍历的元素 i8*)→ ==null;浮点 → ==0.0
elem_bits: int = self.Gen.ExprGenBase.get_llvm_type_bits(elem_ty)
if elem_bits == 0:
self.Gen.TypeGenErr.fatal_error(for_node, "指针迭代: 元素类型不是整数,无法检查 null 终止符")
elem_is_ptr2: int = self.Gen.ExprGenBase.is_ptr_type(elem_ty)
elem_fbits2: int = self.Gen.ExprGenBase.get_llvm_float_bits(elem_ty)
if elem_bits == 0 and elem_is_ptr2 == 0 and elem_fbits2 == 0:
self.Gen.TypeGenErr.fatal_error(for_node, "指针迭代: 元素类型不是整数/指针/浮点,无法检查空终止符")
i32_ty: llvmlite.LLVMType | t.CPtr = llvmlite.Int32(pool)
@@ -1822,15 +1825,26 @@ class StmtGenCifMixin:
# 跳转到 cond
llvmlite.build_br(builder, cond_bb)
# cond 块: load index, GEP, load elem, icmp eq 0
# cond 块: load index, GEP, load elem, 空值判定(整数==0 / 指针==null / 浮点==0.0)
llvmlite.position_at_end(builder, cond_bb)
cur_idx: llvmlite.Value | t.CPtr = llvmlite.build_load(builder, i32_ty, idx_alloca)
elem_ptr: llvmlite.Value | t.CPtr = llvmlite.build_gep(
builder, elem_ty, ptr_val, cur_idx)
cur_elem: llvmlite.Value | t.CPtr = llvmlite.build_load(builder, elem_ty, elem_ptr)
null_val: llvmlite.Value | t.CPtr = llvmlite.const_int(pool, elem_bits, 0)
is_null: llvmlite.Value | t.CPtr = llvmlite.build_icmp(
builder, llvmlite.ICMP_EQ, cur_elem, null_val)
is_null: llvmlite.Value | t.CPtr = None
if elem_is_ptr2 != 0:
null_p: llvmlite.Value | t.CPtr = llvmlite.ConstNull(pool, elem_ty, "null")
if null_p is not None:
is_null = llvmlite.build_icmp(builder, llvmlite.ICMP_EQ, cur_elem, null_p)
elif elem_fbits2 != 0:
zero_f2: llvmlite.Value | t.CPtr = llvmlite.ConstFloat(pool, elem_ty, 0.0)
if zero_f2 is not None:
is_null = llvmlite.build_fcmp(builder, llvmlite.FCMP_OEQ, cur_elem, zero_f2)
else:
null_val: llvmlite.Value | t.CPtr = llvmlite.const_int(pool, elem_bits, 0)
is_null = llvmlite.build_icmp(builder, llvmlite.ICMP_EQ, cur_elem, null_val)
if is_null is None:
is_null = llvmlite.const_int(pool, 1, 0)
llvmlite.build_cond_br(builder, is_null, end_bb, body_bb)
# body 块: store elem to var, 翻译循环体
@@ -1947,6 +1961,9 @@ class StmtGenCifMixin:
viperlib.snprintf(name_buf, 32, "arr.cond.%d", cnt)
cond_bb: llvmlite.BasicBlock | t.CPtr = llvmlite.create_block(pool, func, name_buf)
viperlib.snprintf(name_buf, 32, "arr.elem.%d", cnt)
elem_check_bb: llvmlite.BasicBlock | t.CPtr = llvmlite.create_block(pool, func, name_buf)
viperlib.snprintf(name_buf, 32, "arr.body.%d", cnt)
body_bb: llvmlite.BasicBlock | t.CPtr = llvmlite.create_block(pool, func, name_buf)
@@ -1958,19 +1975,46 @@ class StmtGenCifMixin:
llvmlite.build_br(builder, cond_bb)
# cond 块: load index, icmp slt idx, elem_count
# cond 块: 先查越界(idx < count,越界时不能 GEP+load 判空)
llvmlite.position_at_end(builder, cond_bb)
cur_idx: llvmlite.Value | t.CPtr = llvmlite.build_load(builder, i32_ty, idx_alloca)
count_val: llvmlite.Value | t.CPtr = llvmlite.const_int32(pool, elem_count)
in_bounds: llvmlite.Value | t.CPtr = llvmlite.build_icmp(
builder, llvmlite.ICMP_SLT, cur_idx, count_val)
llvmlite.build_cond_br(builder, in_bounds, body_bb, end_bb)
llvmlite.build_cond_br(builder, in_bounds, elem_check_bb, end_bb)
# body 块: GEP(0, idx) → load elem → store to var → 循环体
llvmlite.position_at_end(builder, body_bb)
# elem_check 块: GEP(0, idx) → load elem → 空值判定(NULL 终止)
# 用户语义:整数/指针/浮点元素按"解引用为空"提前终止(如 split 尾哨兵
# 的 str 数组),同时保留长度上限防越界读;聚合元素只按长度迭代。
llvmlite.position_at_end(builder, elem_check_bb)
elem_ptr: llvmlite.Value | t.CPtr = llvmlite.build_gep_array(
builder, arr_ty, elem_ty, arr_tmp, cur_idx)
cur_elem: llvmlite.Value | t.CPtr = llvmlite.build_load(builder, elem_ty, elem_ptr)
elem_bits_a: int = self.Gen.ExprGenBase.get_llvm_type_bits(elem_ty)
elem_ptr_a: int = self.Gen.ExprGenBase.is_ptr_type(elem_ty)
elem_fbits_a: int = self.Gen.ExprGenBase.get_llvm_float_bits(elem_ty)
if elem_bits_a != 0 or elem_ptr_a != 0 or elem_fbits_a != 0:
is_empty_a: llvmlite.Value | t.CPtr = None
if elem_ptr_a != 0:
null_ap: llvmlite.Value | t.CPtr = llvmlite.ConstNull(pool, elem_ty, "null")
if null_ap is not None:
is_empty_a = llvmlite.build_icmp(builder, llvmlite.ICMP_EQ, cur_elem, null_ap)
elif elem_fbits_a != 0:
zero_af: llvmlite.Value | t.CPtr = llvmlite.ConstFloat(pool, elem_ty, 0.0)
if zero_af is not None:
is_empty_a = llvmlite.build_fcmp(builder, llvmlite.FCMP_OEQ, cur_elem, zero_af)
else:
zero_ai: llvmlite.Value | t.CPtr = llvmlite.const_int(pool, elem_bits_a, 0)
is_empty_a = llvmlite.build_icmp(builder, llvmlite.ICMP_EQ, cur_elem, zero_ai)
if is_empty_a is not None:
llvmlite.build_cond_br(builder, is_empty_a, end_bb, body_bb)
else:
llvmlite.build_br(builder, body_bb)
else:
llvmlite.build_br(builder, body_bb)
# body 块: store elem to var → 循环体
llvmlite.position_at_end(builder, body_bb)
llvmlite.build_store(builder, cur_elem, var_alloca)
old_break: llvmlite.BasicBlock | t.CPtr = trans._break_bb