Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 17 additions & 0 deletions compiler/rustc_attr_parsing/src/attributes/rustc_internal.rs
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,23 @@ impl<S: Stage> NoArgsAttributeParser<S> for RustcNeverReturnsNullPtrParser {
]);
const CREATE: fn(Span) -> AttributeKind = |_| AttributeKind::RustcNeverReturnsNullPtr;
}

pub(crate) struct RustcPanicsWhenNIsZeroParser;

impl<S: Stage> NoArgsAttributeParser<S> for RustcPanicsWhenNIsZeroParser {
const PATH: &[Symbol] = &[sym::rustc_panics_when_n_is_zero];
const ON_DUPLICATE: OnDuplicate<S> = OnDuplicate::Error;
const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(&[
Allow(Target::Fn),
Allow(Target::Method(MethodKind::Inherent)),
Allow(Target::Method(MethodKind::Trait { body: false })),
Allow(Target::Method(MethodKind::Trait { body: true })),
Allow(Target::Method(MethodKind::TraitImpl)),
]);

const CREATE: fn(Span) -> AttributeKind = |_| AttributeKind::RustcPanicsWhenNIsZero;
}

pub(crate) struct RustcNoImplicitAutorefsParser;

impl<S: Stage> NoArgsAttributeParser<S> for RustcNoImplicitAutorefsParser {
Expand Down
1 change: 1 addition & 0 deletions compiler/rustc_attr_parsing/src/context.rs
Original file line number Diff line number Diff line change
Expand Up @@ -311,6 +311,7 @@ attribute_parsers!(
Single<WithoutArgs<RustcNonnullOptimizationGuaranteedParser>>,
Single<WithoutArgs<RustcNounwindParser>>,
Single<WithoutArgs<RustcOffloadKernelParser>>,
Single<WithoutArgs<RustcPanicsWhenNIsZeroParser>>,
Single<WithoutArgs<RustcParenSugarParser>>,
Single<WithoutArgs<RustcPassByValueParser>>,
Single<WithoutArgs<RustcPassIndirectlyInNonRusticAbisParser>>,
Expand Down
4 changes: 4 additions & 0 deletions compiler/rustc_feature/src/builtin_attrs.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1274,6 +1274,10 @@ pub static BUILTIN_ATTRIBUTES: &[BuiltinAttribute] = &[
EncodeCrossCrate::Yes,
"`#[rustc_never_returns_null_ptr]` is used to mark functions returning non-null pointers"
),
rustc_attr!(
rustc_panics_when_n_is_zero, Normal, template!(Word), ErrorFollowing, EncodeCrossCrate::Yes,
"`#[rustc_panics_when_n_is_zero]` is used to mark functions with a const generic argument who makes the function panics when it is zero"
),
rustc_attr!(
rustc_no_implicit_autorefs, AttributeType::Normal, template!(Word), ErrorFollowing, EncodeCrossCrate::Yes,
"`#[rustc_no_implicit_autorefs]` is used to mark functions for which an autoref to the dereference of a raw pointer should not be used as an argument"
Expand Down
3 changes: 3 additions & 0 deletions compiler/rustc_hir/src/attrs/data_structures.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1508,6 +1508,9 @@ pub enum AttributeKind {
/// Represents `#[rustc_offload_kernel]`
RustcOffloadKernel,

/// Represents `#[rustc_panics_when_n_is_zero]` (used for linting).
RustcPanicsWhenNIsZero,

/// Represents `#[rustc_paren_sugar]`.
RustcParenSugar(Span),

Expand Down
1 change: 1 addition & 0 deletions compiler/rustc_hir/src/attrs/encode_cross_crate.rs
Original file line number Diff line number Diff line change
Expand Up @@ -166,6 +166,7 @@ impl AttributeKind {
RustcObjcClass { .. } => No,
RustcObjcSelector { .. } => No,
RustcOffloadKernel => Yes,
RustcPanicsWhenNIsZero => Yes,
RustcParenSugar(..) => No,
RustcPassByValue(..) => Yes,
RustcPassIndirectlyInNonRusticAbis(..) => No,
Expand Down
16 changes: 14 additions & 2 deletions compiler/rustc_mir_transform/src/errors.rs
Original file line number Diff line number Diff line change
Expand Up @@ -122,10 +122,15 @@ pub(crate) struct UnknownPassName<'a> {

pub(crate) struct AssertLint<P> {
pub span: Span,
pub assert_kind: AssertKind<P>,
pub message: AssertLintMessage<P>,
pub lint_kind: AssertLintKind,
}

pub(crate) enum AssertLintMessage<P> {
AssertKind(AssertKind<P>),
Message(&'static str),
}

pub(crate) enum AssertLintKind {
ArithmeticOverflow,
UnconditionalPanic,
Expand All @@ -145,7 +150,14 @@ impl<'a, P: std::fmt::Debug> Diagnostic<'a, ()> for AssertLint<P> {
}
},
);
diag.span_label(self.span, self.assert_kind.to_string());
match self.message {
AssertLintMessage::AssertKind(assert_kind) => {
diag.span_label(self.span, assert_kind.to_string());
}
AssertLintMessage::Message(msg) => {
diag.span_label(self.span, msg);
}
}
diag
}
}
Expand Down
45 changes: 40 additions & 5 deletions compiler/rustc_mir_transform/src/known_panics_lint.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,19 +10,19 @@ use rustc_const_eval::interpret::{
ImmTy, InterpCx, InterpResult, Projectable, Scalar, format_interp_error, interp_ok,
};
use rustc_data_structures::fx::FxHashSet;
use rustc_hir::HirId;
use rustc_hir::def::DefKind;
use rustc_hir::{HirId, find_attr};
use rustc_index::IndexVec;
use rustc_index::bit_set::DenseBitSet;
use rustc_middle::bug;
use rustc_middle::mir::visit::{MutatingUseContext, NonMutatingUseContext, PlaceContext, Visitor};
use rustc_middle::mir::*;
use rustc_middle::ty::layout::{LayoutError, LayoutOf, LayoutOfHelpers, TyAndLayout};
use rustc_middle::ty::{self, ConstInt, ScalarInt, Ty, TyCtxt, TypeVisitableExt};
use rustc_middle::ty::{self, ConstInt, GenericArgKind, ScalarInt, Ty, TyCtxt, TypeVisitableExt};
use rustc_span::Span;
use tracing::{debug, instrument, trace};

use crate::errors::{AssertLint, AssertLintKind};
use crate::errors::{AssertLint, AssertLintKind, AssertLintMessage};

pub(super) struct KnownPanicsLint;

Expand Down Expand Up @@ -288,6 +288,25 @@ impl<'mir, 'tcx> ConstPropagator<'mir, 'tcx> {
}
}

fn report_assert_message(
&self,
location: Location,
lint_kind: AssertLintKind,
msg: &'static str,
) {
let source_info = self.body.source_info(location);
if let Some(lint_root) = self.lint_root(*source_info) {
let span = source_info.span;
let message = AssertLintMessage::<()>::Message(msg);
self.tcx.emit_node_span_lint(
lint_kind.lint(),
lint_root,
span,
AssertLint { span, message, lint_kind },
);
}
}

fn report_assert_as_lint(
&self,
location: Location,
Expand All @@ -297,11 +316,12 @@ impl<'mir, 'tcx> ConstPropagator<'mir, 'tcx> {
let source_info = self.body.source_info(location);
if let Some(lint_root) = self.lint_root(*source_info) {
let span = source_info.span;
let message = AssertLintMessage::AssertKind(assert_kind);
self.tcx.emit_node_span_lint(
lint_kind.lint(),
lint_root,
span,
AssertLint { span, assert_kind, lint_kind },
AssertLint { span, message, lint_kind },
);
}
}
Expand Down Expand Up @@ -765,6 +785,22 @@ impl<'tcx> Visitor<'tcx> for ConstPropagator<'_, 'tcx> {
}
// We failed to evaluate the discriminant, fallback to visiting all successors.
}
TerminatorKind::Call { func, args: _, .. } => {
if let Some((def_id, generic_args)) = func.const_fn_def() {
for arg in generic_args {
if let GenericArgKind::Const(ct) = arg.kind()
&& find_attr!(self.tcx, def_id, RustcPanicsWhenNIsZero)
&& let Some(0) = ct.try_to_target_usize(self.tcx)
{
self.report_assert_message(
location,
AssertLintKind::UnconditionalPanic,
"const parameter `N` is zero",
);
}
}
}
}
// None of these have Operands to const-propagate.
TerminatorKind::Goto { .. }
| TerminatorKind::UnwindResume
Expand All @@ -777,7 +813,6 @@ impl<'tcx> Visitor<'tcx> for ConstPropagator<'_, 'tcx> {
| TerminatorKind::CoroutineDrop
| TerminatorKind::FalseEdge { .. }
| TerminatorKind::FalseUnwind { .. }
| TerminatorKind::Call { .. }
| TerminatorKind::InlineAsm { .. } => {}
}

Expand Down
1 change: 1 addition & 0 deletions compiler/rustc_passes/src/check_attr.rs
Original file line number Diff line number Diff line change
Expand Up @@ -358,6 +358,7 @@ impl<'tcx> CheckAttrVisitor<'tcx> {
| AttributeKind::RustcObjcClass { .. }
| AttributeKind::RustcObjcSelector { .. }
| AttributeKind::RustcOffloadKernel
| AttributeKind::RustcPanicsWhenNIsZero
| AttributeKind::RustcParenSugar(..)
| AttributeKind::RustcPassByValue (..)
| AttributeKind::RustcPassIndirectlyInNonRusticAbis(..)
Expand Down
1 change: 1 addition & 0 deletions compiler/rustc_span/src/symbol.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1756,6 +1756,7 @@ symbols! {
rustc_objc_selector,
rustc_offload_kernel,
rustc_on_unimplemented,
rustc_panics_when_n_is_zero,
rustc_paren_sugar,
rustc_partition_codegened,
rustc_partition_reused,
Expand Down
7 changes: 7 additions & 0 deletions library/core/src/slice/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1332,6 +1332,7 @@ impl<T> [T] {
/// ```
#[stable(feature = "slice_as_chunks", since = "1.88.0")]
#[rustc_const_stable(feature = "slice_as_chunks", since = "1.88.0")]
#[rustc_panics_when_n_is_zero]
#[inline]
#[must_use]
#[track_caller]
Expand Down Expand Up @@ -1390,6 +1391,7 @@ impl<T> [T] {
/// ```
#[stable(feature = "slice_as_chunks", since = "1.88.0")]
#[rustc_const_stable(feature = "slice_as_chunks", since = "1.88.0")]
#[rustc_panics_when_n_is_zero]
#[inline]
#[track_caller]
#[must_use]
Expand Down Expand Up @@ -1437,6 +1439,7 @@ impl<T> [T] {
/// ```
#[stable(feature = "slice_as_chunks", since = "1.88.0")]
#[rustc_const_stable(feature = "slice_as_chunks", since = "1.88.0")]
#[rustc_panics_when_n_is_zero]
#[inline]
#[track_caller]
#[must_use]
Expand Down Expand Up @@ -1492,6 +1495,7 @@ impl<T> [T] {
/// ```
#[stable(feature = "slice_as_chunks", since = "1.88.0")]
#[rustc_const_stable(feature = "slice_as_chunks", since = "1.88.0")]
#[rustc_panics_when_n_is_zero]
#[inline]
#[must_use]
#[track_caller]
Expand Down Expand Up @@ -1546,6 +1550,7 @@ impl<T> [T] {
/// ```
#[stable(feature = "slice_as_chunks", since = "1.88.0")]
#[rustc_const_stable(feature = "slice_as_chunks", since = "1.88.0")]
#[rustc_panics_when_n_is_zero]
#[inline]
#[track_caller]
#[must_use]
Expand Down Expand Up @@ -1599,6 +1604,7 @@ impl<T> [T] {
/// ```
#[stable(feature = "slice_as_chunks", since = "1.88.0")]
#[rustc_const_stable(feature = "slice_as_chunks", since = "1.88.0")]
#[rustc_panics_when_n_is_zero]
#[inline]
#[track_caller]
#[must_use]
Expand Down Expand Up @@ -1641,6 +1647,7 @@ impl<T> [T] {
/// [`windows`]: slice::windows
#[stable(feature = "array_windows", since = "1.94.0")]
#[rustc_const_unstable(feature = "const_slice_make_iter", issue = "137737")]
#[rustc_panics_when_n_is_zero]
#[inline]
#[track_caller]
pub const fn array_windows<const N: usize>(&self) -> ArrayWindows<'_, T, N> {
Expand Down
44 changes: 44 additions & 0 deletions tests/ui/lint/const-n-is-zero.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
// Test that core functions annotated with `#[rustc_panics_when_n_is_zero]` lint when `N` is zero

//@ build-fail

const ZERO: usize = 0;
const ONE: usize = 1;

fn main() {
let s = [1, 2, 3, 4];

let _ = s.array_windows::<0>();
//~^ ERROR this operation will panic at runtime
//~| NOTE `#[deny(unconditional_panic)]` on by default
//~| NOTE const parameter `N` is zero

let _ = s.as_chunks::<{ 0 }>();
//~^ ERROR this operation will panic at runtime
//~| NOTE const parameter `N` is zero
//
let _ = s.as_rchunks::<{ 1 - 1 }>();
//~^ ERROR this operation will panic at runtime
//~| NOTE const parameter `N` is zero

let mut m = [1, 2, 3, 4];

let _ = m.as_chunks_mut::<ZERO>();
//~^ ERROR this operation will panic at runtime
//~| NOTE const parameter `N` is zero

let _ = m.as_rchunks_mut::<{ if ZERO == 0 { 0 } else { 1 } }>();
//~^ ERROR this operation will panic at runtime
//~| NOTE const parameter `N` is zero

let _ = s.array_windows().any(|[]| true);
//~^ ERROR this operation will panic at runtime
//~| NOTE const parameter `N` is zero

// Shouldn't lint
let _ = s.array_windows::<2>();
let _ = s.as_chunks::<1>();
let _ = m.as_chunks_mut::<ONE>();
let _ = m.as_rchunks::<{ 1 + 1 }>();
let _ = m.as_rchunks_mut::<{ if ZERO == 1 { 0 } else { 5 } }>();
}
40 changes: 40 additions & 0 deletions tests/ui/lint/const-n-is-zero.stderr
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
error: this operation will panic at runtime
--> $DIR/const-n-is-zero.rs:11:13
|
LL | let _ = s.array_windows::<0>();
| ^^^^^^^^^^^^^^^^^^^^^^ const parameter `N` is zero
|
= note: `#[deny(unconditional_panic)]` on by default

error: this operation will panic at runtime
--> $DIR/const-n-is-zero.rs:16:13
|
LL | let _ = s.as_chunks::<{ 0 }>();
| ^^^^^^^^^^^^^^^^^^^^^^ const parameter `N` is zero

error: this operation will panic at runtime
--> $DIR/const-n-is-zero.rs:20:13
|
LL | let _ = s.as_rchunks::<{ 1 - 1 }>();
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^ const parameter `N` is zero

error: this operation will panic at runtime
--> $DIR/const-n-is-zero.rs:26:13
|
LL | let _ = m.as_chunks_mut::<ZERO>();
| ^^^^^^^^^^^^^^^^^^^^^^^^^ const parameter `N` is zero

error: this operation will panic at runtime
--> $DIR/const-n-is-zero.rs:30:13
|
LL | let _ = m.as_rchunks_mut::<{ if ZERO == 0 { 0 } else { 1 } }>();
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ const parameter `N` is zero

error: this operation will panic at runtime
--> $DIR/const-n-is-zero.rs:34:13
|
LL | let _ = s.array_windows().any(|[]| true);
| ^^^^^^^^^^^^^^^^^ const parameter `N` is zero

error: aborting due to 6 previous errors

Loading