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 IntDivOrDefaultLiteral {
77 a: &'a crate::ast::Spanned<ResolvedExpr>,
78 b: &'a crate::ast::Spanned<ResolvedExpr>,
79 default_literal: &'a Literal,
80 },
81 ListIndexGet {
82 list: &'a crate::ast::Spanned<ResolvedExpr>,
83 index: &'a crate::ast::Spanned<ResolvedExpr>,
84 },
85 NoneValue,
86 VariantConstructor {
87 qualified_type_name: String,
88 variant_name: String,
89 },
90 StaticRef(String),
91}
92
93#[allow(dead_code)]
96pub enum ResolvedBoolSubjectPlan<'a> {
97 Expr(&'a ResolvedExpr),
98 Compare {
99 lhs: &'a crate::ast::Spanned<ResolvedExpr>,
100 rhs: &'a crate::ast::Spanned<ResolvedExpr>,
101 op: BoolCompareOp,
102 invert: bool,
103 },
104}
105
106#[allow(dead_code)]
112pub struct ResolvedForwardCallPlan<'a> {
113 pub callee: &'a ResolvedCallee,
114 pub forward_slots: Vec<ForwardSlot>,
115 pub args: &'a [crate::ast::Spanned<ResolvedExpr>],
116}
117
118pub enum ForwardSlot {
119 Local { slot: u16 },
120}
121
122pub fn classify_leaf_op_resolved<'a>(
127 expr: &'a ResolvedExpr,
128 is_user_type: &impl Fn(&str) -> bool,
129) -> Option<ResolvedLeafOp<'a>> {
130 match expr {
131 ResolvedExpr::Attr(object, field_name) => {
132 classify_field_access(expr, object, field_name, is_user_type)
133 }
134 ResolvedExpr::Call(callee, args) => classify_leaf_call(callee, args, is_user_type),
135 ResolvedExpr::Ctor(ctor, args) if args.is_empty() => match ctor {
136 ResolvedCtor::Builtin(BuiltinCtor::OptionNone) => Some(ResolvedLeafOp::NoneValue),
137 ResolvedCtor::User {
138 type_id: _,
139 name,
140 ctor_id: _,
141 } => {
142 let _ = name;
154 None
155 }
156 _ => None,
157 },
158 _ => None,
159 }
160}
161
162fn classify_field_access<'a>(
163 full_expr: &'a ResolvedExpr,
164 object: &'a crate::ast::Spanned<ResolvedExpr>,
165 field_name: &'a str,
166 _is_user_type: &impl Fn(&str) -> bool,
167) -> Option<ResolvedLeafOp<'a>> {
168 let dotted = resolved_to_dotted(full_expr);
172 let starts_upper = dotted
173 .as_deref()
174 .and_then(|d| d.chars().next())
175 .is_some_and(|c| c.is_uppercase());
176
177 if !starts_upper {
178 return Some(ResolvedLeafOp::FieldAccess { object, field_name });
179 }
180 dotted.map(ResolvedLeafOp::StaticRef)
188}
189
190fn classify_leaf_call<'a>(
191 callee: &'a ResolvedCallee,
192 args: &'a [crate::ast::Spanned<ResolvedExpr>],
193 is_user_type: &impl Fn(&str) -> bool,
194) -> Option<ResolvedLeafOp<'a>> {
195 let builtin_name = match callee {
196 ResolvedCallee::Builtin(name) => name.as_str(),
197 _ => return None,
198 };
199 match (builtin_name, args.len()) {
200 ("Map.get", 2) => Some(ResolvedLeafOp::MapGet {
201 map: &args[0],
202 key: &args[1],
203 }),
204 ("Map.set", 3) => Some(ResolvedLeafOp::MapSet {
205 map: &args[0],
206 key: &args[1],
207 value: &args[2],
208 }),
209 ("Vector.new", 2) => Some(ResolvedLeafOp::VectorNew {
210 size: &args[0],
211 fill: &args[1],
212 }),
213 ("Vector.get", 2) => classify_list_index_get(&args[0], &args[1]),
214 ("Option.withDefault", 2) => classify_vector_set_or_default(&args[0], &args[1])
215 .or_else(|| classify_vector_get_or_default(&args[0], &args[1])),
216 ("Result.withDefault", 2) => {
217 classify_int_mod_or_div_or_default(&args[0], &args[1], is_user_type)
218 }
219 _ => None,
220 }
221}
222
223fn classify_vector_set_or_default<'a>(
224 option_expr: &'a crate::ast::Spanned<ResolvedExpr>,
225 default_expr: &'a crate::ast::Spanned<ResolvedExpr>,
226) -> Option<ResolvedLeafOp<'a>> {
227 let ResolvedExpr::Call(inner_callee, inner_args) = &option_expr.node else {
228 return None;
229 };
230 if inner_args.len() != 3 {
231 return None;
232 }
233 let is_vector_set =
234 matches!(inner_callee, ResolvedCallee::Builtin(name) if name == "Vector.set");
235 if !is_vector_set {
236 return None;
237 }
238 if default_expr.node != inner_args[0].node {
239 return None;
240 }
241 Some(ResolvedLeafOp::VectorSetOrDefaultSameVector {
242 vector: &inner_args[0],
243 index: &inner_args[1],
244 value: &inner_args[2],
245 })
246}
247
248fn classify_vector_get_or_default<'a>(
249 option_expr: &'a crate::ast::Spanned<ResolvedExpr>,
250 default_expr: &'a crate::ast::Spanned<ResolvedExpr>,
251) -> Option<ResolvedLeafOp<'a>> {
252 let default_literal = match &default_expr.node {
253 ResolvedExpr::Literal(lit) => lit,
254 _ => return None,
255 };
256 let ResolvedExpr::Call(inner_callee, inner_args) = &option_expr.node else {
257 return None;
258 };
259 if inner_args.len() != 2 {
260 return None;
261 }
262 let is_vector_get =
263 matches!(inner_callee, ResolvedCallee::Builtin(name) if name == "Vector.get");
264 if !is_vector_get {
265 return None;
266 }
267 Some(ResolvedLeafOp::VectorGetOrDefaultLiteral {
268 vector: &inner_args[0],
269 index: &inner_args[1],
270 default_literal,
271 })
272}
273
274fn classify_list_index_get<'a>(
275 vector_expr: &'a crate::ast::Spanned<ResolvedExpr>,
276 index: &'a crate::ast::Spanned<ResolvedExpr>,
277) -> Option<ResolvedLeafOp<'a>> {
278 let ResolvedExpr::Call(inner_callee, inner_args) = &vector_expr.node else {
279 return None;
280 };
281 if inner_args.len() != 1 {
282 return None;
283 }
284 let is_from_list =
285 matches!(inner_callee, ResolvedCallee::Builtin(name) if name == "Vector.fromList");
286 if !is_from_list {
287 return None;
288 }
289 Some(ResolvedLeafOp::ListIndexGet {
290 list: &inner_args[0],
291 index,
292 })
293}
294
295fn classify_int_mod_or_div_or_default<'a>(
296 result_expr: &'a crate::ast::Spanned<ResolvedExpr>,
297 default_expr: &'a crate::ast::Spanned<ResolvedExpr>,
298 _is_user_type: &impl Fn(&str) -> bool,
299) -> Option<ResolvedLeafOp<'a>> {
300 let default_literal = match &default_expr.node {
301 ResolvedExpr::Literal(lit) => lit,
302 _ => return None,
303 };
304 let ResolvedExpr::Call(inner_callee, inner_args) = &result_expr.node else {
305 return None;
306 };
307 if inner_args.len() != 2 {
308 return None;
309 }
310 let ResolvedCallee::Builtin(name) = inner_callee else {
311 return None;
312 };
313 match name.as_str() {
314 "Int.mod" => Some(ResolvedLeafOp::IntModOrDefaultLiteral {
315 a: &inner_args[0],
316 b: &inner_args[1],
317 default_literal,
318 }),
319 "Int.div" => Some(ResolvedLeafOp::IntDivOrDefaultLiteral {
320 a: &inner_args[0],
321 b: &inner_args[1],
322 default_literal,
323 }),
324 _ => None,
325 }
326}
327
328pub fn classify_bool_subject_plan_resolved(subject: &ResolvedExpr) -> ResolvedBoolSubjectPlan<'_> {
331 let ResolvedExpr::BinOp(op, lhs, rhs) = subject else {
332 return ResolvedBoolSubjectPlan::Expr(subject);
333 };
334 use crate::ast::BinOp;
335 match op {
336 BinOp::Eq => ResolvedBoolSubjectPlan::Compare {
337 lhs,
338 rhs,
339 op: BoolCompareOp::Eq,
340 invert: false,
341 },
342 BinOp::Lt => ResolvedBoolSubjectPlan::Compare {
343 lhs,
344 rhs,
345 op: BoolCompareOp::Lt,
346 invert: false,
347 },
348 BinOp::Gt => ResolvedBoolSubjectPlan::Compare {
349 lhs,
350 rhs,
351 op: BoolCompareOp::Gt,
352 invert: false,
353 },
354 BinOp::Neq => ResolvedBoolSubjectPlan::Compare {
355 lhs,
356 rhs,
357 op: BoolCompareOp::Eq,
358 invert: true,
359 },
360 BinOp::Gte => ResolvedBoolSubjectPlan::Compare {
361 lhs,
362 rhs,
363 op: BoolCompareOp::Lt,
364 invert: true,
365 },
366 BinOp::Lte => ResolvedBoolSubjectPlan::Compare {
367 lhs,
368 rhs,
369 op: BoolCompareOp::Gt,
370 invert: true,
371 },
372 BinOp::Add | BinOp::Sub | BinOp::Mul | BinOp::Div => ResolvedBoolSubjectPlan::Expr(subject),
373 }
374}
375
376pub fn classify_dispatch_pattern_resolved(
379 pattern: &ResolvedPattern,
380) -> Option<SemanticDispatchPattern> {
381 match pattern {
382 ResolvedPattern::Literal(lit) => Some(SemanticDispatchPattern::Literal(
383 dispatch_literal_from_ast(lit),
384 )),
385 ResolvedPattern::EmptyList => Some(SemanticDispatchPattern::EmptyList),
386 ResolvedPattern::Ctor(ctor, bindings) => match ctor {
387 ResolvedCtor::Builtin(BuiltinCtor::OptionNone) if bindings.is_empty() => {
388 Some(SemanticDispatchPattern::NoneValue)
389 }
390 ResolvedCtor::Builtin(BuiltinCtor::ResultOk) if bindings.len() <= 1 => {
391 Some(SemanticDispatchPattern::WrapperTag(WrapperKind::ResultOk))
392 }
393 ResolvedCtor::Builtin(BuiltinCtor::ResultErr) if bindings.len() <= 1 => {
394 Some(SemanticDispatchPattern::WrapperTag(WrapperKind::ResultErr))
395 }
396 ResolvedCtor::Builtin(BuiltinCtor::OptionSome) if bindings.len() <= 1 => {
397 Some(SemanticDispatchPattern::WrapperTag(WrapperKind::OptionSome))
398 }
399 _ => None,
400 },
401 _ => None,
402 }
403}
404
405pub fn classify_list_match_shape_resolved(arms: &[ResolvedMatchArm]) -> Option<ListMatchShape> {
407 if arms.len() != 2 {
408 return None;
409 }
410 match (&arms[0].pattern, &arms[1].pattern) {
411 (ResolvedPattern::EmptyList, ResolvedPattern::Cons(_, _)) => Some(ListMatchShape {
412 empty_arm_index: 0,
413 cons_arm_index: 1,
414 }),
415 (ResolvedPattern::Cons(_, _), ResolvedPattern::EmptyList) => Some(ListMatchShape {
416 empty_arm_index: 1,
417 cons_arm_index: 0,
418 }),
419 _ => None,
420 }
421}
422
423pub fn classify_bool_match_shape_resolved(arms: &[ResolvedMatchArm]) -> Option<BoolMatchShape> {
425 if arms.len() != 2 {
426 return None;
427 }
428 use crate::ast::Literal as Lit;
429 match (&arms[0].pattern, &arms[1].pattern) {
430 (ResolvedPattern::Literal(Lit::Bool(true)), ResolvedPattern::Literal(Lit::Bool(false))) => {
431 Some(BoolMatchShape {
432 true_arm_index: 0,
433 false_arm_index: 1,
434 })
435 }
436 (ResolvedPattern::Literal(Lit::Bool(false)), ResolvedPattern::Literal(Lit::Bool(true))) => {
437 Some(BoolMatchShape {
438 true_arm_index: 1,
439 false_arm_index: 0,
440 })
441 }
442 (
443 ResolvedPattern::Literal(Lit::Bool(true)),
444 ResolvedPattern::Wildcard | ResolvedPattern::Ident(_),
445 ) => Some(BoolMatchShape {
446 true_arm_index: 0,
447 false_arm_index: 1,
448 }),
449 _ => None,
450 }
451}
452
453pub fn classify_dispatch_table_shape_resolved(
456 arms: &[ResolvedMatchArm],
457) -> Option<DispatchTableShape> {
458 if arms.len() < 2 {
459 return None;
460 }
461 let has_default = matches!(
462 arms.last().map(|a| &a.pattern),
463 Some(ResolvedPattern::Wildcard | ResolvedPattern::Ident(_))
464 );
465 let dispatchable_end = if has_default {
466 arms.len() - 1
467 } else {
468 arms.len()
469 };
470
471 let mut entries = Vec::new();
472 for (arm_index, arm) in arms[..dispatchable_end].iter().enumerate() {
473 let semantic = classify_dispatch_pattern_resolved(&arm.pattern)?;
474 entries.push(DispatchArmPlan {
475 binding: classify_dispatch_binding_resolved(&arm.pattern, &semantic),
476 pattern: semantic,
477 arm_index,
478 });
479 }
480
481 if entries.len() < 2 {
482 return None;
483 }
484
485 let default_arm = has_default.then(|| {
486 let arm_idx = arms.len() - 1;
487 let binding_name = match &arms[arm_idx].pattern {
488 ResolvedPattern::Ident(name) if name != "_" => Some(name.clone()),
489 _ => None,
490 };
491 DispatchDefaultPlan {
492 arm_index: arm_idx,
493 binding_name,
494 }
495 });
496
497 Some(DispatchTableShape {
498 entries,
499 default_arm,
500 })
501}
502
503pub fn classify_match_dispatch_plan_resolved(
506 arms: &[ResolvedMatchArm],
507) -> Option<MatchDispatchPlan> {
508 if let Some(shape) = classify_bool_match_shape_resolved(arms) {
509 return Some(MatchDispatchPlan::Bool(shape));
510 }
511 if let Some(shape) = classify_list_match_shape_resolved(arms) {
512 return Some(MatchDispatchPlan::List(shape));
513 }
514 classify_dispatch_table_shape_resolved(arms).map(MatchDispatchPlan::Table)
515}
516
517fn classify_dispatch_binding_resolved(
518 pattern: &ResolvedPattern,
519 semantic: &SemanticDispatchPattern,
520) -> DispatchBindingPlan {
521 match (pattern, semantic) {
522 (ResolvedPattern::Ctor(_, bindings), SemanticDispatchPattern::WrapperTag(_))
523 if !bindings.is_empty() && bindings[0] != "_" =>
524 {
525 DispatchBindingPlan::WrapperPayload(bindings[0].clone())
526 }
527 _ => DispatchBindingPlan::None,
528 }
529}
530
531fn dispatch_literal_from_ast(lit: &Literal) -> DispatchLiteral {
532 match lit {
533 Literal::Int(i) => DispatchLiteral::Int(*i),
534 Literal::Float(f) => DispatchLiteral::Float(f.to_string()),
535 Literal::Bool(b) => DispatchLiteral::Bool(*b),
536 Literal::Str(s) => DispatchLiteral::Str(s.clone()),
537 Literal::Unit => DispatchLiteral::Unit,
538 }
539}
540
541pub fn resolved_to_dotted(expr: &ResolvedExpr) -> Option<String> {
550 match expr {
551 ResolvedExpr::Ident(name) => Some(name.clone()),
552 ResolvedExpr::Resolved { name, .. } => Some(name.clone()),
553 ResolvedExpr::Attr(obj, field) => {
554 let head = resolved_to_dotted(&obj.node)?;
555 Some(format!("{head}.{field}"))
556 }
557 _ => None,
558 }
559}
560
561pub fn classify_forward_call_resolved<'a>(
570 callee: &'a ResolvedCallee,
571 args: &'a [crate::ast::Spanned<ResolvedExpr>],
572) -> Option<ResolvedForwardCallPlan<'a>> {
573 match callee {
574 ResolvedCallee::Unresolved { .. } => return None,
575 ResolvedCallee::LocalSlot { .. } => return None,
576 ResolvedCallee::Intrinsic(_) => return None,
577 _ => {}
578 }
579
580 let forward_slots = args
581 .iter()
582 .map(classify_forward_arg_resolved)
583 .collect::<Option<Vec<_>>>()?;
584
585 Some(ResolvedForwardCallPlan {
586 callee,
587 forward_slots,
588 args,
589 })
590}
591
592fn classify_forward_arg_resolved(expr: &crate::ast::Spanned<ResolvedExpr>) -> Option<ForwardSlot> {
593 match &expr.node {
594 ResolvedExpr::Resolved { slot, .. } => Some(ForwardSlot::Local { slot: *slot }),
595 _ => None,
596 }
597}
598
599pub use crate::ir::body::ThinKind;
608
609pub enum ResolvedBodyExprPlan<'a> {
611 Expr(&'a ResolvedExpr),
612 Leaf(ResolvedLeafOp<'a>),
613 Call {
614 callee: &'a ResolvedCallee,
615 args: &'a [Spanned<ResolvedExpr>],
616 },
617 ForwardCall(ResolvedForwardCallPlan<'a>),
618}
619
620pub struct ResolvedBodyBindingPlan<'a> {
622 pub name: &'a str,
623 pub expr: ResolvedBodyExprPlan<'a>,
624}
625
626pub enum ResolvedBodyPlan<'a> {
628 SingleExpr(ResolvedBodyExprPlan<'a>),
629 Block {
630 stmts: &'a [ResolvedStmt],
631 bindings: Vec<ResolvedBodyBindingPlan<'a>>,
632 tail: ResolvedBodyExprPlan<'a>,
633 },
634}
635
636pub struct ResolvedThinBodyPlan<'a> {
641 pub params: &'a [(String, crate::ast::Type)],
642 pub body: ResolvedBodyPlan<'a>,
643 pub kind: ThinKind,
644}
645
646pub fn classify_body_expr_plan_resolved<'a>(
647 expr: &'a ResolvedExpr,
648 is_user_type: &impl Fn(&str) -> bool,
649) -> ResolvedBodyExprPlan<'a> {
650 if let Some(leaf) = classify_leaf_op_resolved(expr, is_user_type) {
651 return ResolvedBodyExprPlan::Leaf(leaf);
652 }
653 if let ResolvedExpr::Call(callee, args) = expr {
654 if let Some(plan) = classify_forward_call_resolved(callee, args) {
655 return ResolvedBodyExprPlan::ForwardCall(plan);
656 }
657 if !matches!(callee, ResolvedCallee::LocalSlot { .. }) {
663 return ResolvedBodyExprPlan::Call { callee, args };
664 }
665 }
666 ResolvedBodyExprPlan::Expr(expr)
667}
668
669pub fn classify_body_plan_resolved<'a>(
670 body: &'a ResolvedFnBody,
671 is_user_type: &impl Fn(&str) -> bool,
672) -> Option<ResolvedBodyPlan<'a>> {
673 let stmts = body.stmts();
674 let (tail_stmt, prefix) = stmts.split_last()?;
675
676 let ResolvedStmt::Expr(tail_expr) = tail_stmt else {
677 return None;
678 };
679
680 if prefix.is_empty() {
681 return Some(ResolvedBodyPlan::SingleExpr(
682 classify_body_expr_plan_resolved(&tail_expr.node, is_user_type),
683 ));
684 }
685
686 let mut bindings = Vec::with_capacity(prefix.len());
687 for stmt in prefix {
688 let ResolvedStmt::Binding { name, value, .. } = stmt else {
689 return None;
690 };
691 bindings.push(ResolvedBodyBindingPlan {
692 name: name.as_str(),
693 expr: classify_body_expr_plan_resolved(&value.node, is_user_type),
694 });
695 }
696
697 Some(ResolvedBodyPlan::Block {
698 stmts,
699 bindings,
700 tail: classify_body_expr_plan_resolved(&tail_expr.node, is_user_type),
701 })
702}
703
704pub fn classify_thin_fn_def_resolved<'a>(
705 fd: &'a ResolvedFnDef,
706 is_user_type: &impl Fn(&str) -> bool,
707) -> Option<ResolvedThinBodyPlan<'a>> {
708 let body = classify_body_plan_resolved(&fd.body, is_user_type)?;
709 Some(ResolvedThinBodyPlan {
710 params: &fd.params,
711 kind: classify_thin_kind_resolved(&body, is_user_type)?,
712 body,
713 })
714}
715
716fn classify_thin_kind_resolved(
717 plan: &ResolvedBodyPlan<'_>,
718 is_user_type: &impl Fn(&str) -> bool,
719) -> Option<ThinKind> {
720 match plan {
721 ResolvedBodyPlan::SingleExpr(expr) => classify_thin_expr_kind_resolved(expr, is_user_type),
722 ResolvedBodyPlan::Block { bindings, tail, .. } => {
723 if bindings
724 .iter()
725 .all(|binding| body_expr_is_thin_binding_resolved(&binding.expr))
726 {
727 classify_thin_expr_kind_resolved(tail, is_user_type)
728 } else {
729 None
730 }
731 }
732 }
733}
734
735fn classify_thin_expr_kind_resolved(
736 plan: &ResolvedBodyExprPlan<'_>,
737 _is_user_type: &impl Fn(&str) -> bool,
738) -> Option<ThinKind> {
739 match plan {
740 ResolvedBodyExprPlan::Leaf(_) => Some(ThinKind::Leaf),
741 ResolvedBodyExprPlan::Call { .. } => Some(ThinKind::Direct),
742 ResolvedBodyExprPlan::ForwardCall(_) => Some(ThinKind::Forward),
743 ResolvedBodyExprPlan::Expr(expr) => match expr {
744 ResolvedExpr::Match { arms, .. }
745 if classify_match_dispatch_plan_resolved(arms).is_some() =>
746 {
747 Some(ThinKind::Dispatch)
748 }
749 ResolvedExpr::TailCall { .. } => Some(ThinKind::Tail),
750 _ => None,
751 },
752 }
753}
754
755fn body_expr_is_thin_binding_resolved(plan: &ResolvedBodyExprPlan<'_>) -> bool {
756 match plan {
757 ResolvedBodyExprPlan::Leaf(_)
758 | ResolvedBodyExprPlan::Call { .. }
759 | ResolvedBodyExprPlan::ForwardCall(_) => true,
760 ResolvedBodyExprPlan::Expr(expr) => match expr {
761 ResolvedExpr::Literal(_) | ResolvedExpr::Ident(_) => true,
762 ResolvedExpr::Ctor(_, _) => true,
763 ResolvedExpr::BinOp(_, l, r) => {
765 is_simple_operand_resolved(&l.node) && is_simple_operand_resolved(&r.node)
766 }
767 ResolvedExpr::Call(_, args) => args.iter().all(|a| is_simple_operand_resolved(&a.node)),
769 _ => false,
770 },
771 }
772}
773
774fn is_simple_operand_resolved(expr: &ResolvedExpr) -> bool {
775 matches!(
776 expr,
777 ResolvedExpr::Literal(_) | ResolvedExpr::Ident(_) | ResolvedExpr::Resolved { .. }
778 )
779}
780
781use crate::ir::SymbolTable;
790use crate::ir::{CallPlan, SemanticConstructor, WrapperKind as IrWrapperKind};
791
792pub fn call_plan_from_resolved_callee(
798 callee: &ResolvedCallee,
799 symbol_table: &SymbolTable,
800) -> CallPlan {
801 match callee {
802 ResolvedCallee::Fn(id) => CallPlan::Function(symbol_table.fn_entry(*id).key.canonical()),
803 ResolvedCallee::Builtin(name) => CallPlan::Builtin(name.clone()),
804 ResolvedCallee::Intrinsic(intr) => CallPlan::Builtin(intr.name().to_string()),
805 ResolvedCallee::LocalSlot { .. } | ResolvedCallee::Unresolved { .. } => CallPlan::Dynamic,
806 }
807}
808
809pub fn semantic_constructor_from_resolved_ctor(
811 ctor: &ResolvedCtor,
812 symbol_table: &SymbolTable,
813) -> SemanticConstructor {
814 match ctor {
815 ResolvedCtor::Builtin(BuiltinCtor::ResultOk) => {
816 SemanticConstructor::Wrapper(IrWrapperKind::ResultOk)
817 }
818 ResolvedCtor::Builtin(BuiltinCtor::ResultErr) => {
819 SemanticConstructor::Wrapper(IrWrapperKind::ResultErr)
820 }
821 ResolvedCtor::Builtin(BuiltinCtor::OptionSome) => {
822 SemanticConstructor::Wrapper(IrWrapperKind::OptionSome)
823 }
824 ResolvedCtor::Builtin(BuiltinCtor::OptionNone) => SemanticConstructor::NoneValue,
825 ResolvedCtor::User { type_id, name, .. } => SemanticConstructor::TypeConstructor {
826 qualified_type_name: symbol_table.type_entry(*type_id).key.canonical(),
827 variant_name: name.clone(),
828 },
829 ResolvedCtor::Unresolved { name } => SemanticConstructor::Unknown(name.clone()),
830 }
831}