Auto merge of #51564 - SimonSapin:try-int, r=alexcrichton
Implement always-fallible TryFrom for usize/isize conversions that are infallible on some platforms
This reverts commit 837d6c7023
"Remove TryFrom impls that might become conditionally-infallible with a portability lint".
This fixes #49415 by adding (restoring) missing `TryFrom` impls for integer conversions to or from `usize` or `isize`, by making them always fallible at the type system level (that is, with `Error=TryFromIntError`) even though they happen to be infallible on some platforms (for some values of `size_of::<usize>()`).
They had been removed to allow the possibility to conditionally having some of them be infallible `From` impls instead, depending on the platforms, and have the [portability lint](https://github.com/rust-lang/rfcs/pull/1868) warn when they are used in code that is not already opting into non-portability. For example `#[allow(some_lint)] usize::from(x: u64)` would be valid on code that only targets 64-bit platforms.
This PR gives up on this possiblity for two reasons:
* Based on discussion with @aturon, it seems that the portability lint is not happening any time soon. It’s better to have the conversions be available *at all* than keep blocking them for so long. Portability-lint-gated platform-specific APIs can always be added separately later.
* For code that is fine with fallibility, the alternative would force it to opt into "non-portability" even though there would be no real portability issue.
This commit is contained in:
commit
0fb6e3994f
@ -91,7 +91,7 @@ macro_rules! step_impl_unsigned {
|
||||
#[inline]
|
||||
#[allow(unreachable_patterns)]
|
||||
fn add_usize(&self, n: usize) -> Option<Self> {
|
||||
match <$t>::private_try_from(n) {
|
||||
match <$t>::try_from(n) {
|
||||
Ok(n_as_t) => self.checked_add(n_as_t),
|
||||
Err(_) => None,
|
||||
}
|
||||
@ -123,7 +123,7 @@ macro_rules! step_impl_signed {
|
||||
#[inline]
|
||||
#[allow(unreachable_patterns)]
|
||||
fn add_usize(&self, n: usize) -> Option<Self> {
|
||||
match <$unsigned>::private_try_from(n) {
|
||||
match <$unsigned>::try_from(n) {
|
||||
Ok(n_as_unsigned) => {
|
||||
// Wrapping in unsigned space handles cases like
|
||||
// `-120_i8.add_usize(200) == Some(80_i8)`,
|
||||
@ -461,74 +461,3 @@ impl<A: Step> DoubleEndedIterator for ops::RangeInclusive<A> {
|
||||
|
||||
#[stable(feature = "fused", since = "1.26.0")]
|
||||
impl<A: Step> FusedIterator for ops::RangeInclusive<A> {}
|
||||
|
||||
/// Compensate removal of some impls per
|
||||
/// https://github.com/rust-lang/rust/pull/49305#issuecomment-376293243
|
||||
trait PrivateTryFromUsize: Sized {
|
||||
fn private_try_from(n: usize) -> Result<Self, ()>;
|
||||
}
|
||||
|
||||
impl<T> PrivateTryFromUsize for T where T: TryFrom<usize> {
|
||||
#[inline]
|
||||
fn private_try_from(n: usize) -> Result<Self, ()> {
|
||||
T::try_from(n).map_err(|_| ())
|
||||
}
|
||||
}
|
||||
|
||||
// no possible bounds violation
|
||||
macro_rules! try_from_unbounded {
|
||||
($($target:ty),*) => {$(
|
||||
impl PrivateTryFromUsize for $target {
|
||||
#[inline]
|
||||
fn private_try_from(value: usize) -> Result<Self, ()> {
|
||||
Ok(value as $target)
|
||||
}
|
||||
}
|
||||
)*}
|
||||
}
|
||||
|
||||
// unsigned to signed (only positive bound)
|
||||
#[cfg(any(target_pointer_width = "32", target_pointer_width = "64"))]
|
||||
macro_rules! try_from_upper_bounded {
|
||||
($($target:ty),*) => {$(
|
||||
impl PrivateTryFromUsize for $target {
|
||||
#[inline]
|
||||
fn private_try_from(u: usize) -> Result<$target, ()> {
|
||||
if u > (<$target>::max_value() as usize) {
|
||||
Err(())
|
||||
} else {
|
||||
Ok(u as $target)
|
||||
}
|
||||
}
|
||||
}
|
||||
)*}
|
||||
}
|
||||
|
||||
|
||||
#[cfg(target_pointer_width = "16")]
|
||||
mod ptr_try_from_impls {
|
||||
use super::PrivateTryFromUsize;
|
||||
|
||||
try_from_unbounded!(u16, u32, u64, u128);
|
||||
try_from_unbounded!(i32, i64, i128);
|
||||
}
|
||||
|
||||
#[cfg(target_pointer_width = "32")]
|
||||
mod ptr_try_from_impls {
|
||||
use super::PrivateTryFromUsize;
|
||||
|
||||
try_from_upper_bounded!(u16);
|
||||
try_from_unbounded!(u32, u64, u128);
|
||||
try_from_upper_bounded!(i32);
|
||||
try_from_unbounded!(i64, i128);
|
||||
}
|
||||
|
||||
#[cfg(target_pointer_width = "64")]
|
||||
mod ptr_try_from_impls {
|
||||
use super::PrivateTryFromUsize;
|
||||
|
||||
try_from_upper_bounded!(u16, u32);
|
||||
try_from_unbounded!(u64, u128);
|
||||
try_from_upper_bounded!(i32, i64);
|
||||
try_from_unbounded!(i128);
|
||||
}
|
||||
|
@ -4213,6 +4213,21 @@ impl From<!> for TryFromIntError {
|
||||
}
|
||||
}
|
||||
|
||||
// no possible bounds violation
|
||||
macro_rules! try_from_unbounded {
|
||||
($source:ty, $($target:ty),*) => {$(
|
||||
#[unstable(feature = "try_from", issue = "33417")]
|
||||
impl TryFrom<$source> for $target {
|
||||
type Error = TryFromIntError;
|
||||
|
||||
#[inline]
|
||||
fn try_from(value: $source) -> Result<Self, Self::Error> {
|
||||
Ok(value as $target)
|
||||
}
|
||||
}
|
||||
)*}
|
||||
}
|
||||
|
||||
// only negative bounds
|
||||
macro_rules! try_from_lower_bounded {
|
||||
($source:ty, $($target:ty),*) => {$(
|
||||
@ -4311,20 +4326,27 @@ try_from_both_bounded!(i128, u64, u32, u16, u8);
|
||||
try_from_upper_bounded!(usize, isize);
|
||||
try_from_lower_bounded!(isize, usize);
|
||||
|
||||
try_from_upper_bounded!(usize, u8);
|
||||
try_from_upper_bounded!(usize, i8, i16);
|
||||
try_from_both_bounded!(isize, u8);
|
||||
try_from_both_bounded!(isize, i8);
|
||||
|
||||
#[cfg(target_pointer_width = "16")]
|
||||
mod ptr_try_from_impls {
|
||||
use super::TryFromIntError;
|
||||
use convert::TryFrom;
|
||||
|
||||
// Fallible across platfoms, only implementation differs
|
||||
try_from_upper_bounded!(usize, u8);
|
||||
try_from_unbounded!(usize, u16, u32, u64, u128);
|
||||
try_from_upper_bounded!(usize, i8, i16);
|
||||
try_from_unbounded!(usize, i32, i64, i128);
|
||||
|
||||
try_from_both_bounded!(isize, u8);
|
||||
try_from_lower_bounded!(isize, u16, u32, u64, u128);
|
||||
try_from_both_bounded!(isize, i8);
|
||||
try_from_unbounded!(isize, i16, i32, i64, i128);
|
||||
|
||||
rev!(try_from_upper_bounded, usize, u32, u64, u128);
|
||||
rev!(try_from_lower_bounded, usize, i8, i16);
|
||||
rev!(try_from_both_bounded, usize, i32, i64, i128);
|
||||
|
||||
rev!(try_from_upper_bounded, isize, u16, u32, u64, u128);
|
||||
rev!(try_from_both_bounded, isize, i32, i64, i128);
|
||||
}
|
||||
|
||||
#[cfg(target_pointer_width = "32")]
|
||||
@ -4332,11 +4354,25 @@ mod ptr_try_from_impls {
|
||||
use super::TryFromIntError;
|
||||
use convert::TryFrom;
|
||||
|
||||
// Fallible across platfoms, only implementation differs
|
||||
try_from_both_bounded!(isize, u16);
|
||||
try_from_upper_bounded!(usize, u8, u16);
|
||||
try_from_unbounded!(usize, u32, u64, u128);
|
||||
try_from_upper_bounded!(usize, i8, i16, i32);
|
||||
try_from_unbounded!(usize, i64, i128);
|
||||
|
||||
try_from_both_bounded!(isize, u8, u16);
|
||||
try_from_lower_bounded!(isize, u32, u64, u128);
|
||||
try_from_both_bounded!(isize, i8, i16);
|
||||
try_from_unbounded!(isize, i32, i64, i128);
|
||||
|
||||
rev!(try_from_unbounded, usize, u32);
|
||||
rev!(try_from_upper_bounded, usize, u64, u128);
|
||||
rev!(try_from_lower_bounded, usize, i8, i16, i32);
|
||||
rev!(try_from_both_bounded, usize, i64, i128);
|
||||
|
||||
rev!(try_from_unbounded, isize, u16);
|
||||
rev!(try_from_upper_bounded, isize, u32, u64, u128);
|
||||
rev!(try_from_unbounded, isize, i32);
|
||||
rev!(try_from_both_bounded, isize, i64, i128);
|
||||
}
|
||||
|
||||
#[cfg(target_pointer_width = "64")]
|
||||
@ -4344,11 +4380,25 @@ mod ptr_try_from_impls {
|
||||
use super::TryFromIntError;
|
||||
use convert::TryFrom;
|
||||
|
||||
// Fallible across platfoms, only implementation differs
|
||||
try_from_both_bounded!(isize, u16, u32);
|
||||
try_from_upper_bounded!(usize, u8, u16, u32);
|
||||
try_from_unbounded!(usize, u64, u128);
|
||||
try_from_upper_bounded!(usize, i8, i16, i32, i64);
|
||||
try_from_unbounded!(usize, i128);
|
||||
|
||||
try_from_both_bounded!(isize, u8, u16, u32);
|
||||
try_from_lower_bounded!(isize, u64, u128);
|
||||
try_from_both_bounded!(isize, i8, i16, i32);
|
||||
try_from_unbounded!(isize, i64, i128);
|
||||
|
||||
rev!(try_from_unbounded, usize, u32, u64);
|
||||
rev!(try_from_upper_bounded, usize, u128);
|
||||
rev!(try_from_lower_bounded, usize, i8, i16, i32, i64);
|
||||
rev!(try_from_both_bounded, usize, i128);
|
||||
|
||||
rev!(try_from_unbounded, isize, u16, u32);
|
||||
rev!(try_from_upper_bounded, isize, u64, u128);
|
||||
rev!(try_from_unbounded, isize, i32, i64);
|
||||
rev!(try_from_both_bounded, isize, i128);
|
||||
}
|
||||
|
||||
#[doc(hidden)]
|
||||
|
@ -37,6 +37,15 @@ mod flt2dec;
|
||||
mod dec2flt;
|
||||
mod bignum;
|
||||
|
||||
|
||||
/// Adds the attribute to all items in the block.
|
||||
macro_rules! cfg_block {
|
||||
($(#[$attr:meta]{$($it:item)*})*) => {$($(
|
||||
#[$attr]
|
||||
$it
|
||||
)*)*}
|
||||
}
|
||||
|
||||
/// Groups items that assume the pointer width is either 16/32/64, and has to be altered if
|
||||
/// support for larger/smaller pointer widths are added in the future.
|
||||
macro_rules! assume_usize_width {
|
||||
@ -330,6 +339,42 @@ assume_usize_width! {
|
||||
|
||||
test_impl_try_from_always_ok! { test_try_u16usize, u16, usize }
|
||||
test_impl_try_from_always_ok! { test_try_i16isize, i16, isize }
|
||||
|
||||
test_impl_try_from_always_ok! { test_try_usizeu64, usize, u64 }
|
||||
test_impl_try_from_always_ok! { test_try_usizeu128, usize, u128 }
|
||||
test_impl_try_from_always_ok! { test_try_usizei128, usize, i128 }
|
||||
|
||||
test_impl_try_from_always_ok! { test_try_isizei64, isize, i64 }
|
||||
test_impl_try_from_always_ok! { test_try_isizei128, isize, i128 }
|
||||
|
||||
cfg_block!(
|
||||
#[cfg(target_pointer_width = "16")] {
|
||||
test_impl_try_from_always_ok! { test_try_usizeu16, usize, u16 }
|
||||
test_impl_try_from_always_ok! { test_try_isizei16, isize, i16 }
|
||||
test_impl_try_from_always_ok! { test_try_usizeu32, usize, u32 }
|
||||
test_impl_try_from_always_ok! { test_try_usizei32, usize, i32 }
|
||||
test_impl_try_from_always_ok! { test_try_isizei32, isize, i32 }
|
||||
test_impl_try_from_always_ok! { test_try_usizei64, usize, i64 }
|
||||
}
|
||||
|
||||
#[cfg(target_pointer_width = "32")] {
|
||||
test_impl_try_from_always_ok! { test_try_u16isize, u16, isize }
|
||||
test_impl_try_from_always_ok! { test_try_usizeu32, usize, u32 }
|
||||
test_impl_try_from_always_ok! { test_try_isizei32, isize, i32 }
|
||||
test_impl_try_from_always_ok! { test_try_u32usize, u32, usize }
|
||||
test_impl_try_from_always_ok! { test_try_i32isize, i32, isize }
|
||||
test_impl_try_from_always_ok! { test_try_usizei64, usize, i64 }
|
||||
}
|
||||
|
||||
#[cfg(target_pointer_width = "64")] {
|
||||
test_impl_try_from_always_ok! { test_try_u16isize, u16, isize }
|
||||
test_impl_try_from_always_ok! { test_try_u32usize, u32, usize }
|
||||
test_impl_try_from_always_ok! { test_try_u32isize, u32, isize }
|
||||
test_impl_try_from_always_ok! { test_try_i32isize, i32, isize }
|
||||
test_impl_try_from_always_ok! { test_try_u64usize, u64, usize }
|
||||
test_impl_try_from_always_ok! { test_try_i64isize, i64, isize }
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
/// Conversions where max of $source can be represented as $target,
|
||||
@ -378,6 +423,24 @@ assume_usize_width! {
|
||||
test_impl_try_from_signed_to_unsigned_upper_ok! { test_try_isizeu64, isize, u64 }
|
||||
test_impl_try_from_signed_to_unsigned_upper_ok! { test_try_isizeu128, isize, u128 }
|
||||
test_impl_try_from_signed_to_unsigned_upper_ok! { test_try_isizeusize, isize, usize }
|
||||
|
||||
cfg_block!(
|
||||
#[cfg(target_pointer_width = "16")] {
|
||||
test_impl_try_from_signed_to_unsigned_upper_ok! { test_try_isizeu16, isize, u16 }
|
||||
test_impl_try_from_signed_to_unsigned_upper_ok! { test_try_isizeu32, isize, u32 }
|
||||
}
|
||||
|
||||
#[cfg(target_pointer_width = "32")] {
|
||||
test_impl_try_from_signed_to_unsigned_upper_ok! { test_try_isizeu32, isize, u32 }
|
||||
|
||||
test_impl_try_from_signed_to_unsigned_upper_ok! { test_try_i32usize, i32, usize }
|
||||
}
|
||||
|
||||
#[cfg(target_pointer_width = "64")] {
|
||||
test_impl_try_from_signed_to_unsigned_upper_ok! { test_try_i32usize, i32, usize }
|
||||
test_impl_try_from_signed_to_unsigned_upper_ok! { test_try_i64usize, i64, usize }
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
/// Conversions where max of $source can not be represented as $target,
|
||||
@ -419,9 +482,29 @@ test_impl_try_from_unsigned_to_signed_upper_err! { test_try_u128i64, u128, i64 }
|
||||
test_impl_try_from_unsigned_to_signed_upper_err! { test_try_u128i128, u128, i128 }
|
||||
|
||||
assume_usize_width! {
|
||||
test_impl_try_from_unsigned_to_signed_upper_err! { test_try_u64isize, u64, isize }
|
||||
test_impl_try_from_unsigned_to_signed_upper_err! { test_try_u128isize, u128, isize }
|
||||
|
||||
test_impl_try_from_unsigned_to_signed_upper_err! { test_try_usizei8, usize, i8 }
|
||||
test_impl_try_from_unsigned_to_signed_upper_err! { test_try_usizei16, usize, i16 }
|
||||
test_impl_try_from_unsigned_to_signed_upper_err! { test_try_usizeisize, usize, isize }
|
||||
|
||||
cfg_block!(
|
||||
#[cfg(target_pointer_width = "16")] {
|
||||
test_impl_try_from_unsigned_to_signed_upper_err! { test_try_u16isize, u16, isize }
|
||||
test_impl_try_from_unsigned_to_signed_upper_err! { test_try_u32isize, u32, isize }
|
||||
}
|
||||
|
||||
#[cfg(target_pointer_width = "32")] {
|
||||
test_impl_try_from_unsigned_to_signed_upper_err! { test_try_u32isize, u32, isize }
|
||||
test_impl_try_from_unsigned_to_signed_upper_err! { test_try_usizei32, usize, i32 }
|
||||
}
|
||||
|
||||
#[cfg(target_pointer_width = "64")] {
|
||||
test_impl_try_from_unsigned_to_signed_upper_err! { test_try_usizei32, usize, i32 }
|
||||
test_impl_try_from_unsigned_to_signed_upper_err! { test_try_usizei64, usize, i64 }
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
/// Conversions where min/max of $source can not be represented as $target.
|
||||
@ -481,6 +564,34 @@ test_impl_try_from_same_sign_err! { test_try_i128i64, i128, i64 }
|
||||
|
||||
assume_usize_width! {
|
||||
test_impl_try_from_same_sign_err! { test_try_usizeu8, usize, u8 }
|
||||
test_impl_try_from_same_sign_err! { test_try_u128usize, u128, usize }
|
||||
test_impl_try_from_same_sign_err! { test_try_i128isize, i128, isize }
|
||||
|
||||
cfg_block!(
|
||||
#[cfg(target_pointer_width = "16")] {
|
||||
test_impl_try_from_same_sign_err! { test_try_u32usize, u32, usize }
|
||||
test_impl_try_from_same_sign_err! { test_try_u64usize, u64, usize }
|
||||
|
||||
test_impl_try_from_same_sign_err! { test_try_i32isize, i32, isize }
|
||||
test_impl_try_from_same_sign_err! { test_try_i64isize, i64, isize }
|
||||
}
|
||||
|
||||
#[cfg(target_pointer_width = "32")] {
|
||||
test_impl_try_from_same_sign_err! { test_try_u64usize, u64, usize }
|
||||
test_impl_try_from_same_sign_err! { test_try_usizeu16, usize, u16 }
|
||||
|
||||
test_impl_try_from_same_sign_err! { test_try_i64isize, i64, isize }
|
||||
test_impl_try_from_same_sign_err! { test_try_isizei16, isize, i16 }
|
||||
}
|
||||
|
||||
#[cfg(target_pointer_width = "64")] {
|
||||
test_impl_try_from_same_sign_err! { test_try_usizeu16, usize, u16 }
|
||||
test_impl_try_from_same_sign_err! { test_try_usizeu32, usize, u32 }
|
||||
|
||||
test_impl_try_from_same_sign_err! { test_try_isizei16, isize, i16 }
|
||||
test_impl_try_from_same_sign_err! { test_try_isizei32, isize, i32 }
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
/// Conversions where neither the min nor the max of $source can be represented by
|
||||
@ -525,6 +636,22 @@ test_impl_try_from_signed_to_unsigned_err! { test_try_i128u64, i128, u64 }
|
||||
assume_usize_width! {
|
||||
test_impl_try_from_signed_to_unsigned_err! { test_try_isizeu8, isize, u8 }
|
||||
test_impl_try_from_signed_to_unsigned_err! { test_try_i128usize, i128, usize }
|
||||
|
||||
cfg_block! {
|
||||
#[cfg(target_pointer_width = "16")] {
|
||||
test_impl_try_from_signed_to_unsigned_err! { test_try_i32usize, i32, usize }
|
||||
test_impl_try_from_signed_to_unsigned_err! { test_try_i64usize, i64, usize }
|
||||
}
|
||||
#[cfg(target_pointer_width = "32")] {
|
||||
test_impl_try_from_signed_to_unsigned_err! { test_try_i64usize, i64, usize }
|
||||
|
||||
test_impl_try_from_signed_to_unsigned_err! { test_try_isizeu16, isize, u16 }
|
||||
}
|
||||
#[cfg(target_pointer_width = "64")] {
|
||||
test_impl_try_from_signed_to_unsigned_err! { test_try_isizeu16, isize, u16 }
|
||||
test_impl_try_from_signed_to_unsigned_err! { test_try_isizeu32, isize, u32 }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
macro_rules! test_float {
|
||||
|
@ -10,6 +10,7 @@
|
||||
|
||||
use io::prelude::*;
|
||||
|
||||
use core::convert::TryInto;
|
||||
use cmp;
|
||||
use io::{self, Initializer, SeekFrom, Error, ErrorKind};
|
||||
|
||||
@ -259,26 +260,9 @@ fn slice_write(pos_mut: &mut u64, slice: &mut [u8], buf: &[u8]) -> io::Result<us
|
||||
Ok(amt)
|
||||
}
|
||||
|
||||
/// Compensate removal of some impls per
|
||||
/// https://github.com/rust-lang/rust/pull/49305#issuecomment-376293243
|
||||
#[cfg(any(target_pointer_width = "16",
|
||||
target_pointer_width = "32"))]
|
||||
fn try_into(n: u64) -> Result<usize, ()> {
|
||||
if n <= (<usize>::max_value() as u64) {
|
||||
Ok(n as usize)
|
||||
} else {
|
||||
Err(())
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(any(target_pointer_width = "64"))]
|
||||
fn try_into(n: u64) -> Result<usize, ()> {
|
||||
Ok(n as usize)
|
||||
}
|
||||
|
||||
// Resizing write implementation
|
||||
fn vec_write(pos_mut: &mut u64, vec: &mut Vec<u8>, buf: &[u8]) -> io::Result<usize> {
|
||||
let pos: usize = try_into(*pos_mut).map_err(|_| {
|
||||
let pos: usize = (*pos_mut).try_into().map_err(|_| {
|
||||
Error::new(ErrorKind::InvalidInput,
|
||||
"cursor position exceeds maximum possible vector length")
|
||||
})?;
|
||||
|
Loading…
Reference in New Issue
Block a user