1429
1429
(else
1430
1430
(error " invalid \" try\" form" )))))
1431
1431
1432
- (define (expand-unionall-def name type-ex (allow-local #t ))
1432
+ (define (expand-unionall-def name type-ex (const? #t ))
1433
1433
(if (and (pair? name)
1434
1434
(eq? (car name) 'curly ))
1435
1435
(let ((name (cadr name))
1440
1440
(expand-forms
1441
1441
`(block
1442
1442
(= ,rr (where ,type-ex ,@params))
1443
- (,(if allow-local ' assign-const-if-global 'const ) ,name ,rr)
1443
+ (,(if const? 'const ' assign-const-if-global ) ,name ,rr)
1444
1444
(latestworld-if-toplevel)
1445
1445
,rr)))
1446
1446
(expand-forms
1450
1450
(filter (lambda (x ) (not (underscore-symbol? x))) syms))
1451
1451
1452
1452
; ; Expand `[global] const a::T = val`
1453
- (define (expand-const-decl e (mustassgn #f ))
1454
- (if (length= e 3 ) e
1455
- (let ((arg (cadr e)))
1456
- (if (atom? arg)
1457
- (if mustassgn
1458
- (error " expected assignment after \" const\" " )
1459
- e)
1460
- (case (car arg)
1461
- ((global)
1462
- (expand-const-decl `(const ,(cadr arg)) #t ))
1463
- ((=)
1464
- (cond
1465
- ; ; `const f() = ...` - The `const` here is inoperative, but the syntax happened to work in earlier versions, so simply strip `const`.
1466
- ; ; TODO: Consider whether to keep this in 2.0.
1467
- ((eventually-call? (cadr arg))
1468
- (expand-forms arg))
1469
- ((and (pair? (cadr arg)) (eq? (caadr arg) 'curly ))
1470
- (expand-unionall-def (cadr arg) (caddr arg)))
1471
- ((and (pair? (cadr arg)) (eq? (caadr arg) 'tuple ) (not (has-parameters? (cdr (cadr arg)))))
1472
- ; ; We need this case because `(f(), g()) = (1, 2)` goes through here, which cannot go via the `local` lowering below,
1473
- ; ; because the symbols come out wrong. Sigh... So much effort for such a syntax corner case.
1474
- (expand-tuple-destruct (cdr (cadr arg)) (caddr arg) (lambda (assgn ) `(,(car e) ,assgn))))
1475
- (else
1476
- (let ((rr (make-ssavalue)))
1477
- (expand-forms `(block
1478
- (= ,rr ,(caddr arg))
1479
- (scope-block (block (hardscope)
1480
- (local (= ,(cadr arg) ,rr))
1481
- ,.(map (lambda (v ) `(,(car e) (globalref (thismodule) ,v) ,v)) (filter-not-underscore (lhs-vars (cadr arg))))
1482
- (latestworld)
1483
- ,rr))))))))
1484
- (else (error " expected assignment after \" const\" " )))))))
1453
+ (define (expand-const-decl e )
1454
+ (define (check-assignment asgn )
1455
+ (unless (and (pair? asgn) (eq? (car asgn) '= ))
1456
+ ; ; (const (global x)) is possible due to a parser quirk
1457
+ (error " expected assignment after \" const\" " )))
1458
+ (if (length= e 3 )
1459
+ `(const ,(cadr e) ,(expand-forms (caddr e)))
1460
+ (let ((arg (cadr e)))
1461
+ (case (car arg)
1462
+ ((global) (let ((asgn (cadr arg)))
1463
+ (check-assignment asgn)
1464
+ `(block
1465
+ ,.(map (lambda (v ) `(global ,v))
1466
+ (lhs-bound-names (cadr asgn)))
1467
+ ,(expand-assignment asgn #t ))))
1468
+ ((=) (check-assignment arg)
1469
+ (expand-assignment arg #t ))
1470
+ (else (error " expected assignment after \" const\" " ))))))
1485
1471
1486
1472
(define (expand-atomic-decl e )
1487
1473
(error " unimplemented or unsupported atomic declaration" ))
1538
1524
(eq? (car (cadr lhs)) 'call )))))
1539
1525
(define (assignment-to-function lhs e ) ; ; convert '= expr to 'function expr
1540
1526
(cons 'function (cdr e)))
1527
+ (define (maybe-wrap-const x )
1528
+ (if const? `(const ,x) x))
1541
1529
(cond
1542
1530
((function-lhs? lhs)
1531
+ ; ; `const f() = ...` - The `const` here is inoperative, but the syntax
1532
+ ; ; happened to work in earlier versions, so simply strip `const`.
1543
1533
(expand-forms (assignment-to-function lhs e)))
1544
1534
((and (pair? lhs)
1545
1535
(eq? (car lhs) 'curly ))
1546
- (expand-unionall-def (cadr e) (caddr e)))
1536
+ (expand-unionall-def (cadr e) (caddr e) const? ))
1547
1537
((assignment? (caddr e))
1548
1538
; ; chain of assignments - convert a=b=c to `b=c; a=c`
1549
1539
(let loop ((lhss (list lhs))
1550
1540
(rhs (caddr e)))
1551
1541
(if (and (assignment? rhs) (not (function-lhs? (cadr rhs))))
1552
1542
(loop (cons (cadr rhs) lhss) (caddr rhs))
1553
- (let ((rr (if (symbol-like? rhs) rhs (make-ssavalue))))
1543
+ (let* ((rr (if (symbol-like? rhs) rhs (make-ssavalue)))
1544
+ (lhss (reverse lhss))
1545
+ (lhs0 (car lhss))
1546
+ (lhss (cdr lhss))
1547
+ (lhss (reverse lhss)))
1554
1548
(expand-forms
1555
1549
`(block ,.(if (eq? rr rhs) '() `((= ,rr ,(if (assignment? rhs)
1556
1550
(assignment-to-function (cadr rhs) rhs)
1557
1551
rhs))))
1558
- ,@(map (lambda (l ) `(= ,l ,rr))
1559
- lhss)
1552
+ ,@(map (lambda (l ) `(= ,l ,rr)) lhss)
1553
+ ; ; In const x = y = z, only x becomes const
1554
+ ,(maybe-wrap-const `(= ,lhs0 ,rr))
1560
1555
(unnecessary ,rr)))))))
1561
1556
((or (and (symbol-like? lhs) (valid-name? lhs))
1562
1557
(globalref? lhs))
1563
- (sink-assignment lhs (expand-forms (caddr e))))
1558
+ ; ; TODO: We currently call (latestworld) after every (const _ _), but this
1559
+ ; ; may need to be moved elsewhere if we want to avoid making one const
1560
+ ; ; visible before side effects have been performed (#57484)
1561
+ (if const?
1562
+ (let ((rr (make-ssavalue)))
1563
+ `(block
1564
+ ,(sink-assignment rr (expand-forms (caddr e)))
1565
+ (const ,lhs ,rr)
1566
+ (latestworld)
1567
+ (unnecessary ,rr)))
1568
+ (sink-assignment lhs (expand-forms (caddr e)))))
1564
1569
((atom? lhs)
1565
1570
(error (string " invalid assignment location \" " (deparse lhs) " \" " )))
1566
1571
(else
1567
1572
(case (car lhs)
1568
1573
((|.|)
1569
1574
; ; a.b =
1575
+ (when const?
1576
+ (error (string " cannot declare \" " (deparse lhs) " \" `const`" )))
1570
1577
(let* ((a (cadr lhs))
1571
1578
(b (caddr lhs))
1572
1579
(rhs (caddr e)))
1588
1595
(x (caddr e)))
1589
1596
(if (has-parameters? lhss)
1590
1597
; ; property destructuring
1591
- (expand-property-destruct lhss x)
1598
+ (expand-property-destruct lhss x maybe-wrap-const )
1592
1599
; ; multiple assignment
1593
- (expand-tuple-destruct lhss x))))
1600
+ (expand-tuple-destruct lhss x maybe-wrap-const ))))
1594
1601
((typed_hcat)
1595
1602
(error " invalid spacing in left side of indexed assignment" ))
1596
1603
((typed_vcat typed_ncat)
1597
1604
(error " unexpected \" ;\" in left side of indexed assignment" ))
1598
1605
((ref)
1599
1606
; ; (= (ref a . idxs) rhs)
1607
+ (when const?
1608
+ (error (string " cannot declare \" " (deparse lhs) " \" `const`" )))
1600
1609
(let ((a (cadr lhs))
1601
1610
(idxs (cddr lhs))
1602
1611
(rhs (caddr e)))
1626
1635
(T (caddr lhs))
1627
1636
(rhs (caddr e)))
1628
1637
(let ((e (remove-argument-side-effects x)))
1629
- (expand-forms
1630
- `(block ,@(cdr e)
1631
- (decl ,(car e) ,T)
1632
- (= ,(car e) ,rhs))))))
1638
+ (if const?
1639
+ ; ; This could go through convert-assignment in the closure
1640
+ ; ; conversion pass, but since constants don't have declared types
1641
+ ; ; the way other variables do, we insert convert() here.
1642
+ (expand-forms
1643
+ ; ; TODO: This behaviour (`const _:T = ...` does not call convert,
1644
+ ; ; but still evaluates RHS) should be documented.
1645
+ `(const ,(car e) ,(if (underscore-symbol? (car e))
1646
+ rhs
1647
+ (convert-for-type-decl rhs T #t #f ))))
1648
+ (expand-forms
1649
+ `(block ,@(cdr e)
1650
+ ; ; TODO: When x is a complex expression, this acts as a
1651
+ ; ; typeassert rather than a declaration.
1652
+ ,.(if (underscore-symbol? (car e))
1653
+ '() ; Assignment to _ will ultimately be discarded---don't declare anything
1654
+ `((decl ,(car e) ,T)))
1655
+ ,(maybe-wrap-const `(= ,(car e) ,rhs))))))))
1633
1656
((vcat ncat)
1634
1657
; ; (= (vcat . args) rhs)
1635
1658
(error " use \" (a, b) = ...\" to assign multiple values" ))
2371
2394
(gensy))
2372
2395
(else (make-ssavalue))))
2373
2396
2374
- (define (expand-property-destruct lhs x )
2397
+ (define (expand-property-destruct lhs x (wrap identity) )
2375
2398
(if (not (length= lhs 1 ))
2376
2399
(error (string " invalid assignment location \" " (deparse `(tuple ,lhs)) " \" " )))
2377
2400
(let* ((lhss (cdar lhs))
2386
2409
(cadr field))
2387
2410
(else
2388
2411
(error (string " invalid assignment location \" " (deparse `(tuple ,lhs)) " \" " ))))))
2389
- (expand-forms `(= ,field (call (top getproperty) ,xx (quote ,prop ))))))
2412
+ (expand-forms (wrap `(= ,field (call (top getproperty) ,xx (quote ,prop ) ))))))
2390
2413
lhss)
2391
2414
(unnecessary ,xx))))
2392
2415
2407
2430
(if (null? lhss)
2408
2431
'()
2409
2432
(let* ((lhs (car lhss))
2410
- (wrapfirst (lambda (x i ) (if (= i 1 ) (wrap x) x)))
2411
2433
(lhs- (cond ((or (symbol? lhs) (ssavalue? lhs))
2412
2434
lhs)
2413
2435
((vararg? lhs)
2419
2441
(make-ssavalue))))))
2420
2442
; ; can't use ssavalues if it's a function definition
2421
2443
((eventually-call? lhs) (gensy))
2422
- (else (make-ssavalue)))))
2444
+ (else (make-ssavalue))))
2445
+ ; ; If we use an intermediary lhs, don't wrap `const`.
2446
+ (wrap-subassign (if (eq? lhs lhs-) wrap identity))
2447
+ (wrapfirst (lambda (x i ) (if (= i 1 ) (wrap-subassign x) x))))
2423
2448
(if (and (vararg? lhs) (any vararg? (cdr lhss)))
2424
2449
(error " multiple \" ...\" on lhs of assignment" ))
2425
2450
(if (not (eq? lhs lhs-))
2431
2456
(if (underscore-symbol? (cadr lhs-))
2432
2457
'()
2433
2458
(list (expand-forms
2434
- (wrap `(= ,(cadr lhs-) (call (top rest) ,xx ,@(if (eq? i 1 ) '() `(,st))))))))
2459
+ (wrap-subassign `(= ,(cadr lhs-) (call (top rest) ,xx ,@(if (eq? i 1 ) '() `(,st))))))))
2435
2460
(let ((tail (if (eventually-call? lhs) (gensy) (make-ssavalue))))
2436
2461
(cons (expand-forms
2437
2462
(lower-tuple-assignment
2978
3003
(define (lhs-vars e )
2979
3004
(map decl-var (lhs-decls e)))
2980
3005
3006
+ ; ; Return all the names that will be bound by the assignment LHS, including
3007
+ ; ; curlies and calls.
3008
+ (define (lhs-bound-names e )
3009
+ (cond ((underscore-symbol? e) '() )
3010
+ ((atom? e) (list e))
3011
+ ((and (pair? e) (memq (car e) ' (call curly where |::|)))
3012
+ (lhs-bound-names (cadr e)))
3013
+ ((and (pair? e) (memq (car e) ' (tuple parameters)))
3014
+ (apply append (map lhs-bound-names (cdr e))))))
3015
+
2981
3016
(define (all-decl-vars e ) ; ; map decl-var over every level of an assignment LHS
2982
3017
(cond ((eventually-call? e) e)
2983
3018
((decl? e) (decl-var e))
3004
3039
; ; like v = val, except that if `v` turns out global(either
3005
3040
; ; implicitly or by explicit `global`), it gains an implicit `const`
3006
3041
(set! vars (cons (cadr e) vars)))
3007
- ((=)
3042
+ ((= const )
3008
3043
(let ((v (decl-var (cadr e))))
3009
3044
(find-assigned-vars- (caddr e))
3010
3045
(if (or (ssavalue? v) (globalref? v) (underscore-symbol? v))
3130
3165
((eq? (car e) 'global )
3131
3166
(check-valid-name (cadr e))
3132
3167
e)
3168
+
3133
3169
((eq? (car e) 'assign-const-if-global )
3134
3170
(if (eq? (var-kind (cadr e) scope) 'local )
3135
3171
(if (length= e 2 ) (null) `(= ,@(cdr e)))
3136
- `(const ,@(cdr e))))
3172
+ (resolve-scopes- `(const ,@(cdr e)) scope sp loc )))
3137
3173
((eq? (car e) 'global-if-global )
3138
3174
(if (eq? (var-kind (cadr e) scope) 'local )
3139
3175
' (null)
3140
3176
`(global ,@(cdr e))))
3177
+
3141
3178
((memq (car e) ' (local local-def))
3142
3179
(check-valid-name (cadr e))
3143
3180
; ; remove local decls
3290
3327
,(resolve-scopes- (caddr e) scope)
3291
3328
,(resolve-scopes- (cadddr e) scope (method-expr-static-parameters e))))
3292
3329
(else
3293
- (if (and (eq? (car e) '= ) (symbol? (cadr e))
3330
+ (if (and (memq (car e) ' ( = const) ) (symbol? (cadr e))
3294
3331
scope (null? (lam:args (scope:lam scope)))
3295
3332
(warn-var?! (cadr e) scope)
3296
3333
(= *scopewarn-opt* 1 ))
3410
3447
((local-def) ; ; a local that we know has an assignment that dominates all usages
3411
3448
(let ((vi (get tab (cadr e) #f )))
3412
3449
(vinfo:set-never-undef! vi #t )))
3413
- ((=)
3450
+ ((= const )
3414
3451
(let ((vi (and (symbol? (cadr e)) (get tab (cadr e) #f ))))
3415
3452
(if vi ; if local or captured
3416
3453
(begin (if (vinfo:asgn vi)
@@ -4027,7 +4064,10 @@ f(x) = yt(x)
4027
4064
' (null)
4028
4065
`(newvar ,(cadr e))))))
4029
4066
((const)
4030
- (put! globals (binding-to-globalref (cadr e)) #f )
4067
+ ; ; Check we've expanded surface `const` (1 argument form)
4068
+ (assert (and (length= e 3 )))
4069
+ (when (globalref? (cadr e))
4070
+ (put! globals (cadr e) #f ))
4031
4071
e)
4032
4072
((atomic) e)
4033
4073
((isdefined) ; ; convert isdefined expr to function for closure converted variables
@@ -4379,7 +4419,6 @@ f(x) = yt(x)
4379
4419
(first-line #t )
4380
4420
(current-loc #f )
4381
4421
(rett #f )
4382
- (global-const-error #f )
4383
4422
(vinfo-table (vinfo-to-table (car (lam:vinfo lam))))
4384
4423
(arg-map #f ) ; ; map arguments to new names if they are assigned
4385
4424
(label-counter 0 ) ; ; counter for generating label addresses
@@ -4592,18 +4631,19 @@ f(x) = yt(x)
4592
4631
(cdr cnd)
4593
4632
(list cnd))))))
4594
4633
tests))
4595
- (define (emit-assignment-or-setglobal lhs rhs )
4596
- (if (globalref? lhs)
4634
+ (define (emit-assignment-or-setglobal lhs rhs (op '= ))
4635
+ ; ; (const (globalref _ _) _) does not use setglobal!
4636
+ (if (and (globalref? lhs) (eq? op '= ))
4597
4637
(emit `(call (top setglobal!) ,(cadr lhs) (inert ,(caddr lhs)) ,rhs))
4598
- (emit `(= ,lhs ,rhs))))
4599
- (define (emit-assignment lhs rhs )
4638
+ (emit `(,op ,lhs ,rhs))))
4639
+ (define (emit-assignment lhs rhs (op '= ) )
4600
4640
(if rhs
4601
4641
(if (valid-ir-rvalue? lhs rhs)
4602
- (emit-assignment-or-setglobal lhs rhs)
4642
+ (emit-assignment-or-setglobal lhs rhs op )
4603
4643
(let ((rr (make-ssavalue)))
4604
4644
(emit `(= ,rr ,rhs))
4605
- (emit-assignment-or-setglobal lhs rr)))
4606
- (emit-assignment-or-setglobal lhs `(null))) ; in unreachable code (such as after return), still emit the assignment so that the structure of those uses is preserved
4645
+ (emit-assignment-or-setglobal lhs rr op )))
4646
+ (emit-assignment-or-setglobal lhs `(null) op )) ; in unreachable code (such as after return), still emit the assignment so that the structure of those uses is preserved
4607
4647
#f )
4608
4648
; ; the interpreter loop. `break-labels` keeps track of the labels to jump to
4609
4649
; ; for all currently closing break-blocks.
@@ -4669,7 +4709,12 @@ f(x) = yt(x)
4669
4709
(cond (tail (emit-return tail callex))
4670
4710
(value callex)
4671
4711
(else (emit callex)))))
4672
- ((=)
4712
+ ((= const)
4713
+ (when (eq? (car e) 'const )
4714
+ (when (local-in? (cadr e) lam)
4715
+ (error (string " unsupported `const` declaration on local variable" (format-loc current-loc))))
4716
+ (when (pair? (cadr lam))
4717
+ (error (string " `global const` declaration not allowed inside function" (format-loc current-loc)))))
4673
4718
(let ((lhs (cadr e)))
4674
4719
(if (and (symbol? lhs) (underscore-symbol? lhs))
4675
4720
(compile (caddr e) break-labels value tail)
@@ -4682,10 +4727,10 @@ f(x) = yt(x)
4682
4727
rhs (make-ssavalue))))
4683
4728
(if (not (eq? rr rhs))
4684
4729
(emit `(= ,rr ,rhs)))
4685
- (emit-assignment-or-setglobal lhs rr)
4730
+ (emit-assignment-or-setglobal lhs rr ( car e) )
4686
4731
(if tail (emit-return tail rr))
4687
4732
rr)
4688
- (emit-assignment lhs rhs))))))
4733
+ (emit-assignment lhs rhs ( car e) ))))))
4689
4734
((block)
4690
4735
(let* ((last-fname filename)
4691
4736
(fnm (first-non-meta e))
@@ -4928,14 +4973,6 @@ f(x) = yt(x)
4928
4973
((moved-local)
4929
4974
(set-car! (lam:vinfo lam) (append (car (lam:vinfo lam)) `((,(cadr e) Any 2 ))))
4930
4975
#f )
4931
- ((const)
4932
- (if (local-in? (cadr e) lam)
4933
- (error (string " unsupported `const` declaration on local variable" (format-loc current-loc)))
4934
- (if (pair? (cadr lam))
4935
- ; ; delay this error to allow "misplaced struct" errors to happen first
4936
- (if (not global-const-error)
4937
- (set! global-const-error current-loc))
4938
- (emit e))))
4939
4976
((atomic) (error " misplaced atomic declaration" ))
4940
4977
((isdefined throw_undef_if_not) (if tail (emit-return tail e) e))
4941
4978
((boundscheck) (if tail (emit-return tail e) e))
@@ -5066,8 +5103,6 @@ f(x) = yt(x)
5066
5103
(let ((pexc (pop-exc-expr src-catch-tokens target-catch-tokens)))
5067
5104
(if pexc (set-cdr! point (cons pexc (cdr point)))))))))
5068
5105
handler-goto-fixups)
5069
- (if global-const-error
5070
- (error (string " `global const` declaration not allowed inside function" (format-loc global-const-error))))
5071
5106
(let* ((stmts (reverse! code))
5072
5107
(di (definitely-initialized-vars stmts vi))
5073
5108
(body (cons 'block (filter (lambda (e )
0 commit comments