diff options
| -rw-r--r-- | compiler/rustc_borrowck/src/diagnostics/mod.rs | 161 | ||||
| -rw-r--r-- | compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs | 109 | ||||
| -rw-r--r-- | compiler/rustc_trait_selection/src/infer.rs | 72 |
3 files changed, 161 insertions, 181 deletions
diff --git a/compiler/rustc_borrowck/src/diagnostics/mod.rs b/compiler/rustc_borrowck/src/diagnostics/mod.rs index 5f28500389d..1844e766a82 100644 --- a/compiler/rustc_borrowck/src/diagnostics/mod.rs +++ b/compiler/rustc_borrowck/src/diagnostics/mod.rs @@ -11,7 +11,7 @@ use rustc_hir::def::{CtorKind, Namespace}; use rustc_hir::CoroutineKind; use rustc_index::IndexSlice; use rustc_infer::infer::BoundRegionConversionTime; -use rustc_infer::traits::{FulfillmentErrorCode, SelectionError, TraitEngine, TraitEngineExt}; +use rustc_infer::traits::{FulfillmentErrorCode, SelectionError}; use rustc_middle::mir::tcx::PlaceTy; use rustc_middle::mir::{ AggregateKind, CallSource, ConstOperand, FakeReadCause, Local, LocalInfo, LocalKind, Location, @@ -25,11 +25,9 @@ use rustc_mir_dataflow::move_paths::{InitLocation, LookupResult}; use rustc_span::def_id::LocalDefId; use rustc_span::{symbol::sym, Span, Symbol, DUMMY_SP}; use rustc_target::abi::{FieldIdx, VariantIdx}; -use rustc_trait_selection::solve::FulfillmentCtxt; +use rustc_trait_selection::infer::InferCtxtExt; use rustc_trait_selection::traits::error_reporting::suggestions::TypeErrCtxtExt as _; -use rustc_trait_selection::traits::{ - type_known_to_meet_bound_modulo_regions, Obligation, ObligationCause, -}; +use rustc_trait_selection::traits::type_known_to_meet_bound_modulo_regions; use super::borrow_set::BorrowData; use super::MirBorrowckCtxt; @@ -1175,113 +1173,56 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { } else { vec![(move_span.shrink_to_hi(), ".clone()".to_string())] }; - self.infcx.probe(|_snapshot| { - if let ty::Adt(def, args) = ty.kind() - && !has_sugg - && let Some((def_id, _imp)) = tcx - .all_impls(clone_trait) - .filter_map(|def_id| { - tcx.impl_trait_ref(def_id).map(|r| (def_id, r)) - }) - .map(|(def_id, imp)| (def_id, imp.skip_binder())) - .filter(|(_, imp)| match imp.self_ty().peel_refs().kind() { - ty::Adt(i_def, _) if i_def.did() == def.did() => true, - _ => false, - }) - .next() - { - let mut fulfill_cx = FulfillmentCtxt::new(self.infcx); - // We get all obligations from the impl to talk about specific - // trait bounds. - let obligations = tcx - .predicates_of(def_id) - .instantiate(tcx, args) - .into_iter() - .map(|(clause, span)| { - Obligation::new( - tcx, - ObligationCause::misc( - span, - self.body.source.def_id().expect_local(), - ), - self.param_env, - clause, - ) - }) - .collect::<Vec<_>>(); - fulfill_cx - .register_predicate_obligations(self.infcx, obligations); - // We also register the parent obligation for the type at hand - // implementing `Clone`, to account for bounds that also need - // to be evaluated, like ensuring that `Self: Clone`. - let trait_ref = ty::TraitRef::new(tcx, clone_trait, [ty]); - let obligation = Obligation::new( - tcx, - ObligationCause::dummy(), - self.param_env, - trait_ref, - ); - fulfill_cx - .register_predicate_obligation(self.infcx, obligation); - let errors = fulfill_cx.select_all_or_error(self.infcx); - // We remove the last predicate failure, which corresponds to - // the top-level obligation, because most of the type we only - // care about the other ones, *except* when it is the only one. - // This seems to only be relevant for arbitrary self-types. - // Look at `tests/ui/moves/move-fn-self-receiver.rs`. - let errors = match &errors[..] { - errors @ [] | errors @ [_] | [errors @ .., _] => errors, - }; - let msg = match &errors[..] { - [] => "you can `clone` the value and consume it, but this \ - might not be your desired behavior" - .to_string(), - [error] => { - format!( - "you could `clone` the value and consume it, if \ - the `{}` trait bound could be satisfied", - error.obligation.predicate, - ) - } - [errors @ .., last] => { - format!( - "you could `clone` the value and consume it, if \ - the following trait bounds could be satisfied: {} \ - and `{}`", - errors - .iter() - .map(|e| format!( - "`{}`", - e.obligation.predicate - )) - .collect::<Vec<_>>() - .join(", "), - last.obligation.predicate, - ) - } - }; - err.multipart_suggestion_verbose( - msg, - sugg.clone(), - Applicability::MaybeIncorrect, - ); - for error in errors { - if let FulfillmentErrorCode::CodeSelectionError( - SelectionError::Unimplemented, - ) = error.code - && let ty::PredicateKind::Clause(ty::ClauseKind::Trait( - pred, - )) = error.obligation.predicate.kind().skip_binder() - { - self.infcx.err_ctxt().suggest_derive( - &error.obligation, - err, - error.obligation.predicate.kind().rebind(pred), - ); - } + if let Some(errors) = + self.infcx.could_impl_trait(clone_trait, ty, self.param_env) + && !has_sugg + { + let msg = match &errors[..] { + [] => "you can `clone` the value and consume it, but this \ + might not be your desired behavior" + .to_string(), + [error] => { + format!( + "you could `clone` the value and consume it, if \ + the `{}` trait bound could be satisfied", + error.obligation.predicate, + ) + } + [errors @ .., last] => { + format!( + "you could `clone` the value and consume it, if \ + the following trait bounds could be satisfied: {} \ + and `{}`", + errors + .iter() + .map(|e| format!("`{}`", e.obligation.predicate)) + .collect::<Vec<_>>() + .join(", "), + last.obligation.predicate, + ) + } + }; + err.multipart_suggestion_verbose( + msg, + sugg.clone(), + Applicability::MaybeIncorrect, + ); + for error in errors { + if let FulfillmentErrorCode::CodeSelectionError( + SelectionError::Unimplemented, + ) = error.code + && let ty::PredicateKind::Clause(ty::ClauseKind::Trait( + pred, + )) = error.obligation.predicate.kind().skip_binder() + { + self.infcx.err_ctxt().suggest_derive( + &error.obligation, + err, + error.obligation.predicate.kind().rebind(pred), + ); } } - }); + } } } } diff --git a/compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs b/compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs index b61b029813c..2c9942caab2 100644 --- a/compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs +++ b/compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs @@ -21,7 +21,7 @@ use rustc_hir::{ StmtKind, TyKind, WherePredicate, }; use rustc_hir_analysis::astconv::AstConv; -use rustc_infer::traits::{self, StatementAsExpression, TraitEngineExt}; +use rustc_infer::traits::{self, StatementAsExpression}; use rustc_middle::lint::in_external_macro; use rustc_middle::middle::stability::EvalResult; use rustc_middle::ty::print::with_no_trimmed_paths; @@ -34,7 +34,6 @@ use rustc_span::source_map::Spanned; use rustc_span::symbol::{sym, Ident}; use rustc_span::{Span, Symbol}; use rustc_trait_selection::infer::InferCtxtExt; -use rustc_trait_selection::solve::FulfillmentCtxt; use rustc_trait_selection::traits::error_reporting::suggestions::TypeErrCtxtExt; use rustc_trait_selection::traits::error_reporting::DefIdOrName; use rustc_trait_selection::traits::query::evaluate_obligation::InferCtxtExt as _; @@ -1620,78 +1619,46 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { None, ); } else { - self.infcx.probe(|_snapshot| { - if let ty::Adt(def, args) = expected_ty.kind() - && let Some((def_id, _imp)) = self - .tcx - .all_impls(clone_trait_did) - .filter_map(|def_id| { - self.tcx.impl_trait_ref(def_id).map(|r| (def_id, r)) - }) - .map(|(def_id, imp)| (def_id, imp.skip_binder())) - .filter(|(_, imp)| match imp.self_ty().peel_refs().kind() { - ty::Adt(i_def, _) if i_def.did() == def.did() => true, - _ => false, - }) - .next() - { - let mut fulfill_cx = FulfillmentCtxt::new(&self.infcx); - // We get all obligations from the impl to talk about specific - // trait bounds. - let obligations = self - .tcx - .predicates_of(def_id) - .instantiate(self.tcx, args) - .into_iter() - .map(|(clause, span)| { - traits::Obligation::new( - self.tcx, - traits::ObligationCause::misc(span, self.body_id), - self.param_env, - clause, - ) - }) - .collect::<Vec<_>>(); - fulfill_cx.register_predicate_obligations(&self.infcx, obligations); - let errors = fulfill_cx.select_all_or_error(&self.infcx); - match &errors[..] { - [] => {} - [error] => { - diag.help(format!( - "`Clone` is not implemented because the trait bound `{}` is \ - not satisfied", - error.obligation.predicate, - )); - } - [errors @ .., last] => { - diag.help(format!( - "`Clone` is not implemented because the following trait bounds \ - could not be satisfied: {} and `{}`", - errors - .iter() - .map(|e| format!("`{}`", e.obligation.predicate)) - .collect::<Vec<_>>() - .join(", "), - last.obligation.predicate, - )); - } + if let Some(errors) = + self.could_impl_trait(clone_trait_did, expected_ty, self.param_env) + { + match &errors[..] { + [] => {} + [error] => { + diag.help(format!( + "`Clone` is not implemented because the trait bound `{}` is \ + not satisfied", + error.obligation.predicate, + )); } - for error in errors { - if let traits::FulfillmentErrorCode::CodeSelectionError( - traits::SelectionError::Unimplemented, - ) = error.code - && let ty::PredicateKind::Clause(ty::ClauseKind::Trait(pred)) = - error.obligation.predicate.kind().skip_binder() - { - self.infcx.err_ctxt().suggest_derive( - &error.obligation, - diag, - error.obligation.predicate.kind().rebind(pred), - ); - } + [errors @ .., last] => { + diag.help(format!( + "`Clone` is not implemented because the following trait bounds \ + could not be satisfied: {} and `{}`", + errors + .iter() + .map(|e| format!("`{}`", e.obligation.predicate)) + .collect::<Vec<_>>() + .join(", "), + last.obligation.predicate, + )); } } - }); + for error in errors { + if let traits::FulfillmentErrorCode::CodeSelectionError( + traits::SelectionError::Unimplemented, + ) = error.code + && let ty::PredicateKind::Clause(ty::ClauseKind::Trait(pred)) = + error.obligation.predicate.kind().skip_binder() + { + self.infcx.err_ctxt().suggest_derive( + &error.obligation, + diag, + error.obligation.predicate.kind().rebind(pred), + ); + } + } + } self.suggest_derive(diag, &[(trait_ref.to_predicate(self.tcx), None, None)]); } } diff --git a/compiler/rustc_trait_selection/src/infer.rs b/compiler/rustc_trait_selection/src/infer.rs index 38153cccfdd..992bfd97e0e 100644 --- a/compiler/rustc_trait_selection/src/infer.rs +++ b/compiler/rustc_trait_selection/src/infer.rs @@ -1,8 +1,10 @@ +use crate::solve::FulfillmentCtxt; use crate::traits::query::evaluate_obligation::InferCtxtExt as _; use crate::traits::{self, DefiningAnchor, ObligationCtxt}; use rustc_hir::def_id::DefId; use rustc_hir::lang_items::LangItem; +use rustc_infer::traits::{TraitEngine, TraitEngineExt}; use rustc_middle::arena::ArenaAllocatable; use rustc_middle::infer::canonical::{Canonical, CanonicalQueryResponse, QueryResponse}; use rustc_middle::traits::query::NoSolution; @@ -35,6 +37,13 @@ pub trait InferCtxtExt<'tcx> { params: impl IntoIterator<Item: Into<GenericArg<'tcx>>>, param_env: ty::ParamEnv<'tcx>, ) -> traits::EvaluationResult; + + fn could_impl_trait( + &self, + trait_def_id: DefId, + ty: Ty<'tcx>, + param_env: ty::ParamEnv<'tcx>, + ) -> Option<Vec<traits::FulfillmentError<'tcx>>>; } impl<'tcx> InferCtxtExt<'tcx> for InferCtxt<'tcx> { @@ -76,6 +85,69 @@ impl<'tcx> InferCtxtExt<'tcx> for InferCtxt<'tcx> { }; self.evaluate_obligation(&obligation).unwrap_or(traits::EvaluationResult::EvaluatedToErr) } + + fn could_impl_trait( + &self, + trait_def_id: DefId, + ty: Ty<'tcx>, + param_env: ty::ParamEnv<'tcx>, + ) -> Option<Vec<traits::FulfillmentError<'tcx>>> { + self.probe(|_snapshot| { + if let ty::Adt(def, args) = ty.kind() + && let Some((impl_def_id, _)) = self + .tcx + .all_impls(trait_def_id) + .filter_map(|impl_def_id| { + self.tcx.impl_trait_ref(impl_def_id).map(|r| (impl_def_id, r)) + }) + .map(|(impl_def_id, imp)| (impl_def_id, imp.skip_binder())) + .filter(|(_, imp)| match imp.self_ty().peel_refs().kind() { + ty::Adt(i_def, _) if i_def.did() == def.did() => true, + _ => false, + }) + .next() + { + let mut fulfill_cx = FulfillmentCtxt::new(self); + // We get all obligations from the impl to talk about specific + // trait bounds. + let obligations = self + .tcx + .predicates_of(impl_def_id) + .instantiate(self.tcx, args) + .into_iter() + .map(|(clause, span)| { + traits::Obligation::new( + self.tcx, + traits::ObligationCause::dummy_with_span(span), + param_env, + clause, + ) + }) + .collect::<Vec<_>>(); + fulfill_cx.register_predicate_obligations(self, obligations); + let trait_ref = ty::TraitRef::new(self.tcx, trait_def_id, [ty]); + let obligation = traits::Obligation::new( + self.tcx, + traits::ObligationCause::dummy(), + param_env, + trait_ref, + ); + fulfill_cx.register_predicate_obligation(self, obligation); + let mut errors = fulfill_cx.select_all_or_error(self); + // We remove the last predicate failure, which corresponds to + // the top-level obligation, because most of the type we only + // care about the other ones, *except* when it is the only one. + // This seems to only be relevant for arbitrary self-types. + // Look at `tests/ui/moves/move-fn-self-receiver.rs`. + if errors.len() > 1 { + errors.truncate(errors.len() - 1); + } + Some(errors) + } else { + None + } + }) + } } pub trait InferCtxtBuilderExt<'tcx> { |
