Auto merge of #154802 - matthiaskrgr:rollup-HWNmEyC, r=matthiaskrgr

Rollup of 5 pull requests

Successful merges:

 - rust-lang/rust#154376 (Remove more BuiltinLintDiag variants - part 4)
 - rust-lang/rust#154731 (llvm: Fix array ABI test to not check equality implementation)
 - rust-lang/rust#127534 (feat(core): impl Step for NonZero<u*>)
 - rust-lang/rust#154703 (Fix trailing comma in lifetime suggestion for empty angle brackets)
 - rust-lang/rust#154776 (Fix ICE in read_discriminant for enums with non-contiguous discriminants)
This commit is contained in:
bors
2026-04-04 14:36:56 +00:00
20 changed files with 540 additions and 174 deletions
@@ -121,20 +121,20 @@ pub fn read_discriminant(
// discriminants are int-like.
let discr_val = self.int_to_int_or_float(&tag_val, discr_layout).unwrap();
let discr_bits = discr_val.to_scalar().to_bits(discr_layout.size)?;
// Convert discriminant to variant index. Since we validated the tag against the
// layout range above, this cannot fail.
// Convert discriminant to variant index. The tag may pass the layout range
// check above but still not match any actual variant discriminant (e.g.,
// non-contiguous discriminants with a wrapping valid_range).
let index = match *ty.kind() {
ty::Adt(adt, _) => {
adt.discriminants(*self.tcx).find(|(_, var)| var.val == discr_bits).unwrap()
adt.discriminants(*self.tcx).find(|(_, var)| var.val == discr_bits)
}
ty::Coroutine(def_id, args) => {
let args = args.as_coroutine();
args.discriminants(def_id, *self.tcx)
.find(|(_, var)| var.val == discr_bits)
.unwrap()
args.discriminants(def_id, *self.tcx).find(|(_, var)| var.val == discr_bits)
}
_ => span_bug!(self.cur_span(), "tagged layout for non-adt non-coroutine"),
};
}
.ok_or_else(|| err_ub!(InvalidTag(Scalar::from_uint(tag_bits, tag_layout.size))))?;
// Return the cast value, and the index.
index.0
}
+26 -2
View File
@@ -1,3 +1,5 @@
use std::any::Any;
/// This module provides types and traits for buffering lints until later in compilation.
use rustc_ast::node_id::NodeId;
use rustc_data_structures::fx::FxIndexMap;
@@ -10,9 +12,11 @@
/// We can't implement `Diagnostic` for `BuiltinLintDiag`, because decorating some of its
/// variants requires types we don't have yet. So, handle that case separately.
pub enum DecorateDiagCompat {
/// The third argument of the closure is a `Session`. However, due to the dependency tree,
/// we don't have access to `rustc_session` here, so we downcast it when needed.
Dynamic(
Box<
dyn for<'a> FnOnce(DiagCtxtHandle<'a>, Level) -> Diag<'a, ()>
dyn for<'a> FnOnce(DiagCtxtHandle<'a>, Level, &dyn Any) -> Diag<'a, ()>
+ DynSync
+ DynSend
+ 'static,
@@ -30,7 +34,7 @@ fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
impl<D: for<'a> Diagnostic<'a, ()> + DynSync + DynSend + 'static> From<D> for DecorateDiagCompat {
#[inline]
fn from(d: D) -> Self {
Self::Dynamic(Box::new(|dcx, level| d.into_diag(dcx, level)))
Self::Dynamic(Box::new(|dcx, level, _| d.into_diag(dcx, level)))
}
}
@@ -97,6 +101,26 @@ pub fn dyn_buffer_lint<
node_id: NodeId,
span: impl Into<MultiSpan>,
callback: F,
) {
self.add_early_lint(BufferedEarlyLint {
lint_id: LintId::of(lint),
node_id,
span: Some(span.into()),
diagnostic: DecorateDiagCompat::Dynamic(Box::new(|dcx, level, _| callback(dcx, level))),
});
}
pub fn dyn_buffer_lint_any<
F: for<'a> FnOnce(DiagCtxtHandle<'a>, Level, &dyn Any) -> Diag<'a, ()>
+ DynSend
+ DynSync
+ 'static,
>(
&mut self,
lint: &'static Lint,
node_id: NodeId,
span: impl Into<MultiSpan>,
callback: F,
) {
self.add_early_lint(BufferedEarlyLint {
lint_id: LintId::of(lint),
-11
View File
@@ -7,7 +7,6 @@
use std::path::PathBuf;
use std::thread::panicking;
use rustc_data_structures::sync::{DynSend, DynSync};
use rustc_error_messages::{DiagArgMap, DiagArgName, DiagArgValue, IntoDiagArg};
use rustc_lint_defs::{Applicability, LintExpectationId};
use rustc_macros::{Decodable, Encodable};
@@ -119,16 +118,6 @@ fn into_diag(self, dcx: DiagCtxtHandle<'a>, level: Level) -> Diag<'a, G> {
}
}
impl<'a> Diagnostic<'a, ()>
for Box<
dyn for<'b> FnOnce(DiagCtxtHandle<'b>, Level) -> Diag<'b, ()> + DynSync + DynSend + 'static,
>
{
fn into_diag(self, dcx: DiagCtxtHandle<'a>, level: Level) -> Diag<'a, ()> {
self(dcx, level)
}
}
/// Type used to emit diagnostic through a closure instead of implementing the `Diagnostic` trait.
pub struct DiagDecorator<F: FnOnce(&mut Diag<'_, ()>)>(pub F);
+7 -3
View File
@@ -15,9 +15,9 @@
use rustc_span::{DUMMY_SP, Ident, Span};
use tracing::debug;
use crate::DecorateBuiltinLint;
use crate::context::{EarlyContext, LintContext, LintStore};
use crate::passes::{EarlyLintPass, EarlyLintPassObject};
use crate::{DecorateBuiltinLint, DiagAndSess};
pub(super) mod diagnostics;
@@ -49,8 +49,12 @@ fn check_id(&mut self, id: ast::NodeId) {
},
);
}
DecorateDiagCompat::Dynamic(d) => {
self.context.opt_span_lint(lint_id.lint, span, d);
DecorateDiagCompat::Dynamic(callback) => {
self.context.opt_span_lint(
lint_id.lint,
span,
DiagAndSess { callback, sess: self.context.sess() },
);
}
}
}
+15 -53
View File
@@ -1,5 +1,7 @@
use std::any::Any;
use std::borrow::Cow;
use rustc_data_structures::sync::DynSend;
use rustc_errors::{
Applicability, Diag, DiagArgValue, DiagCtxtHandle, Diagnostic, Level,
elided_lifetime_in_path_suggestion,
@@ -8,12 +10,24 @@
use rustc_middle::ty::TyCtxt;
use rustc_session::Session;
use rustc_session::lint::BuiltinLintDiag;
use tracing::debug;
use crate::lints;
mod check_cfg;
pub struct DiagAndSess<'sess> {
pub callback: Box<
dyn for<'b> FnOnce(DiagCtxtHandle<'b>, Level, &dyn Any) -> Diag<'b, ()> + DynSend + 'static,
>,
pub sess: &'sess Session,
}
impl<'a> Diagnostic<'a, ()> for DiagAndSess<'_> {
fn into_diag(self, dcx: DiagCtxtHandle<'a>, level: Level) -> Diag<'a, ()> {
(self.callback)(dcx, level, self.sess)
}
}
/// This is a diagnostic struct that will decorate a `BuiltinLintDiag`
/// Directly creating the lint structs is expensive, using this will only decorate the lint structs when needed.
pub struct DecorateBuiltinLint<'sess, 'tcx> {
@@ -25,28 +39,6 @@ pub struct DecorateBuiltinLint<'sess, 'tcx> {
impl<'a> Diagnostic<'a, ()> for DecorateBuiltinLint<'_, '_> {
fn into_diag(self, dcx: DiagCtxtHandle<'a>, level: Level) -> Diag<'a, ()> {
match self.diagnostic {
BuiltinLintDiag::AbsPathWithModule(mod_span) => {
let (replacement, applicability) =
match self.sess.source_map().span_to_snippet(mod_span) {
Ok(ref s) => {
// FIXME(Manishearth) ideally the emitting code
// can tell us whether or not this is global
let opt_colon =
if s.trim_start().starts_with("::") { "" } else { "::" };
(format!("crate{opt_colon}{s}"), Applicability::MachineApplicable)
}
Err(_) => ("crate::<path>".to_string(), Applicability::HasPlaceholders),
};
lints::AbsPathWithModule {
sugg: lints::AbsPathWithModuleSugg {
span: mod_span,
applicability,
replacement,
},
}
.into_diag(dcx, level)
}
BuiltinLintDiag::ElidedLifetimesInPaths(
n,
path_span,
@@ -87,36 +79,6 @@ fn into_diag(self, dcx: DiagCtxtHandle<'a>, level: Level) -> Diag<'a, ()> {
}
.into_diag(dcx, level)
}
BuiltinLintDiag::SingleUseLifetime {
param_span,
use_span,
elidable,
deletion_span,
ident,
} => {
debug!(?param_span, ?use_span, ?deletion_span);
let suggestion = if let Some(deletion_span) = deletion_span {
let (use_span, replace_lt) = if elidable {
let use_span =
self.sess.source_map().span_extend_while_whitespace(use_span);
(use_span, String::new())
} else {
(use_span, "'_".to_owned())
};
debug!(?deletion_span, ?use_span);
// issue 107998 for the case such as a wrong function pointer type
// `deletion_span` is empty and there is no need to report lifetime uses here
let deletion_span =
if deletion_span.is_empty() { None } else { Some(deletion_span) };
Some(lints::SingleUseLifetimeSugg { deletion_span, use_span, replace_lt })
} else {
None
};
lints::SingleUseLifetime { suggestion, param_span, use_span, ident }
.into_diag(dcx, level)
}
BuiltinLintDiag::NamedArgumentUsedPositionally {
position_sp_to_replace,
position_sp_for_msg,
+1 -1
View File
@@ -129,7 +129,7 @@
#[rustfmt::skip]
pub use builtin::{MissingDoc, SoftLints};
pub use context::{EarlyContext, LateContext, LintContext, LintStore};
pub use early::diagnostics::{DecorateAttrLint, DecorateBuiltinLint};
pub use early::diagnostics::{DecorateAttrLint, DecorateBuiltinLint, DiagAndSess};
pub use early::{EarlyCheckNode, check_ast_node};
pub use late::{check_crate, late_lint_mod, unerased_lint_store};
pub use levels::LintLevelsBuilder;
-43
View File
@@ -3011,25 +3011,6 @@ pub(crate) struct IllFormedAttributeInput {
pub docs: &'static str,
}
#[derive(Diagnostic)]
#[diag(
"absolute paths must start with `self`, `super`, `crate`, or an external crate name in the 2018 edition"
)]
pub(crate) struct AbsPathWithModule {
#[subdiagnostic]
pub sugg: AbsPathWithModuleSugg,
}
#[derive(Subdiagnostic)]
#[suggestion("use `crate`", code = "{replacement}")]
pub(crate) struct AbsPathWithModuleSugg {
#[primary_span]
pub span: Span,
#[applicability]
pub applicability: Applicability,
pub replacement: String,
}
#[derive(Diagnostic)]
#[diag("hidden lifetime parameters in types are deprecated")]
pub(crate) struct ElidedLifetimesInPaths {
@@ -3081,30 +3062,6 @@ pub(crate) enum UnusedImportsSugg {
},
}
#[derive(Diagnostic)]
#[diag("lifetime parameter `{$ident}` only used once")]
pub(crate) struct SingleUseLifetime {
#[label("this lifetime...")]
pub param_span: Span,
#[label("...is used only here")]
pub use_span: Span,
#[subdiagnostic]
pub suggestion: Option<SingleUseLifetimeSugg>,
pub ident: Ident,
}
#[derive(Subdiagnostic)]
#[multipart_suggestion("elide the single-use lifetime", applicability = "machine-applicable")]
pub(crate) struct SingleUseLifetimeSugg {
#[suggestion_part(code = "")]
pub deletion_span: Option<Span>,
#[suggestion_part(code = "{replace_lt}")]
pub use_span: Span,
pub replace_lt: String,
}
#[derive(Diagnostic)]
#[diag("named argument `{$named_arg_name}` is not used by name")]
pub(crate) struct NamedArgumentUsedPositionally {
-13
View File
@@ -656,7 +656,6 @@ pub enum DeprecatedSinceKind {
// becomes hacky (and it gets allocated).
#[derive(Debug)]
pub enum BuiltinLintDiag {
AbsPathWithModule(Span),
ElidedLifetimesInPaths(usize, Span, bool, Span),
UnusedImports {
remove_whole_use: bool,
@@ -665,18 +664,6 @@ pub enum BuiltinLintDiag {
test_module_span: Option<Span>,
span_snippets: Vec<String>,
},
SingleUseLifetime {
/// Span of the parameter which declares this lifetime.
param_span: Span,
/// Span of the code that should be removed when eliding this lifetime.
/// This span should include leading or trailing comma.
deletion_span: Option<Span>,
/// Span of the single use, or None if the lifetime is never used.
/// If true, the lifetime will be fully elided.
use_span: Span,
elidable: bool,
ident: Ident,
},
NamedArgumentUsedPositionally {
/// Span where the named argument is used by position and will be replaced with the named
/// argument name
+27 -5
View File
@@ -11,7 +11,7 @@
use rustc_data_structures::unord::{UnordMap, UnordSet};
use rustc_errors::codes::*;
use rustc_errors::{
Applicability, Diag, DiagCtxtHandle, ErrorGuaranteed, MultiSpan, SuggestionStyle,
Applicability, Diag, DiagCtxtHandle, Diagnostic, ErrorGuaranteed, MultiSpan, SuggestionStyle,
struct_span_code_err,
};
use rustc_feature::BUILTIN_ATTRIBUTES;
@@ -23,7 +23,6 @@
use rustc_middle::bug;
use rustc_middle::ty::TyCtxt;
use rustc_session::Session;
use rustc_session::lint::BuiltinLintDiag;
use rustc_session::lint::builtin::{
ABSOLUTE_PATHS_NOT_STARTING_WITH_CRATE, AMBIGUOUS_GLOB_IMPORTS, AMBIGUOUS_IMPORT_VISIBILITIES,
AMBIGUOUS_PANIC_IMPORTS, MACRO_EXPANDED_MACRO_EXPORTS_ACCESSED_BY_ABSOLUTE_PATHS,
@@ -510,12 +509,35 @@ pub(crate) fn lint_if_path_starts_with_module(
return;
}
let diag = BuiltinLintDiag::AbsPathWithModule(root_span);
self.lint_buffer.buffer_lint(
self.lint_buffer.dyn_buffer_lint_any(
ABSOLUTE_PATHS_NOT_STARTING_WITH_CRATE,
node_id,
root_span,
diag,
move |dcx, level, sess| {
let (replacement, applicability) = match sess
.downcast_ref::<Session>()
.expect("expected a `Session`")
.source_map()
.span_to_snippet(root_span)
{
Ok(ref s) => {
// FIXME(Manishearth) ideally the emitting code
// can tell us whether or not this is global
let opt_colon = if s.trim_start().starts_with("::") { "" } else { "::" };
(format!("crate{opt_colon}{s}"), Applicability::MachineApplicable)
}
Err(_) => ("crate::<path>".to_string(), Applicability::HasPlaceholders),
};
errors::AbsPathWithModule {
sugg: errors::AbsPathWithModuleSugg {
span: root_span,
applicability,
replacement,
},
}
.into_diag(dcx, level)
},
);
}
+43
View File
@@ -1697,3 +1697,46 @@ pub(crate) struct AssociatedConstElidedLifetime {
#[note("cannot automatically infer `'static` because of other lifetimes in scope")]
pub lifetimes_in_scope: MultiSpan,
}
#[derive(Diagnostic)]
#[diag("lifetime parameter `{$ident}` only used once")]
pub(crate) struct SingleUseLifetime {
#[label("this lifetime...")]
pub param_span: Span,
#[label("...is used only here")]
pub use_span: Span,
#[subdiagnostic]
pub suggestion: Option<SingleUseLifetimeSugg>,
pub ident: Ident,
}
#[derive(Subdiagnostic)]
#[multipart_suggestion("elide the single-use lifetime", applicability = "machine-applicable")]
pub(crate) struct SingleUseLifetimeSugg {
#[suggestion_part(code = "")]
pub deletion_span: Option<Span>,
#[suggestion_part(code = "{replace_lt}")]
pub use_span: Span,
pub replace_lt: String,
}
#[derive(Diagnostic)]
#[diag(
"absolute paths must start with `self`, `super`, `crate`, or an external crate name in the 2018 edition"
)]
pub(crate) struct AbsPathWithModule {
#[subdiagnostic]
pub sugg: AbsPathWithModuleSugg,
}
#[derive(Subdiagnostic)]
#[suggestion("use `crate`", code = "{replacement}")]
pub(crate) struct AbsPathWithModuleSugg {
#[primary_span]
pub span: Span,
#[applicability]
pub applicability: Applicability,
pub replacement: String,
}
+49 -15
View File
@@ -14,7 +14,7 @@
use rustc_data_structures::fx::{FxHashMap, FxHashSet, FxIndexMap, FxIndexSet};
use rustc_errors::codes::*;
use rustc_errors::{
Applicability, Diag, ErrorGuaranteed, MultiSpan, SuggestionStyle, pluralize,
Applicability, Diag, Diagnostic, ErrorGuaranteed, MultiSpan, SuggestionStyle, pluralize,
struct_span_code_err,
};
use rustc_hir as hir;
@@ -3642,22 +3642,52 @@ pub(crate) fn maybe_report_lifetime_uses(
match use_set {
Some(LifetimeUseSet::Many) => {}
Some(LifetimeUseSet::One { use_span, use_ctxt }) => {
debug!(?param.ident, ?param.ident.span, ?use_span);
let elidable = matches!(use_ctxt, LifetimeCtxt::Ref);
let param_ident = param.ident;
let deletion_span =
if param.bounds.is_empty() { deletion_span() } else { None };
self.r.lint_buffer.buffer_lint(
self.r.lint_buffer.dyn_buffer_lint_any(
lint::builtin::SINGLE_USE_LIFETIMES,
param.id,
param.ident.span,
lint::BuiltinLintDiag::SingleUseLifetime {
param_span: param.ident.span,
use_span,
elidable,
deletion_span,
ident: param.ident,
param_ident.span,
move |dcx, level, sess| {
debug!(?param_ident, ?param_ident.span, ?use_span);
let elidable = matches!(use_ctxt, LifetimeCtxt::Ref);
let suggestion = if let Some(deletion_span) = deletion_span {
let (use_span, replace_lt) = if elidable {
let use_span = sess
.downcast_ref::<Session>()
.expect("expected a `Session`")
.source_map()
.span_extend_while_whitespace(use_span);
(use_span, String::new())
} else {
(use_span, "'_".to_owned())
};
debug!(?deletion_span, ?use_span);
// issue 107998 for the case such as a wrong function pointer type
// `deletion_span` is empty and there is no need to report lifetime uses here
let deletion_span = if deletion_span.is_empty() {
None
} else {
Some(deletion_span)
};
Some(errors::SingleUseLifetimeSugg {
deletion_span,
use_span,
replace_lt,
})
} else {
None
};
errors::SingleUseLifetime {
suggestion,
param_span: param_ident.span,
use_span,
ident: param_ident,
}
.into_diag(dcx, level)
},
);
}
@@ -4065,6 +4095,7 @@ fn add_missing_lifetime_specifiers_label<'a>(
};
let mut spans_suggs: Vec<_> = Vec::new();
let source_map = self.r.tcx.sess.source_map();
let build_sugg = |lt: MissingLifetime| match lt.kind {
MissingLifetimeKind::Underscore => {
debug_assert_eq!(lt.count, 1);
@@ -4075,9 +4106,12 @@ fn add_missing_lifetime_specifiers_label<'a>(
(lt.span.shrink_to_hi(), format!("{existing_name} "))
}
MissingLifetimeKind::Comma => {
let sugg: String = std::iter::repeat_n([existing_name.as_str(), ", "], lt.count)
.flatten()
let sugg: String = std::iter::repeat_n(existing_name.as_str(), lt.count)
.intersperse(", ")
.collect();
let is_empty_brackets =
source_map.span_look_ahead(lt.span, ">", Some(50)).is_some();
let sugg = if is_empty_brackets { sugg } else { format!("{sugg}, ") };
(lt.span.shrink_to_hi(), sugg)
}
MissingLifetimeKind::Brackets => {
+1 -1
View File
@@ -344,7 +344,7 @@ pub fn dyn_buffer_lint<
lint,
Some(span.into()),
node_id,
DecorateDiagCompat::Dynamic(Box::new(callback)),
DecorateDiagCompat::Dynamic(Box::new(|dcx, level, _| callback(dcx, level))),
)
}
+136 -10
View File
@@ -15,6 +15,7 @@ unsafe impl TrustedStep for $type {}
)*};
}
unsafe_impl_trusted_step![AsciiChar char i8 i16 i32 i64 i128 isize u8 u16 u32 u64 u128 usize Ipv4Addr Ipv6Addr];
unsafe_impl_trusted_step![NonZero<u8> NonZero<u16> NonZero<u32> NonZero<u64> NonZero<u128> NonZero<usize>];
/// Objects that have a notion of *successor* and *predecessor* operations.
///
@@ -255,10 +256,8 @@ fn backward(start: Self, n: usize) -> Self {
macro_rules! step_integer_impls {
{
narrower than or same width as usize:
$( [ $u_narrower:ident $i_narrower:ident ] ),+;
wider than usize:
$( [ $u_wider:ident $i_wider:ident ] ),+;
[ $( [ $u_narrower:ident $i_narrower:ident ] ),+ ] <= usize <
[ $( [ $u_wider:ident $i_wider:ident ] ),+ ]
} => {
$(
#[allow(unreachable_patterns)]
@@ -437,20 +436,138 @@ fn backward_checked(start: Self, n: usize) -> Option<Self> {
#[cfg(target_pointer_width = "64")]
step_integer_impls! {
narrower than or same width as usize: [u8 i8], [u16 i16], [u32 i32], [u64 i64], [usize isize];
wider than usize: [u128 i128];
[ [u8 i8], [u16 i16], [u32 i32], [u64 i64], [usize isize] ] <= usize < [ [u128 i128] ]
}
#[cfg(target_pointer_width = "32")]
step_integer_impls! {
narrower than or same width as usize: [u8 i8], [u16 i16], [u32 i32], [usize isize];
wider than usize: [u64 i64], [u128 i128];
[ [u8 i8], [u16 i16], [u32 i32], [usize isize] ] <= usize < [ [u64 i64], [u128 i128] ]
}
#[cfg(target_pointer_width = "16")]
step_integer_impls! {
narrower than or same width as usize: [u8 i8], [u16 i16], [usize isize];
wider than usize: [u32 i32], [u64 i64], [u128 i128];
[ [u8 i8], [u16 i16], [usize isize] ] <= usize < [ [u32 i32], [u64 i64], [u128 i128] ]
}
// These are still macro-generated because the integer literals resolve to different types.
macro_rules! step_nonzero_identical_methods {
($int:ident) => {
#[inline]
unsafe fn forward_unchecked(start: Self, n: usize) -> Self {
// SAFETY: the caller has to guarantee that `start + n` doesn't overflow.
unsafe { Self::new_unchecked(start.get().unchecked_add(n as $int)) }
}
#[inline]
unsafe fn backward_unchecked(start: Self, n: usize) -> Self {
// SAFETY: the caller has to guarantee that `start - n` doesn't overflow or hit zero.
unsafe { Self::new_unchecked(start.get().unchecked_sub(n as $int)) }
}
#[inline]
#[allow(arithmetic_overflow)]
#[rustc_inherit_overflow_checks]
fn forward(start: Self, n: usize) -> Self {
// In debug builds, trigger a panic on overflow.
// This should optimize completely out in release builds.
if Self::forward_checked(start, n).is_none() {
let _ = $int::MAX + 1;
}
// Do saturating math (wrapping math causes UB if it wraps to Zero)
start.saturating_add(n as $int)
}
#[inline]
#[allow(arithmetic_overflow)]
#[rustc_inherit_overflow_checks]
fn backward(start: Self, n: usize) -> Self {
// In debug builds, trigger a panic on overflow.
// This should optimize completely out in release builds.
if Self::backward_checked(start, n).is_none() {
let _ = $int::MIN - 1;
}
// Do saturating math (wrapping math causes UB if it wraps to Zero)
Self::new(start.get().saturating_sub(n as $int)).unwrap_or(Self::MIN)
}
#[inline]
fn steps_between(start: &Self, end: &Self) -> (usize, Option<usize>) {
if *start <= *end {
#[allow(irrefutable_let_patterns, reason = "happens on usize or narrower")]
if let Ok(steps) = usize::try_from(end.get() - start.get()) {
(steps, Some(steps))
} else {
(usize::MAX, None)
}
} else {
(0, None)
}
}
};
}
macro_rules! step_nonzero_impls {
{
[$( $narrower:ident ),+] <= usize < [$( $wider:ident ),+]
} => {
$(
#[allow(unreachable_patterns)]
#[unstable(feature = "step_trait", reason = "recently redesigned", issue = "42168")]
impl Step for NonZero<$narrower> {
step_nonzero_identical_methods!($narrower);
#[inline]
fn forward_checked(start: Self, n: usize) -> Option<Self> {
match $narrower::try_from(n) {
Ok(n) => start.checked_add(n),
Err(_) => None, // if n is out of range, `unsigned_start + n` is too
}
}
#[inline]
fn backward_checked(start: Self, n: usize) -> Option<Self> {
match $narrower::try_from(n) {
// *_sub() is not implemented on NonZero<T>
Ok(n) => start.get().checked_sub(n).and_then(Self::new),
Err(_) => None, // if n is out of range, `unsigned_start - n` is too
}
}
}
)+
$(
#[allow(unreachable_patterns)]
#[unstable(feature = "step_trait", reason = "recently redesigned", issue = "42168")]
impl Step for NonZero<$wider> {
step_nonzero_identical_methods!($wider);
#[inline]
fn forward_checked(start: Self, n: usize) -> Option<Self> {
start.checked_add(n as $wider)
}
#[inline]
fn backward_checked(start: Self, n: usize) -> Option<Self> {
start.get().checked_sub(n as $wider).and_then(Self::new)
}
}
)+
};
}
#[cfg(target_pointer_width = "64")]
step_nonzero_impls! {
[u8, u16, u32, u64, usize] <= usize < [u128]
}
#[cfg(target_pointer_width = "32")]
step_nonzero_impls! {
[u8, u16, u32, usize] <= usize < [u64, u128]
}
#[cfg(target_pointer_width = "16")]
step_nonzero_impls! {
[u8, u16, usize] <= usize < [u32, u64, u128]
}
#[unstable(feature = "step_trait", issue = "42168")]
@@ -944,6 +1061,7 @@ unsafe fn __iterator_get_unchecked(&mut self, idx: usize) -> Self::Item
range_exact_iter_impl! {
usize u8 u16
isize i8 i16
NonZero<usize> NonZero<u8> NonZero<u16>
// These are incorrect per the reasoning above,
// but removing them would be a breaking change as they were stabilized in Rust 1.0.0.
@@ -956,22 +1074,30 @@ unsafe fn __iterator_get_unchecked(&mut self, idx: usize) -> Self::Item
unsafe_range_trusted_random_access_impl! {
usize u8 u16
isize i8 i16
NonZero<usize> NonZero<u8> NonZero<u16>
}
#[cfg(target_pointer_width = "32")]
unsafe_range_trusted_random_access_impl! {
u32 i32
NonZero<u32>
}
#[cfg(target_pointer_width = "64")]
unsafe_range_trusted_random_access_impl! {
u32 i32
u64 i64
NonZero<u32>
NonZero<u64>
}
range_incl_exact_iter_impl! {
u8
i8
NonZero<u8>
// Since RangeInclusive<NonZero<uN>> can only be 1..=uN::MAX the length of this range is always
// <= uN::MAX, so they are always valid ExactSizeIterator unlike the ranges that include zero.
NonZero<u16> NonZero<usize>
// These are incorrect per the reasoning above,
// but removing them would be a breaking change as they were stabilized in Rust 1.26.0.
+139
View File
@@ -502,3 +502,142 @@ fn test_double_ended_range() {
panic!("unreachable");
}
}
macro_rules! nz {
(NonZero<$type:ident>($val:literal)) => {
::core::num::NonZero::<$type>::new($val).unwrap()
};
(NonZero<$type:ident>::MAX) => {
::core::num::NonZero::<$type>::new($type::MAX).unwrap()
};
}
macro_rules! nonzero_array {
(NonZero<$type:ident>[$($val:literal),*]) => {
[$(nz!(NonZero<$type>($val))),*]
}
}
macro_rules! nonzero_range {
(NonZero<$type:ident>($($left:literal)?..$($right:literal)?)) => {
nz!(NonZero<$type>($($left)?))..nz!(NonZero<$type>($($right)?))
};
(NonZero<$type:ident>($($left:literal)?..MAX)) => {
nz!(NonZero<$type>($($left)?))..nz!(NonZero<$type>::MAX)
};
(NonZero<$type:ident>($($left:literal)?..=$right:literal)) => {
nz!(NonZero<$type>($($left)?))..=nz!(NonZero<$type>($right))
};
(NonZero<$type:ident>($($left:literal)?..=MAX)) => {
nz!(NonZero<$type>($($left)?))..=nz!(NonZero<$type>::MAX)
};
}
#[test]
fn test_nonzero_range() {
assert_eq!(
nonzero_range!(NonZero<usize>(1..=21)).step_by(5).collect::<Vec<_>>(),
nonzero_array!(NonZero<usize>[1, 6, 11, 16, 21])
);
assert_eq!(
nonzero_range!(NonZero<usize>(1..=20)).step_by(5).collect::<Vec<_>>(),
nonzero_array!(NonZero<usize>[1, 6, 11, 16])
);
assert_eq!(
nonzero_range!(NonZero<usize>(1..20)).step_by(5).collect::<Vec<_>>(),
nonzero_array!(NonZero<usize>[1, 6, 11, 16])
);
assert_eq!(
nonzero_range!(NonZero<usize>(1..21)).rev().step_by(5).collect::<Vec<_>>(),
nonzero_array!(NonZero<usize>[20, 15, 10, 5])
);
assert_eq!(
nonzero_range!(NonZero<usize>(1..21)).rev().step_by(6).collect::<Vec<_>>(),
nonzero_array!(NonZero<usize>[20, 14, 8, 2])
);
assert_eq!(
nonzero_range!(NonZero<u8>(200..255)).step_by(50).collect::<Vec<_>>(),
nonzero_array!(NonZero<u8>[200, 250])
);
assert_eq!(
nonzero_range!(NonZero<usize>(200..5)).step_by(1).collect::<Vec<_>>(),
nonzero_array!(NonZero<usize>[])
);
assert_eq!(
nonzero_range!(NonZero<usize>(200..200)).step_by(1).collect::<Vec<_>>(),
nonzero_array!(NonZero<usize>[])
);
assert_eq!(nonzero_range!(NonZero<u32>(10..20)).step_by(1).size_hint(), (10, Some(10)));
assert_eq!(nonzero_range!(NonZero<u32>(10..20)).step_by(5).size_hint(), (2, Some(2)));
assert_eq!(nonzero_range!(NonZero<u32>(1..21)).rev().step_by(5).size_hint(), (4, Some(4)));
assert_eq!(nonzero_range!(NonZero<u32>(1..21)).rev().step_by(6).size_hint(), (4, Some(4)));
assert_eq!(nonzero_range!(NonZero<u32>(20..1)).step_by(1).size_hint(), (0, Some(0)));
assert_eq!(nonzero_range!(NonZero<u32>(20..20)).step_by(1).size_hint(), (0, Some(0)));
// ExactSizeIterator
assert_eq!(nonzero_range!(NonZero<u8>(1..=MAX)).step_by(1).len(), usize::from(u8::MAX));
assert_eq!(nonzero_range!(NonZero<u16>(1..=MAX)).step_by(1).len(), usize::from(u16::MAX));
assert_eq!(nonzero_range!(NonZero<usize>(1..=MAX)).step_by(1).len(), usize::MAX);
// Limits (next)
let mut range = nonzero_range!(NonZero<u8>(254..=MAX));
assert_eq!(range.next(), Some(nz!(NonZero<u8>(254))));
assert_eq!(range.next(), Some(nz!(NonZero<u8>(255))));
assert_eq!(range.next(), None);
let mut range = nonzero_range!(NonZero<u16>(65534..=MAX));
assert_eq!(range.next(), Some(nz!(NonZero<u16>(65534))));
assert_eq!(range.next(), Some(nz!(NonZero<u16>(65535))));
assert_eq!(range.next(), None);
// Limits (size_hint, exclusive range)
assert_eq!(
nonzero_range!(NonZero<u8>(1..MAX)).step_by(1).size_hint(),
(u8::MAX as usize - 1, Some(u8::MAX as usize - 1))
);
assert_eq!(
nonzero_range!(NonZero<u16>(1..MAX)).step_by(1).size_hint(),
(u16::MAX as usize - 1, Some(u16::MAX as usize - 1))
);
#[cfg(any(target_pointer_width = "32", target_pointer_width = "64"))]
assert_eq!(
nonzero_range!(NonZero<u32>(1..MAX)).step_by(1).size_hint(),
(u32::MAX as usize - 1, Some(u32::MAX as usize - 1))
);
#[cfg(target_pointer_width = "64")]
assert_eq!(
nonzero_range!(NonZero<u64>(1..MAX)).step_by(1).size_hint(),
(u64::MAX as usize - 1, Some(u64::MAX as usize - 1))
);
assert_eq!(nonzero_range!(NonZero<u128>(1..MAX)).step_by(1).size_hint(), (usize::MAX, None));
assert_eq!(
nonzero_range!(NonZero<usize>(1..MAX)).step_by(1).size_hint(),
(usize::MAX - 1, Some(usize::MAX - 1))
);
// Limits (size_hint, inclusive range)
assert_eq!(
nonzero_range!(NonZero<u8>(1..=MAX)).step_by(1).size_hint(),
(u8::MAX as usize, Some(u8::MAX as usize))
);
assert_eq!(
nonzero_range!(NonZero<u16>(1..=MAX)).step_by(1).size_hint(),
(u16::MAX as usize, Some(u16::MAX as usize))
);
#[cfg(any(target_pointer_width = "32", target_pointer_width = "64"))]
assert_eq!(
nonzero_range!(NonZero<u32>(1..=MAX)).step_by(1).size_hint(),
(u32::MAX as usize, Some(u32::MAX as usize))
);
#[cfg(target_pointer_width = "64")]
assert_eq!(
nonzero_range!(NonZero<u64>(1..=MAX)).step_by(1).size_hint(),
(u64::MAX as usize, Some(u64::MAX as usize))
);
assert_eq!(nonzero_range!(NonZero<u128>(1..=MAX)).step_by(1).size_hint(), (usize::MAX, None));
assert_eq!(
nonzero_range!(NonZero<usize>(1..=MAX)).step_by(1).size_hint(),
(usize::MAX, Some(usize::MAX))
);
}
+15 -3
View File
@@ -1,3 +1,6 @@
//@ revisions: llvm-current llvm-next
//@[llvm-current] ignore-llvm-version: 23-99
//@[llvm-next] min-llvm-version: 23
//@ compile-flags: -Copt-level=3 -Z merge-functions=disabled
//@ only-x86_64
#![crate_type = "lib"]
@@ -26,9 +29,18 @@
#[no_mangle]
pub fn array_eq_value_still_passed_by_pointer(a: [u16; 9], b: [u16; 9]) -> bool {
// CHECK-NEXT: start:
// CHECK: %[[CMP:.+]] = tail call i32 @{{bcmp|memcmp}}(ptr {{.*}} dereferenceable(18) %{{.+}}, ptr {{.*}} dereferenceable(18) %{{.+}}, i64 18)
// CHECK-NEXT: %[[EQ:.+]] = icmp eq i32 %[[CMP]], 0
// CHECK-NEXT: ret i1 %[[EQ]]
// CHECK-NOT: alloca
// llvm-current-NEXT: %[[CMP:.+]] = tail call i32 @{{bcmp|memcmp}}(ptr
// llvm-current-SAME: {{.*}} dereferenceable(18) %{{.+}}, ptr {{.*}} dereferenceable(18)
// llvm-current-SAME: %{{.+}}, i64 18)
// llvm-current-NEXT: %[[EQ:.+]] = icmp eq i32 %[[CMP]], 0
// llvm-current-NEXT: ret i1 %[[EQ]]
// CHECK-NOT: call
// New LLVM expands the bcmp earlier, so this becomes wide reads + icmp
// No allocas or calls, and at least one icmp
// llvm-next: icmp
// CHECK-NOT: alloca
// CHECK-NOT: call
a == b
}
+13
View File
@@ -108,4 +108,17 @@ enum UninhDiscriminant {
};
};
// # Regression test for https://github.com/rust-lang/rust/issues/153758
// Discriminants at i64::MIN and i64::MAX produce a wrapping valid_range that covers
// all values. A value like 0 passes the range check but doesn't match any variant.
#[repr(i64)]
#[derive(Copy, Clone)]
enum WideRangeDiscriminants {
A = i64::MIN,
B = i64::MAX,
}
const BAD_WIDE_RANGE_ENUM: WideRangeDiscriminants = unsafe { mem::transmute(0_i64) };
//~^ ERROR expected a valid enum tag
fn main() {}
+12 -1
View File
@@ -129,6 +129,17 @@ LL | std::mem::discriminant(&*(&() as *const () as *const Never));
note: inside `discriminant::<Never>`
--> $SRC_DIR/core/src/mem/mod.rs:LL:COL
error: aborting due to 14 previous errors
error[E0080]: constructing invalid value of type WideRangeDiscriminants: at .<enum-tag>, encountered 0x0, but expected a valid enum tag
--> $DIR/ub-enum.rs:121:1
|
LL | const BAD_WIDE_RANGE_ENUM: WideRangeDiscriminants = unsafe { mem::transmute(0_i64) };
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ it is undefined behavior to use this value
|
= note: the rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior.
= note: the raw bytes of the constant (size: $SIZE, align: $ALIGN) {
HEX_DUMP
}
error: aborting due to 15 previous errors
For more information about this error, try `rustc --explain E0080`.
@@ -28,8 +28,8 @@ LL | type E = Ty<>;
|
help: consider introducing a named lifetime parameter
|
LL | type E<'a> = Ty<'a, >;
| ++++ +++
LL | type E<'a> = Ty<'a>;
| ++++ ++
error[E0106]: missing lifetime specifier
--> $DIR/wrong-number-of-args.rs:120:22
@@ -55,12 +55,12 @@ LL | type F = Box<dyn GenericLifetime<>>;
|
help: consider making the bound lifetime-generic with a new `'a` lifetime
|
LL | type F = Box<dyn for<'a> GenericLifetime<'a, >>;
| +++++++ +++
LL | type F = Box<dyn for<'a> GenericLifetime<'a>>;
| +++++++ ++
help: consider introducing a named lifetime parameter
|
LL | type F<'a> = Box<dyn GenericLifetime<'a, >>;
| ++++ +++
LL | type F<'a> = Box<dyn GenericLifetime<'a>>;
| ++++ ++
error[E0106]: missing lifetime specifier
--> $DIR/wrong-number-of-args.rs:163:43
@@ -0,0 +1,18 @@
// Regression test for <https://github.com/rust-lang/rust/issues/154600>.
//
// When suggesting lifetime parameters for empty angle brackets like `Foo<>`,
// the suggestion should not include a trailing comma (e.g., `Foo<'a>` not `Foo<'a, >`).
// When there are other generic arguments like `Foo<T>`, the trailing comma is needed
// (e.g., `Foo<'a, T>`).
#![crate_type = "lib"]
struct Foo<'a>(&'a ());
type A = Foo<>;
//~^ ERROR missing lifetime specifier [E0106]
struct Bar<'a, T>(&'a T);
type B = Bar<u8>;
//~^ ERROR missing lifetime specifier [E0106]
@@ -0,0 +1,25 @@
error[E0106]: missing lifetime specifier
--> $DIR/E0106-trailing-comma-in-lifetime-suggestion.rs:12:13
|
LL | type A = Foo<>;
| ^ expected named lifetime parameter
|
help: consider introducing a named lifetime parameter
|
LL | type A<'a> = Foo<'a>;
| ++++ ++
error[E0106]: missing lifetime specifier
--> $DIR/E0106-trailing-comma-in-lifetime-suggestion.rs:17:13
|
LL | type B = Bar<u8>;
| ^ expected named lifetime parameter
|
help: consider introducing a named lifetime parameter
|
LL | type B<'a> = Bar<'a, u8>;
| ++++ +++
error: aborting due to 2 previous errors
For more information about this error, try `rustc --explain E0106`.