Skip to content

Commit d8286fe

Browse files
committed
Lint against iterator functions that panics when N is zero
1 parent 5c5722b commit d8286fe

File tree

4 files changed

+153
-4
lines changed

4 files changed

+153
-4
lines changed

compiler/rustc_mir_transform/src/errors.rs

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -159,6 +159,14 @@ impl AssertLintKind {
159159
}
160160
}
161161

162+
#[derive(Diagnostic)]
163+
#[diag("this operation will panic at runtime")]
164+
pub(crate) struct ConstNIsZero {
165+
#[label("const parameter `{$const_param_name}` is zero")]
166+
pub const_param_span: Span,
167+
pub const_param_name: Symbol,
168+
}
169+
162170
#[derive(Diagnostic)]
163171
#[diag("call to inline assembly that may unwind")]
164172
pub(crate) struct AsmUnwindCall {

compiler/rustc_mir_transform/src/known_panics_lint.rs

Lines changed: 38 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -10,19 +10,22 @@ 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::{
22+
self, ConstInt, GenericArgKind, GenericParamDefKind, ScalarInt, Ty, TyCtxt, TypeVisitableExt,
23+
};
24+
use rustc_session::lint::builtin::UNCONDITIONAL_PANIC;
2225
use rustc_span::Span;
2326
use tracing::{debug, instrument, trace};
2427

25-
use crate::errors::{AssertLint, AssertLintKind};
28+
use crate::errors::{AssertLint, AssertLintKind, ConstNIsZero};
2629

2730
pub(super) struct KnownPanicsLint;
2831

@@ -765,6 +768,38 @@ impl<'tcx> Visitor<'tcx> for ConstPropagator<'_, 'tcx> {
765768
}
766769
// We failed to evaluate the discriminant, fallback to visiting all successors.
767770
}
771+
TerminatorKind::Call { func, args: _, .. } => {
772+
if let Some((def_id, generic_args)) = func.const_fn_def() {
773+
for (index, arg) in generic_args.iter().enumerate() {
774+
if let GenericArgKind::Const(ct) = arg.kind() {
775+
let generics = self.tcx.generics_of(def_id);
776+
let param_def = generics.param_at(index, self.tcx);
777+
778+
if let GenericParamDefKind::Const { .. } = param_def.kind
779+
&& find_attr!(self.tcx, param_def.def_id, RustcPanicsWhenZero)
780+
&& let Some(0) = ct.try_to_target_usize(self.tcx)
781+
{
782+
// We managed to figure-out that the value of a
783+
// `#[rustc_panics_when_zero]` const-generic parameter is zero.
784+
//
785+
// Let's report it as an unconditional panic.
786+
let source_info = self.body.source_info(location);
787+
if let Some(lint_root) = self.lint_root(*source_info) {
788+
self.tcx.emit_node_span_lint(
789+
UNCONDITIONAL_PANIC,
790+
lint_root,
791+
source_info.span,
792+
ConstNIsZero {
793+
const_param_span: source_info.span,
794+
const_param_name: param_def.name,
795+
},
796+
);
797+
}
798+
}
799+
}
800+
}
801+
}
802+
}
768803
// None of these have Operands to const-propagate.
769804
TerminatorKind::Goto { .. }
770805
| TerminatorKind::UnwindResume
@@ -777,7 +812,6 @@ impl<'tcx> Visitor<'tcx> for ConstPropagator<'_, 'tcx> {
777812
| TerminatorKind::CoroutineDrop
778813
| TerminatorKind::FalseEdge { .. }
779814
| TerminatorKind::FalseUnwind { .. }
780-
| TerminatorKind::Call { .. }
781815
| TerminatorKind::InlineAsm { .. } => {}
782816
}
783817

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

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

0 commit comments

Comments
 (0)