From aac803a3b717a66cfe9cbf620a7e96551a3bacad Mon Sep 17 00:00:00 2001 From: Yi Sun Date: Sat, 1 Aug 2026 17:28:20 +0800 Subject: [PATCH 1/2] refactor(ir): centralize storage-dtype composition, de-panic array indices compose_var_decl_dtype/compose_var_def_dtype are the documented canonical helpers for combining a base Dtype with an AST declaration shape, but handle_struct_def and type_infer each kept their own inline copies of the same scalar/array composition. Delegate to the helpers wherever the input shapes and semantics are genuinely identical: - module_gen::handle_struct_def now calls compose_var_decl_dtype for struct member storage types; - type_infer::process_var_def delegates its array arm to compose_var_def_dtype; - type_infer::process_var_decl delegates every resolved case to compose_var_decl_dtype. The untyped-scalar arm stays inline (with a comment recording the divergence) because it must remain Pending rather than defaulting to i32. array_index_operand lowered source-derived usize indices with i32::try_from(..).expect(..), panicking on user input. Add Error::ArrayIndexTooLarge { index } and propagate it from the three call sites instead. Also add the missing //! module docs to ir/error.rs, ir/types.rs and ir/gen/static_eval.rs, matching the documented siblings. --- src/ir/error.rs | 11 +++++++++++ src/ir/gen/function_gen.rs | 29 ++++++++++++++++++----------- src/ir/gen/module_gen.rs | 6 ++---- src/ir/gen/static_eval.rs | 7 +++++++ src/ir/gen/type_infer.rs | 36 ++++++++++++++++++------------------ src/ir/types.rs | 7 +++++++ 6 files changed, 63 insertions(+), 33 deletions(-) diff --git a/src/ir/error.rs b/src/ir/error.rs index 3d9be6d..3143314 100644 --- a/src/ir/error.rs +++ b/src/ir/error.rs @@ -1,3 +1,11 @@ +//! Error type for the IR layer. +//! +//! This module defines [`Error`], the single error type produced by IR +//! generation and module-level type registration. Its variants cover name +//! and type resolution failures, duplicate or conflicting definitions, +//! unsupported source constructs, and compile-time evaluation failures; +//! the driver surfaces them to the user as diagnostics. + use crate::ast; use crate::ir::types::Dtype; use std::path::PathBuf; @@ -60,6 +68,9 @@ pub enum Error { #[error("Invalid array expression")] InvalidArrayExpression, + #[error("Array index {index} exceeds the IR's GEP index width (i32::MAX)")] + ArrayIndexTooLarge { index: usize }, + #[error("Reference operator '&' can only be applied to array variables, not '{symbol}'")] InvalidReference { symbol: String }, diff --git a/src/ir/gen/function_gen.rs b/src/ir/gen/function_gen.rs index 51fc354..b9a2984 100644 --- a/src/ir/gen/function_gen.rs +++ b/src/ir/gen/function_gen.rs @@ -17,14 +17,13 @@ use crate::ir::Error; /// Builds an i32-typed [`Operand`] for a GEP index. /// /// Array/struct indices are `usize` in the AST and source-language domain -/// but must be lowered to `i32` to match LLVM IR's GEP index width. The -/// compiler panics if a single declared array or struct exceeds `i32::MAX` -/// elements — this is a hard limit on the emitted IR, not a TeaLang rule, -/// and in practice no program will ever approach it. -fn array_index_operand(index: usize) -> Operand { - Operand::from( - i32::try_from(index).expect("array/struct index exceeds i32::MAX (LLVM GEP width)"), - ) +/// but must be lowered to `i32` to match LLVM IR's GEP index width. A +/// source-derived index that does not fit is rejected with +/// [`Error::ArrayIndexTooLarge`] — this is a hard limit on the emitted IR, +/// not a TeaLang rule, and in practice no program will ever approach it. +fn array_index_operand(index: usize) -> Result { + let index = i32::try_from(index).map_err(|_| Error::ArrayIndexTooLarge { index })?; + Ok(Operand::from(index)) } /// Returns the element type of the array pointed to by `base_ptr`. @@ -267,7 +266,11 @@ impl FunctionGenerator<'_> { let element_ptr = Operand::from(self.fresh_local(elem_ptr_dtype.clone())); let right_elem = self.handle_right_val(val)?; - self.emit_gep(element_ptr.clone(), base_ptr.clone(), array_index_operand(i)); + self.emit_gep( + element_ptr.clone(), + base_ptr.clone(), + array_index_operand(i)?, + ); self.emit_store(right_elem, element_ptr); } Ok(()) @@ -290,7 +293,11 @@ impl FunctionGenerator<'_> { let fill_val = self.handle_right_val(val)?; for i in 0..*count { let element_ptr = Operand::from(self.fresh_local(elem_ptr_dtype.clone())); - self.emit_gep(element_ptr.clone(), base_ptr.clone(), array_index_operand(i)); + self.emit_gep( + element_ptr.clone(), + base_ptr.clone(), + array_index_operand(i)?, + ); self.emit_store(fill_val.clone(), element_ptr); } Ok(()) @@ -749,7 +756,7 @@ impl FunctionGenerator<'_> { self.emit_load(idx.clone(), src); Ok(idx) } - ast::IndexExprInner::Num(num) => Ok(array_index_operand(*num)), + ast::IndexExprInner::Num(num) => array_index_operand(*num), } } } diff --git a/src/ir/gen/module_gen.rs b/src/ir/gen/module_gen.rs index e56e4d8..6bf635b 100644 --- a/src/ir/gen/module_gen.rs +++ b/src/ir/gen/module_gen.rs @@ -10,6 +10,7 @@ use crate::ast; use crate::ir::compute_link_name; use crate::ir::function::{BasicBlock, BlockLabel, Function, FunctionBody, FunctionGenerator}; +use crate::ir::gen::conversions::compose_var_decl_dtype; use crate::ir::gen::type_infer; use crate::ir::module::IrGenerator; use crate::ir::printer::IrPrinter; @@ -534,10 +535,7 @@ impl IrGenerator<'_> { decl.identifier.clone(), StructMember { index, - dtype: match &decl.inner { - ast::VarDeclInner::Scalar => base_dtype, - ast::VarDeclInner::Array(array) => Dtype::array_of(base_dtype, array.len), - }, + dtype: compose_var_decl_dtype(base_dtype, &decl.inner), }, )); } diff --git a/src/ir/gen/static_eval.rs b/src/ir/gen/static_eval.rs index 6298d19..6c53624 100644 --- a/src/ir/gen/static_eval.rs +++ b/src/ir/gen/static_eval.rs @@ -1,3 +1,10 @@ +//! Compile-time (static) evaluation of constant expressions. +//! +//! This module folds AST expressions composed entirely of literals and +//! constant operators into concrete `i32` values during IR generation. +//! Failures that would otherwise only appear at runtime — division by zero +//! and integer overflow — are reported here as compile-time errors. + use crate::ast; use crate::ir::module::IrGenerator; use crate::ir::Error; diff --git a/src/ir/gen/type_infer.rs b/src/ir/gen/type_infer.rs index 9f025ca..e74e9b3 100644 --- a/src/ir/gen/type_infer.rs +++ b/src/ir/gen/type_infer.rs @@ -49,6 +49,7 @@ use std::collections::HashMap; use indexmap::IndexMap; use crate::ast; +use crate::ir::gen::conversions::{compose_var_decl_dtype, compose_var_def_dtype}; use crate::ir::module::Registry; use crate::ir::types::Dtype; use crate::ir::value::GlobalDef; @@ -185,20 +186,19 @@ impl TypeInference<'_> { /// R1 (`let x: T;`) and R4 (`let x;`). fn process_var_decl(&mut self, decl: &ast::VarDecl) { let id = &decl.identifier; - let state = match (&decl.type_specifier, &decl.inner) { - // Typed scalar: let x: T; - (Some(ts), ast::VarDeclInner::Scalar) => VarState::Resolved(Dtype::from(ts)), - // Typed array: let x: [T; N]; - (Some(ts), ast::VarDeclInner::Array(arr)) => { - VarState::Resolved(Dtype::array_of(Dtype::from(ts), arr.len)) - } - // Untyped array (defaults to i32 elements): let x: [; N] — grammatically - // rare but handled for completeness. - (None, ast::VarDeclInner::Array(arr)) => { - VarState::Resolved(Dtype::array_of(Dtype::I32, arr.len)) - } - // Untyped scalar: let x; + let state = match (decl.type_specifier.as_ref(), &decl.inner) { + // Untyped scalar (`let x;`): stays Pending until the first + // assignment — unlike `compose_var_decl_dtype`, which defaults a + // missing specifier to i32, this pass must keep the type open. (None, ast::VarDeclInner::Scalar) => VarState::Pending, + // Typed scalar (`let x: T;`), typed array (`let x: [T; N];`), and + // the grammatically-rare untyped array (`let x: [; N]`, i32 + // elements) all compose the storage dtype the same way, so + // delegate to the canonical helper. + (specifier, inner) => VarState::Resolved(compose_var_decl_dtype( + specifier.map_or(Dtype::I32, Dtype::from), + inner, + )), }; self.env.insert(id.clone(), state); } @@ -225,14 +225,14 @@ impl TypeInference<'_> { self.env.insert(id.clone(), VarState::Resolved(resolved)); } ast::VarDefInner::Array(arr) => { - let elem_type = match &explicit_dtype { - Some(t) => t.clone(), - None => Dtype::I32, - }; self.check_array_initializer(&arr.initializer)?; + // Element type defaults to i32 when no specifier is present; + // the array wrap is the canonical composition, so delegate + // to `compose_var_def_dtype`. + let base = explicit_dtype.clone().unwrap_or(Dtype::I32); self.env.insert( id.clone(), - VarState::Resolved(Dtype::array_of(elem_type, arr.len)), + VarState::Resolved(compose_var_def_dtype(base, &def.inner)), ); } } diff --git a/src/ir/types.rs b/src/ir/types.rs index bc98b4d..53d7536 100644 --- a/src/ir/types.rs +++ b/src/ir/types.rs @@ -1,3 +1,10 @@ +//! Core type definitions for the IR. +//! +//! This module defines [`Dtype`], the IR-level data type threaded through +//! lowering, printing, and the back-end, together with the aggregate and +//! signature descriptors ([`StructType`], [`StructMember`], [`FunctionType`]) +//! stored in the module's type registry. + use crate::ast; use std::fmt::{self, Display, Formatter}; From 48261532e92809e87e1ffa4554dc9cb7938547de Mon Sep 17 00:00:00 2001 From: Yi Sun Date: Sat, 1 Aug 2026 18:45:25 +0800 Subject: [PATCH 2/2] docs(ir): discipline comments per comment spec - removed 16 divider banner groups (4 function_gen, 12 type_infer) - removed 11 obvious narrations/restatements (emit-store, lower-body, eval-condition, jump-past-else, emit-loop-test, 2 short-circuit duplicates, finalise-block, alloca-hoist restatement, insert-hoisted) - removed 1 duplicated impl-block doc (static_eval restated module doc) - rewrote 4 stale/contradictory comments: generate() #Errors claimed an impossible UnsupportedReturnType, 'two-phase' impl doc vs 3-pass body, local_base_dtype unreachable fallback example, post-hoist invariant (referenced empty blocks panic, edges are never dropped) - rewrote 4 first-person formulations to declarative facts - trimmed 1 unsupported claim ('no program will ever approach it') - de-linked 5 unresolved intra-doc links to private/out-of-scope items (doc warnings 27 -> 23) - no TODO/FIXME/HACK tags and no commented-out code found in scope --- src/ir/gen/function_gen.rs | 48 ++++++++++----------------------- src/ir/gen/module_gen.rs | 36 +++++++++++-------------- src/ir/gen/static_eval.rs | 5 ---- src/ir/gen/type_infer.rs | 54 +++----------------------------------- 4 files changed, 32 insertions(+), 111 deletions(-) diff --git a/src/ir/gen/function_gen.rs b/src/ir/gen/function_gen.rs index b9a2984..0d6579a 100644 --- a/src/ir/gen/function_gen.rs +++ b/src/ir/gen/function_gen.rs @@ -19,8 +19,8 @@ use crate::ir::Error; /// Array/struct indices are `usize` in the AST and source-language domain /// but must be lowered to `i32` to match LLVM IR's GEP index width. A /// source-derived index that does not fit is rejected with -/// [`Error::ArrayIndexTooLarge`] — this is a hard limit on the emitted IR, -/// not a TeaLang rule, and in practice no program will ever approach it. +/// [`Error::ArrayIndexTooLarge`]; that limit comes from the emitted IR, +/// not from a TeaLang rule. fn array_index_operand(index: usize) -> Result { let index = i32::try_from(index).map_err(|_| Error::ArrayIndexTooLarge { index })?; Ok(Operand::from(index)) @@ -44,9 +44,6 @@ fn array_element_dtype(base_ptr: &Operand) -> Dtype { } } -// ----------------------------------------------------------------------- -// Function entry-point generation -// ----------------------------------------------------------------------- impl FunctionGenerator<'_> { /// Generates IR for a complete function definition. @@ -59,8 +56,9 @@ impl FunctionGenerator<'_> { /// - `from`: the AST node for the function being compiled. /// /// # Errors - /// Returns an error if the function is not registered in the type registry, - /// an argument name is redefined, or the return type is unsupported. + /// Returns [`Error::FunctionNotDefined`] when the function is absent from + /// the type registry, [`Error::VariableRedefinition`] when an argument + /// name is duplicated, and any error propagated from statement lowering. pub fn generate(&mut self, from: &ast::FnDef) -> Result<(), Error> { let identifier = &from.fn_decl.identifier; let function_type = self @@ -91,12 +89,10 @@ impl FunctionGenerator<'_> { // Allocate a stack slot (pointer to the argument type) for the argument. let slot = self.fresh_local(Dtype::ptr_to(dtype.clone())); self.emit_alloca(Operand::from(&slot)); - // Store the incoming value into the newly allocated stack slot. self.emit_store(Operand::from(arg_local), Operand::from(&slot)); self.local_variables.insert(id.clone(), slot); } - // Lower the function body statement by statement. for stmt in &from.stmts { self.handle_block(stmt, None, None)?; } @@ -123,10 +119,6 @@ impl FunctionGenerator<'_> { } } -// ----------------------------------------------------------------------- -// Statement handlers -// ----------------------------------------------------------------------- - impl FunctionGenerator<'_> { /// Dispatches a single code-block statement to the appropriate handler. /// @@ -169,7 +161,7 @@ impl FunctionGenerator<'_> { /// Inserts a local variable into the current scope's symbol table. /// - /// Records the identifier for scope-exit cleanup via [`record_scoped_local`]. + /// Records the identifier for scope-exit cleanup via `record_scoped_local`. /// Returns `VariableRedefinition` if a variable with the same name already /// exists in the symbol table. fn insert_scoped_local( @@ -201,7 +193,7 @@ impl FunctionGenerator<'_> { /// Allocates stack space for a scalar local and initializes it with `right_val`. /// - /// Combines [`allocate_pointer_local`] with an immediate `store` instruction. + /// Combines `allocate_pointer_local` with an immediate `store` instruction. fn define_scalar_local(&mut self, pointee: Dtype, right_val: Operand) -> Local { let local = self.allocate_pointer_local(pointee); self.emit_store(right_val, Operand::from(&local)); @@ -212,10 +204,11 @@ impl FunctionGenerator<'_> { /// /// For typed declarations the base comes directly from the AST annotation. /// For **untyped scalars**, the base comes from the resolved-types map - /// produced by the type inference pass (falling back to `i32` when - /// inference could not determine anything — e.g. a declared-but-never-used - /// local). **Untyped arrays** always default to `i32` elements because - /// TeaLang does not support inferring an array's element type. + /// produced by the type inference pass; inference rejects any variable + /// still `Pending` at function end, so the map covers every reachable + /// local and the `i32` fallback only defends the invariant. **Untyped + /// arrays** always default to `i32` elements because TeaLang does not + /// support inferring an array's element type. fn local_base_dtype( &self, identifier: &str, @@ -278,7 +271,7 @@ impl FunctionGenerator<'_> { /// Initializes an array from an [`ArrayInitializer`]. /// - /// Delegates to [`init_array`] for explicit element lists. For fill + /// Delegates to `init_array` for explicit element lists. For fill /// initializers, evaluates the fill value once and repeats the store for /// every index up to `count`. pub fn init_array_from( @@ -383,7 +376,6 @@ impl FunctionGenerator<'_> { let false_label = self.alloc_basic_block(); let after_label = self.alloc_basic_block(); - // Evaluate the condition; jump to the appropriate branch. self.handle_bool_unit(&stmt.bool_unit, true_label.clone(), false_label.clone())?; // Emit the then-branch; a new scope is opened so that any locals are cleaned up. @@ -393,7 +385,6 @@ impl FunctionGenerator<'_> { self.handle_block(s, con_label, bre_label)?; } self.exit_scope(); - // Jump past the else-branch to the merge point. self.emit_jump(after_label.clone()); // Emit the (possibly absent) else-branch in its own scope. @@ -430,7 +421,6 @@ impl FunctionGenerator<'_> { // Jump unconditionally into the loop test from the predecessor block. self.emit_jump(test_label.clone()); - // Emit the loop condition test. self.emit_label(test_label.clone()); self.handle_bool_unit(&stmt.bool_unit, true_label.clone(), false_label.clone())?; @@ -484,10 +474,6 @@ impl FunctionGenerator<'_> { } } -// ----------------------------------------------------------------------- -// Expression and value handlers -// ----------------------------------------------------------------------- - impl FunctionGenerator<'_> { /// Lowers a comparison expression into a conditional branch. /// @@ -761,16 +747,12 @@ impl FunctionGenerator<'_> { } } -// ----------------------------------------------------------------------- -// Boolean expression handlers -// ----------------------------------------------------------------------- - impl FunctionGenerator<'_> { /// Lowers a boolean expression to a materialized `i32` value (0 or 1). /// /// Allocates a temporary `i32` stack slot, evaluates the expression as a /// branch (writing 1 on the true path and 0 on the false path via - /// [`emit_bool_materialization`]), then loads and returns the result. + /// `emit_bool_materialization`), then loads and returns the result. fn handle_bool_expr_as_value(&mut self, expr: &ast::BoolExpr) -> Result { let true_label = self.alloc_basic_block(); let false_label = self.alloc_basic_block(); @@ -858,7 +840,6 @@ impl FunctionGenerator<'_> { let eval_right_label = self.alloc_basic_block(); match &expr.op { ast::BoolBiOp::And => { - // Short-circuit AND: only evaluate the right side if the left side is true. self.handle_bool_expr_as_branch( &expr.left, eval_right_label.clone(), @@ -869,7 +850,6 @@ impl FunctionGenerator<'_> { self.handle_bool_expr_as_branch(&expr.right, true_label, false_label)?; } ast::BoolBiOp::Or => { - // Short-circuit OR: only evaluate the right side if the left side is false. self.handle_bool_expr_as_branch( &expr.left, true_label.clone(), diff --git a/src/ir/gen/module_gen.rs b/src/ir/gen/module_gen.rs index 6bf635b..6271d10 100644 --- a/src/ir/gen/module_gen.rs +++ b/src/ir/gen/module_gen.rs @@ -25,7 +25,7 @@ use std::fs; use std::io::Write; use std::rc::Rc; -/// Implements the two-phase `Generator` trait for the module-level IR generator. +/// Drives whole-program IR generation; the pass pipeline is documented on `generate`. impl Generator for IrGenerator<'_> { type Error = Error; @@ -71,10 +71,10 @@ impl Generator for IrGenerator<'_> { } // Pass 2.5: run every module-level plug-in pass registered by - // the driver. We `mem::take` the pipeline out so the pass can - // receive `&mut self` without aliasing the list that dispatched - // it — the generator's `module_passes` field is empty for the - // duration of the loop and gets restored on exit. + // the driver. `mem::take` moves the pipeline out of the generator + // so the pass can receive `&mut self` without aliasing the list that + // dispatched it — the `module_passes` field is empty for the + // duration of the loop and is restored on exit. { let passes = std::mem::take(&mut self.module_passes); let result = passes.run(self); @@ -94,8 +94,8 @@ impl Generator for IrGenerator<'_> { let resolved_types = type_infer::infer_function(&self.registry, &self.module.global_list, fn_def)?; - // Use a scoped FunctionGenerator so its temporary state is - // dropped before we mutably borrow `self.module` below. + // Scoped so the FunctionGenerator's temporary state drops + // before `self.module` is mutably borrowed below. let body = { let mut function_generator = FunctionGenerator::new( &self.registry, @@ -133,7 +133,7 @@ impl Generator for IrGenerator<'_> { /// Emit the complete IR module to the provided writer in textual form. /// /// Delegates to [`IrPrinter::emit_module`] — the same helper used by - /// [`crate::opt::Optimizer::output`] — so that pre-optimization and + /// `crate::opt::Optimizer::output` — so that pre-optimization and /// post-optimization IR dumps share one code path and one canonical /// format. fn output(&self, w: &mut W) -> Result<(), Error> { @@ -238,7 +238,6 @@ impl IrGenerator<'_> { for stmt in irs { if let StmtInner::Label(l) = &stmt.inner { - // Finalise the previous block (if any) and start a new one. if let Some(prev_label) = label.take() { blocks.push(BasicBlock { label: prev_label, @@ -271,9 +270,6 @@ impl IrGenerator<'_> { return blocks; } - // Hoist all allocas from non-entry blocks to the entry block, right - // after the entry label. This ensures all stack allocations happen in - // the entry block (LLVM convention). let mut hoisted_allocas: Vec = Vec::new(); for block in blocks.iter_mut().skip(1) { let (allocas, remaining): (Vec, Vec) = block @@ -283,16 +279,14 @@ impl IrGenerator<'_> { hoisted_allocas.extend(allocas); block.stmts = remaining; } - // Insert hoisted allocas at the beginning of the entry block. blocks[0].stmts.splice(0..0, hoisted_allocas); // Post-hoist invariant: every block still has at least a terminator - // (`return` / `jump` / `cjump`) because the IR generator always emits - // one for reachable blocks, and the terminator is not an alloca so it - // is never hoisted away. Blocks that somehow ended up empty (only an - // alloca-only body) would become dangling jump targets if dropped, so - // we verify — and drop — them together with the edges that reach - // them. + // (`return` / `jump` / `cjump`), because the generator emits one for + // every reachable block and a terminator is never an alloca, so + // hoisting cannot empty a reachable block. An empty block that is + // still referenced by a jump would become a dangling target once + // dropped, so removal below is guarded by that reference check. Self::drop_empty_blocks_or_panic(&mut blocks); blocks @@ -311,8 +305,8 @@ impl IrGenerator<'_> { return; } - // Collect the labels referenced by any remaining (non-empty) block's - // terminator so we can tell whether an empty block is still reachable. + // Collect every label targeted by a non-empty block's terminator; an + // empty block whose label appears in this set is still reachable. let mut referenced: HashSet = HashSet::new(); for block in blocks.iter().filter(|b| !b.stmts.is_empty()) { for stmt in &block.stmts { diff --git a/src/ir/gen/static_eval.rs b/src/ir/gen/static_eval.rs index 6c53624..a5ce6fc 100644 --- a/src/ir/gen/static_eval.rs +++ b/src/ir/gen/static_eval.rs @@ -10,11 +10,6 @@ use crate::ir::module::IrGenerator; use crate::ir::Error; /// Static evaluation methods for the IR generator. -/// -/// These functions perform compile-time (static) evaluation of constant expressions -/// from the AST, folding them into concrete `i32` values. This is used for constant -/// folding during IR generation — expressions composed entirely of literals and -/// constant operations can be reduced to a single integer value at compile time. impl IrGenerator<'_> { /// Statically evaluates a right-hand-side value. /// diff --git a/src/ir/gen/type_infer.rs b/src/ir/gen/type_infer.rs index e74e9b3..6c229cb 100644 --- a/src/ir/gen/type_infer.rs +++ b/src/ir/gen/type_infer.rs @@ -96,10 +96,6 @@ impl TypeInference<'_> { } } -// --------------------------------------------------------------------------- -// Public entry point -// --------------------------------------------------------------------------- - /// Resolve the types of all local variables in `fn_def`. /// /// Returns a map from variable name to its concrete [`Dtype`]. Any variable @@ -147,10 +143,6 @@ pub fn infer_function( Ok(resolved) } -// --------------------------------------------------------------------------- -// Statement processing -// --------------------------------------------------------------------------- - impl TypeInference<'_> { fn process_stmt(&mut self, stmt: &ast::CodeBlockStmt) -> Result<(), Error> { match &stmt.inner { @@ -179,10 +171,6 @@ impl TypeInference<'_> { Ok(()) } - // ----------------------------------------------------------------------- - // Variable declaration (no initializer) - // ----------------------------------------------------------------------- - /// R1 (`let x: T;`) and R4 (`let x;`). fn process_var_decl(&mut self, decl: &ast::VarDecl) { let id = &decl.identifier; @@ -203,10 +191,6 @@ impl TypeInference<'_> { self.env.insert(id.clone(), state); } - // ----------------------------------------------------------------------- - // Variable definition (with initializer) - // ----------------------------------------------------------------------- - /// R2 (`let x: T = e;`) and R3 (`let x = e;`). fn process_var_def(&mut self, def: &ast::VarDef) -> Result<(), Error> { let id = &def.identifier; @@ -253,10 +237,6 @@ impl TypeInference<'_> { Ok(()) } - // ----------------------------------------------------------------------- - // Assignment - // ----------------------------------------------------------------------- - /// R5 / R6: `x = e;` — resolve a Pending local to typeOf(e), or check /// type compatibility against an already-Resolved local. fn process_assignment(&mut self, stmt: &ast::AssignmentStmt) -> Result<(), Error> { @@ -273,9 +253,9 @@ impl TypeInference<'_> { Self::check_compatible(id, &t, &rhs_type)?; } None => { - // Variable not in local env — it may be a global. - // We don't track globals in this pass; IR gen will - // catch undefined references. + // A name absent from the local env may be a global. + // Globals are not tracked in this pass; IR generation + // catches undefined references. } } } @@ -289,10 +269,6 @@ impl TypeInference<'_> { Ok(()) } - // ----------------------------------------------------------------------- - // Branching (if/else) - // ----------------------------------------------------------------------- - /// R7: if/else merging. fn process_if(&mut self, stmt: &ast::IfStmt) -> Result<(), Error> { self.check_bool_unit(&stmt.bool_unit)?; @@ -313,10 +289,6 @@ impl TypeInference<'_> { Ok(()) } - // ----------------------------------------------------------------------- - // Loops - // ----------------------------------------------------------------------- - /// R8: while merging. fn process_while(&mut self, stmt: &ast::WhileStmt) -> Result<(), Error> { self.check_bool_unit(&stmt.bool_unit)?; @@ -329,10 +301,6 @@ impl TypeInference<'_> { Ok(()) } - // ----------------------------------------------------------------------- - // Return - // ----------------------------------------------------------------------- - fn process_return(&mut self, stmt: &ast::ReturnStmt) -> Result<(), Error> { if let Some(val) = &stmt.val { self.type_of_right_val(val)?; @@ -340,10 +308,6 @@ impl TypeInference<'_> { Ok(()) } - // ----------------------------------------------------------------------- - // Environment merging - // ----------------------------------------------------------------------- - /// Merge two branch environments back into `self.env`. /// /// For each variable already in the pre-branch environment: @@ -398,10 +362,6 @@ impl TypeInference<'_> { } } -// --------------------------------------------------------------------------- -// Expression typing -// --------------------------------------------------------------------------- - impl TypeInference<'_> { /// Compute the type of a right-hand-side value. fn type_of_right_val(&self, val: &ast::RightVal) -> Result { @@ -598,10 +558,6 @@ impl TypeInference<'_> { Ok(Self::element_type_of_indexing(&arr_type)) } - // ----------------------------------------------------------------------- - // Boolean expression checking (just validates sub-expressions) - // ----------------------------------------------------------------------- - fn check_bool_expr(&self, expr: &ast::BoolExpr) -> Result<(), Error> { match &expr.inner { ast::BoolExprInner::BoolBiOpExpr(biop) => { @@ -624,10 +580,6 @@ impl TypeInference<'_> { } } - // ----------------------------------------------------------------------- - // Type compatibility check - // ----------------------------------------------------------------------- - fn check_compatible(symbol: &str, expected: &Dtype, actual: &Dtype) -> Result<(), Error> { if expected == actual { return Ok(());