Skip to content

Commit

Permalink
Use Tuple{T,U...}->RT syntax for explicit OpaqueClosure type
Browse files Browse the repository at this point in the history
This provides a means to explicitly specify the return type of an
OpaqueClosure, which previously was not possible without calling
`jl_new_opaque_closure` directly.

As a result, `@opaque AT (...)->...` is no longer an allowed form
to define an OpaqueClosure. Instead `@opaque AT->_ (...)->...`
should be used.
  • Loading branch information
topolarity committed Jun 26, 2024
1 parent 9fecc19 commit 3928aa1
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 (not (pred elt))
(loop (cdr xs))
elt)))))

(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 3928aa1

Please sign in to comment.