Confusing mismatched type error
If you forget to put a semicolon after the last expression in the body of a function that returns no values, rustc will infer that the type of the expression should be (). If the programmer didn't intend to return a value, but merely forgot to type a semicolon, the resulting error can be confusing. Consider, for example, the following toy program:
use std::ptr;
fn main() {
let a = 0;
ptr::read(&a)
}
This produces the following error message:
error[E0308]: mismatched types
--> src/main.rs:5:15
|
5 | ptr::read(&a)
| ^^ expected (), found integral variable
|
= note: expected type `*const ()`
found type `&{integer}`
In this simple example, it may be obvious, but in the somewhat more complicated code I was working with that led me to this issue, I spent a decent amount of time trying to figure out why I was supposedly passing a pointer to () to mem::replace, thus causing the type of the second argument to replace to fail to unify. It took me a while to figure out that this was actually the culprit.
If you forget to put a semicolon after the last expression in the body of a function that returns no values,
rustcwill infer that the type of the expression should be(). If the programmer didn't intend to return a value, but merely forgot to type a semicolon, the resulting error can be confusing. Consider, for example, the following toy program:use std::ptr; fn main() { let a = 0; ptr::read(&a) }This produces the following error message:
error[E0308]: mismatched types --> src/main.rs:5:15 | 5 | ptr::read(&a) | ^^ expected (), found integral variable | = note: expected type `*const ()` found type `&{integer}`In this simple example, it may be obvious, but in the somewhat more complicated code I was working with that led me to this issue, I spent a decent amount of time trying to figure out why I was supposedly passing a pointer to
<!-- TRIAGEBOT_START --> <!-- TRIAGEBOT_ASSIGN_START --> <!-- TRIAGEBOT_ASSIGN_DATA_START$${"user":"sulami"}$$TRIAGEBOT_ASSIGN_DATA_END --> <!-- TRIAGEBOT_ASSIGN_END --> <!-- TRIAGEBOT_END -->()tomem::replace, thus causing the type of the second argument toreplaceto fail to unify. It took me a while to figure out that this was actually the culprit.rustbot at 2023-01-07 12:02:10
Triage: no change.
Esteban Kuber at 2019-09-26 18:25:06
Mentoring notes:
You need to take the
&aexpression, look at its parent to see if it is a method or function call, get its corresponding item return type and see if the return type and the argument that had a type mismatch are both the same type parameter (or really almost the same, to account for things likeTand-> Option<&T>, you can usereferences_typefor this). Once you have that, you can use the existing machinery that points at where type obligations might have come from, like the one that points at the let type in type mismatches for binding assignment.Esteban Kuber at 2023-01-05 19:11:51
I'd like to have a shot at this.
@rustbot claim
Robin Schroer at 2023-01-07 12:02:08
@sulami for an example of a similar case I worked on recently, you can look at https://github.com/rust-lang/rust/pull/106519/files#diff-f1e0bfc4e359c5e1d92ebdc4aafe6dfda183fbdbc9933a3b5e9a7fb274b9d84bR1675-R1717
Esteban Kuber at 2023-01-07 19:14:37
I've pushed a WIP commit and would appreciate some early feedback if this is going in the right direction.
Robin Schroer at 2023-01-08 12:21:05
@sulami added some comments to the commit linked above your comment. Let me know if I was too terse, but the high level idea is you already have the HIR node for the call, which is great. From it you can look at the fields of the node to get the Res::Def if it is available, and with that you can get the original function/method type (I think that
tcx.type_of(def_id)might work for you here, but I'm not sure). Once you have that, you need to check every single input type against the output to see if they are 1) a type parameter and 2) the same. That way you'll be able to tell thatfn foo<T>(x: T) -> T { x }is affected by inference on the argument and the return type. Once we have that the rest should be easy, it's a matter of lining things up and tweaking the output to look the way we want :)Esteban Kuber at 2023-01-08 19:30:35
So I think I've got most things lined up now. I have two questions though:
-
What do we actually want the help message to look like? I just picked something that seemed generally sensible, but I imagine it could be better/more idiomatic somehow.
-
The blast radius of this is actually larger than I expected. It's currently affecting about a dozen ui tests, mostly because something like this is also caught in this:
fn foo(x: Option<Option<u32>>) {} fn main() { foo(Some(42)) }Because
Some(..)is considered a call, the mismatch caught here is in the argument ofSome(), not in the argument offoo(). I'm not sure that should be the case here, but I'm also not quite sure right now how to distinguish this case from the original example provided.
Robin Schroer at 2023-01-11 03:29:06
-
It looks great! Ideally I think that the output would look closer to
error[E0308]: arguments to this function are incorrect --> $DIR/wrong-call-return-type-due-to-generic-arg.rs:27:5 | LL | function(0u32, 8u8) | ^^^^^^^^ ---- --- expected `bool`, found `u8` | | | expected `()`, found `u32` | help: the return type of this call is `u32` due to the type of the argument passed --> $DIR/wrong-call-return-type-due-to-generic-arg.rs:27:5 | LL | function(0u32, 8u8) | ^^^^^^^^^----^^^^^^ | | | this argument determines the return type of `function` note: function defined here --> $DIR/wrong-call-return-type-due-to-generic-arg.rs:1:4 | L | fn function<T, N>(x: T, y: bool) -> T { | ^^^^^^^^ ---- -------You can accomplish that by doing something like
let mut multi_span: MultiSpan = parent_expr.span.into(); multi_span.push_span_label(args[arg_idx].span, format!("this argument influences the return type of `{}`", path.segments[0].ident)); err.span_help(multi_span, ...);You can also look at the
Resof the call to see what item type it resolved to, and only emit these if it is an Assoc Fn or Fn.Esteban Kuber at 2023-01-11 06:12:48
Looks like the error message for the original example is still unchanged even with https://github.com/rust-lang/rust/pull/106752. Is that expected behavior?
Joshua Liebow-Feeser at 2023-08-01 12:24:22