Skip to content
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

Restrict signed(x) = reinterpret(...) to BitIntegers. #33459

Merged
merged 1 commit into from
Oct 4, 2019

Conversation

NHDaly
Copy link
Member

@NHDaly NHDaly commented Oct 3, 2019

Fixes #30473

This is a minor PR, but it fixes an inconsistency in Base that was brought up in #30473 and was blocking the merge of #30445 (comment). :)

I also reordered the definitions to clean them up a bit. :)
(Note that I also deleted the extra method for unsigned(x::Bool) because it's already covered by the default unsigned(x) = convert(Unsigned, x) (since Bool <: Base.BitSigned) == false), and it was a redundant copy/paste. ☺️)


This commit restricts the reinterpret implementation of signed(x)
to only apply to Base.BitIntegers. This brings its behavior in-line
with unsigned(x).

Previously, although unsigned(x) did restrict the reinterpret
implementation to only apply to Base.BitIntegers, signed(x) by
default called reinterpret(Signed, x) for any type T <: Unsigned.

This of course wouldn't work for BigInt, which is presumably why
unsigned(x) was restricted (since BigInt <: Signed but not BigInt <: BitInteger), but although Base contains no non-BitInteger types in
Unsigned (besides Bool), of course users could create such types,
so this should be restricted symmetrically.

Below is an example of the previous, unexpected broken behavior (curtesy @timholy):

struct WeirdUnsigned <: Unsigned
    msg::String
    val::UInt16
end
WeirdUnsigned(x::Int) = WeirdUnsigned("missing", UInt16(x))
Base.Signed(x::WeirdUnsigned) = signed(x.val)
julia> signed(WeirdUnsigned(4))
ERROR: bitcast: expected primitive type value for second argument
Stacktrace:
 [1] reinterpret at ./essentials.jl:417 [inlined]
 [2] signed(::WeirdUnsigned) at ./int.jl:162
 [3] top-level scope at REPL[20]:1

Whereas after this commit, this works as expected, calling the Signed
constructor:

julia> signed(WeirdUnsigned(4))
4

julia> typeof(signed(WeirdUnsigned(4)))
Int16

This should be a non-breaking change. If there exist any primitive type implementations that were relying on the reinterpret
implementation, they may see a slight performance regression, as this
will by default now call Signed(x::T), instead of
reinterpret(typeof(Signed(x)), x) -- so they may want to manually
define the reinterpret implemention themselves.

CC: @JeffreySarnoff, @timholy

This commit restricts the `reinterpret` implementation of `signed(x)`
to only apply to `Base.BitInteger`s. This brings its behavior in-line
with `unsigned(x)`.

Previously, although `unsigned(x)` did restrict the `reinterpret`
implementation to only apply to `Base.BitInteger`s, `signed(x)` by
default called `reinterpret(Signed, x)` for any type `T <: Unsigned`.

This of course wouldn't work for `BigInt`, which is presumably why
`unsigned(x)` was restricted (since `BigInt <: Signed` but not `BigInt
<: BitInteger`), but although _Base_ contains no non-BitInteger types in
`Unsigned` (besides `Bool`), of course users _could_ create such types,
so this should be restricted symmetrically.

Below is an example of the previous, unexpected broken behavior:
```julia
struct WeirdUnsigned <: Unsigned
    msg::String
    val::UInt16
end
WeirdUnsigned(x::Int) = WeirdUnsigned("missing", UInt16(x))
Base.Signed(x::WeirdUnsigned) = signed(x.val)
```
```julia
julia> signed(WeirdUnsigned(4))
ERROR: bitcast: expected primitive type value for second argument
Stacktrace:
 [1] reinterpret at ./essentials.jl:417 [inlined]
 [2] signed(::WeirdUnsigned) at ./int.jl:162
 [3] top-level scope at REPL[20]:1
```

Whereas after this commit, this works as expected, calling the `Signed`
constructor:
```julia
julia> signed(WeirdUnsigned(4))
4

julia> typeof(signed(WeirdUnsigned(4)))
Int16
```

-------------------

This should be a non-breaking change. If there exist any `primitive
type` implementations that were relying on the reinterpret
implementation, they may see a slight performance regression, as this
will by default now call `Signed(x::T)`, instead of
`reinterpret(typeof(Signed(x)), x)` -- so they may want to manually
define the reinterpret implemention themselves.
@JeffreySarnoff
Copy link
Contributor

Thank you. It simplifies coding types that work by reinterpreting both signed and unsigned BitIntegers.

@JeffBezanson
Copy link
Member

Off topic, I'm really surprised we still have signed and unsigned. It seems like those should be x % Signed instead.

@JeffreySarnoff
Copy link
Contributor

^ I agree that x % Signed and x % Unsigned are more betterer

@Keno Keno merged commit 3b1c1fd into JuliaLang:master Oct 4, 2019
@NHDaly NHDaly deleted the unsigned-signed-simplify-BitIntegers branch December 31, 2019 16:56
@JeffBezanson JeffBezanson mentioned this pull request Jan 6, 2020
28 tasks
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

signed(x) and unsigned(x) should agree on whether they'll only reinterpret the standard BitInt types.
5 participants