separate tags and typedefs

C has two namespaces and the database had one. A typedef of a tag's
name wiped it.

Tags, i.e. struct, enum and union, share one namespace; they move to a
table of their own. A name resolves the way C does: the ordinary
identifier first, the tag when that leads nowhere.

While resolving a typedef, the target was taken apart with cadr
whatever it was, so (typedef points (¤ point 4)) reported point's
fields as its own -- an array of four points claiming to be a point,
and a macro reaching through it with v->x. Only a name and
(struct|union|enum NAME) name a type now.
This commit is contained in:
2026-09-21 00:46:15 +03:00
parent e0a228c66e
commit 381ad21d8b
5 changed files with 72 additions and 16 deletions

View File

@@ -15,7 +15,11 @@
srfi-1
srfi-69)
(define +type-db+ (make-hash-table))
;;; Two namespaces: `struct point' and a `point' typedef are separate
;;; declarations. Tags -- struct, union and enum alike -- share the
;;; second table between them
(define +type-db+ (make-hash-table)) ; typedefs and defines
(define +tag-db+ (make-hash-table)) ; struct, union and enums
(define (strip-pub form)
(if (eq? (car form) 'pub) (cdr form) form))
@@ -44,17 +48,17 @@
(list))))
(define (add-struct name form)
(hash-table-set! +type-db+ name
(hash-table-set! +tag-db+ name
(list 'struct name (normalize-fields (aggregate-fields form)))))
(define (add-union name form)
(hash-table-set! +type-db+ name
(hash-table-set! +tag-db+ name
(list 'union name (normalize-fields (aggregate-fields form)))))
;;; ([pub] enum name (value ...))
(define (add-enum name form)
(let ((f (strip-pub form)))
(hash-table-set! +type-db+ name
(hash-table-set! +tag-db+ name
(list 'enum name (if (and (pair? (cddr f)) (list? (caddr f)))
(caddr f)
(list))))))
@@ -70,8 +74,14 @@
(hash-table-set! +type-db+ name
(list 'define name (cddr (strip-pub form)))))
(define (get-tag-info name)
(hash-table-ref/default +tag-db+ name #f))
;;; First look up the ordinary identifier, then tag of that id when
;;; no ordinary one was declared, as C does
(define (get-type-info name)
(hash-table-ref/default +type-db+ name #f))
(or (hash-table-ref/default +type-db+ name #f)
(get-tag-info name)))
;;; ((name type) ...) for a struct or union, #f for anything else --
;;; including a name that was never declared. Callers give the better
@@ -100,19 +110,31 @@
;;; The declaration NAME ultimately names. For a typedef that is the
;;; entry of whatever it stands for, and for anything else it is
;;; NAME's own info.
;;; NAME's own info
(define (target-tag target)
(cond
((symbol? target) target)
((and (pair? target)
(memq (car target) '(struct union enum))
(pair? (cdr target))
(symbol? (cadr target)))
(cadr target))
(else #f)))
(define (resolve-type-info name)
(let ((info (resolve-ordinary-type-info name)))
(if (and info (memq (car info) '(struct union enum)))
info
(or (get-tag-info name) info))))
(define (resolve-ordinary-type-info name)
(let ((info (get-type-info name)))
(and info
(if (eq? (car info) 'typedef)
(let* ((target (get-underlying-type name))
(tag (cond ((symbol? target) target)
((and (pair? target)
(pair? (cdr target))
(symbol? (cadr target)))
(cadr target))
(else #f))))
(and tag (get-type-info tag)))
(let ((tag (target-tag (get-underlying-type name))))
;; A typedef target is written in type position, where
;; `struct point' means the tag
(and tag (or (get-tag-info tag) (get-type-info tag))))
info))))
;;; Type matcher macro