Skip to content

Commit

Permalink
Support @opaque Tuple{T,U...}->RT (...)->... syntax for explicit ar…
Browse files Browse the repository at this point in the history
…g/return types (#54947)

This gives users a way to explicitly specify the return type of an
OpaqueClosure, and it also removes the old syntax `@opaque AT ...` in
preference of `@opaque AT->_ ...`
  • Loading branch information
topolarity authored Jul 4, 2024
1 parent 8f1f223 commit a5f0016
Show file tree
Hide file tree
Showing 5 changed files with 58 additions and 25 deletions.
16 changes: 14 additions & 2 deletions base/opaque_closure.jl
Original file line number Diff line number Diff line change
Expand Up @@ -18,11 +18,23 @@ the argument type may be fixed length even if the function is variadic.
This interface is experimental and subject to change or removal without notice.
"""
macro opaque(ex)
esc(Expr(:opaque_closure, ex))
esc(Expr(:opaque_closure, nothing, nothing, nothing, ex))
end

macro opaque(ty, ex)
esc(Expr(:opaque_closure, ty, ex))
if Base.isexpr(ty, :->)
(AT, body) = ty.args
filter!((n)->!isa(n, Core.LineNumberNode), body.args)
if !Base.isexpr(body, :block) || length(body.args) != 1
error("Opaque closure type must be specified in the form Tuple{T,U...}->RT")
end
RT = only(body.args)
else
error("Opaque closure type must be specified in the form Tuple{T,U...}->RT")
end
AT = (AT !== :_) ? AT : nothing
RT = (RT !== :_) ? RT : nothing
return esc(Expr(:opaque_closure, AT, RT, RT, ex))
end

# OpaqueClosure construction from pre-inferred CodeInfo/IRCode
Expand Down
2 changes: 1 addition & 1 deletion base/show.jl
Original file line number Diff line number Diff line change
Expand Up @@ -3335,8 +3335,8 @@ bitstring(B::BitArray) = sprint(bitshow, B)
function show(io::IO, oc::Core.OpaqueClosure)
A, R = typeof(oc).parameters
show_tuple_as_call(io, Symbol(""), A; hasfirst=false)
print(io, "::", R)
print(io, "->◌")
print(io, "::", R)
end

function show(io::IO, ::MIME"text/plain", oc::Core.OpaqueClosure{A, R}) where {A, R}
Expand Down
32 changes: 23 additions & 9 deletions src/julia-syntax.scm
Original file line number Diff line number Diff line change
Expand Up @@ -2403,6 +2403,18 @@
(cons (car e)
(map expand-forms (cdr e)))))))

(define (find pred e)
(let loop ((xs e))
(if (null? xs)
#f
(let ((elt (car xs)))
(if (pred elt)
elt
(loop (cdr xs)))))))

(define (something e)
(find (lambda (x) (not (equal? x '(null)))) e))

;; table mapping expression head to a function expanding that form
(define expand-table
(table
Expand All @@ -2422,13 +2434,15 @@

'opaque_closure
(lambda (e)
(let* ((ty (and (length> e 2) (expand-forms (cadr e))))
(F (if (length> e 2) (caddr e) (cadr e)))
(let* ((argt (something (list (expand-forms (cadr e)) #f)))
(rt_lb (something (list (expand-forms (caddr e)) #f)))
(rt_ub (something (list (expand-forms (cadddr e)) #f)))
(F (caddddr e))
(isva (let* ((arglist (function-arglist F))
(lastarg (and (pair? arglist) (last arglist))))
(if (and ty (any (lambda (arg)
(if (and argt (any (lambda (arg)
(let ((arg (if (vararg? arg) (cadr arg) arg)))
(not (equal? (arg-type arg) '(core Any)))))
(not (symbol? arg))))
arglist))
(error "Opaque closure argument type may not be specified both in the method signature and separately"))
(if (or (varargexpr? lastarg) (vararg? lastarg))
Expand All @@ -2448,7 +2462,7 @@
(let* ((argtype (foldl (lambda (var ex) `(call (core UnionAll) ,var ,ex))
(expand-forms `(curly (core Tuple) ,@argtypes))
(reverse tvars))))
`(_opaque_closure ,(or ty argtype) ,isva ,(length argtypes) ,functionloc ,lam))))
`(_opaque_closure ,(or argt argtype) ,rt_lb ,rt_ub ,isva ,(length argtypes) ,functionloc ,lam))))

'block
(lambda (e)
Expand Down Expand Up @@ -4014,9 +4028,9 @@ f(x) = yt(x)
e))
(else e))))
((_opaque_closure)
(let* ((isva (caddr e))
(nargs (cadddr e))
(functionloc (caddddr e))
(let* ((isva (car (cddddr e)))
(nargs (cadr (cddddr e)))
(functionloc (caddr (cddddr e)))
(lam2 (last e))
(vis (lam:vinfo lam2))
(cvs (map car (cadr vis))))
Expand All @@ -4028,7 +4042,7 @@ f(x) = yt(x)
v)))
cvs)))
`(new_opaque_closure
,(cadr e) (call (core apply_type) (core Union)) (core Any) (true)
,(cadr e) ,(or (caddr e) '(call (core apply_type) (core Union))) ,(or (cadddr e) '(core Any)) (true)
(opaque_closure_method (null) ,nargs ,isva ,functionloc ,(convert-lambda lam2 (car (lam:args lam2)) #f '() (symbol-to-idx-map cvs)))
,@var-exprs))))
((method)
Expand Down
2 changes: 1 addition & 1 deletion stdlib/InteractiveUtils/test/runtests.jl
Original file line number Diff line number Diff line change
Expand Up @@ -139,7 +139,7 @@ tag = "ANY"
@test !warntype_hastag(ImportIntrinsics15819.sqrt15819, Tuple{Float32}, tag)

@testset "code_warntype OpaqueClosure" begin
g = Base.Experimental.@opaque Tuple{Float64} x -> 0.0
g = Base.Experimental.@opaque Tuple{Float64}->_ x -> 0.0
@test warntype_hastag(g, Tuple{Float64}, "::Float64")
end

Expand Down
31 changes: 19 additions & 12 deletions test/opaque_closure.jl
Original file line number Diff line number Diff line change
Expand Up @@ -151,34 +151,41 @@ end # module test_world_age

function maybe_vararg(isva::Bool)
T = isva ? Vararg{Int} : Int
@opaque Tuple{T} (x...)->x
@opaque Tuple{T}->_ (x...)->x
end
@test maybe_vararg(false)(1) == (1,)
@test_throws MethodError maybe_vararg(false)(1,2,3)
@test maybe_vararg(true)(1) == (1,)
@test maybe_vararg(true)(1,2,3) == (1,2,3)
@test (@opaque Tuple{Int, Int} (a, b, x...)->x)(1,2) === ()
@test (@opaque Tuple{Int, Int} (a, x...)->x)(1,2) === (2,)
@test (@opaque Tuple{Int, Vararg{Int}} (a, x...)->x)(1,2,3,4) === (2,3,4)
@test (@opaque Tuple{Int, Int}->_ (a, b, x...)->x)(1,2) === ()
@test (@opaque Tuple{Int, Int}->Tuple{} (a, b, x...)->x)(1,2) === ()
@test (@opaque _->Tuple{Vararg{Int}} (a, b, x...)->x)(1,2) === ()
@test (@opaque Tuple{Int, Int}->_ (a, x...)->x)(1,2) === (2,)
@test (@opaque Tuple{Int, Int}->Tuple{Int} (a, x...)->x)(1,2) === (2,)
@test (@opaque _->Tuple{Vararg{Int}} (a, x...)->x)(1,2) === (2,)
@test (@opaque Tuple{Int, Vararg{Int}}->_ (a, x...)->x)(1,2,3,4) === (2,3,4)
@test (@opaque Tuple{Int, Vararg{Int}}->Tuple{Vararg{Int}} (a, x...)->x)(1,2,3,4) === (2,3,4)
@test (@opaque (a::Int, x::Int...)->x)(1,2,3) === (2,3)
@test (@opaque _->Tuple{Vararg{Int}} (a::Int, x::Int...)->x)(1,2,3) === (2,3)
@test (@opaque _->_ (a::Int, x::Int...)->x)(1,2,3) === (2,3)

@test_throws ErrorException (@opaque Tuple{Vararg{Int}} x->x)
@test_throws ErrorException (@opaque Tuple{Int, Vararg{Int}} x->x)
@test_throws ErrorException (@opaque Tuple{Int, Int} x->x)
@test_throws ErrorException (@opaque Tuple{Any} (x,y)->x)
@test_throws ErrorException (@opaque Tuple{Vararg{Int}} (x,y...)->x)
@test_throws ErrorException (@opaque Tuple{Int} (x,y,z...)->x)
@test_throws ErrorException (@opaque Tuple{Vararg{Int}}->_ x->x)
@test_throws ErrorException (@opaque Tuple{Int, Vararg{Int}}->_ x->x)
@test_throws ErrorException (@opaque Tuple{Int, Int}->_ x->x)
@test_throws ErrorException (@opaque Tuple{Any}->_ (x,y)->x)
@test_throws ErrorException (@opaque Tuple{Vararg{Int}}->_ (x,y...)->x)
@test_throws ErrorException (@opaque Tuple{Int}->_ (x,y,z...)->x)

# cannot specify types both on arguments and separately
@test_throws ErrorException @eval @opaque Tuple{Any} (x::Int)->x
@test_throws ErrorException @eval @opaque Tuple{Any}->_ (x::Int)->x

# Vargarg in complied mode
mk_va_opaque() = @opaque (x...)->x
@test mk_va_opaque()(1) == (1,)
@test mk_va_opaque()(1,2) == (1,2)

# OpaqueClosure show method
@test repr(@opaque x->Base.inferencebarrier(1)) == "(::Any)::Any->◌"
@test repr(@opaque x->Base.inferencebarrier(1)) == "(::Any)->◌::Any"

# Opaque closure in CodeInfo returned from generated functions
let ci = @code_lowered const_int()
Expand Down

0 comments on commit a5f0016

Please sign in to comment.