Skip to content

Commit fa6986e

Browse files
committed
Suggest bounds in more cases, accounting for type parameters referenced in predicate
1 parent e5e79f8 commit fa6986e

File tree

6 files changed

+136
-14
lines changed

6 files changed

+136
-14
lines changed

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

Lines changed: 67 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -31,8 +31,8 @@ use rustc_middle::ty::print::{
3131
};
3232
use rustc_middle::ty::{
3333
self, AdtKind, GenericArgs, InferTy, IsSuggestable, Ty, TyCtxt, TypeFoldable, TypeFolder,
34-
TypeSuperFoldable, TypeVisitableExt, TypeckResults, Upcast, suggest_arbitrary_trait_bound,
35-
suggest_constraining_type_param,
34+
TypeSuperFoldable, TypeSuperVisitable, TypeVisitableExt, TypeVisitor, TypeckResults, Upcast,
35+
suggest_arbitrary_trait_bound, suggest_constraining_type_param,
3636
};
3737
use rustc_middle::{bug, span_bug};
3838
use rustc_span::def_id::LocalDefId;
@@ -262,6 +262,9 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
262262
_ => (false, None),
263263
};
264264

265+
let mut finder = ParamFinder { .. };
266+
finder.visit_binder(&trait_pred);
267+
265268
// FIXME: Add check for trait bound that is already present, particularly `?Sized` so we
266269
// don't suggest `T: Sized + ?Sized`.
267270
loop {
@@ -410,6 +413,26 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
410413
}
411414
}
412415

416+
hir::Node::TraitItem(hir::TraitItem {
417+
generics,
418+
kind: hir::TraitItemKind::Fn(..),
419+
..
420+
})
421+
| hir::Node::ImplItem(hir::ImplItem {
422+
generics,
423+
trait_item_def_id: None,
424+
kind: hir::ImplItemKind::Fn(..),
425+
..
426+
}) if finder.can_suggest_bound(generics) => {
427+
// Missing generic type parameter bound.
428+
suggest_arbitrary_trait_bound(
429+
self.tcx,
430+
generics,
431+
err,
432+
trait_pred,
433+
associated_ty,
434+
);
435+
}
413436
hir::Node::Item(hir::Item {
414437
kind:
415438
hir::ItemKind::Struct(_, generics, _)
@@ -422,7 +445,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
422445
| hir::ItemKind::Const(_, generics, _, _)
423446
| hir::ItemKind::TraitAlias(_, generics, _),
424447
..
425-
}) if !param_ty => {
448+
}) if finder.can_suggest_bound(generics) => {
426449
// Missing generic type parameter bound.
427450
if suggest_arbitrary_trait_bound(
428451
self.tcx,
@@ -5034,8 +5057,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
50345057
// Suggesting `T: ?Sized` is only valid in an ADT if `T` is only used in a
50355058
// borrow. `struct S<'a, T: ?Sized>(&'a T);` is valid, `struct S<T: ?Sized>(T);`
50365059
// is not. Look for invalid "bare" parameter uses, and suggest using indirection.
5037-
let mut visitor =
5038-
FindTypeParam { param: param.name.ident().name, invalid_spans: vec![], nested: false };
5060+
let mut visitor = FindTypeParam { param: param.name.ident().name, .. };
50395061
visitor.visit_item(item);
50405062
if visitor.invalid_spans.is_empty() {
50415063
return false;
@@ -5198,7 +5220,7 @@ fn hint_missing_borrow<'tcx>(
51985220
/// Used to suggest replacing associated types with an explicit type in `where` clauses.
51995221
#[derive(Debug)]
52005222
pub struct SelfVisitor<'v> {
5201-
pub paths: Vec<&'v hir::Ty<'v>>,
5223+
pub paths: Vec<&'v hir::Ty<'v>> = Vec::new(),
52025224
pub name: Option<Symbol>,
52035225
}
52045226

@@ -5568,7 +5590,7 @@ fn point_at_assoc_type_restriction<G: EmissionGuarantee>(
55685590
);
55695591
// Search for the associated type `Self::{name}`, get
55705592
// its type and suggest replacing the bound with it.
5571-
let mut visitor = SelfVisitor { paths: vec![], name: Some(name) };
5593+
let mut visitor = SelfVisitor { name: Some(name), .. };
55725594
visitor.visit_trait_ref(trait_ref);
55735595
for path in visitor.paths {
55745596
err.span_suggestion_verbose(
@@ -5579,7 +5601,7 @@ fn point_at_assoc_type_restriction<G: EmissionGuarantee>(
55795601
);
55805602
}
55815603
} else {
5582-
let mut visitor = SelfVisitor { paths: vec![], name: None };
5604+
let mut visitor = SelfVisitor { name: None, .. };
55835605
visitor.visit_trait_ref(trait_ref);
55845606
let span: MultiSpan =
55855607
visitor.paths.iter().map(|p| p.span).collect::<Vec<Span>>().into();
@@ -5609,8 +5631,8 @@ fn get_deref_type_and_refs(mut ty: Ty<'_>) -> (Ty<'_>, Vec<hir::Mutability>) {
56095631
/// `param: ?Sized` would be a valid constraint.
56105632
struct FindTypeParam {
56115633
param: rustc_span::Symbol,
5612-
invalid_spans: Vec<Span>,
5613-
nested: bool,
5634+
invalid_spans: Vec<Span> = Vec::new(),
5635+
nested: bool = false,
56145636
}
56155637

56165638
impl<'v> Visitor<'v> for FindTypeParam {
@@ -5648,3 +5670,38 @@ impl<'v> Visitor<'v> for FindTypeParam {
56485670
}
56495671
}
56505672
}
5673+
5674+
/// Look for type parameters in predicates. We use this to identify whether a bound is suitable in
5675+
/// on a given item.
5676+
struct ParamFinder {
5677+
params: Vec<Symbol> = Vec::new(),
5678+
}
5679+
5680+
impl<'tcx> TypeVisitor<TyCtxt<'tcx>> for ParamFinder {
5681+
fn visit_ty(&mut self, t: Ty<'tcx>) -> Self::Result {
5682+
match t.kind() {
5683+
ty::Param(p) => self.params.push(p.name),
5684+
_ => {}
5685+
}
5686+
t.super_visit_with(self)
5687+
}
5688+
}
5689+
5690+
impl ParamFinder {
5691+
/// Whether the `hir::Generics` of the current item can suggest the evaluated bound because its
5692+
/// references to type parameters are present in the generics.
5693+
fn can_suggest_bound(&self, generics: &hir::Generics<'_>) -> bool {
5694+
if self.params.is_empty() {
5695+
// There are no references to type parameters at all, so suggesting the bound
5696+
// would be reasonable.
5697+
return true;
5698+
}
5699+
generics.params.iter().any(|p| match p.name {
5700+
hir::ParamName::Plain(p_name) => {
5701+
// All of the parameters in the bound can be referenced in the current item.
5702+
self.params.iter().any(|p| *p == p_name.name || *p == kw::SelfUpper)
5703+
}
5704+
_ => true,
5705+
})
5706+
}
5707+
}

compiler/rustc_trait_selection/src/lib.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
#![feature(assert_matches)]
2020
#![feature(associated_type_defaults)]
2121
#![feature(box_patterns)]
22+
#![feature(default_field_values)]
2223
#![feature(if_let_guard)]
2324
#![feature(iter_intersperse)]
2425
#![feature(iterator_try_reduce)]

tests/ui/const-generics/failing_goal_with_repeat_expr_anon_const.stderr

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -21,10 +21,6 @@ LL | [0u8; std::mem::size_of::<Self::A>()] == Self::P;
2121
| ^^ no implementation for `[u8; std::mem::size_of::<Self::A>()] == <Self as T>::A`
2222
|
2323
= help: the trait `PartialEq<<Self as T>::A>` is not implemented for `[u8; std::mem::size_of::<Self::A>()]`
24-
help: consider introducing a `where` clause, but there might be an alternative better way to express this requirement
25-
|
26-
LL | pub trait T where [u8; std::mem::size_of::<Self::A>()]: PartialEq<<Self as T>::A> {
27-
| +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
2824

2925
error: aborting due to 3 previous errors
3026

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
//@ run-rustfix
2+
#![allow(dead_code)]
3+
struct Application;
4+
// https://github.com/rust-lang/rust/issues/144734
5+
trait Trait {
6+
type Error: std::error::Error;
7+
8+
fn run(&self) -> Result<(), Self::Error>;
9+
}
10+
11+
#[derive(Debug)]
12+
enum ApplicationError {
13+
Quit,
14+
}
15+
16+
impl Application {
17+
fn thing<T: Trait>(&self, t: T) -> Result<(), ApplicationError> where ApplicationError: From<<T as Trait>::Error> {
18+
t.run()?; //~ ERROR E0277
19+
Ok(())
20+
}
21+
}
22+
23+
fn main() {}
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
//@ run-rustfix
2+
#![allow(dead_code)]
3+
struct Application;
4+
// https://github.com/rust-lang/rust/issues/144734
5+
trait Trait {
6+
type Error: std::error::Error;
7+
8+
fn run(&self) -> Result<(), Self::Error>;
9+
}
10+
11+
#[derive(Debug)]
12+
enum ApplicationError {
13+
Quit,
14+
}
15+
16+
impl Application {
17+
fn thing<T: Trait>(&self, t: T) -> Result<(), ApplicationError> {
18+
t.run()?; //~ ERROR E0277
19+
Ok(())
20+
}
21+
}
22+
23+
fn main() {}
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
error[E0277]: `?` couldn't convert the error to `ApplicationError`
2+
--> $DIR/suggest-complex-bound-on-method.rs:18:16
3+
|
4+
LL | t.run()?;
5+
| -----^ the trait `From<<T as Trait>::Error>` is not implemented for `ApplicationError`
6+
| |
7+
| this can't be annotated with `?` because it has type `Result<_, <T as Trait>::Error>`
8+
|
9+
note: `ApplicationError` needs to implement `From<<T as Trait>::Error>`
10+
--> $DIR/suggest-complex-bound-on-method.rs:12:1
11+
|
12+
LL | enum ApplicationError {
13+
| ^^^^^^^^^^^^^^^^^^^^^
14+
= note: the question mark operation (`?`) implicitly performs a conversion on the error value using the `From` trait
15+
help: consider introducing a `where` clause, but there might be an alternative better way to express this requirement
16+
|
17+
LL | fn thing<T: Trait>(&self, t: T) -> Result<(), ApplicationError> where ApplicationError: From<<T as Trait>::Error> {
18+
| +++++++++++++++++++++++++++++++++++++++++++++++++
19+
20+
error: aborting due to 1 previous error
21+
22+
For more information about this error, try `rustc --explain E0277`.

0 commit comments

Comments
 (0)