-
Notifications
You must be signed in to change notification settings - Fork 12.8k
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
improve the TryFrom implementations #43248
Conversation
r? @brson (rust_highfive has picked a reviewer for you, use r? to override) |
r? @BurntSushi since you're already reviewing #43220 |
Hm, this might be a bit nicer way to do this than my attempt |
Thanks @llogiq! This change makes sense to me. My primary concern here is test coverage. What do our tests look like for What do others think? cc @rust-lang/libs |
I did not see tests either. I'll write some when I find the time. I also think I should add |
@BurntSushi there are tests in |
I agree with @BurntSushi in that it's kind hard to follow what's going on here reading the source code, having an exhaustive test which tests all ints going to all other ints with the same implementation of |
($storage:ty, $target:ty, $($source:ty),*) => {$( | ||
// 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; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Since this can never fail, shouldn't this be an empty type like enum NeverIntError {}
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Oh-- but I see there are cfg'd impls of this for usize
based on the current target's pointer width, so those would have to be kept as TryFromIntError
in order to ensure target compatibility.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think the infallible ones are moving to a blanket impl, per #33417 (comment)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yep-- I was suggesting enum NeverIntError {}
so that this wouldn't be blocked on never_type
. Once never_type
and the blanket impl land, NeverIntError
can be changed to an alias for !
.
Ok, I've added the inline annotations and copied over the tests from #43194 |
src/libcore/num/mod.rs
Outdated
#[unstable(feature = "try_from", issue = "33417")] | ||
#[inline] | ||
impl TryFrom<$source> for $target { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
[00:01:34] error[E0518]: attribute should be applied to function
[00:01:34] --> /checkout/src/libcore/num/mod.rs:2511:9
[00:01:34] |
[00:01:34] 2511 | #[inline]
[00:01:34] | ^^^^^^^^^ requires a function
[00:01:34] ...
[00:01:34] 2588 | try_from_unbounded!(u8, u8, u16, u32, u64, u128);
[00:01:34] | ------------------------------------------------- in this macro invocation
Yeah shouldn't this be applied to fn try_from
?
src/libcore/num/mod.rs
Outdated
let max = <$signed as FromStrRadixHelper>::max_value() as u128; | ||
if u as u128 > max { | ||
fn try_from(u: $source) -> Result<$target, TryFromIntError> { | ||
if u > (<$target as FromStrRadixHelper>::max_value() as $source) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The as FromStrRadixHelper
isn't actually needed. You can simply do <$target>::max_value()
and avoid the extra indirection.
Looks like a real failure?
|
That's strange... Is the target pointer size not one of 16/32/64 on that build? I note that all other builds are successful. Perhaps I should add code for this scenario. |
It says x86_64-unknown-linux-gnu so it should be 64-bit. I don't think any of the other builds run any tests, did it work locally? EDIT: The tests you borrowed from my pr even has a check for non 16/32/64-bit in the "assume_ptr_width" macro that wraps the pointer width specific tests, so that's definitely not the issue. |
OK, but then why does the error only appear on that one LLVM 3.7 builder? |
src/libcore/num/mod.rs
Outdated
#[cfg(target_pointer_width = "64")] try_from_both_bounded!(i128, usize); | ||
|
||
cfg_block!{ | ||
#[cfg(target_pointer_width = "16")] { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can these be done with inner modules instead?
#[cfg(target_pointer_width = "16")]
mod imp {
...
}
#[cfg(target_pointer_width = "32")]
mod imp {
...
}
pub use imp::*;
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You could probably also do the same in the tests.
@kennytm of course, I just reused the macro from the tests.Will wait for travis, though, so I can fix whatever it comes up with. |
By, the way, I'd like to leave the macro in the tests for now, as I think submodules will get their own headers in the test output, right? |
Huh? That tidy error 1 sure looks spurious. |
Looks like there is an extra set of braces. I think you also forgot to pub use the module. |
Ah, thanks. Will fix once I get to my PC. |
src/libcore/num/mod.rs
Outdated
try_from_both_bounded!(i64, u32, u16, u8); | ||
try_from_both_bounded!(i128, u64, u32, u16, u8); | ||
|
||
pub use ptr_try_from_impls.*; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
more typo
[00:01:56] Compiling unwind v0.0.0 (file:///checkout/src/libunwind)
[00:01:56] error: expected one of `::`, `;`, or `as`, found `.`
[00:01:56] --> /checkout/src/libcore/num/mod.rs:2630:27
[00:01:56] |
[00:01:56] 2630 | pub use ptr_try_from_impls.*;
[00:01:56] | ^ expected one of `::`, `;`, or `as` here
@llogiq will you be fixing this? The alternate PR has been ready for quite a while, although it seems to me like having I’ll wait for this to get into landable state or till I get another triage ping on the alternate PR whichever comes sooner to take a decision here. |
Also I feel like @oyvindln deserves some attribution in the commit message for authoring the tests. |
@nagisa I wholeheartedly agree. The only reason I didn't merge his PR directly is I suck at git. 🙁 |
This appears to work. I'll try to squash & add proper attribution in the commit message. |
This removes the need for a 128 bit storage by making use of the fact that there can be either no over/underflow, either one or both, and each time the target type suffices to hold the limit for comparison. The downside is that the code looks a bit more complex. This test code included in this commit is from @oyvindln 's PR. They also greatly helped fixing a number of errors I made along the way. Thanks a lot!
Done! |
@bors r+ |
📌 Commit 72ef15e has been approved by |
try_from_both_bounded!(i128, u64, u32, u16, u8); | ||
|
||
#[unstable(feature = "try_from", issue = "33417")] | ||
pub use self::ptr_try_from_impls::*; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What is this for? You can't (and don't need to) reexport impls.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Really? Because the pub
makes them visible where the mod
s are not. I deem them implementation detail, so I chose to keep them private.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Impls are always visible, even if they're in private modules.
test_impl_try_from_always_ok! { test_try_u64u128, u64, u128 } | ||
test_impl_try_from_always_ok! { test_try_u64i128, u64, i128 } | ||
|
||
test_impl_try_from_always_ok! { test_try_u128, u128, u128 } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
For consistency with the other tests this should be test_try_u128u128
.
try_from_both_bounded!(isize, u8); | ||
try_from_lower_bounded!(isize, usize, u16, u32, u64, u128); | ||
try_from_both_bounded!(isize, i8); | ||
try_from_unbounded!(isize, i16, i32, i64, i128); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This looks like it's missing isize
to isize
. It might be clearer to put the following outside these target specific modules:
try_from_unbounded!(usize, usize);
try_from_upper_bounded!(usize, isize);
try_from_lower_bounded!(isize, usize);
try_from_unbounded!(isize, isize);
cfg_block!( | ||
#[cfg(target_pointer_width = "16")] { | ||
test_impl_try_from_always_ok! { test_try_u16usize, u16, usize } | ||
test_impl_try_from_always_ok! { test_try_i16isize, i16, isize } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
these should be:
test_impl_try_from_always_ok! { test_try_usizeu16, usize, u16 }
test_impl_try_from_always_ok! { test_try_isizei16, isize, i16 }
also missing:
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_usizei64, usize, i64 }
test_impl_try_from_always_ok! { test_try_isizei32, isize, i32 }
test_impl_try_from_always_ok! { test_try_i64i128, i64, i128 } | ||
|
||
test_impl_try_from_always_ok! { test_try_i128i128, i128, i128 } | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
missing:
test_impl_try_from_always_ok! { test_try_usizeusize, usize, usize }
test_impl_try_from_always_ok! { test_try_isizeisize, isize, isize }
test_impl_try_from_always_ok! { test_try_i128i128, i128, i128 } | ||
|
||
assume_usize_width! { | ||
test_impl_try_from_always_ok! { test_try_u8usize, u8, usize } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
missing:
test_impl_try_from_always_ok! { test_try_u8isize, u8, 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 } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
missing:
test_impl_try_from_always_ok! { test_try_u16isize, u16, isize }
test_impl_try_from_always_ok! { test_try_usizei64, usize, i64 }
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_u64usize, u64, usize } | ||
test_impl_try_from_always_ok! { test_try_i64isize, i64, isize } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
missing:
test_impl_try_from_always_ok! { test_try_u16isize, u16, isize }
test_impl_try_from_always_ok! { test_try_u32isize, u32, isize }
cfg_block!( | ||
#[cfg(target_pointer_width = "16")] { | ||
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_u32isize, u64, isize } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
this one should be:
test_impl_try_from_unsigned_to_signed_upper_err! { test_try_u16isize, u16, isize }
improve the TryFrom implementations This removes the need for a 128 bit storage by making use of the fact that there can be either no over/underflow, either one or both, and each time the target type suffices to hold the limit for comparison. This also means that the implementation will work in targets without 128bit support (unless it's for 128bit types, of course). The downside is that the code looks a bit more complex.
☀️ Test successful - status-appveyor, status-travis |
@ollie27 thanks for noticing the missing tests! Do you want to do a followup PR adding them? |
Sure, I'll add them in a new PR. |
Done: #43471 |
I'm confused why this was merged-- don't the infallible |
@cramertj this can be reverted once that NumCast RFC is implemented (which was scheduled to be submitted mid-August). |
@cramertj the infallible impls were already there (https://doc.rust-lang.org/std/convert/trait.TryFrom.html), so AFAIK they'll be removed only when that blanket impl is added. |
Add missing impl and tests for int to int TryFrom impls These were missing from rust-lang#43248. r? @nagisa
This removes the need for a 128 bit storage by making use of the fact that there can be either no over/underflow, either one or both, and each time the target type suffices to hold the limit for comparison. This also means that the implementation will work in targets without 128bit support (unless it's for 128bit types, of course).
The downside is that the code looks a bit more complex.