Skip to content

Commit

Permalink
Merge pull request #181 from JuliaLang/yyc/call
Browse files Browse the repository at this point in the history
Implement at-compat for new style call overloading
  • Loading branch information
yuyichao committed Mar 30, 2016
2 parents 5b10f24 + fa91a2e commit 3046835
Show file tree
Hide file tree
Showing 3 changed files with 94 additions and 21 deletions.
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

0 comments on commit 3046835

Please sign in to comment.