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

On type errors, show closure signature #46350

Closed
wants to merge 2 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 7 additions & 2 deletions src/librustc/hir/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1440,8 +1440,13 @@ pub struct Ty {

impl fmt::Debug for Ty {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "type({})",
print::to_string(print::NO_ANN, |s| s.print_type(self)))
write!(f, "type({})", print::to_string(print::NO_ANN, |s| s.print_type(self)))
}
}

impl fmt::Display for Ty {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{}", print::to_string(print::NO_ANN, |s| s.print_type(self)))
}
}

Expand Down
85 changes: 83 additions & 2 deletions src/librustc/infer/error_reporting/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -588,6 +588,28 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> {
s.push_normal(format!("{}", tnm.ty));
}

fn push_closure<'tcx>(capture: &hir::CaptureClause,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is there any particular reason to scrape this information from the HIR? It can also be extracted from the closure type readily enough. To get the closure signature, you would do something like this (I have a more convenient helper for this on the nll-master branch, but it's not landed yet):

ty::TyClosure(did1, substs1) => {
    let closure_sig_ty = substs.closure_sig_ty(def_id, self.tcx);
    let closure_sig_ty = self.shallow_resolve(&closure_sig_ty);
    let sig: PolyFnSig<'_> = closure_sig_ty.fn_sig(self.tcx);
}

fn_decl: &hir::FnDecl,
s: &mut DiagnosticStyledString) {
let args = fn_decl.inputs.iter()
.map(|arg| format!("{}", arg))
.collect::<Vec<String>>()
.join(", ");
s.push_highlighted(
format!("{}|{}| -> {}",
if capture == &hir::CaptureByValue {
"move "
} else {
""
},
args,
if let hir::Return(ref r_ty) = fn_decl.output {
format!("{}", r_ty)
} else {
"_".to_string()
}));
}

match (&t1.sty, &t2.sty) {
(&ty::TyAdt(def1, sub1), &ty::TyAdt(def2, sub2)) => {
let mut values = (DiagnosticStyledString::new(), DiagnosticStyledString::new());
Expand Down Expand Up @@ -723,8 +745,67 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> {
// When encountering &T != &mut T, highlight only the borrow
(&ty::TyRef(r1, ref tnm1), &ty::TyRef(r2, ref tnm2)) if equals(&tnm1.ty, &tnm2.ty) => {
let mut values = (DiagnosticStyledString::new(), DiagnosticStyledString::new());
push_ty_ref(&r1, tnm1, &mut values.0);
push_ty_ref(&r2, tnm2, &mut values.1);
push_ty_ref(& r1, tnm1, & mut values.0);
push_ty_ref( & r2, tnm2, &mut values.1);
values
}
// When comparing against a closure, print its signature without location
(&ty::TyClosure(did1, _), &ty::TyClosure(did2, _)) => {
let mut values = (DiagnosticStyledString::new(), DiagnosticStyledString::new());
let mut success = false;
if let Some(node_id) = self.tcx.hir.as_local_node_id(did1) {
if let Some(hir::map::NodeExpr(expr)) = self.tcx.hir.find(node_id) {
if let hir::ExprClosure(capture, ref fn_decl, _, _, _) = expr.node {
push_closure(&capture, fn_decl, &mut values.0);
success = true;
}
}
}
if !success { // fallback
values.0.push_highlighted(format!("{}", t1));
}
success = false;
if let Some(node_id) = self.tcx.hir.as_local_node_id(did2) {
if let Some(hir::map::NodeExpr(expr)) = self.tcx.hir.find(node_id) {
if let hir::ExprClosure(capture, ref fn_decl, _, _, _) = expr.node {
push_closure(&capture, fn_decl, &mut values.1);
success = true;
}
}
}
if !success { // fallback
values.1.push_highlighted(format!("{}", t2));
}
values
}
(_, &ty::TyClosure(did, _)) => {
let mut values = (DiagnosticStyledString::highlighted(format!("{}", t1)),
DiagnosticStyledString::new());
if let Some(node_id) = self.tcx.hir.as_local_node_id(did) {
if let Some(hir::map::NodeExpr(expr)) = self.tcx.hir.find(node_id) {
if let hir::ExprClosure(capture, ref fn_decl, _, _, _) = expr.node {
push_closure(&capture, fn_decl, &mut values.1);
return values;
}
}
}
// fallback
values.1.push_highlighted(format!("{}", t2));
values
}
(&ty::TyClosure(did, _), _) => {
let mut values = (DiagnosticStyledString::new(),
DiagnosticStyledString::highlighted(format!("{}", t2)));
if let Some(node_id) = self.tcx.hir.as_local_node_id(did) {
if let Some(hir::map::NodeExpr(expr)) = self.tcx.hir.find(node_id) {
if let hir::ExprClosure(capture, ref fn_decl, _, _, _) = expr.node {
push_closure(&capture, fn_decl, &mut values.0);
return values;
}
}
}
// fallback
values.0.push_highlighted(format!("{}", t1));
values
}

Expand Down
57 changes: 28 additions & 29 deletions src/librustc/traits/error_reporting.rs
Original file line number Diff line number Diff line change
Expand Up @@ -938,51 +938,50 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> {
err
}

fn build_fn_sig_string(&self, trait_ref: &ty::TraitRef<'tcx>) -> String {
let inputs = trait_ref.substs.type_at(1);
let sig = if let ty::TyTuple(inputs, _) = inputs.sty {
self.tcx.mk_fn_sig(
inputs.iter().map(|&x| x),
self.tcx.mk_infer(ty::TyVar(ty::TyVid { index: 0 })),
false,
hir::Unsafety::Normal,
::syntax::abi::Abi::Rust
)
} else {
self.tcx.mk_fn_sig(
::std::iter::once(inputs),
self.tcx.mk_infer(ty::TyVar(ty::TyVid { index: 0 })),
false,
hir::Unsafety::Normal,
::syntax::abi::Abi::Rust
)
};
format!("{}", ty::Binder(sig))
}

fn report_closure_arg_mismatch(&self,
span: Span,
found_span: Option<Span>,
expected_ref: ty::PolyTraitRef<'tcx>,
found: ty::PolyTraitRef<'tcx>)
span: Span,
found_span: Option<Span>,
expected_ref: ty::PolyTraitRef<'tcx>,
found: ty::PolyTraitRef<'tcx>)
-> DiagnosticBuilder<'tcx>
{
fn build_fn_sig_string<'a, 'gcx, 'tcx>(tcx: ty::TyCtxt<'a, 'gcx, 'tcx>,
trait_ref: &ty::TraitRef<'tcx>) -> String {
let inputs = trait_ref.substs.type_at(1);
let sig = if let ty::TyTuple(inputs, _) = inputs.sty {
tcx.mk_fn_sig(
inputs.iter().map(|&x| x),
tcx.mk_infer(ty::TyVar(ty::TyVid { index: 0 })),
false,
hir::Unsafety::Normal,
::syntax::abi::Abi::Rust
)
} else {
tcx.mk_fn_sig(
::std::iter::once(inputs),
tcx.mk_infer(ty::TyVar(ty::TyVid { index: 0 })),
false,
hir::Unsafety::Normal,
::syntax::abi::Abi::Rust
)
};
format!("{}", ty::Binder(sig))
}

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())
self.build_fn_sig_string(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())
self.build_fn_sig_string(expected_ref.skip_binder())
);
err.span_label(found_span, expected_str);

Expand Down
25 changes: 21 additions & 4 deletions src/librustc/ty/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -246,6 +246,22 @@ impl<'a, 'gcx, 'lcx, 'tcx> ty::TyS<'tcx> {
ty::TyError => "type error".to_string(),
}
}

pub fn span(&self, tcx: &TyCtxt<'a, 'gcx, 'lcx>) -> Option<Span> {
match self.sty {
ty::TyClosure(def, _) => {
if let Some(node_id) = tcx.hir.as_local_node_id(def) {
if let Some(hir::map::NodeExpr(expr)) = tcx.hir.find(node_id) {
if let hir::ExprClosure(_, _, _, sp, _) = expr.node {
return Some(sp);
}
}
}
None
}
_ => None,
}
}
}

impl<'a, 'gcx, 'tcx> TyCtxt<'a, 'gcx, 'tcx> {
Expand All @@ -260,10 +276,11 @@ impl<'a, 'gcx, 'tcx> TyCtxt<'a, 'gcx, 'tcx> {
let expected_str = values.expected.sort_string(self);
let found_str = values.found.sort_string(self);
if expected_str == found_str && expected_str == "closure" {
db.span_note(sp,
"no two closures, even if identical, have the same type");
db.span_help(sp,
"consider boxing your closure and/or using it as a trait object");
db.note("no two closures, even if identical, have the same type");
if let Some(sp) = values.expected.span(&self) {
db.span_label(sp, "this closure was expected");
}
db.help("consider boxing your closure and/or using it as a trait object");
}
},
TyParamDefaultMismatch(values) => {
Expand Down
17 changes: 9 additions & 8 deletions src/test/compile-fail/issue-24036.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,28 +10,29 @@

fn closure_to_loc() {
let mut x = |c| c + 1;
//~^ NOTE this closure was expected
x = |c| c + 1;
//~^ ERROR mismatched types
//~| NOTE no two closures, even if identical, have the same type
//~| HELP consider boxing your closure and/or using it as a trait object
//~| expected closure, found a different closure
//~| expected type `[closure
//~| found type `[closure
//~| expected type `|_| -> _`
//~| found type `|_| -> _`
}

fn closure_from_match() {
let x = match 1usize {
//~^ ERROR match arms have incompatible types
//~| NOTE no two closures, even if identical, have the same type
//~| HELP consider boxing your closure and/or using it as a trait object
//~| NOTE expected closure, found a different closure
//~| NOTE expected type `|_| -> _` (closure)
1 => |c| c + 1,
//~^ NOTE this closure was expected
2 => |c| c - 1,
//~^ NOTE match arm with an incompatible type
_ => |c| c - 1
};
//~^^^^^^ ERROR match arms have incompatible types
//~| NOTE no two closures, even if identical, have the same type
//~| HELP consider boxing your closure and/or using it as a trait object
//~| expected closure, found a different closure
//~| expected type `[closure
//~| found type `[closure
}

fn main() { }
2 changes: 1 addition & 1 deletion src/test/ui/block-result/issue-20862.stderr
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ error[E0308]: mismatched types
| ^^^^^^^^^ expected (), found closure
|
= note: expected type `()`
found type `[closure@$DIR/issue-20862.rs:12:5: 12:14 x:_]`
found type `|_| -> _`

error[E0618]: expected function, found `()`
--> $DIR/issue-20862.rs:17:13
Expand Down
2 changes: 1 addition & 1 deletion src/test/ui/span/move-closure.stderr
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ error[E0308]: mismatched types
| ^^^^^^^^^^ expected (), found closure
|
= note: expected type `()`
found type `[closure@$DIR/move-closure.rs:15:17: 15:27]`
found type `move || -> _`

error: aborting due to previous error