1
Fork 0

Modify existing bounds if they exist

This commit is contained in:
Edward Shen 2023-02-04 17:09:19 -08:00 committed by Edward Shen
parent 044a28a409
commit af5a37e844
No known key found for this signature in database
GPG key ID: D6A2AC5596760EE9
13 changed files with 186 additions and 43 deletions

View file

@ -803,6 +803,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
predicates predicates
.iter() .iter()
.map(|(param, constraint)| (param.name.as_str(), &**constraint, None)), .map(|(param, constraint)| (param.name.as_str(), &**constraint, None)),
None,
); );
} }
} }

View file

@ -136,6 +136,7 @@ impl<'tcx> NonConstOp<'tcx> for FnCallNonConst<'tcx> {
&param_ty.name.as_str(), &param_ty.name.as_str(),
&constraint, &constraint,
None, None,
None,
); );
} }
} }

View file

@ -176,6 +176,7 @@ fn visit_implementation_of_copy(tcx: TyCtxt<'_>, impl_did: LocalDefId) {
bounds.iter().map(|(param, constraint, def_id)| { bounds.iter().map(|(param, constraint, def_id)| {
(param.as_str(), constraint.as_str(), *def_id) (param.as_str(), constraint.as_str(), *def_id)
}), }),
None,
); );
err.emit(); err.emit();
} }

View file

@ -1385,6 +1385,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
generics, generics,
diag, diag,
vec![(param.name.as_str(), "Clone", Some(clone_trait_did))].into_iter(), vec![(param.name.as_str(), "Clone", Some(clone_trait_did))].into_iter(),
None,
); );
} else { } else {
self.suggest_derive(diag, &[(trait_ref.to_predicate(self.tcx), None, None)]); self.suggest_derive(diag, &[(trait_ref.to_predicate(self.tcx), None, None)]);

View file

@ -77,41 +77,62 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> {
(ty::Param(p), ty::Alias(ty::Projection, proj)) | (ty::Alias(ty::Projection, proj), ty::Param(p)) (ty::Param(p), ty::Alias(ty::Projection, proj)) | (ty::Alias(ty::Projection, proj), ty::Param(p))
if tcx.def_kind(proj.def_id) != DefKind::ImplTraitPlaceholder => if tcx.def_kind(proj.def_id) != DefKind::ImplTraitPlaceholder =>
{ {
let generics = tcx.generics_of(body_owner_def_id); let p_def_id = tcx
let p_span = tcx.def_span(generics.type_param(p, tcx).def_id); .generics_of(body_owner_def_id)
.type_param(p, tcx)
.def_id;
let p_span = tcx.def_span(p_def_id);
if !sp.contains(p_span) { if !sp.contains(p_span) {
diag.span_label(p_span, "this type parameter"); diag.span_label(p_span, "this type parameter");
} }
let hir = tcx.hir(); let hir = tcx.hir();
let mut note = true; let mut note = true;
if let Some(generics) = generics let parent = p_def_id
.type_param(p, tcx)
.def_id
.as_local() .as_local()
.map(|id| hir.local_def_id_to_hir_id(id)) .and_then(|id| {
.and_then(|id| tcx.hir().find_parent(id)) let local_id = hir.local_def_id_to_hir_id(id);
.as_ref() let generics = tcx.hir().find_parent(local_id)?.generics()?;
.and_then(|node| node.generics()) Some((id, generics))
});
if let Some((local_id, generics)) = parent
{ {
// Synthesize the associated type restriction `Add<Output = Expected>`. // Synthesize the associated type restriction `Add<Output = Expected>`.
// FIXME: extract this logic for use in other diagnostics. // FIXME: extract this logic for use in other diagnostics.
let (trait_ref, assoc_substs) = proj.trait_ref_and_own_substs(tcx); let (trait_ref, assoc_substs) = proj.trait_ref_and_own_substs(tcx);
let path =
tcx.def_path_str_with_substs(trait_ref.def_id, trait_ref.substs);
let item_name = tcx.item_name(proj.def_id); let item_name = tcx.item_name(proj.def_id);
let item_args = self.format_generic_args(assoc_substs); let item_args = self.format_generic_args(assoc_substs);
let path = if path.ends_with('>') { // Here, we try to see if there's an existing
format!( // trait implementation that matches the one that
"{}, {}{} = {}>", // we're suggesting to restrict. If so, find the
&path[..path.len() - 1], // "end", whether it be at the end of the trait
item_name, // or the end of the generic arguments.
item_args, let mut matching_span = None;
p let mut matched_end_of_args = false;
) for bound in generics.bounds_for_param(local_id) {
} else { let potential_spans = bound
format!("{}<{}{} = {}>", path, item_name, item_args, p) .bounds
}; .iter()
.find_map(|bound| {
let bound_trait_path = bound.trait_ref()?.path;
let def_id = bound_trait_path.res.opt_def_id()?;
let generic_args = bound_trait_path.segments.iter().last().map(|path| path.args());
(def_id == trait_ref.def_id).then_some((bound_trait_path.span, generic_args))
});
if let Some((end_of_trait, end_of_args)) = potential_spans {
let args_span = end_of_args.and_then(|args| args.span());
matched_end_of_args = args_span.is_some();
matching_span = args_span
.or_else(|| Some(end_of_trait))
.map(|span| span.shrink_to_hi());
break;
}
}
if matched_end_of_args {
// Append suggestion to the end of our args
let path = format!(", {}{} = {}",item_name, item_args, p);
note = !suggest_constraining_type_param( note = !suggest_constraining_type_param(
tcx, tcx,
generics, generics,
@ -119,7 +140,23 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> {
&format!("{}", proj.self_ty()), &format!("{}", proj.self_ty()),
&path, &path,
None, None,
matching_span,
); );
} else {
// Suggest adding a bound to an existing trait
// or if the trait doesn't exist, add the trait
// and the suggested bounds.
let path = format!("<{}{} = {}>", item_name, item_args, p);
note = !suggest_constraining_type_param(
tcx,
generics,
diag,
&format!("{}", proj.self_ty()),
&path,
None,
matching_span,
);
}
} }
if note { if note {
diag.note("you might be missing a type parameter or trait bound"); diag.note("you might be missing a type parameter or trait bound");

View file

@ -193,6 +193,9 @@ fn suggest_removing_unsized_bound(
} }
/// Suggest restricting a type param with a new bound. /// Suggest restricting a type param with a new bound.
///
/// If `span_to_replace` is provided, then that span will be replaced with the
/// `constraint`. If one wasn't provided, then the full bound will be suggested.
pub fn suggest_constraining_type_param( pub fn suggest_constraining_type_param(
tcx: TyCtxt<'_>, tcx: TyCtxt<'_>,
generics: &hir::Generics<'_>, generics: &hir::Generics<'_>,
@ -200,12 +203,14 @@ pub fn suggest_constraining_type_param(
param_name: &str, param_name: &str,
constraint: &str, constraint: &str,
def_id: Option<DefId>, def_id: Option<DefId>,
span_to_replace: Option<Span>,
) -> bool { ) -> bool {
suggest_constraining_type_params( suggest_constraining_type_params(
tcx, tcx,
generics, generics,
err, err,
[(param_name, constraint, def_id)].into_iter(), [(param_name, constraint, def_id)].into_iter(),
span_to_replace,
) )
} }
@ -215,6 +220,7 @@ pub fn suggest_constraining_type_params<'a>(
generics: &hir::Generics<'_>, generics: &hir::Generics<'_>,
err: &mut Diagnostic, err: &mut Diagnostic,
param_names_and_constraints: impl Iterator<Item = (&'a str, &'a str, Option<DefId>)>, param_names_and_constraints: impl Iterator<Item = (&'a str, &'a str, Option<DefId>)>,
span_to_replace: Option<Span>,
) -> bool { ) -> bool {
let mut grouped = FxHashMap::default(); let mut grouped = FxHashMap::default();
param_names_and_constraints.for_each(|(param_name, constraint, def_id)| { param_names_and_constraints.for_each(|(param_name, constraint, def_id)| {
@ -253,7 +259,9 @@ pub fn suggest_constraining_type_params<'a>(
let mut suggest_restrict = |span, bound_list_non_empty| { let mut suggest_restrict = |span, bound_list_non_empty| {
suggestions.push(( suggestions.push((
span, span,
if bound_list_non_empty { if span_to_replace.is_some() {
constraint.clone()
} else if bound_list_non_empty {
format!(" + {}", constraint) format!(" + {}", constraint)
} else { } else {
format!(" {}", constraint) format!(" {}", constraint)
@ -262,6 +270,11 @@ pub fn suggest_constraining_type_params<'a>(
)) ))
}; };
if let Some(span) = span_to_replace {
suggest_restrict(span, true);
continue;
}
// When the type parameter has been provided bounds // When the type parameter has been provided bounds
// //
// Message: // Message:

View file

@ -679,6 +679,7 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
&param_name, &param_name,
&constraint, &constraint,
Some(trait_pred.def_id()), Some(trait_pred.def_id()),
None,
) { ) {
return; return;
} }
@ -1087,6 +1088,7 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
param.name.as_str(), param.name.as_str(),
"Clone", "Clone",
Some(clone_trait), Some(clone_trait),
None,
); );
} }
err.span_suggestion_verbose( err.span_suggestion_verbose(

View file

@ -16,8 +16,8 @@ LL | for<'b> <Self as UnsafeCopy<'b, T>>::Item: std::ops::Deref<Target = T>,
| ^^^^^^^^^^ required by this bound in `UnsafeCopy` | ^^^^^^^^^^ required by this bound in `UnsafeCopy`
help: consider further restricting this bound help: consider further restricting this bound
| |
LL | impl<T: Copy + std::ops::Deref + Deref<Target = T>> UnsafeCopy<'_, T> for T { LL | impl<T: Copy + std::ops::Deref<Target = T>> UnsafeCopy<'_, T> for T {
| +++++++++++++++++++ | ++++++++++++
error: aborting due to previous error error: aborting due to previous error

View file

@ -15,8 +15,8 @@ LL | type Item<'a>: std::ops::Deref<Target = T>;
| ^^^^^^^^^^ required by this bound in `UnsafeCopy::Item` | ^^^^^^^^^^ required by this bound in `UnsafeCopy::Item`
help: consider further restricting this bound help: consider further restricting this bound
| |
LL | impl<T: Copy + std::ops::Deref + Deref<Target = T>> UnsafeCopy<T> for T { LL | impl<T: Copy + std::ops::Deref<Target = T>> UnsafeCopy<T> for T {
| +++++++++++++++++++ | ++++++++++++
error: aborting due to previous error error: aborting due to previous error

View file

@ -4,7 +4,7 @@ use std::ops::Add;
struct A<B>(B); struct A<B>(B);
impl<B> Add for A<B> where B: Add + Add<Output = B> { impl<B> Add for A<B> where B: Add<Output = B> {
type Output = Self; type Output = Self;
fn add(self, rhs: Self) -> Self { fn add(self, rhs: Self) -> Self {
@ -14,7 +14,7 @@ impl<B> Add for A<B> where B: Add + Add<Output = B> {
struct C<B>(B); struct C<B>(B);
impl<B: Add + Add<Output = B>> Add for C<B> { impl<B: Add<Output = B>> Add for C<B> {
type Output = Self; type Output = Self;
fn add(self, rhs: Self) -> Self { fn add(self, rhs: Self) -> Self {
@ -34,7 +34,7 @@ impl<B: std::ops::Add<Output = B>> Add for D<B> {
struct E<B>(B); struct E<B>(B);
impl<B: Add + Add<Output = B>> Add for E<B> where B: Add<Output = B> { impl<B: Add<Output = B>> Add for E<B> where B: Add<Output = B> {
//~^ ERROR equality constraints are not yet supported in `where` clauses //~^ ERROR equality constraints are not yet supported in `where` clauses
type Output = Self; type Output = Self;

View file

@ -37,8 +37,8 @@ LL | struct A<B>(B);
| ^ | ^
help: consider further restricting this bound help: consider further restricting this bound
| |
LL | impl<B> Add for A<B> where B: Add + Add<Output = B> { LL | impl<B> Add for A<B> where B: Add<Output = B> {
| +++++++++++++++++ | ++++++++++++
error[E0308]: mismatched types error[E0308]: mismatched types
--> $DIR/missing-bounds.rs:21:14 --> $DIR/missing-bounds.rs:21:14
@ -60,8 +60,8 @@ LL | struct C<B>(B);
| ^ | ^
help: consider further restricting this bound help: consider further restricting this bound
| |
LL | impl<B: Add + Add<Output = B>> Add for C<B> { LL | impl<B: Add<Output = B>> Add for C<B> {
| +++++++++++++++++ | ++++++++++++
error[E0369]: cannot add `B` to `B` error[E0369]: cannot add `B` to `B`
--> $DIR/missing-bounds.rs:31:21 --> $DIR/missing-bounds.rs:31:21
@ -96,8 +96,8 @@ LL | struct E<B>(B);
| ^ | ^
help: consider further restricting this bound help: consider further restricting this bound
| |
LL | impl<B: Add + Add<Output = B>> Add for E<B> where <B as Add>::Output = B { LL | impl<B: Add<Output = B>> Add for E<B> where <B as Add>::Output = B {
| +++++++++++++++++ | ++++++++++++
error: aborting due to 5 previous errors error: aborting due to 5 previous errors

View file

@ -0,0 +1,30 @@
pub trait TryAdd<Rhs = Self> {
type Error;
type Output;
fn try_add(self, rhs: Rhs) -> Result<Self::Output, Self::Error>;
}
impl<T: TryAdd> TryAdd for Option<T> {
type Error = <T as TryAdd>::Error;
type Output = Option<<T as TryAdd>::Output>;
fn try_add(self, rhs: Self) -> Result<Self::Output, Self::Error> {
Ok(self) //~ ERROR mismatched types
}
}
struct Other<A>(A);
struct X;
impl<T: TryAdd<Error = X>> TryAdd for Other<T> {
type Error = <T as TryAdd>::Error;
type Output = Other<<T as TryAdd>::Output>;
fn try_add(self, rhs: Self) -> Result<Self::Output, Self::Error> {
Ok(self) //~ ERROR mismatched types
}
}
fn main() {}

View file

@ -0,0 +1,57 @@
error[E0308]: mismatched types
--> $DIR/restrict-existing-type-bounds.rs:13:12
|
LL | impl<T: TryAdd> TryAdd for Option<T> {
| - this type parameter
...
LL | Ok(self)
| -- ^^^^ expected `Option<<T as TryAdd>::Output>`, found `Option<T>`
| |
| arguments to this enum variant are incorrect
|
= note: expected enum `Option<<T as TryAdd>::Output>`
found enum `Option<T>`
help: the type constructed contains `Option<T>` due to the type of the argument passed
--> $DIR/restrict-existing-type-bounds.rs:13:9
|
LL | Ok(self)
| ^^^----^
| |
| this argument influences the type of `Ok`
note: tuple variant defined here
--> $SRC_DIR/core/src/result.rs:LL:COL
help: consider further restricting this bound
|
LL | impl<T: TryAdd<Output = T>> TryAdd for Option<T> {
| ++++++++++++
error[E0308]: mismatched types
--> $DIR/restrict-existing-type-bounds.rs:26:12
|
LL | impl<T: TryAdd<Error = X>> TryAdd for Other<T> {
| - this type parameter
...
LL | Ok(self)
| -- ^^^^ expected `Other<<T as TryAdd>::Output>`, found `Other<T>`
| |
| arguments to this enum variant are incorrect
|
= note: expected struct `Other<<T as TryAdd>::Output>`
found struct `Other<T>`
help: the type constructed contains `Other<T>` due to the type of the argument passed
--> $DIR/restrict-existing-type-bounds.rs:26:9
|
LL | Ok(self)
| ^^^----^
| |
| this argument influences the type of `Ok`
note: tuple variant defined here
--> $SRC_DIR/core/src/result.rs:LL:COL
help: consider further restricting this bound
|
LL | impl<T: TryAdd<Error = X, Output = T>> TryAdd for Other<T> {
| ++++++++++++
error: aborting due to 2 previous errors
For more information about this error, try `rustc --explain E0308`.