diff --git a/compiler/rustc_passes/src/lang_items.rs b/compiler/rustc_passes/src/lang_items.rs index 7e6bb9779f077..a6306ad923dab 100644 --- a/compiler/rustc_passes/src/lang_items.rs +++ b/compiler/rustc_passes/src/lang_items.rs @@ -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; @@ -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)) => { @@ -180,6 +180,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) { + 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. diff --git a/compiler/rustc_typeck/src/check/method/mod.rs b/compiler/rustc_typeck/src/check/method/mod.rs index 0b1129a631249..427102afee103 100644 --- a/compiler/rustc_typeck/src/check/method/mod.rs +++ b/compiler/rustc_typeck/src/check/method/mod.rs @@ -303,8 +303,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { opt_input_types: Option<&[Ty<'tcx>]>, ) -> Option>> { 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` diff --git a/compiler/rustc_typeck/src/check/mod.rs b/compiler/rustc_typeck/src/check/mod.rs index 994206bd41934..0b5387993c948 100644 --- a/compiler/rustc_typeck/src/check/mod.rs +++ b/compiler/rustc_typeck/src/check/mod.rs @@ -1190,3 +1190,14 @@ 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, + expected: usize, +) -> bool { + trait_did.map_or(true, |trait_did| { + let generics = tcx.generics_of(trait_did); + generics.count() == expected + if generics.has_self { 1 } else { 0 } + }) +} diff --git a/compiler/rustc_typeck/src/check/op.rs b/compiler/rustc_typeck/src/check/op.rs index 567cb1a90d0d9..963436d05d8ef 100644 --- a/compiler/rustc_typeck/src/check/op.rs +++ b/compiler/rustc_typeck/src/check/op.rs @@ -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; @@ -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)) diff --git a/compiler/rustc_typeck/src/check/place_op.rs b/compiler/rustc_typeck/src/check/place_op.rs index 5bd385107ca39..a63aec07ad1c0 100644 --- a/compiler/rustc_typeck/src/check/place_op.rs +++ b/compiler/rustc_typeck/src/check/place_op.rs @@ -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; @@ -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, @@ -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, diff --git a/src/test/ui/lang-item-missing-generator.rs b/src/test/ui/lang-items/lang-item-missing-generator.rs similarity index 100% rename from src/test/ui/lang-item-missing-generator.rs rename to src/test/ui/lang-items/lang-item-missing-generator.rs diff --git a/src/test/ui/lang-item-missing-generator.stderr b/src/test/ui/lang-items/lang-item-missing-generator.stderr similarity index 100% rename from src/test/ui/lang-item-missing-generator.stderr rename to src/test/ui/lang-items/lang-item-missing-generator.stderr diff --git a/src/test/ui/lang-item-missing.rs b/src/test/ui/lang-items/lang-item-missing.rs similarity index 100% rename from src/test/ui/lang-item-missing.rs rename to src/test/ui/lang-items/lang-item-missing.rs diff --git a/src/test/ui/lang-item-missing.stderr b/src/test/ui/lang-items/lang-item-missing.stderr similarity index 100% rename from src/test/ui/lang-item-missing.stderr rename to src/test/ui/lang-items/lang-item-missing.stderr diff --git a/src/test/ui/lang-items/wrong-number-generic-args-add.rs b/src/test/ui/lang-items/wrong-number-generic-args-add.rs new file mode 100644 index 0000000000000..9f4f2464a1e65 --- /dev/null +++ b/src/test/ui/lang-items/wrong-number-generic-args-add.rs @@ -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] +} diff --git a/src/test/ui/lang-items/wrong-number-generic-args-add.stderr b/src/test/ui/lang-items/wrong-number-generic-args-add.stderr new file mode 100644 index 0000000000000..6f89441fd285d --- /dev/null +++ b/src/test/ui/lang-items/wrong-number-generic-args-add.stderr @@ -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`. diff --git a/src/test/ui/lang-items/wrong-number-generic-args-index.rs b/src/test/ui/lang-items/wrong-number-generic-args-index.rs new file mode 100644 index 0000000000000..1d90e63dc5470 --- /dev/null +++ b/src/test/ui/lang-items/wrong-number-generic-args-index.rs @@ -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] +} diff --git a/src/test/ui/lang-items/wrong-number-generic-args-index.stderr b/src/test/ui/lang-items/wrong-number-generic-args-index.stderr new file mode 100644 index 0000000000000..bc3f19ff27623 --- /dev/null +++ b/src/test/ui/lang-items/wrong-number-generic-args-index.stderr @@ -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`.