verify.scm 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363
  1. ;;; Diagnostic checker for CPS
  2. ;;; Copyright (C) 2014-2021 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. (define (propagate* succs out)
  92. (let lp ((succs succs) (changed '()) (defs defs))
  93. (match succs
  94. (() (values changed defs))
  95. ((succ . succs)
  96. (let-values (((changed* defs) (propagate defs succ out)))
  97. (lp succs (append changed* changed) defs))))))
  98. (match (intmap-ref conts label)
  99. (($ $kargs names vars term)
  100. (let ((out (fold1 adjoin-def vars in)))
  101. (match term
  102. (($ $continue k)
  103. (propagate1 k out))
  104. (($ $branch kf kt)
  105. (propagate2 kf kt out))
  106. (($ $switch kf kt*)
  107. (propagate* (cons kf kt*) out))
  108. (($ $prompt k kh)
  109. (propagate2 k kh out))
  110. (($ $throw)
  111. (propagate0 out)))))
  112. (($ $kreceive arity k)
  113. (propagate1 k in))
  114. (($ $kfun src meta self tail clause)
  115. (let ((out (if self (adjoin-def self in) in)))
  116. (if clause
  117. (propagate1 clause out)
  118. (propagate0 out))))
  119. (($ $kclause arity kbody kalt)
  120. (if kalt
  121. (propagate2 kbody kalt in)
  122. (propagate1 kbody in)))
  123. (($ $ktail) (propagate0 in)))))
  124. (worklist-fold* visit-cont
  125. (intset kfun)
  126. (intmap-add empty-intmap kfun empty-intset)))
  127. (define (intmap-for-each f map)
  128. (intmap-fold (lambda (k v seed) (f k v) seed) map *unspecified*))
  129. (define (check-valid-var-uses conts kfun)
  130. (define (adjoin-def var defs) (intset-add defs var))
  131. (let visit-fun ((kfun kfun) (free empty-intset) (first-order empty-intset))
  132. (define (visit-exp exp bound first-order)
  133. (define (check-use var)
  134. (unless (intset-ref bound var)
  135. (error "unbound var" var)))
  136. (define (visit-first-order kfun)
  137. (if (intset-ref first-order kfun)
  138. first-order
  139. (visit-fun kfun empty-intset (intset-add first-order kfun))))
  140. (match exp
  141. ((or ($ $const) ($ $prim)) first-order)
  142. (($ $fun kfun)
  143. (visit-fun kfun bound first-order))
  144. (($ $const-fun kfun)
  145. (visit-first-order kfun))
  146. (($ $code kfun)
  147. (visit-first-order kfun))
  148. (($ $rec names vars (($ $fun kfuns) ...))
  149. (let ((bound (fold1 adjoin-def vars bound)))
  150. (fold1 (lambda (kfun first-order)
  151. (visit-fun kfun bound first-order))
  152. kfuns first-order)))
  153. (($ $values args)
  154. (for-each check-use args)
  155. first-order)
  156. (($ $call proc args)
  157. (check-use proc)
  158. (for-each check-use args)
  159. first-order)
  160. (($ $callk kfun proc args)
  161. (when proc (check-use proc))
  162. (for-each check-use args)
  163. (visit-first-order kfun))
  164. (($ $primcall name param args)
  165. (for-each check-use args)
  166. first-order)))
  167. (define (visit-term term bound first-order)
  168. (define (check-use var)
  169. (unless (intset-ref bound var)
  170. (error "unbound var" var)))
  171. (define (visit-first-order kfun)
  172. (if (intset-ref first-order kfun)
  173. first-order
  174. (visit-fun kfun empty-intset (intset-add first-order kfun))))
  175. (match term
  176. (($ $continue k src exp)
  177. (match exp
  178. ((or ($ $const) ($ $prim)) first-order)
  179. (($ $fun kfun)
  180. (visit-fun kfun bound first-order))
  181. (($ $const-fun kfun)
  182. (visit-first-order kfun))
  183. (($ $code kfun)
  184. (visit-first-order kfun))
  185. (($ $rec names vars (($ $fun kfuns) ...))
  186. (let ((bound (fold1 adjoin-def vars bound)))
  187. (fold1 (lambda (kfun first-order)
  188. (visit-fun kfun bound first-order))
  189. kfuns first-order)))
  190. (($ $values args)
  191. (for-each check-use args)
  192. first-order)
  193. (($ $call proc args)
  194. (check-use proc)
  195. (for-each check-use args)
  196. first-order)
  197. (($ $callk kfun proc args)
  198. (when proc (check-use proc))
  199. (for-each check-use args)
  200. (visit-first-order kfun))
  201. (($ $primcall name param args)
  202. (for-each check-use args)
  203. first-order)))
  204. (($ $branch kf kt src name param args)
  205. (for-each check-use args)
  206. first-order)
  207. (($ $switch kf kt* src arg)
  208. (check-use arg)
  209. first-order)
  210. (($ $prompt k kh src escape? tag)
  211. (check-use tag)
  212. first-order)
  213. (($ $throw src op param args)
  214. (for-each check-use args)
  215. first-order)))
  216. (intmap-fold
  217. (lambda (label bound first-order)
  218. (let ((bound (intset-union free bound)))
  219. (match (intmap-ref conts label)
  220. (($ $kargs names vars term)
  221. (visit-term term (fold1 adjoin-def vars bound) first-order))
  222. (_ first-order))))
  223. (compute-available-definitions conts kfun)
  224. first-order)))
  225. (define (check-label-partition conts kfun)
  226. ;; A continuation can only belong to one function.
  227. (intmap-fold
  228. (lambda (kfun body seen)
  229. (intset-fold
  230. (lambda (label seen)
  231. (intmap-add seen label kfun
  232. (lambda (old new)
  233. (error "label used by two functions" label old new))))
  234. body
  235. seen))
  236. (compute-reachable-functions conts kfun)
  237. empty-intmap))
  238. (define (compute-reachable-labels conts kfun)
  239. (intmap-fold (lambda (kfun body seen) (intset-union seen body))
  240. (compute-reachable-functions conts kfun)
  241. empty-intset))
  242. (define (check-arities conts kfun)
  243. (define (check-arity exp cont)
  244. (define (assert-unary)
  245. (match cont
  246. (($ $kargs (_) (_)) #t)
  247. (_ (error "expected unary continuation" cont))))
  248. (define (assert-nullary)
  249. (match cont
  250. (($ $kargs () ()) #t)
  251. (_ (error "expected unary continuation" cont))))
  252. (define (assert-n-ary n)
  253. (match cont
  254. (($ $kargs names vars)
  255. (unless (= (length vars) n)
  256. (error "expected n-ary continuation" n cont)))
  257. (_ (error "expected $kargs continuation" cont))))
  258. (match exp
  259. ((or ($ $const) ($ $prim) ($ $const-fun) ($ $code) ($ $fun))
  260. (assert-unary))
  261. (($ $rec names vars funs)
  262. (unless (= (length names) (length vars) (length funs))
  263. (error "invalid $rec" exp))
  264. (assert-n-ary (length names))
  265. (match cont
  266. (($ $kargs names vars*)
  267. (unless (equal? vars* vars)
  268. (error "bound variable mismatch" vars vars*)))))
  269. (($ $values args)
  270. (match cont
  271. (($ $ktail) #t)
  272. (_ (assert-n-ary (length args)))))
  273. (($ $call proc args)
  274. (match cont
  275. ((or ($ $kreceive) ($ $ktail)) #t)
  276. (_ (error "expected $kreceive or $ktail continuation" cont))))
  277. (($ $callk k proc args)
  278. (match cont
  279. ((or ($ $kargs) ($ $kreceive) ($ $ktail)) #t)
  280. (_ (error "expected $kargs, $kreceive or $ktail continuation" cont))))
  281. (($ $primcall name param args)
  282. (match cont
  283. (($ $kargs) #t)
  284. (($ $kreceive)
  285. (match exp
  286. (($ $primcall 'call-thunk/no-inline #f (thunk)) #t)
  287. (_ (cont (error "bad continuation" exp cont)))))))))
  288. (define (check-term term)
  289. (define (assert-nullary k)
  290. (match (intmap-ref conts k)
  291. (($ $kargs () ()) #t)
  292. (cont (error "expected nullary cont" cont))))
  293. (match term
  294. (($ $continue k src exp)
  295. (check-arity exp (intmap-ref conts k)))
  296. (($ $branch kf kt src op param args)
  297. (assert-nullary kf)
  298. (assert-nullary kt))
  299. (($ $switch kf kt* src arg)
  300. (assert-nullary kf)
  301. (for-each assert-nullary kt*))
  302. (($ $prompt k kh src escape? tag)
  303. (assert-nullary k)
  304. (match (intmap-ref conts kh)
  305. (($ $kreceive) #t)
  306. (cont (error "bad prompt handler" cont))))
  307. (($ $throw)
  308. #t)))
  309. (let ((reachable (compute-reachable-labels conts kfun)))
  310. (intmap-for-each
  311. (lambda (label cont)
  312. (when (intset-ref reachable label)
  313. (match cont
  314. (($ $kargs names vars term)
  315. (unless (= (length names) (length vars))
  316. (error "broken $kargs" label names vars))
  317. (check-term term))
  318. (_ #t))))
  319. conts)))
  320. (define (check-functions-bound-once conts kfun)
  321. (let ((reachable (compute-reachable-labels conts kfun)))
  322. (define (add-fun fun functions)
  323. (when (intset-ref functions fun)
  324. (error "function already bound" fun))
  325. (intset-add functions fun))
  326. (intmap-fold
  327. (lambda (label cont functions)
  328. (if (intset-ref reachable label)
  329. (match cont
  330. (($ $kargs _ _ ($ $continue _ _ ($ $fun kfun)))
  331. (add-fun kfun functions))
  332. (($ $kargs _ _ ($ $continue _ _ ($ $rec _ _ (($ $fun kfuns) ...))))
  333. (fold1 add-fun kfuns functions))
  334. (_ functions))
  335. functions))
  336. conts
  337. empty-intset)))
  338. (define (verify conts)
  339. (check-distinct-vars conts)
  340. (check-label-partition conts 0)
  341. (check-valid-var-uses conts 0)
  342. (check-arities conts 0)
  343. (check-functions-bound-once conts 0)
  344. conts)