1use crate::ast::{Literal, Spanned};
23use crate::ir::hir::{
24 BuiltinCtor, ResolvedCallee, ResolvedCtor, ResolvedExpr, ResolvedFnBody, ResolvedFnDef,
25 ResolvedMatchArm, ResolvedPattern, ResolvedStmt,
26};
27use crate::ir::{
28 BoolCompareOp, BoolMatchShape, DispatchArmPlan, DispatchBindingPlan, DispatchDefaultPlan,
29 DispatchLiteral, DispatchTableShape, ListMatchShape, MatchDispatchPlan,
30 SemanticDispatchPattern, WrapperKind,
31};
32
33#[allow(dead_code)]
43pub enum ResolvedLeafOp<'a> {
44 FieldAccess {
45 object: &'a crate::ast::Spanned<ResolvedExpr>,
46 field_name: &'a str,
47 },
48 MapGet {
49 map: &'a crate::ast::Spanned<ResolvedExpr>,
50 key: &'a crate::ast::Spanned<ResolvedExpr>,
51 },
52 MapSet {
53 map: &'a crate::ast::Spanned<ResolvedExpr>,
54 key: &'a crate::ast::Spanned<ResolvedExpr>,
55 value: &'a crate::ast::Spanned<ResolvedExpr>,
56 },
57 VectorNew {
58 size: &'a crate::ast::Spanned<ResolvedExpr>,
59 fill: &'a crate::ast::Spanned<ResolvedExpr>,
60 },
61 VectorSetOrDefaultSameVector {
62 vector: &'a crate::ast::Spanned<ResolvedExpr>,
63 index: &'a crate::ast::Spanned<ResolvedExpr>,
64 value: &'a crate::ast::Spanned<ResolvedExpr>,
65 },
66 VectorGetOrDefaultLiteral {
67 vector: &'a crate::ast::Spanned<ResolvedExpr>,
68 index: &'a crate::ast::Spanned<ResolvedExpr>,
69 default_literal: &'a Literal,
70 },
71 IntModOrDefaultLiteral {
72 a: &'a crate::ast::Spanned<ResolvedExpr>,
73 b: &'a crate::ast::Spanned<ResolvedExpr>,
74 default_literal: &'a Literal,
75 },
76 ListIndexGet {
77 list: &'a crate::ast::Spanned<ResolvedExpr>,
78 index: &'a crate::ast::Spanned<ResolvedExpr>,
79 },
80 NoneValue,
81 VariantConstructor {
82 qualified_type_name: String,
83 variant_name: String,
84 },
85 StaticRef(String),
86}
87
88#[allow(dead_code)]
91pub enum ResolvedBoolSubjectPlan<'a> {
92 Expr(&'a ResolvedExpr),
93 Compare {
94 lhs: &'a crate::ast::Spanned<ResolvedExpr>,
95 rhs: &'a crate::ast::Spanned<ResolvedExpr>,
96 op: BoolCompareOp,
97 invert: bool,
98 },
99}
100
101#[allow(dead_code)]
107pub struct ResolvedForwardCallPlan<'a> {
108 pub callee: &'a ResolvedCallee,
109 pub forward_slots: Vec<ForwardSlot>,
110 pub args: &'a [crate::ast::Spanned<ResolvedExpr>],
111}
112
113pub enum ForwardSlot {
114 Local { slot: u16 },
115}
116
117pub fn classify_leaf_op_resolved<'a>(
122 expr: &'a ResolvedExpr,
123 is_user_type: &impl Fn(&str) -> bool,
124) -> Option<ResolvedLeafOp<'a>> {
125 match expr {
126 ResolvedExpr::Attr(object, field_name) => {
127 classify_field_access(expr, object, field_name, is_user_type)
128 }
129 ResolvedExpr::Call(callee, args) => classify_leaf_call(callee, args, is_user_type),
130 ResolvedExpr::Ctor(ctor, args) if args.is_empty() => match ctor {
131 ResolvedCtor::Builtin(BuiltinCtor::OptionNone) => Some(ResolvedLeafOp::NoneValue),
132 ResolvedCtor::User {
133 type_id: _,
134 name,
135 ctor_id: _,
136 } => {
137 let _ = name;
149 None
150 }
151 _ => None,
152 },
153 _ => None,
154 }
155}
156
157fn classify_field_access<'a>(
158 full_expr: &'a ResolvedExpr,
159 object: &'a crate::ast::Spanned<ResolvedExpr>,
160 field_name: &'a str,
161 _is_user_type: &impl Fn(&str) -> bool,
162) -> Option<ResolvedLeafOp<'a>> {
163 let dotted = resolved_to_dotted(full_expr);
167 let starts_upper = dotted
168 .as_deref()
169 .and_then(|d| d.chars().next())
170 .is_some_and(|c| c.is_uppercase());
171
172 if !starts_upper {
173 return Some(ResolvedLeafOp::FieldAccess { object, field_name });
174 }
175 dotted.map(ResolvedLeafOp::StaticRef)
183}
184
185fn classify_leaf_call<'a>(
186 callee: &'a ResolvedCallee,
187 args: &'a [crate::ast::Spanned<ResolvedExpr>],
188 is_user_type: &impl Fn(&str) -> bool,
189) -> Option<ResolvedLeafOp<'a>> {
190 let builtin_name = match callee {
191 ResolvedCallee::Builtin(name) => name.as_str(),
192 _ => return None,
193 };
194 match (builtin_name, args.len()) {
195 ("Map.get", 2) => Some(ResolvedLeafOp::MapGet {
196 map: &args[0],
197 key: &args[1],
198 }),
199 ("Map.set", 3) => Some(ResolvedLeafOp::MapSet {
200 map: &args[0],
201 key: &args[1],
202 value: &args[2],
203 }),
204 ("Vector.new", 2) => Some(ResolvedLeafOp::VectorNew {
205 size: &args[0],
206 fill: &args[1],
207 }),
208 ("Vector.get", 2) => classify_list_index_get(&args[0], &args[1]),
209 ("Option.withDefault", 2) => classify_vector_set_or_default(&args[0], &args[1])
210 .or_else(|| classify_vector_get_or_default(&args[0], &args[1])),
211 ("Result.withDefault", 2) => classify_int_mod_or_default(&args[0], &args[1], is_user_type),
212 _ => None,
213 }
214}
215
216fn classify_vector_set_or_default<'a>(
217 option_expr: &'a crate::ast::Spanned<ResolvedExpr>,
218 default_expr: &'a crate::ast::Spanned<ResolvedExpr>,
219) -> Option<ResolvedLeafOp<'a>> {
220 let ResolvedExpr::Call(inner_callee, inner_args) = &option_expr.node else {
221 return None;
222 };
223 if inner_args.len() != 3 {
224 return None;
225 }
226 let is_vector_set =
227 matches!(inner_callee, ResolvedCallee::Builtin(name) if name == "Vector.set");
228 if !is_vector_set {
229 return None;
230 }
231 if default_expr.node != inner_args[0].node {
232 return None;
233 }
234 Some(ResolvedLeafOp::VectorSetOrDefaultSameVector {
235 vector: &inner_args[0],
236 index: &inner_args[1],
237 value: &inner_args[2],
238 })
239}
240
241fn classify_vector_get_or_default<'a>(
242 option_expr: &'a crate::ast::Spanned<ResolvedExpr>,
243 default_expr: &'a crate::ast::Spanned<ResolvedExpr>,
244) -> Option<ResolvedLeafOp<'a>> {
245 let default_literal = match &default_expr.node {
246 ResolvedExpr::Literal(lit) => lit,
247 _ => return None,
248 };
249 let ResolvedExpr::Call(inner_callee, inner_args) = &option_expr.node else {
250 return None;
251 };
252 if inner_args.len() != 2 {
253 return None;
254 }
255 let is_vector_get =
256 matches!(inner_callee, ResolvedCallee::Builtin(name) if name == "Vector.get");
257 if !is_vector_get {
258 return None;
259 }
260 Some(ResolvedLeafOp::VectorGetOrDefaultLiteral {
261 vector: &inner_args[0],
262 index: &inner_args[1],
263 default_literal,
264 })
265}
266
267fn classify_list_index_get<'a>(
268 vector_expr: &'a crate::ast::Spanned<ResolvedExpr>,
269 index: &'a crate::ast::Spanned<ResolvedExpr>,
270) -> Option<ResolvedLeafOp<'a>> {
271 let ResolvedExpr::Call(inner_callee, inner_args) = &vector_expr.node else {
272 return None;
273 };
274 if inner_args.len() != 1 {
275 return None;
276 }
277 let is_from_list =
278 matches!(inner_callee, ResolvedCallee::Builtin(name) if name == "Vector.fromList");
279 if !is_from_list {
280 return None;
281 }
282 Some(ResolvedLeafOp::ListIndexGet {
283 list: &inner_args[0],
284 index,
285 })
286}
287
288fn classify_int_mod_or_default<'a>(
289 result_expr: &'a crate::ast::Spanned<ResolvedExpr>,
290 default_expr: &'a crate::ast::Spanned<ResolvedExpr>,
291 _is_user_type: &impl Fn(&str) -> bool,
292) -> Option<ResolvedLeafOp<'a>> {
293 let default_literal = match &default_expr.node {
294 ResolvedExpr::Literal(lit) => lit,
295 _ => return None,
296 };
297 let ResolvedExpr::Call(inner_callee, inner_args) = &result_expr.node else {
298 return None;
299 };
300 if inner_args.len() != 2 {
301 return None;
302 }
303 let is_int_mod = matches!(inner_callee, ResolvedCallee::Builtin(name) if name == "Int.mod");
304 if !is_int_mod {
305 return None;
306 }
307 Some(ResolvedLeafOp::IntModOrDefaultLiteral {
308 a: &inner_args[0],
309 b: &inner_args[1],
310 default_literal,
311 })
312}
313
314pub fn classify_bool_subject_plan_resolved(subject: &ResolvedExpr) -> ResolvedBoolSubjectPlan<'_> {
317 let ResolvedExpr::BinOp(op, lhs, rhs) = subject else {
318 return ResolvedBoolSubjectPlan::Expr(subject);
319 };
320 use crate::ast::BinOp;
321 match op {
322 BinOp::Eq => ResolvedBoolSubjectPlan::Compare {
323 lhs,
324 rhs,
325 op: BoolCompareOp::Eq,
326 invert: false,
327 },
328 BinOp::Lt => ResolvedBoolSubjectPlan::Compare {
329 lhs,
330 rhs,
331 op: BoolCompareOp::Lt,
332 invert: false,
333 },
334 BinOp::Gt => ResolvedBoolSubjectPlan::Compare {
335 lhs,
336 rhs,
337 op: BoolCompareOp::Gt,
338 invert: false,
339 },
340 BinOp::Neq => ResolvedBoolSubjectPlan::Compare {
341 lhs,
342 rhs,
343 op: BoolCompareOp::Eq,
344 invert: true,
345 },
346 BinOp::Gte => ResolvedBoolSubjectPlan::Compare {
347 lhs,
348 rhs,
349 op: BoolCompareOp::Lt,
350 invert: true,
351 },
352 BinOp::Lte => ResolvedBoolSubjectPlan::Compare {
353 lhs,
354 rhs,
355 op: BoolCompareOp::Gt,
356 invert: true,
357 },
358 BinOp::Add | BinOp::Sub | BinOp::Mul | BinOp::Div => ResolvedBoolSubjectPlan::Expr(subject),
359 }
360}
361
362pub fn classify_dispatch_pattern_resolved(
365 pattern: &ResolvedPattern,
366) -> Option<SemanticDispatchPattern> {
367 match pattern {
368 ResolvedPattern::Literal(lit) => Some(SemanticDispatchPattern::Literal(
369 dispatch_literal_from_ast(lit),
370 )),
371 ResolvedPattern::EmptyList => Some(SemanticDispatchPattern::EmptyList),
372 ResolvedPattern::Ctor(ctor, bindings) => match ctor {
373 ResolvedCtor::Builtin(BuiltinCtor::OptionNone) if bindings.is_empty() => {
374 Some(SemanticDispatchPattern::NoneValue)
375 }
376 ResolvedCtor::Builtin(BuiltinCtor::ResultOk) if bindings.len() <= 1 => {
377 Some(SemanticDispatchPattern::WrapperTag(WrapperKind::ResultOk))
378 }
379 ResolvedCtor::Builtin(BuiltinCtor::ResultErr) if bindings.len() <= 1 => {
380 Some(SemanticDispatchPattern::WrapperTag(WrapperKind::ResultErr))
381 }
382 ResolvedCtor::Builtin(BuiltinCtor::OptionSome) if bindings.len() <= 1 => {
383 Some(SemanticDispatchPattern::WrapperTag(WrapperKind::OptionSome))
384 }
385 _ => None,
386 },
387 _ => None,
388 }
389}
390
391pub fn classify_list_match_shape_resolved(arms: &[ResolvedMatchArm]) -> Option<ListMatchShape> {
393 if arms.len() != 2 {
394 return None;
395 }
396 match (&arms[0].pattern, &arms[1].pattern) {
397 (ResolvedPattern::EmptyList, ResolvedPattern::Cons(_, _)) => Some(ListMatchShape {
398 empty_arm_index: 0,
399 cons_arm_index: 1,
400 }),
401 (ResolvedPattern::Cons(_, _), ResolvedPattern::EmptyList) => Some(ListMatchShape {
402 empty_arm_index: 1,
403 cons_arm_index: 0,
404 }),
405 _ => None,
406 }
407}
408
409pub fn classify_bool_match_shape_resolved(arms: &[ResolvedMatchArm]) -> Option<BoolMatchShape> {
411 if arms.len() != 2 {
412 return None;
413 }
414 use crate::ast::Literal as Lit;
415 match (&arms[0].pattern, &arms[1].pattern) {
416 (ResolvedPattern::Literal(Lit::Bool(true)), ResolvedPattern::Literal(Lit::Bool(false))) => {
417 Some(BoolMatchShape {
418 true_arm_index: 0,
419 false_arm_index: 1,
420 })
421 }
422 (ResolvedPattern::Literal(Lit::Bool(false)), ResolvedPattern::Literal(Lit::Bool(true))) => {
423 Some(BoolMatchShape {
424 true_arm_index: 1,
425 false_arm_index: 0,
426 })
427 }
428 (
429 ResolvedPattern::Literal(Lit::Bool(true)),
430 ResolvedPattern::Wildcard | ResolvedPattern::Ident(_),
431 ) => Some(BoolMatchShape {
432 true_arm_index: 0,
433 false_arm_index: 1,
434 }),
435 _ => None,
436 }
437}
438
439pub fn classify_dispatch_table_shape_resolved(
442 arms: &[ResolvedMatchArm],
443) -> Option<DispatchTableShape> {
444 if arms.len() < 2 {
445 return None;
446 }
447 let has_default = matches!(
448 arms.last().map(|a| &a.pattern),
449 Some(ResolvedPattern::Wildcard | ResolvedPattern::Ident(_))
450 );
451 let dispatchable_end = if has_default {
452 arms.len() - 1
453 } else {
454 arms.len()
455 };
456
457 let mut entries = Vec::new();
458 for (arm_index, arm) in arms[..dispatchable_end].iter().enumerate() {
459 let semantic = classify_dispatch_pattern_resolved(&arm.pattern)?;
460 entries.push(DispatchArmPlan {
461 binding: classify_dispatch_binding_resolved(&arm.pattern, &semantic),
462 pattern: semantic,
463 arm_index,
464 });
465 }
466
467 if entries.len() < 2 {
468 return None;
469 }
470
471 let default_arm = has_default.then(|| {
472 let arm_idx = arms.len() - 1;
473 let binding_name = match &arms[arm_idx].pattern {
474 ResolvedPattern::Ident(name) if name != "_" => Some(name.clone()),
475 _ => None,
476 };
477 DispatchDefaultPlan {
478 arm_index: arm_idx,
479 binding_name,
480 }
481 });
482
483 Some(DispatchTableShape {
484 entries,
485 default_arm,
486 })
487}
488
489pub fn classify_match_dispatch_plan_resolved(
492 arms: &[ResolvedMatchArm],
493) -> Option<MatchDispatchPlan> {
494 if let Some(shape) = classify_bool_match_shape_resolved(arms) {
495 return Some(MatchDispatchPlan::Bool(shape));
496 }
497 if let Some(shape) = classify_list_match_shape_resolved(arms) {
498 return Some(MatchDispatchPlan::List(shape));
499 }
500 classify_dispatch_table_shape_resolved(arms).map(MatchDispatchPlan::Table)
501}
502
503fn classify_dispatch_binding_resolved(
504 pattern: &ResolvedPattern,
505 semantic: &SemanticDispatchPattern,
506) -> DispatchBindingPlan {
507 match (pattern, semantic) {
508 (ResolvedPattern::Ctor(_, bindings), SemanticDispatchPattern::WrapperTag(_))
509 if !bindings.is_empty() && bindings[0] != "_" =>
510 {
511 DispatchBindingPlan::WrapperPayload(bindings[0].clone())
512 }
513 _ => DispatchBindingPlan::None,
514 }
515}
516
517fn dispatch_literal_from_ast(lit: &Literal) -> DispatchLiteral {
518 match lit {
519 Literal::Int(i) => DispatchLiteral::Int(*i),
520 Literal::Float(f) => DispatchLiteral::Float(f.to_string()),
521 Literal::Bool(b) => DispatchLiteral::Bool(*b),
522 Literal::Str(s) => DispatchLiteral::Str(s.clone()),
523 Literal::Unit => DispatchLiteral::Unit,
524 }
525}
526
527pub fn resolved_to_dotted(expr: &ResolvedExpr) -> Option<String> {
536 match expr {
537 ResolvedExpr::Ident(name) => Some(name.clone()),
538 ResolvedExpr::Resolved { name, .. } => Some(name.clone()),
539 ResolvedExpr::Attr(obj, field) => {
540 let head = resolved_to_dotted(&obj.node)?;
541 Some(format!("{head}.{field}"))
542 }
543 _ => None,
544 }
545}
546
547pub fn classify_forward_call_resolved<'a>(
556 callee: &'a ResolvedCallee,
557 args: &'a [crate::ast::Spanned<ResolvedExpr>],
558) -> Option<ResolvedForwardCallPlan<'a>> {
559 match callee {
560 ResolvedCallee::Unresolved { .. } => return None,
561 ResolvedCallee::LocalSlot { .. } => return None,
562 ResolvedCallee::Intrinsic(_) => return None,
563 _ => {}
564 }
565
566 let forward_slots = args
567 .iter()
568 .map(classify_forward_arg_resolved)
569 .collect::<Option<Vec<_>>>()?;
570
571 Some(ResolvedForwardCallPlan {
572 callee,
573 forward_slots,
574 args,
575 })
576}
577
578fn classify_forward_arg_resolved(expr: &crate::ast::Spanned<ResolvedExpr>) -> Option<ForwardSlot> {
579 match &expr.node {
580 ResolvedExpr::Resolved { slot, .. } => Some(ForwardSlot::Local { slot: *slot }),
581 _ => None,
582 }
583}
584
585pub use crate::ir::body::ThinKind;
594
595pub enum ResolvedBodyExprPlan<'a> {
597 Expr(&'a ResolvedExpr),
598 Leaf(ResolvedLeafOp<'a>),
599 Call {
600 callee: &'a ResolvedCallee,
601 args: &'a [Spanned<ResolvedExpr>],
602 },
603 ForwardCall(ResolvedForwardCallPlan<'a>),
604}
605
606pub struct ResolvedBodyBindingPlan<'a> {
608 pub name: &'a str,
609 pub expr: ResolvedBodyExprPlan<'a>,
610}
611
612pub enum ResolvedBodyPlan<'a> {
614 SingleExpr(ResolvedBodyExprPlan<'a>),
615 Block {
616 stmts: &'a [ResolvedStmt],
617 bindings: Vec<ResolvedBodyBindingPlan<'a>>,
618 tail: ResolvedBodyExprPlan<'a>,
619 },
620}
621
622pub struct ResolvedThinBodyPlan<'a> {
627 pub params: &'a [(String, crate::ast::Type)],
628 pub body: ResolvedBodyPlan<'a>,
629 pub kind: ThinKind,
630}
631
632pub fn classify_body_expr_plan_resolved<'a>(
633 expr: &'a ResolvedExpr,
634 is_user_type: &impl Fn(&str) -> bool,
635) -> ResolvedBodyExprPlan<'a> {
636 if let Some(leaf) = classify_leaf_op_resolved(expr, is_user_type) {
637 return ResolvedBodyExprPlan::Leaf(leaf);
638 }
639 if let ResolvedExpr::Call(callee, args) = expr {
640 if let Some(plan) = classify_forward_call_resolved(callee, args) {
641 return ResolvedBodyExprPlan::ForwardCall(plan);
642 }
643 if !matches!(callee, ResolvedCallee::LocalSlot { .. }) {
649 return ResolvedBodyExprPlan::Call { callee, args };
650 }
651 }
652 ResolvedBodyExprPlan::Expr(expr)
653}
654
655pub fn classify_body_plan_resolved<'a>(
656 body: &'a ResolvedFnBody,
657 is_user_type: &impl Fn(&str) -> bool,
658) -> Option<ResolvedBodyPlan<'a>> {
659 let stmts = body.stmts();
660 let (tail_stmt, prefix) = stmts.split_last()?;
661
662 let ResolvedStmt::Expr(tail_expr) = tail_stmt else {
663 return None;
664 };
665
666 if prefix.is_empty() {
667 return Some(ResolvedBodyPlan::SingleExpr(
668 classify_body_expr_plan_resolved(&tail_expr.node, is_user_type),
669 ));
670 }
671
672 let mut bindings = Vec::with_capacity(prefix.len());
673 for stmt in prefix {
674 let ResolvedStmt::Binding { name, value, .. } = stmt else {
675 return None;
676 };
677 bindings.push(ResolvedBodyBindingPlan {
678 name: name.as_str(),
679 expr: classify_body_expr_plan_resolved(&value.node, is_user_type),
680 });
681 }
682
683 Some(ResolvedBodyPlan::Block {
684 stmts,
685 bindings,
686 tail: classify_body_expr_plan_resolved(&tail_expr.node, is_user_type),
687 })
688}
689
690pub fn classify_thin_fn_def_resolved<'a>(
691 fd: &'a ResolvedFnDef,
692 is_user_type: &impl Fn(&str) -> bool,
693) -> Option<ResolvedThinBodyPlan<'a>> {
694 let body = classify_body_plan_resolved(&fd.body, is_user_type)?;
695 Some(ResolvedThinBodyPlan {
696 params: &fd.params,
697 kind: classify_thin_kind_resolved(&body, is_user_type)?,
698 body,
699 })
700}
701
702fn classify_thin_kind_resolved(
703 plan: &ResolvedBodyPlan<'_>,
704 is_user_type: &impl Fn(&str) -> bool,
705) -> Option<ThinKind> {
706 match plan {
707 ResolvedBodyPlan::SingleExpr(expr) => classify_thin_expr_kind_resolved(expr, is_user_type),
708 ResolvedBodyPlan::Block { bindings, tail, .. } => {
709 if bindings
710 .iter()
711 .all(|binding| body_expr_is_thin_binding_resolved(&binding.expr))
712 {
713 classify_thin_expr_kind_resolved(tail, is_user_type)
714 } else {
715 None
716 }
717 }
718 }
719}
720
721fn classify_thin_expr_kind_resolved(
722 plan: &ResolvedBodyExprPlan<'_>,
723 _is_user_type: &impl Fn(&str) -> bool,
724) -> Option<ThinKind> {
725 match plan {
726 ResolvedBodyExprPlan::Leaf(_) => Some(ThinKind::Leaf),
727 ResolvedBodyExprPlan::Call { .. } => Some(ThinKind::Direct),
728 ResolvedBodyExprPlan::ForwardCall(_) => Some(ThinKind::Forward),
729 ResolvedBodyExprPlan::Expr(expr) => match expr {
730 ResolvedExpr::Match { arms, .. }
731 if classify_match_dispatch_plan_resolved(arms).is_some() =>
732 {
733 Some(ThinKind::Dispatch)
734 }
735 ResolvedExpr::TailCall { .. } => Some(ThinKind::Tail),
736 _ => None,
737 },
738 }
739}
740
741fn body_expr_is_thin_binding_resolved(plan: &ResolvedBodyExprPlan<'_>) -> bool {
742 match plan {
743 ResolvedBodyExprPlan::Leaf(_)
744 | ResolvedBodyExprPlan::Call { .. }
745 | ResolvedBodyExprPlan::ForwardCall(_) => true,
746 ResolvedBodyExprPlan::Expr(expr) => match expr {
747 ResolvedExpr::Literal(_) | ResolvedExpr::Ident(_) => true,
748 ResolvedExpr::Ctor(_, _) => true,
749 ResolvedExpr::BinOp(_, l, r) => {
751 is_simple_operand_resolved(&l.node) && is_simple_operand_resolved(&r.node)
752 }
753 ResolvedExpr::Call(_, args) => args.iter().all(|a| is_simple_operand_resolved(&a.node)),
755 _ => false,
756 },
757 }
758}
759
760fn is_simple_operand_resolved(expr: &ResolvedExpr) -> bool {
761 matches!(
762 expr,
763 ResolvedExpr::Literal(_) | ResolvedExpr::Ident(_) | ResolvedExpr::Resolved { .. }
764 )
765}
766
767use crate::ir::SymbolTable;
776use crate::ir::{CallPlan, SemanticConstructor, WrapperKind as IrWrapperKind};
777
778pub fn call_plan_from_resolved_callee(
784 callee: &ResolvedCallee,
785 symbol_table: &SymbolTable,
786) -> CallPlan {
787 match callee {
788 ResolvedCallee::Fn(id) => CallPlan::Function(symbol_table.fn_entry(*id).key.canonical()),
789 ResolvedCallee::Builtin(name) => CallPlan::Builtin(name.clone()),
790 ResolvedCallee::Intrinsic(intr) => CallPlan::Builtin(intr.name().to_string()),
791 ResolvedCallee::LocalSlot { .. } | ResolvedCallee::Unresolved { .. } => CallPlan::Dynamic,
792 }
793}
794
795pub fn semantic_constructor_from_resolved_ctor(
797 ctor: &ResolvedCtor,
798 symbol_table: &SymbolTable,
799) -> SemanticConstructor {
800 match ctor {
801 ResolvedCtor::Builtin(BuiltinCtor::ResultOk) => {
802 SemanticConstructor::Wrapper(IrWrapperKind::ResultOk)
803 }
804 ResolvedCtor::Builtin(BuiltinCtor::ResultErr) => {
805 SemanticConstructor::Wrapper(IrWrapperKind::ResultErr)
806 }
807 ResolvedCtor::Builtin(BuiltinCtor::OptionSome) => {
808 SemanticConstructor::Wrapper(IrWrapperKind::OptionSome)
809 }
810 ResolvedCtor::Builtin(BuiltinCtor::OptionNone) => SemanticConstructor::NoneValue,
811 ResolvedCtor::User { type_id, name, .. } => SemanticConstructor::TypeConstructor {
812 qualified_type_name: symbol_table.type_entry(*type_id).key.canonical(),
813 variant_name: name.clone(),
814 },
815 ResolvedCtor::Unresolved { name } => SemanticConstructor::Unknown(name.clone()),
816 }
817}