Deduplicate some logic

This commit is contained in:
Esteban Küber 2023-11-21 21:24:51 +00:00
parent a754fcaa43
commit 210a672005
3 changed files with 161 additions and 181 deletions

View file

@ -11,7 +11,7 @@ use rustc_hir::def::{CtorKind, Namespace};
use rustc_hir::CoroutineKind; use rustc_hir::CoroutineKind;
use rustc_index::IndexSlice; use rustc_index::IndexSlice;
use rustc_infer::infer::BoundRegionConversionTime; 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::tcx::PlaceTy;
use rustc_middle::mir::{ use rustc_middle::mir::{
AggregateKind, CallSource, ConstOperand, FakeReadCause, Local, LocalInfo, LocalKind, Location, 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::def_id::LocalDefId;
use rustc_span::{symbol::sym, Span, Symbol, DUMMY_SP}; use rustc_span::{symbol::sym, Span, Symbol, DUMMY_SP};
use rustc_target::abi::{FieldIdx, VariantIdx}; 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::error_reporting::suggestions::TypeErrCtxtExt as _;
use rustc_trait_selection::traits::{ use rustc_trait_selection::traits::type_known_to_meet_bound_modulo_regions;
type_known_to_meet_bound_modulo_regions, Obligation, ObligationCause,
};
use super::borrow_set::BorrowData; use super::borrow_set::BorrowData;
use super::MirBorrowckCtxt; use super::MirBorrowckCtxt;
@ -1175,113 +1173,56 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
} else { } else {
vec![(move_span.shrink_to_hi(), ".clone()".to_string())] vec![(move_span.shrink_to_hi(), ".clone()".to_string())]
}; };
self.infcx.probe(|_snapshot| { if let Some(errors) =
if let ty::Adt(def, args) = ty.kind() self.infcx.could_impl_trait(clone_trait, ty, self.param_env)
&& !has_sugg && !has_sugg
&& let Some((def_id, _imp)) = tcx {
.all_impls(clone_trait) let msg = match &errors[..] {
.filter_map(|def_id| { [] => "you can `clone` the value and consume it, but this \
tcx.impl_trait_ref(def_id).map(|r| (def_id, r)) might not be your desired behavior"
}) .to_string(),
.map(|(def_id, imp)| (def_id, imp.skip_binder())) [error] => {
.filter(|(_, imp)| match imp.self_ty().peel_refs().kind() { format!(
ty::Adt(i_def, _) if i_def.did() == def.did() => true, "you could `clone` the value and consume it, if \
_ => false, the `{}` trait bound could be satisfied",
}) error.obligation.predicate,
.next() )
{ }
let mut fulfill_cx = FulfillmentCtxt::new(self.infcx); [errors @ .., last] => {
// We get all obligations from the impl to talk about specific format!(
// trait bounds. "you could `clone` the value and consume it, if \
let obligations = tcx the following trait bounds could be satisfied: {} \
.predicates_of(def_id) and `{}`",
.instantiate(tcx, args) errors
.into_iter() .iter()
.map(|(clause, span)| { .map(|e| format!("`{}`", e.obligation.predicate))
Obligation::new( .collect::<Vec<_>>()
tcx, .join(", "),
ObligationCause::misc( last.obligation.predicate,
span, )
self.body.source.def_id().expect_local(), }
), };
self.param_env, err.multipart_suggestion_verbose(
clause, msg,
) sugg.clone(),
}) Applicability::MaybeIncorrect,
.collect::<Vec<_>>(); );
fulfill_cx for error in errors {
.register_predicate_obligations(self.infcx, obligations); if let FulfillmentErrorCode::CodeSelectionError(
// We also register the parent obligation for the type at hand SelectionError::Unimplemented,
// implementing `Clone`, to account for bounds that also need ) = error.code
// to be evaluated, like ensuring that `Self: Clone`. && let ty::PredicateKind::Clause(ty::ClauseKind::Trait(
let trait_ref = ty::TraitRef::new(tcx, clone_trait, [ty]); pred,
let obligation = Obligation::new( )) = error.obligation.predicate.kind().skip_binder()
tcx, {
ObligationCause::dummy(), self.infcx.err_ctxt().suggest_derive(
self.param_env, &error.obligation,
trait_ref, err,
); error.obligation.predicate.kind().rebind(pred),
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),
);
}
} }
} }
}); }
} }
} }
} }

View file

@ -21,7 +21,7 @@ use rustc_hir::{
StmtKind, TyKind, WherePredicate, StmtKind, TyKind, WherePredicate,
}; };
use rustc_hir_analysis::astconv::AstConv; 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::lint::in_external_macro;
use rustc_middle::middle::stability::EvalResult; use rustc_middle::middle::stability::EvalResult;
use rustc_middle::ty::print::with_no_trimmed_paths; 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::symbol::{sym, Ident};
use rustc_span::{Span, Symbol}; use rustc_span::{Span, Symbol};
use rustc_trait_selection::infer::InferCtxtExt; 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::suggestions::TypeErrCtxtExt;
use rustc_trait_selection::traits::error_reporting::DefIdOrName; use rustc_trait_selection::traits::error_reporting::DefIdOrName;
use rustc_trait_selection::traits::query::evaluate_obligation::InferCtxtExt as _; use rustc_trait_selection::traits::query::evaluate_obligation::InferCtxtExt as _;
@ -1620,78 +1619,46 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
None, None,
); );
} else { } else {
self.infcx.probe(|_snapshot| { if let Some(errors) =
if let ty::Adt(def, args) = expected_ty.kind() self.could_impl_trait(clone_trait_did, expected_ty, self.param_env)
&& let Some((def_id, _imp)) = self {
.tcx match &errors[..] {
.all_impls(clone_trait_did) [] => {}
.filter_map(|def_id| { [error] => {
self.tcx.impl_trait_ref(def_id).map(|r| (def_id, r)) diag.help(format!(
}) "`Clone` is not implemented because the trait bound `{}` is \
.map(|(def_id, imp)| (def_id, imp.skip_binder())) not satisfied",
.filter(|(_, imp)| match imp.self_ty().peel_refs().kind() { error.obligation.predicate,
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,
));
}
} }
for error in errors { [errors @ .., last] => {
if let traits::FulfillmentErrorCode::CodeSelectionError( diag.help(format!(
traits::SelectionError::Unimplemented, "`Clone` is not implemented because the following trait bounds \
) = error.code could not be satisfied: {} and `{}`",
&& let ty::PredicateKind::Clause(ty::ClauseKind::Trait(pred)) = errors
error.obligation.predicate.kind().skip_binder() .iter()
{ .map(|e| format!("`{}`", e.obligation.predicate))
self.infcx.err_ctxt().suggest_derive( .collect::<Vec<_>>()
&error.obligation, .join(", "),
diag, last.obligation.predicate,
error.obligation.predicate.kind().rebind(pred), ));
);
}
} }
} }
}); 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)]); self.suggest_derive(diag, &[(trait_ref.to_predicate(self.tcx), None, None)]);
} }
} }

View file

@ -1,8 +1,10 @@
use crate::solve::FulfillmentCtxt;
use crate::traits::query::evaluate_obligation::InferCtxtExt as _; use crate::traits::query::evaluate_obligation::InferCtxtExt as _;
use crate::traits::{self, DefiningAnchor, ObligationCtxt}; use crate::traits::{self, DefiningAnchor, ObligationCtxt};
use rustc_hir::def_id::DefId; use rustc_hir::def_id::DefId;
use rustc_hir::lang_items::LangItem; use rustc_hir::lang_items::LangItem;
use rustc_infer::traits::{TraitEngine, TraitEngineExt};
use rustc_middle::arena::ArenaAllocatable; use rustc_middle::arena::ArenaAllocatable;
use rustc_middle::infer::canonical::{Canonical, CanonicalQueryResponse, QueryResponse}; use rustc_middle::infer::canonical::{Canonical, CanonicalQueryResponse, QueryResponse};
use rustc_middle::traits::query::NoSolution; use rustc_middle::traits::query::NoSolution;
@ -35,6 +37,13 @@ pub trait InferCtxtExt<'tcx> {
params: impl IntoIterator<Item: Into<GenericArg<'tcx>>>, params: impl IntoIterator<Item: Into<GenericArg<'tcx>>>,
param_env: ty::ParamEnv<'tcx>, param_env: ty::ParamEnv<'tcx>,
) -> traits::EvaluationResult; ) -> 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> { 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) 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> { pub trait InferCtxtBuilderExt<'tcx> {