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

Report an error if a lang item has the wrong number of generic arguments #85339

Merged
merged 3 commits into from
May 18, 2021
Merged
Show file tree
Hide file tree
Changes from 2 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
132 changes: 127 additions & 5 deletions compiler/rustc_passes/src/lang_items.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,12 +13,13 @@ use crate::weak_lang_items;
use rustc_middle::middle::cstore::ExternCrate;
use rustc_middle::ty::TyCtxt;

use rustc_errors::struct_span_err;
use rustc_errors::{pluralize, struct_span_err};
use rustc_hir as hir;
use rustc_hir::def_id::{DefId, LOCAL_CRATE};
use rustc_hir::itemlikevisit::ItemLikeVisitor;
use rustc_hir::lang_items::{extract, ITEM_REFS};
use rustc_hir::{HirId, LangItem, LanguageItems, Target};
use rustc_span::Span;

use rustc_middle::ty::query::Providers;

Expand Down Expand Up @@ -61,8 +62,7 @@ impl LanguageItemCollector<'tcx> {
match ITEM_REFS.get(&value).cloned() {
// Known lang item with attribute on correct target.
Some((item_index, expected_target)) if actual_target == expected_target => {
let def_id = self.tcx.hir().local_def_id(hir_id);
self.collect_item(item_index, def_id.to_def_id());
self.collect_item_extended(item_index, hir_id, span);
}
// Known lang item with attribute on incorrect target.
Some((_, expected_target)) => {
Expand Down Expand Up @@ -100,11 +100,12 @@ impl LanguageItemCollector<'tcx> {
}

fn collect_item(&mut self, item_index: usize, item_def_id: DefId) {
let lang_item = LangItem::from_u32(item_index as u32).unwrap();
varkor marked this conversation as resolved.
Show resolved Hide resolved
let name = lang_item.name();

// Check for duplicates.
if let Some(original_def_id) = self.items.items[item_index] {
if original_def_id != item_def_id {
let lang_item = LangItem::from_u32(item_index as u32).unwrap();
let name = lang_item.name();
let mut err = match self.tcx.hir().span_if_local(item_def_id) {
Some(span) => struct_span_err!(
self.tcx.sess,
Expand Down Expand Up @@ -180,6 +181,127 @@ impl LanguageItemCollector<'tcx> {
self.items.groups[group as usize].push(item_def_id);
}
}

// 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.
fn collect_item_extended(&mut self, item_index: usize, hir_id: HirId, span: Span) {
varkor marked this conversation as resolved.
Show resolved Hide resolved
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
| LangItem::Send
| LangItem::UnwindSafe
| LangItem::RefUnwindSafe
=> Some(0),

// Not a trait
_ => None,
};

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),
};

if expected_num != actual_num {
// 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{}",
name,
expected_num,
pluralize!(expected_num)
)
.span_label(
generics_span,
format!(
"this trait has {} generic argument{}, not {}",
actual_num,
pluralize!(actual_num),
expected_num
),
)
.emit();
}
}
}
}

/// Traverses and collects all the lang items in all crates.
Expand Down
4 changes: 2 additions & 2 deletions compiler/rustc_typeck/src/check/method/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -303,8 +303,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
opt_input_types: Option<&[Ty<'tcx>]>,
) -> Option<InferOk<'tcx, MethodCallee<'tcx>>> {
debug!(
"lookup_in_trait_adjusted(self_ty={:?}, m_name={}, trait_def_id={:?})",
self_ty, m_name, trait_def_id
"lookup_in_trait_adjusted(self_ty={:?}, m_name={}, trait_def_id={:?}, opt_input_types={:?})",
self_ty, m_name, trait_def_id, opt_input_types
);

// Construct a trait-reference `self_ty : Trait<input_tys>`
Expand Down
14 changes: 14 additions & 0 deletions compiler/rustc_typeck/src/check/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1190,3 +1190,17 @@ fn fatally_break_rust(sess: &Session) {
fn potentially_plural_count(count: usize, word: &str) -> String {
format!("{} {}{}", count, word, pluralize!(count))
}

fn has_expected_num_generic_args<'tcx>(
tcx: TyCtxt<'tcx>,
trait_did: Option<DefId>,
mut expected: usize,
) -> bool {
trait_did.map_or(true, |trait_did| {
let generics = tcx.generics_of(trait_did);
if generics.has_self {
expected += 1;
}
generics.count() == expected
varkor marked this conversation as resolved.
Show resolved Hide resolved
})
}
19 changes: 18 additions & 1 deletion compiler/rustc_typeck/src/check/op.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
//! Code related to processing overloaded binary and unary operators.

use super::method::MethodCallee;
use super::FnCtxt;
use super::{has_expected_num_generic_args, FnCtxt};
use rustc_ast as ast;
use rustc_errors::{self, struct_span_err, Applicability, DiagnosticBuilder};
use rustc_hir as hir;
Expand Down Expand Up @@ -795,6 +795,23 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
lhs_ty, op, opname, trait_did
);

// Catches cases like #83893, where a lang item is declared with the
// wrong number of generic arguments. Should have yielded an error
// elsewhere by now, but we have to catch it here so that we do not
// index `other_tys` out of bounds (if the lang item has too many
// generic arguments, `other_tys` is too short).
if !has_expected_num_generic_args(
self.tcx,
trait_did,
match op {
// Binary ops have a generic right-hand side, unary ops don't
Op::Binary(..) => 1,
Op::Unary(..) => 0,
},
) {
return Err(());
}

let method = trait_did.and_then(|trait_did| {
let opname = Ident::with_dummy_span(opname);
self.lookup_method_in_trait(span, opname, trait_did, lhs_ty, Some(other_tys))
Expand Down
32 changes: 31 additions & 1 deletion compiler/rustc_typeck/src/check/place_op.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
use crate::check::method::MethodCallee;
use crate::check::{FnCtxt, PlaceOp};
use crate::check::{has_expected_num_generic_args, FnCtxt, PlaceOp};
use rustc_hir as hir;
use rustc_infer::infer::type_variable::{TypeVariableOrigin, TypeVariableOriginKind};
use rustc_infer::infer::InferOk;
Expand Down Expand Up @@ -153,6 +153,21 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
PlaceOp::Deref => (self.tcx.lang_items().deref_trait(), sym::deref),
PlaceOp::Index => (self.tcx.lang_items().index_trait(), sym::index),
};

// If the lang item was declared incorrectly, stop here so that we don't
// run into an ICE (#83893). The error is reported where the lang item is
// declared.
if !has_expected_num_generic_args(
self.tcx,
imm_tr,
match op {
PlaceOp::Deref => 0,
PlaceOp::Index => 1,
},
) {
return None;
}

imm_tr.and_then(|trait_did| {
self.lookup_method_in_trait(
span,
Expand All @@ -177,6 +192,21 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
PlaceOp::Deref => (self.tcx.lang_items().deref_mut_trait(), sym::deref_mut),
PlaceOp::Index => (self.tcx.lang_items().index_mut_trait(), sym::index_mut),
};

// If the lang item was declared incorrectly, stop here so that we don't
// run into an ICE (#83893). The error is reported where the lang item is
// declared.
if !has_expected_num_generic_args(
self.tcx,
mut_tr,
match op {
PlaceOp::Deref => 0,
PlaceOp::Index => 1,
},
) {
return None;
}

mut_tr.and_then(|trait_did| {
self.lookup_method_in_trait(
span,
Expand Down
20 changes: 20 additions & 0 deletions src/test/ui/lang-items/wrong-number-generic-args-add.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
// Checks whether declaring a lang item with the wrong number
// of generic arguments crashes the compiler (issue #83893).

#![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]

fn ice() {
let r = 5;
let a = 6;
r + a
//~^ ERROR: cannot add `{integer}` to `{integer}` [E0369]
}
20 changes: 20 additions & 0 deletions src/test/ui/lang-items/wrong-number-generic-args-add.stderr
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
error[E0718]: `add` language item must be applied to a trait with 1 generic argument
--> $DIR/wrong-number-generic-args-add.rs:11:1
|
LL | #[lang = "add"]
| ^^^^^^^^^^^^^^^
LL | trait MyAdd<'a, T> {}
| ------- this trait has 2 generic arguments, not 1

error[E0369]: cannot add `{integer}` to `{integer}`
--> $DIR/wrong-number-generic-args-add.rs:18:7
|
LL | r + a
| - ^ - {integer}
| |
| {integer}

error: aborting due to 2 previous errors

Some errors have detailed explanations: E0369, E0718.
For more information about an error, try `rustc --explain E0369`.
19 changes: 19 additions & 0 deletions src/test/ui/lang-items/wrong-number-generic-args-index.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
// Checks whether declaring a lang item with the wrong number
// of generic arguments crashes the compiler (issue #83893).

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

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

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

fn ice() {
let arr = [0; 5];
let _ = arr[2];
//~^ ERROR: cannot index into a value of type `[{integer}; 5]` [E0608]
}
18 changes: 18 additions & 0 deletions src/test/ui/lang-items/wrong-number-generic-args-index.stderr
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
error[E0718]: `index` language item must be applied to a trait with 1 generic argument
--> $DIR/wrong-number-generic-args-index.rs:11:1
|
LL | #[lang = "index"]
| ^^^^^^^^^^^^^^^^^
LL | trait MyIndex<'a, T> {}
| ------- this trait has 2 generic arguments, not 1

error[E0608]: cannot index into a value of type `[{integer}; 5]`
--> $DIR/wrong-number-generic-args-index.rs:17:13
|
LL | let _ = arr[2];
| ^^^^^^

error: aborting due to 2 previous errors

Some errors have detailed explanations: E0608, E0718.
For more information about an error, try `rustc --explain E0608`.