diff options
| author | Fabian Wolff <fabi.wolff@arcor.de> | 2021-05-15 19:53:16 +0200 |
|---|---|---|
| committer | Fabian Wolff <fabi.wolff@arcor.de> | 2021-05-15 19:53:16 +0200 |
| commit | 7217d767b2054ac98da4f1840934f35f8285890c (patch) | |
| tree | 68473dc0c6c9a7a8bdf57006ce76996bdf215ddc /compiler | |
| parent | b439be03c9c30d173c45785a03a03cba98af5ab6 (diff) | |
| download | rust-7217d767b2054ac98da4f1840934f35f8285890c.tar.gz rust-7217d767b2054ac98da4f1840934f35f8285890c.zip | |
Report an error if a lang item has the wrong number of generic arguments
Diffstat (limited to 'compiler')
| -rw-r--r-- | compiler/rustc_passes/src/lang_items.rs | 88 | ||||
| -rw-r--r-- | compiler/rustc_typeck/src/check/method/mod.rs | 4 | ||||
| -rw-r--r-- | compiler/rustc_typeck/src/check/op.rs | 18 | ||||
| -rw-r--r-- | compiler/rustc_typeck/src/check/place_op.rs | 32 |
4 files changed, 135 insertions, 7 deletions
diff --git a/compiler/rustc_passes/src/lang_items.rs b/compiler/rustc_passes/src/lang_items.rs index 7e6bb9779f0..111bdd1a117 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)) => { @@ -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(); + 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, @@ -180,6 +181,83 @@ impl LanguageItemCollector<'tcx> { self.items.groups[group as usize].push(item_def_id); } } + + 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. Binary and indexing operations have one (for the RHS/index), + // unary operations have no generic arguments. + + let expected_num = match lang_item { + 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 => Some(1), + + LangItem::Neg | LangItem::Not | LangItem::Deref | LangItem::DerefMut => Some(0), + + // FIXME: add more cases? + _ => 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 0b1129a6312..427102afee1 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<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>` diff --git a/compiler/rustc_typeck/src/check/op.rs b/compiler/rustc_typeck/src/check/op.rs index 567cb1a90d0..87cb2d6d70c 100644 --- a/compiler/rustc_typeck/src/check/op.rs +++ b/compiler/rustc_typeck/src/check/op.rs @@ -795,6 +795,24 @@ 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 let Some(trait_did) = trait_did { + let generics = self.tcx.generics_of(trait_did); + let expected_num = match op { + // Binary ops have a generic right-hand side, unary ops don't + Op::Binary(..) => 1, + Op::Unary(..) => 0, + } + if generics.has_self { 1 } else { 0 }; + let num_generics = generics.count(); + if num_generics != expected_num { + 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 5bd385107ca..23677d04d73 100644 --- a/compiler/rustc_typeck/src/check/place_op.rs +++ b/compiler/rustc_typeck/src/check/place_op.rs @@ -153,6 +153,22 @@ 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 let Some(trait_did) = imm_tr { + let generics = self.tcx.generics_of(trait_did); + let expected_num = match op { + PlaceOp::Deref => 0, + PlaceOp::Index => 1, + } + if generics.has_self { 1 } else { 0 }; + let num_generics = generics.count(); + if num_generics != expected_num { + return None; + } + } + imm_tr.and_then(|trait_did| { self.lookup_method_in_trait( span, @@ -177,6 +193,22 @@ 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 let Some(trait_did) = mut_tr { + let generics = self.tcx.generics_of(trait_did); + let expected_num = match op { + PlaceOp::Deref => 0, + PlaceOp::Index => 1, + } + if generics.has_self { 1 } else { 0 }; + let num_generics = generics.count(); + if num_generics != expected_num { + return None; + } + } + mut_tr.and_then(|trait_did| { self.lookup_method_in_trait( span, |
