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 b0c41eb

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 d3a3939 commit b0c41eb

File tree

5 files changed

+195
-12
lines changed

5 files changed

+195
-12
lines changed

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

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

13681464
unsatisfied_bounds = true;
13691465
}

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

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -242,9 +242,9 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
242242
trait_pred: ty::PolyTraitPredicate<'tcx>,
243243
associated_ty: Option<(&'static str, Ty<'tcx>)>,
244244
mut body_id: LocalDefId,
245-
) {
245+
) -> bool{
246246
if trait_pred.skip_binder().polarity != ty::PredicatePolarity::Positive {
247-
return;
247+
returnfalse;
248248
}
249249

250250
let trait_pred = self.resolve_numeric_literals_with_default(trait_pred);
@@ -279,7 +279,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
279279
trait_pred,
280280
Some((ident, bounds)),
281281
);
282-
return;
282+
returntrue;
283283
}
284284

285285
hir::Node::TraitItem(hir::TraitItem {
@@ -293,7 +293,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
293293
self.tcx, body_id, generics, "`Self`", err, None, projection, trait_pred,
294294
None,
295295
);
296-
return;
296+
returntrue;
297297
}
298298

299299
hir::Node::TraitItem(hir::TraitItem {
@@ -321,7 +321,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
321321
trait_pred,
322322
None,
323323
);
324-
return;
324+
returntrue;
325325
}
326326
hir::Node::Item(hir::Item {
327327
kind:
@@ -341,7 +341,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
341341
trait_pred,
342342
None,
343343
);
344-
return;
344+
returntrue;
345345
}
346346

347347
hir::Node::Item(hir::Item {
@@ -374,7 +374,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
374374
.iter()
375375
.all(|g| g.is_suggestable(self.tcx, false))
376376
{
377-
return;
377+
returnfalse;
378378
}
379379
// Missing generic type parameter bound.
380380
let param_name = self_ty.to_string();
@@ -406,7 +406,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
406406
Some(trait_pred.def_id()),
407407
None,
408408
) {
409-
return;
409+
returntrue;
410410
}
411411
}
412412

@@ -432,10 +432,10 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
432432
trait_pred,
433433
associated_ty,
434434
) {
435-
return;
435+
returntrue;
436436
}
437437
}
438-
hir::Node::Crate(..) => return,
438+
hir::Node::Crate(..) => returnfalse,
439439

440440
_ => {}
441441
}
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 によって変換されたページ (->オリジナル) /