Skip to content

Commit a5f0016

Browse files
authored
Support @opaque Tuple{T,U...}->RT (...)->... syntax for explicit arg/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->_ ...`
1 parent 8f1f223 commit a5f0016

File tree

5 files changed

+58
-25
lines changed

5 files changed

+58
-25
lines changed

base/opaque_closure.jl

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,11 +18,23 @@ the argument type may be fixed length even if the function is variadic.
1818
This interface is experimental and subject to change or removal without notice.
1919
"""
2020
macro opaque(ex)
21-
esc(Expr(:opaque_closure, ex))
21+
esc(Expr(:opaque_closure, nothing, nothing, nothing, ex))
2222
end
2323

2424
macro opaque(ty, ex)
25-
esc(Expr(:opaque_closure, ty, ex))
25+
if Base.isexpr(ty, :->)
26+
(AT, body) = ty.args
27+
filter!((n)->!isa(n, Core.LineNumberNode), body.args)
28+
if !Base.isexpr(body, :block) || length(body.args) != 1
29+
error("Opaque closure type must be specified in the form Tuple{T,U...}->RT")
30+
end
31+
RT = only(body.args)
32+
else
33+
error("Opaque closure type must be specified in the form Tuple{T,U...}->RT")
34+
end
35+
AT = (AT !== :_) ? AT : nothing
36+
RT = (RT !== :_) ? RT : nothing
37+
return esc(Expr(:opaque_closure, AT, RT, RT, ex))
2638
end
2739

2840
# OpaqueClosure construction from pre-inferred CodeInfo/IRCode

base/show.jl

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3335,8 +3335,8 @@ bitstring(B::BitArray) = sprint(bitshow, B)
33353335
function show(io::IO, oc::Core.OpaqueClosure)
33363336
A, R = typeof(oc).parameters
33373337
show_tuple_as_call(io, Symbol(""), A; hasfirst=false)
3338-
print(io, "::", R)
33393338
print(io, "->◌")
3339+
print(io, "::", R)
33403340
end
33413341

33423342
function show(io::IO, ::MIME"text/plain", oc::Core.OpaqueClosure{A, R}) where {A, R}

src/julia-syntax.scm

Lines changed: 23 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -2403,6 +2403,18 @@
24032403
(cons (car e)
24042404
(map expand-forms (cdr e)))))))
24052405

2406+
(define (find pred e)
2407+
(let loop ((xs e))
2408+
(if (null? xs)
2409+
#f
2410+
(let ((elt (car xs)))
2411+
(if (pred elt)
2412+
elt
2413+
(loop (cdr xs)))))))
2414+
2415+
(define (something e)
2416+
(find (lambda (x) (not (equal? x '(null)))) e))
2417+
24062418
;; table mapping expression head to a function expanding that form
24072419
(define expand-table
24082420
(table
@@ -2422,13 +2434,15 @@
24222434

24232435
'opaque_closure
24242436
(lambda (e)
2425-
(let* ((ty (and (length> e 2) (expand-forms (cadr e))))
2426-
(F (if (length> e 2) (caddr e) (cadr e)))
2437+
(let* ((argt (something (list (expand-forms (cadr e)) #f)))
2438+
(rt_lb (something (list (expand-forms (caddr e)) #f)))
2439+
(rt_ub (something (list (expand-forms (cadddr e)) #f)))
2440+
(F (caddddr e))
24272441
(isva (let* ((arglist (function-arglist F))
24282442
(lastarg (and (pair? arglist) (last arglist))))
2429-
(if (and ty (any (lambda (arg)
2443+
(if (and argt (any (lambda (arg)
24302444
(let ((arg (if (vararg? arg) (cadr arg) arg)))
2431-
(not (equal? (arg-type arg) '(core Any)))))
2445+
(not (symbol? arg))))
24322446
arglist))
24332447
(error "Opaque closure argument type may not be specified both in the method signature and separately"))
24342448
(if (or (varargexpr? lastarg) (vararg? lastarg))
@@ -2448,7 +2462,7 @@
24482462
(let* ((argtype (foldl (lambda (var ex) `(call (core UnionAll) ,var ,ex))
24492463
(expand-forms `(curly (core Tuple) ,@argtypes))
24502464
(reverse tvars))))
2451-
`(_opaque_closure ,(or ty argtype) ,isva ,(length argtypes) ,functionloc ,lam))))
2465+
`(_opaque_closure ,(or argt argtype) ,rt_lb ,rt_ub ,isva ,(length argtypes) ,functionloc ,lam))))
24522466

24532467
'block
24542468
(lambda (e)
@@ -4014,9 +4028,9 @@ f(x) = yt(x)
40144028
e))
40154029
(else e))))
40164030
((_opaque_closure)
4017-
(let* ((isva (caddr e))
4018-
(nargs (cadddr e))
4019-
(functionloc (caddddr e))
4031+
(let* ((isva (car (cddddr e)))
4032+
(nargs (cadr (cddddr e)))
4033+
(functionloc (caddr (cddddr e)))
40204034
(lam2 (last e))
40214035
(vis (lam:vinfo lam2))
40224036
(cvs (map car (cadr vis))))
@@ -4028,7 +4042,7 @@ f(x) = yt(x)
40284042
v)))
40294043
cvs)))
40304044
`(new_opaque_closure
4031-
,(cadr e) (call (core apply_type) (core Union)) (core Any) (true)
4045+
,(cadr e) ,(or (caddr e) '(call (core apply_type) (core Union))) ,(or (cadddr e) '(core Any)) (true)
40324046
(opaque_closure_method (null) ,nargs ,isva ,functionloc ,(convert-lambda lam2 (car (lam:args lam2)) #f '() (symbol-to-idx-map cvs)))
40334047
,@var-exprs))))
40344048
((method)

stdlib/InteractiveUtils/test/runtests.jl

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -139,7 +139,7 @@ tag = "ANY"
139139
@test !warntype_hastag(ImportIntrinsics15819.sqrt15819, Tuple{Float32}, tag)
140140

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

test/opaque_closure.jl

Lines changed: 19 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -151,34 +151,41 @@ end # module test_world_age
151151

152152
function maybe_vararg(isva::Bool)
153153
T = isva ? Vararg{Int} : Int
154-
@opaque Tuple{T} (x...)->x
154+
@opaque Tuple{T}->_ (x...)->x
155155
end
156156
@test maybe_vararg(false)(1) == (1,)
157157
@test_throws MethodError maybe_vararg(false)(1,2,3)
158158
@test maybe_vararg(true)(1) == (1,)
159159
@test maybe_vararg(true)(1,2,3) == (1,2,3)
160-
@test (@opaque Tuple{Int, Int} (a, b, x...)->x)(1,2) === ()
161-
@test (@opaque Tuple{Int, Int} (a, x...)->x)(1,2) === (2,)
162-
@test (@opaque Tuple{Int, Vararg{Int}} (a, x...)->x)(1,2,3,4) === (2,3,4)
160+
@test (@opaque Tuple{Int, Int}->_ (a, b, x...)->x)(1,2) === ()
161+
@test (@opaque Tuple{Int, Int}->Tuple{} (a, b, x...)->x)(1,2) === ()
162+
@test (@opaque _->Tuple{Vararg{Int}} (a, b, x...)->x)(1,2) === ()
163+
@test (@opaque Tuple{Int, Int}->_ (a, x...)->x)(1,2) === (2,)
164+
@test (@opaque Tuple{Int, Int}->Tuple{Int} (a, x...)->x)(1,2) === (2,)
165+
@test (@opaque _->Tuple{Vararg{Int}} (a, x...)->x)(1,2) === (2,)
166+
@test (@opaque Tuple{Int, Vararg{Int}}->_ (a, x...)->x)(1,2,3,4) === (2,3,4)
167+
@test (@opaque Tuple{Int, Vararg{Int}}->Tuple{Vararg{Int}} (a, x...)->x)(1,2,3,4) === (2,3,4)
163168
@test (@opaque (a::Int, x::Int...)->x)(1,2,3) === (2,3)
169+
@test (@opaque _->Tuple{Vararg{Int}} (a::Int, x::Int...)->x)(1,2,3) === (2,3)
170+
@test (@opaque _->_ (a::Int, x::Int...)->x)(1,2,3) === (2,3)
164171

165-
@test_throws ErrorException (@opaque Tuple{Vararg{Int}} x->x)
166-
@test_throws ErrorException (@opaque Tuple{Int, Vararg{Int}} x->x)
167-
@test_throws ErrorException (@opaque Tuple{Int, Int} x->x)
168-
@test_throws ErrorException (@opaque Tuple{Any} (x,y)->x)
169-
@test_throws ErrorException (@opaque Tuple{Vararg{Int}} (x,y...)->x)
170-
@test_throws ErrorException (@opaque Tuple{Int} (x,y,z...)->x)
172+
@test_throws ErrorException (@opaque Tuple{Vararg{Int}}->_ x->x)
173+
@test_throws ErrorException (@opaque Tuple{Int, Vararg{Int}}->_ x->x)
174+
@test_throws ErrorException (@opaque Tuple{Int, Int}->_ x->x)
175+
@test_throws ErrorException (@opaque Tuple{Any}->_ (x,y)->x)
176+
@test_throws ErrorException (@opaque Tuple{Vararg{Int}}->_ (x,y...)->x)
177+
@test_throws ErrorException (@opaque Tuple{Int}->_ (x,y,z...)->x)
171178

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

175182
# Vargarg in complied mode
176183
mk_va_opaque() = @opaque (x...)->x
177184
@test mk_va_opaque()(1) == (1,)
178185
@test mk_va_opaque()(1,2) == (1,2)
179186

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

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

0 commit comments

Comments
 (0)