1use core::cmp::Ordering;
2use std::cmp;
34use rustc_index::IndexVec;
5use rustc_middle::ty::error::TypeError;
67impl ::std::fmt::Debug for ExpectedIdx {
fn fmt(&self, fmt: &mut ::std::fmt::Formatter<'_>) -> ::std::fmt::Result {
fmt.write_fmt(format_args!("ExpectedIdx({0})", self.as_u32()))
}
}rustc_index::newtype_index! {
8#[orderable]
9 #[debug_format = "ExpectedIdx({})"]
10pub(crate) struct ExpectedIdx {}
11}1213impl ::std::fmt::Debug for ProvidedIdx {
fn fmt(&self, fmt: &mut ::std::fmt::Formatter<'_>) -> ::std::fmt::Result {
fmt.write_fmt(format_args!("ProvidedIdx({0})", self.as_u32()))
}
}rustc_index::newtype_index! {
14#[orderable]
15 #[debug_format = "ProvidedIdx({})"]
16pub(crate) struct ProvidedIdx {}
17}1819impl ExpectedIdx {
20pub(crate) fn to_provided_idx(self) -> ProvidedIdx {
21ProvidedIdx::from_usize(self.as_usize())
22 }
23}
2425impl ProvidedIdx {
26pub(crate) fn to_expected_idx(self) -> ExpectedIdx {
27ExpectedIdx::from_u32(self.as_u32())
28 }
29}
3031// An issue that might be found in the compatibility matrix
32#[derive(#[automatically_derived]
impl ::core::fmt::Debug for Issue {
#[inline]
fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result {
match self {
Issue::Invalid(__self_0) =>
::core::fmt::Formatter::debug_tuple_field1_finish(f,
"Invalid", &__self_0),
Issue::Missing(__self_0) =>
::core::fmt::Formatter::debug_tuple_field1_finish(f,
"Missing", &__self_0),
Issue::Extra(__self_0) =>
::core::fmt::Formatter::debug_tuple_field1_finish(f, "Extra",
&__self_0),
Issue::Swap(__self_0, __self_1) =>
::core::fmt::Formatter::debug_tuple_field2_finish(f, "Swap",
__self_0, &__self_1),
Issue::Permutation(__self_0) =>
::core::fmt::Formatter::debug_tuple_field1_finish(f,
"Permutation", &__self_0),
}
}
}Debug)]
33enum Issue {
34/// The given argument is the invalid type for the input
35Invalid(usize),
36/// There is a missing input
37Missing(usize),
38/// There's a superfluous argument
39Extra(usize),
40/// Two arguments should be swapped
41Swap(usize, usize),
42/// Several arguments should be reordered
43Permutation(Vec<Option<usize>>),
44}
4546#[derive(#[automatically_derived]
impl<'tcx> ::core::clone::Clone for Compatibility<'tcx> {
#[inline]
fn clone(&self) -> Compatibility<'tcx> {
match self {
Compatibility::Compatible => Compatibility::Compatible,
Compatibility::Incompatible(__self_0) =>
Compatibility::Incompatible(::core::clone::Clone::clone(__self_0)),
}
}
}Clone, #[automatically_derived]
impl<'tcx> ::core::fmt::Debug for Compatibility<'tcx> {
#[inline]
fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result {
match self {
Compatibility::Compatible =>
::core::fmt::Formatter::write_str(f, "Compatible"),
Compatibility::Incompatible(__self_0) =>
::core::fmt::Formatter::debug_tuple_field1_finish(f,
"Incompatible", &__self_0),
}
}
}Debug, #[automatically_derived]
impl<'tcx> ::core::cmp::Eq for Compatibility<'tcx> {
#[inline]
#[doc(hidden)]
#[coverage(off)]
fn assert_receiver_is_total_eq(&self) {
let _: ::core::cmp::AssertParamIsEq<Option<TypeError<'tcx>>>;
}
}Eq, #[automatically_derived]
impl<'tcx> ::core::cmp::PartialEq for Compatibility<'tcx> {
#[inline]
fn eq(&self, other: &Compatibility<'tcx>) -> bool {
let __self_discr = ::core::intrinsics::discriminant_value(self);
let __arg1_discr = ::core::intrinsics::discriminant_value(other);
__self_discr == __arg1_discr &&
match (self, other) {
(Compatibility::Incompatible(__self_0),
Compatibility::Incompatible(__arg1_0)) =>
__self_0 == __arg1_0,
_ => true,
}
}
}PartialEq)]
47pub(crate) enum Compatibility<'tcx> {
48 Compatible,
49 Incompatible(Option<TypeError<'tcx>>),
50}
5152/// Similar to `Issue`, but contains some extra information
53#[derive(#[automatically_derived]
impl<'tcx> ::core::fmt::Debug for Error<'tcx> {
#[inline]
fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result {
match self {
Error::Invalid(__self_0, __self_1, __self_2) =>
::core::fmt::Formatter::debug_tuple_field3_finish(f,
"Invalid", __self_0, __self_1, &__self_2),
Error::Missing(__self_0) =>
::core::fmt::Formatter::debug_tuple_field1_finish(f,
"Missing", &__self_0),
Error::Extra(__self_0) =>
::core::fmt::Formatter::debug_tuple_field1_finish(f, "Extra",
&__self_0),
Error::Swap(__self_0, __self_1, __self_2, __self_3) =>
::core::fmt::Formatter::debug_tuple_field4_finish(f, "Swap",
__self_0, __self_1, __self_2, &__self_3),
Error::Permutation(__self_0) =>
::core::fmt::Formatter::debug_tuple_field1_finish(f,
"Permutation", &__self_0),
}
}
}Debug, #[automatically_derived]
impl<'tcx> ::core::cmp::PartialEq for Error<'tcx> {
#[inline]
fn eq(&self, other: &Error<'tcx>) -> bool {
let __self_discr = ::core::intrinsics::discriminant_value(self);
let __arg1_discr = ::core::intrinsics::discriminant_value(other);
__self_discr == __arg1_discr &&
match (self, other) {
(Error::Invalid(__self_0, __self_1, __self_2),
Error::Invalid(__arg1_0, __arg1_1, __arg1_2)) =>
__self_0 == __arg1_0 && __self_1 == __arg1_1 &&
__self_2 == __arg1_2,
(Error::Missing(__self_0), Error::Missing(__arg1_0)) =>
__self_0 == __arg1_0,
(Error::Extra(__self_0), Error::Extra(__arg1_0)) =>
__self_0 == __arg1_0,
(Error::Swap(__self_0, __self_1, __self_2, __self_3),
Error::Swap(__arg1_0, __arg1_1, __arg1_2, __arg1_3)) =>
__self_0 == __arg1_0 && __self_1 == __arg1_1 &&
__self_2 == __arg1_2 && __self_3 == __arg1_3,
(Error::Permutation(__self_0), Error::Permutation(__arg1_0))
=> __self_0 == __arg1_0,
_ => unsafe { ::core::intrinsics::unreachable() }
}
}
}PartialEq, #[automatically_derived]
impl<'tcx> ::core::cmp::Eq for Error<'tcx> {
#[inline]
#[doc(hidden)]
#[coverage(off)]
fn assert_receiver_is_total_eq(&self) {
let _: ::core::cmp::AssertParamIsEq<ProvidedIdx>;
let _: ::core::cmp::AssertParamIsEq<ExpectedIdx>;
let _: ::core::cmp::AssertParamIsEq<Compatibility<'tcx>>;
let _: ::core::cmp::AssertParamIsEq<Vec<(ExpectedIdx, ProvidedIdx)>>;
}
}Eq)]
54pub(crate) enum Error<'tcx> {
55/// The provided argument is the invalid type for the expected input
56Invalid(ProvidedIdx, ExpectedIdx, Compatibility<'tcx>),
57/// There is a missing input
58Missing(ExpectedIdx),
59/// There's a superfluous argument
60Extra(ProvidedIdx),
61/// Two arguments should be swapped
62Swap(ProvidedIdx, ProvidedIdx, ExpectedIdx, ExpectedIdx),
63/// Several arguments should be reordered
64Permutation(Vec<(ExpectedIdx, ProvidedIdx)>),
65}
6667impl Ordfor Error<'_> {
68fn cmp(&self, other: &Self) -> Ordering {
69let key = |error: &Error<'_>| -> usize {
70match error {
71 Error::Invalid(..) => 0,
72 Error::Extra(_) => 1,
73 Error::Missing(_) => 2,
74 Error::Swap(..) => 3,
75 Error::Permutation(..) => 4,
76 }
77 };
78match (self, other) {
79 (Error::Invalid(a, _, _), Error::Invalid(b, _, _)) => a.cmp(b),
80 (Error::Extra(a), Error::Extra(b)) => a.cmp(b),
81 (Error::Missing(a), Error::Missing(b)) => a.cmp(b),
82 (Error::Swap(a, b, ..), Error::Swap(c, d, ..)) => a.cmp(c).then(b.cmp(d)),
83 (Error::Permutation(a), Error::Permutation(b)) => a.cmp(b),
84_ => key(self).cmp(&key(other)),
85 }
86 }
87}
8889impl PartialOrdfor Error<'_> {
90fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
91Some(self.cmp(other))
92 }
93}
9495pub(crate) struct ArgMatrix<'tcx> {
96/// Maps the indices in the `compatibility_matrix` rows to the indices of
97 /// the *user provided* inputs
98provided_indices: Vec<ProvidedIdx>,
99/// Maps the indices in the `compatibility_matrix` columns to the indices
100 /// of the *expected* args
101expected_indices: Vec<ExpectedIdx>,
102/// The first dimension (rows) are the remaining user provided inputs to
103 /// match and the second dimension (cols) are the remaining expected args
104 /// to match
105compatibility_matrix: Vec<Vec<Compatibility<'tcx>>>,
106}
107108impl<'tcx> ArgMatrix<'tcx> {
109pub(crate) fn new<F: FnMut(ProvidedIdx, ExpectedIdx) -> Compatibility<'tcx>>(
110 provided_count: usize,
111 expected_input_count: usize,
112mut is_compatible: F,
113 ) -> Self {
114let compatibility_matrix = (0..provided_count)
115 .map(|i| {
116 (0..expected_input_count)
117 .map(|j| is_compatible(ProvidedIdx::from_usize(i), ExpectedIdx::from_usize(j)))
118 .collect()
119 })
120 .collect();
121ArgMatrix {
122 provided_indices: (0..provided_count).map(ProvidedIdx::from_usize).collect(),
123 expected_indices: (0..expected_input_count).map(ExpectedIdx::from_usize).collect(),
124compatibility_matrix,
125 }
126 }
127128/// Remove a given input from consideration
129fn eliminate_provided(&mut self, idx: usize) {
130self.provided_indices.remove(idx);
131self.compatibility_matrix.remove(idx);
132 }
133134/// Remove a given argument from consideration
135fn eliminate_expected(&mut self, idx: usize) {
136self.expected_indices.remove(idx);
137for row in &mut self.compatibility_matrix {
138 row.remove(idx);
139 }
140 }
141142/// "satisfy" an input with a given arg, removing both from consideration
143fn satisfy_input(&mut self, provided_idx: usize, expected_idx: usize) {
144self.eliminate_provided(provided_idx);
145self.eliminate_expected(expected_idx);
146 }
147148// Returns a `Vec` of (user input, expected arg) of matched arguments. These
149 // are inputs on the remaining diagonal that match.
150fn eliminate_satisfied(&mut self) -> Vec<(ProvidedIdx, ExpectedIdx)> {
151let num_args = cmp::min(self.provided_indices.len(), self.expected_indices.len());
152let mut eliminated = ::alloc::vec::Vec::new()vec![];
153for i in (0..num_args).rev() {
154if #[allow(non_exhaustive_omitted_patterns)] match self.compatibility_matrix[i][i]
{
Compatibility::Compatible => true,
_ => false,
}matches!(self.compatibility_matrix[i][i], Compatibility::Compatible) {
155 eliminated.push((self.provided_indices[i], self.expected_indices[i]));
156self.satisfy_input(i, i);
157 }
158 }
159eliminated160 }
161162// Find some issue in the compatibility matrix
163fn find_issue(&self) -> Option<Issue> {
164let mat = &self.compatibility_matrix;
165let ai = &self.expected_indices;
166let ii = &self.provided_indices;
167168// Issue: 100478, when we end the iteration,
169 // `next_unmatched_idx` will point to the index of the first unmatched
170let mut next_unmatched_idx = 0;
171for i in 0..cmp::max(ai.len(), ii.len()) {
172// If we eliminate the last row, any left-over arguments are considered missing
173if i >= mat.len() {
174return Some(Issue::Missing(next_unmatched_idx));
175 }
176// If we eliminate the last column, any left-over inputs are extra
177if mat[i].is_empty() {
178return Some(Issue::Extra(next_unmatched_idx));
179 }
180181// Make sure we don't pass the bounds of our matrix
182let is_arg = i < ai.len();
183let is_input = i < ii.len();
184if is_arg && is_input && #[allow(non_exhaustive_omitted_patterns)] match mat[i][i] {
Compatibility::Compatible => true,
_ => false,
}matches!(mat[i][i], Compatibility::Compatible) {
185// This is a satisfied input, so move along
186next_unmatched_idx += 1;
187continue;
188 }
189190// If this argument can satisfy some input, then this argument is satisfiable
191let unsatisfiable = if is_arg {
192 !mat.iter().take(ii.len()).any(|c| #[allow(non_exhaustive_omitted_patterns)] match c[i] {
Compatibility::Compatible => true,
_ => false,
}matches!(c[i], Compatibility::Compatible))
193 } else {
194true
195};
196// If this input can be satisfied by some argument, then this input is useful
197let useless = if is_input {
198 !mat[i].iter().take(ai.len()).any(|c| #[allow(non_exhaustive_omitted_patterns)] match c {
Compatibility::Compatible => true,
_ => false,
}matches!(c, Compatibility::Compatible))
199 } else {
200true
201};
202203match (is_input, is_arg, useless, unsatisfiable) {
204// If an argument is unsatisfied, and the input in its position is useless
205 // then the most likely explanation is that we just got the types wrong
206(true, true, true, true) => return Some(Issue::Invalid(i)),
207// Otherwise, if an input is useless then indicate that this is an extra input
208(true, _, true, _) => return Some(Issue::Extra(i)),
209// Otherwise, if an argument is unsatisfiable, indicate that it's missing
210(_, true, _, true) => return Some(Issue::Missing(i)),
211 (true, true, _, _) => {
212// The argument isn't useless, and the input isn't unsatisfied,
213 // so look for a parameter we might swap it with
214 // We look for swaps explicitly, instead of just falling back on permutations
215 // so that cases like (A,B,C,D) given (B,A,D,C) show up as two swaps,
216 // instead of a large permutation of 4 elements.
217for j in 0..cmp::min(ai.len(), ii.len()) {
218if i == j || #[allow(non_exhaustive_omitted_patterns)] match mat[j][j] {
Compatibility::Compatible => true,
_ => false,
}matches!(mat[j][j], Compatibility::Compatible) {
219continue;
220 }
221if #[allow(non_exhaustive_omitted_patterns)] match mat[i][j] {
Compatibility::Compatible => true,
_ => false,
}matches!(mat[i][j], Compatibility::Compatible)222 && #[allow(non_exhaustive_omitted_patterns)] match mat[j][i] {
Compatibility::Compatible => true,
_ => false,
}matches!(mat[j][i], Compatibility::Compatible)223 {
224return Some(Issue::Swap(i, j));
225 }
226 }
227 }
228_ => {
229continue;
230 }
231 }
232 }
233234// We didn't find any of the individual issues above, but
235 // there might be a larger permutation of parameters, so we now check for that
236 // by checking for cycles
237 // We use a double option at position i in this vec to represent:
238 // - None: We haven't computed anything about this argument yet
239 // - Some(None): This argument definitely doesn't participate in a cycle
240 // - Some(Some(x)): the i-th argument could permute to the x-th position
241let mut permutation: Vec<Option<Option<usize>>> = ::alloc::vec::from_elem(None, mat.len())vec![None; mat.len()];
242let mut permutation_found = false;
243for i in 0..mat.len() {
244if permutation[i].is_some() {
245// We've already decided whether this argument is or is not in a loop
246continue;
247 }
248249let mut stack = ::alloc::vec::Vec::new()vec![];
250let mut j = i;
251let mut last = i;
252let mut is_cycle = true;
253loop {
254 stack.push(j);
255// Look for params this one could slot into
256let compat: Vec<_> =
257 mat[j]
258 .iter()
259 .enumerate()
260 .filter_map(|(i, c)| {
261if #[allow(non_exhaustive_omitted_patterns)] match c {
Compatibility::Compatible => true,
_ => false,
}matches!(c, Compatibility::Compatible) { Some(i) } else { None }
262 })
263 .collect();
264if compat.len() < 1 {
265// try to find a cycle even when this could go into multiple slots, see #101097
266is_cycle = false;
267break;
268 }
269 j = compat[0];
270if stack.contains(&j) {
271 last = j;
272break;
273 }
274 }
275if stack.len() <= 2 {
276// If we encounter a cycle of 1 or 2 elements, we'll let the
277 // "satisfy" and "swap" code above handle those
278is_cycle = false;
279 }
280// We've built up some chain, some of which might be a cycle
281 // ex: [1,2,3,4]; last = 2; j = 2;
282 // So, we want to mark 4, 3, and 2 as part of a permutation
283permutation_found = is_cycle;
284while let Some(x) = stack.pop() {
285if is_cycle {
286 permutation[x] = Some(Some(j));
287 j = x;
288if j == last {
289// From here on out, we're a tail leading into a cycle,
290 // not the cycle itself
291is_cycle = false;
292 }
293 } else {
294// Some(None) ensures we save time by skipping this argument again
295permutation[x] = Some(None);
296 }
297 }
298 }
299300if permutation_found {
301// Map unwrap to remove the first layer of Some
302let final_permutation: Vec<Option<usize>> =
303permutation.into_iter().map(|x| x.unwrap()).collect();
304return Some(Issue::Permutation(final_permutation));
305 }
306None307 }
308309// Obviously, detecting exact user intention is impossible, so the goal here is to
310 // come up with as likely of a story as we can to be helpful.
311 //
312 // We'll iteratively removed "satisfied" input/argument pairs,
313 // then check for the cases above, until we've eliminated the entire grid
314 //
315 // We'll want to know which arguments and inputs these rows and columns correspond to
316 // even after we delete them.
317pub(crate) fn find_errors(
318mut self,
319 ) -> (Vec<Error<'tcx>>, IndexVec<ExpectedIdx, Option<ProvidedIdx>>) {
320let provided_arg_count = self.provided_indices.len();
321322let mut errors: Vec<Error<'tcx>> = ::alloc::vec::Vec::new()vec![];
323// For each expected argument, the matched *actual* input
324let mut matched_inputs: IndexVec<ExpectedIdx, Option<ProvidedIdx>> =
325IndexVec::from_elem_n(None, self.expected_indices.len());
326327// Before we start looking for issues, eliminate any arguments that are already satisfied,
328 // so that an argument which is already spoken for by the input it's in doesn't
329 // spill over into another similarly typed input
330 // ex:
331 // fn some_func(_a: i32, _b: i32) {}
332 // some_func(1, "");
333 // Without this elimination, the first argument causes the second argument
334 // to show up as both a missing input and extra argument, rather than
335 // just an invalid type.
336for (provided, expected) in self.eliminate_satisfied() {
337 matched_inputs[expected] = Some(provided);
338 }
339340while !self.provided_indices.is_empty() || !self.expected_indices.is_empty() {
341let res = self.find_issue();
342match res {
343Some(Issue::Invalid(idx)) => {
344let compatibility = self.compatibility_matrix[idx][idx].clone();
345let input_idx = self.provided_indices[idx];
346let arg_idx = self.expected_indices[idx];
347self.satisfy_input(idx, idx);
348 errors.push(Error::Invalid(input_idx, arg_idx, compatibility));
349 }
350Some(Issue::Extra(idx)) => {
351let input_idx = self.provided_indices[idx];
352self.eliminate_provided(idx);
353 errors.push(Error::Extra(input_idx));
354 }
355Some(Issue::Missing(idx)) => {
356let arg_idx = self.expected_indices[idx];
357self.eliminate_expected(idx);
358 errors.push(Error::Missing(arg_idx));
359 }
360Some(Issue::Swap(idx, other)) => {
361let input_idx = self.provided_indices[idx];
362let other_input_idx = self.provided_indices[other];
363let arg_idx = self.expected_indices[idx];
364let other_arg_idx = self.expected_indices[other];
365let (min, max) = (cmp::min(idx, other), cmp::max(idx, other));
366self.satisfy_input(min, max);
367// Subtract 1 because we already removed the "min" row
368self.satisfy_input(max - 1, min);
369 errors.push(Error::Swap(input_idx, other_input_idx, arg_idx, other_arg_idx));
370 matched_inputs[other_arg_idx] = Some(input_idx);
371 matched_inputs[arg_idx] = Some(other_input_idx);
372 }
373Some(Issue::Permutation(args)) => {
374let mut idxs: Vec<usize> = args.iter().filter_map(|&a| a).collect();
375376let mut real_idxs: IndexVec<ProvidedIdx, Option<(ExpectedIdx, ProvidedIdx)>> =
377 IndexVec::from_elem_n(None, provided_arg_count);
378for (src, dst) in
379args.iter().enumerate().filter_map(|(src, dst)| dst.map(|dst| (src, dst)))
380 {
381let src_input_idx = self.provided_indices[src];
382let dst_input_idx = self.provided_indices[dst];
383let dest_arg_idx = self.expected_indices[dst];
384 real_idxs[src_input_idx] = Some((dest_arg_idx, dst_input_idx));
385 matched_inputs[dest_arg_idx] = Some(src_input_idx);
386 }
387 idxs.sort();
388 idxs.reverse();
389for i in idxs {
390self.satisfy_input(i, i);
391 }
392 errors.push(Error::Permutation(real_idxs.into_iter().flatten().collect()));
393 }
394None => {
395// We didn't find any issues, so we need to push the algorithm forward
396 // First, eliminate any arguments that currently satisfy their inputs
397let eliminated = self.eliminate_satisfied();
398if !!eliminated.is_empty() {
{
::core::panicking::panic_fmt(format_args!("didn\'t eliminated any indice in this round"));
}
};assert!(!eliminated.is_empty(), "didn't eliminated any indice in this round");
399for (inp, arg) in eliminated {
400 matched_inputs[arg] = Some(inp);
401 }
402 }
403 };
404 }
405406// sort errors with same type by the order they appear in the source
407 // so that suggestion will be handled properly, see #112507
408errors.sort();
409 (errors, matched_inputs)
410 }
411}