diff options
Diffstat (limited to 'compiler/rustc_hir_typeck/src/fn_ctxt')
-rw-r--r-- | compiler/rustc_hir_typeck/src/fn_ctxt/_impl.rs | 167 | ||||
-rw-r--r-- | compiler/rustc_hir_typeck/src/fn_ctxt/adjust_fulfillment_errors.rs | 864 | ||||
-rw-r--r-- | compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs | 608 | ||||
-rw-r--r-- | compiler/rustc_hir_typeck/src/fn_ctxt/mod.rs | 17 | ||||
-rw-r--r-- | compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs | 127 |
5 files changed, 1245 insertions, 538 deletions
diff --git a/compiler/rustc_hir_typeck/src/fn_ctxt/_impl.rs b/compiler/rustc_hir_typeck/src/fn_ctxt/_impl.rs index 6ed8adb47..60e55c7b0 100644 --- a/compiler/rustc_hir_typeck/src/fn_ctxt/_impl.rs +++ b/compiler/rustc_hir_typeck/src/fn_ctxt/_impl.rs @@ -23,16 +23,16 @@ use rustc_infer::infer::InferResult; use rustc_middle::ty::adjustment::{Adjust, Adjustment, AutoBorrow, AutoBorrowMutability}; use rustc_middle::ty::error::TypeError; use rustc_middle::ty::fold::TypeFoldable; -use rustc_middle::ty::visit::TypeVisitable; +use rustc_middle::ty::visit::{TypeVisitable, TypeVisitableExt}; use rustc_middle::ty::{ - self, AdtKind, CanonicalUserType, DefIdTree, GenericParamDefKind, Ty, UserType, + self, AdtKind, CanonicalUserType, DefIdTree, GenericParamDefKind, Ty, TyCtxt, UserType, }; use rustc_middle::ty::{GenericArgKind, SubstsRef, UserSelfTy, UserSubsts}; use rustc_session::lint; use rustc_span::def_id::LocalDefId; use rustc_span::hygiene::DesugaringKind; use rustc_span::symbol::{kw, sym, Ident}; -use rustc_span::{Span, DUMMY_SP}; +use rustc_span::Span; use rustc_trait_selection::traits::error_reporting::TypeErrCtxtExt as _; use rustc_trait_selection::traits::{self, NormalizeExt, ObligationCauseCode, ObligationCtxt}; @@ -315,7 +315,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { pub(in super::super) fn normalize<T>(&self, span: Span, value: T) -> T where - T: TypeFoldable<'tcx>, + T: TypeFoldable<TyCtxt<'tcx>>, { self.register_infer_ok_obligations( self.at(&self.misc(span), self.param_env).normalize(value), @@ -443,7 +443,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // sufficiently enforced with erased regions. =) fn can_contain_user_lifetime_bounds<T>(t: T) -> bool where - T: TypeVisitable<'tcx>, + T: TypeVisitable<TyCtxt<'tcx>>, { t.has_free_regions() || t.has_projections() || t.has_infer_types() } @@ -451,11 +451,10 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { pub fn node_ty(&self, id: hir::HirId) -> Ty<'tcx> { match self.typeck_results.borrow().node_types().get(id) { Some(&t) => t, - None if let Some(e) = self.tainted_by_errors() => self.tcx.ty_error_with_guaranteed(e), + None if let Some(e) = self.tainted_by_errors() => self.tcx.ty_error(e), None => { bug!( - "no type for node {}: {} in fcx {}", - id, + "no type for node {} in fcx {}", self.tcx.hir().node_to_string(id), self.tag() ); @@ -466,7 +465,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { pub fn node_ty_opt(&self, id: hir::HirId) -> Option<Ty<'tcx>> { match self.typeck_results.borrow().node_types().get(id) { Some(&t) => Some(t), - None if let Some(e) = self.tainted_by_errors() => Some(self.tcx.ty_error_with_guaranteed(e)), + None if let Some(e) = self.tainted_by_errors() => Some(self.tcx.ty_error(e)), None => None, } } @@ -517,16 +516,72 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } pub(in super::super) fn resolve_generator_interiors(&self, def_id: DefId) { + if self.tcx.sess.opts.unstable_opts.drop_tracking_mir { + self.save_generator_interior_predicates(def_id); + return; + } + + self.select_obligations_where_possible(|_| {}); + let mut generators = self.deferred_generator_interiors.borrow_mut(); - for (body_id, interior, kind) in generators.drain(..) { - self.select_obligations_where_possible(|_| {}); + for (_, body_id, interior, kind) in generators.drain(..) { crate::generator_interior::resolve_interior(self, def_id, body_id, interior, kind); + self.select_obligations_where_possible(|_| {}); + } + } + + /// Unify the inference variables corresponding to generator witnesses, and save all the + /// predicates that were stalled on those inference variables. + /// + /// This process allows to conservatively save all predicates that do depend on the generator + /// interior types, for later processing by `check_generator_obligations`. + /// + /// We must not attempt to select obligations after this method has run, or risk query cycle + /// ICE. + #[instrument(level = "debug", skip(self))] + fn save_generator_interior_predicates(&self, def_id: DefId) { + // Try selecting all obligations that are not blocked on inference variables. + // Once we start unifying generator witnesses, trying to select obligations on them will + // trigger query cycle ICEs, as doing so requires MIR. + self.select_obligations_where_possible(|_| {}); + + let generators = std::mem::take(&mut *self.deferred_generator_interiors.borrow_mut()); + debug!(?generators); + + for &(expr_def_id, body_id, interior, _) in generators.iter() { + debug!(?expr_def_id); + + // Create the `GeneratorWitness` type that we will unify with `interior`. + let substs = ty::InternalSubsts::identity_for_item( + self.tcx, + self.tcx.typeck_root_def_id(expr_def_id.to_def_id()), + ); + let witness = self.tcx.mk_generator_witness_mir(expr_def_id.to_def_id(), substs); + + // Unify `interior` with `witness` and collect all the resulting obligations. + let span = self.tcx.hir().body(body_id).value.span; + let ok = self + .at(&self.misc(span), self.param_env) + .eq(interior, witness) + .expect("Failed to unify generator interior type"); + let mut obligations = ok.obligations; + + // Also collect the obligations that were unstalled by this unification. + obligations + .extend(self.fulfillment_cx.borrow_mut().drain_unstalled_obligations(&self.infcx)); + + let obligations = obligations.into_iter().map(|o| (o.predicate, o.cause)).collect(); + debug!(?obligations); + self.typeck_results + .borrow_mut() + .generator_interior_predicates + .insert(expr_def_id, obligations); } } #[instrument(skip(self), level = "debug")] - pub(in super::super) fn select_all_obligations_or_error(&self) { - let mut errors = self.fulfillment_cx.borrow_mut().select_all_or_error(&self); + pub(in super::super) fn report_ambiguity_errors(&self) { + let mut errors = self.fulfillment_cx.borrow_mut().collect_remaining_errors(); if !errors.is_empty() { self.adjust_fulfillment_errors_for_expr_obligation(&mut errors); @@ -608,12 +663,14 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { ty::PredicateKind::Clause(ty::Clause::Trait(..)) | ty::PredicateKind::Clause(ty::Clause::Projection(..)) + | ty::PredicateKind::Clause(ty::Clause::ConstArgHasType(..)) | ty::PredicateKind::Subtype(..) | ty::PredicateKind::Coerce(..) | ty::PredicateKind::Clause(ty::Clause::RegionOutlives(..)) | ty::PredicateKind::Clause(ty::Clause::TypeOutlives(..)) | ty::PredicateKind::WellFormed(..) | ty::PredicateKind::ObjectSafe(..) + | ty::PredicateKind::AliasEq(..) | ty::PredicateKind::ConstEvaluatable(..) | ty::PredicateKind::ConstEquate(..) // N.B., this predicate is created by breaking down a @@ -644,7 +701,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } pub(in super::super) fn err_args(&self, len: usize) -> Vec<Ty<'tcx>> { - vec![self.tcx.ty_error(); len] + let ty_error = self.tcx.ty_error_misc(); + vec![ty_error; len] } /// Unifies the output type with the expected type early, for more coercions @@ -680,7 +738,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { if let ty::subst::GenericArgKind::Type(ty) = ty.unpack() && let ty::Alias(ty::Opaque, ty::AliasTy { def_id, .. }) = *ty.kind() && let Some(def_id) = def_id.as_local() - && self.opaque_type_origin(def_id, DUMMY_SP).is_some() { + && self.opaque_type_origin(def_id).is_some() { return None; } } @@ -720,9 +778,9 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let def_kind = self.tcx.def_kind(def_id); let item_ty = if let DefKind::Variant = def_kind { - self.tcx.bound_type_of(self.tcx.parent(def_id)) + self.tcx.type_of(self.tcx.parent(def_id)) } else { - self.tcx.bound_type_of(def_id) + self.tcx.type_of(def_id) }; let substs = self.fresh_substs_for_item(span, def_id); let ty = item_ty.subst(self.tcx, substs); @@ -866,6 +924,22 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { kind: hir::ImplItemKind::Fn(ref sig, ..), .. }) => Some((&sig.decl, ident, false)), + Node::Expr(&hir::Expr { + hir_id, + kind: hir::ExprKind::Closure(..), + .. + }) if let Some(Node::Expr(&hir::Expr { + hir_id, + kind: hir::ExprKind::Call(..), + .. + })) = self.tcx.hir().find_parent(hir_id) && + let Some(Node::Item(&hir::Item { + ident, + kind: hir::ItemKind::Fn(ref sig, ..), + .. + })) = self.tcx.hir().find_parent(hir_id) => { + Some((&sig.decl, ident, ident.name != sym::main)) + }, _ => None, } } @@ -926,43 +1000,6 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } } - pub(in super::super) fn note_need_for_fn_pointer( - &self, - err: &mut Diagnostic, - expected: Ty<'tcx>, - found: Ty<'tcx>, - ) { - let (sig, did, substs) = match (&expected.kind(), &found.kind()) { - (ty::FnDef(did1, substs1), ty::FnDef(did2, substs2)) => { - let sig1 = self.tcx.bound_fn_sig(*did1).subst(self.tcx, substs1); - let sig2 = self.tcx.bound_fn_sig(*did2).subst(self.tcx, substs2); - if sig1 != sig2 { - return; - } - err.note( - "different `fn` items always have unique types, even if their signatures are \ - the same", - ); - (sig1, *did1, substs1) - } - (ty::FnDef(did, substs), ty::FnPtr(sig2)) => { - let sig1 = self.tcx.bound_fn_sig(*did).subst(self.tcx, substs); - if sig1 != *sig2 { - return; - } - (sig1, *did, substs) - } - _ => return, - }; - err.help(&format!("change the expected type to be function pointer `{}`", sig)); - err.help(&format!( - "if the expected type is due to type inference, cast the expected `fn` to a function \ - pointer: `{} as {}`", - self.tcx.def_path_str_with_substs(did, substs), - sig - )); - } - // Instantiates the given path, which must refer to an item with the given // number of type parameters and type. #[instrument(skip(self, span), level = "debug")] @@ -1095,7 +1132,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { .unwrap_or(false); let (res, self_ctor_substs) = if let Res::SelfCtor(impl_def_id) = res { - let ty = self.handle_raw_ty(span, tcx.at(span).type_of(impl_def_id)); + let ty = self.handle_raw_ty(span, tcx.at(span).type_of(impl_def_id).subst_identity()); match ty.normalized.ty_adt_def() { Some(adt_def) if adt_def.has_ctor() => { let (ctor_kind, ctor_def_id) = adt_def.non_enum_variant().ctor.unwrap(); @@ -1125,7 +1162,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } } let reported = err.emit(); - return (tcx.ty_error_with_guaranteed(reported), res); + return (tcx.ty_error(reported), res); } } } else { @@ -1191,7 +1228,15 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } (GenericParamDefKind::Const { .. }, GenericArg::Infer(inf)) => { let tcx = self.fcx.tcx(); - self.fcx.ct_infer(tcx.type_of(param.def_id), Some(param), inf.span).into() + self.fcx + .ct_infer( + tcx.type_of(param.def_id) + .no_bound_vars() + .expect("const parameter types cannot be generic"), + Some(param), + inf.span, + ) + .into() } _ => unreachable!(), } @@ -1213,7 +1258,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // If we have a default, then we it doesn't matter that we're not // inferring the type arguments: we provide the default where any // is missing. - tcx.bound_type_of(param.def_id).subst(tcx, substs.unwrap()).into() + tcx.type_of(param.def_id).subst(tcx, substs.unwrap()).into() } else { // If no type arguments were provided, we have to infer them. // This case also occurs as a result of some malformed input, e.g. @@ -1261,7 +1306,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // Substitute the values for the type parameters into the type of // the referenced item. - let ty = tcx.bound_type_of(def_id); + let ty = tcx.type_of(def_id); assert!(!substs.has_escaping_bound_vars()); assert!(!ty.0.has_escaping_bound_vars()); let ty_substituted = self.normalize(span, ty.subst(tcx, substs)); @@ -1272,7 +1317,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // type parameters, which we can infer by unifying the provided `Self` // with the substituted impl type. // This also occurs for an enum variant on a type alias. - let impl_ty = self.normalize(span, tcx.bound_type_of(impl_def_id).subst(tcx, substs)); + let impl_ty = self.normalize(span, tcx.type_of(impl_def_id).subst(tcx, substs)); let self_ty = self.normalize(span, self_ty); match self.at(&self.misc(span), self.param_env).eq(impl_ty, self_ty) { Ok(ok) => self.register_infer_ok_obligations(ok), @@ -1373,7 +1418,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { .emit_inference_failure_err((**self).body_id, sp, ty.into(), E0282, true) .emit() }); - let err = self.tcx.ty_error_with_guaranteed(e); + let err = self.tcx.ty_error(e); self.demand_suptype(sp, err, ty); err } diff --git a/compiler/rustc_hir_typeck/src/fn_ctxt/adjust_fulfillment_errors.rs b/compiler/rustc_hir_typeck/src/fn_ctxt/adjust_fulfillment_errors.rs new file mode 100644 index 000000000..b09886fe3 --- /dev/null +++ b/compiler/rustc_hir_typeck/src/fn_ctxt/adjust_fulfillment_errors.rs @@ -0,0 +1,864 @@ +use crate::FnCtxt; +use rustc_hir as hir; +use rustc_hir::def::Res; +use rustc_hir::def_id::DefId; +use rustc_infer::traits::ObligationCauseCode; +use rustc_middle::ty::{ + self, DefIdTree, Ty, TyCtxt, TypeSuperVisitable, TypeVisitable, TypeVisitor, +}; +use rustc_span::{self, Span}; +use rustc_trait_selection::traits; + +use std::ops::ControlFlow; + +impl<'a, 'tcx> FnCtxt<'a, 'tcx> { + pub 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; }; + + 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::Clause(ty::Clause::Trait(pred)) => pred.trait_ref.substs, + ty::PredicateKind::Clause(ty::Clause::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_parent(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.blame_specific_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.blame_specific_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( + hir::def::DefKind::Struct | hir::def::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 { + let refined_expr = self.point_at_field_if_possible( + def_id, + param, + variant_def_id, + fields, + ); + + match refined_expr { + None => {} + Some((refined_expr, _)) => { + error.obligation.cause.span = refined_expr + .span + .find_ancestor_in_same_ctxt(error.obligation.cause.span) + .unwrap_or(refined_expr.span); + 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 point_at_path_if_possible( + &self, + error: &mut traits::FulfillmentError<'tcx>, + def_id: DefId, + param: ty::GenericArg<'tcx>, + qpath: &hir::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<TyCtxt<'tcx>>>( + &self, + item_def_id: DefId, + t: T, + ) -> Option<ty::GenericArg<'tcx>> { + struct FindAmbiguousParameter<'a, 'tcx>(&'a FnCtxt<'a, 'tcx>, DefId); + impl<'tcx> TypeVisitor<TyCtxt<'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 rustc_infer::infer::type_variable::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 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_field_if_possible( + &self, + def_id: DefId, + param_to_point_at: ty::GenericArg<'tcx>, + variant_def_id: DefId, + expr_fields: &[hir::ExprField<'tcx>], + ) -> Option<(&'tcx hir::Expr<'tcx>, Ty<'tcx>)> { + 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); + Self::find_param_in_ty(field_ty.into(), 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) + { + return Some((expr_field.expr, self.tcx.type_of(field.did).subst_identity())); + } + } + } + + None + } + + /// - `blame_specific_*` means that the function will recursively traverse the expression, + /// looking for the most-specific-possible span to blame. + /// + /// - `point_at_*` means that the function will only go "one level", pointing at the specific + /// expression mentioned. + /// + /// `blame_specific_arg_if_possible` will find the most-specific expression anywhere inside + /// the provided function call expression, and mark it as responsible for the fullfillment + /// error. + fn blame_specific_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 ty = self.tcx.type_of(def_id).subst_identity(); + if !ty.is_fn() { + return false; + } + let sig = ty.fn_sig(self.tcx).skip_binder(); + let args_referencing_param: Vec<_> = sig + .inputs() + .iter() + .enumerate() + .filter(|(_, ty)| Self::find_param_in_ty((**ty).into(), 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); + + if let hir::Node::Expr(arg_expr) = self.tcx.hir().get(arg.hir_id) { + // This is more specific than pointing at the entire argument. + self.blame_specific_expr_if_possible(error, arg_expr) + } + + 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 + } + + /** + * Recursively searches for the most-specific blamable expression. + * For example, if you have a chain of constraints like: + * - want `Vec<i32>: Copy` + * - because `Option<Vec<i32>>: Copy` needs `Vec<i32>: Copy` because `impl <T: Copy> Copy for Option<T>` + * - because `(Option<Vec<i32>, bool)` needs `Option<Vec<i32>>: Copy` because `impl <A: Copy, B: Copy> Copy for (A, B)` + * then if you pass in `(Some(vec![1, 2, 3]), false)`, this helper `point_at_specific_expr_if_possible` + * will find the expression `vec![1, 2, 3]` as the "most blameable" reason for this missing constraint. + * + * This function only updates the error span. + */ + pub fn blame_specific_expr_if_possible( + &self, + error: &mut traits::FulfillmentError<'tcx>, + expr: &'tcx hir::Expr<'tcx>, + ) { + // Whether it succeeded or failed, it likely made some amount of progress. + // In the very worst case, it's just the same `expr` we originally passed in. + let expr = match self.blame_specific_expr_if_possible_for_obligation_cause_code( + &error.obligation.cause.code(), + expr, + ) { + Ok(expr) => expr, + Err(expr) => expr, + }; + + // Either way, use this expression to update the error span. + // If it doesn't overlap the existing span at all, use the original span. + // FIXME: It would possibly be better to do this more continuously, at each level... + error.obligation.cause.span = expr + .span + .find_ancestor_in_same_ctxt(error.obligation.cause.span) + .unwrap_or(error.obligation.cause.span); + } + + fn blame_specific_expr_if_possible_for_obligation_cause_code( + &self, + obligation_cause_code: &traits::ObligationCauseCode<'tcx>, + expr: &'tcx hir::Expr<'tcx>, + ) -> Result<&'tcx hir::Expr<'tcx>, &'tcx hir::Expr<'tcx>> { + match obligation_cause_code { + traits::ObligationCauseCode::ExprBindingObligation(_, _, _, _) => { + // This is the "root"; we assume that the `expr` is already pointing here. + // Therefore, we return `Ok` so that this `expr` can be refined further. + Ok(expr) + } + traits::ObligationCauseCode::ImplDerivedObligation(impl_derived) => self + .blame_specific_expr_if_possible_for_derived_predicate_obligation( + impl_derived, + expr, + ), + _ => { + // We don't recognize this kind of constraint, so we cannot refine the expression + // any further. + Err(expr) + } + } + } + + /// We want to achieve the error span in the following example: + /// + /// ```ignore (just for demonstration) + /// struct Burrito<Filling> { + /// filling: Filling, + /// } + /// impl <Filling: Delicious> Delicious for Burrito<Filling> {} + /// fn eat_delicious_food<Food: Delicious>(_food: Food) {} + /// + /// fn will_type_error() { + /// eat_delicious_food(Burrito { filling: Kale }); + /// } // ^--- The trait bound `Kale: Delicious` + /// // is not satisfied + /// ``` + /// + /// Without calling this function, the error span will cover the entire argument expression. + /// + /// Before we do any of this logic, we recursively call `point_at_specific_expr_if_possible` on the parent + /// obligation. Hence we refine the `expr` "outwards-in" and bail at the first kind of expression/impl we don't recognize. + /// + /// This function returns a `Result<&Expr, &Expr>` - either way, it returns the `Expr` whose span should be + /// reported as an error. If it is `Ok`, then it means it refined successfull. If it is `Err`, then it may be + /// only a partial success - but it cannot be refined even further. + fn blame_specific_expr_if_possible_for_derived_predicate_obligation( + &self, + obligation: &traits::ImplDerivedObligationCause<'tcx>, + expr: &'tcx hir::Expr<'tcx>, + ) -> Result<&'tcx hir::Expr<'tcx>, &'tcx hir::Expr<'tcx>> { + // First, we attempt to refine the `expr` for our span using the parent obligation. + // If this cannot be done, then we are already stuck, so we stop early (hence the use + // of the `?` try operator here). + let expr = self.blame_specific_expr_if_possible_for_obligation_cause_code( + &*obligation.derived.parent_code, + expr, + )?; + + // This is the "trait" (meaning, the predicate "proved" by this `impl`) which provides the `Self` type we care about. + // For the purposes of this function, we hope that it is a `struct` type, and that our current `expr` is a literal of + // that struct type. + let impl_trait_self_ref = if self.tcx.is_trait_alias(obligation.impl_or_alias_def_id) { + self.tcx.mk_trait_ref( + obligation.impl_or_alias_def_id, + ty::InternalSubsts::identity_for_item(self.tcx, obligation.impl_or_alias_def_id), + ) + } else { + self.tcx + .impl_trait_ref(obligation.impl_or_alias_def_id) + .map(|impl_def| impl_def.skip_binder()) + // It is possible that this is absent. In this case, we make no progress. + .ok_or(expr)? + }; + + // We only really care about the `Self` type itself, which we extract from the ref. + let impl_self_ty: Ty<'tcx> = impl_trait_self_ref.self_ty(); + + let impl_predicates: ty::GenericPredicates<'tcx> = + self.tcx.predicates_of(obligation.impl_or_alias_def_id); + let Some(impl_predicate_index) = obligation.impl_def_predicate_index else { + // We don't have the index, so we can only guess. + return Err(expr); + }; + + if impl_predicate_index >= impl_predicates.predicates.len() { + // This shouldn't happen, but since this is only a diagnostic improvement, avoid breaking things. + return Err(expr); + } + let relevant_broken_predicate: ty::PredicateKind<'tcx> = + impl_predicates.predicates[impl_predicate_index].0.kind().skip_binder(); + + match relevant_broken_predicate { + ty::PredicateKind::Clause(ty::Clause::Trait(broken_trait)) => { + // ... + self.blame_specific_part_of_expr_corresponding_to_generic_param( + broken_trait.trait_ref.self_ty().into(), + expr, + impl_self_ty.into(), + ) + } + _ => Err(expr), + } + } + + /// Drills into `expr` to arrive at the equivalent location of `find_generic_param` in `in_ty`. + /// For example, given + /// - expr: `(Some(vec![1, 2, 3]), false)` + /// - param: `T` + /// - in_ty: `(Option<Vec<T>, bool)` + /// we would drill until we arrive at `vec![1, 2, 3]`. + /// + /// If successful, we return `Ok(refined_expr)`. If unsuccesful, we return `Err(partially_refined_expr`), + /// which will go as far as possible. For example, given `(foo(), false)` instead, we would drill to + /// `foo()` and then return `Err("foo()")`. + /// + /// This means that you can (and should) use the `?` try operator to chain multiple calls to this + /// function with different types, since you can only continue drilling the second time if you + /// succeeded the first time. + fn blame_specific_part_of_expr_corresponding_to_generic_param( + &self, + param: ty::GenericArg<'tcx>, + expr: &'tcx hir::Expr<'tcx>, + in_ty: ty::GenericArg<'tcx>, + ) -> Result<&'tcx hir::Expr<'tcx>, &'tcx hir::Expr<'tcx>> { + if param == in_ty { + // The types match exactly, so we have drilled as far as we can. + return Ok(expr); + } + + let ty::GenericArgKind::Type(in_ty) = in_ty.unpack() else { + return Err(expr); + }; + + if let ( + hir::ExprKind::AddrOf(_borrow_kind, _borrow_mutability, borrowed_expr), + ty::Ref(_ty_region, ty_ref_type, _ty_mutability), + ) = (&expr.kind, in_ty.kind()) + { + // We can "drill into" the borrowed expression. + return self.blame_specific_part_of_expr_corresponding_to_generic_param( + param, + borrowed_expr, + (*ty_ref_type).into(), + ); + } + + if let (hir::ExprKind::Tup(expr_elements), ty::Tuple(in_ty_elements)) = + (&expr.kind, in_ty.kind()) + { + if in_ty_elements.len() != expr_elements.len() { + return Err(expr); + } + // Find out which of `in_ty_elements` refer to `param`. + // FIXME: It may be better to take the first if there are multiple, + // just so that the error points to a smaller expression. + let Some((drill_expr, drill_ty)) = Self::is_iterator_singleton(expr_elements.iter().zip( in_ty_elements.iter()).filter(|(_expr_elem, in_ty_elem)| { + Self::find_param_in_ty((*in_ty_elem).into(), param) + })) else { + // The param is not mentioned, or it is mentioned in multiple indexes. + return Err(expr); + }; + + return self.blame_specific_part_of_expr_corresponding_to_generic_param( + param, + drill_expr, + drill_ty.into(), + ); + } + + if let ( + hir::ExprKind::Struct(expr_struct_path, expr_struct_fields, _expr_struct_rest), + ty::Adt(in_ty_adt, in_ty_adt_generic_args), + ) = (&expr.kind, in_ty.kind()) + { + // First, confirm that this struct is the same one as in the types, and if so, + // find the right variant. + let Res::Def(expr_struct_def_kind, expr_struct_def_id) = self.typeck_results.borrow().qpath_res(expr_struct_path, expr.hir_id) else { + return Err(expr); + }; + + let variant_def_id = match expr_struct_def_kind { + hir::def::DefKind::Struct => { + if in_ty_adt.did() != expr_struct_def_id { + // FIXME: Deal with type aliases? + return Err(expr); + } + expr_struct_def_id + } + hir::def::DefKind::Variant => { + // If this is a variant, its parent is the type definition. + if in_ty_adt.did() != self.tcx.parent(expr_struct_def_id) { + // FIXME: Deal with type aliases? + return Err(expr); + } + expr_struct_def_id + } + _ => { + return Err(expr); + } + }; + + // We need to know which of the generic parameters mentions our target param. + // We expect that at least one of them does, since it is expected to be mentioned. + let Some((drill_generic_index, generic_argument_type)) = + Self::is_iterator_singleton( + in_ty_adt_generic_args.iter().enumerate().filter( + |(_index, in_ty_generic)| { + Self::find_param_in_ty(*in_ty_generic, param) + }, + ), + ) else { + return Err(expr); + }; + + let struct_generic_parameters: &ty::Generics = self.tcx.generics_of(in_ty_adt.did()); + if drill_generic_index >= struct_generic_parameters.params.len() { + return Err(expr); + } + + let param_to_point_at_in_struct = self.tcx.mk_param_from_def( + struct_generic_parameters.param_at(drill_generic_index, self.tcx), + ); + + // We make 3 steps: + // Suppose we have a type like + // ```ignore (just for demonstration) + // struct ExampleStruct<T> { + // enabled: bool, + // item: Option<(usize, T, bool)>, + // } + // + // f(ExampleStruct { + // enabled: false, + // item: Some((0, Box::new(String::new()), 1) }, true)), + // }); + // ``` + // Here, `f` is passed a `ExampleStruct<Box<String>>`, but it wants + // for `String: Copy`, which isn't true here. + // + // (1) First, we drill into `.item` and highlight that expression + // (2) Then we use the template type `Option<(usize, T, bool)>` to + // drill into the `T`, arriving at a `Box<String>` expression. + // (3) Then we keep going, drilling into this expression using our + // outer contextual information. + + // (1) Find the (unique) field which mentions the type in our constraint: + let (field_expr, field_type) = self + .point_at_field_if_possible( + in_ty_adt.did(), + param_to_point_at_in_struct, + variant_def_id, + expr_struct_fields, + ) + .ok_or(expr)?; + + // (2) Continue drilling into the struct, ignoring the struct's + // generic argument types. + let expr = self.blame_specific_part_of_expr_corresponding_to_generic_param( + param_to_point_at_in_struct, + field_expr, + field_type.into(), + )?; + + // (3) Continue drilling into the expression, having "passed + // through" the struct entirely. + return self.blame_specific_part_of_expr_corresponding_to_generic_param( + param, + expr, + generic_argument_type, + ); + } + + if let ( + hir::ExprKind::Call(expr_callee, expr_args), + ty::Adt(in_ty_adt, in_ty_adt_generic_args), + ) = (&expr.kind, in_ty.kind()) + { + let hir::ExprKind::Path(expr_callee_path) = &expr_callee.kind else { + // FIXME: This case overlaps with another one worth handling, + // which should happen above since it applies to non-ADTs: + // we can drill down into regular generic functions. + return Err(expr); + }; + // This is (possibly) a constructor call, like `Some(...)` or `MyStruct(a, b, c)`. + + let Res::Def(expr_struct_def_kind, expr_ctor_def_id) = self.typeck_results.borrow().qpath_res(expr_callee_path, expr_callee.hir_id) else { + return Err(expr); + }; + + let variant_def_id = match expr_struct_def_kind { + hir::def::DefKind::Ctor(hir::def::CtorOf::Struct, hir::def::CtorKind::Fn) => { + if in_ty_adt.did() != self.tcx.parent(expr_ctor_def_id) { + // FIXME: Deal with type aliases? + return Err(expr); + } + self.tcx.parent(expr_ctor_def_id) + } + hir::def::DefKind::Ctor(hir::def::CtorOf::Variant, hir::def::CtorKind::Fn) => { + // For a typical enum like + // `enum Blah<T> { Variant(T) }` + // we get the following resolutions: + // - expr_ctor_def_id ::: DefId(0:29 ~ source_file[b442]::Blah::Variant::{constructor#0}) + // - self.tcx.parent(expr_ctor_def_id) ::: DefId(0:28 ~ source_file[b442]::Blah::Variant) + // - self.tcx.parent(self.tcx.parent(expr_ctor_def_id)) ::: DefId(0:26 ~ source_file[b442]::Blah) + + // Therefore, we need to go up once to obtain the variant and up twice to obtain the type. + // Note that this pattern still holds even when we `use` a variant or `use` an enum type to rename it, or chain `use` expressions + // together; this resolution is handled automatically by `qpath_res`. + + // FIXME: Deal with type aliases? + if in_ty_adt.did() == self.tcx.parent(self.tcx.parent(expr_ctor_def_id)) { + // The constructor definition refers to the "constructor" of the variant: + // For example, `Some(5)` triggers this case. + self.tcx.parent(expr_ctor_def_id) + } else { + // FIXME: Deal with type aliases? + return Err(expr); + } + } + _ => { + return Err(expr); + } + }; + + // We need to know which of the generic parameters mentions our target param. + // We expect that at least one of them does, since it is expected to be mentioned. + let Some((drill_generic_index, generic_argument_type)) = + Self::is_iterator_singleton( + in_ty_adt_generic_args.iter().enumerate().filter( + |(_index, in_ty_generic)| { + Self::find_param_in_ty(*in_ty_generic, param) + }, + ), + ) else { + return Err(expr); + }; + + let struct_generic_parameters: &ty::Generics = self.tcx.generics_of(in_ty_adt.did()); + if drill_generic_index >= struct_generic_parameters.params.len() { + return Err(expr); + } + + let param_to_point_at_in_struct = self.tcx.mk_param_from_def( + struct_generic_parameters.param_at(drill_generic_index, self.tcx), + ); + + // We make 3 steps: + // Suppose we have a type like + // ```ignore (just for demonstration) + // struct ExampleStruct<T> { + // enabled: bool, + // item: Option<(usize, T, bool)>, + // } + // + // f(ExampleStruct { + // enabled: false, + // item: Some((0, Box::new(String::new()), 1) }, true)), + // }); + // ``` + // Here, `f` is passed a `ExampleStruct<Box<String>>`, but it wants + // for `String: Copy`, which isn't true here. + // + // (1) First, we drill into `.item` and highlight that expression + // (2) Then we use the template type `Option<(usize, T, bool)>` to + // drill into the `T`, arriving at a `Box<String>` expression. + // (3) Then we keep going, drilling into this expression using our + // outer contextual information. + + // (1) Find the (unique) field index which mentions the type in our constraint: + let Some((field_index, field_type)) = Self::is_iterator_singleton( + in_ty_adt + .variant_with_id(variant_def_id) + .fields + .iter() + .map(|field| field.ty(self.tcx, *in_ty_adt_generic_args)) + .enumerate() + .filter(|(_index, field_type)| Self::find_param_in_ty((*field_type).into(), param)) + ) else { + return Err(expr); + }; + + if field_index >= expr_args.len() { + return Err(expr); + } + + // (2) Continue drilling into the struct, ignoring the struct's + // generic argument types. + let expr = self.blame_specific_part_of_expr_corresponding_to_generic_param( + param_to_point_at_in_struct, + &expr_args[field_index], + field_type.into(), + )?; + + // (3) Continue drilling into the expression, having "passed + // through" the struct entirely. + return self.blame_specific_part_of_expr_corresponding_to_generic_param( + param, + expr, + generic_argument_type, + ); + } + + // At this point, none of the basic patterns matched. + // One major possibility which remains is that we have a function call. + // In this case, it's often possible to dive deeper into the call to find something to blame, + // but this is not always possible. + + Err(expr) + } + + // FIXME: This can be made into a private, non-impl function later. + /// Traverses the given ty (either a `ty::Ty` or a `ty::GenericArg`) and searches for references + /// to the given `param_to_point_at`. Returns `true` if it finds any use of the param. + pub fn find_param_in_ty( + ty: ty::GenericArg<'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; + } + if let ty::GenericArgKind::Type(ty) = arg.unpack() + && let ty::Alias(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 + } + + // FIXME: This can be made into a private, non-impl function later. + /// Returns `Some(iterator.next())` if it has exactly one item, and `None` otherwise. + pub fn is_iterator_singleton<T>(mut iterator: impl Iterator<Item = T>) -> Option<T> { + match (iterator.next(), iterator.next()) { + (_, Some(_)) => None, + (first, _) => first, + } + } +} diff --git a/compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs b/compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs index 2d841d53f..a46bdeb41 100644 --- a/compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs +++ b/compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs @@ -10,7 +10,9 @@ use crate::{ }; use rustc_ast as ast; use rustc_data_structures::fx::FxIndexSet; -use rustc_errors::{pluralize, Applicability, Diagnostic, DiagnosticId, MultiSpan}; +use rustc_errors::{ + pluralize, Applicability, Diagnostic, DiagnosticId, ErrorGuaranteed, MultiSpan, +}; use rustc_hir as hir; use rustc_hir::def::{CtorOf, DefKind, Res}; use rustc_hir::def_id::DefId; @@ -25,8 +27,8 @@ use rustc_infer::infer::type_variable::{TypeVariableOrigin, TypeVariableOriginKi 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_middle::ty::visit::TypeVisitableExt; +use rustc_middle::ty::{self, DefIdTree, IsSuggestable, Ty}; use rustc_session::Session; use rustc_span::symbol::{kw, Ident}; use rustc_span::{self, sym, Span}; @@ -34,8 +36,6 @@ use rustc_trait_selection::traits::{self, ObligationCauseCode, SelectionContext} use std::iter; use std::mem; -use std::ops::ControlFlow; -use std::slice; impl<'a, 'tcx> FnCtxt<'a, 'tcx> { pub(in super::super) fn check_casts(&mut self) { @@ -73,13 +73,13 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let ty = self.typeck_results.borrow().expr_ty_adjusted(expr); let ty = self.resolve_vars_if_possible(ty); if ty.has_non_region_infer() { - self.tcx.ty_error() + self.tcx.ty_error_misc() } 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)); + .check_asm(asm, enclosing_id); } } @@ -101,7 +101,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let err_inputs = match tuple_arguments { DontTupleArguments => err_inputs, - TupleArguments => vec![self.tcx.intern_tup(&err_inputs)], + TupleArguments => vec![self.tcx.mk_tup(&err_inputs)], }; self.check_argument_types( @@ -114,7 +114,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { tuple_arguments, method.ok().map(|method| method.def_id), ); - return self.tcx.ty_error(); + return self.tcx.ty_error_misc(); } let method = method.unwrap(); @@ -534,7 +534,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { .typeck_results .borrow() .expr_ty_adjusted_opt(*expr) - .unwrap_or_else(|| tcx.ty_error()); + .unwrap_or_else(|| tcx.ty_error_misc()); (self.resolve_vars_if_possible(ty), normalize_span(expr.span)) }) .collect(); @@ -641,7 +641,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { && 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( + let provided_as_tuple = tcx.mk_tup_from_iter( provided_arg_tys.iter().map(|(ty, _)| *ty).skip(mismatch_idx).take(tys.len()), ); @@ -756,15 +756,20 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } 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 trace = mk_trace(provided_span, formal_and_expected_inputs[*expected_idx], provided_ty); - if !matches!(trace.cause.as_failure_code(*e), FailureCode::Error0308(_)) { - self.err_ctxt().report_and_explain_type_error(trace, *e).emit(); - return true; - } - false - }); + 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 trace = + mk_trace(provided_span, formal_and_expected_inputs[*expected_idx], provided_ty); + if !matches!(trace.cause.as_failure_code(*e), FailureCode::Error0308(_)) { + self.err_ctxt().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() { @@ -798,6 +803,24 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { full_call_span, format!("arguments to this {} are incorrect", call_name), ); + if let (Some(callee_ty), hir::ExprKind::MethodCall(_, rcvr, _, _)) = + (callee_ty, &call_expr.kind) + { + // Type that would have accepted this argument if it hadn't been inferred earlier. + // FIXME: We leave an inference variable for now, but it'd be nice to get a more + // specific type to increase the accuracy of the diagnostic. + let expected = self.infcx.next_ty_var(TypeVariableOrigin { + kind: TypeVariableOriginKind::MiscVariable, + span: full_call_span, + }); + self.point_at_expr_source_of_inferred_type( + &mut err, + rcvr, + expected, + callee_ty, + provided_arg_span, + ); + } // Call out where the function is defined self.label_fn_like( &mut err, @@ -847,7 +870,27 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } let mut suggestion_text = SuggestionText::None; + let ty_to_snippet = |ty: Ty<'tcx>, expected_idx: ExpectedIdx| { + if ty.is_unit() { + "()".to_string() + } else if ty.is_suggestable(tcx, false) { + format!("/* {} */", ty) + } else if let Some(fn_def_id) = fn_def_id + && self.tcx.def_kind(fn_def_id).is_fn_like() + && let self_implicit = + matches!(call_expr.kind, hir::ExprKind::MethodCall(..)) as usize + && let Some(arg) = self.tcx.fn_arg_names(fn_def_id) + .get(expected_idx.as_usize() + self_implicit) + && arg.name != kw::SelfLower + { + format!("/* {} */", arg.name) + } else { + "/* value */".to_string() + } + }; + let mut errors = errors.into_iter().peekable(); + let mut suggestions = vec![]; while let Some(error) = errors.next() { match error { Error::Invalid(provided_idx, expected_idx, compatibility) => { @@ -888,12 +931,24 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { "".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, - }; + .push((provided_span, format!("unexpected argument{}", provided_ty_name))); + let mut span = provided_span; + if span.can_be_used_for_suggestions() { + if arg_idx.index() > 0 + && let Some((_, prev)) = provided_arg_tys + .get(ProvidedIdx::from_usize(arg_idx.index() - 1) + ) { + // Include previous comma + span = prev.shrink_to_hi().to(span); + } + suggestions.push((span, String::new())); + + 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, @@ -1078,6 +1133,45 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } } + // Incorporate the argument changes in the removal suggestion. + // When a type is *missing*, and the rest are additional, we want to suggest these with a + // multipart suggestion, but in order to do so we need to figure out *where* the arg that + // was provided but had the wrong type should go, because when looking at `expected_idx` + // that is the position in the argument list in the definition, while `provided_idx` will + // not be present. So we have to look at what the *last* provided position was, and point + // one after to suggest the replacement. FIXME(estebank): This is hacky, and there's + // probably a better more involved change we can make to make this work. + // For example, if we have + // ``` + // fn foo(i32, &'static str) {} + // foo((), (), ()); + // ``` + // what should be suggested is + // ``` + // foo(/* i32 */, /* &str */); + // ``` + // which includes the replacement of the first two `()` for the correct type, and the + // removal of the last `()`. + let mut prev = -1; + for (expected_idx, provided_idx) in matched_inputs.iter_enumerated() { + // We want to point not at the *current* argument expression index, but rather at the + // index position where it *should have been*, which is *after* the previous one. + if let Some(provided_idx) = provided_idx { + prev = provided_idx.index() as i64; + } + let idx = ProvidedIdx::from_usize((prev + 1) as usize); + if let None = provided_idx + && let Some((_, arg_span)) = provided_arg_tys.get(idx) + { + // There is a type that was *not* found anywhere, so it isn't a move, but a + // replacement and we look at what type it should have been. This will allow us + // To suggest a multipart suggestion when encountering `foo(1, "")` where the def + // was `fn foo(())`. + let (_, expected_ty) = formal_and_expected_inputs[expected_idx]; + suggestions.push((*arg_span, ty_to_snippet(expected_ty, expected_idx))); + } + } + // 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 { @@ -1095,7 +1189,12 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { Some(format!("provide the argument{}", if plural { "s" } else { "" })) } SuggestionText::Remove(plural) => { - Some(format!("remove the extra argument{}", if plural { "s" } else { "" })) + err.multipart_suggestion( + &format!("remove the extra argument{}", if plural { "s" } else { "" }), + suggestions, + Applicability::HasPlaceholders, + ); + None } SuggestionText::Swap => Some("swap these arguments".to_string()), SuggestionText::Reorder => Some("reorder these arguments".to_string()), @@ -1134,20 +1233,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } 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 if let Some(fn_def_id) = fn_def_id - && self.tcx.def_kind(fn_def_id).is_fn_like() - && let self_implicit = matches!(call_expr.kind, hir::ExprKind::MethodCall(..)) as usize - && let Some(arg) = self.tcx.fn_arg_names(fn_def_id).get(expected_idx.as_usize() + self_implicit) - && arg.name != kw::SelfLower - { - format!("/* {} */", arg.name) - } else { - "/* value */".to_string() - } + ty_to_snippet(expected_ty, expected_idx) }; suggestion += &suggestion_text; } @@ -1201,7 +1287,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { 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_misc(), } } @@ -1209,15 +1295,15 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { &self, qpath: &QPath<'_>, hir_id: hir::HirId, - ) -> Option<(&'tcx ty::VariantDef, Ty<'tcx>)> { + ) -> Result<(&'tcx ty::VariantDef, Ty<'tcx>), ErrorGuaranteed> { 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( - self.tcx.sess.delay_span_bug(path_span, "`Res::Err` but no error emitted"), - ); - return None; + let guar = + self.tcx.sess.delay_span_bug(path_span, "`Res::Err` but no error emitted"); + self.set_tainted_by_errors(guar); + return Err(guar); } Res::Def(DefKind::Variant, _) => match ty.normalized.ty_adt_def() { Some(adt) => { @@ -1245,28 +1331,26 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // Check bounds on type arguments used in the path. self.add_required_obligations_for_hir(path_span, did, substs, hir_id); - Some((variant, ty.normalized)) + Ok((variant, ty.normalized)) } else { - match ty.normalized.kind() { - ty::Error(_) => { + Err(match *ty.normalized.kind() { + ty::Error(guar) => { // 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). + guar } - _ => { - struct_span_err!( - self.tcx.sess, - path_span, - E0071, - "expected struct, variant or union type, found {}", - ty.normalized.sort_string(self.tcx) - ) - .span_label(path_span, "not a struct") - .emit(); - } - } - None + _ => struct_span_err!( + self.tcx.sess, + path_span, + E0071, + "expected struct, variant or union type, found {}", + ty.normalized.sort_string(self.tcx) + ) + .span_label(path_span, "not a struct") + .emit(), + }) } } @@ -1313,11 +1397,11 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // 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(ty), _) => (None, Some(ty.span)), // Bias towards the explicit user type. (_, Some(init)) => { - (true, Some(init.span.find_ancestor_inside(decl.span).unwrap_or(init.span))) + (Some(init), 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. + _ => (None, None), // We have `let $pat;`, so the expected type is unconstrained. }; // Type check the pattern. Override if necessary to avoid knock-on errors. @@ -1422,11 +1506,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { 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) + CoerceMany::with_coercion_sites(coerce_to_ty, blk.expr.as_slice()) }; let prev_diverges = self.diverges.get(); @@ -1630,9 +1710,9 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { pat: &'tcx hir::Pat<'tcx>, ty: Ty<'tcx>, ) { - if ty.references_error() { + if let Err(guar) = ty.error_reported() { // Override the types everywhere with `err()` to avoid knock on errors. - let err = self.tcx.ty_error(); + let err = self.tcx.ty_error(guar); self.write_ty(hir_id, err); self.write_ty(pat.hir_id, err); let local_ty = LocalTy { decl_ty: err, revealed_ty: err }; @@ -1652,7 +1732,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { match *qpath { QPath::Resolved(ref maybe_qself, ref path) => { let self_ty = maybe_qself.as_ref().map(|qself| self.to_ty(qself).raw); - let ty = self.astconv().res_to_ty(self_ty, path, true); + let ty = self.astconv().res_to_ty(self_ty, path, hir_id, true); (path.res, self.handle_raw_ty(path_span, ty)) } QPath::TypeRelative(ref qself, ref segment) => { @@ -1661,7 +1741,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let result = self .astconv() .associated_path_to_ty(hir_id, path_span, ty.raw, qself, segment, true); - let ty = result.map(|(ty, _, _)| ty).unwrap_or_else(|_| self.tcx().ty_error()); + let ty = + result.map(|(ty, _, _)| ty).unwrap_or_else(|guar| self.tcx().ty_error(guar)); let ty = self.handle_raw_ty(path_span, ty); let result = result.map(|(_, kind, def_id)| (kind, def_id)); @@ -1739,353 +1820,6 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } } - 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; }; - - 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::Clause(ty::Clause::Trait(pred)) => pred.trait_ref.substs, - ty::PredicateKind::Clause(ty::Clause::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_parent(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 ty = self.tcx.type_of(def_id); - if !ty.is_fn() { - return false; - } - let sig = ty.fn_sig(self.tcx).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, @@ -2114,7 +1848,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { match *callee_ty.kind() { ty::Param(param) => { let param = - self.tcx.generics_of(self.body_id.owner).type_param(¶m, self.tcx); + self.tcx.generics_of(self.body_id).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; @@ -2123,7 +1857,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // and point at that. let instantiated = self .tcx - .explicit_predicates_of(self.body_id.owner) + .explicit_predicates_of(self.body_id) .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 @@ -2200,8 +1934,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { 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))); + err.span_note(spans, &format!("{} defined here", self.tcx.def_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 { @@ -2214,31 +1947,10 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { }; 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)), + &format!("{} defined here", self.tcx.def_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::Alias(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 -} diff --git a/compiler/rustc_hir_typeck/src/fn_ctxt/mod.rs b/compiler/rustc_hir_typeck/src/fn_ctxt/mod.rs index 428fde642..1dea3e6f9 100644 --- a/compiler/rustc_hir_typeck/src/fn_ctxt/mod.rs +++ b/compiler/rustc_hir_typeck/src/fn_ctxt/mod.rs @@ -1,4 +1,5 @@ mod _impl; +mod adjust_fulfillment_errors; mod arg_matrix; mod checks; mod suggestions; @@ -10,14 +11,14 @@ pub use suggestions::*; use crate::coercion::DynamicCoerceMany; use crate::{Diverges, EnclosingBreakables, Inherited}; use rustc_hir as hir; -use rustc_hir::def_id::DefId; +use rustc_hir::def_id::{DefId, LocalDefId}; use rustc_hir_analysis::astconv::AstConv; use rustc_infer::infer; use rustc_infer::infer::error_reporting::TypeErrCtxt; use rustc_infer::infer::type_variable::{TypeVariableOrigin, TypeVariableOriginKind}; use rustc_middle::infer::unify_key::{ConstVariableOrigin, ConstVariableOriginKind}; use rustc_middle::ty::subst::GenericArgKind; -use rustc_middle::ty::{self, Const, Ty, TyCtxt, TypeVisitable}; +use rustc_middle::ty::{self, Const, Ty, TyCtxt, TypeVisitableExt}; use rustc_session::Session; use rustc_span::symbol::Ident; use rustc_span::{self, Span, DUMMY_SP}; @@ -38,7 +39,7 @@ use std::ops::Deref; /// [`ItemCtxt`]: rustc_hir_analysis::collect::ItemCtxt /// [`InferCtxt`]: infer::InferCtxt pub struct FnCtxt<'a, 'tcx> { - pub(super) body_id: hir::HirId, + pub(super) body_id: LocalDefId, /// The parameter environment used for proving trait obligations /// in this function. This can change when we descend into @@ -117,7 +118,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { pub fn new( inh: &'a Inherited<'tcx>, param_env: ty::ParamEnv<'tcx>, - body_id: hir::HirId, + body_id: LocalDefId, ) -> FnCtxt<'a, 'tcx> { FnCtxt { body_id, @@ -204,7 +205,7 @@ impl<'a, 'tcx> AstConv<'tcx> for FnCtxt<'a, 'tcx> { } fn item_def_id(&self) -> DefId { - self.body_id.owner.to_def_id() + self.body_id.to_def_id() } fn get_type_parameter_bounds( @@ -288,7 +289,7 @@ impl<'a, 'tcx> AstConv<'tcx> for FnCtxt<'a, 'tcx> { item_segment: &hir::PathSegment<'_>, poly_trait_ref: ty::PolyTraitRef<'tcx>, ) -> Ty<'tcx> { - let trait_ref = self.replace_bound_vars_with_fresh_vars( + let trait_ref = self.instantiate_binder_with_fresh_vars( span, infer::LateBoundRegionConversionTime::AssocTypeProjection(item_def_id), poly_trait_ref, @@ -324,6 +325,10 @@ impl<'a, 'tcx> AstConv<'tcx> for FnCtxt<'a, 'tcx> { let ty = if !ty.has_escaping_bound_vars() { self.normalize(span, ty) } else { ty }; self.write_ty(hir_id, ty) } + + fn infcx(&self) -> Option<&infer::InferCtxt<'tcx>> { + Some(&self.infcx) + } } /// Represents a user-provided type in the raw form (never normalized). diff --git a/compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs b/compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs index 4d673ac91..c49621b7c 100644 --- a/compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs +++ b/compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs @@ -1,6 +1,7 @@ use super::FnCtxt; use crate::errors::{AddReturnTypeSuggestion, ExpectedReturnTypeLabel}; +use crate::fluent_generated as fluent; use crate::method::probe::{IsSuggestion, Mode, ProbeScope}; use rustc_ast::util::parser::{ExprPrecedence, PREC_POSTFIX}; use rustc_errors::{Applicability, Diagnostic, MultiSpan}; @@ -13,9 +14,10 @@ use rustc_hir::{ use rustc_hir_analysis::astconv::AstConv; use rustc_infer::traits::{self, StatementAsExpression}; use rustc_middle::lint::in_external_macro; +use rustc_middle::ty::print::with_no_trimmed_paths; use rustc_middle::ty::{ self, suggest_constraining_type_params, Binder, DefIdTree, IsSuggestable, ToPredicate, Ty, - TypeVisitable, + TypeVisitableExt, }; use rustc_session::errors::ExprParenthesesNeeded; use rustc_span::source_map::Spanned; @@ -31,7 +33,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { self.typeck_results .borrow() .liberated_fn_sigs() - .get(self.tcx.hir().parent_id(self.body_id)) + .get(self.tcx.hir().local_def_id_to_hir_id(self.body_id)) .copied() } @@ -119,7 +121,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { DefIdOrName::DefId(def_id) => match self.tcx.def_kind(def_id) { DefKind::Ctor(CtorOf::Struct, _) => "construct this tuple struct".to_string(), DefKind::Ctor(CtorOf::Variant, _) => "construct this tuple variant".to_string(), - kind => format!("call this {}", kind.descr(def_id)), + kind => format!("call this {}", self.tcx.def_kind_descr(kind, def_id)), }, DefIdOrName::Name(name) => format!("call this {name}"), }; @@ -164,7 +166,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { &self, ty: Ty<'tcx>, ) -> Option<(DefIdOrName, Ty<'tcx>, Vec<Ty<'tcx>>)> { - self.err_ctxt().extract_callable_info(self.body_id, self.param_env, ty) + let body_hir_id = self.tcx.hir().local_def_id_to_hir_id(self.body_id); + self.err_ctxt().extract_callable_info(body_hir_id, self.param_env, ty) } pub fn suggest_two_fn_call( @@ -337,7 +340,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { CtorOf::Variant => "an enum variant", })); } else { - let descr = kind.descr(def_id); + let descr = self.tcx.def_kind_descr(kind, def_id); err.span_label(sp, format!("{descr} `{name}` defined here")); } return true; @@ -413,11 +416,16 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { if let ty::Adt(adt, _) = peeled.kind() && Some(adt.did()) == self.tcx.lang_items().string() { + let sugg = if ref_cnt == 0 { + ".as_deref()" + } else { + ".map(|x| x.as_str())" + }; err.span_suggestion_verbose( expr.span.shrink_to_hi(), - "try converting the passed type into a `&str`", - format!(".map(|x| &*{}x)", "*".repeat(ref_cnt)), - Applicability::MaybeIncorrect, + fluent::hir_typeck_convert_to_str, + sugg, + Applicability::MachineApplicable, ); return true; } @@ -681,7 +689,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { return true; } &hir::FnRetTy::DefaultReturn(span) if expected.is_unit() => { - if found.is_suggestable(self.tcx, false) { + if let Some(found) = found.make_suggestable(self.tcx, false) { err.subdiagnostic(AddReturnTypeSuggestion::Add { span, found: found.to_string() }); return true; } else if let ty::Closure(_, substs) = found.kind() @@ -698,10 +706,38 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } } hir::FnRetTy::Return(ty) => { + let span = ty.span; + + if let hir::TyKind::OpaqueDef(item_id, ..) = ty.kind + && let hir::Node::Item(hir::Item { + kind: hir::ItemKind::OpaqueTy(op_ty), + .. + }) = self.tcx.hir().get(item_id.hir_id()) + && let hir::OpaqueTy { + bounds: [bound], .. + } = op_ty + && let hir::GenericBound::LangItemTrait( + hir::LangItem::Future, _, _, generic_args) = bound + && let hir::GenericArgs { bindings: [ty_binding], .. } = generic_args + && let hir::TypeBinding { kind, .. } = ty_binding + && let hir::TypeBindingKind::Equality { term } = kind + && let hir::Term::Ty(term_ty) = term { + // Check if async function's return type was omitted. + // Don't emit suggestions if the found type is `impl Future<...>`. + debug!("suggest_missing_return_type: found = {:?}", found); + if found.is_suggestable(self.tcx, false) { + if term_ty.span.is_empty() { + err.subdiagnostic(AddReturnTypeSuggestion::Add { span, found: found.to_string() }); + return true; + } else { + err.subdiagnostic(ExpectedReturnTypeLabel::Other { span, expected }); + } + } + } + // Only point to return type if the expected type is the return type, as if they // are not, the expectation must have been caused by something else. debug!("suggest_missing_return_type: return type {:?} node {:?}", ty, ty.kind); - let span = ty.span; let ty = self.astconv().ast_ty_to_ty(ty); debug!("suggest_missing_return_type: return type {:?}", ty); debug!("suggest_missing_return_type: expected type {:?}", ty); @@ -980,7 +1016,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let expr_inner_ty = substs.type_at(0); let expected_inner_ty = expected_substs.type_at(0); if let ty::Ref(_, ty, hir::Mutability::Not) = expr_inner_ty.kind() - && self.can_eq(self.param_env, *ty, expected_inner_ty).is_ok() + && self.can_eq(self.param_env, *ty, expected_inner_ty) { let def_path = self.tcx.def_path_str(adt_def.did()); if self.type_is_copy_modulo_regions(self.param_env, *ty, expr.span) { @@ -1019,7 +1055,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { if let Some(result_did) = self.tcx.get_diagnostic_item(sym::Result) && adt_def.did() == result_did // Check that the error types are equal - && self.can_eq(self.param_env, substs.type_at(1), expected_substs.type_at(1)).is_ok() + && self.can_eq(self.param_env, substs.type_at(1), expected_substs.type_at(1)) { return suggest_copied_or_cloned(); } else if let Some(option_did) = self.tcx.get_diagnostic_item(sym::Option) @@ -1238,6 +1274,49 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } } + /// Suggest providing `std::ptr::null()` or `std::ptr::null_mut()` if they + /// pass in a literal 0 to an raw pointer. + #[instrument(skip(self, err))] + pub(crate) fn suggest_null_ptr_for_literal_zero_given_to_ptr_arg( + &self, + err: &mut Diagnostic, + expr: &hir::Expr<'_>, + expected_ty: Ty<'tcx>, + ) -> bool { + // Expected type needs to be a raw pointer. + let ty::RawPtr(ty::TypeAndMut { mutbl, .. }) = expected_ty.kind() else { + return false; + }; + + // Provided expression needs to be a literal `0`. + let ExprKind::Lit(Spanned { + node: rustc_ast::LitKind::Int(0, _), + span, + }) = expr.kind else { + return false; + }; + + // We need to find a null pointer symbol to suggest + let null_sym = match mutbl { + hir::Mutability::Not => sym::ptr_null, + hir::Mutability::Mut => sym::ptr_null_mut, + }; + let Some(null_did) = self.tcx.get_diagnostic_item(null_sym) else { + return false; + }; + let null_path_str = with_no_trimmed_paths!(self.tcx.def_path_str(null_did)); + + // We have satisfied all requirements to provide a suggestion. Emit it. + err.span_suggestion( + span, + format!("if you meant to create a null pointer, use `{null_path_str}()`"), + null_path_str + "()", + Applicability::MachineApplicable, + ); + + true + } + pub(crate) fn suggest_associated_const( &self, err: &mut Diagnostic, @@ -1258,16 +1337,17 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { hir::Path { segments: [segment], .. }, )) | hir::ExprKind::Path(QPath::TypeRelative(ty, segment)) => { - let self_ty = self.astconv().ast_ty_to_ty(ty); - if let Ok(pick) = self.probe_for_name( - Mode::Path, - Ident::new(capitalized_name, segment.ident.span), - Some(expected_ty), - IsSuggestion(true), - self_ty, - expr.hir_id, - ProbeScope::TraitsInScope, - ) { + if let Some(self_ty) = self.typeck_results.borrow().node_type_opt(ty.hir_id) + && let Ok(pick) = self.probe_for_name( + Mode::Path, + Ident::new(capitalized_name, segment.ident.span), + Some(expected_ty), + IsSuggestion(true), + self_ty, + expr.hir_id, + ProbeScope::TraitsInScope, + ) + { (pick.item, segment) } else { return false; @@ -1299,7 +1379,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // Same item return false; } - let item_ty = self.tcx.type_of(item.def_id); + let item_ty = self.tcx.type_of(item.def_id).subst_identity(); // FIXME(compiler-errors): This check is *so* rudimentary if item_ty.needs_subst() { return false; @@ -1379,6 +1459,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { generics, diag, vec![(param.name.as_str(), "Clone", Some(clone_trait_did))].into_iter(), + None, ); } else { self.suggest_derive(diag, &[(trait_ref.to_predicate(self.tcx), None, None)]); |