Skip to main content

tupa_codegen/
lib.rs

1use std::collections::HashMap;
2
3use tupa_parser::{Expr, ExprKind, Function, Item, Program, Stmt, Type};
4pub mod execution_plan;
5#[allow(unused_imports)]
6use tupa_typecheck::{typecheck_program_with_warnings, Ty};
7
8#[derive(Debug, Clone, Copy, PartialEq, Eq)]
9enum SimpleTy {
10    I64,
11    F64,
12    Bool,
13    I64Ptr,
14    F64Ptr,
15    Str,
16    StrPtr,
17    BoolPtr,
18    Void,
19    ClosurePtr,
20    Unknown,
21}
22
23pub fn generate_stub(program: &Program) -> String {
24    let mut codegen = Codegen::default();
25    codegen.emit_program(program);
26    codegen.finish()
27}
28
29pub fn generate_stub_with_types(program: &Program) -> String {
30    let mut codegen = Codegen {
31        type_info: Some(HashMap::new()),
32        ..Default::default()
33    };
34    codegen.emit_program(program);
35    codegen.finish()
36}
37
38#[derive(Default)]
39struct Codegen {
40    lines: Vec<String>,
41    globals: Vec<String>,
42    string_pool: HashMap<String, (String, usize)>,
43    temp: usize,
44    label: usize,
45    loop_stack: Vec<LoopLabels>,
46    fmt_int_emitted: bool,
47    fmt_float_emitted: bool,
48    printf_declared: bool,
49    puts_declared: bool,
50    strcmp_declared: bool,
51    strlen_declared: bool,
52    malloc_declared: bool,
53    strcpy_declared: bool,
54    strcat_declared: bool,
55    snprintf_declared: bool,
56    powf_declared: bool,
57    function_sigs: HashMap<String, FuncSig>,
58    used_vars: HashMap<String, bool>,
59    #[allow(dead_code)]
60    type_info: Option<HashMap<String, tupa_typecheck::Ty>>,
61}
62
63#[derive(Debug, Clone)]
64struct LocalVar {
65    ptr: String,
66    ty: SimpleTy,
67    used: bool,
68}
69
70#[allow(dead_code)]
71#[derive(Debug, Clone)]
72struct ClosureEnv {
73    vars: Vec<(String, SimpleTy)>, // (name, type) of captured variables
74    struct_name: String,
75}
76
77#[derive(Debug, Clone)]
78struct LoopLabels {
79    break_label: String,
80    continue_label: String,
81}
82
83#[derive(Debug, Clone)]
84struct FuncSig {
85    #[allow(dead_code)]
86    params: Vec<SimpleTy>,
87    ret: SimpleTy,
88}
89
90impl Codegen {
91    fn declare_snprintf(&mut self) {
92        if !self.snprintf_declared {
93            self.globals
94                .push("declare i32 @snprintf(i8*, i64, i8*, ...)".to_string());
95            self.snprintf_declared = true;
96        }
97    }
98
99    fn declare_powf(&mut self) {
100        if !self.powf_declared {
101            self.globals
102                .push("declare double @llvm.pow.f64(double, double)".to_string());
103            self.powf_declared = true;
104        }
105    }
106    // Implementação real de concatenação de strings
107    fn emit_string_concat(&mut self, left: ExprValue, right: ExprValue) -> ExprValue {
108        self.declare_strlen();
109        self.declare_malloc();
110        self.declare_strcpy();
111        self.declare_strcat();
112        // strlen(left)
113        let len_left = self.fresh_temp();
114        self.lines.push(format!(
115            "  {len_left} = call i64 @strlen(i8* {})",
116            left.llvm_value
117        ));
118        // strlen(right)
119        let len_right = self.fresh_temp();
120        self.lines.push(format!(
121            "  {len_right} = call i64 @strlen(i8* {})",
122            right.llvm_value
123        ));
124        // total = len_left + len_right + 1
125        let total = self.fresh_temp();
126        self.lines
127            .push(format!("  {total} = add i64 {len_left}, {len_right}"));
128        let total1 = self.fresh_temp();
129        self.lines.push(format!("  {total1} = add i64 {total}, 1"));
130        // malloc(total1)
131        let buf = self.fresh_temp();
132        self.lines
133            .push(format!("  {buf} = call i8* @malloc(i64 {total1})"));
134        // strcpy(buf, left)
135        self.lines.push(format!(
136            "  call i8* @strcpy(i8* {buf}, i8* {})",
137            left.llvm_value
138        ));
139        // strcat(buf, right)
140        self.lines.push(format!(
141            "  call i8* @strcat(i8* {buf}, i8* {})",
142            right.llvm_value
143        ));
144        ExprValue::new(SimpleTy::Str, buf)
145    }
146
147    // Implementação real de formatação de valor como string
148    fn emit_format_value(&mut self, val: ExprValue) -> Option<ExprValue> {
149        match val.ty {
150            SimpleTy::I64 => {
151                self.declare_printf();
152                let (fmt, len) = self.intern_format_int();
153                let fmt_ptr = self.fresh_temp();
154                self.lines.push(format!("  {fmt_ptr} = getelementptr inbounds [{len} x i8], [{len} x i8]* {fmt}, i64 0, i64 0"));
155                // Aloca buffer
156                self.declare_malloc();
157                let buf = self.fresh_temp();
158                self.lines
159                    .push(format!("  {buf} = call i8* @malloc(i64 32)"));
160                // snprintf
161                self.declare_snprintf();
162                self.lines.push(format!(
163                    "  call i32 @snprintf(i8* {buf}, i64 32, i8* {fmt_ptr}, i64 {})",
164                    val.llvm_value
165                ));
166                Some(ExprValue::new(SimpleTy::Str, buf))
167            }
168            SimpleTy::F64 => {
169                self.declare_printf();
170                let (fmt, len) = self.intern_format_float();
171                let fmt_ptr = self.fresh_temp();
172                self.lines.push(format!("  {fmt_ptr} = getelementptr inbounds [{len} x i8], [{len} x i8]* {fmt}, i64 0, i64 0"));
173                self.declare_malloc();
174                let buf = self.fresh_temp();
175                self.lines
176                    .push(format!("  {buf} = call i8* @malloc(i64 32)"));
177                self.declare_snprintf();
178                self.lines.push(format!(
179                    "  call i32 @snprintf(i8* {buf}, i64 32, i8* {fmt_ptr}, double {})",
180                    val.llvm_value
181                ));
182                Some(ExprValue::new(SimpleTy::Str, buf))
183            }
184            SimpleTy::Bool => {
185                // bool para string: "true" ou "false"
186                let true_str = self.intern_string("true").0;
187                let false_str = self.intern_string("false").0;
188                let select = self.fresh_temp();
189                self.lines.push(format!(
190                    "  {select} = select i1 {}, i8* {}, i8* {}",
191                    val.llvm_value, true_str, false_str
192                ));
193                Some(ExprValue::new(SimpleTy::Str, select))
194            }
195            SimpleTy::Str => Some(val),
196            _ => None,
197        }
198    }
199    fn emit_program(&mut self, program: &Program) {
200        for item in &program.items {
201            let func = match item {
202                Item::Function(func) => func,
203                Item::Enum(_) => continue,  // enums don't have functions
204                Item::Trait(_) => continue, // traits don't have functions
205                Item::Pipeline(_) => continue, // pipelines don't have functions
206            };
207            let params = func
208                .params
209                .iter()
210                .map(|p| self.simple_ty_from_type(&p.ty))
211                .collect::<Vec<_>>();
212            let ret = match func.return_type.as_ref() {
213                Some(ty) => self.simple_ty_from_type(ty),
214                None => SimpleTy::Void,
215            };
216            self.function_sigs
217                .insert(func.name.clone(), FuncSig { params, ret });
218        }
219        for item in &program.items {
220            match item {
221                Item::Function(func) => self.emit_function(func),
222                Item::Enum(_) => {}     // enums don't emit code yet
223                Item::Trait(_) => {}    // traits don't emit code yet
224                Item::Pipeline(_) => {} // pipelines don't emit code
225            }
226        }
227    }
228
229    fn is_var_used(&self, name: &str) -> bool {
230        self.used_vars.get(name).copied().unwrap_or(true)
231    }
232
233    fn collect_used_vars(&self, func: &Function) -> HashMap<String, bool> {
234        let mut env = HashMap::new();
235        for param in &func.params {
236            env.insert(param.name.clone(), false);
237        }
238        self.collect_used_in_block(&func.body, &mut env);
239        env
240    }
241
242    fn merge_used_vars(&self, parent: &mut HashMap<String, bool>, child: &HashMap<String, bool>) {
243        for (name, used) in parent.iter_mut() {
244            if let Some(child_used) = child.get(name) {
245                if *child_used {
246                    *used = true;
247                }
248            }
249        }
250    }
251
252    fn collect_used_in_block(&self, stmts: &[Stmt], env: &mut HashMap<String, bool>) {
253        for stmt in stmts {
254            self.collect_used_in_stmt(stmt, env);
255        }
256    }
257
258    fn collect_used_in_stmt(&self, stmt: &Stmt, env: &mut HashMap<String, bool>) {
259        match stmt {
260            Stmt::Let { name, expr, .. } => {
261                self.collect_used_in_expr(expr, env);
262                env.insert(name.clone(), false);
263            }
264            Stmt::Expr(expr) => {
265                self.collect_used_in_expr(expr, env);
266            }
267            Stmt::Return(expr) => {
268                if let Some(expr) = expr {
269                    self.collect_used_in_expr(expr, env);
270                }
271            }
272            Stmt::While { condition, body } => {
273                self.collect_used_in_expr(condition, env);
274                let mut inner = env.clone();
275                self.collect_used_in_block(body, &mut inner);
276                self.merge_used_vars(env, &inner);
277            }
278            Stmt::For { name, iter, body } => {
279                self.collect_used_in_expr(iter, env);
280                let mut inner = env.clone();
281                inner.insert(name.clone(), false);
282                self.collect_used_in_block(body, &mut inner);
283                self.merge_used_vars(env, &inner);
284            }
285            Stmt::Break | Stmt::Continue => {}
286            Stmt::Lambda { body, .. } => {
287                self.collect_used_in_expr(body, env);
288            }
289        }
290    }
291
292    fn fold_expr(&self, expr: &Expr) -> Expr {
293        match &expr.kind {
294            ExprKind::Binary { op, left, right } => {
295                let folded_left = self.fold_expr(left);
296                let folded_right = self.fold_expr(right);
297
298                match (&folded_left.kind, &folded_right.kind, op) {
299                    (ExprKind::Int(a), ExprKind::Int(b), tupa_parser::BinaryOp::Add) => Expr {
300                        kind: ExprKind::Int(a + b),
301                        span: expr.span,
302                    },
303                    (ExprKind::Int(a), ExprKind::Int(b), tupa_parser::BinaryOp::Sub) => Expr {
304                        kind: ExprKind::Int(a - b),
305                        span: expr.span,
306                    },
307                    (ExprKind::Int(a), ExprKind::Int(b), tupa_parser::BinaryOp::Mul) => Expr {
308                        kind: ExprKind::Int(a * b),
309                        span: expr.span,
310                    },
311                    (ExprKind::Int(a), ExprKind::Int(b), tupa_parser::BinaryOp::Div) if *b != 0 => {
312                        Expr {
313                            kind: ExprKind::Int(a / b),
314                            span: expr.span,
315                        }
316                    }
317                    (ExprKind::Int(a), ExprKind::Int(b), tupa_parser::BinaryOp::Pow) => {
318                        if *b >= 0 {
319                            let Ok(exp) = u32::try_from(*b) else {
320                                return Expr {
321                                    kind: ExprKind::Binary {
322                                        op: op.clone(),
323                                        left: Box::new(folded_left),
324                                        right: Box::new(folded_right),
325                                    },
326                                    span: expr.span,
327                                };
328                            };
329                            Expr {
330                                kind: ExprKind::Int(a.pow(exp)),
331                                span: expr.span,
332                            }
333                        } else {
334                            Expr {
335                                kind: ExprKind::Binary {
336                                    op: op.clone(),
337                                    left: Box::new(folded_left),
338                                    right: Box::new(folded_right),
339                                },
340                                span: expr.span,
341                            }
342                        }
343                    }
344                    (ExprKind::Float(a), ExprKind::Float(b), tupa_parser::BinaryOp::Add) => Expr {
345                        kind: ExprKind::Float(a + b),
346                        span: expr.span,
347                    },
348                    (ExprKind::Float(a), ExprKind::Float(b), tupa_parser::BinaryOp::Sub) => Expr {
349                        kind: ExprKind::Float(a - b),
350                        span: expr.span,
351                    },
352                    (ExprKind::Float(a), ExprKind::Float(b), tupa_parser::BinaryOp::Mul) => Expr {
353                        kind: ExprKind::Float(a * b),
354                        span: expr.span,
355                    },
356                    (ExprKind::Float(a), ExprKind::Float(b), tupa_parser::BinaryOp::Div)
357                        if *b != 0.0 =>
358                    {
359                        Expr {
360                            kind: ExprKind::Float(a / b),
361                            span: expr.span,
362                        }
363                    }
364                    (ExprKind::Float(a), ExprKind::Float(b), tupa_parser::BinaryOp::Pow) => Expr {
365                        kind: ExprKind::Float(a.powf(*b)),
366                        span: expr.span,
367                    },
368                    (ExprKind::Bool(a), ExprKind::Bool(b), tupa_parser::BinaryOp::And) => Expr {
369                        kind: ExprKind::Bool(*a && *b),
370                        span: expr.span,
371                    },
372                    (ExprKind::Bool(a), ExprKind::Bool(b), tupa_parser::BinaryOp::Or) => Expr {
373                        kind: ExprKind::Bool(*a || *b),
374                        span: expr.span,
375                    },
376                    _ => Expr {
377                        kind: ExprKind::Binary {
378                            op: op.clone(),
379                            left: Box::new(folded_left),
380                            right: Box::new(folded_right),
381                        },
382                        span: expr.span,
383                    },
384                }
385            }
386            ExprKind::Unary { op, expr: operand } => {
387                let folded_operand = self.fold_expr(operand);
388
389                match (&folded_operand.kind, op) {
390                    (ExprKind::Int(a), tupa_parser::UnaryOp::Neg) => Expr {
391                        kind: ExprKind::Int(-a),
392                        span: expr.span,
393                    },
394                    (ExprKind::Float(a), tupa_parser::UnaryOp::Neg) => Expr {
395                        kind: ExprKind::Float(-a),
396                        span: expr.span,
397                    },
398                    (ExprKind::Bool(a), tupa_parser::UnaryOp::Not) => Expr {
399                        kind: ExprKind::Bool(!a),
400                        span: expr.span,
401                    },
402                    _ => Expr {
403                        kind: ExprKind::Unary {
404                            op: op.clone(),
405                            expr: Box::new(folded_operand),
406                        },
407                        span: expr.span,
408                    },
409                }
410            }
411            _ => expr.clone(),
412        }
413    }
414
415    fn collect_used_in_expr(&self, expr: &Expr, env: &mut HashMap<String, bool>) {
416        match &expr.kind {
417            ExprKind::Int(_)
418            | ExprKind::Float(_)
419            | ExprKind::Str(_)
420            | ExprKind::Bool(_)
421            | ExprKind::Null => {}
422            ExprKind::Ident(name) => {
423                if let Some(used) = env.get_mut(name) {
424                    *used = true;
425                }
426            }
427            ExprKind::Binary { left, right, .. } => {
428                self.collect_used_in_expr(left, env);
429                self.collect_used_in_expr(right, env);
430            }
431            ExprKind::Unary { expr: operand, .. } => {
432                self.collect_used_in_expr(operand, env);
433            }
434            ExprKind::Call { callee, args } => {
435                self.collect_used_in_expr(callee, env);
436                for arg in args {
437                    self.collect_used_in_expr(arg, env);
438                }
439            }
440            ExprKind::Assign { name: _name, expr } => {
441                self.collect_used_in_expr(expr, env);
442            }
443            ExprKind::Index { expr: array, index } => {
444                self.collect_used_in_expr(array, env);
445                self.collect_used_in_expr(index, env);
446            }
447            ExprKind::ArrayLiteral(elements) => {
448                for elem in elements {
449                    self.collect_used_in_expr(elem, env);
450                }
451            }
452            ExprKind::Tuple(items) => {
453                for item in items {
454                    self.collect_used_in_expr(item, env);
455                }
456            }
457            ExprKind::Field { expr: base, .. } => {
458                self.collect_used_in_expr(base, env);
459            }
460            ExprKind::Lambda { params, body } => {
461                let mut inner = env.clone();
462                for param in params {
463                    inner.insert(param.clone(), false);
464                }
465                self.collect_used_in_expr(body, &mut inner);
466                self.merge_used_vars(env, &inner);
467            }
468            ExprKind::Block(stmts) => {
469                let mut inner = env.clone();
470                self.collect_used_in_block(stmts, &mut inner);
471                self.merge_used_vars(env, &inner);
472            }
473            ExprKind::If {
474                condition,
475                then_branch,
476                else_branch,
477            } => {
478                self.collect_used_in_expr(condition, env);
479                let mut then_env = env.clone();
480                self.collect_used_in_block(then_branch, &mut then_env);
481                if let Some(else_branch) = else_branch {
482                    let mut else_env = env.clone();
483                    match else_branch {
484                        tupa_parser::ElseBranch::Block(block) => {
485                            self.collect_used_in_block(block, &mut else_env)
486                        }
487                        tupa_parser::ElseBranch::If(expr) => {
488                            self.collect_used_in_expr(expr, &mut else_env)
489                        }
490                    }
491                    self.merge_used_vars(env, &then_env);
492                    self.merge_used_vars(env, &else_env);
493                } else {
494                    self.merge_used_vars(env, &then_env);
495                }
496            }
497            ExprKind::AssignIndex {
498                expr: array,
499                index,
500                value,
501            } => {
502                self.collect_used_in_expr(array, env);
503                self.collect_used_in_expr(index, env);
504                self.collect_used_in_expr(value, env);
505            }
506            ExprKind::Await(expr) => {
507                self.collect_used_in_expr(expr, env);
508            }
509            ExprKind::Match {
510                expr: match_expr,
511                arms,
512            } => {
513                self.collect_used_in_expr(match_expr, env);
514                for arm in arms {
515                    let mut arm_env = env.clone();
516                    if let tupa_parser::Pattern::Ident(name) = &arm.pattern {
517                        arm_env.insert(name.clone(), false);
518                    }
519                    if let Some(guard) = &arm.guard {
520                        self.collect_used_in_expr(guard, &mut arm_env);
521                    }
522                    self.collect_used_in_expr(&arm.expr, &mut arm_env);
523                    self.merge_used_vars(env, &arm_env);
524                }
525            }
526        }
527    }
528
529    fn emit_function(&mut self, func: &Function) {
530        let mut env: HashMap<String, LocalVar> = HashMap::new();
531        self.used_vars = self.collect_used_vars(func);
532        let ret_ty = match func.return_type.as_ref() {
533            Some(Type::Ident(name)) if name == "i64" => SimpleTy::I64,
534            Some(Type::Ident(name)) if name == "f64" => SimpleTy::F64,
535            Some(Type::Ident(name)) if name == "bool" => SimpleTy::Bool,
536            Some(Type::Ident(name)) if name == "string" => SimpleTy::Str,
537            Some(Type::Array { elem, .. }) if matches!(**elem, Type::Ident(ref n) if n == "i64") => {
538                SimpleTy::I64Ptr
539            }
540            Some(Type::Array { elem, .. }) if matches!(**elem, Type::Ident(ref n) if n == "f64") => {
541                SimpleTy::F64Ptr
542            }
543            Some(Type::Array { elem, .. }) if matches!(**elem, Type::Ident(ref n) if n == "string") => {
544                SimpleTy::StrPtr
545            }
546            Some(Type::Array { elem, .. }) if matches!(**elem, Type::Ident(ref n) if n == "bool") => {
547                SimpleTy::BoolPtr
548            }
549            Some(Type::Slice { elem }) if matches!(**elem, Type::Ident(ref n) if n == "i64") => {
550                SimpleTy::I64Ptr
551            }
552            Some(Type::Slice { elem }) if matches!(**elem, Type::Ident(ref n) if n == "f64") => {
553                SimpleTy::F64Ptr
554            }
555            Some(Type::Slice { elem }) if matches!(**elem, Type::Ident(ref n) if n == "string") => {
556                SimpleTy::StrPtr
557            }
558            Some(Type::Slice { elem }) if matches!(**elem, Type::Ident(ref n) if n == "bool") => {
559                SimpleTy::BoolPtr
560            }
561            Some(_) => SimpleTy::Unknown,
562            None => SimpleTy::Void,
563        };
564        let llvm_ret = match ret_ty {
565            SimpleTy::I64 => "i64",
566            SimpleTy::F64 => "double",
567            SimpleTy::Bool => "i1",
568            SimpleTy::I64Ptr => "i64*",
569            SimpleTy::F64Ptr => "double*",
570            SimpleTy::Str => "i8*",
571            SimpleTy::StrPtr => "i8**",
572            SimpleTy::BoolPtr => "i1*",
573            _ => "void",
574        };
575
576        let params = func
577            .params
578            .iter()
579            .map(|p| {
580                let ty = self.map_type(&p.ty);
581                format!("{} %{}", ty, p.name)
582            })
583            .collect::<Vec<_>>()
584            .join(", ");
585
586        self.lines
587            .push(format!("define {llvm_ret} @{}({params}) {{", func.name));
588        self.lines.push("entry:".to_string());
589
590        for param in &func.params {
591            if !self.is_var_used(&param.name) {
592                continue;
593            }
594            let ty = match self.map_type(&param.ty).as_str() {
595                "i64" => SimpleTy::I64,
596                "double" => SimpleTy::F64,
597                "i1" => SimpleTy::Bool,
598                "i64*" => SimpleTy::I64Ptr,
599                "double*" => SimpleTy::F64Ptr,
600                "i8*" => SimpleTy::Str,
601                "i8**" => SimpleTy::StrPtr,
602                "i1*" => SimpleTy::BoolPtr,
603                _ => SimpleTy::Unknown,
604            };
605            let ptr = format!("%{}", param.name);
606            let alloca = self.fresh_temp();
607            self.lines
608                .push(format!("  {alloca} = alloca {}", self.map_type(&param.ty)));
609            self.lines.push(format!(
610                "  store {} {ptr}, {}* {alloca}",
611                self.map_type(&param.ty),
612                self.map_type(&param.ty)
613            ));
614            env.insert(
615                param.name.clone(),
616                LocalVar {
617                    ptr: alloca,
618                    ty,
619                    used: false,
620                },
621            );
622        }
623
624        let mut returned = false;
625        let last_index = func.body.len().saturating_sub(1);
626        for (idx, stmt) in func.body.iter().enumerate() {
627            if returned {
628                break;
629            }
630            if idx == last_index {
631                if let Stmt::Expr(expr) = stmt {
632                    if ret_ty != SimpleTy::Void {
633                        let value = self.emit_expr(expr, &mut env);
634                        let llvm_ty = self.llvm_ty(value.ty);
635                        self.lines
636                            .push(format!("  ret {llvm_ty} {}", value.llvm_value));
637                        returned = true;
638                        break;
639                    }
640                }
641            }
642            if self.emit_stmt(stmt, &mut env, ret_ty) == ControlFlow::Return {
643                returned = true;
644            }
645        }
646
647        if returned {
648            self.lines.push("}".to_string());
649            self.lines.push(String::new());
650            return;
651        }
652
653        if ret_ty == SimpleTy::I64 {
654            self.lines.push("  ret i64 0".to_string());
655        } else if ret_ty == SimpleTy::F64 {
656            self.lines.push("  ret double 0.0".to_string());
657        } else if ret_ty == SimpleTy::Bool {
658            self.lines.push("  ret i1 0".to_string());
659        } else if ret_ty == SimpleTy::I64Ptr {
660            self.lines.push("  ret i64* null".to_string());
661        } else if ret_ty == SimpleTy::F64Ptr {
662            self.lines.push("  ret double* null".to_string());
663        } else if ret_ty == SimpleTy::Str {
664            self.lines.push("  ret i8* null".to_string());
665        } else if ret_ty == SimpleTy::StrPtr {
666            self.lines.push("  ret i8** null".to_string());
667        } else if ret_ty == SimpleTy::BoolPtr {
668            self.lines.push("  ret i1* null".to_string());
669        } else {
670            self.lines.push("  ret void".to_string());
671        }
672        self.lines.push("}".to_string());
673        self.lines.push(String::new());
674    }
675
676    fn emit_stmt(
677        &mut self,
678        stmt: &Stmt,
679        env: &mut HashMap<String, LocalVar>,
680        ret_ty: SimpleTy,
681    ) -> ControlFlow {
682        match stmt {
683            Stmt::Let { name, expr, .. } => {
684                if !self.is_var_used(name) {
685                    self.emit_expr(expr, env);
686                    return ControlFlow::None;
687                }
688                let value = self.emit_expr(expr, env);
689                let alloca = self.fresh_temp();
690                let llvm_ty = self.llvm_ty(value.ty);
691                self.lines.push(format!("  {alloca} = alloca {llvm_ty}"));
692                self.lines.push(format!(
693                    "  store {llvm_ty} {}, {llvm_ty}* {alloca}",
694                    value.llvm_value
695                ));
696                env.insert(
697                    name.clone(),
698                    LocalVar {
699                        ptr: alloca,
700                        ty: value.ty,
701                        used: false,
702                    },
703                );
704                ControlFlow::None
705            }
706            Stmt::While { condition, body } => {
707                let head = self.fresh_label("while.head");
708                let body_label = self.fresh_label("while.body");
709                let end_label = self.fresh_label("while.end");
710
711                self.loop_stack.push(LoopLabels {
712                    break_label: end_label.clone(),
713                    continue_label: head.clone(),
714                });
715
716                self.lines.push(format!("  br label %{head}"));
717                self.lines.push(format!("{head}:"));
718                let cond_val = self.emit_expr(condition, env);
719                let cond = if cond_val.ty == SimpleTy::Bool {
720                    cond_val.llvm_value
721                } else {
722                    "0".to_string()
723                };
724                self.lines.push(format!(
725                    "  br i1 {cond}, label %{body_label}, label %{end_label}"
726                ));
727                self.lines.push(format!("{body_label}:"));
728                let mut body_used = HashMap::new();
729                self.collect_used_in_block(body, &mut body_used);
730                let previous_used = std::mem::take(&mut self.used_vars);
731                self.used_vars = body_used;
732                let body_flow = self.emit_block(body, env, ret_ty);
733                self.used_vars = previous_used;
734                let body_terminates = matches!(
735                    body_flow,
736                    ControlFlow::Break | ControlFlow::Continue | ControlFlow::Return
737                );
738                if !body_terminates {
739                    self.lines.push(format!("  br label %{head}"));
740                }
741                self.lines.push(format!("{end_label}:"));
742                self.loop_stack.pop();
743                match body_flow {
744                    ControlFlow::Return => ControlFlow::Return,
745                    _ => ControlFlow::None,
746                }
747            }
748            Stmt::For { name, iter, body } => {
749                let (start, end) = match self.extract_range(iter, env) {
750                    Some(range) => range,
751                    None => {
752                        // Unsupported for iterator - only ranges are supported
753                        return ControlFlow::None;
754                    }
755                };
756
757                let mut body_used = HashMap::new();
758                body_used.insert(name.clone(), false);
759                self.collect_used_in_block(body, &mut body_used);
760                let should_bind = body_used.get(name).copied().unwrap_or(false);
761                let previous_used = std::mem::take(&mut self.used_vars);
762                self.used_vars = body_used;
763
764                let idx_alloca = self.fresh_temp();
765                self.lines.push("  ; for-range".to_string());
766                self.lines.push(format!("  {idx_alloca} = alloca i64"));
767                self.lines.push(format!(
768                    "  store i64 {}, i64* {idx_alloca}",
769                    start.llvm_value
770                ));
771
772                let mut previous = None;
773                let mut binding_active = false;
774                let loop_var_alloca = if should_bind {
775                    let alloca = self.fresh_temp();
776                    self.lines.push(format!("  {alloca} = alloca i64"));
777                    previous = env.insert(
778                        name.clone(),
779                        LocalVar {
780                            ptr: alloca.clone(),
781                            ty: SimpleTy::I64,
782                            used: false,
783                        },
784                    );
785                    binding_active = true;
786                    Some(alloca)
787                } else {
788                    None
789                };
790
791                let head = self.fresh_label("for.head");
792                let body_label = self.fresh_label("for.body");
793                let step_label = self.fresh_label("for.step");
794                let end_label = self.fresh_label("for.end");
795
796                self.loop_stack.push(LoopLabels {
797                    break_label: end_label.clone(),
798                    continue_label: step_label.clone(),
799                });
800
801                self.lines.push(format!("  br label %{head}"));
802                self.lines.push(format!("{head}:"));
803                let idx_val = self.fresh_temp();
804                self.lines
805                    .push(format!("  {idx_val} = load i64, i64* {idx_alloca}"));
806                let cmp = self.fresh_temp();
807                self.lines.push(format!(
808                    "  {cmp} = icmp slt i64 {idx_val}, {}",
809                    end.llvm_value
810                ));
811                self.lines.push(format!(
812                    "  br i1 {cmp}, label %{body_label}, label %{end_label}"
813                ));
814
815                self.lines.push(format!("{body_label}:"));
816                if let Some(loop_var_alloca) = &loop_var_alloca {
817                    self.lines
818                        .push(format!("  store i64 {idx_val}, i64* {loop_var_alloca}"));
819                }
820                let body_flow = self.emit_block(body, env, ret_ty);
821                if !matches!(
822                    body_flow,
823                    ControlFlow::Break | ControlFlow::Continue | ControlFlow::Return
824                ) {
825                    self.lines.push(format!("  br label %{step_label}"));
826                }
827
828                self.lines.push(format!("{step_label}:"));
829                let idx_next = self.fresh_temp();
830                self.lines
831                    .push(format!("  {idx_next} = add i64 {idx_val}, 1"));
832                self.lines
833                    .push(format!("  store i64 {idx_next}, i64* {idx_alloca}"));
834                self.lines.push(format!("  br label %{head}"));
835
836                self.lines.push(format!("{end_label}:"));
837                self.loop_stack.pop();
838                if binding_active {
839                    if let Some(prev) = previous {
840                        env.insert(name.clone(), prev);
841                    } else {
842                        env.remove(name);
843                    }
844                }
845                self.used_vars = previous_used;
846                match body_flow {
847                    ControlFlow::Return => ControlFlow::Return,
848                    _ => ControlFlow::None,
849                }
850            }
851            Stmt::Break => {
852                if let Some(labels) = self.loop_stack.last() {
853                    self.lines
854                        .push(format!("  br label %{}", labels.break_label));
855                    ControlFlow::Break
856                } else {
857                    // Break outside loop - this should be caught by parser/typechecker
858                    // For now, we'll treat it as unreachable code
859                    self.lines
860                        .push("  ; break outside loop - unreachable".to_string());
861                    ControlFlow::None
862                }
863            }
864            Stmt::Continue => {
865                if let Some(labels) = self.loop_stack.last() {
866                    self.lines
867                        .push(format!("  br label %{}", labels.continue_label));
868                    ControlFlow::Continue
869                } else {
870                    // Continue outside loop - this should be caught by parser/typechecker
871                    // For now, we'll treat it as unreachable code
872                    self.lines
873                        .push("  ; continue outside loop - unreachable".to_string());
874                    ControlFlow::None
875                }
876            }
877            Stmt::Expr(expr) => self.emit_expr_stmt(expr, env, ret_ty),
878            Stmt::Return(expr) => {
879                if let Some(expr) = expr {
880                    let value = self.emit_expr(expr, env);
881                    let llvm_ty = self.llvm_ty(value.ty);
882                    self.lines
883                        .push(format!("  ret {llvm_ty} {}", value.llvm_value));
884                } else {
885                    match ret_ty {
886                        SimpleTy::I64 => self.lines.push("  ret i64 0".to_string()),
887                        SimpleTy::Bool => self.lines.push("  ret i1 0".to_string()),
888                        SimpleTy::I64Ptr => self.lines.push("  ret i64* null".to_string()),
889                        SimpleTy::Str => self.lines.push("  ret i8* null".to_string()),
890                        _ => self.lines.push("  ret void".to_string()),
891                    }
892                }
893                ControlFlow::Return
894            }
895            Stmt::Lambda { .. } => {
896                // Not supported as a statement; skip or error as needed
897                ControlFlow::None
898            }
899        }
900    }
901
902    fn emit_expr_stmt(
903        &mut self,
904        expr: &Expr,
905        env: &mut HashMap<String, LocalVar>,
906        ret_ty: SimpleTy,
907    ) -> ControlFlow {
908        match &expr.kind {
909            ExprKind::If {
910                condition,
911                then_branch,
912                else_branch,
913            } => self.emit_if_stmt(condition, then_branch, else_branch.as_ref(), env, ret_ty),
914            ExprKind::Match { expr, arms } => self.emit_match_stmt(expr, arms, env, ret_ty),
915            _ => {
916                self.emit_expr(expr, env);
917                ControlFlow::None
918            }
919        }
920    }
921
922    fn emit_block(
923        &mut self,
924        stmts: &[Stmt],
925        env: &mut HashMap<String, LocalVar>,
926        ret_ty: SimpleTy,
927    ) -> ControlFlow {
928        for stmt in stmts {
929            let flow = self.emit_stmt(stmt, env, ret_ty);
930            if flow != ControlFlow::None {
931                return flow;
932            }
933        }
934        ControlFlow::None
935    }
936
937    fn emit_if_stmt(
938        &mut self,
939        condition: &Expr,
940        then_branch: &[Stmt],
941        else_branch: Option<&tupa_parser::ElseBranch>,
942        env: &mut HashMap<String, LocalVar>,
943        ret_ty: SimpleTy,
944    ) -> ControlFlow {
945        let cond_val = self.emit_expr(condition, env);
946        let cond = if cond_val.ty == SimpleTy::Bool {
947            cond_val.llvm_value
948        } else {
949            "0".to_string()
950        };
951        let then_label = self.fresh_label("if.then");
952        let else_label = self.fresh_label("if.else");
953        let end_label = self.fresh_label("if.end");
954
955        self.lines.push(format!(
956            "  br i1 {cond}, label %{then_label}, label %{else_label}"
957        ));
958        self.lines.push(format!("{then_label}:"));
959        let then_flow = self.emit_block(then_branch, env, ret_ty);
960        if then_flow == ControlFlow::None {
961            self.lines.push(format!("  br label %{end_label}"));
962        }
963        self.lines.push(format!("{else_label}:"));
964        let else_flow = match else_branch {
965            Some(tupa_parser::ElseBranch::Block(block)) => self.emit_block(block, env, ret_ty),
966            Some(tupa_parser::ElseBranch::If(expr)) => self.emit_expr_stmt(expr, env, ret_ty),
967            None => ControlFlow::None,
968        };
969        if else_flow == ControlFlow::None {
970            self.lines.push(format!("  br label %{end_label}"));
971        }
972        self.lines.push(format!("{end_label}:"));
973        match (then_flow, else_flow) {
974            (ControlFlow::Return, ControlFlow::Return) => ControlFlow::Return,
975            _ => ControlFlow::None,
976        }
977    }
978
979    fn emit_match_stmt(
980        &mut self,
981        scrutinee: &Expr,
982        arms: &[tupa_parser::MatchArm],
983        env: &mut HashMap<String, LocalVar>,
984        ret_ty: SimpleTy,
985    ) -> ControlFlow {
986        let value = self.emit_expr(scrutinee, env);
987        if value.ty != SimpleTy::I64 && value.ty != SimpleTy::Str && value.ty != SimpleTy::Bool {
988            // Match on unsupported type - for now, only support i64, str, and bool
989            return ControlFlow::None;
990        }
991        let end_label = self.fresh_label("match.end");
992        let mut arm_labels: Vec<String> = Vec::new();
993        let mut fallthrough_labels: Vec<String> = Vec::new();
994        let mut guard_labels: Vec<Option<String>> = Vec::new();
995
996        for _ in arms {
997            arm_labels.push(self.fresh_label("match.arm"));
998            fallthrough_labels.push(self.fresh_label("match.next"));
999            guard_labels.push(None);
1000        }
1001
1002        for (idx, arm) in arms.iter().enumerate() {
1003            if arm.guard.is_some() {
1004                guard_labels[idx] = Some(self.fresh_label("match.guard"));
1005            }
1006        }
1007
1008        for (idx, arm) in arms.iter().enumerate() {
1009            let next_label = if idx + 1 < arms.len() {
1010                &fallthrough_labels[idx]
1011            } else {
1012                &end_label
1013            };
1014            let arm_target = guard_labels[idx].as_ref().unwrap_or(&arm_labels[idx]);
1015            let binding_name = match &arm.pattern {
1016                tupa_parser::Pattern::Ident(name) => Some(name.clone()),
1017                _ => None,
1018            };
1019            let should_bind = binding_name
1020                .as_ref()
1021                .map(|name| self.is_var_used(name))
1022                .unwrap_or(false);
1023            match (&arm.pattern, value.ty) {
1024                (tupa_parser::Pattern::Int(value_literal), SimpleTy::I64) => {
1025                    let cmp = self.fresh_temp();
1026                    self.lines.push(format!(
1027                        "  {cmp} = icmp eq i64 {}, {}",
1028                        value.llvm_value, value_literal
1029                    ));
1030                    self.lines.push(format!(
1031                        "  br i1 {cmp}, label %{}, label %{}",
1032                        arm_target, next_label
1033                    ));
1034                }
1035                (tupa_parser::Pattern::Bool(value_literal), SimpleTy::Bool) => {
1036                    let cmp = self.fresh_temp();
1037                    let literal = if *value_literal { "1" } else { "0" };
1038                    self.lines.push(format!(
1039                        "  {cmp} = icmp eq i1 {}, {}",
1040                        value.llvm_value, literal
1041                    ));
1042                    self.lines.push(format!(
1043                        "  br i1 {cmp}, label %{}, label %{}",
1044                        arm_target, next_label
1045                    ));
1046                }
1047                (tupa_parser::Pattern::Str(lit), SimpleTy::Str) => {
1048                    let (global, len) = self.intern_string(lit);
1049                    let ptr = self.fresh_temp();
1050                    self.lines.push(format!(
1051                        "  {ptr} = getelementptr inbounds [{len} x i8], [{len} x i8]* {global}, i64 0, i64 0"
1052                    ));
1053                    self.declare_strcmp();
1054                    let cmp = self.fresh_temp();
1055                    self.lines.push(format!(
1056                        "  {cmp} = call i32 @strcmp(i8* {}, i8* {ptr})",
1057                        value.llvm_value
1058                    ));
1059                    let is_eq = self.fresh_temp();
1060                    self.lines.push(format!("  {is_eq} = icmp eq i32 {cmp}, 0"));
1061                    self.lines.push(format!(
1062                        "  br i1 {is_eq}, label %{}, label %{}",
1063                        arm_target, next_label
1064                    ));
1065                }
1066                (tupa_parser::Pattern::Wildcard, _) | (tupa_parser::Pattern::Ident(_), _) => {
1067                    self.lines.push(format!("  br label %{}", arm_target));
1068                }
1069                _ => {
1070                    self.lines.push(format!("  br label %{}", next_label));
1071                }
1072            }
1073
1074            let mut prev_binding: Option<LocalVar> = None;
1075            let mut bound = false;
1076            let mut binding_active = false;
1077            if let Some(guard_label) = &guard_labels[idx] {
1078                self.lines.push(format!("{guard_label}:"));
1079                if let Some(name) = &binding_name {
1080                    if should_bind {
1081                        let llvm_ty = self.llvm_ty(value.ty);
1082                        let alloca = self.fresh_temp();
1083                        self.lines.push(format!("  {alloca} = alloca {llvm_ty}"));
1084                        self.lines.push(format!(
1085                            "  store {llvm_ty} {}, {llvm_ty}* {alloca}",
1086                            value.llvm_value
1087                        ));
1088                        prev_binding = env.insert(
1089                            name.clone(),
1090                            LocalVar {
1091                                ptr: alloca,
1092                                ty: value.ty,
1093                                used: false,
1094                            },
1095                        );
1096                        bound = true;
1097                        binding_active = true;
1098                    }
1099                }
1100                let guard_value = self.emit_expr(arm.guard.as_ref().unwrap(), env);
1101                let cond = if guard_value.ty == SimpleTy::Bool {
1102                    guard_value.llvm_value
1103                } else {
1104                    "0".to_string()
1105                };
1106                self.lines.push(format!(
1107                    "  br i1 {cond}, label %{}, label %{}",
1108                    arm_labels[idx], next_label
1109                ));
1110            }
1111
1112            self.lines.push(format!("{}:", arm_labels[idx]));
1113            if !bound {
1114                if let Some(name) = &binding_name {
1115                    if should_bind {
1116                        let llvm_ty = self.llvm_ty(value.ty);
1117                        let alloca = self.fresh_temp();
1118                        self.lines.push(format!("  {alloca} = alloca {llvm_ty}"));
1119                        self.lines.push(format!(
1120                            "  store {llvm_ty} {}, {llvm_ty}* {alloca}",
1121                            value.llvm_value
1122                        ));
1123                        prev_binding = env.insert(
1124                            name.clone(),
1125                            LocalVar {
1126                                ptr: alloca,
1127                                ty: value.ty,
1128                                used: false,
1129                            },
1130                        );
1131                        binding_active = true;
1132                    }
1133                }
1134            }
1135            let returned = match &arm.expr.kind {
1136                ExprKind::Block(stmts) => self.emit_block(stmts, env, ret_ty),
1137                _ => self.emit_expr_stmt(&arm.expr, env, ret_ty),
1138            };
1139            if returned == ControlFlow::None {
1140                self.lines.push(format!("  br label %{end_label}"));
1141            }
1142            if binding_active {
1143                if let Some(name) = &binding_name {
1144                    if let Some(prev) = prev_binding.take() {
1145                        env.insert(name.clone(), prev);
1146                    } else {
1147                        env.remove(name);
1148                    }
1149                }
1150            }
1151            if idx + 1 < arms.len() {
1152                self.lines.push(format!("{}:", fallthrough_labels[idx]));
1153            }
1154        }
1155
1156        self.lines.push(format!("{end_label}:"));
1157        ControlFlow::None
1158    }
1159
1160    fn emit_block_expr(
1161        &mut self,
1162        stmts: &[Stmt],
1163        env: &mut HashMap<String, LocalVar>,
1164        ret_ty: SimpleTy,
1165    ) -> ExprValue {
1166        if stmts.is_empty() {
1167            return ExprValue::new(SimpleTy::Void, "0".to_string());
1168        }
1169        let last_index = stmts.len().saturating_sub(1);
1170        for (idx, stmt) in stmts.iter().enumerate() {
1171            if idx == last_index {
1172                if let Stmt::Expr(expr) = stmt {
1173                    return self.emit_expr(expr, env);
1174                }
1175                self.emit_stmt(stmt, env, ret_ty);
1176                return ExprValue::new(SimpleTy::Void, "0".to_string());
1177            }
1178            if self.emit_stmt(stmt, env, ret_ty) == ControlFlow::Return {
1179                return ExprValue::new(SimpleTy::Void, "0".to_string());
1180            }
1181        }
1182        ExprValue::new(SimpleTy::Void, "0".to_string())
1183    }
1184
1185    fn infer_block_expr_ty(&self, stmts: &[Stmt], env: &HashMap<String, LocalVar>) -> SimpleTy {
1186        for stmt in stmts.iter().rev() {
1187            match stmt {
1188                Stmt::Expr(expr) => return self.infer_expr_ty(expr, env),
1189                Stmt::Return(Some(expr)) => return self.infer_expr_ty(expr, env),
1190                Stmt::Return(None) => return SimpleTy::Void,
1191                _ => continue,
1192            }
1193        }
1194        SimpleTy::Void
1195    }
1196
1197    fn infer_expr_ty(&self, expr: &Expr, env: &HashMap<String, LocalVar>) -> SimpleTy {
1198        match &expr.kind {
1199            ExprKind::Int(_) => SimpleTy::I64,
1200            ExprKind::Float(_) => SimpleTy::F64,
1201            ExprKind::Bool(_) => SimpleTy::Bool,
1202            ExprKind::Str(_) => SimpleTy::Str,
1203            ExprKind::ArrayLiteral(items) => {
1204                if items.is_empty() {
1205                    return SimpleTy::I64Ptr;
1206                }
1207                let first = self.infer_expr_ty(&items[0], env);
1208                if first == SimpleTy::I64 {
1209                    SimpleTy::I64Ptr
1210                } else if first == SimpleTy::F64 {
1211                    SimpleTy::F64Ptr
1212                } else if first == SimpleTy::Str {
1213                    SimpleTy::StrPtr
1214                } else if first == SimpleTy::Bool {
1215                    SimpleTy::BoolPtr
1216                } else {
1217                    SimpleTy::Unknown
1218                }
1219            }
1220            ExprKind::Tuple(_) => SimpleTy::Unknown,
1221            ExprKind::Ident(name) => env.get(name).map(|v| v.ty).unwrap_or(SimpleTy::Unknown),
1222            ExprKind::Assign { expr, .. } => self.infer_expr_ty(expr, env),
1223            ExprKind::AssignIndex { expr, .. } => match self.infer_expr_ty(expr, env) {
1224                SimpleTy::F64Ptr => SimpleTy::F64,
1225                SimpleTy::I64Ptr => SimpleTy::I64,
1226                SimpleTy::StrPtr => SimpleTy::Str,
1227                SimpleTy::BoolPtr => SimpleTy::Bool,
1228                _ => SimpleTy::Unknown,
1229            },
1230            ExprKind::Index { expr, .. } => match self.infer_expr_ty(expr, env) {
1231                SimpleTy::F64Ptr => SimpleTy::F64,
1232                SimpleTy::I64Ptr => SimpleTy::I64,
1233                SimpleTy::StrPtr => SimpleTy::Str,
1234                SimpleTy::BoolPtr => SimpleTy::Bool,
1235                _ => SimpleTy::Unknown,
1236            },
1237            ExprKind::Unary { op, expr } => {
1238                let inner = self.infer_expr_ty(expr, env);
1239                match (op, inner) {
1240                    (tupa_parser::UnaryOp::Neg, SimpleTy::I64) => SimpleTy::I64,
1241                    (tupa_parser::UnaryOp::Neg, SimpleTy::F64) => SimpleTy::F64,
1242                    (tupa_parser::UnaryOp::Not, SimpleTy::Bool) => SimpleTy::Bool,
1243                    _ => SimpleTy::Unknown,
1244                }
1245            }
1246            ExprKind::Binary { op, left, right } => {
1247                let l = self.infer_expr_ty(left, env);
1248                let r = self.infer_expr_ty(right, env);
1249                match (l, r) {
1250                    (SimpleTy::I64, SimpleTy::I64) => match op {
1251                        tupa_parser::BinaryOp::Equal
1252                        | tupa_parser::BinaryOp::NotEqual
1253                        | tupa_parser::BinaryOp::Less
1254                        | tupa_parser::BinaryOp::LessEqual
1255                        | tupa_parser::BinaryOp::Greater
1256                        | tupa_parser::BinaryOp::GreaterEqual => SimpleTy::Bool,
1257                        _ => SimpleTy::I64,
1258                    },
1259                    (SimpleTy::F64, SimpleTy::F64) => match op {
1260                        tupa_parser::BinaryOp::Equal
1261                        | tupa_parser::BinaryOp::NotEqual
1262                        | tupa_parser::BinaryOp::Less
1263                        | tupa_parser::BinaryOp::LessEqual
1264                        | tupa_parser::BinaryOp::Greater
1265                        | tupa_parser::BinaryOp::GreaterEqual => SimpleTy::Bool,
1266                        _ => SimpleTy::F64,
1267                    },
1268                    (SimpleTy::Bool, SimpleTy::Bool) => match op {
1269                        tupa_parser::BinaryOp::And
1270                        | tupa_parser::BinaryOp::Or
1271                        | tupa_parser::BinaryOp::Equal
1272                        | tupa_parser::BinaryOp::NotEqual => SimpleTy::Bool,
1273                        _ => SimpleTy::Unknown,
1274                    },
1275                    (SimpleTy::Str, SimpleTy::Str) => match op {
1276                        tupa_parser::BinaryOp::Add => SimpleTy::Str,
1277                        tupa_parser::BinaryOp::Equal | tupa_parser::BinaryOp::NotEqual => {
1278                            SimpleTy::Bool
1279                        }
1280                        _ => SimpleTy::Unknown,
1281                    },
1282                    (SimpleTy::Str, SimpleTy::I64 | SimpleTy::F64 | SimpleTy::Bool)
1283                    | (SimpleTy::I64 | SimpleTy::F64 | SimpleTy::Bool, SimpleTy::Str)
1284                        if matches!(op, tupa_parser::BinaryOp::Add) =>
1285                    {
1286                        SimpleTy::Str
1287                    }
1288                    _ => SimpleTy::Unknown,
1289                }
1290            }
1291            ExprKind::Call { callee, .. } => {
1292                if let ExprKind::Ident(name) = &callee.kind {
1293                    if name == "print" {
1294                        return SimpleTy::Void;
1295                    }
1296                    if let Some(sig) = self.function_sigs.get(name) {
1297                        return sig.ret;
1298                    }
1299                }
1300                SimpleTy::Unknown
1301            }
1302            ExprKind::If {
1303                then_branch,
1304                else_branch,
1305                ..
1306            } => match else_branch {
1307                Some(tupa_parser::ElseBranch::Block(block)) => {
1308                    let then_ty = self.infer_block_expr_ty(then_branch, env);
1309                    let else_ty = self.infer_block_expr_ty(block, env);
1310                    if then_ty == else_ty {
1311                        then_ty
1312                    } else {
1313                        SimpleTy::Unknown
1314                    }
1315                }
1316                Some(tupa_parser::ElseBranch::If(expr)) => {
1317                    let then_ty = self.infer_block_expr_ty(then_branch, env);
1318                    let else_ty = self.infer_expr_ty(expr, env);
1319                    if then_ty == else_ty {
1320                        then_ty
1321                    } else {
1322                        SimpleTy::Unknown
1323                    }
1324                }
1325                None => SimpleTy::Void,
1326            },
1327            ExprKind::Match { arms, .. } => {
1328                let mut expected: Option<SimpleTy> = None;
1329                for arm in arms {
1330                    let arm_ty = match &arm.expr.kind {
1331                        ExprKind::Block(stmts) => self.infer_block_expr_ty(stmts, env),
1332                        _ => self.infer_expr_ty(&arm.expr, env),
1333                    };
1334                    match expected {
1335                        None => expected = Some(arm_ty),
1336                        Some(prev) if prev != arm_ty => return SimpleTy::Unknown,
1337                        _ => {}
1338                    }
1339                }
1340                expected.unwrap_or(SimpleTy::Void)
1341            }
1342            ExprKind::Block(stmts) => self.infer_block_expr_ty(stmts, env),
1343            _ => SimpleTy::Unknown,
1344        }
1345    }
1346
1347    fn emit_if_expr(
1348        &mut self,
1349        condition: &Expr,
1350        then_branch: &[Stmt],
1351        else_branch: Option<&tupa_parser::ElseBranch>,
1352        env: &mut HashMap<String, LocalVar>,
1353        ret_ty: SimpleTy,
1354    ) -> ExprValue {
1355        if else_branch.is_none() {
1356            self.emit_if_stmt(condition, then_branch, else_branch, env, ret_ty);
1357            return ExprValue::new(SimpleTy::Void, "0".to_string());
1358        }
1359        let result_ty = match else_branch {
1360            Some(tupa_parser::ElseBranch::Block(block)) => {
1361                let then_ty = self.infer_block_expr_ty(then_branch, env);
1362                let else_ty = self.infer_block_expr_ty(block, env);
1363                if then_ty == else_ty {
1364                    then_ty
1365                } else {
1366                    SimpleTy::Unknown
1367                }
1368            }
1369            Some(tupa_parser::ElseBranch::If(expr)) => {
1370                let then_ty = self.infer_block_expr_ty(then_branch, env);
1371                let else_ty = self.infer_expr_ty(expr, env);
1372                if then_ty == else_ty {
1373                    then_ty
1374                } else {
1375                    SimpleTy::Unknown
1376                }
1377            }
1378            None => SimpleTy::Void,
1379        };
1380        if matches!(result_ty, SimpleTy::Void | SimpleTy::Unknown) {
1381            self.emit_if_stmt(condition, then_branch, else_branch, env, ret_ty);
1382            return ExprValue::new(SimpleTy::Unknown, "0".to_string());
1383        }
1384
1385        let result_alloca = self.fresh_temp();
1386        let llvm_ty = self.llvm_ty(result_ty);
1387        self.lines
1388            .push(format!("  {result_alloca} = alloca {llvm_ty}"));
1389
1390        let cond_val = self.emit_expr(condition, env);
1391        let cond = if cond_val.ty == SimpleTy::Bool {
1392            cond_val.llvm_value
1393        } else {
1394            "0".to_string()
1395        };
1396        let then_label = self.fresh_label("if.then");
1397        let else_label = self.fresh_label("if.else");
1398        let end_label = self.fresh_label("if.end");
1399
1400        self.lines.push(format!(
1401            "  br i1 {cond}, label %{then_label}, label %{else_label}"
1402        ));
1403        self.lines.push(format!("{then_label}:"));
1404        let then_val = self.emit_block_expr(then_branch, env, ret_ty);
1405        if then_val.ty == result_ty {
1406            self.lines.push(format!(
1407                "  store {llvm_ty} {}, {llvm_ty}* {result_alloca}",
1408                then_val.llvm_value
1409            ));
1410        }
1411        self.lines.push(format!("  br label %{end_label}"));
1412
1413        self.lines.push(format!("{else_label}:"));
1414        let else_val = match else_branch {
1415            Some(tupa_parser::ElseBranch::Block(block)) => self.emit_block_expr(block, env, ret_ty),
1416            Some(tupa_parser::ElseBranch::If(expr)) => self.emit_expr(expr, env),
1417            None => ExprValue::new(SimpleTy::Void, "0".to_string()),
1418        };
1419        if else_val.ty == result_ty {
1420            self.lines.push(format!(
1421                "  store {llvm_ty} {}, {llvm_ty}* {result_alloca}",
1422                else_val.llvm_value
1423            ));
1424        }
1425        self.lines.push(format!("  br label %{end_label}"));
1426        self.lines.push(format!("{end_label}:"));
1427        let out = self.fresh_temp();
1428        self.lines.push(format!(
1429            "  {out} = load {llvm_ty}, {llvm_ty}* {result_alloca}"
1430        ));
1431        ExprValue::new(result_ty, out)
1432    }
1433
1434    fn emit_match_expr(
1435        &mut self,
1436        scrutinee: &Expr,
1437        arms: &[tupa_parser::MatchArm],
1438        env: &mut HashMap<String, LocalVar>,
1439        ret_ty: SimpleTy,
1440    ) -> ExprValue {
1441        let value = self.emit_expr(scrutinee, env);
1442        if value.ty != SimpleTy::I64 && value.ty != SimpleTy::Str && value.ty != SimpleTy::Bool {
1443            // Match on unsupported type - for now, only support i64, str, and bool
1444            return ExprValue::new(SimpleTy::Unknown, "0".to_string());
1445        }
1446
1447        let mut result_ty: Option<SimpleTy> = None;
1448        for arm in arms {
1449            let arm_ty = match &arm.expr.kind {
1450                ExprKind::Block(stmts) => self.infer_block_expr_ty(stmts, env),
1451                _ => self.infer_expr_ty(&arm.expr, env),
1452            };
1453            match result_ty {
1454                None => result_ty = Some(arm_ty),
1455                Some(prev) if prev != arm_ty => {
1456                    result_ty = Some(SimpleTy::Unknown);
1457                    break;
1458                }
1459                _ => {}
1460            }
1461        }
1462        let result_ty = result_ty.unwrap_or(SimpleTy::Void);
1463        if matches!(result_ty, SimpleTy::Void | SimpleTy::Unknown) {
1464            self.emit_match_stmt(scrutinee, arms, env, ret_ty);
1465            return ExprValue::new(SimpleTy::Unknown, "0".to_string());
1466        }
1467
1468        let result_alloca = self.fresh_temp();
1469        let llvm_ty = self.llvm_ty(result_ty);
1470        self.lines
1471            .push(format!("  {result_alloca} = alloca {llvm_ty}"));
1472
1473        let end_label = self.fresh_label("match.end");
1474        let mut arm_labels: Vec<String> = Vec::new();
1475        let mut fallthrough_labels: Vec<String> = Vec::new();
1476        let mut guard_labels: Vec<Option<String>> = Vec::new();
1477
1478        for _ in arms {
1479            arm_labels.push(self.fresh_label("match.arm"));
1480            fallthrough_labels.push(self.fresh_label("match.next"));
1481            guard_labels.push(None);
1482        }
1483
1484        for (idx, arm) in arms.iter().enumerate() {
1485            if arm.guard.is_some() {
1486                guard_labels[idx] = Some(self.fresh_label("match.guard"));
1487            }
1488        }
1489
1490        for (idx, arm) in arms.iter().enumerate() {
1491            let next_label = if idx + 1 < arms.len() {
1492                &fallthrough_labels[idx]
1493            } else {
1494                &end_label
1495            };
1496            let arm_target = guard_labels[idx].as_ref().unwrap_or(&arm_labels[idx]);
1497            let binding_name = match &arm.pattern {
1498                tupa_parser::Pattern::Ident(name) => Some(name.clone()),
1499                _ => None,
1500            };
1501            let should_bind = binding_name
1502                .as_ref()
1503                .map(|name| self.is_var_used(name))
1504                .unwrap_or(false);
1505            match (&arm.pattern, value.ty) {
1506                (tupa_parser::Pattern::Int(value_literal), SimpleTy::I64) => {
1507                    let cmp = self.fresh_temp();
1508                    self.lines.push(format!(
1509                        "  {cmp} = icmp eq i64 {}, {}",
1510                        value.llvm_value, value_literal
1511                    ));
1512                    self.lines.push(format!(
1513                        "  br i1 {cmp}, label %{}, label %{}",
1514                        arm_target, next_label
1515                    ));
1516                }
1517                (tupa_parser::Pattern::Bool(value_literal), SimpleTy::Bool) => {
1518                    let cmp = self.fresh_temp();
1519                    let literal = if *value_literal { "1" } else { "0" };
1520                    self.lines.push(format!(
1521                        "  {cmp} = icmp eq i1 {}, {}",
1522                        value.llvm_value, literal
1523                    ));
1524                    self.lines.push(format!(
1525                        "  br i1 {cmp}, label %{}, label %{}",
1526                        arm_target, next_label
1527                    ));
1528                }
1529                (tupa_parser::Pattern::Str(lit), SimpleTy::Str) => {
1530                    let (global, len) = self.intern_string(lit);
1531                    let ptr = self.fresh_temp();
1532                    self.lines.push(format!(
1533                        "  {ptr} = getelementptr inbounds [{len} x i8], [{len} x i8]* {global}, i64 0, i64 0"
1534                    ));
1535                    self.declare_strcmp();
1536                    let cmp = self.fresh_temp();
1537                    self.lines.push(format!(
1538                        "  {cmp} = call i32 @strcmp(i8* {}, i8* {ptr})",
1539                        value.llvm_value
1540                    ));
1541                    let is_eq = self.fresh_temp();
1542                    self.lines.push(format!("  {is_eq} = icmp eq i32 {cmp}, 0"));
1543                    self.lines.push(format!(
1544                        "  br i1 {is_eq}, label %{}, label %{}",
1545                        arm_target, next_label
1546                    ));
1547                }
1548                (tupa_parser::Pattern::Wildcard, _) | (tupa_parser::Pattern::Ident(_), _) => {
1549                    self.lines.push(format!("  br label %{}", arm_target));
1550                }
1551                _ => {
1552                    self.lines.push(format!("  br label %{}", next_label));
1553                }
1554            }
1555
1556            let mut prev_binding: Option<LocalVar> = None;
1557            let mut bound = false;
1558            let mut binding_active = false;
1559            if let Some(guard_label) = &guard_labels[idx] {
1560                self.lines.push(format!("{guard_label}:"));
1561                if let Some(name) = &binding_name {
1562                    if should_bind {
1563                        let llvm_val_ty = self.llvm_ty(value.ty);
1564                        let alloca = self.fresh_temp();
1565                        self.lines
1566                            .push(format!("  {alloca} = alloca {llvm_val_ty}"));
1567                        self.lines.push(format!(
1568                            "  store {llvm_val_ty} {}, {llvm_val_ty}* {alloca}",
1569                            value.llvm_value
1570                        ));
1571                        prev_binding = env.insert(
1572                            name.clone(),
1573                            LocalVar {
1574                                ptr: alloca,
1575                                ty: value.ty,
1576                                used: false,
1577                            },
1578                        );
1579                        bound = true;
1580                        binding_active = true;
1581                    }
1582                }
1583                let guard_value = self.emit_expr(arm.guard.as_ref().unwrap(), env);
1584                let cond = if guard_value.ty == SimpleTy::Bool {
1585                    guard_value.llvm_value
1586                } else {
1587                    "0".to_string()
1588                };
1589                self.lines.push(format!(
1590                    "  br i1 {cond}, label %{}, label %{}",
1591                    arm_labels[idx], next_label
1592                ));
1593            }
1594
1595            self.lines.push(format!("{}:", arm_labels[idx]));
1596            if !bound {
1597                if let Some(name) = &binding_name {
1598                    if should_bind {
1599                        let llvm_val_ty = self.llvm_ty(value.ty);
1600                        let alloca = self.fresh_temp();
1601                        self.lines
1602                            .push(format!("  {alloca} = alloca {llvm_val_ty}"));
1603                        self.lines.push(format!(
1604                            "  store {llvm_val_ty} {}, {llvm_val_ty}* {alloca}",
1605                            value.llvm_value
1606                        ));
1607                        prev_binding = env.insert(
1608                            name.clone(),
1609                            LocalVar {
1610                                ptr: alloca,
1611                                ty: value.ty,
1612                                used: false,
1613                            },
1614                        );
1615                        binding_active = true;
1616                    }
1617                }
1618            }
1619            let arm_val = match &arm.expr.kind {
1620                ExprKind::Block(stmts) => self.emit_block_expr(stmts, env, ret_ty),
1621                _ => self.emit_expr(&arm.expr, env),
1622            };
1623            if arm_val.ty == result_ty {
1624                self.lines.push(format!(
1625                    "  store {llvm_ty} {}, {llvm_ty}* {result_alloca}",
1626                    arm_val.llvm_value
1627                ));
1628            }
1629            self.lines.push(format!("  br label %{end_label}"));
1630
1631            if binding_active {
1632                if let Some(name) = &binding_name {
1633                    if let Some(prev) = prev_binding.take() {
1634                        env.insert(name.clone(), prev);
1635                    } else {
1636                        env.remove(name);
1637                    }
1638                }
1639            }
1640            if idx + 1 < arms.len() {
1641                self.lines.push(format!("{}:", fallthrough_labels[idx]));
1642            }
1643        }
1644
1645        self.lines.push(format!("{end_label}:"));
1646        let out = self.fresh_temp();
1647        self.lines.push(format!(
1648            "  {out} = load {llvm_ty}, {llvm_ty}* {result_alloca}"
1649        ));
1650        ExprValue::new(result_ty, out)
1651    }
1652
1653    fn emit_expr(&mut self, expr: &Expr, env: &mut HashMap<String, LocalVar>) -> ExprValue {
1654        // Apply constant folding first
1655        let folded_expr = self.fold_expr(expr);
1656        match &folded_expr.kind {
1657            ExprKind::Int(value) => ExprValue::new(SimpleTy::I64, value.to_string()),
1658            ExprKind::Float(value) => ExprValue::new(SimpleTy::F64, value.to_string()),
1659            ExprKind::Bool(value) => {
1660                let literal = if *value { "1" } else { "0" };
1661                ExprValue::new(SimpleTy::Bool, literal.to_string())
1662            }
1663            ExprKind::Str(value) => {
1664                let (global, len) = self.intern_string(value);
1665                let ptr = self.fresh_temp();
1666                self.lines.push(format!(
1667                    "  {ptr} = getelementptr inbounds [{len} x i8], [{len} x i8]* {global}, i64 0, i64 0"
1668                ));
1669                ExprValue::new(SimpleTy::Str, ptr)
1670            }
1671            ExprKind::Lambda { params, body } => {
1672                // Check if this is a closure that captures variables
1673                let lambda_name = format!("lambda_{}", self.temp);
1674                self.temp += 1;
1675
1676                // For now, assume all lambdas can capture and create environment
1677                // In a full implementation, we'd check which variables are actually captured
1678                let _env_struct_name = format!("env_{}", lambda_name);
1679
1680                // Create environment struct type
1681                let env_struct_name = format!("env_{}", lambda_name);
1682
1683                let mut used_env: HashMap<String, bool> =
1684                    env.keys().map(|name| (name.clone(), false)).collect();
1685                self.collect_used_in_expr(body, &mut used_env);
1686                let mut captured_vars = Vec::new();
1687                for (var_name, var_info) in env.iter() {
1688                    if params.contains(var_name) {
1689                        continue;
1690                    }
1691                    if used_env.get(var_name).copied().unwrap_or(false) {
1692                        captured_vars.push((var_name.clone(), var_info.ty));
1693                    }
1694                }
1695
1696                let env_var_names = captured_vars
1697                    .iter()
1698                    .map(|(name, _)| name.clone())
1699                    .collect::<Vec<_>>();
1700                let _env_var_types = captured_vars.iter().map(|(_, ty)| *ty).collect::<Vec<_>>();
1701
1702                if !captured_vars.is_empty() {
1703                    // Define environment struct
1704                    self.globals.push(format!("%{} = type {{", env_struct_name));
1705                    for (var_name, var_ty) in &captured_vars {
1706                        self.globals
1707                            .push(format!("  {} {}", self.llvm_ty(*var_ty), var_name));
1708                    }
1709                    self.globals.push("}".to_string());
1710                }
1711
1712                // Create lambda function
1713                let mut lambda_codegen = Codegen {
1714                    temp: self.temp,
1715                    ..Default::default()
1716                };
1717                self.temp += 100;
1718
1719                // Lambda parameters: environment pointer, then actual params
1720                let mut lambda_params = vec!["i8* %env".to_string()];
1721                for param in params.iter() {
1722                    lambda_params.push(format!("i64 %{}", param));
1723                }
1724                let param_decls = lambda_params.join(", ");
1725
1726                lambda_codegen
1727                    .lines
1728                    .push(format!("define i64 @{lambda_name}({param_decls}) {{"));
1729                lambda_codegen.lines.push("entry:".to_string());
1730
1731                // Set up environment access
1732                let mut lambda_env = HashMap::new();
1733                if !env_var_names.is_empty() {
1734                    // Cast environment pointer to struct pointer
1735                    let env_struct_ptr = lambda_codegen.fresh_temp();
1736                    lambda_codegen.lines.push(format!(
1737                        "  {env_struct_ptr} = bitcast i8* %env to %{}*",
1738                        env_struct_name
1739                    ));
1740
1741                    // Load captured variables from environment
1742                    for (i, (var_name, var_ty)) in captured_vars.iter().enumerate() {
1743                        let var_ptr = lambda_codegen.fresh_temp();
1744                        lambda_codegen.lines.push(format!(
1745                            "  {var_ptr} = getelementptr inbounds %{}, %{}* {env_struct_ptr}, i32 0, i32 {}",
1746                            env_struct_name, env_struct_name, i
1747                        ));
1748
1749                        let var_value = lambda_codegen.fresh_temp();
1750                        lambda_codegen.lines.push(format!(
1751                            "  {var_value} = load {}, {}* {var_ptr}",
1752                            self.llvm_ty(*var_ty),
1753                            self.llvm_ty(*var_ty)
1754                        ));
1755
1756                        let var_alloca = lambda_codegen.fresh_temp();
1757                        lambda_codegen
1758                            .lines
1759                            .push(format!("  {var_alloca} = alloca {}", self.llvm_ty(*var_ty)));
1760                        lambda_codegen.lines.push(format!(
1761                            "  store {} {var_value}, {}* {var_alloca}",
1762                            self.llvm_ty(*var_ty),
1763                            self.llvm_ty(*var_ty)
1764                        ));
1765
1766                        lambda_env.insert(
1767                            var_name.clone(),
1768                            LocalVar {
1769                                ptr: var_alloca,
1770                                ty: *var_ty,
1771                                used: false,
1772                            },
1773                        );
1774                    }
1775                }
1776
1777                // Set up parameters
1778                for param in params {
1779                    let alloca = lambda_codegen.fresh_temp();
1780                    lambda_codegen
1781                        .lines
1782                        .push(format!("  {alloca} = alloca i64"));
1783                    lambda_codegen
1784                        .lines
1785                        .push(format!("  store i64 %{param}, i64* {alloca}"));
1786                    lambda_env.insert(
1787                        param.clone(),
1788                        LocalVar {
1789                            ptr: alloca,
1790                            ty: SimpleTy::I64,
1791                            used: false,
1792                        },
1793                    );
1794                }
1795
1796                // Emit the body
1797                let result = lambda_codegen.emit_expr(body, &mut lambda_env);
1798                lambda_codegen
1799                    .lines
1800                    .push(format!("  ret i64 {}", result.llvm_value));
1801                lambda_codegen.lines.push("}".to_string());
1802
1803                // Add lambda function to globals
1804                self.globals.extend(lambda_codegen.lines);
1805
1806                // Create closure object
1807                if !env_var_names.is_empty() {
1808                    // Allocate environment on heap
1809                    self.ensure_malloc_declared();
1810                    let env_size_ptr = self.fresh_temp();
1811                    self.lines.push(format!(
1812                        "  {env_size_ptr} = getelementptr %{}, %{}* null, i32 1",
1813                        env_struct_name, env_struct_name
1814                    ));
1815                    let env_size = self.fresh_temp();
1816                    self.lines.push(format!(
1817                        "  {env_size} = ptrtoint %{}* {env_size_ptr} to i64",
1818                        env_struct_name
1819                    ));
1820                    let env_mem = self.fresh_temp();
1821                    self.lines
1822                        .push(format!("  {env_mem} = call i8* @malloc(i64 {env_size})"));
1823
1824                    let env_struct_ptr = self.fresh_temp();
1825                    self.lines.push(format!(
1826                        "  {env_struct_ptr} = bitcast i8* {env_mem} to %{}*",
1827                        env_struct_name
1828                    ));
1829
1830                    // Store captured variables in environment
1831                    for (i, var_name) in env_var_names.iter().enumerate() {
1832                        if let Some(var_info) = env.get(var_name) {
1833                            let field_ptr = self.fresh_temp();
1834                            self.lines.push(format!(
1835                                "  {field_ptr} = getelementptr inbounds %{}, %{}* {env_struct_ptr}, i32 0, i32 {}",
1836                                env_struct_name, env_struct_name, i
1837                            ));
1838
1839                            let loaded = self.fresh_temp();
1840                            self.lines.push(format!(
1841                                "  {loaded} = load {}, {}* {}",
1842                                self.llvm_ty(var_info.ty),
1843                                self.llvm_ty(var_info.ty),
1844                                var_info.ptr
1845                            ));
1846                            self.lines.push(format!(
1847                                "  store {} {loaded}, {}* {field_ptr}",
1848                                self.llvm_ty(var_info.ty),
1849                                self.llvm_ty(var_info.ty)
1850                            ));
1851                        }
1852                    }
1853
1854                    // Return environment pointer as closure
1855                    let bitcast = self.fresh_temp();
1856                    self.lines.push(format!(
1857                        "  {bitcast} = bitcast %{}* {env_struct_ptr} to i8*",
1858                        env_struct_name
1859                    ));
1860
1861                    ExprValue::new(SimpleTy::ClosurePtr, bitcast)
1862                } else {
1863                    // No captures - return function pointer directly
1864                    let bitcast = self.fresh_temp();
1865                    self.lines.push(format!(
1866                        "  {bitcast} = bitcast i64 (i8*, i64)* @{lambda_name} to i8*"
1867                    ));
1868                    ExprValue::new(SimpleTy::ClosurePtr, bitcast)
1869                }
1870            }
1871            ExprKind::ArrayLiteral(items) => {
1872                if items.is_empty() {
1873                    // Empty array literal - return null pointer for any type
1874                    return ExprValue::new(SimpleTy::I64Ptr, "null".to_string());
1875                }
1876                let mut values = Vec::new();
1877                for item in items {
1878                    let value = self.emit_expr(item, env);
1879                    values.push(value);
1880                }
1881                let elem_ty = values.first().map(|v| v.ty).unwrap_or(SimpleTy::Unknown);
1882                if !values.iter().all(|v| v.ty == elem_ty) {
1883                    // Non-uniform array literal - this should be caught by typechecker
1884                    // but we'll handle it gracefully by returning unknown
1885                    return ExprValue::new(SimpleTy::Unknown, "0".to_string());
1886                }
1887                let len = values.len();
1888                match elem_ty {
1889                    SimpleTy::I64 => {
1890                        let arr = self.fresh_temp();
1891                        self.lines.push(format!("  {arr} = alloca [{len} x i64]"));
1892                        for (idx, value) in values.into_iter().enumerate() {
1893                            let elem_ptr = self.fresh_temp();
1894                            self.lines.push(format!(
1895                                "  {elem_ptr} = getelementptr inbounds [{len} x i64], [{len} x i64]* {arr}, i64 0, i64 {idx}"
1896                            ));
1897                            self.lines
1898                                .push(format!("  store i64 {}, i64* {elem_ptr}", value.llvm_value));
1899                        }
1900                        let data_ptr = self.fresh_temp();
1901                        self.lines.push(format!(
1902                            "  {data_ptr} = getelementptr inbounds [{len} x i64], [{len} x i64]* {arr}, i64 0, i64 0"
1903                        ));
1904                        ExprValue::new(SimpleTy::I64Ptr, data_ptr)
1905                    }
1906                    SimpleTy::F64 => {
1907                        let arr = self.fresh_temp();
1908                        self.lines
1909                            .push(format!("  {arr} = alloca [{len} x double]"));
1910                        for (idx, value) in values.into_iter().enumerate() {
1911                            let elem_ptr = self.fresh_temp();
1912                            self.lines.push(format!(
1913                                "  {elem_ptr} = getelementptr inbounds [{len} x double], [{len} x double]* {arr}, i64 0, i64 {idx}"
1914                            ));
1915                            self.lines.push(format!(
1916                                "  store double {}, double* {elem_ptr}",
1917                                value.llvm_value
1918                            ));
1919                        }
1920                        let data_ptr = self.fresh_temp();
1921                        self.lines.push(format!(
1922                            "  {data_ptr} = getelementptr inbounds [{len} x double], [{len} x double]* {arr}, i64 0, i64 0"
1923                        ));
1924                        ExprValue::new(SimpleTy::F64Ptr, data_ptr)
1925                    }
1926                    SimpleTy::Str => {
1927                        let arr = self.fresh_temp();
1928                        self.lines.push(format!("  {arr} = alloca [{len} x i8*]"));
1929                        for (idx, value) in values.into_iter().enumerate() {
1930                            let elem_ptr = self.fresh_temp();
1931                            self.lines.push(format!(
1932                                "  {elem_ptr} = getelementptr inbounds [{len} x i8*], [{len} x i8*]* {arr}, i64 0, i64 {idx}"
1933                            ));
1934                            self.lines
1935                                .push(format!("  store i8* {}, i8** {elem_ptr}", value.llvm_value));
1936                        }
1937                        let data_ptr = self.fresh_temp();
1938                        self.lines.push(format!(
1939                            "  {data_ptr} = getelementptr inbounds [{len} x i8*], [{len} x i8*]* {arr}, i64 0, i64 0"
1940                        ));
1941                        ExprValue::new(SimpleTy::StrPtr, data_ptr)
1942                    }
1943                    SimpleTy::Bool => {
1944                        let arr = self.fresh_temp();
1945                        self.lines.push(format!("  {arr} = alloca [{len} x i1]"));
1946                        for (idx, value) in values.into_iter().enumerate() {
1947                            let elem_ptr = self.fresh_temp();
1948                            self.lines.push(format!(
1949                                "  {elem_ptr} = getelementptr inbounds [{len} x i1], [{len} x i1]* {arr}, i64 0, i64 {idx}"
1950                            ));
1951                            self.lines
1952                                .push(format!("  store i1 {}, i1* {elem_ptr}", value.llvm_value));
1953                        }
1954                        let data_ptr = self.fresh_temp();
1955                        self.lines.push(format!(
1956                            "  {data_ptr} = getelementptr inbounds [{len} x i1], [{len} x i1]* {arr}, i64 0, i64 0"
1957                        ));
1958                        ExprValue::new(SimpleTy::BoolPtr, data_ptr)
1959                    }
1960                    _ => {
1961                        // Unsupported array element type
1962                        ExprValue::new(SimpleTy::Unknown, "0".to_string())
1963                    }
1964                }
1965            }
1966            ExprKind::Tuple(items) => {
1967                for item in items {
1968                    self.emit_expr(item, env);
1969                }
1970                ExprValue::new(SimpleTy::Unknown, "0".to_string())
1971            }
1972            ExprKind::Ident(name) => {
1973                if let Some(var) = env.get_mut(name) {
1974                    var.used = true;
1975                    let llvm_ty = self.llvm_ty(var.ty);
1976                    let tmp = self.fresh_temp();
1977                    self.lines
1978                        .push(format!("  {tmp} = load {llvm_ty}, {llvm_ty}* {}", var.ptr));
1979                    return ExprValue::new(var.ty, tmp);
1980                }
1981                ExprValue::new(SimpleTy::Unknown, "0".to_string())
1982            }
1983            ExprKind::Assign { name, expr } => {
1984                let value = self.emit_expr(expr, env);
1985                if let Some(var) = env.get_mut(name) {
1986                    var.used = true;
1987                    let llvm_ty = self.llvm_ty(var.ty);
1988                    self.lines.push(format!(
1989                        "  store {llvm_ty} {}, {llvm_ty}* {}",
1990                        value.llvm_value, var.ptr
1991                    ));
1992                    ExprValue::new(var.ty, value.llvm_value)
1993                } else {
1994                    // Assign to unknown variable - this should be caught by typechecker
1995                    ExprValue::new(SimpleTy::Unknown, "0".to_string())
1996                }
1997            }
1998            ExprKind::AssignIndex { expr, index, value } => {
1999                let base = self.emit_expr(expr, env);
2000                let idx = self.emit_expr(index, env);
2001                let rhs = self.emit_expr(value, env);
2002                if idx.ty != SimpleTy::I64 {
2003                    // Non-i64 index - this should be caught by typechecker
2004                    return ExprValue::new(SimpleTy::Unknown, "0".to_string());
2005                }
2006                match (base.ty, rhs.ty) {
2007                    (SimpleTy::I64Ptr, SimpleTy::I64) => {
2008                        let elem_ptr = self.fresh_temp();
2009                        self.lines.push(format!(
2010                            "  {elem_ptr} = getelementptr inbounds i64, i64* {}, i64 {}",
2011                            base.llvm_value, idx.llvm_value
2012                        ));
2013                        self.lines
2014                            .push(format!("  store i64 {}, i64* {elem_ptr}", rhs.llvm_value));
2015                        ExprValue::new(SimpleTy::I64, rhs.llvm_value)
2016                    }
2017                    (SimpleTy::F64Ptr, SimpleTy::F64) => {
2018                        let elem_ptr = self.fresh_temp();
2019                        self.lines.push(format!(
2020                            "  {elem_ptr} = getelementptr inbounds double, double* {}, i64 {}",
2021                            base.llvm_value, idx.llvm_value
2022                        ));
2023                        self.lines.push(format!(
2024                            "  store double {}, double* {elem_ptr}",
2025                            rhs.llvm_value
2026                        ));
2027                        ExprValue::new(SimpleTy::F64, rhs.llvm_value)
2028                    }
2029                    (SimpleTy::StrPtr, SimpleTy::Str) => {
2030                        let elem_ptr = self.fresh_temp();
2031                        self.lines.push(format!(
2032                            "  {elem_ptr} = getelementptr inbounds i8*, i8** {}, i64 {}",
2033                            base.llvm_value, idx.llvm_value
2034                        ));
2035                        self.lines
2036                            .push(format!("  store i8* {}, i8** {elem_ptr}", rhs.llvm_value));
2037                        ExprValue::new(SimpleTy::Str, rhs.llvm_value)
2038                    }
2039                    (SimpleTy::BoolPtr, SimpleTy::Bool) => {
2040                        let elem_ptr = self.fresh_temp();
2041                        self.lines.push(format!(
2042                            "  {elem_ptr} = getelementptr inbounds i1, i1* {}, i64 {}",
2043                            base.llvm_value, idx.llvm_value
2044                        ));
2045                        self.lines
2046                            .push(format!("  store i1 {}, i1* {elem_ptr}", rhs.llvm_value));
2047                        ExprValue::new(SimpleTy::Bool, rhs.llvm_value)
2048                    }
2049                    _ => {
2050                        // Unsupported index assignment type combination
2051                        ExprValue::new(SimpleTy::Unknown, "0".to_string())
2052                    }
2053                }
2054            }
2055            ExprKind::Index { expr, index } => {
2056                let base = self.emit_expr(expr, env);
2057                let idx = self.emit_expr(index, env);
2058                if base.ty == SimpleTy::I64Ptr && idx.ty == SimpleTy::I64 {
2059                    let elem_ptr = self.fresh_temp();
2060                    self.lines.push(format!(
2061                        "  {elem_ptr} = getelementptr inbounds i64, i64* {}, i64 {}",
2062                        base.llvm_value, idx.llvm_value
2063                    ));
2064                    let tmp = self.fresh_temp();
2065                    self.lines
2066                        .push(format!("  {tmp} = load i64, i64* {elem_ptr}"));
2067                    return ExprValue::new(SimpleTy::I64, tmp);
2068                }
2069                if base.ty == SimpleTy::F64Ptr && idx.ty == SimpleTy::I64 {
2070                    let elem_ptr = self.fresh_temp();
2071                    self.lines.push(format!(
2072                        "  {elem_ptr} = getelementptr inbounds double, double* {}, i64 {}",
2073                        base.llvm_value, idx.llvm_value
2074                    ));
2075                    let tmp = self.fresh_temp();
2076                    self.lines
2077                        .push(format!("  {tmp} = load double, double* {elem_ptr}"));
2078                    return ExprValue::new(SimpleTy::F64, tmp);
2079                }
2080                if base.ty == SimpleTy::StrPtr && idx.ty == SimpleTy::I64 {
2081                    let elem_ptr = self.fresh_temp();
2082                    self.lines.push(format!(
2083                        "  {elem_ptr} = getelementptr inbounds i8*, i8** {}, i64 {}",
2084                        base.llvm_value, idx.llvm_value
2085                    ));
2086                    let tmp = self.fresh_temp();
2087                    self.lines
2088                        .push(format!("  {tmp} = load i8*, i8** {elem_ptr}"));
2089                    return ExprValue::new(SimpleTy::Str, tmp);
2090                }
2091                if base.ty == SimpleTy::BoolPtr && idx.ty == SimpleTy::I64 {
2092                    let elem_ptr = self.fresh_temp();
2093                    self.lines.push(format!(
2094                        "  {elem_ptr} = getelementptr inbounds i1, i1* {}, i64 {}",
2095                        base.llvm_value, idx.llvm_value
2096                    ));
2097                    let tmp = self.fresh_temp();
2098                    self.lines
2099                        .push(format!("  {tmp} = load i1, i1* {elem_ptr}"));
2100                    return ExprValue::new(SimpleTy::Bool, tmp);
2101                }
2102                // Unsupported index operation
2103                ExprValue::new(SimpleTy::Unknown, "0".to_string())
2104            }
2105            ExprKind::If {
2106                condition,
2107                then_branch,
2108                else_branch,
2109            } => self.emit_if_expr(
2110                condition,
2111                then_branch,
2112                else_branch.as_ref(),
2113                env,
2114                SimpleTy::Void,
2115            ),
2116            ExprKind::Match { expr, arms } => self.emit_match_expr(expr, arms, env, SimpleTy::Void),
2117            ExprKind::Unary { op, expr } => {
2118                let inner = self.emit_expr(expr, env);
2119                match (op, inner.ty) {
2120                    (tupa_parser::UnaryOp::Neg, SimpleTy::I64) => {
2121                        let tmp = self.fresh_temp();
2122                        self.lines
2123                            .push(format!("  {tmp} = sub i64 0, {}", inner.llvm_value));
2124                        ExprValue::new(SimpleTy::I64, tmp)
2125                    }
2126                    (tupa_parser::UnaryOp::Neg, SimpleTy::F64) => {
2127                        let tmp = self.fresh_temp();
2128                        self.lines
2129                            .push(format!("  {tmp} = fneg double {}", inner.llvm_value));
2130                        ExprValue::new(SimpleTy::F64, tmp)
2131                    }
2132                    (tupa_parser::UnaryOp::Not, SimpleTy::Bool) => {
2133                        let tmp = self.fresh_temp();
2134                        self.lines
2135                            .push(format!("  {tmp} = xor i1 {}, 1", inner.llvm_value));
2136                        ExprValue::new(SimpleTy::Bool, tmp)
2137                    }
2138                    _ => {
2139                        // Unsupported unary operation
2140                        ExprValue::new(SimpleTy::Unknown, "0".to_string())
2141                    }
2142                }
2143            }
2144            ExprKind::Call { callee, args } => {
2145                // Suporte a print
2146                if let ExprKind::Ident(name) = &callee.kind {
2147                    if name == "print" {
2148                        if let Some(arg) = args.first() {
2149                            let val = self.emit_expr(arg, env);
2150                            match val.ty {
2151                                SimpleTy::Str => {
2152                                    self.declare_puts();
2153                                    self.lines
2154                                        .push(format!("  call i32 @puts(i8* {})", val.llvm_value));
2155                                }
2156                                SimpleTy::I64 => {
2157                                    self.declare_printf();
2158                                    let (fmt, len) = self.intern_format_int();
2159                                    let fmt_ptr = self.fresh_temp();
2160                                    self.lines.push(format!("  {fmt_ptr} = getelementptr inbounds [{len} x i8], [{len} x i8]* {fmt}, i64 0, i64 0"));
2161                                    self.lines.push(format!(
2162                                        "  call i32 (i8*, ...) @printf(i8* {fmt_ptr}, i64 {})",
2163                                        val.llvm_value
2164                                    ));
2165                                }
2166                                SimpleTy::F64 => {
2167                                    self.declare_printf();
2168                                    let (fmt, len) = self.intern_format_float();
2169                                    let fmt_ptr = self.fresh_temp();
2170                                    self.lines.push(format!("  {fmt_ptr} = getelementptr inbounds [{len} x i8], [{len} x i8]* {fmt}, i64 0, i64 0"));
2171                                    self.lines.push(format!(
2172                                        "  call i32 (i8*, ...) @printf(i8* {fmt_ptr}, double {})",
2173                                        val.llvm_value
2174                                    ));
2175                                }
2176                                SimpleTy::Bool => {
2177                                    self.declare_printf();
2178                                    let (fmt, len) = self.intern_format_int();
2179                                    let fmt_ptr = self.fresh_temp();
2180                                    self.lines.push(format!("  {fmt_ptr} = getelementptr inbounds [{len} x i8], [{len} x i8]* {fmt}, i64 0, i64 0"));
2181                                    let zext = self.fresh_temp();
2182                                    self.lines.push(format!(
2183                                        "  {zext} = zext i1 {} to i64",
2184                                        val.llvm_value
2185                                    ));
2186                                    self.lines.push(format!(
2187                                        "  call i32 (i8*, ...) @printf(i8* {fmt_ptr}, i64 {zext})"
2188                                    ));
2189                                }
2190                                _ => {
2191                                    self.lines
2192                                        .push("  ; print de tipo não suportado".to_string());
2193                                }
2194                            }
2195                        }
2196                        return ExprValue::new(SimpleTy::Void, "0".to_string());
2197                    }
2198                }
2199                // Chamada de função/lambda
2200                // Se for identificador de função conhecida, use @nome
2201                let (is_direct_fn, fn_name) = match &callee.kind {
2202                    ExprKind::Ident(name) => {
2203                        if self.function_sigs.contains_key(name) {
2204                            (true, name.clone())
2205                        } else {
2206                            (false, String::new())
2207                        }
2208                    }
2209                    _ => (false, String::new()),
2210                };
2211                let mut arg_values = Vec::new();
2212                for arg in args {
2213                    arg_values.push(self.emit_expr(arg, env));
2214                }
2215                let args_llvm = arg_values
2216                    .iter()
2217                    .map(|v| match v.ty {
2218                        SimpleTy::I64 => format!("i64 {}", v.llvm_value),
2219                        SimpleTy::F64 => format!("double {}", v.llvm_value),
2220                        SimpleTy::Bool => format!("i1 {}", v.llvm_value),
2221                        SimpleTy::Str => format!("i8* {}", v.llvm_value),
2222                        _ => format!("i64 {}", v.llvm_value),
2223                    })
2224                    .collect::<Vec<_>>()
2225                    .join(", ");
2226                let tmp = self.fresh_temp();
2227                if is_direct_fn {
2228                    self.lines
2229                        .push(format!("  {tmp} = call i64 @{fn_name}({})", args_llvm));
2230                } else {
2231                    let callee_val = self.emit_expr(callee, env);
2232                    self.lines.push(format!(
2233                        "  {tmp} = call i64 {}({})",
2234                        callee_val.llvm_value, args_llvm
2235                    ));
2236                }
2237                ExprValue::new(SimpleTy::I64, tmp)
2238            }
2239            ExprKind::Binary { op, left, right } => {
2240                if matches!(op, tupa_parser::BinaryOp::Range) {
2241                    // Range expressions are handled in for loops, not as standalone expressions
2242                    return ExprValue::new(SimpleTy::Unknown, "0".to_string());
2243                }
2244                if matches!(op, tupa_parser::BinaryOp::Add) {
2245                    if let (ExprKind::Str(lhs), ExprKind::Str(rhs)) = (&left.kind, &right.kind) {
2246                        let combined = format!("{lhs}{rhs}");
2247                        let (global, len) = self.intern_string(&combined);
2248                        let ptr = self.fresh_temp();
2249                        self.lines.push(format!(
2250                            "  {ptr} = getelementptr inbounds [{len} x i8], [{len} x i8]* {global}, i64 0, i64 0"
2251                        ));
2252                        return ExprValue::new(SimpleTy::Str, ptr);
2253                    }
2254                }
2255                let left_val = self.emit_expr(left, env);
2256                let right_val = self.emit_expr(right, env);
2257                match (left_val.ty, right_val.ty) {
2258                    (SimpleTy::I64, SimpleTy::I64) => {
2259                        if matches!(op, tupa_parser::BinaryOp::Pow) {
2260                            let result_alloca = self.fresh_temp();
2261                            self.lines.push(format!("  {result_alloca} = alloca i64"));
2262                            self.lines
2263                                .push(format!("  store i64 1, i64* {result_alloca}"));
2264
2265                            let exp_alloca = self.fresh_temp();
2266                            self.lines.push(format!("  {exp_alloca} = alloca i64"));
2267                            self.lines.push(format!(
2268                                "  store i64 {}, i64* {exp_alloca}",
2269                                right_val.llvm_value
2270                            ));
2271
2272                            let head = self.fresh_label("pow.head");
2273                            let body = self.fresh_label("pow.body");
2274                            let end = self.fresh_label("pow.end");
2275
2276                            self.lines.push(format!("  br label %{head}"));
2277                            self.lines.push(format!("{head}:"));
2278                            let exp_val = self.fresh_temp();
2279                            self.lines
2280                                .push(format!("  {exp_val} = load i64, i64* {exp_alloca}"));
2281                            let cond = self.fresh_temp();
2282                            self.lines
2283                                .push(format!("  {cond} = icmp sgt i64 {exp_val}, 0"));
2284                            self.lines
2285                                .push(format!("  br i1 {cond}, label %{body}, label %{end}"));
2286
2287                            self.lines.push(format!("{body}:"));
2288                            let res_val = self.fresh_temp();
2289                            self.lines
2290                                .push(format!("  {res_val} = load i64, i64* {result_alloca}"));
2291                            let mul = self.fresh_temp();
2292                            self.lines.push(format!(
2293                                "  {mul} = mul i64 {res_val}, {}",
2294                                left_val.llvm_value
2295                            ));
2296                            self.lines
2297                                .push(format!("  store i64 {mul}, i64* {result_alloca}"));
2298                            let next_exp = self.fresh_temp();
2299                            self.lines
2300                                .push(format!("  {next_exp} = sub i64 {exp_val}, 1"));
2301                            self.lines
2302                                .push(format!("  store i64 {next_exp}, i64* {exp_alloca}"));
2303                            self.lines.push(format!("  br label %{head}"));
2304
2305                            self.lines.push(format!("{end}:"));
2306                            let out = self.fresh_temp();
2307                            self.lines
2308                                .push(format!("  {out} = load i64, i64* {result_alloca}"));
2309                            return ExprValue::new(SimpleTy::I64, out);
2310                        }
2311                        let op = match op {
2312                            tupa_parser::BinaryOp::Add => Some("add"),
2313                            tupa_parser::BinaryOp::Sub => Some("sub"),
2314                            tupa_parser::BinaryOp::Mul => Some("mul"),
2315                            tupa_parser::BinaryOp::Div => Some("sdiv"),
2316                            tupa_parser::BinaryOp::Equal => Some("icmp eq"),
2317                            tupa_parser::BinaryOp::NotEqual => Some("icmp ne"),
2318                            tupa_parser::BinaryOp::Less => Some("icmp slt"),
2319                            tupa_parser::BinaryOp::LessEqual => Some("icmp sle"),
2320                            tupa_parser::BinaryOp::Greater => Some("icmp sgt"),
2321                            tupa_parser::BinaryOp::GreaterEqual => Some("icmp sge"),
2322                            _ => None,
2323                        };
2324                        if let Some(op) = op {
2325                            let tmp = self.fresh_temp();
2326                            if op.starts_with("icmp") {
2327                                self.lines.push(format!(
2328                                    "  {tmp} = {op} i64 {}, {}",
2329                                    left_val.llvm_value, right_val.llvm_value
2330                                ));
2331                                return ExprValue::new(SimpleTy::Bool, tmp);
2332                            }
2333                            self.lines.push(format!(
2334                                "  {tmp} = {op} i64 {}, {}",
2335                                left_val.llvm_value, right_val.llvm_value
2336                            ));
2337                            return ExprValue::new(SimpleTy::I64, tmp);
2338                        }
2339                    }
2340                    (SimpleTy::F64, SimpleTy::F64) => {
2341                        if matches!(op, tupa_parser::BinaryOp::Pow) {
2342                            self.declare_powf();
2343                            let tmp = self.fresh_temp();
2344                            self.lines.push(format!(
2345                                "  {tmp} = call double @llvm.pow.f64(double {}, double {})",
2346                                left_val.llvm_value, right_val.llvm_value
2347                            ));
2348                            return ExprValue::new(SimpleTy::F64, tmp);
2349                        }
2350                        let op = match op {
2351                            tupa_parser::BinaryOp::Add => Some("fadd"),
2352                            tupa_parser::BinaryOp::Sub => Some("fsub"),
2353                            tupa_parser::BinaryOp::Mul => Some("fmul"),
2354                            tupa_parser::BinaryOp::Div => Some("fdiv"),
2355                            tupa_parser::BinaryOp::Equal => Some("fcmp oeq"),
2356                            tupa_parser::BinaryOp::NotEqual => Some("fcmp one"),
2357                            tupa_parser::BinaryOp::Less => Some("fcmp olt"),
2358                            tupa_parser::BinaryOp::LessEqual => Some("fcmp ole"),
2359                            tupa_parser::BinaryOp::Greater => Some("fcmp ogt"),
2360                            tupa_parser::BinaryOp::GreaterEqual => Some("fcmp oge"),
2361                            _ => None,
2362                        };
2363                        if let Some(op) = op {
2364                            let tmp = self.fresh_temp();
2365                            if op.starts_with("fcmp") {
2366                                self.lines.push(format!(
2367                                    "  {tmp} = {op} double {}, {}",
2368                                    left_val.llvm_value, right_val.llvm_value
2369                                ));
2370                                return ExprValue::new(SimpleTy::Bool, tmp);
2371                            }
2372                            self.lines.push(format!(
2373                                "  {tmp} = {op} double {}, {}",
2374                                left_val.llvm_value, right_val.llvm_value
2375                            ));
2376                            return ExprValue::new(SimpleTy::F64, tmp);
2377                        }
2378                    }
2379                    (SimpleTy::Bool, SimpleTy::Bool) => match op {
2380                        tupa_parser::BinaryOp::And => {
2381                            let result_alloca = self.fresh_temp();
2382                            self.lines.push(format!("  {result_alloca} = alloca i1"));
2383                            self.lines
2384                                .push(format!("  store i1 0, i1* {result_alloca}"));
2385                            let rhs_label = self.fresh_label("and.rhs");
2386                            let end_label = self.fresh_label("and.end");
2387                            self.lines.push(format!(
2388                                "  br i1 {}, label %{rhs_label}, label %{end_label}",
2389                                left_val.llvm_value
2390                            ));
2391                            self.lines.push(format!("{rhs_label}:"));
2392                            let rhs_val = self.emit_expr(right, env);
2393                            if rhs_val.ty == SimpleTy::Bool {
2394                                self.lines.push(format!(
2395                                    "  store i1 {}, i1* {result_alloca}",
2396                                    rhs_val.llvm_value
2397                                ));
2398                            }
2399                            self.lines.push(format!("  br label %{end_label}"));
2400                            self.lines.push(format!("{end_label}:"));
2401                            let out = self.fresh_temp();
2402                            self.lines
2403                                .push(format!("  {out} = load i1, i1* {result_alloca}"));
2404                            return ExprValue::new(SimpleTy::Bool, out);
2405                        }
2406                        tupa_parser::BinaryOp::Or => {
2407                            let result_alloca = self.fresh_temp();
2408                            self.lines.push(format!("  {result_alloca} = alloca i1"));
2409                            self.lines
2410                                .push(format!("  store i1 1, i1* {result_alloca}"));
2411                            let rhs_label = self.fresh_label("or.rhs");
2412                            let end_label = self.fresh_label("or.end");
2413                            self.lines.push(format!(
2414                                "  br i1 {}, label %{end_label}, label %{rhs_label}",
2415                                left_val.llvm_value
2416                            ));
2417                            self.lines.push(format!("{rhs_label}:"));
2418                            let rhs_val = self.emit_expr(right, env);
2419                            if rhs_val.ty == SimpleTy::Bool {
2420                                self.lines.push(format!(
2421                                    "  store i1 {}, i1* {result_alloca}",
2422                                    rhs_val.llvm_value
2423                                ));
2424                            }
2425                            self.lines.push(format!("  br label %{end_label}"));
2426                            self.lines.push(format!("{end_label}:"));
2427                            let out = self.fresh_temp();
2428                            self.lines
2429                                .push(format!("  {out} = load i1, i1* {result_alloca}"));
2430                            return ExprValue::new(SimpleTy::Bool, out);
2431                        }
2432                        tupa_parser::BinaryOp::Equal | tupa_parser::BinaryOp::NotEqual => {
2433                            let op = if matches!(op, tupa_parser::BinaryOp::Equal) {
2434                                "icmp eq"
2435                            } else {
2436                                "icmp ne"
2437                            };
2438                            let tmp = self.fresh_temp();
2439                            self.lines.push(format!(
2440                                "  {tmp} = {op} i1 {}, {}",
2441                                left_val.llvm_value, right_val.llvm_value
2442                            ));
2443                            return ExprValue::new(SimpleTy::Bool, tmp);
2444                        }
2445                        _ => {}
2446                    },
2447                    (SimpleTy::Str, SimpleTy::Str) => {
2448                        match op {
2449                            tupa_parser::BinaryOp::Add => {
2450                                return self.emit_string_concat(left_val, right_val);
2451                            }
2452                            tupa_parser::BinaryOp::Equal | tupa_parser::BinaryOp::NotEqual => {
2453                                let op = if matches!(op, tupa_parser::BinaryOp::Equal) {
2454                                    "icmp eq"
2455                                } else {
2456                                    "icmp ne"
2457                                };
2458                                self.declare_strcmp();
2459                                let tmp = self.fresh_temp();
2460                                self.lines.push(format!(
2461                                    "  {tmp} = call i32 @strcmp(i8* {}, i8* {})",
2462                                    left_val.llvm_value, right_val.llvm_value
2463                                ));
2464                                let bool_tmp = self.fresh_temp();
2465                                self.lines.push(format!("  {bool_tmp} = {op} i32 {tmp}, 0"));
2466                                return ExprValue::new(SimpleTy::Bool, bool_tmp);
2467                            }
2468                            _ => {}
2469                        }
2470                        // Unsupported string binary operation
2471                        return ExprValue::new(SimpleTy::Unknown, "0".to_string());
2472                    }
2473                    (SimpleTy::Str, SimpleTy::I64 | SimpleTy::F64 | SimpleTy::Bool)
2474                        if matches!(op, tupa_parser::BinaryOp::Add) =>
2475                    {
2476                        if let Some(rhs_str) = self.emit_format_value(right_val) {
2477                            return self.emit_string_concat(left_val, rhs_str);
2478                        }
2479                    }
2480                    (SimpleTy::I64 | SimpleTy::F64 | SimpleTy::Bool, SimpleTy::Str)
2481                        if matches!(op, tupa_parser::BinaryOp::Add) =>
2482                    {
2483                        if let Some(lhs_str) = self.emit_format_value(left_val) {
2484                            return self.emit_string_concat(lhs_str, right_val);
2485                        }
2486                    }
2487                    _ => {}
2488                }
2489                // Unsupported binary operation
2490                ExprValue::new(SimpleTy::Unknown, "0".to_string())
2491            }
2492            _ => {
2493                // Unsupported expression type
2494                ExprValue::new(SimpleTy::Unknown, "0".to_string())
2495            }
2496        }
2497    }
2498
2499    fn map_type(&self, ty: &Type) -> String {
2500        match ty {
2501            Type::Ident(name) if name == "i64" => "i64".to_string(),
2502            Type::Ident(name) if name == "f64" => "double".to_string(),
2503            Type::Ident(name) if name == "bool" => "i1".to_string(),
2504            Type::Ident(name) if name == "string" => "i8*".to_string(),
2505            Type::Array { elem, .. } if matches!(**elem, Type::Ident(ref n) if n == "i64") => {
2506                "i64*".to_string()
2507            }
2508            Type::Array { elem, .. } if matches!(**elem, Type::Ident(ref n) if n == "f64") => {
2509                "double*".to_string()
2510            }
2511            Type::Array { elem, .. } if matches!(**elem, Type::Ident(ref n) if n == "string") => {
2512                "i8**".to_string()
2513            }
2514            Type::Array { elem, .. } if matches!(**elem, Type::Ident(ref n) if n == "bool") => {
2515                "i1*".to_string()
2516            }
2517            Type::Slice { elem } if matches!(**elem, Type::Ident(ref n) if n == "i64") => {
2518                "i64*".to_string()
2519            }
2520            Type::Slice { elem } if matches!(**elem, Type::Ident(ref n) if n == "f64") => {
2521                "double*".to_string()
2522            }
2523            Type::Slice { elem } if matches!(**elem, Type::Ident(ref n) if n == "string") => {
2524                "i8**".to_string()
2525            }
2526            Type::Slice { elem } if matches!(**elem, Type::Ident(ref n) if n == "bool") => {
2527                "i1*".to_string()
2528            }
2529            Type::Tuple(_) => "void".to_string(),
2530            _ => "void".to_string(),
2531        }
2532    }
2533
2534    fn simple_ty_from_type(&self, ty: &Type) -> SimpleTy {
2535        match ty {
2536            Type::Ident(name) if name == "i64" => SimpleTy::I64,
2537            Type::Ident(name) if name == "f64" => SimpleTy::F64,
2538            Type::Ident(name) if name == "bool" => SimpleTy::Bool,
2539            Type::Ident(name) if name == "string" => SimpleTy::Str,
2540            Type::Array { elem, .. } if matches!(**elem, Type::Ident(ref n) if n == "i64") => {
2541                SimpleTy::I64Ptr
2542            }
2543            Type::Array { elem, .. } if matches!(**elem, Type::Ident(ref n) if n == "f64") => {
2544                SimpleTy::F64Ptr
2545            }
2546            Type::Array { elem, .. } if matches!(**elem, Type::Ident(ref n) if n == "string") => {
2547                SimpleTy::StrPtr
2548            }
2549            Type::Array { elem, .. } if matches!(**elem, Type::Ident(ref n) if n == "bool") => {
2550                SimpleTy::BoolPtr
2551            }
2552            Type::Slice { elem } if matches!(**elem, Type::Ident(ref n) if n == "i64") => {
2553                SimpleTy::I64Ptr
2554            }
2555            Type::Slice { elem } if matches!(**elem, Type::Ident(ref n) if n == "f64") => {
2556                SimpleTy::F64Ptr
2557            }
2558            Type::Slice { elem } if matches!(**elem, Type::Ident(ref n) if n == "string") => {
2559                SimpleTy::StrPtr
2560            }
2561            Type::Slice { elem } if matches!(**elem, Type::Ident(ref n) if n == "bool") => {
2562                SimpleTy::BoolPtr
2563            }
2564            Type::Tuple(_) => SimpleTy::Unknown,
2565            _ => SimpleTy::Unknown,
2566        }
2567    }
2568
2569    fn llvm_ty(&self, ty: SimpleTy) -> &'static str {
2570        match ty {
2571            SimpleTy::I64 => "i64",
2572            SimpleTy::F64 => "double",
2573            SimpleTy::Bool => "i1",
2574            SimpleTy::I64Ptr => "i64*",
2575            SimpleTy::F64Ptr => "double*",
2576            SimpleTy::Str => "i8*",
2577            SimpleTy::StrPtr => "i8**",
2578            SimpleTy::BoolPtr => "i1*",
2579            SimpleTy::Void => "void",
2580            SimpleTy::ClosurePtr => "i8*", // Closures are represented as opaque pointers for now
2581            SimpleTy::Unknown => "i64",
2582        }
2583    }
2584
2585    fn intern_string(&mut self, value: &str) -> (String, usize) {
2586        if let Some((name, len)) = self.string_pool.get(value) {
2587            return (name.clone(), *len);
2588        }
2589        let (escaped, len) = escape_llvm_string(value);
2590        let name = format!("@.str{}", self.string_pool.len());
2591        let literal =
2592            format!("{name} = private unnamed_addr constant [{len} x i8] c\"{escaped}\\00\"");
2593        self.globals.push(literal);
2594        self.string_pool
2595            .insert(value.to_string(), (name.clone(), len));
2596        (name, len)
2597    }
2598
2599    fn intern_format_int(&mut self) -> (String, usize) {
2600        if self.fmt_int_emitted {
2601            return ("@.fmt_int".to_string(), 5);
2602        }
2603        self.fmt_int_emitted = true;
2604        let literal = "@.fmt_int = private unnamed_addr constant [5 x i8] c\"%ld\\0A\\00\"";
2605        self.globals.push(literal.to_string());
2606        ("@.fmt_int".to_string(), 5)
2607    }
2608
2609    fn intern_format_float(&mut self) -> (String, usize) {
2610        if self.fmt_float_emitted {
2611            return ("@.fmt_float".to_string(), 4);
2612        }
2613        self.fmt_float_emitted = true;
2614        let literal = "@.fmt_float = private unnamed_addr constant [4 x i8] c\"%f\\0A\\00\"";
2615        self.globals.push(literal.to_string());
2616        ("@.fmt_float".to_string(), 4)
2617    }
2618
2619    fn declare_printf(&mut self) {
2620        if !self.printf_declared {
2621            self.globals
2622                .push("declare i32 @printf(i8*, ...)".to_string());
2623            self.printf_declared = true;
2624        }
2625    }
2626
2627    fn declare_puts(&mut self) {
2628        if !self.puts_declared {
2629            self.globals.push("declare i32 @puts(i8*)".to_string());
2630            self.puts_declared = true;
2631        }
2632    }
2633
2634    fn declare_strcmp(&mut self) {
2635        if !self.strcmp_declared {
2636            self.globals
2637                .push("declare i32 @strcmp(i8*, i8*)".to_string());
2638            self.strcmp_declared = true;
2639        }
2640    }
2641
2642    fn declare_strlen(&mut self) {
2643        if !self.strlen_declared {
2644            self.globals.push("declare i64 @strlen(i8*)".to_string());
2645            self.strlen_declared = true;
2646        }
2647    }
2648
2649    fn declare_malloc(&mut self) {
2650        if !self.malloc_declared {
2651            self.globals.push("declare i8* @malloc(i64)".to_string());
2652            self.malloc_declared = true;
2653        }
2654    }
2655
2656    fn ensure_malloc_declared(&mut self) {
2657        if !self.malloc_declared {
2658            self.globals.push("declare i8* @malloc(i64)".to_string());
2659            self.malloc_declared = true;
2660        }
2661    }
2662
2663    fn declare_strcpy(&mut self) {
2664        if !self.strcpy_declared {
2665            self.globals
2666                .push("declare i8* @strcpy(i8*, i8*)".to_string());
2667            self.strcpy_declared = true;
2668        }
2669    }
2670
2671    fn declare_strcat(&mut self) {
2672        if !self.strcat_declared {
2673            self.globals
2674                .push("declare i8* @strcat(i8*, i8*)".to_string());
2675            self.strcat_declared = true;
2676        }
2677    }
2678
2679    fn fresh_temp(&mut self) -> String {
2680        let name = format!("%t{}", self.temp);
2681        self.temp += 1;
2682        name
2683    }
2684
2685    fn fresh_label(&mut self, prefix: &str) -> String {
2686        let name = format!("{prefix}{}", self.label);
2687        self.label += 1;
2688        name
2689    }
2690
2691    fn extract_range(
2692        &mut self,
2693        iter: &Expr,
2694        env: &mut HashMap<String, LocalVar>,
2695    ) -> Option<(ExprValue, ExprValue)> {
2696        if let ExprKind::Binary { op, left, right } = &iter.kind {
2697            if *op != tupa_parser::BinaryOp::Range {
2698                return None;
2699            }
2700            let start = self.emit_expr(left, env);
2701            let end = self.emit_expr(right, env);
2702            if start.ty == SimpleTy::I64 && end.ty == SimpleTy::I64 {
2703                return Some((start, end));
2704            }
2705        }
2706        None
2707    }
2708
2709    fn finish(mut self) -> String {
2710        if !self.globals.is_empty() {
2711            self.globals.push(String::new());
2712        }
2713        self.globals.extend(self.lines);
2714        self.globals.join("\n")
2715    }
2716}
2717
2718struct ExprValue {
2719    ty: SimpleTy,
2720    llvm_value: String,
2721}
2722
2723#[derive(Debug, Clone, Copy, PartialEq, Eq)]
2724enum ControlFlow {
2725    None,
2726    Break,
2727    Continue,
2728    Return,
2729}
2730
2731impl ExprValue {
2732    fn new(ty: SimpleTy, llvm_value: String) -> Self {
2733        Self { ty, llvm_value }
2734    }
2735}
2736
2737fn escape_llvm_string(value: &str) -> (String, usize) {
2738    let mut out = String::new();
2739    let mut len = 0usize;
2740    for &byte in value.as_bytes() {
2741        len += 1;
2742        match byte {
2743            b'\\' => out.push_str("\\5C"),
2744            b'"' => out.push_str("\\22"),
2745            b'\n' => out.push_str("\\0A"),
2746            b'\r' => out.push_str("\\0D"),
2747            b'\t' => out.push_str("\\09"),
2748            0x20..=0x7E => out.push(byte as char),
2749            _ => out.push_str(&format!("\\{:02X}", byte)),
2750        }
2751    }
2752    (out, len + 1)
2753}
2754
2755#[cfg(test)]
2756mod tests {
2757    use super::*;
2758    use tupa_lexer::Span;
2759    use tupa_parser::{BinaryOp, Expr, ExprKind, Function, Item, Param, Program, Stmt, Type};
2760
2761    #[test]
2762    fn test_empty_function_codegen() {
2763        let program = Program {
2764            items: vec![Item::Function(Function {
2765                attrs: vec![],
2766                external_spec: None,
2767                name: "main".to_string(),
2768                params: vec![],
2769                return_type: None,
2770                body: vec![],
2771            })],
2772        };
2773        let code = generate_stub(&program);
2774        assert!(code.contains("define void @main()"));
2775        assert!(code.contains("entry:"));
2776        assert!(code.contains("ret void"));
2777    }
2778
2779    #[test]
2780    fn test_function_with_return_codegen() {
2781        let program = Program {
2782            items: vec![Item::Function(Function {
2783                attrs: vec![],
2784                external_spec: None,
2785                name: "test".to_string(),
2786                params: vec![],
2787                return_type: Some(Type::Ident("i64".to_string())),
2788                body: vec![Stmt::Return(Some(Expr {
2789                    kind: ExprKind::Int(42),
2790                    span: Span { start: 0, end: 0 },
2791                }))],
2792            })],
2793        };
2794        let code = generate_stub(&program);
2795        assert!(code.contains("define i64 @test()"));
2796        assert!(code.contains("ret i64 42"));
2797    }
2798
2799    #[test]
2800    fn test_string_literal_codegen() {
2801        let program = Program {
2802            items: vec![Item::Function(Function {
2803                attrs: vec![],
2804                external_spec: None,
2805                name: "test".to_string(),
2806                params: vec![],
2807                return_type: None,
2808                body: vec![Stmt::Expr(Expr {
2809                    kind: ExprKind::Str("hello".to_string()),
2810                    span: Span { start: 0, end: 0 },
2811                })],
2812            })],
2813        };
2814        let code = generate_stub(&program);
2815        assert!(code.contains("@.str"));
2816        assert!(code.contains("hello"));
2817    }
2818
2819    #[test]
2820    fn test_f64_pow_codegen() {
2821        let program = Program {
2822            items: vec![Item::Function(Function {
2823                attrs: vec![],
2824                external_spec: None,
2825                name: "powf".to_string(),
2826                params: vec![Param {
2827                    name: "x".to_string(),
2828                    ty: Type::Ident("f64".to_string()),
2829                }],
2830                return_type: Some(Type::Ident("f64".to_string())),
2831                body: vec![Stmt::Return(Some(Expr {
2832                    kind: ExprKind::Binary {
2833                        op: BinaryOp::Pow,
2834                        left: Box::new(Expr {
2835                            kind: ExprKind::Ident("x".to_string()),
2836                            span: Span { start: 0, end: 0 },
2837                        }),
2838                        right: Box::new(Expr {
2839                            kind: ExprKind::Float(3.0),
2840                            span: Span { start: 0, end: 0 },
2841                        }),
2842                    },
2843                    span: Span { start: 0, end: 0 },
2844                }))],
2845            })],
2846        };
2847        let code = generate_stub(&program);
2848        assert!(code.contains("declare double @llvm.pow.f64(double, double)"));
2849        assert!(code.contains("call double @llvm.pow.f64"));
2850    }
2851
2852    #[test]
2853    fn test_f64_pow_folding() {
2854        let program = Program {
2855            items: vec![Item::Function(Function {
2856                attrs: vec![],
2857                external_spec: None,
2858                name: "powf_const".to_string(),
2859                params: vec![],
2860                return_type: Some(Type::Ident("f64".to_string())),
2861                body: vec![Stmt::Return(Some(Expr {
2862                    kind: ExprKind::Binary {
2863                        op: BinaryOp::Pow,
2864                        left: Box::new(Expr {
2865                            kind: ExprKind::Float(2.0),
2866                            span: Span { start: 0, end: 0 },
2867                        }),
2868                        right: Box::new(Expr {
2869                            kind: ExprKind::Float(3.0),
2870                            span: Span { start: 0, end: 0 },
2871                        }),
2872                    },
2873                    span: Span { start: 0, end: 0 },
2874                }))],
2875            })],
2876        };
2877        let code = generate_stub(&program);
2878        assert!(code.contains("ret double 8"));
2879        assert!(!code.contains("llvm.pow.f64"));
2880    }
2881
2882    #[test]
2883    fn test_i64_pow_folding() {
2884        let program = Program {
2885            items: vec![Item::Function(Function {
2886                attrs: vec![],
2887                external_spec: None,
2888                name: "powi_const".to_string(),
2889                params: vec![],
2890                return_type: Some(Type::Ident("i64".to_string())),
2891                body: vec![Stmt::Return(Some(Expr {
2892                    kind: ExprKind::Binary {
2893                        op: BinaryOp::Pow,
2894                        left: Box::new(Expr {
2895                            kind: ExprKind::Int(2),
2896                            span: Span { start: 0, end: 0 },
2897                        }),
2898                        right: Box::new(Expr {
2899                            kind: ExprKind::Int(3),
2900                            span: Span { start: 0, end: 0 },
2901                        }),
2902                    },
2903                    span: Span { start: 0, end: 0 },
2904                }))],
2905            })],
2906        };
2907        let code = generate_stub(&program);
2908        assert!(code.contains("ret i64 8"));
2909    }
2910
2911    #[test]
2912    fn test_lambda_captures_only_used_vars() {
2913        let program = Program {
2914            items: vec![Item::Function(Function {
2915                attrs: vec![],
2916                external_spec: None,
2917                name: "main".to_string(),
2918                params: vec![],
2919                return_type: None,
2920                body: vec![
2921                    Stmt::Let {
2922                        name: "x".to_string(),
2923                        ty: None,
2924                        expr: Expr {
2925                            kind: ExprKind::Int(1),
2926                            span: Span { start: 0, end: 0 },
2927                        },
2928                    },
2929                    Stmt::Let {
2930                        name: "y".to_string(),
2931                        ty: None,
2932                        expr: Expr {
2933                            kind: ExprKind::Int(2),
2934                            span: Span { start: 0, end: 0 },
2935                        },
2936                    },
2937                    Stmt::Let {
2938                        name: "f".to_string(),
2939                        ty: None,
2940                        expr: Expr {
2941                            kind: ExprKind::Lambda {
2942                                params: vec!["z".to_string()],
2943                                body: Box::new(Expr {
2944                                    kind: ExprKind::Binary {
2945                                        op: BinaryOp::Add,
2946                                        left: Box::new(Expr {
2947                                            kind: ExprKind::Ident("x".to_string()),
2948                                            span: Span { start: 0, end: 0 },
2949                                        }),
2950                                        right: Box::new(Expr {
2951                                            kind: ExprKind::Ident("z".to_string()),
2952                                            span: Span { start: 0, end: 0 },
2953                                        }),
2954                                    },
2955                                    span: Span { start: 0, end: 0 },
2956                                }),
2957                            },
2958                            span: Span { start: 0, end: 0 },
2959                        },
2960                    },
2961                ],
2962            })],
2963        };
2964        let code = generate_stub(&program);
2965        assert!(code.contains("%env_lambda_"));
2966        assert!(code.contains(" i64 x"));
2967        assert!(!code.contains(" i64 y"));
2968    }
2969}