Skip to main content

rustc_hir_typeck/fn_ctxt/
arg_matrix.rs

1use core::cmp::Ordering;
2use std::cmp;
3
4use rustc_index::IndexVec;
5use rustc_middle::ty::error::TypeError;
6
7impl ::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({})"]
10    pub(crate) struct ExpectedIdx {}
11}
12
13impl ::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({})"]
16    pub(crate) struct ProvidedIdx {}
17}
18
19impl ExpectedIdx {
20    pub(crate) fn to_provided_idx(self) -> ProvidedIdx {
21        ProvidedIdx::from_usize(self.as_usize())
22    }
23}
24
25impl ProvidedIdx {
26    pub(crate) fn to_expected_idx(self) -> ExpectedIdx {
27        ExpectedIdx::from_u32(self.as_u32())
28    }
29}
30
31// 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
35    Invalid(usize),
36    /// There is a missing input
37    Missing(usize),
38    /// There's a superfluous argument
39    Extra(usize),
40    /// Two arguments should be swapped
41    Swap(usize, usize),
42    /// Several arguments should be reordered
43    Permutation(Vec<Option<usize>>),
44}
45
46#[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}
51
52/// 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
56    Invalid(ProvidedIdx, ExpectedIdx, Compatibility<'tcx>),
57    /// There is a missing input
58    Missing(ExpectedIdx),
59    /// There's a superfluous argument
60    Extra(ProvidedIdx),
61    /// Two arguments should be swapped
62    Swap(ProvidedIdx, ProvidedIdx, ExpectedIdx, ExpectedIdx),
63    /// Several arguments should be reordered
64    Permutation(Vec<(ExpectedIdx, ProvidedIdx)>),
65}
66
67impl Ord for Error<'_> {
68    fn cmp(&self, other: &Self) -> Ordering {
69        let key = |error: &Error<'_>| -> usize {
70            match error {
71                Error::Invalid(..) => 0,
72                Error::Extra(_) => 1,
73                Error::Missing(_) => 2,
74                Error::Swap(..) => 3,
75                Error::Permutation(..) => 4,
76            }
77        };
78        match (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}
88
89impl PartialOrd for Error<'_> {
90    fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
91        Some(self.cmp(other))
92    }
93}
94
95pub(crate) struct ArgMatrix<'tcx> {
96    /// Maps the indices in the `compatibility_matrix` rows to the indices of
97    /// the *user provided* inputs
98    provided_indices: Vec<ProvidedIdx>,
99    /// Maps the indices in the `compatibility_matrix` columns to the indices
100    /// of the *expected* args
101    expected_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
105    compatibility_matrix: Vec<Vec<Compatibility<'tcx>>>,
106}
107
108impl<'tcx> ArgMatrix<'tcx> {
109    pub(crate) fn new<F: FnMut(ProvidedIdx, ExpectedIdx) -> Compatibility<'tcx>>(
110        provided_count: usize,
111        expected_input_count: usize,
112        mut is_compatible: F,
113    ) -> Self {
114        let 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();
121        ArgMatrix {
122            provided_indices: (0..provided_count).map(ProvidedIdx::from_usize).collect(),
123            expected_indices: (0..expected_input_count).map(ExpectedIdx::from_usize).collect(),
124            compatibility_matrix,
125        }
126    }
127
128    /// Remove a given input from consideration
129    fn eliminate_provided(&mut self, idx: usize) {
130        self.provided_indices.remove(idx);
131        self.compatibility_matrix.remove(idx);
132    }
133
134    /// Remove a given argument from consideration
135    fn eliminate_expected(&mut self, idx: usize) {
136        self.expected_indices.remove(idx);
137        for row in &mut self.compatibility_matrix {
138            row.remove(idx);
139        }
140    }
141
142    /// "satisfy" an input with a given arg, removing both from consideration
143    fn satisfy_input(&mut self, provided_idx: usize, expected_idx: usize) {
144        self.eliminate_provided(provided_idx);
145        self.eliminate_expected(expected_idx);
146    }
147
148    // Returns a `Vec` of (user input, expected arg) of matched arguments. These
149    // are inputs on the remaining diagonal that match.
150    fn eliminate_satisfied(&mut self) -> Vec<(ProvidedIdx, ExpectedIdx)> {
151        let num_args = cmp::min(self.provided_indices.len(), self.expected_indices.len());
152        let mut eliminated = ::alloc::vec::Vec::new()vec![];
153        for i in (0..num_args).rev() {
154            if #[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]));
156                self.satisfy_input(i, i);
157            }
158        }
159        eliminated
160    }
161
162    // Find some issue in the compatibility matrix
163    fn find_issue(&self) -> Option<Issue> {
164        let mat = &self.compatibility_matrix;
165        let ai = &self.expected_indices;
166        let ii = &self.provided_indices;
167
168        // Issue: 100478, when we end the iteration,
169        // `next_unmatched_idx` will point to the index of the first unmatched
170        let mut next_unmatched_idx = 0;
171        for i in 0..cmp::max(ai.len(), ii.len()) {
172            // If we eliminate the last row, any left-over arguments are considered missing
173            if i >= mat.len() {
174                return Some(Issue::Missing(next_unmatched_idx));
175            }
176            // If we eliminate the last column, any left-over inputs are extra
177            if mat[i].is_empty() {
178                return Some(Issue::Extra(next_unmatched_idx));
179            }
180
181            // Make sure we don't pass the bounds of our matrix
182            let is_arg = i < ai.len();
183            let is_input = i < ii.len();
184            if 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
186                next_unmatched_idx += 1;
187                continue;
188            }
189
190            // If this argument can satisfy some input, then this argument is satisfiable
191            let 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 {
194                true
195            };
196            // If this input can be satisfied by some argument, then this input is useful
197            let 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 {
200                true
201            };
202
203            match (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.
217                    for j in 0..cmp::min(ai.len(), ii.len()) {
218                        if i == j || #[allow(non_exhaustive_omitted_patterns)] match mat[j][j] {
    Compatibility::Compatible => true,
    _ => false,
}matches!(mat[j][j], Compatibility::Compatible) {
219                            continue;
220                        }
221                        if #[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                        {
224                            return Some(Issue::Swap(i, j));
225                        }
226                    }
227                }
228                _ => {
229                    continue;
230                }
231            }
232        }
233
234        // 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
241        let mut permutation: Vec<Option<Option<usize>>> = ::alloc::vec::from_elem(None, mat.len())vec![None; mat.len()];
242        let mut permutation_found = false;
243        for i in 0..mat.len() {
244            if permutation[i].is_some() {
245                // We've already decided whether this argument is or is not in a loop
246                continue;
247            }
248
249            let mut stack = ::alloc::vec::Vec::new()vec![];
250            let mut j = i;
251            let mut last = i;
252            let mut is_cycle = true;
253            loop {
254                stack.push(j);
255                // Look for params this one could slot into
256                let compat: Vec<_> =
257                    mat[j]
258                        .iter()
259                        .enumerate()
260                        .filter_map(|(i, c)| {
261                            if #[allow(non_exhaustive_omitted_patterns)] match c {
    Compatibility::Compatible => true,
    _ => false,
}matches!(c, Compatibility::Compatible) { Some(i) } else { None }
262                        })
263                        .collect();
264                if compat.len() < 1 {
265                    // try to find a cycle even when this could go into multiple slots, see #101097
266                    is_cycle = false;
267                    break;
268                }
269                j = compat[0];
270                if stack.contains(&j) {
271                    last = j;
272                    break;
273                }
274            }
275            if 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
278                is_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
283            permutation_found = is_cycle;
284            while let Some(x) = stack.pop() {
285                if is_cycle {
286                    permutation[x] = Some(Some(j));
287                    j = x;
288                    if j == last {
289                        // From here on out, we're a tail leading into a cycle,
290                        // not the cycle itself
291                        is_cycle = false;
292                    }
293                } else {
294                    // Some(None) ensures we save time by skipping this argument again
295                    permutation[x] = Some(None);
296                }
297            }
298        }
299
300        if permutation_found {
301            // Map unwrap to remove the first layer of Some
302            let final_permutation: Vec<Option<usize>> =
303                permutation.into_iter().map(|x| x.unwrap()).collect();
304            return Some(Issue::Permutation(final_permutation));
305        }
306        None
307    }
308
309    // 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.
317    pub(crate) fn find_errors(
318        mut self,
319    ) -> (Vec<Error<'tcx>>, IndexVec<ExpectedIdx, Option<ProvidedIdx>>) {
320        let provided_arg_count = self.provided_indices.len();
321
322        let mut errors: Vec<Error<'tcx>> = ::alloc::vec::Vec::new()vec![];
323        // For each expected argument, the matched *actual* input
324        let mut matched_inputs: IndexVec<ExpectedIdx, Option<ProvidedIdx>> =
325            IndexVec::from_elem_n(None, self.expected_indices.len());
326
327        // 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.
336        for (provided, expected) in self.eliminate_satisfied() {
337            matched_inputs[expected] = Some(provided);
338        }
339
340        while !self.provided_indices.is_empty() || !self.expected_indices.is_empty() {
341            let res = self.find_issue();
342            match res {
343                Some(Issue::Invalid(idx)) => {
344                    let compatibility = self.compatibility_matrix[idx][idx].clone();
345                    let input_idx = self.provided_indices[idx];
346                    let arg_idx = self.expected_indices[idx];
347                    self.satisfy_input(idx, idx);
348                    errors.push(Error::Invalid(input_idx, arg_idx, compatibility));
349                }
350                Some(Issue::Extra(idx)) => {
351                    let input_idx = self.provided_indices[idx];
352                    self.eliminate_provided(idx);
353                    errors.push(Error::Extra(input_idx));
354                }
355                Some(Issue::Missing(idx)) => {
356                    let arg_idx = self.expected_indices[idx];
357                    self.eliminate_expected(idx);
358                    errors.push(Error::Missing(arg_idx));
359                }
360                Some(Issue::Swap(idx, other)) => {
361                    let input_idx = self.provided_indices[idx];
362                    let other_input_idx = self.provided_indices[other];
363                    let arg_idx = self.expected_indices[idx];
364                    let other_arg_idx = self.expected_indices[other];
365                    let (min, max) = (cmp::min(idx, other), cmp::max(idx, other));
366                    self.satisfy_input(min, max);
367                    // Subtract 1 because we already removed the "min" row
368                    self.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                }
373                Some(Issue::Permutation(args)) => {
374                    let mut idxs: Vec<usize> = args.iter().filter_map(|&a| a).collect();
375
376                    let mut real_idxs: IndexVec<ProvidedIdx, Option<(ExpectedIdx, ProvidedIdx)>> =
377                        IndexVec::from_elem_n(None, provided_arg_count);
378                    for (src, dst) in
379                        args.iter().enumerate().filter_map(|(src, dst)| dst.map(|dst| (src, dst)))
380                    {
381                        let src_input_idx = self.provided_indices[src];
382                        let dst_input_idx = self.provided_indices[dst];
383                        let 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();
389                    for i in idxs {
390                        self.satisfy_input(i, i);
391                    }
392                    errors.push(Error::Permutation(real_idxs.into_iter().flatten().collect()));
393                }
394                None => {
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
397                    let eliminated = self.eliminate_satisfied();
398                    if !!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");
399                    for (inp, arg) in eliminated {
400                        matched_inputs[arg] = Some(inp);
401                    }
402                }
403            };
404        }
405
406        // sort errors with same type by the order they appear in the source
407        // so that suggestion will be handled properly, see #112507
408        errors.sort();
409        (errors, matched_inputs)
410    }
411}