From 1376c5a617be5c25655d0d7cb63e3beaa5a6e026 Mon Sep 17 00:00:00 2001 From: Daniel Baumann Date: Wed, 17 Apr 2024 14:20:39 +0200 Subject: Merging upstream version 1.70.0+dfsg1. Signed-off-by: Daniel Baumann --- compiler/rustc_hir_typeck/src/fn_ctxt/_impl.rs | 216 ++++++++++++--------- .../src/fn_ctxt/adjust_fulfillment_errors.rs | 121 ++++++------ compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs | 91 ++++++--- compiler/rustc_hir_typeck/src/fn_ctxt/mod.rs | 6 +- .../rustc_hir_typeck/src/fn_ctxt/suggestions.rs | 91 ++++----- 5 files changed, 295 insertions(+), 230 deletions(-) (limited to 'compiler/rustc_hir_typeck/src/fn_ctxt') diff --git a/compiler/rustc_hir_typeck/src/fn_ctxt/_impl.rs b/compiler/rustc_hir_typeck/src/fn_ctxt/_impl.rs index 60e55c7b0..f736f7a96 100644 --- a/compiler/rustc_hir_typeck/src/fn_ctxt/_impl.rs +++ b/compiler/rustc_hir_typeck/src/fn_ctxt/_impl.rs @@ -19,13 +19,13 @@ use rustc_hir_analysis::astconv::{ }; use rustc_infer::infer::canonical::{Canonical, OriginalQueryValues, QueryResponse}; use rustc_infer::infer::error_reporting::TypeAnnotationNeeded::E0282; -use rustc_infer::infer::InferResult; +use rustc_infer::infer::{DefineOpaqueTypes, 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, TypeVisitableExt}; use rustc_middle::ty::{ - self, AdtKind, CanonicalUserType, DefIdTree, GenericParamDefKind, Ty, TyCtxt, UserType, + self, AdtKind, CanonicalUserType, GenericParamDefKind, Ty, TyCtxt, UserType, }; use rustc_middle::ty::{GenericArgKind, SubstsRef, UserSelfTy, UserSubsts}; use rustc_session::lint; @@ -33,6 +33,7 @@ use rustc_span::def_id::LocalDefId; use rustc_span::hygiene::DesugaringKind; use rustc_span::symbol::{kw, sym, Ident}; use rustc_span::Span; +use rustc_target::abi::FieldIdx; use rustc_trait_selection::traits::error_reporting::TypeErrCtxtExt as _; use rustc_trait_selection::traits::{self, NormalizeExt, ObligationCauseCode, ObligationCtxt}; @@ -147,7 +148,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } } - pub fn write_field_index(&self, hir_id: hir::HirId, index: usize) { + pub fn write_field_index(&self, hir_id: hir::HirId, index: FieldIdx) { self.typeck_results.borrow_mut().field_indices_mut().insert(hir_id, index); } @@ -301,16 +302,12 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { span: Span, def_id: DefId, substs: SubstsRef<'tcx>, - ) -> (ty::InstantiatedPredicates<'tcx>, Vec) { + ) -> ty::InstantiatedPredicates<'tcx> { let bounds = self.tcx.predicates_of(def_id); - let spans: Vec = bounds.predicates.iter().map(|(_, span)| *span).collect(); let result = bounds.instantiate(self.tcx, substs); let result = self.normalize(span, result); - debug!( - "instantiate_bounds(bounds={:?}, substs={:?}) = {:?}, {:?}", - bounds, substs, result, spans, - ); - (result, spans) + debug!("instantiate_bounds(bounds={:?}, substs={:?}) = {:?}", bounds, substs, result); + result } pub(in super::super) fn normalize(&self, span: Span, value: T) -> T @@ -562,7 +559,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let span = self.tcx.hir().body(body_id).value.span; let ok = self .at(&self.misc(span), self.param_env) - .eq(interior, witness) + .eq(DefineOpaqueTypes::No, interior, witness) .expect("Failed to unify generator interior type"); let mut obligations = ok.obligations; @@ -581,11 +578,11 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { #[instrument(skip(self), level = "debug")] pub(in super::super) fn report_ambiguity_errors(&self) { - let mut errors = self.fulfillment_cx.borrow_mut().collect_remaining_errors(); + let mut errors = self.fulfillment_cx.borrow_mut().collect_remaining_errors(self); if !errors.is_empty() { self.adjust_fulfillment_errors_for_expr_obligation(&mut errors); - self.err_ctxt().report_fulfillment_errors(&errors, self.inh.body_id); + self.err_ctxt().report_fulfillment_errors(&errors); } } @@ -598,7 +595,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { if !result.is_empty() { mutate_fulfillment_errors(&mut result); self.adjust_fulfillment_errors_for_expr_obligation(&mut result); - self.err_ctxt().report_fulfillment_errors(&result, self.inh.body_id); + self.err_ctxt().report_fulfillment_errors(&result); } } @@ -670,7 +667,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { | ty::PredicateKind::Clause(ty::Clause::TypeOutlives(..)) | ty::PredicateKind::WellFormed(..) | ty::PredicateKind::ObjectSafe(..) - | ty::PredicateKind::AliasEq(..) + | ty::PredicateKind::AliasRelate(..) | ty::PredicateKind::ConstEvaluatable(..) | ty::PredicateKind::ConstEquate(..) // N.B., this predicate is created by breaking down a @@ -902,56 +899,74 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { ) } - /// Given a function `Node`, return its `FnDecl` if it exists, or `None` otherwise. + /// Given a function `Node`, return its `HirId` and `FnDecl` if it exists. Given a closure + /// that is the child of a function, return that function's `HirId` and `FnDecl` instead. + /// This may seem confusing at first, but this is used in diagnostics for `async fn`, + /// for example, where most of the type checking actually happens within a nested closure, + /// but we often want access to the parent function's signature. + /// + /// Otherwise, return false. pub(in super::super) fn get_node_fn_decl( &self, node: Node<'tcx>, - ) -> Option<(&'tcx hir::FnDecl<'tcx>, Ident, bool)> { + ) -> Option<(hir::HirId, &'tcx hir::FnDecl<'tcx>, Ident, bool)> { match node { - Node::Item(&hir::Item { ident, kind: hir::ItemKind::Fn(ref sig, ..), .. }) => { + Node::Item(&hir::Item { + ident, + kind: hir::ItemKind::Fn(ref sig, ..), + owner_id, + .. + }) => { // This is less than ideal, it will not suggest a return type span on any // method called `main`, regardless of whether it is actually the entry point, // but it will still present it as the reason for the expected type. - Some((&sig.decl, ident, ident.name != sym::main)) + Some(( + hir::HirId::make_owner(owner_id.def_id), + &sig.decl, + ident, + ident.name != sym::main, + )) } Node::TraitItem(&hir::TraitItem { ident, kind: hir::TraitItemKind::Fn(ref sig, ..), + owner_id, .. - }) => Some((&sig.decl, ident, true)), + }) => Some((hir::HirId::make_owner(owner_id.def_id), &sig.decl, ident, true)), Node::ImplItem(&hir::ImplItem { ident, kind: hir::ImplItemKind::Fn(ref sig, ..), + owner_id, .. - }) => 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 { + }) => Some((hir::HirId::make_owner(owner_id.def_id), &sig.decl, ident, false)), + Node::Expr(&hir::Expr { hir_id, kind: hir::ExprKind::Closure(..), .. }) + if let Some(Node::Item(&hir::Item { + ident, + kind: hir::ItemKind::Fn(ref sig, ..), + owner_id, + .. + })) = self.tcx.hir().find_parent(hir_id) => Some(( + hir::HirId::make_owner(owner_id.def_id), + &sig.decl, ident, - kind: hir::ItemKind::Fn(ref sig, ..), - .. - })) = self.tcx.hir().find_parent(hir_id) => { - Some((&sig.decl, ident, ident.name != sym::main)) - }, + ident.name != sym::main, + )), _ => None, } } - /// Given a `HirId`, return the `FnDecl` of the method it is enclosed by and whether a + /// Given a `HirId`, return the `HirId` of the enclosing function, its `FnDecl`, and whether a /// suggestion can be made, `None` otherwise. - pub fn get_fn_decl(&self, blk_id: hir::HirId) -> Option<(&'tcx hir::FnDecl<'tcx>, bool)> { + pub fn get_fn_decl( + &self, + blk_id: hir::HirId, + ) -> Option<(hir::HirId, &'tcx hir::FnDecl<'tcx>, bool)> { // Get enclosing Fn, if it is a function or a trait method, unless there's a `loop` or // `while` before reaching it, as block tail returns are not available in them. self.tcx.hir().get_return_block(blk_id).and_then(|blk_id| { let parent = self.tcx.hir().get(blk_id); - self.get_node_fn_decl(parent).map(|(fn_decl, _, is_main)| (fn_decl, is_main)) + self.get_node_fn_decl(parent) + .map(|(fn_id, fn_decl, _, is_main)| (fn_id, fn_decl, is_main)) }) } @@ -959,44 +974,75 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { &self, err: &mut Diagnostic, expr: &hir::Expr<'_>, - expected: Ty<'tcx>, + expected: Option>, found: Ty<'tcx>, ) { if found != self.tcx.types.unit { return; } - if let ExprKind::MethodCall(path_segment, rcvr, ..) = expr.kind { - if self - .typeck_results + + let ExprKind::MethodCall(path_segment, rcvr, ..) = expr.kind else { + return; + }; + + let rcvr_has_the_expected_type = self + .typeck_results + .borrow() + .expr_ty_adjusted_opt(rcvr) + .and_then(|ty| expected.map(|expected_ty| expected_ty.peel_refs() == ty.peel_refs())) + .unwrap_or(false); + + let prev_call_mutates_and_returns_unit = || { + self.typeck_results .borrow() - .expr_ty_adjusted_opt(rcvr) - .map_or(true, |ty| expected.peel_refs() != ty.peel_refs()) - { - return; - } - let mut sp = MultiSpan::from_span(path_segment.ident.span); - sp.push_span_label( - path_segment.ident.span, - format!( - "this call modifies {} in-place", - match rcvr.kind { - ExprKind::Path(QPath::Resolved( - None, - hir::Path { segments: [segment], .. }, - )) => format!("`{}`", segment.ident), - _ => "its receiver".to_string(), - } - ), - ); + .type_dependent_def_id(expr.hir_id) + .map(|def_id| self.tcx.fn_sig(def_id).skip_binder().skip_binder()) + .and_then(|sig| sig.inputs_and_output.split_last()) + .map(|(output, inputs)| { + output.is_unit() + && inputs + .get(0) + .and_then(|self_ty| self_ty.ref_mutability()) + .map_or(false, rustc_ast::Mutability::is_mut) + }) + .unwrap_or(false) + }; + + if !(rcvr_has_the_expected_type || prev_call_mutates_and_returns_unit()) { + return; + } + + let mut sp = MultiSpan::from_span(path_segment.ident.span); + sp.push_span_label( + path_segment.ident.span, + format!( + "this call modifies {} in-place", + match rcvr.kind { + ExprKind::Path(QPath::Resolved( + None, + hir::Path { segments: [segment], .. }, + )) => format!("`{}`", segment.ident), + _ => "its receiver".to_string(), + } + ), + ); + + let modifies_rcvr_note = + format!("method `{}` modifies its receiver in-place", path_segment.ident); + if rcvr_has_the_expected_type { sp.push_span_label( rcvr.span, "you probably want to use this value after calling the method...", ); + err.span_note(sp, &modifies_rcvr_note); + err.note(&format!("...instead of the `()` output of method `{}`", path_segment.ident)); + } else if let ExprKind::MethodCall(..) = rcvr.kind { err.span_note( sp, - &format!("method `{}` modifies its receiver in-place", path_segment.ident), + modifies_rcvr_note.clone() + ", it is not meant to be used in method chains.", ); - err.note(&format!("...instead of the `()` output of method `{}`", path_segment.ident)); + } else { + err.span_note(sp, &modifies_rcvr_note); } } @@ -1319,7 +1365,11 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // This also occurs for an enum variant on a type alias. 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) { + match self.at(&self.misc(span), self.param_env).eq( + DefineOpaqueTypes::No, + impl_ty, + self_ty, + ) { Ok(ok) => self.register_infer_ok_obligations(ok), Err(_) => { self.tcx.sess.delay_span_bug( @@ -1367,41 +1417,19 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { ) { let param_env = self.param_env; - let remap = match self.tcx.def_kind(def_id) { - // Associated consts have `Self: ~const Trait` bounds that should be satisfiable when - // `Self: Trait` is satisfied because it does not matter whether the impl is `const`. - // Therefore we have to remap the param env here to be non-const. - hir::def::DefKind::AssocConst => true, - hir::def::DefKind::AssocFn - if self.tcx.def_kind(self.tcx.parent(def_id)) == hir::def::DefKind::Trait => - { - // N.B.: All callsites to this function involve checking a path expression. - // - // When instantiating a trait method as a function item, it does not actually matter whether - // the trait is `const` or not, or whether `where T: ~const Tr` needs to be satisfied as - // `const`. If we were to introduce instantiating trait methods as `const fn`s, we would - // check that after this, either via a bound `where F: ~const FnOnce` or when coercing to a - // `const fn` pointer. - // - // FIXME(fee1-dead) FIXME(const_trait_impl): update this doc when trait methods can satisfy - // `~const FnOnce` or can be coerced to `const fn` pointer. - true - } - _ => false, - }; - let (bounds, _) = self.instantiate_bounds(span, def_id, &substs); + let bounds = self.instantiate_bounds(span, def_id, &substs); - for mut obligation in traits::predicates_for_generics( + for obligation in traits::predicates_for_generics( |idx, predicate_span| { traits::ObligationCause::new(span, self.body_id, code(idx, predicate_span)) }, param_env, bounds, ) { - if remap { - obligation = obligation.without_const(self.tcx); - } - self.register_predicate(obligation); + // N.B. We are remapping all predicates to non-const since we don't know if we just + // want them as function pointers or we are calling them from a const-context. The + // actual checking will occur in `rustc_const_eval::transform::check_consts`. + self.register_predicate(obligation.without_const(self.tcx)); } } @@ -1415,7 +1443,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } else { let e = self.tainted_by_errors().unwrap_or_else(|| { self.err_ctxt() - .emit_inference_failure_err((**self).body_id, sp, ty.into(), E0282, true) + .emit_inference_failure_err(self.body_id, sp, ty.into(), E0282, true) .emit() }); let err = self.tcx.ty_error(e); 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 index b09886fe3..f879ccbb3 100644 --- a/compiler/rustc_hir_typeck/src/fn_ctxt/adjust_fulfillment_errors.rs +++ b/compiler/rustc_hir_typeck/src/fn_ctxt/adjust_fulfillment_errors.rs @@ -3,10 +3,8 @@ 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_middle::ty::{self, Ty, TyCtxt, TypeSuperVisitable, TypeVisitable, TypeVisitor}; +use rustc_span::{self, symbol::kw, Span}; use rustc_trait_selection::traits; use std::ops::ControlFlow; @@ -27,17 +25,28 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { 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(), + ty::PredicateKind::Clause(ty::Clause::Trait(pred)) => pred.trait_ref.substs.to_vec(), + ty::PredicateKind::Clause(ty::Clause::Projection(pred)) => { + pred.projection_ty.substs.to_vec() + } + ty::PredicateKind::Clause(ty::Clause::ConstArgHasType(arg, ty)) => { + vec![ty.into(), arg.into()] + } + ty::PredicateKind::ConstEvaluatable(e) => vec![e.into()], + _ => return false, }; - let find_param_matching = |matches: &dyn Fn(&ty::ParamTy) -> bool| { - predicate_substs.types().find_map(|ty| { - ty.walk().find_map(|arg| { + let find_param_matching = |matches: &dyn Fn(ty::ParamTerm) -> bool| { + predicate_substs.iter().find_map(|arg| { + arg.walk().find_map(|arg| { if let ty::GenericArgKind::Type(ty) = arg.unpack() - && let ty::Param(param_ty) = ty.kind() - && matches(param_ty) + && let ty::Param(param_ty) = *ty.kind() + && matches(ty::ParamTerm::Ty(param_ty)) + { + Some(arg) + } else if let ty::GenericArgKind::Const(ct) = arg.unpack() + && let ty::ConstKind::Param(param_ct) = ct.kind() + && matches(ty::ParamTerm::Const(param_ct)) { Some(arg) } else { @@ -49,26 +58,27 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // 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 + let mut param_to_point_at = find_param_matching(&|param_term| { + self.tcx.parent(generics.param_at(param_term.index(), 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 + let mut fallback_param_to_point_at = find_param_matching(&|param_term| { + self.tcx.parent(generics.param_at(param_term.index(), self.tcx).def_id) != def_id + && !matches!(param_term, ty::ParamTerm::Ty(ty) if ty.name == 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); + let mut self_param_to_point_at = find_param_matching( + &|param_term| matches!(param_term, ty::ParamTerm::Ty(ty) if ty.name == 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 { + if let traits::FulfillmentErrorCode::CodeAmbiguity { .. } = error.code { fallback_param_to_point_at = None; self_param_to_point_at = None; param_to_point_at = @@ -227,15 +237,12 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { .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; }; + .get(index) else { return false; }; error.obligation.cause.span = arg .span() .find_ancestor_in_same_ctxt(error.obligation.cause.span) @@ -302,7 +309,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { .iter() .filter(|field| { let field_ty = field.ty(self.tcx, identity_substs); - Self::find_param_in_ty(field_ty.into(), param_to_point_at) + find_param_in_ty(field_ty.into(), param_to_point_at) }) .collect(); @@ -348,7 +355,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { .inputs() .iter() .enumerate() - .filter(|(_, ty)| Self::find_param_in_ty((**ty).into(), param_to_point_at)) + .filter(|(_, ty)| 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() @@ -459,7 +466,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { /// 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 + /// reported as an error. If it is `Ok`, then it means it refined successful. 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, @@ -527,7 +534,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { /// - in_ty: `(Option, 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`), + /// If successful, we return `Ok(refined_expr)`. If unsuccessful, 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()")`. /// @@ -571,8 +578,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // 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) + let Some((drill_expr, drill_ty)) = is_iterator_singleton(expr_elements.iter().zip( in_ty_elements.iter()).filter(|(_expr_elem, in_ty_elem)| { + 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); @@ -620,10 +627,10 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // 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( + is_iterator_singleton( in_ty_adt_generic_args.iter().enumerate().filter( |(_index, in_ty_generic)| { - Self::find_param_in_ty(*in_ty_generic, param) + find_param_in_ty(*in_ty_generic, param) }, ), ) else { @@ -743,10 +750,10 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // 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( + is_iterator_singleton( in_ty_adt_generic_args.iter().enumerate().filter( |(_index, in_ty_generic)| { - Self::find_param_in_ty(*in_ty_generic, param) + find_param_in_ty(*in_ty_generic, param) }, ), ) else { @@ -785,14 +792,14 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // 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( + let Some((field_index, field_type)) = 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)) + .filter(|(_index, field_type)| find_param_in_ty((*field_type).into(), param)) ) else { return Err(expr); }; @@ -825,20 +832,20 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { 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() +/// 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. +fn find_param_in_ty<'tcx>( + 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 @@ -849,16 +856,14 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // in some UI tests. walk.skip_current_subtree(); } - } - false } + 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(mut iterator: impl Iterator) -> Option { - match (iterator.next(), iterator.next()) { - (_, Some(_)) => None, - (first, _) => first, - } +/// Returns `Some(iterator.next())` if it has exactly one item, and `None` otherwise. +fn is_iterator_singleton(mut iterator: impl Iterator) -> Option { + 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 a46bdeb41..ea1b52daa 100644 --- a/compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs +++ b/compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs @@ -24,14 +24,14 @@ use rustc_hir_analysis::structured_errors::StructuredDiagnostic; 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_infer::infer::{DefineOpaqueTypes, InferOk}; use rustc_middle::ty::adjustment::AllowTwoPhase; use rustc_middle::ty::visit::TypeVisitableExt; -use rustc_middle::ty::{self, DefIdTree, IsSuggestable, Ty}; +use rustc_middle::ty::{self, IsSuggestable, Ty}; use rustc_session::Session; use rustc_span::symbol::{kw, Ident}; -use rustc_span::{self, sym, Span}; +use rustc_span::{self, sym, BytePos, Span}; use rustc_trait_selection::traits::{self, ObligationCauseCode, SelectionContext}; use std::iter; @@ -301,9 +301,11 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // 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 supertype_error = self.at(&self.misc(provided_arg.span), self.param_env).sup( + DefineOpaqueTypes::No, + formal_input_ty, + coerced_ty, + ); let subtyping_error = match supertype_error { Ok(InferOk { obligations, value: () }) => { self.register_predicates(obligations); @@ -470,7 +472,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { err_code: &str, fn_def_id: Option, call_span: Span, - call_expr: &hir::Expr<'tcx>, + call_expr: &'tcx hir::Expr<'tcx>, ) { // Next, let's construct the error let (error_span, full_call_span, call_name, is_method) = match &call_expr.kind { @@ -585,7 +587,9 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // 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() + self.at(&self.misc(arg_span), self.param_env) + .sup(DefineOpaqueTypes::No, formal_input_ty, coerced_ty) + .err() }); // Same as above: if either the coerce type or the checked type is an error type, @@ -764,7 +768,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { 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(_)) { + if !matches!(trace.cause.as_failure_code(*e), FailureCode::Error0308) { self.err_ctxt().report_and_explain_type_error(trace, *e).emit(); return true; } @@ -803,24 +807,20 @@ 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) + + if let hir::ExprKind::MethodCall(_, rcvr, _, _) = call_expr.kind + && provided_idx.as_usize() == expected_idx.as_usize() { - // 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( + self.note_source_of_type_mismatch_constraint( &mut err, rcvr, - expected, - callee_ty, - provided_arg_span, + crate::demand::TypeMismatchSource::Arg { + call_expr, + incompatible_arg: provided_idx.as_usize(), + }, ); } + // Call out where the function is defined self.label_fn_like( &mut err, @@ -890,8 +890,13 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { }; let mut errors = errors.into_iter().peekable(); + let mut only_extras_so_far = errors + .peek() + .map_or(false, |first| matches!(first, Error::Extra(arg_idx) if arg_idx.index() == 0)); let mut suggestions = vec![]; while let Some(error) = errors.next() { + only_extras_so_far &= matches!(error, Error::Extra(_)); + match error { Error::Invalid(provided_idx, expected_idx, compatibility) => { let (formal_ty, expected_ty) = formal_and_expected_inputs[expected_idx]; @@ -937,10 +942,34 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { 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); - } + ) { + // Include previous comma + span = prev.shrink_to_hi().to(span); + } + + // Is last argument for deletion in a row starting from the 0-th argument? + // Then delete the next comma, so we are not left with `f(, ...)` + // + // fn f() {} + // - f(0, 1,) + // + f() + if only_extras_so_far + && errors + .peek() + .map_or(true, |next_error| !matches!(next_error, Error::Extra(_))) + { + let next = provided_arg_tys + .get(arg_idx + 1) + .map(|&(_, sp)| sp) + .unwrap_or_else(|| { + // Subtract one to move before `)` + call_expr.span.with_lo(call_expr.span.hi() - BytePos(1)) + }); + + // Include next comma + span = span.until(next); + } + suggestions.push((span, String::new())); suggestion_text = match suggestion_text { @@ -1158,11 +1187,11 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // 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; + continue; } let idx = ProvidedIdx::from_usize((prev + 1) as usize); - if let None = provided_idx - && let Some((_, arg_span)) = provided_arg_tys.get(idx) - { + if let Some((_, arg_span)) = provided_arg_tys.get(idx) { + prev += 1; // 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 @@ -1380,7 +1409,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { self.demand_eqtype(init.span, local_ty, init_ty); init_ty } else { - self.check_expr_coercable_to_type(init, local_ty, None) + self.check_expr_coercible_to_type(init, local_ty, None) } } @@ -1665,7 +1694,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { /// 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).def_id); - self.get_node_fn_decl(parent).map(|(fn_decl, ident, _)| (fn_decl, ident)) + 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 diff --git a/compiler/rustc_hir_typeck/src/fn_ctxt/mod.rs b/compiler/rustc_hir_typeck/src/fn_ctxt/mod.rs index 1dea3e6f9..c6fd0b610 100644 --- a/compiler/rustc_hir_typeck/src/fn_ctxt/mod.rs +++ b/compiler/rustc_hir_typeck/src/fn_ctxt/mod.rs @@ -211,13 +211,13 @@ impl<'a, 'tcx> AstConv<'tcx> for FnCtxt<'a, 'tcx> { fn get_type_parameter_bounds( &self, _: Span, - def_id: DefId, + def_id: LocalDefId, _: Ident, ) -> ty::GenericPredicates<'tcx> { let tcx = self.tcx; - let item_def_id = tcx.hir().ty_param_owner(def_id.expect_local()); + let item_def_id = tcx.hir().ty_param_owner(def_id); let generics = tcx.generics_of(item_def_id); - let index = generics.param_def_id_to_index[&def_id]; + let index = generics.param_def_id_to_index[&def_id.to_def_id()]; ty::GenericPredicates { parent: None, predicates: tcx.arena.alloc_from_iter( diff --git a/compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs b/compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs index c49621b7c..5fda4e191 100644 --- a/compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs +++ b/compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs @@ -16,7 +16,7 @@ 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, + self, suggest_constraining_type_params, Binder, IsSuggestable, ToPredicate, Ty, TypeVisitableExt, }; use rustc_session::errors::ExprParenthesesNeeded; @@ -64,8 +64,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let expr = expr.peel_drop_temps(); self.suggest_missing_semicolon(err, expr, expected, false); let mut pointing_at_return_type = false; - if let Some((fn_decl, can_suggest)) = self.get_fn_decl(blk_id) { - let fn_id = self.tcx.hir().get_return_block(blk_id).unwrap(); + if let Some((fn_id, fn_decl, can_suggest)) = self.get_fn_decl(blk_id) { pointing_at_return_type = self.suggest_missing_return_type( err, &fn_decl, @@ -166,8 +165,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { &self, ty: Ty<'tcx>, ) -> Option<(DefIdOrName, Ty<'tcx>, Vec>)> { - 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) + self.err_ctxt().extract_callable_info(self.body_id, self.param_env, ty) } pub fn suggest_two_fn_call( @@ -669,6 +667,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { /// This routine checks if the return type is left as default, the method is not part of an /// `impl` block and that it isn't the `main` method. If so, it suggests setting the return /// type. + #[instrument(level = "trace", skip(self, err))] pub(in super::super) fn suggest_missing_return_type( &self, err: &mut Diagnostic, @@ -705,28 +704,24 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { return true } } - 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 { + hir::FnRetTy::Return(hir_ty) => { + let span = hir_ty.span; + + if let hir::TyKind::OpaqueDef(item_id, ..) = hir_ty.kind + && let hir::Node::Item(hir::Item { + kind: hir::ItemKind::OpaqueTy(op_ty), + .. + }) = self.tcx.hir().get(item_id.hir_id()) + && let [hir::GenericBound::LangItemTrait( + hir::LangItem::Future, _, _, generic_args)] = op_ty.bounds + && let hir::GenericArgs { bindings: [ty_binding], .. } = generic_args + && let hir::TypeBindingKind::Equality { term: hir::Term::Ty(term) } = ty_binding.kind + { // 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); + debug!(?found); if found.is_suggestable(self.tcx, false) { - if term_ty.span.is_empty() { + if term.span.is_empty() { err.subdiagnostic(AddReturnTypeSuggestion::Add { span, found: found.to_string() }); return true; } else { @@ -737,11 +732,11 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // 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 ty = self.astconv().ast_ty_to_ty(ty); - debug!("suggest_missing_return_type: return type {:?}", ty); - debug!("suggest_missing_return_type: expected type {:?}", ty); - let bound_vars = self.tcx.late_bound_vars(fn_id); + debug!("return type {:?}", hir_ty); + let ty = self.astconv().ast_ty_to_ty(hir_ty); + debug!("return type {:?}", ty); + debug!("expected type {:?}", expected); + let bound_vars = self.tcx.late_bound_vars(hir_ty.hir_id.owner.into()); let ty = Binder::bind_with_vars(ty, bound_vars); let ty = self.normalize(span, ty); let ty = self.tcx.erase_late_bound_regions(ty); @@ -988,13 +983,18 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { ) .must_apply_modulo_regions() { - diag.span_suggestion_verbose( - expr.span.shrink_to_hi(), - "consider using clone here", - ".clone()", - Applicability::MachineApplicable, - ); - return true; + let suggestion = match self.maybe_get_struct_pattern_shorthand_field(expr) { + Some(ident) => format!(": {}.clone()", ident), + None => ".clone()".to_string() + }; + + diag.span_suggestion_verbose( + expr.span.shrink_to_hi(), + "consider using clone here", + suggestion, + Applicability::MachineApplicable, + ); + return true; } false } @@ -1015,11 +1015,11 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let mut suggest_copied_or_cloned = || { 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) + if let &ty::Ref(_, ty, hir::Mutability::Not) = expr_inner_ty.kind() + && 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) { + if self.type_is_copy_modulo_regions(self.param_env, ty) { diag.span_suggestion_verbose( expr.span.shrink_to_hi(), format!( @@ -1033,9 +1033,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { && rustc_trait_selection::traits::type_known_to_meet_bound_modulo_regions( self, self.param_env, - *ty, + ty, clone_did, - expr.span ) { diag.span_suggestion_verbose( @@ -1156,13 +1155,17 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { return false; } - diag.span_suggestion( + let suggestion = match self.maybe_get_struct_pattern_shorthand_field(expr) { + Some(ident) => format!(": {}.is_some()", ident), + None => ".is_some()".to_string(), + }; + + diag.span_suggestion_verbose( expr.span.shrink_to_hi(), "use `Option::is_some` to test if the `Option` has a value", - ".is_some()", + suggestion, Applicability::MachineApplicable, ); - true } -- cgit v1.2.3