Skip to content

Commit 52e14cc

Browse files
authored
Don't create a type parameter in the closure for captured @nospecialize arguments (#58426)
When we capture a variable without boxing, we always generate a type parameter for the closure. This probably isn't what the user wants if the captured variable is an argument marked `@nospecialize`. Before: ``` julia> K(@nospecialize(x)) = @nospecialize(y) -> x K (generic function with 1 method) julia> f, g = K(1), K("a") (var"#K##0#K##1"{Int64}(1), var"#K##0#K##1"{String}("a")) julia> f(2), g(2) (1, "a") julia> methods(f)[1].specializations svec(MethodInstance for (::var"#K##0#K##1"{Int64})(::Any), MethodInstance for (::var"#K##0#K##1"{String})(::Any), nothing, nothing, nothing, nothing, nothing) julia> fieldtypes(typeof(f)), fieldtypes(typeof(g)) ((Int64,), (String,)) ``` After: ``` julia> K(@nospecialize(x)) = @nospecialize(y) -> x K (generic function with 1 method) julia> f, g = K(1), K("a") (var"#K##0#K##1"(1), var"#K##0#K##1"("a")) julia> f(2), g(2) (1, "a") julia> methods(f)[1].specializations MethodInstance for (::var"#K##0#K##1")(::Any) julia> fieldtypes(typeof(f)), fieldtypes(typeof(g)) ((Any,), (Any,)) ```
1 parent 34070fa commit 52e14cc

File tree

3 files changed

+29
-26
lines changed

3 files changed

+29
-26
lines changed

src/ast.scm

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -493,6 +493,7 @@
493493
(define (vinfo:never-undef v) (< 0 (logand (caddr v) 4)))
494494
(define (vinfo:read v) (< 0 (logand (caddr v) 8)))
495495
(define (vinfo:sa v) (< 0 (logand (caddr v) 16)))
496+
(define (vinfo:nospecialize v) (< 0 (logand (caddr v) 128)))
496497
(define (set-bit x b val) (if val (logior x b) (logand x (lognot b))))
497498
;; record whether var is captured
498499
(define (vinfo:set-capt! v c) (set-car! (cddr v) (set-bit (caddr v) 1 c)))
@@ -507,6 +508,7 @@
507508
;; occurs undef: mask 32
508509
;; whether var is called (occurs in function call head position)
509510
(define (vinfo:set-called! v a) (set-car! (cddr v) (set-bit (caddr v) 64 a)))
511+
(define (vinfo:set-nospecialize! v c) (set-car! (cddr v) (set-bit (caddr v) 128 c)))
510512

511513
(define var-info-for assq)
512514

src/julia-syntax.scm

Lines changed: 18 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -3492,10 +3492,15 @@
34923492
(define (analyze-vars e env captvars sp tab)
34933493
(if (or (atom? e) (quoted? e))
34943494
(begin
3495-
(if (symbol? e)
3496-
(let ((vi (get tab e #f)))
3497-
(if vi
3498-
(vinfo:set-read! vi #t))))
3495+
(cond
3496+
((symbol? e)
3497+
(let ((vi (get tab e #f)))
3498+
(if vi
3499+
(vinfo:set-read! vi #t))))
3500+
((nospecialize-meta? e)
3501+
(let ((vi (get tab (caddr e) #f)))
3502+
(if vi
3503+
(vinfo:set-nospecialize! vi #t)))))
34993504
e)
35003505
(case (car e)
35013506
((local-def) ;; a local that we know has an assignment that dominates all usages
@@ -3594,21 +3599,6 @@ f(x) = yt(x)
35943599
(call (core _typebody!) (false) ,s (call (core svec) ,@types))
35953600
(return (null)))))))))
35963601

3597-
(define (type-for-closure name fields super)
3598-
(let ((s (make-ssavalue)))
3599-
`((thunk ,(linearize `(lambda ()
3600-
(() () 0 ())
3601-
(block (global ,name)
3602-
(= ,s (call (core _structtype) (thismodule) (inert ,name) (call (core svec))
3603-
(call (core svec) ,@(map quotify fields))
3604-
(call (core svec))
3605-
(false) ,(length fields)))
3606-
(call (core _setsuper!) ,s ,super)
3607-
(const (globalref (thismodule) ,name) ,s)
3608-
(call (core _typebody!) (false) ,s
3609-
(call (core svec) ,@(map (lambda (v) '(core Box)) fields)))
3610-
(return (null)))))))))
3611-
36123602
;; better versions of above, but they get handled wrong in many places
36133603
;; need to fix that in order to handle #265 fully (and use the definitions)
36143604

@@ -4022,6 +4012,10 @@ f(x) = yt(x)
40224012
(let ((cv (assq v (cadr (lam:vinfo lam)))))
40234013
(and cv (vinfo:asgn cv) (vinfo:capt cv)))))
40244014

4015+
(define (is-var-nospecialize? v lam)
4016+
(let ((vi (assq v (car (lam:vinfo lam)))))
4017+
(and vi (vinfo:nospecialize vi))))
4018+
40254019
(define (toplevel-preserving? e)
40264020
(and (pair? e) (memq (car e) '(if elseif block trycatch tryfinally trycatchelse))))
40274021

@@ -4313,16 +4307,14 @@ f(x) = yt(x)
43134307
(closure-param-syms (map (lambda (s) (make-ssavalue)) closure-param-names))
43144308
(typedef ;; expression to define the type
43154309
(let* ((fieldtypes (map (lambda (v)
4316-
(if (is-var-boxed? v lam)
4317-
'(core Box)
4318-
(make-ssavalue)))
4310+
(cond ((is-var-boxed? v lam) '(core Box))
4311+
((is-var-nospecialize? v lam) (vinfo:type (assq v (car (lam:vinfo lam)))))
4312+
(else (make-ssavalue))))
43194313
capt-vars))
43204314
(para (append closure-param-syms
43214315
(filter ssavalue? fieldtypes)))
43224316
(fieldnames (append closure-param-names (filter (lambda (v) (not (is-var-boxed? v lam))) capt-vars))))
4323-
(if (null? para)
4324-
(type-for-closure type-name capt-vars '(core Function))
4325-
(type-for-closure-parameterized type-name para fieldnames capt-vars fieldtypes '(core Function)))))
4317+
(type-for-closure-parameterized type-name para fieldnames capt-vars fieldtypes '(core Function))))
43264318
(mk-method ;; expression to make the method
43274319
(if short '()
43284320
(let* ((iskw ;; TODO jb/functions need more robust version of this
@@ -4352,7 +4344,7 @@ f(x) = yt(x)
43524344
(P (append
43534345
closure-param-names
43544346
(filter identity (map (lambda (v ve)
4355-
(if (is-var-boxed? v lam)
4347+
(if (or (is-var-boxed? v lam) (is-var-nospecialize? v lam))
43564348
#f
43574349
`(call (core _typeof_captured_variable) ,ve)))
43584350
capt-vars var-exprs)))))

test/syntax.jl

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4330,3 +4330,12 @@ let ex = @Meta.lower function return_my_method(); 1; end
43304330
code[end] = Core.ReturnNode(Core.SSAValue(idx))
43314331
@test isa(Core.eval(@__MODULE__, ex), Method)
43324332
end
4333+
4334+
# Capturing a @nospecialize argument should result in an Any field in the closure
4335+
module NoSpecClosure
4336+
K(@nospecialize(x)) = y -> x
4337+
end
4338+
let f = NoSpecClosure.K(1)
4339+
@test f(2) == 1
4340+
@test typeof(f).parameters == Core.svec()
4341+
end

0 commit comments

Comments
 (0)