diff --git a/src/JLD.jl b/src/JLD.jl index 1c5df8e..c599eb7 100644 --- a/src/JLD.jl +++ b/src/JLD.jl @@ -873,12 +873,55 @@ writeas(gr::GlobalRef) = GlobalRefSerializer(gr) ### Converting attribute strings to Julia types +const _where_macrocall = Symbol("@where") +function expand_where_macro(e::Expr) + if TYPESYSTEM_06 + e.head = :where + shift!(e.args) + else + e.head = :let + tv = e.args[2] + if isa(tv, Symbol) + sym = tv + tv = :(TypeVar($sym)) + elseif isa(tv, Expr) + if tv.head === :comparison + lb = tv.args[1] + sym = tv.args[3] + ub = tv.args[5] + tv = :(TypeVar($sym, $lb, $ub)) + elseif tv.head === :(<:) + sym = tv.args[1] + ub = tv.args[2] + tv = :(TypeVar($sym, $ub)) + else + return false + end + else + return false + end + e.args[2] = Expr(:(=), sym, tv) + end + return true +end + is_valid_type_ex(s::Symbol) = true is_valid_type_ex(s::QuoteNode) = true is_valid_type_ex{T}(::T) = isbits(T) -is_valid_type_ex(e::Expr) = (((e.head == :curly || e.head == :tuple || e.head == :.) && all(is_valid_type_ex, e.args)) || - (e.head == :where && is_valid_type_ex(e.args[1])) || - (e.head == :call && (e.args[1] == :Union || e.args[1] == :TypeVar || e.args[1] == :symbol))) +function is_valid_type_ex(e::Expr) + if e.head === :curly || e.head == :tuple || e.head == :. + return all(is_valid_type_ex, e.args) + elseif e.head === :where + return is_valid_type_ex(e.args[1]) + elseif e.head === :let && length(e.args) == 2 + return is_valid_type_ex(e.args[1]) && + is_valid_type_ex(e.args[2].args[2]) + elseif e.head == :call + f = e.args[1] + return f === :Union || f === :TypeVar || f === :symbol + end + return false +end const typemap_Core = Dict( :Uint8 => :UInt8, @@ -891,45 +934,44 @@ const typemap_Core = Dict( const _typedict = Dict{Compat.UTF8String,Type}() fixtypes(typ) = typ -@eval begin - function fixtypes(typ::Expr) - if typ.head == :. - if length(typ.args) == 2 && typ.args[1] == :Core - arg = typ.args[2].value - return Expr(:., :Core, QuoteNode(get(typemap_Core, arg, arg))) - else - return typ - end - elseif typ == :(Core.Type{TypeVar(:T,Union(Core.Any,Core.Undef))}) || typ == :(Core.Type{TypeVar(:T)}) - # Work around https://github.com/JuliaLang/julia/issues/8226 and the removal of Top - return :(Core.Type) +function fixtypes(typ::Expr) + if typ.head === :macrocall && typ.args[1] === _where_macrocall + expand_where_macro(typ) + end + if typ.head === :. + if length(typ.args) == 2 && typ.args[1] === :Core + arg = typ.args[2].value + return Expr(:., :Core, QuoteNode(get(typemap_Core, arg, arg))) + else + return typ end + elseif typ == :(Core.Type{TypeVar(:T,Union(Core.Any,Core.Undef))}) || typ == :(Core.Type{TypeVar(:T)}) + # Work around https://github.com/JuliaLang/julia/issues/8226 and the removal of Top + return :(Core.Type) + end - for i = 1:length(typ.args) - typ.args[i] = fixtypes(typ.args[i]) - end + for i = 1:length(typ.args) + typ.args[i] = fixtypes(typ.args[i]) + end - if (typ.head == :call && !isempty(typ.args) && - typ.args[1] == :Union) - return Expr(:curly, typ.args...) - end + if (typ.head === :call && !isempty(typ.args) && + typ.args[1] === :Union) + return Expr(:curly, typ.args...) + end - if typ.head == :tuple - return Expr(:curly, :Tuple, typ.args...) - end - typ + if typ.head === :tuple + return Expr(:curly, :Tuple, typ.args...) end + return typ end function _julia_type(s::AbstractString) typ = get(_typedict, s, UnconvertedType) if typ == UnconvertedType - local sp - try - sp = parse(s) - catch err + sp = parse(s, raise=false) + if (isa(sp, Expr) && (sp.head == :error || sp.head == :continue || sp.head == :incomplete)) println("error parsing type string ", s) - rethrow(err) + eval(sp) end typ = julia_type(fixtypes(sp)) if typ != UnsupportedType @@ -975,12 +1017,14 @@ function full_typename(io::IO, file::JldFile, x::Core.BottomType) print(io, "Union()") end function full_typename(io::IO, file::JldFile, x::UnionAll) + x == Type && return print(io, "Type") + print(io, "@where(") full_typename(io, file, x.body) - print(io, " where ") + print(io, ',') tv = x.var - if is(tv.lb, Union{}) && is(tv.ub, Any) + if tv.lb === Union{} && tv.ub === Any print(io, tv.name) - elseif is(tv.lb, Union{}) + elseif tv.lb === Union{} print(io, tv.name) print(io, "<:") full_typename(io, file, tv.ub) @@ -991,6 +1035,7 @@ function full_typename(io::IO, file::JldFile, x::UnionAll) print(io, "<:") full_typename(io, file, tv.ub) end + print(io, ')') end function full_typename(io::IO, file::JldFile, tv::TypeVar) print(io, tv.name) diff --git a/test/jldtests.jl b/test/jldtests.jl index fad3f5d..8e41aad 100644 --- a/test/jldtests.jl +++ b/test/jldtests.jl @@ -87,14 +87,14 @@ end objwithpointer = ObjWithPointer(0) # Custom BitsType (#99) bitstype 64 MyBT -bt = reinterpret(MyBT, @compat Int64(55)) +bt = reinterpret(MyBT, Int64(55)) # Symbol arrays (#100) sa_asc = [:a, :b] sa_utf8 = [:α, :β] # SubArray (to test tuple type params) subarray = view([1:5;], 1:5) # Array of empty tuples (to test tuple type params) -arr_empty_tuple = (@compat Tuple{})[] +arr_empty_tuple = (Tuple{})[] immutable EmptyImmutable end emptyimmutable = EmptyImmutable() arr_emptyimmutable = [emptyimmutable] @@ -155,17 +155,23 @@ immutable Vague end vague = Vague(7) # Immutable with a union of BitsTypes -@compat immutable BitsUnion +immutable BitsUnion x::Union{Int64, Float64} end bitsunion = BitsUnion(5.0) # Immutable with a union of Types -@compat immutable TypeUnionField - x::Union{Type{Int64}, Type{Float64}} +let UT = if JLD.TYPESYSTEM_06 + eval(parse("Type{T} where T <: Union{Int64, Float64}")) + else + Union{Type{Int64}, Type{Float64}} + end + @eval immutable TypeUnionField + x::$UT + end end typeunionfield = TypeUnionField(Int64) # Generic union type field -@compat immutable GenericUnionField +immutable GenericUnionField x::Union{Vector{Int},Int} end genericunionfield = GenericUnionField(1) @@ -199,11 +205,11 @@ bigdata = [1:10000;] bigints = big(3).^(1:100) bigfloats = big(3.2).^(1:100) # None -@compat none = Union{} -@compat nonearr = Array(Union{}, 5) +none = Union{} +nonearr = Array(Union{}, 5) # nothing/Void scalar_nothing = nothing -@compat vector_nothing = Union{Int,Void}[1,nothing] +vector_nothing = Union{Int,Void}[1,nothing] # some data big enough to ensure that compression is used: Abig = kron(eye(10), rand(20,20)) @@ -217,7 +223,7 @@ bitsparambool = BitsParams{true}() bitsparamsymbol = BitsParams{:x}() bitsparamint = BitsParams{1}() bitsparamuint = BitsParams{0x01}() -bitsparamint16 = BitsParams{@compat Int16(1)}() +bitsparamint16 = BitsParams{Int16(1)}() # Tuple of tuples tuple_of_tuples = (1, 2, (3, 4, [5, 6]), [7, 8]) @@ -237,7 +243,7 @@ natyperef = Any[NALikeType(), NALikeType()] iseq(x,y) = isequal(x,y) iseq(x::MyStruct, y::MyStruct) = (x.len == y.len && x.data == y.data) iseq(x::MyImmutable, y::MyImmutable) = (isequal(x.x, y.x) && isequal(x.y, y.y) && isequal(x.z, y.z)) -@compat iseq(x::Union{EmptyTI, EmptyTT}, y::Union{EmptyTI, EmptyTT}) = isequal(x.x, y.x) +iseq(x::Union{EmptyTI, EmptyTT}, y::Union{EmptyTI, EmptyTT}) = isequal(x.x, y.x) iseq(c1::Array{Base.Sys.CPUinfo}, c2::Array{Base.Sys.CPUinfo}) = length(c1) == length(c2) && all([iseq(c1[i], c2[i]) for i = 1:length(c1)]) function iseq(c1::Base.Sys.CPUinfo, c2::Base.Sys.CPUinfo) for n in fieldnames(Base.Sys.CPUinfo) @@ -248,7 +254,7 @@ function iseq(c1::Base.Sys.CPUinfo, c2::Base.Sys.CPUinfo) true end iseq(x::MyUnicodeStruct☺, y::MyUnicodeStruct☺) = (x.α == y.α && x.∂ₓα == y.∂ₓα) -@compat iseq(x::Array{Union{}}, y::Array{Union{}}) = size(x) == size(y) +iseq(x::Array{Union{}}, y::Array{Union{}}) = size(x) == size(y) macro check(fid, sym) ex = quote let tmp @@ -712,9 +718,9 @@ for compatible in (false, true), compress in (false, true) end # Issue #106 - save(fn, "i106", Mod106.typ(@compat(Int64(1)), Mod106.UnexportedT), compress=compress) + save(fn, "i106", Mod106.typ(Int64(1), Mod106.UnexportedT), compress=compress) i106 = load(fn, "i106") - @assert i106 == Mod106.typ(@compat(Int64(1)), Mod106.UnexportedT) + @assert i106 == Mod106.typ(Int64(1), Mod106.UnexportedT) # bracket syntax for datasets jldopen(fn, "w", compatible=compatible, compress=compress) do file