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)))
16201627 (T (caddr lhs))
16211628 (rhs (caddr e)))
16221629 (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))))))))
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
29983021 ;; like v = val, except that if `v` turns out global(either
29993022 ;; implicitly or by explicit `global`), it gains an implicit `const`
30003023 (set! vars (cons (cadr e) vars)))
3001- ((=)
3024+ ((= const )
30023025 (let ((v (decl-var (cadr e))))
30033026 (find-assigned-vars- (caddr e))
30043027 (if (or (ssavalue? v) (globalref? v) (underscore-symbol? v))
31273150 ((eq? (car e) 'assign-const-if-global)
31283151 (if (eq? (var-kind (cadr e) scope) 'local)
31293152 (if (length= e 2) (null) `(= ,@(cdr e)))
3130- `(const ,@(cdr e))))
3153+ (resolve-scopes- `(const ,@(cdr e)) scope sp loc )))
31313154 ((memq (car e) '(local local-def))
31323155 (check-valid-name (cadr e))
31333156 ;; remove local decls
32803303 ,(resolve-scopes- (caddr e) scope)
32813304 ,(resolve-scopes- (cadddr e) scope (method-expr-static-parameters e))))
32823305 (else
3283- (if (and (eq? (car e) '= ) (symbol? (cadr e))
3306+ (if (and (memq (car e) '(= const) ) (symbol? (cadr e))
32843307 scope (null? (lam: args (scope: lam scope)))
32853308 (warn-var?! (cadr e) scope)
32863309 (= *scopewarn-opt* 1))
34003423 ((local-def) ;; a local that we know has an assignment that dominates all usages
34013424 (let ((vi (get tab (cadr e) #f)))
34023425 (vinfo: set-never-undef! vi #t)))
3403- ((=)
3426+ ((= const )
34043427 (let ((vi (and (symbol? (cadr e)) (get tab (cadr e) #f))))
34053428 (if vi ; if local or captured
34063429 (begin (if (vinfo: asgn vi)
@@ -4017,7 +4040,10 @@ f(x) = yt(x)
40174040 '(null)
40184041 `(newvar ,(cadr e))))))
40194042 ((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))
40214047 e)
40224048 ((atomic) e)
40234049 ((isdefined) ;; convert isdefined expr to function for closure converted variables
@@ -4369,7 +4395,6 @@ f(x) = yt(x)
43694395 (first-line #t)
43704396 (current-loc #f)
43714397 (rett #f)
4372- (global-const-error #f)
43734398 (vinfo-table (vinfo-to-table (car (lam: vinfo lam))))
43744399 (arg-map #f) ;; map arguments to new names if they are assigned
43754400 (label-counter 0) ;; counter for generating label addresses
@@ -4582,18 +4607,19 @@ f(x) = yt(x)
45824607 (cdr cnd)
45834608 (list cnd))))))
45844609 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 '=))
45874613 (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 '=) )
45904616 (if rhs
45914617 (if (valid-ir-rvalue? lhs rhs)
4592- (emit-assignment-or-setglobal lhs rhs)
4618+ (emit-assignment-or-setglobal lhs rhs op )
45934619 (let ((rr (make-ssavalue)))
45944620 (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
45974623 #f)
45984624 ;; the interpreter loop. `break-labels` keeps track of the labels to jump to
45994625 ;; for all currently closing break-blocks.
@@ -4659,7 +4685,12 @@ f(x) = yt(x)
46594685 (cond (tail (emit-return tail callex))
46604686 (value callex)
46614687 (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)))))
46634694 (let ((lhs (cadr e)))
46644695 (if (and (symbol? lhs) (underscore-symbol? lhs))
46654696 (compile (caddr e) break-labels value tail)
@@ -4672,10 +4703,10 @@ f(x) = yt(x)
46724703 rhs (make-ssavalue))))
46734704 (if (not (eq? rr rhs))
46744705 (emit `(= ,rr ,rhs)))
4675- (emit-assignment-or-setglobal lhs rr)
4706+ (emit-assignment-or-setglobal lhs rr (car e) )
46764707 (if tail (emit-return tail rr))
46774708 rr)
4678- (emit-assignment lhs rhs))))))
4709+ (emit-assignment lhs rhs (car e) ))))))
46794710 ((block)
46804711 (let* ((last-fname filename)
46814712 (fnm (first-non-meta e))
@@ -4918,14 +4949,6 @@ f(x) = yt(x)
49184949 ((moved-local)
49194950 (set-car! (lam: vinfo lam) (append (car (lam: vinfo lam)) `((,(cadr e) Any 2))))
49204951 #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))))
49294952 ((atomic) (error "misplaced atomic declaration"))
49304953 ((isdefined throw_undef_if_not) (if tail (emit-return tail e) e))
49314954 ((boundscheck) (if tail (emit-return tail e) e))
@@ -5056,8 +5079,6 @@ f(x) = yt(x)
50565079 (let ((pexc (pop-exc-expr src-catch-tokens target-catch-tokens)))
50575080 (if pexc (set-cdr! point (cons pexc (cdr point)))))))))
50585081 handler-goto-fixups)
5059- (if global-const-error
5060- (error (string "`global const` declaration not allowed inside function" (format-loc global-const-error))))
50615082 (let* ((stmts (reverse! code))
50625083 (di (definitely-initialized-vars stmts vi))
50635084 (body (cons 'block (filter (lambda (e)
0 commit comments