Skip to content

Commit 9863bf5

Browse files
committed
Auto merge of #87875 - asquared31415:generic-lang-items, r=cjgillot
Improve detection of generics on lang items Adds detection for the required generics for all lang items. Many lang items require an exact or minimum amount of generic arguments and if they don't exist, the compiler will ICE. This does not add any additional validation about bounds on generics or any other lang item restrictions. Fixes one of the ICEs in #87573 cc `@FabianWolff`
2 parents 1a9ac38 + 385a233 commit 9863bf5

12 files changed

+330
-319
lines changed

compiler/rustc_error_codes/src/error_codes/E0152.md

+1-1
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ Erroneous code example:
66
#![feature(lang_items)]
77
88
#[lang = "owned_box"]
9-
struct Foo; // error: duplicate lang item found: `owned_box`
9+
struct Foo<T>(T); // error: duplicate lang item found: `owned_box`
1010
```
1111

1212
Lang items are already implemented in the standard library. Unless you are

compiler/rustc_hir/src/lang_items.rs

+163-151
Large diffs are not rendered by default.

compiler/rustc_passes/src/lang_items.rs

+35-87
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ use rustc_errors::{pluralize, struct_span_err};
1818
use rustc_hir as hir;
1919
use rustc_hir::def_id::DefId;
2020
use rustc_hir::itemlikevisit::ItemLikeVisitor;
21-
use rustc_hir::lang_items::{extract, ITEM_REFS};
21+
use rustc_hir::lang_items::{extract, GenericRequirement, ITEM_REFS};
2222
use rustc_hir::{HirId, LangItem, LanguageItems, Target};
2323
use rustc_span::Span;
2424

@@ -183,121 +183,69 @@ impl LanguageItemCollector<'tcx> {
183183
}
184184

185185
// Like collect_item() above, but also checks whether the lang item is declared
186-
// with the right number of generic arguments if it is a trait.
186+
// with the right number of generic arguments.
187187
fn collect_item_extended(&mut self, item_index: usize, hir_id: HirId, span: Span) {
188188
let item_def_id = self.tcx.hir().local_def_id(hir_id).to_def_id();
189189
let lang_item = LangItem::from_u32(item_index as u32).unwrap();
190190
let name = lang_item.name();
191191

192-
self.collect_item(item_index, item_def_id);
193-
194192
// Now check whether the lang_item has the expected number of generic
195-
// arguments if it is a trait. Generally speaking, binary and indexing
196-
// operations have one (for the RHS/index), unary operations have none,
197-
// and the rest also have none except for the closure traits (one for
198-
// the argument list), generators (one for the resume argument),
199-
// ordering/equality relations (one for the RHS), and various conversion
200-
// traits.
201-
202-
let expected_num = match lang_item {
203-
// Binary operations
204-
LangItem::Add
205-
| LangItem::Sub
206-
| LangItem::Mul
207-
| LangItem::Div
208-
| LangItem::Rem
209-
| LangItem::BitXor
210-
| LangItem::BitAnd
211-
| LangItem::BitOr
212-
| LangItem::Shl
213-
| LangItem::Shr
214-
| LangItem::AddAssign
215-
| LangItem::SubAssign
216-
| LangItem::MulAssign
217-
| LangItem::DivAssign
218-
| LangItem::RemAssign
219-
| LangItem::BitXorAssign
220-
| LangItem::BitAndAssign
221-
| LangItem::BitOrAssign
222-
| LangItem::ShlAssign
223-
| LangItem::ShrAssign
224-
| LangItem::Index
225-
| LangItem::IndexMut
226-
227-
// Miscellaneous
228-
| LangItem::Unsize
229-
| LangItem::CoerceUnsized
230-
| LangItem::DispatchFromDyn
231-
| LangItem::Fn
232-
| LangItem::FnMut
233-
| LangItem::FnOnce
234-
| LangItem::Generator
235-
| LangItem::PartialEq
236-
| LangItem::PartialOrd
237-
=> Some(1),
238-
239-
// Unary operations
240-
LangItem::Neg
241-
| LangItem::Not
242-
243-
// Miscellaneous
244-
| LangItem::Deref
245-
| LangItem::DerefMut
246-
| LangItem::Sized
247-
| LangItem::StructuralPeq
248-
| LangItem::StructuralTeq
249-
| LangItem::Copy
250-
| LangItem::Clone
251-
| LangItem::Sync
252-
| LangItem::DiscriminantKind
253-
| LangItem::PointeeTrait
254-
| LangItem::Freeze
255-
| LangItem::Drop
256-
| LangItem::Receiver
257-
| LangItem::Future
258-
| LangItem::Unpin
259-
| LangItem::Termination
260-
| LangItem::Try
261-
=> Some(0),
193+
// arguments. Generally speaking, binary and indexing operations have
194+
// one (for the RHS/index), unary operations have none, the closure
195+
// traits have one for the argument list, generators have one for the
196+
// resume argument, and ordering/equality relations have one for the RHS
197+
// Some other types like Box and various functions like drop_in_place
198+
// have minimum requirements.
262199

263-
// Not a trait
264-
_ => None,
265-
};
200+
if let hir::Node::Item(hir::Item { kind, span: item_span, .. }) = self.tcx.hir().get(hir_id)
201+
{
202+
let (actual_num, generics_span) = match kind.generics() {
203+
Some(generics) => (generics.params.len(), generics.span),
204+
None => (0, *item_span),
205+
};
266206

267-
if let Some(expected_num) = expected_num {
268-
let (actual_num, generics_span) = match self.tcx.hir().get(hir_id) {
269-
hir::Node::Item(hir::Item {
270-
kind: hir::ItemKind::Trait(_, _, generics, ..),
271-
..
272-
}) => (generics.params.len(), generics.span),
273-
_ => bug!("op/index/deref lang item target is not a trait: {:?}", lang_item),
207+
let required = match lang_item.required_generics() {
208+
GenericRequirement::Exact(num) if num != actual_num => {
209+
Some((format!("{}", num), pluralize!(num)))
210+
}
211+
GenericRequirement::Minimum(num) if actual_num < num => {
212+
Some((format!("at least {}", num), pluralize!(num)))
213+
}
214+
// If the number matches, or there is no requirement, handle it normally
215+
_ => None,
274216
};
275217

276-
if expected_num != actual_num {
218+
if let Some((range_str, pluralized)) = required {
277219
// We are issuing E0718 "incorrect target" here, because while the
278220
// item kind of the target is correct, the target is still wrong
279221
// because of the wrong number of generic arguments.
280222
struct_span_err!(
281223
self.tcx.sess,
282224
span,
283225
E0718,
284-
"`{}` language item must be applied to a trait with {} generic argument{}",
226+
"`{}` language item must be applied to a {} with {} generic argument{}",
285227
name,
286-
expected_num,
287-
pluralize!(expected_num)
228+
kind.descr(),
229+
range_str,
230+
pluralized,
288231
)
289232
.span_label(
290233
generics_span,
291234
format!(
292-
"this trait has {} generic argument{}, not {}",
235+
"this {} has {} generic argument{}",
236+
kind.descr(),
293237
actual_num,
294238
pluralize!(actual_num),
295-
expected_num
296239
),
297240
)
298241
.emit();
242+
243+
// return early to not collect the lang item
244+
return;
299245
}
300246
}
247+
248+
self.collect_item(item_index, item_def_id);
301249
}
302250
}
303251

src/test/ui/error-codes/E0152.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
#![feature(lang_items)]
33

44
#[lang = "owned_box"]
5-
struct Foo; //~ ERROR E0152
5+
struct Foo<T>(T); //~ ERROR E0152
66

77
fn main() {
88
}

src/test/ui/error-codes/E0152.stderr

+2-2
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
error[E0152]: found duplicate lang item `owned_box`
22
--> $DIR/E0152.rs:5:1
33
|
4-
LL | struct Foo;
5-
| ^^^^^^^^^^^
4+
LL | struct Foo<T>(T);
5+
| ^^^^^^^^^^^^^^^^^
66
|
77
= note: the lang item is first defined in crate `alloc` (which `std` depends on)
88
= note: first definition in `alloc` loaded from SYSROOT/liballoc-*.rlib
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
// build-pass
2+
3+
#![feature(lang_items,no_core)]
4+
#![no_core]
5+
#![crate_type="lib"]
6+
7+
#[lang = "sized"]
8+
trait MySized {}
9+
10+
#[lang = "copy"]
11+
trait MyCopy {}
12+
13+
#[lang = "drop"]
14+
trait MyDrop<T> {}
15+
16+
struct S;
17+
18+
impl<T> MyDrop<T> for S {}
19+
20+
#[lang = "i32"]
21+
impl<'a> i32 {
22+
fn foo() {}
23+
}
24+
25+
fn bar() {
26+
i32::foo();
27+
S;
28+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
// Checks whether declaring a lang item with the wrong number
2+
// of generic arguments crashes the compiler (issue #83893, #87573, and part of #9307).
3+
4+
#![feature(lang_items, no_core)]
5+
#![no_core]
6+
#![crate_type = "lib"]
7+
8+
#[lang = "sized"]
9+
trait MySized {}
10+
11+
#[lang = "add"]
12+
trait MyAdd<'a, T> {}
13+
//~^^ ERROR: `add` language item must be applied to a trait with 1 generic argument [E0718]
14+
15+
#[lang = "drop_in_place"]
16+
//~^ ERROR `drop_in_place` language item must be applied to a function with at least 1 generic
17+
fn my_ptr_drop() {}
18+
19+
#[lang = "index"]
20+
trait MyIndex<'a, T> {}
21+
//~^^ ERROR: `index` language item must be applied to a trait with 1 generic argument [E0718]
22+
23+
#[lang = "phantom_data"]
24+
//~^ ERROR `phantom_data` language item must be applied to a struct with 1 generic argument
25+
struct MyPhantomData<T, U>;
26+
//~^ ERROR parameter `T` is never used
27+
//~| ERROR parameter `U` is never used
28+
29+
fn ice() {
30+
// Use add
31+
let r = 5;
32+
let a = 6;
33+
r + a;
34+
35+
// Use drop in place
36+
my_ptr_drop();
37+
38+
// Use index
39+
let arr = [0; 5];
40+
let _ = arr[2];
41+
42+
// Use phantomdata
43+
let _ = MyPhantomData::<(), i32>;
44+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
error[E0718]: `add` language item must be applied to a trait with 1 generic argument
2+
--> $DIR/lang-item-generic-requirements.rs:11:1
3+
|
4+
LL | #[lang = "add"]
5+
| ^^^^^^^^^^^^^^^
6+
LL | trait MyAdd<'a, T> {}
7+
| ------- this trait has 2 generic arguments
8+
9+
error[E0718]: `drop_in_place` language item must be applied to a function with at least 1 generic argument
10+
--> $DIR/lang-item-generic-requirements.rs:15:1
11+
|
12+
LL | #[lang = "drop_in_place"]
13+
| ^^^^^^^^^^^^^^^^^^^^^^^^^
14+
LL |
15+
LL | fn my_ptr_drop() {}
16+
| - this function has 0 generic arguments
17+
18+
error[E0718]: `index` language item must be applied to a trait with 1 generic argument
19+
--> $DIR/lang-item-generic-requirements.rs:19:1
20+
|
21+
LL | #[lang = "index"]
22+
| ^^^^^^^^^^^^^^^^^
23+
LL | trait MyIndex<'a, T> {}
24+
| ------- this trait has 2 generic arguments
25+
26+
error[E0718]: `phantom_data` language item must be applied to a struct with 1 generic argument
27+
--> $DIR/lang-item-generic-requirements.rs:23:1
28+
|
29+
LL | #[lang = "phantom_data"]
30+
| ^^^^^^^^^^^^^^^^^^^^^^^^
31+
LL |
32+
LL | struct MyPhantomData<T, U>;
33+
| ------ this struct has 2 generic arguments
34+
35+
error[E0392]: parameter `T` is never used
36+
--> $DIR/lang-item-generic-requirements.rs:25:22
37+
|
38+
LL | struct MyPhantomData<T, U>;
39+
| ^ unused parameter
40+
|
41+
= help: consider removing `T` or referring to it in a field
42+
= help: if you intended `T` to be a const parameter, use `const T: usize` instead
43+
44+
error[E0392]: parameter `U` is never used
45+
--> $DIR/lang-item-generic-requirements.rs:25:25
46+
|
47+
LL | struct MyPhantomData<T, U>;
48+
| ^ unused parameter
49+
|
50+
= help: consider removing `U` or referring to it in a field
51+
= help: if you intended `U` to be a const parameter, use `const U: usize` instead
52+
53+
error: aborting due to 6 previous errors
54+
55+
Some errors have detailed explanations: E0392, E0718.
56+
For more information about an error, try `rustc --explain E0392`.

src/test/ui/lang-items/wrong-number-generic-args-add.rs

-20
This file was deleted.

src/test/ui/lang-items/wrong-number-generic-args-add.stderr

-20
This file was deleted.

src/test/ui/lang-items/wrong-number-generic-args-index.rs

-19
This file was deleted.

0 commit comments

Comments
 (0)