Skip to content
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

have simpler diagnostic when passing arg to closure and missing borrow #64915

Closed
matthiaskrgr opened this issue Sep 30, 2019 · 12 comments · Fixed by #102813
Closed

have simpler diagnostic when passing arg to closure and missing borrow #64915

matthiaskrgr opened this issue Sep 30, 2019 · 12 comments · Fixed by #102813
Assignees
Labels
A-closures Area: Closures (`|…| { … }`) A-diagnostics Area: Messages for errors, warnings, and lints A-suggestion-diagnostics Area: Suggestions generated by the compiler applied by `cargo fix` C-enhancement Category: An issue proposing an enhancement or a PR with one. E-mentor Call for participation: This issue has a mentor. Use #t-compiler/help on Zulip for discussion. T-compiler Relevant to the compiler team, which will review and decide on the PR/issue.

Comments

@matthiaskrgr
Copy link
Member

matthiaskrgr commented Sep 30, 2019

playground: https://play.rust-lang.org/?version=stable&mode=debug&edition=2018&gist=9f1e00046f513d4d9da8790042e52ca9

Code:

struct MyStruct {
    age: u32
}

fn run<F: Fn(&MyStruct) -> bool>(func: F, data: &MyStruct) -> bool {
  func(&data)
} 

fn main() {
    let m = MyStruct { age: 4};
    let x = run(|x:MyStruct| x.age > 3, &m);
    println!("{}", x);
}

The error I got was

   Compiling playground v0.0.1 (/playground)
error[E0631]: type mismatch in closure arguments
  --> src/main.rs:12:13
   |
6  | fn run<F: Fn(&MyStruct) -> bool>(func: F, data: &MyStruct) -> bool {
   |    ---    --------------------- required by this bound in `run`
...
12 |     let x = run(|x:MyStruct| x.age > 3, &m);
   |             ^^^ ---------------------- found signature of `fn(MyStruct) -> _`
   |             |
   |             expected signature of `for<'r> fn(&'r MyStruct) -> _`

which is quite convoluted in my opinion.
It turned out I was only missing a borrow:
let x = run(|x:MyStruct| x.age > 3, &m);
=>
let x = run(|x:&MyStruct| x.age > 3, &m);

A simple "hint: add missing borrow: "&MyStruct"" with corresponding span would have been a lot more helpful here in my opinion.

@jonas-schievink jonas-schievink added A-closures Area: Closures (`|…| { … }`) A-diagnostics Area: Messages for errors, warnings, and lints C-enhancement Category: An issue proposing an enhancement or a PR with one. T-compiler Relevant to the compiler team, which will review and decide on the PR/issue. labels Sep 30, 2019
@estebank estebank added the A-suggestion-diagnostics Area: Suggestions generated by the compiler applied by `cargo fix` label Sep 30, 2019
@estebank
Copy link
Contributor

estebank commented Oct 1, 2019

The following function needs to be changed to inspect and compare the expected and found argument and return types and modify err to be more targeted.

fn report_closure_arg_mismatch(
&self,
span: Span,
found_span: Option<Span>,
expected_ref: ty::PolyTraitRef<'tcx>,
found: ty::PolyTraitRef<'tcx>,
) -> DiagnosticBuilder<'tcx> {
fn build_fn_sig_string<'tcx>(tcx: TyCtxt<'tcx>, trait_ref: &ty::TraitRef<'tcx>) -> String {
let inputs = trait_ref.substs.type_at(1);
let sig = if let ty::Tuple(inputs) = inputs.kind {
tcx.mk_fn_sig(
inputs.iter().map(|k| k.expect_ty()),
tcx.mk_ty_infer(ty::TyVar(ty::TyVid { index: 0 })),
false,
hir::Unsafety::Normal,
::rustc_target::spec::abi::Abi::Rust
)
} else {
tcx.mk_fn_sig(
::std::iter::once(inputs),
tcx.mk_ty_infer(ty::TyVar(ty::TyVid { index: 0 })),
false,
hir::Unsafety::Normal,
::rustc_target::spec::abi::Abi::Rust
)
};
ty::Binder::bind(sig).to_string()
}
let argument_is_closure = expected_ref.skip_binder().substs.type_at(0).is_closure();
let mut err = struct_span_err!(self.tcx.sess, span, E0631,
"type mismatch in {} arguments",
if argument_is_closure { "closure" } else { "function" });
let found_str = format!(
"expected signature of `{}`",
build_fn_sig_string(self.tcx, found.skip_binder())
);
err.span_label(span, found_str);
let found_span = found_span.unwrap_or(span);
let expected_str = format!(
"found signature of `{}`",
build_fn_sig_string(self.tcx, expected_ref.skip_binder())
);
err.span_label(found_span, expected_str);
err
}
}

Sadly at that point you don't have access to the closure expression, but you get it using self.tcx.hir().get_if_local(found_did.unwrap()) here

let found_did = match found_trait_ty.kind {
ty::Closure(did, _) | ty::Foreign(did) | ty::FnDef(did, _) => Some(did),
ty::Adt(def, _) => Some(def.did),
_ => None,
};

With that then you could extract the appropriate span to suggest a code change using err.span_suggestion.

@estebank estebank added the E-mentor Call for participation: This issue has a mentor. Use #t-compiler/help on Zulip for discussion. label Oct 1, 2019
@workingjubilee
Copy link
Member

I would be happy to work on implementing this, as it seems like a wonderful opportunity to get into the span code.

@estebank
Copy link
Contributor

estebank commented Oct 1, 2019

Feel free to reach out!

@Quantumplation
Copy link
Contributor

@rustbot claim

It's been a while since @workingjubilee expressed interest, so I'm going to take a crack at this!

@estebank Trying to understand the function you pointed out, and the variable naming has me all twisted around. Are the variable names for found_str and expected_str swapped, or is there some subtlety I'm missing?

@rustbot rustbot self-assigned this Oct 26, 2019
@estebank
Copy link
Contributor

@Quantumplation I believe they are just incorrectly swapped.

@workingjubilee
Copy link
Member

workingjubilee commented Oct 26, 2019

Ah, I didn't realize I should have formally claimed this mechanically.

I had been delayed a bit, but I have been trying to get into things the past few nights.

@Quantumplation
Copy link
Contributor

@workingjubilee ah! feel free to claim it from me then! Also happy to collaborate, if you want to find me on Discord!

@workingjubilee
Copy link
Member

Shiver me timbers, it's piracy on the Rust Sea!!!

@rustbot claim

@rustbot rustbot assigned rustbot and unassigned rustbot Oct 26, 2019
@JohnTitor
Copy link
Member

Triage: I'm going to release assignment due to inactivity.
@workingjubilee If you're still interested in this, feel free to re-claim.
@rustbot release-assignment

@rustbot rustbot removed their assignment Jul 24, 2020
@krupitskas
Copy link

krupitskas commented Aug 18, 2020

Working on it.
@rustbot claim

@krupitskas
Copy link

Still can't find a free dedicated time to sit, read docs and finish it. Some hints can be found in closed pull requests.

@rustbot release-assignment

@Akida31
Copy link
Contributor

Akida31 commented Oct 8, 2022

@rustbot claim

Manishearth added a commit to Manishearth/rust that referenced this issue Nov 11, 2022
…stic_when_passing_arg_to_closure_and_missing_borrow, r=estebank

Simpler diagnostic when passing arg to closure and missing borrow

fixes rust-lang#64915

I followed roughly the instructions and the older PR rust-lang#76362.
The number of references for the expected and the found types will be compared and depending on which has more the diagnostic will be emitted.

I'm not quite sure if my approach with the many `span_bug!`s is good, it could lead to some ICEs. Would it be better if  those errors are ignored?

As far as I know the following code works similarly but in a different context. Is this probably reusable since it looks like it would emit better diagnostics?
https://github.com/rust-lang/rust/blob/a688a0305fad9219505a8f2576446510601bafe8/compiler/rustc_hir_analysis/src/check/demand.rs#L713-L1061

When running the tests locally, a codegen test failed. Is there something I can/ should do about that?

If you have some improvements/ corrections please say so and I will happily include them.

r? `@estebank` (as you added the mentoring instructions to the issue)
@bors bors closed this as completed in 0f529f0 Dec 13, 2022
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
A-closures Area: Closures (`|…| { … }`) A-diagnostics Area: Messages for errors, warnings, and lints A-suggestion-diagnostics Area: Suggestions generated by the compiler applied by `cargo fix` C-enhancement Category: An issue proposing an enhancement or a PR with one. E-mentor Call for participation: This issue has a mentor. Use #t-compiler/help on Zulip for discussion. T-compiler Relevant to the compiler team, which will review and decide on the PR/issue.
Projects
None yet
Development

Successfully merging a pull request may close this issue.

9 participants