Skip to content

Commit 0a8cacf

Browse files
committed
Lint against iterator functions that panics when N is zero
1 parent 70118e7 commit 0a8cacf

4 files changed

Lines changed: 138 additions & 7 deletions

File tree

compiler/rustc_mir_transform/src/errors.rs

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -122,10 +122,15 @@ pub(crate) struct UnknownPassName<'a> {
122122

123123
pub(crate) struct AssertLint<P> {
124124
pub span: Span,
125-
pub assert_kind: AssertKind<P>,
125+
pub message: AssertLintMessage<P>,
126126
pub lint_kind: AssertLintKind,
127127
}
128128

129+
pub(crate) enum AssertLintMessage<P> {
130+
AssertKind(AssertKind<P>),
131+
Message(&'static str),
132+
}
133+
129134
pub(crate) enum AssertLintKind {
130135
ArithmeticOverflow,
131136
UnconditionalPanic,
@@ -145,7 +150,14 @@ impl<'a, P: std::fmt::Debug> Diagnostic<'a, ()> for AssertLint<P> {
145150
}
146151
},
147152
);
148-
diag.span_label(self.span, self.assert_kind.to_string());
153+
match self.message {
154+
AssertLintMessage::AssertKind(assert_kind) => {
155+
diag.span_label(self.span, assert_kind.to_string());
156+
}
157+
AssertLintMessage::Message(msg) => {
158+
diag.span_label(self.span, msg);
159+
}
160+
}
149161
diag
150162
}
151163
}

compiler/rustc_mir_transform/src/known_panics_lint.rs

Lines changed: 40 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -10,19 +10,19 @@ use rustc_const_eval::interpret::{
1010
ImmTy, InterpCx, InterpResult, Projectable, Scalar, format_interp_error, interp_ok,
1111
};
1212
use rustc_data_structures::fx::FxHashSet;
13-
use rustc_hir::HirId;
1413
use rustc_hir::def::DefKind;
14+
use rustc_hir::{HirId, find_attr};
1515
use rustc_index::IndexVec;
1616
use rustc_index::bit_set::DenseBitSet;
1717
use rustc_middle::bug;
1818
use rustc_middle::mir::visit::{MutatingUseContext, NonMutatingUseContext, PlaceContext, Visitor};
1919
use rustc_middle::mir::*;
2020
use rustc_middle::ty::layout::{LayoutError, LayoutOf, LayoutOfHelpers, TyAndLayout};
21-
use rustc_middle::ty::{self, ConstInt, ScalarInt, Ty, TyCtxt, TypeVisitableExt};
21+
use rustc_middle::ty::{self, ConstInt, GenericArgKind, ScalarInt, Ty, TyCtxt, TypeVisitableExt};
2222
use rustc_span::Span;
2323
use tracing::{debug, instrument, trace};
2424

25-
use crate::errors::{AssertLint, AssertLintKind};
25+
use crate::errors::{AssertLint, AssertLintKind, AssertLintMessage};
2626

2727
pub(super) struct KnownPanicsLint;
2828

@@ -288,6 +288,25 @@ impl<'mir, 'tcx> ConstPropagator<'mir, 'tcx> {
288288
}
289289
}
290290

291+
fn report_assert_message(
292+
&self,
293+
location: Location,
294+
lint_kind: AssertLintKind,
295+
msg: &'static str,
296+
) {
297+
let source_info = self.body.source_info(location);
298+
if let Some(lint_root) = self.lint_root(*source_info) {
299+
let span = source_info.span;
300+
let message = AssertLintMessage::<()>::Message(msg);
301+
self.tcx.emit_node_span_lint(
302+
lint_kind.lint(),
303+
lint_root,
304+
span,
305+
AssertLint { span, message, lint_kind },
306+
);
307+
}
308+
}
309+
291310
fn report_assert_as_lint(
292311
&self,
293312
location: Location,
@@ -297,11 +316,12 @@ impl<'mir, 'tcx> ConstPropagator<'mir, 'tcx> {
297316
let source_info = self.body.source_info(location);
298317
if let Some(lint_root) = self.lint_root(*source_info) {
299318
let span = source_info.span;
319+
let message = AssertLintMessage::AssertKind(assert_kind);
300320
self.tcx.emit_node_span_lint(
301321
lint_kind.lint(),
302322
lint_root,
303323
span,
304-
AssertLint { span, assert_kind, lint_kind },
324+
AssertLint { span, message, lint_kind },
305325
);
306326
}
307327
}
@@ -765,6 +785,22 @@ impl<'tcx> Visitor<'tcx> for ConstPropagator<'_, 'tcx> {
765785
}
766786
// We failed to evaluate the discriminant, fallback to visiting all successors.
767787
}
788+
TerminatorKind::Call { func, args: _, .. } => {
789+
if let Some((def_id, generic_args)) = func.const_fn_def() {
790+
for arg in generic_args {
791+
if let GenericArgKind::Const(ct) = arg.kind()
792+
&& find_attr!(self.tcx, def_id, RustcPanicsWhenNIsZero)
793+
&& let Some(0) = ct.try_to_target_usize(self.tcx)
794+
{
795+
self.report_assert_message(
796+
location,
797+
AssertLintKind::UnconditionalPanic,
798+
"const parameter `N` is zero",
799+
);
800+
}
801+
}
802+
}
803+
}
768804
// None of these have Operands to const-propagate.
769805
TerminatorKind::Goto { .. }
770806
| TerminatorKind::UnwindResume
@@ -777,7 +813,6 @@ impl<'tcx> Visitor<'tcx> for ConstPropagator<'_, 'tcx> {
777813
| TerminatorKind::CoroutineDrop
778814
| TerminatorKind::FalseEdge { .. }
779815
| TerminatorKind::FalseUnwind { .. }
780-
| TerminatorKind::Call { .. }
781816
| TerminatorKind::InlineAsm { .. } => {}
782817
}
783818

tests/ui/lint/const-n-is-zero.rs

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
// Test that core functions annotated with `#[rustc_panics_when_n_is_zero]` lint when `N` is zero
2+
3+
//@ build-fail
4+
5+
const ZERO: usize = 0;
6+
const ONE: usize = 1;
7+
8+
fn main() {
9+
let s = [1, 2, 3, 4];
10+
11+
let _ = s.array_windows::<0>();
12+
//~^ ERROR this operation will panic at runtime
13+
//~| NOTE `#[deny(unconditional_panic)]` on by default
14+
//~| NOTE const parameter `N` is zero
15+
16+
let _ = s.as_chunks::<{ 0 }>();
17+
//~^ ERROR this operation will panic at runtime
18+
//~| NOTE const parameter `N` is zero
19+
//
20+
let _ = s.as_rchunks::<{ 1 - 1 }>();
21+
//~^ ERROR this operation will panic at runtime
22+
//~| NOTE const parameter `N` is zero
23+
24+
let mut m = [1, 2, 3, 4];
25+
26+
let _ = m.as_chunks_mut::<ZERO>();
27+
//~^ ERROR this operation will panic at runtime
28+
//~| NOTE const parameter `N` is zero
29+
30+
let _ = m.as_rchunks_mut::<{ if ZERO == 0 { 0 } else { 1 } }>();
31+
//~^ ERROR this operation will panic at runtime
32+
//~| NOTE const parameter `N` is zero
33+
34+
let _ = s.array_windows().any(|[]| true);
35+
//~^ ERROR this operation will panic at runtime
36+
//~| NOTE const parameter `N` is zero
37+
38+
// Shouldn't lint
39+
let _ = s.array_windows::<2>();
40+
let _ = s.as_chunks::<1>();
41+
let _ = m.as_chunks_mut::<ONE>();
42+
let _ = m.as_rchunks::<{ 1 + 1 }>();
43+
let _ = m.as_rchunks_mut::<{ if ZERO == 1 { 0 } else { 5 } }>();
44+
}
Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
error: this operation will panic at runtime
2+
--> $DIR/const-n-is-zero.rs:11:13
3+
|
4+
LL | let _ = s.array_windows::<0>();
5+
| ^^^^^^^^^^^^^^^^^^^^^^ const parameter `N` is zero
6+
|
7+
= note: `#[deny(unconditional_panic)]` on by default
8+
9+
error: this operation will panic at runtime
10+
--> $DIR/const-n-is-zero.rs:16:13
11+
|
12+
LL | let _ = s.as_chunks::<{ 0 }>();
13+
| ^^^^^^^^^^^^^^^^^^^^^^ const parameter `N` is zero
14+
15+
error: this operation will panic at runtime
16+
--> $DIR/const-n-is-zero.rs:20:13
17+
|
18+
LL | let _ = s.as_rchunks::<{ 1 - 1 }>();
19+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^ const parameter `N` is zero
20+
21+
error: this operation will panic at runtime
22+
--> $DIR/const-n-is-zero.rs:26:13
23+
|
24+
LL | let _ = m.as_chunks_mut::<ZERO>();
25+
| ^^^^^^^^^^^^^^^^^^^^^^^^^ const parameter `N` is zero
26+
27+
error: this operation will panic at runtime
28+
--> $DIR/const-n-is-zero.rs:30:13
29+
|
30+
LL | let _ = m.as_rchunks_mut::<{ if ZERO == 0 { 0 } else { 1 } }>();
31+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ const parameter `N` is zero
32+
33+
error: this operation will panic at runtime
34+
--> $DIR/const-n-is-zero.rs:34:13
35+
|
36+
LL | let _ = s.array_windows().any(|[]| true);
37+
| ^^^^^^^^^^^^^^^^^ const parameter `N` is zero
38+
39+
error: aborting due to 6 previous errors
40+

0 commit comments

Comments
 (0)