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(crate::ast::Literal::BigInt(_)) => None,
387 ResolvedPattern::Literal(lit) => Some(SemanticDispatchPattern::Literal(
388 dispatch_literal_from_ast(lit),
389 )),
390 ResolvedPattern::EmptyList => Some(SemanticDispatchPattern::EmptyList),
391 ResolvedPattern::Ctor(ctor, bindings) => match ctor {
392 ResolvedCtor::Builtin(BuiltinCtor::OptionNone) if bindings.is_empty() => {
393 Some(SemanticDispatchPattern::NoneValue)
394 }
395 ResolvedCtor::Builtin(BuiltinCtor::ResultOk) if bindings.len() <= 1 => {
396 Some(SemanticDispatchPattern::WrapperTag(WrapperKind::ResultOk))
397 }
398 ResolvedCtor::Builtin(BuiltinCtor::ResultErr) if bindings.len() <= 1 => {
399 Some(SemanticDispatchPattern::WrapperTag(WrapperKind::ResultErr))
400 }
401 ResolvedCtor::Builtin(BuiltinCtor::OptionSome) if bindings.len() <= 1 => {
402 Some(SemanticDispatchPattern::WrapperTag(WrapperKind::OptionSome))
403 }
404 _ => None,
405 },
406 _ => None,
407 }
408}
409
410pub fn classify_list_match_shape_resolved(arms: &[ResolvedMatchArm]) -> Option<ListMatchShape> {
412 if arms.len() != 2 {
413 return None;
414 }
415 match (&arms[0].pattern, &arms[1].pattern) {
416 (ResolvedPattern::EmptyList, ResolvedPattern::Cons(_, _)) => Some(ListMatchShape {
417 empty_arm_index: 0,
418 cons_arm_index: 1,
419 }),
420 (ResolvedPattern::Cons(_, _), ResolvedPattern::EmptyList) => Some(ListMatchShape {
421 empty_arm_index: 1,
422 cons_arm_index: 0,
423 }),
424 _ => None,
425 }
426}
427
428pub fn classify_bool_match_shape_resolved(arms: &[ResolvedMatchArm]) -> Option<BoolMatchShape> {
430 if arms.len() != 2 {
431 return None;
432 }
433 use crate::ast::Literal as Lit;
434 match (&arms[0].pattern, &arms[1].pattern) {
435 (ResolvedPattern::Literal(Lit::Bool(true)), ResolvedPattern::Literal(Lit::Bool(false))) => {
436 Some(BoolMatchShape {
437 true_arm_index: 0,
438 false_arm_index: 1,
439 })
440 }
441 (ResolvedPattern::Literal(Lit::Bool(false)), ResolvedPattern::Literal(Lit::Bool(true))) => {
442 Some(BoolMatchShape {
443 true_arm_index: 1,
444 false_arm_index: 0,
445 })
446 }
447 (
448 ResolvedPattern::Literal(Lit::Bool(true)),
449 ResolvedPattern::Wildcard | ResolvedPattern::Ident(_),
450 ) => Some(BoolMatchShape {
451 true_arm_index: 0,
452 false_arm_index: 1,
453 }),
454 _ => None,
455 }
456}
457
458pub fn classify_dispatch_table_shape_resolved(
461 arms: &[ResolvedMatchArm],
462) -> Option<DispatchTableShape> {
463 if arms.len() < 2 {
464 return None;
465 }
466 let has_default = matches!(
467 arms.last().map(|a| &a.pattern),
468 Some(ResolvedPattern::Wildcard | ResolvedPattern::Ident(_))
469 );
470 let dispatchable_end = if has_default {
471 arms.len() - 1
472 } else {
473 arms.len()
474 };
475
476 let mut entries = Vec::new();
477 for (arm_index, arm) in arms[..dispatchable_end].iter().enumerate() {
478 let semantic = classify_dispatch_pattern_resolved(&arm.pattern)?;
479 entries.push(DispatchArmPlan {
480 binding: classify_dispatch_binding_resolved(&arm.pattern, &semantic),
481 pattern: semantic,
482 arm_index,
483 });
484 }
485
486 if entries.len() < 2 {
487 return None;
488 }
489
490 let default_arm = has_default.then(|| {
491 let arm_idx = arms.len() - 1;
492 let binding_name = match &arms[arm_idx].pattern {
493 ResolvedPattern::Ident(name) if name != "_" => Some(name.clone()),
494 _ => None,
495 };
496 DispatchDefaultPlan {
497 arm_index: arm_idx,
498 binding_name,
499 }
500 });
501
502 Some(DispatchTableShape {
503 entries,
504 default_arm,
505 })
506}
507
508pub fn classify_match_dispatch_plan_resolved(
511 arms: &[ResolvedMatchArm],
512) -> Option<MatchDispatchPlan> {
513 if let Some(shape) = classify_bool_match_shape_resolved(arms) {
514 return Some(MatchDispatchPlan::Bool(shape));
515 }
516 if let Some(shape) = classify_list_match_shape_resolved(arms) {
517 return Some(MatchDispatchPlan::List(shape));
518 }
519 classify_dispatch_table_shape_resolved(arms).map(MatchDispatchPlan::Table)
520}
521
522fn classify_dispatch_binding_resolved(
523 pattern: &ResolvedPattern,
524 semantic: &SemanticDispatchPattern,
525) -> DispatchBindingPlan {
526 match (pattern, semantic) {
527 (ResolvedPattern::Ctor(_, bindings), SemanticDispatchPattern::WrapperTag(_))
528 if !bindings.is_empty() && bindings[0] != "_" =>
529 {
530 DispatchBindingPlan::WrapperPayload(bindings[0].clone())
531 }
532 _ => DispatchBindingPlan::None,
533 }
534}
535
536fn dispatch_literal_from_ast(lit: &Literal) -> DispatchLiteral {
537 match lit {
538 Literal::Int(i) => DispatchLiteral::Int(*i),
539 Literal::Float(f) => DispatchLiteral::Float(f.to_string()),
540 Literal::Bool(b) => DispatchLiteral::Bool(*b),
541 Literal::Str(s) => DispatchLiteral::Str(s.clone()),
542 Literal::Unit => DispatchLiteral::Unit,
543 Literal::BigInt(_) => {
546 unreachable!("BigInt literal patterns are excluded from dispatch tables")
547 }
548 }
549}
550
551pub fn resolved_to_dotted(expr: &ResolvedExpr) -> Option<String> {
560 match expr {
561 ResolvedExpr::Ident(name) => Some(name.clone()),
562 ResolvedExpr::Resolved { name, .. } => Some(name.clone()),
563 ResolvedExpr::Attr(obj, field) => {
564 let head = resolved_to_dotted(&obj.node)?;
565 Some(format!("{head}.{field}"))
566 }
567 _ => None,
568 }
569}
570
571pub fn classify_forward_call_resolved<'a>(
580 callee: &'a ResolvedCallee,
581 args: &'a [crate::ast::Spanned<ResolvedExpr>],
582) -> Option<ResolvedForwardCallPlan<'a>> {
583 match callee {
584 ResolvedCallee::Unresolved { .. } => return None,
585 ResolvedCallee::LocalSlot { .. } => return None,
586 ResolvedCallee::Intrinsic(_) => return None,
587 _ => {}
588 }
589
590 let forward_slots = args
591 .iter()
592 .map(classify_forward_arg_resolved)
593 .collect::<Option<Vec<_>>>()?;
594
595 Some(ResolvedForwardCallPlan {
596 callee,
597 forward_slots,
598 args,
599 })
600}
601
602fn classify_forward_arg_resolved(expr: &crate::ast::Spanned<ResolvedExpr>) -> Option<ForwardSlot> {
603 match &expr.node {
604 ResolvedExpr::Resolved { slot, .. } => Some(ForwardSlot::Local { slot: *slot }),
605 _ => None,
606 }
607}
608
609pub use crate::ir::body::ThinKind;
618
619pub enum ResolvedBodyExprPlan<'a> {
621 Expr(&'a ResolvedExpr),
622 Leaf(ResolvedLeafOp<'a>),
623 Call {
624 callee: &'a ResolvedCallee,
625 args: &'a [Spanned<ResolvedExpr>],
626 },
627 ForwardCall(ResolvedForwardCallPlan<'a>),
628}
629
630pub struct ResolvedBodyBindingPlan<'a> {
632 pub name: &'a str,
633 pub expr: ResolvedBodyExprPlan<'a>,
634}
635
636pub enum ResolvedBodyPlan<'a> {
638 SingleExpr(ResolvedBodyExprPlan<'a>),
639 Block {
640 stmts: &'a [ResolvedStmt],
641 bindings: Vec<ResolvedBodyBindingPlan<'a>>,
642 tail: ResolvedBodyExprPlan<'a>,
643 },
644}
645
646pub struct ResolvedThinBodyPlan<'a> {
651 pub params: &'a [(String, crate::ast::Type)],
652 pub body: ResolvedBodyPlan<'a>,
653 pub kind: ThinKind,
654}
655
656pub fn classify_body_expr_plan_resolved<'a>(
657 expr: &'a ResolvedExpr,
658 is_user_type: &impl Fn(&str) -> bool,
659) -> ResolvedBodyExprPlan<'a> {
660 if let Some(leaf) = classify_leaf_op_resolved(expr, is_user_type) {
661 return ResolvedBodyExprPlan::Leaf(leaf);
662 }
663 if let ResolvedExpr::Call(callee, args) = expr {
664 if let Some(plan) = classify_forward_call_resolved(callee, args) {
665 return ResolvedBodyExprPlan::ForwardCall(plan);
666 }
667 if !matches!(callee, ResolvedCallee::LocalSlot { .. }) {
673 return ResolvedBodyExprPlan::Call { callee, args };
674 }
675 }
676 ResolvedBodyExprPlan::Expr(expr)
677}
678
679pub fn classify_body_plan_resolved<'a>(
680 body: &'a ResolvedFnBody,
681 is_user_type: &impl Fn(&str) -> bool,
682) -> Option<ResolvedBodyPlan<'a>> {
683 let stmts = body.stmts();
684 let (tail_stmt, prefix) = stmts.split_last()?;
685
686 let ResolvedStmt::Expr(tail_expr) = tail_stmt else {
687 return None;
688 };
689
690 if prefix.is_empty() {
691 return Some(ResolvedBodyPlan::SingleExpr(
692 classify_body_expr_plan_resolved(&tail_expr.node, is_user_type),
693 ));
694 }
695
696 let mut bindings = Vec::with_capacity(prefix.len());
697 for stmt in prefix {
698 let ResolvedStmt::Binding { name, value, .. } = stmt else {
699 return None;
700 };
701 bindings.push(ResolvedBodyBindingPlan {
702 name: name.as_str(),
703 expr: classify_body_expr_plan_resolved(&value.node, is_user_type),
704 });
705 }
706
707 Some(ResolvedBodyPlan::Block {
708 stmts,
709 bindings,
710 tail: classify_body_expr_plan_resolved(&tail_expr.node, is_user_type),
711 })
712}
713
714pub fn classify_thin_fn_def_resolved<'a>(
715 fd: &'a ResolvedFnDef,
716 is_user_type: &impl Fn(&str) -> bool,
717) -> Option<ResolvedThinBodyPlan<'a>> {
718 let body = classify_body_plan_resolved(&fd.body, is_user_type)?;
719 Some(ResolvedThinBodyPlan {
720 params: &fd.params,
721 kind: classify_thin_kind_resolved(&body, is_user_type)?,
722 body,
723 })
724}
725
726fn classify_thin_kind_resolved(
727 plan: &ResolvedBodyPlan<'_>,
728 is_user_type: &impl Fn(&str) -> bool,
729) -> Option<ThinKind> {
730 match plan {
731 ResolvedBodyPlan::SingleExpr(expr) => classify_thin_expr_kind_resolved(expr, is_user_type),
732 ResolvedBodyPlan::Block { bindings, tail, .. } => {
733 if bindings
734 .iter()
735 .all(|binding| body_expr_is_thin_binding_resolved(&binding.expr))
736 {
737 classify_thin_expr_kind_resolved(tail, is_user_type)
738 } else {
739 None
740 }
741 }
742 }
743}
744
745fn classify_thin_expr_kind_resolved(
746 plan: &ResolvedBodyExprPlan<'_>,
747 _is_user_type: &impl Fn(&str) -> bool,
748) -> Option<ThinKind> {
749 match plan {
750 ResolvedBodyExprPlan::Leaf(_) => Some(ThinKind::Leaf),
751 ResolvedBodyExprPlan::Call { .. } => Some(ThinKind::Direct),
752 ResolvedBodyExprPlan::ForwardCall(_) => Some(ThinKind::Forward),
753 ResolvedBodyExprPlan::Expr(expr) => match expr {
754 ResolvedExpr::Match { arms, .. }
755 if classify_match_dispatch_plan_resolved(arms).is_some() =>
756 {
757 Some(ThinKind::Dispatch)
758 }
759 ResolvedExpr::TailCall { .. } => Some(ThinKind::Tail),
760 _ => None,
761 },
762 }
763}
764
765fn body_expr_is_thin_binding_resolved(plan: &ResolvedBodyExprPlan<'_>) -> bool {
766 match plan {
767 ResolvedBodyExprPlan::Leaf(_)
768 | ResolvedBodyExprPlan::Call { .. }
769 | ResolvedBodyExprPlan::ForwardCall(_) => true,
770 ResolvedBodyExprPlan::Expr(expr) => match expr {
771 ResolvedExpr::Literal(_) | ResolvedExpr::Ident(_) => true,
772 ResolvedExpr::Ctor(_, _) => true,
773 ResolvedExpr::BinOp(_, l, r) => {
775 is_simple_operand_resolved(&l.node) && is_simple_operand_resolved(&r.node)
776 }
777 ResolvedExpr::Call(_, args) => args.iter().all(|a| is_simple_operand_resolved(&a.node)),
779 _ => false,
780 },
781 }
782}
783
784fn is_simple_operand_resolved(expr: &ResolvedExpr) -> bool {
785 matches!(
786 expr,
787 ResolvedExpr::Literal(_) | ResolvedExpr::Ident(_) | ResolvedExpr::Resolved { .. }
788 )
789}
790
791use crate::ir::SymbolTable;
800use crate::ir::{CallPlan, SemanticConstructor, WrapperKind as IrWrapperKind};
801
802pub fn call_plan_from_resolved_callee(
808 callee: &ResolvedCallee,
809 symbol_table: &SymbolTable,
810) -> CallPlan {
811 match callee {
812 ResolvedCallee::Fn(id) => CallPlan::Function(symbol_table.fn_entry(*id).key.canonical()),
813 ResolvedCallee::Builtin(name) => CallPlan::Builtin(name.clone()),
814 ResolvedCallee::Intrinsic(intr) => CallPlan::Builtin(intr.name().to_string()),
815 ResolvedCallee::LocalSlot { .. } | ResolvedCallee::Unresolved { .. } => CallPlan::Dynamic,
816 }
817}
818
819pub fn semantic_constructor_from_resolved_ctor(
821 ctor: &ResolvedCtor,
822 symbol_table: &SymbolTable,
823) -> SemanticConstructor {
824 match ctor {
825 ResolvedCtor::Builtin(BuiltinCtor::ResultOk) => {
826 SemanticConstructor::Wrapper(IrWrapperKind::ResultOk)
827 }
828 ResolvedCtor::Builtin(BuiltinCtor::ResultErr) => {
829 SemanticConstructor::Wrapper(IrWrapperKind::ResultErr)
830 }
831 ResolvedCtor::Builtin(BuiltinCtor::OptionSome) => {
832 SemanticConstructor::Wrapper(IrWrapperKind::OptionSome)
833 }
834 ResolvedCtor::Builtin(BuiltinCtor::OptionNone) => SemanticConstructor::NoneValue,
835 ResolvedCtor::User { type_id, name, .. } => SemanticConstructor::TypeConstructor {
836 qualified_type_name: symbol_table.type_entry(*type_id).key.canonical(),
837 variant_name: name.clone(),
838 },
839 ResolvedCtor::Unresolved { name } => SemanticConstructor::Unknown(name.clone()),
840 }
841}