From 4337089098e5adefa56ef249a5b200cb0693fcf5 Mon Sep 17 00:00:00 2001 From: Fabian Wolff Date: Sat, 12 Jun 2021 12:48:04 +0200 Subject: [PATCH 1/2] Add basic checks for well-formedness of `fn`/`fn_mut` lang items --- compiler/rustc_typeck/src/check/callee.rs | 18 ++++--- compiler/rustc_typeck/src/check/wfcheck.rs | 34 +++++++++++++ .../lang-items/fn-fn_mut-call-ill-formed.rs | 27 ++++++++++ .../fn-fn_mut-call-ill-formed.stderr | 14 +++++ src/test/ui/lang-items/issue-83471.rs | 23 +++++++++ src/test/ui/lang-items/issue-83471.stderr | 51 +++++++++++++++++++ 6 files changed, 161 insertions(+), 6 deletions(-) create mode 100644 src/test/ui/lang-items/fn-fn_mut-call-ill-formed.rs create mode 100644 src/test/ui/lang-items/fn-fn_mut-call-ill-formed.stderr create mode 100644 src/test/ui/lang-items/issue-83471.rs create mode 100644 src/test/ui/lang-items/issue-83471.stderr diff --git a/compiler/rustc_typeck/src/check/callee.rs b/compiler/rustc_typeck/src/check/callee.rs index 1cc06b8c2e544..e007d971bb072 100644 --- a/compiler/rustc_typeck/src/check/callee.rs +++ b/compiler/rustc_typeck/src/check/callee.rs @@ -246,12 +246,18 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { if borrow { // Check for &self vs &mut self in the method signature. Since this is either // the Fn or FnMut trait, it should be one of those. - let (region, mutbl) = - if let ty::Ref(r, _, mutbl) = method.sig.inputs()[0].kind() { - (r, mutbl) - } else { - span_bug!(call_expr.span, "input to call/call_mut is not a ref?"); - }; + let (region, mutbl) = if let ty::Ref(r, _, mutbl) = + method.sig.inputs()[0].kind() + { + (r, mutbl) + } else { + // The `fn`/`fn_mut` lang item is ill-formed, which should have + // caused an error elsewhere. + self.tcx + .sess + .delay_span_bug(call_expr.span, "input to call/call_mut is not a ref?"); + return None; + }; let mutbl = match mutbl { hir::Mutability::Not => AutoBorrowMutability::Not, diff --git a/compiler/rustc_typeck/src/check/wfcheck.rs b/compiler/rustc_typeck/src/check/wfcheck.rs index 01ec801228e2e..d9f9d9dafff52 100644 --- a/compiler/rustc_typeck/src/check/wfcheck.rs +++ b/compiler/rustc_typeck/src/check/wfcheck.rs @@ -200,6 +200,40 @@ pub fn check_trait_item(tcx: TyCtxt<'_>, def_id: LocalDefId) { }; check_object_unsafe_self_trait_by_name(tcx, &trait_item); check_associated_item(tcx, trait_item.hir_id(), span, method_sig); + + let encl_trait_hir_id = tcx.hir().get_parent_item(hir_id); + let encl_trait = tcx.hir().expect_item(encl_trait_hir_id); + if [tcx.lang_items().fn_trait(), tcx.lang_items().fn_mut_trait()] + .contains(&Some(encl_trait.def_id.to_def_id())) + && trait_item.ident.name.to_ident_string() == "call" + { + // We are looking at the `call` function of the `fn` or `fn_mut` lang item. + // Do some rudimentary sanity checking to avoid an ICE later (issue #83471). + if let Some(method_sig @ hir::FnSig { decl, .. }) = method_sig { + if let &[self_ty, _] = &decl.inputs { + if !matches!(self_ty.kind, hir::TyKind::Rptr(_, _)) { + tcx.sess.struct_span_err( + self_ty.span, + "first argument of `call` in `fn`/`fn_mut` lang item must be a reference", + ).emit(); + } + } else { + tcx.sess + .struct_span_err( + method_sig.span, + "`call` function in `fn`/`fn_mut` lang item takes exactly two arguments", + ) + .emit(); + } + } else { + tcx.sess + .struct_span_err( + trait_item.span, + "`call` trait item in `fn`/`fn_mut` lang item must be a function", + ) + .emit(); + } + } } fn could_be_self(trait_def_id: LocalDefId, ty: &hir::Ty<'_>) -> bool { diff --git a/src/test/ui/lang-items/fn-fn_mut-call-ill-formed.rs b/src/test/ui/lang-items/fn-fn_mut-call-ill-formed.rs new file mode 100644 index 0000000000000..90dc972d8a84a --- /dev/null +++ b/src/test/ui/lang-items/fn-fn_mut-call-ill-formed.rs @@ -0,0 +1,27 @@ +// Make sure that an error is reported if the `call` function of the +// `fn`/`fn_mut` lang item is grossly ill-formed. + +#![feature(lang_items)] +#![feature(no_core)] +#![no_core] + +#[lang = "fn"] +trait MyFn { + const call: i32 = 42; + //~^ ERROR: `call` trait item in `fn`/`fn_mut` lang item must be a function +} + +#[lang = "fn_mut"] +trait MyFnMut { + fn call(i: i32, j: i32) -> i32 { i + j } + //~^ ERROR: first argument of `call` in `fn`/`fn_mut` lang item must be a reference +} + +fn main() { + let a = || 42; + a(); + + let mut i = 0; + let mut b = || { i += 1; }; + b(); +} diff --git a/src/test/ui/lang-items/fn-fn_mut-call-ill-formed.stderr b/src/test/ui/lang-items/fn-fn_mut-call-ill-formed.stderr new file mode 100644 index 0000000000000..fea8e52b891d6 --- /dev/null +++ b/src/test/ui/lang-items/fn-fn_mut-call-ill-formed.stderr @@ -0,0 +1,14 @@ +error: `call` trait item in `fn`/`fn_mut` lang item must be a function + --> $DIR/fn-fn_mut-call-ill-formed.rs:10:5 + | +LL | const call: i32 = 42; + | ^^^^^^^^^^^^^^^^^^^^^ + +error: first argument of `call` in `fn`/`fn_mut` lang item must be a reference + --> $DIR/fn-fn_mut-call-ill-formed.rs:16:16 + | +LL | fn call(i: i32, j: i32) -> i32 { i + j } + | ^^^ + +error: aborting due to 2 previous errors + diff --git a/src/test/ui/lang-items/issue-83471.rs b/src/test/ui/lang-items/issue-83471.rs new file mode 100644 index 0000000000000..b32aa03415152 --- /dev/null +++ b/src/test/ui/lang-items/issue-83471.rs @@ -0,0 +1,23 @@ +// Regression test for the ICE reported in issue #83471. + +#![crate_type="lib"] +#![feature(no_core)] +#![no_core] + +#[lang = "sized"] +//~^ ERROR: language items are subject to change [E0658] +trait Sized {} + +#[lang = "fn"] +//~^ ERROR: language items are subject to change [E0658] +//~| ERROR: `fn` language item must be applied to a trait with 1 generic argument +trait Fn { + fn call(export_name); + //~^ ERROR: expected type + //~| WARNING: anonymous parameters are deprecated + //~| WARNING: this is accepted in the current edition +} +fn call_through_fn_trait() { + a() + //~^ ERROR: cannot find function +} diff --git a/src/test/ui/lang-items/issue-83471.stderr b/src/test/ui/lang-items/issue-83471.stderr new file mode 100644 index 0000000000000..c6130bb3370be --- /dev/null +++ b/src/test/ui/lang-items/issue-83471.stderr @@ -0,0 +1,51 @@ +error[E0573]: expected type, found built-in attribute `export_name` + --> $DIR/issue-83471.rs:15:13 + | +LL | fn call(export_name); + | ^^^^^^^^^^^ not a type + +error[E0425]: cannot find function `a` in this scope + --> $DIR/issue-83471.rs:21:5 + | +LL | a() + | ^ not found in this scope + +error[E0658]: language items are subject to change + --> $DIR/issue-83471.rs:7:1 + | +LL | #[lang = "sized"] + | ^^^^^^^^^^^^^^^^^ + | + = help: add `#![feature(lang_items)]` to the crate attributes to enable + +error[E0658]: language items are subject to change + --> $DIR/issue-83471.rs:11:1 + | +LL | #[lang = "fn"] + | ^^^^^^^^^^^^^^ + | + = help: add `#![feature(lang_items)]` to the crate attributes to enable + +warning: anonymous parameters are deprecated and will be removed in the next edition. + --> $DIR/issue-83471.rs:15:13 + | +LL | fn call(export_name); + | ^^^^^^^^^^^ help: try naming the parameter or explicitly ignoring it: `_: export_name` + | + = note: `#[warn(anonymous_parameters)]` on by default + = warning: this is accepted in the current edition (Rust 2015) but is a hard error in Rust 2018! + = note: for more information, see issue #41686 + +error[E0718]: `fn` language item must be applied to a trait with 1 generic argument + --> $DIR/issue-83471.rs:11:1 + | +LL | #[lang = "fn"] + | ^^^^^^^^^^^^^^ +... +LL | trait Fn { + | - this trait has 0 generic arguments + +error: aborting due to 5 previous errors; 1 warning emitted + +Some errors have detailed explanations: E0425, E0573, E0658, E0718. +For more information about an error, try `rustc --explain E0425`. From cb6c13977aa23f1d814a5504cf8b03a76743d06b Mon Sep 17 00:00:00 2001 From: Fabian Wolff Date: Fri, 24 Sep 2021 18:55:14 +0200 Subject: [PATCH 2/2] Make error message for malformed `fn`/`fn_mut` lang item more specific --- compiler/rustc_typeck/src/check/wfcheck.rs | 41 ++++++++++++++----- .../lang-items/fn-fn_mut-call-ill-formed.rs | 4 +- .../fn-fn_mut-call-ill-formed.stderr | 4 +- 3 files changed, 34 insertions(+), 15 deletions(-) diff --git a/compiler/rustc_typeck/src/check/wfcheck.rs b/compiler/rustc_typeck/src/check/wfcheck.rs index d9f9d9dafff52..df9399c56c7e9 100644 --- a/compiler/rustc_typeck/src/check/wfcheck.rs +++ b/compiler/rustc_typeck/src/check/wfcheck.rs @@ -203,25 +203,41 @@ pub fn check_trait_item(tcx: TyCtxt<'_>, def_id: LocalDefId) { let encl_trait_hir_id = tcx.hir().get_parent_item(hir_id); let encl_trait = tcx.hir().expect_item(encl_trait_hir_id); - if [tcx.lang_items().fn_trait(), tcx.lang_items().fn_mut_trait()] - .contains(&Some(encl_trait.def_id.to_def_id())) - && trait_item.ident.name.to_ident_string() == "call" + let encl_trait_def_id = encl_trait.def_id.to_def_id(); + let fn_lang_item_name = if Some(encl_trait_def_id) == tcx.lang_items().fn_trait() { + Some("fn") + } else if Some(encl_trait_def_id) == tcx.lang_items().fn_mut_trait() { + Some("fn_mut") + } else { + None + }; + + if let (Some(fn_lang_item_name), "call") = + (fn_lang_item_name, trait_item.ident.name.to_ident_string().as_str()) { // We are looking at the `call` function of the `fn` or `fn_mut` lang item. // Do some rudimentary sanity checking to avoid an ICE later (issue #83471). - if let Some(method_sig @ hir::FnSig { decl, .. }) = method_sig { + if let Some(hir::FnSig { decl, span, .. }) = method_sig { if let &[self_ty, _] = &decl.inputs { if !matches!(self_ty.kind, hir::TyKind::Rptr(_, _)) { - tcx.sess.struct_span_err( - self_ty.span, - "first argument of `call` in `fn`/`fn_mut` lang item must be a reference", - ).emit(); + tcx.sess + .struct_span_err( + self_ty.span, + &format!( + "first argument of `call` in `{}` lang item must be a reference", + fn_lang_item_name + ), + ) + .emit(); } } else { tcx.sess .struct_span_err( - method_sig.span, - "`call` function in `fn`/`fn_mut` lang item takes exactly two arguments", + *span, + &format!( + "`call` function in `{}` lang item takes exactly two arguments", + fn_lang_item_name + ), ) .emit(); } @@ -229,7 +245,10 @@ pub fn check_trait_item(tcx: TyCtxt<'_>, def_id: LocalDefId) { tcx.sess .struct_span_err( trait_item.span, - "`call` trait item in `fn`/`fn_mut` lang item must be a function", + &format!( + "`call` trait item in `{}` lang item must be a function", + fn_lang_item_name + ), ) .emit(); } diff --git a/src/test/ui/lang-items/fn-fn_mut-call-ill-formed.rs b/src/test/ui/lang-items/fn-fn_mut-call-ill-formed.rs index 90dc972d8a84a..52bd8136d9c0c 100644 --- a/src/test/ui/lang-items/fn-fn_mut-call-ill-formed.rs +++ b/src/test/ui/lang-items/fn-fn_mut-call-ill-formed.rs @@ -8,13 +8,13 @@ #[lang = "fn"] trait MyFn { const call: i32 = 42; - //~^ ERROR: `call` trait item in `fn`/`fn_mut` lang item must be a function + //~^ ERROR: `call` trait item in `fn` lang item must be a function } #[lang = "fn_mut"] trait MyFnMut { fn call(i: i32, j: i32) -> i32 { i + j } - //~^ ERROR: first argument of `call` in `fn`/`fn_mut` lang item must be a reference + //~^ ERROR: first argument of `call` in `fn_mut` lang item must be a reference } fn main() { diff --git a/src/test/ui/lang-items/fn-fn_mut-call-ill-formed.stderr b/src/test/ui/lang-items/fn-fn_mut-call-ill-formed.stderr index fea8e52b891d6..82bdae270c8bb 100644 --- a/src/test/ui/lang-items/fn-fn_mut-call-ill-formed.stderr +++ b/src/test/ui/lang-items/fn-fn_mut-call-ill-formed.stderr @@ -1,10 +1,10 @@ -error: `call` trait item in `fn`/`fn_mut` lang item must be a function +error: `call` trait item in `fn` lang item must be a function --> $DIR/fn-fn_mut-call-ill-formed.rs:10:5 | LL | const call: i32 = 42; | ^^^^^^^^^^^^^^^^^^^^^ -error: first argument of `call` in `fn`/`fn_mut` lang item must be a reference +error: first argument of `call` in `fn_mut` lang item must be a reference --> $DIR/fn-fn_mut-call-ill-formed.rs:16:16 | LL | fn call(i: i32, j: i32) -> i32 { i + j }