-
Notifications
You must be signed in to change notification settings - Fork 13.4k
Account for type parameters in bound suggestion #134937
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: master
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change | ||||||||||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
@@ -33,8 +33,9 @@ use rustc_middle::ty::print::{ | |||||||||||||||||||||
}; | ||||||||||||||||||||||
use rustc_middle::ty::{ | ||||||||||||||||||||||
self, AdtKind, GenericArgs, InferTy, IsSuggestable, ToPolyTraitRef, Ty, TyCtxt, TypeFoldable, | ||||||||||||||||||||||
TypeFolder, TypeSuperFoldable, TypeVisitableExt, TypeckResults, Upcast, | ||||||||||||||||||||||
suggest_arbitrary_trait_bound, suggest_constraining_type_param, | ||||||||||||||||||||||
TypeFolder, TypeSuperFoldable, TypeSuperVisitable, TypeVisitableExt, TypeVisitor, | ||||||||||||||||||||||
TypeckResults, Upcast, UpcastFrom, suggest_arbitrary_trait_bound, | ||||||||||||||||||||||
suggest_constraining_type_param, | ||||||||||||||||||||||
}; | ||||||||||||||||||||||
use rustc_middle::{bug, span_bug}; | ||||||||||||||||||||||
use rustc_span::def_id::LocalDefId; | ||||||||||||||||||||||
|
@@ -260,6 +261,11 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { | |||||||||||||||||||||
_ => (false, None), | ||||||||||||||||||||||
}; | ||||||||||||||||||||||
|
||||||||||||||||||||||
let mut v = ParamFinder { params: vec![] }; | ||||||||||||||||||||||
// Get all type parameters from the predicate. If the predicate references a type parameter | ||||||||||||||||||||||
// at all, then we can only suggestion a bound on an item that has access to that parameter. | ||||||||||||||||||||||
v.visit_predicate(UpcastFrom::upcast_from(trait_pred, self.tcx)); | ||||||||||||||||||||||
|
||||||||||||||||||||||
// FIXME: Add check for trait bound that is already present, particularly `?Sized` so we | ||||||||||||||||||||||
// don't suggest `T: Sized + ?Sized`. | ||||||||||||||||||||||
loop { | ||||||||||||||||||||||
|
@@ -327,6 +333,40 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { | |||||||||||||||||||||
); | ||||||||||||||||||||||
return; | ||||||||||||||||||||||
} | ||||||||||||||||||||||
|
||||||||||||||||||||||
hir::Node::TraitItem(hir::TraitItem { | ||||||||||||||||||||||
generics, | ||||||||||||||||||||||
kind: hir::TraitItemKind::Fn(fn_sig, ..), | ||||||||||||||||||||||
.. | ||||||||||||||||||||||
}) | ||||||||||||||||||||||
| hir::Node::ImplItem(hir::ImplItem { | ||||||||||||||||||||||
generics, | ||||||||||||||||||||||
kind: hir::ImplItemKind::Fn(fn_sig, ..), | ||||||||||||||||||||||
.. | ||||||||||||||||||||||
}) if projection.is_none() | ||||||||||||||||||||||
&& !param_ty | ||||||||||||||||||||||
&& generics.params.iter().any(|param| { | ||||||||||||||||||||||
v.params.iter().any(|p| p.name == param.name.ident().name) | ||||||||||||||||||||||
}) => | ||||||||||||||||||||||
{ | ||||||||||||||||||||||
// This associated function has a generic type parameter that matches a | ||||||||||||||||||||||
// parameter from the trait predicate, which means that we should suggest | ||||||||||||||||||||||
// constraining the complex type here, and not at the trait/impl level (if | ||||||||||||||||||||||
// it doesn't have that type parameter). This can be something like | ||||||||||||||||||||||
// `Type: From<Param>`. | ||||||||||||||||||||||
suggest_restriction( | ||||||||||||||||||||||
self.tcx, | ||||||||||||||||||||||
body_id, | ||||||||||||||||||||||
generics, | ||||||||||||||||||||||
"the type", | ||||||||||||||||||||||
err, | ||||||||||||||||||||||
Some(fn_sig), | ||||||||||||||||||||||
projection, | ||||||||||||||||||||||
trait_pred, | ||||||||||||||||||||||
None, | ||||||||||||||||||||||
); | ||||||||||||||||||||||
} | ||||||||||||||||||||||
|
||||||||||||||||||||||
hir::Node::Item(hir::Item { | ||||||||||||||||||||||
kind: | ||||||||||||||||||||||
hir::ItemKind::Trait(_, _, generics, ..) | ||||||||||||||||||||||
|
@@ -425,7 +465,11 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { | |||||||||||||||||||||
| hir::ItemKind::Const(_, generics, _) | ||||||||||||||||||||||
| hir::ItemKind::TraitAlias(generics, _), | ||||||||||||||||||||||
.. | ||||||||||||||||||||||
}) if !param_ty => { | ||||||||||||||||||||||
}) if !param_ty | ||||||||||||||||||||||
&& (generics.params.iter().any(|param| { | ||||||||||||||||||||||
v.params.iter().any(|p| p.name == param.name.ident().name) | ||||||||||||||||||||||
}) || v.params.is_empty()) => | ||||||||||||||||||||||
{ | ||||||||||||||||||||||
Comment on lines
+468
to
+472
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Don't you want this?
Suggested change
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I'm confused by the logic: what's a suggestion you're trying to avoid emitting by adding this condition? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. We currently suggest
Which is incorrect because the
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. With the logic as you've written it, it seems like the incorrect suggestion would be emitted because |
||||||||||||||||||||||
// Missing generic type parameter bound. | ||||||||||||||||||||||
if suggest_arbitrary_trait_bound( | ||||||||||||||||||||||
self.tcx, | ||||||||||||||||||||||
|
@@ -5439,6 +5483,23 @@ fn get_deref_type_and_refs(mut ty: Ty<'_>) -> (Ty<'_>, Vec<hir::Mutability>) { | |||||||||||||||||||||
(ty, refs) | ||||||||||||||||||||||
} | ||||||||||||||||||||||
|
||||||||||||||||||||||
/// Look for type parameters. | ||||||||||||||||||||||
struct ParamFinder { | ||||||||||||||||||||||
params: Vec<ty::ParamTy>, | ||||||||||||||||||||||
} | ||||||||||||||||||||||
|
||||||||||||||||||||||
impl<'tcx> TypeVisitor<TyCtxt<'tcx>> for ParamFinder { | ||||||||||||||||||||||
fn visit_ty(&mut self, t: Ty<'tcx>) { | ||||||||||||||||||||||
match t.kind() { | ||||||||||||||||||||||
ty::Param(param) if param.name != kw::SelfUpper => { | ||||||||||||||||||||||
self.params.push(*param); | ||||||||||||||||||||||
} | ||||||||||||||||||||||
_ => {} | ||||||||||||||||||||||
} | ||||||||||||||||||||||
t.super_visit_with(self) | ||||||||||||||||||||||
} | ||||||||||||||||||||||
} | ||||||||||||||||||||||
|
||||||||||||||||||||||
/// Look for type `param` in an ADT being used only through a reference to confirm that suggesting | ||||||||||||||||||||||
/// `param: ?Sized` would be a valid constraint. | ||||||||||||||||||||||
struct FindTypeParam { | ||||||||||||||||||||||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,31 @@ | ||
//@ run-rustfix | ||
|
||
#[derive(Clone)] | ||
struct A { | ||
x: String | ||
} | ||
|
||
struct B { | ||
x: String | ||
} | ||
|
||
impl From<A> for B { | ||
fn from(a: A) -> Self { | ||
B { x: a.x } | ||
} | ||
} | ||
|
||
impl B { | ||
pub fn from_many<T: Into<B> + Clone>(v: Vec<T>) -> Self where B: From<T> { | ||
B { x: v.iter().map(|e| B::from(e.clone()).x).collect::<Vec<String>>().join(" ") } | ||
//~^ ERROR the trait bound `B: From<T>` is not satisfied | ||
} | ||
} | ||
|
||
fn main() { | ||
let _b: B = B { x: "foobar".to_string() }; | ||
let a: A = A { x: "frob".to_string() }; | ||
let ab: B = a.into(); | ||
println!("Hello, {}!", ab.x); | ||
let _c: B = B::from_many(vec![A { x: "x".to_string() }]); | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,31 @@ | ||
//@ run-rustfix | ||
|
||
#[derive(Clone)] | ||
struct A { | ||
x: String | ||
} | ||
|
||
struct B { | ||
x: String | ||
} | ||
|
||
impl From<A> for B { | ||
fn from(a: A) -> Self { | ||
B { x: a.x } | ||
} | ||
} | ||
|
||
impl B { | ||
pub fn from_many<T: Into<B> + Clone>(v: Vec<T>) -> Self { | ||
B { x: v.iter().map(|e| B::from(e.clone()).x).collect::<Vec<String>>().join(" ") } | ||
//~^ ERROR the trait bound `B: From<T>` is not satisfied | ||
} | ||
} | ||
|
||
fn main() { | ||
let _b: B = B { x: "foobar".to_string() }; | ||
let a: A = A { x: "frob".to_string() }; | ||
let ab: B = a.into(); | ||
println!("Hello, {}!", ab.x); | ||
let _c: B = B::from_many(vec![A { x: "x".to_string() }]); | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,14 @@ | ||
error[E0277]: the trait bound `B: From<T>` is not satisfied | ||
--> $DIR/suggest-restriction-involving-type-param.rs:20:33 | ||
| | ||
LL | B { x: v.iter().map(|e| B::from(e.clone()).x).collect::<Vec<String>>().join(" ") } | ||
| ^ the trait `From<T>` is not implemented for `B` | ||
| | ||
help: consider further restricting the type | ||
| | ||
LL | pub fn from_many<T: Into<B> + Clone>(v: Vec<T>) -> Self where B: From<T> { | ||
| ++++++++++++++++ | ||
|
||
error: aborting due to 1 previous error | ||
|
||
For more information about this error, try `rustc --explain E0277`. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
just use
.upcast()
.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Also, what do you mean? We should never be suggesting a bound involving a parameter that we don't have "access to" --we shouldn't be checking predicates involving parameters that aren't in scope anyways?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We currently have a fallback where regardless of what the predicate that failed, we end up suggesting a bound (with weasel wording that there might be a better way). This is in place because there are some complex (if uncommon) bounds that are still useful to have that were given only by that. But in #104089, we had a report of that fallback doing the wrong thing (obvious in retrospect). The predicate might be something like
Self: From<T>
, where theT
is a param only in the associated item and not at thetrait
/impl
level. That's what this additional check guards against: If any part of the predicate references one of the generics of that item, we don't skip suggesting the bound at that level because it would be invalid in the parent item.