abstract type Gate{N} end;
struct IdentGate{N} <: Gate{N}
wire::UInt8
function IdentGate{N}(w::Int) where {N}
@assert 1 <= w <= N
new(w)
end
end
GateProduct{N} = Array{Gate{N}, 1}
convert(::Type{GateProduct{N}}, g::Gate{N}) where {N} = GateProduct{N}([g])
Explicit conversion of IdentGate to GateProduct works.
However, it is never called automatically.
In particular,
a = Array{GateProduct{2}, 1}()
push!(a, IdentGate{2}(1))
Does not work. Is this expected behaviour?
As per documentation, convert should be called to construct GateProduct{2} from IdentGate{2}
which is a Gate{2} instance.
No, in fact import Base.convert was included more than once.
I have narrowed down the issue to one file which imported Base.convert being included more than once.
Once includes are made unique, the issue goes away.
Does this sound like a bug?
Ok, the example in your top post works with an import Base: convert so would be good if you could supply an example that duplicates the broken behavior.
The issue is following. For some reason in my codebase I ended up with one convert definition
which was not preceded with import Base.convert. Then all subsequent Base.convert import were inhibited until it hit Gate conversion.
I don’t know if this is technically bug, because I saw a warning in the console, so there is that
You should be able to work around this by explicitly defining Base.convert(::Type{GateProduct[N}}, g::Gate{N}) where {N} = ... - that way the definition of the conversion method doesn’t depend on convert not being overwritten before importing Base.convert.
Working
julia> struct Dummy end
julia> convert(::Type{Dummy}, x::Int) = 2
convert (generic function with 1 method)
julia> abstract type Gate{N} end;
julia> struct IdentGate{N} <: Gate{N}
wire::UInt8
function IdentGate{N}(w::Int) where {N}
@assert 1 <= w <= N
new(w)
end
end
julia> GateProduct{N} = Array{Gate{N}, 1}
Array{Gate{N},1} where N
julia> Base.convert(::Type{GateProduct{N}}, g::Gate{N}) where {N} = GateProduct{N}([g])
julia> using Test
julia> @testset "Double include overloading bug" begin
a = Array{GateProduct{2}, 1}()
push!(a, IdentGate{2}(1))
@test true == true
end
Test Summary: | Pass Total
Double include overloading bug | 1 1
Test.DefaultTestSet("Double include overloading bug", Any[], 1, false)
Broken
julia> struct Dummy end
julia> convert(::Type{Dummy}, x::Int) = 2
convert (generic function with 1 method)
julia> import Base.convert
WARNING: import of Base.convert into Main conflicts with an existing identifier; ignored.
julia> abstract type Gate{N} end;
julia> struct IdentGate{N} <: Gate{N}
wire::UInt8
function IdentGate{N}(w::Int) where {N}
@assert 1 <= w <= N
new(w)
end
end
julia> GateProduct{N} = Array{Gate{N}, 1}
Array{Gate{N},1} where N
julia> convert(::Type{GateProduct{N}}, g::Gate{N}) where {N} = GateProduct{N}([g])
convert (generic function with 2 methods)
julia> using Test
julia> @testset "Double include overloading bug" begin
a = Array{GateProduct{2}, 1}()
push!(a, IdentGate{2}(1))
@test true == true
end
Double include overloading bug: Error During Test at REPL[9]:1
Got exception outside of a @test
MethodError: Cannot `convert` an object of type IdentGate{2} to an object of type Array{Gate{2},1}
Closest candidates are:
convert(::Type{T<:Array}, !Matched::AbstractArray) where T<:Array at array.jl:489
convert(::Type{T<:AbstractArray}, !Matched::T<:AbstractArray) where T<:AbstractArray at abstractarray.jl:14
Note how the REPL already mentions the problem:
julia> struct Dummy end
julia> convert(::Type{Dummy}, x::Int) = 2
convert (generic function with 1 method)
julia> import Base.convert
WARNING: import of Base.convert into Main conflicts with an existing identifier; ignored.
I suspect this is intended behaviour, because the imported identfier really should not overwrite existing things.