1
0
forked from alex-eg/sex

read a var's type as a type, not as a call

`walk-parts' walked the whole `var' form, and a `fn' type's parameter
list is shaped like a call: `(fn ((c int)) int)' came out as
`int (*fp)(c int)'. A closure literal had no type, so calling one in
place missed the rewrite; sextest fed sexc symbols Sex cannot read.
This commit is contained in:
2026-09-30 23:20:55 +03:00
parent e87463be87
commit fe72a109bf
4 changed files with 153 additions and 13 deletions

View File

@@ -274,12 +274,15 @@
;;; (fn sum ((a int) (b int)) int ...) -> (fn ((int) (int)) int).
;;; Anything that is not a plain (name type) -- a variadic tail -- goes
;;; through untouched.
(define (arglist-types arglist)
(map (lambda (param)
(if (and (pair? param) (= 2 (length param)) (named-arg? param))
(list (second param))
param))
arglist))
(define (fn-type-of fn-form)
`(fn ,(map (lambda (param)
(if (and (pair? param) (= 2 (length param)) (named-arg? param))
(list (second param))
param))
(sex-fn-arglist fn-form))
`(fn ,(arglist-types (sex-fn-arglist fn-form))
,(sex-fn-return-type fn-form)))
(define (aux-name! env make)
@@ -379,8 +382,24 @@
(fourth form))))))))
((var)
;; the initializer is walked before the name it binds is in scope
(let* ((walked (resolve-wildcard (walk-parts form env) env))
;; the initializer is walked before the name it binds is in
;; scope; the type is resolved rather than walked, a `fn' type's
;; parameter list being indistinguishable from a call -- walking
;; `(fn ((c int)) int)' with a closure named `c' in scope would
;; rewrite the parameter as a call of it
(let* ((prefix (if (>= (length form) 3)
(append (take form 2)
(list (resolve-closure-types
(expand-type (third form) env))))
form))
(walked (resolve-wildcard
(copy-form-source!
form
(append prefix
(if (> (length form) 3)
(walk-body (drop form 3) env)
(list))))
env))
(bound (if (>= (length walked) 4)
(copy-form-source!
walked
@@ -408,12 +427,16 @@
(else
(let ((closure (receiver-closure-type (car form) env)))
(if closure
(copy-form-source!
form
`(,(register-closure-call! closure form)
,(walk-statement (car form) env)
,@(map (lambda (argument) (walk-statement argument env))
(cdr form))))
;; the receiver is walked first: a closure written where it
;; is called registers its struct on the way, and the call
;; helper's signature mentions that struct
(let ((receiver (walk-statement (car form) env)))
(copy-form-source!
form
`(,(register-closure-call! closure form)
,receiver
,@(map (lambda (argument) (walk-statement argument env))
(cdr form)))))
(convert-arguments (walk-parts form env) env))))))))
;;; `(var n _ (strlen s))' becomes `(var n size-t (strlen s))'.
@@ -507,6 +530,21 @@
(else (pair (cdr params) (- remaining 1)
(cons (unwrap-type (car params)) acc))))))
;;; A `fn' header has its macros expanded and its closure types
;;; resolved without being walked; a `var' type is the same thing in the
;;; same position, and gets the same two. A macro standing in for a type
;;; may still ask `(type-of x)' while it does so.
(define (expand-type type env)
(let ((expanded
(parameterize ((current-type-of
(lambda (queried)
(unresolve-closure-types
(expression-type queried env)))))
(macro-expand (list type)))))
(if (and (pair? expanded) (null? (cdr expanded)))
(car expanded)
expanded)))
(define (walk-parts form env)
(copy-form-source! form (walk-body form env)))
@@ -575,6 +613,11 @@
(cddr expr)))
((cast) (and (= 3 (length expr)) (third expr)))
((sizeof) 'size-t)
;; a closure literal is its own type: the first three elements
;; already spell one, so calling one where it is written resolves
;; like calling one through a name
((closure) (and (closure-expression? expr)
`(closure ,(arglist-types (second expr)) ,(third expr))))
;; `c-and' and `c-or' are the names from before `&&' and `||'
((== != < > <= >= && |\|\|| ! c-and c-or) 'bool)
((+ - / %) (arithmetic-type expr env))