Printing user defined unsigned primitive typed

Following the advice in this post I declared a user defined primitive type of 32 bits.
This works when declared as a signed 32 bit integer:

abstract type MyType <: Signed end
primitive type MyType32 <: MyType 32 end
MyType32(x::Int32) = reinterpret(MyType32, x)
Int32(x::MyType32) = reinterpret(Int32, x)
Base.show(io::IO, x::MyType32) = print(io, Int32(x))

x=MyType32(Int32(5))
println(x)

However, when changing to unsigned integers:

abstract type MyType <: Unsigned end
primitive type MyType32 <: MyType 32 end
MyType32(x::UInt32) = reinterpret(MyType32, x)
UInt32(x::MyType32) = reinterpret(UInt32, x)
Base.show(io::IO, x::MyType32) = print(io, UInt32(x))

x=MyType32(UInt32(5))
println(x)

The println function raises an error:
LoadError: promotion of types MyType32 and Int64 failed to change any arguments

The stack trace shows that println calls string which calls dec, and suddenly the value becomes Int64 which is not supported by my primitive type

you need to define corresponding converters:

Base.convert(::Type{T}, x::MyType32) where {T<:Integer} = convert(T, UInt32(x))
Base.convert(::Type{MyType32}, x::T) where {T<:Integer} = MyType32(convert(UInt32, x))
2 Likes

Thanks for your reply, and naturally it works.
Please clarify the flow of function calls when println(x) is called.
When adding debug print statements I could not see that the additional Base.convert methods are called. Yet, evidently, they are necessary for the sample program to compile and run

I think the stacktrace already told this every clearly:

julia> println(x)
ERROR: promotion of types MyType32 and Int64 failed to change any arguments
Stacktrace:
 [1] error(::String, ::String, ::String) at ./error.jl:42
 [2] sametype_error(::Tuple{MyType32,Int64}) at ./promotion.jl:308
 [3] not_sametype(::Tuple{MyType32,Int64}, ::Tuple{MyType32,Int64}) at ./promotion.jl:302
 [4] promote at ./promotion.jl:285 [inlined]
 [5] ==(::MyType32, ::Int64) at ./promotion.jl:350
 [6] ndigits0zpb(::MyType32, ::Int64) at ./intfuncs.jl:450
 [7] ndigits0z(::MyType32, ::Int64) at ./intfuncs.jl:512

In REPL, you could simply type the stacktrace number and press Ctrl+Q to jump to the source code and search for the root cause manually.

Another way is to use Debugger.jl:

# we start from [5] ==(::MyType32, ::Int64) at ./promotion.jl:350 
# since it's simple to make a trivial test example and it's also every close to the final error site
julia> @enter x == 5 
[ Info: tracking Base
In ==(x, y) at promotion.jl:350
>350  ==(x::Number, y::Number) = (==)(promote(x,y)...)

About to run: (promote)(5, 5)
# tell debugger to break on error
1|debug> bp on
Breaking on error
# continue
1|debug> c
Breaking for error:
ERROR: promotion of types MyType32 and Int64 failed to change any arguments
Stacktrace:
 [1] error(::Tuple{String,String,String}) at error.jl:42
 [2] sametype_error(::Tuple{MyType32,Int64}) at promotion.jl:308
 [3] not_sametype(::Tuple{MyType32,Int64}, ::Tuple{MyType32,Int64}) at promotion.jl:302
 [4] promote(::MyType32, ::Int64) at promotion.jl:285
 [5] ==(::MyType32, ::Int64) at promotion.jl:350

In error(s) at error.jl:41
 40  function error(s::Vararg{Any,N}) where {N}
 41      @_noinline_meta
>42      throw(ErrorException(Main.Base.string(s...)))
 43  end

About to run: (throw)(ErrorException("promotion of types MyType32 and Int64 failed to change any arguments"))

We arrived at the error site, but often this is not the root cause, so let’s go down to the stacktrace:

# OK, just some error message handling stuff, nothing helpful
1|debug> down
In sametype_error(input) at promotion.jl:307
 306  function sametype_error(input)
 307      @_noinline_meta
>308      error("promotion of types ",
 309            join(map(x->string(typeof(x)), input), ", ", " and "),
 310            " failed to change any arguments")
 311  end

About to run: (error)("promotion of types ", "MyType32 and Int64", " failed to change any arguments")
# same
2|debug> down
In not_sametype(x, y) at promotion.jl:302
>302  not_sametype(x::T, y::T) where {T} = sametype_error(x)

About to run: (Base.sametype_error)((5, 5))
# finally, this function truely did something non-trivial by calling `_promote(x,y)`
3|debug> down
In promote(x, y) at promotion.jl:283
 282  function promote(x, y)
 283      @_inline_meta
 284      px, py = _promote(x, y)
>285      not_sametype((x,y), (px,py))
 286      px, py
 287  end

About to run: (Base.not_sametype)((5, 5), (5, 5))

# so, let's add a breakpoint at 284 and quit
4|debug> bp add 284
┌ Warning: skipping (::getfield(Base, Symbol("##23#24")))(x) in Base at promotion.jl:309 to avoid parsing too much code
└ @ Revise ~/.julia/packages/Revise/agmgx/src/Revise.jl:813
Breaking on error
1] /Applications/Julia-1.1.app/Contents/Resources/julia/share/julia/base/promotion.jl:284 

4|debug> q
# enter again and continue to the bp
julia> @enter x == 5
In ==(x, y) at promotion.jl:350
>350  ==(x::Number, y::Number) = (==)(promote(x,y)...)

About to run: (promote)(5, 5)
1|debug> bp
Breaking on error
1] /Applications/Julia-1.1.app/Contents/Resources/julia/share/julia/base/promotion.jl:284

1|debug> c
Hit breakpoint:
In promote(x, y) at promotion.jl:283
 282  function promote(x, y)
 283      @_inline_meta
>284      px, py = _promote(x, y)
 285      not_sametype((x,y), (px,py))
 286      px, py
 287  end

About to run: (Base._promote)(5, 5)
1|debug> s
In _promote(x, y) at promotion.jl:259
 258  function _promote(x::T, y::S) where {T,S}
 259      @_inline_meta
>260      R = promote_type(T, S)
 261      return (convert(R, x), convert(R, y))
 262  end

About to run: (promote_type)(MyType32, Int64)

then, it’s clear that we need to define a converter for MyType32 to Integer:

1|debug> n
In _promote(x, y) at promotion.jl:259
 258  function _promote(x::T, y::S) where {T,S}
 259      @_inline_meta
 260      R = promote_type(T, S)
>261      return (convert(R, x), convert(R, y))
 262  end

About to run: (convert)(Integer, 5)
1|debug> fr
[1] _promote(x, y) at promotion.jl:259
  | x::MyType32 = 5
  | y::Int64 = 5
  | R::DataType = Integer
  | T::DataType = MyType32
  | S::DataType = Int64
1|debug> 
3 Likes