Skip to content

Commit e341923

Browse files
committed
Allow coercing NonZero<T> to T
1 parent ab2eecc commit e341923

File tree

13 files changed

+341
-1
lines changed

13 files changed

+341
-1
lines changed

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_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 }
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
//@ check-pass
2+
3+
use std::num::NonZero;
4+
5+
trait Foo<T>: Sized {
6+
fn bar(self, other: T) {}
7+
}
8+
9+
impl Foo<u8> for u8 {}
10+
impl Foo<u16> for u16 {}
11+
12+
trait Bar {}
13+
impl Bar for u8 {}
14+
impl Bar for u16 {}
15+
fn foo(_: impl Bar) {}
16+
17+
fn main() {
18+
// Check that we can coerce
19+
let x = NonZero::new(5_u8).unwrap();
20+
let y: u8 = x;
21+
22+
// Can coerce by looking at the trait
23+
let x = NonZero::new(5_u8).unwrap();
24+
5_u8.bar(x);
25+
26+
// Check that we can infer the nonzero wrapped type through the coercion
27+
let a = NonZero::new(5).unwrap();
28+
let b: u8 = a;
29+
30+
let a = NonZero::new(5).unwrap();
31+
5_u8.bar(a);
32+
}
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
use std::num::NonZero;
2+
3+
trait Foo: Sized {
4+
fn foo(&self) {}
5+
fn bar(self) {}
6+
}
7+
8+
impl Foo for u8 {}
9+
impl Foo for u16 {}
10+
11+
fn foo(_: impl Foo) {}
12+
13+
fn main() {
14+
let x = NonZero::new(5_u8).unwrap();
15+
x.foo();
16+
//~^ ERROR: no method named `foo` found for struct `NonZero` in the current scope
17+
x.bar();
18+
//~^ ERROR: no method named `bar` found for struct `NonZero` in the current scope
19+
foo(x);
20+
//~^ ERROR: the trait bound `NonZero<u8>: Foo` is not satisfied
21+
foo(x as _);
22+
23+
let a = NonZero::new(5).unwrap();
24+
a.foo();
25+
//~^ ERROR: no method named `foo` found for struct `NonZero` in the current scope
26+
a.bar();
27+
//~^ ERROR: no method named `bar` found for struct `NonZero` in the current scope
28+
foo(a);
29+
//~^ ERROR: the trait bound `NonZero<{integer}>: Foo` is not satisfied
30+
foo(a as _);
31+
}
Lines changed: 90 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,90 @@
1+
error[E0599]: no method named `foo` found for struct `NonZero` in the current scope
2+
--> $DIR/non_zero_coerce_fail.rs:15:7
3+
|
4+
LL | x.foo();
5+
| ^^^ method not found in `NonZero<u8>`
6+
|
7+
= help: items from traits can only be used if the trait is implemented and in scope
8+
note: `Foo` defines an item `foo`, perhaps you need to implement it
9+
--> $DIR/non_zero_coerce_fail.rs:3:1
10+
|
11+
LL | trait Foo: Sized {
12+
| ^^^^^^^^^^^^^^^^
13+
14+
error[E0599]: no method named `bar` found for struct `NonZero` in the current scope
15+
--> $DIR/non_zero_coerce_fail.rs:17:7
16+
|
17+
LL | x.bar();
18+
| ^^^ method not found in `NonZero<u8>`
19+
|
20+
= help: items from traits can only be used if the trait is implemented and in scope
21+
note: `Foo` defines an item `bar`, perhaps you need to implement it
22+
--> $DIR/non_zero_coerce_fail.rs:3:1
23+
|
24+
LL | trait Foo: Sized {
25+
| ^^^^^^^^^^^^^^^^
26+
27+
error[E0277]: the trait bound `NonZero<u8>: Foo` is not satisfied
28+
--> $DIR/non_zero_coerce_fail.rs:19:9
29+
|
30+
LL | foo(x);
31+
| --- ^ the trait `Foo` is not implemented for `NonZero<u8>`
32+
| |
33+
| required by a bound introduced by this call
34+
|
35+
= help: the following other types implement trait `Foo`:
36+
u16
37+
u8
38+
note: required by a bound in `foo`
39+
--> $DIR/non_zero_coerce_fail.rs:11:16
40+
|
41+
LL | fn foo(_: impl Foo) {}
42+
| ^^^ required by this bound in `foo`
43+
44+
error[E0599]: no method named `foo` found for struct `NonZero` in the current scope
45+
--> $DIR/non_zero_coerce_fail.rs:24:7
46+
|
47+
LL | a.foo();
48+
| ^^^ method not found in `NonZero<{integer}>`
49+
|
50+
= help: items from traits can only be used if the trait is implemented and in scope
51+
note: `Foo` defines an item `foo`, perhaps you need to implement it
52+
--> $DIR/non_zero_coerce_fail.rs:3:1
53+
|
54+
LL | trait Foo: Sized {
55+
| ^^^^^^^^^^^^^^^^
56+
57+
error[E0599]: no method named `bar` found for struct `NonZero` in the current scope
58+
--> $DIR/non_zero_coerce_fail.rs:26:7
59+
|
60+
LL | a.bar();
61+
| ^^^ method not found in `NonZero<{integer}>`
62+
|
63+
= help: items from traits can only be used if the trait is implemented and in scope
64+
note: `Foo` defines an item `bar`, perhaps you need to implement it
65+
--> $DIR/non_zero_coerce_fail.rs:3:1
66+
|
67+
LL | trait Foo: Sized {
68+
| ^^^^^^^^^^^^^^^^
69+
70+
error[E0277]: the trait bound `NonZero<{integer}>: Foo` is not satisfied
71+
--> $DIR/non_zero_coerce_fail.rs:28:9
72+
|
73+
LL | foo(a);
74+
| --- ^ the trait `Foo` is not implemented for `NonZero<{integer}>`
75+
| |
76+
| required by a bound introduced by this call
77+
|
78+
= help: the following other types implement trait `Foo`:
79+
u16
80+
u8
81+
note: required by a bound in `foo`
82+
--> $DIR/non_zero_coerce_fail.rs:11:16
83+
|
84+
LL | fn foo(_: impl Foo) {}
85+
| ^^^ required by this bound in `foo`
86+
87+
error: aborting due to 6 previous errors
88+
89+
Some errors have detailed explanations: E0277, E0599.
90+
For more information about an error, try `rustc --explain E0277`.
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
use std::num::NonZero;
2+
3+
trait Foo<T>: Sized {
4+
fn foo(&self, other: &T) {}
5+
fn bar(self, other: T) {}
6+
}
7+
8+
impl Foo<u8> for u8 {}
9+
impl Foo<u16> for u16 {}
10+
11+
fn main() {
12+
let x = NonZero::new(5_u8).unwrap();
13+
5_u8.foo(&x);
14+
//~^ ERROR: mismatched types
15+
5_u8.bar(x);
16+
5.foo(&x);
17+
//~^ ERROR: the trait bound `{integer}: Foo<NonZero<u8>>` is not satisfied
18+
5.bar(x);
19+
//~^ ERROR: the trait bound `{integer}: Foo<NonZero<u8>>` is not satisfied
20+
21+
let a = NonZero::new(5).unwrap();
22+
5_u8.foo(&a);
23+
//~^ ERROR: mismatched types
24+
5_u8.bar(a);
25+
5.foo(&a);
26+
//~^ ERROR: the trait bound `{integer}: Foo<NonZero<u8>>` is not satisfied
27+
5.bar(a);
28+
//~^ ERROR: the trait bound `{integer}: Foo<NonZero<u8>>` is not satisfied
29+
}
Lines changed: 84 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,84 @@
1+
error[E0308]: mismatched types
2+
--> $DIR/non_zero_coerce_fail2.rs:13:14
3+
|
4+
LL | 5_u8.foo(&x);
5+
| --- ^^ expected `&u8`, found `&NonZero<u8>`
6+
| |
7+
| arguments to this method are incorrect
8+
|
9+
= note: expected reference `&u8`
10+
found reference `&NonZero<u8>`
11+
note: method defined here
12+
--> $DIR/non_zero_coerce_fail2.rs:4:8
13+
|
14+
LL | fn foo(&self, other: &T) {}
15+
| ^^^ ---------
16+
17+
error[E0277]: the trait bound `{integer}: Foo<NonZero<u8>>` is not satisfied
18+
--> $DIR/non_zero_coerce_fail2.rs:16:11
19+
|
20+
LL | 5.foo(&x);
21+
| --- ^^ the trait `Foo<NonZero<u8>>` is not implemented for `{integer}`
22+
| |
23+
| required by a bound introduced by this call
24+
|
25+
= help: the following other types implement trait `Foo<T>`:
26+
`u16` implements `Foo<u16>`
27+
`u8` implements `Foo<u8>`
28+
29+
error[E0277]: the trait bound `{integer}: Foo<NonZero<u8>>` is not satisfied
30+
--> $DIR/non_zero_coerce_fail2.rs:18:11
31+
|
32+
LL | 5.bar(x);
33+
| --- ^ the trait `Foo<NonZero<u8>>` is not implemented for `{integer}`
34+
| |
35+
| required by a bound introduced by this call
36+
|
37+
= help: the following other types implement trait `Foo<T>`:
38+
`u16` implements `Foo<u16>`
39+
`u8` implements `Foo<u8>`
40+
41+
error[E0308]: mismatched types
42+
--> $DIR/non_zero_coerce_fail2.rs:22:14
43+
|
44+
LL | 5_u8.foo(&a);
45+
| --- ^^ expected `&u8`, found `&NonZero<{integer}>`
46+
| |
47+
| arguments to this method are incorrect
48+
|
49+
= note: expected reference `&u8`
50+
found reference `&NonZero<{integer}>`
51+
note: method defined here
52+
--> $DIR/non_zero_coerce_fail2.rs:4:8
53+
|
54+
LL | fn foo(&self, other: &T) {}
55+
| ^^^ ---------
56+
57+
error[E0277]: the trait bound `{integer}: Foo<NonZero<u8>>` is not satisfied
58+
--> $DIR/non_zero_coerce_fail2.rs:25:11
59+
|
60+
LL | 5.foo(&a);
61+
| --- ^^ the trait `Foo<NonZero<u8>>` is not implemented for `{integer}`
62+
| |
63+
| required by a bound introduced by this call
64+
|
65+
= help: the following other types implement trait `Foo<T>`:
66+
`u16` implements `Foo<u16>`
67+
`u8` implements `Foo<u8>`
68+
69+
error[E0277]: the trait bound `{integer}: Foo<NonZero<u8>>` is not satisfied
70+
--> $DIR/non_zero_coerce_fail2.rs:27:11
71+
|
72+
LL | 5.bar(a);
73+
| --- ^ the trait `Foo<NonZero<u8>>` is not implemented for `{integer}`
74+
| |
75+
| required by a bound introduced by this call
76+
|
77+
= help: the following other types implement trait `Foo<T>`:
78+
`u16` implements `Foo<u16>`
79+
`u8` implements `Foo<u8>`
80+
81+
error: aborting due to 6 previous errors
82+
83+
Some errors have detailed explanations: E0277, E0308.
84+
For more information about an error, try `rustc --explain E0277`.
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
use std::num::NonZero;
2+
3+
trait Foo {}
4+
5+
impl Foo for u8 {}
6+
impl Foo for u16 {}
7+
8+
fn foo(_: impl Foo) {}
9+
10+
fn main() {
11+
let x = NonZero::new(5_u8).unwrap();
12+
foo(x as _);
13+
//~^ ERROR: type annotations needed
14+
}

0 commit comments

Comments
 (0)