Skip to content

Commit b356b93

Browse files
Rollup merge of rust-lang#151278 - estebank:issue-108894, r=davidtwco
Provide more context on trait bounds being unmet due to imperfect derive When encountering a value that has a borrow checker error where the type was previously moved, when suggesting cloning verify that it is not already being derived. If it is, explain why the `derive(Clone)` doesn't apply: ``` note: if `TypedAddress<T>` implemented `Clone`, you could clone the value --> $DIR/derive-clone-implicit-bound.rs:6:1 | LL | #[derive(Clone, Copy)] | ----- derived `Clone` adds implicit bounds on type parameters LL | pub struct TypedAddress<T>{ | ^^^^^^^^^^^^^^^^^^^^^^^^-^ | | | | | introduces an implicit `T: Clone` bound | consider manually implementing `Clone` for this type ... LL | let old = self.return_value(offset); | ------ you could clone this value ``` When encountering a bound coming from a derive macro, suggest manual impl of the trait. Use the span for the specific param when adding bounds in builtin derive macros, so the diagnostic will point at them as well as the derive macro itself. ``` note: required for `Id<SomeNode>` to implement `PartialEq` --> $DIR/derive-implicit-bound.rs:5:10 | LL | #[derive(PartialEq, Eq)] | ^^^^^^^^^ LL | pub struct Id<T>(PhantomData<T>); | - unsatisfied trait bound introduced in this `derive` macro = help: consider manually implementing `PartialEq` to avoid undesired bounds ``` Mention that the trait could be manually implemented in E0599. Fix rust-lang#108894. Address rust-lang#143714. Address #rust-lang#146515 (but ideally would also suggest constraining the fn bound correctly as well).
2 parents 66daca1 + 6756561 commit b356b93

File tree

81 files changed

+821
-359
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

81 files changed

+821
-359
lines changed

compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs

Lines changed: 44 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1256,7 +1256,7 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> {
12561256
self.suggest_cloning_inner(err, ty, expr);
12571257
}
12581258
} else if let ty::Adt(def, args) = ty.kind()
1259-
&& def.did().as_local().is_some()
1259+
&& let Some(local_did) = def.did().as_local()
12601260
&& def.variants().iter().all(|variant| {
12611261
variant
12621262
.fields
@@ -1266,12 +1266,50 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> {
12661266
{
12671267
let ty_span = self.infcx.tcx.def_span(def.did());
12681268
let mut span: MultiSpan = ty_span.into();
1269-
span.push_span_label(ty_span, "consider implementing `Clone` for this type");
1270-
span.push_span_label(expr.span, "you could clone this value");
1271-
err.span_note(
1272-
span,
1273-
format!("if `{ty}` implemented `Clone`, you could clone the value"),
1269+
let mut derive_clone = false;
1270+
self.infcx.tcx.for_each_relevant_impl(
1271+
self.infcx.tcx.lang_items().clone_trait().unwrap(),
1272+
ty,
1273+
|def_id| {
1274+
if self.infcx.tcx.is_automatically_derived(def_id) {
1275+
derive_clone = true;
1276+
span.push_span_label(
1277+
self.infcx.tcx.def_span(def_id),
1278+
"derived `Clone` adds implicit bounds on type parameters",
1279+
);
1280+
if let Some(generics) = self.infcx.tcx.hir_get_generics(local_did) {
1281+
for param in generics.params {
1282+
if let hir::GenericParamKind::Type { .. } = param.kind {
1283+
span.push_span_label(
1284+
param.span,
1285+
format!(
1286+
"introduces an implicit `{}: Clone` bound",
1287+
param.name.ident()
1288+
),
1289+
);
1290+
}
1291+
}
1292+
}
1293+
}
1294+
},
12741295
);
1296+
let msg = if !derive_clone {
1297+
span.push_span_label(
1298+
ty_span,
1299+
format!(
1300+
"consider {}implementing `Clone` for this type",
1301+
if derive_clone { "manually " } else { "" }
1302+
),
1303+
);
1304+
format!("if `{ty}` implemented `Clone`, you could clone the value")
1305+
} else {
1306+
format!("if all bounds were met, you could clone the value")
1307+
};
1308+
span.push_span_label(expr.span, "you could clone this value");
1309+
err.span_note(span, msg);
1310+
if derive_clone {
1311+
err.help("consider manually implementing `Clone` to avoid undesired bounds");
1312+
}
12751313
} else if let ty::Param(param) = ty.kind()
12761314
&& let Some(_clone_trait_def) = self.infcx.tcx.lang_items().clone_trait()
12771315
&& let generics = self.infcx.tcx.generics_of(self.mir_def_id())

compiler/rustc_builtin_macros/src/deriving/generic/mod.rs

Lines changed: 11 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -638,27 +638,27 @@ impl<'a> TraitDef<'a> {
638638
GenericParamKind::Type { .. } => {
639639
// Extra restrictions on the generics parameters to the
640640
// type being derived upon.
641+
let span = param.ident.span.with_ctxt(ctxt);
641642
let bounds: Vec<_> = self
642643
.additional_bounds
643644
.iter()
644645
.map(|p| {
645-
cx.trait_bound(
646-
p.to_path(cx, self.span, type_ident, generics),
647-
self.is_const,
648-
)
646+
cx.trait_bound(p.to_path(cx, span, type_ident, generics), self.is_const)
649647
})
650648
.chain(
651649
// Add a bound for the current trait.
652-
self.skip_path_as_bound
653-
.not()
654-
.then(|| cx.trait_bound(trait_path.clone(), self.is_const)),
650+
self.skip_path_as_bound.not().then(|| {
651+
let mut trait_path = trait_path.clone();
652+
trait_path.span = span;
653+
cx.trait_bound(trait_path, self.is_const)
654+
}),
655655
)
656656
.chain({
657657
// Add a `Copy` bound if required.
658658
if is_packed && self.needs_copy_as_bound_if_packed {
659659
let p = deriving::path_std!(marker::Copy);
660660
Some(cx.trait_bound(
661-
p.to_path(cx, self.span, type_ident, generics),
661+
p.to_path(cx, span, type_ident, generics),
662662
self.is_const,
663663
))
664664
} else {
@@ -671,7 +671,7 @@ impl<'a> TraitDef<'a> {
671671
)
672672
.collect();
673673

674-
cx.typaram(param.ident.span.with_ctxt(ctxt), param.ident, bounds, None)
674+
cx.typaram(span, param.ident, bounds, None)
675675
}
676676
GenericParamKind::Const { ty, span, .. } => {
677677
let const_nodefault_kind = GenericParamKind::Const {
@@ -791,7 +791,8 @@ impl<'a> TraitDef<'a> {
791791
.collect();
792792

793793
// Create the type of `self`.
794-
let path = cx.path_all(self.span, false, vec![type_ident], self_params);
794+
let path =
795+
cx.path_all(type_ident.span.with_ctxt(ctxt), false, vec![type_ident], self_params);
795796
let self_type = cx.ty_path(path);
796797
let rustc_const_unstable =
797798
cx.path_ident(self.span, Ident::new(sym::rustc_const_unstable, self.span));

compiler/rustc_hir/src/hir.rs

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
// ignore-tidy-filelength
22
use std::borrow::Cow;
33
use std::fmt;
4+
use std::ops::Not;
45

56
use rustc_abi::ExternAbi;
67
use rustc_ast::attr::AttributeExt;
@@ -1012,10 +1013,14 @@ impl<'hir> Generics<'hir> {
10121013

10131014
span_for_parentheses.map_or_else(
10141015
|| {
1015-
// We include bounds that come from a `#[derive(_)]` but point at the user's code,
1016-
// as we use this method to get a span appropriate for suggestions.
1016+
// We include bounds that come from a `#[derive(_)]` but point at the user's
1017+
// code, as we use this method to get a span appropriate for suggestions.
10171018
let bs = bound.span();
1018-
bs.can_be_used_for_suggestions().then(|| (bs.shrink_to_hi(), None))
1019+
// We use `from_expansion` instead of `can_be_used_for_suggestions` because
1020+
// the trait bound from imperfect derives do point at the type parameter,
1021+
// but expanded to a where clause, so we want to ignore those. This is only
1022+
// true for derive intrinsics.
1023+
bs.from_expansion().not().then(|| (bs.shrink_to_hi(), None))
10191024
},
10201025
|span| Some((span.shrink_to_hi(), Some(span.shrink_to_lo()))),
10211026
)

compiler/rustc_hir_analysis/messages.ftl

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -142,6 +142,7 @@ hir_analysis_copy_impl_on_non_adt =
142142
hir_analysis_copy_impl_on_type_with_dtor =
143143
the trait `Copy` cannot be implemented for this type; the type has a destructor
144144
.label = `Copy` not allowed on types with destructors
145+
.note = destructor declared here
145146
146147
hir_analysis_cross_crate_traits = cross-crate traits with a default impl, like `{$traits}`, can only be implemented for a struct/enum type, not `{$self_ty}`
147148
.label = can't implement cross-crate trait with a default impl for non-struct/enum type

compiler/rustc_hir_analysis/src/coherence/builtin.rs

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -122,9 +122,10 @@ fn visit_implementation_of_copy(checker: &Checker<'_>) -> Result<(), ErrorGuaran
122122
let span = tcx.hir_expect_item(impl_did).expect_impl().self_ty.span;
123123
Err(tcx.dcx().emit_err(errors::CopyImplOnNonAdt { span }))
124124
}
125-
Err(CopyImplementationError::HasDestructor) => {
125+
Err(CopyImplementationError::HasDestructor(did)) => {
126126
let span = tcx.hir_expect_item(impl_did).expect_impl().self_ty.span;
127-
Err(tcx.dcx().emit_err(errors::CopyImplOnTypeWithDtor { span }))
127+
let impl_ = tcx.def_span(did);
128+
Err(tcx.dcx().emit_err(errors::CopyImplOnTypeWithDtor { span, impl_ }))
128129
}
129130
Err(CopyImplementationError::HasUnsafeFields) => {
130131
let span = tcx.hir_expect_item(impl_did).expect_impl().self_ty.span;

compiler/rustc_hir_analysis/src/errors.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -278,6 +278,8 @@ pub(crate) struct CopyImplOnTypeWithDtor {
278278
#[primary_span]
279279
#[label]
280280
pub span: Span,
281+
#[note]
282+
pub impl_: Span,
281283
}
282284

283285
#[derive(Diagnostic)]

compiler/rustc_hir_analysis/src/hir_ty_lowering/dyn_trait.rs

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -592,7 +592,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
592592
if span.can_be_used_for_suggestions()
593593
&& poly_trait_ref.trait_ref.trait_def_id().is_some()
594594
&& !self.maybe_suggest_impl_trait(span, hir_id, hir_bounds, &mut diag)
595-
&& !self.maybe_suggest_dyn_trait(hir_id, sugg, &mut diag)
595+
&& !self.maybe_suggest_dyn_trait(hir_id, span, sugg, &mut diag)
596596
{
597597
self.maybe_suggest_add_generic_impl_trait(span, hir_id, &mut diag);
598598
}
@@ -750,10 +750,14 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
750750
fn maybe_suggest_dyn_trait(
751751
&self,
752752
hir_id: hir::HirId,
753+
span: Span,
753754
sugg: Vec<(Span, String)>,
754755
diag: &mut Diag<'_>,
755756
) -> bool {
756757
let tcx = self.tcx();
758+
if span.in_derive_expansion() {
759+
return false;
760+
}
757761

758762
// Look at the direct HIR parent, since we care about the relationship between
759763
// the type and the thing that directly encloses it.

compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs

Lines changed: 83 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1932,25 +1932,94 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
19321932
None,
19331933
);
19341934
} else {
1935+
let mut suggest_derive = true;
19351936
if let Some(errors) =
19361937
self.type_implements_trait_shallow(clone_trait_did, expected_ty, self.param_env)
19371938
{
1939+
let manually_impl = "consider manually implementing `Clone` to avoid the \
1940+
implicit type parameter bounds";
19381941
match &errors[..] {
19391942
[] => {}
19401943
[error] => {
1941-
diag.help(format!(
1942-
"`Clone` is not implemented because the trait bound `{}` is \
1943-
not satisfied",
1944-
error.obligation.predicate,
1945-
));
1944+
let msg = "`Clone` is not implemented because a trait bound is not \
1945+
satisfied";
1946+
if let traits::ObligationCauseCode::ImplDerived(data) =
1947+
error.obligation.cause.code()
1948+
{
1949+
let mut span: MultiSpan = data.span.into();
1950+
if self.tcx.is_automatically_derived(data.impl_or_alias_def_id) {
1951+
span.push_span_label(
1952+
data.span,
1953+
format!(
1954+
"derive introduces an implicit `{}` bound",
1955+
error.obligation.predicate
1956+
),
1957+
);
1958+
}
1959+
diag.span_help(span, msg);
1960+
if self.tcx.is_automatically_derived(data.impl_or_alias_def_id)
1961+
&& data.impl_or_alias_def_id.is_local()
1962+
{
1963+
diag.help(manually_impl);
1964+
suggest_derive = false;
1965+
}
1966+
} else {
1967+
diag.help(msg);
1968+
}
19461969
}
19471970
_ => {
1948-
diag.help(format!(
1949-
"`Clone` is not implemented because the following trait bounds \
1950-
could not be satisfied: {}",
1951-
listify(&errors, |e| format!("`{}`", e.obligation.predicate))
1952-
.unwrap(),
1953-
));
1971+
let unsatisfied_bounds: Vec<_> = errors
1972+
.iter()
1973+
.filter_map(|error| match error.obligation.cause.code() {
1974+
traits::ObligationCauseCode::ImplDerived(data) => {
1975+
let pre = if self
1976+
.tcx
1977+
.is_automatically_derived(data.impl_or_alias_def_id)
1978+
{
1979+
"derive introduces an implicit "
1980+
} else {
1981+
""
1982+
};
1983+
Some((
1984+
data.span,
1985+
format!(
1986+
"{pre}unsatisfied trait bound `{}`",
1987+
error.obligation.predicate
1988+
),
1989+
))
1990+
}
1991+
_ => None,
1992+
})
1993+
.collect();
1994+
let msg = "`Clone` is not implemented because the some trait bounds \
1995+
could not be satisfied";
1996+
if errors.len() == unsatisfied_bounds.len() {
1997+
let mut unsatisfied_bounds_spans: MultiSpan = unsatisfied_bounds
1998+
.iter()
1999+
.map(|(span, _)| *span)
2000+
.collect::<Vec<Span>>()
2001+
.into();
2002+
for (span, label) in unsatisfied_bounds {
2003+
unsatisfied_bounds_spans.push_span_label(span, label);
2004+
}
2005+
diag.span_help(unsatisfied_bounds_spans, msg);
2006+
if errors.iter().all(|error| match error.obligation.cause.code() {
2007+
traits::ObligationCauseCode::ImplDerived(data) => {
2008+
self.tcx.is_automatically_derived(data.impl_or_alias_def_id)
2009+
&& data.impl_or_alias_def_id.is_local()
2010+
}
2011+
_ => false,
2012+
}) {
2013+
diag.help(manually_impl);
2014+
suggest_derive = false;
2015+
}
2016+
} else {
2017+
diag.help(format!(
2018+
"{msg}: {}",
2019+
listify(&errors, |e| format!("`{}`", e.obligation.predicate))
2020+
.unwrap(),
2021+
));
2022+
}
19542023
}
19552024
}
19562025
for error in errors {
@@ -1968,7 +2037,9 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
19682037
}
19692038
}
19702039
}
1971-
self.suggest_derive(diag, &vec![(trait_ref.upcast(self.tcx), None, None)]);
2040+
if suggest_derive {
2041+
self.suggest_derive(diag, &vec![(trait_ref.upcast(self.tcx), None, None)]);
2042+
}
19722043
}
19732044
}
19742045
}

0 commit comments

Comments
 (0)