sigil_parser/
lower.rs

1//! AST to IR Lowering Pass
2//!
3//! Converts the parsed AST into the AI-facing IR format.
4//! This pass:
5//! - Generates unique IDs for all definitions
6//! - Infers types where needed (or uses placeholders)
7//! - Propagates evidentiality through expressions
8//! - Normalizes pipeline operations
9
10use crate::ast::{self, SourceFile};
11use crate::ir::*;
12use std::collections::HashMap;
13
14/// Counter for generating unique IDs
15struct IdGenerator {
16    counters: HashMap<String, usize>,
17}
18
19impl IdGenerator {
20    fn new() -> Self {
21        Self {
22            counters: HashMap::new(),
23        }
24    }
25
26    fn next(&mut self, prefix: &str) -> String {
27        let count = self.counters.entry(prefix.to_string()).or_insert(0);
28        *count += 1;
29        format!("{}_{:03}", prefix, count)
30    }
31}
32
33/// Lowering context
34pub struct LoweringContext {
35    ids: IdGenerator,
36    /// Current scope for variable resolution
37    scope: Vec<HashMap<String, String>>,
38    /// Inferred types for expressions (placeholder for now)
39    type_cache: HashMap<String, IrType>,
40}
41
42impl LoweringContext {
43    pub fn new() -> Self {
44        Self {
45            ids: IdGenerator::new(),
46            scope: vec![HashMap::new()],
47            type_cache: HashMap::new(),
48        }
49    }
50
51    fn push_scope(&mut self) {
52        self.scope.push(HashMap::new());
53    }
54
55    fn pop_scope(&mut self) {
56        self.scope.pop();
57    }
58
59    fn bind_var(&mut self, name: &str) -> String {
60        let id = self.ids.next("var");
61        if let Some(scope) = self.scope.last_mut() {
62            scope.insert(name.to_string(), id.clone());
63        }
64        id
65    }
66
67    fn lookup_var(&self, name: &str) -> Option<String> {
68        for scope in self.scope.iter().rev() {
69            if let Some(id) = scope.get(name) {
70                return Some(id.clone());
71            }
72        }
73        None
74    }
75}
76
77impl Default for LoweringContext {
78    fn default() -> Self {
79        Self::new()
80    }
81}
82
83/// Lower a complete source file to IR
84pub fn lower_source_file(source: &str, ast: &SourceFile) -> IrModule {
85    let mut ctx = LoweringContext::new();
86    let mut module = IrModule::new(source.to_string());
87
88    for item in &ast.items {
89        lower_item(&mut ctx, &mut module, &item.node);
90    }
91
92    module
93}
94
95fn lower_item(ctx: &mut LoweringContext, module: &mut IrModule, item: &ast::Item) {
96    match item {
97        ast::Item::Function(f) => {
98            if let Some(ir_fn) = lower_function(ctx, f) {
99                module.functions.push(ir_fn);
100            }
101        }
102        ast::Item::Struct(s) => {
103            module.types.push(lower_struct_def(ctx, s));
104        }
105        ast::Item::Enum(e) => {
106            module.types.push(lower_enum_def(ctx, e));
107        }
108        ast::Item::Trait(t) => {
109            module.traits.push(lower_trait_def(ctx, t));
110        }
111        ast::Item::Impl(i) => {
112            module.impls.push(lower_impl_block(ctx, i));
113        }
114        ast::Item::TypeAlias(t) => {
115            module.types.push(lower_type_alias(ctx, t));
116        }
117        ast::Item::Const(c) => {
118            module.constants.push(lower_const_def(ctx, c));
119        }
120        ast::Item::Module(m) => {
121            if let Some(items) = &m.items {
122                for item in items {
123                    lower_item(ctx, module, &item.node);
124                }
125            }
126        }
127        ast::Item::Static(_) | ast::Item::Actor(_) | ast::Item::Use(_) | ast::Item::ExternBlock(_) => {
128            // TODO: Handle these items
129        }
130    }
131}
132
133fn lower_function(ctx: &mut LoweringContext, f: &ast::Function) -> Option<IrFunction> {
134    let id = ctx.ids.next("fn");
135    ctx.push_scope();
136
137    let params: Vec<IrParam> = f
138        .params
139        .iter()
140        .map(|p| lower_param(ctx, p))
141        .collect();
142
143    let return_type = f
144        .return_type
145        .as_ref()
146        .map(|t| lower_type_expr(t))
147        .unwrap_or(IrType::Unit);
148
149    let body = f.body.as_ref().map(|b| lower_block(ctx, b));
150
151    let mut attributes = Vec::new();
152    if f.is_async {
153        attributes.push("async".to_string());
154    }
155    if f.attrs.inline.is_some() {
156        attributes.push("inline".to_string());
157    }
158    if f.attrs.naked {
159        attributes.push("naked".to_string());
160    }
161    if f.attrs.no_mangle {
162        attributes.push("no_mangle".to_string());
163    }
164
165    ctx.pop_scope();
166
167    Some(IrFunction {
168        name: f.name.name.clone(),
169        id,
170        visibility: lower_visibility(f.visibility),
171        generics: lower_generics(&f.generics),
172        params,
173        return_type,
174        body,
175        attributes,
176        is_async: f.is_async,
177        span: None,
178    })
179}
180
181fn lower_param(ctx: &mut LoweringContext, p: &ast::Param) -> IrParam {
182    let name = extract_pattern_name(&p.pattern);
183    let evidence = extract_pattern_evidence(&p.pattern);
184    ctx.bind_var(&name);
185
186    IrParam {
187        name,
188        ty: lower_type_expr(&p.ty),
189        evidence,
190    }
191}
192
193fn extract_pattern_name(p: &ast::Pattern) -> String {
194    match p {
195        ast::Pattern::Ident { name, .. } => name.name.clone(),
196        ast::Pattern::Tuple(pats) if !pats.is_empty() => extract_pattern_name(&pats[0]),
197        _ => "_".to_string(),
198    }
199}
200
201fn extract_pattern_evidence(p: &ast::Pattern) -> IrEvidence {
202    match p {
203        ast::Pattern::Ident { evidentiality, .. } => {
204            evidentiality.map(lower_evidentiality).unwrap_or(IrEvidence::Known)
205        }
206        _ => IrEvidence::Known,
207    }
208}
209
210fn lower_visibility(v: ast::Visibility) -> IrVisibility {
211    match v {
212        ast::Visibility::Public => IrVisibility::Public,
213        ast::Visibility::Private => IrVisibility::Private,
214        ast::Visibility::Crate => IrVisibility::Crate,
215        ast::Visibility::Super => IrVisibility::Private,
216    }
217}
218
219fn lower_evidentiality(e: ast::Evidentiality) -> IrEvidence {
220    match e {
221        ast::Evidentiality::Known => IrEvidence::Known,
222        ast::Evidentiality::Uncertain => IrEvidence::Uncertain,
223        ast::Evidentiality::Reported => IrEvidence::Reported,
224        ast::Evidentiality::Paradox => IrEvidence::Paradox,
225    }
226}
227
228fn lower_generics(g: &Option<ast::Generics>) -> Vec<IrGenericParam> {
229    g.as_ref()
230        .map(|g| {
231            g.params
232                .iter()
233                .filter_map(|p| match p {
234                    ast::GenericParam::Type { name, bounds, .. } => Some(IrGenericParam {
235                        name: name.name.clone(),
236                        bounds: bounds.iter().map(type_expr_to_string).collect(),
237                    }),
238                    ast::GenericParam::Const { name, .. } => Some(IrGenericParam {
239                        name: name.name.clone(),
240                        bounds: vec!["const".to_string()],
241                    }),
242                    ast::GenericParam::Lifetime(l) => Some(IrGenericParam {
243                        name: l.clone(),
244                        bounds: vec!["lifetime".to_string()],
245                    }),
246                })
247                .collect()
248        })
249        .unwrap_or_default()
250}
251
252fn type_expr_to_string(t: &ast::TypeExpr) -> String {
253    match t {
254        ast::TypeExpr::Path(p) => p
255            .segments
256            .iter()
257            .map(|s| s.ident.name.clone())
258            .collect::<Vec<_>>()
259            .join("::"),
260        _ => "?".to_string(),
261    }
262}
263
264fn lower_type_expr(t: &ast::TypeExpr) -> IrType {
265    match t {
266        ast::TypeExpr::Path(p) => {
267            let name = p
268                .segments
269                .iter()
270                .map(|s| s.ident.name.clone())
271                .collect::<Vec<_>>()
272                .join("::");
273
274            // Check for primitive types
275            match name.as_str() {
276                "i8" | "i16" | "i32" | "i64" | "i128" | "isize" | "u8" | "u16" | "u32" | "u64"
277                | "u128" | "usize" | "f32" | "f64" | "bool" | "char" | "str" => {
278                    IrType::Primitive { name }
279                }
280                "()" => IrType::Unit,
281                "!" | "never" => IrType::Never,
282                _ => {
283                    let generics = p
284                        .segments
285                        .last()
286                        .and_then(|s| s.generics.as_ref())
287                        .map(|gs| gs.iter().map(lower_type_expr).collect())
288                        .unwrap_or_default();
289
290                    IrType::Named { name, generics }
291                }
292            }
293        }
294        ast::TypeExpr::Reference { mutable, inner } => IrType::Reference {
295            mutable: *mutable,
296            inner: Box::new(lower_type_expr(inner)),
297        },
298        ast::TypeExpr::Pointer { mutable, inner } => IrType::Pointer {
299            mutable: *mutable,
300            inner: Box::new(lower_type_expr(inner)),
301        },
302        ast::TypeExpr::Array { element, size: _ } => IrType::Array {
303            element: Box::new(lower_type_expr(element)),
304            size: None, // Would need const eval
305        },
306        ast::TypeExpr::Slice(inner) => IrType::Slice {
307            element: Box::new(lower_type_expr(inner)),
308        },
309        ast::TypeExpr::Tuple(elements) => IrType::Tuple {
310            elements: elements.iter().map(lower_type_expr).collect(),
311        },
312        ast::TypeExpr::Function {
313            params,
314            return_type,
315        } => IrType::Function {
316            params: params.iter().map(lower_type_expr).collect(),
317            return_type: Box::new(
318                return_type
319                    .as_ref()
320                    .map(|r| lower_type_expr(r))
321                    .unwrap_or(IrType::Unit),
322            ),
323            is_async: false,
324        },
325        ast::TypeExpr::Evidential {
326            inner,
327            evidentiality,
328        } => IrType::Evidential {
329            inner: Box::new(lower_type_expr(inner)),
330            evidence: lower_evidentiality(*evidentiality),
331        },
332        ast::TypeExpr::Cycle { .. } => IrType::Cycle { modulus: 0 },
333        ast::TypeExpr::Simd { element, lanes } => IrType::Simd {
334            element: Box::new(lower_type_expr(element)),
335            lanes: *lanes as usize,
336        },
337        ast::TypeExpr::Atomic(inner) => IrType::Atomic {
338            inner: Box::new(lower_type_expr(inner)),
339        },
340        ast::TypeExpr::Never => IrType::Never,
341        ast::TypeExpr::Infer => IrType::Infer,
342    }
343}
344
345fn lower_block(ctx: &mut LoweringContext, block: &ast::Block) -> IrOperation {
346    ctx.push_scope();
347
348    let mut statements: Vec<IrOperation> = block
349        .stmts
350        .iter()
351        .filter_map(|s| lower_stmt(ctx, s))
352        .collect();
353
354    if let Some(expr) = &block.expr {
355        statements.push(lower_expr(ctx, expr));
356    }
357
358    ctx.pop_scope();
359
360    IrOperation::Block {
361        statements,
362        ty: IrType::Infer,
363        evidence: IrEvidence::Known,
364    }
365}
366
367fn lower_stmt(ctx: &mut LoweringContext, stmt: &ast::Stmt) -> Option<IrOperation> {
368    match stmt {
369        ast::Stmt::Let { pattern, ty, init } => {
370            let ir_pattern = lower_pattern(ctx, pattern);
371            let type_annotation = ty.as_ref().map(lower_type_expr);
372            let init_expr = init
373                .as_ref()
374                .map(|e| Box::new(lower_expr(ctx, e)))
375                .unwrap_or_else(|| {
376                    Box::new(IrOperation::Literal {
377                        variant: LiteralVariant::Null,
378                        value: serde_json::Value::Null,
379                        ty: IrType::Unit,
380                        evidence: IrEvidence::Known,
381                    })
382                });
383
384            Some(IrOperation::Let {
385                pattern: ir_pattern,
386                type_annotation,
387                init: init_expr,
388                evidence: IrEvidence::Known,
389            })
390        }
391        ast::Stmt::Expr(e) => Some(lower_expr(ctx, e)),
392        ast::Stmt::Semi(e) => Some(lower_expr(ctx, e)),
393        ast::Stmt::Item(_) => None, // Handled at module level
394    }
395}
396
397fn lower_pattern(ctx: &mut LoweringContext, pattern: &ast::Pattern) -> IrPattern {
398    match pattern {
399        ast::Pattern::Ident {
400            mutable,
401            name,
402            evidentiality,
403        } => {
404            ctx.bind_var(&name.name);
405            IrPattern::Ident {
406                name: name.name.clone(),
407                mutable: *mutable,
408                evidence: evidentiality.map(lower_evidentiality),
409            }
410        }
411        ast::Pattern::Tuple(pats) => IrPattern::Tuple {
412            elements: pats.iter().map(|p| lower_pattern(ctx, p)).collect(),
413        },
414        ast::Pattern::Struct { path, fields, rest } => IrPattern::Struct {
415            path: path
416                .segments
417                .iter()
418                .map(|s| s.ident.name.clone())
419                .collect::<Vec<_>>()
420                .join("::"),
421            fields: fields
422                .iter()
423                .map(|f| {
424                    (
425                        f.name.name.clone(),
426                        f.pattern
427                            .as_ref()
428                            .map(|p| lower_pattern(ctx, p))
429                            .unwrap_or(IrPattern::Ident {
430                                name: f.name.name.clone(),
431                                mutable: false,
432                                evidence: None,
433                            }),
434                    )
435                })
436                .collect(),
437            rest: *rest,
438        },
439        ast::Pattern::TupleStruct { path, fields } => IrPattern::TupleStruct {
440            path: path
441                .segments
442                .iter()
443                .map(|s| s.ident.name.clone())
444                .collect::<Vec<_>>()
445                .join("::"),
446            fields: fields.iter().map(|p| lower_pattern(ctx, p)).collect(),
447        },
448        ast::Pattern::Slice(pats) => IrPattern::Slice {
449            elements: pats.iter().map(|p| lower_pattern(ctx, p)).collect(),
450        },
451        ast::Pattern::Or(pats) => IrPattern::Or {
452            patterns: pats.iter().map(|p| lower_pattern(ctx, p)).collect(),
453        },
454        ast::Pattern::Literal(lit) => IrPattern::Literal {
455            value: lower_literal_value(lit),
456        },
457        ast::Pattern::Range {
458            start,
459            end,
460            inclusive,
461        } => IrPattern::Range {
462            start: start.as_ref().map(|p| Box::new(lower_pattern(ctx, p))),
463            end: end.as_ref().map(|p| Box::new(lower_pattern(ctx, p))),
464            inclusive: *inclusive,
465        },
466        ast::Pattern::Wildcard | ast::Pattern::Rest => IrPattern::Wildcard,
467    }
468}
469
470fn lower_expr(ctx: &mut LoweringContext, expr: &ast::Expr) -> IrOperation {
471    match expr {
472        ast::Expr::Literal(lit) => lower_literal(lit),
473
474        ast::Expr::Path(path) => {
475            let name = path
476                .segments
477                .iter()
478                .map(|s| s.ident.name.clone())
479                .collect::<Vec<_>>()
480                .join("::");
481            let id = ctx.lookup_var(&name).unwrap_or_else(|| name.clone());
482
483            IrOperation::Var {
484                name,
485                id,
486                ty: IrType::Infer,
487                evidence: IrEvidence::Known,
488            }
489        }
490
491        ast::Expr::Binary { left, op, right } => {
492            let left_ir = lower_expr(ctx, left);
493            let right_ir = lower_expr(ctx, right);
494            let left_ev = get_operation_evidence(&left_ir);
495            let right_ev = get_operation_evidence(&right_ir);
496
497            IrOperation::Binary {
498                operator: lower_binop(*op),
499                left: Box::new(left_ir),
500                right: Box::new(right_ir),
501                ty: IrType::Infer,
502                evidence: left_ev.join(right_ev),
503            }
504        }
505
506        ast::Expr::Unary { op, expr: inner } => {
507            let inner_ir = lower_expr(ctx, inner);
508            let evidence = get_operation_evidence(&inner_ir);
509
510            IrOperation::Unary {
511                operator: lower_unaryop(*op),
512                operand: Box::new(inner_ir),
513                ty: IrType::Infer,
514                evidence,
515            }
516        }
517
518        ast::Expr::Call { func, args } => {
519            let func_name = match func.as_ref() {
520                ast::Expr::Path(p) => p
521                    .segments
522                    .iter()
523                    .map(|s| s.ident.name.clone())
524                    .collect::<Vec<_>>()
525                    .join("::"),
526                _ => "anonymous".to_string(),
527            };
528
529            let args_ir: Vec<IrOperation> = args.iter().map(|a| lower_expr(ctx, a)).collect();
530            let evidence = args_ir
531                .iter()
532                .map(get_operation_evidence)
533                .fold(IrEvidence::Known, |acc, e| acc.join(e));
534
535            IrOperation::Call {
536                function: func_name.clone(),
537                function_id: format!("fn_{}", func_name),
538                args: args_ir,
539                type_args: vec![],
540                ty: IrType::Infer,
541                evidence,
542            }
543        }
544
545        ast::Expr::MethodCall {
546            receiver,
547            method,
548            args,
549        } => {
550            let receiver_ir = lower_expr(ctx, receiver);
551            let args_ir: Vec<IrOperation> = args.iter().map(|a| lower_expr(ctx, a)).collect();
552            let evidence = std::iter::once(get_operation_evidence(&receiver_ir))
553                .chain(args_ir.iter().map(get_operation_evidence))
554                .fold(IrEvidence::Known, |acc, e| acc.join(e));
555
556            IrOperation::MethodCall {
557                receiver: Box::new(receiver_ir),
558                method: method.name.clone(),
559                args: args_ir,
560                type_args: vec![],
561                ty: IrType::Infer,
562                evidence,
563            }
564        }
565
566        ast::Expr::Pipe { expr, operations } => lower_pipeline(ctx, expr, operations),
567
568        ast::Expr::If {
569            condition,
570            then_branch,
571            else_branch,
572        } => {
573            let cond_ir = lower_expr(ctx, condition);
574            let then_ir = lower_block(ctx, then_branch);
575            let else_ir = else_branch.as_ref().map(|e| Box::new(lower_expr(ctx, e)));
576
577            let evidence = get_operation_evidence(&cond_ir)
578                .join(get_operation_evidence(&then_ir))
579                .join(
580                    else_ir
581                        .as_ref()
582                        .map(|e| get_operation_evidence(e))
583                        .unwrap_or(IrEvidence::Known),
584                );
585
586            IrOperation::If {
587                condition: Box::new(cond_ir),
588                then_branch: Box::new(then_ir),
589                else_branch: else_ir,
590                ty: IrType::Infer,
591                evidence,
592            }
593        }
594
595        ast::Expr::Match { expr: scrutinee, arms } => {
596            let scrutinee_ir = lower_expr(ctx, scrutinee);
597            let arms_ir: Vec<IrMatchArm> = arms
598                .iter()
599                .map(|arm| {
600                    ctx.push_scope();
601                    let pattern = lower_pattern(ctx, &arm.pattern);
602                    let guard = arm.guard.as_ref().map(|g| lower_expr(ctx, g));
603                    let body = lower_expr(ctx, &arm.body);
604                    ctx.pop_scope();
605                    IrMatchArm {
606                        pattern,
607                        guard,
608                        body,
609                    }
610                })
611                .collect();
612
613            IrOperation::Match {
614                scrutinee: Box::new(scrutinee_ir),
615                arms: arms_ir,
616                ty: IrType::Infer,
617                evidence: IrEvidence::Known,
618            }
619        }
620
621        ast::Expr::Loop(block) => IrOperation::Loop {
622            variant: LoopVariant::Infinite,
623            condition: None,
624            iterator: None,
625            body: Box::new(lower_block(ctx, block)),
626            ty: IrType::Never,
627            evidence: IrEvidence::Known,
628        },
629
630        ast::Expr::While { condition, body } => IrOperation::Loop {
631            variant: LoopVariant::While,
632            condition: Some(Box::new(lower_expr(ctx, condition))),
633            iterator: None,
634            body: Box::new(lower_block(ctx, body)),
635            ty: IrType::Unit,
636            evidence: IrEvidence::Known,
637        },
638
639        ast::Expr::For { pattern, iter, body } => {
640            ctx.push_scope();
641            let pat = lower_pattern(ctx, pattern);
642            let iter_ir = lower_expr(ctx, iter);
643            let body_ir = lower_block(ctx, body);
644            ctx.pop_scope();
645
646            IrOperation::Loop {
647                variant: LoopVariant::For,
648                condition: None,
649                iterator: Some(IrForIterator {
650                    pattern: pat,
651                    iterable: Box::new(iter_ir),
652                }),
653                body: Box::new(body_ir),
654                ty: IrType::Unit,
655                evidence: IrEvidence::Known,
656            }
657        }
658
659        ast::Expr::Closure { params, body } => {
660            ctx.push_scope();
661            let params_ir: Vec<IrParam> = params
662                .iter()
663                .map(|p| {
664                    let name = extract_pattern_name(&p.pattern);
665                    ctx.bind_var(&name);
666                    IrParam {
667                        name,
668                        ty: p.ty.as_ref().map(lower_type_expr).unwrap_or(IrType::Infer),
669                        evidence: IrEvidence::Known,
670                    }
671                })
672                .collect();
673            let body_ir = lower_expr(ctx, body);
674            ctx.pop_scope();
675
676            IrOperation::Closure {
677                params: params_ir,
678                body: Box::new(body_ir),
679                captures: vec![],
680                ty: IrType::Infer,
681                evidence: IrEvidence::Known,
682            }
683        }
684
685        ast::Expr::Block(block) => lower_block(ctx, block),
686
687        ast::Expr::Array(elements) => {
688            let elements_ir: Vec<IrOperation> = elements.iter().map(|e| lower_expr(ctx, e)).collect();
689            IrOperation::Array {
690                elements: elements_ir,
691                ty: IrType::Infer,
692                evidence: IrEvidence::Known,
693            }
694        }
695
696        ast::Expr::Tuple(elements) => {
697            let elements_ir: Vec<IrOperation> = elements.iter().map(|e| lower_expr(ctx, e)).collect();
698            IrOperation::Tuple {
699                elements: elements_ir,
700                ty: IrType::Infer,
701                evidence: IrEvidence::Known,
702            }
703        }
704
705        ast::Expr::Struct { path, fields, rest } => {
706            let name = path
707                .segments
708                .iter()
709                .map(|s| s.ident.name.clone())
710                .collect::<Vec<_>>()
711                .join("::");
712
713            let fields_ir: Vec<(String, IrOperation)> = fields
714                .iter()
715                .map(|f| {
716                    let value = f
717                        .value
718                        .as_ref()
719                        .map(|v| lower_expr(ctx, v))
720                        .unwrap_or_else(|| IrOperation::Var {
721                            name: f.name.name.clone(),
722                            id: ctx.lookup_var(&f.name.name).unwrap_or_default(),
723                            ty: IrType::Infer,
724                            evidence: IrEvidence::Known,
725                        });
726                    (f.name.name.clone(), value)
727                })
728                .collect();
729
730            IrOperation::StructInit {
731                name,
732                fields: fields_ir,
733                rest: rest.as_ref().map(|r| Box::new(lower_expr(ctx, r))),
734                ty: IrType::Infer,
735                evidence: IrEvidence::Known,
736            }
737        }
738
739        ast::Expr::Field { expr: inner, field } => IrOperation::Field {
740            expr: Box::new(lower_expr(ctx, inner)),
741            field: field.name.clone(),
742            ty: IrType::Infer,
743            evidence: IrEvidence::Known,
744        },
745
746        ast::Expr::Index { expr: inner, index } => IrOperation::Index {
747            expr: Box::new(lower_expr(ctx, inner)),
748            index: Box::new(lower_expr(ctx, index)),
749            ty: IrType::Infer,
750            evidence: IrEvidence::Known,
751        },
752
753        ast::Expr::Assign { target, value } => IrOperation::Assign {
754            target: Box::new(lower_expr(ctx, target)),
755            value: Box::new(lower_expr(ctx, value)),
756            evidence: IrEvidence::Known,
757        },
758
759        ast::Expr::Return(value) => IrOperation::Return {
760            value: value.as_ref().map(|v| Box::new(lower_expr(ctx, v))),
761            evidence: IrEvidence::Known,
762        },
763
764        ast::Expr::Break(value) => IrOperation::Break {
765            value: value.as_ref().map(|v| Box::new(lower_expr(ctx, v))),
766            evidence: IrEvidence::Known,
767        },
768
769        ast::Expr::Continue => IrOperation::Continue {
770            evidence: IrEvidence::Known,
771        },
772
773        ast::Expr::Await(inner) => {
774            let inner_ir = lower_expr(ctx, inner);
775            let evidence = get_operation_evidence(&inner_ir);
776            IrOperation::Await {
777                expr: Box::new(inner_ir),
778                ty: IrType::Infer,
779                evidence,
780            }
781        }
782
783        ast::Expr::Try(inner) => {
784            let inner_ir = lower_expr(ctx, inner);
785            IrOperation::Try {
786                expr: Box::new(inner_ir),
787                ty: IrType::Infer,
788                evidence: IrEvidence::Uncertain, // Try always produces uncertain
789            }
790        }
791
792        ast::Expr::Unsafe(block) => IrOperation::Unsafe {
793            body: Box::new(lower_block(ctx, block)),
794            ty: IrType::Infer,
795            evidence: IrEvidence::Paradox, // Unsafe always produces paradox
796        },
797
798        ast::Expr::Cast { expr: inner, ty } => {
799            let inner_ir = lower_expr(ctx, inner);
800            let evidence = get_operation_evidence(&inner_ir);
801            IrOperation::Cast {
802                expr: Box::new(inner_ir),
803                target_type: lower_type_expr(ty),
804                ty: lower_type_expr(ty),
805                evidence,
806            }
807        }
808
809        ast::Expr::Evidential {
810            expr: inner,
811            evidentiality,
812        } => {
813            let inner_ir = lower_expr(ctx, inner);
814            let from_evidence = get_operation_evidence(&inner_ir);
815            let to_evidence = lower_evidentiality(*evidentiality);
816
817            IrOperation::EvidenceCoerce {
818                operation: EvidenceOp::Mark,
819                expr: Box::new(inner_ir),
820                from_evidence,
821                to_evidence,
822                ty: IrType::Infer,
823            }
824        }
825
826        ast::Expr::Morpheme { kind, body } => {
827            let body_ir = lower_expr(ctx, body);
828            IrOperation::Morpheme {
829                morpheme: lower_morpheme_kind(*kind),
830                symbol: morpheme_symbol(*kind).to_string(),
831                input: Box::new(IrOperation::Var {
832                    name: "_".to_string(),
833                    id: "implicit_input".to_string(),
834                    ty: IrType::Infer,
835                    evidence: IrEvidence::Known,
836                }),
837                body: Some(Box::new(body_ir)),
838                ty: IrType::Infer,
839                evidence: IrEvidence::Known,
840            }
841        }
842
843        ast::Expr::Incorporation { segments } => {
844            let segs: Vec<IncorporationSegment> = segments
845                .iter()
846                .map(|s| {
847                    if s.args.is_some() {
848                        IncorporationSegment::Verb {
849                            name: s.name.name.clone(),
850                        }
851                    } else {
852                        IncorporationSegment::Noun {
853                            name: s.name.name.clone(),
854                        }
855                    }
856                })
857                .collect();
858
859            // Collect all args from all segments
860            let mut args: Vec<IrOperation> = Vec::new();
861            for seg in segments {
862                if let Some(ref seg_args) = seg.args {
863                    for a in seg_args {
864                        args.push(lower_expr(ctx, a));
865                    }
866                }
867            }
868
869            IrOperation::Incorporation {
870                segments: segs,
871                args,
872                ty: IrType::Infer,
873                evidence: IrEvidence::Known,
874            }
875        }
876
877        // Protocol operations - all yield Reported evidence
878        ast::Expr::HttpRequest {
879            method,
880            url,
881            headers,
882            body,
883            timeout,
884        } => IrOperation::HttpRequest {
885            method: lower_http_method(*method),
886            url: Box::new(lower_expr(ctx, url)),
887            headers: if headers.is_empty() {
888                None
889            } else {
890                Some(Box::new(IrOperation::Array {
891                    elements: headers
892                        .iter()
893                        .map(|(k, v)| IrOperation::Tuple {
894                            elements: vec![lower_expr(ctx, k), lower_expr(ctx, v)],
895                            ty: IrType::Infer,
896                            evidence: IrEvidence::Known,
897                        })
898                        .collect(),
899                    ty: IrType::Infer,
900                    evidence: IrEvidence::Known,
901                }))
902            },
903            body: body.as_ref().map(|b| Box::new(lower_expr(ctx, b))),
904            timeout: timeout.as_ref().map(|t| Box::new(lower_expr(ctx, t))),
905            ty: IrType::Infer,
906            evidence: IrEvidence::Reported,
907        },
908
909        ast::Expr::GrpcCall {
910            service,
911            method,
912            message,
913            metadata,
914            timeout,
915        } => IrOperation::GrpcCall {
916            service: expr_to_string(service),
917            method: expr_to_string(method),
918            message: Box::new(
919                message
920                    .as_ref()
921                    .map(|m| lower_expr(ctx, m))
922                    .unwrap_or(IrOperation::Literal {
923                        variant: LiteralVariant::Null,
924                        value: serde_json::Value::Null,
925                        ty: IrType::Unit,
926                        evidence: IrEvidence::Known,
927                    }),
928            ),
929            metadata: if metadata.is_empty() {
930                None
931            } else {
932                Some(Box::new(IrOperation::Array {
933                    elements: metadata
934                        .iter()
935                        .map(|(k, v)| IrOperation::Tuple {
936                            elements: vec![lower_expr(ctx, k), lower_expr(ctx, v)],
937                            ty: IrType::Infer,
938                            evidence: IrEvidence::Known,
939                        })
940                        .collect(),
941                    ty: IrType::Infer,
942                    evidence: IrEvidence::Known,
943                }))
944            },
945            timeout: timeout.as_ref().map(|t| Box::new(lower_expr(ctx, t))),
946            ty: IrType::Infer,
947            evidence: IrEvidence::Reported,
948        },
949
950        // Handle remaining expression types with placeholders
951        _ => IrOperation::Literal {
952            variant: LiteralVariant::Null,
953            value: serde_json::json!({"unhandled": format!("{:?}", std::mem::discriminant(expr))}),
954            ty: IrType::Infer,
955            evidence: IrEvidence::Known,
956        },
957    }
958}
959
960fn lower_pipeline(
961    ctx: &mut LoweringContext,
962    input: &ast::Expr,
963    operations: &[ast::PipeOp],
964) -> IrOperation {
965    let input_ir = lower_expr(ctx, input);
966    let mut evidence = get_operation_evidence(&input_ir);
967
968    let steps: Vec<IrPipelineStep> = operations
969        .iter()
970        .map(|op| {
971            let step = lower_pipe_op(ctx, op);
972            // Update evidence based on operation
973            evidence = evidence.join(pipe_op_evidence(op));
974            step
975        })
976        .collect();
977
978    IrOperation::Pipeline {
979        input: Box::new(input_ir),
980        steps,
981        ty: IrType::Infer,
982        evidence,
983    }
984}
985
986fn lower_pipe_op(ctx: &mut LoweringContext, op: &ast::PipeOp) -> IrPipelineStep {
987    match op {
988        ast::PipeOp::Transform(body) => IrPipelineStep::Morpheme {
989            morpheme: MorphemeKind::Transform,
990            symbol: "τ".to_string(),
991            body: Some(Box::new(lower_expr(ctx, body))),
992        },
993        ast::PipeOp::Filter(body) => IrPipelineStep::Morpheme {
994            morpheme: MorphemeKind::Filter,
995            symbol: "φ".to_string(),
996            body: Some(Box::new(lower_expr(ctx, body))),
997        },
998        ast::PipeOp::Sort(field) => IrPipelineStep::Morpheme {
999            morpheme: MorphemeKind::Sort,
1000            symbol: "σ".to_string(),
1001            body: field.as_ref().map(|f| {
1002                Box::new(IrOperation::Var {
1003                    name: f.name.clone(),
1004                    id: f.name.clone(),
1005                    ty: IrType::Infer,
1006                    evidence: IrEvidence::Known,
1007                })
1008            }),
1009        },
1010        ast::PipeOp::Reduce(body) => IrPipelineStep::Morpheme {
1011            morpheme: MorphemeKind::Reduce,
1012            symbol: "ρ".to_string(),
1013            body: Some(Box::new(lower_expr(ctx, body))),
1014        },
1015        ast::PipeOp::First => IrPipelineStep::Morpheme {
1016            morpheme: MorphemeKind::First,
1017            symbol: "α".to_string(),
1018            body: None,
1019        },
1020        ast::PipeOp::Last => IrPipelineStep::Morpheme {
1021            morpheme: MorphemeKind::Last,
1022            symbol: "ω".to_string(),
1023            body: None,
1024        },
1025        ast::PipeOp::Middle => IrPipelineStep::Morpheme {
1026            morpheme: MorphemeKind::Middle,
1027            symbol: "μ".to_string(),
1028            body: None,
1029        },
1030        ast::PipeOp::Choice => IrPipelineStep::Morpheme {
1031            morpheme: MorphemeKind::Choice,
1032            symbol: "χ".to_string(),
1033            body: None,
1034        },
1035        ast::PipeOp::Nth(n) => IrPipelineStep::Morpheme {
1036            morpheme: MorphemeKind::Nth,
1037            symbol: "ν".to_string(),
1038            body: Some(Box::new(lower_expr(ctx, n))),
1039        },
1040        ast::PipeOp::Next => IrPipelineStep::Morpheme {
1041            morpheme: MorphemeKind::Next,
1042            symbol: "ξ".to_string(),
1043            body: None,
1044        },
1045        ast::PipeOp::Method { name, args } => IrPipelineStep::Method {
1046            name: name.name.clone(),
1047            args: args.iter().map(|a| lower_expr(ctx, a)).collect(),
1048        },
1049        ast::PipeOp::Await => IrPipelineStep::Await,
1050        ast::PipeOp::Named { prefix, body } => {
1051            let fn_name = prefix
1052                .iter()
1053                .map(|i| i.name.clone())
1054                .collect::<Vec<_>>()
1055                .join("·");
1056            IrPipelineStep::Call {
1057                function: fn_name,
1058                args: body
1059                    .as_ref()
1060                    .map(|b| vec![lower_expr(ctx, b)])
1061                    .unwrap_or_default(),
1062            }
1063        }
1064        // Protocol operations in pipeline
1065        ast::PipeOp::Send(data) => IrPipelineStep::Protocol {
1066            operation: ProtocolOp::Send,
1067            config: Some(Box::new(lower_expr(ctx, data))),
1068        },
1069        ast::PipeOp::Recv => IrPipelineStep::Protocol {
1070            operation: ProtocolOp::Recv,
1071            config: None,
1072        },
1073        ast::PipeOp::Stream(handler) => IrPipelineStep::Protocol {
1074            operation: ProtocolOp::Stream,
1075            config: Some(Box::new(lower_expr(ctx, handler))),
1076        },
1077        ast::PipeOp::Connect(config) => IrPipelineStep::Protocol {
1078            operation: ProtocolOp::Connect,
1079            config: config.as_ref().map(|c| Box::new(lower_expr(ctx, c))),
1080        },
1081        ast::PipeOp::Close => IrPipelineStep::Protocol {
1082            operation: ProtocolOp::Close,
1083            config: None,
1084        },
1085        ast::PipeOp::Timeout(ms) => IrPipelineStep::Protocol {
1086            operation: ProtocolOp::Timeout,
1087            config: Some(Box::new(lower_expr(ctx, ms))),
1088        },
1089        ast::PipeOp::Retry { count, strategy } => IrPipelineStep::Protocol {
1090            operation: ProtocolOp::Retry,
1091            config: Some(Box::new(IrOperation::Tuple {
1092                elements: vec![
1093                    lower_expr(ctx, count),
1094                    strategy
1095                        .as_ref()
1096                        .map(|s| lower_expr(ctx, s))
1097                        .unwrap_or(IrOperation::Literal {
1098                            variant: LiteralVariant::String,
1099                            value: serde_json::json!("exponential"),
1100                            ty: IrType::Primitive {
1101                                name: "str".to_string(),
1102                            },
1103                            evidence: IrEvidence::Known,
1104                        }),
1105                ],
1106                ty: IrType::Infer,
1107                evidence: IrEvidence::Known,
1108            })),
1109        },
1110        _ => IrPipelineStep::Identity,
1111    }
1112}
1113
1114fn pipe_op_evidence(op: &ast::PipeOp) -> IrEvidence {
1115    match op {
1116        // Protocol operations always produce Reported evidence
1117        ast::PipeOp::Send(_)
1118        | ast::PipeOp::Recv
1119        | ast::PipeOp::Stream(_)
1120        | ast::PipeOp::Connect(_)
1121        | ast::PipeOp::Close => IrEvidence::Reported,
1122        _ => IrEvidence::Known,
1123    }
1124}
1125
1126fn lower_literal(lit: &ast::Literal) -> IrOperation {
1127    match lit {
1128        ast::Literal::Int { value, base, suffix } => IrOperation::Literal {
1129            variant: LiteralVariant::Int,
1130            value: serde_json::json!({
1131                "value": value,
1132                "base": format!("{:?}", base),
1133                "suffix": suffix
1134            }),
1135            ty: suffix
1136                .as_ref()
1137                .map(|s| IrType::Primitive { name: s.clone() })
1138                .unwrap_or(IrType::Primitive {
1139                    name: "i64".to_string(),
1140                }),
1141            evidence: IrEvidence::Known,
1142        },
1143        ast::Literal::Float { value, suffix } => IrOperation::Literal {
1144            variant: LiteralVariant::Float,
1145            value: serde_json::json!({"value": value, "suffix": suffix}),
1146            ty: suffix
1147                .as_ref()
1148                .map(|s| IrType::Primitive { name: s.clone() })
1149                .unwrap_or(IrType::Primitive {
1150                    name: "f64".to_string(),
1151                }),
1152            evidence: IrEvidence::Known,
1153        },
1154        ast::Literal::String(s)
1155        | ast::Literal::MultiLineString(s)
1156        | ast::Literal::RawString(s) => IrOperation::Literal {
1157            variant: LiteralVariant::String,
1158            value: serde_json::Value::String(s.clone()),
1159            ty: IrType::Primitive {
1160                name: "str".to_string(),
1161            },
1162            evidence: IrEvidence::Known,
1163        },
1164        ast::Literal::Char(c) => IrOperation::Literal {
1165            variant: LiteralVariant::Char,
1166            value: serde_json::Value::String(c.to_string()),
1167            ty: IrType::Primitive {
1168                name: "char".to_string(),
1169            },
1170            evidence: IrEvidence::Known,
1171        },
1172        ast::Literal::Bool(b) => IrOperation::Literal {
1173            variant: LiteralVariant::Bool,
1174            value: serde_json::Value::Bool(*b),
1175            ty: IrType::Primitive {
1176                name: "bool".to_string(),
1177            },
1178            evidence: IrEvidence::Known,
1179        },
1180        ast::Literal::Null | ast::Literal::Empty => IrOperation::Literal {
1181            variant: LiteralVariant::Null,
1182            value: serde_json::Value::Null,
1183            ty: IrType::Unit,
1184            evidence: IrEvidence::Known,
1185        },
1186        _ => IrOperation::Literal {
1187            variant: LiteralVariant::Null,
1188            value: serde_json::Value::Null,
1189            ty: IrType::Infer,
1190            evidence: IrEvidence::Known,
1191        },
1192    }
1193}
1194
1195fn lower_literal_value(lit: &ast::Literal) -> serde_json::Value {
1196    match lit {
1197        ast::Literal::Int { value, .. } => serde_json::json!(value),
1198        ast::Literal::Float { value, .. } => serde_json::json!(value),
1199        ast::Literal::String(s) => serde_json::Value::String(s.clone()),
1200        ast::Literal::Bool(b) => serde_json::Value::Bool(*b),
1201        ast::Literal::Char(c) => serde_json::Value::String(c.to_string()),
1202        ast::Literal::Null => serde_json::Value::Null,
1203        _ => serde_json::Value::Null,
1204    }
1205}
1206
1207fn lower_binop(op: ast::BinOp) -> BinaryOp {
1208    match op {
1209        ast::BinOp::Add => BinaryOp::Add,
1210        ast::BinOp::Sub => BinaryOp::Sub,
1211        ast::BinOp::Mul => BinaryOp::Mul,
1212        ast::BinOp::Div => BinaryOp::Div,
1213        ast::BinOp::Rem => BinaryOp::Rem,
1214        ast::BinOp::Pow => BinaryOp::Pow,
1215        ast::BinOp::And => BinaryOp::And,
1216        ast::BinOp::Or => BinaryOp::Or,
1217        ast::BinOp::BitAnd => BinaryOp::BitAnd,
1218        ast::BinOp::BitOr => BinaryOp::BitOr,
1219        ast::BinOp::BitXor => BinaryOp::BitXor,
1220        ast::BinOp::Shl => BinaryOp::Shl,
1221        ast::BinOp::Shr => BinaryOp::Shr,
1222        ast::BinOp::Eq => BinaryOp::Eq,
1223        ast::BinOp::Ne => BinaryOp::Ne,
1224        ast::BinOp::Lt => BinaryOp::Lt,
1225        ast::BinOp::Le => BinaryOp::Le,
1226        ast::BinOp::Gt => BinaryOp::Gt,
1227        ast::BinOp::Ge => BinaryOp::Ge,
1228        ast::BinOp::Concat => BinaryOp::Concat,
1229    }
1230}
1231
1232fn lower_unaryop(op: ast::UnaryOp) -> UnaryOp {
1233    match op {
1234        ast::UnaryOp::Neg => UnaryOp::Neg,
1235        ast::UnaryOp::Not => UnaryOp::Not,
1236        ast::UnaryOp::Deref => UnaryOp::Deref,
1237        ast::UnaryOp::Ref => UnaryOp::Ref,
1238        ast::UnaryOp::RefMut => UnaryOp::RefMut,
1239    }
1240}
1241
1242fn lower_morpheme_kind(kind: ast::MorphemeKind) -> MorphemeKind {
1243    match kind {
1244        ast::MorphemeKind::Transform => MorphemeKind::Transform,
1245        ast::MorphemeKind::Filter => MorphemeKind::Filter,
1246        ast::MorphemeKind::Sort => MorphemeKind::Sort,
1247        ast::MorphemeKind::Reduce => MorphemeKind::Reduce,
1248        ast::MorphemeKind::Lambda => MorphemeKind::Lambda,
1249        ast::MorphemeKind::Sum => MorphemeKind::Sum,
1250        ast::MorphemeKind::Product => MorphemeKind::Product,
1251        ast::MorphemeKind::First => MorphemeKind::First,
1252        ast::MorphemeKind::Last => MorphemeKind::Last,
1253        ast::MorphemeKind::Middle => MorphemeKind::Middle,
1254        ast::MorphemeKind::Choice => MorphemeKind::Choice,
1255        ast::MorphemeKind::Nth => MorphemeKind::Nth,
1256        ast::MorphemeKind::Next => MorphemeKind::Next,
1257    }
1258}
1259
1260fn morpheme_symbol(kind: ast::MorphemeKind) -> &'static str {
1261    match kind {
1262        ast::MorphemeKind::Transform => "τ",
1263        ast::MorphemeKind::Filter => "φ",
1264        ast::MorphemeKind::Sort => "σ",
1265        ast::MorphemeKind::Reduce => "ρ",
1266        ast::MorphemeKind::Lambda => "λ",
1267        ast::MorphemeKind::Sum => "Σ",
1268        ast::MorphemeKind::Product => "Π",
1269        ast::MorphemeKind::First => "α",
1270        ast::MorphemeKind::Last => "ω",
1271        ast::MorphemeKind::Middle => "μ",
1272        ast::MorphemeKind::Choice => "χ",
1273        ast::MorphemeKind::Nth => "ν",
1274        ast::MorphemeKind::Next => "ξ",
1275    }
1276}
1277
1278fn lower_http_method(method: ast::HttpMethod) -> HttpMethod {
1279    match method {
1280        ast::HttpMethod::Get => HttpMethod::Get,
1281        ast::HttpMethod::Post => HttpMethod::Post,
1282        ast::HttpMethod::Put => HttpMethod::Put,
1283        ast::HttpMethod::Delete => HttpMethod::Delete,
1284        ast::HttpMethod::Patch => HttpMethod::Patch,
1285        ast::HttpMethod::Head => HttpMethod::Head,
1286        ast::HttpMethod::Options => HttpMethod::Options,
1287        ast::HttpMethod::Connect => HttpMethod::Connect,
1288        ast::HttpMethod::Trace => HttpMethod::Trace,
1289    }
1290}
1291
1292fn get_operation_evidence(op: &IrOperation) -> IrEvidence {
1293    match op {
1294        IrOperation::Literal { evidence, .. }
1295        | IrOperation::Var { evidence, .. }
1296        | IrOperation::Let { evidence, .. }
1297        | IrOperation::Binary { evidence, .. }
1298        | IrOperation::Unary { evidence, .. }
1299        | IrOperation::Call { evidence, .. }
1300        | IrOperation::MethodCall { evidence, .. }
1301        | IrOperation::Closure { evidence, .. }
1302        | IrOperation::If { evidence, .. }
1303        | IrOperation::Match { evidence, .. }
1304        | IrOperation::Loop { evidence, .. }
1305        | IrOperation::Block { evidence, .. }
1306        | IrOperation::Pipeline { evidence, .. }
1307        | IrOperation::Morpheme { evidence, .. }
1308        | IrOperation::Fork { evidence, .. }
1309        | IrOperation::Identity { evidence, .. }
1310        | IrOperation::Array { evidence, .. }
1311        | IrOperation::Tuple { evidence, .. }
1312        | IrOperation::StructInit { evidence, .. }
1313        | IrOperation::Field { evidence, .. }
1314        | IrOperation::Index { evidence, .. }
1315        | IrOperation::Assign { evidence, .. }
1316        | IrOperation::Break { evidence, .. }
1317        | IrOperation::Continue { evidence }
1318        | IrOperation::Return { evidence, .. }
1319        | IrOperation::Incorporation { evidence, .. }
1320        | IrOperation::Affect { evidence, .. }
1321        | IrOperation::HttpRequest { evidence, .. }
1322        | IrOperation::GrpcCall { evidence, .. }
1323        | IrOperation::WebSocket { evidence, .. }
1324        | IrOperation::KafkaOp { evidence, .. }
1325        | IrOperation::Await { evidence, .. }
1326        | IrOperation::Unsafe { evidence, .. }
1327        | IrOperation::Cast { evidence, .. }
1328        | IrOperation::Try { evidence, .. } => *evidence,
1329        IrOperation::EvidenceCoerce { to_evidence, .. } => *to_evidence,
1330    }
1331}
1332
1333fn expr_to_string(e: &ast::Expr) -> String {
1334    match e {
1335        ast::Expr::Path(p) => p
1336            .segments
1337            .iter()
1338            .map(|s| s.ident.name.clone())
1339            .collect::<Vec<_>>()
1340            .join("::"),
1341        ast::Expr::Literal(ast::Literal::String(s)) => s.clone(),
1342        _ => "dynamic".to_string(),
1343    }
1344}
1345
1346// === Type definitions lowering ===
1347
1348fn lower_struct_def(_ctx: &mut LoweringContext, s: &ast::StructDef) -> IrTypeDef {
1349    let fields = match &s.fields {
1350        ast::StructFields::Named(fields) => fields
1351            .iter()
1352            .map(|f| IrField {
1353                name: f.name.name.clone(),
1354                ty: lower_type_expr(&f.ty),
1355                visibility: lower_visibility(f.visibility),
1356            })
1357            .collect(),
1358        ast::StructFields::Tuple(types) => types
1359            .iter()
1360            .enumerate()
1361            .map(|(i, t)| IrField {
1362                name: format!("{}", i),
1363                ty: lower_type_expr(t),
1364                visibility: IrVisibility::Public,
1365            })
1366            .collect(),
1367        ast::StructFields::Unit => vec![],
1368    };
1369
1370    IrTypeDef::Struct {
1371        name: s.name.name.clone(),
1372        generics: lower_generics(&s.generics),
1373        fields,
1374        span: None,
1375    }
1376}
1377
1378fn lower_enum_def(_ctx: &mut LoweringContext, e: &ast::EnumDef) -> IrTypeDef {
1379    let variants = e
1380        .variants
1381        .iter()
1382        .map(|v| {
1383            let fields = match &v.fields {
1384                ast::StructFields::Named(fields) => Some(
1385                    fields
1386                        .iter()
1387                        .map(|f| IrField {
1388                            name: f.name.name.clone(),
1389                            ty: lower_type_expr(&f.ty),
1390                            visibility: lower_visibility(f.visibility),
1391                        })
1392                        .collect(),
1393                ),
1394                ast::StructFields::Tuple(types) => Some(
1395                    types
1396                        .iter()
1397                        .enumerate()
1398                        .map(|(i, t)| IrField {
1399                            name: format!("{}", i),
1400                            ty: lower_type_expr(t),
1401                            visibility: IrVisibility::Public,
1402                        })
1403                        .collect(),
1404                ),
1405                ast::StructFields::Unit => None,
1406            };
1407
1408            IrVariant {
1409                name: v.name.name.clone(),
1410                fields,
1411                discriminant: None,
1412            }
1413        })
1414        .collect();
1415
1416    IrTypeDef::Enum {
1417        name: e.name.name.clone(),
1418        generics: lower_generics(&e.generics),
1419        variants,
1420        span: None,
1421    }
1422}
1423
1424fn lower_type_alias(_ctx: &mut LoweringContext, t: &ast::TypeAlias) -> IrTypeDef {
1425    IrTypeDef::TypeAlias {
1426        name: t.name.name.clone(),
1427        generics: lower_generics(&t.generics),
1428        target: lower_type_expr(&t.ty),
1429        span: None,
1430    }
1431}
1432
1433fn lower_trait_def(ctx: &mut LoweringContext, t: &ast::TraitDef) -> IrTraitDef {
1434    let methods = t
1435        .items
1436        .iter()
1437        .filter_map(|item| match item {
1438            ast::TraitItem::Function(f) => lower_function(ctx, f),
1439            _ => None,
1440        })
1441        .collect();
1442
1443    IrTraitDef {
1444        name: t.name.name.clone(),
1445        generics: lower_generics(&t.generics),
1446        super_traits: t.supertraits.iter().map(type_expr_to_string).collect(),
1447        methods,
1448        span: None,
1449    }
1450}
1451
1452fn lower_impl_block(ctx: &mut LoweringContext, i: &ast::ImplBlock) -> IrImplBlock {
1453    let methods = i
1454        .items
1455        .iter()
1456        .filter_map(|item| match item {
1457            ast::ImplItem::Function(f) => lower_function(ctx, f),
1458            _ => None,
1459        })
1460        .collect();
1461
1462    IrImplBlock {
1463        trait_name: i.trait_.as_ref().map(|t| {
1464            t.segments
1465                .iter()
1466                .map(|s| s.ident.name.clone())
1467                .collect::<Vec<_>>()
1468                .join("::")
1469        }),
1470        target_type: lower_type_expr(&i.self_ty),
1471        generics: lower_generics(&i.generics),
1472        methods,
1473        span: None,
1474    }
1475}
1476
1477fn lower_const_def(ctx: &mut LoweringContext, c: &ast::ConstDef) -> IrConstant {
1478    IrConstant {
1479        name: c.name.name.clone(),
1480        ty: lower_type_expr(&c.ty),
1481        value: lower_expr(ctx, &c.value),
1482        visibility: lower_visibility(c.visibility),
1483        span: None,
1484    }
1485}