-
Notifications
You must be signed in to change notification settings - Fork 12.8k
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
Point at method call when type annotations are needed #65951
Conversation
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
abb0f56
to
11b772d
Compare
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.
WDYT @estebank?
11b772d
to
f468ef3
Compare
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
@estebank are you blocked here looking for advice? |
This comment has been minimized.
This comment has been minimized.
f468ef3
to
9d6ad88
Compare
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
9d6ad88
to
d8528d0
Compare
☀️ Try build successful - checks-azure |
Queued f6cbf0379bcde7c56241c313eafb438da272e069 with parent 7dbfb0a, future comparison URL. |
Finished benchmarking try commit f6cbf0379bcde7c56241c313eafb438da272e069, comparison URL. |
I don't know how noisy the Max-RSS metrics are, but the worst impact was ~16%, but it seems inside of the noise bands (given the ~12% improvement elsewhere). |
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.
OK, this is looking pretty good, I don't think we need the new table though. I left some ideas for improvements but I don't think they're really necessary. I'd be game to r+ if we can remove the extra table especially.
| ^^^^^^^ | ||
| | | ||
| cannot infer type | ||
| help: consider specifying the type argument in the method call: `collect::<B>` |
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.
I'm a bit unsure about introducing the letter B
here. It could be that the name happens to shadow a name that's already in scope for the user, which might be confusing. I wonder if we could write something like collect::<_>
or is that potentially more confusing? (Especially as it would not be especially helpful if they were to write that exact thing, though the same applies to B
.)
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.
I think the only way to improve on this is not to use _
but rather to attempt to give a reasonable type that could fit there, but that would be out of scope for this PR. My main objective here is to 1) bring attention to the fact that this method has a type argument and 2) what the turbofish looks like.
LL | lst.sort_by_key(|&(v, _)| v.iter().sum()); | ||
| ^^^^^^^^^^^ --- help: consider specifying the type argument in the method call: `sum::<S>` | ||
| | | ||
| cannot infer type for `K` |
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.
This is pre-existing, but I wonder if we could say
LL | lst.sort_by_key(|&(v, _)| v.iter().sum());
| ^^^^^^^^^^^ --- help: consider specifying the type argument in the method call: `sum::<S>`
| |
| cannot infer type for type parameter `K` declared on `sort_by_key`
or something like that?
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.
I know, it's kind of tricky to do it right at the moment, but the slight rewording could be tackled (mentioning sort_by_key might be much harder)
let method_defs = borrow.node_method_def_id(); | ||
if let Some(did) = method_defs.get(e.hir_id) { | ||
let generics = self.tcx.generics_of(*did); | ||
if !generics.params.is_empty() { |
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.
So I was debating if this condition is sufficient. I decided it probably is, though we could also choose to be more selective.
One thing is that there won't always be a direct connection between the type parameters and the return type. e.g. it might be
fn foo<T: Iterator>() -> T::Item
but, then again, it's still true that specifying T
would help to figure out the Item
type.
Another is that there could be many parameters.
I could imagine searching through the substitutions for the call to detect cases (like collect
) where the return type is directly linked to a parameter and limiting to that case, or highlighting which parameter specifies the return type in the case that there are many (e.g., maybe writing "try writing out the return type X
, as in foo.bar::<_, _, X>()
"
But it's probably "good enough" the way it is, tbh, those seem like "potential improvements". (And the best phrasing is sort of unclear anyway.)
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.
One thing I did wonder is if we want to be careful around "defaults", though they are currently being phased out on functions:
fn foo<T, U = u64>()...
you could imagine not showing U
in our suggestions. But nobody quite knows what the semantics of said defaults should be so it's probably "ok as is".
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.
error: defaults for type parameters are only allowed in `struct`, `enum`, `type`, or `trait` definitions.
--> file12.rs:1:11
|
1 | fn foo<T, U = u64>() -> T {
| ^
|
= note: `#[deny(invalid_type_param_default)]` on by default
= warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
= note: for more information, see issue #36887 <https://github.com/rust-lang/rust/issues/36887>
with #[allow(invalid_type_param_default)]
we do something more reasonable already IMO:
error[E0282]: type annotations needed
--> file12.rs:8:13
|
8 | let _ = foo();
| - ^^^ cannot infer type for `T`
| |
| consider giving this pattern a type
For the Iterator::Item
case, the output is not ideal, but not any worse than it already is:
error[E0282]: type annotations needed
--> file12.rs:8:13
|
8 | let _ = foo();
| ^^^ cannot infer type for `T`
error[E0284]: type annotations needed
--> file12.rs:13:15
|
13 | let _ = S.foo();
| ^^^ cannot infer type for `T`
|
= note: cannot resolve `<_ as std::iter::Iterator>::Item == _`
I'm inclined at leaving things as they are for now.
The last case is because we have a visitor looking at the method calls for something that resolves to T
, but what we should be looking is a method call that resolves to <T as std::iter::Iterator>::Item
instead (for this case).
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.
Another interesting case:
trait T {
type A;
fn foo(&self) -> Self::A {
panic!()
}
}
struct S<X>(std::marker::PhantomData<X>);
impl<X> T for S<X> {
type A = X;
}
fn main() {
S(std::marker::PhantomData).foo();
}
error[E0282]: type annotations needed
--> file12.rs:14:5
|
2 | type A;
| ------- `<Self as T>::A` defined here
...
14 | S(std::marker::PhantomData).foo();
| ^--------------------------------
| |
| this method call resolves to `<Self as T>::A`
| cannot infer type for type parameter `X`
@nikomatsakis I would call this ready to be merged. |
@@ -8,7 +8,7 @@ async fn bar<T>() -> () {} | |||
async fn foo() { | |||
bar().await; | |||
//~^ ERROR type inside `async fn` body must be known in this context | |||
//~| NOTE cannot infer type for `T` | |||
//~| NOTE cannot infer type for type parameter `T` |
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.
@estebank do you think it's possible/desirable for us to say "type parameter T
declared on bar
" or something like that?
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.
I think it is desirable, but not trivial to accomplish at the moment. The only way I can see of relating a type param and where it came from is with judicious use of a visitor, which I believe will require enough tweaking that would push this PR to not land in the next beta. I can certainly follow up on that. Note that neither this change or that one will address the cases where we say "cannot infer type 🤷" and nothing else.
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.
I'm not going to block this PR over this, because I think it's an orthogonal issue, but I think this is how we would make the change I am talking about.
impl<'a, 'tcx> InferCtxt<'a, 'tcx> { | ||
pub fn extract_type_name( | ||
&self, | ||
ty: Ty<'tcx>, | ||
highlight: Option<ty::print::RegionHighlightMode>, | ||
) -> (String, Option<Span>) { | ||
) -> (String, Option<Span>, Cow<'static, str>) { | ||
if let ty::Infer(ty::TyVar(ty_vid)) = ty.kind { | ||
let ty_vars = self.type_variables.borrow(); | ||
let var_origin = ty_vars.var_origin(ty_vid); | ||
if let TypeVariableOriginKind::TypeParameterDefinition(name) = var_origin.kind { |
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.
I think @estebank if we wanted to say more about where the type parameter came from, we would augment this enum with the DefId
of the parameter instead of its name (I imagine we can extract the name -- as well as the context -- from that DefId
)
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.
Filed #67277 to track this
@bors r+ |
📌 Commit da023c0 has been approved by |
☀️ Test successful - checks-azure |
…type-parameter, r=estebank Indicate origin of where type parameter for uninferred types Based on rust-lang#65951 (which is not merge yet), fixes rust-lang#67277. This PR improves a little the diagnostic for code like: ``` async fn foo() { bar().await; } async fn bar<T>() -> () {} ``` by showing: ``` error[E0698]: type inside `async fn` body must be known in this context --> unresolved_type_param.rs:9:5 | 9 | bar().await; | ^^^ cannot infer type for type parameter `T` declared on the function `bar` | ... ``` (The ``` declared on the function `bar` ``` part is new) A small side note: `Vec` and `slice` seem to resist this change, because querying `item_name()` panics, and `get_opt_name()` returns `None`. r? @estebank
…type-parameter, r=estebank Indicate origin of where type parameter for uninferred types Based on rust-lang#65951 (which is not merge yet), fixes rust-lang#67277. This PR improves a little the diagnostic for code like: ``` async fn foo() { bar().await; } async fn bar<T>() -> () {} ``` by showing: ``` error[E0698]: type inside `async fn` body must be known in this context --> unresolved_type_param.rs:9:5 | 9 | bar().await; | ^^^ cannot infer type for type parameter `T` declared on the function `bar` | ... ``` (The ``` declared on the function `bar` ``` part is new) A small side note: `Vec` and `slice` seem to resist this change, because querying `item_name()` panics, and `get_opt_name()` returns `None`. r? @estebank
…type-parameter, r=estebank Indicate origin of where type parameter for uninferred types Based on rust-lang#65951 (which is not merge yet), fixes rust-lang#67277. This PR improves a little the diagnostic for code like: ``` async fn foo() { bar().await; } async fn bar<T>() -> () {} ``` by showing: ``` error[E0698]: type inside `async fn` body must be known in this context --> unresolved_type_param.rs:9:5 | 9 | bar().await; | ^^^ cannot infer type for type parameter `T` declared on the function `bar` | ... ``` (The ``` declared on the function `bar` ``` part is new) A small side note: `Vec` and `slice` seem to resist this change, because querying `item_name()` panics, and `get_opt_name()` returns `None`. r? @estebank
…type-parameter, r=estebank Indicate origin of where type parameter for uninferred types Based on rust-lang#65951 (which is not merge yet), fixes rust-lang#67277. This PR improves a little the diagnostic for code like: ``` async fn foo() { bar().await; } async fn bar<T>() -> () {} ``` by showing: ``` error[E0698]: type inside `async fn` body must be known in this context --> unresolved_type_param.rs:9:5 | 9 | bar().await; | ^^^ cannot infer type for type parameter `T` declared on the function `bar` | ... ``` (The ``` declared on the function `bar` ``` part is new) A small side note: `Vec` and `slice` seem to resist this change, because querying `item_name()` panics, and `get_opt_name()` returns `None`. r? @estebank
Fix #49391, fix #46333, fix #48089. CC #58517, #63502, #63082.
Fixes #40015
r? @nikomatsakis