diff --git a/README.md b/README.md index 124c43ad4..8a578eb57 100644 --- a/README.md +++ b/README.md @@ -36,6 +36,8 @@ wherever you want to use syntax that differs in the latest Julia Currently, the `@compat` macro supports the following syntaxes: +* `@compat (a::B{T}){T}(c) = d` - the Julia 0.5-style call overload. + * `@compat Dict(foo => bar, baz => qux)` - type-inferred `Dict` construction. (Also works for `DataStructures.OrderedDict`) * `@compat Dict{Foo,Bar}(foo => bar, baz => qux)` - type-declared `Dict` construction. (Also works for `DataStructures.OrderedDict`) diff --git a/src/Compat.jl b/src/Compat.jl index 7d059b11e..379ae9c36 100644 --- a/src/Compat.jl +++ b/src/Compat.jl @@ -375,14 +375,35 @@ if VERSION < v"0.4.0-dev+3732" end end +if VERSION < v"0.5.0-dev+2396" + function new_style_call_overload(ex::Expr) + # Not a function call + ((ex.head === :(=) || ex.head === :function) && + length(ex.args) == 2 && isexpr(ex.args[1], :call)) || return false + callee = (ex.args[1]::Expr).args[1] + # Only Expr function name can be call overload + isa(callee, Expr) || return false + callee = callee::Expr + # (a::A)() = ... + callee.head === :(::) && return true + # The other case is with type parameter. + # Filter out everything without one. + (callee.head === :curly && length(callee.args) >= 1) || return false + # Check what the type parameter applies to is a Expr(:(::)) + return isexpr(callee.args[1], :(::)) + end +else + new_style_call_overload(ex::Expr) = false +end + function _compat(ex::Expr) - if ex.head == :call + if ex.head === :call f = ex.args[1] - if VERSION < v"0.4.0-dev+980" && (f == :Dict || (isexpr(f, :curly) && length(f.args) == 3 && f.args[1] == :Dict)) + if VERSION < v"0.4.0-dev+980" && (f === :Dict || (isexpr(f, :curly) && length(f.args) == 3 && f.args[1] === :Dict)) ex = rewrite_dict(ex) - elseif VERSION < v"0.4.0-dev+980" && (f == :OrderedDict || (isexpr(f, :curly) && length(f.args) == 3 && f.args[1] == :OrderedDict)) + elseif VERSION < v"0.4.0-dev+980" && (f === :OrderedDict || (isexpr(f, :curly) && length(f.args) == 3 && f.args[1] === :OrderedDict)) ex = rewrite_ordereddict(ex) - elseif VERSION < v"0.4.0-dev+129" && (f == :split || f == :rsplit) && length(ex.args) >= 4 && isexpr(ex.args[4], :kw) + elseif VERSION < v"0.4.0-dev+129" && (f === :split || f === :rsplit) && length(ex.args) >= 4 && isexpr(ex.args[4], :kw) ex = rewrite_split(ex, f) elseif VERSION < v"0.4.0-dev+3732" && haskey(calltypes, f) && length(ex.args) > 1 T = ex.args[1] @@ -391,63 +412,77 @@ function _compat(ex::Expr) else ex = Expr(:(::), Expr(:call, :convert, T, ex.args[2:end]...), T) end - elseif VERSION < v"0.4.0-dev+3732" && (f == :map && haskey(calltypes, ex.args[2]) && length(ex.args) > 2) + elseif VERSION < v"0.4.0-dev+3732" && (f === :map && haskey(calltypes, ex.args[2]) && length(ex.args) > 2) ex = Expr(:call, calltypes[ex.args[2]], ex.args[3:end]...) - elseif VERSION < v"0.4.0-dev+1419" && isexpr(f, :curly) && f.args[1] == :Ptr && length(ex.args) == 2 && ex.args[2] == 0 + elseif VERSION < v"0.4.0-dev+1419" && isexpr(f, :curly) && f.args[1] === :Ptr && length(ex.args) == 2 && ex.args[2] == 0 ex = Expr(:call, :zero, f) - elseif VERSION < v"0.4.0-dev+4356" && f == :chol + elseif VERSION < v"0.4.0-dev+4356" && f === :chol s = ex.args[3] - if isexpr(s, :curly) && s.args[1] == :Val + if isexpr(s, :curly) && s.args[1] === :Val ex = Expr(:call, :chol, ex.args[2], s.args[2]) end - elseif VERSION < v"0.4.0-dev+4739" && isexpr(f, :curly) && (f.args[1] == :Vector || f.args[1] == :Array) - if f.args[1] == :Vector && length(ex.args) == 1 + elseif VERSION < v"0.4.0-dev+4739" && isexpr(f, :curly) && (f.args[1] === :Vector || f.args[1] === :Array) + if f.args[1] === :Vector && length(ex.args) == 1 ex = Expr(:call, :Array, f.args[2], 0) else ex = Expr(:call, :Array, f.args[2], ex.args[2:end]...) end - elseif VERSION < v"0.4.0-dev+4389" && f == :withenv + elseif VERSION < v"0.4.0-dev+4389" && f === :withenv rewrite_pairs_to_tuples!(ex) end - elseif ex.head == :curly + elseif ex.head === :curly f = ex.args[1] - if VERSION < v"0.4.0-dev+4319" && f == :Tuple + if VERSION < v"0.4.0-dev+4319" && f === :Tuple args = ex.args[2:end] has_ellipsis = any(args) do arg - isa(arg, Expr) && (arg.head == :...) + isa(arg, Expr) && (arg.head === :...) end ex = if has_ellipsis Expr(:call, TopNode(:tuple), args...) else Expr(:tuple, args...) end - elseif VERSION < v"0.4.0-dev+5379" && f == :Union + elseif VERSION < v"0.4.0-dev+5379" && f === :Union ex = Expr(:call,:Union,ex.args[2:end]...) elseif ex == :(Ptr{Void}) # Do no change Ptr{Void} to Ptr{Nothing}: 0.4.0-dev+768 return ex end - elseif ex.head == :macrocall + elseif ex.head === :macrocall f = ex.args[1] - if f == symbol("@generated") && VERSION < v"0.4.0-dev+4387" + if f === symbol("@generated") && VERSION < v"0.4.0-dev+4387" f = ex.args[2] if isexpr(f, :function) ex = Expr(:stagedfunction, f.args...) end end - elseif VERSION < v"0.4.0-dev+5322" && ex.head == :(::) && isa(ex.args[end], Symbol) + elseif VERSION < v"0.4.0-dev+5322" && ex.head === :(::) && isa(ex.args[end], Symbol) # Replace Base.Timer with Compat.Timer2 in type declarations - if ex.args[end] == :Timer || ex.args[end] == :(Base.Timer) + if ex.args[end] === :Timer || ex.args[end] == :(Base.Timer) ex.args[end] = :(Compat.Timer2) end - elseif ex.head == :quote && isa(ex.args[1], Symbol) + elseif ex.head === :quote && isa(ex.args[1], Symbol) # Passthrough return ex + elseif new_style_call_overload(ex) + if ((ex.args[1]::Expr).args[1]::Expr).head === :(::) + # (:function, (:call, :(:(::), <1>), <2>), ) -> + # (:function, (:call, :(Base.call), :(:(::), <1>), <2>), ) + unshift!((ex.args[1]::Expr).args, :(Base.call)) + else + # (:function, (:call, :(curly, :(:(::), <1>), <3>), <2>), ) -> + # (:function, (:call, :(curly, :(Base.call), <3>), :(:(::), <1>), <2>), ) + callexpr = ex.args[1]::Expr + callee = callexpr.args[1]::Expr + obj = callee.args[1]::Expr + callee.args[1] = :(Base.call) + insert!(callexpr.args, 2, obj) + end end return Expr(ex.head, map(_compat, ex.args)...) end function _compat(ex::Symbol) - if VERSION < v"0.4.0-dev+768" && ex == :Void + if VERSION < v"0.4.0-dev+768" && ex === :Void return :Nothing end return ex diff --git a/test/runtests.jl b/test/runtests.jl index 58b13fb16..bf0731f0f 100644 --- a/test/runtests.jl +++ b/test/runtests.jl @@ -857,3 +857,39 @@ let foreach((args...)->push!(a,args), [2,4,6], [10,20,30]) @test a == [(2,10),(4,20),(6,30)] end + +module CallTest + +using Base.Test, Compat + +immutable A + a +end + +immutable B{T} + b::T +end + +if VERSION >= v"0.4" + @compat (::Type{A})() = A(1) + @compat (::Type{B})() = B{Int}() + @compat (::Type{B{T}}){T}() = B{T}(zero(T)) + + @compat (a::A)() = a.a + @compat (a::A)(b) = (a.a, b) + @compat (b::B{T}){T}() = b.b, T + @compat (b::B{T}){T}(c::T) = 1 + @compat (b::B{T}){T,T2}(c::T2) = 0 + + @test A() === A(1) + @test B() === B(0) + @test B{Float64}() === B(0.0) + + @test A(1)() === 1 + @test A(1)(2) === (1, 2) + @test B(0)() === (0, Int) + @test B(0)(1) === 1 + @test B(0)(1.0) === 0 +end + +end