diff --git a/docs/analysis/architecture-analysis-report-2026-06-06-0050.md b/docs/analysis/architecture-analysis-report-2026-06-06-0050.md new file mode 100644 index 0000000..a5668c8 --- /dev/null +++ b/docs/analysis/architecture-analysis-report-2026-06-06-0050.md @@ -0,0 +1,307 @@ +# L Language 架构分析报告 v0.6 + +> 日期: 2026-06-06 00:50 | 自动生成 | **重大代码变更** +> 上次代码基线: `a15cd9d` (2026-06-05 17:08 报告, v0.5) +> 当前 HEAD: `de91886` fix: CreateProcess 替代 system() 调 ld.lld,消除 shell 转义问题 +> **版本: v0.6** + +--- + +## 代码变更检查 + +``` +git log a15cd9d..HEAD → 20 新提交, 36 文件变更, +2483/-163 行 +``` + +自 v0.5 以来累计 20 个提交,涵盖 6 个新语言特性、语法差异化调整、链接器重构、安装包系统。 + +详细提交链: + +| # | 提交 | 类别 | +|---|------|------| +| 1-2 | CHANGELOG/语言参考文档 | 文档 | +| 3-4 | CLAUDE.md 语言设计哲学 | 文档 | +| 5 | 数组初始化 `= arr` 语法说明 | 文档 | +| 6 | match/sema 单元测试 (145→158) | 测试 | +| 7 | 数组支持 struct 元素类型 `Point[N]` | P1-5 修复 | +| 8 | 测试计数更新 145→158 + 23→24 | 测试 | +| 9 | for..in 范围语法 `..` → `to` | 语法差异化 | +| 10 | `let mut` → `var` | 语法差异化 | +| 11 | `[T; N]` → `T[N]` 后置语法 | 语法差异化 | +| 12 | `impl` → `extend` | 语法差异化 | +| 13 | 新增 i32/u64/char 类型 + 字符字面量 | 新功能 | +| 14 | guard 语句 | 新功能 | +| 15 | 命名参数 | 新功能 | +| 16 | 管道 `|>` + 字符串插值 `\(expr)` — P0 四特性收官 | 新功能 | +| 17 | README/CHANGELOG/语言参考更新至 v0.6 | 文档 | +| 18 | 链接器 gcc → clang + lld | 基础设施 | +| 19 | NSIS 安装包脚本 | 基础设施 | +| 20 | CreateProcess 替代 system() 调 ld.lld | 基础设施 | + +--- + +## 1. 当前架构快照 (v0.6) + +### 1.1 编译流水线 + +``` +源码(.l) → Lexer(词法) → Parser(语法) → Sema(语义) → Codegen(LLVM IR) → Target(obj) → clang+lld 链接(.exe) + 58 Token 25 AST 节点 类型标注 LLVMModuleRef .o 文件 优先 clang→fallback gcc +``` + +v0.5 的 GCC 链接器已被 clang + lld 替代(自动 fallback)。`system()` 调用被 `CreateProcess` 替代,消除 shell 转义问题。 + +### 1.2 模块清单与行数 + +| 模块 | 文件 | 行数 | vs v0.5 | 职责 | +|------|------|------|---------|------| +| include/ | l_lang.h | 55 | -2 | TypeKind (13), SourceLoc, 公共类型 | +| lexer/ | lexer.c + token.c (+ .h) | 168+53+47 = 268 | +11 | 手写状态机, 58 Token 类型 | +| ast/ | ast.c + ast.h | 221+152 = 373 | -31 | 25 种 AST 节点, 工厂函数 (arg_names 扩展) | +| parser/ | parser.c + parser.h | 851+10 = 861 | +23 | 递归下降+Pratt, guard/pipe/插值去糖 | +| sema/ | sema.c + symbol.c (+ .h) | 871+148+63 = 1082 | +77 | 类型推断, can_implicit_convert, 命名参数重排 | +| codegen/ | codegen.c + target.c (+ .h) | 764+30+15 = 809 | -55 | LLVM IR 生成, coerce_int 隐式转换 | +| driver/ | main.c + error.c (+ .h) | 205+46+18 = 269 | +58 | 流水线串联, CreateProcess 链接 | +| util/ | arena.c + arena.h | 39+13 = 52 | 不变 | Bump allocator (8MB) | +| **实现总计** | | **~3,769 行** | **+434 vs v0.5** | | + +> 注意: codegen.c 从 824 行收缩到 764 行(-7.3%),说明新加入的 pipe/guard/插值等功能去糖后不需要 codegen 层改动。 + +### 1.3 核心类型系统变化 + +| 枚举 | v0.5 | v0.6 | 变更 | +|------|------|------|------| +| TypeKind | 10 | **13** | +TYPE_I32, TYPE_U64, TYPE_CHAR | +| TokenKind | 50 | **58** | +TOK_VAR, TOK_GUARD, TOK_I32, TOK_U64, TOK_CHAR, TOK_CHAR_LIT, TOK_PIPE, TOK_TO; TOK_IMPL→TOK_EXTEND | +| AstNodeKind | 25 | **25** | 不变 — 所有新功能经去糖复用现有节点 | +| SymbolKind | 5 | **5** | 不变 | + +**TypeKind 完整列表**: TYPE_I32, TYPE_I64, TYPE_U64, TYPE_F64, TYPE_BOOL, TYPE_CHAR, TYPE_STR, TYPE_VOID, TYPE_STRUCT, TYPE_ENUM, TYPE_ARRAY, TYPE_UNKNOWN, TYPE_ERROR + +**Token 新增**: + +| 新增 Token | 用途 | +|------------|------| +| TOK_VAR | `var` 关键字 (替代 `let mut`) | +| TOK_GUARD | `guard` 关键字 | +| TOK_I32 / TOK_U64 / TOK_CHAR | 新基本类型 | +| TOK_CHAR_LIT | 字符字面量 `'"'"'A'"'"'` | +| TOK_PIPE | 管道运算符 `|>` | +| TOK_TO | 范围运算符 `to` (替代 `..`) | +| TOK_EXTEND | `extend` 关键字 (替代 `impl`) | + +**AST 结构体扩展**: +- `AST_CALL_EXPR.call` 新增 `arg_names` 字段 — 支持命名参数 +- `AST_METHOD_CALL.method_call` 新增 `arg_names` 字段 — 支持命名参数 +- `AST_LITERAL_EXPR.literal` 新增 `lit_type` + union 扩展 — 支持 char 字面量 + +**Symbol 扩展**: +- `Symbol.param_names` 新增 — 支持命名参数匹配 + +--- + +## 2. 功能完成度总览 + +### 2.1 P0 (v0.1-v0.4) — 100% + +全 12 项保持完成状态。 + +### 2.2 P1 (v0.5) — 100% + +全 6 项保持完成,P1-5 (数组 struct 元素类型) 在本轮修复。 + +### 2.3 P0-扩展 (v0.6 新增) — 100% + +| # | 功能 | 去糖策略 | 灵感来源 | 影响层 | +|---|------|---------|---------|--------| +| 13 | `i32` / `u64` / `char` 类型 | TypeKind 扩展 + can_implicit_convert | Rust/ML | lexer/ast/sema/codegen | +| 14 | `guard x >= 0 else { return -1; }` | parser 去糖为 if-else | Swift | parser | +| 15 | `draw_rect(width: 10, height: 20)` | sema 重排序为位置参数 | Python/Swift | parser/sema | +| 16 | `10 |> double() |> add(5)` | parser 去糖为 `add(double(10), 5)` | F#/Elixir | parser | +| 17 | `"Hello, \(name)!"` | lexer+parser 去糖为 `str+str` | Swift/Rust | lexer/parser | +| 18 | `extend` 方法块 | 即原 `impl` mangle,改名 | 差异化 | lexer | + +### 2.4 语法差异化 (L 语言独有) + +| L 语法 | Rust 对应 | 理由 | +|--------|----------|------| +| `var` | `let mut` | 更简洁,2 字符 vs 6 字符 | +| `T[N]` | `[T; N]` | C 风格直觉,减少括号 | +| `to` | `..` | 明确语义 (省略终值 `to` vs 闭区间 `..=`) | +| `extend` | `impl` | 避免 Rust 预留词 | +| `guard` | 无直接对应 | Swift 灵感,前条件守卫 | +| `|>` | 无直接对应 | F#/Elixir 灵感,函数组合 | +| `\(expr)` | 无直接对应 | Swift 风格字符串插值 | + +--- + +## 3. Rust 对标缺失清单 + +### 3.1 已完成 + +| 特性 | 状态 | 版本 | +|------|------|------| +| 默认不可变 (let) / 可变 (var) | ✅ | v0.1-v0.6 | +| 复合赋值 | ✅ | v0.2 | +| for + range | ✅ | v0.3 | +| struct 具名域 | ✅ | v0.4 | +| impl/extend 方法 | ✅ | v0.5 | +| enum + match | ✅ | v0.5 | +| 类型别名 | ✅ | v0.5 | +| 数组固定大小 | ✅ | v0.5 | +| 数组 struct 元素类型 | ✅ | v0.6 | +| RAII 自动释放 | ✅ | v0.4 | +| 命名参数 | ✅ | v0.6 | +| 管道运算符 | ✅ | v0.6 | +| 字符串插值 | ✅ | v0.6 | +| guard 守卫语句 | ✅ | v0.6 | +| i32/u64/char 多种整数类型 | ✅ | v0.6 | + +### 3.2 缺失 (P2 — 中期, 按难度排序) + +| # | 功能 | 预计工时 | 优先级 | Rust 对应 | 说明 | +|---|------|---------|--------|----------|------| +| 19 | 表达式作为值 (块返回最后表达式) | 2-3 天 | **高** | 块是表达式 | sema/codegen 需处理块类型推断, 当前 block→void | +| 20 | 枚举关联数据 (ADT) | 2-3 天 | **高** | `enum Option` | 当前仅简单常量枚举, 需 payload + 模式匹配扩展 | +| 21 | `if let` / `while let` | 1 天 | 中 | `if let Some(x) = opt` | 基于枚举关联数据 | +| 22 | 模块系统 `mod` + `use` | 3-5 天 | **高** | `mod`, `use`, `pub` | 多文件编译, 符号可见性 | +| 23 | 泛型 (单态化) | 5-7 天 | 中 | `fn(x: T)` | sema 类型参数收集, codegen 复制 | +| 24 | trait / 接口 | 5-7 天 | 中 | `trait`, `impl Trait` | vtable 或单态化分派 | +| 25 | 引用/切片 | 7-10 天 | 中 | `&T`, `&[T]` | 需指针层次, 生命周期标注 | + +### 3.3 缺失 (P3 — 长期) + +| # | 功能 | 预计工时 | Rust 对应 | +|---|------|---------|----------| +| 26 | 所有权 / 借用检查 | 2-4 周 | borrow checker | +| 27 | 闭包 (lambda) | 3-5 天 | `|x| x+1` | +| 28 | 自举 (L 编译 L) | 4-6 周 | — | +| 29 | 标准库 (prelude) | 4-6 周 | std | + +--- + +## 4. 测试覆盖 + +| 测试文件 | 测试函数 | vs v0.5 | 覆盖范围 | +|---------|----------|---------|---------| +| test_lexer.c | 3 | 不变 | token 识别 | +| test_parser.c | 5 | 不变 | 语法解析基本路径 | +| test_sema.c | 24 | +3 | i32/u64/char 转换, named args, guard, 管道, 插值 | +| test_codegen.c | 10 | +1 | 隐式转换, 命名参数, i32/u64/char codegen | +| **单元合计** | **42** | **+4** | | +| 集成 .l 程序 | **29** | **+6** | 24_array_struct, 25_new_types, 26_guard, 27_named_args, 28_pipe, 29_interp | +| **测试总计** | **71** | **+10** | | + +新集成测试覆盖: +- `24_array_struct.l` — struct 数组 (P1-5 修复验证) +- `25_new_types.l` — i32/u64/char 类型 + 隐式转换 +- `26_guard.l` — guard 守卫语句 +- `27_named_args.l` — 命名参数任意顺序 +- `28_pipe.l` — 管道 |> 函数组合 +- `29_interp.l` — 字符串插值 + +--- + +## 5. 代码审查 — 风险区域 + +### 5.1 去糖策略的评价 + +v0.6 延续了 v0.5 的去糖哲学: pipe/guard/插值/named_args 全在 parser 层去糖为现有 AST 节点。优势是 sema/codegen 改动极小(codegen 甚至缩水 60 行),但代价是: + +1. **parser.c 进一步膨胀**: 825→851 行,去糖逻辑散落在 `parse_expr` 和 `parse_statement` 之间 +2. **错误消息质量下降**: 去糖后生成的 AST 丢失原始语法信息,错误报告指向去糖后的 if-else 而非原始 `guard` 语句 +3. **调试/IR 可读性**: 用户写 `10 |> double()` 但 IR 中看到的是 `double(10)`,不利于学习 + +**建议**: v0.7 考虑将 guard/match/named_args/pipe 去糖抽取为独立 desugar pass,在 parser 生成 AST 之后、sema 之前运行。这既能保持"去糖"优势,又能改善错误消息质量。 + +### 5.2 sema.c 持续膨胀 + +sema.c 从 809→871 行 (v0.5→v0.6),analyze_expr 仍然是单函数怪兽 (~380 行)。新增的 `can_implicit_convert()` 是好的模块化进步,但类型检查逻辑仍需拆分。 + +### 5.3 TypeKind 耦合未改善 + +TypeKind 从 10 增加到 13,修改范围涉及 7+ 文件。加新类型仍是全局搜索替换作业。 + +### 5.4 链接器变更风险 + +gcc→clang+lld 变更是正确方向,但 fallback 链 (clang→gcc) 增加了 CI 配置复杂度。自包含安装包 (39MB) 和 NSIS 安装包 (62MB) 是两个并行方案,后续需统一。 + +--- + +## 6. 技术债务更新 + +### 6.1 已解决 + +| # | 原问题 | 解决提交 | +|---|--------|---------| +| 5 | 数组 struct 元素类型 `[Point; N]` 未实现 | `a45f7d8` | +| 6 | match/sema 无独立单元测试 | `beac40f` | +| 9 | CHANGELOG 未更新 v0.4/v0.5 | `5a0bf60` + `2baf762` | + +### 6.2 当前债务 (优先级排序) + +| # | 问题 | 严重度 | 现状 | 备注 | +|---|------|--------|------|------| +| 1 | analyze_expr 膨胀 (~380 行) | **高** | 未修复, 反而增加 | sema.c 871 行, +62 vs v0.5 | +| 2 | parser.c 单文件 851 行 | **高** | 未修复, +26 行 | 去糖逻辑散落 | +| 3 | 去糖无独立 pass, 错误消息受损 | **中** | 需求显现 | v0.6 新暴露 | +| 4 | codegen.c 824→764 行, 但仍有优化空间 | 中 | 改善中 | struct/element_type 逻辑可精简 | +| 5 | TypeKind 耦合 (改 7+ 文件) | 低 | 未修复 | 13 种类型, 加新类型成本递增 | +| 6 | AST Visitor 缺失 | 低 | 未修复 | 加节点需改 4 个 switch | +| 7 | LLVM 22 无 mem2reg C API | 低 | 平台限制 | IR 含冗余 alloca/store/load | + +--- + +## 7. 基础设施变更 + +| 组件 | v0.5 | v0.6 | 影响 | +|------|------|------|------| +| 链接器 | GCC (外部依赖) | clang + lld (优先), fallback gcc | 用户需安装 LLVM 或将 ld.lld 打包 | +| 进程启动 | `system()` | `CreateProcess` | 消除 shell 注入/转义风险 | +| 发布 | 无 | NSIS 安装包 (62MB) / 自包含包 (39MB) | 零依赖部署 | +| 文档 | README + CHANGELOG | +语言参考手册 (643 行) + CLAUDE.md | 开发者友好 | + +--- + +## 8. 与 PRD 对照 + +PRD v0.1 设定了"学习编译器全流程"的短期目标。对照 PRD 中的非目标列表: + +| PRD 非目标 (v0.1 不做) | 当前状态 | +|------------------------|---------| +| 字符串类型 | ✅ v0.2 实现 `str` | +| 数组/切片/结构体 | ✅ v0.4-v0.5 实现 struct/array | +| 模块系统 | ❌ 仍缺失 | +| 泛型/trait | ❌ 仍缺失 | +| 模式匹配 | ✅ v0.5 match (简化) | +| 标准库 | ❌ 仍缺失 | +| 垃圾回收 | ❌ 不做 (采用 RAII) | + +v0.6 已大幅超出 v0.1 PRD 范围。建议更新 PRD 到 v0.6 以反映当前功能边界。 + +--- + +## 9. 推荐开发路线图 + +### v0.7 (短期, 2-3 天) + +1. **独立 desugar pass** — 将 guard/pipe/named_args 去糖从 parser 移到独立 pass,改善错误消息和 parser 复杂度 +2. **analyze_expr 拆分** — 按节点类型拆分为 4-6 个辅助函数 +3. **枚举关联数据 (ADT)** — `enum Option { Some(i64), None }`,打开类型安全编程新范式 +4. **表达式作为值** — `let x = if a { 1 } else { 2 }`,函数体最后表达式即返回值 + +### v0.8 (中期, 1 周) + +1. **模块系统** — `mod` + `use` + `pub`,多文件编译 +2. `if let` / `while let` — 简化 ADT 匹配 +3. 标准库起步: `io`, `convert`, `vec` + +### v0.9 (中期, 1-2 周) + +1. 泛型 (单态化) +2. trait / 接口 +3. AST Visitor 宏驱动 + +--- + +*本报告由 Codex 自动生成于 2026-06-06 00:50。自上次代码基线 `a15cd9d` (v0.5) 以来共 20 个提交, +2483/-163 行。P0 扩展 6 个新特性全部实现, 语法完成差异化定型, 链接器/进程启动基础设施升级。v0.7 应聚焦 parser 结构优化和枚举 ADT。* diff --git a/src/sema/sema.c b/src/sema/sema.c index 26da16e..f004634 100644 --- a/src/sema/sema.c +++ b/src/sema/sema.c @@ -52,442 +52,377 @@ static bool is_comparable(TypeKind a, TypeKind b) { // === 向前声明 === static void analyze_node(AstNode* node, Scope* scope, ErrorList* errors, Arena* a); -// === 检查表达式 === -static void analyze_expr(AstNode* node, Scope* scope, ErrorList* errors, Arena* a) { - switch (node->kind) { - case AST_LITERAL_EXPR: - break; // 类型已在创建时设置 +// === 表达式类型检查辅助函数 === +static void analyze_expr(AstNode* node, Scope* scope, ErrorList* errors, Arena* a); - case AST_IDENT_EXPR: { - Symbol* sym = scope_lookup(scope, node->as.ident.name); - if (!sym) { +static void analyze_ident_expr(AstNode* node, Scope* scope, ErrorList* errors, Arena* a) { + (void)a; + Symbol* sym = scope_lookup(scope, node->as.ident.name); + if (!sym) { + error_add(errors, "", node->loc.line, node->loc.col, + "未定义的变量 '%s'", node->as.ident.name); + node->type.kind = TYPE_ERROR; + } else if (sym->is_type_alias) { + error_add(errors, "", node->loc.line, node->loc.col, + "'%s' 是类型别名,不能作为表达式使用", node->as.ident.name); + node->type.kind = TYPE_ERROR; + } else if (sym->kind == SYM_FUNCTION) { + error_add(errors, "", node->loc.line, node->loc.col, + "'%s' 是函数,不能作为表达式使用", node->as.ident.name); + node->type.kind = TYPE_ERROR; + } else { + node->type.kind = sym->type; + if (sym->type == TYPE_STRUCT && sym->struct_type_name) + node->type.struct_name = sym->struct_type_name; + if (sym->type == TYPE_ARRAY) { + node->type.element_type = sym->array_element_type; + node->type.element_struct_name = sym->array_element_struct_name; + node->type.array_size = sym->array_size; + } + } +} + +static void analyze_unary_expr(AstNode* node, Scope* scope, ErrorList* errors, Arena* a) { + analyze_expr(node->as.unary.operand, scope, errors, a); + TypeKind inner = node->as.unary.operand->type.kind; + if (node->as.unary.op == OP_NEG) { + if (!is_numeric(inner)) { error_add(errors, "", node->loc.line, node->loc.col, - "未定义的变量 '%s'", node->as.ident.name); - node->type.kind = TYPE_ERROR; - } else if (sym->is_type_alias) { - error_add(errors, "", node->loc.line, node->loc.col, - "'%s' 是类型别名,不能作为表达式使用", node->as.ident.name); - node->type.kind = TYPE_ERROR; - } else if (sym->kind == SYM_FUNCTION) { - error_add(errors, "", node->loc.line, node->loc.col, - "'%s' 是函数,不能作为表达式使用", node->as.ident.name); + "一元 '-' 只能用于数值类型"); node->type.kind = TYPE_ERROR; } else { - node->type.kind = sym->type; - if (sym->type == TYPE_STRUCT && sym->struct_type_name) { - node->type.struct_name = sym->struct_type_name; - } - if (sym->type == TYPE_ARRAY) { - node->type.element_type = sym->array_element_type; - node->type.element_struct_name = sym->array_element_struct_name; - node->type.array_size = sym->array_size; - } + node->type.kind = inner; } - break; - } - - case AST_UNARY_EXPR: { - analyze_expr(node->as.unary.operand, scope, errors, a); - TypeKind inner = node->as.unary.operand->type.kind; - if (node->as.unary.op == OP_NEG) { - if (!is_numeric(inner)) { - error_add(errors, "", node->loc.line, node->loc.col, - "一元 '-' 只能用于数值类型"); - node->type.kind = TYPE_ERROR; - } else { - node->type.kind = inner; - } - } else { // OP_NOT - if (inner != TYPE_BOOL) { - error_add(errors, "", node->loc.line, node->loc.col, - "'!' 只能用于布尔类型,得到 '%s'", type_name(inner)); - node->type.kind = TYPE_ERROR; - } else { - node->type.kind = TYPE_BOOL; - } - } - break; - } - - case AST_BINARY_EXPR: { - analyze_expr(node->as.binary.left, scope, errors, a); - analyze_expr(node->as.binary.right, scope, errors, a); - TypeKind l = node->as.binary.left->type.kind; - TypeKind r = node->as.binary.right->type.kind; - if (l == TYPE_ERROR || r == TYPE_ERROR) { node->type.kind = TYPE_ERROR; break; } - - switch (node->as.binary.op) { - case OP_ADD: - if (l == TYPE_STR || r == TYPE_STR) { - // 字符串拼接:两边都必须是 str 类型 - if (l != TYPE_STR || r != TYPE_STR) { - error_add(errors, "", node->loc.line, node->loc.col, - "字符串拼接需要两边都是 str 类型,得到 '%s' + '%s'", - type_name(l), type_name(r)); - node->type.kind = TYPE_ERROR; - } else { - node->type.kind = TYPE_STR; - } - } else if (!is_numeric(l) || !is_numeric(r)) { - error_add(errors, "", node->loc.line, node->loc.col, - "算术运算需要数值类型"); - node->type.kind = TYPE_ERROR; - } else { - node->type.kind = promote(l, r); - } - break; - case OP_SUB: case OP_MUL: case OP_DIV: case OP_MOD: - if (!is_numeric(l) || !is_numeric(r)) { - error_add(errors, "", node->loc.line, node->loc.col, - "算术运算需要数值类型"); - node->type.kind = TYPE_ERROR; - } else { - node->type.kind = promote(l, r); - } - break; - case OP_EQ: case OP_NE: case OP_LT: case OP_GT: case OP_LE: case OP_GE: - if (!is_comparable(l, r)) { - error_add(errors, "", node->loc.line, node->loc.col, - "类型 '%s' 和 '%s' 无法比较", type_name(l), type_name(r)); - node->type.kind = TYPE_ERROR; - } else { - node->type.kind = TYPE_BOOL; - } - break; - case OP_AND: case OP_OR: - if (l != TYPE_BOOL || r != TYPE_BOOL) { - error_add(errors, "", node->loc.line, node->loc.col, - "逻辑运算需要布尔类型"); - node->type.kind = TYPE_ERROR; - } else { - node->type.kind = TYPE_BOOL; - } - break; - default: break; - } - break; - } - - case AST_CALL_EXPR: { - Symbol* sym = scope_lookup(scope, node->as.call.name); - if (!sym || sym->kind != SYM_FUNCTION) { + } else { // OP_NOT + if (inner != TYPE_BOOL) { error_add(errors, "", node->loc.line, node->loc.col, - "未定义的函数 '%s'", node->as.call.name); + "'!' 只能用于布尔类型,得到 '%s'", type_name(inner)); node->type.kind = TYPE_ERROR; - // 即使函数未定义,也要分析参数表达式(它们可能有更多错误) - for (size_t i = 0; i < node->as.call.arg_count; i++) { - analyze_expr(node->as.call.args[i], scope, errors, a); - } - break; + } else { + node->type.kind = TYPE_BOOL; } - if (node->as.call.arg_count != sym->param_count) { + } +} + +static void analyze_binary_expr(AstNode* node, Scope* scope, ErrorList* errors, Arena* a) { + analyze_expr(node->as.binary.left, scope, errors, a); + analyze_expr(node->as.binary.right, scope, errors, a); + TypeKind l = node->as.binary.left->type.kind; + TypeKind r = node->as.binary.right->type.kind; + if (l == TYPE_ERROR || r == TYPE_ERROR) { node->type.kind = TYPE_ERROR; return; } + + switch (node->as.binary.op) { + case OP_ADD: + if (l == TYPE_STR || r == TYPE_STR) { + if (l != TYPE_STR || r != TYPE_STR) { + error_add(errors, "", node->loc.line, node->loc.col, + "字符串拼接需要两边都是 str 类型,得到 '%s' + '%s'", + type_name(l), type_name(r)); + node->type.kind = TYPE_ERROR; + } else { + node->type.kind = TYPE_STR; + } + } else if (!is_numeric(l) || !is_numeric(r)) { + error_add(errors, "", node->loc.line, node->loc.col, "算术运算需要数值类型"); + node->type.kind = TYPE_ERROR; + } else { + node->type.kind = promote(l, r); + } + break; + case OP_SUB: case OP_MUL: case OP_DIV: case OP_MOD: + if (!is_numeric(l) || !is_numeric(r)) { + error_add(errors, "", node->loc.line, node->loc.col, "算术运算需要数值类型"); + node->type.kind = TYPE_ERROR; + } else { + node->type.kind = promote(l, r); + } + break; + case OP_EQ: case OP_NE: case OP_LT: case OP_GT: case OP_LE: case OP_GE: + if (!is_comparable(l, r)) { error_add(errors, "", node->loc.line, node->loc.col, - "函数 '%s' 需要 %zu 个参数,但提供了 %zu 个", - node->as.call.name, sym->param_count, node->as.call.arg_count); + "类型 '%s' 和 '%s' 无法比较", type_name(l), type_name(r)); node->type.kind = TYPE_ERROR; - // 即使参数数量不匹配,也分析已有的参数 - for (size_t i = 0; i < node->as.call.arg_count; i++) { - analyze_expr(node->as.call.args[i], scope, errors, a); - } - break; + } else { + node->type.kind = TYPE_BOOL; } - // 命名参数重排序: 将命名 arg 按参数名映射到正确位置 - if (node->as.call.arg_names) { - AstNode* reordered[16] = {0}; - for (size_t i = 0; i < node->as.call.arg_count; i++) { - if (node->as.call.arg_names[i]) { - // 查找参数名匹配 - bool found = false; - for (size_t j = 0; j < sym->param_count; j++) { - if (sym->param_names && sym->param_names[j] && - strcmp(node->as.call.arg_names[i], sym->param_names[j]) == 0) { - reordered[j] = node->as.call.args[i]; - found = true; break; - } - } - if (!found) { - error_add(errors, "", node->loc.line, node->loc.col, - "函数 '%s' 没有名为 '%s' 的参数", - node->as.call.name, node->as.call.arg_names[i]); - node->type.kind = TYPE_ERROR; return; - } - } else { - // 位置参数保持原位 - reordered[i] = node->as.call.args[i]; + break; + case OP_AND: case OP_OR: + if (l != TYPE_BOOL || r != TYPE_BOOL) { + error_add(errors, "", node->loc.line, node->loc.col, "逻辑运算需要布尔类型"); + node->type.kind = TYPE_ERROR; + } else { + node->type.kind = TYPE_BOOL; + } + break; + default: break; + } +} + +// 参数类型匹配检查(CALL_EXPR 和 METHOD_CALL 共用) +static void check_arg_type(AstNode* arg, TypeKind expected, const char* expected_sname, + size_t idx, AstNode* call_node, Scope* scope, + ErrorList* errors, Arena* a) { + (void)scope; (void)a; + TypeKind actual = arg->type.kind; + if (actual == TYPE_ERROR) return; + if (expected == TYPE_STRUCT) { + const char* actual_name = arg->type.struct_name; + if (actual != TYPE_STRUCT || !actual_name || !expected_sname || + strcmp(actual_name, expected_sname) != 0) { + error_add(errors, "", call_node->loc.line, call_node->loc.col, + "参数 %zu 类型不匹配: 期望 '%s',得到 '%s'", + idx + 1, expected_sname ? expected_sname : "struct", + actual_name ? actual_name : type_name(actual)); + } + return; + } + if (actual == expected) return; + if (expected == TYPE_I64 && actual == TYPE_ENUM) return; + if (can_implicit_convert(actual, expected)) return; + if (actual == TYPE_I64 && arg->kind == AST_LITERAL_EXPR + && (expected == TYPE_I32 || expected == TYPE_U64 || expected == TYPE_CHAR)) return; + error_add(errors, "", call_node->loc.line, call_node->loc.col, + "参数 %zu 类型不匹配: 期望 '%s',得到 '%s'", + idx + 1, type_name(expected), type_name(actual)); +} + +// 命名参数重排序(CALL_EXPR 和 METHOD_CALL 共用) +static bool reorder_named_args(AstNode* node, Symbol* sym, int param_offset, + ErrorList* errors, const char* call_name) { + AstNode** args = node->as.call.args; + const char** arg_names = node->as.call.arg_names; + size_t arg_count = node->as.call.arg_count; + if (!arg_names) return true; + AstNode* reordered[16] = {0}; + for (size_t i = 0; i < arg_count; i++) { + if (arg_names[i]) { + bool found = false; + for (size_t j = param_offset; j < sym->param_count; j++) { + if (sym->param_names && sym->param_names[j] && + strcmp(arg_names[i], sym->param_names[j]) == 0) { + reordered[j - param_offset] = args[i]; + found = true; break; } } - // 填充未指定的命名参数(用 NULL 跳过,后续检查会报错) - memcpy(node->as.call.args, reordered, node->as.call.arg_count * sizeof(AstNode*)); + if (!found) { + error_add(errors, "", node->loc.line, node->loc.col, + "'%s' 没有名为 '%s' 的参数", call_name, arg_names[i]); + return false; + } + } else { + reordered[i] = args[i]; } - for (size_t i = 0; i < node->as.call.arg_count; i++) { + } + memcpy(args, reordered, arg_count * sizeof(AstNode*)); + return true; +} + +static void analyze_call_expr(AstNode* node, Scope* scope, ErrorList* errors, Arena* a) { + Symbol* sym = scope_lookup(scope, node->as.call.name); + if (!sym || sym->kind != SYM_FUNCTION) { + error_add(errors, "", node->loc.line, node->loc.col, + "未定义的函数 '%s'", node->as.call.name); + node->type.kind = TYPE_ERROR; + for (size_t i = 0; i < node->as.call.arg_count; i++) analyze_expr(node->as.call.args[i], scope, errors, a); - TypeKind actual = node->as.call.args[i]->type.kind; - TypeKind expected = sym->param_types[i]; - if (actual != TYPE_ERROR) { - if (expected == TYPE_STRUCT) { - // 结构体参数:比较具体类型名 - const char* actual_name = node->as.call.args[i]->type.struct_name; - const char* expected_name = sym->param_struct_names ? sym->param_struct_names[i] : NULL; - if (actual != TYPE_STRUCT || !actual_name || !expected_name || - strcmp(actual_name, expected_name) != 0) { - error_add(errors, "", node->loc.line, node->loc.col, - "参数 %zu 类型不匹配: 期望 '%s',得到 '%s'", - i + 1, - expected_name ? expected_name : "struct", - actual_name ? actual_name : type_name(actual)); - } - } else if (actual != expected && - !(expected == TYPE_I64 && actual == TYPE_ENUM) && - !can_implicit_convert(actual, expected) && - !(actual == TYPE_I64 && node->as.call.args[i]->kind == AST_LITERAL_EXPR - && (expected == TYPE_I32 || expected == TYPE_U64 || expected == TYPE_CHAR))) { - error_add(errors, "", node->loc.line, node->loc.col, - "参数 %zu 类型不匹配: 期望 '%s',得到 '%s'", - i + 1, type_name(expected), type_name(actual)); - } - } - } - node->type.kind = sym->return_type; - if (sym->return_type == TYPE_STRUCT && sym->return_struct_type_name) { - node->type.struct_name = sym->return_struct_type_name; - } - break; + return; } + if (node->as.call.arg_count != sym->param_count) { + error_add(errors, "", node->loc.line, node->loc.col, + "函数 '%s' 需要 %zu 个参数,但提供了 %zu 个", + node->as.call.name, sym->param_count, node->as.call.arg_count); + node->type.kind = TYPE_ERROR; + for (size_t i = 0; i < node->as.call.arg_count; i++) + analyze_expr(node->as.call.args[i], scope, errors, a); + return; + } + if (!reorder_named_args(node, sym, 0, errors, node->as.call.name)) { + node->type.kind = TYPE_ERROR; return; + } + for (size_t i = 0; i < node->as.call.arg_count; i++) { + analyze_expr(node->as.call.args[i], scope, errors, a); + check_arg_type(node->as.call.args[i], sym->param_types[i], + sym->param_struct_names ? sym->param_struct_names[i] : NULL, + i, node, scope, errors, a); + } + node->type.kind = sym->return_type; + if (sym->return_type == TYPE_STRUCT && sym->return_struct_type_name) + node->type.struct_name = sym->return_struct_type_name; +} - case AST_FIELD_ACCESS: { - analyze_expr(node->as.field_access.object, scope, errors, a); - AstNode* obj = node->as.field_access.object; - if (obj->type.kind == TYPE_ERROR) { - node->type.kind = TYPE_ERROR; - break; +static void analyze_field_access(AstNode* node, Scope* scope, ErrorList* errors, Arena* a) { + analyze_expr(node->as.field_access.object, scope, errors, a); + AstNode* obj = node->as.field_access.object; + if (obj->type.kind == TYPE_ERROR) { node->type.kind = TYPE_ERROR; return; } + if (obj->type.kind != TYPE_STRUCT) { + error_add(errors, "", node->loc.line, node->loc.col, + "类型 '%s' 不是结构体,不能访问字段 '%s'", + type_name(obj->type.kind), node->as.field_access.field); + node->type.kind = TYPE_ERROR; return; + } + const char* struct_name = obj->type.struct_name; + if (!struct_name) { + error_add(errors, "", node->loc.line, node->loc.col, "无法确定结构体类型"); + node->type.kind = TYPE_ERROR; return; + } + Symbol* struct_sym = scope_lookup_struct(scope, struct_name); + if (!struct_sym) { + error_add(errors, "", node->loc.line, node->loc.col, + "未定义的结构体 '%s'", struct_name); + node->type.kind = TYPE_ERROR; return; + } + int fi = scope_struct_field_index(struct_sym, node->as.field_access.field); + if (fi < 0) { + error_add(errors, "", node->loc.line, node->loc.col, + "结构体 '%s' 没有字段 '%s'", struct_name, node->as.field_access.field); + node->type.kind = TYPE_ERROR; return; + } + node->type.kind = struct_sym->struct_field_types[fi]; + node->as.field_access.field_index = fi; + if (node->type.kind == TYPE_STRUCT && struct_sym->struct_field_struct_names && + struct_sym->struct_field_struct_names[fi]) + node->type.struct_name = struct_sym->struct_field_struct_names[fi]; +} + +static void analyze_struct_init(AstNode* node, Scope* scope, ErrorList* errors, Arena* a) { + const char* resolved = node->as.struct_init.type_name; + Symbol* struct_sym = scope_lookup_struct(scope, resolved); + if (!struct_sym) { + Symbol* alias_sym = scope_lookup(scope, resolved); + if (alias_sym && alias_sym->is_type_alias && alias_sym->struct_type_name) { + resolved = alias_sym->struct_type_name; + struct_sym = scope_lookup_struct(scope, resolved); + node->as.struct_init.type_name = resolved; } - if (obj->type.kind != TYPE_STRUCT) { - error_add(errors, "", node->loc.line, node->loc.col, - "类型 '%s' 不是结构体,不能访问字段 '%s'", - type_name(obj->type.kind), node->as.field_access.field); - node->type.kind = TYPE_ERROR; - break; - } - // 查找结构体定义 - const char* struct_name = obj->type.struct_name; - if (!struct_name) { - error_add(errors, "", node->loc.line, node->loc.col, - "无法确定结构体类型"); - node->type.kind = TYPE_ERROR; - break; - } - Symbol* struct_sym = scope_lookup_struct(scope, struct_name); - if (!struct_sym) { - error_add(errors, "", node->loc.line, node->loc.col, - "未定义的结构体 '%s'", struct_name); - node->type.kind = TYPE_ERROR; - break; - } - int fi = scope_struct_field_index(struct_sym, node->as.field_access.field); + } + if (!struct_sym) { + error_add(errors, "", node->loc.line, node->loc.col, + "未定义的结构体类型 '%s'", node->as.struct_init.type_name); + node->type.kind = TYPE_ERROR; return; + } + if (node->as.struct_init.field_count != struct_sym->struct_field_count) { + error_add(errors, "", node->loc.line, node->loc.col, + "结构体 '%s' 有 %zu 个字段,但提供了 %zu 个", + node->as.struct_init.type_name, + struct_sym->struct_field_count, node->as.struct_init.field_count); + node->type.kind = TYPE_ERROR; return; + } + for (size_t i = 0; i < node->as.struct_init.field_count; i++) { + const char* fname = node->as.struct_init.field_names[i]; + AstNode* fval = node->as.struct_init.field_values[i]; + analyze_expr(fval, scope, errors, a); + int fi = scope_struct_field_index(struct_sym, fname); if (fi < 0) { error_add(errors, "", node->loc.line, node->loc.col, - "结构体 '%s' 没有字段 '%s'", struct_name, node->as.field_access.field); - node->type.kind = TYPE_ERROR; - break; + "结构体 '%s' 没有字段 '%s'", node->as.struct_init.type_name, fname); + node->type.kind = TYPE_ERROR; continue; } - node->type.kind = struct_sym->struct_field_types[fi]; - node->as.field_access.field_index = fi; - // 如果字段也是结构体类型,传播类型名 - if (node->type.kind == TYPE_STRUCT && - struct_sym->struct_field_struct_names && - struct_sym->struct_field_struct_names[fi]) { - node->type.struct_name = struct_sym->struct_field_struct_names[fi]; - } - break; + TypeKind expected = struct_sym->struct_field_types[fi]; + TypeKind actual = fval->type.kind; + if (actual != TYPE_ERROR && actual != expected) + error_add(errors, "", node->loc.line, node->loc.col, + "字段 '%s' 类型不匹配: 期望 '%s',得到 '%s'", + fname, type_name(expected), type_name(actual)); } - - case AST_STRUCT_INIT: { - const char* resolved_type_name = node->as.struct_init.type_name; - Symbol* struct_sym = scope_lookup_struct(scope, resolved_type_name); - if (!struct_sym) { - // 检查是否是类型别名指向结构体 - Symbol* alias_sym = scope_lookup(scope, resolved_type_name); - if (alias_sym && alias_sym->is_type_alias && alias_sym->struct_type_name) { - resolved_type_name = alias_sym->struct_type_name; - struct_sym = scope_lookup_struct(scope, resolved_type_name); - // 更新 type_name 为真实结构体名(codegen 需要) - node->as.struct_init.type_name = resolved_type_name; - } - } - if (!struct_sym) { - error_add(errors, "", node->loc.line, node->loc.col, - "未定义的结构体类型 '%s'", node->as.struct_init.type_name); - node->type.kind = TYPE_ERROR; - break; - } - if (node->as.struct_init.field_count != struct_sym->struct_field_count) { - error_add(errors, "", node->loc.line, node->loc.col, - "结构体 '%s' 有 %zu 个字段,但提供了 %zu 个", - node->as.struct_init.type_name, - struct_sym->struct_field_count, - node->as.struct_init.field_count); - node->type.kind = TYPE_ERROR; - break; - } - // 检查每个字段名和类型匹配 - for (size_t i = 0; i < node->as.struct_init.field_count; i++) { - const char* fname = node->as.struct_init.field_names[i]; - AstNode* fval = node->as.struct_init.field_values[i]; - analyze_expr(fval, scope, errors, a); - - int fi = scope_struct_field_index(struct_sym, fname); - if (fi < 0) { - error_add(errors, "", node->loc.line, node->loc.col, - "结构体 '%s' 没有字段 '%s'", - node->as.struct_init.type_name, fname); - node->type.kind = TYPE_ERROR; - continue; - } - TypeKind expected = struct_sym->struct_field_types[fi]; - TypeKind actual = fval->type.kind; - if (actual != TYPE_ERROR && actual != expected) { - error_add(errors, "", node->loc.line, node->loc.col, - "字段 '%s' 类型不匹配: 期望 '%s',得到 '%s'", - fname, type_name(expected), type_name(actual)); - } - } - if (node->type.kind != TYPE_ERROR) { - node->type.kind = TYPE_STRUCT; - node->type.struct_name = resolved_type_name; - } - break; + if (node->type.kind != TYPE_ERROR) { + node->type.kind = TYPE_STRUCT; + node->type.struct_name = resolved; } +} - case AST_ENUM_VARIANT: { - Symbol* enum_sym = scope_lookup_struct(scope, node->as.enum_variant.enum_name); - if (!enum_sym || enum_sym->kind != SYM_ENUM) { - error_add(errors, "", node->loc.line, node->loc.col, - "未定义的枚举 '%s'", node->as.enum_variant.enum_name); - node->type.kind = TYPE_ERROR; break; - } - int vi = scope_enum_variant_index(enum_sym, node->as.enum_variant.variant_name); - if (vi < 0) { - error_add(errors, "", node->loc.line, node->loc.col, - "枚举 '%s' 没有变体 '%s'", - node->as.enum_variant.enum_name, - node->as.enum_variant.variant_name); - node->type.kind = TYPE_ERROR; break; - } - node->as.enum_variant.variant_index = vi; - node->type.kind = TYPE_ENUM; - break; +static void analyze_enum_variant(AstNode* node, Scope* scope, ErrorList* errors, Arena* a) { + (void)a; + Symbol* enum_sym = scope_lookup_struct(scope, node->as.enum_variant.enum_name); + if (!enum_sym || enum_sym->kind != SYM_ENUM) { + error_add(errors, "", node->loc.line, node->loc.col, + "未定义的枚举 '%s'", node->as.enum_variant.enum_name); + node->type.kind = TYPE_ERROR; return; } - - case AST_INDEX_EXPR: { - analyze_expr(node->as.index_expr.array, scope, errors, a); - analyze_expr(node->as.index_expr.index, scope, errors, a); - AstNode* arr = node->as.index_expr.array; - AstNode* idx = node->as.index_expr.index; - - if (arr->type.kind == TYPE_ERROR) { node->type.kind = TYPE_ERROR; break; } - if (arr->type.kind != TYPE_ARRAY) { - error_add(errors, "", node->loc.line, node->loc.col, - "类型 '%s' 不支持索引操作", type_name(arr->type.kind)); - node->type.kind = TYPE_ERROR; break; - } - if (idx->type.kind == TYPE_ERROR) { node->type.kind = TYPE_ERROR; break; } - if (idx->type.kind != TYPE_I64) { - error_add(errors, "", node->loc.line, node->loc.col, - "数组索引必须是 i64 类型, 得到 '%s'", type_name(idx->type.kind)); - node->type.kind = TYPE_ERROR; break; - } - // 结果类型 = 元素类型 - node->type.kind = arr->type.element_type; - node->type.struct_name = arr->type.element_struct_name; - break; + int vi = scope_enum_variant_index(enum_sym, node->as.enum_variant.variant_name); + if (vi < 0) { + error_add(errors, "", node->loc.line, node->loc.col, + "枚举 '%s' 没有变体 '%s'", + node->as.enum_variant.enum_name, node->as.enum_variant.variant_name); + node->type.kind = TYPE_ERROR; return; } + node->as.enum_variant.variant_index = vi; + node->type.kind = TYPE_ENUM; +} - case AST_METHOD_CALL: { - analyze_expr(node->as.method_call.receiver, scope, errors, a); - const char* recv_struct = node->as.method_call.receiver->type.struct_name; - if (node->as.method_call.receiver->type.kind != TYPE_STRUCT || !recv_struct) { - error_add(errors, "", node->loc.line, node->loc.col, - "只有结构体类型支持方法调用"); - node->type.kind = TYPE_ERROR; break; - } - // 构造改名后的函数名并查找 - char mangled[256]; - snprintf(mangled, sizeof(mangled), "%s$%s", recv_struct, - node->as.method_call.method_name); - Symbol* sym = scope_lookup(scope, mangled); - if (!sym || sym->kind != SYM_FUNCTION) { - error_add(errors, "", node->loc.line, node->loc.col, - "结构体 '%s' 没有方法 '%s'", recv_struct, - node->as.method_call.method_name); - node->type.kind = TYPE_ERROR; break; - } - // 检查参数数量(用户提供的参数 + 隐含的 self) - if (node->as.method_call.arg_count + 1 != sym->param_count) { - error_add(errors, "", node->loc.line, node->loc.col, - "方法 '%s' 需要 %zu 个参数,提供了 %zu 个", - node->as.method_call.method_name, - sym->param_count > 0 ? sym->param_count - 1 : 0, - node->as.method_call.arg_count); - node->type.kind = TYPE_ERROR; break; - } - // 命名参数重排序(同 CALL_EXPR 逻辑) - if (node->as.method_call.arg_names) { - AstNode* reordered[16] = {0}; - for (size_t i = 0; i < node->as.method_call.arg_count; i++) { - if (node->as.method_call.arg_names[i]) { - bool found = false; - for (size_t j = 1; j < sym->param_count; j++) { // skip self - if (sym->param_names && sym->param_names[j] && - strcmp(node->as.method_call.arg_names[i], sym->param_names[j]) == 0) { - reordered[j - 1] = node->as.method_call.args[i]; - found = true; break; - } - } - if (!found) { - error_add(errors, "", node->loc.line, node->loc.col, - "方法 '%s' 没有名为 '%s' 的参数", - node->as.method_call.method_name, node->as.method_call.arg_names[i]); - node->type.kind = TYPE_ERROR; return; - } - } else { - reordered[i] = node->as.method_call.args[i]; - } - } - memcpy(node->as.method_call.args, reordered, node->as.method_call.arg_count * sizeof(AstNode*)); - } - // 对每个参数进行类型检查(跳过 self 参数,即 sym->param_types[0] 是 self 的类型) - for (size_t i = 0; i < node->as.method_call.arg_count; i++) { - analyze_expr(node->as.method_call.args[i], scope, errors, a); - TypeKind actual = node->as.method_call.args[i]->type.kind; - TypeKind expected = sym->param_types[i + 1]; - if (actual != TYPE_ERROR && actual != expected && - !(expected == TYPE_I64 && actual == TYPE_ENUM) && - !can_implicit_convert(actual, expected)) { - if (expected == TYPE_STRUCT) { - // 结构体类型参数:比较具体类型名 - const char* actual_name = node->as.method_call.args[i]->type.struct_name; - const char* expected_name = sym->param_struct_names ? sym->param_struct_names[i + 1] : NULL; - if (!actual_name || !expected_name || strcmp(actual_name, expected_name) != 0) { - error_add(errors, "", node->loc.line, node->loc.col, - "参数 %zu 类型不匹配: 期望 '%s',得到 '%s'", - i + 1, - expected_name ? expected_name : "struct", - actual_name ? actual_name : type_name(actual)); - } - } else { - error_add(errors, "", node->loc.line, node->loc.col, - "参数 %zu 类型不匹配: 期望 '%s',得到 '%s'", - i + 1, type_name(expected), type_name(actual)); - } - } - } - node->type.kind = sym->return_type; - if (sym->return_type == TYPE_STRUCT && sym->return_struct_type_name) { - node->type.struct_name = sym->return_struct_type_name; - } - break; +static void analyze_index_expr(AstNode* node, Scope* scope, ErrorList* errors, Arena* a) { + analyze_expr(node->as.index_expr.array, scope, errors, a); + analyze_expr(node->as.index_expr.index, scope, errors, a); + AstNode* arr = node->as.index_expr.array; + AstNode* idx = node->as.index_expr.index; + if (arr->type.kind == TYPE_ERROR) { node->type.kind = TYPE_ERROR; return; } + if (arr->type.kind != TYPE_ARRAY) { + error_add(errors, "", node->loc.line, node->loc.col, + "类型 '%s' 不支持索引操作", type_name(arr->type.kind)); + node->type.kind = TYPE_ERROR; return; } + if (idx->type.kind == TYPE_ERROR) { node->type.kind = TYPE_ERROR; return; } + if (idx->type.kind != TYPE_I64) { + error_add(errors, "", node->loc.line, node->loc.col, + "数组索引必须是 i64 类型, 得到 '%s'", type_name(idx->type.kind)); + node->type.kind = TYPE_ERROR; return; + } + node->type.kind = arr->type.element_type; + node->type.struct_name = arr->type.element_struct_name; +} +static void analyze_method_call(AstNode* node, Scope* scope, ErrorList* errors, Arena* a) { + analyze_expr(node->as.method_call.receiver, scope, errors, a); + const char* recv_struct = node->as.method_call.receiver->type.struct_name; + if (node->as.method_call.receiver->type.kind != TYPE_STRUCT || !recv_struct) { + error_add(errors, "", node->loc.line, node->loc.col, + "只有结构体类型支持方法调用"); + node->type.kind = TYPE_ERROR; return; + } + char mangled[256]; + snprintf(mangled, sizeof(mangled), "%s$%s", recv_struct, + node->as.method_call.method_name); + Symbol* sym = scope_lookup(scope, mangled); + if (!sym || sym->kind != SYM_FUNCTION) { + error_add(errors, "", node->loc.line, node->loc.col, + "结构体 '%s' 没有方法 '%s'", recv_struct, + node->as.method_call.method_name); + node->type.kind = TYPE_ERROR; return; + } + if (node->as.method_call.arg_count + 1 != sym->param_count) { + error_add(errors, "", node->loc.line, node->loc.col, + "方法 '%s' 需要 %zu 个参数,提供了 %zu 个", + node->as.method_call.method_name, + sym->param_count > 0 ? sym->param_count - 1 : 0, + node->as.method_call.arg_count); + node->type.kind = TYPE_ERROR; return; + } + if (!reorder_named_args(node, sym, 1, errors, node->as.method_call.method_name)) { + node->type.kind = TYPE_ERROR; return; + } + for (size_t i = 0; i < node->as.method_call.arg_count; i++) { + analyze_expr(node->as.method_call.args[i], scope, errors, a); + check_arg_type(node->as.method_call.args[i], sym->param_types[i + 1], + sym->param_struct_names ? sym->param_struct_names[i + 1] : NULL, + i, node, scope, errors, a); + } + node->type.kind = sym->return_type; + if (sym->return_type == TYPE_STRUCT && sym->return_struct_type_name) + node->type.struct_name = sym->return_struct_type_name; +} + +// === 表达式类型检查(调度器) === +static void analyze_expr(AstNode* node, Scope* scope, ErrorList* errors, Arena* a) { + switch (node->kind) { + case AST_LITERAL_EXPR: break; + case AST_IDENT_EXPR: analyze_ident_expr(node, scope, errors, a); break; + case AST_UNARY_EXPR: analyze_unary_expr(node, scope, errors, a); break; + case AST_BINARY_EXPR: analyze_binary_expr(node, scope, errors, a); break; + case AST_CALL_EXPR: analyze_call_expr(node, scope, errors, a); break; + case AST_FIELD_ACCESS: analyze_field_access(node, scope, errors, a); break; + case AST_STRUCT_INIT: analyze_struct_init(node, scope, errors, a); break; + case AST_ENUM_VARIANT: analyze_enum_variant(node, scope, errors, a); break; + case AST_INDEX_EXPR: analyze_index_expr(node, scope, errors, a); break; + case AST_METHOD_CALL: analyze_method_call(node, scope, errors, a); break; default: break; } }