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
|
||||
;;;
|
||||
;;; `(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
|
||||
;;; `for' each open a frame; innermost first.
|
||||
;;; a closure-typed `c' outside it is still a closure after it. Every
|
||||
;;; 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)
|
||||
(hash-table-set! (car (hash-table-ref env :scopes)) name type))
|
||||
@@ -351,7 +356,8 @@
|
||||
(cons walk-embed-result (walk-body expansion env)))))
|
||||
(else
|
||||
(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)
|
||||
(let ((name (aux-name! env make-lambda-name)))
|
||||
@@ -595,15 +601,26 @@
|
||||
(cond
|
||||
((not left) right)
|
||||
((not right) left)
|
||||
((equal? left right) left)
|
||||
(else
|
||||
(let ((l (underlying (parse-type left)))
|
||||
(r (underlying (parse-type right))))
|
||||
(cond
|
||||
((or (ptr-type? l) (array-type? l)) left)
|
||||
((or (ptr-type? r) (array-type? r)) right)
|
||||
((< (conversion-rank l) (conversion-rank r)) right)
|
||||
(else left))))))
|
||||
((< (conversion-rank l) (conversion-rank r)) (promoted right r))
|
||||
(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
|
||||
(define (conversion-rank type)
|
||||
|
||||
Reference in New Issue
Block a user