about summary refs log tree commit diff
path: root/compiler
diff options
context:
space:
mode:
Diffstat (limited to 'compiler')
-rw-r--r--compiler/rustc_passes/src/lang_items.rs127
-rw-r--r--compiler/rustc_typeck/src/check/method/mod.rs4
-rw-r--r--compiler/rustc_typeck/src/check/mod.rs11
-rw-r--r--compiler/rustc_typeck/src/check/op.rs19
-rw-r--r--compiler/rustc_typeck/src/check/place_op.rs32
5 files changed, 186 insertions, 7 deletions
diff --git a/compiler/rustc_passes/src/lang_items.rs b/compiler/rustc_passes/src/lang_items.rs
index cfc18062d53..118fcca4508 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;
 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 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/mod.rs b/compiler/rustc_typeck/src/check/mod.rs
index d6db2e1d76f..ad7853b7cd0 100644
--- a/compiler/rustc_typeck/src/check/mod.rs
+++ b/compiler/rustc_typeck/src/check/mod.rs
@@ -1187,3 +1187,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<DefId>,
+    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 567cb1a90d0..963436d05d8 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 5bd385107ca..a63aec07ad1 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,