minor tweaks to a peval test
[bpt/guile.git] / test-suite / tests / peval.test
CommitLineData
de1eb420
AW
1;;;; tree-il.test --- test suite for compiling tree-il -*- scheme -*-
2;;;; Andy Wingo <wingo@pobox.com> --- May 2009
3;;;;
4;;;; Copyright (C) 2009, 2010, 2011, 2012 Free Software Foundation, Inc.
5;;;;
6;;;; This library is free software; you can redistribute it and/or
7;;;; modify it under the terms of the GNU Lesser General Public
8;;;; License as published by the Free Software Foundation; either
9;;;; version 3 of the License, or (at your option) any later version.
10;;;;
11;;;; This library is distributed in the hope that it will be useful,
12;;;; but WITHOUT ANY WARRANTY; without even the implied warranty of
13;;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14;;;; Lesser General Public License for more details.
15;;;;
16;;;; You should have received a copy of the GNU Lesser General Public
17;;;; License along with this library; if not, write to the Free Software
18;;;; Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
19
20(define-module (test-suite tree-il)
21 #:use-module (test-suite lib)
22 #:use-module (system base compile)
23 #:use-module (system base pmatch)
24 #:use-module (system base message)
25 #:use-module (language tree-il)
26 #:use-module (language tree-il primitives)
27 #:use-module (language glil)
28 #:use-module (srfi srfi-13))
29
30(define peval
31 ;; The partial evaluator.
32 (@@ (language tree-il optimize) peval))
33
34(define-syntax pass-if-peval
35 (syntax-rules (resolve-primitives)
36 ((_ in pat)
37 (pass-if-peval in pat
38 (compile 'in #:from 'scheme #:to 'tree-il)))
39 ((_ resolve-primitives in pat)
40 (pass-if-peval in pat
41 (expand-primitives!
42 (resolve-primitives!
43 (compile 'in #:from 'scheme #:to 'tree-il)
44 (current-module)))))
45 ((_ in pat code)
46 (pass-if 'in
47 (let ((evaled (unparse-tree-il (peval code))))
48 (pmatch evaled
49 (pat #t)
50 (_ (pk 'peval-mismatch)
51 ((@ (ice-9 pretty-print) pretty-print)
52 'in)
53 (newline)
54 ((@ (ice-9 pretty-print) pretty-print)
55 evaled)
56 (newline)
57 ((@ (ice-9 pretty-print) pretty-print)
58 'pat)
59 (newline)
60 #f)))))))
61
62\f
63(with-test-prefix "partial evaluation"
64
65 (pass-if-peval
66 ;; First order, primitive.
67 (let ((x 1) (y 2)) (+ x y))
68 (const 3))
69
70 (pass-if-peval
71 ;; First order, thunk.
72 (let ((x 1) (y 2))
73 (let ((f (lambda () (+ x y))))
74 (f)))
75 (const 3))
76
77 (pass-if-peval resolve-primitives
78 ;; First order, let-values (requires primitive expansion for
79 ;; `call-with-values'.)
80 (let ((x 0))
81 (call-with-values
82 (lambda () (if (zero? x) (values 1 2) (values 3 4)))
83 (lambda (a b)
84 (+ a b))))
85 (const 3))
86
87 (pass-if-peval resolve-primitives
88 ;; First order, multiple values.
89 (let ((x 1) (y 2))
90 (values x y))
91 (apply (primitive values) (const 1) (const 2)))
92
93 (pass-if-peval resolve-primitives
94 ;; First order, multiple values truncated.
95 (let ((x (values 1 'a)) (y 2))
96 (values x y))
97 (apply (primitive values) (const 1) (const 2)))
98
99 (pass-if-peval resolve-primitives
100 ;; First order, multiple values truncated.
101 (or (values 1 2) 3)
102 (const 1))
103
104 (pass-if-peval
105 ;; First order, coalesced, mutability preserved.
106 (cons 0 (cons 1 (cons 2 (list 3 4 5))))
107 (apply (primitive list)
108 (const 0) (const 1) (const 2) (const 3) (const 4) (const 5)))
109
110 (pass-if-peval
111 ;; First order, coalesced, immutability preserved.
112 (cons 0 (cons 1 (cons 2 '(3 4 5))))
113 (apply (primitive cons) (const 0)
114 (apply (primitive cons) (const 1)
115 (apply (primitive cons) (const 2)
116 (const (3 4 5))))))
117
118 ;; These two tests doesn't work any more because we changed the way we
119 ;; deal with constants -- now the algorithm will see a construction as
120 ;; being bound to the lexical, so it won't propagate it. It can't
121 ;; even propagate it in the case that it is only referenced once,
122 ;; because:
123 ;;
124 ;; (let ((x (cons 1 2))) (lambda () x))
125 ;;
126 ;; is not the same as
127 ;;
128 ;; (lambda () (cons 1 2))
129 ;;
130 ;; Perhaps if we determined that not only was it only referenced once,
131 ;; it was not closed over by a lambda, then we could propagate it, and
132 ;; re-enable these two tests.
133 ;;
134 #;
135 (pass-if-peval
136 ;; First order, mutability preserved.
137 (let loop ((i 3) (r '()))
138 (if (zero? i)
139 r
140 (loop (1- i) (cons (cons i i) r))))
141 (apply (primitive list)
142 (apply (primitive cons) (const 1) (const 1))
143 (apply (primitive cons) (const 2) (const 2))
144 (apply (primitive cons) (const 3) (const 3))))
145 ;;
146 ;; See above.
147 #;
148 (pass-if-peval
149 ;; First order, evaluated.
150 (let loop ((i 7)
151 (r '()))
152 (if (<= i 0)
153 (car r)
154 (loop (1- i) (cons i r))))
155 (const 1))
156
157 ;; Instead here are tests for what happens for the above cases: they
158 ;; unroll but they don't fold.
159 (pass-if-peval
160 (let loop ((i 3) (r '()))
161 (if (zero? i)
162 r
163 (loop (1- i) (cons (cons i i) r))))
164 (let (r) (_)
165 ((apply (primitive list)
166 (apply (primitive cons) (const 3) (const 3))))
167 (let (r) (_)
168 ((apply (primitive cons)
169 (apply (primitive cons) (const 2) (const 2))
170 (lexical r _)))
171 (apply (primitive cons)
172 (apply (primitive cons) (const 1) (const 1))
173 (lexical r _)))))
174
175 ;; See above.
176 (pass-if-peval
177 (let loop ((i 4)
178 (r '()))
179 (if (<= i 0)
180 (car r)
181 (loop (1- i) (cons i r))))
182 (let (r) (_)
183 ((apply (primitive list) (const 4)))
184 (let (r) (_)
185 ((apply (primitive cons)
186 (const 3)
187 (lexical r _)))
188 (let (r) (_)
189 ((apply (primitive cons)
190 (const 2)
191 (lexical r _)))
192 (let (r) (_)
193 ((apply (primitive cons)
194 (const 1)
195 (lexical r _)))
196 (apply (primitive car)
197 (lexical r _)))))))
198
199 ;; Static sums.
200 (pass-if-peval
201 (let loop ((l '(1 2 3 4)) (sum 0))
202 (if (null? l)
203 sum
204 (loop (cdr l) (+ sum (car l)))))
205 (const 10))
206
207 (pass-if-peval resolve-primitives
208 (let ((string->chars
209 (lambda (s)
210 (define (char-at n)
211 (string-ref s n))
212 (define (len)
213 (string-length s))
214 (let loop ((i 0))
215 (if (< i (len))
216 (cons (char-at i)
217 (loop (1+ i)))
218 '())))))
219 (string->chars "yo"))
220 (apply (primitive list) (const #\y) (const #\o)))
221
222 (pass-if-peval
223 ;; Primitives in module-refs are resolved (the expansion of `pmatch'
224 ;; below leads to calls to (@@ (system base pmatch) car) and
225 ;; similar, which is what we want to be inlined.)
226 (begin
227 (use-modules (system base pmatch))
228 (pmatch '(a b c d)
229 ((a b . _)
230 #t)))
231 (begin
232 (apply . _)
233 (const #t)))
234
235 (pass-if-peval
236 ;; Mutability preserved.
237 ((lambda (x y z) (list x y z)) 1 2 3)
238 (apply (primitive list) (const 1) (const 2) (const 3)))
239
240 (pass-if-peval
241 ;; Don't propagate effect-free expressions that operate on mutable
242 ;; objects.
243 (let* ((x (list 1))
244 (y (car x)))
245 (set-car! x 0)
246 y)
247 (let (x) (_) ((apply (primitive list) (const 1)))
248 (let (y) (_) ((apply (primitive car) (lexical x _)))
249 (begin
250 (apply (toplevel set-car!) (lexical x _) (const 0))
251 (lexical y _)))))
252
253 (pass-if-peval
254 ;; Don't propagate effect-free expressions that operate on objects we
255 ;; don't know about.
256 (let ((y (car x)))
257 (set-car! x 0)
258 y)
259 (let (y) (_) ((apply (primitive car) (toplevel x)))
260 (begin
261 (apply (toplevel set-car!) (toplevel x) (const 0))
262 (lexical y _))))
263
264 (pass-if-peval
265 ;; Infinite recursion
266 ((lambda (x) (x x)) (lambda (x) (x x)))
267 (let (x) (_)
268 ((lambda _
269 (lambda-case
270 (((x) _ _ _ _ _)
271 (apply (lexical x _) (lexical x _))))))
272 (apply (lexical x _) (lexical x _))))
273
274 (pass-if-peval
275 ;; First order, aliased primitive.
276 (let* ((x *) (y (x 1 2))) y)
277 (const 2))
278
279 (pass-if-peval
280 ;; First order, shadowed primitive.
281 (begin
282 (define (+ x y) (pk x y))
283 (+ 1 2))
284 (begin
285 (define +
286 (lambda (_)
287 (lambda-case
288 (((x y) #f #f #f () (_ _))
289 (apply (toplevel pk) (lexical x _) (lexical y _))))))
290 (apply (toplevel +) (const 1) (const 2))))
291
292 (pass-if-peval
293 ;; First-order, effects preserved.
294 (let ((x 2))
295 (do-something!)
296 x)
297 (begin
298 (apply (toplevel do-something!))
299 (const 2)))
300
301 (pass-if-peval
302 ;; First order, residual bindings removed.
303 (let ((x 2) (y 3))
304 (* (+ x y) z))
305 (apply (primitive *) (const 5) (toplevel z)))
306
307 (pass-if-peval
308 ;; First order, with lambda.
309 (define (foo x)
310 (define (bar z) (* z z))
311 (+ x (bar 3)))
312 (define foo
313 (lambda (_)
314 (lambda-case
315 (((x) #f #f #f () (_))
316 (apply (primitive +) (lexical x _) (const 9)))))))
317
318 (pass-if-peval
319 ;; First order, with lambda inlined & specialized twice.
320 (let ((f (lambda (x y)
321 (+ (* x top) y)))
322 (x 2)
323 (y 3))
324 (+ (* x (f x y))
325 (f something x)))
326 (apply (primitive +)
327 (apply (primitive *)
328 (const 2)
329 (apply (primitive +) ; (f 2 3)
330 (apply (primitive *)
331 (const 2)
332 (toplevel top))
333 (const 3)))
334 (let (x) (_) ((toplevel something)) ; (f something 2)
335 ;; `something' is not const, so preserve order of
336 ;; effects with a lexical binding.
337 (apply (primitive +)
338 (apply (primitive *)
339 (lexical x _)
340 (toplevel top))
341 (const 2)))))
342
343 (pass-if-peval
344 ;; First order, with lambda inlined & specialized 3 times.
345 (let ((f (lambda (x y) (if (> x 0) y x))))
346 (+ (f -1 0)
347 (f 1 0)
348 (f -1 y)
349 (f 2 y)
350 (f z y)))
351 (apply (primitive +)
352 (const -1) ; (f -1 0)
353 (const 0) ; (f 1 0)
354 (begin (toplevel y) (const -1)) ; (f -1 y)
355 (toplevel y) ; (f 2 y)
356 (let (x y) (_ _) ((toplevel z) (toplevel y)) ; (f z y)
357 (if (apply (primitive >) (lexical x _) (const 0))
358 (lexical y _)
359 (lexical x _)))))
360
361 (pass-if-peval
362 ;; First order, conditional.
363 (let ((y 2))
364 (lambda (x)
365 (if (> y 0)
366 (display x)
367 'never-reached)))
368 (lambda ()
369 (lambda-case
370 (((x) #f #f #f () (_))
371 (apply (toplevel display) (lexical x _))))))
372
373 (pass-if-peval
374 ;; First order, recursive procedure.
375 (letrec ((fibo (lambda (n)
376 (if (<= n 1)
377 n
378 (+ (fibo (- n 1))
379 (fibo (- n 2)))))))
380 (fibo 4))
381 (const 3))
382
383 (pass-if-peval
384 ;; Don't propagate toplevel references, as intervening expressions
385 ;; could alter their bindings.
386 (let ((x top))
387 (foo)
388 x)
389 (let (x) (_) ((toplevel top))
390 (begin
391 (apply (toplevel foo))
392 (lexical x _))))
393
394 (pass-if-peval
395 ;; Higher order.
396 ((lambda (f x)
397 (f (* (car x) (cadr x))))
398 (lambda (x)
399 (+ x 1))
400 '(2 3))
401 (const 7))
402
403 (pass-if-peval
404 ;; Higher order with optional argument (default value).
405 ((lambda* (f x #:optional (y 0))
406 (+ y (f (* (car x) (cadr x)))))
407 (lambda (x)
408 (+ x 1))
409 '(2 3))
410 (const 7))
411
412 (pass-if-peval
413 ;; Higher order with optional argument (caller-supplied value).
414 ((lambda* (f x #:optional (y 0))
415 (+ y (f (* (car x) (cadr x)))))
416 (lambda (x)
417 (+ x 1))
418 '(2 3)
419 35)
420 (const 42))
421
422 (pass-if-peval
423 ;; Higher order with optional argument (side-effecting default
424 ;; value).
425 ((lambda* (f x #:optional (y (foo)))
426 (+ y (f (* (car x) (cadr x)))))
427 (lambda (x)
428 (+ x 1))
429 '(2 3))
430 (let (y) (_) ((apply (toplevel foo)))
431 (apply (primitive +) (lexical y _) (const 7))))
432
433 (pass-if-peval
434 ;; Higher order with optional argument (caller-supplied value).
435 ((lambda* (f x #:optional (y (foo)))
436 (+ y (f (* (car x) (cadr x)))))
437 (lambda (x)
438 (+ x 1))
439 '(2 3)
440 35)
441 (const 42))
442
443 (pass-if-peval
444 ;; Higher order.
445 ((lambda (f) (f x)) (lambda (x) x))
446 (toplevel x))
447
448 (pass-if-peval
449 ;; Bug reported at
450 ;; <https://lists.gnu.org/archive/html/bug-guile/2011-09/msg00019.html>.
451 (let ((fold (lambda (f g) (f (g top)))))
452 (fold 1+ (lambda (x) x)))
453 (apply (primitive 1+) (toplevel top)))
454
455 (pass-if-peval
456 ;; Procedure not inlined when residual code contains recursive calls.
457 ;; <http://debbugs.gnu.org/9542>
458 (letrec ((fold (lambda (f x3 b null? car cdr)
459 (if (null? x3)
460 b
461 (f (car x3) (fold f (cdr x3) b null? car cdr))))))
462 (fold * x 1 zero? (lambda (x1) x1) (lambda (x2) (- x2 1))))
463 (letrec (fold) (_) (_)
464 (apply (lexical fold _)
465 (primitive *)
466 (toplevel x)
467 (const 1)
468 (primitive zero?)
469 (lambda ()
470 (lambda-case
471 (((x1) #f #f #f () (_))
472 (lexical x1 _))))
473 (lambda ()
474 (lambda-case
475 (((x2) #f #f #f () (_))
476 (apply (primitive -) (lexical x2 _) (const 1))))))))
477
478 (pass-if "inlined lambdas are alpha-renamed"
479 ;; In this example, `make-adder' is inlined more than once; thus,
480 ;; they should use different gensyms for their arguments, because
481 ;; the various optimization passes assume uniquely-named variables.
482 ;;
483 ;; Bug reported at
484 ;; <https://lists.gnu.org/archive/html/bug-guile/2011-09/msg00019.html> and
485 ;; <https://lists.gnu.org/archive/html/bug-guile/2011-09/msg00029.html>.
486 (pmatch (unparse-tree-il
487 (peval (compile
488 '(let ((make-adder
489 (lambda (x) (lambda (y) (+ x y)))))
490 (cons (make-adder 1) (make-adder 2)))
491 #:to 'tree-il)))
492 ((apply (primitive cons)
493 (lambda ()
494 (lambda-case
495 (((y) #f #f #f () (,gensym1))
496 (apply (primitive +)
497 (const 1)
498 (lexical y ,ref1)))))
499 (lambda ()
500 (lambda-case
501 (((y) #f #f #f () (,gensym2))
502 (apply (primitive +)
503 (const 2)
504 (lexical y ,ref2))))))
505 (and (eq? gensym1 ref1)
506 (eq? gensym2 ref2)
507 (not (eq? gensym1 gensym2))))
508 (_ #f)))
509
510 (pass-if-peval
511 ;; Unused letrec bindings are pruned.
512 (letrec ((a (lambda () (b)))
513 (b (lambda () (a)))
514 (c (lambda (x) x)))
515 (c 10))
516 (const 10))
517
518 (pass-if-peval
519 ;; Unused letrec bindings are pruned.
520 (letrec ((a (foo!))
521 (b (lambda () (a)))
522 (c (lambda (x) x)))
523 (c 10))
524 (begin (apply (toplevel foo!))
525 (const 10)))
526
527 (pass-if-peval
528 ;; Higher order, mutually recursive procedures.
529 (letrec ((even? (lambda (x)
530 (or (= 0 x)
531 (odd? (- x 1)))))
532 (odd? (lambda (x)
533 (not (even? x)))))
534 (and (even? 4) (odd? 7)))
535 (const #t))
536
537 (pass-if-peval
538 ;; Memv with constants.
539 (memv 1 '(3 2 1))
540 (const '(1)))
541
542 (pass-if-peval
543 ;; Memv with non-constant list. It could fold but doesn't
544 ;; currently.
545 (memv 1 (list 3 2 1))
546 (apply (primitive memv)
547 (const 1)
548 (apply (primitive list) (const 3) (const 2) (const 1))))
549
550 (pass-if-peval
551 ;; Memv with non-constant key, constant list, test context
552 (case foo
553 ((3 2 1) 'a)
554 (else 'b))
555 (let (key) (_) ((toplevel foo))
556 (if (if (apply (primitive eqv?) (lexical key _) (const 3))
557 (const #t)
558 (if (apply (primitive eqv?) (lexical key _) (const 2))
559 (const #t)
560 (apply (primitive eqv?) (lexical key _) (const 1))))
561 (const a)
562 (const b))))
563
564 (pass-if-peval
565 ;; Memv with non-constant key, empty list, test context. Currently
566 ;; doesn't fold entirely.
567 (case foo
568 (() 'a)
569 (else 'b))
570 (begin (toplevel foo) (const b)))
571
572 ;;
573 ;; Below are cases where constant propagation should bail out.
574 ;;
575
576 (pass-if-peval
577 ;; Non-constant lexical is not propagated.
578 (let ((v (make-vector 6 #f)))
579 (lambda (n)
580 (vector-set! v n n)))
581 (let (v) (_)
582 ((apply (toplevel make-vector) (const 6) (const #f)))
583 (lambda ()
584 (lambda-case
585 (((n) #f #f #f () (_))
586 (apply (toplevel vector-set!)
587 (lexical v _) (lexical n _) (lexical n _)))))))
588
589 (pass-if-peval
590 ;; Mutable lexical is not propagated.
591 (let ((v (vector 1 2 3)))
592 (lambda ()
593 v))
594 (let (v) (_)
595 ((apply (primitive vector) (const 1) (const 2) (const 3)))
596 (lambda ()
597 (lambda-case
598 ((() #f #f #f () ())
599 (lexical v _))))))
600
601 (pass-if-peval
602 ;; Lexical that is not provably pure is not inlined nor propagated.
603 (let* ((x (if (> p q) (frob!) (display 'chbouib)))
604 (y (* x 2)))
605 (+ x x y))
606 (let (x) (_) ((if (apply (primitive >) (toplevel p) (toplevel q))
607 (apply (toplevel frob!))
608 (apply (toplevel display) (const chbouib))))
609 (let (y) (_) ((apply (primitive *) (lexical x _) (const 2)))
610 (apply (primitive +)
611 (lexical x _) (lexical x _) (lexical y _)))))
612
613 (pass-if-peval
614 ;; Non-constant arguments not propagated to lambdas.
615 ((lambda (x y z)
616 (vector-set! x 0 0)
617 (set-car! y 0)
618 (set-cdr! z '()))
619 (vector 1 2 3)
620 (make-list 10)
621 (list 1 2 3))
622 (let (x y z) (_ _ _)
623 ((apply (primitive vector) (const 1) (const 2) (const 3))
624 (apply (toplevel make-list) (const 10))
625 (apply (primitive list) (const 1) (const 2) (const 3)))
626 (begin
627 (apply (toplevel vector-set!)
628 (lexical x _) (const 0) (const 0))
629 (apply (toplevel set-car!)
630 (lexical y _) (const 0))
631 (apply (toplevel set-cdr!)
632 (lexical z _) (const ())))))
633
634 (pass-if-peval
635 (let ((foo top-foo) (bar top-bar))
636 (let* ((g (lambda (x y) (+ x y)))
637 (f (lambda (g x) (g x x))))
638 (+ (f g foo) (f g bar))))
639 (let (foo bar) (_ _) ((toplevel top-foo) (toplevel top-bar))
640 (apply (primitive +)
641 (apply (primitive +) (lexical foo _) (lexical foo _))
642 (apply (primitive +) (lexical bar _) (lexical bar _)))))
643
644 (pass-if-peval
645 ;; Fresh objects are not turned into constants, nor are constants
646 ;; turned into fresh objects.
647 (let* ((c '(2 3))
648 (x (cons 1 c))
649 (y (cons 0 x)))
650 y)
651 (let (x) (_) ((apply (primitive cons) (const 1) (const (2 3))))
652 (apply (primitive cons) (const 0) (lexical x _))))
653
654 (pass-if-peval
655 ;; Bindings mutated.
656 (let ((x 2))
657 (set! x 3)
658 x)
659 (let (x) (_) ((const 2))
660 (begin
661 (set! (lexical x _) (const 3))
662 (lexical x _))))
663
664 (pass-if-peval
665 ;; Bindings mutated.
666 (letrec ((x 0)
667 (f (lambda ()
668 (set! x (+ 1 x))
669 x)))
670 (frob f) ; may mutate `x'
671 x)
672 (letrec (x) (_) ((const 0))
673 (begin
674 (apply (toplevel frob) (lambda _ _))
675 (lexical x _))))
676
677 (pass-if-peval
678 ;; Bindings mutated.
679 (letrec ((f (lambda (x)
680 (set! f (lambda (_) x))
681 x)))
682 (f 2))
683 (letrec _ . _))
684
685 (pass-if-peval
686 ;; Bindings possibly mutated.
687 (let ((x (make-foo)))
688 (frob! x) ; may mutate `x'
689 x)
690 (let (x) (_) ((apply (toplevel make-foo)))
691 (begin
692 (apply (toplevel frob!) (lexical x _))
693 (lexical x _))))
694
695 (pass-if-peval
696 ;; Inlining stops at recursive calls with dynamic arguments.
697 (let loop ((x x))
698 (if (< x 0) x (loop (1- x))))
699 (letrec (loop) (_) ((lambda (_)
700 (lambda-case
701 (((x) #f #f #f () (_))
702 (if _ _
703 (apply (lexical loop _)
704 (apply (primitive 1-)
705 (lexical x _))))))))
706 (apply (lexical loop _) (toplevel x))))
707
708 (pass-if-peval
709 ;; Recursion on the 2nd argument is fully evaluated.
710 (let ((x (top)))
711 (let loop ((x x) (y 10))
712 (if (> y 0)
713 (loop x (1- y))
714 (foo x y))))
715 (let (x) (_) ((apply (toplevel top)))
716 (apply (toplevel foo) (lexical x _) (const 0))))
717
718 (pass-if-peval
719 ;; Inlining aborted when residual code contains recursive calls.
720 ;;
721 ;; <http://debbugs.gnu.org/9542>
722 (let loop ((x x) (y 0))
723 (if (> y 0)
724 (loop (1- x) (1- y))
725 (if (< x 0)
726 x
727 (loop (1+ x) (1+ y)))))
728 (letrec (loop) (_) ((lambda (_)
729 (lambda-case
730 (((x y) #f #f #f () (_ _))
731 (if (apply (primitive >)
732 (lexical y _) (const 0))
733 _ _)))))
734 (apply (lexical loop _) (toplevel x) (const 0))))
735
736 (pass-if-peval
737 ;; Infinite recursion: `peval' gives up and leaves it as is.
738 (letrec ((f (lambda (x) (g (1- x))))
739 (g (lambda (x) (h (1+ x))))
740 (h (lambda (x) (f x))))
741 (f 0))
742 (letrec _ . _))
743
744 (pass-if-peval
745 ;; Infinite recursion: all the arguments to `loop' are static, but
746 ;; unrolling it would lead `peval' to enter an infinite loop.
747 (let loop ((x 0))
748 (and (< x top)
749 (loop (1+ x))))
750 (letrec (loop) (_) ((lambda . _))
751 (apply (lexical loop _) (const 0))))
752
753 (pass-if-peval
754 ;; This test checks that the `start' binding is indeed residualized.
755 ;; See the `referenced?' procedure in peval's `prune-bindings'.
756 (let ((pos 0))
de1eb420 757 (let ((here (let ((start pos)) (lambda () start))))
1cd63115 758 (set! pos 1) ;; Cause references to `pos' to residualize.
de1eb420
AW
759 (here)))
760 (let (pos) (_) ((const 0))
1cd63115
AW
761 (let (here) (_) (_)
762 (begin
763 (set! (lexical pos _) (const 1))
de1eb420
AW
764 (apply (lexical here _))))))
765
766 (pass-if-peval
767 ;; FIXME: should this one residualize the binding?
768 (letrec ((a a))
769 1)
770 (const 1))
771
772 (pass-if-peval
773 ;; This is a fun one for peval to handle.
774 (letrec ((a a))
775 a)
776 (letrec (a) (_) ((lexical a _))
777 (lexical a _)))
778
779 (pass-if-peval
780 ;; Another interesting recursive case.
781 (letrec ((a b) (b a))
782 a)
783 (letrec (a) (_) ((lexical a _))
784 (lexical a _)))
785
786 (pass-if-peval
787 ;; Another pruning case, that `a' is residualized.
788 (letrec ((a (lambda () (a)))
789 (b (lambda () (a)))
790 (c (lambda (x) x)))
791 (let ((d (foo b)))
792 (c d)))
793
794 ;; "b c a" is the current order that we get with unordered letrec,
795 ;; but it's not important to this test, so if it changes, just adapt
796 ;; the test.
797 (letrec (b c a) (_ _ _)
798 ((lambda _
799 (lambda-case
800 ((() #f #f #f () ())
801 (apply (lexical a _)))))
802 (lambda _
803 (lambda-case
804 (((x) #f #f #f () (_))
805 (lexical x _))))
806 (lambda _
807 (lambda-case
808 ((() #f #f #f () ())
809 (apply (lexical a _))))))
810 (let (d)
811 (_)
812 ((apply (toplevel foo) (lexical b _)))
813 (apply (lexical c _)
814 (lexical d _)))))
815
816 (pass-if-peval
817 ;; In this case, we can prune the bindings. `a' ends up being copied
818 ;; because it is only referenced once in the source program. Oh
819 ;; well.
820 (letrec* ((a (lambda (x) (top x)))
821 (b (lambda () a)))
822 (foo (b) (b)))
823 (apply (toplevel foo)
824 (lambda _
825 (lambda-case
826 (((x) #f #f #f () (_))
827 (apply (toplevel top) (lexical x _)))))
828 (lambda _
829 (lambda-case
830 (((x) #f #f #f () (_))
831 (apply (toplevel top) (lexical x _)))))))
832
833 (pass-if-peval
834 ;; Constant folding: cons of #nil does not make list
835 (cons 1 #nil)
836 (apply (primitive cons) (const 1) (const '#nil)))
837
838 (pass-if-peval
839 ;; Constant folding: cons
840 (begin (cons 1 2) #f)
841 (const #f))
842
843 (pass-if-peval
844 ;; Constant folding: cons
845 (begin (cons (foo) 2) #f)
846 (begin (apply (toplevel foo)) (const #f)))
847
848 (pass-if-peval
849 ;; Constant folding: cons
850 (if (cons 0 0) 1 2)
851 (const 1))
852
853 (pass-if-peval
854 ;; Constant folding: car+cons
855 (car (cons 1 0))
856 (const 1))
857
858 (pass-if-peval
859 ;; Constant folding: cdr+cons
860 (cdr (cons 1 0))
861 (const 0))
862
863 (pass-if-peval
864 ;; Constant folding: car+cons, impure
865 (car (cons 1 (bar)))
866 (begin (apply (toplevel bar)) (const 1)))
867
868 (pass-if-peval
869 ;; Constant folding: cdr+cons, impure
870 (cdr (cons (bar) 0))
871 (begin (apply (toplevel bar)) (const 0)))
872
873 (pass-if-peval
874 ;; Constant folding: car+list
875 (car (list 1 0))
876 (const 1))
877
878 (pass-if-peval
879 ;; Constant folding: cdr+list
880 (cdr (list 1 0))
881 (apply (primitive list) (const 0)))
882
883 (pass-if-peval
884 ;; Constant folding: car+list, impure
885 (car (list 1 (bar)))
886 (begin (apply (toplevel bar)) (const 1)))
887
888 (pass-if-peval
889 ;; Constant folding: cdr+list, impure
890 (cdr (list (bar) 0))
891 (begin (apply (toplevel bar)) (apply (primitive list) (const 0))))
892
893 (pass-if-peval
894 resolve-primitives
895 ;; Non-constant guards get lexical bindings.
896 (dynamic-wind foo (lambda () bar) baz)
897 (let (pre post) (_ _) ((toplevel foo) (toplevel baz))
898 (dynwind (lexical pre _) (toplevel bar) (lexical post _))))
899
900 (pass-if-peval
901 resolve-primitives
902 ;; Constant guards don't need lexical bindings.
903 (dynamic-wind (lambda () foo) (lambda () bar) (lambda () baz))
904 (dynwind
905 (lambda ()
906 (lambda-case
907 ((() #f #f #f () ()) (toplevel foo))))
908 (toplevel bar)
909 (lambda ()
910 (lambda-case
911 ((() #f #f #f () ()) (toplevel baz))))))
912
913 (pass-if-peval
914 resolve-primitives
915 ;; Prompt is removed if tag is unreferenced
916 (let ((tag (make-prompt-tag)))
917 (call-with-prompt tag
918 (lambda () 1)
919 (lambda args args)))
920 (const 1))
921
922 (pass-if-peval
923 resolve-primitives
924 ;; Prompt is removed if tag is unreferenced, with explicit stem
925 (let ((tag (make-prompt-tag "foo")))
926 (call-with-prompt tag
927 (lambda () 1)
928 (lambda args args)))
929 (const 1))
930
931 ;; Handler lambda inlined
932 (pass-if-peval
933 resolve-primitives
934 (call-with-prompt tag
935 (lambda () 1)
936 (lambda (k x) x))
937 (prompt (toplevel tag)
938 (const 1)
939 (lambda-case
940 (((k x) #f #f #f () (_ _))
941 (lexical x _)))))
942
943 ;; Handler toplevel not inlined
944 (pass-if-peval
945 resolve-primitives
946 (call-with-prompt tag
947 (lambda () 1)
948 handler)
949 (let (handler) (_) ((toplevel handler))
950 (prompt (toplevel tag)
951 (const 1)
952 (lambda-case
953 ((() #f args #f () (_))
954 (apply (primitive @apply)
955 (lexical handler _)
956 (lexical args _)))))))
957
958 (pass-if-peval
959 resolve-primitives
960 ;; `while' without `break' or `continue' has no prompts and gets its
961 ;; condition folded. Unfortunately the outer `lp' does not yet get
962 ;; elided.
963 (while #t #t)
964 (letrec (lp) (_)
965 ((lambda _
966 (lambda-case
967 ((() #f #f #f () ())
968 (letrec (loop) (_)
969 ((lambda _
970 (lambda-case
971 ((() #f #f #f () ())
972 (apply (lexical loop _))))))
973 (apply (lexical loop _)))))))
974 (apply (lexical lp _))))
975
976 (pass-if-peval
977 resolve-primitives
978 (lambda (a . rest)
979 (apply (lambda (x y) (+ x y))
980 a rest))
981 (lambda _
982 (lambda-case
983 (((x y) #f #f #f () (_ _))
984 _))))
985
986 (pass-if-peval resolve-primitives
987 (car '(1 2))
988 (const 1)))