recon.scm 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336
  1. ; Part of Scheme 48 1.9. See file COPYING for notices and license.
  2. ; Authors: Richard Kelsey, Jonathan Rees, Mike Sperber
  3. ; Rudimentary type reconstruction, hardly worthy of the name.
  4. ; Currently, NODE-TYPE is called in two places. One is to determine
  5. ; the type of the right-hand side of a DEFINE for a variable that is
  6. ; never assigned, so uses of the variable can be checked later. The
  7. ; other is when compiling a call, to check types of arguments and
  8. ; produce warning messages.
  9. ; This is heuristic, to say the least. It's not clear what the right
  10. ; interface or formalism is for Scheme; I'm still experimenting.
  11. ; Obviously we can't do Hindley-Milner inference. Not only does
  12. ; Scheme have subtyping, but it also has dependent types up the wazoo.
  13. ; For example, the following is perfectly correct Scheme:
  14. ;
  15. ; (define (foo x y) (if (even? x) (car y) (vector-ref y 3)))
  16. (define (node-type node)
  17. (reconstruct node 'fast any-values-type))
  18. (define (reconstruct-type node env)
  19. (reconstruct node '() any-values-type))
  20. (define (reconstruct node constrained want-type)
  21. ((operator-table-ref reconstructors (node-operator-id node))
  22. node
  23. constrained
  24. want-type))
  25. (define (examine node constrained want-type)
  26. (if (pair? constrained)
  27. (reconstruct node constrained want-type)
  28. want-type))
  29. (define reconstructors
  30. (make-operator-table (lambda (node constrained want-type)
  31. (reconstruct-call (node-form node)
  32. constrained
  33. want-type))))
  34. (define (define-reconstructor name type proc)
  35. (operator-define! reconstructors name type proc))
  36. (define-reconstructor 'lambda syntax-type
  37. (lambda (node constrained want-type)
  38. (reconstruct-lambda node constrained want-type #f)))
  39. (define-reconstructor 'flat-lambda syntax-type
  40. (lambda (node constrained want-type)
  41. (reconstruct-lambda node constrained want-type #f)))
  42. (define (reconstruct-lambda node constrained want-type called?)
  43. (if (eq? constrained 'fast)
  44. any-procedure-type
  45. (let* ((form (node-form node))
  46. (want-result (careful-codomain want-type))
  47. (formals (cadr form))
  48. (alist (map (lambda (node)
  49. (cons node value-type))
  50. (normalize-formals formals)))
  51. (cod (reconstruct (last form) ; works for normal and flat
  52. (if called?
  53. (append alist constrained)
  54. alist)
  55. want-result)))
  56. (procedure-type (if (n-ary? formals)
  57. any-values-type ;lose
  58. (make-some-values-type (map cdr alist)))
  59. cod
  60. #t))))
  61. (define (careful-codomain proc-type)
  62. (if (procedure-type? proc-type)
  63. (procedure-type-codomain proc-type)
  64. any-values-type))
  65. (define-reconstructor 'name 'leaf
  66. (lambda (node constrained want-type)
  67. (if (eq? constrained 'fast)
  68. (reconstruct-name node)
  69. (let ((z (assq node constrained)))
  70. (if z
  71. (let ((type (meet-type (cdr z) want-type)))
  72. (begin (set-cdr! z type)
  73. type))
  74. (reconstruct-name node))))))
  75. (define (reconstruct-name node)
  76. (let ((probe (node-ref node 'binding)))
  77. (if (binding? probe)
  78. (let ((type (binding-type probe)))
  79. (cond ((variable-type? type)
  80. (variable-value-type type))
  81. ((subtype? type value-type)
  82. type)
  83. (else
  84. value-type)))
  85. value-type)))
  86. (define-reconstructor 'call 'internal
  87. (lambda (node constrained want-type)
  88. (let ((form (node-form node)))
  89. (cond ((proc->reconstructor (car form))
  90. => (lambda (recon)
  91. (recon (cdr form) constrained want-type)))
  92. (else
  93. (reconstruct-call form constrained want-type))))))
  94. ; See if PROC is a primop or a variable bound to a primop, and then return
  95. ; that primops reconstructor, if it has one.
  96. (define (proc->reconstructor proc)
  97. (cond ((name-node? proc)
  98. (let ((probe (node-ref proc 'binding)))
  99. (if (and probe
  100. (binding? probe)
  101. (primop? (binding-static probe)))
  102. (table-ref primop-reconstructors
  103. (binding-static probe))
  104. #f)))
  105. ((literal-node? proc)
  106. (if (primop? (node-form proc))
  107. (table-ref primop-reconstructors
  108. (node-form proc))
  109. #f))
  110. (else #f)))
  111. (define (reconstruct-call form constrained want-type)
  112. (let* ((want-op-type (procedure-type any-arguments-type
  113. want-type
  114. #f))
  115. (op-type (if (lambda-node? (car form))
  116. (reconstruct-lambda (car form)
  117. constrained
  118. want-op-type
  119. #t)
  120. (reconstruct (car form)
  121. constrained
  122. want-op-type)))
  123. (args (cdr form))
  124. (lose (lambda ()
  125. (for-each (lambda (arg)
  126. (examine arg constrained value-type))
  127. args))))
  128. (if (procedure-type? op-type)
  129. (begin (if (restrictive? op-type)
  130. (let loop ((args args)
  131. (dom (procedure-type-domain op-type)))
  132. (if (not (or (null? args)
  133. (empty-rail-type? dom)))
  134. (begin (examine (car args)
  135. constrained
  136. (head-type dom))
  137. (loop (cdr args) (tail-type dom)))))
  138. (lose))
  139. (procedure-type-codomain op-type))
  140. (begin (lose)
  141. any-values-type))))
  142. (define-reconstructor 'literal 'leaf
  143. (lambda (node constrained want-type)
  144. (constant-type (node-form node))))
  145. (define-reconstructor 'quote syntax-type
  146. (lambda (node constrained want-type)
  147. (constant-type (cadr (node-form node)))))
  148. (define-reconstructor 'unspecific #f
  149. (lambda (node constrained wnat-type)
  150. unspecific-type))
  151. (define-reconstructor 'unassigned #f
  152. (lambda (node constrained wnat-type)
  153. unspecific-type))
  154. (define-reconstructor 'if syntax-type
  155. (lambda (node constrained want-type)
  156. (let ((form (node-form node)))
  157. (examine (cadr form) constrained value-type)
  158. ;; Fork off two different constrain sets
  159. (let ((con-alist (fork-constraints constrained))
  160. (alt-alist (fork-constraints constrained)))
  161. (let ((con-type (reconstruct (caddr form) con-alist want-type))
  162. (alt-type (reconstruct (cadddr form) alt-alist want-type)))
  163. (if (pair? constrained)
  164. (for-each (lambda (c1 c2 c)
  165. (set-cdr! c (join-type (cdr c1) (cdr c2))))
  166. con-alist
  167. alt-alist
  168. constrained))
  169. (join-type con-type alt-type))))))
  170. (define (fork-constraints constrained)
  171. (if (pair? constrained)
  172. (map (lambda (x) (cons (car x) (cdr x)))
  173. constrained)
  174. constrained))-
  175. (define-reconstructor 'begin syntax-type
  176. (lambda (node constrained want-type)
  177. ;; This is unsound - there might be a throw out of some subform
  178. ;; other than the final one.
  179. (do ((forms (cdr (node-form node)) (cdr forms)))
  180. ((null? (cdr forms))
  181. (reconstruct (car forms) constrained want-type))
  182. (examine (car forms) constrained any-values-type))))
  183. (define-reconstructor 'set! syntax-type
  184. (lambda (node constrained want-type)
  185. (examine (caddr (node-form node)) constrained value-type)
  186. unspecific-type))
  187. (let ((letrec-reconstructor
  188. (lambda (node constrained want-type)
  189. (let ((form (node-form node)))
  190. (reconstruct-letrec (cadr form) (caddr form) constrained want-type)))))
  191. (define-reconstructor 'letrec syntax-type
  192. letrec-reconstructor)
  193. (define-reconstructor 'letrec* syntax-type
  194. letrec-reconstructor))
  195. (define-reconstructor 'pure-letrec syntax-type
  196. (lambda (node constrained want-type)
  197. (let ((form (node-form node)))
  198. (reconstruct-letrec (cadr form) (cadddr form) constrained want-type))))
  199. (define (reconstruct-letrec specs body constrained want-type)
  200. (if (eq? constrained 'fast)
  201. (reconstruct body 'fast want-type)
  202. (let ((alist (map (lambda (spec)
  203. (cons (car spec)
  204. (reconstruct (cadr spec)
  205. constrained
  206. value-type)))
  207. specs)))
  208. (reconstruct body
  209. (append alist constrained)
  210. want-type))))
  211. (define-reconstructor 'loophole syntax-type
  212. (lambda (node constrained want-type)
  213. (let ((args (cdr (node-form node))))
  214. (examine (cadr args) constrained any-values-type)
  215. (car args))))
  216. (define (node->type node)
  217. (if (node? node)
  218. (let ((form (node-form node)))
  219. (if (pair? form)
  220. (map node->type form)
  221. (desyntaxify form)))
  222. (desyntaxify node)))
  223. (define-reconstructor 'define syntax-type
  224. (lambda (node constrained want-type)
  225. ':definition))
  226. (define-reconstructor 'lap syntax-type
  227. (lambda (node constrained want-type)
  228. any-procedure-type))
  229. ; --------------------
  230. ; Primops.
  231. ;
  232. ; Most primops just have the types assigned in comp-prim.scm.
  233. (define primop-reconstructors (make-symbol-table))
  234. (define (define-primop-reconstructor name proc)
  235. (table-set! primop-reconstructors name proc))
  236. (define-reconstructor 'primitive-procedure syntax-type
  237. (lambda (node constrained want-type)
  238. (primop-type (get-primop (cadr (node-form node))))))
  239. (define-primop-reconstructor 'values
  240. (lambda (args constrained want-type)
  241. (make-some-values-type (map (lambda (node)
  242. (meet-type
  243. (reconstruct node constrained value-type)
  244. value-type))
  245. args))))
  246. (define-primop-reconstructor 'call-with-values
  247. (lambda (args constrained want-type)
  248. (if (= (length args) 2)
  249. (let ((thunk-type (reconstruct (car args)
  250. constrained
  251. (procedure-type empty-rail-type
  252. any-values-type
  253. #f))))
  254. (careful-codomain
  255. (reconstruct (cadr args)
  256. constrained
  257. (procedure-type (careful-codomain thunk-type)
  258. any-values-type
  259. #f))))
  260. error-type)))
  261. (define (reconstruct-apply args constrained want-type)
  262. (if (not (null? args))
  263. (let ((proc-type (reconstruct (car args)
  264. constrained
  265. any-procedure-type)))
  266. (for-each (lambda (arg) (examine arg constrained value-type))
  267. (cdr args))
  268. (careful-codomain proc-type))
  269. error-type))
  270. (define-primop-reconstructor 'apply reconstruct-apply)
  271. (define-primop-reconstructor 'primitive-catch reconstruct-apply)
  272. (define (constant-type x)
  273. (cond ((number? x)
  274. (meet-type (if (exact? x) exact-type inexact-type)
  275. (cond ((integer? x) integer-type)
  276. ((rational? x) rational-type)
  277. ((real? x) real-type)
  278. ((complex? x) complex-type)
  279. (else number-type))))
  280. ((boolean? x) boolean-type)
  281. ((pair? x) pair-type)
  282. ((string? x) string-type)
  283. ((char? x) char-type)
  284. ((null? x) null-type)
  285. ((symbol? x) symbol-type)
  286. ((primop? x) (primop-type x))
  287. ((vector? x) vector-type)
  288. (else value-type)))