From 7ec721dd6deece7111690cc2e8c5ccccbc129c09 Mon Sep 17 00:00:00 2001 From: Oli Scherer Date: Mon, 7 Jul 2025 14:08:10 +0000 Subject: [PATCH 1/3] Make `NonZero` a lang item --- compiler/rustc_hir/src/lang_items.rs | 2 ++ compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs | 2 +- library/core/src/num/nonzero.rs | 2 +- .../src/methods/useless_nonzero_new_unchecked.rs | 3 ++- src/tools/clippy/clippy_lints/src/non_zero_suggestions.rs | 4 ++-- .../clippy_lints/src/operators/arithmetic_side_effects.rs | 3 ++- .../clippy/clippy_lints/src/operators/integer_division.rs | 4 ++-- .../clippy_lints/src/transmute/transmute_int_to_non_zero.rs | 5 +++-- 8 files changed, 15 insertions(+), 10 deletions(-) diff --git a/compiler/rustc_hir/src/lang_items.rs b/compiler/rustc_hir/src/lang_items.rs index 9f7f4c7583412..a9e4494d848e6 100644 --- a/compiler/rustc_hir/src/lang_items.rs +++ b/compiler/rustc_hir/src/lang_items.rs @@ -428,6 +428,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; diff --git a/compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs b/compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs index 1d16c3af7fb75..377a2446ab1d3 100644 --- a/compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs +++ b/compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs @@ -2581,7 +2581,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; } diff --git a/library/core/src/num/nonzero.rs b/library/core/src/num/nonzero.rs index efb0665b7f461..f2aa9bc82fa59 100644 --- a/library/core/src/num/nonzero.rs +++ b/library/core/src/num/nonzero.rs @@ -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::NonZeroInner); macro_rules! impl_nonzero_fmt { diff --git a/src/tools/clippy/clippy_lints/src/methods/useless_nonzero_new_unchecked.rs b/src/tools/clippy/clippy_lints/src/methods/useless_nonzero_new_unchecked.rs index c6f54159c7a78..cfa7e5b724139 100644 --- a/src/tools/clippy/clippy_lints/src/methods/useless_nonzero_new_unchecked.rs +++ b/src/tools/clippy/clippy_lints/src/methods/useless_nonzero_new_unchecked.rs @@ -7,6 +7,7 @@ use rustc_errors::Applicability; use rustc_hir::{Block, BlockCheckMode, Expr, ExprKind, Node, QPath, UnsafeSource}; use rustc_lint::LateContext; use rustc_span::sym; +use rustc_hir::LangItem; use super::USELESS_NONZERO_NEW_UNCHECKED; @@ -15,7 +16,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) - && cx.typeck_results().node_type(ty.hir_id).is_diag_item(cx, sym::NonZero) + && cx.typeck_results().node_type(ty.hir_id).is_lang_item(cx, LangItem::NonZero) && msrv.meets(cx, msrvs::CONST_UNWRAP) { let mut app = Applicability::MachineApplicable; diff --git a/src/tools/clippy/clippy_lints/src/non_zero_suggestions.rs b/src/tools/clippy/clippy_lints/src/non_zero_suggestions.rs index 1b8ab1bdedf8a..6864d4b880567 100644 --- a/src/tools/clippy/clippy_lints/src/non_zero_suggestions.rs +++ b/src/tools/clippy/clippy_lints/src/non_zero_suggestions.rs @@ -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; @@ -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); diff --git a/src/tools/clippy/clippy_lints/src/operators/arithmetic_side_effects.rs b/src/tools/clippy/clippy_lints/src/operators/arithmetic_side_effects.rs index 0a6499e095832..5074089984887 100644 --- a/src/tools/clippy/clippy_lints/src/operators/arithmetic_side_effects.rs +++ b/src/tools/clippy/clippy_lints/src/operators/arithmetic_side_effects.rs @@ -5,6 +5,7 @@ use clippy_utils::diagnostics::span_lint; use clippy_utils::res::MaybeDef; use clippy_utils::{expr_or_init, is_from_proc_macro, is_lint_allowed, peel_hir_expr_refs, peel_hir_expr_unary, sym}; 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; @@ -90,7 +91,7 @@ impl ArithmeticSideEffects { fn is_non_zero_u(cx: &LateContext<'_>, ty: Ty<'_>) -> bool { if let ty::Adt(adt, substs) = ty.kind() - && cx.tcx.is_diagnostic_item(sym::NonZero, adt.did()) + && cx.tcx.is_lang_item(adt.did(),LangItem::NonZero) && let int_type = substs.type_at(0) && matches!(int_type.kind(), ty::Uint(_)) { diff --git a/src/tools/clippy/clippy_lints/src/operators/integer_division.rs b/src/tools/clippy/clippy_lints/src/operators/integer_division.rs index 1620312474e99..1b8647f06c49c 100644 --- a/src/tools/clippy/clippy_lints/src/operators/integer_division.rs +++ b/src/tools/clippy/clippy_lints/src/operators/integer_division.rs @@ -2,7 +2,7 @@ use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::res::MaybeDef; use rustc_hir as hir; use rustc_lint::LateContext; -use rustc_span::symbol::sym; +use rustc_hir::LangItem; use super::INTEGER_DIVISION; @@ -16,7 +16,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() || right_ty.is_diag_item(cx, sym::NonZero)) + && (right_ty.is_integral() || right_ty.is_lang_item(cx, LangItem::NonZero)) { #[expect(clippy::collapsible_span_lint_calls, reason = "rust-clippy#7797")] span_lint_and_then(cx, INTEGER_DIVISION, expr.span, "integer division", |diag| { diff --git a/src/tools/clippy/clippy_lints/src/transmute/transmute_int_to_non_zero.rs b/src/tools/clippy/clippy_lints/src/transmute/transmute_int_to_non_zero.rs index 2257aa1b73c87..70e6b37da707c 100644 --- a/src/tools/clippy/clippy_lints/src/transmute/transmute_int_to_non_zero.rs +++ b/src/tools/clippy/clippy_lints/src/transmute/transmute_int_to_non_zero.rs @@ -2,10 +2,11 @@ use super::TRANSMUTE_INT_TO_NON_ZERO; use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::sugg::Sugg; use rustc_errors::Applicability; -use rustc_hir::Expr; +use rustc_hir::{Expr}; use rustc_lint::LateContext; use rustc_middle::ty::{self, Ty}; use rustc_span::symbol::sym; +use rustc_hir::LangItem; /// Checks for `transmute_int_to_non_zero` lint. /// Returns `true` if it's triggered, otherwise returns `false`. @@ -18,7 +19,7 @@ pub(super) fn check<'tcx>( ) -> bool { if let ty::Int(_) | ty::Uint(_) = from_ty.kind() && let ty::Adt(adt, substs) = to_ty.kind() - && cx.tcx.is_diagnostic_item(sym::NonZero, adt.did()) + && cx.tcx.is_lang_item( adt.did(), LangItem::NonZero) && let int_ty = substs.type_at(0) && from_ty == int_ty { From 25bbcfc2c0db74f7afd5fd376383fccc0c3b63d0 Mon Sep 17 00:00:00 2001 From: Oli Scherer Date: Mon, 7 Jul 2025 14:08:10 +0000 Subject: [PATCH 2/3] Add some `NonZero` tests --- .../mismatched_types/non_zero_assigned_lit.rs | 8 + .../non_zero_assigned_lit.stderr | 18 ++ .../non_zero_assigned_oflo_lit.rs | 14 ++ .../non_zero_assigned_oflo_lit.stderr | 48 +++++ .../non_zero_assigned_something.rs | 40 ++++ .../non_zero_assigned_something.stderr | 180 +++++++++++++++++- 6 files changed, 306 insertions(+), 2 deletions(-) create mode 100644 tests/ui/mismatched_types/non_zero_assigned_lit.rs create mode 100644 tests/ui/mismatched_types/non_zero_assigned_lit.stderr create mode 100644 tests/ui/mismatched_types/non_zero_assigned_oflo_lit.rs create mode 100644 tests/ui/mismatched_types/non_zero_assigned_oflo_lit.stderr diff --git a/tests/ui/mismatched_types/non_zero_assigned_lit.rs b/tests/ui/mismatched_types/non_zero_assigned_lit.rs new file mode 100644 index 0000000000000..9c4814b5bfdf6 --- /dev/null +++ b/tests/ui/mismatched_types/non_zero_assigned_lit.rs @@ -0,0 +1,8 @@ +#![allow(overflowing_literals)] + +fn main() { + let x: std::num::NonZero = -128; + //~^ ERROR mismatched types + //~| HELP consider calling `NonZero::new` + assert_eq!(x.get(), -128_i8); +} diff --git a/tests/ui/mismatched_types/non_zero_assigned_lit.stderr b/tests/ui/mismatched_types/non_zero_assigned_lit.stderr new file mode 100644 index 0000000000000..2e0f6486810f2 --- /dev/null +++ b/tests/ui/mismatched_types/non_zero_assigned_lit.stderr @@ -0,0 +1,18 @@ +error[E0308]: mismatched types + --> $DIR/non_zero_assigned_lit.rs:4:36 + | +LL | let x: std::num::NonZero = -128; + | --------------------- ^^^^ expected `NonZero`, found integer + | | + | expected due to this + | + = note: expected struct `NonZero` + found type `{integer}` +help: consider calling `NonZero::new` + | +LL | let x: std::num::NonZero = NonZero::new(-128).unwrap(); + | +++++++++++++ ++++++++++ + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0308`. diff --git a/tests/ui/mismatched_types/non_zero_assigned_oflo_lit.rs b/tests/ui/mismatched_types/non_zero_assigned_oflo_lit.rs new file mode 100644 index 0000000000000..c66991b512cbc --- /dev/null +++ b/tests/ui/mismatched_types/non_zero_assigned_oflo_lit.rs @@ -0,0 +1,14 @@ +#![allow(overflowing_literals)] + +fn main() { + let _: std::num::NonZero = 256; + //~^ ERROR mismatched types + //~| HELP consider calling `NonZero::new` + + let _: std::num::NonZero = -128; + //~^ ERROR mismatched types + //~| HELP consider calling `NonZero::new` + let _: std::num::NonZero = -129; + //~^ ERROR mismatched types + //~| HELP consider calling `NonZero::new` +} diff --git a/tests/ui/mismatched_types/non_zero_assigned_oflo_lit.stderr b/tests/ui/mismatched_types/non_zero_assigned_oflo_lit.stderr new file mode 100644 index 0000000000000..cfcae44db0af0 --- /dev/null +++ b/tests/ui/mismatched_types/non_zero_assigned_oflo_lit.stderr @@ -0,0 +1,48 @@ +error[E0308]: mismatched types + --> $DIR/non_zero_assigned_oflo_lit.rs:4:36 + | +LL | let _: std::num::NonZero = 256; + | --------------------- ^^^ expected `NonZero`, found integer + | | + | expected due to this + | + = note: expected struct `NonZero` + found type `{integer}` +help: consider calling `NonZero::new` + | +LL | let _: std::num::NonZero = NonZero::new(256).unwrap(); + | +++++++++++++ ++++++++++ + +error[E0308]: mismatched types + --> $DIR/non_zero_assigned_oflo_lit.rs:8:36 + | +LL | let _: std::num::NonZero = -128; + | --------------------- ^^^^ expected `NonZero`, found integer + | | + | expected due to this + | + = note: expected struct `NonZero` + found type `{integer}` +help: consider calling `NonZero::new` + | +LL | let _: std::num::NonZero = NonZero::new(-128).unwrap(); + | +++++++++++++ ++++++++++ + +error[E0308]: mismatched types + --> $DIR/non_zero_assigned_oflo_lit.rs:11:36 + | +LL | let _: std::num::NonZero = -129; + | --------------------- ^^^^ expected `NonZero`, found integer + | | + | expected due to this + | + = note: expected struct `NonZero` + found type `{integer}` +help: consider calling `NonZero::new` + | +LL | let _: std::num::NonZero = NonZero::new(-129).unwrap(); + | +++++++++++++ ++++++++++ + +error: aborting due to 3 previous errors + +For more information about this error, try `rustc --explain E0308`. diff --git a/tests/ui/mismatched_types/non_zero_assigned_something.rs b/tests/ui/mismatched_types/non_zero_assigned_something.rs index e94d5249d6a84..51322a88a2115 100644 --- a/tests/ui/mismatched_types/non_zero_assigned_something.rs +++ b/tests/ui/mismatched_types/non_zero_assigned_something.rs @@ -1,7 +1,47 @@ fn main() { + let _: std::num::NonZero = 0; + //~^ ERROR mismatched types + //~| HELP consider calling `NonZero::new` + + let _: Option> = 0; + //~^ ERROR mismatched types + //~| HELP consider calling `NonZero::new` + let _: std::num::NonZero = 1; //~^ ERROR mismatched types //~| HELP consider calling `NonZero::new` + let _: std::num::NonZero = 1u8; + //~^ ERROR mismatched types + let _: std::num::NonZero = 1u64; + //~^ ERROR mismatched types + //~| HELP consider calling `NonZero::new` + + let _: std::num::NonZero = 255; + //~^ ERROR mismatched types + //~| HELP consider calling `NonZero::new` + let _: std::num::NonZero = 256; + //~^ ERROR mismatched types + //~| HELP consider calling `NonZero::new` + + let _: std::num::NonZero = -10; + //~^ ERROR mismatched types + //~| HELP consider calling `NonZero::new` + + let _: std::num::NonZero = -128; + //~^ ERROR mismatched types + //~| HELP consider calling `NonZero::new` + let _: std::num::NonZero = -129; + //~^ ERROR mismatched types + //~| HELP consider calling `NonZero::new` + let _: std::num::NonZero = -1; + //~^ ERROR mismatched types + //~| HELP consider calling `NonZero::new` + let _: std::num::NonZero = 0; + //~^ ERROR mismatched types + //~| HELP consider calling `NonZero::new` + let _: std::num::NonZero = 1; + //~^ ERROR mismatched types + //~| HELP consider calling `NonZero::new` let _: Option> = 1; //~^ ERROR mismatched types diff --git a/tests/ui/mismatched_types/non_zero_assigned_something.stderr b/tests/ui/mismatched_types/non_zero_assigned_something.stderr index aa015bd2efcc9..f50d2960f8686 100644 --- a/tests/ui/mismatched_types/non_zero_assigned_something.stderr +++ b/tests/ui/mismatched_types/non_zero_assigned_something.stderr @@ -1,6 +1,36 @@ error[E0308]: mismatched types --> $DIR/non_zero_assigned_something.rs:2:37 | +LL | let _: std::num::NonZero = 0; + | ---------------------- ^ expected `NonZero`, found integer + | | + | expected due to this + | + = note: expected struct `NonZero` + found type `{integer}` +help: consider calling `NonZero::new` + | +LL | let _: std::num::NonZero = NonZero::new(0).unwrap(); + | +++++++++++++ ++++++++++ + +error[E0308]: mismatched types + --> $DIR/non_zero_assigned_something.rs:6:45 + | +LL | let _: Option> = 0; + | ------------------------------ ^ expected `Option>`, found integer + | | + | expected due to this + | + = note: expected enum `Option>` + found type `{integer}` +help: consider calling `NonZero::new` + | +LL | let _: Option> = NonZero::new(0); + | +++++++++++++ + + +error[E0308]: mismatched types + --> $DIR/non_zero_assigned_something.rs:10:37 + | LL | let _: std::num::NonZero = 1; | ---------------------- ^ expected `NonZero`, found integer | | @@ -14,7 +44,153 @@ LL | let _: std::num::NonZero = NonZero::new(1).unwrap(); | +++++++++++++ ++++++++++ error[E0308]: mismatched types - --> $DIR/non_zero_assigned_something.rs:6:45 + --> $DIR/non_zero_assigned_something.rs:13:37 + | +LL | let _: std::num::NonZero = 1u8; + | ---------------------- ^^^ expected `NonZero`, found `u8` + | | + | expected due to this + | + = note: expected struct `NonZero` + found type `u8` + +error[E0308]: mismatched types + --> $DIR/non_zero_assigned_something.rs:15:37 + | +LL | let _: std::num::NonZero = 1u64; + | ---------------------- ^^^^ expected `NonZero`, found `u64` + | | + | expected due to this + | + = note: expected struct `NonZero` + found type `u64` +help: consider calling `NonZero::new` + | +LL | let _: std::num::NonZero = NonZero::new(1u64).unwrap(); + | +++++++++++++ ++++++++++ + +error[E0308]: mismatched types + --> $DIR/non_zero_assigned_something.rs:19:36 + | +LL | let _: std::num::NonZero = 255; + | --------------------- ^^^ expected `NonZero`, found integer + | | + | expected due to this + | + = note: expected struct `NonZero` + found type `{integer}` +help: consider calling `NonZero::new` + | +LL | let _: std::num::NonZero = NonZero::new(255).unwrap(); + | +++++++++++++ ++++++++++ + +error[E0308]: mismatched types + --> $DIR/non_zero_assigned_something.rs:22:36 + | +LL | let _: std::num::NonZero = 256; + | --------------------- ^^^ expected `NonZero`, found integer + | | + | expected due to this + | + = note: expected struct `NonZero` + found type `{integer}` +help: consider calling `NonZero::new` + | +LL | let _: std::num::NonZero = NonZero::new(256).unwrap(); + | +++++++++++++ ++++++++++ + +error[E0308]: mismatched types + --> $DIR/non_zero_assigned_something.rs:26:36 + | +LL | let _: std::num::NonZero = -10; + | --------------------- ^^^ expected `NonZero`, found integer + | | + | expected due to this + | + = note: expected struct `NonZero` + found type `{integer}` +help: consider calling `NonZero::new` + | +LL | let _: std::num::NonZero = NonZero::new(-10).unwrap(); + | +++++++++++++ ++++++++++ + +error[E0308]: mismatched types + --> $DIR/non_zero_assigned_something.rs:30:36 + | +LL | let _: std::num::NonZero = -128; + | --------------------- ^^^^ expected `NonZero`, found integer + | | + | expected due to this + | + = note: expected struct `NonZero` + found type `{integer}` +help: consider calling `NonZero::new` + | +LL | let _: std::num::NonZero = NonZero::new(-128).unwrap(); + | +++++++++++++ ++++++++++ + +error[E0308]: mismatched types + --> $DIR/non_zero_assigned_something.rs:33:36 + | +LL | let _: std::num::NonZero = -129; + | --------------------- ^^^^ expected `NonZero`, found integer + | | + | expected due to this + | + = note: expected struct `NonZero` + found type `{integer}` +help: consider calling `NonZero::new` + | +LL | let _: std::num::NonZero = NonZero::new(-129).unwrap(); + | +++++++++++++ ++++++++++ + +error[E0308]: mismatched types + --> $DIR/non_zero_assigned_something.rs:36:36 + | +LL | let _: std::num::NonZero = -1; + | --------------------- ^^ expected `NonZero`, found integer + | | + | expected due to this + | + = note: expected struct `NonZero` + found type `{integer}` +help: consider calling `NonZero::new` + | +LL | let _: std::num::NonZero = NonZero::new(-1).unwrap(); + | +++++++++++++ ++++++++++ + +error[E0308]: mismatched types + --> $DIR/non_zero_assigned_something.rs:39:36 + | +LL | let _: std::num::NonZero = 0; + | --------------------- ^ expected `NonZero`, found integer + | | + | expected due to this + | + = note: expected struct `NonZero` + found type `{integer}` +help: consider calling `NonZero::new` + | +LL | let _: std::num::NonZero = NonZero::new(0).unwrap(); + | +++++++++++++ ++++++++++ + +error[E0308]: mismatched types + --> $DIR/non_zero_assigned_something.rs:42:36 + | +LL | let _: std::num::NonZero = 1; + | --------------------- ^ expected `NonZero`, found integer + | | + | expected due to this + | + = note: expected struct `NonZero` + found type `{integer}` +help: consider calling `NonZero::new` + | +LL | let _: std::num::NonZero = NonZero::new(1).unwrap(); + | +++++++++++++ ++++++++++ + +error[E0308]: mismatched types + --> $DIR/non_zero_assigned_something.rs:46:45 | LL | let _: Option> = 1; | ------------------------------ ^ expected `Option>`, found integer @@ -28,6 +204,6 @@ help: consider calling `NonZero::new` LL | let _: Option> = NonZero::new(1); | +++++++++++++ + -error: aborting due to 2 previous errors +error: aborting due to 14 previous errors For more information about this error, try `rustc --explain E0308`. From 871e2e317f13abba68ff7fcbf3e21a0c07c73260 Mon Sep 17 00:00:00 2001 From: Oli Scherer Date: Tue, 8 Jul 2025 07:53:24 +0000 Subject: [PATCH 3/3] Allow coercing `NonZero` to `T` --- compiler/rustc_hir_typeck/src/coercion.rs | 16 ++++ .../rustc_hir_typeck/src/expr_use_visitor.rs | 3 + .../rustc_hir_typeck/src/fn_ctxt/_impl.rs | 2 +- compiler/rustc_lint/src/autorefs.rs | 1 + compiler/rustc_middle/src/ty/adjustment.rs | 3 + compiler/rustc_mir_build/src/thir/cx/expr.rs | 28 ++++++ tests/ui/mismatched_types/non_zero_coerce.rs | 32 +++++++ .../mismatched_types/non_zero_coerce_fail.rs | 31 +++++++ .../non_zero_coerce_fail.stderr | 90 +++++++++++++++++++ .../mismatched_types/non_zero_coerce_fail2.rs | 29 ++++++ .../non_zero_coerce_fail2.stderr | 84 +++++++++++++++++ .../non_zero_coerce_fail_infer.rs | 14 +++ .../non_zero_coerce_fail_infer.stderr | 9 ++ 13 files changed, 341 insertions(+), 1 deletion(-) create mode 100644 tests/ui/mismatched_types/non_zero_coerce.rs create mode 100644 tests/ui/mismatched_types/non_zero_coerce_fail.rs create mode 100644 tests/ui/mismatched_types/non_zero_coerce_fail.stderr create mode 100644 tests/ui/mismatched_types/non_zero_coerce_fail2.rs create mode 100644 tests/ui/mismatched_types/non_zero_coerce_fail2.stderr create mode 100644 tests/ui/mismatched_types/non_zero_coerce_fail_infer.rs create mode 100644 tests/ui/mismatched_types/non_zero_coerce_fail_infer.stderr diff --git a/compiler/rustc_hir_typeck/src/coercion.rs b/compiler/rustc_hir_typeck/src/coercion.rs index 857e4f66489ab..74c485d70564d 100644 --- a/compiler/rustc_hir_typeck/src/coercion.rs +++ b/compiler/rustc_hir_typeck/src/coercion.rs @@ -265,6 +265,9 @@ impl<'f, 'tcx> Coerce<'f, 'tcx> { // It cannot convert closures that require unsafe. self.coerce_closure_to_fn(a, closure_def_id_a, args_a, b) } + ty::Adt(adt, args) if self.tcx.is_lang_item(adt.did(), hir::LangItem::NonZero) => { + self.coerce_non_zero(a, args.type_at(0), b) + } _ => { // Otherwise, just use unification rules. self.unify(a, b) @@ -856,6 +859,19 @@ impl<'f, 'tcx> Coerce<'f, 'tcx> { self.unify_and(a, b, [], Adjust::ReborrowPin(mut_b)) } + fn coerce_non_zero( + &self, + non_zero: Ty<'tcx>, + wrapped_ty: Ty<'tcx>, + target_ty: Ty<'tcx>, + ) -> CoerceResult<'tcx> { + if target_ty.is_ty_var() { + return self.unify(non_zero, target_ty); + } + self.commit_if_ok(|_| self.unify_and(wrapped_ty, target_ty, [], Adjust::NonZeroIntoInner)) + .or_else(|_| self.unify(non_zero, target_ty)) + } + fn coerce_from_safe_fn( &self, fn_ty_a: ty::PolyFnSig<'tcx>, diff --git a/compiler/rustc_hir_typeck/src/expr_use_visitor.rs b/compiler/rustc_hir_typeck/src/expr_use_visitor.rs index b9f1463d8007d..cf5d0c19cfe88 100644 --- a/compiler/rustc_hir_typeck/src/expr_use_visitor.rs +++ b/compiler/rustc_hir_typeck/src/expr_use_visitor.rs @@ -854,6 +854,8 @@ impl<'tcx, Cx: TypeInformationCtxt<'tcx>, D: Delegate<'tcx>> ExprUseVisitor<'tcx }; self.delegate.borrow_mut().borrow(&place_with_id, place_with_id.hir_id, bk); } + + adjustment::Adjust::NonZeroIntoInner => {} } place_with_id = self.cat_expr_adjusted(expr, place_with_id, adjustment)?; } @@ -1344,6 +1346,7 @@ impl<'tcx, Cx: TypeInformationCtxt<'tcx>, D: Delegate<'tcx>> ExprUseVisitor<'tcx adjustment::Adjust::NeverToAny | adjustment::Adjust::Pointer(_) | adjustment::Adjust::Borrow(_) + | adjustment::Adjust::NonZeroIntoInner | adjustment::Adjust::ReborrowPin(..) => { // Result is an rvalue. Ok(self.cat_rvalue(expr.hir_id, target)) diff --git a/compiler/rustc_hir_typeck/src/fn_ctxt/_impl.rs b/compiler/rustc_hir_typeck/src/fn_ctxt/_impl.rs index 38586c844445b..f27fcf7d398cf 100644 --- a/compiler/rustc_hir_typeck/src/fn_ctxt/_impl.rs +++ b/compiler/rustc_hir_typeck/src/fn_ctxt/_impl.rs @@ -284,7 +284,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // FIXME(const_trait_impl): We could enforce these; they correspond to // `&mut T: DerefMut` tho, so it's kinda moot. } - Adjust::Borrow(_) => { + Adjust::NonZeroIntoInner | Adjust::Borrow(_) => { // No effects to enforce here. } } diff --git a/compiler/rustc_lint/src/autorefs.rs b/compiler/rustc_lint/src/autorefs.rs index 5490a3aac9b7a..109337d321a4e 100644 --- a/compiler/rustc_lint/src/autorefs.rs +++ b/compiler/rustc_lint/src/autorefs.rs @@ -171,6 +171,7 @@ fn has_implicit_borrow(Adjustment { kind, .. }: &Adjustment<'_>) -> Option<(Muta | Adjust::Pointer(..) | Adjust::ReborrowPin(..) | Adjust::Deref(None) + | Adjust::NonZeroIntoInner | Adjust::Borrow(AutoBorrow::RawPtr(..)) => None, } } diff --git a/compiler/rustc_middle/src/ty/adjustment.rs b/compiler/rustc_middle/src/ty/adjustment.rs index 2920c9cb42ab4..fa05eb124bc2f 100644 --- a/compiler/rustc_middle/src/ty/adjustment.rs +++ b/compiler/rustc_middle/src/ty/adjustment.rs @@ -105,6 +105,9 @@ pub enum Adjust { /// Take a pinned reference and reborrow as a `Pin<&mut T>` or `Pin<&T>`. ReborrowPin(hir::Mutability), + + /// Turn a `NonZero` into `T` + NonZeroIntoInner, } /// An overloaded autoderef step, representing a `Deref(Mut)::deref(_mut)` diff --git a/compiler/rustc_mir_build/src/thir/cx/expr.rs b/compiler/rustc_mir_build/src/thir/cx/expr.rs index 8ce0b73e47e36..9e1b36ee169d1 100644 --- a/compiler/rustc_mir_build/src/thir/cx/expr.rs +++ b/compiler/rustc_mir_build/src/thir/cx/expr.rs @@ -240,6 +240,34 @@ impl<'tcx> ThirBuildCx<'tcx> { debug!(?kind); kind } + Adjust::NonZeroIntoInner => { + // These only happen on `NonZero` + let ty::Adt(adt, args) = expr.ty.kind() else { + span_bug!(span, "nonzero adjustment not based on adt: {expr:#?}") + }; + // Find the right field type + let ty = self + .tcx + .type_of(adt.variant(FIRST_VARIANT).fields[FieldIdx::ZERO].did) + .instantiate(self.tcx, args); + // Get rid of the `ZeroablePrimitive` projection + let ty = self.tcx.normalize_erasing_regions(self.typing_env, ty); + let lhs = self.thir.exprs.push(expr); + ExprKind::Field { + lhs: self.thir.exprs.push(Expr { + span, + temp_lifetime, + ty, + kind: ExprKind::Field { + lhs, + variant_index: FIRST_VARIANT, + name: FieldIdx::ZERO, + }, + }), + variant_index: FIRST_VARIANT, + name: FieldIdx::ZERO, + } + } }; Expr { temp_lifetime, ty: adjustment.target, span, kind } diff --git a/tests/ui/mismatched_types/non_zero_coerce.rs b/tests/ui/mismatched_types/non_zero_coerce.rs new file mode 100644 index 0000000000000..b0599506b25dc --- /dev/null +++ b/tests/ui/mismatched_types/non_zero_coerce.rs @@ -0,0 +1,32 @@ +//@ check-pass + +use std::num::NonZero; + +trait Foo: Sized { + fn bar(self, other: T) {} +} + +impl Foo for u8 {} +impl Foo for u16 {} + +trait Bar {} +impl Bar for u8 {} +impl Bar for u16 {} +fn foo(_: impl Bar) {} + +fn main() { + // Check that we can coerce + let x = NonZero::new(5_u8).unwrap(); + let y: u8 = x; + + // Can coerce by looking at the trait + let x = NonZero::new(5_u8).unwrap(); + 5_u8.bar(x); + + // Check that we can infer the nonzero wrapped type through the coercion + let a = NonZero::new(5).unwrap(); + let b: u8 = a; + + let a = NonZero::new(5).unwrap(); + 5_u8.bar(a); +} diff --git a/tests/ui/mismatched_types/non_zero_coerce_fail.rs b/tests/ui/mismatched_types/non_zero_coerce_fail.rs new file mode 100644 index 0000000000000..f5d4cace8e3ed --- /dev/null +++ b/tests/ui/mismatched_types/non_zero_coerce_fail.rs @@ -0,0 +1,31 @@ +use std::num::NonZero; + +trait Foo: Sized { + fn foo(&self) {} + fn bar(self) {} +} + +impl Foo for u8 {} +impl Foo for u16 {} + +fn foo(_: impl Foo) {} + +fn main() { + let x = NonZero::new(5_u8).unwrap(); + x.foo(); + //~^ ERROR: no method named `foo` found for struct `NonZero` in the current scope + x.bar(); + //~^ ERROR: no method named `bar` found for struct `NonZero` in the current scope + foo(x); + //~^ ERROR: the trait bound `NonZero: Foo` is not satisfied + foo(x as _); + + let a = NonZero::new(5).unwrap(); + a.foo(); + //~^ ERROR: no method named `foo` found for struct `NonZero` in the current scope + a.bar(); + //~^ ERROR: no method named `bar` found for struct `NonZero` in the current scope + foo(a); + //~^ ERROR: the trait bound `NonZero<{integer}>: Foo` is not satisfied + foo(a as _); +} diff --git a/tests/ui/mismatched_types/non_zero_coerce_fail.stderr b/tests/ui/mismatched_types/non_zero_coerce_fail.stderr new file mode 100644 index 0000000000000..4c619254e8317 --- /dev/null +++ b/tests/ui/mismatched_types/non_zero_coerce_fail.stderr @@ -0,0 +1,90 @@ +error[E0599]: no method named `foo` found for struct `NonZero` in the current scope + --> $DIR/non_zero_coerce_fail.rs:15:7 + | +LL | x.foo(); + | ^^^ method not found in `NonZero` + | + = help: items from traits can only be used if the trait is implemented and in scope +note: `Foo` defines an item `foo`, perhaps you need to implement it + --> $DIR/non_zero_coerce_fail.rs:3:1 + | +LL | trait Foo: Sized { + | ^^^^^^^^^^^^^^^^ + +error[E0599]: no method named `bar` found for struct `NonZero` in the current scope + --> $DIR/non_zero_coerce_fail.rs:17:7 + | +LL | x.bar(); + | ^^^ method not found in `NonZero` + | + = help: items from traits can only be used if the trait is implemented and in scope +note: `Foo` defines an item `bar`, perhaps you need to implement it + --> $DIR/non_zero_coerce_fail.rs:3:1 + | +LL | trait Foo: Sized { + | ^^^^^^^^^^^^^^^^ + +error[E0277]: the trait bound `NonZero: Foo` is not satisfied + --> $DIR/non_zero_coerce_fail.rs:19:9 + | +LL | foo(x); + | --- ^ the trait `Foo` is not implemented for `NonZero` + | | + | required by a bound introduced by this call + | + = help: the following other types implement trait `Foo`: + u16 + u8 +note: required by a bound in `foo` + --> $DIR/non_zero_coerce_fail.rs:11:16 + | +LL | fn foo(_: impl Foo) {} + | ^^^ required by this bound in `foo` + +error[E0599]: no method named `foo` found for struct `NonZero` in the current scope + --> $DIR/non_zero_coerce_fail.rs:24:7 + | +LL | a.foo(); + | ^^^ method not found in `NonZero<{integer}>` + | + = help: items from traits can only be used if the trait is implemented and in scope +note: `Foo` defines an item `foo`, perhaps you need to implement it + --> $DIR/non_zero_coerce_fail.rs:3:1 + | +LL | trait Foo: Sized { + | ^^^^^^^^^^^^^^^^ + +error[E0599]: no method named `bar` found for struct `NonZero` in the current scope + --> $DIR/non_zero_coerce_fail.rs:26:7 + | +LL | a.bar(); + | ^^^ method not found in `NonZero<{integer}>` + | + = help: items from traits can only be used if the trait is implemented and in scope +note: `Foo` defines an item `bar`, perhaps you need to implement it + --> $DIR/non_zero_coerce_fail.rs:3:1 + | +LL | trait Foo: Sized { + | ^^^^^^^^^^^^^^^^ + +error[E0277]: the trait bound `NonZero<{integer}>: Foo` is not satisfied + --> $DIR/non_zero_coerce_fail.rs:28:9 + | +LL | foo(a); + | --- ^ the trait `Foo` is not implemented for `NonZero<{integer}>` + | | + | required by a bound introduced by this call + | + = help: the following other types implement trait `Foo`: + u16 + u8 +note: required by a bound in `foo` + --> $DIR/non_zero_coerce_fail.rs:11:16 + | +LL | fn foo(_: impl Foo) {} + | ^^^ required by this bound in `foo` + +error: aborting due to 6 previous errors + +Some errors have detailed explanations: E0277, E0599. +For more information about an error, try `rustc --explain E0277`. diff --git a/tests/ui/mismatched_types/non_zero_coerce_fail2.rs b/tests/ui/mismatched_types/non_zero_coerce_fail2.rs new file mode 100644 index 0000000000000..12c0992162bd5 --- /dev/null +++ b/tests/ui/mismatched_types/non_zero_coerce_fail2.rs @@ -0,0 +1,29 @@ +use std::num::NonZero; + +trait Foo: Sized { + fn foo(&self, other: &T) {} + fn bar(self, other: T) {} +} + +impl Foo for u8 {} +impl Foo for u16 {} + +fn main() { + let x = NonZero::new(5_u8).unwrap(); + 5_u8.foo(&x); + //~^ ERROR: mismatched types + 5_u8.bar(x); + 5.foo(&x); + //~^ ERROR: the trait bound `{integer}: Foo>` is not satisfied + 5.bar(x); + //~^ ERROR: the trait bound `{integer}: Foo>` is not satisfied + + let a = NonZero::new(5).unwrap(); + 5_u8.foo(&a); + //~^ ERROR: mismatched types + 5_u8.bar(a); + 5.foo(&a); + //~^ ERROR: the trait bound `{integer}: Foo>` is not satisfied + 5.bar(a); + //~^ ERROR: the trait bound `{integer}: Foo>` is not satisfied +} diff --git a/tests/ui/mismatched_types/non_zero_coerce_fail2.stderr b/tests/ui/mismatched_types/non_zero_coerce_fail2.stderr new file mode 100644 index 0000000000000..e09c7eafff755 --- /dev/null +++ b/tests/ui/mismatched_types/non_zero_coerce_fail2.stderr @@ -0,0 +1,84 @@ +error[E0308]: mismatched types + --> $DIR/non_zero_coerce_fail2.rs:13:14 + | +LL | 5_u8.foo(&x); + | --- ^^ expected `&u8`, found `&NonZero` + | | + | arguments to this method are incorrect + | + = note: expected reference `&u8` + found reference `&NonZero` +note: method defined here + --> $DIR/non_zero_coerce_fail2.rs:4:8 + | +LL | fn foo(&self, other: &T) {} + | ^^^ --------- + +error[E0277]: the trait bound `{integer}: Foo>` is not satisfied + --> $DIR/non_zero_coerce_fail2.rs:16:11 + | +LL | 5.foo(&x); + | --- ^^ the trait `Foo>` is not implemented for `{integer}` + | | + | required by a bound introduced by this call + | + = help: the following other types implement trait `Foo`: + `u16` implements `Foo` + `u8` implements `Foo` + +error[E0277]: the trait bound `{integer}: Foo>` is not satisfied + --> $DIR/non_zero_coerce_fail2.rs:18:11 + | +LL | 5.bar(x); + | --- ^ the trait `Foo>` is not implemented for `{integer}` + | | + | required by a bound introduced by this call + | + = help: the following other types implement trait `Foo`: + `u16` implements `Foo` + `u8` implements `Foo` + +error[E0308]: mismatched types + --> $DIR/non_zero_coerce_fail2.rs:22:14 + | +LL | 5_u8.foo(&a); + | --- ^^ expected `&u8`, found `&NonZero<{integer}>` + | | + | arguments to this method are incorrect + | + = note: expected reference `&u8` + found reference `&NonZero<{integer}>` +note: method defined here + --> $DIR/non_zero_coerce_fail2.rs:4:8 + | +LL | fn foo(&self, other: &T) {} + | ^^^ --------- + +error[E0277]: the trait bound `{integer}: Foo>` is not satisfied + --> $DIR/non_zero_coerce_fail2.rs:25:11 + | +LL | 5.foo(&a); + | --- ^^ the trait `Foo>` is not implemented for `{integer}` + | | + | required by a bound introduced by this call + | + = help: the following other types implement trait `Foo`: + `u16` implements `Foo` + `u8` implements `Foo` + +error[E0277]: the trait bound `{integer}: Foo>` is not satisfied + --> $DIR/non_zero_coerce_fail2.rs:27:11 + | +LL | 5.bar(a); + | --- ^ the trait `Foo>` is not implemented for `{integer}` + | | + | required by a bound introduced by this call + | + = help: the following other types implement trait `Foo`: + `u16` implements `Foo` + `u8` implements `Foo` + +error: aborting due to 6 previous errors + +Some errors have detailed explanations: E0277, E0308. +For more information about an error, try `rustc --explain E0277`. diff --git a/tests/ui/mismatched_types/non_zero_coerce_fail_infer.rs b/tests/ui/mismatched_types/non_zero_coerce_fail_infer.rs new file mode 100644 index 0000000000000..a0f98e726474b --- /dev/null +++ b/tests/ui/mismatched_types/non_zero_coerce_fail_infer.rs @@ -0,0 +1,14 @@ +use std::num::NonZero; + +trait Foo {} + +impl Foo for u8 {} +impl Foo for u16 {} + +fn foo(_: impl Foo) {} + +fn main() { + let x = NonZero::new(5_u8).unwrap(); + foo(x as _); + //~^ ERROR: type annotations needed +} diff --git a/tests/ui/mismatched_types/non_zero_coerce_fail_infer.stderr b/tests/ui/mismatched_types/non_zero_coerce_fail_infer.stderr new file mode 100644 index 0000000000000..cd95a18469984 --- /dev/null +++ b/tests/ui/mismatched_types/non_zero_coerce_fail_infer.stderr @@ -0,0 +1,9 @@ +error[E0282]: type annotations needed + --> $DIR/non_zero_coerce_fail_infer.rs:12:14 + | +LL | foo(x as _); + | ^ cannot infer type + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0282`.