Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Commit 350d3e6

Browse files
committed
Suggest derive(Trait) or T: Trait from transitive obligation in some cases
With code like the following ```rust struct Ctx<A> { a_map: HashMap<String, B<A>>, } struct B<A> { a: A, } ``` the derived trait will have an implicit restriction on `A: Clone` for both types. When referenced as follows: ```rust fn foo<Z>(ctx: &mut Ctx<Z>) { let a_map = ctx.a_map.clone(); //~ ERROR E0599 } ``` suggest constraining `Z`: ``` error[E0599]: the method `clone` exists for struct `HashMap<String, B<Z>>`, but its trait bounds were not satisfied --> $DIR/type-or-type-param-missing-transitive-trait-contraint.rs:16:27 | LL | struct B<A> { | ----------- doesn't satisfy `B<Z>: Clone` ... LL | let a_map = ctx.a_map.clone(); | ^^^^^ method cannot be called on `HashMap<String, B<Z>>` due to unsatisfied trait bounds | = note: the following trait bounds were not satisfied: `B<Z>: Clone` which is required by `HashMap<String, B<Z>>: Clone` help: consider restricting type parameter `Z` | LL | fn foo<Z: std::clone::Clone>(ctx: &mut Ctx<Z>) { | +++++++++++++++++++ ``` When referenced as follows, with a specific type `S`: ```rust struct S; fn bar(ctx: &mut Ctx<S>) { let a_map = ctx.a_map.clone(); //~ ERROR E0599 } ``` suggest `derive`ing the appropriate trait on the local type: ``` error[E0599]: the method `clone` exists for struct `HashMap<String, B<S>>`, but its trait bounds were not satisfied --> $DIR/type-or-type-param-missing-transitive-trait-contraint.rs:21:27 | LL | struct B<A> { | ----------- doesn't satisfy `B<S>: Clone` ... LL | let a_map = ctx.a_map.clone(); | ^^^^^ method cannot be called on `HashMap<String, B<S>>` due to unsatisfied trait bounds | = note: the following trait bounds were not satisfied: `B<S>: Clone` which is required by `HashMap<String, B<S>>: Clone` help: consider annotating `S` with `#[derive(Clone)]` | LL + #[derive(Clone)] LL | struct S; | ```
1 parent 3811f40 commit 350d3e6

File tree

5 files changed

+192
-11
lines changed

5 files changed

+192
-11
lines changed

‎compiler/rustc_hir_typeck/src/method/suggest.rs

Lines changed: 95 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@ use rustc_span::{edit_distance, ErrorGuaranteed, ExpnKind, FileName, MacroKind,
3737
use rustc_span::{Symbol, DUMMY_SP};
3838
use rustc_trait_selection::error_reporting::traits::on_unimplemented::OnUnimplementedNote;
3939
use rustc_trait_selection::error_reporting::traits::on_unimplemented::TypeErrCtxtExt as _;
40+
use rustc_trait_selection::error_reporting::traits::suggestions::TypeErrCtxtExt;
4041
use rustc_trait_selection::infer::InferCtxtExt;
4142
use rustc_trait_selection::traits::query::evaluate_obligation::InferCtxtExt as _;
4243
use rustc_trait_selection::traits::{
@@ -1368,7 +1369,100 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
13681369
"the following trait bounds were not satisfied:\n{bound_list}"
13691370
));
13701371
}
1371-
suggested_derive = self.suggest_derive(&mut err, unsatisfied_predicates);
1372+
1373+
let mut suggest_derive = true;
1374+
for (pred, _, cause) in unsatisfied_predicates {
1375+
let Some(ty::PredicateKind::Clause(ty::ClauseKind::Trait(trait_pred))) =
1376+
pred.kind().no_bound_vars()
1377+
else {
1378+
continue;
1379+
};
1380+
let (adt, params) = match trait_pred.self_ty().kind() {
1381+
ty::Adt(adt, params) if adt.did().is_local() => (*adt, params),
1382+
_ => continue,
1383+
};
1384+
if self
1385+
.tcx
1386+
.all_impls(trait_pred.def_id())
1387+
.filter_map(|imp_did| {
1388+
self.tcx.impl_trait_header(imp_did).map(|h| (imp_did, h))
1389+
})
1390+
.filter(|(did, header)| {
1391+
let imp = header.trait_ref.instantiate_identity();
1392+
let impl_adt = match imp.self_ty().ty_adt_def() {
1393+
Some(impl_adt) if adt.did().is_local() => impl_adt,
1394+
_ => return false,
1395+
};
1396+
header.polarity == ty::ImplPolarity::Positive
1397+
&& impl_adt == adt
1398+
&& self.tcx.is_automatically_derived(*did)
1399+
})
1400+
.count()
1401+
== 1
1402+
{
1403+
// We now know that for this predicate, there *was* a `derive(Trait)` for
1404+
// the trait at hand, so we don't want to suggest writing that again.
1405+
for param in &params[..] {
1406+
// Look at the type parameters for the currently obligated type to see
1407+
// if a restriciton of `TypeParam: Trait` would help. If the
1408+
// instantiated type param is not a type param but instead an actual
1409+
// type, see if we can suggest `derive(Trait)` on *that* type.
1410+
// See `tests/ui/suggestions/f1000.rs`
1411+
let Some(ty) = param.as_type() else {
1412+
continue;
1413+
};
1414+
match ty.kind() {
1415+
ty::Adt(adt, _) if adt.did().is_local() => {
1416+
// The type param at hand is a local type, try to suggest
1417+
// `derive(Trait)`.
1418+
let trait_ref =
1419+
ty::TraitRef::new(tcx, trait_pred.trait_ref.def_id, [ty]);
1420+
let trait_pred = ty::Binder::dummy(ty::TraitPredicate {
1421+
trait_ref,
1422+
polarity: ty::PredicatePolarity::Positive,
1423+
});
1424+
suggested_derive = self.suggest_derive(
1425+
&mut err,
1426+
&[(
1427+
<_ as ty::UpcastFrom<_, _>>::upcast_from(
1428+
trait_pred, self.tcx,
1429+
),
1430+
None,
1431+
cause.clone(),
1432+
)],
1433+
);
1434+
}
1435+
ty::Param(_) => {
1436+
// It was a type param. See if it corresponds to the current
1437+
// `fn` and suggest `T: Trait`.
1438+
if let Some(obligation) = cause {
1439+
let trait_ref = ty::TraitRef::new(
1440+
tcx,
1441+
trait_pred.trait_ref.def_id,
1442+
[ty],
1443+
);
1444+
let trait_pred = ty::Binder::dummy(ty::TraitPredicate {
1445+
trait_ref,
1446+
polarity: ty::PredicatePolarity::Positive,
1447+
});
1448+
suggested_derive =
1449+
self.err_ctxt().suggest_restricting_param_bound(
1450+
&mut err,
1451+
trait_pred,
1452+
None,
1453+
obligation.body_id,
1454+
);
1455+
}
1456+
}
1457+
_ => {}
1458+
}
1459+
}
1460+
suggest_derive = false
1461+
}
1462+
}
1463+
if suggest_derive {
1464+
suggested_derive = self.suggest_derive(&mut err, unsatisfied_predicates);
1465+
}
13721466

13731467
unsatisfied_bounds = true;
13741468
}

‎compiler/rustc_trait_selection/src/error_reporting/traits/suggestions.rs

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -249,9 +249,9 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
249249
trait_pred: ty::PolyTraitPredicate<'tcx>,
250250
associated_ty: Option<(&'static str, Ty<'tcx>)>,
251251
mut body_id: LocalDefId,
252-
) {
252+
) -> bool{
253253
if trait_pred.skip_binder().polarity != ty::PredicatePolarity::Positive {
254-
return;
254+
returnfalse;
255255
}
256256

257257
let trait_pred = self.resolve_numeric_literals_with_default(trait_pred);
@@ -286,7 +286,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
286286
trait_pred,
287287
Some((ident, bounds)),
288288
);
289-
return;
289+
returntrue;
290290
}
291291

292292
hir::Node::TraitItem(hir::TraitItem {
@@ -300,7 +300,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
300300
self.tcx, body_id, generics, "`Self`", err, None, projection, trait_pred,
301301
None,
302302
);
303-
return;
303+
returntrue;
304304
}
305305

306306
hir::Node::TraitItem(hir::TraitItem {
@@ -328,7 +328,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
328328
trait_pred,
329329
None,
330330
);
331-
return;
331+
returntrue;
332332
}
333333
hir::Node::Item(hir::Item {
334334
kind:
@@ -348,7 +348,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
348348
trait_pred,
349349
None,
350350
);
351-
return;
351+
returntrue;
352352
}
353353

354354
hir::Node::Item(hir::Item {
@@ -381,7 +381,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
381381
.iter()
382382
.all(|g| g.is_suggestable(self.tcx, false))
383383
{
384-
return;
384+
returnfalse;
385385
}
386386
// Missing generic type parameter bound.
387387
let param_name = self_ty.to_string();
@@ -413,7 +413,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
413413
Some(trait_pred.def_id()),
414414
None,
415415
) {
416-
return;
416+
returntrue;
417417
}
418418
}
419419

@@ -439,10 +439,10 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
439439
trait_pred,
440440
associated_ty,
441441
) {
442-
return;
442+
returntrue;
443443
}
444444
}
445-
hir::Node::Crate(..) => return,
445+
hir::Node::Crate(..) => returnfalse,
446446

447447
_ => {}
448448
}
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
//@ run-rustfix
2+
#![allow(warnings)]
3+
use std::collections::HashMap;
4+
5+
#[derive(Clone)]
6+
struct Ctx<A> {
7+
a_map: HashMap<String, B<A>>,
8+
}
9+
10+
#[derive(Clone)]
11+
struct B<A> {
12+
a: A,
13+
}
14+
15+
fn foo<Z: std::clone::Clone>(ctx: &mut Ctx<Z>) {
16+
let a_map = ctx.a_map.clone(); //~ ERROR E0599
17+
}
18+
19+
#[derive(Clone)]
20+
struct S;
21+
fn bar(ctx: &mut Ctx<S>) {
22+
let a_map = ctx.a_map.clone(); //~ ERROR E0599
23+
}
24+
25+
fn main() {}
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
//@ run-rustfix
2+
#![allow(warnings)]
3+
use std::collections::HashMap;
4+
5+
#[derive(Clone)]
6+
struct Ctx<A> {
7+
a_map: HashMap<String, B<A>>,
8+
}
9+
10+
#[derive(Clone)]
11+
struct B<A> {
12+
a: A,
13+
}
14+
15+
fn foo<Z>(ctx: &mut Ctx<Z>) {
16+
let a_map = ctx.a_map.clone(); //~ ERROR E0599
17+
}
18+
19+
struct S;
20+
fn bar(ctx: &mut Ctx<S>) {
21+
let a_map = ctx.a_map.clone(); //~ ERROR E0599
22+
}
23+
24+
fn main() {}
Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
error[E0599]: the method `clone` exists for struct `HashMap<String, B<Z>>`, but its trait bounds were not satisfied
2+
--> $DIR/type-or-type-param-missing-transitive-trait-contraint.rs:16:27
3+
|
4+
LL | struct B<A> {
5+
| ----------- doesn't satisfy `B<Z>: Clone`
6+
...
7+
LL | let a_map = ctx.a_map.clone();
8+
| ^^^^^ method cannot be called on `HashMap<String, B<Z>>` due to unsatisfied trait bounds
9+
|
10+
= note: the following trait bounds were not satisfied:
11+
`B<Z>: Clone`
12+
which is required by `HashMap<String, B<Z>>: Clone`
13+
help: consider restricting type parameter `Z`
14+
|
15+
LL | fn foo<Z: std::clone::Clone>(ctx: &mut Ctx<Z>) {
16+
| +++++++++++++++++++
17+
18+
error[E0599]: the method `clone` exists for struct `HashMap<String, B<S>>`, but its trait bounds were not satisfied
19+
--> $DIR/type-or-type-param-missing-transitive-trait-contraint.rs:21:27
20+
|
21+
LL | struct B<A> {
22+
| ----------- doesn't satisfy `B<S>: Clone`
23+
...
24+
LL | let a_map = ctx.a_map.clone();
25+
| ^^^^^ method cannot be called on `HashMap<String, B<S>>` due to unsatisfied trait bounds
26+
|
27+
= note: the following trait bounds were not satisfied:
28+
`B<S>: Clone`
29+
which is required by `HashMap<String, B<S>>: Clone`
30+
help: consider annotating `S` with `#[derive(Clone)]`
31+
|
32+
LL + #[derive(Clone)]
33+
LL | struct S;
34+
|
35+
36+
error: aborting due to 2 previous errors
37+
38+
For more information about this error, try `rustc --explain E0599`.

0 commit comments

Comments
(0)

AltStyle によって変換されたページ (->オリジナル) /