diff options
Diffstat (limited to 'compiler')
| -rw-r--r-- | compiler/rustc_middle/src/ty/print/pretty.rs | 21 | ||||
| -rw-r--r-- | compiler/rustc_trait_selection/src/traits/error_reporting/mod.rs | 2 | ||||
| -rw-r--r-- | compiler/rustc_typeck/src/check/method/mod.rs | 66 | ||||
| -rw-r--r-- | compiler/rustc_typeck/src/check/method/suggest.rs | 121 | ||||
| -rw-r--r-- | compiler/rustc_typeck/src/check/op.rs | 68 | 
5 files changed, 209 insertions, 69 deletions
diff --git a/compiler/rustc_middle/src/ty/print/pretty.rs b/compiler/rustc_middle/src/ty/print/pretty.rs index f8a476266d6..c9f0b508f59 100644 --- a/compiler/rustc_middle/src/ty/print/pretty.rs +++ b/compiler/rustc_middle/src/ty/print/pretty.rs @@ -2171,10 +2171,26 @@ impl fmt::Debug for TraitRefPrintOnlyTraitPath<'tcx> { } } +/// Wrapper type for `ty::TraitRef` which opts-in to pretty printing only +/// the trait name. That is, it will print `Trait` instead of +/// `<T as Trait<U>>`. +#[derive(Copy, Clone, TypeFoldable, Lift)] +pub struct TraitRefPrintOnlyTraitName<'tcx>(ty::TraitRef<'tcx>); + +impl fmt::Debug for TraitRefPrintOnlyTraitName<'tcx> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + fmt::Display::fmt(self, f) + } +} + impl ty::TraitRef<'tcx> { pub fn print_only_trait_path(self) -> TraitRefPrintOnlyTraitPath<'tcx> { TraitRefPrintOnlyTraitPath(self) } + + pub fn print_only_trait_name(self) -> TraitRefPrintOnlyTraitName<'tcx> { + TraitRefPrintOnlyTraitName(self) + } } impl ty::Binder<'tcx, ty::TraitRef<'tcx>> { @@ -2193,6 +2209,7 @@ forward_display_to_print! { ty::Binder<'tcx, ty::ExistentialPredicate<'tcx>>, ty::Binder<'tcx, ty::TraitRef<'tcx>>, ty::Binder<'tcx, TraitRefPrintOnlyTraitPath<'tcx>>, + ty::Binder<'tcx, TraitRefPrintOnlyTraitName<'tcx>>, ty::Binder<'tcx, ty::FnSig<'tcx>>, ty::Binder<'tcx, ty::TraitPredicate<'tcx>>, ty::Binder<'tcx, ty::SubtypePredicate<'tcx>>, @@ -2255,6 +2272,10 @@ define_print_and_forward_display! { p!(print_def_path(self.0.def_id, self.0.substs)); } + TraitRefPrintOnlyTraitName<'tcx> { + p!(print_def_path(self.0.def_id, &[])); + } + ty::ParamTy { p!(write("{}", self.name)) } diff --git a/compiler/rustc_trait_selection/src/traits/error_reporting/mod.rs b/compiler/rustc_trait_selection/src/traits/error_reporting/mod.rs index 6447e4cbf2b..88e8df81488 100644 --- a/compiler/rustc_trait_selection/src/traits/error_reporting/mod.rs +++ b/compiler/rustc_trait_selection/src/traits/error_reporting/mod.rs @@ -521,7 +521,7 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> { "this error might have been caused by changes to \ Rust's type-inference algorithm (see issue #48950 \ <https://github.com/rust-lang/rust/issues/48950> \ - for more information).", + for more information)", ); err.help("did you intend to use the type `()` here instead?"); } diff --git a/compiler/rustc_typeck/src/check/method/mod.rs b/compiler/rustc_typeck/src/check/method/mod.rs index 65b3ceb8698..f0f2470e80a 100644 --- a/compiler/rustc_typeck/src/check/method/mod.rs +++ b/compiler/rustc_typeck/src/check/method/mod.rs @@ -290,29 +290,14 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { ) } - /// `lookup_method_in_trait` is used for overloaded operators. - /// It does a very narrow slice of what the normal probe/confirm path does. - /// In particular, it doesn't really do any probing: it simply constructs - /// an obligation for a particular trait with the given self type and checks - /// whether that trait is implemented. - // - // FIXME(#18741): it seems likely that we can consolidate some of this - // code with the other method-lookup code. In particular, the second half - // of this method is basically the same as confirmation. - #[instrument(level = "debug", skip(self, span, opt_input_types))] - pub fn lookup_method_in_trait( + pub(super) fn obligation_for_method( &self, span: Span, - m_name: Ident, trait_def_id: DefId, self_ty: Ty<'tcx>, opt_input_types: Option<&[Ty<'tcx>]>, - ) -> Option<InferOk<'tcx, MethodCallee<'tcx>>> { - debug!( - "lookup_in_trait_adjusted(self_ty={:?}, m_name={}, trait_def_id={:?}, opt_input_types={:?})", - self_ty, m_name, trait_def_id, opt_input_types - ); - + ) -> (traits::Obligation<'tcx, ty::Predicate<'tcx>>, &'tcx ty::List<ty::subst::GenericArg<'tcx>>) + { // Construct a trait-reference `self_ty : Trait<input_tys>` let substs = InternalSubsts::for_item(self.tcx, trait_def_id, |param, _| { match param.kind { @@ -332,17 +317,48 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // Construct an obligation let poly_trait_ref = ty::Binder::dummy(trait_ref); - let obligation = traits::Obligation::misc( - span, - self.body_id, - self.param_env, - poly_trait_ref.without_const().to_predicate(self.tcx), + ( + traits::Obligation::misc( + span, + self.body_id, + self.param_env, + poly_trait_ref.without_const().to_predicate(self.tcx), + ), + substs, + ) + } + + /// `lookup_method_in_trait` is used for overloaded operators. + /// It does a very narrow slice of what the normal probe/confirm path does. + /// In particular, it doesn't really do any probing: it simply constructs + /// an obligation for a particular trait with the given self type and checks + /// whether that trait is implemented. + // + // FIXME(#18741): it seems likely that we can consolidate some of this + // code with the other method-lookup code. In particular, the second half + // of this method is basically the same as confirmation. + #[instrument(level = "debug", skip(self, span, opt_input_types))] + pub(super) fn lookup_method_in_trait( + &self, + span: Span, + m_name: Ident, + trait_def_id: DefId, + self_ty: Ty<'tcx>, + opt_input_types: Option<&[Ty<'tcx>]>, + ) -> Option<InferOk<'tcx, MethodCallee<'tcx>>> { + debug!( + "lookup_in_trait_adjusted(self_ty={:?}, m_name={}, trait_def_id={:?}, opt_input_types={:?})", + self_ty, m_name, trait_def_id, opt_input_types ); + let (obligation, substs) = + self.obligation_for_method(span, trait_def_id, self_ty, opt_input_types); + // Now we want to know if this can be matched if !self.predicate_may_hold(&obligation) { debug!("--> Cannot match obligation"); - return None; // Cannot be matched, no such method resolution is possible. + // Cannot be matched, no such method resolution is possible. + return None; } // Trait must have a method named `m_name` and it should not have @@ -416,7 +432,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { ty::Binder::dummy(ty::PredicateKind::WellFormed(method_ty.into())).to_predicate(tcx), )); - let callee = MethodCallee { def_id, substs: trait_ref.substs, sig: fn_sig }; + let callee = MethodCallee { def_id, substs, sig: fn_sig }; debug!("callee = {:?}", callee); diff --git a/compiler/rustc_typeck/src/check/method/suggest.rs b/compiler/rustc_typeck/src/check/method/suggest.rs index d849e1d5d28..8c7ec219464 100644 --- a/compiler/rustc_typeck/src/check/method/suggest.rs +++ b/compiler/rustc_typeck/src/check/method/suggest.rs @@ -15,9 +15,9 @@ use rustc_middle::ty::print::with_crate_prefix; use rustc_middle::ty::{self, ToPredicate, Ty, TyCtxt, TypeFoldable, WithConstness}; use rustc_span::lev_distance; use rustc_span::symbol::{kw, sym, Ident}; -use rustc_span::{source_map, FileName, Span}; +use rustc_span::{source_map, FileName, MultiSpan, Span}; use rustc_trait_selection::traits::query::evaluate_obligation::InferCtxtExt; -use rustc_trait_selection::traits::Obligation; +use rustc_trait_selection::traits::{FulfillmentError, Obligation}; use std::cmp::Ordering; use std::iter; @@ -969,6 +969,79 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { None } + crate fn note_unmet_impls_on_type( + &self, + err: &mut rustc_errors::DiagnosticBuilder<'_>, + errors: Vec<FulfillmentError<'tcx>>, + ) { + let all_local_types_needing_impls = + errors.iter().all(|e| match e.obligation.predicate.kind().skip_binder() { + ty::PredicateKind::Trait(pred) => match pred.self_ty().kind() { + ty::Adt(def, _) => def.did.is_local(), + _ => false, + }, + _ => false, + }); + let mut preds: Vec<_> = errors + .iter() + .filter_map(|e| match e.obligation.predicate.kind().skip_binder() { + ty::PredicateKind::Trait(pred) => Some(pred), + _ => None, + }) + .collect(); + preds.sort_by_key(|pred| (pred.def_id(), pred.self_ty())); + let def_ids = preds + .iter() + .filter_map(|pred| match pred.self_ty().kind() { + ty::Adt(def, _) => Some(def.did), + _ => None, + }) + .collect::<FxHashSet<_>>(); + let sm = self.tcx.sess.source_map(); + let mut spans: MultiSpan = def_ids + .iter() + .filter_map(|def_id| { + let span = self.tcx.def_span(*def_id); + if span.is_dummy() { None } else { Some(sm.guess_head_span(span)) } + }) + .collect::<Vec<_>>() + .into(); + + for pred in &preds { + match pred.self_ty().kind() { + ty::Adt(def, _) => { + spans.push_span_label( + sm.guess_head_span(self.tcx.def_span(def.did)), + format!("must implement `{}`", pred.trait_ref.print_only_trait_path()), + ); + } + _ => {} + } + } + + if all_local_types_needing_impls && spans.primary_span().is_some() { + let msg = if preds.len() == 1 { + format!( + "an implementation of `{}` might be missing for `{}`", + preds[0].trait_ref.print_only_trait_path(), + preds[0].self_ty() + ) + } else { + format!( + "the following type{} would have to `impl` {} required trait{} for this \ + operation to be valid", + pluralize!(def_ids.len()), + if def_ids.len() == 1 { "its" } else { "their" }, + pluralize!(preds.len()), + ) + }; + err.span_note(spans, &msg); + } + + let preds: Vec<_> = errors.iter().map(|e| (e.obligation.predicate, None)).collect(); + self.suggest_derive(err, &preds); + } + fn suggest_derive( &self, err: &mut DiagnosticBuilder<'_>, @@ -1010,7 +1083,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { return Some(( format!("{}", trait_ref.self_ty()), self.tcx.def_span(adt_def.did), - format!("{}", trait_ref.print_only_trait_path()), + format!("{}", trait_ref.print_only_trait_name()), )); } None @@ -1033,6 +1106,48 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { acc }, ); + let mut traits: Vec<_> = unsatisfied_predicates + .iter() + .filter_map(|(pred, _)| { + let trait_pred = + if let ty::PredicateKind::Trait(trait_pred) = pred.kind().skip_binder() { + trait_pred + } else { + return None; + }; + if let ty::Adt(adt_def, _) = trait_pred.trait_ref.self_ty().kind() { + if !adt_def.did.is_local() { + return None; + } + } else { + return None; + }; + + let did = trait_pred.def_id(); + let diagnostic_items = self.tcx.diagnostic_items(did.krate); + + if !derivables + .iter() + .any(|trait_derivable| diagnostic_items.get(trait_derivable) == Some(&did)) + { + Some(self.tcx.def_span(did)) + } else { + None + } + }) + .collect(); + traits.sort(); + traits.dedup(); + + let len = traits.len(); + if len > 0 { + let span: MultiSpan = traits.into(); + err.span_note( + span, + &format!("the following trait{} must be implemented", pluralize!(len),), + ); + } + for (self_name, self_span, traits) in &derives_grouped { err.span_suggestion_verbose( self_span.shrink_to_lo(), diff --git a/compiler/rustc_typeck/src/check/op.rs b/compiler/rustc_typeck/src/check/op.rs index d0a32420df3..79e004a47db 100644 --- a/compiler/rustc_typeck/src/check/op.rs +++ b/compiler/rustc_typeck/src/check/op.rs @@ -18,6 +18,7 @@ use rustc_span::source_map::Spanned; use rustc_span::symbol::{sym, Ident}; use rustc_span::Span; use rustc_trait_selection::infer::InferCtxtExt; +use rustc_trait_selection::traits::{FulfillmentError, TraitEngine, TraitEngineExt}; use std::ops::ControlFlow; @@ -257,12 +258,10 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { method.sig.output() } // error types are considered "builtin" - Err(()) if lhs_ty.references_error() || rhs_ty.references_error() => { - self.tcx.ty_error() - } - Err(()) => { + Err(_) if lhs_ty.references_error() || rhs_ty.references_error() => self.tcx.ty_error(), + Err(errors) => { let source_map = self.tcx.sess.source_map(); - let (mut err, missing_trait, use_output, involves_fn) = match is_assign { + let (mut err, missing_trait, use_output) = match is_assign { IsAssign::Yes => { let mut err = struct_span_err!( self.tcx.sess, @@ -289,7 +288,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { hir::BinOpKind::Shr => Some("std::ops::ShrAssign"), _ => None, }; - (err, missing_trait, false, false) + self.note_unmet_impls_on_type(&mut err, errors); + (err, missing_trait, false) } IsAssign::No => { let (message, missing_trait, use_output) = match op.node { @@ -376,9 +376,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { }; let mut err = struct_span_err!(self.tcx.sess, op.span, E0369, "{}", message.as_str()); - let mut involves_fn = false; if !lhs_expr.span.eq(&rhs_expr.span) { - involves_fn |= self.add_type_neq_err_label( + self.add_type_neq_err_label( &mut err, lhs_expr.span, lhs_ty, @@ -386,7 +385,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { op, is_assign, ); - involves_fn |= self.add_type_neq_err_label( + self.add_type_neq_err_label( &mut err, rhs_expr.span, rhs_ty, @@ -395,10 +394,10 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { is_assign, ); } - (err, missing_trait, use_output, involves_fn) + self.note_unmet_impls_on_type(&mut err, errors); + (err, missing_trait, use_output) } }; - let mut suggested_deref = false; if let Ref(_, rty, _) = lhs_ty.kind() { if { self.infcx.type_is_copy_modulo_regions(self.param_env, rty, lhs_expr.span) @@ -423,7 +422,6 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { "*".to_string(), rustc_errors::Applicability::MachineApplicable, ); - suggested_deref = true; } } } @@ -474,8 +472,6 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } else { bug!("type param visitor stored a non type param: {:?}", ty.kind()); } - } else if !suggested_deref && !involves_fn { - suggest_impl_missing(&mut err, lhs_ty, missing_trait); } } err.emit(); @@ -665,7 +661,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { self.write_method_call(ex.hir_id, method); method.sig.output() } - Err(()) => { + Err(errors) => { let actual = self.resolve_vars_if_possible(operand_ty); if !actual.references_error() { let mut err = struct_span_err!( @@ -720,12 +716,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { Str | Never | Char | Tuple(_) | Array(_, _) => {} Ref(_, lty, _) if *lty.kind() == Str => {} _ => { - let missing_trait = match op { - hir::UnOp::Neg => "std::ops::Neg", - hir::UnOp::Not => "std::ops::Not", - hir::UnOp::Deref => "std::ops::UnDerf", - }; - suggest_impl_missing(&mut err, operand_ty, missing_trait); + self.note_unmet_impls_on_type(&mut err, errors); } } } @@ -741,7 +732,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { lhs_ty: Ty<'tcx>, other_tys: &[Ty<'tcx>], op: Op, - ) -> Result<MethodCallee<'tcx>, ()> { + ) -> Result<MethodCallee<'tcx>, Vec<FulfillmentError<'tcx>>> { let lang = self.tcx.lang_items(); let span = match op { @@ -820,22 +811,31 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { Op::Unary(..) => 0, }, ) { - return Err(()); + return Err(vec![]); } + let opname = Ident::with_dummy_span(opname); let method = trait_did.and_then(|trait_did| { - let opname = Ident::with_dummy_span(opname); self.lookup_method_in_trait(span, opname, trait_did, lhs_ty, Some(other_tys)) }); - match method { - Some(ok) => { + match (method, trait_did) { + (Some(ok), _) => { let method = self.register_infer_ok_obligations(ok); self.select_obligations_where_possible(false, |_| {}); - Ok(method) } - None => Err(()), + (None, None) => Err(vec![]), + (None, Some(trait_did)) => { + let (obligation, _) = + self.obligation_for_method(span, trait_did, lhs_ty, Some(other_tys)); + let mut fulfill = <dyn TraitEngine<'_>>::new(self.tcx); + fulfill.register_predicate_obligation(self, obligation); + Err(match fulfill.select_where_possible(&self.infcx) { + Err(errors) => errors, + _ => vec![], + }) + } } } } @@ -962,18 +962,6 @@ fn is_builtin_binop<'tcx>(lhs: Ty<'tcx>, rhs: Ty<'tcx>, op: hir::BinOp) -> bool } } -/// If applicable, note that an implementation of `trait` for `ty` may fix the error. -fn suggest_impl_missing(err: &mut DiagnosticBuilder<'_>, ty: Ty<'_>, missing_trait: &str) { - if let Adt(def, _) = ty.peel_refs().kind() { - if def.did.is_local() { - err.note(&format!( - "an implementation of `{}` might be missing for `{}`", - missing_trait, ty - )); - } - } -} - fn suggest_constraining_param( tcx: TyCtxt<'_>, body_id: hir::HirId,  | 
