1423
1423
(else
1424
1424
(error " invalid \" try\" form" )))))
1425
1425
1426
- (define (expand-unionall-def name type-ex (allow-local #t ))
1426
+ (define (expand-unionall-def name type-ex (const? #t ))
1427
1427
(if (and (pair? name)
1428
1428
(eq? (car name) 'curly ))
1429
1429
(let ((name (cadr name))
1434
1434
(expand-forms
1435
1435
`(block
1436
1436
(= ,rr (where ,type-ex ,@params))
1437
- (,(if allow-local ' assign-const-if-global 'const ) ,name ,rr)
1437
+ (,(if const? 'const ' assign-const-if-global ) ,name ,rr)
1438
1438
(latestworld-if-toplevel)
1439
1439
,rr)))
1440
1440
(expand-forms
1444
1444
(filter (lambda (x ) (not (underscore-symbol? x))) syms))
1445
1445
1446
1446
; ; Expand `[global] const a::T = val`
1447
- (define (expand-const-decl e (mustassgn #f ))
1448
- (if (length= e 3 ) e
1449
- (let ((arg (cadr e)))
1450
- (if (atom? arg)
1451
- (if mustassgn
1452
- (error " expected assignment after \" const\" " )
1453
- e)
1454
- (case (car arg)
1455
- ((global)
1456
- (expand-const-decl `(const ,(cadr arg)) #t ))
1457
- ((=)
1458
- (cond
1459
- ; ; `const f() = ...` - The `const` here is inoperative, but the syntax happened to work in earlier versions, so simply strip `const`.
1460
- ; ; TODO: Consider whether to keep this in 2.0.
1461
- ((eventually-call? (cadr arg))
1462
- (expand-forms arg))
1463
- ((and (pair? (cadr arg)) (eq? (caadr arg) 'curly ))
1464
- (expand-unionall-def (cadr arg) (caddr arg)))
1465
- ((and (pair? (cadr arg)) (eq? (caadr arg) 'tuple ) (not (has-parameters? (cdr (cadr arg)))))
1466
- ; ; We need this case because `(f(), g()) = (1, 2)` goes through here, which cannot go via the `local` lowering below,
1467
- ; ; because the symbols come out wrong. Sigh... So much effort for such a syntax corner case.
1468
- (expand-tuple-destruct (cdr (cadr arg)) (caddr arg) (lambda (assgn ) `(,(car e) ,assgn))))
1469
- (else
1470
- (let ((rr (make-ssavalue)))
1471
- (expand-forms `(block
1472
- (= ,rr ,(caddr arg))
1473
- (scope-block (block (hardscope)
1474
- (local (= ,(cadr arg) ,rr))
1475
- ,.(map (lambda (v ) `(,(car e) (globalref (thismodule) ,v) ,v)) (filter-not-underscore (lhs-vars (cadr arg))))
1476
- (latestworld)
1477
- ,rr))))))))
1478
- (else (error " expected assignment after \" const\" " )))))))
1447
+ (define (expand-const-decl e )
1448
+ (define (check-assignment asgn )
1449
+ (unless (and (pair? asgn) (eq? (car asgn) '= ))
1450
+ ; ; (const (global x)) is possible due to a parser quirk
1451
+ (error " expected assignment after \" const\" " )))
1452
+ (if (length= e 3 )
1453
+ `(const ,(cadr e) ,(expand-forms (caddr e)))
1454
+ (let ((arg (cadr e)))
1455
+ (case (car arg)
1456
+ ((global) (let ((asgn (cadr arg)))
1457
+ (check-assignment asgn)
1458
+ `(block
1459
+ ,.(map (lambda (v ) `(global ,v))
1460
+ (filter-not-underscore (lhs-vars (cadr asgn))))
1461
+ ,(expand-assignment asgn #t ))))
1462
+ ((=) (check-assignment arg)
1463
+ (expand-assignment arg #t ))
1464
+ (else (error " expected assignment after \" const\" " ))))))
1479
1465
1480
1466
(define (expand-atomic-decl e )
1481
1467
(error " unimplemented or unsupported atomic declaration" ))
1532
1518
(eq? (car (cadr lhs)) 'call )))))
1533
1519
(define (assignment-to-function lhs e ) ; ; convert '= expr to 'function expr
1534
1520
(cons 'function (cdr e)))
1521
+ (define (maybe-wrap-const x )
1522
+ (if const? `(const ,x) x))
1535
1523
(cond
1536
1524
((function-lhs? lhs)
1525
+ ; ; `const f() = ...` - The `const` here is inoperative, but the syntax
1526
+ ; ; happened to work in earlier versions, so simply strip `const`.
1537
1527
(expand-forms (assignment-to-function lhs e)))
1538
1528
((and (pair? lhs)
1539
1529
(eq? (car lhs) 'curly ))
1540
- (expand-unionall-def (cadr e) (caddr e)))
1530
+ (expand-unionall-def (cadr e) (caddr e) const? ))
1541
1531
((assignment? (caddr e))
1542
1532
; ; chain of assignments - convert a=b=c to `b=c; a=c`
1543
1533
(let loop ((lhss (list lhs))
1544
1534
(rhs (caddr e)))
1545
1535
(if (and (assignment? rhs) (not (function-lhs? (cadr rhs))))
1546
1536
(loop (cons (cadr rhs) lhss) (caddr rhs))
1547
- (let ((rr (if (symbol-like? rhs) rhs (make-ssavalue))))
1537
+ (let ((rr (if (symbol-like? rhs) rhs (make-ssavalue)))
1538
+ (lhss (reverse lhss)))
1548
1539
(expand-forms
1549
1540
`(block ,.(if (eq? rr rhs) '() `((= ,rr ,(if (assignment? rhs)
1550
1541
(assignment-to-function (cadr rhs) rhs)
1551
1542
rhs))))
1543
+ ; ; In const x = y = z, only x becomes const
1544
+ ,(maybe-wrap-const `(= ,(car lhss) ,rr))
1552
1545
,@(map (lambda (l ) `(= ,l ,rr))
1553
- lhss)
1546
+ ( cdr lhss) )
1554
1547
(unnecessary ,rr)))))))
1555
1548
((or (and (symbol-like? lhs) (valid-name? lhs))
1556
1549
(globalref? lhs))
1557
- (sink-assignment lhs (expand-forms (caddr e))))
1550
+ ; ; TODO: We currently call (latestworld) after every (const _ _), but this
1551
+ ; ; may need to be moved elsewhere if we want to avoid making one const
1552
+ ; ; visible before side effects have been performed (#57484)
1553
+ (if const?
1554
+ (let ((rr (make-ssavalue)))
1555
+ `(block
1556
+ ,(sink-assignment rr (expand-forms (caddr e)))
1557
+ (const ,lhs ,rr)
1558
+ (latestworld)
1559
+ (unnecessary ,rr)))
1560
+ (sink-assignment lhs (expand-forms (caddr e)))))
1558
1561
((atom? lhs)
1559
1562
(error (string " invalid assignment location \" " (deparse lhs) " \" " )))
1560
1563
(else
1561
1564
(case (car lhs)
1562
1565
((|.|)
1563
1566
; ; a.b =
1567
+ (when const?
1568
+ (error (string " cannot declare \" " (deparse lhs) " \" `const`" )))
1564
1569
(let* ((a (cadr lhs))
1565
1570
(b (caddr lhs))
1566
1571
(rhs (caddr e)))
1582
1587
(x (caddr e)))
1583
1588
(if (has-parameters? lhss)
1584
1589
; ; property destructuring
1585
- (expand-property-destruct lhss x)
1590
+ (expand-property-destruct lhss x maybe-wrap-const )
1586
1591
; ; multiple assignment
1587
- (expand-tuple-destruct lhss x))))
1592
+ (expand-tuple-destruct lhss x maybe-wrap-const ))))
1588
1593
((typed_hcat)
1589
1594
(error " invalid spacing in left side of indexed assignment" ))
1590
1595
((typed_vcat typed_ncat)
1591
1596
(error " unexpected \" ;\" in left side of indexed assignment" ))
1592
1597
((ref)
1593
1598
; ; (= (ref a . idxs) rhs)
1599
+ (when const?
1600
+ (error (string " cannot declare \" " (deparse lhs) " \" `const`" )))
1594
1601
(let ((a (cadr lhs))
1595
1602
(idxs (cddr lhs))
1596
1603
(rhs (caddr e)))
1620
1627
(T (caddr lhs))
1621
1628
(rhs (caddr e)))
1622
1629
(let ((e (remove-argument-side-effects x)))
1623
- (expand-forms
1624
- `(block ,@(cdr e)
1625
- (decl ,(car e) ,T)
1626
- (= ,(car e) ,rhs))))))
1630
+ (if const?
1631
+ ; ; This could go through convert-assignment in the closure
1632
+ ; ; conversion pass, but since constants don't have declared types
1633
+ ; ; the way other variables do, we insert convert() here.
1634
+ (expand-forms
1635
+ ; ; TODO: This behaviour (`const _:T = ...` does not call convert,
1636
+ ; ; but still evaluates RHS) should be documented.
1637
+ `(const ,(car e) ,(if (underscore-symbol? (car e))
1638
+ rhs
1639
+ (convert-for-type-decl rhs T #t #f ))))
1640
+ (expand-forms
1641
+ `(block ,@(cdr e)
1642
+ ; ; TODO: When x is a complex expression, this acts as a
1643
+ ; ; typeassert rather than a declaration.
1644
+ ,.(if (underscore-symbol? (car e))
1645
+ '() ; Assignment to _ will ultimately be discarded---don't declare anything
1646
+ `((decl ,(car e) ,T)))
1647
+ ,(maybe-wrap-const `(= ,(car e) ,rhs))))))))
1627
1648
((vcat ncat)
1628
1649
; ; (= (vcat . args) rhs)
1629
1650
(error " use \" (a, b) = ...\" to assign multiple values" ))
2365
2386
(gensy))
2366
2387
(else (make-ssavalue))))
2367
2388
2368
- (define (expand-property-destruct lhs x )
2389
+ (define (expand-property-destruct lhs x (wrap identity) )
2369
2390
(if (not (length= lhs 1 ))
2370
2391
(error (string " invalid assignment location \" " (deparse `(tuple ,lhs)) " \" " )))
2371
2392
(let* ((lhss (cdar lhs))
2380
2401
(cadr field))
2381
2402
(else
2382
2403
(error (string " invalid assignment location \" " (deparse `(tuple ,lhs)) " \" " ))))))
2383
- (expand-forms `(= ,field (call (top getproperty) ,xx (quote ,prop ))))))
2404
+ (expand-forms (wrap `(= ,field (call (top getproperty) ,xx (quote ,prop ) ))))))
2384
2405
lhss)
2385
2406
(unnecessary ,xx))))
2386
2407
2401
2422
(if (null? lhss)
2402
2423
'()
2403
2424
(let* ((lhs (car lhss))
2404
- (wrapfirst (lambda (x i ) (if (= i 1 ) (wrap x) x)))
2405
2425
(lhs- (cond ((or (symbol? lhs) (ssavalue? lhs))
2406
2426
lhs)
2407
2427
((vararg? lhs)
2413
2433
(make-ssavalue))))))
2414
2434
; ; can't use ssavalues if it's a function definition
2415
2435
((eventually-call? lhs) (gensy))
2416
- (else (make-ssavalue)))))
2436
+ (else (make-ssavalue))))
2437
+ ; ; If we use an intermediary lhs, don't wrap `const`.
2438
+ (wrap-subassign (if (eq? lhs lhs-) wrap identity))
2439
+ (wrapfirst (lambda (x i ) (if (= i 1 ) (wrap-subassign x) x))))
2417
2440
(if (and (vararg? lhs) (any vararg? (cdr lhss)))
2418
2441
(error " multiple \" ...\" on lhs of assignment" ))
2419
2442
(if (not (eq? lhs lhs-))
2425
2448
(if (underscore-symbol? (cadr lhs-))
2426
2449
'()
2427
2450
(list (expand-forms
2428
- (wrap `(= ,(cadr lhs-) (call (top rest) ,xx ,@(if (eq? i 1 ) '() `(,st))))))))
2451
+ (wrap-subassign `(= ,(cadr lhs-) (call (top rest) ,xx ,@(if (eq? i 1 ) '() `(,st))))))))
2429
2452
(let ((tail (if (eventually-call? lhs) (gensy) (make-ssavalue))))
2430
2453
(cons (expand-forms
2431
2454
(lower-tuple-assignment
2998
3021
; ; like v = val, except that if `v` turns out global(either
2999
3022
; ; implicitly or by explicit `global`), it gains an implicit `const`
3000
3023
(set! vars (cons (cadr e) vars)))
3001
- ((=)
3024
+ ((= const )
3002
3025
(let ((v (decl-var (cadr e))))
3003
3026
(find-assigned-vars- (caddr e))
3004
3027
(if (or (ssavalue? v) (globalref? v) (underscore-symbol? v))
3127
3150
((eq? (car e) 'assign-const-if-global )
3128
3151
(if (eq? (var-kind (cadr e) scope) 'local )
3129
3152
(if (length= e 2 ) (null) `(= ,@(cdr e)))
3130
- `(const ,@(cdr e))))
3153
+ (resolve-scopes- `(const ,@(cdr e)) scope sp loc )))
3131
3154
((memq (car e) ' (local local-def))
3132
3155
(check-valid-name (cadr e))
3133
3156
; ; remove local decls
3280
3303
,(resolve-scopes- (caddr e) scope)
3281
3304
,(resolve-scopes- (cadddr e) scope (method-expr-static-parameters e))))
3282
3305
(else
3283
- (if (and (eq? (car e) '= ) (symbol? (cadr e))
3306
+ (if (and (memq (car e) ' ( = const) ) (symbol? (cadr e))
3284
3307
scope (null? (lam:args (scope:lam scope)))
3285
3308
(warn-var?! (cadr e) scope)
3286
3309
(= *scopewarn-opt* 1 ))
3400
3423
((local-def) ; ; a local that we know has an assignment that dominates all usages
3401
3424
(let ((vi (get tab (cadr e) #f )))
3402
3425
(vinfo:set-never-undef! vi #t )))
3403
- ((=)
3426
+ ((= const )
3404
3427
(let ((vi (and (symbol? (cadr e)) (get tab (cadr e) #f ))))
3405
3428
(if vi ; if local or captured
3406
3429
(begin (if (vinfo:asgn vi)
@@ -4017,7 +4040,10 @@ f(x) = yt(x)
4017
4040
' (null)
4018
4041
`(newvar ,(cadr e))))))
4019
4042
((const)
4020
- (put! globals (binding-to-globalref (cadr e)) #f )
4043
+ ; ; Check we've expanded surface `const` (1 argument form)
4044
+ (assert (and (length= e 3 )))
4045
+ (when (globalref? (cadr e))
4046
+ (put! globals (cadr e) #f ))
4021
4047
e)
4022
4048
((atomic) e)
4023
4049
((isdefined) ; ; convert isdefined expr to function for closure converted variables
@@ -4369,7 +4395,6 @@ f(x) = yt(x)
4369
4395
(first-line #t )
4370
4396
(current-loc #f )
4371
4397
(rett #f )
4372
- (global-const-error #f )
4373
4398
(vinfo-table (vinfo-to-table (car (lam:vinfo lam))))
4374
4399
(arg-map #f ) ; ; map arguments to new names if they are assigned
4375
4400
(label-counter 0 ) ; ; counter for generating label addresses
@@ -4582,18 +4607,19 @@ f(x) = yt(x)
4582
4607
(cdr cnd)
4583
4608
(list cnd))))))
4584
4609
tests))
4585
- (define (emit-assignment-or-setglobal lhs rhs )
4586
- (if (globalref? lhs)
4610
+ (define (emit-assignment-or-setglobal lhs rhs (op '= ))
4611
+ ; ; (const (globalref _ _) _) does not use setglobal!
4612
+ (if (and (globalref? lhs) (eq? op '= ))
4587
4613
(emit `(call (top setglobal!) ,(cadr lhs) (inert ,(caddr lhs)) ,rhs))
4588
- (emit `(= ,lhs ,rhs))))
4589
- (define (emit-assignment lhs rhs )
4614
+ (emit `(,op ,lhs ,rhs))))
4615
+ (define (emit-assignment lhs rhs (op '= ) )
4590
4616
(if rhs
4591
4617
(if (valid-ir-rvalue? lhs rhs)
4592
- (emit-assignment-or-setglobal lhs rhs)
4618
+ (emit-assignment-or-setglobal lhs rhs op )
4593
4619
(let ((rr (make-ssavalue)))
4594
4620
(emit `(= ,rr ,rhs))
4595
- (emit-assignment-or-setglobal lhs rr)))
4596
- (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
4621
+ (emit-assignment-or-setglobal lhs rr op )))
4622
+ (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
4597
4623
#f )
4598
4624
; ; the interpreter loop. `break-labels` keeps track of the labels to jump to
4599
4625
; ; for all currently closing break-blocks.
@@ -4659,7 +4685,12 @@ f(x) = yt(x)
4659
4685
(cond (tail (emit-return tail callex))
4660
4686
(value callex)
4661
4687
(else (emit callex)))))
4662
- ((=)
4688
+ ((= const)
4689
+ (when (eq? (car e) 'const )
4690
+ (when (local-in? (cadr e) lam)
4691
+ (error (string " unsupported `const` declaration on local variable" (format-loc current-loc))))
4692
+ (when (pair? (cadr lam))
4693
+ (error (string " `global const` declaration not allowed inside function" (format-loc current-loc)))))
4663
4694
(let ((lhs (cadr e)))
4664
4695
(if (and (symbol? lhs) (underscore-symbol? lhs))
4665
4696
(compile (caddr e) break-labels value tail)
@@ -4672,10 +4703,10 @@ f(x) = yt(x)
4672
4703
rhs (make-ssavalue))))
4673
4704
(if (not (eq? rr rhs))
4674
4705
(emit `(= ,rr ,rhs)))
4675
- (emit-assignment-or-setglobal lhs rr)
4706
+ (emit-assignment-or-setglobal lhs rr ( car e) )
4676
4707
(if tail (emit-return tail rr))
4677
4708
rr)
4678
- (emit-assignment lhs rhs))))))
4709
+ (emit-assignment lhs rhs ( car e) ))))))
4679
4710
((block)
4680
4711
(let* ((last-fname filename)
4681
4712
(fnm (first-non-meta e))
@@ -4918,14 +4949,6 @@ f(x) = yt(x)
4918
4949
((moved-local)
4919
4950
(set-car! (lam:vinfo lam) (append (car (lam:vinfo lam)) `((,(cadr e) Any 2 ))))
4920
4951
#f )
4921
- ((const)
4922
- (if (local-in? (cadr e) lam)
4923
- (error (string " unsupported `const` declaration on local variable" (format-loc current-loc)))
4924
- (if (pair? (cadr lam))
4925
- ; ; delay this error to allow "misplaced struct" errors to happen first
4926
- (if (not global-const-error)
4927
- (set! global-const-error current-loc))
4928
- (emit e))))
4929
4952
((atomic) (error " misplaced atomic declaration" ))
4930
4953
((isdefined throw_undef_if_not) (if tail (emit-return tail e) e))
4931
4954
((boundscheck) (if tail (emit-return tail e) e))
@@ -5056,8 +5079,6 @@ f(x) = yt(x)
5056
5079
(let ((pexc (pop-exc-expr src-catch-tokens target-catch-tokens)))
5057
5080
(if pexc (set-cdr! point (cons pexc (cdr point)))))))))
5058
5081
handler-goto-fixups)
5059
- (if global-const-error
5060
- (error (string " `global const` declaration not allowed inside function" (format-loc global-const-error))))
5061
5082
(let* ((stmts (reverse! code))
5062
5083
(di (definitely-initialized-vars stmts vi))
5063
5084
(body (cons 'block (filter (lambda (e )
0 commit comments