scope and promote the way C does
A `while' or `switch' body is a block with no `do' around it, and its declarations landed in the enclosing frame. Two operands of one narrow type skipped the conversions, so `(+ c c)' answered `char'.
This commit is contained in:
29
semen.scm
29
semen.scm
@@ -295,8 +295,13 @@
|
|||||||
;;; The scope chain
|
;;; The scope chain
|
||||||
;;;
|
;;;
|
||||||
;;; `(do (var c int 9) ...)' declares a `c' that ends with the block, so
|
;;; `(do (var c int 9) ...)' declares a `c' that ends with the block, so
|
||||||
;;; a closure-typed `c' outside it is still a closure after it. `do' and
|
;;; a closure-typed `c' outside it is still a closure after it. Every
|
||||||
;;; `for' each open a frame; innermost first.
|
;;; form whose body C brackets opens a frame; innermost first.
|
||||||
|
;;;
|
||||||
|
;;; One frame per form is enough, rather than one per arm: a `case' label
|
||||||
|
;;; opens no scope in C either, and a declaration is not a statement, so
|
||||||
|
;;; the only way to write one in an `if' arm is the `do' that already
|
||||||
|
;;; brings its own.
|
||||||
|
|
||||||
(define (declare-name! env name type)
|
(define (declare-name! env name type)
|
||||||
(hash-table-set! (car (hash-table-ref env :scopes)) name type))
|
(hash-table-set! (car (hash-table-ref env :scopes)) name type))
|
||||||
@@ -351,7 +356,8 @@
|
|||||||
(cons walk-embed-result (walk-body expansion env)))))
|
(cons walk-embed-result (walk-body expansion env)))))
|
||||||
(else
|
(else
|
||||||
(case (car form)
|
(case (car form)
|
||||||
((do for) (with-scope env (lambda () (walk-parts form env))))
|
((do for while if switch)
|
||||||
|
(with-scope env (lambda () (walk-parts form env))))
|
||||||
|
|
||||||
((lambda)
|
((lambda)
|
||||||
(let ((name (aux-name! env make-lambda-name)))
|
(let ((name (aux-name! env make-lambda-name)))
|
||||||
@@ -595,15 +601,26 @@
|
|||||||
(cond
|
(cond
|
||||||
((not left) right)
|
((not left) right)
|
||||||
((not right) left)
|
((not right) left)
|
||||||
((equal? left right) left)
|
|
||||||
(else
|
(else
|
||||||
(let ((l (underlying (parse-type left)))
|
(let ((l (underlying (parse-type left)))
|
||||||
(r (underlying (parse-type right))))
|
(r (underlying (parse-type right))))
|
||||||
(cond
|
(cond
|
||||||
((or (ptr-type? l) (array-type? l)) left)
|
((or (ptr-type? l) (array-type? l)) left)
|
||||||
((or (ptr-type? r) (array-type? r)) right)
|
((or (ptr-type? r) (array-type? r)) right)
|
||||||
((< (conversion-rank l) (conversion-rank r)) right)
|
((< (conversion-rank l) (conversion-rank r)) (promoted right r))
|
||||||
(else left))))))
|
(else (promoted left l)))))))
|
||||||
|
|
||||||
|
;;; Anything narrower than `int' is promoted to one before the
|
||||||
|
;;; arithmetic happens, so two `char's join as `int' and not as `char'.
|
||||||
|
;;; Operands of the same type reach here too, which is the whole point:
|
||||||
|
;;; `(+ c c)' is where the promotion is invisible and the truncation is
|
||||||
|
;;; not. `unsigned' alone is `unsigned int' and stays as written.
|
||||||
|
(define (promoted written type)
|
||||||
|
(if (and (prim-type? type)
|
||||||
|
(any (lambda (word) (memq word '(char short bool _Bool)))
|
||||||
|
(prim-name type)))
|
||||||
|
'int
|
||||||
|
written))
|
||||||
|
|
||||||
;;; `char' and `short' promote to `int', so the ranks start there
|
;;; `char' and `short' promote to `int', so the ranks start there
|
||||||
(define (conversion-rank type)
|
(define (conversion-rank type)
|
||||||
|
|||||||
@@ -6,8 +6,10 @@
|
|||||||
"arrays: 30"
|
"arrays: 30"
|
||||||
"loop: 0 1 2"
|
"loop: 0 1 2"
|
||||||
"shadowed: 9 then 42"
|
"shadowed: 9 then 42"
|
||||||
|
"still an int: 200"
|
||||||
"partial: 1 2.5"
|
"partial: 1 2.5"
|
||||||
"joined: 43.5 84 49 1"
|
"joined: 43.5 84 49 1"
|
||||||
|
"promoted: 200 60000 3705032704"
|
||||||
"from elements: 4 9 0")
|
"from elements: 4 9 0")
|
||||||
(return 0)
|
(return 0)
|
||||||
|
|
||||||
@@ -55,11 +57,24 @@
|
|||||||
(printf " %d" i))
|
(printf " %d" i))
|
||||||
(printf "\n")
|
(printf "\n")
|
||||||
|
|
||||||
;; a block's declarations end with it
|
;; a block's declarations end with it, and so do the declarations of
|
||||||
|
;; everything else C brackets -- a `while' body is a block with no
|
||||||
|
;; `do' written around it
|
||||||
(do (var n _ 9)
|
(do (var n _ 9)
|
||||||
(printf "shadowed: %d then " n))
|
(printf "shadowed: %d then " n))
|
||||||
(printf "%d\n" n)
|
(printf "%d\n" n)
|
||||||
|
|
||||||
|
(var wide int 200)
|
||||||
|
(while false (var wide char 1) (printf "%d" wide))
|
||||||
|
(if false (do (var wide char 1) (printf "%d" wide)))
|
||||||
|
;; a statement before the declaration: a label may not be followed by
|
||||||
|
;; one until C23
|
||||||
|
(switch a (case 1 (printf "") (var wide char 1) (printf "%d" wide) (break)))
|
||||||
|
;; a copy, not a sum: an arithmetic result would be promoted to `int'
|
||||||
|
;; whatever leaked, and say nothing
|
||||||
|
(var copy _ wide)
|
||||||
|
(printf "still an int: %d\n" copy)
|
||||||
|
|
||||||
;; a wildcard inside a written type: only it is solved
|
;; a wildcard inside a written type: only it is solved
|
||||||
(var pp2 (* _) (& p))
|
(var pp2 (* _) (& p))
|
||||||
(printf "partial: %d %g\n" (-> pp2 x) (-> pp2 y))
|
(printf "partial: %d %g\n" (-> pp2 x) (-> pp2 y))
|
||||||
@@ -74,6 +89,17 @@
|
|||||||
(var single _ (+ g g))
|
(var single _ (+ g g))
|
||||||
(printf "joined: %g %d %ld %g\n" mixed same wider single)
|
(printf "joined: %g %d %ld %g\n" mixed same wider single)
|
||||||
|
|
||||||
|
;; ...including the promotions, which two operands of one narrow type
|
||||||
|
;; are exactly where they show: `char' + `char' is an `int'
|
||||||
|
(var c1 char 100)
|
||||||
|
(var c2 char 100)
|
||||||
|
(var h1 short 30000)
|
||||||
|
(var narrow _ (+ c1 c2))
|
||||||
|
(var narrower _ (+ h1 h1))
|
||||||
|
(var kept (unsigned int) 4000000000)
|
||||||
|
(var unpromoted _ (+ kept kept))
|
||||||
|
(printf "promoted: %d %d %u\n" narrow narrower unpromoted)
|
||||||
|
|
||||||
;; a brace initializer has no type of its own, but its elements solve
|
;; a brace initializer has no type of its own, but its elements solve
|
||||||
;; the hole in the array type around it -- and the length stays as
|
;; the hole in the array type around it -- and the length stays as
|
||||||
;; written, whether or not every slot is initialized
|
;; written, whether or not every slot is initialized
|
||||||
|
|||||||
Reference in New Issue
Block a user