use crate::astconv::AstConv;
use crate::check::coercion::CoerceMany;
+use crate::check::fn_ctxt::arg_matrix::{
+ ArgMatrix, Compatibility, Error, ExpectedIdx, ProvidedIdx,
+};
+use crate::check::gather_locals::Declaration;
+use crate::check::intrinsicck::InlineAsmCtxt;
use crate::check::method::MethodCallee;
use crate::check::Expectation::*;
use crate::check::TupleArgumentsFlag::*;
potentially_plural_count, struct_span_err, BreakableCtxt, Diverges, Expectation, FnCtxt,
LocalTy, Needs, TupleArgumentsFlag,
};
+use crate::structured_errors::StructuredDiagnostic;
use rustc_ast as ast;
-use rustc_errors::{Applicability, DiagnosticBuilder, DiagnosticId};
+use rustc_data_structures::fx::FxHashSet;
+use rustc_errors::{pluralize, Applicability, Diagnostic, DiagnosticId, MultiSpan};
use rustc_hir as hir;
-use rustc_hir::def::{DefKind, Res};
+use rustc_hir::def::{CtorOf, DefKind, Res};
use rustc_hir::def_id::DefId;
use rustc_hir::{ExprKind, Node, QPath};
+use rustc_index::vec::IndexVec;
+use rustc_infer::infer::error_reporting::{FailureCode, ObligationCauseExt};
+use rustc_infer::infer::type_variable::{TypeVariableOrigin, TypeVariableOriginKind};
+use rustc_infer::infer::InferOk;
+use rustc_infer::infer::TypeTrace;
use rustc_middle::ty::adjustment::AllowTwoPhase;
-use rustc_middle::ty::fold::TypeFoldable;
-use rustc_middle::ty::{self, Ty};
+use rustc_middle::ty::visit::TypeVisitable;
+use rustc_middle::ty::{self, DefIdTree, IsSuggestable, Ty, TypeSuperVisitable, TypeVisitor};
use rustc_session::Session;
-use rustc_span::symbol::{sym, Ident};
-use rustc_span::{self, MultiSpan, Span};
-use rustc_trait_selection::traits::{self, ObligationCauseCode, StatementAsExpression};
+use rustc_span::symbol::Ident;
+use rustc_span::{self, sym, Span};
+use rustc_trait_selection::traits::{self, ObligationCauseCode, SelectionContext};
-use std::mem::replace;
+use std::iter;
+use std::ops::ControlFlow;
use std::slice;
impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
pub(in super::super) fn check_casts(&self) {
let mut deferred_cast_checks = self.deferred_cast_checks.borrow_mut();
+ debug!("FnCtxt::check_casts: {} deferred checks", deferred_cast_checks.len());
for cast in deferred_cast_checks.drain(..) {
cast.check(self);
}
}
+ pub(in super::super) fn check_transmutes(&self) {
+ let mut deferred_transmute_checks = self.deferred_transmute_checks.borrow_mut();
+ debug!("FnCtxt::check_transmutes: {} deferred checks", deferred_transmute_checks.len());
+ for (from, to, span) in deferred_transmute_checks.drain(..) {
+ self.check_transmute(span, from, to);
+ }
+ }
+
+ pub(in super::super) fn check_asms(&self) {
+ let mut deferred_asm_checks = self.deferred_asm_checks.borrow_mut();
+ debug!("FnCtxt::check_asm: {} deferred checks", deferred_asm_checks.len());
+ for (asm, hir_id) in deferred_asm_checks.drain(..) {
+ let enclosing_id = self.tcx.hir().enclosing_body_owner(hir_id);
+ let get_operand_ty = |expr| {
+ let ty = self.typeck_results.borrow().expr_ty_adjusted(expr);
+ let ty = self.resolve_vars_if_possible(ty);
+ if ty.has_infer_types_or_consts() {
+ assert!(self.is_tainted_by_errors());
+ self.tcx.ty_error()
+ } else {
+ self.tcx.erase_regions(ty)
+ }
+ };
+ InlineAsmCtxt::new_in_fn(self.tcx, self.param_env, get_operand_ty)
+ .check_asm(asm, self.tcx.hir().local_def_id_to_hir_id(enclosing_id));
+ }
+ }
+
pub(in super::super) fn check_method_argument_types(
&self,
sp: Span,
let err_inputs = match tuple_arguments {
DontTupleArguments => err_inputs,
- TupleArguments => vec![self.tcx.intern_tup(&err_inputs[..])],
+ TupleArguments => vec![self.tcx.intern_tup(&err_inputs)],
};
self.check_argument_types(
sp,
expr,
- &err_inputs[..],
- &[],
+ &err_inputs,
+ None,
args_no_rcvr,
false,
tuple_arguments,
- None,
+ method.ok().map(|method| method.def_id),
);
return self.tcx.ty_error();
}
let method = method.unwrap();
// HACK(eddyb) ignore self in the definition (see above).
- let expected_arg_tys = self.expected_inputs_for_expected_output(
+ let expected_input_tys = self.expected_inputs_for_expected_output(
sp,
expected,
method.sig.output(),
sp,
expr,
&method.sig.inputs()[1..],
- &expected_arg_tys[..],
+ expected_input_tys,
args_no_rcvr,
method.sig.c_variadic,
tuple_arguments,
/// method calls and overloaded operators.
pub(in super::super) fn check_argument_types(
&self,
- sp: Span,
- expr: &'tcx hir::Expr<'tcx>,
- fn_inputs: &[Ty<'tcx>],
- expected_arg_tys: &[Ty<'tcx>],
- args: &'tcx [hir::Expr<'tcx>],
+ // Span enclosing the call site
+ call_span: Span,
+ // Expression of the call site
+ call_expr: &'tcx hir::Expr<'tcx>,
+ // Types (as defined in the *signature* of the target function)
+ formal_input_tys: &[Ty<'tcx>],
+ // More specific expected types, after unifying with caller output types
+ expected_input_tys: Option<Vec<Ty<'tcx>>>,
+ // The expressions for each provided argument
+ provided_args: &'tcx [hir::Expr<'tcx>],
+ // Whether the function is variadic, for example when imported from C
c_variadic: bool,
+ // Whether the arguments have been bundled in a tuple (ex: closures)
tuple_arguments: TupleArgumentsFlag,
- def_id: Option<DefId>,
+ // The DefId for the function being called, for better error messages
+ fn_def_id: Option<DefId>,
) {
let tcx = self.tcx;
- // Grab the argument types, supplying fresh type variables
- // if the wrong number of arguments were supplied
- let supplied_arg_count = if tuple_arguments == DontTupleArguments { args.len() } else { 1 };
+
+ // Conceptually, we've got some number of expected inputs, and some number of provided arguments
+ // and we can form a grid of whether each argument could satisfy a given input:
+ // in1 | in2 | in3 | ...
+ // arg1 ? | | |
+ // arg2 | ? | |
+ // arg3 | | ? |
+ // ...
+ // Initially, we just check the diagonal, because in the case of correct code
+ // these are the only checks that matter
+ // However, in the unhappy path, we'll fill in this whole grid to attempt to provide
+ // better error messages about invalid method calls.
// All the input types from the fn signature must outlive the call
// so as to validate implied bounds.
- for (&fn_input_ty, arg_expr) in fn_inputs.iter().zip(args.iter()) {
+ for (&fn_input_ty, arg_expr) in iter::zip(formal_input_tys, provided_args) {
self.register_wf_obligation(fn_input_ty.into(), arg_expr.span, traits::MiscObligation);
}
- let expected_arg_count = fn_inputs.len();
-
- let param_count_error = |expected_count: usize,
- arg_count: usize,
- error_code: &str,
- c_variadic: bool,
- sugg_unit: bool| {
- let (span, start_span, args) = match &expr.kind {
- hir::ExprKind::Call(hir::Expr { span, .. }, args) => (*span, *span, &args[..]),
- hir::ExprKind::MethodCall(path_segment, span, args, _) => (
- *span,
- // `sp` doesn't point at the whole `foo.bar()`, only at `bar`.
- path_segment
- .args
- .and_then(|args| args.args.iter().last())
- // Account for `foo.bar::<T>()`.
- .map(|arg| {
- // Skip the closing `>`.
- tcx.sess
- .source_map()
- .next_point(tcx.sess.source_map().next_point(arg.span()))
- })
- .unwrap_or(*span),
- &args[1..], // Skip the receiver.
- ),
- k => span_bug!(sp, "checking argument types on a non-call: `{:?}`", k),
- };
- let arg_spans = if args.is_empty() {
- // foo()
- // ^^^-- supplied 0 arguments
- // |
- // expected 2 arguments
- vec![tcx.sess.source_map().next_point(start_span).with_hi(sp.hi())]
- } else {
- // foo(1, 2, 3)
- // ^^^ - - - supplied 3 arguments
- // |
- // expected 2 arguments
- args.iter().map(|arg| arg.span).collect::<Vec<Span>>()
- };
-
- let mut err = tcx.sess.struct_span_err_with_code(
- span,
- &format!(
- "this function takes {}{} but {} {} supplied",
- if c_variadic { "at least " } else { "" },
- potentially_plural_count(expected_count, "argument"),
- potentially_plural_count(arg_count, "argument"),
- if arg_count == 1 { "was" } else { "were" }
- ),
- DiagnosticId::Error(error_code.to_owned()),
- );
- let label = format!("supplied {}", potentially_plural_count(arg_count, "argument"));
- for (i, span) in arg_spans.into_iter().enumerate() {
- err.span_label(
- span,
- if arg_count == 0 || i + 1 == arg_count { &label } else { "" },
- );
- }
+ let mut err_code = "E0061";
- if let Some(def_id) = def_id {
- if let Some(node) = tcx.hir().get_if_local(def_id) {
- let mut spans: MultiSpan = node
- .ident()
- .map(|ident| ident.span)
- .unwrap_or_else(|| tcx.hir().span(node.hir_id().unwrap()))
- .into();
-
- if let Some(id) = node.body_id() {
- let body = tcx.hir().body(id);
- for param in body.params {
- spans.push_span_label(param.span, String::new());
- }
- }
-
- let def_kind = tcx.def_kind(def_id);
- err.span_note(spans, &format!("{} defined here", def_kind.descr(def_id)));
- }
- }
-
- if sugg_unit {
- let sugg_span = tcx.sess.source_map().end_point(expr.span);
- // remove closing `)` from the span
- let sugg_span = sugg_span.shrink_to_lo();
- err.span_suggestion(
- sugg_span,
- "expected the unit value `()`; create it with empty parentheses",
- String::from("()"),
- Applicability::MachineApplicable,
- );
- } else {
- err.span_label(
- span,
- format!(
- "expected {}{}",
- if c_variadic { "at least " } else { "" },
- potentially_plural_count(expected_count, "argument")
- ),
- );
- }
- err.emit();
- };
-
- let mut expected_arg_tys = expected_arg_tys.to_vec();
-
- let formal_tys = if tuple_arguments == TupleArguments {
- let tuple_type = self.structurally_resolved_type(sp, fn_inputs[0]);
+ // If the arguments should be wrapped in a tuple (ex: closures), unwrap them here
+ let (formal_input_tys, expected_input_tys) = if tuple_arguments == TupleArguments {
+ let tuple_type = self.structurally_resolved_type(call_span, formal_input_tys[0]);
match tuple_type.kind() {
- ty::Tuple(arg_types) if arg_types.len() != args.len() => {
- param_count_error(arg_types.len(), args.len(), "E0057", false, false);
- expected_arg_tys = vec![];
- self.err_args(args.len())
- }
+ // We expected a tuple and got a tuple
ty::Tuple(arg_types) => {
- expected_arg_tys = match expected_arg_tys.get(0) {
- Some(&ty) => match ty.kind() {
- ty::Tuple(ref tys) => tys.iter().map(|k| k.expect_ty()).collect(),
- _ => vec![],
+ // Argument length differs
+ if arg_types.len() != provided_args.len() {
+ err_code = "E0057";
+ }
+ let expected_input_tys = match expected_input_tys {
+ Some(expected_input_tys) => match expected_input_tys.get(0) {
+ Some(ty) => match ty.kind() {
+ ty::Tuple(tys) => Some(tys.iter().collect()),
+ _ => None,
+ },
+ None => None,
},
- None => vec![],
+ None => None,
};
- arg_types.iter().map(|k| k.expect_ty()).collect()
+ (arg_types.iter().collect(), expected_input_tys)
}
_ => {
+ // Otherwise, there's a mismatch, so clear out what we're expecting, and set
+ // our input types to err_args so we don't blow up the error messages
struct_span_err!(
tcx.sess,
- sp,
+ call_span,
E0059,
"cannot use call notation; the first type parameter \
for the function trait is neither a tuple nor unit"
)
.emit();
- expected_arg_tys = vec![];
- self.err_args(args.len())
+ (self.err_args(provided_args.len()), None)
}
}
- } else if expected_arg_count == supplied_arg_count {
- fn_inputs.to_vec()
- } else if c_variadic {
- if supplied_arg_count >= expected_arg_count {
- fn_inputs.to_vec()
- } else {
- param_count_error(expected_arg_count, supplied_arg_count, "E0060", true, false);
- expected_arg_tys = vec![];
- self.err_args(supplied_arg_count)
- }
} else {
- // is the missing argument of type `()`?
- let sugg_unit = if expected_arg_tys.len() == 1 && supplied_arg_count == 0 {
- self.resolve_vars_if_possible(&expected_arg_tys[0]).is_unit()
- } else if fn_inputs.len() == 1 && supplied_arg_count == 0 {
- self.resolve_vars_if_possible(&fn_inputs[0]).is_unit()
- } else {
- false
- };
- param_count_error(expected_arg_count, supplied_arg_count, "E0061", false, sugg_unit);
+ (formal_input_tys.to_vec(), expected_input_tys)
+ };
- expected_arg_tys = vec![];
- self.err_args(supplied_arg_count)
+ // If there are no external expectations at the call site, just use the types from the function defn
+ let expected_input_tys = if let Some(expected_input_tys) = expected_input_tys {
+ assert_eq!(expected_input_tys.len(), formal_input_tys.len());
+ expected_input_tys
+ } else {
+ formal_input_tys.clone()
};
- debug!(
- "check_argument_types: formal_tys={:?}",
- formal_tys.iter().map(|t| self.ty_to_string(*t)).collect::<Vec<String>>()
- );
+ let minimum_input_count = expected_input_tys.len();
+ let provided_arg_count = provided_args.len();
+
+ let is_const_eval_select = matches!(fn_def_id, Some(def_id) if
+ self.tcx.def_kind(def_id) == hir::def::DefKind::Fn
+ && self.tcx.is_intrinsic(def_id)
+ && self.tcx.item_name(def_id) == sym::const_eval_select);
- // If there is no expectation, expect formal_tys.
- let expected_arg_tys =
- if !expected_arg_tys.is_empty() { expected_arg_tys } else { formal_tys.clone() };
+ // We introduce a helper function to demand that a given argument satisfy a given input
+ // This is more complicated than just checking type equality, as arguments could be coerced
+ // This version writes those types back so further type checking uses the narrowed types
+ let demand_compatible = |idx| {
+ let formal_input_ty: Ty<'tcx> = formal_input_tys[idx];
+ let expected_input_ty: Ty<'tcx> = expected_input_tys[idx];
+ let provided_arg = &provided_args[idx];
- let mut final_arg_types: Vec<(usize, Ty<'_>, Ty<'_>)> = vec![];
+ debug!("checking argument {}: {:?} = {:?}", idx, provided_arg, formal_input_ty);
+
+ // We're on the happy path here, so we'll do a more involved check and write back types
+ // To check compatibility, we'll do 3 things:
+ // 1. Unify the provided argument with the expected type
+ let expectation = Expectation::rvalue_hint(self, expected_input_ty);
+
+ let checked_ty = self.check_expr_with_expectation(provided_arg, expectation);
+
+ // 2. Coerce to the most detailed type that could be coerced
+ // to, which is `expected_ty` if `rvalue_hint` returns an
+ // `ExpectHasType(expected_ty)`, or the `formal_ty` otherwise.
+ let coerced_ty = expectation.only_has_type(self).unwrap_or(formal_input_ty);
+
+ // Cause selection errors caused by resolving a single argument to point at the
+ // argument and not the call. This lets us customize the span pointed to in the
+ // fulfillment error to be more accurate.
+ let coerced_ty = self.resolve_vars_with_obligations(coerced_ty);
+
+ let coerce_error = self
+ .try_coerce(provided_arg, checked_ty, coerced_ty, AllowTwoPhase::Yes, None)
+ .err();
+
+ if coerce_error.is_some() {
+ return Compatibility::Incompatible(coerce_error);
+ }
+
+ // Check that second and third argument of `const_eval_select` must be `FnDef`, and additionally that
+ // the second argument must be `const fn`. The first argument must be a tuple, but this is already expressed
+ // in the function signature (`F: FnOnce<ARG>`), so I did not bother to add another check here.
+ //
+ // This check is here because there is currently no way to express a trait bound for `FnDef` types only.
+ if is_const_eval_select && (1..=2).contains(&idx) {
+ if let ty::FnDef(def_id, _) = checked_ty.kind() {
+ if idx == 1 && !self.tcx.is_const_fn_raw(*def_id) {
+ self.tcx
+ .sess
+ .struct_span_err(provided_arg.span, "this argument must be a `const fn`")
+ .help("consult the documentation on `const_eval_select` for more information")
+ .emit();
+ }
+ } else {
+ self.tcx
+ .sess
+ .struct_span_err(provided_arg.span, "this argument must be a function item")
+ .note(format!("expected a function item, found {checked_ty}"))
+ .help(
+ "consult the documentation on `const_eval_select` for more information",
+ )
+ .emit();
+ }
+ }
+
+ // 3. Check if the formal type is a supertype of the checked one
+ // and register any such obligations for future type checks
+ let supertype_error = self
+ .at(&self.misc(provided_arg.span), self.param_env)
+ .sup(formal_input_ty, coerced_ty);
+ let subtyping_error = match supertype_error {
+ Ok(InferOk { obligations, value: () }) => {
+ self.register_predicates(obligations);
+ None
+ }
+ Err(err) => Some(err),
+ };
+
+ // If neither check failed, the types are compatible
+ match subtyping_error {
+ None => Compatibility::Compatible,
+ Some(_) => Compatibility::Incompatible(subtyping_error),
+ }
+ };
+
+ // To start, we only care "along the diagonal", where we expect every
+ // provided arg to be in the right spot
+ let mut compatibility_diagonal =
+ vec![Compatibility::Incompatible(None); provided_args.len()];
+
+ // Keep track of whether we *could possibly* be satisfied, i.e. whether we're on the happy path
+ // if the wrong number of arguments were supplied, we CAN'T be satisfied,
+ // and if we're c_variadic, the supplied arguments must be >= the minimum count from the function
+ // otherwise, they need to be identical, because rust doesn't currently support variadic functions
+ let mut call_appears_satisfied = if c_variadic {
+ provided_arg_count >= minimum_input_count
+ } else {
+ provided_arg_count == minimum_input_count
+ };
// Check the arguments.
// We do this in a pretty awful way: first we type-check any arguments
// that are not closures, then we type-check the closures. This is so
// that we have more information about the types of arguments when we
// type-check the functions. This isn't really the right way to do this.
- for &check_closures in &[false, true] {
- debug!("check_closures={}", check_closures);
-
+ for check_closures in [false, true] {
// More awful hacks: before we check argument types, try to do
// an "opportunistic" trait resolution of any trait bounds on
// the call. This helps coercions.
if check_closures {
- self.select_obligations_where_possible(false, |errors| {
- self.point_at_type_arg_instead_of_call_if_possible(errors, expr);
- self.point_at_arg_instead_of_call_if_possible(
- errors,
- &final_arg_types[..],
- sp,
- &args,
- );
- })
+ self.select_obligations_where_possible(false, |_| {})
}
- // For C-variadic functions, we don't have a declared type for all of
- // the arguments hence we only do our usual type checking with
- // the arguments who's types we do know.
- let t = if c_variadic {
- expected_arg_count
- } else if tuple_arguments == TupleArguments {
- args.len()
- } else {
- supplied_arg_count
- };
- for (i, arg) in args.iter().take(t).enumerate() {
+ // Check each argument, to satisfy the input it was provided for
+ // Visually, we're traveling down the diagonal of the compatibility matrix
+ for (idx, arg) in provided_args.iter().enumerate() {
// Warn only for the first loop (the "no closures" one).
// Closure arguments themselves can't be diverging, but
// a previous argument can, e.g., `foo(panic!(), || {})`.
self.warn_if_unreachable(arg.hir_id, arg.span, "expression");
}
- let is_closure = match arg.kind {
- ExprKind::Closure(..) => true,
- _ => false,
- };
+ // For C-variadic functions, we don't have a declared type for all of
+ // the arguments hence we only do our usual type checking with
+ // the arguments who's types we do know. However, we *can* check
+ // for unreachable expressions (see above).
+ // FIXME: unreachable warning current isn't emitted
+ if idx >= minimum_input_count {
+ continue;
+ }
+ let is_closure = matches!(arg.kind, ExprKind::Closure { .. });
if is_closure != check_closures {
continue;
}
- debug!("checking the argument");
- let formal_ty = formal_tys[i];
-
- // The special-cased logic below has three functions:
- // 1. Provide as good of an expected type as possible.
- let expected = Expectation::rvalue_hint(self, expected_arg_tys[i]);
-
- let checked_ty = self.check_expr_with_expectation(&arg, expected);
+ let compatible = demand_compatible(idx);
+ let is_compatible = matches!(compatible, Compatibility::Compatible);
+ compatibility_diagonal[idx] = compatible;
- // 2. Coerce to the most detailed type that could be coerced
- // to, which is `expected_ty` if `rvalue_hint` returns an
- // `ExpectHasType(expected_ty)`, or the `formal_ty` otherwise.
- let coerce_ty = expected.only_has_type(self).unwrap_or(formal_ty);
- // We're processing function arguments so we definitely want to use
- // two-phase borrows.
- self.demand_coerce(&arg, checked_ty, coerce_ty, None, AllowTwoPhase::Yes);
- final_arg_types.push((i, checked_ty, coerce_ty));
-
- // 3. Relate the expected type and the formal one,
- // if the expected type was used for the coercion.
- self.demand_suptype(arg.span, formal_ty, coerce_ty);
+ if !is_compatible {
+ call_appears_satisfied = false;
+ }
}
}
- // We also need to make sure we at least write the ty of the other
- // arguments which we skipped above.
- if c_variadic {
- fn variadic_error<'tcx>(s: &Session, span: Span, t: Ty<'tcx>, cast_ty: &str) {
- use crate::structured_errors::{StructuredDiagnostic, VariadicError};
- VariadicError::new(s, span, t, cast_ty).diagnostic().emit();
- }
+ if c_variadic && provided_arg_count < minimum_input_count {
+ err_code = "E0060";
+ }
- for arg in args.iter().skip(expected_arg_count) {
- let arg_ty = self.check_expr(&arg);
+ for arg in provided_args.iter().skip(minimum_input_count) {
+ // Make sure we've checked this expr at least once.
+ let arg_ty = self.check_expr(&arg);
+
+ // If the function is c-style variadic, we skipped a bunch of arguments
+ // so we need to check those, and write out the types
+ // Ideally this would be folded into the above, for uniform style
+ // but c-variadic is already a corner case
+ if c_variadic {
+ fn variadic_error<'tcx>(
+ sess: &'tcx Session,
+ span: Span,
+ ty: Ty<'tcx>,
+ cast_ty: &str,
+ ) {
+ use crate::structured_errors::MissingCastForVariadicArg;
+
+ MissingCastForVariadicArg { sess, span, ty, cast_ty }.diagnostic().emit();
+ }
// There are a few types which get autopromoted when passed via varargs
// in C but we just error out instead and require explicit casts.
let arg_ty = self.structurally_resolved_type(arg.span, arg_ty);
match arg_ty.kind() {
- ty::Float(ast::FloatTy::F32) => {
+ ty::Float(ty::FloatTy::F32) => {
variadic_error(tcx.sess, arg.span, arg_ty, "c_double");
}
- ty::Int(ast::IntTy::I8 | ast::IntTy::I16) | ty::Bool => {
+ ty::Int(ty::IntTy::I8 | ty::IntTy::I16) | ty::Bool => {
variadic_error(tcx.sess, arg.span, arg_ty, "c_int");
}
- ty::Uint(ast::UintTy::U8 | ast::UintTy::U16) => {
+ ty::Uint(ty::UintTy::U8 | ty::UintTy::U16) => {
variadic_error(tcx.sess, arg.span, arg_ty, "c_uint");
}
ty::FnDef(..) => {
let ptr_ty = self.tcx.mk_fn_ptr(arg_ty.fn_sig(self.tcx));
- let ptr_ty = self.resolve_vars_if_possible(&ptr_ty);
+ let ptr_ty = self.resolve_vars_if_possible(ptr_ty);
variadic_error(tcx.sess, arg.span, arg_ty, &ptr_ty.to_string());
}
_ => {}
}
}
}
+
+ if !call_appears_satisfied {
+ let compatibility_diagonal = IndexVec::from_raw(compatibility_diagonal);
+ let provided_args = IndexVec::from_iter(provided_args.iter().take(if c_variadic {
+ minimum_input_count
+ } else {
+ provided_arg_count
+ }));
+ debug_assert_eq!(
+ formal_input_tys.len(),
+ expected_input_tys.len(),
+ "expected formal_input_tys to be the same size as expected_input_tys"
+ );
+ let formal_and_expected_inputs = IndexVec::from_iter(
+ formal_input_tys
+ .iter()
+ .copied()
+ .zip(expected_input_tys.iter().copied())
+ .map(|vars| self.resolve_vars_if_possible(vars)),
+ );
+
+ self.report_arg_errors(
+ compatibility_diagonal,
+ formal_and_expected_inputs,
+ provided_args,
+ c_variadic,
+ err_code,
+ fn_def_id,
+ call_span,
+ call_expr,
+ );
+ }
+ }
+
+ fn report_arg_errors(
+ &self,
+ compatibility_diagonal: IndexVec<ProvidedIdx, Compatibility<'tcx>>,
+ formal_and_expected_inputs: IndexVec<ExpectedIdx, (Ty<'tcx>, Ty<'tcx>)>,
+ provided_args: IndexVec<ProvidedIdx, &'tcx hir::Expr<'tcx>>,
+ c_variadic: bool,
+ err_code: &str,
+ fn_def_id: Option<DefId>,
+ call_span: Span,
+ call_expr: &hir::Expr<'tcx>,
+ ) {
+ // Next, let's construct the error
+ let (error_span, full_call_span, ctor_of, is_method) = match &call_expr.kind {
+ hir::ExprKind::Call(
+ hir::Expr { hir_id, span, kind: hir::ExprKind::Path(qpath), .. },
+ _,
+ ) => {
+ if let Res::Def(DefKind::Ctor(of, _), _) =
+ self.typeck_results.borrow().qpath_res(qpath, *hir_id)
+ {
+ (call_span, *span, Some(of), false)
+ } else {
+ (call_span, *span, None, false)
+ }
+ }
+ hir::ExprKind::Call(hir::Expr { span, .. }, _) => (call_span, *span, None, false),
+ hir::ExprKind::MethodCall(path_segment, _, _, span) => {
+ let ident_span = path_segment.ident.span;
+ let ident_span = if let Some(args) = path_segment.args {
+ ident_span.with_hi(args.span_ext.hi())
+ } else {
+ ident_span
+ };
+ // methods are never ctors
+ (*span, ident_span, None, true)
+ }
+ k => span_bug!(call_span, "checking argument types on a non-call: `{:?}`", k),
+ };
+ let args_span = error_span.trim_start(full_call_span).unwrap_or(error_span);
+ let call_name = match ctor_of {
+ Some(CtorOf::Struct) => "struct",
+ Some(CtorOf::Variant) => "enum variant",
+ None => "function",
+ };
+
+ // Don't print if it has error types or is just plain `_`
+ fn has_error_or_infer<'tcx>(tys: impl IntoIterator<Item = Ty<'tcx>>) -> bool {
+ tys.into_iter().any(|ty| ty.references_error() || ty.is_ty_var())
+ }
+
+ self.set_tainted_by_errors();
+ let tcx = self.tcx;
+
+ // Get the argument span in the context of the call span so that
+ // suggestions and labels are (more) correct when an arg is a
+ // macro invocation.
+ let normalize_span = |span: Span| -> Span {
+ let normalized_span = span.find_ancestor_inside(error_span).unwrap_or(span);
+ // Sometimes macros mess up the spans, so do not normalize the
+ // arg span to equal the error span, because that's less useful
+ // than pointing out the arg expr in the wrong context.
+ if normalized_span.source_equal(error_span) { span } else { normalized_span }
+ };
+
+ // 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), normalize_span(expr.span))
+ })
+ .collect();
+ let callee_expr = match &call_expr.peel_blocks().kind {
+ hir::ExprKind::Call(callee, _) => Some(*callee),
+ hir::ExprKind::MethodCall(_, receiver, ..) => {
+ if let Some((DefKind::AssocFn, def_id)) =
+ self.typeck_results.borrow().type_dependent_def(call_expr.hir_id)
+ && let Some(assoc) = tcx.opt_associated_item(def_id)
+ && assoc.fn_has_self_parameter
+ {
+ Some(*receiver)
+ } else {
+ None
+ }
+ }
+ _ => None,
+ };
+ let callee_ty = callee_expr
+ .and_then(|callee_expr| self.typeck_results.borrow().expr_ty_adjusted_opt(callee_expr));
+
+ // 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
+ let check_compatible = |provided_idx: ProvidedIdx, expected_idx: ExpectedIdx| {
+ if provided_idx.as_usize() == expected_idx.as_usize() {
+ return compatibility_diagonal[provided_idx].clone();
+ }
+
+ let (formal_input_ty, expected_input_ty) = formal_and_expected_inputs[expected_idx];
+ // If either is an error type, we defy the usual convention and consider them to *not* be
+ // coercible. This prevents our error message heuristic from trying to pass errors into
+ // every argument.
+ if (formal_input_ty, expected_input_ty).references_error() {
+ return Compatibility::Incompatible(None);
+ }
+
+ 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(arg_ty, coerced_ty);
+ if !can_coerce {
+ return Compatibility::Incompatible(Some(ty::error::TypeError::Sorts(
+ ty::error::ExpectedFound::new(true, coerced_ty, arg_ty),
+ )));
+ }
+
+ // Using probe here, since we don't want this subtyping to affect inference.
+ let subtyping_error = self.probe(|_| {
+ 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, arg_ty).references_error();
+ match (references_error, subtyping_error) {
+ (false, None) => Compatibility::Compatible,
+ (_, subtyping_error) => Compatibility::Incompatible(subtyping_error),
+ }
+ };
+
+ // The algorithm here is inspired by levenshtein distance and longest common subsequence.
+ // We'll try to detect 4 different types of mistakes:
+ // - An extra parameter has been provided that doesn't satisfy *any* of the other inputs
+ // - An input is missing, which isn't satisfied by *any* of the other arguments
+ // - Some number of arguments have been provided in the wrong order
+ // - A type is straight up invalid
+
+ // First, let's find the errors
+ let (mut errors, matched_inputs) =
+ ArgMatrix::new(provided_args.len(), formal_and_expected_inputs.len(), check_compatible)
+ .find_errors();
+
+ // 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)| {
+ if let Compatibility::Incompatible(Some(terr)) = c {
+ Some((i, *terr))
+ } else {
+ None
+ }
+ })
+ {
+ // Is the first bad expected argument a tuple?
+ // Do we have as many extra provided arguments as the tuple's length?
+ // If so, we might have just forgotten to wrap some args in a tuple.
+ if let Some(ty::Tuple(tys)) =
+ formal_and_expected_inputs.get(mismatch_idx.into()).map(|tys| tys.1.kind())
+ // If the tuple is unit, we're not actually wrapping any arguments.
+ && !tys.is_empty()
+ && provided_arg_tys.len() == formal_and_expected_inputs.len() - 1 + tys.len()
+ {
+ // Wrap up the N provided arguments starting at this position in a tuple.
+ let provided_as_tuple = tcx.mk_tup(
+ provided_arg_tys.iter().map(|(ty, _)| *ty).skip(mismatch_idx).take(tys.len()),
+ );
+
+ let mut satisfied = true;
+ // Check if the newly wrapped tuple + rest of the arguments are compatible.
+ for ((_, expected_ty), provided_ty) in std::iter::zip(
+ formal_and_expected_inputs.iter().skip(mismatch_idx),
+ [provided_as_tuple].into_iter().chain(
+ provided_arg_tys.iter().map(|(ty, _)| *ty).skip(mismatch_idx + tys.len()),
+ ),
+ ) {
+ if !self.can_coerce(provided_ty, *expected_ty) {
+ satisfied = false;
+ break;
+ }
+ }
+
+ // If they're compatible, suggest wrapping in an arg, and we're done!
+ // Take some care with spans, so we don't suggest wrapping a macro's
+ // innards in parenthesis, for example.
+ if satisfied
+ && let Some((_, lo)) =
+ provided_arg_tys.get(ProvidedIdx::from_usize(mismatch_idx))
+ && let Some((_, hi)) =
+ provided_arg_tys.get(ProvidedIdx::from_usize(mismatch_idx + tys.len() - 1))
+ {
+ let mut err;
+ if tys.len() == 1 {
+ // A tuple wrap suggestion actually occurs within,
+ // so don't do anything special here.
+ err = self.report_and_explain_type_error(
+ TypeTrace::types(
+ &self.misc(*lo),
+ true,
+ formal_and_expected_inputs[mismatch_idx.into()].1,
+ provided_arg_tys[mismatch_idx.into()].0,
+ ),
+ terr,
+ );
+ err.span_label(
+ full_call_span,
+ format!("arguments to this {} are incorrect", call_name),
+ );
+ } else {
+ err = tcx.sess.struct_span_err_with_code(
+ full_call_span,
+ &format!(
+ "this {} takes {}{} but {} {} supplied",
+ call_name,
+ if c_variadic { "at least " } else { "" },
+ potentially_plural_count(
+ formal_and_expected_inputs.len(),
+ "argument"
+ ),
+ potentially_plural_count(provided_args.len(), "argument"),
+ pluralize!("was", provided_args.len())
+ ),
+ DiagnosticId::Error(err_code.to_owned()),
+ );
+ err.multipart_suggestion_verbose(
+ "wrap these arguments in parentheses to construct a tuple",
+ vec![
+ (lo.shrink_to_lo(), "(".to_string()),
+ (hi.shrink_to_hi(), ")".to_string()),
+ ],
+ Applicability::MachineApplicable,
+ );
+ };
+ self.label_fn_like(
+ &mut err,
+ fn_def_id,
+ callee_ty,
+ Some(mismatch_idx),
+ is_method,
+ );
+ err.emit();
+ return;
+ }
+ }
+ }
+
+ // Okay, so here's where it gets complicated in regards to what errors
+ // we emit and how.
+ // There are 3 different "types" of errors we might encounter.
+ // 1) Missing/extra/swapped arguments
+ // 2) Valid but incorrect arguments
+ // 3) Invalid arguments
+ // - Currently I think this only comes up with `CyclicTy`
+ //
+ // We first need to go through, remove those from (3) and emit those
+ // as their own error, particularly since they're error code and
+ // message is special. From what I can tell, we *must* emit these
+ // here (vs somewhere prior to this function) since the arguments
+ // become invalid *because* of how they get used in the function.
+ // It is what it is.
+
+ if errors.is_empty() {
+ if cfg!(debug_assertions) {
+ span_bug!(error_span, "expected errors from argument matrix");
+ } else {
+ tcx.sess
+ .struct_span_err(
+ error_span,
+ "argument type mismatch was detected, \
+ but rustc had trouble determining where",
+ )
+ .note(
+ "we would appreciate a bug report: \
+ https://github.com/rust-lang/rust/issues/new",
+ )
+ .emit();
+ }
+ return;
+ }
+
+ errors.drain_filter(|error| {
+ let Error::Invalid(provided_idx, expected_idx, Compatibility::Incompatible(Some(e))) = error else { return false };
+ let (provided_ty, provided_span) = provided_arg_tys[*provided_idx];
+ let (expected_ty, _) = formal_and_expected_inputs[*expected_idx];
+ let cause = &self.misc(provided_span);
+ let trace = TypeTrace::types(cause, true, expected_ty, provided_ty);
+ if !matches!(trace.cause.as_failure_code(*e), FailureCode::Error0308(_)) {
+ self.report_and_explain_type_error(trace, *e).emit();
+ return true;
+ }
+ false
+ });
+
+ // We're done if we found errors, but we already emitted them.
+ if errors.is_empty() {
+ return;
+ }
+
+ // Okay, now that we've emitted the special errors separately, we
+ // are only left missing/extra/swapped and mismatched arguments, both
+ // can be collated pretty easily if needed.
+
+ // Next special case: if there is only one "Incompatible" error, just emit that
+ if let [
+ Error::Invalid(provided_idx, expected_idx, Compatibility::Incompatible(Some(err))),
+ ] = &errors[..]
+ {
+ let (formal_ty, expected_ty) = formal_and_expected_inputs[*expected_idx];
+ let (provided_ty, provided_arg_span) = provided_arg_tys[*provided_idx];
+ let cause = &self.misc(provided_arg_span);
+ let trace = TypeTrace::types(cause, true, expected_ty, provided_ty);
+ let mut err = self.report_and_explain_type_error(trace, *err);
+ self.emit_coerce_suggestions(
+ &mut err,
+ &provided_args[*provided_idx],
+ provided_ty,
+ Expectation::rvalue_hint(self, expected_ty)
+ .only_has_type(self)
+ .unwrap_or(formal_ty),
+ None,
+ None,
+ );
+ err.span_label(
+ full_call_span,
+ format!("arguments to this {} are incorrect", call_name),
+ );
+ // Call out where the function is defined
+ self.label_fn_like(
+ &mut err,
+ fn_def_id,
+ callee_ty,
+ Some(expected_idx.as_usize()),
+ is_method,
+ );
+ err.emit();
+ return;
+ }
+
+ let mut err = if formal_and_expected_inputs.len() == provided_args.len() {
+ struct_span_err!(
+ tcx.sess,
+ full_call_span,
+ E0308,
+ "arguments to this {} are incorrect",
+ call_name,
+ )
+ } else {
+ tcx.sess.struct_span_err_with_code(
+ full_call_span,
+ &format!(
+ "this {} takes {}{} but {} {} supplied",
+ call_name,
+ if c_variadic { "at least " } else { "" },
+ potentially_plural_count(formal_and_expected_inputs.len(), "argument"),
+ potentially_plural_count(provided_args.len(), "argument"),
+ pluralize!("was", provided_args.len())
+ ),
+ DiagnosticId::Error(err_code.to_owned()),
+ )
+ };
+
+ // As we encounter issues, keep track of what we want to provide for the suggestion
+ let mut labels = vec![];
+ // If there is a single error, we give a specific suggestion; otherwise, we change to
+ // "did you mean" with the suggested function call
+ enum SuggestionText {
+ None,
+ Provide(bool),
+ Remove(bool),
+ Swap,
+ Reorder,
+ DidYouMean,
+ }
+ let mut suggestion_text = SuggestionText::None;
+
+ let mut errors = errors.into_iter().peekable();
+ while let Some(error) = errors.next() {
+ match error {
+ Error::Invalid(provided_idx, expected_idx, compatibility) => {
+ let (formal_ty, expected_ty) = formal_and_expected_inputs[expected_idx];
+ let (provided_ty, provided_span) = provided_arg_tys[provided_idx];
+ if let Compatibility::Incompatible(error) = compatibility {
+ let cause = &self.misc(provided_span);
+ let trace = TypeTrace::types(cause, true, expected_ty, provided_ty);
+ if let Some(e) = error {
+ self.note_type_err(
+ &mut err,
+ &trace.cause,
+ None,
+ Some(trace.values),
+ e,
+ false,
+ true,
+ );
+ }
+ }
+
+ self.emit_coerce_suggestions(
+ &mut err,
+ &provided_args[provided_idx],
+ provided_ty,
+ Expectation::rvalue_hint(self, expected_ty)
+ .only_has_type(self)
+ .unwrap_or(formal_ty),
+ None,
+ None,
+ );
+ }
+ Error::Extra(arg_idx) => {
+ let (provided_ty, provided_span) = provided_arg_tys[arg_idx];
+ let provided_ty_name = if !has_error_or_infer([provided_ty]) {
+ // FIXME: not suggestable, use something else
+ format!(" of type `{}`", provided_ty)
+ } else {
+ "".to_string()
+ };
+ labels
+ .push((provided_span, format!("argument{} unexpected", provided_ty_name)));
+ suggestion_text = match suggestion_text {
+ SuggestionText::None => SuggestionText::Remove(false),
+ SuggestionText::Remove(_) => SuggestionText::Remove(true),
+ _ => SuggestionText::DidYouMean,
+ };
+ }
+ Error::Missing(expected_idx) => {
+ // If there are multiple missing arguments adjacent to each other,
+ // then we can provide a single error.
+
+ let mut missing_idxs = vec![expected_idx];
+ while let Some(e) = errors.next_if(|e| {
+ matches!(e, Error::Missing(next_expected_idx)
+ if *next_expected_idx == *missing_idxs.last().unwrap() + 1)
+ }) {
+ match e {
+ Error::Missing(expected_idx) => missing_idxs.push(expected_idx),
+ _ => unreachable!(),
+ }
+ }
+
+ // NOTE: Because we might be re-arranging arguments, might have extra
+ // arguments, etc. it's hard to *really* know where we should provide
+ // this error label, so as a heuristic, we point to the provided arg, or
+ // to the call if the missing inputs pass the provided args.
+ match &missing_idxs[..] {
+ &[expected_idx] => {
+ let (_, input_ty) = formal_and_expected_inputs[expected_idx];
+ let span = if let Some((_, arg_span)) =
+ provided_arg_tys.get(expected_idx.to_provided_idx())
+ {
+ *arg_span
+ } else {
+ args_span
+ };
+ let rendered = if !has_error_or_infer([input_ty]) {
+ format!(" of type `{}`", input_ty)
+ } else {
+ "".to_string()
+ };
+ labels.push((span, format!("an argument{} is missing", rendered)));
+ suggestion_text = match suggestion_text {
+ SuggestionText::None => SuggestionText::Provide(false),
+ SuggestionText::Provide(_) => SuggestionText::Provide(true),
+ _ => SuggestionText::DidYouMean,
+ };
+ }
+ &[first_idx, second_idx] => {
+ let (_, first_expected_ty) = formal_and_expected_inputs[first_idx];
+ let (_, second_expected_ty) = formal_and_expected_inputs[second_idx];
+ let span = if let (Some((_, first_span)), Some((_, second_span))) = (
+ provided_arg_tys.get(first_idx.to_provided_idx()),
+ provided_arg_tys.get(second_idx.to_provided_idx()),
+ ) {
+ first_span.to(*second_span)
+ } else {
+ args_span
+ };
+ let rendered =
+ if !has_error_or_infer([first_expected_ty, second_expected_ty]) {
+ format!(
+ " of type `{}` and `{}`",
+ first_expected_ty, second_expected_ty
+ )
+ } else {
+ "".to_string()
+ };
+ labels.push((span, format!("two arguments{} are missing", rendered)));
+ suggestion_text = match suggestion_text {
+ SuggestionText::None | SuggestionText::Provide(_) => {
+ SuggestionText::Provide(true)
+ }
+ _ => SuggestionText::DidYouMean,
+ };
+ }
+ &[first_idx, second_idx, third_idx] => {
+ let (_, first_expected_ty) = formal_and_expected_inputs[first_idx];
+ let (_, second_expected_ty) = formal_and_expected_inputs[second_idx];
+ let (_, third_expected_ty) = formal_and_expected_inputs[third_idx];
+ let span = if let (Some((_, first_span)), Some((_, third_span))) = (
+ provided_arg_tys.get(first_idx.to_provided_idx()),
+ provided_arg_tys.get(third_idx.to_provided_idx()),
+ ) {
+ first_span.to(*third_span)
+ } else {
+ args_span
+ };
+ let rendered = if !has_error_or_infer([
+ first_expected_ty,
+ second_expected_ty,
+ third_expected_ty,
+ ]) {
+ format!(
+ " of type `{}`, `{}`, and `{}`",
+ first_expected_ty, second_expected_ty, third_expected_ty
+ )
+ } else {
+ "".to_string()
+ };
+ labels.push((span, format!("three arguments{} are missing", rendered)));
+ suggestion_text = match suggestion_text {
+ SuggestionText::None | SuggestionText::Provide(_) => {
+ SuggestionText::Provide(true)
+ }
+ _ => SuggestionText::DidYouMean,
+ };
+ }
+ missing_idxs => {
+ let first_idx = *missing_idxs.first().unwrap();
+ let last_idx = *missing_idxs.last().unwrap();
+ // NOTE: Because we might be re-arranging arguments, might have extra arguments, etc.
+ // It's hard to *really* know where we should provide this error label, so this is a
+ // decent heuristic
+ let span = if let (Some((_, first_span)), Some((_, last_span))) = (
+ provided_arg_tys.get(first_idx.to_provided_idx()),
+ provided_arg_tys.get(last_idx.to_provided_idx()),
+ ) {
+ first_span.to(*last_span)
+ } else {
+ args_span
+ };
+ labels.push((span, format!("multiple arguments are missing")));
+ suggestion_text = match suggestion_text {
+ SuggestionText::None | SuggestionText::Provide(_) => {
+ SuggestionText::Provide(true)
+ }
+ _ => SuggestionText::DidYouMean,
+ };
+ }
+ }
+ }
+ Error::Swap(
+ first_provided_idx,
+ second_provided_idx,
+ first_expected_idx,
+ second_expected_idx,
+ ) => {
+ let (first_provided_ty, first_span) = provided_arg_tys[first_provided_idx];
+ let (_, first_expected_ty) = formal_and_expected_inputs[first_expected_idx];
+ let first_provided_ty_name = if !has_error_or_infer([first_provided_ty]) {
+ format!(", found `{}`", first_provided_ty)
+ } else {
+ String::new()
+ };
+ labels.push((
+ first_span,
+ format!("expected `{}`{}", first_expected_ty, first_provided_ty_name),
+ ));
+
+ let (second_provided_ty, second_span) = provided_arg_tys[second_provided_idx];
+ let (_, second_expected_ty) = formal_and_expected_inputs[second_expected_idx];
+ let second_provided_ty_name = if !has_error_or_infer([second_provided_ty]) {
+ format!(", found `{}`", second_provided_ty)
+ } else {
+ String::new()
+ };
+ labels.push((
+ second_span,
+ format!("expected `{}`{}", second_expected_ty, second_provided_ty_name),
+ ));
+
+ suggestion_text = match suggestion_text {
+ SuggestionText::None => SuggestionText::Swap,
+ _ => SuggestionText::DidYouMean,
+ };
+ }
+ Error::Permutation(args) => {
+ for (dst_arg, dest_input) in args {
+ let (_, expected_ty) = formal_and_expected_inputs[dst_arg];
+ let (provided_ty, provided_span) = provided_arg_tys[dest_input];
+ let provided_ty_name = if !has_error_or_infer([provided_ty]) {
+ format!(", found `{}`", provided_ty)
+ } else {
+ String::new()
+ };
+ labels.push((
+ provided_span,
+ format!("expected `{}`{}", expected_ty, provided_ty_name),
+ ));
+ }
+
+ suggestion_text = match suggestion_text {
+ SuggestionText::None => SuggestionText::Reorder,
+ _ => SuggestionText::DidYouMean,
+ };
+ }
+ }
+ }
+
+ // If we have less than 5 things to say, it would be useful to call out exactly what's wrong
+ if labels.len() <= 5 {
+ for (span, label) in labels {
+ err.span_label(span, label);
+ }
+ }
+
+ // Call out where the function is defined
+ self.label_fn_like(&mut err, fn_def_id, callee_ty, None, is_method);
+
+ // And add a suggestion block for all of the parameters
+ let suggestion_text = match suggestion_text {
+ SuggestionText::None => None,
+ SuggestionText::Provide(plural) => {
+ Some(format!("provide the argument{}", if plural { "s" } else { "" }))
+ }
+ SuggestionText::Remove(plural) => {
+ Some(format!("remove the extra argument{}", if plural { "s" } else { "" }))
+ }
+ SuggestionText::Swap => Some("swap these arguments".to_string()),
+ SuggestionText::Reorder => Some("reorder these arguments".to_string()),
+ SuggestionText::DidYouMean => Some("did you mean".to_string()),
+ };
+ if let Some(suggestion_text) = suggestion_text {
+ let source_map = self.sess().source_map();
+ let (mut suggestion, suggestion_span) =
+ if let Some(call_span) = full_call_span.find_ancestor_inside(error_span) {
+ ("(".to_string(), call_span.shrink_to_hi().to(error_span.shrink_to_hi()))
+ } else {
+ (
+ format!(
+ "{}(",
+ source_map.span_to_snippet(full_call_span).unwrap_or_else(|_| {
+ fn_def_id.map_or("".to_string(), |fn_def_id| {
+ tcx.item_name(fn_def_id).to_string()
+ })
+ })
+ ),
+ error_span,
+ )
+ };
+ let mut needs_comma = false;
+ for (expected_idx, provided_idx) in matched_inputs.iter_enumerated() {
+ if needs_comma {
+ suggestion += ", ";
+ } else {
+ needs_comma = true;
+ }
+ let suggestion_text = if let Some(provided_idx) = provided_idx
+ && let (_, provided_span) = provided_arg_tys[*provided_idx]
+ && let Ok(arg_text) = source_map.span_to_snippet(provided_span)
+ {
+ arg_text
+ } else {
+ // Propose a placeholder of the correct type
+ let (_, expected_ty) = formal_and_expected_inputs[expected_idx];
+ if expected_ty.is_unit() {
+ "()".to_string()
+ } else if expected_ty.is_suggestable(tcx, false) {
+ format!("/* {} */", expected_ty)
+ } else {
+ "/* value */".to_string()
+ }
+ };
+ suggestion += &suggestion_text;
+ }
+ suggestion += ")";
+ err.span_suggestion_verbose(
+ suggestion_span,
+ &suggestion_text,
+ suggestion,
+ Applicability::HasPlaceholders,
+ );
+ }
+
+ err.emit();
}
// AST fragment checking
}
ast::LitKind::Byte(_) => tcx.types.u8,
ast::LitKind::Char(_) => tcx.types.char,
- ast::LitKind::Int(_, ast::LitIntType::Signed(t)) => tcx.mk_mach_int(t),
- ast::LitKind::Int(_, ast::LitIntType::Unsigned(t)) => tcx.mk_mach_uint(t),
+ ast::LitKind::Int(_, ast::LitIntType::Signed(t)) => tcx.mk_mach_int(ty::int_ty(t)),
+ ast::LitKind::Int(_, ast::LitIntType::Unsigned(t)) => tcx.mk_mach_uint(ty::uint_ty(t)),
ast::LitKind::Int(_, ast::LitIntType::Unsuffixed) => {
let opt_ty = expected.to_option(self).and_then(|ty| match ty.kind() {
ty::Int(_) | ty::Uint(_) => Some(ty),
});
opt_ty.unwrap_or_else(|| self.next_int_var())
}
- ast::LitKind::Float(_, ast::LitFloatType::Suffixed(t)) => tcx.mk_mach_float(t),
+ ast::LitKind::Float(_, ast::LitFloatType::Suffixed(t)) => {
+ tcx.mk_mach_float(ty::float_ty(t))
+ }
ast::LitKind::Float(_, ast::LitFloatType::Unsuffixed) => {
let opt_ty = expected.to_option(self).and_then(|ty| match ty.kind() {
ty::Float(_) => Some(ty),
opt_ty.unwrap_or_else(|| self.next_float_var())
}
ast::LitKind::Bool(_) => tcx.types.bool,
- ast::LitKind::Err(_) => tcx.ty_error(),
+ ast::LitKind::Err => tcx.ty_error(),
}
}
qpath: &QPath<'_>,
hir_id: hir::HirId,
) -> Option<(&'tcx ty::VariantDef, Ty<'tcx>)> {
- let path_span = qpath.qself_span();
+ let path_span = qpath.span();
let (def, ty) = self.finish_resolving_struct_path(qpath, path_span, hir_id);
let variant = match def {
Res::Err => {
return None;
}
Res::Def(DefKind::Variant, _) => match ty.kind() {
- ty::Adt(adt, substs) => Some((adt.variant_of_res(def), adt.did, substs)),
+ ty::Adt(adt, substs) => Some((adt.variant_of_res(def), adt.did(), substs)),
_ => bug!("unexpected type: {:?}", ty),
},
Res::Def(DefKind::Struct | DefKind::Union | DefKind::TyAlias | DefKind::AssocTy, _)
- | Res::SelfTy(..) => match ty.kind() {
+ | Res::SelfTy { .. } => match ty.kind() {
ty::Adt(adt, substs) if !adt.is_enum() => {
- Some((adt.non_enum_variant(), adt.did, substs))
+ Some((adt.non_enum_variant(), adt.did(), substs))
}
_ => None,
},
self.write_user_type_annotation_from_substs(hir_id, did, substs, None);
// Check bounds on type arguments used in the path.
- let (bounds, _) = self.instantiate_bounds(path_span, did, substs);
- let cause =
- traits::ObligationCause::new(path_span, self.body_id, traits::ItemObligation(did));
- self.add_obligations_for_parameters(cause, bounds);
+ self.add_required_obligations_for_hir(path_span, did, substs, hir_id);
Some((variant, ty))
} else {
- struct_span_err!(
- self.tcx.sess,
- path_span,
- E0071,
- "expected struct, variant or union type, found {}",
- ty.sort_string(self.tcx)
- )
- .span_label(path_span, "not a struct")
- .emit();
+ match ty.kind() {
+ ty::Error(_) => {
+ // E0071 might be caused by a spelling error, which will have
+ // already caused an error message and probably a suggestion
+ // elsewhere. Refrain from emitting more unhelpful errors here
+ // (issue #88844).
+ }
+ _ => {
+ struct_span_err!(
+ self.tcx.sess,
+ path_span,
+ E0071,
+ "expected struct, variant or union type, found {}",
+ ty.sort_string(self.tcx)
+ )
+ .span_label(path_span, "not a struct")
+ .emit();
+ }
+ }
None
}
}
pub fn check_decl_initializer(
&self,
- local: &'tcx hir::Local<'tcx>,
+ hir_id: hir::HirId,
+ pat: &'tcx hir::Pat<'tcx>,
init: &'tcx hir::Expr<'tcx>,
) -> Ty<'tcx> {
// FIXME(tschottdorf): `contains_explicit_ref_binding()` must be removed
// for #42640 (default match binding modes).
//
// See #44848.
- let ref_bindings = local.pat.contains_explicit_ref_binding();
+ let ref_bindings = pat.contains_explicit_ref_binding();
- let local_ty = self.local_ty(init.span, local.hir_id).revealed_ty;
+ let local_ty = self.local_ty(init.span, hir_id).revealed_ty;
if let Some(m) = ref_bindings {
// Somewhat subtle: if we have a `ref` binding in the pattern,
// we want to avoid introducing coercions for the RHS. This is
}
}
- /// Type check a `let` statement.
- pub fn check_decl_local(&self, local: &'tcx hir::Local<'tcx>) {
+ pub(in super::super) fn check_decl(&self, decl: Declaration<'tcx>) {
// Determine and write the type which we'll check the pattern against.
- let ty = self.local_ty(local.span, local.hir_id).decl_ty;
- self.write_ty(local.hir_id, ty);
+ let decl_ty = self.local_ty(decl.span, decl.hir_id).decl_ty;
+ self.write_ty(decl.hir_id, decl_ty);
// Type check the initializer.
- if let Some(ref init) = local.init {
- let init_ty = self.check_decl_initializer(local, &init);
- self.overwrite_local_ty_if_err(local, ty, init_ty);
+ if let Some(ref init) = decl.init {
+ let init_ty = self.check_decl_initializer(decl.hir_id, decl.pat, &init);
+ self.overwrite_local_ty_if_err(decl.hir_id, decl.pat, decl_ty, init_ty);
}
// Does the expected pattern type originate from an expression and what is the span?
- let (origin_expr, ty_span) = match (local.ty, local.init) {
+ let (origin_expr, ty_span) = match (decl.ty, decl.init) {
(Some(ty), _) => (false, Some(ty.span)), // Bias towards the explicit user type.
- (_, Some(init)) => (true, Some(init.span)), // No explicit type; so use the scrutinee.
+ (_, Some(init)) => {
+ (true, Some(init.span.find_ancestor_inside(decl.span).unwrap_or(init.span)))
+ } // No explicit type; so use the scrutinee.
_ => (false, None), // We have `let $pat;`, so the expected type is unconstrained.
};
// Type check the pattern. Override if necessary to avoid knock-on errors.
- self.check_pat_top(&local.pat, ty, ty_span, origin_expr);
- let pat_ty = self.node_ty(local.pat.hir_id);
- self.overwrite_local_ty_if_err(local, ty, pat_ty);
+ self.check_pat_top(&decl.pat, decl_ty, ty_span, origin_expr);
+ let pat_ty = self.node_ty(decl.pat.hir_id);
+ self.overwrite_local_ty_if_err(decl.hir_id, decl.pat, decl_ty, pat_ty);
+
+ if let Some(blk) = decl.els {
+ let previous_diverges = self.diverges.get();
+ let else_ty = self.check_block_with_expected(blk, NoExpectation);
+ let cause = self.cause(blk.span, ObligationCauseCode::LetElse);
+ if let Some(mut err) =
+ self.demand_eqtype_with_origin(&cause, self.tcx.types.never, else_ty)
+ {
+ err.emit();
+ }
+ self.diverges.set(previous_diverges);
+ }
}
- pub fn check_stmt(&self, stmt: &'tcx hir::Stmt<'tcx>) {
+ /// Type check a `let` statement.
+ pub fn check_decl_local(&self, local: &'tcx hir::Local<'tcx>) {
+ self.check_decl(local.into());
+ }
+
+ pub fn check_stmt(&self, stmt: &'tcx hir::Stmt<'tcx>, is_last: bool) {
// Don't do all the complex logic below for `DeclItem`.
match stmt.kind {
hir::StmtKind::Item(..) => return,
let old_has_errors = self.has_errors.replace(false);
match stmt.kind {
- hir::StmtKind::Local(ref l) => {
- self.check_decl_local(&l);
+ hir::StmtKind::Local(l) => {
+ self.check_decl_local(l);
}
// Ignore for now.
hir::StmtKind::Item(_) => {}
hir::StmtKind::Expr(ref expr) => {
// Check with expected type of `()`.
self.check_expr_has_type_or_error(&expr, self.tcx.mk_unit(), |err| {
- self.suggest_semicolon_at_end(expr.span, err);
+ if expr.can_have_side_effects() {
+ self.suggest_semicolon_at_end(expr.span, err);
+ }
});
}
hir::StmtKind::Semi(ref expr) => {
- self.check_expr(&expr);
+ // All of this is equivalent to calling `check_expr`, but it is inlined out here
+ // in order to capture the fact that this `match` is the last statement in its
+ // function. This is done for better suggestions to remove the `;`.
+ let expectation = match expr.kind {
+ hir::ExprKind::Match(..) if is_last => IsLast(stmt.span),
+ _ => NoExpectation,
+ };
+ self.check_expr_with_expectation(expr, expectation);
}
}
blk: &'tcx hir::Block<'tcx>,
expected: Expectation<'tcx>,
) -> Ty<'tcx> {
- let prev = {
- let mut fcx_ps = self.ps.borrow_mut();
- let unsafety_state = fcx_ps.recurse(blk);
- replace(&mut *fcx_ps, unsafety_state)
- };
+ let prev = self.ps.replace(self.ps.get().recurse(blk));
// In some cases, blocks have just one exit, but other blocks
// can be targeted by multiple breaks. This can happen both
let ctxt = BreakableCtxt { coerce: Some(coerce), may_break: false };
let (ctxt, ()) = self.with_breakable_ctxt(blk.hir_id, ctxt, || {
- for s in blk.stmts {
- self.check_stmt(s);
+ for (pos, s) in blk.stmts.iter().enumerate() {
+ self.check_stmt(s, blk.stmts.len() - 1 == pos);
}
// check the tail expression **without** holding the
let tail_expr = tail_expr.unwrap();
let span = self.get_expr_coercion_span(tail_expr);
let cause = self.cause(span, ObligationCauseCode::BlockTailExpression(blk.hir_id));
- coerce.coerce(self, &cause, tail_expr, tail_expr_ty);
+ let ty_for_diagnostic = coerce.merged_ty();
+ // We use coerce_inner here because we want to augment the error
+ // suggesting to wrap the block in square brackets if it might've
+ // been mistaken array syntax
+ coerce.coerce_inner(
+ self,
+ &cause,
+ Some(tail_expr),
+ tail_expr_ty,
+ Some(&mut |diag: &mut Diagnostic| {
+ self.suggest_block_to_brackets(diag, blk, tail_expr_ty, ty_for_diagnostic);
+ }),
+ false,
+ );
} else {
// Subtle: if there is no explicit tail expression,
// that is typically equivalent to a tail expression
&self.misc(sp),
&mut |err| {
if let Some(expected_ty) = expected.only_has_type(self) {
- self.consider_hint_about_removing_semicolon(blk, expected_ty, err);
+ if !self.consider_removing_semicolon(blk, expected_ty, err) {
+ self.consider_returning_binding(blk, expected_ty, err);
+ }
+ if expected_ty == self.tcx.types.bool {
+ // If this is caused by a missing `let` in a `while let`,
+ // silence this redundant error, as we already emit E0070.
+
+ // Our block must be a `assign desugar local; assignment`
+ if let Some(hir::Node::Block(hir::Block {
+ stmts:
+ [
+ hir::Stmt {
+ kind:
+ hir::StmtKind::Local(hir::Local {
+ source:
+ hir::LocalSource::AssignDesugar(_),
+ ..
+ }),
+ ..
+ },
+ hir::Stmt {
+ kind:
+ hir::StmtKind::Expr(hir::Expr {
+ kind: hir::ExprKind::Assign(..),
+ ..
+ }),
+ ..
+ },
+ ],
+ ..
+ })) = self.tcx.hir().find(blk.hir_id)
+ {
+ self.comes_from_while_condition(blk.hir_id, |_| {
+ err.downgrade_to_delayed_bug();
+ })
+ }
+ }
}
if let Some(fn_span) = fn_span {
err.span_label(
self.write_ty(blk.hir_id, ty);
- *self.ps.borrow_mut() = prev;
+ self.ps.set(prev);
ty
}
- pub(in super::super) fn check_rustc_args_require_const(
- &self,
- def_id: DefId,
- hir_id: hir::HirId,
- span: Span,
- ) {
- // We're only interested in functions tagged with
- // #[rustc_args_required_const], so ignore anything that's not.
- if !self.tcx.has_attr(def_id, sym::rustc_args_required_const) {
- return;
- }
-
- // If our calling expression is indeed the function itself, we're good!
- // If not, generate an error that this can only be called directly.
- if let Node::Expr(expr) = self.tcx.hir().get(self.tcx.hir().get_parent_node(hir_id)) {
- if let ExprKind::Call(ref callee, ..) = expr.kind {
- if callee.hir_id == hir_id {
- return;
- }
- }
- }
-
- self.tcx.sess.span_err(
- span,
- "this function can only be invoked directly, not through a function pointer",
- );
- }
-
- /// A common error is to add an extra semicolon:
- ///
- /// ```
- /// fn foo() -> usize {
- /// 22;
- /// }
- /// ```
- ///
- /// This routine checks if the final statement in a block is an
- /// expression with an explicit semicolon whose type is compatible
- /// with `expected_ty`. If so, it suggests removing the semicolon.
- fn consider_hint_about_removing_semicolon(
- &self,
- blk: &'tcx hir::Block<'tcx>,
- expected_ty: Ty<'tcx>,
- err: &mut DiagnosticBuilder<'_>,
- ) {
- if let Some((span_semi, boxed)) = self.could_remove_semicolon(blk, expected_ty) {
- if let StatementAsExpression::NeedsBoxing = boxed {
- err.span_suggestion_verbose(
- span_semi,
- "consider removing this semicolon and boxing the expression",
- String::new(),
- Applicability::HasPlaceholders,
- );
- } else {
- err.span_suggestion_short(
- span_semi,
- "consider removing this semicolon",
- String::new(),
- Applicability::MachineApplicable,
- );
- }
- }
- }
-
fn parent_item_span(&self, id: hir::HirId) -> Option<Span> {
- let node = self.tcx.hir().get(self.tcx.hir().get_parent_item(id));
+ let node = self.tcx.hir().get_by_def_id(self.tcx.hir().get_parent_item(id));
match node {
Node::Item(&hir::Item { kind: hir::ItemKind::Fn(_, _, body_id), .. })
| Node::ImplItem(&hir::ImplItem { kind: hir::ImplItemKind::Fn(_, body_id), .. }) => {
/// Given a function block's `HirId`, returns its `FnDecl` if it exists, or `None` otherwise.
fn get_parent_fn_decl(&self, blk_id: hir::HirId) -> Option<(&'tcx hir::FnDecl<'tcx>, Ident)> {
- let parent = self.tcx.hir().get(self.tcx.hir().get_parent_item(blk_id));
+ let parent = self.tcx.hir().get_by_def_id(self.tcx.hir().get_parent_item(blk_id));
self.get_node_fn_decl(parent).map(|(fn_decl, ident, _)| (fn_decl, ident))
}
/// // ^^^^ point at this instead of the whole `if` expression
/// ```
fn get_expr_coercion_span(&self, expr: &hir::Expr<'_>) -> rustc_span::Span {
- if let hir::ExprKind::Match(_, arms, _) = &expr.kind {
- let arm_spans: Vec<Span> = arms
- .iter()
- .filter_map(|arm| {
- self.in_progress_typeck_results
- .and_then(|typeck_results| {
- typeck_results.borrow().node_type_opt(arm.body.hir_id)
- })
- .and_then(|arm_ty| {
- if arm_ty.is_never() {
- None
- } else {
- Some(match &arm.body.kind {
- // Point at the tail expression when possible.
- hir::ExprKind::Block(block, _) => {
- block.expr.as_ref().map(|e| e.span).unwrap_or(block.span)
- }
- _ => arm.body.span,
- })
- }
- })
- })
- .collect();
- if arm_spans.len() == 1 {
- return arm_spans[0];
+ let check_in_progress = |elem: &hir::Expr<'_>| {
+ self.typeck_results.borrow().node_type_opt(elem.hir_id).filter(|ty| !ty.is_never()).map(
+ |_| match elem.kind {
+ // Point at the tail expression when possible.
+ hir::ExprKind::Block(block, _) => block.expr.map_or(block.span, |e| e.span),
+ _ => elem.span,
+ },
+ )
+ };
+
+ if let hir::ExprKind::If(_, _, Some(el)) = expr.kind {
+ if let Some(rslt) = check_in_progress(el) {
+ return rslt;
}
}
+
+ if let hir::ExprKind::Match(_, arms, _) = expr.kind {
+ let mut iter = arms.iter().filter_map(|arm| check_in_progress(arm.body));
+ if let Some(span) = iter.next() {
+ if iter.next().is_none() {
+ return span;
+ }
+ }
+ }
+
expr.span
}
fn overwrite_local_ty_if_err(
&self,
- local: &'tcx hir::Local<'tcx>,
+ hir_id: hir::HirId,
+ pat: &'tcx hir::Pat<'tcx>,
decl_ty: Ty<'tcx>,
ty: Ty<'tcx>,
) {
if ty.references_error() {
// Override the types everywhere with `err()` to avoid knock on errors.
- self.write_ty(local.hir_id, ty);
- self.write_ty(local.pat.hir_id, ty);
+ self.write_ty(hir_id, ty);
+ self.write_ty(pat.hir_id, ty);
let local_ty = LocalTy { decl_ty, revealed_ty: ty };
- self.locals.borrow_mut().insert(local.hir_id, local_ty);
- self.locals.borrow_mut().insert(local.pat.hir_id, local_ty);
+ self.locals.borrow_mut().insert(hir_id, local_ty);
+ self.locals.borrow_mut().insert(pat.hir_id, local_ty);
}
}
match *qpath {
QPath::Resolved(ref maybe_qself, ref path) => {
let self_ty = maybe_qself.as_ref().map(|qself| self.to_ty(qself));
- let ty = AstConv::res_to_ty(self, self_ty, path, true);
+ let ty = <dyn AstConv<'_>>::res_to_ty(self, self_ty, path, true);
(path.res, ty)
}
QPath::TypeRelative(ref qself, ref segment) => {
let ty = self.to_ty(qself);
- let res = if let hir::TyKind::Path(QPath::Resolved(_, ref path)) = qself.kind {
- path.res
- } else {
- Res::Err
- };
- let result =
- AstConv::associated_path_to_ty(self, hir_id, path_span, ty, res, segment, true);
+ let result = <dyn AstConv<'_>>::associated_path_to_ty(
+ self, hir_id, path_span, ty, qself, segment, true,
+ );
let ty = result.map(|(ty, _, _)| ty).unwrap_or_else(|_| self.tcx().ty_error());
let result = result.map(|(_, kind, def_id)| (kind, def_id));
// Write back the new resolution.
self.write_resolution(hir_id, result);
- (result.map(|(kind, def_id)| Res::Def(kind, def_id)).unwrap_or(Res::Err), ty)
+ (result.map_or(Res::Err, |(kind, def_id)| Res::Def(kind, def_id)), ty)
}
- QPath::LangItem(lang_item, span) => {
- self.resolve_lang_item_path(lang_item, span, hir_id)
+ QPath::LangItem(lang_item, span, id) => {
+ self.resolve_lang_item_path(lang_item, span, hir_id, id)
}
}
}
- /// Given a vec of evaluated `FulfillmentError`s and an `fn` call argument expressions, we walk
- /// the checked and coerced types for each argument to see if any of the `FulfillmentError`s
- /// reference a type argument. The reason to walk also the checked type is that the coerced type
- /// can be not easily comparable with predicate type (because of coercion). If the types match
- /// for either checked or coerced type, and there's only *one* argument that does, we point at
- /// the corresponding argument's expression span instead of the `fn` call path span.
- fn point_at_arg_instead_of_call_if_possible(
+ /// Given a vector of fulfillment errors, try to adjust the spans of the
+ /// errors to more accurately point at the cause of the failure.
+ ///
+ /// This applies to calls, methods, and struct expressions. This will also
+ /// try to deduplicate errors that are due to the same cause but might
+ /// have been created with different [`ObligationCause`][traits::ObligationCause]s.
+ pub(super) fn adjust_fulfillment_errors_for_expr_obligation(
&self,
errors: &mut Vec<traits::FulfillmentError<'tcx>>,
- final_arg_types: &[(usize, Ty<'tcx>, Ty<'tcx>)],
- call_sp: Span,
- args: &'tcx [hir::Expr<'tcx>],
) {
- // We *do not* do this for desugared call spans to keep good diagnostics when involving
- // the `?` operator.
- if call_sp.desugaring_kind().is_some() {
- return;
- }
+ // Store a mapping from `(Span, Predicate) -> ObligationCause`, so that
+ // other errors that have the same span and predicate can also get fixed,
+ // even if their `ObligationCauseCode` isn't an `Expr*Obligation` kind.
+ // This is important since if we adjust one span but not the other, then
+ // we will have "duplicated" the error on the UI side.
+ let mut remap_cause = FxHashSet::default();
+ let mut not_adjusted = vec![];
for error in errors {
- // Only if the cause is somewhere inside the expression we want try to point at arg.
- // Otherwise, it means that the cause is somewhere else and we should not change
- // anything because we can break the correct span.
- if !call_sp.contains(error.obligation.cause.span) {
- continue;
+ let before_span = error.obligation.cause.span;
+ if self.adjust_fulfillment_error_for_expr_obligation(error)
+ || before_span != error.obligation.cause.span
+ {
+ // Store both the predicate and the predicate *without constness*
+ // since sometimes we instantiate and check both of these in a
+ // method call, for example.
+ remap_cause.insert((
+ before_span,
+ error.obligation.predicate,
+ error.obligation.cause.clone(),
+ ));
+ remap_cause.insert((
+ before_span,
+ error.obligation.predicate.without_const(self.tcx),
+ error.obligation.cause.clone(),
+ ));
+ } else {
+ // If it failed to be adjusted once around, it may be adjusted
+ // via the "remap cause" mapping the second time...
+ not_adjusted.push(error);
}
+ }
- if let ty::PredicateAtom::Trait(predicate, _) =
- error.obligation.predicate.skip_binders()
- {
- // Collect the argument position for all arguments that could have caused this
- // `FulfillmentError`.
- let mut referenced_in = final_arg_types
- .iter()
- .map(|&(i, checked_ty, _)| (i, checked_ty))
- .chain(final_arg_types.iter().map(|&(i, _, coerced_ty)| (i, coerced_ty)))
- .flat_map(|(i, ty)| {
- let ty = self.resolve_vars_if_possible(&ty);
- // We walk the argument type because the argument's type could have
- // been `Option<T>`, but the `FulfillmentError` references `T`.
- if ty.walk().any(|arg| arg == predicate.self_ty().into()) {
- Some(i)
- } else {
- None
+ for error in not_adjusted {
+ for (span, predicate, cause) in &remap_cause {
+ if *predicate == error.obligation.predicate
+ && span.contains(error.obligation.cause.span)
+ {
+ error.obligation.cause = cause.clone();
+ continue;
+ }
+ }
+ }
+ }
+
+ fn adjust_fulfillment_error_for_expr_obligation(
+ &self,
+ error: &mut traits::FulfillmentError<'tcx>,
+ ) -> bool {
+ let (traits::ExprItemObligation(def_id, hir_id, idx) | traits::ExprBindingObligation(def_id, _, hir_id, idx))
+ = *error.obligation.cause.code().peel_derives() else { return false; };
+ let hir = self.tcx.hir();
+ let hir::Node::Expr(expr) = hir.get(hir_id) else { return false; };
+
+ // Skip over mentioning async lang item
+ if Some(def_id) == self.tcx.lang_items().from_generator_fn()
+ && error.obligation.cause.span.desugaring_kind()
+ == Some(rustc_span::DesugaringKind::Async)
+ {
+ return false;
+ }
+
+ let Some(unsubstituted_pred) =
+ self.tcx.predicates_of(def_id).instantiate_identity(self.tcx).predicates.into_iter().nth(idx)
+ else { return false; };
+
+ let generics = self.tcx.generics_of(def_id);
+ let predicate_substs = match unsubstituted_pred.kind().skip_binder() {
+ ty::PredicateKind::Trait(pred) => pred.trait_ref.substs,
+ ty::PredicateKind::Projection(pred) => pred.projection_ty.substs,
+ _ => ty::List::empty(),
+ };
+
+ let find_param_matching = |matches: &dyn Fn(&ty::ParamTy) -> bool| {
+ predicate_substs.types().find_map(|ty| {
+ ty.walk().find_map(|arg| {
+ if let ty::GenericArgKind::Type(ty) = arg.unpack()
+ && let ty::Param(param_ty) = ty.kind()
+ && matches(param_ty)
+ {
+ Some(arg)
+ } else {
+ None
+ }
+ })
+ })
+ };
+
+ // Prefer generics that are local to the fn item, since these are likely
+ // to be the cause of the unsatisfied predicate.
+ let mut param_to_point_at = find_param_matching(&|param_ty| {
+ self.tcx.parent(generics.type_param(param_ty, self.tcx).def_id) == def_id
+ });
+ // Fall back to generic that isn't local to the fn item. This will come
+ // from a trait or impl, for example.
+ let mut fallback_param_to_point_at = find_param_matching(&|param_ty| {
+ self.tcx.parent(generics.type_param(param_ty, self.tcx).def_id) != def_id
+ && param_ty.name != rustc_span::symbol::kw::SelfUpper
+ });
+ // Finally, the `Self` parameter is possibly the reason that the predicate
+ // is unsatisfied. This is less likely to be true for methods, because
+ // method probe means that we already kinda check that the predicates due
+ // to the `Self` type are true.
+ let mut self_param_to_point_at =
+ find_param_matching(&|param_ty| param_ty.name == rustc_span::symbol::kw::SelfUpper);
+
+ // Finally, for ambiguity-related errors, we actually want to look
+ // for a parameter that is the source of the inference type left
+ // over in this predicate.
+ if let traits::FulfillmentErrorCode::CodeAmbiguity = error.code {
+ fallback_param_to_point_at = None;
+ self_param_to_point_at = None;
+ param_to_point_at =
+ self.find_ambiguous_parameter_in(def_id, error.root_obligation.predicate);
+ }
+
+ if self.closure_span_overlaps_error(error, expr.span) {
+ return false;
+ }
+
+ match &expr.kind {
+ hir::ExprKind::Path(qpath) => {
+ if let hir::Node::Expr(hir::Expr {
+ kind: hir::ExprKind::Call(callee, args),
+ hir_id: call_hir_id,
+ span: call_span,
+ ..
+ }) = hir.get(hir.get_parent_node(expr.hir_id))
+ && callee.hir_id == expr.hir_id
+ {
+ if self.closure_span_overlaps_error(error, *call_span) {
+ return false;
+ }
+
+ for param in
+ [param_to_point_at, fallback_param_to_point_at, self_param_to_point_at]
+ .into_iter()
+ .flatten()
+ {
+ if self.point_at_arg_if_possible(
+ error,
+ def_id,
+ param,
+ *call_hir_id,
+ callee.span,
+ None,
+ args,
+ )
+ {
+ return true;
+ }
+ }
+ }
+ // Notably, we only point to params that are local to the
+ // item we're checking, since those are the ones we are able
+ // to look in the final `hir::PathSegment` for. Everything else
+ // would require a deeper search into the `qpath` than I think
+ // is worthwhile.
+ if let Some(param_to_point_at) = param_to_point_at
+ && self.point_at_path_if_possible(error, def_id, param_to_point_at, qpath)
+ {
+ return true;
+ }
+ }
+ hir::ExprKind::MethodCall(segment, receiver, args, ..) => {
+ for param in [param_to_point_at, fallback_param_to_point_at, self_param_to_point_at]
+ .into_iter()
+ .flatten()
+ {
+ if self.point_at_arg_if_possible(
+ error,
+ def_id,
+ param,
+ hir_id,
+ segment.ident.span,
+ Some(receiver),
+ args,
+ ) {
+ return true;
+ }
+ }
+ if let Some(param_to_point_at) = param_to_point_at
+ && self.point_at_generic_if_possible(error, def_id, param_to_point_at, segment)
+ {
+ return true;
+ }
+ }
+ hir::ExprKind::Struct(qpath, fields, ..) => {
+ if let Res::Def(DefKind::Struct | DefKind::Variant, variant_def_id) =
+ self.typeck_results.borrow().qpath_res(qpath, hir_id)
+ {
+ for param in
+ [param_to_point_at, fallback_param_to_point_at, self_param_to_point_at]
+ {
+ if let Some(param) = param
+ && self.point_at_field_if_possible(
+ error,
+ def_id,
+ param,
+ variant_def_id,
+ fields,
+ )
+ {
+ return true;
}
- })
- .collect::<Vec<usize>>();
+ }
+ }
+ if let Some(param_to_point_at) = param_to_point_at
+ && self.point_at_path_if_possible(error, def_id, param_to_point_at, qpath)
+ {
+ return true;
+ }
+ }
+ _ => {}
+ }
+
+ false
+ }
+
+ fn closure_span_overlaps_error(
+ &self,
+ error: &traits::FulfillmentError<'tcx>,
+ span: Span,
+ ) -> bool {
+ if let traits::FulfillmentErrorCode::CodeSelectionError(
+ traits::SelectionError::OutputTypeParameterMismatch(_, expected, _),
+ ) = error.code
+ && let ty::Closure(def_id, _) | ty::Generator(def_id, ..) = expected.skip_binder().self_ty().kind()
+ && span.overlaps(self.tcx.def_span(*def_id))
+ {
+ true
+ } else {
+ false
+ }
+ }
+
+ fn point_at_arg_if_possible(
+ &self,
+ error: &mut traits::FulfillmentError<'tcx>,
+ def_id: DefId,
+ param_to_point_at: ty::GenericArg<'tcx>,
+ call_hir_id: hir::HirId,
+ callee_span: Span,
+ receiver: Option<&'tcx hir::Expr<'tcx>>,
+ args: &'tcx [hir::Expr<'tcx>],
+ ) -> bool {
+ let sig = self.tcx.fn_sig(def_id).skip_binder();
+ let args_referencing_param: Vec<_> = sig
+ .inputs()
+ .iter()
+ .enumerate()
+ .filter(|(_, ty)| find_param_in_ty(**ty, param_to_point_at))
+ .collect();
+ // If there's one field that references the given generic, great!
+ if let [(idx, _)] = args_referencing_param.as_slice()
+ && let Some(arg) = receiver
+ .map_or(args.get(*idx), |rcvr| if *idx == 0 { Some(rcvr) } else { args.get(*idx - 1) }) {
+ error.obligation.cause.span = arg.span.find_ancestor_in_same_ctxt(error.obligation.cause.span).unwrap_or(arg.span);
+ error.obligation.cause.map_code(|parent_code| {
+ ObligationCauseCode::FunctionArgumentObligation {
+ arg_hir_id: arg.hir_id,
+ call_hir_id,
+ parent_code,
+ }
+ });
+ return true;
+ } else if args_referencing_param.len() > 0 {
+ // If more than one argument applies, then point to the callee span at least...
+ // We have chance to fix this up further in `point_at_generics_if_possible`
+ error.obligation.cause.span = callee_span;
+ }
+
+ false
+ }
- // Both checked and coerced types could have matched, thus we need to remove
- // duplicates.
+ fn point_at_field_if_possible(
+ &self,
+ error: &mut traits::FulfillmentError<'tcx>,
+ def_id: DefId,
+ param_to_point_at: ty::GenericArg<'tcx>,
+ variant_def_id: DefId,
+ expr_fields: &[hir::ExprField<'tcx>],
+ ) -> bool {
+ let def = self.tcx.adt_def(def_id);
+
+ let identity_substs = ty::InternalSubsts::identity_for_item(self.tcx, def_id);
+ let fields_referencing_param: Vec<_> = def
+ .variant_with_id(variant_def_id)
+ .fields
+ .iter()
+ .filter(|field| {
+ let field_ty = field.ty(self.tcx, identity_substs);
+ find_param_in_ty(field_ty, param_to_point_at)
+ })
+ .collect();
+
+ if let [field] = fields_referencing_param.as_slice() {
+ for expr_field in expr_fields {
+ // Look for the ExprField that matches the field, using the
+ // same rules that check_expr_struct uses for macro hygiene.
+ if self.tcx.adjust_ident(expr_field.ident, variant_def_id) == field.ident(self.tcx)
+ {
+ error.obligation.cause.span = expr_field
+ .expr
+ .span
+ .find_ancestor_in_same_ctxt(error.obligation.cause.span)
+ .unwrap_or(expr_field.span);
+ return true;
+ }
+ }
+ }
- // We sort primitive type usize here and can use unstable sort
- referenced_in.sort_unstable();
- referenced_in.dedup();
+ false
+ }
- if let (Some(ref_in), None) = (referenced_in.pop(), referenced_in.pop()) {
- // We make sure that only *one* argument matches the obligation failure
- // and we assign the obligation's span to its expression's.
- error.obligation.cause.make_mut().span = args[ref_in].span;
- error.points_at_arg_span = true;
+ fn point_at_path_if_possible(
+ &self,
+ error: &mut traits::FulfillmentError<'tcx>,
+ def_id: DefId,
+ param: ty::GenericArg<'tcx>,
+ qpath: &QPath<'tcx>,
+ ) -> bool {
+ match qpath {
+ hir::QPath::Resolved(_, path) => {
+ if let Some(segment) = path.segments.last()
+ && self.point_at_generic_if_possible(error, def_id, param, segment)
+ {
+ return true;
+ }
+ }
+ hir::QPath::TypeRelative(_, segment) => {
+ if self.point_at_generic_if_possible(error, def_id, param, segment) {
+ return true;
}
}
+ _ => {}
}
+
+ false
}
- /// Given a vec of evaluated `FulfillmentError`s and an `fn` call expression, we walk the
- /// `PathSegment`s and resolve their type parameters to see if any of the `FulfillmentError`s
- /// were caused by them. If they were, we point at the corresponding type argument's span
- /// instead of the `fn` call path span.
- fn point_at_type_arg_instead_of_call_if_possible(
+ fn point_at_generic_if_possible(
&self,
- errors: &mut Vec<traits::FulfillmentError<'tcx>>,
- call_expr: &'tcx hir::Expr<'tcx>,
+ error: &mut traits::FulfillmentError<'tcx>,
+ def_id: DefId,
+ param_to_point_at: ty::GenericArg<'tcx>,
+ segment: &hir::PathSegment<'tcx>,
+ ) -> bool {
+ let own_substs = self
+ .tcx
+ .generics_of(def_id)
+ .own_substs(ty::InternalSubsts::identity_for_item(self.tcx, def_id));
+ let Some((index, _)) = own_substs
+ .iter()
+ .filter(|arg| matches!(arg.unpack(), ty::GenericArgKind::Type(_)))
+ .enumerate()
+ .find(|(_, arg)| **arg == param_to_point_at) else { return false };
+ let Some(arg) = segment
+ .args()
+ .args
+ .iter()
+ .filter(|arg| matches!(arg, hir::GenericArg::Type(_)))
+ .nth(index) else { return false; };
+ error.obligation.cause.span = arg
+ .span()
+ .find_ancestor_in_same_ctxt(error.obligation.cause.span)
+ .unwrap_or(arg.span());
+ true
+ }
+
+ fn find_ambiguous_parameter_in<T: TypeVisitable<'tcx>>(
+ &self,
+ item_def_id: DefId,
+ t: T,
+ ) -> Option<ty::GenericArg<'tcx>> {
+ struct FindAmbiguousParameter<'a, 'tcx>(&'a FnCtxt<'a, 'tcx>, DefId);
+ impl<'tcx> TypeVisitor<'tcx> for FindAmbiguousParameter<'_, 'tcx> {
+ type BreakTy = ty::GenericArg<'tcx>;
+ fn visit_ty(&mut self, ty: Ty<'tcx>) -> std::ops::ControlFlow<Self::BreakTy> {
+ if let Some(origin) = self.0.type_var_origin(ty)
+ && let TypeVariableOriginKind::TypeParameterDefinition(_, Some(def_id)) =
+ origin.kind
+ && let generics = self.0.tcx.generics_of(self.1)
+ && let Some(index) = generics.param_def_id_to_index(self.0.tcx, def_id)
+ && let Some(subst) = ty::InternalSubsts::identity_for_item(self.0.tcx, self.1)
+ .get(index as usize)
+ {
+ ControlFlow::Break(*subst)
+ } else {
+ ty.super_visit_with(self)
+ }
+ }
+ }
+ t.visit_with(&mut FindAmbiguousParameter(self, item_def_id)).break_value()
+ }
+
+ fn label_fn_like(
+ &self,
+ err: &mut Diagnostic,
+ callable_def_id: Option<DefId>,
+ callee_ty: Option<Ty<'tcx>>,
+ // A specific argument should be labeled, instead of all of them
+ expected_idx: Option<usize>,
+ is_method: bool,
) {
- if let hir::ExprKind::Call(path, _) = &call_expr.kind {
- if let hir::ExprKind::Path(qpath) = &path.kind {
- if let hir::QPath::Resolved(_, path) = &qpath {
- for error in errors {
- if let ty::PredicateAtom::Trait(predicate, _) =
- error.obligation.predicate.skip_binders()
+ let Some(mut def_id) = callable_def_id else {
+ return;
+ };
+
+ if let Some(assoc_item) = self.tcx.opt_associated_item(def_id)
+ // Possibly points at either impl or trait item, so try to get it
+ // to point to trait item, then get the parent.
+ // This parent might be an impl in the case of an inherent function,
+ // but the next check will fail.
+ && let maybe_trait_item_def_id = assoc_item.trait_item_def_id.unwrap_or(def_id)
+ && let maybe_trait_def_id = self.tcx.parent(maybe_trait_item_def_id)
+ // Just an easy way to check "trait_def_id == Fn/FnMut/FnOnce"
+ && let Some(call_kind) = ty::ClosureKind::from_def_id(self.tcx, maybe_trait_def_id)
+ && let Some(callee_ty) = callee_ty
+ {
+ let callee_ty = callee_ty.peel_refs();
+ match *callee_ty.kind() {
+ ty::Param(param) => {
+ let param =
+ self.tcx.generics_of(self.body_id.owner).type_param(¶m, self.tcx);
+ if param.kind.is_synthetic() {
+ // if it's `impl Fn() -> ..` then just fall down to the def-id based logic
+ def_id = param.def_id;
+ } else {
+ // Otherwise, find the predicate that makes this generic callable,
+ // and point at that.
+ let instantiated = self
+ .tcx
+ .explicit_predicates_of(self.body_id.owner)
+ .instantiate_identity(self.tcx);
+ // FIXME(compiler-errors): This could be problematic if something has two
+ // fn-like predicates with different args, but callable types really never
+ // do that, so it's OK.
+ for (predicate, span) in
+ std::iter::zip(instantiated.predicates, instantiated.spans)
{
- // If any of the type arguments in this path segment caused the
- // `FullfillmentError`, point at its span (#61860).
- for arg in path
- .segments
- .iter()
- .filter_map(|seg| seg.args.as_ref())
- .flat_map(|a| a.args.iter())
+ if let ty::PredicateKind::Trait(pred) = predicate.kind().skip_binder()
+ && pred.self_ty().peel_refs() == callee_ty
+ && ty::ClosureKind::from_def_id(self.tcx, pred.def_id()).is_some()
{
- if let hir::GenericArg::Type(hir_ty) = &arg {
- if let hir::TyKind::Path(hir::QPath::TypeRelative(..)) =
- &hir_ty.kind
- {
- // Avoid ICE with associated types. As this is best
- // effort only, it's ok to ignore the case. It
- // would trigger in `is_send::<T::AssocType>();`
- // from `typeck-default-trait-impl-assoc-type.rs`.
- } else {
- let ty = AstConv::ast_ty_to_ty(self, hir_ty);
- let ty = self.resolve_vars_if_possible(&ty);
- if ty == predicate.self_ty() {
- error.obligation.cause.make_mut().span = hir_ty.span;
- }
- }
- }
+ err.span_note(span, "callable defined here");
+ return;
+ }
+ }
+ }
+ }
+ ty::Opaque(new_def_id, _)
+ | ty::Closure(new_def_id, _)
+ | ty::FnDef(new_def_id, _) => {
+ def_id = new_def_id;
+ }
+ _ => {
+ // Look for a user-provided impl of a `Fn` trait, and point to it.
+ let new_def_id = self.probe(|_| {
+ let trait_ref = ty::TraitRef::new(
+ call_kind.to_def_id(self.tcx),
+ self.tcx.mk_substs(
+ [
+ ty::GenericArg::from(callee_ty),
+ self.next_ty_var(TypeVariableOrigin {
+ kind: TypeVariableOriginKind::MiscVariable,
+ span: rustc_span::DUMMY_SP,
+ })
+ .into(),
+ ]
+ .into_iter(),
+ ),
+ );
+ let obligation = traits::Obligation::new(
+ traits::ObligationCause::dummy(),
+ self.param_env,
+ ty::Binder::dummy(ty::TraitPredicate {
+ trait_ref,
+ constness: ty::BoundConstness::NotConst,
+ polarity: ty::ImplPolarity::Positive,
+ }),
+ );
+ match SelectionContext::new(&self).select(&obligation) {
+ Ok(Some(traits::ImplSource::UserDefined(impl_source))) => {
+ Some(impl_source.impl_def_id)
}
+ _ => None,
}
+ });
+ if let Some(new_def_id) = new_def_id {
+ def_id = new_def_id;
+ } else {
+ return;
}
}
}
}
+
+ if let Some(def_span) = self.tcx.def_ident_span(def_id) && !def_span.is_dummy() {
+ let mut spans: MultiSpan = def_span.into();
+
+ let params = self
+ .tcx
+ .hir()
+ .get_if_local(def_id)
+ .and_then(|node| node.body_id())
+ .into_iter()
+ .flat_map(|id| self.tcx.hir().body(id).params)
+ .skip(if is_method { 1 } else { 0 });
+
+ for (_, param) in params
+ .into_iter()
+ .enumerate()
+ .filter(|(idx, _)| expected_idx.map_or(true, |expected_idx| expected_idx == *idx))
+ {
+ spans.push_span_label(param.span, "");
+ }
+
+ let def_kind = self.tcx.def_kind(def_id);
+ err.span_note(spans, &format!("{} defined here", def_kind.descr(def_id)));
+ } else if let Some(hir::Node::Expr(e)) = self.tcx.hir().get_if_local(def_id)
+ && let hir::ExprKind::Closure(hir::Closure { body, .. }) = &e.kind
+ {
+ let param = expected_idx
+ .and_then(|expected_idx| self.tcx.hir().body(*body).params.get(expected_idx));
+ let (kind, span) = if let Some(param) = param {
+ ("closure parameter", param.span)
+ } else {
+ ("closure", self.tcx.def_span(def_id))
+ };
+ err.span_note(span, &format!("{} defined here", kind));
+ } else {
+ let def_kind = self.tcx.def_kind(def_id);
+ err.span_note(
+ self.tcx.def_span(def_id),
+ &format!("{} defined here", def_kind.descr(def_id)),
+ );
+ }
+ }
+}
+
+fn find_param_in_ty<'tcx>(ty: Ty<'tcx>, param_to_point_at: ty::GenericArg<'tcx>) -> bool {
+ let mut walk = ty.walk();
+ while let Some(arg) = walk.next() {
+ if arg == param_to_point_at {
+ return true;
+ } else if let ty::GenericArgKind::Type(ty) = arg.unpack()
+ && let ty::Projection(..) = ty.kind()
+ {
+ // This logic may seem a bit strange, but typically when
+ // we have a projection type in a function signature, the
+ // argument that's being passed into that signature is
+ // not actually constraining that projection's substs in
+ // a meaningful way. So we skip it, and see improvements
+ // in some UI tests.
+ walk.skip_current_subtree();
+ }
}
+ false
}