Skip to content

Commit 1bfd987

Browse files
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`. ```
2 parents a181806 + 7b30198 commit 1bfd987

13 files changed

+263
-7
lines changed

compiler/rustc_passes/src/lang_items.rs

+124-3
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;
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)) => {
@@ -180,6 +180,127 @@ impl LanguageItemCollector<'tcx> {
180180
self.items.groups[group as usize].push(item_def_id);
181181
}
182182
}
183+
184+
// Like collect_item() above, but also checks whether the lang item is declared
185+
// with the right number of generic arguments if it is a trait.
186+
fn collect_item_extended(&mut self, item_index: usize, hir_id: HirId, span: Span) {
187+
let item_def_id = self.tcx.hir().local_def_id(hir_id).to_def_id();
188+
let lang_item = LangItem::from_u32(item_index as u32).unwrap();
189+
let name = lang_item.name();
190+
191+
self.collect_item(item_index, item_def_id);
192+
193+
// Now check whether the lang_item has the expected number of generic
194+
// arguments if it is a trait. Generally speaking, binary and indexing
195+
// operations have one (for the RHS/index), unary operations have none,
196+
// and the rest also have none except for the closure traits (one for
197+
// the argument list), generators (one for the resume argument),
198+
// ordering/equality relations (one for the RHS), and various conversion
199+
// traits.
200+
201+
let expected_num = match lang_item {
202+
// Binary operations
203+
LangItem::Add
204+
| LangItem::Sub
205+
| LangItem::Mul
206+
| LangItem::Div
207+
| LangItem::Rem
208+
| LangItem::BitXor
209+
| LangItem::BitAnd
210+
| LangItem::BitOr
211+
| LangItem::Shl
212+
| LangItem::Shr
213+
| LangItem::AddAssign
214+
| LangItem::SubAssign
215+
| LangItem::MulAssign
216+
| LangItem::DivAssign
217+
| LangItem::RemAssign
218+
| LangItem::BitXorAssign
219+
| LangItem::BitAndAssign
220+
| LangItem::BitOrAssign
221+
| LangItem::ShlAssign
222+
| LangItem::ShrAssign
223+
| LangItem::Index
224+
| LangItem::IndexMut
225+
226+
// Miscellaneous
227+
| LangItem::Unsize
228+
| LangItem::CoerceUnsized
229+
| LangItem::DispatchFromDyn
230+
| LangItem::Fn
231+
| LangItem::FnMut
232+
| LangItem::FnOnce
233+
| LangItem::Generator
234+
| LangItem::PartialEq
235+
| LangItem::PartialOrd
236+
=> Some(1),
237+
238+
// Unary operations
239+
LangItem::Neg
240+
| LangItem::Not
241+
242+
// Miscellaneous
243+
| LangItem::Deref
244+
| LangItem::DerefMut
245+
| LangItem::Sized
246+
| LangItem::StructuralPeq
247+
| LangItem::StructuralTeq
248+
| LangItem::Copy
249+
| LangItem::Clone
250+
| LangItem::Sync
251+
| LangItem::DiscriminantKind
252+
| LangItem::PointeeTrait
253+
| LangItem::Freeze
254+
| LangItem::Drop
255+
| LangItem::Receiver
256+
| LangItem::Future
257+
| LangItem::Unpin
258+
| LangItem::Termination
259+
| LangItem::Try
260+
| LangItem::Send
261+
| LangItem::UnwindSafe
262+
| LangItem::RefUnwindSafe
263+
=> Some(0),
264+
265+
// Not a trait
266+
_ => None,
267+
};
268+
269+
if let Some(expected_num) = expected_num {
270+
let (actual_num, generics_span) = match self.tcx.hir().get(hir_id) {
271+
hir::Node::Item(hir::Item {
272+
kind: hir::ItemKind::Trait(_, _, generics, ..),
273+
..
274+
}) => (generics.params.len(), generics.span),
275+
_ => bug!("op/index/deref lang item target is not a trait: {:?}", lang_item),
276+
};
277+
278+
if expected_num != actual_num {
279+
// We are issuing E0718 "incorrect target" here, because while the
280+
// item kind of the target is correct, the target is still wrong
281+
// because of the wrong number of generic arguments.
282+
struct_span_err!(
283+
self.tcx.sess,
284+
span,
285+
E0718,
286+
"`{}` language item must be applied to a trait with {} generic argument{}",
287+
name,
288+
expected_num,
289+
pluralize!(expected_num)
290+
)
291+
.span_label(
292+
generics_span,
293+
format!(
294+
"this trait has {} generic argument{}, not {}",
295+
actual_num,
296+
pluralize!(actual_num),
297+
expected_num
298+
),
299+
)
300+
.emit();
301+
}
302+
}
303+
}
183304
}
184305

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

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

+2-2
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/mod.rs

+11
Original file line numberDiff line numberDiff line change
@@ -1187,3 +1187,14 @@ fn fatally_break_rust(sess: &Session) {
11871187
fn potentially_plural_count(count: usize, word: &str) -> String {
11881188
format!("{} {}{}", count, word, pluralize!(count))
11891189
}
1190+
1191+
fn has_expected_num_generic_args<'tcx>(
1192+
tcx: TyCtxt<'tcx>,
1193+
trait_did: Option<DefId>,
1194+
expected: usize,
1195+
) -> bool {
1196+
trait_did.map_or(true, |trait_did| {
1197+
let generics = tcx.generics_of(trait_did);
1198+
generics.count() == expected + if generics.has_self { 1 } else { 0 }
1199+
})
1200+
}

compiler/rustc_typeck/src/check/op.rs

+18-1
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
//! Code related to processing overloaded binary and unary operators.
22
33
use super::method::MethodCallee;
4-
use super::FnCtxt;
4+
use super::{has_expected_num_generic_args, FnCtxt};
55
use rustc_ast as ast;
66
use rustc_errors::{self, struct_span_err, Applicability, DiagnosticBuilder};
77
use rustc_hir as hir;
@@ -795,6 +795,23 @@ 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 !has_expected_num_generic_args(
804+
self.tcx,
805+
trait_did,
806+
match op {
807+
// Binary ops have a generic right-hand side, unary ops don't
808+
Op::Binary(..) => 1,
809+
Op::Unary(..) => 0,
810+
},
811+
) {
812+
return Err(());
813+
}
814+
798815
let method = trait_did.and_then(|trait_did| {
799816
let opname = Ident::with_dummy_span(opname);
800817
self.lookup_method_in_trait(span, opname, trait_did, lhs_ty, Some(other_tys))

compiler/rustc_typeck/src/check/place_op.rs

+31-1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
use crate::check::method::MethodCallee;
2-
use crate::check::{FnCtxt, PlaceOp};
2+
use crate::check::{has_expected_num_generic_args, FnCtxt, PlaceOp};
33
use rustc_hir as hir;
44
use rustc_infer::infer::type_variable::{TypeVariableOrigin, TypeVariableOriginKind};
55
use rustc_infer::infer::InferOk;
@@ -153,6 +153,21 @@ 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 !has_expected_num_generic_args(
161+
self.tcx,
162+
imm_tr,
163+
match op {
164+
PlaceOp::Deref => 0,
165+
PlaceOp::Index => 1,
166+
},
167+
) {
168+
return None;
169+
}
170+
156171
imm_tr.and_then(|trait_did| {
157172
self.lookup_method_in_trait(
158173
span,
@@ -177,6 +192,21 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
177192
PlaceOp::Deref => (self.tcx.lang_items().deref_mut_trait(), sym::deref_mut),
178193
PlaceOp::Index => (self.tcx.lang_items().index_mut_trait(), sym::index_mut),
179194
};
195+
196+
// If the lang item was declared incorrectly, stop here so that we don't
197+
// run into an ICE (#83893). The error is reported where the lang item is
198+
// declared.
199+
if !has_expected_num_generic_args(
200+
self.tcx,
201+
mut_tr,
202+
match op {
203+
PlaceOp::Deref => 0,
204+
PlaceOp::Index => 1,
205+
},
206+
) {
207+
return None;
208+
}
209+
180210
mut_tr.and_then(|trait_did| {
181211
self.lookup_method_in_trait(
182212
span,
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+
}
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`.
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+
}
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)