1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
//! Overload selection for shape-typed function cases.
use std::cmp::Ordering;
use sim_kernel::{Cx, Diagnostic, PreparedArgs, Result, shape_is_subshape_of};
use super::{FunctionCase, FunctionObject, SelectedCase};
use crate::diagnostics::{callable_mismatch_diagnostic, overload_selection_diagnostic};
impl FunctionObject {
/// Pick the overload case that best matches the prepared arguments.
///
/// Cases whose argument shape accepts the arguments are ordered by priority
/// first, then by proven argument-shape specificity (the subshape lattice),
/// and only then by additive match score: a case whose argument shape is a
/// proven subshape of another's is strictly more specific and wins even at
/// an equal or lower score. Returns [`Error::NoMatchingOverload`] when
/// nothing matches and [`Error::AmbiguousOverload`] when two top cases
/// remain unordered (equal priority, neither shape subsumes the other, and
/// equal score).
///
/// [`Error::NoMatchingOverload`]: sim_kernel::Error::NoMatchingOverload
/// [`Error::AmbiguousOverload`]: sim_kernel::Error::AmbiguousOverload
pub fn select_case<'a>(
&'a self,
cx: &mut Cx,
prepared: &PreparedArgs,
) -> Result<SelectedCase<'a>> {
let cases = self.cases.iter().collect::<Vec<_>>();
let (matches, diagnostics) = self.collect_matches(cx, prepared, &cases)?;
self.select_best_case(cx, matches, diagnostics)
}
pub(in crate::functions) fn collect_matches<'a>(
&'a self,
cx: &mut Cx,
prepared: &PreparedArgs,
cases: &[&'a FunctionCase],
) -> Result<(Vec<SelectedCase<'a>>, Vec<Diagnostic>)> {
let mut matches = Vec::new();
let mut diagnostics = Vec::new();
let args = cx.new_list(prepared.values().to_vec())?;
for case in cases {
let matched = case.args.check_value(cx, args.clone())?;
if matched.accepted {
matches.push(SelectedCase {
case,
match_result: matched,
});
} else {
diagnostics.extend(matched.diagnostics);
}
}
Ok((matches, diagnostics))
}
pub(in crate::functions) fn select_best_case<'a>(
&'a self,
cx: &mut Cx,
mut matches: Vec<SelectedCase<'a>>,
mut diagnostics: Vec<Diagnostic>,
) -> Result<SelectedCase<'a>> {
if matches.is_empty() {
diagnostics.insert(
0,
overload_selection_diagnostic(&self.symbol, "no matching overload case"),
);
diagnostics.push(callable_mismatch_diagnostic(
&self.symbol,
"an applicable case",
"rejected arguments",
));
return Err(sim_kernel::Error::NoMatchingOverload {
function: self.id,
diagnostics,
});
}
// Order by priority then additive score as a stable starting point,
// then refine with the proven subshape lattice so a strictly more
// specific overload is preferred over a tying or higher-scoring one.
matches.sort_by(|left, right| {
right
.case
.priority
.cmp(&left.case.priority)
.then_with(|| right.match_result.score.cmp(&left.match_result.score))
});
let mut best = 0usize;
for index in 1..matches.len() {
if self
.compare_cases(cx, &matches[index], &matches[best])?
.is_gt()
{
best = index;
}
}
// The selection is ambiguous only when another accepted case cannot be
// ordered against the best one: equal priority, neither argument shape
// a proven subshape of the other, and an equal additive score.
let mut candidates = vec![matches[best].case.id];
for index in 0..matches.len() {
if index != best
&& self
.compare_cases(cx, &matches[best], &matches[index])?
.is_eq()
{
candidates.push(matches[index].case.id);
}
}
if candidates.len() > 1 {
cx.push_diagnostic(overload_selection_diagnostic(
&self.symbol,
"ambiguous top-ranked overload cases",
));
return Err(sim_kernel::Error::AmbiguousOverload {
function: self.id,
candidates,
});
}
Ok(matches[best].clone())
}
/// Order two accepted cases: priority, then proven subshape specificity,
/// then additive match score.
///
/// `Greater` means `a` is preferred over `b`. A case whose argument shape
/// is a proven subshape of the other's is strictly more specific and wins
/// before the additive score is consulted; `Equal` means the runtime could
/// not order the two and selection is ambiguous.
fn compare_cases(
&self,
cx: &mut Cx,
a: &SelectedCase<'_>,
b: &SelectedCase<'_>,
) -> Result<Ordering> {
match a.case.priority.cmp(&b.case.priority) {
Ordering::Equal => {}
other => return Ok(other),
}
let a_subshape = shape_is_subshape_of(cx, a.case.args.as_ref(), b.case.args.as_ref())?;
let b_subshape = shape_is_subshape_of(cx, b.case.args.as_ref(), a.case.args.as_ref())?;
match (a_subshape, b_subshape) {
(true, false) => return Ok(Ordering::Greater),
(false, true) => return Ok(Ordering::Less),
_ => {}
}
Ok(a.match_result.score.cmp(&b.match_result.score))
}
}