verify.scm 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352
  1. ;;; Diagnostic checker for CPS
  2. ;;; Copyright (C) 2014, 2015, 2017, 2018 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 (srfi srfi-11)
  29. #:export (verify))
  30. (define (intset-pop set)
  31. (match (intset-next set)
  32. (#f (values set #f))
  33. (i (values (intset-remove set i) i))))
  34. (define-syntax-rule (make-worklist-folder* seed ...)
  35. (lambda (f worklist seed ...)
  36. (let lp ((worklist worklist) (seed seed) ...)
  37. (call-with-values (lambda () (intset-pop worklist))
  38. (lambda (worklist i)
  39. (if i
  40. (call-with-values (lambda () (f i seed ...))
  41. (lambda (i* seed ...)
  42. (let add ((i* i*) (worklist worklist))
  43. (match i*
  44. (() (lp worklist seed ...))
  45. ((i . i*) (add i* (intset-add worklist i)))))))
  46. (values seed ...)))))))
  47. (define worklist-fold*
  48. (case-lambda
  49. ((f worklist seed)
  50. ((make-worklist-folder* seed) f worklist seed))))
  51. (define (check-distinct-vars conts)
  52. (define (adjoin-def var seen)
  53. (when (intset-ref seen var)
  54. (error "duplicate var name" seen var))
  55. (intset-add seen var))
  56. (intmap-fold
  57. (lambda (label cont seen)
  58. (match (intmap-ref conts label)
  59. (($ $kargs names vars term)
  60. (fold1 adjoin-def vars seen))
  61. (($ $kfun src meta self tail clause)
  62. (adjoin-def self seen))
  63. (_ seen))
  64. )
  65. conts
  66. empty-intset))
  67. (define (compute-available-definitions conts kfun)
  68. "Compute and return a map of LABEL->VAR..., where VAR... are the
  69. definitions that are available at LABEL."
  70. (define (adjoin-def var defs)
  71. (when (intset-ref defs var)
  72. (error "var already present in defs" defs var))
  73. (intset-add defs var))
  74. (define (propagate defs succ out)
  75. (let* ((in (intmap-ref defs succ (lambda (_) #f)))
  76. (in* (if in (intset-intersect in out) out)))
  77. (if (eq? in in*)
  78. (values '() defs)
  79. (values (list succ)
  80. (intmap-add defs succ in* (lambda (old new) new))))))
  81. (define (visit-cont label defs)
  82. (let ((in (intmap-ref defs label)))
  83. (define (propagate0 out)
  84. (values '() defs))
  85. (define (propagate1 succ out)
  86. (propagate defs succ out))
  87. (define (propagate2 succ0 succ1 out)
  88. (let*-values (((changed0 defs) (propagate defs succ0 out))
  89. ((changed1 defs) (propagate defs succ1 out)))
  90. (values (append changed0 changed1) defs)))
  91. (match (intmap-ref conts label)
  92. (($ $kargs names vars term)
  93. (let ((out (fold1 adjoin-def vars in)))
  94. (match term
  95. (($ $continue k)
  96. (propagate1 k out))
  97. (($ $branch kf kt)
  98. (propagate2 kf kt out))
  99. (($ $prompt k kh)
  100. (propagate2 k kh out))
  101. (($ $throw)
  102. (propagate0 out)))))
  103. (($ $kreceive arity k)
  104. (propagate1 k in))
  105. (($ $kfun src meta self tail clause)
  106. (let ((out (adjoin-def self in)))
  107. (if clause
  108. (propagate1 clause out)
  109. (propagate0 out))))
  110. (($ $kclause arity kbody kalt)
  111. (if kalt
  112. (propagate2 kbody kalt in)
  113. (propagate1 kbody in)))
  114. (($ $ktail) (propagate0 in)))))
  115. (worklist-fold* visit-cont
  116. (intset kfun)
  117. (intmap-add empty-intmap kfun empty-intset)))
  118. (define (intmap-for-each f map)
  119. (intmap-fold (lambda (k v seed) (f k v) seed) map *unspecified*))
  120. (define (check-valid-var-uses conts kfun)
  121. (define (adjoin-def var defs) (intset-add defs var))
  122. (let visit-fun ((kfun kfun) (free empty-intset) (first-order empty-intset))
  123. (define (visit-exp exp bound first-order)
  124. (define (check-use var)
  125. (unless (intset-ref bound var)
  126. (error "unbound var" var)))
  127. (define (visit-first-order kfun)
  128. (if (intset-ref first-order kfun)
  129. first-order
  130. (visit-fun kfun empty-intset (intset-add first-order kfun))))
  131. (match exp
  132. ((or ($ $const) ($ $prim)) first-order)
  133. ;; todo: $closure
  134. (($ $fun kfun)
  135. (visit-fun kfun bound first-order))
  136. (($ $closure kfun)
  137. (visit-first-order kfun))
  138. (($ $code kfun)
  139. (visit-first-order kfun))
  140. (($ $rec names vars (($ $fun kfuns) ...))
  141. (let ((bound (fold1 adjoin-def vars bound)))
  142. (fold1 (lambda (kfun first-order)
  143. (visit-fun kfun bound first-order))
  144. kfuns first-order)))
  145. (($ $values args)
  146. (for-each check-use args)
  147. first-order)
  148. (($ $call proc args)
  149. (check-use proc)
  150. (for-each check-use args)
  151. first-order)
  152. (($ $callk kfun proc args)
  153. (check-use proc)
  154. (for-each check-use args)
  155. (visit-first-order kfun))
  156. (($ $primcall name param args)
  157. (for-each check-use args)
  158. first-order)))
  159. (define (visit-term term bound first-order)
  160. (define (check-use var)
  161. (unless (intset-ref bound var)
  162. (error "unbound var" var)))
  163. (define (visit-first-order kfun)
  164. (if (intset-ref first-order kfun)
  165. first-order
  166. (visit-fun kfun empty-intset (intset-add first-order kfun))))
  167. (match term
  168. (($ $continue k src exp)
  169. (match exp
  170. ((or ($ $const) ($ $prim)) first-order)
  171. ;; todo: $closure
  172. (($ $fun kfun)
  173. (visit-fun kfun bound first-order))
  174. (($ $closure kfun)
  175. (visit-first-order kfun))
  176. (($ $code kfun)
  177. (visit-first-order kfun))
  178. (($ $rec names vars (($ $fun kfuns) ...))
  179. (let ((bound (fold1 adjoin-def vars bound)))
  180. (fold1 (lambda (kfun first-order)
  181. (visit-fun kfun bound first-order))
  182. kfuns first-order)))
  183. (($ $values args)
  184. (for-each check-use args)
  185. first-order)
  186. (($ $call proc args)
  187. (check-use proc)
  188. (for-each check-use args)
  189. first-order)
  190. (($ $callk kfun proc args)
  191. (check-use proc)
  192. (for-each check-use args)
  193. (visit-first-order kfun))
  194. (($ $primcall name param args)
  195. (for-each check-use args)
  196. first-order)))
  197. (($ $branch kf kt src name param args)
  198. (for-each check-use args)
  199. first-order)
  200. (($ $prompt k kh src escape? tag)
  201. (check-use tag)
  202. first-order)
  203. (($ $throw src op param args)
  204. (for-each check-use args)
  205. first-order)))
  206. (intmap-fold
  207. (lambda (label bound first-order)
  208. (let ((bound (intset-union free bound)))
  209. (match (intmap-ref conts label)
  210. (($ $kargs names vars term)
  211. (visit-term term (fold1 adjoin-def vars bound) first-order))
  212. (_ first-order))))
  213. (compute-available-definitions conts kfun)
  214. first-order)))
  215. (define (check-label-partition conts kfun)
  216. ;; A continuation can only belong to one function.
  217. (intmap-fold
  218. (lambda (kfun body seen)
  219. (intset-fold
  220. (lambda (label seen)
  221. (intmap-add seen label kfun
  222. (lambda (old new)
  223. (error "label used by two functions" label old new))))
  224. body
  225. seen))
  226. (compute-reachable-functions conts kfun)
  227. empty-intmap))
  228. (define (compute-reachable-labels conts kfun)
  229. (intmap-fold (lambda (kfun body seen) (intset-union seen body))
  230. (compute-reachable-functions conts kfun)
  231. empty-intset))
  232. (define (check-arities conts kfun)
  233. (define (check-arity exp cont)
  234. (define (assert-unary)
  235. (match cont
  236. (($ $kargs (_) (_)) #t)
  237. (_ (error "expected unary continuation" cont))))
  238. (define (assert-nullary)
  239. (match cont
  240. (($ $kargs () ()) #t)
  241. (_ (error "expected unary continuation" cont))))
  242. (define (assert-n-ary n)
  243. (match cont
  244. (($ $kargs names vars)
  245. (unless (= (length vars) n)
  246. (error "expected n-ary continuation" n cont)))
  247. (_ (error "expected $kargs continuation" cont))))
  248. (define (assert-kreceive-or-ktail)
  249. (match cont
  250. ((or ($ $kreceive) ($ $ktail)) #t)
  251. (_ (error "expected $kreceive or $ktail continuation" cont))))
  252. (match exp
  253. ((or ($ $const) ($ $prim) ($ $closure) ($ $code) ($ $fun))
  254. (assert-unary))
  255. (($ $rec names vars funs)
  256. (unless (= (length names) (length vars) (length funs))
  257. (error "invalid $rec" exp))
  258. (assert-n-ary (length names))
  259. (match cont
  260. (($ $kargs names vars*)
  261. (unless (equal? vars* vars)
  262. (error "bound variable mismatch" vars vars*)))))
  263. (($ $values args)
  264. (match cont
  265. (($ $ktail) #t)
  266. (_ (assert-n-ary (length args)))))
  267. (($ $call proc args)
  268. (assert-kreceive-or-ktail))
  269. (($ $callk k proc args)
  270. (assert-kreceive-or-ktail))
  271. (($ $primcall name param args)
  272. (match cont
  273. (($ $kargs) #t)
  274. (($ $kreceive)
  275. (match exp
  276. (($ $primcall 'call-thunk/no-inline #f (thunk)) #t)
  277. (_ (cont (error "bad continuation" exp cont)))))))))
  278. (define (check-term term)
  279. (match term
  280. (($ $continue k src exp)
  281. (check-arity exp (intmap-ref conts k)))
  282. (($ $branch kf kt src op param args)
  283. (match (intmap-ref conts kf)
  284. (($ $kargs () ()) #t)
  285. (cont (error "bad kf" cont)))
  286. (match (intmap-ref conts kt)
  287. (($ $kargs () ()) #t)
  288. (cont (error "bad kt" cont))))
  289. (($ $prompt k kh src escape? tag)
  290. (match (intmap-ref conts k)
  291. (($ $kargs () ()) #t)
  292. (cont (error "bad prompt body" cont)))
  293. (match (intmap-ref conts kh)
  294. (($ $kreceive) #t)
  295. (cont (error "bad prompt handler" cont))))
  296. (($ $throw)
  297. #t)))
  298. (let ((reachable (compute-reachable-labels conts kfun)))
  299. (intmap-for-each
  300. (lambda (label cont)
  301. (when (intset-ref reachable label)
  302. (match cont
  303. (($ $kargs names vars term)
  304. (unless (= (length names) (length vars))
  305. (error "broken $kargs" label names vars))
  306. (check-term term))
  307. (_ #t))))
  308. conts)))
  309. (define (check-functions-bound-once conts kfun)
  310. (let ((reachable (compute-reachable-labels conts kfun)))
  311. (define (add-fun fun functions)
  312. (when (intset-ref functions fun)
  313. (error "function already bound" fun))
  314. (intset-add functions fun))
  315. (intmap-fold
  316. (lambda (label cont functions)
  317. (if (intset-ref reachable label)
  318. (match cont
  319. (($ $kargs _ _ ($ $continue _ _ ($ $fun kfun)))
  320. (add-fun kfun functions))
  321. (($ $kargs _ _ ($ $continue _ _ ($ $rec _ _ (($ $fun kfuns) ...))))
  322. (fold1 add-fun kfuns functions))
  323. (_ functions))
  324. functions))
  325. conts
  326. empty-intset)))
  327. (define (verify conts)
  328. (check-distinct-vars conts)
  329. (check-label-partition conts 0)
  330. (check-valid-var-uses conts 0)
  331. (check-arities conts 0)
  332. (check-functions-bound-once conts 0)
  333. conts)