show the cases the comments were describing
All checks were successful
Sex CI / build-linux (pull_request) Successful in 5m18s
All checks were successful
Sex CI / build-linux (pull_request) Successful in 5m18s
This commit is contained in:
@@ -381,11 +381,13 @@ forms, and what remains."
|
|||||||
;; are not part of it: ((* const char)), (int)
|
;; are not part of it: ((* const char)), (int)
|
||||||
(walk-type (maybe-unwrap-type arg))))
|
(walk-type (maybe-unwrap-type arg))))
|
||||||
|
|
||||||
;;; A parameter reaches fmt-c as `(type name)' and nothing else: it
|
;;; fmt-c reads a parameter as `(type name)', taking the name with
|
||||||
;;; reads the name out with `cadr', so a nameless one is the type and
|
;;; `cadr'. A nameless one is the type and an explicit #f:
|
||||||
;;; an explicit #f. Handing it the bare type instead made it read the
|
;;;
|
||||||
;;; type's own second word as the name -- `(* const char)' lost its
|
;;; (* const char) -> const char the star read as the name
|
||||||
;;; star -- and a one-word type had no second word to read at all.
|
;;; ((* const char) #f) -> const char *
|
||||||
|
;;; (int) -> (cadr) error
|
||||||
|
;;; (int #f) -> int
|
||||||
(define (walk-arglist form)
|
(define (walk-arglist form)
|
||||||
;; E.g.:
|
;; E.g.:
|
||||||
;; ((float) (int) (const char) (* const char) (¤ (* const struct res) 32))
|
;; ((float) (int) (const char) (* const char) (¤ (* const struct res) 32))
|
||||||
|
|||||||
@@ -509,9 +509,9 @@
|
|||||||
((eq? a b) #t)
|
((eq? a b) #t)
|
||||||
((unknown-type? a) #t)
|
((unknown-type? a) #t)
|
||||||
((unknown-type? b) #t)
|
((unknown-type? b) #t)
|
||||||
;; Whichever side is free takes the binding, so that a rigid
|
;; Whichever side is free takes the binding: `(unify a r)' and
|
||||||
;; variable is solved *to* rather than solved, in either order.
|
;; `(unify r a)' both leave `a' bound to `r'. Two rigid and
|
||||||
;; Both rigid and distinct is the mismatch `eq?' above let through.
|
;; distinct is the mismatch `eq?' above let through.
|
||||||
((and (tvar? a) (not (tvar-rigid? a))) (bind-tvar! a b form))
|
((and (tvar? a) (not (tvar-rigid? a))) (bind-tvar! a b form))
|
||||||
((and (tvar? b) (not (tvar-rigid? b))) (bind-tvar! b a form))
|
((and (tvar? b) (not (tvar-rigid? b))) (bind-tvar! b a form))
|
||||||
((or (tvar? a) (tvar? b)) (type-mismatch a b form))
|
((or (tvar? a) (tvar? b)) (type-mismatch a b form))
|
||||||
|
|||||||
131
semen.scm
131
semen.scm
@@ -299,12 +299,15 @@
|
|||||||
;;;
|
;;;
|
||||||
;;; `(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. Every
|
;;; a closure-typed `c' outside it is still a closure after it. Every
|
||||||
;;; form whose body C brackets opens 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
|
;;; (var v double 3.75)
|
||||||
;;; opens no scope in C either, and a declaration is not a statement, so
|
;;; (while (< v 0) (var v char 1) ...)
|
||||||
;;; the only way to write one in an `if' arm is the `do' that already
|
;;; (var m _ (+ v 1)) ; double, not char
|
||||||
;;; brings its own.
|
;;;
|
||||||
|
;;; One frame per form, not one per arm: a `case' label opens no scope
|
||||||
|
;;; in C, and an `if' arm can only declare inside a `do', which 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))
|
||||||
@@ -383,10 +386,11 @@
|
|||||||
|
|
||||||
((var)
|
((var)
|
||||||
;; the initializer is walked before the name it binds is in
|
;; the initializer is walked before the name it binds is in
|
||||||
;; scope; the type is resolved rather than walked, a `fn' type's
|
;; scope; the type is resolved rather than walked, a parameter
|
||||||
;; parameter list being indistinguishable from a call -- walking
|
;; list being shaped like a call:
|
||||||
;; `(fn ((c int)) int)' with a closure named `c' in scope would
|
;;
|
||||||
;; rewrite the parameter as a call of it
|
;; (var c (closure ((int)) int) ...)
|
||||||
|
;; (var fp (fn ((c int)) int) ...) ; int (*fp)(int)
|
||||||
(let* ((prefix (if (>= (length form) 3)
|
(let* ((prefix (if (>= (length form) 3)
|
||||||
(append (take form 2)
|
(append (take form 2)
|
||||||
(list (resolve-closure-types
|
(list (resolve-closure-types
|
||||||
@@ -427,9 +431,9 @@
|
|||||||
(else
|
(else
|
||||||
(let ((closure (receiver-closure-type (car form) env)))
|
(let ((closure (receiver-closure-type (car form) env)))
|
||||||
(if closure
|
(if closure
|
||||||
;; the receiver is walked first: a closure written where it
|
;; the receiver is walked first: `((closure ((x int)) int
|
||||||
;; is called registers its struct on the way, and the call
|
;; () ...) 5)' registers `struct ƛint_int' on the way, and
|
||||||
;; helper's signature mentions that struct
|
;; the call helper's signature names it
|
||||||
(let ((receiver (walk-statement (car form) env)))
|
(let ((receiver (walk-statement (car form) env)))
|
||||||
(copy-form-source!
|
(copy-form-source!
|
||||||
form
|
form
|
||||||
@@ -530,10 +534,12 @@
|
|||||||
(else (pair (cdr params) (- remaining 1)
|
(else (pair (cdr params) (- remaining 1)
|
||||||
(cons (unwrap-type (car params)) acc))))))
|
(cons (unwrap-type (car params)) acc))))))
|
||||||
|
|
||||||
;;; A `fn' header has its macros expanded and its closure types
|
;;; What a `fn' header gets, a `var' type gets -- macro expansion and
|
||||||
;;; resolved without being walked; a `var' type is the same thing in the
|
;;; closure resolution, no walk:
|
||||||
;;; same position, and gets the same two. A macro standing in for a type
|
;;;
|
||||||
;;; may still ask `(type-of x)' while it does so.
|
;;; (defmacro (ty) 'int) (var x (ty) 0) -> int x = 0;
|
||||||
|
;;;
|
||||||
|
;;; and the macro may ask `(type-of x)' while it stands in for a type.
|
||||||
(define (expand-type type env)
|
(define (expand-type type env)
|
||||||
(let ((expanded
|
(let ((expanded
|
||||||
(parameterize ((current-type-of
|
(parameterize ((current-type-of
|
||||||
@@ -594,8 +600,8 @@
|
|||||||
(and (symbol? (car expr)) (get-return-type (car expr))))
|
(and (symbol? (car expr)) (get-return-type (car expr))))
|
||||||
(else
|
(else
|
||||||
(case (car expr)
|
(case (car expr)
|
||||||
;; subscripting an array gives its element type, and a pointer
|
;; (¤ pts 1), pts : (¤ struct point 2) -> (struct point)
|
||||||
;; subscripts the same way
|
;; (¤ p 1), p : (* int) -> int
|
||||||
((¤) (let ((base (expression-type (second expr) env)))
|
((¤) (let ((base (expression-type (second expr) env)))
|
||||||
(or (array-element-type base) (pointer-target base))))
|
(or (array-element-type base) (pointer-target base))))
|
||||||
;; unary `&' takes an address; with two operands it is bitwise and
|
;; unary `&' takes an address; with two operands it is bitwise and
|
||||||
@@ -613,9 +619,8 @@
|
|||||||
(cddr expr)))
|
(cddr expr)))
|
||||||
((cast) (and (= 3 (length expr)) (third expr)))
|
((cast) (and (= 3 (length expr)) (third expr)))
|
||||||
((sizeof) 'size-t)
|
((sizeof) 'size-t)
|
||||||
;; a closure literal is its own type: the first three elements
|
;; `(closure ((x int)) int () ...)' is a `(closure ((int)) int)',
|
||||||
;; already spell one, so calling one where it is written resolves
|
;; so `((closure ((x int)) int () (return x)) 5)' is a call
|
||||||
;; like calling one through a name
|
|
||||||
((closure) (and (closure-expression? expr)
|
((closure) (and (closure-expression? expr)
|
||||||
`(closure ,(arglist-types (second expr)) ,(third expr))))
|
`(closure ,(arglist-types (second expr)) ,(third expr))))
|
||||||
;; `c-and' and `c-or' are the names from before `&&' and `||'
|
;; `c-and' and `c-or' are the names from before `&&' and `||'
|
||||||
@@ -623,11 +628,11 @@
|
|||||||
((+ - / %) (arithmetic-type expr env))
|
((+ - / %) (arithmetic-type expr env))
|
||||||
;; the bitwise operators join like the arithmetic ones
|
;; the bitwise operators join like the arithmetic ones
|
||||||
((^ |\||) (arithmetic-type expr env))
|
((^ |\||) (arithmetic-type expr env))
|
||||||
;; a shift does not join: the result is the promoted left operand,
|
;; a shift is the promoted left operand, not a join:
|
||||||
;; and the right one says only how far
|
;; (<< l b), l : long -> long; (>> c b), c : char -> int
|
||||||
((<< >>) (promoted-type (expression-type (second expr) env)))
|
((<< >>) (promoted-type (expression-type (second expr) env)))
|
||||||
;; ...and an increment is not a join either -- it is the operand,
|
;; ...and an increment is the operand unpromoted:
|
||||||
;; unpromoted, being what is written back to it
|
;; (++ c), c : char -> char
|
||||||
((++ --) (expression-type (second expr) env))
|
((++ --) (expression-type (second expr) env))
|
||||||
;; otherwise a call: a closure answers with its own return type,
|
;; otherwise a call: a closure answers with its own return type,
|
||||||
;; anything else with what its signature says
|
;; anything else with what its signature says
|
||||||
@@ -661,37 +666,36 @@
|
|||||||
(cond
|
(cond
|
||||||
((or (ptr-type? l) (array-type? l)) (decayed left l))
|
((or (ptr-type? l) (array-type? l)) (decayed left l))
|
||||||
((or (ptr-type? r) (array-type? r)) (decayed right r))
|
((or (ptr-type? r) (array-type? r)) (decayed right r))
|
||||||
;; one type on both sides needs no ranking, which is the only way
|
;; one type on both sides needs no ranking:
|
||||||
;; a name we never parsed a declaration for joins at all
|
;; (+ n n), n : size-t -> size-t
|
||||||
((and (prim-type? l) (prim-type? r) (equal? (prim-name l) (prim-name r)))
|
((and (prim-type? l) (prim-type? r) (equal? (prim-name l) (prim-name r)))
|
||||||
(promoted left l))
|
(promoted left l))
|
||||||
((or (unrankable? l) (unrankable? r)) '?)
|
((or (unrankable? l) (unrankable? r)) '?)
|
||||||
((< (conversion-rank l) (conversion-rank r)) (promoted right r))
|
((< (conversion-rank l) (conversion-rank r)) (promoted right r))
|
||||||
((> (conversion-rank l) (conversion-rank r)) (promoted left l))
|
((> (conversion-rank l) (conversion-rank r)) (promoted left l))
|
||||||
;; at equal rank C takes the unsigned one, whichever side it is
|
;; (+ i u) and (+ u i) are both unsigned int
|
||||||
;; written on
|
|
||||||
((unsigned-type? r) (promoted right r))
|
((unsigned-type? r) (promoted right r))
|
||||||
(else (promoted left l)))))))
|
(else (promoted left l)))))))
|
||||||
|
|
||||||
;;; A name we never parsed a declaration for -- `size-t', `GLuint' --
|
;;; `size-t', `GLuint': no declaration parsed, so no rank to compare.
|
||||||
;;; has no rank we can know, so a join that would have to compare one
|
;;;
|
||||||
;;; answers `?' instead of taking whichever operand came first.
|
;;; (var m _ (+ 1 n)) n : size-t -> type of this is unknown
|
||||||
;;; `resolve-wildcard' turns that into "write it out", which is the only
|
;;; (var m size-t (+ 1 n)) -> size_t m = 1 + n;
|
||||||
;;; honest thing to say about it.
|
|
||||||
(define (unrankable? type)
|
(define (unrankable? type)
|
||||||
(and (prim-type? type) (not (c-primitive? type))))
|
(and (prim-type? type) (not (c-primitive? type))))
|
||||||
|
|
||||||
(define (unsigned-type? type)
|
(define (unsigned-type? type)
|
||||||
(and (prim-type? type) (memq 'unsigned (prim-name type)) #t))
|
(and (prim-type? type) (memq 'unsigned (prim-name type)) #t))
|
||||||
|
|
||||||
;;; Anything narrower than `int' is promoted to one before the
|
;;; Narrower than `int' promotes to one:
|
||||||
;;; 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) c : char 100 -> int 200, not char -56
|
||||||
;;; `(+ c c)' is where the promotion is invisible and the truncation is
|
;;; (+ h h) h : short 30000 -> int 60000, not short -5536
|
||||||
;;; not. `unsigned' alone is `unsigned int' and stays as written.
|
;;; (+ u u) u : unsigned -> unsigned -- `unsigned' is unsigned int
|
||||||
;;; An array is a pointer to its first element the moment it is an
|
;;; An array operand is a pointer to its first element:
|
||||||
;;; operand, so `(+ a 1)' is a `(* int)' and not the `(¤ int 4)' that
|
;;;
|
||||||
;;; `a' was declared as -- which is not a type an initializer can have.
|
;;; (var p _ (+ a 1)) a : (¤ int 4) -> int * p = a + 1;
|
||||||
|
;;; not int p[4] = a + 1;
|
||||||
(define (decayed written type)
|
(define (decayed written type)
|
||||||
(if (array-type? type) (unparse-type (decay type)) written))
|
(if (array-type? type) (unparse-type (decay type)) written))
|
||||||
|
|
||||||
@@ -776,11 +780,11 @@
|
|||||||
|
|
||||||
(define +closure-env-bytes+ 16)
|
(define +closure-env-bytes+ 16)
|
||||||
|
|
||||||
;;; +closure-env-bytes+ for maximum capacity, and an alignment wide
|
;;; +closure-env-bytes+ for capacity, the widest built-ins for
|
||||||
;;; enough for anything that fits in them, hence union. `max_align_t'
|
;;; alignment, hence union -- a union takes the strictest alignment of
|
||||||
;;; would say that in one word, but it is C11 and the target is C99, so
|
;;; its members. `max_align_t' would say the second in one word:
|
||||||
;;; the widest built-ins say it instead: a union is aligned for the
|
;;;
|
||||||
;;; strictest of its members.
|
;;; sexc hello-world.sex -- -std=c99 unknown type name 'max_align_t'
|
||||||
(define +closure-env-type+ 'ƛenv)
|
(define +closure-env-type+ 'ƛenv)
|
||||||
|
|
||||||
(define (closure-env-declaration)
|
(define (closure-env-declaration)
|
||||||
@@ -854,15 +858,14 @@
|
|||||||
*pending-closure-structs*)))))
|
*pending-closure-structs*)))))
|
||||||
(delete-duplicates (aggregates-in type))))
|
(delete-duplicates (aggregates-in type))))
|
||||||
|
|
||||||
;;; Where one argument ends and the next begins has to survive the
|
;;; The words inside an argument take the single separator, the
|
||||||
;;; flattening, or `((long long))' and `((long) (long))' mangle alike and
|
;;; arguments a doubled one:
|
||||||
;;; the second signature silently reuses the first one's struct. Words
|
|
||||||
;;; within an argument keep the single separator; the arguments take a
|
|
||||||
;;; doubled one.
|
|
||||||
;;;
|
;;;
|
||||||
;;; Not proof against a type name that mangles to a trailing `_' of its
|
;;; (closure ((long long)) int) -> ƛlong_long_int
|
||||||
;;; own -- for that the arguments would have to carry their lengths, and
|
;;; (closure ((long) (long)) int) -> ƛlong__long_int
|
||||||
;;; the name in the C is worth more than the last of the ambiguity.
|
;;;
|
||||||
|
;;; A word whose first character mangles to `_' still aliases the
|
||||||
|
;;; doubled separator: `((a -b))' and `((a) (b))' are both `a__b'.
|
||||||
(define (mangle-arglist args)
|
(define (mangle-arglist args)
|
||||||
(if (null? args)
|
(if (null? args)
|
||||||
"void"
|
"void"
|
||||||
@@ -1040,8 +1043,8 @@
|
|||||||
(let ((name (capture-name capture)))
|
(let ((name (capture-name capture)))
|
||||||
(unless (symbol? name)
|
(unless (symbol? name)
|
||||||
(sex-error form "a closure capture needs a name" capture))
|
(sex-error form "a closure capture needs a name" capture))
|
||||||
;; the same lookup either way: a capture that borrows a name can
|
;; the same lookup either way, so `(closure ((x int)) int (scale)
|
||||||
;; borrow a global's or a function's, not only a local's
|
;; ...)' borrows a global's `scale' as readily as a local's
|
||||||
(let ((type (expression-type (capture-argument capture) env)))
|
(let ((type (expression-type (capture-argument capture) env)))
|
||||||
(unless type
|
(unless type
|
||||||
(sex-error form "cannot infer what is captured as" name))
|
(sex-error form "cannot infer what is captured as" name))
|
||||||
@@ -1128,23 +1131,21 @@
|
|||||||
((union) (add-union name form))
|
((union) (add-union name form))
|
||||||
((enum) (add-enum name form))))))
|
((enum) (add-enum name form))))))
|
||||||
|
|
||||||
;;; A toplevel form has no function around it and so no scope chain. A
|
;;; No function around a toplevel form, so no scope chain: what a
|
||||||
;;; name in a global's initializer is another global's or a function's,
|
;;; global's initializer names comes from `get-name-type' alone.
|
||||||
;;; which `get-name-type' answers without one.
|
|
||||||
(define (make-toplevel-env)
|
(define (make-toplevel-env)
|
||||||
(let ((env (make-hash-table)))
|
(let ((env (make-hash-table)))
|
||||||
(set! (hash-table-ref env :scopes) (list))
|
(set! (hash-table-ref env :scopes) (list))
|
||||||
env))
|
env))
|
||||||
|
|
||||||
(define (process-global-var sex-var acc)
|
(define (process-global-var sex-var acc)
|
||||||
;; A global is not walked for lambdas, but its type still has to stop
|
;; A global is not walked for lambdas, but the writer spells neither
|
||||||
;; saying `closure' before the writer sees it, and a `_' still has to
|
;; `closure' nor `_': `(var n _ 1)' has to reach it as `int n = 1'
|
||||||
;; be written out: the writer has no spelling for one either way.
|
|
||||||
(let* ((resolved (resolve-closure-types sex-var))
|
(let* ((resolved (resolve-closure-types sex-var))
|
||||||
(qualifier (and (memq (car resolved) '(pub extern)) (car resolved)))
|
(qualifier (and (memq (car resolved) '(pub extern)) (car resolved)))
|
||||||
(core (if qualifier (cdr resolved) resolved))
|
(core (if qualifier (cdr resolved) resolved))
|
||||||
;; `extern' declares without initializing, so there is nothing
|
;; `(extern var n int)' has no initializer to work a `_' out
|
||||||
;; for a `_' to be worked out from
|
;; from
|
||||||
(core (if (eq? 'extern qualifier)
|
(core (if (eq? 'extern qualifier)
|
||||||
core
|
core
|
||||||
(resolve-wildcard core (make-toplevel-env))))
|
(resolve-wildcard core (make-toplevel-env))))
|
||||||
|
|||||||
@@ -87,10 +87,9 @@
|
|||||||
;; `process' returns one record; `process-input-port' is named from
|
;; `process' returns one record; `process-input-port' is named from
|
||||||
;; the child's side, so it is the port we write to.
|
;; the child's side, so it is the port we write to.
|
||||||
;;
|
;;
|
||||||
;; Sex has no symbol escaping -- `|' is an operator there, not a
|
;; Sex reads no symbol escaping -- `|' is an operator there. Left
|
||||||
;; quote -- so the forms go out the way they were written. Left to
|
;; on, `(|| a b)' leaves here as `(|\|\|| a b)' and reaches sexc
|
||||||
;; escape, `||' would leave here as `|\|\||' and reach sexc as a
|
;; as a different symbol.
|
||||||
;; different symbol.
|
|
||||||
(let* ((proc (process compiler (append (list "-o" compiled-file) flags)))
|
(let* ((proc (process compiler (append (list "-o" compiled-file) flags)))
|
||||||
(sexc-stdin (process-input-port proc)))
|
(sexc-stdin (process-input-port proc)))
|
||||||
(symbol-escape #f)
|
(symbol-escape #f)
|
||||||
|
|||||||
34
types.scm
34
types.scm
@@ -240,14 +240,14 @@
|
|||||||
|
|
||||||
;;; The shape of a written type
|
;;; The shape of a written type
|
||||||
;;;
|
;;;
|
||||||
;;; Three places have to tell a type from something that merely
|
;;; Where a type ends, asked by an arglist and by an array bound:
|
||||||
;;; contains one: an arglist entry is either `(name type)' or a bare
|
;;;
|
||||||
;;; type, and an array's last element is either a bound or the last
|
;;; (f1 float) a name and a type (unsigned int) a type
|
||||||
;;; word of its element type. They used to answer it separately, and
|
;;; (¤ int 4) four of int (¤ const t) unsized, of const t
|
||||||
;;; disagreed.
|
;;; (¤ mytype N) N of mytype (¤ * size-t) unsized, of (* size-t)
|
||||||
|
|
||||||
;;; A qualifier can never end a type, which is what tells `(¤ const t)'
|
;;; A qualifier cannot end a type: `(¤ const t)' is unsized, `(¤ int 4)'
|
||||||
;;; -- an unsized array of `t' -- from `(¤ int 4)'.
|
;;; is four of int.
|
||||||
(define +c-qualifiers+ '(const volatile restrict _Atomic))
|
(define +c-qualifiers+ '(const volatile restrict _Atomic))
|
||||||
|
|
||||||
(define +c-specifiers+
|
(define +c-specifiers+
|
||||||
@@ -271,20 +271,22 @@
|
|||||||
(pair? (cdr arg)) ; 1 element args are always type
|
(pair? (cdr arg)) ; 1 element args are always type
|
||||||
(not (type-head? arg))))
|
(not (type-head? arg))))
|
||||||
|
|
||||||
;;; Is NAME a typedef, as opposed to a `define'd constant? Both live in
|
;;; A typedef and a `define' share +type-db+; only the typedef is part
|
||||||
;;; the same table, and only the first is part of a type.
|
;;; of a type:
|
||||||
|
;;;
|
||||||
|
;;; (typedef small int) -> (¤ small N) is N of small
|
||||||
|
;;; (define CAP 4) -> (¤ int CAP) is CAP of int
|
||||||
(define (typedef-name? name)
|
(define (typedef-name? name)
|
||||||
(let ((info (and (symbol? name) (get-type-info name))))
|
(let ((info (and (symbol? name) (get-type-info name))))
|
||||||
(and info (memq (car info) '(typedef struct union enum)) #t)))
|
(and info (memq (car info) '(typedef struct union enum)) #t)))
|
||||||
|
|
||||||
;;; `(¤ int N)' is N of int
|
;;; The last element is a bound only where what precedes it already
|
||||||
;;; `(¤ unsigned int)' is an unsized array of unsigned int
|
;;; spells a whole type -- a specifier, a tag after its keyword, or a
|
||||||
|
;;; typedef we have seen declared:
|
||||||
;;;
|
;;;
|
||||||
;;; The last element is a bound only if what precedes it is already a
|
;;; (¤ int 4) four of int (¤ unsigned int) unsized
|
||||||
;;; complete type, so `(¤ const mytype)' and `(¤ * size-t)' end in the
|
;;; (¤ mytype CAP) CAP of mytype (¤ struct point) unsized
|
||||||
;;; last word of their element type and not in a bound. A type is
|
;;; (¤ const mytype) unsized (¤ * size-t) unsized
|
||||||
;;; complete when it ends in a specifier, in a tag following its
|
|
||||||
;;; keyword, or in a typedef we have seen declared.
|
|
||||||
;;;
|
;;;
|
||||||
;;; TYPE is the whole `(¤ ...)' form.
|
;;; TYPE is the whole `(¤ ...)' form.
|
||||||
(define (array-bound? type)
|
(define (array-bound? type)
|
||||||
|
|||||||
Reference in New Issue
Block a user