Skip to content

Commit 4a4dd8e

Browse files
Auto merge of #143629 - oli-obk:limited-nonzero-coercion, r=<try>
Limited nonzero coercion coercing `NonZero<T>` to `T` if all the types are available at the cast site. This is fairly limited, as (as the tests show), this does not allow "reborrowing" `&NonZero<T>` as `&T`. It also fails quickly if the types aren't fully clear at the coercion site, but only resolved later. Also cannot aid in trait selection or similar, even if there is clearly only one option. Related: * #143594 * rust-lang/rfcs#3786 cc `@traviscross`
2 parents 040e2f8 + e341923 commit 4a4dd8e

27 files changed

+663
-17
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/coercion.rs

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -263,6 +263,9 @@ impl<'f, 'tcx> Coerce<'f, 'tcx> {
263263
// It cannot convert closures that require unsafe.
264264
self.coerce_closure_to_fn(a, closure_def_id_a, args_a, b)
265265
}
266+
ty::Adt(adt, args) if self.tcx.is_lang_item(adt.did(), hir::LangItem::NonZero) => {
267+
self.coerce_non_zero(a, args.type_at(0), b)
268+
}
266269
_ => {
267270
// Otherwise, just use unification rules.
268271
self.unify(a, b)
@@ -827,6 +830,19 @@ impl<'f, 'tcx> Coerce<'f, 'tcx> {
827830
self.unify_and(a, b, [], Adjust::ReborrowPin(mut_b))
828831
}
829832

833+
fn coerce_non_zero(
834+
&self,
835+
non_zero: Ty<'tcx>,
836+
wrapped_ty: Ty<'tcx>,
837+
target_ty: Ty<'tcx>,
838+
) -> CoerceResult<'tcx> {
839+
if target_ty.is_ty_var() {
840+
return self.unify(non_zero, target_ty);
841+
}
842+
self.commit_if_ok(|_| self.unify_and(wrapped_ty, target_ty, [], Adjust::NonZeroIntoInner))
843+
.or_else(|_| self.unify(non_zero, target_ty))
844+
}
845+
830846
fn coerce_from_safe_fn(
831847
&self,
832848
fn_ty_a: ty::PolyFnSig<'tcx>,

compiler/rustc_hir_typeck/src/expr_use_visitor.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -854,6 +854,8 @@ impl<'tcx, Cx: TypeInformationCtxt<'tcx>, D: Delegate<'tcx>> ExprUseVisitor<'tcx
854854
};
855855
self.delegate.borrow_mut().borrow(&place_with_id, place_with_id.hir_id, bk);
856856
}
857+
858+
adjustment::Adjust::NonZeroIntoInner => {}
857859
}
858860
place_with_id = self.cat_expr_adjusted(expr, place_with_id, adjustment)?;
859861
}
@@ -1336,6 +1338,7 @@ impl<'tcx, Cx: TypeInformationCtxt<'tcx>, D: Delegate<'tcx>> ExprUseVisitor<'tcx
13361338
adjustment::Adjust::NeverToAny
13371339
| adjustment::Adjust::Pointer(_)
13381340
| adjustment::Adjust::Borrow(_)
1341+
| adjustment::Adjust::NonZeroIntoInner
13391342
| adjustment::Adjust::ReborrowPin(..) => {
13401343
// Result is an rvalue.
13411344
Ok(self.cat_rvalue(expr.hir_id, target))

compiler/rustc_hir_typeck/src/fn_ctxt/_impl.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -290,7 +290,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
290290
// FIXME(const_trait_impl): We could enforce these; they correspond to
291291
// `&mut T: DerefMut` tho, so it's kinda moot.
292292
}
293-
Adjust::Borrow(_) => {
293+
Adjust::NonZeroIntoInner | Adjust::Borrow(_) => {
294294
// No effects to enforce here.
295295
}
296296
}

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/autorefs.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -171,6 +171,7 @@ fn has_implicit_borrow(Adjustment { kind, .. }: &Adjustment<'_>) -> Option<(Muta
171171
| Adjust::Pointer(..)
172172
| Adjust::ReborrowPin(..)
173173
| Adjust::Deref(None)
174+
| Adjust::NonZeroIntoInner
174175
| Adjust::Borrow(AutoBorrow::RawPtr(..)) => None,
175176
}
176177
}

compiler/rustc_middle/src/ty/adjustment.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -105,6 +105,9 @@ pub enum Adjust {
105105

106106
/// Take a pinned reference and reborrow as a `Pin<&mut T>` or `Pin<&T>`.
107107
ReborrowPin(hir::Mutability),
108+
109+
/// Turn a `NonZero<T>` into `T`
110+
NonZeroIntoInner,
108111
}
109112

110113
/// An overloaded autoderef step, representing a `Deref(Mut)::deref(_mut)`

compiler/rustc_mir_build/src/thir/cx/expr.rs

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -239,6 +239,34 @@ impl<'tcx> ThirBuildCx<'tcx> {
239239
debug!(?kind);
240240
kind
241241
}
242+
Adjust::NonZeroIntoInner => {
243+
// These only happen on `NonZero`
244+
let ty::Adt(adt, args) = expr.ty.kind() else {
245+
span_bug!(span, "nonzero adjustment not based on adt: {expr:#?}")
246+
};
247+
// Find the right field type
248+
let ty = self
249+
.tcx
250+
.type_of(adt.variant(FIRST_VARIANT).fields[FieldIdx::ZERO].did)
251+
.instantiate(self.tcx, args);
252+
// Get rid of the `ZeroablePrimitive` projection
253+
let ty = self.tcx.normalize_erasing_regions(self.typing_env, ty);
254+
let lhs = self.thir.exprs.push(expr);
255+
ExprKind::Field {
256+
lhs: self.thir.exprs.push(Expr {
257+
span,
258+
temp_lifetime,
259+
ty,
260+
kind: ExprKind::Field {
261+
lhs,
262+
variant_index: FIRST_VARIANT,
263+
name: FieldIdx::ZERO,
264+
},
265+
}),
266+
variant_index: FIRST_VARIANT,
267+
name: FieldIdx::ZERO,
268+
}
269+
}
242270
};
243271

244272
Expr { temp_lifetime, ty: adjustment.target, span, kind }

library/core/src/num/nonzero.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -123,7 +123,7 @@ impl_zeroable_primitive!(
123123
#[stable(feature = "generic_nonzero", since = "1.79.0")]
124124
#[repr(transparent)]
125125
#[rustc_nonnull_optimization_guaranteed]
126-
#[rustc_diagnostic_item = "NonZero"]
126+
#[lang = "NonZero"]
127127
pub struct NonZero<T: ZeroablePrimitive>(T::NonZeroInner);
128128

129129
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;

0 commit comments

Comments
 (0)