verify.scm 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350
  1. ;;; Diagnostic checker for CPS
  2. ;;; Copyright (C) 2014-2019 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 (and self (not #f)) 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 (if self (adjoin-def self in) 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. (($ $fun kfun)
  134. (visit-fun kfun bound first-order))
  135. (($ $const-fun kfun)
  136. (visit-first-order kfun))
  137. (($ $code kfun)
  138. (visit-first-order kfun))
  139. (($ $rec names vars (($ $fun kfuns) ...))
  140. (let ((bound (fold1 adjoin-def vars bound)))
  141. (fold1 (lambda (kfun first-order)
  142. (visit-fun kfun bound first-order))
  143. kfuns first-order)))
  144. (($ $values args)
  145. (for-each check-use args)
  146. first-order)
  147. (($ $call proc args)
  148. (check-use proc)
  149. (for-each check-use args)
  150. first-order)
  151. (($ $callk kfun proc args)
  152. (when proc (check-use proc))
  153. (for-each check-use args)
  154. (visit-first-order kfun))
  155. (($ $primcall name param args)
  156. (for-each check-use args)
  157. first-order)))
  158. (define (visit-term term bound first-order)
  159. (define (check-use var)
  160. (unless (intset-ref bound var)
  161. (error "unbound var" var)))
  162. (define (visit-first-order kfun)
  163. (if (intset-ref first-order kfun)
  164. first-order
  165. (visit-fun kfun empty-intset (intset-add first-order kfun))))
  166. (match term
  167. (($ $continue k src exp)
  168. (match exp
  169. ((or ($ $const) ($ $prim)) first-order)
  170. (($ $fun kfun)
  171. (visit-fun kfun bound first-order))
  172. (($ $const-fun kfun)
  173. (visit-first-order kfun))
  174. (($ $code kfun)
  175. (visit-first-order kfun))
  176. (($ $rec names vars (($ $fun kfuns) ...))
  177. (let ((bound (fold1 adjoin-def vars bound)))
  178. (fold1 (lambda (kfun first-order)
  179. (visit-fun kfun bound first-order))
  180. kfuns first-order)))
  181. (($ $values args)
  182. (for-each check-use args)
  183. first-order)
  184. (($ $call proc args)
  185. (check-use proc)
  186. (for-each check-use args)
  187. first-order)
  188. (($ $callk kfun proc args)
  189. (when proc (check-use proc))
  190. (for-each check-use args)
  191. (visit-first-order kfun))
  192. (($ $primcall name param args)
  193. (for-each check-use args)
  194. first-order)))
  195. (($ $branch kf kt src name param args)
  196. (for-each check-use args)
  197. first-order)
  198. (($ $prompt k kh src escape? tag)
  199. (check-use tag)
  200. first-order)
  201. (($ $throw src op param args)
  202. (for-each check-use args)
  203. first-order)))
  204. (intmap-fold
  205. (lambda (label bound first-order)
  206. (let ((bound (intset-union free bound)))
  207. (match (intmap-ref conts label)
  208. (($ $kargs names vars term)
  209. (visit-term term (fold1 adjoin-def vars bound) first-order))
  210. (_ first-order))))
  211. (compute-available-definitions conts kfun)
  212. first-order)))
  213. (define (check-label-partition conts kfun)
  214. ;; A continuation can only belong to one function.
  215. (intmap-fold
  216. (lambda (kfun body seen)
  217. (intset-fold
  218. (lambda (label seen)
  219. (intmap-add seen label kfun
  220. (lambda (old new)
  221. (error "label used by two functions" label old new))))
  222. body
  223. seen))
  224. (compute-reachable-functions conts kfun)
  225. empty-intmap))
  226. (define (compute-reachable-labels conts kfun)
  227. (intmap-fold (lambda (kfun body seen) (intset-union seen body))
  228. (compute-reachable-functions conts kfun)
  229. empty-intset))
  230. (define (check-arities conts kfun)
  231. (define (check-arity exp cont)
  232. (define (assert-unary)
  233. (match cont
  234. (($ $kargs (_) (_)) #t)
  235. (_ (error "expected unary continuation" cont))))
  236. (define (assert-nullary)
  237. (match cont
  238. (($ $kargs () ()) #t)
  239. (_ (error "expected unary continuation" cont))))
  240. (define (assert-n-ary n)
  241. (match cont
  242. (($ $kargs names vars)
  243. (unless (= (length vars) n)
  244. (error "expected n-ary continuation" n cont)))
  245. (_ (error "expected $kargs continuation" cont))))
  246. (define (assert-kreceive-or-ktail)
  247. (match cont
  248. ((or ($ $kreceive) ($ $ktail)) #t)
  249. (_ (error "expected $kreceive or $ktail continuation" cont))))
  250. (match exp
  251. ((or ($ $const) ($ $prim) ($ $const-fun) ($ $code) ($ $fun))
  252. (assert-unary))
  253. (($ $rec names vars funs)
  254. (unless (= (length names) (length vars) (length funs))
  255. (error "invalid $rec" exp))
  256. (assert-n-ary (length names))
  257. (match cont
  258. (($ $kargs names vars*)
  259. (unless (equal? vars* vars)
  260. (error "bound variable mismatch" vars vars*)))))
  261. (($ $values args)
  262. (match cont
  263. (($ $ktail) #t)
  264. (_ (assert-n-ary (length args)))))
  265. (($ $call proc args)
  266. (assert-kreceive-or-ktail))
  267. (($ $callk k proc args)
  268. (assert-kreceive-or-ktail))
  269. (($ $primcall name param args)
  270. (match cont
  271. (($ $kargs) #t)
  272. (($ $kreceive)
  273. (match exp
  274. (($ $primcall 'call-thunk/no-inline #f (thunk)) #t)
  275. (_ (cont (error "bad continuation" exp cont)))))))))
  276. (define (check-term term)
  277. (match term
  278. (($ $continue k src exp)
  279. (check-arity exp (intmap-ref conts k)))
  280. (($ $branch kf kt src op param args)
  281. (match (intmap-ref conts kf)
  282. (($ $kargs () ()) #t)
  283. (cont (error "bad kf" cont)))
  284. (match (intmap-ref conts kt)
  285. (($ $kargs () ()) #t)
  286. (cont (error "bad kt" cont))))
  287. (($ $prompt k kh src escape? tag)
  288. (match (intmap-ref conts k)
  289. (($ $kargs () ()) #t)
  290. (cont (error "bad prompt body" cont)))
  291. (match (intmap-ref conts kh)
  292. (($ $kreceive) #t)
  293. (cont (error "bad prompt handler" cont))))
  294. (($ $throw)
  295. #t)))
  296. (let ((reachable (compute-reachable-labels conts kfun)))
  297. (intmap-for-each
  298. (lambda (label cont)
  299. (when (intset-ref reachable label)
  300. (match cont
  301. (($ $kargs names vars term)
  302. (unless (= (length names) (length vars))
  303. (error "broken $kargs" label names vars))
  304. (check-term term))
  305. (_ #t))))
  306. conts)))
  307. (define (check-functions-bound-once conts kfun)
  308. (let ((reachable (compute-reachable-labels conts kfun)))
  309. (define (add-fun fun functions)
  310. (when (intset-ref functions fun)
  311. (error "function already bound" fun))
  312. (intset-add functions fun))
  313. (intmap-fold
  314. (lambda (label cont functions)
  315. (if (intset-ref reachable label)
  316. (match cont
  317. (($ $kargs _ _ ($ $continue _ _ ($ $fun kfun)))
  318. (add-fun kfun functions))
  319. (($ $kargs _ _ ($ $continue _ _ ($ $rec _ _ (($ $fun kfuns) ...))))
  320. (fold1 add-fun kfuns functions))
  321. (_ functions))
  322. functions))
  323. conts
  324. empty-intset)))
  325. (define (verify conts)
  326. (check-distinct-vars conts)
  327. (check-label-partition conts 0)
  328. (check-valid-var-uses conts 0)
  329. (check-arities conts 0)
  330. (check-functions-bound-once conts 0)
  331. conts)