Skip to content

Commit cf76d4e

Browse files
committed
Allow initializing NonZero with literals
1 parent 0d11be5 commit cf76d4e

File tree

17 files changed

+260
-35
lines changed

17 files changed

+260
-35
lines changed

compiler/rustc_hir/src/lang_items.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -426,6 +426,8 @@ language_item_table! {
426426
String, sym::String, string, Target::Struct, GenericRequirement::None;
427427
CStr, sym::CStr, c_str, Target::Struct, GenericRequirement::None;
428428

429+
NonZero, sym::NonZero, non_zero, Target::Struct, GenericRequirement::Exact(1);
430+
429431
// Experimental lang items for implementing contract pre- and post-condition checking.
430432
ContractBuildCheckEnsures, sym::contract_build_check_ensures, contract_build_check_ensures_fn, Target::Fn, GenericRequirement::None;
431433
ContractCheckRequires, sym::contract_check_requires, contract_check_requires_fn, Target::Fn, GenericRequirement::None;

compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1665,6 +1665,14 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
16651665
_ => unreachable!(),
16661666
}
16671667
}
1668+
ty::Adt(adt, ..) if tcx.is_lang_item(adt.did(), LangItem::NonZero) => {
1669+
if i.get() == 0 {
1670+
// FIXME: report a nice error
1671+
None
1672+
} else {
1673+
Some(ty)
1674+
}
1675+
}
16681676
_ => None,
16691677
});
16701678
opt_ty.unwrap_or_else(|| self.next_int_var())

compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2487,7 +2487,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
24872487
_ => return false,
24882488
};
24892489

2490-
if !self.tcx.is_diagnostic_item(sym::NonZero, adt.did()) {
2490+
if !self.tcx.is_lang_item(adt.did(), LangItem::NonZero) {
24912491
return false;
24922492
}
24932493

compiler/rustc_lint/src/lints.rs

Lines changed: 26 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ use rustc_hir as hir;
1212
use rustc_hir::def::Namespace;
1313
use rustc_hir::def_id::DefId;
1414
use rustc_hir::intravisit::VisitorExt;
15-
use rustc_macros::{LintDiagnostic, Subdiagnostic};
15+
use rustc_macros::{Diagnostic, LintDiagnostic, Subdiagnostic};
1616
use rustc_middle::ty::inhabitedness::InhabitedPredicate;
1717
use rustc_middle::ty::{Clause, PolyExistentialTraitRef, Ty, TyCtxt};
1818
use rustc_session::Session;
@@ -1714,6 +1714,19 @@ pub(crate) struct OverflowingInt<'a> {
17141714
#[subdiagnostic]
17151715
pub help: Option<OverflowingIntHelp<'a>>,
17161716
}
1717+
#[derive(Diagnostic)]
1718+
#[diag(lint_overflowing_int)]
1719+
#[note]
1720+
pub(crate) struct OverflowingIntError<'a> {
1721+
#[primary_span]
1722+
pub span: Span,
1723+
pub ty: &'a str,
1724+
pub lit: String,
1725+
pub min: i128,
1726+
pub max: u128,
1727+
#[subdiagnostic]
1728+
pub help: Option<OverflowingIntHelp<'a>>,
1729+
}
17171730

17181731
#[derive(Subdiagnostic)]
17191732
#[help(lint_help)]
@@ -1739,6 +1752,18 @@ pub(crate) struct OverflowingUInt<'a> {
17391752
pub max: u128,
17401753
}
17411754

1755+
#[derive(Diagnostic)]
1756+
#[diag(lint_overflowing_uint)]
1757+
#[note]
1758+
pub(crate) struct OverflowingUIntError<'a> {
1759+
#[primary_span]
1760+
pub span: Span,
1761+
pub ty: &'a str,
1762+
pub lit: String,
1763+
pub min: u128,
1764+
pub max: u128,
1765+
}
1766+
17421767
#[derive(LintDiagnostic)]
17431768
#[diag(lint_overflowing_literal)]
17441769
#[note]

compiler/rustc_lint/src/types/literal.rs

Lines changed: 71 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -11,8 +11,9 @@ use crate::LateContext;
1111
use crate::context::LintContext;
1212
use crate::lints::{
1313
OnlyCastu8ToChar, OverflowingBinHex, OverflowingBinHexSign, OverflowingBinHexSignBitSub,
14-
OverflowingBinHexSub, OverflowingInt, OverflowingIntHelp, OverflowingLiteral, OverflowingUInt,
15-
RangeEndpointOutOfRange, UseInclusiveRange,
14+
OverflowingBinHexSub, OverflowingInt, OverflowingIntError, OverflowingIntHelp,
15+
OverflowingLiteral, OverflowingUInt, OverflowingUIntError, RangeEndpointOutOfRange,
16+
UseInclusiveRange,
1617
};
1718
use crate::types::{OVERFLOWING_LITERALS, TypeLimits};
1819

@@ -250,6 +251,7 @@ fn lint_int_literal<'tcx>(
250251
lit: &hir::Lit,
251252
t: ty::IntTy,
252253
v: u128,
254+
force_error: bool,
253255
) {
254256
let int_type = t.normalize(cx.sess().target.pointer_width);
255257
let (min, max) = int_ty_range(int_type);
@@ -287,11 +289,22 @@ fn lint_int_literal<'tcx>(
287289
let help = get_type_suggestion(cx.typeck_results().node_type(hir_id), v, negative)
288290
.map(|suggestion_ty| OverflowingIntHelp { suggestion_ty });
289291

290-
cx.emit_span_lint(
291-
OVERFLOWING_LITERALS,
292-
span,
293-
OverflowingInt { ty: t.name_str(), lit, min, max, help },
294-
);
292+
if force_error {
293+
cx.tcx.dcx().emit_err(OverflowingIntError {
294+
span,
295+
ty: t.name_str(),
296+
lit,
297+
min,
298+
max,
299+
help,
300+
});
301+
} else {
302+
cx.emit_span_lint(
303+
OVERFLOWING_LITERALS,
304+
span,
305+
OverflowingInt { ty: t.name_str(), lit, min, max, help },
306+
);
307+
}
295308
}
296309
}
297310

@@ -301,6 +314,7 @@ fn lint_uint_literal<'tcx>(
301314
span: Span,
302315
lit: &hir::Lit,
303316
t: ty::UintTy,
317+
force_error: bool,
304318
) {
305319
let uint_type = t.normalize(cx.sess().target.pointer_width);
306320
let (min, max) = uint_ty_range(uint_type);
@@ -344,10 +358,9 @@ fn lint_uint_literal<'tcx>(
344358
);
345359
return;
346360
}
347-
cx.emit_span_lint(
348-
OVERFLOWING_LITERALS,
349-
span,
350-
OverflowingUInt {
361+
if force_error {
362+
cx.tcx.dcx().emit_err(OverflowingUIntError {
363+
span,
351364
ty: t.name_str(),
352365
lit: cx
353366
.sess()
@@ -356,8 +369,23 @@ fn lint_uint_literal<'tcx>(
356369
.unwrap_or_else(|_| lit_val.to_string()),
357370
min,
358371
max,
359-
},
360-
);
372+
});
373+
} else {
374+
cx.emit_span_lint(
375+
OVERFLOWING_LITERALS,
376+
span,
377+
OverflowingUInt {
378+
ty: t.name_str(),
379+
lit: cx
380+
.sess()
381+
.source_map()
382+
.span_to_snippet(lit.span)
383+
.unwrap_or_else(|_| lit_val.to_string()),
384+
min,
385+
max,
386+
},
387+
);
388+
}
361389
}
362390
}
363391

@@ -369,18 +397,39 @@ pub(crate) fn lint_literal<'tcx>(
369397
lit: &hir::Lit,
370398
negated: bool,
371399
) {
372-
match *cx.typeck_results().node_type(hir_id).kind() {
400+
lint_literal_inner(
401+
cx,
402+
type_limits,
403+
hir_id,
404+
cx.typeck_results().node_type(hir_id),
405+
span,
406+
lit,
407+
negated,
408+
false,
409+
)
410+
}
411+
pub(crate) fn lint_literal_inner<'tcx>(
412+
cx: &LateContext<'tcx>,
413+
type_limits: &TypeLimits,
414+
hir_id: HirId,
415+
ty: Ty<'tcx>,
416+
span: Span,
417+
lit: &hir::Lit,
418+
negated: bool,
419+
force_error: bool,
420+
) {
421+
match *ty.kind() {
373422
ty::Int(t) => {
374423
match lit.node {
375424
ast::LitKind::Int(v, ast::LitIntType::Signed(_) | ast::LitIntType::Unsuffixed) => {
376-
lint_int_literal(cx, type_limits, hir_id, span, lit, t, v.get())
425+
lint_int_literal(cx, type_limits, hir_id, span, lit, t, v.get(), force_error)
377426
}
378427
_ => bug!(),
379428
};
380429
}
381430
ty::Uint(t) => {
382431
assert!(!negated);
383-
lint_uint_literal(cx, hir_id, span, lit, t)
432+
lint_uint_literal(cx, hir_id, span, lit, t, force_error)
384433
}
385434
ty::Float(t) => {
386435
let (is_infinite, sym) = match lit.node {
@@ -409,6 +458,12 @@ pub(crate) fn lint_literal<'tcx>(
409458
);
410459
}
411460
}
461+
ty::Pat(base, ..) if base.is_integral() => {
462+
lint_literal_inner(cx, type_limits, hir_id, base, span, lit, negated, true)
463+
}
464+
ty::Adt(adt, args) if cx.tcx.is_lang_item(adt.did(), hir::LangItem::NonZero) => {
465+
lint_literal_inner(cx, type_limits, hir_id, args.type_at(0), span, lit, negated, true)
466+
}
412467
_ => {}
413468
}
414469
}

compiler/rustc_mir_build/src/builder/expr/as_constant.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -107,6 +107,7 @@ fn lit_to_mir_constant<'tcx>(tcx: TyCtxt<'tcx>, lit_input: LitToConstInput<'tcx>
107107

108108
let lit_ty = match *ty.kind() {
109109
ty::Pat(base, _) => base,
110+
ty::Adt(adt, args) if tcx.is_lang_item(adt.did(), LangItem::NonZero) => args.type_at(0),
110111
_ => ty,
111112
};
112113

library/core/src/num/nonzero.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -114,7 +114,7 @@ impl_zeroable_primitive!(
114114
#[stable(feature = "generic_nonzero", since = "1.79.0")]
115115
#[repr(transparent)]
116116
#[rustc_nonnull_optimization_guaranteed]
117-
#[rustc_diagnostic_item = "NonZero"]
117+
#[lang = "NonZero"]
118118
pub struct NonZero<T: ZeroablePrimitive>(T::NonZeroInner);
119119

120120
macro_rules! impl_nonzero_fmt {

src/tools/clippy/clippy_lints/src/methods/useless_nonzero_new_unchecked.rs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,9 @@ use clippy_utils::diagnostics::{span_lint_and_sugg, span_lint_and_then};
22
use clippy_utils::is_inside_always_const_context;
33
use clippy_utils::msrvs::{self, Msrv};
44
use clippy_utils::source::snippet_with_applicability;
5-
use clippy_utils::ty::is_type_diagnostic_item;
5+
use clippy_utils::ty::is_type_lang_item;
66
use rustc_errors::Applicability;
7-
use rustc_hir::{Block, BlockCheckMode, Expr, ExprKind, Node, QPath, UnsafeSource};
7+
use rustc_hir::{Block, BlockCheckMode, Expr, ExprKind, LangItem, Node, QPath, UnsafeSource};
88
use rustc_lint::LateContext;
99
use rustc_span::sym;
1010

@@ -15,7 +15,7 @@ pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &Expr<'_>, func: &Expr<'
1515
&& segment.ident.name == sym::new_unchecked
1616
&& let [init_arg] = args
1717
&& is_inside_always_const_context(cx.tcx, expr.hir_id)
18-
&& is_type_diagnostic_item(cx, cx.typeck_results().node_type(ty.hir_id), sym::NonZero)
18+
&& is_type_lang_item(cx, cx.typeck_results().node_type(ty.hir_id), LangItem::NonZero)
1919
&& msrv.meets(cx, msrvs::CONST_UNWRAP)
2020
{
2121
let mut app = Applicability::MachineApplicable;

src/tools/clippy/clippy_lints/src/non_zero_suggestions.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ use clippy_utils::source::snippet;
33
use clippy_utils::sym;
44
use rustc_ast::ast::BinOpKind;
55
use rustc_errors::Applicability;
6-
use rustc_hir::{Expr, ExprKind};
6+
use rustc_hir::{Expr, ExprKind, LangItem};
77
use rustc_lint::{LateContext, LateLintPass};
88
use rustc_middle::ty::{self, Ty};
99
use rustc_session::declare_lint_pass;
@@ -81,7 +81,7 @@ fn check_non_zero_conversion(cx: &LateContext<'_>, expr: &Expr<'_>, applicabilit
8181
// Check if the receiver type is a NonZero type
8282
if let ty::Adt(adt_def, _) = receiver_ty.kind()
8383
&& adt_def.is_struct()
84-
&& cx.tcx.get_diagnostic_name(adt_def.did()) == Some(sym::NonZero)
84+
&& cx.tcx.is_lang_item(adt_def.did(), LangItem::NonZero)
8585
&& let Some(target_non_zero_type) = get_target_non_zero_type(target_ty)
8686
{
8787
let arg_snippet = get_arg_snippet(cx, arg, rcv_path);

src/tools/clippy/clippy_lints/src/operators/arithmetic_side_effects.rs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ use clippy_utils::diagnostics::span_lint;
55
use clippy_utils::ty::is_type_diagnostic_item;
66
use clippy_utils::{expr_or_init, is_from_proc_macro, is_lint_allowed, peel_hir_expr_refs, peel_hir_expr_unary};
77
use rustc_data_structures::fx::{FxHashMap, FxHashSet};
8+
use rustc_hir::LangItem;
89
use rustc_lint::{LateContext, LateLintPass};
910
use rustc_middle::ty::{self, Ty};
1011
use rustc_session::impl_lint_pass;
@@ -102,7 +103,7 @@ impl ArithmeticSideEffects {
102103

103104
let ty::Adt(adt, substs) = ty.kind() else { return false };
104105

105-
if !tcx.is_diagnostic_item(sym::NonZero, adt.did()) {
106+
if !tcx.is_lang_item(adt.did(), LangItem::NonZero) {
106107
return false;
107108
}
108109

src/tools/clippy/clippy_lints/src/operators/integer_division.rs

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,7 @@
11
use clippy_utils::diagnostics::span_lint_and_then;
2-
use clippy_utils::ty::is_type_diagnostic_item;
3-
use rustc_hir as hir;
2+
use clippy_utils::ty::is_type_lang_item;
3+
use rustc_hir::{self as hir, LangItem};
44
use rustc_lint::LateContext;
5-
use rustc_span::symbol::sym;
65

76
use super::INTEGER_DIVISION;
87

@@ -16,7 +15,7 @@ pub(crate) fn check<'tcx>(
1615
if op == hir::BinOpKind::Div
1716
&& cx.typeck_results().expr_ty(left).is_integral()
1817
&& let right_ty = cx.typeck_results().expr_ty(right)
19-
&& (right_ty.is_integral() || is_type_diagnostic_item(cx, right_ty, sym::NonZero))
18+
&& (right_ty.is_integral() || is_type_lang_item(cx, right_ty, LangItem::NonZero))
2019
{
2120
#[expect(clippy::collapsible_span_lint_calls, reason = "rust-clippy#7797")]
2221
span_lint_and_then(cx, INTEGER_DIVISION, expr.span, "integer division", |diag| {

src/tools/clippy/clippy_lints/src/transmute/transmute_int_to_non_zero.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ use super::TRANSMUTE_INT_TO_NON_ZERO;
22
use clippy_utils::diagnostics::span_lint_and_then;
33
use clippy_utils::sugg;
44
use rustc_errors::Applicability;
5-
use rustc_hir::Expr;
5+
use rustc_hir::{Expr, LangItem};
66
use rustc_lint::LateContext;
77
use rustc_middle::ty::{self, Ty};
88
use rustc_span::symbol::sym;
@@ -22,7 +22,7 @@ pub(super) fn check<'tcx>(
2222
return false;
2323
};
2424

25-
if !tcx.is_diagnostic_item(sym::NonZero, adt.did()) {
25+
if !tcx.is_lang_item(adt.did(), LangItem::NonZero) {
2626
return false;
2727
}
2828

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
//@ check-pass
2+
3+
#![allow(overflowing_literals)]
4+
5+
fn main() {
6+
let x: std::num::NonZero<i8> = -128;
7+
assert_eq!(x.get(), -128_i8);
8+
}
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
//! Ensure overflowing literals are not allowed for
2+
//! layout constrained types like `NonZero`, as that makes
3+
//! it harder to perform the layout checks. Instead
4+
//! we hard error if such literals are out of range.
5+
6+
#![allow(overflowing_literals)]
7+
8+
fn main() {
9+
let _: std::num::NonZero<u8> = 256;
10+
//~^ ERROR literal out of range
11+
12+
let _: std::num::NonZero<i8> = -128;
13+
let _: std::num::NonZero<i8> = -129;
14+
//~^ ERROR literal out of range
15+
}
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
error: literal out of range for `u8`
2+
--> $DIR/non_zero_assigned_oflo_lit.rs:9:36
3+
|
4+
LL | let _: std::num::NonZero<u8> = 256;
5+
| ^^^
6+
|
7+
= note: the literal `256` does not fit into the type `u8` whose range is `0..=255`
8+
9+
error: literal out of range for `i8`
10+
--> $DIR/non_zero_assigned_oflo_lit.rs:13:36
11+
|
12+
LL | let _: std::num::NonZero<i8> = -129;
13+
| ^^^^
14+
|
15+
= note: the literal `-129` does not fit into the type `i8` whose range is `-128..=127`
16+
17+
error: aborting due to 2 previous errors
18+

0 commit comments

Comments
 (0)