Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Improve detection of generics on lang items #87875

Merged
merged 1 commit into from
Aug 25, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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 @@ -17,7 +17,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 @@ -182,121 +182,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.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

IIUC, minimum requirements exist in cases where the other generics can be inferred, either through a function parameter or through defaulted parameters. So, why do some traits (Unsize, CoerceUnsized) 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