-
Notifications
You must be signed in to change notification settings - Fork 12.9k
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
Tracking issue for platform-dependent-API TryFrom impls involving usize/isize #49415
Comments
More discussion on this is also in #48593. |
Not having these impls at all is a "hole" in the functionality of this trait: #49305 (comment) |
An idea: impl TryFrom<u64> for usize {
type Error = SizeConversionError<u64>;
// fn try_from ...
}
impl<T> From<SizeConverstionError<T>> for TryFromIntError {
// fn from ...
}
// Mark use of this trait with portability lint
// The philosophy is similar to e.g. AsRawFd trait
trait SizeConversionExt {
type Value;
fn conversion_never_fails(self) -> Self::Value;
}
// pseudo attribute - don't know the real one
#[cfg(pointer_size = 64)]
impl SizeConversionExt for Result<u64, SizeConverstionError<u64>> {
type Value = u64;
fn conversion_never_fails(self) -> Self::Value {
match self {
Ok(val) => val,
Err(e) => e.uninhabited,
}
}
}
// Portable code:
fn portable<T: TryInto<usize>>(val: T) {
match val.try_into() {
// ...
}
}
// For X86-64
fn non_portable(val: u64) {
#[allow(non-portable)]
use /*something::something*/SizeConversionExt;
let val: usize = val.try_into().conversion_never_fails();
// Do whatever with val
} |
I think that making all of these conversions go through |
@clarcharr I’m not sure what you mean. Note that though a generic impl, any I also don’t know what you mean by performance: whatever the error type, with sufficiently advanced inlining the optimizer can see that a given impl only ever returns |
Bitter experience over many years of porting code that performs integer conversions suggests to me that people will use infallible conversions on code that will later be run on platforms where the conversion does indeed fail, creating a portability headache. Even in the presence of a lint, this stuff often leaches deeply into the design of a program, including things which a lint won't pick up, where it can become extremely difficult to unpick. As @SimonSapin says, with sufficiently good optimisations -- which, IIRC the last time I looked at this is already the case -- both the |
I agree with @ltratt. I've recently participated on porting some code from 32 to 64 bit and it wasn't fun. That being said, performance is fine in simple cases, but I guess it would get worse with more complex code where errors are propagated and handled by the caller. That's why I'd prefer what I wrote above: have a special error type with a private field of type |
Is there any evidence of a performance degradation for having Just to be clear. I'm in favour of having all |
This issue is not at all about performance. It’s about choosing (or not) to have APIs vary across targets. |
I must have missed some argument. What is the downside with having |
@kevincox I personally think that’s what we should do anyway, but as explained in “Background” of the original message of this thread it prevents having non-portable portability-lint-gated |
Thanks for explaining. One solution would be https://github.com/rust-lang/rfcs/blob/master/text/1210-impl-specialization.md. I believe this would allow the blanket However until then (or if that never comes to fruition) I think I would still prefer having the |
The
|
Has there been any progress towards making a decision on this? Selfishly, I would like not to be stuck using a nightly from March (because the removed |
I would like this to be finalised too. To me it seems that first option (use |
The generic Coords is waiting on reoslution for: rust-lang/rust#49415
Prior to #49305 the error types for |
I would prefer the error type to carry information about from which integer the conversion was attempted as without it compiler optimizations will be probably impossible without whole-program analysis. That being said, I prefer cross-platform solution. (Note: AVR has 16 bit addressing.) |
Wouldn't it be possible to make these all implement |
@Kixunil Do you mean a single error type that would contain some kind of enum? What would you do with that info? What would be the public APIs? Or if you mean separate error types, that’s potentially a lot of types for all combinations of “infallible if usize is at {least,most} {16,32,64,128} bits”. @clarcharr Is it meaningful for a type to be unstable if stable APIs can still produce a value of that type? |
@SimonSapin The only way to produce that type would be from It is a sort of hack, but it would allow us to at least add |
By produce a value of that type I meant something like |
To provide more context about my idea, I wrote a proof-of-concept. |
@Kixunil This would probably needs two type parameters to support conversions in both directions. But more importantly, |
@SimonSapin Good point. Hypothetically, if compatibility lint was easier to implement on methods, it could still be helpful. The associated type could be hidden by sealing the trait. Anyway, I prefer compatibility-first approach. Edit: I just realized, we don't need that trait at all. We could just conditionally compile |
@Kixunil I only vaguely guess what you mean by sealing the trait, but I believe this also doesn’t solve the problem. |
@SimonSapin similar to what |
…le with a portability lint" This reverts commit 837d6c7. Fixes rust-lang#49415
@SimonSapin Hi Simon, thanks for pushing this forward. Unfortunately I'm really blocked on this issue (in particular it's the |
Unfortunately, |
Ok, many thanks. I just noticed that you have a revert commit for this, is that likely to be merged into master? |
This would be my preferred outcome. I’m gonna talk to people this week about what’s going on with the portability lint and what to do process-wise. |
Many thanks. |
🔔 🔔 Proposed resolution: PR #51564 restores |
Implement always-fallible TryFrom for usize/isize conversions that are infallible on some platforms This reverts commit 837d6c7 "Remove TryFrom impls that might become conditionally-infallible with a portability lint". This fixes rust-lang#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](rust-lang/rfcs#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.
Implement always-fallible TryFrom for usize/isize conversions that are infallible on some platforms This reverts commit 837d6c7 "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](rust-lang/rfcs#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.
The generic Coords is waiting on resolution for: rust-lang/rust#49415
Background
For converting between different primitive integer types we have impls of the
From
trait (and so ofTryFrom<Error=!>
through the genericimpl<T, U> TryFrom<T> for U where U: From<T>
) for conversions that always succeed (for exampleu8
tou32
), andTryFrom<Error=TryFromIntError>
for other conversions (for exampleu32
tou8
, which returnErr(_)
on values that would overflow).When
usize
orisize
is involved however, some conversions can be fallible or infallible depending on the target platform’s pointer width. (For exampleu64
tousize
on 64-bit v.s. 32-bit platforms.)There is desire to make such impls infallible with
From
whenever possible. Since APIs would be different on the target platform, their usage should be gated on a portability lint that is not implemented yet. (The lint would additionally need to be able to "see" that a non-portable impl is used indirectly through a generic impl likeimpl<T, U> TryFrom<T> for U where U: From<T>
orimpl<T, U> Into<U> for T where U: From<T>
.)A downside of this is that even code that would be portable because it doesn’t care about the error type (because it only tests for the presence of an error, or works in both case though e.g. the
From
conversion inside the?
operator) would still need top opt into "non-portability" in order to silence the lint.In order to be able to stabilize the
TryFrom
trait without waiting for the portability lint to be implemented, #49305 removed the affected impls. They are those markedNP
in the table in #49305 (comment):TryFrom<usize>
foru16
,u32
,u64
,u128
,i32
,i64
,i128
TryFrom<isize>
fori16
,i32
,i64
,i128
TryFrom<_> for usize
:u32
,u64
,u128
TryFrom<_> for isize
:u16
,u32
,u64
,u128
,i32
,i64
,i128
Alternatives
We want to have these impls eventually, but need to chose between:
TryFrom<Error=TryFromIntError>
on all platforms.The text was updated successfully, but these errors were encountered: