Skip to content

Commit

Permalink
Auto merge of #87875 - asquared31415:generic-lang-items, r=cjgillot
Browse files Browse the repository at this point in the history
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`
  • Loading branch information
bors committed Aug 25, 2021
2 parents 1a9ac38 + 385a233 commit 9863bf5
Show file tree
Hide file tree
Showing 12 changed files with 330 additions and 319 deletions.
2 changes: 1 addition & 1 deletion compiler/rustc_error_codes/src/error_codes/E0152.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ Erroneous code example:
#![feature(lang_items)]
#[lang = "owned_box"]
struct Foo; // error: duplicate lang item found: `owned_box`
struct Foo<T>(T); // error: duplicate lang item found: `owned_box`
```

Lang items are already implemented in the standard library. Unless you are
Expand Down
314 changes: 163 additions & 151 deletions compiler/rustc_hir/src/lang_items.rs

Large diffs are not rendered by default.

122 changes: 35 additions & 87 deletions compiler/rustc_passes/src/lang_items.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ use rustc_errors::{pluralize, struct_span_err};
use rustc_hir as hir;
use rustc_hir::def_id::DefId;
use rustc_hir::itemlikevisit::ItemLikeVisitor;
use rustc_hir::lang_items::{extract, ITEM_REFS};
use rustc_hir::lang_items::{extract, GenericRequirement, ITEM_REFS};
use rustc_hir::{HirId, LangItem, LanguageItems, Target};
use rustc_span::Span;

Expand Down Expand Up @@ -183,121 +183,69 @@ impl LanguageItemCollector<'tcx> {
}

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

self.collect_item(item_index, item_def_id);

// Now check whether the lang_item has the expected number of generic
// arguments if it is a trait. Generally speaking, binary and indexing
// operations have one (for the RHS/index), unary operations have none,
// and the rest also have none except for the closure traits (one for
// the argument list), generators (one for the resume argument),
// ordering/equality relations (one for the RHS), and various conversion
// traits.

let expected_num = match lang_item {
// Binary operations
LangItem::Add
| LangItem::Sub
| LangItem::Mul
| LangItem::Div
| LangItem::Rem
| LangItem::BitXor
| LangItem::BitAnd
| LangItem::BitOr
| LangItem::Shl
| LangItem::Shr
| LangItem::AddAssign
| LangItem::SubAssign
| LangItem::MulAssign
| LangItem::DivAssign
| LangItem::RemAssign
| LangItem::BitXorAssign
| LangItem::BitAndAssign
| LangItem::BitOrAssign
| LangItem::ShlAssign
| LangItem::ShrAssign
| LangItem::Index
| LangItem::IndexMut

// Miscellaneous
| LangItem::Unsize
| LangItem::CoerceUnsized
| LangItem::DispatchFromDyn
| LangItem::Fn
| LangItem::FnMut
| LangItem::FnOnce
| LangItem::Generator
| LangItem::PartialEq
| LangItem::PartialOrd
=> Some(1),

// Unary operations
LangItem::Neg
| LangItem::Not

// Miscellaneous
| LangItem::Deref
| LangItem::DerefMut
| LangItem::Sized
| LangItem::StructuralPeq
| LangItem::StructuralTeq
| LangItem::Copy
| LangItem::Clone
| LangItem::Sync
| LangItem::DiscriminantKind
| LangItem::PointeeTrait
| LangItem::Freeze
| LangItem::Drop
| LangItem::Receiver
| LangItem::Future
| LangItem::Unpin
| LangItem::Termination
| LangItem::Try
=> Some(0),
// arguments. Generally speaking, binary and indexing operations have
// one (for the RHS/index), unary operations have none, the closure
// traits have one for the argument list, generators have one for the
// resume argument, and ordering/equality relations have one for the RHS
// Some other types like Box and various functions like drop_in_place
// have minimum requirements.

// Not a trait
_ => None,
};
if let hir::Node::Item(hir::Item { kind, span: item_span, .. }) = self.tcx.hir().get(hir_id)
{
let (actual_num, generics_span) = match kind.generics() {
Some(generics) => (generics.params.len(), generics.span),
None => (0, *item_span),
};

if let Some(expected_num) = expected_num {
let (actual_num, generics_span) = match self.tcx.hir().get(hir_id) {
hir::Node::Item(hir::Item {
kind: hir::ItemKind::Trait(_, _, generics, ..),
..
}) => (generics.params.len(), generics.span),
_ => bug!("op/index/deref lang item target is not a trait: {:?}", lang_item),
let required = match lang_item.required_generics() {
GenericRequirement::Exact(num) if num != actual_num => {
Some((format!("{}", num), pluralize!(num)))
}
GenericRequirement::Minimum(num) if actual_num < num => {
Some((format!("at least {}", num), pluralize!(num)))
}
// If the number matches, or there is no requirement, handle it normally
_ => None,
};

if expected_num != actual_num {
if let Some((range_str, pluralized)) = required {
// We are issuing E0718 "incorrect target" here, because while the
// item kind of the target is correct, the target is still wrong
// because of the wrong number of generic arguments.
struct_span_err!(
self.tcx.sess,
span,
E0718,
"`{}` language item must be applied to a trait with {} generic argument{}",
"`{}` language item must be applied to a {} with {} generic argument{}",
name,
expected_num,
pluralize!(expected_num)
kind.descr(),
range_str,
pluralized,
)
.span_label(
generics_span,
format!(
"this trait has {} generic argument{}, not {}",
"this {} has {} generic argument{}",
kind.descr(),
actual_num,
pluralize!(actual_num),
expected_num
),
)
.emit();

// return early to not collect the lang item
return;
}
}

self.collect_item(item_index, item_def_id);
}
}

Expand Down
2 changes: 1 addition & 1 deletion src/test/ui/error-codes/E0152.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
#![feature(lang_items)]

#[lang = "owned_box"]
struct Foo; //~ ERROR E0152
struct Foo<T>(T); //~ ERROR E0152

fn main() {
}
4 changes: 2 additions & 2 deletions src/test/ui/error-codes/E0152.stderr
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
error[E0152]: found duplicate lang item `owned_box`
--> $DIR/E0152.rs:5:1
|
LL | struct Foo;
| ^^^^^^^^^^^
LL | struct Foo<T>(T);
| ^^^^^^^^^^^^^^^^^
|
= note: the lang item is first defined in crate `alloc` (which `std` depends on)
= note: first definition in `alloc` loaded from SYSROOT/liballoc-*.rlib
Expand Down
28 changes: 28 additions & 0 deletions src/test/ui/lang-items/lang-item-correct-generics.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
// build-pass

#![feature(lang_items,no_core)]
#![no_core]
#![crate_type="lib"]

#[lang = "sized"]
trait MySized {}

#[lang = "copy"]
trait MyCopy {}

#[lang = "drop"]
trait MyDrop<T> {}

struct S;

impl<T> MyDrop<T> for S {}

#[lang = "i32"]
impl<'a> i32 {
fn foo() {}
}

fn bar() {
i32::foo();
S;
}
44 changes: 44 additions & 0 deletions src/test/ui/lang-items/lang-item-generic-requirements.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
// Checks whether declaring a lang item with the wrong number
// of generic arguments crashes the compiler (issue #83893, #87573, and part of #9307).

#![feature(lang_items, no_core)]
#![no_core]
#![crate_type = "lib"]

#[lang = "sized"]
trait MySized {}

#[lang = "add"]
trait MyAdd<'a, T> {}
//~^^ ERROR: `add` language item must be applied to a trait with 1 generic argument [E0718]

#[lang = "drop_in_place"]
//~^ ERROR `drop_in_place` language item must be applied to a function with at least 1 generic
fn my_ptr_drop() {}

#[lang = "index"]
trait MyIndex<'a, T> {}
//~^^ ERROR: `index` language item must be applied to a trait with 1 generic argument [E0718]

#[lang = "phantom_data"]
//~^ ERROR `phantom_data` language item must be applied to a struct with 1 generic argument
struct MyPhantomData<T, U>;
//~^ ERROR parameter `T` is never used
//~| ERROR parameter `U` is never used

fn ice() {
// Use add
let r = 5;
let a = 6;
r + a;

// Use drop in place
my_ptr_drop();

// Use index
let arr = [0; 5];
let _ = arr[2];

// Use phantomdata
let _ = MyPhantomData::<(), i32>;
}
56 changes: 56 additions & 0 deletions src/test/ui/lang-items/lang-item-generic-requirements.stderr
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
error[E0718]: `add` language item must be applied to a trait with 1 generic argument
--> $DIR/lang-item-generic-requirements.rs:11:1
|
LL | #[lang = "add"]
| ^^^^^^^^^^^^^^^
LL | trait MyAdd<'a, T> {}
| ------- this trait has 2 generic arguments

error[E0718]: `drop_in_place` language item must be applied to a function with at least 1 generic argument
--> $DIR/lang-item-generic-requirements.rs:15:1
|
LL | #[lang = "drop_in_place"]
| ^^^^^^^^^^^^^^^^^^^^^^^^^
LL |
LL | fn my_ptr_drop() {}
| - this function has 0 generic arguments

error[E0718]: `index` language item must be applied to a trait with 1 generic argument
--> $DIR/lang-item-generic-requirements.rs:19:1
|
LL | #[lang = "index"]
| ^^^^^^^^^^^^^^^^^
LL | trait MyIndex<'a, T> {}
| ------- this trait has 2 generic arguments

error[E0718]: `phantom_data` language item must be applied to a struct with 1 generic argument
--> $DIR/lang-item-generic-requirements.rs:23:1
|
LL | #[lang = "phantom_data"]
| ^^^^^^^^^^^^^^^^^^^^^^^^
LL |
LL | struct MyPhantomData<T, U>;
| ------ this struct has 2 generic arguments

error[E0392]: parameter `T` is never used
--> $DIR/lang-item-generic-requirements.rs:25:22
|
LL | struct MyPhantomData<T, U>;
| ^ unused parameter
|
= help: consider removing `T` or referring to it in a field
= help: if you intended `T` to be a const parameter, use `const T: usize` instead

error[E0392]: parameter `U` is never used
--> $DIR/lang-item-generic-requirements.rs:25:25
|
LL | struct MyPhantomData<T, U>;
| ^ unused parameter
|
= help: consider removing `U` or referring to it in a field
= help: if you intended `U` to be a const parameter, use `const U: usize` instead

error: aborting due to 6 previous errors

Some errors have detailed explanations: E0392, E0718.
For more information about an error, try `rustc --explain E0392`.
20 changes: 0 additions & 20 deletions src/test/ui/lang-items/wrong-number-generic-args-add.rs

This file was deleted.

20 changes: 0 additions & 20 deletions src/test/ui/lang-items/wrong-number-generic-args-add.stderr

This file was deleted.

19 changes: 0 additions & 19 deletions src/test/ui/lang-items/wrong-number-generic-args-index.rs

This file was deleted.

Loading

0 comments on commit 9863bf5

Please sign in to comment.