1;;;; eval.test --- tests guile's evaluator     -*- scheme -*-
2;;;; Copyright (C) 2000-2001,2003-2015,2017,2019,2020
3;;;;   Free Software Foundation, Inc.
4;;;;
5;;;; This library is free software; you can redistribute it and/or
6;;;; modify it under the terms of the GNU Lesser General Public
7;;;; License as published by the Free Software Foundation; either
8;;;; version 3 of the License, or (at your option) any later version.
9;;;;
10;;;; This library is distributed in the hope that it will be useful,
11;;;; but WITHOUT ANY WARRANTY; without even the implied warranty of
12;;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13;;;; Lesser General Public License for more details.
14;;;;
15;;;; You should have received a copy of the GNU Lesser General Public
16;;;; License along with this library; if not, write to the Free Software
17;;;; Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
18
19(define-module (test-suite test-eval)
20  :use-module (test-suite lib)
21  :use-module ((srfi srfi-1) :select (unfold count))
22  :use-module ((system vm vm) :select (call-with-stack-overflow-handler))
23  :use-module ((system vm frame) :select (frame-call-representation))
24  :use-module (ice-9 documentation)
25  :use-module (ice-9 local-eval))
26
27
28(define exception:bad-expression
29  (cons 'syntax-error "Bad expression"))
30
31(define exception:failed-match
32  (cons 'syntax-error "failed to match any pattern"))
33
34(define exception:not-a-list
35  (cons 'wrong-type-arg "Not a list"))
36
37(define exception:wrong-length
38  (cons 'wrong-type-arg "wrong length"))
39
40;;;
41;;; miscellaneous
42;;;
43
44(define (documented? object)
45  (not (not (object-documentation object))))
46
47
48;;;
49;;; memoization
50;;;
51
52(with-test-prefix "memoization"
53  (pass-if "transparency"
54    (let ((x '(begin 1)))
55      (eval x (current-module))
56      (equal? '(begin 1) x))))
57
58
59;;;
60;;; eval
61;;;
62
63(with-test-prefix "evaluator"
64
65  (pass-if "definitions return #<unspecified>"
66    (eq? (primitive-eval '(define test-var 'foo))
67         (if #f #f)))
68
69  (with-test-prefix "symbol lookup"
70
71    (with-test-prefix "top level"
72
73      (with-test-prefix "unbound"
74
75	(pass-if-exception "variable reference"
76	  exception:unbound-var
77	  x)
78
79	(pass-if-exception "procedure"
80	  exception:unbound-var
81	  (x)))))
82
83  (with-test-prefix "parameter error"
84
85    ;; This is currently a bug in guile:
86    ;; Macros are accepted as function parameters.
87    ;; Functions that 'apply' macros are rewritten!!!
88
89    (pass-if-exception "macro as argument"
90      exception:failed-match
91      (primitive-eval
92       '(let ((f (lambda (p a b) (p a b))))
93          (f and #t #t))))
94
95    (pass-if-exception "passing macro as parameter"
96      exception:failed-match
97      (primitive-eval
98       '(let* ((f (lambda (p a b) (p a b)))
99               (foo (procedure-source f)))
100          (f and #t #t)
101          (equal? (procedure-source f) foo))))
102
103    ))
104
105;;;
106;;; call
107;;;
108
109(with-test-prefix "call"
110
111  (with-test-prefix "wrong number of arguments"
112
113    (pass-if-exception "((lambda () #f) 1)"
114      exception:wrong-num-args
115      ((lambda () #f) 1))
116
117    (pass-if-exception "((lambda (x) #f))"
118      exception:wrong-num-args
119      ((lambda (x) #f)))
120
121    (pass-if-exception "((lambda (x) #f) 1 2)"
122      exception:wrong-num-args
123      ((lambda (x) #f) 1 2))
124
125    (pass-if-exception "((lambda (x y) #f))"
126      exception:wrong-num-args
127      ((lambda (x y) #f)))
128
129    (pass-if-exception "((lambda (x y) #f) 1)"
130      exception:wrong-num-args
131      ((lambda (x y) #f) 1))
132
133    (pass-if-exception "((lambda (x y) #f) 1 2 3)"
134      exception:wrong-num-args
135      ((lambda (x y) #f) 1 2 3))
136
137    (pass-if-exception "((lambda (x . rest) #f))"
138      exception:wrong-num-args
139      ((lambda (x . rest) #f)))
140
141    (pass-if-exception "((lambda (x y . rest) #f))"
142      exception:wrong-num-args
143      ((lambda (x y . rest) #f)))
144
145    (pass-if-exception "((lambda (x y . rest) #f) 1)"
146      exception:wrong-num-args
147      ((lambda (x y . rest) #f) 1))))
148
149;;;
150;;; apply
151;;;
152
153(with-test-prefix "apply"
154
155  (with-test-prefix "scm_tc7_subr_2o"
156
157    ;; prior to guile 1.6.9 and 1.8.1 this called the function with
158    ;; SCM_UNDEFINED, which in the case of make-vector resulted in
159    ;; wrong-type-arg, instead of the intended wrong-num-args
160    (pass-if-exception "0 args" exception:wrong-num-args
161      (apply make-vector '()))
162
163    (pass-if "1 arg"
164      (vector? (apply make-vector '(1))))
165
166    (pass-if "2 args"
167      (vector? (apply make-vector '(1 2))))
168
169    ;; prior to guile 1.6.9 and 1.8.1 this error wasn't detected
170    (pass-if-exception "3 args" exception:wrong-num-args
171      (apply make-vector '(1 2 3)))))
172
173;;;
174;;; map
175;;;
176
177(with-test-prefix "map"
178
179  ;; Is documentation available?
180
181  (expect-fail "documented?"
182    (documented? map))
183
184  (with-test-prefix "argument error"
185
186    (with-test-prefix "non list argument"
187      #t)
188
189    (with-test-prefix "different length lists"
190
191      (pass-if-exception "first list empty"
192        exception:wrong-length
193	(map + '() '(1)))
194
195      (pass-if-exception "second list empty"
196        exception:wrong-length
197	(map + '(1) '()))
198
199      (pass-if-exception "first list shorter"
200	exception:wrong-length
201	(map + '(1) '(2 3)))
202
203      (pass-if-exception "second list shorter"
204	exception:wrong-length
205	(map + '(1 2) '(3)))
206    )))
207
208(with-test-prefix "for-each"
209
210  (pass-if-exception "1 arg, non-list, even number of elements"
211      exception:not-a-list
212    (for-each values '(1 2 3 4 . 5)))
213
214  (pass-if-exception "1 arg, non-list, odd number of elements"
215      exception:not-a-list
216    (for-each values '(1 2 3 . 4))))
217
218;;;
219;;; define with procedure-name
220;;;
221
222;; names are only set on top-level procedures (currently), so these can't be
223;; hidden in a let
224;;
225(define foo-closure (lambda () "hello"))
226(define bar-closure foo-closure)
227;; make sure that make-procedure-with-setter returns an anonymous
228;; procedure-with-setter by passing it an anonymous getter.
229(define foo-pws (make-procedure-with-setter
230                 (lambda (x) (car x))
231                 (lambda (x y) (set-car! x y))))
232(define bar-pws foo-pws)
233
234(with-test-prefix "define set procedure-name"
235
236  (pass-if "closure"
237    (eq? 'foo-closure (procedure-name bar-closure)))
238
239  (expect-fail "procedure-with-setter" ; FIXME: `pass-if' when it's supported
240    (eq? 'foo-pws (procedure-name bar-pws))))
241
242;;;
243;;; promises
244;;;
245
246(with-test-prefix "promises"
247
248  (with-test-prefix "basic promise behaviour"
249
250    (pass-if "delay gives a promise"
251      (promise? (delay 1)))
252
253    (pass-if "force evaluates a promise"
254      (eqv? (force (delay (+ 1 2))) 3))
255
256    (pass-if "a forced promise is a promise"
257      (let ((p (delay (+ 1 2))))
258	(force p)
259	(promise? p)))
260
261    (pass-if "forcing a forced promise works"
262      (let ((p (delay (+ 1 2))))
263	(force p)
264	(eqv? (force p) 3)))
265
266    (pass-if "a promise is evaluated once"
267      (let* ((x 1)
268	     (p (delay (+ x 1))))
269	(force p)
270	(set! x (+ x 1))
271	(eqv? (force p) 2)))
272
273    (pass-if "a promise may call itself"
274      (define p
275	(let ((x 0))
276	  (delay
277	    (begin
278	      (set! x (+ x 1))
279	      (if (> x 1) x (force p))))))
280      (eqv? (force p) 2))
281
282    (pass-if "a promise carries its environment"
283      (let* ((x 1) (p #f))
284	(let* ((x 2))
285	  (set! p (delay (+ x 1))))
286	(eqv? (force p) 3)))
287
288    (pass-if "a forced promise does not reference its environment"
289      (let* ((g (make-guardian))
290	     (p #f))
291	(let* ((x (cons #f #f)))
292	  (g x)
293	  (set! p (delay (car x))))
294	(force p)
295        (gc)
296        ;; Though this test works reliably when running just eval.test,
297        ;; it often does the unresolved case when running the full
298        ;; suite.  Adding this extra gc makes the full-suite behavior
299        ;; pass more reliably.
300	(gc)
301        (if (not (equal? (g) (cons #f #f)))
302	    (throw 'unresolved)
303	    #t))))
304
305  (with-test-prefix "extended promise behaviour"
306
307    (pass-if-exception "forcing a non-promise object is not supported"
308      exception:wrong-type-arg
309      (force 1))
310
311    (pass-if "unmemoizing a promise"
312      (display-backtrace
313       (let ((stack #f))
314         (false-if-exception
315          (with-throw-handler #t
316                              (lambda ()
317                                (let ((f (lambda (g) (delay (g)))))
318                                  (force (f error))))
319                              (lambda _
320                                (set! stack (make-stack #t)))))
321         stack)
322       (%make-void-port "w"))
323      #t)))
324
325
326;;;
327;;; stacks
328;;;
329
330(define (stack->frames stack)
331  ;; Return the list of frames comprising STACK.
332  (unfold (lambda (i)
333            (>= i (stack-length stack)))
334          (lambda (i)
335            (stack-ref stack i))
336          1+
337          0))
338
339(define (make-tagged-trimmed-stack tag spec)
340  (catch 'result
341    (lambda ()
342      (call-with-prompt
343        tag
344        (lambda ()
345          (with-throw-handler 'wrong-type-arg
346            (lambda () (substring 'wrong 'type 'arg))
347            (lambda _ (throw 'result (apply make-stack spec)))))
348        (lambda () (throw 'make-stack-failed))))
349    (lambda (key result) result)))
350
351(define tag (make-prompt-tag "foo"))
352
353(with-test-prefix "stacks"
354  (pass-if "stack involving a primitive"
355    ;; The primitive involving the error must appear exactly once on the
356    ;; stack.
357    (let* ((stack (make-tagged-trimmed-stack tag '(#t)))
358           (frames (stack->frames stack))
359           (num (count (lambda (frame) (eq? (frame-procedure-name frame)
360                                            'substring))
361                       frames)))
362      (= num 1)))
363
364  (pass-if "arguments of a primitive stack frame"
365    ;; Create a stack with two primitive frames and make sure the
366    ;; arguments are correct.
367    (let* ((stack (make-tagged-trimmed-stack tag '(#t)))
368           (call-list (map frame-call-representation (stack->frames stack))))
369      (and (equal? (car call-list) '(make-stack #t))
370           (pair? (member '(substring wrong type arg)
371                          (cdr call-list))))))
372
373  (pass-if "inner trim with prompt tag"
374    (let* ((stack (make-tagged-trimmed-stack tag `(#t ,tag)))
375           (frames (stack->frames stack)))
376      ;; the top frame on the stack is the body of the catch, and the
377      ;; next frame is the with-exception-handler corresponding to the
378      ;; (catch 'result ...)
379      (eq? (car (frame-call-representation (cadr frames)))
380           'with-exception-handler)))
381
382  (pass-if "outer trim with prompt tag"
383    (let* ((stack (make-tagged-trimmed-stack tag `(#t 0 ,tag)))
384           (frames (stack->frames stack)))
385      ;; the top frame on the stack is the make-stack call, and the last
386      ;; frame is the (with-throw-handler 'wrong-type-arg ...)
387      (and (eq? (car (frame-call-representation (car frames)))
388                'make-stack)
389           (eq? (car (frame-call-representation (car (last-pair frames))))
390                'with-exception-handler)))))
391
392;;;
393;;; letrec init evaluation
394;;;
395
396(with-test-prefix "letrec init evaluation"
397
398  (pass-if "lots of inits calculated in correct order"
399    (equal? (letrec ((a 'a) (b 'b) (c 'c) (d 'd)
400		     (e 'e) (f 'f) (g 'g) (h 'h)
401		     (i 'i) (j 'j) (k 'k) (l 'l)
402		     (m 'm) (n 'n) (o 'o) (p 'p)
403		     (q 'q) (r 'r) (s 's) (t 't)
404		     (u 'u) (v 'v) (w 'w) (x 'x)
405		     (y 'y) (z 'z))
406	      (list a b c d e f g h i j k l m
407		    n o p q r s t u v w x y z))
408	    '(a b c d e f g h i j k l m
409	      n o p q r s t u v w x y z))))
410
411;;;
412;;; values
413;;;
414
415(with-test-prefix "values"
416
417  (pass-if "single value"
418    (equal? 1 (values 1)))
419
420  (pass-if "call-with-values"
421    (equal? (call-with-values (lambda () (values 1 2 3 4)) list)
422            '(1 2 3 4)))
423
424  (pass-if "equal?"
425    (equal? (values 1 2 3 4) (values 1 2 3 4))))
426
427;;;
428;;; stack overflow handling
429;;;
430
431(with-test-prefix "stack overflow handlers"
432  (define (trigger-overflow)
433    (trigger-overflow)
434    (error "not reached"))
435
436  (define (dynwind-test n)
437    (catch 'foo
438      (lambda ()
439        (call-with-stack-overflow-handler n
440          (lambda ()
441            (dynamic-wind (lambda () #t)
442                          trigger-overflow
443                          trigger-overflow))
444          (lambda ()
445            (throw 'foo))))
446      (lambda _ #t)))
447
448  (pass-if-exception "limit should be number"
449      exception:wrong-type-arg
450    (call-with-stack-overflow-handler #t
451      trigger-overflow trigger-overflow))
452
453  (pass-if-exception "limit should be exact integer"
454      exception:wrong-type-arg
455    (call-with-stack-overflow-handler 2.0
456      trigger-overflow trigger-overflow))
457
458  (pass-if-exception "limit should be nonnegative"
459      exception:out-of-range
460    (call-with-stack-overflow-handler -1
461      trigger-overflow trigger-overflow))
462
463  (pass-if-exception "limit should be positive"
464      exception:out-of-range
465    (call-with-stack-overflow-handler 0
466      trigger-overflow trigger-overflow))
467
468  (pass-if-exception "limit should be within address space"
469      exception:out-of-range
470    (call-with-stack-overflow-handler (ash 1 64)
471      trigger-overflow trigger-overflow))
472
473  (pass-if "exception on overflow"
474    (catch 'foo
475      (lambda ()
476        (call-with-stack-overflow-handler 10000
477          trigger-overflow
478          (lambda ()
479            (throw 'foo))))
480      (lambda _ #t)))
481
482  (pass-if "exception on overflow with dynwind"
483    ;; Try all limits between 1 and 200 words.
484    (let lp ((n 1))
485      (or (= n 200)
486          (and (dynwind-test n)
487               (lp (1+ n))))))
488
489  (pass-if-exception "overflow handler should return number"
490      exception:wrong-type-arg
491    (call-with-stack-overflow-handler 1000
492      trigger-overflow
493      (lambda () #t)))
494  (pass-if-exception "overflow handler should return exact integer"
495      exception:wrong-type-arg
496    (call-with-stack-overflow-handler 1000
497      trigger-overflow
498      (lambda () 2.0)))
499  (pass-if-exception "overflow handler should be nonnegative"
500      exception:out-of-range
501    (call-with-stack-overflow-handler 1000
502      trigger-overflow
503      (lambda () -1)))
504  (pass-if-exception "overflow handler should be positive"
505      exception:out-of-range
506    (call-with-stack-overflow-handler 1000
507      trigger-overflow
508      (lambda () 0)))
509
510  (letrec ((fac (lambda (n)
511                  (if (zero? n) 1 (* n (fac (1- n)))))))
512    (pass-if-equal "overflow handler can allow recursion to continue"
513        (fac 10)
514      (call-with-stack-overflow-handler 1
515        (lambda () (fac 10))
516        (lambda () 1)))))
517
518;;;
519;;; docstrings
520;;;
521
522(with-test-prefix "docstrings"
523
524  (pass-if-equal "fixed closure"
525      '("hello" "world")
526    (map procedure-documentation
527         (list (eval '(lambda (a b) "hello" (+ a b))
528                     (current-module))
529               (eval '(lambda (a b) "world" (- a b))
530                     (current-module)))))
531
532  (pass-if-equal "fixed closure with many args"
533      "So many args."
534    (procedure-documentation
535     (eval '(lambda (a b c d e f g h i j k)
536              "So many args."
537              (+ a b))
538           (current-module))))
539
540  (pass-if-equal "general closure"
541      "How general."
542    (procedure-documentation
543     (eval '(lambda* (a b #:key k #:rest r)
544              "How general."
545              (+ a b))
546           (current-module)))))
547
548;;;
549;;; local-eval
550;;;
551
552(with-test-prefix "local evaluation"
553
554  (pass-if "local-eval"
555
556    (let* ((env1 (local-eval '(let ((x 1) (y 2) (z 3))
557                                (define-syntax-rule (foo x) (quote x))
558                                (the-environment))
559                             (current-module)))
560           (env2 (local-eval '(let ((x 111) (a 'a))
561                                (define-syntax-rule (bar x) (quote x))
562                                (the-environment))
563                           env1)))
564      (local-eval '(set! x 11) env1)
565      (local-eval '(set! y 22) env1)
566      (local-eval '(set! z 33) env2)
567      (and (equal? (local-eval '(list x y z) env1)
568                   '(11 22 33))
569           (equal? (local-eval '(list x y z a) env2)
570                   '(111 22 33 a)))))
571
572  (pass-if "local-compile"
573
574    (let* ((env1 (local-compile '(let ((x 1) (y 2) (z 3))
575                                   (define-syntax-rule (foo x) (quote x))
576                                   (the-environment))
577                                (current-module)))
578           (env2 (local-compile '(let ((x 111) (a 'a))
579                                   (define-syntax-rule (bar x) (quote x))
580                                   (the-environment))
581                                env1)))
582      (local-compile '(set! x 11) env1)
583      (local-compile '(set! y 22) env1)
584      (local-compile '(set! z 33) env2)
585      (and (equal? (local-compile '(list x y z) env1)
586                   '(11 22 33))
587           (equal? (local-compile '(list x y z a) env2)
588                   '(111 22 33 a)))))
589
590  (pass-if "the-environment within a macro"
591    (let ((module-a-name '(test module the-environment a))
592          (module-b-name '(test module the-environment b)))
593      (let ((module-a (resolve-module module-a-name))
594            (module-b (resolve-module module-b-name)))
595        (module-use! module-a (resolve-interface '(guile)))
596        (module-use! module-a (resolve-interface '(ice-9 local-eval)))
597        (eval '(begin
598                 (define z 3)
599                 (define-syntax-rule (test)
600                   (let ((x 1) (y 2))
601                     (the-environment))))
602              module-a)
603        (module-use! module-b (resolve-interface '(guile)))
604        (let ((env (local-eval `(let ((x 111) (y 222))
605                                  ((@@ ,module-a-name test)))
606                               module-b)))
607          (equal? (local-eval '(list x y z) env)
608                  '(1 2 3))))))
609
610  (pass-if "capture pattern variables"
611    (let ((env (syntax-case #'(((a 1) (b 2) (c 3))
612                               ((d 4) (e 5) (f 6))) ()
613                 ((((k v) ...) ...) (the-environment)))))
614      (equal? (syntax->datum (local-eval '#'((k ... v ...) ...) env))
615              '((a b c 1 2 3) (d e f 4 5 6)))))
616
617  (pass-if "mixed primitive-eval, local-eval and local-compile"
618
619    (let* ((env1 (primitive-eval '(let ((x 1) (y 2) (z 3))
620                                    (define-syntax-rule (foo x) (quote x))
621                                    (the-environment))))
622           (env2 (local-eval '(let ((x 111) (a 'a))
623                                (define-syntax-rule (bar x) (quote x))
624                                (the-environment))
625                             env1))
626           (env3 (local-compile '(let ((y 222) (b 'b))
627                                   (the-environment))
628                                env2)))
629      (local-eval    '(set! x 11) env1)
630      (local-compile '(set! y 22) env2)
631      (local-eval    '(set! z 33) env2)
632      (local-compile '(set! a (* y 2)) env3)
633      (and (equal? (local-compile '(list x y z) env1)
634                   '(11 22 33))
635           (equal? (local-eval '(list x y z a) env2)
636                   '(111 22 33 444))
637           (equal? (local-eval '(list x y z a b) env3)
638                   '(111 222 33 444 b))))))
639
640;;; eval.test ends here
641