Skip to content

Commit af5a37e

Browse files
committed
Modify existing bounds if they exist
1 parent 044a28a commit af5a37e

File tree

13 files changed

+186
-43
lines changed

13 files changed

+186
-43
lines changed

compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs

+1
Original file line numberDiff line numberDiff line change
@@ -803,6 +803,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
803803
predicates
804804
.iter()
805805
.map(|(param, constraint)| (param.name.as_str(), &**constraint, None)),
806+
None,
806807
);
807808
}
808809
}

compiler/rustc_const_eval/src/transform/check_consts/ops.rs

+1
Original file line numberDiff line numberDiff line change
@@ -136,6 +136,7 @@ impl<'tcx> NonConstOp<'tcx> for FnCallNonConst<'tcx> {
136136
&param_ty.name.as_str(),
137137
&constraint,
138138
None,
139+
None,
139140
);
140141
}
141142
}

compiler/rustc_hir_analysis/src/coherence/builtin.rs

+1
Original file line numberDiff line numberDiff line change
@@ -176,6 +176,7 @@ fn visit_implementation_of_copy(tcx: TyCtxt<'_>, impl_did: LocalDefId) {
176176
bounds.iter().map(|(param, constraint, def_id)| {
177177
(param.as_str(), constraint.as_str(), *def_id)
178178
}),
179+
None,
179180
);
180181
err.emit();
181182
}

compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs

+1
Original file line numberDiff line numberDiff line change
@@ -1385,6 +1385,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
13851385
generics,
13861386
diag,
13871387
vec![(param.name.as_str(), "Clone", Some(clone_trait_did))].into_iter(),
1388+
None,
13881389
);
13891390
} else {
13901391
self.suggest_derive(diag, &[(trait_ref.to_predicate(self.tcx), None, None)]);

compiler/rustc_infer/src/infer/error_reporting/note_and_explain.rs

+66-29
Original file line numberDiff line numberDiff line change
@@ -77,49 +77,86 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> {
7777
(ty::Param(p), ty::Alias(ty::Projection, proj)) | (ty::Alias(ty::Projection, proj), ty::Param(p))
7878
if tcx.def_kind(proj.def_id) != DefKind::ImplTraitPlaceholder =>
7979
{
80-
let generics = tcx.generics_of(body_owner_def_id);
81-
let p_span = tcx.def_span(generics.type_param(p, tcx).def_id);
80+
let p_def_id = tcx
81+
.generics_of(body_owner_def_id)
82+
.type_param(p, tcx)
83+
.def_id;
84+
let p_span = tcx.def_span(p_def_id);
8285
if !sp.contains(p_span) {
8386
diag.span_label(p_span, "this type parameter");
8487
}
8588
let hir = tcx.hir();
8689
let mut note = true;
87-
if let Some(generics) = generics
88-
.type_param(p, tcx)
89-
.def_id
90+
let parent = p_def_id
9091
.as_local()
91-
.map(|id| hir.local_def_id_to_hir_id(id))
92-
.and_then(|id| tcx.hir().find_parent(id))
93-
.as_ref()
94-
.and_then(|node| node.generics())
92+
.and_then(|id| {
93+
let local_id = hir.local_def_id_to_hir_id(id);
94+
let generics = tcx.hir().find_parent(local_id)?.generics()?;
95+
Some((id, generics))
96+
});
97+
if let Some((local_id, generics)) = parent
9598
{
9699
// Synthesize the associated type restriction `Add<Output = Expected>`.
97100
// FIXME: extract this logic for use in other diagnostics.
98101
let (trait_ref, assoc_substs) = proj.trait_ref_and_own_substs(tcx);
99-
let path =
100-
tcx.def_path_str_with_substs(trait_ref.def_id, trait_ref.substs);
101102
let item_name = tcx.item_name(proj.def_id);
102103
let item_args = self.format_generic_args(assoc_substs);
103104

104-
let path = if path.ends_with('>') {
105-
format!(
106-
"{}, {}{} = {}>",
107-
&path[..path.len() - 1],
108-
item_name,
109-
item_args,
110-
p
111-
)
105+
// Here, we try to see if there's an existing
106+
// trait implementation that matches the one that
107+
// we're suggesting to restrict. If so, find the
108+
// "end", whether it be at the end of the trait
109+
// or the end of the generic arguments.
110+
let mut matching_span = None;
111+
let mut matched_end_of_args = false;
112+
for bound in generics.bounds_for_param(local_id) {
113+
let potential_spans = bound
114+
.bounds
115+
.iter()
116+
.find_map(|bound| {
117+
let bound_trait_path = bound.trait_ref()?.path;
118+
let def_id = bound_trait_path.res.opt_def_id()?;
119+
let generic_args = bound_trait_path.segments.iter().last().map(|path| path.args());
120+
(def_id == trait_ref.def_id).then_some((bound_trait_path.span, generic_args))
121+
});
122+
123+
if let Some((end_of_trait, end_of_args)) = potential_spans {
124+
let args_span = end_of_args.and_then(|args| args.span());
125+
matched_end_of_args = args_span.is_some();
126+
matching_span = args_span
127+
.or_else(|| Some(end_of_trait))
128+
.map(|span| span.shrink_to_hi());
129+
break;
130+
}
131+
}
132+
133+
if matched_end_of_args {
134+
// Append suggestion to the end of our args
135+
let path = format!(", {}{} = {}",item_name, item_args, p);
136+
note = !suggest_constraining_type_param(
137+
tcx,
138+
generics,
139+
diag,
140+
&format!("{}", proj.self_ty()),
141+
&path,
142+
None,
143+
matching_span,
144+
);
112145
} else {
113-
format!("{}<{}{} = {}>", path, item_name, item_args, p)
114-
};
115-
note = !suggest_constraining_type_param(
116-
tcx,
117-
generics,
118-
diag,
119-
&format!("{}", proj.self_ty()),
120-
&path,
121-
None,
122-
);
146+
// Suggest adding a bound to an existing trait
147+
// or if the trait doesn't exist, add the trait
148+
// and the suggested bounds.
149+
let path = format!("<{}{} = {}>", item_name, item_args, p);
150+
note = !suggest_constraining_type_param(
151+
tcx,
152+
generics,
153+
diag,
154+
&format!("{}", proj.self_ty()),
155+
&path,
156+
None,
157+
matching_span,
158+
);
159+
}
123160
}
124161
if note {
125162
diag.note("you might be missing a type parameter or trait bound");

compiler/rustc_middle/src/ty/diagnostics.rs

+14-1
Original file line numberDiff line numberDiff line change
@@ -193,19 +193,24 @@ fn suggest_removing_unsized_bound(
193193
}
194194

195195
/// Suggest restricting a type param with a new bound.
196+
///
197+
/// If `span_to_replace` is provided, then that span will be replaced with the
198+
/// `constraint`. If one wasn't provided, then the full bound will be suggested.
196199
pub fn suggest_constraining_type_param(
197200
tcx: TyCtxt<'_>,
198201
generics: &hir::Generics<'_>,
199202
err: &mut Diagnostic,
200203
param_name: &str,
201204
constraint: &str,
202205
def_id: Option<DefId>,
206+
span_to_replace: Option<Span>,
203207
) -> bool {
204208
suggest_constraining_type_params(
205209
tcx,
206210
generics,
207211
err,
208212
[(param_name, constraint, def_id)].into_iter(),
213+
span_to_replace,
209214
)
210215
}
211216

@@ -215,6 +220,7 @@ pub fn suggest_constraining_type_params<'a>(
215220
generics: &hir::Generics<'_>,
216221
err: &mut Diagnostic,
217222
param_names_and_constraints: impl Iterator<Item = (&'a str, &'a str, Option<DefId>)>,
223+
span_to_replace: Option<Span>,
218224
) -> bool {
219225
let mut grouped = FxHashMap::default();
220226
param_names_and_constraints.for_each(|(param_name, constraint, def_id)| {
@@ -253,7 +259,9 @@ pub fn suggest_constraining_type_params<'a>(
253259
let mut suggest_restrict = |span, bound_list_non_empty| {
254260
suggestions.push((
255261
span,
256-
if bound_list_non_empty {
262+
if span_to_replace.is_some() {
263+
constraint.clone()
264+
} else if bound_list_non_empty {
257265
format!(" + {}", constraint)
258266
} else {
259267
format!(" {}", constraint)
@@ -262,6 +270,11 @@ pub fn suggest_constraining_type_params<'a>(
262270
))
263271
};
264272

273+
if let Some(span) = span_to_replace {
274+
suggest_restrict(span, true);
275+
continue;
276+
}
277+
265278
// When the type parameter has been provided bounds
266279
//
267280
// Message:

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

+2
Original file line numberDiff line numberDiff line change
@@ -679,6 +679,7 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
679679
&param_name,
680680
&constraint,
681681
Some(trait_pred.def_id()),
682+
None,
682683
) {
683684
return;
684685
}
@@ -1087,6 +1088,7 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
10871088
param.name.as_str(),
10881089
"Clone",
10891090
Some(clone_trait),
1091+
None,
10901092
);
10911093
}
10921094
err.span_suggestion_verbose(

tests/ui/associated-types/hr-associated-type-projection-1.stderr

+2-2
Original file line numberDiff line numberDiff line change
@@ -16,8 +16,8 @@ LL | for<'b> <Self as UnsafeCopy<'b, T>>::Item: std::ops::Deref<Target = T>,
1616
| ^^^^^^^^^^ required by this bound in `UnsafeCopy`
1717
help: consider further restricting this bound
1818
|
19-
LL | impl<T: Copy + std::ops::Deref + Deref<Target = T>> UnsafeCopy<'_, T> for T {
20-
| +++++++++++++++++++
19+
LL | impl<T: Copy + std::ops::Deref<Target = T>> UnsafeCopy<'_, T> for T {
20+
| ++++++++++++
2121

2222
error: aborting due to previous error
2323

tests/ui/generic-associated-types/issue-68656-unsized-values.stderr

+2-2
Original file line numberDiff line numberDiff line change
@@ -15,8 +15,8 @@ LL | type Item<'a>: std::ops::Deref<Target = T>;
1515
| ^^^^^^^^^^ required by this bound in `UnsafeCopy::Item`
1616
help: consider further restricting this bound
1717
|
18-
LL | impl<T: Copy + std::ops::Deref + Deref<Target = T>> UnsafeCopy<T> for T {
19-
| +++++++++++++++++++
18+
LL | impl<T: Copy + std::ops::Deref<Target = T>> UnsafeCopy<T> for T {
19+
| ++++++++++++
2020

2121
error: aborting due to previous error
2222

tests/ui/generic-associated-types/missing-bounds.fixed

+3-3
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ use std::ops::Add;
44

55
struct A<B>(B);
66

7-
impl<B> Add for A<B> where B: Add + Add<Output = B> {
7+
impl<B> Add for A<B> where B: Add<Output = B> {
88
type Output = Self;
99

1010
fn add(self, rhs: Self) -> Self {
@@ -14,7 +14,7 @@ impl<B> Add for A<B> where B: Add + Add<Output = B> {
1414

1515
struct C<B>(B);
1616

17-
impl<B: Add + Add<Output = B>> Add for C<B> {
17+
impl<B: Add<Output = B>> Add for C<B> {
1818
type Output = Self;
1919

2020
fn add(self, rhs: Self) -> Self {
@@ -34,7 +34,7 @@ impl<B: std::ops::Add<Output = B>> Add for D<B> {
3434

3535
struct E<B>(B);
3636

37-
impl<B: Add + Add<Output = B>> Add for E<B> where B: Add<Output = B> {
37+
impl<B: Add<Output = B>> Add for E<B> where B: Add<Output = B> {
3838
//~^ ERROR equality constraints are not yet supported in `where` clauses
3939
type Output = Self;
4040

tests/ui/generic-associated-types/missing-bounds.stderr

+6-6
Original file line numberDiff line numberDiff line change
@@ -37,8 +37,8 @@ LL | struct A<B>(B);
3737
| ^
3838
help: consider further restricting this bound
3939
|
40-
LL | impl<B> Add for A<B> where B: Add + Add<Output = B> {
41-
| +++++++++++++++++
40+
LL | impl<B> Add for A<B> where B: Add<Output = B> {
41+
| ++++++++++++
4242

4343
error[E0308]: mismatched types
4444
--> $DIR/missing-bounds.rs:21:14
@@ -60,8 +60,8 @@ LL | struct C<B>(B);
6060
| ^
6161
help: consider further restricting this bound
6262
|
63-
LL | impl<B: Add + Add<Output = B>> Add for C<B> {
64-
| +++++++++++++++++
63+
LL | impl<B: Add<Output = B>> Add for C<B> {
64+
| ++++++++++++
6565

6666
error[E0369]: cannot add `B` to `B`
6767
--> $DIR/missing-bounds.rs:31:21
@@ -96,8 +96,8 @@ LL | struct E<B>(B);
9696
| ^
9797
help: consider further restricting this bound
9898
|
99-
LL | impl<B: Add + Add<Output = B>> Add for E<B> where <B as Add>::Output = B {
100-
| +++++++++++++++++
99+
LL | impl<B: Add<Output = B>> Add for E<B> where <B as Add>::Output = B {
100+
| ++++++++++++
101101

102102
error: aborting due to 5 previous errors
103103

Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
pub trait TryAdd<Rhs = Self> {
2+
type Error;
3+
type Output;
4+
5+
fn try_add(self, rhs: Rhs) -> Result<Self::Output, Self::Error>;
6+
}
7+
8+
impl<T: TryAdd> TryAdd for Option<T> {
9+
type Error = <T as TryAdd>::Error;
10+
type Output = Option<<T as TryAdd>::Output>;
11+
12+
fn try_add(self, rhs: Self) -> Result<Self::Output, Self::Error> {
13+
Ok(self) //~ ERROR mismatched types
14+
}
15+
}
16+
17+
struct Other<A>(A);
18+
19+
struct X;
20+
21+
impl<T: TryAdd<Error = X>> TryAdd for Other<T> {
22+
type Error = <T as TryAdd>::Error;
23+
type Output = Other<<T as TryAdd>::Output>;
24+
25+
fn try_add(self, rhs: Self) -> Result<Self::Output, Self::Error> {
26+
Ok(self) //~ ERROR mismatched types
27+
}
28+
}
29+
30+
fn main() {}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
error[E0308]: mismatched types
2+
--> $DIR/restrict-existing-type-bounds.rs:13:12
3+
|
4+
LL | impl<T: TryAdd> TryAdd for Option<T> {
5+
| - this type parameter
6+
...
7+
LL | Ok(self)
8+
| -- ^^^^ expected `Option<<T as TryAdd>::Output>`, found `Option<T>`
9+
| |
10+
| arguments to this enum variant are incorrect
11+
|
12+
= note: expected enum `Option<<T as TryAdd>::Output>`
13+
found enum `Option<T>`
14+
help: the type constructed contains `Option<T>` due to the type of the argument passed
15+
--> $DIR/restrict-existing-type-bounds.rs:13:9
16+
|
17+
LL | Ok(self)
18+
| ^^^----^
19+
| |
20+
| this argument influences the type of `Ok`
21+
note: tuple variant defined here
22+
--> $SRC_DIR/core/src/result.rs:LL:COL
23+
help: consider further restricting this bound
24+
|
25+
LL | impl<T: TryAdd<Output = T>> TryAdd for Option<T> {
26+
| ++++++++++++
27+
28+
error[E0308]: mismatched types
29+
--> $DIR/restrict-existing-type-bounds.rs:26:12
30+
|
31+
LL | impl<T: TryAdd<Error = X>> TryAdd for Other<T> {
32+
| - this type parameter
33+
...
34+
LL | Ok(self)
35+
| -- ^^^^ expected `Other<<T as TryAdd>::Output>`, found `Other<T>`
36+
| |
37+
| arguments to this enum variant are incorrect
38+
|
39+
= note: expected struct `Other<<T as TryAdd>::Output>`
40+
found struct `Other<T>`
41+
help: the type constructed contains `Other<T>` due to the type of the argument passed
42+
--> $DIR/restrict-existing-type-bounds.rs:26:9
43+
|
44+
LL | Ok(self)
45+
| ^^^----^
46+
| |
47+
| this argument influences the type of `Ok`
48+
note: tuple variant defined here
49+
--> $SRC_DIR/core/src/result.rs:LL:COL
50+
help: consider further restricting this bound
51+
|
52+
LL | impl<T: TryAdd<Error = X, Output = T>> TryAdd for Other<T> {
53+
| ++++++++++++
54+
55+
error: aborting due to 2 previous errors
56+
57+
For more information about this error, try `rustc --explain E0308`.

0 commit comments

Comments
 (0)