| 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::*; |
| use crate::check::{ |
| 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_data_structures::fx::FxHashSet; |
| use rustc_errors::{pluralize, Applicability, Diagnostic, DiagnosticId, MultiSpan}; |
| use rustc_hir as hir; |
| 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::visit::TypeVisitable; |
| use rustc_middle::ty::{self, DefIdTree, IsSuggestable, Ty, TypeSuperVisitable, TypeVisitor}; |
| use rustc_session::Session; |
| use rustc_span::symbol::Ident; |
| use rustc_span::{self, sym, Span}; |
| use rustc_trait_selection::traits::{self, ObligationCauseCode, SelectionContext}; |
| |
| 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, |
| expr: &'tcx hir::Expr<'tcx>, |
| method: Result<MethodCallee<'tcx>, ()>, |
| args_no_rcvr: &'tcx [hir::Expr<'tcx>], |
| tuple_arguments: TupleArgumentsFlag, |
| expected: Expectation<'tcx>, |
| ) -> Ty<'tcx> { |
| let has_error = match method { |
| Ok(method) => method.substs.references_error() || method.sig.references_error(), |
| Err(_) => true, |
| }; |
| if has_error { |
| let err_inputs = self.err_args(args_no_rcvr.len()); |
| |
| let err_inputs = match tuple_arguments { |
| DontTupleArguments => err_inputs, |
| TupleArguments => vec![self.tcx.intern_tup(&err_inputs)], |
| }; |
| |
| self.check_argument_types( |
| sp, |
| expr, |
| &err_inputs, |
| None, |
| args_no_rcvr, |
| false, |
| tuple_arguments, |
| 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_input_tys = self.expected_inputs_for_expected_output( |
| sp, |
| expected, |
| method.sig.output(), |
| &method.sig.inputs()[1..], |
| ); |
| self.check_argument_types( |
| sp, |
| expr, |
| &method.sig.inputs()[1..], |
| expected_input_tys, |
| args_no_rcvr, |
| method.sig.c_variadic, |
| tuple_arguments, |
| Some(method.def_id), |
| ); |
| method.sig.output() |
| } |
| |
| /// Generic function that factors out common logic from function calls, |
| /// method calls and overloaded operators. |
| pub(in super::super) fn check_argument_types( |
| &self, |
| // 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, |
| // The DefId for the function being called, for better error messages |
| fn_def_id: Option<DefId>, |
| ) { |
| let tcx = self.tcx; |
| |
| // 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 iter::zip(formal_input_tys, provided_args) { |
| self.register_wf_obligation(fn_input_ty.into(), arg_expr.span, traits::MiscObligation); |
| } |
| |
| let mut err_code = "E0061"; |
| |
| // 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() { |
| // We expected a tuple and got a tuple |
| ty::Tuple(arg_types) => { |
| // 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 => None, |
| }; |
| (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, |
| call_span, |
| E0059, |
| "cannot use call notation; the first type parameter \ |
| for the function trait is neither a tuple nor unit" |
| ) |
| .emit(); |
| (self.err_args(provided_args.len()), None) |
| } |
| } |
| } else { |
| (formal_input_tys.to_vec(), expected_input_tys) |
| }; |
| |
| // 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() |
| }; |
| |
| 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); |
| |
| // 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]; |
| |
| 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] { |
| // 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, |_| {}) |
| } |
| |
| // 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!(), || {})`. |
| if !check_closures { |
| self.warn_if_unreachable(arg.hir_id, arg.span, "expression"); |
| } |
| |
| // 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; |
| } |
| |
| let compatible = demand_compatible(idx); |
| let is_compatible = matches!(compatible, Compatibility::Compatible); |
| compatibility_diagonal[idx] = compatible; |
| |
| if !is_compatible { |
| call_appears_satisfied = false; |
| } |
| } |
| } |
| |
| if c_variadic && provided_arg_count < minimum_input_count { |
| err_code = "E0060"; |
| } |
| |
| 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(ty::FloatTy::F32) => { |
| variadic_error(tcx.sess, arg.span, arg_ty, "c_double"); |
| } |
| ty::Int(ty::IntTy::I8 | ty::IntTy::I16) | ty::Bool => { |
| variadic_error(tcx.sess, arg.span, arg_ty, "c_int"); |
| } |
| 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); |
| 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 |
| pub(in super::super) fn check_lit( |
| &self, |
| lit: &hir::Lit, |
| expected: Expectation<'tcx>, |
| ) -> Ty<'tcx> { |
| let tcx = self.tcx; |
| |
| match lit.node { |
| ast::LitKind::Str(..) => tcx.mk_static_str(), |
| ast::LitKind::ByteStr(ref v) => { |
| tcx.mk_imm_ref(tcx.lifetimes.re_static, tcx.mk_array(tcx.types.u8, v.len() as u64)) |
| } |
| ast::LitKind::Byte(_) => tcx.types.u8, |
| ast::LitKind::Char(_) => tcx.types.char, |
| 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), |
| ty::Char => Some(tcx.types.u8), |
| ty::RawPtr(..) => Some(tcx.types.usize), |
| ty::FnDef(..) | ty::FnPtr(_) => Some(tcx.types.usize), |
| _ => None, |
| }); |
| opt_ty.unwrap_or_else(|| self.next_int_var()) |
| } |
| 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), |
| _ => None, |
| }); |
| opt_ty.unwrap_or_else(|| self.next_float_var()) |
| } |
| ast::LitKind::Bool(_) => tcx.types.bool, |
| ast::LitKind::Err => tcx.ty_error(), |
| } |
| } |
| |
| pub fn check_struct_path( |
| &self, |
| qpath: &QPath<'_>, |
| hir_id: hir::HirId, |
| ) -> Option<(&'tcx ty::VariantDef, Ty<'tcx>)> { |
| let path_span = qpath.span(); |
| let (def, ty) = self.finish_resolving_struct_path(qpath, path_span, hir_id); |
| let variant = match def { |
| Res::Err => { |
| self.set_tainted_by_errors(); |
| return None; |
| } |
| Res::Def(DefKind::Variant, _) => match ty.kind() { |
| 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() { |
| ty::Adt(adt, substs) if !adt.is_enum() => { |
| Some((adt.non_enum_variant(), adt.did(), substs)) |
| } |
| _ => None, |
| }, |
| _ => bug!("unexpected definition: {:?}", def), |
| }; |
| |
| if let Some((variant, did, substs)) = variant { |
| debug!("check_struct_path: did={:?} substs={:?}", did, substs); |
| self.write_user_type_annotation_from_substs(hir_id, did, substs, None); |
| |
| // Check bounds on type arguments used in the path. |
| self.add_required_obligations_for_hir(path_span, did, substs, hir_id); |
| |
| Some((variant, ty)) |
| } else { |
| 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, |
| 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 = pat.contains_explicit_ref_binding(); |
| |
| 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 |
| // both because it helps preserve sanity and, in the case of |
| // ref mut, for soundness (issue #23116). In particular, in |
| // the latter case, we need to be clear that the type of the |
| // referent for the reference that results is *equal to* the |
| // type of the place it is referencing, and not some |
| // supertype thereof. |
| let init_ty = self.check_expr_with_needs(init, Needs::maybe_mut_place(m)); |
| self.demand_eqtype(init.span, local_ty, init_ty); |
| init_ty |
| } else { |
| self.check_expr_coercable_to_type(init, local_ty, None) |
| } |
| } |
| |
| pub(in super::super) fn check_decl(&self, decl: Declaration<'tcx>) { |
| // Determine and write the type which we'll check the pattern against. |
| 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) = 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 (decl.ty, decl.init) { |
| (Some(ty), _) => (false, Some(ty.span)), // Bias towards the explicit user type. |
| (_, 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(&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); |
| } |
| } |
| |
| /// 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, |
| hir::StmtKind::Local(..) | hir::StmtKind::Expr(..) | hir::StmtKind::Semi(..) => {} |
| } |
| |
| self.warn_if_unreachable(stmt.hir_id, stmt.span, "statement"); |
| |
| // Hide the outer diverging and `has_errors` flags. |
| let old_diverges = self.diverges.replace(Diverges::Maybe); |
| let old_has_errors = self.has_errors.replace(false); |
| |
| match stmt.kind { |
| 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| { |
| if expr.can_have_side_effects() { |
| self.suggest_semicolon_at_end(expr.span, err); |
| } |
| }); |
| } |
| hir::StmtKind::Semi(ref 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); |
| } |
| } |
| |
| // Combine the diverging and `has_error` flags. |
| self.diverges.set(self.diverges.get() | old_diverges); |
| self.has_errors.set(self.has_errors.get() | old_has_errors); |
| } |
| |
| pub fn check_block_no_value(&self, blk: &'tcx hir::Block<'tcx>) { |
| let unit = self.tcx.mk_unit(); |
| let ty = self.check_block_with_expected(blk, ExpectHasType(unit)); |
| |
| // if the block produces a `!` value, that can always be |
| // (effectively) coerced to unit. |
| if !ty.is_never() { |
| self.demand_suptype(blk.span, unit, ty); |
| } |
| } |
| |
| pub(in super::super) fn check_block_with_expected( |
| &self, |
| blk: &'tcx hir::Block<'tcx>, |
| expected: Expectation<'tcx>, |
| ) -> Ty<'tcx> { |
| 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 |
| // with labeled blocks as well as when we desugar |
| // a `try { ... }` expression. |
| // |
| // Example 1: |
| // |
| // 'a: { if true { break 'a Err(()); } Ok(()) } |
| // |
| // Here we would wind up with two coercions, one from |
| // `Err(())` and the other from the tail expression |
| // `Ok(())`. If the tail expression is omitted, that's a |
| // "forced unit" -- unless the block diverges, in which |
| // case we can ignore the tail expression (e.g., `'a: { |
| // break 'a 22; }` would not force the type of the block |
| // to be `()`). |
| let tail_expr = blk.expr.as_ref(); |
| let coerce_to_ty = expected.coercion_target_type(self, blk.span); |
| let coerce = if blk.targeted_by_break { |
| CoerceMany::new(coerce_to_ty) |
| } else { |
| let tail_expr: &[&hir::Expr<'_>] = match tail_expr { |
| Some(e) => slice::from_ref(e), |
| None => &[], |
| }; |
| CoerceMany::with_coercion_sites(coerce_to_ty, tail_expr) |
| }; |
| |
| let prev_diverges = self.diverges.get(); |
| let ctxt = BreakableCtxt { coerce: Some(coerce), may_break: false }; |
| |
| let (ctxt, ()) = self.with_breakable_ctxt(blk.hir_id, ctxt, || { |
| for (pos, s) in blk.stmts.iter().enumerate() { |
| self.check_stmt(s, blk.stmts.len() - 1 == pos); |
| } |
| |
| // check the tail expression **without** holding the |
| // `enclosing_breakables` lock below. |
| let tail_expr_ty = tail_expr.map(|t| self.check_expr_with_expectation(t, expected)); |
| |
| let mut enclosing_breakables = self.enclosing_breakables.borrow_mut(); |
| let ctxt = enclosing_breakables.find_breakable(blk.hir_id); |
| let coerce = ctxt.coerce.as_mut().unwrap(); |
| if let Some(tail_expr_ty) = tail_expr_ty { |
| 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)); |
| 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 |
| // of `()` -- except if the block diverges. In that |
| // case, there is no value supplied from the tail |
| // expression (assuming there are no other breaks, |
| // this implies that the type of the block will be |
| // `!`). |
| // |
| // #41425 -- label the implicit `()` as being the |
| // "found type" here, rather than the "expected type". |
| if !self.diverges.get().is_always() { |
| // #50009 -- Do not point at the entire fn block span, point at the return type |
| // span, as it is the cause of the requirement, and |
| // `consider_hint_about_removing_semicolon` will point at the last expression |
| // if it were a relevant part of the error. This improves usability in editors |
| // that highlight errors inline. |
| let mut sp = blk.span; |
| let mut fn_span = None; |
| if let Some((decl, ident)) = self.get_parent_fn_decl(blk.hir_id) { |
| let ret_sp = decl.output.span(); |
| if let Some(block_sp) = self.parent_item_span(blk.hir_id) { |
| // HACK: on some cases (`ui/liveness/liveness-issue-2163.rs`) the |
| // output would otherwise be incorrect and even misleading. Make sure |
| // the span we're aiming at correspond to a `fn` body. |
| if block_sp == blk.span { |
| sp = ret_sp; |
| fn_span = Some(ident.span); |
| } |
| } |
| } |
| coerce.coerce_forced_unit( |
| self, |
| &self.misc(sp), |
| &mut |err| { |
| if let Some(expected_ty) = expected.only_has_type(self) { |
| 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( |
| fn_span, |
| "implicitly returns `()` as its body has no tail or `return` \ |
| expression", |
| ); |
| } |
| }, |
| false, |
| ); |
| } |
| } |
| }); |
| |
| if ctxt.may_break { |
| // If we can break from the block, then the block's exit is always reachable |
| // (... as long as the entry is reachable) - regardless of the tail of the block. |
| self.diverges.set(prev_diverges); |
| } |
| |
| let mut ty = ctxt.coerce.unwrap().complete(self); |
| |
| if self.has_errors.get() || ty.references_error() { |
| ty = self.tcx.ty_error() |
| } |
| |
| self.write_ty(blk.hir_id, ty); |
| |
| self.ps.set(prev); |
| ty |
| } |
| |
| fn parent_item_span(&self, id: hir::HirId) -> Option<Span> { |
| 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), .. }) => { |
| let body = self.tcx.hir().body(body_id); |
| if let ExprKind::Block(block, _) = &body.value.kind { |
| return Some(block.span); |
| } |
| } |
| _ => {} |
| } |
| None |
| } |
| |
| /// 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_by_def_id(self.tcx.hir().get_parent_item(blk_id)); |
| self.get_node_fn_decl(parent).map(|(fn_decl, ident, _)| (fn_decl, ident)) |
| } |
| |
| /// If `expr` is a `match` expression that has only one non-`!` arm, use that arm's tail |
| /// expression's `Span`, otherwise return `expr.span`. This is done to give better errors |
| /// when given code like the following: |
| /// ```text |
| /// if false { return 0i32; } else { 1u32 } |
| /// // ^^^^ point at this instead of the whole `if` expression |
| /// ``` |
| fn get_expr_coercion_span(&self, expr: &hir::Expr<'_>) -> rustc_span::Span { |
| 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, |
| 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(hir_id, ty); |
| self.write_ty(pat.hir_id, ty); |
| let local_ty = LocalTy { decl_ty, revealed_ty: ty }; |
| self.locals.borrow_mut().insert(hir_id, local_ty); |
| self.locals.borrow_mut().insert(pat.hir_id, local_ty); |
| } |
| } |
| |
| // Finish resolving a path in a struct expression or pattern `S::A { .. }` if necessary. |
| // The newly resolved definition is written into `type_dependent_defs`. |
| fn finish_resolving_struct_path( |
| &self, |
| qpath: &QPath<'_>, |
| path_span: Span, |
| hir_id: hir::HirId, |
| ) -> (Res, Ty<'tcx>) { |
| match *qpath { |
| QPath::Resolved(ref maybe_qself, ref path) => { |
| let self_ty = maybe_qself.as_ref().map(|qself| self.to_ty(qself)); |
| 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 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_or(Res::Err, |(kind, def_id)| Res::Def(kind, def_id)), ty) |
| } |
| QPath::LangItem(lang_item, span, id) => { |
| self.resolve_lang_item_path(lang_item, span, hir_id, id) |
| } |
| } |
| } |
| |
| /// 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>>, |
| ) { |
| // 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 { |
| 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); |
| } |
| } |
| |
| 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; |
| } |
| } |
| } |
| 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 |
| } |
| |
| 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; |
| } |
| } |
| } |
| |
| false |
| } |
| |
| 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 |
| } |
| |
| fn point_at_generic_if_possible( |
| &self, |
| 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, |
| ) { |
| 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 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() |
| { |
| 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 |
| } |