about summary refs log tree commit diff
diff options
context:
space:
mode:
authorGuillaume Gomez <guillaume1.gomez@gmail.com>2021-05-18 14:08:51 +0200
committerGitHub <noreply@github.com>2021-05-18 14:08:51 +0200
commit1bfd987f6911ec463c8d1cc10f80dd77a883d92c (patch)
tree57a91ed45200ea25d8eac13a64f4c18d210ca63d
parenta181806b8cbd21736b86ee6ee2f4797c0a4a58b4 (diff)
parent7b301985faa73b1404cbc21ffe7c7f859a293448 (diff)
downloadrust-1bfd987f6911ec463c8d1cc10f80dd77a883d92c.tar.gz
rust-1bfd987f6911ec463c8d1cc10f80dd77a883d92c.zip
Rollup merge of #85339 - FabianWolff:issue-83893, r=varkor
Report an error if a lang item has the wrong number of generic arguments

This pull request fixes #83893. The issue is that the lang item code currently checks whether the lang item has the correct item kind (e.g. a `#[lang="add"]` has to be a trait), but not whether the item has the correct number of generic arguments.

This can lead to an "index out of bounds" ICE when the compiler tries to create more substitutions than there are suitable types available (if the lang item was declared with too many generic arguments).

For instance, here is a reduced ("reduced" in the sense that it does not trigger additional errors) version of the example given in #83893:
```rust
#![feature(lang_items,no_core)]
#![no_core]
#![crate_type="lib"]

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

#[lang = "add"]
trait MyAdd<'a, T> {}

fn ice() {
    let r = 5;
    let a = 6;
    r + a
}
```
On current nightly, this immediately causes an ICE without any warnings or errors emitted. With the changes in this PR, however, I get no ICE and two errors:
```
error[E0718]: `add` language item must be applied to a trait with 1 generic argument
 --> pr-ex.rs:8:1
  |
8 | #[lang = "add"]
  | ^^^^^^^^^^^^^^^
9 | trait MyAdd<'a, T> {}
  |            ------- this trait has 2 generic arguments, not 1

error[E0369]: cannot add `{integer}` to `{integer}`
  --> pr-ex.rs:14:7
   |
14 |     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`.
```
-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
-rw-r--r--src/test/ui/lang-items/lang-item-missing-generator.rs (renamed from src/test/ui/lang-item-missing-generator.rs)0
-rw-r--r--src/test/ui/lang-items/lang-item-missing-generator.stderr (renamed from src/test/ui/lang-item-missing-generator.stderr)0
-rw-r--r--src/test/ui/lang-items/lang-item-missing.rs (renamed from src/test/ui/lang-item-missing.rs)0
-rw-r--r--src/test/ui/lang-items/lang-item-missing.stderr (renamed from src/test/ui/lang-item-missing.stderr)0
-rw-r--r--src/test/ui/lang-items/wrong-number-generic-args-add.rs20
-rw-r--r--src/test/ui/lang-items/wrong-number-generic-args-add.stderr20
-rw-r--r--src/test/ui/lang-items/wrong-number-generic-args-index.rs19
-rw-r--r--src/test/ui/lang-items/wrong-number-generic-args-index.stderr18
13 files changed, 263 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,
diff --git a/src/test/ui/lang-item-missing-generator.rs b/src/test/ui/lang-items/lang-item-missing-generator.rs
index 0c329542928..0c329542928 100644
--- a/src/test/ui/lang-item-missing-generator.rs
+++ b/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
index fa13bf0b127..fa13bf0b127 100644
--- a/src/test/ui/lang-item-missing-generator.stderr
+++ b/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
index 4e26343242e..4e26343242e 100644
--- a/src/test/ui/lang-item-missing.rs
+++ b/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
index f7516c7d377..f7516c7d377 100644
--- a/src/test/ui/lang-item-missing.stderr
+++ b/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 00000000000..9f4f2464a1e
--- /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 00000000000..6f89441fd28
--- /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 00000000000..1d90e63dc54
--- /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 00000000000..bc3f19ff276
--- /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`.