diff options
Diffstat (limited to 'compiler/rustc_typeck/src/check/fn_ctxt/arg_matrix.rs')
-rw-r--r-- | compiler/rustc_typeck/src/check/fn_ctxt/arg_matrix.rs | 376 |
1 files changed, 0 insertions, 376 deletions
diff --git a/compiler/rustc_typeck/src/check/fn_ctxt/arg_matrix.rs b/compiler/rustc_typeck/src/check/fn_ctxt/arg_matrix.rs deleted file mode 100644 index 7602f2550..000000000 --- a/compiler/rustc_typeck/src/check/fn_ctxt/arg_matrix.rs +++ /dev/null @@ -1,376 +0,0 @@ -use std::cmp; - -use rustc_index::vec::IndexVec; -use rustc_middle::ty::error::TypeError; - -rustc_index::newtype_index! { - pub(crate) struct ExpectedIdx { - DEBUG_FORMAT = "ExpectedIdx({})", - } -} - -rustc_index::newtype_index! { - pub(crate) struct ProvidedIdx { - DEBUG_FORMAT = "ProvidedIdx({})", - } -} - -impl ExpectedIdx { - pub fn to_provided_idx(self) -> ProvidedIdx { - ProvidedIdx::from_usize(self.as_usize()) - } -} - -// An issue that might be found in the compatibility matrix -#[derive(Debug)] -enum Issue { - /// The given argument is the invalid type for the input - Invalid(usize), - /// There is a missing input - Missing(usize), - /// There's a superfluous argument - Extra(usize), - /// Two arguments should be swapped - Swap(usize, usize), - /// Several arguments should be reordered - Permutation(Vec<Option<usize>>), -} - -#[derive(Clone, Debug)] -pub(crate) enum Compatibility<'tcx> { - Compatible, - Incompatible(Option<TypeError<'tcx>>), -} - -/// Similar to `Issue`, but contains some extra information -#[derive(Debug)] -pub(crate) enum Error<'tcx> { - /// The provided argument is the invalid type for the expected input - Invalid(ProvidedIdx, ExpectedIdx, Compatibility<'tcx>), - /// There is a missing input - Missing(ExpectedIdx), - /// There's a superfluous argument - Extra(ProvidedIdx), - /// Two arguments should be swapped - Swap(ProvidedIdx, ProvidedIdx, ExpectedIdx, ExpectedIdx), - /// Several arguments should be reordered - Permutation(Vec<(ExpectedIdx, ProvidedIdx)>), -} - -pub(crate) struct ArgMatrix<'tcx> { - /// Maps the indices in the `compatibility_matrix` rows to the indices of - /// the *user provided* inputs - provided_indices: Vec<ProvidedIdx>, - /// Maps the indices in the `compatibility_matrix` columns to the indices - /// of the *expected* args - expected_indices: Vec<ExpectedIdx>, - /// The first dimension (rows) are the remaining user provided inputs to - /// match and the second dimension (cols) are the remaining expected args - /// to match - compatibility_matrix: Vec<Vec<Compatibility<'tcx>>>, -} - -impl<'tcx> ArgMatrix<'tcx> { - pub(crate) fn new<F: FnMut(ProvidedIdx, ExpectedIdx) -> Compatibility<'tcx>>( - provided_count: usize, - expected_input_count: usize, - mut is_compatible: F, - ) -> Self { - let compatibility_matrix = (0..provided_count) - .map(|i| { - (0..expected_input_count) - .map(|j| is_compatible(ProvidedIdx::from_usize(i), ExpectedIdx::from_usize(j))) - .collect() - }) - .collect(); - ArgMatrix { - provided_indices: (0..provided_count).map(ProvidedIdx::from_usize).collect(), - expected_indices: (0..expected_input_count).map(ExpectedIdx::from_usize).collect(), - compatibility_matrix, - } - } - - /// Remove a given input from consideration - fn eliminate_provided(&mut self, idx: usize) { - self.provided_indices.remove(idx); - self.compatibility_matrix.remove(idx); - } - - /// Remove a given argument from consideration - fn eliminate_expected(&mut self, idx: usize) { - self.expected_indices.remove(idx); - for row in &mut self.compatibility_matrix { - row.remove(idx); - } - } - - /// "satisfy" an input with a given arg, removing both from consideration - fn satisfy_input(&mut self, provided_idx: usize, expected_idx: usize) { - self.eliminate_provided(provided_idx); - self.eliminate_expected(expected_idx); - } - - // Returns a `Vec` of (user input, expected arg) of matched arguments. These - // are inputs on the remaining diagonal that match. - fn eliminate_satisfied(&mut self) -> Vec<(ProvidedIdx, ExpectedIdx)> { - let num_args = cmp::min(self.provided_indices.len(), self.expected_indices.len()); - let mut eliminated = vec![]; - for i in (0..num_args).rev() { - if matches!(self.compatibility_matrix[i][i], Compatibility::Compatible) { - eliminated.push((self.provided_indices[i], self.expected_indices[i])); - self.satisfy_input(i, i); - } - } - eliminated - } - - // Find some issue in the compatibility matrix - fn find_issue(&self) -> Option<Issue> { - let mat = &self.compatibility_matrix; - let ai = &self.expected_indices; - let ii = &self.provided_indices; - - for i in 0..cmp::max(ai.len(), ii.len()) { - // If we eliminate the last row, any left-over inputs are considered missing - if i >= mat.len() { - return Some(Issue::Missing(i)); - } - // If we eliminate the last column, any left-over arguments are extra - if mat[i].len() == 0 { - return Some(Issue::Extra(i)); - } - - // Make sure we don't pass the bounds of our matrix - let is_arg = i < ai.len(); - let is_input = i < ii.len(); - if is_arg && is_input && matches!(mat[i][i], Compatibility::Compatible) { - // This is a satisfied input, so move along - continue; - } - - let mut useless = true; - let mut unsatisfiable = true; - if is_arg { - for j in 0..ii.len() { - // If we find at least one input this argument could satisfy - // this argument isn't unsatisfiable - if matches!(mat[j][i], Compatibility::Compatible) { - unsatisfiable = false; - break; - } - } - } - if is_input { - for j in 0..ai.len() { - // If we find at least one argument that could satisfy this input - // this argument isn't useless - if matches!(mat[i][j], Compatibility::Compatible) { - useless = false; - break; - } - } - } - - match (is_input, is_arg, useless, unsatisfiable) { - // If an argument is unsatisfied, and the input in its position is useless - // then the most likely explanation is that we just got the types wrong - (true, true, true, true) => return Some(Issue::Invalid(i)), - // Otherwise, if an input is useless, then indicate that this is an extra argument - (true, _, true, _) => return Some(Issue::Extra(i)), - // Otherwise, if an argument is unsatisfiable, indicate that it's missing - (_, true, _, true) => return Some(Issue::Missing(i)), - (true, true, _, _) => { - // The argument isn't useless, and the input isn't unsatisfied, - // so look for a parameter we might swap it with - // We look for swaps explicitly, instead of just falling back on permutations - // so that cases like (A,B,C,D) given (B,A,D,C) show up as two swaps, - // instead of a large permutation of 4 elements. - for j in 0..cmp::min(ai.len(), ii.len()) { - if i == j || matches!(mat[j][j], Compatibility::Compatible) { - continue; - } - if matches!(mat[i][j], Compatibility::Compatible) - && matches!(mat[j][i], Compatibility::Compatible) - { - return Some(Issue::Swap(i, j)); - } - } - } - _ => { - continue; - } - } - } - - // We didn't find any of the individual issues above, but - // there might be a larger permutation of parameters, so we now check for that - // by checking for cycles - // We use a double option at position i in this vec to represent: - // - None: We haven't computed anything about this argument yet - // - Some(None): This argument definitely doesn't participate in a cycle - // - Some(Some(x)): the i-th argument could permute to the x-th position - let mut permutation: Vec<Option<Option<usize>>> = vec![None; mat.len()]; - let mut permutation_found = false; - for i in 0..mat.len() { - if permutation[i].is_some() { - // We've already decided whether this argument is or is not in a loop - continue; - } - - let mut stack = vec![]; - let mut j = i; - let mut last = i; - let mut is_cycle = true; - loop { - stack.push(j); - // Look for params this one could slot into - let compat: Vec<_> = - mat[j] - .iter() - .enumerate() - .filter_map(|(i, c)| { - if matches!(c, Compatibility::Compatible) { Some(i) } else { None } - }) - .collect(); - if compat.len() != 1 { - // this could go into multiple slots, don't bother exploring both - is_cycle = false; - break; - } - j = compat[0]; - if stack.contains(&j) { - last = j; - break; - } - } - if stack.len() <= 2 { - // If we encounter a cycle of 1 or 2 elements, we'll let the - // "satisfy" and "swap" code above handle those - is_cycle = false; - } - // We've built up some chain, some of which might be a cycle - // ex: [1,2,3,4]; last = 2; j = 2; - // So, we want to mark 4, 3, and 2 as part of a permutation - permutation_found = is_cycle; - while let Some(x) = stack.pop() { - if is_cycle { - permutation[x] = Some(Some(j)); - j = x; - if j == last { - // From here on out, we're a tail leading into a cycle, - // not the cycle itself - is_cycle = false; - } - } else { - // Some(None) ensures we save time by skipping this argument again - permutation[x] = Some(None); - } - } - } - - if permutation_found { - // Map unwrap to remove the first layer of Some - let final_permutation: Vec<Option<usize>> = - permutation.into_iter().map(|x| x.unwrap()).collect(); - return Some(Issue::Permutation(final_permutation)); - } - return None; - } - - // Obviously, detecting exact user intention is impossible, so the goal here is to - // come up with as likely of a story as we can to be helpful. - // - // We'll iteratively removed "satisfied" input/argument pairs, - // then check for the cases above, until we've eliminated the entire grid - // - // We'll want to know which arguments and inputs these rows and columns correspond to - // even after we delete them. - pub(crate) fn find_errors( - mut self, - ) -> (Vec<Error<'tcx>>, IndexVec<ExpectedIdx, Option<ProvidedIdx>>) { - let provided_arg_count = self.provided_indices.len(); - - let mut errors: Vec<Error<'tcx>> = vec![]; - // For each expected argument, the matched *actual* input - let mut matched_inputs: IndexVec<ExpectedIdx, Option<ProvidedIdx>> = - IndexVec::from_elem_n(None, self.expected_indices.len()); - - // Before we start looking for issues, eliminate any arguments that are already satisfied, - // so that an argument which is already spoken for by the input it's in doesn't - // spill over into another similarly typed input - // ex: - // fn some_func(_a: i32, _b: i32) {} - // some_func(1, ""); - // Without this elimination, the first argument causes the second argument - // to show up as both a missing input and extra argument, rather than - // just an invalid type. - for (provided, expected) in self.eliminate_satisfied() { - matched_inputs[expected] = Some(provided); - } - - while !self.provided_indices.is_empty() || !self.expected_indices.is_empty() { - match self.find_issue() { - Some(Issue::Invalid(idx)) => { - let compatibility = self.compatibility_matrix[idx][idx].clone(); - let input_idx = self.provided_indices[idx]; - let arg_idx = self.expected_indices[idx]; - self.satisfy_input(idx, idx); - errors.push(Error::Invalid(input_idx, arg_idx, compatibility)); - } - Some(Issue::Extra(idx)) => { - let input_idx = self.provided_indices[idx]; - self.eliminate_provided(idx); - errors.push(Error::Extra(input_idx)); - } - Some(Issue::Missing(idx)) => { - let arg_idx = self.expected_indices[idx]; - self.eliminate_expected(idx); - errors.push(Error::Missing(arg_idx)); - } - Some(Issue::Swap(idx, other)) => { - let input_idx = self.provided_indices[idx]; - let other_input_idx = self.provided_indices[other]; - let arg_idx = self.expected_indices[idx]; - let other_arg_idx = self.expected_indices[other]; - let (min, max) = (cmp::min(idx, other), cmp::max(idx, other)); - self.satisfy_input(min, max); - // Subtract 1 because we already removed the "min" row - self.satisfy_input(max - 1, min); - errors.push(Error::Swap(input_idx, other_input_idx, arg_idx, other_arg_idx)); - matched_inputs[other_arg_idx] = Some(input_idx); - matched_inputs[arg_idx] = Some(other_input_idx); - } - Some(Issue::Permutation(args)) => { - let mut idxs: Vec<usize> = args.iter().filter_map(|&a| a).collect(); - - let mut real_idxs: IndexVec<ProvidedIdx, Option<(ExpectedIdx, ProvidedIdx)>> = - IndexVec::from_elem_n(None, provided_arg_count); - for (src, dst) in - args.iter().enumerate().filter_map(|(src, dst)| dst.map(|dst| (src, dst))) - { - let src_input_idx = self.provided_indices[src]; - let dst_input_idx = self.provided_indices[dst]; - let dest_arg_idx = self.expected_indices[dst]; - real_idxs[src_input_idx] = Some((dest_arg_idx, dst_input_idx)); - matched_inputs[dest_arg_idx] = Some(src_input_idx); - } - idxs.sort(); - idxs.reverse(); - for i in idxs { - self.satisfy_input(i, i); - } - errors.push(Error::Permutation(real_idxs.into_iter().flatten().collect())); - } - None => { - // We didn't find any issues, so we need to push the algorithm forward - // First, eliminate any arguments that currently satisfy their inputs - for (inp, arg) in self.eliminate_satisfied() { - matched_inputs[arg] = Some(inp); - } - } - }; - } - - return (errors, matched_inputs); - } -} |