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