sim_shape/functions/
select.rs1use std::cmp::Ordering;
4
5use sim_kernel::{Cx, Diagnostic, PreparedArgs, Result, shape_is_subshape_of};
6
7use super::{FunctionCase, FunctionObject, SelectedCase};
8use crate::diagnostics::{callable_mismatch_diagnostic, overload_selection_diagnostic};
9
10impl FunctionObject {
11 pub fn select_case<'a>(
25 &'a self,
26 cx: &mut Cx,
27 prepared: &PreparedArgs,
28 ) -> Result<SelectedCase<'a>> {
29 let cases = self.cases.iter().collect::<Vec<_>>();
30 let (matches, diagnostics) = self.collect_matches(cx, prepared, &cases)?;
31 self.select_best_case(cx, matches, diagnostics)
32 }
33
34 pub(in crate::functions) fn collect_matches<'a>(
35 &'a self,
36 cx: &mut Cx,
37 prepared: &PreparedArgs,
38 cases: &[&'a FunctionCase],
39 ) -> Result<(Vec<SelectedCase<'a>>, Vec<Diagnostic>)> {
40 let mut matches = Vec::new();
41 let mut diagnostics = Vec::new();
42 let args = cx.new_list(prepared.values().to_vec())?;
43
44 for case in cases {
45 let matched = case.args.check_value(cx, args.clone())?;
46 if matched.accepted {
47 matches.push(SelectedCase {
48 case,
49 match_result: matched,
50 });
51 } else {
52 diagnostics.extend(matched.diagnostics);
53 }
54 }
55
56 Ok((matches, diagnostics))
57 }
58
59 pub(in crate::functions) fn select_best_case<'a>(
60 &'a self,
61 cx: &mut Cx,
62 mut matches: Vec<SelectedCase<'a>>,
63 mut diagnostics: Vec<Diagnostic>,
64 ) -> Result<SelectedCase<'a>> {
65 if matches.is_empty() {
66 diagnostics.insert(
67 0,
68 overload_selection_diagnostic(&self.symbol, "no matching overload case"),
69 );
70 diagnostics.push(callable_mismatch_diagnostic(
71 &self.symbol,
72 "an applicable case",
73 "rejected arguments",
74 ));
75 return Err(sim_kernel::Error::NoMatchingOverload {
76 function: self.id,
77 diagnostics,
78 });
79 }
80
81 matches.sort_by(|left, right| {
85 right
86 .case
87 .priority
88 .cmp(&left.case.priority)
89 .then_with(|| right.match_result.score.cmp(&left.match_result.score))
90 });
91
92 let mut best = 0usize;
93 for index in 1..matches.len() {
94 if self
95 .compare_cases(cx, &matches[index], &matches[best])?
96 .is_gt()
97 {
98 best = index;
99 }
100 }
101
102 let mut candidates = vec![matches[best].case.id];
106 for index in 0..matches.len() {
107 if index != best
108 && self
109 .compare_cases(cx, &matches[best], &matches[index])?
110 .is_eq()
111 {
112 candidates.push(matches[index].case.id);
113 }
114 }
115 if candidates.len() > 1 {
116 cx.push_diagnostic(overload_selection_diagnostic(
117 &self.symbol,
118 "ambiguous top-ranked overload cases",
119 ));
120 return Err(sim_kernel::Error::AmbiguousOverload {
121 function: self.id,
122 candidates,
123 });
124 }
125
126 Ok(matches[best].clone())
127 }
128
129 fn compare_cases(
137 &self,
138 cx: &mut Cx,
139 a: &SelectedCase<'_>,
140 b: &SelectedCase<'_>,
141 ) -> Result<Ordering> {
142 match a.case.priority.cmp(&b.case.priority) {
143 Ordering::Equal => {}
144 other => return Ok(other),
145 }
146
147 let a_subshape = shape_is_subshape_of(cx, a.case.args.as_ref(), b.case.args.as_ref())?;
148 let b_subshape = shape_is_subshape_of(cx, b.case.args.as_ref(), a.case.args.as_ref())?;
149 match (a_subshape, b_subshape) {
150 (true, false) => return Ok(Ordering::Greater),
151 (false, true) => return Ok(Ordering::Less),
152 _ => {}
153 }
154
155 Ok(a.match_result.score.cmp(&b.match_result.score))
156 }
157}