resolve-free-vars.scm 9.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289
  1. ;;; Resolving free top-level references to modules
  2. ;;; Copyright (C) 2021-2022
  3. ;;; Free Software Foundation, Inc.
  4. ;;;
  5. ;;; This library is free software: you can redistribute it and/or modify
  6. ;;; it under the terms of the GNU Lesser General Public License as
  7. ;;; published by the Free Software Foundation, either version 3 of the
  8. ;;; License, or (at your option) any later version.
  9. ;;;
  10. ;;; This library is distributed in the hope that it will be useful, but
  11. ;;; WITHOUT ANY WARRANTY; without even the implied warranty of
  12. ;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
  13. ;;; Lesser General Public License for more details.
  14. ;;;
  15. ;;; You should have received a copy of the GNU Lesser General Public
  16. ;;; License along with this program. If not, see
  17. ;;; <http://www.gnu.org/licenses/>.
  18. (define-module (language tree-il resolve-free-vars)
  19. #:use-module (ice-9 match)
  20. #:use-module (language tree-il)
  21. #:use-module ((srfi srfi-1) #:select (filter-map))
  22. #:export (resolve-free-vars))
  23. (define (compute-assigned-lexicals exp)
  24. (define assigned-lexicals '())
  25. (define (add-assigned-lexical! var)
  26. (set! assigned-lexicals (cons var assigned-lexicals)))
  27. ((make-tree-il-folder)
  28. exp
  29. (lambda (exp)
  30. (match exp
  31. (($ <lexical-set> _ _ var _)
  32. (add-assigned-lexical! var)
  33. (values))
  34. (_ (values))))
  35. (lambda (exp)
  36. (values)))
  37. assigned-lexicals)
  38. (define (make-resolver mod local-definitions)
  39. ;; Given that module A imports B and C, and X is free in A,
  40. ;; unfortunately there are a few things preventing us from knowing
  41. ;; whether the binding proceeds from B or C, just based on the text:
  42. ;;
  43. ;; - Renamers are evaluated at run-time.
  44. ;; - Just using B doesn't let us know what definitions are in B.
  45. ;;
  46. ;; So instead of using the source program to determine where a binding
  47. ;; comes from, we use the first-class module interface.
  48. (define (imported-resolver iface)
  49. (let ((by-var (make-hash-table)))
  50. ;; When resolving a free variable, Guile visits all used modules
  51. ;; to see if there is a binding. If one of those imports is an
  52. ;; autoload, it's possible that the autoload interface fails to
  53. ;; load. In that case Guile will issue a warning and consider the
  54. ;; binding not found in that module. Here we try to produce the
  55. ;; same behavior at optimization time that we do at expand time
  56. ;; that we would do at run time.
  57. (false-if-exception
  58. (let ((public-iface (resolve-interface (module-name iface))))
  59. (module-for-each (lambda (name var)
  60. (hashq-set! by-var var name))
  61. public-iface))
  62. #:warning "Failed to determine exported bindings from module ~a:\n"
  63. (module-name iface))
  64. (lambda (name)
  65. (let ((var (module-variable iface name)))
  66. (and var
  67. (cons (module-name iface)
  68. (hashq-ref by-var var)))))))
  69. (define the-module (resolve-module mod))
  70. (define resolvers
  71. (map imported-resolver (module-uses the-module)))
  72. (lambda (name)
  73. (cond
  74. ((or (module-local-variable the-module name)
  75. (memq name local-definitions))
  76. 'local)
  77. (else
  78. (match (filter-map (lambda (resolve) (resolve name)) resolvers)
  79. (() 'unknown)
  80. (((mod . #f)) 'unknown)
  81. (((mod . public-name)) (cons mod public-name))
  82. ((_ _ . _) 'duplicate))))))
  83. ;;; Record all bindings in a module, to know whether a toplevel-ref is
  84. ;;; an import or not. If toplevel-ref to imported variable, transform
  85. ;;; to module-ref or primitive-ref. New pass before peval.
  86. (define (compute-free-var-resolver exp)
  87. (define assigned-lexicals (compute-assigned-lexicals exp))
  88. (define module-definitions '())
  89. (define module-lexicals '())
  90. (define bindings '())
  91. (define (add-module-definition! mod args)
  92. (set! module-definitions (acons mod args module-definitions)))
  93. (define (add-module-lexical! var mod)
  94. (unless (memq var assigned-lexicals)
  95. (set! module-lexicals (acons var mod module-lexicals))))
  96. (define (add-binding! mod name)
  97. (set! bindings (acons mod name bindings)))
  98. (define (record-bindings! mod vars vals)
  99. (for-each
  100. (lambda (var val)
  101. (match val
  102. (($ <call> _ ($ <module-ref> _ '(guile) 'define-module* #f)
  103. (($ <const> _ mod) . args))
  104. (add-module-definition! mod args)
  105. (add-module-lexical! var mod))
  106. (($ <primcall> _ 'current-module ())
  107. (when mod
  108. (add-module-lexical! var mod)))
  109. (_ #f)))
  110. vars vals))
  111. ;; Thread a conservative idea of what the current module is through
  112. ;; the visit. Visiting an expression returns the name of the current
  113. ;; module when the expression completes, or #f if unknown. Record the
  114. ;; define-module* forms, if any, and note any toplevel definitions.
  115. (define (visit exp) (visit/mod exp #f))
  116. (define (visit* exps)
  117. (unless (null? exps)
  118. (visit (car exps))
  119. (visit* (cdr exps))))
  120. (define (visit+ exps mod)
  121. (match exps
  122. (() mod)
  123. ((exp . exps)
  124. (let lp ((mod' (visit/mod exp mod)) (exps exps))
  125. (match exps
  126. (() mod')
  127. ((exp . exps)
  128. (lp (and (equal? mod' (visit/mod exp mod)) mod')
  129. exps)))))))
  130. (define (visit/mod exp mod)
  131. (match exp
  132. ((or ($ <void>) ($ <const>) ($ <primitive-ref>) ($ <lexical-ref>)
  133. ($ <module-ref>) ($ <toplevel-ref>))
  134. mod)
  135. (($ <call> _ ($ <module-ref> _ '(guile) 'set-current-module #f)
  136. (($ <lexical-ref> _ _ var)))
  137. (assq-ref module-lexicals var))
  138. (($ <call> _ proc args)
  139. (visit proc)
  140. (visit* args)
  141. #f)
  142. (($ <primcall> _ _ args)
  143. ;; There is no primcall that sets the current module.
  144. (visit+ args mod))
  145. (($ <conditional> src test consequent alternate)
  146. (visit+ (list consequent alternate) (visit/mod test mod)))
  147. (($ <lexical-set> src name gensym exp)
  148. (visit/mod exp mod))
  149. (($ <toplevel-set> src mod name exp)
  150. (visit/mod exp mod))
  151. (($ <module-set> src mod name public? exp)
  152. (visit/mod exp mod))
  153. (($ <toplevel-define> src mod name exp)
  154. (add-binding! mod name)
  155. (visit/mod exp mod))
  156. (($ <lambda> src meta body)
  157. (when body (visit body))
  158. mod)
  159. (($ <lambda-case> src req opt rest kw inits gensyms body alternate)
  160. (visit* inits)
  161. (let* ((bodies (cons body inits))
  162. (bodies (if alternate (cons alternate bodies) bodies)))
  163. (visit+ bodies mod)))
  164. (($ <seq> src head tail)
  165. (visit/mod tail (visit/mod head mod)))
  166. (($ <let> src names gensyms vals body)
  167. (record-bindings! mod gensyms vals)
  168. (visit/mod body (visit+ vals mod)))
  169. (($ <letrec> src in-order? names gensyms vals body)
  170. (record-bindings! mod gensyms vals)
  171. (visit/mod body (visit+ vals mod)))
  172. (($ <fix> src names gensyms vals body)
  173. (record-bindings! mod gensyms vals)
  174. (visit/mod body (visit+ vals mod)))
  175. (($ <let-values> src exp body)
  176. (visit/mod body (visit/mod exp mod)))
  177. (($ <prompt> src escape-only? tag body handler)
  178. (visit+ (list body handler) (visit/mod tag mod)))
  179. (($ <abort> src tag args tail)
  180. (visit tag)
  181. (visit* args)
  182. (visit tail)
  183. #f)))
  184. (visit exp)
  185. (define (kwarg-ref args kw kt kf)
  186. (let lp ((args args))
  187. (match args
  188. (() (kf))
  189. ((($ <const> _ (? keyword? kw')) val . args)
  190. (if (eq? kw' kw)
  191. (kt val)
  192. (lp args)))
  193. ((_ _ . args)
  194. (lp args)))))
  195. (define (kwarg-ref/const args kw kt kf)
  196. (kwarg-ref args kw
  197. (lambda (exp)
  198. (match exp
  199. (($ <const> _ val') (kt val'))
  200. (_ (kf))))
  201. kf))
  202. (define (has-constant-initarg? args kw val)
  203. (kwarg-ref/const args kw
  204. (lambda (val')
  205. (equal? val val'))
  206. (lambda () #f)))
  207. ;; Collect declarative modules defined once in this compilation unit.
  208. (define declarative-modules
  209. (let lp ((defs module-definitions) (not-declarative '()) (declarative '()))
  210. (match defs
  211. (() declarative)
  212. (((mod . args) . defs)
  213. (cond ((member mod not-declarative)
  214. (lp defs not-declarative declarative))
  215. ((or (assoc mod defs) ;; doubly defined?
  216. (not (has-constant-initarg? args #:declarative? #t)))
  217. (lp defs (cons mod not-declarative) declarative))
  218. (else
  219. (lp defs not-declarative (cons mod declarative))))))))
  220. (define resolvers
  221. (map (lambda (mod)
  222. (define resolve
  223. (make-resolver mod
  224. (filter-map (match-lambda
  225. ((mod' . name)
  226. (and (equal? mod mod') name)))
  227. bindings)))
  228. (cons mod resolve))
  229. declarative-modules))
  230. (lambda (mod name)
  231. (cond
  232. ((assoc-ref resolvers mod)
  233. => (lambda (resolve) (resolve name)))
  234. (else 'unknown))))
  235. (define (resolve-free-vars exp)
  236. "Traverse @var{exp}, extracting module-level definitions."
  237. (define resolve
  238. (compute-free-var-resolver exp))
  239. (post-order
  240. (lambda (exp)
  241. (match exp
  242. (($ <toplevel-ref> src mod name)
  243. (match (resolve mod name)
  244. ((or 'unknown 'duplicate 'local) exp)
  245. ((mod . name)
  246. (make-module-ref src mod name #t))))
  247. (($ <toplevel-set> src mod name val)
  248. (match (resolve mod name)
  249. ((or 'unknown 'duplicate 'local) exp)
  250. ((mod . name)
  251. (make-module-set src mod name #t val))))
  252. (exp exp)))
  253. exp))