1use crate::ast::{self, SourceFile};
11use crate::ir::*;
12use std::collections::HashMap;
13
14struct 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
33pub struct LoweringContext {
35 ids: IdGenerator,
36 scope: Vec<HashMap<String, String>>,
38 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
83pub 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 }
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 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, },
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, }
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, }
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, },
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 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 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 _ => 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 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 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 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
1346fn 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}