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

Implement at-compat for new style call overloading #181

Merged
merged 2 commits into from
Mar 30, 2016
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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`)
Expand Down
77 changes: 56 additions & 21 deletions src/Compat.jl
Original file line number Diff line number Diff line change
Expand Up @@ -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]
Expand All @@ -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>), <body>) ->
# (:function, (:call, :(Base.call), :(:(::), <1>), <2>), <body>)
unshift!((ex.args[1]::Expr).args, :(Base.call))
else
# (:function, (:call, :(curly, :(:(::), <1>), <3>), <2>), <body>) ->
# (:function, (:call, :(curly, :(Base.call), <3>), :(:(::), <1>), <2>), <body>)
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
Expand Down
36 changes: 36 additions & 0 deletions test/runtests.jl
Original file line number Diff line number Diff line change
Expand Up @@ -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