Skip to content

Add non-portable infallible conversion traits for usize/isize #71360

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

Closed
wants to merge 1 commit into from
Closed
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
1 change: 1 addition & 0 deletions src/libcore/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -238,6 +238,7 @@ pub mod pin;
pub mod raw;
pub mod result;
pub mod sync;
pub mod target;

#[cfg(not(test))] // See #65860
pub mod fmt;
Expand Down
204 changes: 204 additions & 0 deletions src/libcore/target.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,204 @@
#![unstable(feature = "non_portable_conversion", issue = /* FIXME */ "none")]

//! Target-specific functionality
//!
//! ## Background: `From` and `Into` are portable
//!
//! The `From` and `Into` traits are in the prelude, so they don’t need to be imported with `use`.
//! They provide conversions that are infallible.
//! For example, `From<u32> for u64` is implemented:
//!
//! ```
//! assert_eq!(u64::from(7_u32), 7_u64);
//! ```
//!
//! … but `From<u64> for u32` is not, because larger values cannot be represented:
//!
//! ```compile_fail,E0277
//! let x = 7_u64;
//! // error: `std::convert::From<u32>` is not implemented for `u16`
//! let _ = u32::from(x); // (What if `x` was `7_000_000_000_000_u64` ?)
//! ```
//!
//! Additionally, `From` and `Into` impls are portable:
//! they only exist when they are infallible regardless of the target.
//! For example, converting `u64` to `usize` would be infallible
//! if the target happens to have 64-bit pointers,
//! but the `From` trait still doesn’t allow it:
//!
//! ```compile_fail,E0277
//! let x = 7_u64;
//! // error: `std::convert::From<u64>` is not implemented for `usize`
//! let _ = usize::from(x);
//! ```
//!
//! This conversion is possible with the `TryFrom` trait:
//!
//! ```
//! use std::convert::TryFrom;
//!
//! assert_eq!(usize::try_from(7_u64).unwrap(), 7_usize);
//! ```
//!
//! However, because `try_from` is fallible, this may require using `.unwrap()`.
//! In a less trivial case, it may not be obvious to readers of the code that
//! this can never panic (on 64-bit platforms).
//!
//! ## Non-portable conversion traits
//!
//! This module provides integer conversion traits that are only available for some targets.
//! They provide the "missing" integer conversions
//! for code that only cares about portability to some platforms.
//! For example, an application that only runs on 64-bit servers could use:
//!
//! ```
//! #![feature(non_portable_conversion)]
//!
//! # // Make this test a no-op on non-64-bit platforms
//! # #[cfg(target_pointer_width = "64")]
//! use std::target::PointerWidthGe64From;
//!
//! # #[cfg(target_pointer_width = "64")]
//! assert_eq!(usize::target_from(7_u64), 7_usize);
//! ```
//!
//! Here the code does not have a panic branch at all.
//! In return, it does not compile on some targets.
//!
//! ```compile_fail,E0432
//! #![feature(non_portable_conversion)]
//!
//! // These two never exist at the same time:
//! use std::target::PointerWidthGe64From;
//! use std::target::PointerWidthLe32From;
//! // error[E0432]: unresolved import
//! ```
//!
//! The mandatory import `std::target` is an indication to writers and readers
//! of non-portable, target-specific code.
//! This is similar to the `std::os` module.
//!
//! The trait names contain `Ge` or `Le` which mean “greater then or equal”
//! and “less than or equal” respectively,
//! like in the `ge` or `le` methods of the `PartialOrd` trait.

macro_rules! common_attribute {
( #[$attr: meta] $( $item: item )+ ) => {
$(
#[$attr]
$item
)+
}
}

macro_rules! target_category {
(
[$( $ptr_width: expr ),+]
$doc: tt
$FromTrait: ident $( : $BlanketImplForOtherFromTrait: ident )?
$IntoTrait: ident
$( $from_ty: ty => $to_ty: ty, )+
) => {
common_attribute! {
#[cfg(any(doc, $(target_pointer_width = $ptr_width),+ ))]

/// Similar to `convert::From`, only available for targets where the pointer size is
#[doc = $doc]
/// bits.
pub trait $FromTrait<T>: Sized {
/// Performs the conversion.
fn target_from(_: T) -> Self;
}

/// Similar to `convert::Into`, only available for targets where the pointer size is
#[doc = $doc]
/// bits.
pub trait $IntoTrait<T>: Sized {
/// Performs the conversion.
fn target_into(self) -> T;
}

// From implies Into
impl<T, U> $IntoTrait<U> for T
where
U: $FromTrait<T>,
{
fn target_into(self) -> U {
U::target_from(self)
}
}

$(
// For example: if the pointer width >= 64 bits, then it is also >= 32 bits
impl<T, U> $FromTrait<U> for T
where
T: $BlanketImplForOtherFromTrait<U>,
{
fn target_from(x: U) -> T {
$BlanketImplForOtherFromTrait::target_from(x)
}
}
)?

$(
impl $FromTrait<$from_ty> for $to_ty {
fn target_from(x: $from_ty) -> $to_ty { x as $to_ty }
}
)+
}
}
}

target_category! {
["64" /*, "128", ... */]
"greater than or equal (`Ge`) to 64"
PointerWidthGe64From: PointerWidthGe32From
PointerWidthGe64Into
u32 => isize,
u64 => usize,
i64 => isize,
}

target_category! {
["32", "64" /*, "128", ... */]
"greater than or equal (`Ge`) to 32"
PointerWidthGe32From
PointerWidthGe32Into
u16 => isize,
u32 => usize,
i32 => isize,
}

target_category! {
["16"]
"less than or equal (`Le`) to 16"
PointerWidthLe16From: PointerWidthLe32From
PointerWidthLe16Into
usize => u16,
isize => i16,
usize => i32,
}

target_category! {
["16", "32"]
"less than or equal (`Le`) to 32"
PointerWidthLe32From: PointerWidthLe64From
PointerWidthLe32Into
usize => u32,
isize => i32,
usize => i64,
}

target_category! {
["16", "32", "64"]
"less than or equal (`Le`) to 64"
PointerWidthLe64From /* : PointerWidthLe128From */
PointerWidthLe64Into
usize => u64,
isize => i64,
usize => i128,

// If adding `PointerWidthLe128From`, these should move there:
usize => u128,
isize => i128,
}
3 changes: 3 additions & 0 deletions src/libstd/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -284,6 +284,7 @@
#![cfg_attr(not(bootstrap), feature(negative_impls))]
#![feature(never_type)]
#![feature(nll)]
#![feature(non_portable_conversion)]
#![feature(optin_builtin_traits)]
#![feature(or_patterns)]
#![feature(panic_info_message)]
Expand Down Expand Up @@ -431,6 +432,8 @@ pub use core::ptr;
pub use core::raw;
#[stable(feature = "rust1", since = "1.0.0")]
pub use core::result;
#[unstable(feature = "non_portable_conversion", issue = /* FIXME */ "none")]
pub use core::target;
#[stable(feature = "i128", since = "1.26.0")]
pub use core::u128;
#[stable(feature = "rust1", since = "1.0.0")]
Expand Down