Skip to content

Commit 145b331

Browse files
committed
lowering: Plumb through arg destructuring code into all wrapper functions
In case the default argument of some other argument makes use of the destructured argument. Fixes #53832.
1 parent cd523fe commit 145b331

File tree

3 files changed

+51
-21
lines changed

3 files changed

+51
-21
lines changed

src/jlfrontend.scm

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -22,10 +22,10 @@
2222
`(incomplete ,msg)
2323
(cons 'error (cdr e))))
2424
(begin
25-
;;(newline)
26-
;;(display "unexpected error: ")
27-
;;(prn e)
28-
;;(print-stack-trace (stacktrace))
25+
(newline)
26+
(display "unexpected error: ")
27+
(prn e)
28+
(print-stack-trace (stacktrace))
2929
'(error "malformed expression"))))
3030
thk))
3131

src/julia-syntax.scm

Lines changed: 35 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -337,7 +337,7 @@
337337

338338
;; construct the (method ...) expression for one primitive method definition,
339339
;; assuming optional and keyword args are already handled
340-
(define (method-def-expr- name sparams argl body (rett '(core Any)))
340+
(define (method-def-expr- name sparams argl argl-stmts body (rett '(core Any)))
341341
(if
342342
(any kwarg? argl)
343343
;; has optional positional args
@@ -356,7 +356,7 @@
356356
(dfl (map caddr kws)))
357357
(receive
358358
(vararg req) (separate vararg? argl)
359-
(optional-positional-defs name sparams req opt dfl body
359+
(optional-positional-defs name sparams req opt dfl argl-stmts body
360360
(append req opt vararg) rett)))))
361361
;; no optional positional args
362362
(let* ((names (map car sparams))
@@ -442,7 +442,7 @@
442442
,@(map make-assignment names vals)
443443
,expr))
444444

445-
(define (keywords-method-def-expr name sparams argl body rett)
445+
(define (keywords-method-def-expr name sparams argl argl-stmts body rett)
446446
(let* ((kargl (cdar argl)) ;; keyword expressions (= k v)
447447
(annotations (map (lambda (a) `(meta ,(cadr a) ,(arg-name (cadr (caddr a)))))
448448
(filter nospecialize-meta? kargl)))
@@ -530,6 +530,7 @@
530530
;; strip type off function self argument if not needed for a static param.
531531
;; then it is ok for cl-convert to move this definition above the original def.
532532
,@not-optional ,@vararg)
533+
argl-stmts
533534
(insert-after-meta `(block
534535
,@stmts)
535536
(cons `(meta nkw ,(+ (length vars) (length restkw)))
@@ -538,9 +539,10 @@
538539

539540
;; call with no keyword args
540541
,(method-def-expr-
541-
name positional-sparams pargl-all
542+
name positional-sparams pargl-all argl-stmts
542543
`(block
543544
,@(keep-first linenum? (without-generated prologue))
545+
,@(filter identity argl-stmts)
544546
,(let (;; call mangled(vals..., [rest_kw,] pargs..., [vararg]...)
545547
(ret `(return (call ,mangled
546548
,@(if ordered-defaults keynames vals)
@@ -558,6 +560,7 @@
558560
;; if there are optional positional args, we need to be able to reference the function name
559561
,(if (any kwarg? `(,@pargl ,@vararg)) (gensy) UNUSED)
560562
(call (core kwftype) ,ftype)) ,kwdecl ,@pargl ,@vararg)
563+
argl-stmts
561564
`(block
562565
;; propagate method metadata to keyword sorter
563566
,@(map propagate-method-meta (filter meta? prologue))
@@ -571,6 +574,7 @@
571574
`(meta ,(cadr m) ,@(filter (lambda (v) (not (memq v keynames)))
572575
(cddr m))))
573576
(filter nospecialize-meta? prologue))
577+
,@(filter identity argl-stmts)
574578
;; If not using slots for the keyword argument values, still declare them
575579
;; for reflection purposes.
576580
,@(if ssa-keyvars?
@@ -655,15 +659,25 @@
655659
(else
656660
(loop filtered (cdr params))))))
657661

658-
(define (optional-positional-defs name sparams req opt dfl body overall-argl rett)
662+
;; Get the list of all symbols introduced either by the `args` or by any
663+
;; destructuring expression for that arg in `argl-stmts`.
664+
(define (arg-introduced-syms args argl-stmts)
665+
(apply append! (map
666+
(lambda (arg stmt)
667+
(if stmt (find-assigned-vars stmt) (list arg)))
668+
args argl-stmts)))
669+
670+
(define (optional-positional-defs name sparams req opt dfl argl-stmts body overall-argl rett)
659671
(let ((prologue (without-generated (extract-method-prologue body))))
660672
`(block
661673
,@(map (lambda (n)
662674
(let* ((passed (append req (list-head opt n)))
663675
;; only keep static parameters used by these arguments
664-
(sp (filter-sparams (cons 'list passed) sparams))
665-
(vals (list-tail dfl n))
666-
(absent (list-tail opt n)) ;; absent arguments
676+
(sp (filter-sparams (cons 'list passed) sparams))
677+
(vals (list-tail dfl n))
678+
(absent (list-tail opt n)) ;; absent arguments
679+
(absent-syms (arg-introduced-syms absent (list-tail argl-stmts n)))
680+
(passed-stmts (list-head argl-stmts (+ (length req) n)))
667681
(body
668682
(if (any vararg? (butlast vals))
669683
;; Forbid splat in all but the final default value
@@ -674,23 +688,27 @@
674688
;; contain "e" such that...
675689
(any (lambda (a)
676690
;; "e" is in an absent arg
691+
;; (or other symbol introduced
692+
;; by destructuring an absent arg)
677693
(contains (lambda (u)
678694
(eq? u e))
679695
a))
680-
absent))
696+
absent-syms))
681697
defaultv))
682698
vals)
683699
;; then add only one next argument
684700
`(block
685701
,@prologue
702+
,@(filter identity passed-stmts)
686703
(call ,(arg-name (car req)) ,@(map arg-name (cdr passed)) ,(car vals)))
687704
;; otherwise add all
688705
`(block
689706
,@prologue
707+
,@(filter identity passed-stmts)
690708
(call ,(arg-name (car req)) ,@(map arg-name (cdr passed)) ,@vals))))))
691-
(method-def-expr- name sp passed body)))
709+
(method-def-expr- name sp passed argl-stmts body)))
692710
(iota (length opt)))
693-
,(method-def-expr- name sparams overall-argl body rett))))
711+
,(method-def-expr- name sparams overall-argl argl-stmts body rett))))
694712

695713
;; strip empty (parameters ...), normalizing `f(x;)` to `f(x)`.
696714
(define (remove-empty-parameters argl)
@@ -729,14 +747,14 @@
729747
;; definitions without keyword arguments are passed to method-def-expr-,
730748
;; which handles optional positional arguments by adding the needed small
731749
;; boilerplate definitions.
732-
(define (method-def-expr name sparams argl body rett)
750+
(define (method-def-expr name sparams argl argl-stmts body rett)
733751
(let ((argl (throw-unassigned-kw-args (remove-empty-parameters argl))))
734752
(if (has-parameters? argl)
735753
;; has keywords
736754
(begin (check-kw-args (cdar argl))
737-
(keywords-method-def-expr name sparams argl body rett))
755+
(keywords-method-def-expr name sparams argl argl-stmts body rett))
738756
;; no keywords
739-
(method-def-expr- name sparams argl body rett))))
757+
(method-def-expr- name sparams argl argl-stmts body rett))))
740758

741759
(define (struct-def-expr name params super fields mut)
742760
(receive
@@ -1184,7 +1202,7 @@
11841202
(reverse (cons '(null) stmts))))
11851203
(let ((a (transform-arg (car argl))))
11861204
(loop (cdr argl) (cons (car a) newa)
1187-
(if (cdr a) (cons (cdr a) stmts) stmts))))))
1205+
(cons (cdr a) stmts))))))
11881206

11891207
(define (expand-function-def- e)
11901208
(let* ((name (cadr e))
@@ -1226,7 +1244,7 @@
12261244
(farg (if (decl? argname)
12271245
(adj-decl argname)
12281246
`(|::| |#self#| (call (core Typeof) ,argname))))
1229-
(body (insert-after-meta body (cdr argl-stmts)))
1247+
(body (insert-after-meta body (filter identity (cdr argl-stmts))))
12301248
(argl (cdr argl))
12311249
(argl (fix-arglist
12321250
(arglist-unshift argl farg)
@@ -1236,7 +1254,7 @@
12361254
(name (if (or (decl? name) (and (pair? name) (memq (car name) '(curly where))))
12371255
#f name)))
12381256
(expand-forms
1239-
(method-def-expr name sparams argl body rett))))
1257+
(method-def-expr name sparams argl (cdr argl-stmts) body rett))))
12401258
(else
12411259
(error (string "invalid assignment location \"" (deparse name) "\""))))))
12421260

test/syntax.jl

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3683,3 +3683,15 @@ end
36833683
# Issue #53729 - Lowering recursion into Expr(:toplevel)
36843684
@test eval(Expr(:let, Expr(:block), Expr(:block, Expr(:toplevel, :(f53729(x) = x)), :(x=1)))) == 1
36853685
@test f53729(2) == 2
3686+
3687+
# Issue #53832 - interaction of arg destructuring and wrappers
3688+
f53832((a, b), c=a) = (a, b, c)
3689+
@test f53832((1,2)) == (1,2,1)
3690+
3691+
g53832((a, b); c=a, d=b) = (a, b, c, b)
3692+
@test g53832((1,2)) == (1,2,1,2)
3693+
@test g53832((1,2); c=3) == (1,2,3,2)
3694+
3695+
h53832((a, b), (c, d)=(a+10, b+100), (e, f)=(c+1000, d+10000)) = (a, b, c, d, e, f)
3696+
@test h53832((1,2)) == (1, 2, 11, 102, 1011, 10102)
3697+
@test h53832((1,2), (3, 4)) == (1, 2, 3, 4, 1003, 10004)

0 commit comments

Comments
 (0)