14231423 (else
14241424 (error "invalid \"try\" form")))))
14251425
1426- (define (expand-unionall-def name type-ex (allow-local #t))
1426+ (define (expand-unionall-def name type-ex (const? #t))
14271427 (if (and (pair? name)
14281428 (eq? (car name) 'curly))
14291429 (let ((name (cadr name))
14341434 (expand-forms
14351435 `(block
14361436 (= ,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)
14381438 (latestworld-if-toplevel)
14391439 ,rr)))
14401440 (expand-forms
14441444 (filter (lambda (x) (not (underscore-symbol? x))) syms))
14451445
14461446;; 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\""))))))
14791465
14801466(define (expand-atomic-decl e)
14811467 (error "unimplemented or unsupported atomic declaration"))
15321518 (eq? (car (cadr lhs)) 'call)))))
15331519 (define (assignment-to-function lhs e) ;; convert '= expr to 'function expr
15341520 (cons 'function (cdr e)))
1521+ (define (maybe-wrap-const x)
1522+ (if const? `(const ,x) x))
15351523 (cond
15361524 ((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`.
15371527 (expand-forms (assignment-to-function lhs e)))
15381528 ((and (pair? lhs)
15391529 (eq? (car lhs) 'curly))
1540- (expand-unionall-def (cadr e) (caddr e)))
1530+ (expand-unionall-def (cadr e) (caddr e) const? ))
15411531 ((assignment? (caddr e))
15421532 ;; chain of assignments - convert a=b=c to `b=c; a=c`
15431533 (let loop ((lhss (list lhs))
15441534 (rhs (caddr e)))
15451535 (if (and (assignment? rhs) (not (function-lhs? (cadr rhs))))
15461536 (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)))
15481539 (expand-forms
15491540 `(block ,.(if (eq? rr rhs) '() `((= ,rr ,(if (assignment? rhs)
15501541 (assignment-to-function (cadr rhs) rhs)
15511542 rhs))))
1543+ ;; In const x = y = z, only x becomes const
1544+ ,(maybe-wrap-const `(= ,(car lhss) ,rr))
15521545 ,@(map (lambda (l) `(= ,l ,rr))
1553- lhss)
1546+ (cdr lhss) )
15541547 (unnecessary ,rr)))))))
15551548 ((or (and (symbol-like? lhs) (valid-name? lhs))
15561549 (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)))))
15581561 ((atom? lhs)
15591562 (error (string "invalid assignment location \"" (deparse lhs) "\"")))
15601563 (else
15611564 (case (car lhs)
15621565 ((|.|)
15631566 ;; a.b =
1567+ (when const?
1568+ (error (string "cannot declare \"" (deparse lhs) "\" `const`")))
15641569 (let* ((a (cadr lhs))
15651570 (b (caddr lhs))
15661571 (rhs (caddr e)))
15821587 (x (caddr e)))
15831588 (if (has-parameters? lhss)
15841589 ;; property destructuring
1585- (expand-property-destruct lhss x)
1590+ (expand-property-destruct lhss x maybe-wrap-const )
15861591 ;; multiple assignment
1587- (expand-tuple-destruct lhss x))))
1592+ (expand-tuple-destruct lhss x maybe-wrap-const ))))
15881593 ((typed_hcat)
15891594 (error "invalid spacing in left side of indexed assignment"))
15901595 ((typed_vcat typed_ncat)
15911596 (error "unexpected \";\" in left side of indexed assignment"))
15921597 ((ref)
15931598 ;; (= (ref a . idxs) rhs)
1599+ (when const?
1600+ (error (string "cannot declare \"" (deparse lhs) "\" `const`")))
15941601 (let ((a (cadr lhs))
15951602 (idxs (cddr lhs))
15961603 (rhs (caddr e)))
16191626 (let ((x (cadr lhs))
16201627 (T (caddr lhs))
16211628 (rhs (caddr e)))
1622- (let ((e (remove-argument-side-effects x)))
1623- (expand-forms
1624- `(block ,@(cdr e)
1625- (decl ,(car e) ,T)
1626- (= ,(car e) ,rhs))))))
1629+ (if const?
1630+ ;; This could go through convert-assignment in the closure
1631+ ;; conversion pass, but since constants don't have declared types
1632+ ;; the way other variables do, we insert convert() here.
1633+ (expand-forms
1634+ ;; TODO: This behaviour (`const _:T = ...` does not call convert,
1635+ ;; but still evaluates RHS) should be documented.
1636+ `(const ,x ,(if (underscore-symbol? (car e))
1637+ rhs
1638+ (convert-for-type-decl rhs T #t #f))))
1639+ (let ((e (remove-argument-side-effects x)))
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))))))))
16271648 ((vcat ncat)
16281649 ;; (= (vcat . args) rhs)
16291650 (error "use \"(a, b) = ...\" to assign multiple values"))
23652386 (gensy))
23662387 (else (make-ssavalue))))
23672388
2368- (define (expand-property-destruct lhs x)
2389+ (define (expand-property-destruct lhs x (wrap identity) )
23692390 (if (not (length= lhs 1))
23702391 (error (string "invalid assignment location \"" (deparse `(tuple ,lhs)) "\"")))
23712392 (let* ((lhss (cdar lhs))
23802401 (cadr field))
23812402 (else
23822403 (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) ))))))
23842405 lhss)
23852406 (unnecessary ,xx))))
23862407
24012422 (if (null? lhss)
24022423 '()
24032424 (let* ((lhs (car lhss))
2404- (wrapfirst (lambda (x i) (if (= i 1) (wrap x) x)))
24052425 (lhs- (cond ((or (symbol? lhs) (ssavalue? lhs))
24062426 lhs)
24072427 ((vararg? lhs)
24132433 (make-ssavalue))))))
24142434 ;; can't use ssavalues if it's a function definition
24152435 ((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))))
24172440 (if (and (vararg? lhs) (any vararg? (cdr lhss)))
24182441 (error "multiple \"...\" on lhs of assignment"))
24192442 (if (not (eq? lhs lhs-))
24252448 (if (underscore-symbol? (cadr lhs-))
24262449 '()
24272450 (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))))))))
24292452 (let ((tail (if (eventually-call? lhs) (gensy) (make-ssavalue))))
24302453 (cons (expand-forms
24312454 (lower-tuple-assignment
29963019 ;; like v = val, except that if `v` turns out global(either
29973020 ;; implicitly or by explicit `global`), it gains an implicit `const`
29983021 (set! vars (cons (cadr e) vars)))
2999- ((=)
3022+ ((= const )
30003023 (let ((v (decl-var (cadr e))))
30013024 (find-assigned-vars- (caddr e))
30023025 (if (or (ssavalue? v) (globalref? v) (underscore-symbol? v))
31253148 ((eq? (car e) 'assign-const-if-global)
31263149 (if (eq? (var-kind (cadr e) scope) 'local)
31273150 (if (length= e 2) (null) `(= ,@(cdr e)))
3128- `(const ,@(cdr e))))
3151+ (resolve-scopes- `(const ,@(cdr e)) scope sp loc )))
31293152 ((memq (car e) '(local local-def))
31303153 (check-valid-name (cadr e))
31313154 ;; remove local decls
32783301 ,(resolve-scopes- (caddr e) scope)
32793302 ,(resolve-scopes- (cadddr e) scope (method-expr-static-parameters e))))
32803303 (else
3281- (if (and (eq? (car e) '= ) (symbol? (cadr e))
3304+ (if (and (memq (car e) '(= const) ) (symbol? (cadr e))
32823305 scope (null? (lam: args (scope: lam scope)))
32833306 (warn-var?! (cadr e) scope)
32843307 (= *scopewarn-opt* 1))
33983421 ((local-def) ;; a local that we know has an assignment that dominates all usages
33993422 (let ((vi (get tab (cadr e) #f)))
34003423 (vinfo: set-never-undef! vi #t)))
3401- ((=)
3424+ ((= const )
34023425 (let ((vi (and (symbol? (cadr e)) (get tab (cadr e) #f))))
34033426 (if vi ; if local or captured
34043427 (begin (if (vinfo: asgn vi)
@@ -4015,7 +4038,10 @@ f(x) = yt(x)
40154038 '(null)
40164039 `(newvar ,(cadr e))))))
40174040 ((const)
4018- (put! globals (binding-to-globalref (cadr e)) #f)
4041+ ;; Check we've expanded surface `const` (1 argument form)
4042+ (assert (and (length= e 3)))
4043+ (when (globalref? (cadr e))
4044+ (put! globals (cadr e) #f))
40194045 e)
40204046 ((atomic) e)
40214047 ((isdefined) ;; convert isdefined expr to function for closure converted variables
@@ -4368,7 +4394,6 @@ f(x) = yt(x)
43684394 (first-line #t)
43694395 (current-loc #f)
43704396 (rett #f)
4371- (global-const-error #f)
43724397 (vinfo-table (vinfo-to-table (car (lam: vinfo lam))))
43734398 (arg-map #f) ;; map arguments to new names if they are assigned
43744399 (label-counter 0) ;; counter for generating label addresses
@@ -4581,18 +4606,19 @@ f(x) = yt(x)
45814606 (cdr cnd)
45824607 (list cnd))))))
45834608 tests))
4584- (define (emit-assignment-or-setglobal lhs rhs)
4585- (if (globalref? lhs)
4609+ (define (emit-assignment-or-setglobal lhs rhs (op '=))
4610+ ;; (const (globalref _ _) _) does not use setglobal!
4611+ (if (and (globalref? lhs) (eq? op '=))
45864612 (emit `(call (top setglobal!) ,(cadr lhs) (inert ,(caddr lhs)) ,rhs))
4587- (emit `(= ,lhs ,rhs))))
4588- (define (emit-assignment lhs rhs)
4613+ (emit `(,op ,lhs ,rhs))))
4614+ (define (emit-assignment lhs rhs (op '=) )
45894615 (if rhs
45904616 (if (valid-ir-rvalue? lhs rhs)
4591- (emit-assignment-or-setglobal lhs rhs)
4617+ (emit-assignment-or-setglobal lhs rhs op )
45924618 (let ((rr (make-ssavalue)))
45934619 (emit `(= ,rr ,rhs))
4594- (emit-assignment-or-setglobal lhs rr)))
4595- (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
4620+ (emit-assignment-or-setglobal lhs rr op )))
4621+ (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
45964622 #f)
45974623 ;; the interpreter loop. `break-labels` keeps track of the labels to jump to
45984624 ;; for all currently closing break-blocks.
@@ -4658,7 +4684,12 @@ f(x) = yt(x)
46584684 (cond (tail (emit-return tail callex))
46594685 (value callex)
46604686 (else (emit callex)))))
4661- ((=)
4687+ ((= const)
4688+ (when (eq? (car e) 'const)
4689+ (when (local-in? (cadr e) lam)
4690+ (error (string "unsupported `const` declaration on local variable" (format-loc current-loc))))
4691+ (when (pair? (cadr lam))
4692+ (error (string "`global const` declaration not allowed inside function" (format-loc current-loc)))))
46624693 (let ((lhs (cadr e)))
46634694 (if (and (symbol? lhs) (underscore-symbol? lhs))
46644695 (compile (caddr e) break-labels value tail)
@@ -4671,10 +4702,10 @@ f(x) = yt(x)
46714702 rhs (make-ssavalue))))
46724703 (if (not (eq? rr rhs))
46734704 (emit `(= ,rr ,rhs)))
4674- (emit-assignment-or-setglobal lhs rr)
4705+ (emit-assignment-or-setglobal lhs rr (car e) )
46754706 (if tail (emit-return tail rr))
46764707 rr)
4677- (emit-assignment lhs rhs))))))
4708+ (emit-assignment lhs rhs (car e) ))))))
46784709 ((block)
46794710 (let* ((last-fname filename)
46804711 (fnm (first-non-meta e))
@@ -4917,14 +4948,6 @@ f(x) = yt(x)
49174948 ((moved-local)
49184949 (set-car! (lam: vinfo lam) (append (car (lam: vinfo lam)) `((,(cadr e) Any 2))))
49194950 #f)
4920- ((const)
4921- (if (local-in? (cadr e) lam)
4922- (error (string "unsupported `const` declaration on local variable" (format-loc current-loc)))
4923- (if (pair? (cadr lam))
4924- ;; delay this error to allow "misplaced struct" errors to happen first
4925- (if (not global-const-error)
4926- (set! global-const-error current-loc))
4927- (emit e))))
49284951 ((atomic) (error "misplaced atomic declaration"))
49294952 ((isdefined throw_undef_if_not) (if tail (emit-return tail e) e))
49304953 ((boundscheck) (if tail (emit-return tail e) e))
@@ -5055,8 +5078,6 @@ f(x) = yt(x)
50555078 (let ((pexc (pop-exc-expr src-catch-tokens target-catch-tokens)))
50565079 (if pexc (set-cdr! point (cons pexc (cdr point)))))))))
50575080 handler-goto-fixups)
5058- (if global-const-error
5059- (error (string "`global const` declaration not allowed inside function" (format-loc global-const-error))))
50605081 (let* ((stmts (reverse! code))
50615082 (di (definitely-initialized-vars stmts vi))
50625083 (body (cons 'block (filter (lambda (e)
0 commit comments