Do not call check_expr in check_compatible, since it has side-effects and we've already checked all args

This commit is contained in:
Michael Goulet 2022-07-01 22:16:05 +00:00
parent 86b8dd5389
commit 6858fbc101
4 changed files with 24 additions and 45 deletions

View File

@ -271,7 +271,7 @@ pub(super) fn check_expr_with_expectation_and_args(
}
#[instrument(skip(self, expr), level = "debug")]
pub(super) fn check_expr_kind(
fn check_expr_kind(
&self,
expr: &'tcx hir::Expr<'tcx>,
expected: Expectation<'tcx>,

View File

@ -483,6 +483,19 @@ fn has_error_or_infer<'tcx>(tys: impl IntoIterator<Item = Ty<'tcx>>) -> bool {
self.set_tainted_by_errors();
let tcx = self.tcx;
// Precompute the provided types and spans, since that's all we typically need for below
let provided_arg_tys: IndexVec<ProvidedIdx, (Ty<'tcx>, Span)> = provided_args
.iter()
.map(|expr| {
let ty = self
.typeck_results
.borrow()
.expr_ty_adjusted_opt(*expr)
.unwrap_or_else(|| tcx.ty_error());
(self.resolve_vars_if_possible(ty), expr.span)
})
.collect();
// A "softer" version of the `demand_compatible`, which checks types without persisting them,
// and treats error types differently
// This will allow us to "probe" for other argument orders that would likely have been correct
@ -499,31 +512,23 @@ fn has_error_or_infer<'tcx>(tys: impl IntoIterator<Item = Ty<'tcx>>) -> bool {
return Compatibility::Incompatible(None);
}
let provided_arg: &hir::Expr<'tcx> = &provided_args[provided_idx];
let expectation = Expectation::rvalue_hint(self, expected_input_ty);
// FIXME: check that this is safe; I don't believe this commits any of the obligations, but I can't be sure.
//
// I had another method of "soft" type checking before,
// but it was failing to find the type of some expressions (like "")
// so I prodded this method and made it pub(super) so I could call it, and it seems to work well.
let checked_ty = self.check_expr_kind(provided_arg, expectation);
let (arg_ty, arg_span) = provided_arg_tys[provided_idx];
let expectation = Expectation::rvalue_hint(self, expected_input_ty);
let coerced_ty = expectation.only_has_type(self).unwrap_or(formal_input_ty);
let can_coerce = self.can_coerce(checked_ty, coerced_ty);
let can_coerce = self.can_coerce(arg_ty, coerced_ty);
if !can_coerce {
return Compatibility::Incompatible(None);
}
// Using probe here, since we don't want this subtyping to affect inference.
let subtyping_error = self.probe(|_| {
self.at(&self.misc(provided_arg.span), self.param_env)
.sup(formal_input_ty, coerced_ty)
.err()
self.at(&self.misc(arg_span), self.param_env).sup(formal_input_ty, coerced_ty).err()
});
// Same as above: if either the coerce type or the checked type is an error type,
// consider them *not* compatible.
let references_error = (coerced_ty, checked_ty).references_error();
let references_error = (coerced_ty, arg_ty).references_error();
match (references_error, subtyping_error) {
(false, None) => Compatibility::Compatible,
(_, subtyping_error) => Compatibility::Incompatible(subtyping_error),
@ -542,19 +547,6 @@ fn has_error_or_infer<'tcx>(tys: impl IntoIterator<Item = Ty<'tcx>>) -> bool {
ArgMatrix::new(provided_args.len(), formal_and_expected_inputs.len(), check_compatible)
.find_errors();
// Precompute the provided types and spans, since that's all we typically need for below
let provided_arg_tys: IndexVec<ProvidedIdx, (Ty<'tcx>, Span)> = provided_args
.iter()
.map(|expr| {
let ty = self
.typeck_results
.borrow()
.expr_ty_adjusted_opt(*expr)
.unwrap_or_else(|| tcx.ty_error());
(self.resolve_vars_if_possible(ty), expr.span)
})
.collect();
// First, check if we just need to wrap some arguments in a tuple.
if let Some((mismatch_idx, terr)) =
compatibility_diagonal.iter().enumerate().find_map(|(i, c)| {

View File

@ -1,7 +1,6 @@
fn main() {
let needlesArr: Vec<char> = vec!['a', 'f'];
needlesArr.iter().fold(|x, y| {
//~^ ERROR this function takes 2 arguments but 1 argument was supplied
});
//~^^ ERROR mismatched types
//~| ERROR this function takes 2 arguments but 1 argument was supplied
}

View File

@ -1,21 +1,9 @@
error[E0308]: mismatched types
--> $DIR/issue-3044.rs:3:35
|
LL | needlesArr.iter().fold(|x, y| {
| ____________________________------_^
| | |
| | the expected closure
LL | | });
| |_____^ expected closure, found `()`
|
= note: expected closure `[closure@$DIR/issue-3044.rs:3:28: 3:34]`
found unit type `()`
error[E0061]: this function takes 2 arguments but 1 argument was supplied
--> $DIR/issue-3044.rs:3:23
|
LL | needlesArr.iter().fold(|x, y| {
| _______________________^^^^-
LL | |
LL | | });
| |______- an argument is missing
|
@ -27,10 +15,10 @@ LL | fn fold<B, F>(mut self, init: B, mut f: F) -> B
help: provide the argument
|
LL ~ needlesArr.iter().fold(|x, y| {
LL +
LL ~ }, /* value */);
|
error: aborting due to 2 previous errors
error: aborting due to previous error
Some errors have detailed explanations: E0061, E0308.
For more information about an error, try `rustc --explain E0061`.
For more information about this error, try `rustc --explain E0061`.