~ chicken-core (chicken-5) /tests/compiler-tests.scm


  1;;;; compiler-tests.scm
  2
  3
  4(import (chicken bitwise) (chicken flonum) (chicken foreign)
  5	(chicken condition) (srfi 4))
  6(import-for-syntax (chicken syntax) (chicken string))
  7
  8;; test dropping of previous toplevel assignments
  9
 10(define (foo) (define (bar) 1) (bar 2))	; will trigger error later
 11(define bar 1)
 12(define (baz) 2)
 13(define (foo) 'ok)
 14
 15(assert (eq? 'ok (foo)))
 16
 17(assert (= 1 (foreign-type-size "char")))
 18(let* ((words->bytes (foreign-lambda int "C_wordstobytes" int))
 19       (bytes-in-a-word (words->bytes 1)))
 20  (assert (= bytes-in-a-word (foreign-type-size "C_word"))))
 21
 22;; test hiding of unexported toplevel variables
 23
 24(module foo (bar)
 25  (import scheme chicken.base)
 26  (declare (hide bar))
 27  (define (bar x) (+ x 1)))
 28
 29(assert (not (##sys#symbol-has-toplevel-binding? 'foo#bar)))
 30
 31
 32;;; rev. 12104 (reported by Jörg Wittenberger)
 33;
 34; - canonicalization of assignment to location didn't walk expansion recursively
 35
 36(define test-location
 37  (let-location ((again bool #f))
 38    (lambda ()
 39      ((foreign-lambda*
 40	   int
 41	   (((c-pointer bool) again))
 42	 "*again=1; return(1);")
 43       (location again))
 44      again)))
 45
 46(assert (test-location))
 47
 48
 49;;; rev. 12188 (reported by Jörg Wittenberger)
 50;
 51; - generated init-assignment refers to alias, but alias isn't seen later)
 52
 53(module
 54 x
 55 (bar)
 56 (import scheme chicken.base chicken.foreign)
 57
 58 (define (bar n)
 59  (let-location ((off integer 0))
 60    (lambda () 
 61      ((foreign-lambda*
 62	   void
 63	   (((c-pointer integer) i))
 64	 "(*i)++;")
 65       (location off))
 66      off)))
 67)
 68
 69(import x)
 70(assert (= 1 ((bar 42))))
 71
 72
 73;; Test custom foreign type conversions
 74
 75(module y (my-add1)
 76  (import scheme (chicken base) (chicken foreign))
 77
 78  (define-foreign-type my-int integer add1 sub1)
 79
 80  (define my-add1 (foreign-lambda* my-int ((my-int x)) "C_return(x+1);")))
 81
 82(import y)
 83(assert (= 2 (my-add1 1)))
 84
 85;;; rev. 14574 (reported by Peter Bex)
 86;
 87; - type specifiers in foreign-lambda in macros are incorrectly renamed
 88; - variable names and type specifiers in foreign-lambda* and
 89;    foreign-primitive in macros are incorrectly renamed
 90
 91(let-syntax ((strlen-macro
 92              (syntax-rules ()
 93                ((strlen-macro arg)
 94                 (print ((foreign-lambda int strlen c-string) arg)))))
 95             (strlen-macro*
 96              (syntax-rules ()
 97                ((strlen-macro* arg)
 98                 (print ((foreign-lambda* int ((c-string str))
 99                                          "C_return(strlen(str));") arg)))))
100             (strlen-safe-macro
101              (syntax-rules ()
102                ((strlen-safe-macro arg)
103                 (print ((foreign-safe-lambda int strlen c-string) arg)))))
104             (strlen-safe-macro*
105              (syntax-rules ()
106                ((strlen-safe-macro* arg)
107                 (print ((foreign-safe-lambda* int ((c-string str))
108                                               "C_return(strlen(str));") arg)))))
109             (strlen-primitive-macro
110              (syntax-rules ()
111                ((strlen-primitive-macro* arg)
112                 (print ((foreign-primitive int ((c-string str))
113                                            "C_return(strlen(str));") arg))))))
114  (strlen-macro "hello, world")
115  (strlen-macro* "hello, world")
116  (strlen-safe-macro "hello, world")
117  (strlen-safe-macro* "hello, world")
118  (strlen-primitive-macro "hello, world"))
119
120;; Type specifiers and variable names in foreign-lambda in macros
121;; are incorrectly renamed in modules, too.
122(foreign-declare "void foo(void *abc) { printf(\"hi\\n\"); }")
123;; This is silly but at least it ensures we can represent enum values
124(foreign-declare "enum intlimits {min=INT_MIN, zero=0, max=INT_MAX};")
125
126(module foo ()
127  (import scheme chicken.base chicken.foreign)
128
129  (let-syntax ((fl
130                (syntax-rules ()
131                  ((_)
132                   (foreign-lambda void foo (c-pointer void)))))
133               (fl*
134                (syntax-rules ()
135                  ((_)
136                   (foreign-lambda* void (((c-pointer void) a))
137                                    "C_return(a);"))))
138               (fp
139                (syntax-rules ()
140                  ((_)
141                   (foreign-primitive void (((c-pointer void) a))
142                                      "C_return(a);")))))
143    (fl)
144    (fl*)
145    (fp)))
146
147
148;; "const" qualifier should have no visible effect in Scheme
149(define-syntax generate-external
150  (syntax-rules ()
151    ((_) (define-external
152           (print_foo ((const c-string) foo))
153           void
154           (assert (string? foo))
155           (print foo)))))
156(generate-external)
157((foreign-safe-lambda* void () 
158   "print_foo(\"bar\");"))
159
160
161;; Unused arguments in foreign callback wrappers are not optimized away (#584)
162(module bla (foo)
163
164(import (prefix scheme s:) (only chicken.base assert) chicken.foreign)
165
166(define-external
167  (blabla (int a) (c-string b) (int c) (int d) (c-string e) (int f))
168  int
169  f)
170
171(s:define (foo) ((foreign-safe-lambda* int () "C_return(blabla(1, \"2\", 3, 4, \"5\", 6));")))
172
173(assert (location blabla))
174)
175
176(import bla)
177(assert (= (foo) 6))
178
179
180;;; compiler-syntax for map/for-each must be careful when the
181;   operator may have side-effects (currently only lambda exprs and symbols
182;   are allowed)
183
184(let ((x #f))
185  (define (f1 x) (print* x " "))
186  (map f1 '(1 2 3))
187  (newline)
188  (map (begin (assert (not x)) 
189	      (set! x #t)
190	      f1)
191       '(1 2 3))
192  (map (lambda (x) (print* ":" x)) '(1 2 3))
193  (newline))
194
195(let ((x #f))
196  (define (f1 x) (print* x " "))
197  (let-syntax ((f1 (syntax-rules ()
198		     ((_ y) 
199		      (begin
200			(assert (not x))
201			(set! x #t)
202			f1)))))
203    (for-each f1 '(1 2 3))))
204
205(newline)
206
207;; Test safety of ##sys#make-c-string
208(handle-exceptions exn (print "Good, unrepresentable C strings cause errors")
209                   (print "BUG! We got, without error, length = "
210                          ((foreign-lambda* int ((c-string str))
211                                            "C_return(strlen(str));")
212                           "foo\x00bar")))
213
214
215;; failed compile-time argument count check (reported by Alan Post)
216;; cbb27fe380ff8e45cdf04d812e1ec649bf45ca47
217
218(define (foo)
219  (define (bar #!key a)
220    (define (baz)
221      (bar a: #t))
222    baz)
223  bar)
224
225
226;; global-propagation must also invalidate alias to global if global
227;; itself is assigned (reported by Sven Hartrumpf)
228
229(define gp-test-global 0)
230
231(define (gp-test)
232  (let ((a gp-test-global)
233	(b gp-test-global))
234    (set! gp-test-global 1)
235    (assert (zero? a))
236    (assert (zero? b))))
237
238(gp-test)
239
240;; Optimizer would "lift" inner-bar out of its let and replace
241;; outer-bar with it, even though it wasn't visible yet.  Caused by
242;; broken cps-conversion (underlying problem for #1068).
243(assert (equal? 1 (let ((outer-bar (##core#undefined)))
244                    (let ((inner-bar (let ((tmp (lambda (x)
245                                                  (if x '1 (outer-bar '#t)))))
246                                       tmp)))
247                      (set! outer-bar inner-bar)
248                      (outer-bar #f)))))
249
250;; Slightly modified version which broke after fixing the above due 
251;; to replacement optimization getting triggered.  This replacement 
252;; caused outer-bar to get replaced by inner-bar, even within itself, 
253;; thereby causing an undefined variable reference. 
254(assert (equal? 1 (let ((outer-bar (##core#undefined))) 
255                    (let ((inner-bar (lambda (x)
256                                       (if x '1 (outer-bar outer-bar))))) 
257                      (set! outer-bar inner-bar) 
258                      (outer-bar '#f))))) 
259
260;; Found by Claude Marinier: Huge literals with a length which need
261;; more than 3 bytes to encode would get silently truncated.  We'll
262;; prevent constant-folding if it would lead to such large literals.
263(let* ((bignum (expt 2 70000000))
264       ;; This prevents complete evaluation at compile-time
265       (unknown-bignum ((foreign-lambda* scheme-object
266			    ((scheme-object n)) "C_return(n);") bignum)))
267  (assert (equal? 70000001 (integer-length unknown-bignum))))
268
269
270;; Test that encode-literal/decode-literal use the proper functions
271;; to decode number literals.
272(assert (equal? '(+inf.0 -inf.0) (list (fp/ 1.0 0.0) (fp/ -1.0 0.0))))
273
274;; Test that encode-literal doesn't drop digits for extreme flonum values.
275
276;; This number is 2^971 * (2^53 - 1), and is the positive "all ones" number for
277;; 64-bit flonums with precision 53 and significand/mantissa 10.
278;; If we want to support 32-bit flonums or flonums with different precision
279;; or significand, we need a cond-expand here or something.
280;; Technically, even larger decimal numbers can be represented by flonums.
281;; This number can correctly be compared exactly.
282(assert (= (* (- (expt 2 flonum-precision) 1)
283              (expt 2 (- flonum-maximum-exponent flonum-precision)))
284           179769313486231570814527423731704356798070567525844996598917476803157260780028538760589558632766878171540458953514382464234321326889464182768467546703537516986049910576551282076245490090389328944075868508455133942304583236903222948165808559332123348274797826204144723168738177180919299881250404026184124858368.0
285           (string->number "179769313486231570814527423731704356798070567525844996598917476803157260780028538760589558632766878171540458953514382464234321326889464182768467546703537516986049910576551282076245490090389328944075868508455133942304583236903222948165808559332123348274797826204144723168738177180919299881250404026184124858368.0")))
286
287;; #955: unsigned-integer64 arg returned magnitude instead of Scheme object.
288(assert (eqv? #xAB54A98CEB1F0AD2
289	      ((foreign-lambda* unsigned-integer64 ((unsigned-integer64 x))
290		 "C_return(x);")
291	       #xAB54A98CEB1F0AD2)))
292
293
294;; Test the maximum and minimum values of the FFI's integer types
295(define-syntax test-ffi-type-limits
296  (syntax-rules (signed unsigned)
297    ((_ ?type-name unsigned ?bits)
298     (let ((limit (arithmetic-shift 1 ?bits)))
299       (print "Testing unsigned FFI type \"" '?type-name "\" (" ?bits " bits):")
300       (print "Can hold maximum value " (sub1 limit) "...")
301       (assert
302	(eqv? (sub1 limit)
303	      ((foreign-lambda* ?type-name ((?type-name x))
304		 "C_return(x);") (sub1 limit))))
305       (print "Cannot hold one more than maximum value, " limit "...")
306       (assert
307	(handle-exceptions exn #t
308	  (begin ((foreign-lambda* ?type-name ((?type-name x))
309		    "C_return(x);") limit)
310		 #f)))
311       (print "Cannot hold -1 (any fixnum negative value)")
312       (assert
313	(handle-exceptions exn #t
314	  (begin ((foreign-lambda* ?type-name ((?type-name x))
315		    "C_return(x);") -1)
316		 #f)))
317       (print "Cannot hold -2^64 (any bignum negative value < smallest int64)")
318       (assert
319	(handle-exceptions exn #t
320	  (begin ((foreign-lambda* ?type-name ((?type-name x))
321		    "C_return(x);") #x-10000000000000000)
322		 #f)))))
323    ((_ ?type-name signed ?bits)
324     (let ((limit (arithmetic-shift 1 (sub1 ?bits))))
325       (print "Testing signed FFI type \"" '?type-name "\" (" ?bits " bits):")
326       (print "Can hold maximum value " (sub1 limit) "...")
327       (assert
328	(eqv? (sub1 limit)
329	      ((foreign-lambda* ?type-name ((?type-name x))
330		 "C_return(x);") (sub1 limit))))
331       (print "Can hold minimum value " (- limit) "...")
332       (assert
333	(eqv? (- limit)
334	      ((foreign-lambda* ?type-name ((?type-name x))
335		 "C_return(x);") (- limit))))
336       (print "Cannot hold one more than maximum value " limit "...")
337       (assert
338	(handle-exceptions exn #t
339	  (begin ((foreign-lambda* ?type-name ((?type-name x))
340		    "C_return(x);") limit)
341		 #f)))
342       (print "Cannot hold one less than minimum value " (- limit) "...")
343       (assert
344	(handle-exceptions exn #t
345	  (begin ((foreign-lambda* ?type-name ((?type-name x))
346		    "C_return(x);") (sub1 (- limit)))
347		 #f)))))))
348
349(test-ffi-type-limits unsigned-integer32 unsigned 32)
350(test-ffi-type-limits integer32 signed 32)
351
352(test-ffi-type-limits unsigned-integer64 unsigned 64)
353(test-ffi-type-limits integer64 signed 64)
354
355(test-ffi-type-limits
356 unsigned-integer unsigned
357 (foreign-value "sizeof(unsigned int) * CHAR_BIT" int))
358
359(test-ffi-type-limits
360 integer signed (foreign-value "sizeof(int) * CHAR_BIT" int))
361
362(test-ffi-type-limits
363 (enum intlimits) signed
364 (foreign-value "sizeof(enum intlimits) * CHAR_BIT" int))
365
366(test-ffi-type-limits
367 unsigned-long unsigned
368 (foreign-value "sizeof(unsigned long) * CHAR_BIT" int))
369
370(test-ffi-type-limits
371 long signed (foreign-value "sizeof(long) * CHAR_BIT" int))
372
373(test-ffi-type-limits
374 ssize_t signed (foreign-value "sizeof(ssize_t) * CHAR_BIT" int))
375
376(test-ffi-type-limits
377 size_t unsigned (foreign-value "sizeof(size_t) * CHAR_BIT" int))
378
379
380;; #1059: foreign vector types use wrong lolevel accessors, causing
381;; paranoid DEBUGBUILD assertions to fail.
382(define-syntax srfi-4-vector-length
383  (er-macro-transformer
384   (lambda (e r c)
385     (let* ((type (symbol->string (strip-syntax (cadr e))))
386	    (base-type (string-translate* type '(("nonnull-" . ""))))
387	    (length-procedure-name (string-append base-type "-length")))
388       `(,(string->symbol length-procedure-name) ,(caddr e))))))
389
390(define-syntax s4v-sum
391  (syntax-rules ()
392    ((_ "integer" type arg)
393     ((foreign-lambda* int ((type v) (int len))
394        "int i, result = 0;"
395        "for (i = 0; i < len; ++i) {"
396        "  result += (int)v[i];"
397        "}"
398        "C_return(result);") arg (srfi-4-vector-length type arg)))
399    ((_ "float" type arg)
400     ((foreign-lambda* double ((type v) (int len))
401        "int i; double result = 0.0;"
402        "for (i = 0; i < len; ++i) {"
403        "  result += v[i];"
404        "}"
405        "C_return(result);") arg (srfi-4-vector-length type arg)))))
406(assert (= 10 (s4v-sum "integer" u8vector '#u8(1 2 3 4))))
407(assert (= 10 (s4v-sum "integer" u16vector '#u16(1 2 3 4))))
408(assert (= 10 (s4v-sum "integer" u32vector '#u32(1 2 3 4))))
409(assert (= 10 (s4v-sum "integer" s64vector '#s64(1 2 3 4))))
410(assert (= 10 (s4v-sum "integer" nonnull-u8vector '#u8(1 2 3 4))))
411(assert (= 10 (s4v-sum "integer" nonnull-u16vector '#u16(1 2 3 4))))
412(assert (= 10 (s4v-sum "integer" nonnull-u32vector '#u32(1 2 3 4))))
413(assert (= 10 (s4v-sum "integer" nonnull-u64vector '#u64(1 2 3 4))))
414(assert (= -10 (s4v-sum "integer" s8vector '#s8(-1 -2 -3 -4))))
415(assert (= -10 (s4v-sum "integer" s16vector '#s16(-1 -2 -3 -4))))
416(assert (= -10 (s4v-sum "integer" s32vector '#s32(-1 -2 -3 -4))))
417(assert (= -10 (s4v-sum "integer" s64vector '#s64(-1 -2 -3 -4))))
418(assert (= -10 (s4v-sum "integer" nonnull-s8vector '#s8(-1 -2 -3 -4))))
419(assert (= -10 (s4v-sum "integer" nonnull-s16vector '#s16(-1 -2 -3 -4))))
420(assert (= -10 (s4v-sum "integer" nonnull-s32vector '#s32(-1 -2 -3 -4))))
421(assert (= -10 (s4v-sum "integer" nonnull-s64vector '#s64(-1 -2 -3 -4))))
422(assert (= 12.0 (s4v-sum "float" f32vector '#f32(1.5 2.5 3.5 4.5))))
423(assert (= 12.0 (s4v-sum "float" f64vector '#f64(1.5 2.5 3.5 4.5))))
424(assert (= 12.0 (s4v-sum "float" nonnull-f32vector '#f32(1.5 2.5 3.5 4.5))))
425(assert (= 12.0 (s4v-sum "float" nonnull-f64vector '#f64(1.5 2.5 3.5 4.5))))
426
427
428;; Reported by Jörg Wittenberger: in some cases, -profile would
429;; generate calls to procedures.  This was due to calls to pure
430;; procedures not getting replaced with explicitly consed rest
431;; list when the procedures themselves were hidden.
432(module explicitly-consed-rest-args-bug (do-it also-do-it)
433 (import scheme chicken.base chicken.type)
434
435 (: get-value (* * #!rest * --> *))
436 (define (get-value x y . rest)
437   (apply x y rest))
438 (define (do-it arg)
439   (get-value arg 2))
440 (define (also-do-it arg)
441   (get-value arg 3))
442)
443
444; let-location with const-wrapped type
445(let-location ((foo (const c-string) "boo"))
446  (assert (equal? foo "boo")))
447
448; #1424: a foreign lambda with const return type was wrongly rejected
449(let ((v0 ((foreign-lambda* c-string () "C_return(\"str\");")))
450      (v1 ((foreign-lambda* (const c-string) () "C_return(\"str\");"))))
451  (assert (equal? v0 v1)))
452
453; #1630: inlining may result in incorrectly flagged argument-
454;   count errors.
455(define (outer x y)
456  (define (append-map proc . lsts)
457    (if (null? lsts)
458        (proc 1)
459        (apply proc lsts)))
460  (append-map (lambda (a) (assert (= a 1))))
461  (append-map (lambda (a b) (assert (and (= a 3) (= b 4))))
462    x y))
463(outer 3 4)
464
465; #1703: argvector re-use interfered with rest-arg optimization
466(define reduce (lambda (_l ini) (+ ini 1)))
467
468(print ((lambda xs (reduce xs (car xs))) 1 2 3)) ;; prints 2
469
470(define fold- (lambda xs (reduce xs (car xs))))
471(print (fold- 1 2 3))
472
473; libraries are only loaded when entry point is called
474(let ()
475  (if #f (require-library (chicken repl)))
476  (assert (not (##sys#symbol-has-toplevel-binding? 'chicken.repl#repl)))
477  (if #t (require-library (chicken repl)))
478  (assert (##sys#symbol-has-toplevel-binding? 'chicken.repl#repl)))
Trap