From 400d343796065e57e78d92b4cf74c78bec38452a Mon Sep 17 00:00:00 2001 From: Maybe Waffle Date: Fri, 25 Feb 2022 23:34:25 +0300 Subject: [PATCH] Suggest adding `Copy` bound when Adt is moved out Previously we've only suggested adding `Copy` bounds when the type being moved/copied is a type parameter (generic). With this commit we also suggest adding bounds when a type - Can be copy - All predicates that need to be satisfied for that are based on type params i.e. we will suggest `T: Copy` for `Option`, but won't suggest anything for `Option`. Future work: it would be nice to also suggest adding `.clone()` calls --- .../src/diagnostics/conflict_errors.rs | 63 ++++++++++++++++++- 1 file changed, 62 insertions(+), 1 deletion(-) diff --git a/compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs b/compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs index eb906d5fde7b4..e8ecb3cb6f3e7 100644 --- a/compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs +++ b/compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs @@ -5,16 +5,21 @@ use rustc_errors::{Applicability, Diagnostic, DiagnosticBuilder, ErrorReported}; use rustc_hir as hir; use rustc_hir::def_id::DefId; use rustc_hir::{AsyncGeneratorKind, GeneratorKind}; +use rustc_infer::infer::TyCtxtInferExt; +use rustc_infer::traits::ObligationCause; use rustc_middle::mir::{ self, AggregateKind, BindingForm, BorrowKind, ClearCrossCrate, ConstraintCategory, FakeReadCause, LocalDecl, LocalInfo, LocalKind, Location, Operand, Place, PlaceRef, ProjectionElem, Rvalue, Statement, StatementKind, Terminator, TerminatorKind, VarBindingForm, }; -use rustc_middle::ty::{self, suggest_constraining_type_param, Ty}; +use rustc_middle::ty::{ + self, suggest_constraining_type_param, suggest_constraining_type_params, PredicateKind, Ty, +}; use rustc_mir_dataflow::move_paths::{InitKind, MoveOutIndex, MovePathIndex}; use rustc_span::symbol::sym; use rustc_span::{BytePos, MultiSpan, Span, DUMMY_SP}; use rustc_trait_selection::infer::InferCtxtExt; +use rustc_trait_selection::traits::TraitEngineExt as _; use crate::borrow_set::TwoPhaseActivation; use crate::borrowck_errors; @@ -423,7 +428,63 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { None, ); } + } else { + // Try to find predicates on *generic params* that would allow copying `ty` + + let tcx = self.infcx.tcx; + let generics = tcx.generics_of(self.mir_def_id()); + if let Some(hir_generics) = tcx + .typeck_root_def_id(self.mir_def_id().to_def_id()) + .as_local() + .and_then(|def_id| tcx.hir().get_generics(def_id)) + { + let predicates: Result, _> = tcx.infer_ctxt().enter(|infcx| { + let mut fulfill_cx = + >::new(infcx.tcx); + + let copy_did = infcx.tcx.lang_items().copy_trait().unwrap(); + let cause = ObligationCause::new( + span, + self.mir_hir_id(), + rustc_infer::traits::ObligationCauseCode::MiscObligation, + ); + fulfill_cx.register_bound(&infcx, self.param_env, ty, copy_did, cause); + let errors = fulfill_cx.select_where_possible(&infcx); + + // Only emit suggestion if all required predicates are on generic + errors + .into_iter() + .map(|err| match err.obligation.predicate.kind().skip_binder() { + PredicateKind::Trait(predicate) => { + match predicate.self_ty().kind() { + ty::Param(param_ty) => Ok(( + generics.type_param(param_ty, tcx), + predicate + .trait_ref + .print_only_trait_path() + .to_string(), + )), + _ => Err(()), + } + } + _ => Err(()), + }) + .collect() + }); + + if let Ok(predicates) = predicates { + suggest_constraining_type_params( + tcx, + hir_generics, + &mut err, + predicates.iter().map(|(param, constraint)| { + (param.name.as_str(), &**constraint, None) + }), + ); + } + } } + let span = if let Some(local) = place.as_local() { let decl = &self.body.local_decls[local]; Some(decl.source_info.span)