Skip to content

Commit 7217d76

Browse files
committed
Report an error if a lang item has the wrong number of generic arguments
1 parent b439be0 commit 7217d76

12 files changed

+212
-7
lines changed

compiler/rustc_passes/src/lang_items.rs

Lines changed: 83 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -13,12 +13,13 @@ use crate::weak_lang_items;
1313
use rustc_middle::middle::cstore::ExternCrate;
1414
use rustc_middle::ty::TyCtxt;
1515

16-
use rustc_errors::struct_span_err;
16+
use rustc_errors::{pluralize, struct_span_err};
1717
use rustc_hir as hir;
1818
use rustc_hir::def_id::{DefId, LOCAL_CRATE};
1919
use rustc_hir::itemlikevisit::ItemLikeVisitor;
2020
use rustc_hir::lang_items::{extract, ITEM_REFS};
2121
use rustc_hir::{HirId, LangItem, LanguageItems, Target};
22+
use rustc_span::Span;
2223

2324
use rustc_middle::ty::query::Providers;
2425

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

102102
fn collect_item(&mut self, item_index: usize, item_def_id: DefId) {
103+
let lang_item = LangItem::from_u32(item_index as u32).unwrap();
104+
let name = lang_item.name();
105+
103106
// Check for duplicates.
104107
if let Some(original_def_id) = self.items.items[item_index] {
105108
if original_def_id != item_def_id {
106-
let lang_item = LangItem::from_u32(item_index as u32).unwrap();
107-
let name = lang_item.name();
108109
let mut err = match self.tcx.hir().span_if_local(item_def_id) {
109110
Some(span) => struct_span_err!(
110111
self.tcx.sess,
@@ -180,6 +181,83 @@ impl LanguageItemCollector<'tcx> {
180181
self.items.groups[group as usize].push(item_def_id);
181182
}
182183
}
184+
185+
fn collect_item_extended(&mut self, item_index: usize, hir_id: HirId, span: Span) {
186+
let item_def_id = self.tcx.hir().local_def_id(hir_id).to_def_id();
187+
let lang_item = LangItem::from_u32(item_index as u32).unwrap();
188+
let name = lang_item.name();
189+
190+
self.collect_item(item_index, item_def_id);
191+
192+
// Now check whether the lang_item has the expected number of generic
193+
// arguments. Binary and indexing operations have one (for the RHS/index),
194+
// unary operations have no generic arguments.
195+
196+
let expected_num = match lang_item {
197+
LangItem::Add
198+
| LangItem::Sub
199+
| LangItem::Mul
200+
| LangItem::Div
201+
| LangItem::Rem
202+
| LangItem::BitXor
203+
| LangItem::BitAnd
204+
| LangItem::BitOr
205+
| LangItem::Shl
206+
| LangItem::Shr
207+
| LangItem::AddAssign
208+
| LangItem::SubAssign
209+
| LangItem::MulAssign
210+
| LangItem::DivAssign
211+
| LangItem::RemAssign
212+
| LangItem::BitXorAssign
213+
| LangItem::BitAndAssign
214+
| LangItem::BitOrAssign
215+
| LangItem::ShlAssign
216+
| LangItem::ShrAssign
217+
| LangItem::Index
218+
| LangItem::IndexMut => Some(1),
219+
220+
LangItem::Neg | LangItem::Not | LangItem::Deref | LangItem::DerefMut => Some(0),
221+
222+
// FIXME: add more cases?
223+
_ => None,
224+
};
225+
226+
if let Some(expected_num) = expected_num {
227+
let (actual_num, generics_span) = match self.tcx.hir().get(hir_id) {
228+
hir::Node::Item(hir::Item {
229+
kind: hir::ItemKind::Trait(_, _, generics, ..),
230+
..
231+
}) => (generics.params.len(), generics.span),
232+
_ => bug!("op/index/deref lang item target is not a trait: {:?}", lang_item),
233+
};
234+
235+
if expected_num != actual_num {
236+
// We are issuing E0718 "incorrect target" here, because while the
237+
// item kind of the target is correct, the target is still wrong
238+
// because of the wrong number of generic arguments.
239+
struct_span_err!(
240+
self.tcx.sess,
241+
span,
242+
E0718,
243+
"`{}` language item must be applied to a trait with {} generic argument{}",
244+
name,
245+
expected_num,
246+
pluralize!(expected_num)
247+
)
248+
.span_label(
249+
generics_span,
250+
format!(
251+
"this trait has {} generic argument{}, not {}",
252+
actual_num,
253+
pluralize!(actual_num),
254+
expected_num
255+
),
256+
)
257+
.emit();
258+
}
259+
}
260+
}
183261
}
184262

185263
/// Traverses and collects all the lang items in all crates.

compiler/rustc_typeck/src/check/method/mod.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -303,8 +303,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
303303
opt_input_types: Option<&[Ty<'tcx>]>,
304304
) -> Option<InferOk<'tcx, MethodCallee<'tcx>>> {
305305
debug!(
306-
"lookup_in_trait_adjusted(self_ty={:?}, m_name={}, trait_def_id={:?})",
307-
self_ty, m_name, trait_def_id
306+
"lookup_in_trait_adjusted(self_ty={:?}, m_name={}, trait_def_id={:?}, opt_input_types={:?})",
307+
self_ty, m_name, trait_def_id, opt_input_types
308308
);
309309

310310
// Construct a trait-reference `self_ty : Trait<input_tys>`

compiler/rustc_typeck/src/check/op.rs

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -795,6 +795,24 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
795795
lhs_ty, op, opname, trait_did
796796
);
797797

798+
// Catches cases like #83893, where a lang item is declared with the
799+
// wrong number of generic arguments. Should have yielded an error
800+
// elsewhere by now, but we have to catch it here so that we do not
801+
// index `other_tys` out of bounds (if the lang item has too many
802+
// generic arguments, `other_tys` is too short).
803+
if let Some(trait_did) = trait_did {
804+
let generics = self.tcx.generics_of(trait_did);
805+
let expected_num = match op {
806+
// Binary ops have a generic right-hand side, unary ops don't
807+
Op::Binary(..) => 1,
808+
Op::Unary(..) => 0,
809+
} + if generics.has_self { 1 } else { 0 };
810+
let num_generics = generics.count();
811+
if num_generics != expected_num {
812+
return Err(());
813+
}
814+
}
815+
798816
let method = trait_did.and_then(|trait_did| {
799817
let opname = Ident::with_dummy_span(opname);
800818
self.lookup_method_in_trait(span, opname, trait_did, lhs_ty, Some(other_tys))

compiler/rustc_typeck/src/check/place_op.rs

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -153,6 +153,22 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
153153
PlaceOp::Deref => (self.tcx.lang_items().deref_trait(), sym::deref),
154154
PlaceOp::Index => (self.tcx.lang_items().index_trait(), sym::index),
155155
};
156+
157+
// If the lang item was declared incorrectly, stop here so that we don't
158+
// run into an ICE (#83893). The error is reported where the lang item is
159+
// declared.
160+
if let Some(trait_did) = imm_tr {
161+
let generics = self.tcx.generics_of(trait_did);
162+
let expected_num = match op {
163+
PlaceOp::Deref => 0,
164+
PlaceOp::Index => 1,
165+
} + if generics.has_self { 1 } else { 0 };
166+
let num_generics = generics.count();
167+
if num_generics != expected_num {
168+
return None;
169+
}
170+
}
171+
156172
imm_tr.and_then(|trait_did| {
157173
self.lookup_method_in_trait(
158174
span,
@@ -177,6 +193,22 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
177193
PlaceOp::Deref => (self.tcx.lang_items().deref_mut_trait(), sym::deref_mut),
178194
PlaceOp::Index => (self.tcx.lang_items().index_mut_trait(), sym::index_mut),
179195
};
196+
197+
// If the lang item was declared incorrectly, stop here so that we don't
198+
// run into an ICE (#83893). The error is reported where the lang item is
199+
// declared.
200+
if let Some(trait_did) = mut_tr {
201+
let generics = self.tcx.generics_of(trait_did);
202+
let expected_num = match op {
203+
PlaceOp::Deref => 0,
204+
PlaceOp::Index => 1,
205+
} + if generics.has_self { 1 } else { 0 };
206+
let num_generics = generics.count();
207+
if num_generics != expected_num {
208+
return None;
209+
}
210+
}
211+
180212
mut_tr.and_then(|trait_did| {
181213
self.lookup_method_in_trait(
182214
span,
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
// Checks whether declaring a lang item with the wrong number
2+
// of generic arguments crashes the compiler (issue #83893).
3+
4+
#![feature(lang_items,no_core)]
5+
#![no_core]
6+
#![crate_type="lib"]
7+
8+
#[lang = "sized"]
9+
trait MySized {}
10+
11+
#[lang = "add"]
12+
trait MyAdd<'a, T> {}
13+
//~^^ ERROR: `add` language item must be applied to a trait with 1 generic argument [E0718]
14+
15+
fn ice() {
16+
let r = 5;
17+
let a = 6;
18+
r + a
19+
//~^ ERROR: cannot add `{integer}` to `{integer}` [E0369]
20+
}
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
error[E0718]: `add` language item must be applied to a trait with 1 generic argument
2+
--> $DIR/wrong-number-generic-args-add.rs:11:1
3+
|
4+
LL | #[lang = "add"]
5+
| ^^^^^^^^^^^^^^^
6+
LL | trait MyAdd<'a, T> {}
7+
| ------- this trait has 2 generic arguments, not 1
8+
9+
error[E0369]: cannot add `{integer}` to `{integer}`
10+
--> $DIR/wrong-number-generic-args-add.rs:18:7
11+
|
12+
LL | r + a
13+
| - ^ - {integer}
14+
| |
15+
| {integer}
16+
17+
error: aborting due to 2 previous errors
18+
19+
Some errors have detailed explanations: E0369, E0718.
20+
For more information about an error, try `rustc --explain E0369`.
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
// Checks whether declaring a lang item with the wrong number
2+
// of generic arguments crashes the compiler (issue #83893).
3+
4+
#![feature(lang_items,no_core)]
5+
#![no_core]
6+
#![crate_type="lib"]
7+
8+
#[lang = "sized"]
9+
trait MySized {}
10+
11+
#[lang = "index"]
12+
trait MyIndex<'a, T> {}
13+
//~^^ ERROR: `index` language item must be applied to a trait with 1 generic argument [E0718]
14+
15+
fn ice() {
16+
let arr = [0; 5];
17+
let _ = arr[2];
18+
//~^ ERROR: cannot index into a value of type `[{integer}; 5]` [E0608]
19+
}
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
error[E0718]: `index` language item must be applied to a trait with 1 generic argument
2+
--> $DIR/wrong-number-generic-args-index.rs:11:1
3+
|
4+
LL | #[lang = "index"]
5+
| ^^^^^^^^^^^^^^^^^
6+
LL | trait MyIndex<'a, T> {}
7+
| ------- this trait has 2 generic arguments, not 1
8+
9+
error[E0608]: cannot index into a value of type `[{integer}; 5]`
10+
--> $DIR/wrong-number-generic-args-index.rs:17:13
11+
|
12+
LL | let _ = arr[2];
13+
| ^^^^^^
14+
15+
error: aborting due to 2 previous errors
16+
17+
Some errors have detailed explanations: E0608, E0718.
18+
For more information about an error, try `rustc --explain E0608`.

0 commit comments

Comments
 (0)