fix: for循环变量作用域 + 列表推导crash (两个已知bug)

Bug 1 - For循环变量作用域:
- AST_BLOCK 在 sema 中未创建子作用域 → 连续 for 循环用同名变量报"重复定义"
- 修复: sema AST_BLOCK 创建 block_scope (scope_new)
- 修复: codegen AST_BLOCK 保存/恢复 var_table 实现块级变量隔离

Bug 2 - 列表推导 >2元素 crash:
- sema 对 TYPE_ARRAY 标注跳过 init 分析 → 列表推导表达式未被semantize
- 导致 codegen 处 element_type=0, array_size=0 → LLVM alloca 崩溃
- 修复: 仅自引用 (= 变量名) 跳过分析,列表推导等正常分析
- 修复: cg_list_comp 使用 to_llvm_type(elem) 而非 type_info_to_llvm(full_array)

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
2026-06-07 18:03:25 +08:00
parent f5c0650a97
commit 6d5f8092a7
3 changed files with 15 additions and 6 deletions
+3 -2
View File
@@ -476,8 +476,9 @@ static AstDispatch cg_dispatch;
static LLVMValueRef cg_list_comp_impl(CgCtx* ctx, AstNode* node) {
TypeInfo* ti = &node->type;
LLVMTypeRef elem_ty = type_info_to_llvm(ctx, ti);
LLVMTypeRef arr_ty = LLVMArrayType(elem_ty, (unsigned)ti->array_size);
LLVMTypeRef elem_ty = to_llvm_type(ctx, ti->element_type);
LLVMTypeRef arr_ty = LLVMArrayType(elem_ty,
ti->array_size > 0 ? (unsigned)ti->array_size : 1);
LLVMValueRef result = LLVMBuildAlloca(ctx->builder, arr_ty, "list");
// 初始化为零
LLVMBuildStore(ctx->builder, LLVMConstNull(arr_ty), result);
+3 -1
View File
@@ -205,10 +205,12 @@ void codegen_stmt(CgCtx* ctx, AstNode* node) {
case AST_BLOCK: {
if (++codegen_depth > MAX_CODEGEN_DEPTH) { codegen_depth--; return; }
size_t block_mark = ctx->cleanup_count;
VarEntry* saved_table = ctx->var_table;
for (size_t i = 0; i < node->as.block.stmt_count; i++) {
codegen_stmt(ctx, node->as.block.stmts[i]);
}
cleanup_emit(ctx, block_mark); // 作用域退出: 释放块内 str 堆分配
ctx->var_table = saved_table;
cleanup_emit(ctx, block_mark);
codegen_depth--;
break;
}
+9 -3
View File
@@ -223,9 +223,10 @@ void analyze_node(AstNode* node, Scope* scope, ErrorList* errors, Arena* a) {
break;
}
case AST_BLOCK:
case AST_BLOCK: {
Scope* block_scope = scope_new(a, scope);
for (size_t i = 0; i < node->as.block.stmt_count; i++) {
analyze_node(node->as.block.stmts[i], scope, errors, a);
analyze_node(node->as.block.stmts[i], block_scope, errors, a);
}
// 表达式作为值: 块类型 = 最后一条产生值的语句类型
if (node->as.block.stmt_count > 0) {
@@ -245,6 +246,7 @@ void analyze_node(AstNode* node, Scope* scope, ErrorList* errors, Arena* a) {
}
}
break;
}
case AST_LET_STMT: {
TypeKind var_type;
@@ -253,9 +255,13 @@ void analyze_node(AstNode* node, Scope* scope, ErrorList* errors, Arena* a) {
if (node->as.let_stmt.has_type_annot) {
if (node->as.let_stmt.annot_type == TYPE_ARRAY) {
// 数组类型标注: 跳过 init 分析 (init 是自引用的占位符)
is_array_type = true;
var_type = TYPE_ARRAY;
// 分析 init — 除非是自引用 (如 let a: i64[3] = a;)
bool self_ref = (node->as.let_stmt.init->kind == AST_IDENT_EXPR
&& strcmp(node->as.let_stmt.init->as.ident.name,
node->as.let_stmt.name) == 0);
if (!self_ref) analyze_expr(node->as.let_stmt.init, scope, errors, a);
} else {
analyze_expr(node->as.let_stmt.init, scope, errors, a);
TypeKind inferred = node->as.let_stmt.init->type.kind;