Skip to content

Allow initializing NonZero with literals #143594

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Draft
wants to merge 3 commits into
base: master
Choose a base branch
from
Draft
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
2 changes: 2 additions & 0 deletions compiler/rustc_hir/src/lang_items.rs
Original file line number Diff line number Diff line change
Expand Up @@ -426,6 +426,8 @@ language_item_table! {
String, sym::String, string, Target::Struct, GenericRequirement::None;
CStr, sym::CStr, c_str, Target::Struct, GenericRequirement::None;

NonZero, sym::NonZero, non_zero, Target::Struct, GenericRequirement::Exact(1);

// Experimental lang items for implementing contract pre- and post-condition checking.
ContractBuildCheckEnsures, sym::contract_build_check_ensures, contract_build_check_ensures_fn, Target::Fn, GenericRequirement::None;
ContractCheckRequires, sym::contract_check_requires, contract_check_requires_fn, Target::Fn, GenericRequirement::None;
Expand Down
8 changes: 8 additions & 0 deletions compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1665,6 +1665,14 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
_ => unreachable!(),
}
}
ty::Adt(adt, ..) if tcx.is_lang_item(adt.did(), LangItem::NonZero) => {
if i.get() == 0 {
// FIXME: report a nice error
None
} else {
Some(ty)
}
}
_ => None,
});
opt_ty.unwrap_or_else(|| self.next_int_var())
Expand Down
2 changes: 1 addition & 1 deletion compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2487,7 +2487,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
_ => return false,
};

if !self.tcx.is_diagnostic_item(sym::NonZero, adt.did()) {
if !self.tcx.is_lang_item(adt.did(), LangItem::NonZero) {
return false;
}

Expand Down
27 changes: 26 additions & 1 deletion compiler/rustc_lint/src/lints.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ use rustc_hir as hir;
use rustc_hir::def::Namespace;
use rustc_hir::def_id::DefId;
use rustc_hir::intravisit::VisitorExt;
use rustc_macros::{LintDiagnostic, Subdiagnostic};
use rustc_macros::{Diagnostic, LintDiagnostic, Subdiagnostic};
use rustc_middle::ty::inhabitedness::InhabitedPredicate;
use rustc_middle::ty::{Clause, PolyExistentialTraitRef, Ty, TyCtxt};
use rustc_session::Session;
Expand Down Expand Up @@ -1713,6 +1713,19 @@ pub(crate) struct OverflowingInt<'a> {
#[subdiagnostic]
pub help: Option<OverflowingIntHelp<'a>>,
}
#[derive(Diagnostic)]
#[diag(lint_overflowing_int)]
#[note]
pub(crate) struct OverflowingIntError<'a> {
#[primary_span]
pub span: Span,
pub ty: &'a str,
pub lit: String,
pub min: i128,
pub max: u128,
#[subdiagnostic]
pub help: Option<OverflowingIntHelp<'a>>,
}

#[derive(Subdiagnostic)]
#[help(lint_help)]
Expand All @@ -1738,6 +1751,18 @@ pub(crate) struct OverflowingUInt<'a> {
pub max: u128,
}

#[derive(Diagnostic)]
#[diag(lint_overflowing_uint)]
#[note]
pub(crate) struct OverflowingUIntError<'a> {
#[primary_span]
pub span: Span,
pub ty: &'a str,
pub lit: String,
pub min: u128,
pub max: u128,
}

#[derive(LintDiagnostic)]
#[diag(lint_overflowing_literal)]
#[note]
Expand Down
87 changes: 71 additions & 16 deletions compiler/rustc_lint/src/types/literal.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,9 @@ use crate::LateContext;
use crate::context::LintContext;
use crate::lints::{
OnlyCastu8ToChar, OverflowingBinHex, OverflowingBinHexSign, OverflowingBinHexSignBitSub,
OverflowingBinHexSub, OverflowingInt, OverflowingIntHelp, OverflowingLiteral, OverflowingUInt,
RangeEndpointOutOfRange, UseInclusiveRange,
OverflowingBinHexSub, OverflowingInt, OverflowingIntError, OverflowingIntHelp,
OverflowingLiteral, OverflowingUInt, OverflowingUIntError, RangeEndpointOutOfRange,
UseInclusiveRange,
};
use crate::types::{OVERFLOWING_LITERALS, TypeLimits};

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

cx.emit_span_lint(
OVERFLOWING_LITERALS,
span,
OverflowingInt { ty: t.name_str(), lit, min, max, help },
);
if force_error {
cx.tcx.dcx().emit_err(OverflowingIntError {
span,
ty: t.name_str(),
lit,
min,
max,
help,
});
} else {
cx.emit_span_lint(
OVERFLOWING_LITERALS,
span,
OverflowingInt { ty: t.name_str(), lit, min, max, help },
);
}
}
}

Expand All @@ -301,6 +314,7 @@ fn lint_uint_literal<'tcx>(
span: Span,
lit: &hir::Lit,
t: ty::UintTy,
force_error: bool,
) {
let uint_type = t.normalize(cx.sess().target.pointer_width);
let (min, max) = uint_ty_range(uint_type);
Expand Down Expand Up @@ -344,10 +358,9 @@ fn lint_uint_literal<'tcx>(
);
return;
}
cx.emit_span_lint(
OVERFLOWING_LITERALS,
span,
OverflowingUInt {
if force_error {
cx.tcx.dcx().emit_err(OverflowingUIntError {
span,
ty: t.name_str(),
lit: cx
.sess()
Expand All @@ -356,8 +369,23 @@ fn lint_uint_literal<'tcx>(
.unwrap_or_else(|_| lit_val.to_string()),
min,
max,
},
);
});
} else {
cx.emit_span_lint(
OVERFLOWING_LITERALS,
span,
OverflowingUInt {
ty: t.name_str(),
lit: cx
.sess()
.source_map()
.span_to_snippet(lit.span)
.unwrap_or_else(|_| lit_val.to_string()),
min,
max,
},
);
}
}
}

Expand All @@ -369,18 +397,39 @@ pub(crate) fn lint_literal<'tcx>(
lit: &hir::Lit,
negated: bool,
) {
match *cx.typeck_results().node_type(hir_id).kind() {
lint_literal_inner(
cx,
type_limits,
hir_id,
cx.typeck_results().node_type(hir_id),
span,
lit,
negated,
false,
)
}
pub(crate) fn lint_literal_inner<'tcx>(
cx: &LateContext<'tcx>,
type_limits: &TypeLimits,
hir_id: HirId,
ty: Ty<'tcx>,
span: Span,
lit: &hir::Lit,
negated: bool,
force_error: bool,
) {
match *ty.kind() {
ty::Int(t) => {
match lit.node {
ast::LitKind::Int(v, ast::LitIntType::Signed(_) | ast::LitIntType::Unsuffixed) => {
lint_int_literal(cx, type_limits, hir_id, span, lit, t, v.get())
lint_int_literal(cx, type_limits, hir_id, span, lit, t, v.get(), force_error)
}
_ => bug!(),
};
}
ty::Uint(t) => {
assert!(!negated);
lint_uint_literal(cx, hir_id, span, lit, t)
lint_uint_literal(cx, hir_id, span, lit, t, force_error)
}
ty::Float(t) => {
let (is_infinite, sym) = match lit.node {
Expand Down Expand Up @@ -409,6 +458,12 @@ pub(crate) fn lint_literal<'tcx>(
);
}
}
ty::Pat(base, ..) if base.is_integral() => {
lint_literal_inner(cx, type_limits, hir_id, base, span, lit, negated, true)
}
ty::Adt(adt, args) if cx.tcx.is_lang_item(adt.did(), hir::LangItem::NonZero) => {
lint_literal_inner(cx, type_limits, hir_id, args.type_at(0), span, lit, negated, true)
}
_ => {}
}
}
1 change: 1 addition & 0 deletions compiler/rustc_mir_build/src/builder/expr/as_constant.rs
Original file line number Diff line number Diff line change
Expand Up @@ -107,6 +107,7 @@ fn lit_to_mir_constant<'tcx>(tcx: TyCtxt<'tcx>, lit_input: LitToConstInput<'tcx>

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

Expand Down
2 changes: 1 addition & 1 deletion library/core/src/num/nonzero.rs
Original file line number Diff line number Diff line change
Expand Up @@ -123,7 +123,7 @@ impl_zeroable_primitive!(
#[stable(feature = "generic_nonzero", since = "1.79.0")]
#[repr(transparent)]
#[rustc_nonnull_optimization_guaranteed]
#[rustc_diagnostic_item = "NonZero"]
#[lang = "NonZero"]
pub struct NonZero<T: ZeroablePrimitive>(T::NonZeroInner);

macro_rules! impl_nonzero_fmt {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,9 @@ use clippy_utils::diagnostics::{span_lint_and_sugg, span_lint_and_then};
use clippy_utils::is_inside_always_const_context;
use clippy_utils::msrvs::{self, Msrv};
use clippy_utils::source::snippet_with_applicability;
use clippy_utils::ty::is_type_diagnostic_item;
use clippy_utils::ty::is_type_lang_item;
use rustc_errors::Applicability;
use rustc_hir::{Block, BlockCheckMode, Expr, ExprKind, Node, QPath, UnsafeSource};
use rustc_hir::{Block, BlockCheckMode, Expr, ExprKind, LangItem, Node, QPath, UnsafeSource};
use rustc_lint::LateContext;
use rustc_span::sym;

Expand All @@ -15,7 +15,7 @@ pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &Expr<'_>, func: &Expr<'
&& segment.ident.name == sym::new_unchecked
&& let [init_arg] = args
&& is_inside_always_const_context(cx.tcx, expr.hir_id)
&& is_type_diagnostic_item(cx, cx.typeck_results().node_type(ty.hir_id), sym::NonZero)
&& is_type_lang_item(cx, cx.typeck_results().node_type(ty.hir_id), LangItem::NonZero)
&& msrv.meets(cx, msrvs::CONST_UNWRAP)
{
let mut app = Applicability::MachineApplicable;
Expand Down
4 changes: 2 additions & 2 deletions src/tools/clippy/clippy_lints/src/non_zero_suggestions.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ use clippy_utils::source::snippet;
use clippy_utils::sym;
use rustc_ast::ast::BinOpKind;
use rustc_errors::Applicability;
use rustc_hir::{Expr, ExprKind};
use rustc_hir::{Expr, ExprKind, LangItem};
use rustc_lint::{LateContext, LateLintPass};
use rustc_middle::ty::{self, Ty};
use rustc_session::declare_lint_pass;
Expand Down Expand Up @@ -81,7 +81,7 @@ fn check_non_zero_conversion(cx: &LateContext<'_>, expr: &Expr<'_>, applicabilit
// Check if the receiver type is a NonZero type
if let ty::Adt(adt_def, _) = receiver_ty.kind()
&& adt_def.is_struct()
&& cx.tcx.get_diagnostic_name(adt_def.did()) == Some(sym::NonZero)
&& cx.tcx.is_lang_item(adt_def.did(), LangItem::NonZero)
&& let Some(target_non_zero_type) = get_target_non_zero_type(target_ty)
{
let arg_snippet = get_arg_snippet(cx, arg, rcv_path);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ use clippy_utils::diagnostics::span_lint;
use clippy_utils::ty::is_type_diagnostic_item;
use clippy_utils::{expr_or_init, is_from_proc_macro, is_lint_allowed, peel_hir_expr_refs, peel_hir_expr_unary};
use rustc_data_structures::fx::{FxHashMap, FxHashSet};
use rustc_hir::LangItem;
use rustc_lint::{LateContext, LateLintPass};
use rustc_middle::ty::{self, Ty};
use rustc_session::impl_lint_pass;
Expand Down Expand Up @@ -102,7 +103,7 @@ impl ArithmeticSideEffects {

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

if !tcx.is_diagnostic_item(sym::NonZero, adt.did()) {
if !tcx.is_lang_item(adt.did(), LangItem::NonZero) {
return false;
}

Expand Down
Original file line number Diff line number Diff line change
@@ -1,8 +1,7 @@
use clippy_utils::diagnostics::span_lint_and_then;
use clippy_utils::ty::is_type_diagnostic_item;
use rustc_hir as hir;
use clippy_utils::ty::is_type_lang_item;
use rustc_hir::{self as hir, LangItem};
use rustc_lint::LateContext;
use rustc_span::symbol::sym;

use super::INTEGER_DIVISION;

Expand All @@ -16,7 +15,7 @@ pub(crate) fn check<'tcx>(
if op == hir::BinOpKind::Div
&& cx.typeck_results().expr_ty(left).is_integral()
&& let right_ty = cx.typeck_results().expr_ty(right)
&& (right_ty.is_integral() || is_type_diagnostic_item(cx, right_ty, sym::NonZero))
&& (right_ty.is_integral() || is_type_lang_item(cx, right_ty, LangItem::NonZero))
{
#[expect(clippy::collapsible_span_lint_calls, reason = "rust-clippy#7797")]
span_lint_and_then(cx, INTEGER_DIVISION, expr.span, "integer division", |diag| {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ use super::TRANSMUTE_INT_TO_NON_ZERO;
use clippy_utils::diagnostics::span_lint_and_then;
use clippy_utils::sugg;
use rustc_errors::Applicability;
use rustc_hir::Expr;
use rustc_hir::{Expr, LangItem};
use rustc_lint::LateContext;
use rustc_middle::ty::{self, Ty};
use rustc_span::symbol::sym;
Expand All @@ -22,7 +22,7 @@ pub(super) fn check<'tcx>(
return false;
};

if !tcx.is_diagnostic_item(sym::NonZero, adt.did()) {
if !tcx.is_lang_item(adt.did(), LangItem::NonZero) {
return false;
}

Expand Down
8 changes: 8 additions & 0 deletions tests/ui/mismatched_types/non_zero_assigned_lit.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
//@ check-pass

#![allow(overflowing_literals)]

fn main() {
let x: std::num::NonZero<i8> = -128;
assert_eq!(x.get(), -128_i8);
}
15 changes: 15 additions & 0 deletions tests/ui/mismatched_types/non_zero_assigned_oflo_lit.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
//! Ensure overflowing literals are not allowed for
//! layout constrained types like `NonZero`, as that makes
//! it harder to perform the layout checks. Instead
//! we hard error if such literals are out of range.
#![allow(overflowing_literals)]

fn main() {
let _: std::num::NonZero<u8> = 256;
//~^ ERROR literal out of range
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Here's the erroring example


let _: std::num::NonZero<i8> = -128;
let _: std::num::NonZero<i8> = -129;
//~^ ERROR literal out of range
}
18 changes: 18 additions & 0 deletions tests/ui/mismatched_types/non_zero_assigned_oflo_lit.stderr
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
error: literal out of range for `u8`
--> $DIR/non_zero_assigned_oflo_lit.rs:9:36
|
LL | let _: std::num::NonZero<u8> = 256;
| ^^^
|
= note: the literal `256` does not fit into the type `u8` whose range is `0..=255`

error: literal out of range for `i8`
--> $DIR/non_zero_assigned_oflo_lit.rs:13:36
|
LL | let _: std::num::NonZero<i8> = -129;
| ^^^^
|
= note: the literal `-129` does not fit into the type `i8` whose range is `-128..=127`

error: aborting due to 2 previous errors

Loading
Loading