Skip to content

Commit 043972f

Browse files
committed
Auto merge of #86246 - FabianWolff:issue-83471, r=estebank
Add basic checks for well-formedness of `fn`/`fn_mut` lang items This pull request fixes #83471. Lang items are never actually checked for well-formedness (#9307). This means that one can get an ICE quite easily, e.g. as follows: ```rust #![feature(lang_items)] #[lang = "fn"] trait MyFn { const call: i32 = 42; } fn main() { (|| 42)(); } ``` or this: ```rust #![feature(lang_items)] #[lang = "fn"] trait MyFn { fn call(i: i32, j: i32); } fn main() { (|| 42)(); } ``` Ideally, there should probably be a more comprehensive strategy for checking lang items for well-formedness, but for the time being, I have added some rudimentary well-formedness checks that prevent #83471 and similar issues.
2 parents 7342213 + cb6c139 commit 043972f

File tree

6 files changed

+180
-6
lines changed

6 files changed

+180
-6
lines changed

compiler/rustc_typeck/src/check/callee.rs

+12-6
Original file line numberDiff line numberDiff line change
@@ -246,12 +246,18 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
246246
if borrow {
247247
// Check for &self vs &mut self in the method signature. Since this is either
248248
// the Fn or FnMut trait, it should be one of those.
249-
let (region, mutbl) =
250-
if let ty::Ref(r, _, mutbl) = method.sig.inputs()[0].kind() {
251-
(r, mutbl)
252-
} else {
253-
span_bug!(call_expr.span, "input to call/call_mut is not a ref?");
254-
};
249+
let (region, mutbl) = if let ty::Ref(r, _, mutbl) =
250+
method.sig.inputs()[0].kind()
251+
{
252+
(r, mutbl)
253+
} else {
254+
// The `fn`/`fn_mut` lang item is ill-formed, which should have
255+
// caused an error elsewhere.
256+
self.tcx
257+
.sess
258+
.delay_span_bug(call_expr.span, "input to call/call_mut is not a ref?");
259+
return None;
260+
};
255261

256262
let mutbl = match mutbl {
257263
hir::Mutability::Not => AutoBorrowMutability::Not,

compiler/rustc_typeck/src/check/wfcheck.rs

+53
Original file line numberDiff line numberDiff line change
@@ -200,6 +200,59 @@ pub fn check_trait_item(tcx: TyCtxt<'_>, def_id: LocalDefId) {
200200
};
201201
check_object_unsafe_self_trait_by_name(tcx, &trait_item);
202202
check_associated_item(tcx, trait_item.hir_id(), span, method_sig);
203+
204+
let encl_trait_hir_id = tcx.hir().get_parent_item(hir_id);
205+
let encl_trait = tcx.hir().expect_item(encl_trait_hir_id);
206+
let encl_trait_def_id = encl_trait.def_id.to_def_id();
207+
let fn_lang_item_name = if Some(encl_trait_def_id) == tcx.lang_items().fn_trait() {
208+
Some("fn")
209+
} else if Some(encl_trait_def_id) == tcx.lang_items().fn_mut_trait() {
210+
Some("fn_mut")
211+
} else {
212+
None
213+
};
214+
215+
if let (Some(fn_lang_item_name), "call") =
216+
(fn_lang_item_name, trait_item.ident.name.to_ident_string().as_str())
217+
{
218+
// We are looking at the `call` function of the `fn` or `fn_mut` lang item.
219+
// Do some rudimentary sanity checking to avoid an ICE later (issue #83471).
220+
if let Some(hir::FnSig { decl, span, .. }) = method_sig {
221+
if let &[self_ty, _] = &decl.inputs {
222+
if !matches!(self_ty.kind, hir::TyKind::Rptr(_, _)) {
223+
tcx.sess
224+
.struct_span_err(
225+
self_ty.span,
226+
&format!(
227+
"first argument of `call` in `{}` lang item must be a reference",
228+
fn_lang_item_name
229+
),
230+
)
231+
.emit();
232+
}
233+
} else {
234+
tcx.sess
235+
.struct_span_err(
236+
*span,
237+
&format!(
238+
"`call` function in `{}` lang item takes exactly two arguments",
239+
fn_lang_item_name
240+
),
241+
)
242+
.emit();
243+
}
244+
} else {
245+
tcx.sess
246+
.struct_span_err(
247+
trait_item.span,
248+
&format!(
249+
"`call` trait item in `{}` lang item must be a function",
250+
fn_lang_item_name
251+
),
252+
)
253+
.emit();
254+
}
255+
}
203256
}
204257

205258
fn could_be_self(trait_def_id: LocalDefId, ty: &hir::Ty<'_>) -> bool {
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
// Make sure that an error is reported if the `call` function of the
2+
// `fn`/`fn_mut` lang item is grossly ill-formed.
3+
4+
#![feature(lang_items)]
5+
#![feature(no_core)]
6+
#![no_core]
7+
8+
#[lang = "fn"]
9+
trait MyFn<T> {
10+
const call: i32 = 42;
11+
//~^ ERROR: `call` trait item in `fn` lang item must be a function
12+
}
13+
14+
#[lang = "fn_mut"]
15+
trait MyFnMut<T> {
16+
fn call(i: i32, j: i32) -> i32 { i + j }
17+
//~^ ERROR: first argument of `call` in `fn_mut` lang item must be a reference
18+
}
19+
20+
fn main() {
21+
let a = || 42;
22+
a();
23+
24+
let mut i = 0;
25+
let mut b = || { i += 1; };
26+
b();
27+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
error: `call` trait item in `fn` lang item must be a function
2+
--> $DIR/fn-fn_mut-call-ill-formed.rs:10:5
3+
|
4+
LL | const call: i32 = 42;
5+
| ^^^^^^^^^^^^^^^^^^^^^
6+
7+
error: first argument of `call` in `fn_mut` lang item must be a reference
8+
--> $DIR/fn-fn_mut-call-ill-formed.rs:16:16
9+
|
10+
LL | fn call(i: i32, j: i32) -> i32 { i + j }
11+
| ^^^
12+
13+
error: aborting due to 2 previous errors
14+

src/test/ui/lang-items/issue-83471.rs

+23
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
// Regression test for the ICE reported in issue #83471.
2+
3+
#![crate_type="lib"]
4+
#![feature(no_core)]
5+
#![no_core]
6+
7+
#[lang = "sized"]
8+
//~^ ERROR: language items are subject to change [E0658]
9+
trait Sized {}
10+
11+
#[lang = "fn"]
12+
//~^ ERROR: language items are subject to change [E0658]
13+
//~| ERROR: `fn` language item must be applied to a trait with 1 generic argument
14+
trait Fn {
15+
fn call(export_name);
16+
//~^ ERROR: expected type
17+
//~| WARNING: anonymous parameters are deprecated
18+
//~| WARNING: this is accepted in the current edition
19+
}
20+
fn call_through_fn_trait() {
21+
a()
22+
//~^ ERROR: cannot find function
23+
}
+51
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
error[E0573]: expected type, found built-in attribute `export_name`
2+
--> $DIR/issue-83471.rs:15:13
3+
|
4+
LL | fn call(export_name);
5+
| ^^^^^^^^^^^ not a type
6+
7+
error[E0425]: cannot find function `a` in this scope
8+
--> $DIR/issue-83471.rs:21:5
9+
|
10+
LL | a()
11+
| ^ not found in this scope
12+
13+
error[E0658]: language items are subject to change
14+
--> $DIR/issue-83471.rs:7:1
15+
|
16+
LL | #[lang = "sized"]
17+
| ^^^^^^^^^^^^^^^^^
18+
|
19+
= help: add `#![feature(lang_items)]` to the crate attributes to enable
20+
21+
error[E0658]: language items are subject to change
22+
--> $DIR/issue-83471.rs:11:1
23+
|
24+
LL | #[lang = "fn"]
25+
| ^^^^^^^^^^^^^^
26+
|
27+
= help: add `#![feature(lang_items)]` to the crate attributes to enable
28+
29+
warning: anonymous parameters are deprecated and will be removed in the next edition.
30+
--> $DIR/issue-83471.rs:15:13
31+
|
32+
LL | fn call(export_name);
33+
| ^^^^^^^^^^^ help: try naming the parameter or explicitly ignoring it: `_: export_name`
34+
|
35+
= note: `#[warn(anonymous_parameters)]` on by default
36+
= warning: this is accepted in the current edition (Rust 2015) but is a hard error in Rust 2018!
37+
= note: for more information, see issue #41686 <https://github.com/rust-lang/rust/issues/41686>
38+
39+
error[E0718]: `fn` language item must be applied to a trait with 1 generic argument
40+
--> $DIR/issue-83471.rs:11:1
41+
|
42+
LL | #[lang = "fn"]
43+
| ^^^^^^^^^^^^^^
44+
...
45+
LL | trait Fn {
46+
| - this trait has 0 generic arguments
47+
48+
error: aborting due to 5 previous errors; 1 warning emitted
49+
50+
Some errors have detailed explanations: E0425, E0573, E0658, E0718.
51+
For more information about an error, try `rustc --explain E0425`.

0 commit comments

Comments
 (0)