Skip to content

Commit 3200982

Browse files
committed
Auto merge of #108138 - compiler-errors:malformed-fn-trait, r=TaKO8Ki
Move `Fn*` traits malformedness protections to typeck I found it strange that we were doing a custom well-formedness check just for the `Fn*` traits' `call_*` fn items. My understanding from the git history is that this is just to avoid ICEs later on in typeck. Well, that well-formedness check isn't even implemented correctly for `FnOnce::call_once`, or `FnMut::call_mut` for that matter. Instead, this PR just makes the typeck checks more robust, and leaves it up to the call-site to report errors when lang items are implemented in funny ways. This coincidentally fixes another ICE where a the `Add` lang item is implemented with a `add` item that's a const instead of a method.
2 parents bda32a4 + 2a700d4 commit 3200982

16 files changed

+221
-74
lines changed

compiler/rustc_hir_analysis/src/check/wfcheck.rs

+1-51
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ use rustc_middle::mir::ConstraintCategory;
1616
use rustc_middle::ty::query::Providers;
1717
use rustc_middle::ty::trait_def::TraitSpecializationKind;
1818
use rustc_middle::ty::{
19-
self, ir::TypeVisitor, AdtKind, DefIdTree, GenericParamDefKind, Ty, TyCtxt, TypeFoldable,
19+
self, ir::TypeVisitor, AdtKind, GenericParamDefKind, Ty, TyCtxt, TypeFoldable,
2020
TypeSuperVisitable,
2121
};
2222
use rustc_middle::ty::{GenericArgKind, InternalSubsts};
@@ -277,56 +277,6 @@ fn check_trait_item(tcx: TyCtxt<'_>, trait_item: &hir::TraitItem<'_>) {
277277
};
278278
check_object_unsafe_self_trait_by_name(tcx, trait_item);
279279
check_associated_item(tcx, def_id, span, method_sig);
280-
281-
let encl_trait_def_id = tcx.local_parent(def_id);
282-
let encl_trait = tcx.hir().expect_item(encl_trait_def_id);
283-
let encl_trait_def_id = encl_trait.owner_id.to_def_id();
284-
let fn_lang_item_name = if Some(encl_trait_def_id) == tcx.lang_items().fn_trait() {
285-
Some("fn")
286-
} else if Some(encl_trait_def_id) == tcx.lang_items().fn_mut_trait() {
287-
Some("fn_mut")
288-
} else {
289-
None
290-
};
291-
292-
if let (Some(fn_lang_item_name), "call") =
293-
(fn_lang_item_name, trait_item.ident.name.to_ident_string().as_str())
294-
{
295-
// We are looking at the `call` function of the `fn` or `fn_mut` lang item.
296-
// Do some rudimentary sanity checking to avoid an ICE later (issue #83471).
297-
if let Some(hir::FnSig { decl, span, .. }) = method_sig {
298-
if let [self_ty, _] = decl.inputs {
299-
if !matches!(self_ty.kind, hir::TyKind::Ref(_, _)) {
300-
tcx.sess
301-
.struct_span_err(
302-
self_ty.span,
303-
&format!(
304-
"first argument of `call` in `{fn_lang_item_name}` lang item must be a reference",
305-
),
306-
)
307-
.emit();
308-
}
309-
} else {
310-
tcx.sess
311-
.struct_span_err(
312-
*span,
313-
&format!(
314-
"`call` function in `{fn_lang_item_name}` lang item takes exactly two arguments",
315-
),
316-
)
317-
.emit();
318-
}
319-
} else {
320-
tcx.sess
321-
.struct_span_err(
322-
trait_item.span,
323-
&format!(
324-
"`call` trait item in `{fn_lang_item_name}` lang item must be a function",
325-
),
326-
)
327-
.emit();
328-
}
329-
}
330280
}
331281

332282
/// Require that the user writes where clauses on GATs for the implicit

compiler/rustc_hir_typeck/src/callee.rs

+12-2
Original file line numberDiff line numberDiff line change
@@ -247,6 +247,15 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
247247
adjusted_ty,
248248
opt_input_type.as_ref().map(slice::from_ref),
249249
) {
250+
// Check for `self` receiver on the method, otherwise we can't use this as a `Fn*` trait.
251+
if !self.tcx.associated_item(ok.value.def_id).fn_has_self_parameter {
252+
self.tcx.sess.delay_span_bug(
253+
call_expr.span,
254+
"input to overloaded call fn is not a self receiver",
255+
);
256+
return None;
257+
}
258+
250259
let method = self.register_infer_ok_obligations(ok);
251260
let mut autoref = None;
252261
if borrow {
@@ -257,7 +266,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
257266
// caused an error elsewhere.
258267
self.tcx
259268
.sess
260-
.delay_span_bug(call_expr.span, "input to call/call_mut is not a ref?");
269+
.delay_span_bug(call_expr.span, "input to call/call_mut is not a ref");
261270
return None;
262271
};
263272

@@ -271,6 +280,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
271280
target: method.sig.inputs()[0],
272281
});
273282
}
283+
274284
return Some((autoref, method));
275285
}
276286
}
@@ -823,7 +833,7 @@ impl<'a, 'tcx> DeferredCallResolution<'tcx> {
823833
);
824834
err.help(
825835
"make sure the `fn`/`fn_mut`/`fn_once` lang items are defined \
826-
and have associated `call`/`call_mut`/`call_once` functions",
836+
and have correctly defined `call`/`call_mut`/`call_once` methods",
827837
);
828838
err.emit();
829839
}

compiler/rustc_hir_typeck/src/method/mod.rs

+6
Original file line numberDiff line numberDiff line change
@@ -380,6 +380,12 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
380380
);
381381
return None;
382382
};
383+
384+
if method_item.kind != ty::AssocKind::Fn {
385+
self.tcx.sess.delay_span_bug(tcx.def_span(method_item.def_id), "not a method");
386+
return None;
387+
}
388+
383389
let def_id = method_item.def_id;
384390
let generics = tcx.generics_of(def_id);
385391

tests/ui/lang-items/bad-add-impl.rs

+18
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
#![feature(no_core)]
2+
#![feature(lang_items)]
3+
#![no_core]
4+
5+
#[lang = "sized"]
6+
trait Sized {}
7+
8+
#[lang = "add"]
9+
trait Add<T> {
10+
const add: u32 = 1u32;
11+
}
12+
13+
impl Add<u32> for u32 {}
14+
15+
fn main() {
16+
1u32 + 1u32;
17+
//~^ ERROR cannot add `u32` to `u32`
18+
}
+11
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
error[E0369]: cannot add `u32` to `u32`
2+
--> $DIR/bad-add-impl.rs:16:10
3+
|
4+
LL | 1u32 + 1u32;
5+
| ---- ^ ---- u32
6+
| |
7+
| u32
8+
9+
error: aborting due to previous error
10+
11+
For more information about this error, try `rustc --explain E0369`.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
error: failed to find an overloaded call trait for closure call
2+
--> $DIR/fn-fn_mut-call-ill-formed.rs:39:5
3+
|
4+
LL | a();
5+
| ^^^
6+
|
7+
= help: make sure the `fn`/`fn_mut`/`fn_once` lang items are defined and have correctly defined `call`/`call_mut`/`call_once` methods
8+
9+
error: failed to find an overloaded call trait for closure call
10+
--> $DIR/fn-fn_mut-call-ill-formed.rs:43:5
11+
|
12+
LL | b();
13+
| ^^^
14+
|
15+
= help: make sure the `fn`/`fn_mut`/`fn_once` lang items are defined and have correctly defined `call`/`call_mut`/`call_once` methods
16+
17+
error: aborting due to 2 previous errors
18+
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
error: failed to find an overloaded call trait for closure call
2+
--> $DIR/fn-fn_mut-call-ill-formed.rs:39:5
3+
|
4+
LL | a();
5+
| ^^^
6+
|
7+
= help: make sure the `fn`/`fn_mut`/`fn_once` lang items are defined and have correctly defined `call`/`call_mut`/`call_once` methods
8+
9+
error: failed to find an overloaded call trait for closure call
10+
--> $DIR/fn-fn_mut-call-ill-formed.rs:43:5
11+
|
12+
LL | b();
13+
| ^^^
14+
|
15+
= help: make sure the `fn`/`fn_mut`/`fn_once` lang items are defined and have correctly defined `call`/`call_mut`/`call_once` methods
16+
17+
error: aborting due to 2 previous errors
18+
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
error: failed to find an overloaded call trait for closure call
2+
--> $DIR/fn-fn_mut-call-ill-formed.rs:42:5
3+
|
4+
LL | a();
5+
| ^^^
6+
|
7+
= help: make sure the `fn`/`fn_mut`/`fn_once` lang items are defined and have correctly defined `call`/`call_mut`/`call_once` methods
8+
9+
error: failed to find an overloaded call trait for closure call
10+
--> $DIR/fn-fn_mut-call-ill-formed.rs:47:5
11+
|
12+
LL | b();
13+
| ^^^
14+
|
15+
= help: make sure the `fn`/`fn_mut`/`fn_once` lang items are defined and have correctly defined `call`/`call_mut`/`call_once` methods
16+
17+
error: aborting due to 2 previous errors
18+
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
error: failed to find an overloaded call trait for closure call
2+
--> $DIR/fn-fn_mut-call-ill-formed.rs:42:5
3+
|
4+
LL | a();
5+
| ^^^
6+
|
7+
= help: make sure the `fn`/`fn_mut`/`fn_once` lang items are defined and have correctly defined `call`/`call_mut`/`call_once` methods
8+
9+
error: failed to find an overloaded call trait for closure call
10+
--> $DIR/fn-fn_mut-call-ill-formed.rs:47:5
11+
|
12+
LL | b();
13+
| ^^^
14+
|
15+
= help: make sure the `fn`/`fn_mut`/`fn_once` lang items are defined and have correctly defined `call`/`call_mut`/`call_once` methods
16+
17+
error: aborting due to 2 previous errors
18+
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
error: failed to find an overloaded call trait for closure call
2+
--> $DIR/fn-fn_mut-call-ill-formed.rs:42:5
3+
|
4+
LL | a();
5+
| ^^^
6+
|
7+
= help: make sure the `fn`/`fn_mut`/`fn_once` lang items are defined and have correctly defined `call`/`call_mut`/`call_once` methods
8+
9+
error: failed to find an overloaded call trait for closure call
10+
--> $DIR/fn-fn_mut-call-ill-formed.rs:47:5
11+
|
12+
LL | b();
13+
| ^^^
14+
|
15+
= help: make sure the `fn`/`fn_mut`/`fn_once` lang items are defined and have correctly defined `call`/`call_mut`/`call_once` methods
16+
17+
error: aborting due to 2 previous errors
18+
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
error: failed to find an overloaded call trait for closure call
2+
--> $DIR/fn-fn_mut-call-ill-formed.rs:42:5
3+
|
4+
LL | a();
5+
| ^^^
6+
|
7+
= help: make sure the `fn`/`fn_mut`/`fn_once` lang items are defined and have correctly defined `call`/`call_mut`/`call_once` methods
8+
9+
error: failed to find an overloaded call trait for closure call
10+
--> $DIR/fn-fn_mut-call-ill-formed.rs:47:5
11+
|
12+
LL | b();
13+
| ^^^
14+
|
15+
= help: make sure the `fn`/`fn_mut`/`fn_once` lang items are defined and have correctly defined `call`/`call_mut`/`call_once` methods
16+
17+
error: aborting due to 2 previous errors
18+
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
error: failed to find an overloaded call trait for closure call
2+
--> $DIR/fn-fn_mut-call-ill-formed.rs:42:5
3+
|
4+
LL | a();
5+
| ^^^
6+
|
7+
= help: make sure the `fn`/`fn_mut`/`fn_once` lang items are defined and have correctly defined `call`/`call_mut`/`call_once` methods
8+
9+
error: failed to find an overloaded call trait for closure call
10+
--> $DIR/fn-fn_mut-call-ill-formed.rs:47:5
11+
|
12+
LL | b();
13+
| ^^^
14+
|
15+
= help: make sure the `fn`/`fn_mut`/`fn_once` lang items are defined and have correctly defined `call`/`call_mut`/`call_once` methods
16+
17+
error: aborting due to 2 previous errors
18+
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
error: failed to find an overloaded call trait for closure call
2+
--> $DIR/fn-fn_mut-call-ill-formed.rs:42:5
3+
|
4+
LL | a();
5+
| ^^^
6+
|
7+
= help: make sure the `fn`/`fn_mut`/`fn_once` lang items are defined and have correctly defined `call`/`call_mut`/`call_once` methods
8+
9+
error: failed to find an overloaded call trait for closure call
10+
--> $DIR/fn-fn_mut-call-ill-formed.rs:47:5
11+
|
12+
LL | b();
13+
| ^^^
14+
|
15+
= help: make sure the `fn`/`fn_mut`/`fn_once` lang items are defined and have correctly defined `call`/`call_mut`/`call_once` methods
16+
17+
error: aborting due to 2 previous errors
18+
Original file line numberDiff line numberDiff line change
@@ -1,27 +1,49 @@
1-
// Make sure that an error is reported if the `call` function of the
2-
// `fn`/`fn_mut` lang item is grossly ill-formed.
1+
// revisions: fn_once_bad_item fn_once_bad_sig fn_mut_bad_item fn_mut_bad_sig fn_bad_item fn_bad_sig
32

43
#![feature(lang_items)]
54
#![feature(no_core)]
65
#![no_core]
76

7+
#[lang = "sized"]
8+
trait Sized {}
9+
10+
#[cfg(any(fn_bad_item, fn_bad_sig))]
811
#[lang = "fn"]
912
trait MyFn<T> {
13+
#[cfg(fn_bad_sig)]
14+
fn call(i: i32) -> i32 { 0 }
15+
16+
#[cfg(fn_bad_item)]
1017
const call: i32 = 42;
11-
//~^ ERROR: `call` trait item in `fn` lang item must be a function
1218
}
1319

20+
#[cfg(any(fn_mut_bad_item, fn_mut_bad_sig))]
1421
#[lang = "fn_mut"]
1522
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
23+
#[cfg(fn_mut_bad_sig)]
24+
fn call_mut(i: i32) -> i32 { 0 }
25+
26+
#[cfg(fn_mut_bad_item)]
27+
const call_mut: i32 = 42;
28+
}
29+
30+
#[cfg(any(fn_once_bad_item, fn_once_bad_sig))]
31+
#[lang = "fn_once"]
32+
trait MyFnOnce<T> {
33+
#[cfg(fn_once_bad_sig)]
34+
fn call_once(i: i32) -> i32 { 0 }
35+
36+
#[cfg(fn_once_bad_item)]
37+
const call_once: i32 = 42;
1838
}
1939

2040
fn main() {
2141
let a = || 42;
2242
a();
43+
//~^ ERROR failed to find an overloaded call trait for closure call
2344

2445
let mut i = 0;
25-
let mut b = || { i += 1; };
46+
let mut b = || { };
2647
b();
48+
//~^ ERROR failed to find an overloaded call trait for closure call
2749
}

tests/ui/lang-items/fn-fn_mut-call-ill-formed.stderr

-14
This file was deleted.

tests/ui/lang-items/issue-86238.stderr

+1-1
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ error: failed to find an overloaded call trait for closure call
44
LL | one()
55
| ^^^^^
66
|
7-
= help: make sure the `fn`/`fn_mut`/`fn_once` lang items are defined and have associated `call`/`call_mut`/`call_once` functions
7+
= help: make sure the `fn`/`fn_mut`/`fn_once` lang items are defined and have correctly defined `call`/`call_mut`/`call_once` methods
88

99
error: aborting due to previous error
1010

0 commit comments

Comments
 (0)