verify.scm 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305
  1. ;;; Diagnostic checker for CPS
  2. ;;; Copyright (C) 2014, 2015 Free Software Foundation, Inc.
  3. ;;;
  4. ;;; This library is free software: you can redistribute it and/or modify
  5. ;;; it under the terms of the GNU Lesser General Public License as
  6. ;;; published by the Free Software Foundation, either version 3 of the
  7. ;;; License, or (at your option) any later version.
  8. ;;;
  9. ;;; This library is distributed in the hope that it will be useful, but
  10. ;;; WITHOUT ANY WARRANTY; without even the implied warranty of
  11. ;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
  12. ;;; Lesser General Public License for more details.
  13. ;;;
  14. ;;; You should have received a copy of the GNU Lesser General Public
  15. ;;; License along with this program. If not, see
  16. ;;; <http://www.gnu.org/licenses/>.
  17. ;;; Commentary:
  18. ;;;
  19. ;;; A routine to detect invalid CPS.
  20. ;;;
  21. ;;; Code:
  22. (define-module (language cps verify)
  23. #:use-module (ice-9 match)
  24. #:use-module (language cps)
  25. #:use-module (language cps utils)
  26. #:use-module (language cps intmap)
  27. #:use-module (language cps intset)
  28. #:use-module (language cps primitives)
  29. #:use-module (srfi srfi-11)
  30. #:export (verify))
  31. (define (intset-pop set)
  32. (match (intset-next set)
  33. (#f (values set #f))
  34. (i (values (intset-remove set i) i))))
  35. (define-syntax-rule (make-worklist-folder* seed ...)
  36. (lambda (f worklist seed ...)
  37. (let lp ((worklist worklist) (seed seed) ...)
  38. (call-with-values (lambda () (intset-pop worklist))
  39. (lambda (worklist i)
  40. (if i
  41. (call-with-values (lambda () (f i seed ...))
  42. (lambda (i* seed ...)
  43. (let add ((i* i*) (worklist worklist))
  44. (match i*
  45. (() (lp worklist seed ...))
  46. ((i . i*) (add i* (intset-add worklist i)))))))
  47. (values seed ...)))))))
  48. (define worklist-fold*
  49. (case-lambda
  50. ((f worklist seed)
  51. ((make-worklist-folder* seed) f worklist seed))))
  52. (define (check-distinct-vars conts)
  53. (define (adjoin-def var seen)
  54. (when (intset-ref seen var)
  55. (error "duplicate var name" seen var))
  56. (intset-add seen var))
  57. (intmap-fold
  58. (lambda (label cont seen)
  59. (match (intmap-ref conts label)
  60. (($ $kargs names vars ($ $continue k src exp))
  61. (fold1 adjoin-def vars seen))
  62. (($ $kfun src meta self tail clause)
  63. (adjoin-def self seen))
  64. (_ seen))
  65. )
  66. conts
  67. empty-intset))
  68. (define (compute-available-definitions conts kfun)
  69. "Compute and return a map of LABEL->VAR..., where VAR... are the
  70. definitions that are available at LABEL."
  71. (define (adjoin-def var defs)
  72. (when (intset-ref defs var)
  73. (error "var already present in defs" defs var))
  74. (intset-add defs var))
  75. (define (propagate defs succ out)
  76. (let* ((in (intmap-ref defs succ (lambda (_) #f)))
  77. (in* (if in (intset-intersect in out) out)))
  78. (if (eq? in in*)
  79. (values '() defs)
  80. (values (list succ)
  81. (intmap-add defs succ in* (lambda (old new) new))))))
  82. (define (visit-cont label defs)
  83. (let ((in (intmap-ref defs label)))
  84. (define (propagate0 out)
  85. (values '() defs))
  86. (define (propagate1 succ out)
  87. (propagate defs succ out))
  88. (define (propagate2 succ0 succ1 out)
  89. (let*-values (((changed0 defs) (propagate defs succ0 out))
  90. ((changed1 defs) (propagate defs succ1 out)))
  91. (values (append changed0 changed1) defs)))
  92. (match (intmap-ref conts label)
  93. (($ $kargs names vars ($ $continue k src exp))
  94. (let ((out (fold1 adjoin-def vars in)))
  95. (match exp
  96. (($ $branch kt) (propagate2 k kt out))
  97. (($ $prompt escape? tag handler) (propagate2 k handler out))
  98. (_ (propagate1 k out)))))
  99. (($ $kreceive arity k)
  100. (propagate1 k in))
  101. (($ $kfun src meta self tail clause)
  102. (let ((out (adjoin-def self in)))
  103. (if clause
  104. (propagate1 clause out)
  105. (propagate0 out))))
  106. (($ $kclause arity kbody kalt)
  107. (if kalt
  108. (propagate2 kbody kalt in)
  109. (propagate1 kbody in)))
  110. (($ $ktail) (propagate0 in)))))
  111. (worklist-fold* visit-cont
  112. (intset kfun)
  113. (intmap-add empty-intmap kfun empty-intset)))
  114. (define (intmap-for-each f map)
  115. (intmap-fold (lambda (k v seed) (f k v) seed) map *unspecified*))
  116. (define (check-valid-var-uses conts kfun)
  117. (define (adjoin-def var defs) (intset-add defs var))
  118. (let visit-fun ((kfun kfun) (free empty-intset) (first-order empty-intset))
  119. (define (visit-exp exp bound first-order)
  120. (define (check-use var)
  121. (unless (intset-ref bound var)
  122. (error "unbound var" var)))
  123. (define (visit-first-order kfun)
  124. (if (intset-ref first-order kfun)
  125. first-order
  126. (visit-fun kfun empty-intset (intset-add first-order kfun))))
  127. (match exp
  128. ((or ($ $const) ($ $prim)) first-order)
  129. ;; todo: $closure
  130. (($ $fun kfun)
  131. (visit-fun kfun bound first-order))
  132. (($ $closure kfun)
  133. (visit-first-order kfun))
  134. (($ $rec names vars (($ $fun kfuns) ...))
  135. (let ((bound (fold1 adjoin-def vars bound)))
  136. (fold1 (lambda (kfun first-order)
  137. (visit-fun kfun bound first-order))
  138. kfuns first-order)))
  139. (($ $values args)
  140. (for-each check-use args)
  141. first-order)
  142. (($ $call proc args)
  143. (check-use proc)
  144. (for-each check-use args)
  145. first-order)
  146. (($ $callk kfun proc args)
  147. (check-use proc)
  148. (for-each check-use args)
  149. (visit-first-order kfun))
  150. (($ $branch kt ($ $values (arg)))
  151. (check-use arg)
  152. first-order)
  153. (($ $branch kt ($ $primcall name args))
  154. (for-each check-use args)
  155. first-order)
  156. (($ $primcall name args)
  157. (for-each check-use args)
  158. first-order)
  159. (($ $prompt escape? tag handler)
  160. (check-use tag)
  161. first-order)))
  162. (intmap-fold
  163. (lambda (label bound first-order)
  164. (let ((bound (intset-union free bound)))
  165. (match (intmap-ref conts label)
  166. (($ $kargs names vars ($ $continue k src exp))
  167. (visit-exp exp (fold1 adjoin-def vars bound) first-order))
  168. (_ first-order))))
  169. (compute-available-definitions conts kfun)
  170. first-order)))
  171. (define (check-label-partition conts kfun)
  172. ;; A continuation can only belong to one function.
  173. (intmap-fold
  174. (lambda (kfun body seen)
  175. (intset-fold
  176. (lambda (label seen)
  177. (intmap-add seen label kfun
  178. (lambda (old new)
  179. (error "label used by two functions" label old new))))
  180. body
  181. seen))
  182. (compute-reachable-functions conts kfun)
  183. empty-intmap))
  184. (define (compute-reachable-labels conts kfun)
  185. (intmap-fold (lambda (kfun body seen) (intset-union seen body))
  186. (compute-reachable-functions conts kfun)
  187. empty-intset))
  188. (define (check-arities conts kfun)
  189. (define (check-arity exp cont)
  190. (define (assert-unary)
  191. (match cont
  192. (($ $kargs (_) (_)) #t)
  193. (_ (error "expected unary continuation" cont))))
  194. (define (assert-nullary)
  195. (match cont
  196. (($ $kargs () ()) #t)
  197. (_ (error "expected unary continuation" cont))))
  198. (define (assert-n-ary n)
  199. (match cont
  200. (($ $kargs names vars)
  201. (unless (= (length vars) n)
  202. (error "expected n-ary continuation" n cont)))
  203. (_ (error "expected $kargs continuation" cont))))
  204. (define (assert-kreceive-or-ktail)
  205. (match cont
  206. ((or ($ $kreceive) ($ $ktail)) #t)
  207. (_ (error "expected $kreceive or $ktail continuation" cont))))
  208. (match exp
  209. ((or ($ $const) ($ $prim) ($ $closure) ($ $fun))
  210. (assert-unary))
  211. (($ $rec names vars funs)
  212. (unless (= (length names) (length vars) (length funs))
  213. (error "invalid $rec" exp))
  214. (assert-n-ary (length names))
  215. (match cont
  216. (($ $kargs names vars*)
  217. (unless (equal? vars* vars)
  218. (error "bound variable mismatch" vars vars*)))))
  219. (($ $values args)
  220. (match cont
  221. (($ $ktail) #t)
  222. (_ (assert-n-ary (length args)))))
  223. (($ $call proc args)
  224. (assert-kreceive-or-ktail))
  225. (($ $callk k proc args)
  226. (assert-kreceive-or-ktail))
  227. (($ $branch kt exp)
  228. (assert-nullary)
  229. (match (intmap-ref conts kt)
  230. (($ $kargs () ()) #t)
  231. (cont (error "bad kt" cont))))
  232. (($ $primcall name args)
  233. (match cont
  234. (($ $kargs names)
  235. (match (prim-arity name)
  236. ((out . in)
  237. (unless (= in (length args))
  238. (error "bad arity to primcall" name args in))
  239. (unless (= out (length names))
  240. (error "bad return arity from primcall" name names out)))))
  241. (($ $kreceive)
  242. (when (false-if-exception (prim-arity name))
  243. (error "primitive should continue to $kargs, not $kreceive" name)))
  244. (($ $ktail)
  245. (error "primitive should continue to $kargs, not $ktail" name))))
  246. (($ $prompt escape? tag handler)
  247. (assert-nullary)
  248. (match (intmap-ref conts handler)
  249. (($ $kreceive) #t)
  250. (cont (error "bad handler" cont))))))
  251. (let ((reachable (compute-reachable-labels conts kfun)))
  252. (intmap-for-each
  253. (lambda (label cont)
  254. (when (intset-ref reachable label)
  255. (match cont
  256. (($ $kargs names vars ($ $continue k src exp))
  257. (unless (= (length names) (length vars))
  258. (error "broken $kargs" label names vars))
  259. (check-arity exp (intmap-ref conts k)))
  260. (_ #t))))
  261. conts)))
  262. (define (check-functions-bound-once conts kfun)
  263. (let ((reachable (compute-reachable-labels conts kfun)))
  264. (define (add-fun fun functions)
  265. (when (intset-ref functions fun)
  266. (error "function already bound" fun))
  267. (intset-add functions fun))
  268. (intmap-fold
  269. (lambda (label cont functions)
  270. (if (intset-ref reachable label)
  271. (match cont
  272. (($ $kargs _ _ ($ $continue _ _ ($ $fun kfun)))
  273. (add-fun kfun functions))
  274. (($ $kargs _ _ ($ $continue _ _ ($ $rec _ _ (($ $fun kfuns) ...))))
  275. (fold1 add-fun kfuns functions))
  276. (_ functions))
  277. functions))
  278. conts
  279. empty-intset)))
  280. (define (verify conts)
  281. (check-distinct-vars conts)
  282. (check-label-partition conts 0)
  283. (check-valid-var-uses conts 0)
  284. (check-arities conts 0)
  285. (check-functions-bound-once conts 0)
  286. conts)