~ chicken-core (chicken-5) /scrutinizer.scm
Trap1;;;; scrutinizer.scm - The CHICKEN Scheme compiler (local flow analysis)
2;
3; Copyright (c) 2009-2022, The CHICKEN Team
4; All rights reserved.
5;
6; Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following
7; conditions are met:
8;
9; Redistributions of source code must retain the above copyright notice, this list of conditions and the following
10; disclaimer.
11; Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following
12; disclaimer in the documentation and/or other materials provided with the distribution.
13; Neither the name of the author nor the names of its contributors may be used to endorse or promote
14; products derived from this software without specific prior written permission.
15;
16; THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS
17; OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
18; AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR
19; CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
20; CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
21; SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
22; THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
23; OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
24; POSSIBILITY OF SUCH DAMAGE.
25
26
27(declare
28 (unit scrutinizer)
29 (uses data-structures expand extras pathname port support internal))
30
31(module chicken.compiler.scrutinizer
32 (scrutinize load-type-database emit-types-file
33 validate-type check-and-validate-type install-specializations
34 ;; Exported for use in the tests:
35 match-types refine-types type<=?)
36
37(import scheme
38 chicken.base
39 chicken.compiler.support
40 chicken.fixnum
41 chicken.format
42 chicken.internal
43 chicken.io
44 chicken.keyword
45 chicken.pathname
46 chicken.platform
47 chicken.plist
48 chicken.sort
49 chicken.port
50 chicken.pretty-print
51 chicken.string
52 chicken.syntax)
53
54(include "tweaks")
55(include "mini-srfi-1.scm")
56
57(define d-depth 0)
58(define scrutiny-debug #t)
59(define *complain?* #f)
60
61(define (d fstr . args)
62 (when (and scrutiny-debug (##sys#debug-mode?))
63 (printf "[debug|~a] ~a~?~%" d-depth (make-string d-depth #\space) fstr args)) )
64
65(define dd d)
66(define ddd d)
67
68(define-syntax d (syntax-rules () ((_ . _) (void))))
69(define-syntax dd (syntax-rules () ((_ . _) (void))))
70(define-syntax ddd (syntax-rules () ((_ . _) (void))))
71
72
73;;; Walk node tree, keeping type and binding information
74;
75; result specifiers:
76;
77; SPEC = * | (TYPE1 ...)
78; TYPE = (or TYPE1 TYPE2 ...)
79; | (not TYPE)
80; | (struct NAME)
81; | (procedure [NAME] (TYPE1 ... [#!optional TYPE1 ...] [#!rest [TYPE | values]]) . RESULTS)
82; | VALUE
83; | BASIC
84; | COMPLEX
85; | (forall (TVAR1 ...) TYPE)
86; | (refine (SYMBOL ...) VALUE)
87; | deprecated
88; | (deprecated NAME)
89; VALUE = string | symbol | keyword | char | number |
90; boolean | true | false |
91; null | eof | bwp | blob | pointer | port | locative | fixnum |
92; float | bignum | ratnum | cplxnum | integer | pointer-vector
93; BASIC = * | list | pair | procedure | vector | undefined | noreturn | values
94; COMPLEX = (pair TYPE TYPE)
95; | (vector-of TYPE)
96; | (list-of TYPE)
97; | (vector TYPE1 ...)
98; | (list TYPE1 ...)
99; RESULTS = *
100; | (TYPE1 ...)
101; TVAR = (VAR TYPE) | VAR
102;
103; global symbol properties:
104;
105; ##compiler#type -> TYPESPEC
106; ##compiler#type-source -> 'db | 'local | 'inference
107; ##compiler#predicate -> TYPESPEC
108; ##compiler#specializations -> (SPECIALIZATION ...)
109; ##compiler#local-specializations -> (SPECIALIZATION ...)
110; ##compiler#enforce -> BOOL
111; ##compiler#special-result-type -> PROCEDURE
112; ##compiler#escape -> #f | 'yes | 'no
113; ##compiler#type-abbreviation -> TYPESPEC
114;; ##compiler#tv-root -> STRING
115;
116; specialization specifiers:
117;
118; SPECIALIZATION = ((TYPE ... [#!rest TYPE]) [RESULTS] TEMPLATE)
119; TEMPLATE = #(INDEX)
120; | #(INDEX ...)
121; | #(SYMBOL)
122; | INTEGER | SYMBOL | STRING
123; | (quote CONSTANT)
124; | (TEMPLATE . TEMPLATE)
125;
126; As an alternative to the "#!rest" and "#!optional" keywords, "&rest" or "&optional"
127; may be used.
128
129
130(define-constant +fragment-max-length+ 6)
131(define-constant +fragment-max-depth+ 4)
132(define-constant +maximal-union-type-length+ 20)
133(define-constant +maximal-complex-object-constructor-result-type-length+ 256)
134
135(define-constant value-types
136 '(string symbol keyword char null boolean true false blob eof bwp
137 fixnum float number integer bignum ratnum cplxnum
138 pointer-vector port pointer locative))
139
140(define-constant basic-types
141 '(* list pair procedure vector undefined deprecated noreturn values))
142
143(define-constant struct-types
144 '(u8vector s8vector u16vector s16vector u32vector s32vector u64vector
145 s64vector f32vector f64vector thread queue environment time
146 continuation lock mmap condition hash-table tcp-listener))
147
148(define-constant type-expansions
149 '((pair . (pair * *))
150 (list . (list-of *))
151 (vector . (vector-of *))
152 (boolean . (or true false))
153 (integer . (or fixnum bignum))
154 (number . (or fixnum float bignum ratnum cplxnum))
155 (procedure . (procedure (#!rest *) . *))))
156
157(define-inline (struct-type? t)
158 (and (pair? t) (eq? (car t) 'struct)))
159
160(define-inline (value-type? t)
161 (or (struct-type? t) (memq t value-types)))
162
163(define specialization-statistics '())
164(define trail '())
165
166(define (walked-result n)
167 (first (node-parameters n))) ; assumes ##core#the/result node
168
169(define (type-always-immediate? t)
170 (cond ((pair? t)
171 (case (car t)
172 ((or) (every type-always-immediate? (cdr t)))
173 ((forall) (type-always-immediate? (third t)))
174 (else #f)))
175 ((memq t '(eof bwp null fixnum char boolean undefined)) #t)
176 (else #f)))
177
178(define (scrutinize node db complain specialize strict block-compilation)
179 (d "################################## SCRUTINIZE ##################################")
180 (define (report loc msg . args)
181 (when *complain?*
182 (warning
183 (conc (location-name loc)
184 (sprintf "~?" msg args)))))
185
186 (set! *complain?* complain)
187
188 (let ((blist '()) ; (((VAR . FLOW) TYPE) ...)
189 (aliased '())
190 (noreturn #f)
191 (dropped-branches 0)
192 (assigned-immediates 0)
193 (errors #f)
194 (safe-calls 0))
195
196 (define (constant-result lit)
197 (cond ((string? lit) 'string)
198 ((keyword? lit) 'keyword)
199 ((symbol? lit) 'symbol)
200 ;; Do not assume fixnum width matches target platforms!
201 ((or (big-fixnum? lit) (small-bignum? lit)) 'integer)
202 ((fixnum? lit) 'fixnum)
203 ((bignum? lit) 'bignum)
204 ((flonum? lit) 'float) ; Why not "flonum", for consistency?
205 ((ratnum? lit) 'ratnum)
206 ((cplxnum? lit) 'cplxnum)
207 ((boolean? lit)
208 (if lit 'true 'false))
209 ((null? lit) 'null)
210 ((list? lit)
211 `(list ,@(map constant-result lit)))
212 ((pair? lit)
213 (simplify-type
214 `(pair ,(constant-result (car lit)) ,(constant-result (cdr lit)))))
215 ((eof-object? lit) 'eof)
216 ;; TODO: Remove once we have a bootstrapping libchicken with bwp-object?
217 ((##core#inline "C_bwpp" lit) #;(bwp-object? lit) 'bwp)
218 ((vector? lit)
219 (simplify-type
220 `(vector ,@(map constant-result (vector->list lit)))))
221 ((and (not (##sys#immediate? lit)) (##sys#generic-structure? lit))
222 `(struct ,(##sys#slot lit 0)))
223 ((char? lit) 'char)
224 (else '*)))
225
226 (define (global-result id loc node)
227 (cond ((variable-mark id '##compiler#type) =>
228 (lambda (a)
229 (cond
230 ((eq? a 'deprecated)
231 (r-deprecated-identifier loc node id)
232 '(*))
233 ((and (pair? a) (eq? (car a) 'deprecated))
234 (r-deprecated-identifier loc node id (cadr a))
235 '(*))
236 (else (list a)))))
237 (else '(*))))
238
239 (define (blist-type id flow)
240 (cond ((find (lambda (b)
241 (and (eq? id (caar b))
242 (memq (cdar b) flow)) )
243 blist)
244 => cdr)
245 (else #f)))
246
247 (define (variable-result id e loc node flow)
248 (cond ((blist-type id flow) => list)
249 ((and (not strict)
250 (db-get db id 'assigned)
251 (not (variable-mark id '##compiler#type-source)))
252 '(*))
253 ((assq id e) =>
254 (lambda (a)
255 (cond ((eq? 'undefined (cdr a))
256 #;(report
257 loc
258 "access to variable `~a' which has an undefined value"
259 (real-name id db))
260 '(*))
261 (else (list (cdr a))))))
262 (else (global-result id loc node))))
263
264 (define (always-true1 t)
265 (cond ((pair? t)
266 (case (car t)
267 ((or) (every always-true1 (cdr t)))
268 ((not) (not (always-true1 (second t))))
269 ((forall) (always-true1 (third t)))
270 (else #t)))
271 ((memq t '(* boolean false undefined noreturn)) #f)
272 (else #t)))
273
274 (define (always-true if-node test-node t loc)
275 (and-let* ((_ (always-true1 t)))
276 (r-cond-test-always-true loc if-node test-node t)
277 #t))
278
279 (define (always-false if-node test-node t loc)
280 (and-let* ((_ (eq? t 'false)))
281 (r-cond-test-always-false loc if-node test-node)
282 #t))
283
284 (define (single tv r-value-count-mismatch)
285 (if (eq? '* tv)
286 '*
287 (let ((n (length tv)))
288 (cond ((= 1 n) (car tv))
289 ((zero? n)
290 (r-value-count-mismatch tv)
291 'undefined)
292 (else
293 (r-value-count-mismatch tv)
294 (first tv))))))
295
296 (define add-loc cons)
297
298 (define (get-specializations name)
299 (let* ((a (variable-mark name '##compiler#local-specializations))
300 (b (variable-mark name '##compiler#specializations))
301 (c (append (or a '()) (or b '()))))
302 (and (pair? c) c)))
303
304 (define (call-result node args loc typeenv)
305 (let* ((actualtypes (map walked-result args))
306 (ptype (car actualtypes))
307 (pptype? (procedure-type? ptype))
308 (nargs (length (cdr args)))
309 (xptype `(procedure ,(make-list nargs '*) *))
310 (typeenv (append-map type-typeenv actualtypes))
311 (op #f))
312 (d " call: ~a, te: ~a" actualtypes typeenv)
313 (cond ((and (not pptype?) (not (match-types xptype ptype typeenv)))
314 (r-invalid-called-procedure-type
315 loc node (resolve xptype typeenv) (car args) (resolve ptype typeenv))
316 (values '* #f))
317 (else
318 (let-values (((atypes values-rest ok alen)
319 (procedure-argument-types ptype nargs typeenv)))
320 (unless ok
321 (r-proc-call-argument-count-mismatch loc node alen nargs ptype))
322 (do ((actualtypes (cdr actualtypes) (cdr actualtypes))
323 (anodes (cdr args) (cdr anodes))
324 (atypes atypes (cdr atypes))
325 (i 1 (add1 i)))
326 ((or (null? actualtypes) (null? atypes)))
327 (unless (match-types
328 (car atypes)
329 (car actualtypes)
330 typeenv)
331 (r-proc-call-argument-type-mismatch
332 loc node i
333 (car anodes)
334 (resolve (car atypes) typeenv)
335 (resolve (car actualtypes) typeenv)
336 ptype)))
337 (when (noreturn-procedure-type? ptype)
338 (set! noreturn #t))
339 (let ((r (procedure-result-types ptype values-rest (cdr actualtypes) typeenv)))
340 (let* ((pn (procedure-name ptype))
341 (trail0 trail))
342 (when pn
343 (cond ((and (fx= 1 nargs)
344 (variable-mark pn '##compiler#predicate)) =>
345 (lambda (pt)
346 (cond ((match-argument-types (list pt) (cdr actualtypes) typeenv)
347 (r-pred-call-always-true
348 loc node pt (cadr actualtypes))
349 (when specialize
350 (specialize-node!
351 node (cdr args)
352 `(let ((#(tmp) #(1))) '#t))
353 (set! r '(true))
354 (set! op (list pn pt))))
355 ((begin
356 (trail-restore trail0 typeenv)
357 (match-argument-types (list `(not ,pt)) (cdr actualtypes) typeenv))
358 (r-pred-call-always-false
359 loc node pt (cadr actualtypes))
360 (when specialize
361 (specialize-node!
362 node (cdr args)
363 `(let ((#(tmp) #(1))) '#f))
364 (set! r '(false))
365 (set! op (list pt `(not ,pt)))))
366 (else (trail-restore trail0 typeenv)))))
367 ((maybe-constant-fold-call node (node-subexpressions node)
368 (lambda (ok res _constant?)
369 (and ok (cons res ok))))
370 => (lambda (res.ok)
371 ;; Actual type doesn't matter; the node gets walked again
372 (set! r '*)
373 (mutate-node! node (list 'quote (car res.ok)))))
374 ((and specialize (get-specializations pn)) =>
375 (lambda (specs)
376 (let loop ((specs specs))
377 (and (pair? specs)
378 (let* ((spec (car specs))
379 (stype (first spec))
380 (tenv2 (append
381 (append-map type-typeenv stype)
382 typeenv)))
383 (cond ((match-argument-types stype (cdr actualtypes) tenv2)
384 (set! op (cons pn (car spec)))
385 (set! typeenv tenv2)
386 (let* ((r2 (and (pair? (cddr spec))
387 (second spec)))
388 (rewrite (if r2
389 (third spec)
390 (second spec))))
391 (specialize-node! node (cdr args) rewrite)
392 (when r2 (set! r r2))))
393 (else
394 (trail-restore trail0 tenv2)
395 (loop (cdr specs))))))))))
396 (when op
397 (d " specialized: `~s' for ~a" (car op) (cdr op))
398 (cond ((assoc op specialization-statistics) =>
399 (lambda (a) (set-cdr! a (add1 (cdr a)))))
400 (else
401 (set! specialization-statistics
402 (cons (cons op 1)
403 specialization-statistics))))))
404 (when (and specialize (not op) (procedure-type? ptype)
405 (eq? '##core#call (node-class node)))
406 (set-car! (node-parameters node) #t)
407 (set! safe-calls (add1 safe-calls))))
408 (let ((r (if (eq? '* r) r (map (cut resolve <> typeenv) r))))
409 (d " result-types: ~a" r)
410 (values r op))))))))
411
412 (define tag
413 (let ((n 0))
414 (lambda ()
415 (set! n (add1 n))
416 n)))
417
418 (define (add-to-blist var flow type)
419 (let loop ((var var))
420 (set! blist (alist-update! (cons var flow) type blist equal?))
421 (let ((a (assq var aliased)))
422 (when a
423 (d " applying to alias: ~a -> ~a" (cdr a) type)
424 (loop (cdr a))))))
425
426 (define (initial-argument-types dest vars argc)
427 (if (and dest strict
428 (variable-mark dest '##compiler#type-source))
429 (let* ((ptype (variable-mark dest '##compiler#type))
430 (typeenv (type-typeenv ptype)))
431 (if (procedure-type? ptype)
432 (map (cut resolve <> typeenv)
433 (nth-value 0 (procedure-argument-types ptype argc '() #t)))
434 (make-list argc '*)))
435 (make-list argc '*)))
436
437 (define (walk n e loc dest flow ctags) ; returns result specifier
438 (let ((subs (node-subexpressions n))
439 (params (node-parameters n))
440 (class (node-class n)) )
441 (dd "walk: ~a ~s (loc: ~a, dest: ~a, flow: ~a)"
442 class params loc dest flow)
443 #;(dd "walk: ~a ~s (loc: ~a, dest: ~a, flow: ~a, blist: ~a, e: ~a)"
444 class params loc dest flow blist e)
445 (set! d-depth (add1 d-depth))
446 (let ((results
447 (case class
448 ((##core#the/result) (list (first params))) ; already walked
449 ((quote) (list (constant-result (first params))))
450 ((##core#undefined) '(*))
451 ((##core#proc) '(procedure))
452 ((##core#variable) (variable-result (first params) e loc n flow))
453 ((##core#inline_ref)
454 (list (foreign-type->scrutiny-type (second params) 'result)))
455 ((##core#inline_loc_ref)
456 (list (foreign-type->scrutiny-type (first params) 'result)))
457 ((if)
458 (let ((tags (cons (tag) (tag)))
459 (tst (first subs))
460 (nor-1 noreturn))
461 (set! noreturn #f)
462 (let* ((rt (single (walk tst e loc #f flow tags)
463 (cut r-conditional-value-count-invalid loc n tst <>)))
464 (c (second subs))
465 (a (third subs))
466 (nor0 noreturn))
467 (cond
468 ((and (always-true n tst rt loc) specialize)
469 (set! dropped-branches (add1 dropped-branches))
470 (mutate-node! n `(let ((,(gensym) ,tst)) ,c))
471 (walk n e loc dest flow ctags))
472 ((and (always-false n tst rt loc) specialize)
473 (set! dropped-branches (add1 dropped-branches))
474 (mutate-node! n `(let ((,(gensym) ,tst)) ,a))
475 (walk n e loc dest flow ctags))
476 (else
477 (let* ((r1 (walk c e loc dest (cons (car tags) flow) #f))
478 (nor1 noreturn))
479 (set! noreturn #f)
480 (let* ((r2 (walk a e loc dest (cons (cdr tags) flow) #f))
481 (nor2 noreturn))
482 (set! noreturn (or nor-1 nor0 (and nor1 nor2)))
483 ;; when only one branch is noreturn, add blist entries for
484 ;; all in other branch:
485 (when (or (and nor1 (not nor2))
486 (and nor2 (not nor1)))
487 (let ((yestag (if nor1 (cdr tags) (car tags))))
488 (for-each
489 (lambda (ble)
490 (when (eq? (cdar ble) yestag)
491 (d "adding blist entry ~a for single returning conditional branch"
492 ble)
493 (add-to-blist (caar ble) (car flow) (cdr ble))))
494 blist)))
495 (cond ((and (not (eq? '* r1)) (not (eq? '* r2)))
496 ;;(dd " branches: ~s:~s / ~s:~s" nor1 r1 nor2 r2)
497 (cond ((and (not nor1) (not nor2)
498 (not (= (length r1) (length r2))))
499 (r-cond-branch-value-count-mismatch loc n c a r1 r2)
500 '*)
501 (nor1 r2)
502 (nor2 r1)
503 (else
504 (dd "merge branch results: ~s + ~s" r1 r2)
505 (map (lambda (t1 t2)
506 (simplify-type `(or ,t1 ,t2)))
507 r1 r2))))
508 (else '*)))))))))
509 ((let)
510 ;; before CPS-conversion, `let'-nodes may have multiple bindings
511 (let loop ((vars params) (body subs) (e2 '()))
512 (if (null? vars)
513 (walk (car body) (append e2 e) loc dest flow ctags)
514 (let* ((var (car vars))
515 (val (car body))
516 (t (single (walk val e loc var flow #f)
517 (cut r-let-value-count-invalid loc var n val <>))))
518 (when (and (eq? (node-class val) '##core#variable)
519 (not (db-get db var 'assigned)))
520 (let ((var2 (first (node-parameters val))))
521 (unless (db-get db var2 'assigned) ;XXX too conservative?
522 (set! aliased (alist-cons var var2 aliased)))))
523 (loop (cdr vars) (cdr body) (alist-cons (car vars) t e2))))))
524 ((##core#lambda lambda)
525 (##sys#decompose-lambda-list
526 (first params)
527 (lambda (vars argc rest)
528 (let* ((namelst (if dest (list dest) '()))
529 (inits (initial-argument-types dest vars argc))
530 (args (append inits (if rest '(#!rest) '())))
531 (e2 (append (map (lambda (v i) (cons v i))
532 (if rest (butlast vars) vars)
533 inits)
534 e)))
535 (when dest
536 (d "~a: initial-argument types: ~a" dest inits))
537 (fluid-let ((blist '())
538 (noreturn #f)
539 (aliased '()))
540 (let* ((initial-tag (tag))
541 (r (walk (first subs)
542 (if rest (alist-cons rest 'list e2) e2)
543 (add-loc dest loc)
544 #f (list initial-tag) #f)))
545 #;(when (and specialize
546 dest
547 (variable-mark dest '##compiler#type-source)
548 (not unsafe))
549 (debugging 'x "checks argument-types" dest) ;XXX
550 ;; [1] this is subtle: we don't want argtype-checks to be
551 ;; generated for toplevel defs other than user-declared ones.
552 ;; But since the ##compiler#type-source mark is set AFTER
553 ;; the lambda has been walked (see below, [2]), nothing is added.
554 (generate-type-checks! n dest vars inits))
555 (list
556 (append
557 '(procedure)
558 namelst
559 (list
560 (let loop ((argc argc) (vars vars) (args args))
561 (cond ((zero? argc) args)
562 ((and (not (db-get db (car vars) 'assigned))
563 (assoc (cons (car vars) initial-tag) blist))
564 =>
565 (lambda (a)
566 (cons
567 (cond ((eq? (cdr a) '*) '*)
568 (else
569 (d "adjusting procedure argument type for `~a' to: ~a"
570 (car vars) (cdr a))
571 (cdr a) ))
572 (loop (sub1 argc) (cdr vars) (cdr args)))))
573 (else
574 (cons
575 (car args)
576 (loop (sub1 argc) (cdr vars) (cdr args)))))))
577 r))))))))
578 ((set! ##core#set!)
579 (let* ((var (first params))
580 (type (variable-mark var '##compiler#type))
581 (rt (single (walk (first subs) e loc var flow #f)
582 (cut r-assignment-value-count-invalid
583 loc var n (first subs) <>)))
584 (typeenv (append
585 (if type (type-typeenv type) '())
586 (type-typeenv rt)))
587 (b (assq var e)) )
588 (when (and type (not b)
589 (not (or (eq? type 'deprecated)
590 (and (pair? type)
591 (eq? (car type) 'deprecated))))
592 (not (match-types type rt typeenv)))
593 (when strict (set! errors #t))
594 (r-toplevel-var-assignment-type-mismatch loc n rt var type (first subs)))
595 (when (and (not type) ;XXX global declaration could allow this
596 (not b)
597 (not (eq? '* rt))
598 (not (db-get db var 'unknown)))
599 (and-let* ((val (or (db-get db var 'value)
600 (db-get db var 'local-value))))
601 (when (and (eq? val (first subs))
602 (or (not (variable-visible? var block-compilation))
603 (not (eq? (variable-mark var '##compiler#inline)
604 'no))))
605 (let ((rtlst (list (cons #f (tree-copy rt)))))
606 (smash-component-types! rtlst "global")
607 (let ((rt (cdar rtlst)))
608 (debugging '|I| (sprintf "(: ~s ~s)" var rt))
609 ;; [2] sets property, but lambda has already been walked,
610 ;; so no type-checks are generated (see also [1], above)
611 ;; note that implicit declarations are not enforcing
612 (mark-variable var '##compiler#type-source 'inference)
613 (mark-variable var '##compiler#type rt))))))
614 (when b
615 (cond ((eq? 'undefined (cdr b)) (set-cdr! b rt))
616 #;(strict
617 (let ((ot (or (blist-type var flow) (cdr b))))
618 ;;XXX compiler-syntax for "map" will introduce
619 ;; assignments that trigger this warning, so this
620 ;; is currently disabled
621 (unless (compatible-types? ot rt)
622 (report
623 loc
624 "variable `~a' of type `~a' was modified to a value of type `~a'"
625 var ot rt)))))
626 (let ((t (if (or strict (not (db-get db var 'captured)))
627 rt
628 '*))
629 (fl (car flow)))
630 ;; For each outer flow F, change the var's
631 ;; type to (or t <old-type@F>). Add a new
632 ;; entry for current flow if it's missing.
633 ;;
634 ;; Motivating example:
635 ;;
636 ;; (let* ((x 1)
637 ;; (y x)) ; y x : fixnum @ flow f_1
638 ;; (if foo
639 ;; (set! y 'a)) ; y : symbol @ flow f_2
640 ;; y) ; (1) @ flow f_1
641 ;;
642 ;; At point (1) the type of y can be inferred
643 ;; to be (or fixnum symbol). The type of x
644 ;; should stay unchanged, however.
645 (let loop ((bl blist) (fl-found? #f))
646 (cond ((null? bl)
647 (unless fl-found?
648 (dd "set! ~a in ~a (new) --> ~a" var fl t)
649 (set! blist (alist-cons (cons var fl) t blist))))
650 ((eq? var (ble-id (car bl)))
651 (let* ((ble (car bl))
652 (old-type (ble-type ble))
653 (t2 (simplify-type `(or ,t ,old-type))))
654 (dd "set! ~a in ~a, or old ~a with ~a --> ~a"
655 var tag old-type t t2)
656 (ble-type-set! ble t2)
657 (loop (cdr bl) (or fl-found? (eq? fl (ble-tag ble))))))
658 (else (loop (cdr bl) fl-found?))))))
659
660 (when (type-always-immediate? rt)
661 (d " assignment to var ~a in ~a is always immediate" var loc)
662 (set! assigned-immediates (add1 assigned-immediates))
663 (set-cdr! params '(#t)))
664
665 '(undefined)))
666 ((##core#primitive) '*)
667 ((##core#call)
668 (let* ((f (fragment n))
669 (len (length subs))
670 (args (map (lambda (n2 i)
671 (make-node
672 '##core#the/result
673 (list
674 (single
675 (walk n2 e loc #f flow #f)
676 (cut r-proc-call-argument-value-count loc n i n2 <>)))
677 (list n2)))
678 subs
679 (iota len)))
680 (fn (walked-result (car args)))
681 (pn (procedure-name fn))
682 (typeenv (type-typeenv
683 `(or ,@(map walked-result args)))) ; hack
684 (enforces
685 (and pn (variable-mark pn '##compiler#enforce)))
686 (pt (and pn (variable-mark pn '##compiler#predicate))))
687 (let-values (((r specialized?)
688 (call-result n args loc typeenv)))
689 (define (smash)
690 (when (and (not strict)
691 (or (not pn)
692 (and
693 (not (variable-mark pn '##compiler#pure))
694 (not (variable-mark pn '##compiler#clean)))))
695 (smash-component-types! e "env")
696 (smash-component-types! blist "blist")))
697 (cond (specialized?
698 (walk n e loc dest flow ctags)
699 (smash)
700 ;; keep type, as the specialization may contain icky stuff
701 ;; like "##core#inline", etc.
702 (if (eq? '* r)
703 r
704 (map (cut resolve <> typeenv) r)))
705 ((eq? 'quote (node-class n)) ; Call got constant folded
706 (walk n e loc dest flow ctags))
707 (else
708 (for-each
709 (lambda (arg argr)
710 (when (eq? '##core#variable (node-class arg))
711 (let* ((var (first (node-parameters arg)))
712 (a (or (blist-type var flow) (alist-ref var e)))
713 (argr (resolve argr typeenv))
714 (oparg? (eq? arg (first subs)))
715 (pred (and pt
716 ctags
717 (not (db-get db var 'assigned))
718 (not oparg?))))
719 (cond (pred
720 ;;XXX is this needed? "typeenv" is the te of "args",
721 ;; not of "pt":
722 (let ((pt (resolve pt typeenv)))
723 (d " predicate `~a' indicates `~a' is ~a in flow ~a"
724 pn var pt (car ctags))
725 (add-to-blist
726 var (car ctags)
727 (if (not a) pt (refine-types a pt)))
728 ;; if the variable type is an "or"-type, we can
729 ;; can remove all elements that match the predicate
730 ;; type
731 (when a
732 ;;XXX hack, again:
733 (let ((at (refine-types a `(not ,pt))))
734 (when at
735 (d " predicate `~a' indicates `~a' is ~a in flow ~a"
736 pn var at (cdr ctags))
737 (add-to-blist var (cdr ctags) at))))))
738 (a
739 (when enforces
740 (let ((ar (if (db-get db var 'assigned)
741 '* ; XXX necessary?
742 (refine-types a argr))))
743 (d " assuming: ~a -> ~a (flow: ~a)"
744 var ar (car flow))
745 (add-to-blist var (car flow) ar)
746 (when ctags
747 (add-to-blist var (car ctags) ar)
748 (add-to-blist var (cdr ctags) ar)))))
749 ((and oparg?
750 (variable-mark
751 var
752 '##compiler#special-result-type))
753 => (lambda (srt)
754 (dd " hardcoded special result-type: ~a" var)
755 (set! r (srt n args loc r))))))))
756 subs
757 (cons
758 fn
759 (nth-value
760 0
761 (procedure-argument-types fn (sub1 len) typeenv))))
762 (smash)
763 (if (eq? '* r)
764 r
765 (map (cut resolve <> typeenv) r)))))))
766 ((##core#the)
767 (let ((t (first params))
768 (rt (walk (first subs) e loc dest flow ctags)))
769 (cond ((eq? rt '*))
770 ((null? rt) (r-zero-values-for-the loc (first subs) t))
771 (else
772 (when (> (length rt) 1)
773 (r-too-many-values-for-the loc (first subs) t rt))
774 (when (and (second params)
775 (not (compatible-types? t (first rt))))
776 (when strict (set! errors #t))
777 (r-type-mismatch-in-the loc (first subs) (first rt) t))))
778 (list t)))
779 ((##core#typecase)
780 (let* ((ts (walk (first subs) e loc #f flow ctags))
781 (trail0 trail)
782 (typeenv0 (type-typeenv (car ts))))
783 ;; first exp is always a variable so ts must be of length 1
784 (let loop ((types (cdr params)) (subs (cdr subs)))
785 (if (null? types)
786 (fail-compiler-typecase loc n (car ts) (cdr params))
787 (let ((typeenv (append (type-typeenv (car types)) typeenv0)))
788 (if (match-types (car types) (car ts) typeenv #t)
789 (begin ; drops exp
790 (mutate-node! n (car subs))
791 (walk n e loc dest flow ctags))
792 (begin
793 (trail-restore trail0 typeenv)
794 (loop (cdr types) (cdr subs)))))))))
795 ((##core#switch ##core#cond)
796 (bomb "scrutinize: unexpected node class" class))
797 (else
798 (for-each (lambda (n) (walk n e loc #f flow #f)) subs)
799 '*))))
800 (set! d-depth (sub1 d-depth))
801 (dd "walked ~a -> ~a flow: ~a" class results flow)
802 results)))
803
804 (let ((rn (walk (first (node-subexpressions node)) '() '() #f (list (tag)) #f)))
805 (when (pair? specialization-statistics)
806 (with-debugging-output
807 '(o e)
808 (lambda ()
809 (print "specializations:")
810 (for-each
811 (lambda (ss)
812 (printf " ~a ~s~%" (cdr ss) (car ss)))
813 specialization-statistics))))
814 (when (positive? safe-calls)
815 (debugging '(o e) "safe calls" safe-calls))
816 (when (positive? dropped-branches)
817 (debugging '(o e) "dropped branches" dropped-branches))
818 (when (positive? assigned-immediates)
819 (debugging '(o e) "assignments to immediate values" assigned-immediates))
820 (d "############################### SCRUTINIZE FINISH ##############################")
821 (when errors
822 (quit-compiling "some variable types do not satisfy strictness"))
823 rn)))
824
825
826;;; replace pair/vector types with components to variants with undetermined
827;; component types (used for env or blist); also convert "list[-of]" types
828;; into "pair", since mutation may take place
829
830(define (smash-component-types! lst where)
831 ;; assumes list of the form "((_ . T1) ...)"
832 (do ((lst lst (cdr lst)))
833 ((null? lst))
834 (let loop ((t (cdar lst))
835 (change! (cute set-cdr! (car lst) <>)))
836 (when (pair? t)
837 (case (car t)
838 ((vector-of)
839 (dd " smashing `~s' in ~a" (caar lst) where)
840 (change! 'vector)
841 (car t))
842 ((vector)
843 (dd " smashing `~s' in ~a" (caar lst) where)
844 ;; (vector x y z) => (vector * * *)
845 (change! (cons 'vector (map (constantly '*) (cdr t))))
846 (car t))
847 ((list-of list)
848 (dd " smashing `~s' in ~a" (caar lst) where)
849 (change! '(or pair null))
850 (car t))
851 ((pair)
852 (dd " smashing `~s' in ~a" (caar lst) where)
853 (change! (car t))
854 (car t))
855 ((forall)
856 (loop (third t)
857 (cute set-car! (cddr t) <>))))))))
858
859
860;;; blist (binding list) helpers
861;;
862;; - Entries (ble) in blist have type ((symbol . fixnum) . type)
863
864(define ble-id caar) ; variable name : symbol
865(define ble-tag cdar) ; block tag : fixnum
866(define ble-type cdr) ; variable type : valid type sexp
867(define ble-type-set! set-cdr!)
868
869
870;;; Type-matching
871;
872; - "all" means: all elements in `or'-types in second argument must match
873
874(define (match-types t1 t2 #!optional (typeenv (type-typeenv `(or ,t1 ,t2))) all)
875
876 (define (match-args args1 args2)
877 (d "match args: ~s <-> ~s" args1 args2)
878 (let loop ((args1 args1) (args2 args2) (opt1 #f) (opt2 #f))
879 (cond ((null? args1)
880 (or opt2
881 (null? args2)
882 (optargs? (car args2))))
883 ((null? args2)
884 (or opt1
885 (optargs? (car args1))))
886 ((eq? '#!optional (car args1))
887 (loop (cdr args1) args2 #t opt2))
888 ((eq? '#!optional (car args2))
889 (loop args1 (cdr args2) opt1 #t))
890 ((eq? '#!rest (car args1))
891 (match-rest (rest-type (cdr args1)) args2 opt2))
892 ((eq? '#!rest (car args2))
893 (match-rest (rest-type (cdr args2)) args1 opt1))
894 ((match1 (car args1) (car args2))
895 (loop (cdr args1) (cdr args2) opt1 opt2))
896 (else #f))))
897
898 (define (match-rest rtype args opt) ;XXX currently ignores `opt'
899 (let-values (((head tail) (span (lambda (x) (not (eq? '#!rest x))) args)))
900 (and (every
901 (lambda (t)
902 (or (eq? '#!optional t)
903 (match1 rtype t)))
904 head)
905 (match1 rtype (if (pair? tail) (rest-type (cdr tail)) '*)))))
906
907 (define (optargs? a)
908 (memq a '(#!rest #!optional)))
909
910 (define (match-results results1 results2)
911 (cond ((eq? '* results1))
912 ((eq? '* results2) (not all))
913 ((null? results1) (null? results2))
914 ((null? results2) #f)
915 ((and (memq (car results1) '(undefined noreturn))
916 (memq (car results2) '(undefined noreturn))))
917 ((match1 (car results1) (car results2))
918 (match-results (cdr results1) (cdr results2)))
919 (else #f)))
920
921 (define (rawmatch1 t1 t2)
922 (fluid-let ((all #f))
923 (match1 t1 t2)))
924
925 (define (every-match1 lst1 lst2)
926 (let loop ((lst1 lst1) (lst2 lst2))
927 (cond ((null? lst1))
928 ((match1 (car lst1) (car lst2)) (loop (cdr lst1) (cdr lst2)))
929 (else #f))))
930
931 (define (match1 t1 t2)
932 ;; note: the order of determining the type is important
933 (dd " match1: ~s <-> ~s" t1 t2)
934 (cond ((eq? t1 t2))
935 ;;XXX do we have to handle circularities?
936 ((and (symbol? t1) (assq t1 typeenv)) =>
937 (lambda (e)
938 (cond ((second e)
939 (and (match1 (second e) t2)
940 (or (not (third e)) ; constraint
941 (rawmatch1 (third e) t2))))
942 ;; special case for two unbound typevars
943 ((and (symbol? t2) (assq t2 typeenv)) =>
944 (lambda (e2)
945 ;;XXX probably not fully right, consider:
946 ;; (forall (a b) ((a a b) ->)) + (forall (c d) ((c d d) ->))
947 ;; or is this not a problem? I don't know right now...
948 (or (not (second e2))
949 (and (match1 t1 (second e2))
950 (or (not (third e2)) ; constraint
951 (rawmatch1 t1 (third e2)))))))
952 ((or (not (third e))
953 (rawmatch1 (third e) t2))
954 (dd " unify ~a = ~a" t1 t2)
955 (set! trail (cons t1 trail))
956 (set-car! (cdr e) t2)
957 #t)
958 (else #f))))
959 ((and (symbol? t2) (assq t2 typeenv)) =>
960 (lambda (e)
961 (cond ((second e)
962 (and (match1 t1 (second e))
963 (or (not (third e)) ; constraint
964 (rawmatch1 t1 (third e)))))
965 ((or (not (third e))
966 (rawmatch1 t1 (third e)))
967 (dd " unify ~a = ~a" t2 t1)
968 (set! trail (cons t2 trail))
969 (set-car! (cdr e) t1)
970 #t)
971 (else #f))))
972 ((eq? t1 '*))
973 ((eq? t2 '*) (not all))
974 ((eq? t1 'undefined) #f)
975 ((eq? t2 'undefined) #f)
976 ((eq? t1 'noreturn))
977 ((eq? t2 'noreturn))
978 ((maybe-expand-type t1) => (cut match1 <> t2))
979 ((maybe-expand-type t2) => (cut match1 t1 <>))
980 ((and (pair? t1) (eq? 'not (car t1)))
981 (fluid-let ((all (not all)))
982 (let* ((trail0 trail)
983 (m (match1 (cadr t1) t2)))
984 (trail-restore trail0 typeenv)
985 (not m))))
986 ((and (pair? t2) (eq? 'not (car t2)))
987 (and (not all)
988 (fluid-let ((all #t))
989 (let* ((trail0 trail)
990 (m (match1 (cadr t2) t1)))
991 (trail-restore trail0 typeenv)
992 (not m)))))
993 ;; this is subtle: "or" types for t2 are less restrictive,
994 ;; so we handle them before "or" types for t1
995 ((and (pair? t2) (eq? 'or (car t2)))
996 (over-all-instantiations
997 (cdr t2)
998 typeenv
999 all
1000 (lambda (t) (match1 t1 t))))
1001 ;; s.a.
1002 ((and (pair? t1) (eq? 'or (car t1)))
1003 (over-all-instantiations
1004 (cdr t1)
1005 typeenv
1006 #f
1007 (lambda (t) (match1 t t2)))) ; o-a-i ensures at least one element matches
1008 ((and (pair? t1) (eq? 'forall (car t1)))
1009 (match1 (third t1) t2)) ; assumes typeenv has already been extracted
1010 ((and (pair? t2) (eq? 'forall (car t2)))
1011 (match1 t1 (third t2))) ; assumes typeenv has already been extracted
1012 ((eq? 'procedure t1)
1013 (and (pair? t2) (eq? 'procedure (car t2))))
1014 ((eq? 'procedure t2)
1015 (and (not all)
1016 (pair? t1) (eq? 'procedure (car t1))))
1017 ((eq? t1 'null)
1018 (and (not all)
1019 (pair? t2) (eq? 'list-of (car t2))))
1020 ((eq? t2 'null)
1021 (and (pair? t1) (eq? 'list-of (car t1))))
1022 ((and (pair? t1) (pair? t2) (eq? (car t1) (car t2)))
1023 (case (car t1)
1024 ((procedure)
1025 (let ((args1 (procedure-arguments t1))
1026 (args2 (procedure-arguments t2))
1027 (results1 (procedure-results t1))
1028 (results2 (procedure-results t2)))
1029 (and (match-args args1 args2)
1030 (match-results results1 results2))))
1031 ((struct) (equal? t1 t2))
1032 ((pair) (every-match1 (cdr t1) (cdr t2)))
1033 ((list-of vector-of) (match1 (second t1) (second t2)))
1034 ((list vector)
1035 (and (= (length t1) (length t2))
1036 (every-match1 (cdr t1) (cdr t2))))
1037 ((refine)
1038 (and (match1 (third t1) (third t2))
1039 (or (not all)
1040 (lset<=/eq? (second t1) (second t2)))))
1041 (else #f)))
1042 ((and (pair? t1) (eq? 'refine (car t1)))
1043 (and (not all) (match1 (third t1) t2)))
1044 ((and (pair? t2) (eq? 'refine (car t2)))
1045 (match1 t1 (third t2)))
1046 ((and (pair? t1) (eq? 'pair (car t1)))
1047 (and (pair? t2)
1048 (case (car t2)
1049 ((list-of)
1050 (and (not all)
1051 (match1 (second t1) (second t2))
1052 (match1 (third t1) t2)))
1053 ((list)
1054 (and (pair? (cdr t2))
1055 (match1 (second t1) (second t2))
1056 (match1 (third t1)
1057 (if (null? (cddr t2))
1058 'null
1059 `(list ,@(cddr t2))))))
1060 (else #f))))
1061 ((and (pair? t2) (eq? 'pair (car t2)))
1062 (and (pair? t1)
1063 (case (car t1)
1064 ((list-of)
1065 (and (not all)
1066 (match1 (second t1) (second t2))
1067 (match1 t1 (third t2))))
1068 ((list)
1069 (and (pair? (cdr t1))
1070 (match1 (second t1) (second t2))
1071 (match1 (if (null? (cddr t1))
1072 'null
1073 `(list ,@(cddr t1)))
1074 (third t2))))
1075 (else #f))))
1076 ((and (pair? t1) (eq? 'list (car t1)))
1077 (and (not all)
1078 (pair? t2) (eq? 'list-of (car t2))
1079 (over-all-instantiations
1080 (cdr t1)
1081 typeenv
1082 #t
1083 (cute match1 <> (second t2)))))
1084 ((and (pair? t1) (eq? 'list-of (car t1)))
1085 (and (pair? t2) (eq? 'list (car t2))
1086 (over-all-instantiations
1087 (cdr t2)
1088 typeenv
1089 #t
1090 (cute match1 (second t1) <>))))
1091 ((and (pair? t1) (eq? 'vector (car t1)))
1092 (and (not all)
1093 (pair? t2) (eq? 'vector-of (car t2))
1094 (over-all-instantiations
1095 (cdr t1)
1096 typeenv
1097 #t
1098 (cute match1 <> (second t2)))))
1099 ((and (pair? t1) (eq? 'vector-of (car t1)))
1100 (and (pair? t2) (eq? 'vector (car t2))
1101 (over-all-instantiations
1102 (cdr t2)
1103 typeenv
1104 #t
1105 (cute match1 (second t1) <>))))
1106 (else #f)))
1107
1108 (dd "match (~a) ~a <-> ~a" (if all "all" "any") t1 t2)
1109 (let ((m (match1 t1 t2)))
1110 (dd "match (~a) ~s <-> ~s -> ~s" (if all "all" "any") t1 t2 m)
1111 m))
1112
1113
1114(define (match-argument-types typelist atypes typeenv)
1115 ;; this doesn't need optional: it is only used for predicate- and specialization
1116 ;; matching
1117 (let loop ((tl typelist) (atypes atypes))
1118 (cond ((null? tl) (null? atypes))
1119 ((null? atypes) #f)
1120 ((equal? '(#!rest) tl))
1121 ((eq? (car tl) '#!rest)
1122 (every
1123 (lambda (at)
1124 (match-types (cadr tl) at typeenv #t))
1125 atypes))
1126 ((match-types (car tl) (car atypes) typeenv #t)
1127 (loop (cdr tl) (cdr atypes)))
1128 (else #f))))
1129
1130
1131;;; Simplify type specifier
1132;
1133; - coalesces "forall" and renames type-variables
1134; - also removes unused typevars
1135
1136(define (simplify-type t)
1137 (let ((typeenv '()) ; ((VAR1 . NEWVAR1) ...)
1138 (constraints '()) ; ((VAR1 TYPE1) ...)
1139 (used '()))
1140 (define (simplify t)
1141 ;;(dd "simplify/rec: ~s" t)
1142 (call/cc
1143 (lambda (return)
1144 (cond ((pair? t)
1145 (case (car t)
1146 ((forall)
1147 (let ((typevars (second t)))
1148 (set! typeenv
1149 (append (map (lambda (v)
1150 (let ((v (if (symbol? v) v (first v))))
1151 (cons v (make-tv v))))
1152 typevars)
1153 typeenv))
1154 (set! constraints
1155 (append (filter-map
1156 (lambda (v)
1157 (and (pair? v) v))
1158 typevars)
1159 constraints))
1160 (simplify (third t))))
1161 ((or)
1162 (let ((ts (delete-duplicates (map simplify (cdr t)) eq?)))
1163 (cond ((null? ts) '*)
1164 ((null? (cdr ts)) (car ts))
1165 ((> (length ts) +maximal-union-type-length+)
1166 (d "union-type cutoff! (~a): ~s" (length ts) ts)
1167 '*)
1168 ((every procedure-type? ts)
1169 (if (any (cut eq? 'procedure <>) ts)
1170 'procedure
1171 (foldl
1172 (lambda (pt t)
1173 (let* ((name1 (procedure-name t))
1174 (atypes1 (procedure-arguments t))
1175 (rtypes1 (procedure-results t))
1176 (name2 (procedure-name pt))
1177 (atypes2 (procedure-arguments pt))
1178 (rtypes2 (procedure-results pt)))
1179 (append
1180 '(procedure)
1181 (if (and name1 name2 (eq? name1 name2)) (list name1) '())
1182 (list (merge-argument-types atypes1 atypes2))
1183 (merge-result-types rtypes1 rtypes2))))
1184 (car ts)
1185 (cdr ts))))
1186 ((lset=/eq? '(true false) ts) 'boolean)
1187 ((lset=/eq? '(fixnum bignum) ts) 'integer)
1188 ((lset=/eq? '(fixnum float bignum ratnum cplxnum) ts) 'number)
1189 (else
1190 (let* ((ts (append-map
1191 (lambda (t)
1192 (let ((t (simplify t)))
1193 (cond ((and (pair? t) (eq? 'or (car t)))
1194 (cdr t))
1195 ((eq? t 'undefined) (return 'undefined))
1196 ((eq? t 'noreturn) (return '*))
1197 (else (list t)))))
1198 ts))
1199 (ts2 (let loop ((ts ts) (done '()))
1200 (cond ((null? ts) (reverse done))
1201 ((eq? '* (car ts)) (return '*))
1202 ((any (cut type<=? (car ts) <>) (cdr ts))
1203 (loop (cdr ts) done))
1204 ((any (cut type<=? (car ts) <>) done)
1205 (loop (cdr ts) done))
1206 (else (loop (cdr ts) (cons (car ts) done)))))))
1207 (if (equal? ts2 (cdr t))
1208 t
1209 (simplify `(or ,@ts2))))))))
1210 ((refine)
1211 (let ((rs (second t))
1212 (t2 (simplify (third t))))
1213 (cond ((null? rs) t2)
1214 ((refinement-type? t2)
1215 (list 'refine (lset-union/eq? (second t2) rs) (third t2)))
1216 (else
1217 (list 'refine (delete-duplicates rs eq?) t2)))))
1218 ((pair)
1219 (let ((tcar (simplify (second t)))
1220 (tcdr (simplify (third t))))
1221 (if (and (eq? '* tcar) (eq? '* tcdr))
1222 'pair
1223 (canonicalize-list-type
1224 `(pair ,tcar ,tcdr)))))
1225 ((vector-of)
1226 (let ((t2 (simplify (second t))))
1227 (if (eq? t2 '*)
1228 'vector
1229 `(,(car t) ,t2))))
1230 ((list-of)
1231 (let ((t2 (simplify (second t))))
1232 (if (eq? t2 '*)
1233 'list
1234 `(,(car t) ,t2))))
1235 ((list)
1236 (if (null? (cdr t))
1237 'null
1238 `(list ,@(map simplify (cdr t)))))
1239 ((vector)
1240 `(vector ,@(map simplify (cdr t))))
1241 ((procedure)
1242 (let* ((name (and (named? t) (cadr t)))
1243 (rtypes (if name (cdddr t) (cddr t))))
1244 (append
1245 '(procedure)
1246 (if name (list name) '())
1247 (list (map simplify (if name (third t) (second t))))
1248 (if (eq? '* rtypes)
1249 '*
1250 (map simplify rtypes)))))
1251 (else t)))
1252 ((assq t typeenv) =>
1253 (lambda (e)
1254 (set! used (lset-adjoin/eq? used t))
1255 (cdr e)))
1256 (else t)))))
1257 (let ((t2 (simplify t)))
1258 (when (pair? used)
1259 (set! t2
1260 `(forall ,(filter-map
1261 (lambda (e)
1262 (and (memq (car e) used)
1263 (let ((v (cdr e)))
1264 (cond ((assq (car e) constraints) =>
1265 (lambda (c)
1266 (list v (simplify (cadr c)))))
1267 (else v)))))
1268 typeenv)
1269 ,t2)))
1270 (dd "simplify: ~a -> ~a" t t2)
1271 t2)))
1272
1273(define (maybe-expand-type t)
1274 (and (symbol? t)
1275 (alist-ref t type-expansions eq?)))
1276
1277;;; Merging types
1278
1279(define (merge-argument-types ts1 ts2)
1280 ;; this could be more elegantly done by combining non-matching arguments/llists
1281 ;; into "(or (procedure ...) (procedure ...))" and then simplifying
1282 (cond ((null? ts1)
1283 (cond ((null? ts2) '())
1284 ((memq (car ts2) '(#!rest #!optional)) ts2)
1285 (else '(#!rest))))
1286 ((null? ts2) '(#!rest)) ;XXX giving up
1287 ((eq? '#!rest (car ts1))
1288 (cond ((and (pair? ts2) (eq? '#!rest (car ts2)))
1289 `(#!rest
1290 ,(simplify-type
1291 `(or ,(rest-type (cdr ts1))
1292 ,(rest-type (cdr ts2))))))
1293 (else '(#!rest)))) ;XXX giving up
1294 ((eq? '#!optional (car ts1))
1295 (cond ((and (pair? ts2) (eq? '#!optional (car ts2)))
1296 `(#!optional
1297 ,(simplify-type `(or ,(cadr ts1) ,(cadr ts2)))
1298 ,@(merge-argument-types (cddr ts1) (cddr ts2))))
1299 (else '(#!rest)))) ;XXX
1300 ((memq (car ts2) '(#!rest #!optional))
1301 (merge-argument-types ts2 ts1))
1302 (else (cons (simplify-type `(or ,(car ts1) ,(car ts2)))
1303 (merge-argument-types (cdr ts1) (cdr ts2))))))
1304
1305(define (merge-result-types ts11 ts21) ;XXX possibly overly conservative
1306 (call/cc
1307 (lambda (return)
1308 (let loop ((ts1 ts11) (ts2 ts21))
1309 (cond ((and (null? ts1) (null? ts2)) '())
1310 ((or (atom? ts1) (atom? ts2)) (return '*))
1311 (else (cons (simplify-type `(or ,(car ts1) ,(car ts2)))
1312 (loop (cdr ts1) (cdr ts2)))))))))
1313
1314
1315(define (compatible-types? t1 t2 #!optional (te (type-typeenv `(or ,t1 ,t2))))
1316 (or (type<=? t1 t2 te)
1317 (type<=? t2 t1 te)))
1318
1319(define (type-min t1 t2 #!optional (te (type-typeenv `(or ,t1 ,t2))))
1320 (cond ((type<=? t1 t2 te) t1)
1321 ((type<=? t2 t1 te) t2)
1322 (else #f)))
1323
1324(define (type<=? t1 t2 #!optional (te (type-typeenv `(or ,t1 ,t2))))
1325 (with-trail-restore
1326 te
1327 (lambda ()
1328 (match-types t2 t1 te #t))))
1329
1330;;
1331;; Combines the information in `t1' and `t2' to produce a smaller type,
1332;; with a preference for `t2' if no smaller type can be determined.
1333;; Merges refinements at each step.
1334;;
1335
1336(define (refine-types t1 t2)
1337
1338 (define (refine t1 t2 te)
1339 (let loop ((t1 t1) (t2 t2))
1340 (cond
1341 ((maybe-expand-type t1) => (cut loop <> t2))
1342 ((maybe-expand-type t2) => (cut loop t1 <>))
1343 ((and (pair? t1) (memq (car t1) '(forall refine)))
1344 (let ((t1* (loop (third t1) t2)))
1345 (and t1* (list (car t1) (second t1) t1*))))
1346 ((and (pair? t2) (memq (car t2) '(forall refine)))
1347 (let ((t2* (loop t1 (third t2))))
1348 (and t2* (list (car t2) (second t2) t2*))))
1349 ;; (or pair null) ~> (list-of a) -> (list-of a)
1350 ((and (pair? t1) (eq? (car t1) 'or)
1351 (lset=/eq? '(null pair) (cdr t1))
1352 (and (pair? t2) (eq? 'list-of (car t2))))
1353 t2)
1354 ((and (pair? t1) (eq? (car t1) 'or))
1355 (let ((ts (filter-map (lambda (t) (loop t t2)) (cdr t1))))
1356 (and (pair? ts) (cons 'or ts))))
1357 ((and (pair? t1)
1358 (memq (car t1) '(pair list vector vector-of list-of))
1359 (pair? t2)
1360 (eq? (car t1) (car t2))
1361 (eq? (length t1) (length t2)))
1362 (let ((ts (map loop (cdr t1) (cdr t2))))
1363 (and (every identity ts) (cons (car t1) ts))))
1364 (else
1365 (type-min t1 t2 te)))))
1366
1367 (let* ((te (type-typeenv `(or ,t1 ,t2)))
1368 (rt (or (refine t1 t2 te) t2)))
1369 (if (eq? rt t2)
1370 rt
1371 (simplify-type rt))))
1372
1373;;; various operations on procedure types
1374
1375(define (procedure-type? t)
1376 (or (eq? 'procedure t)
1377 (and (pair? t)
1378 (case (car t)
1379 ((forall) (procedure-type? (third t)))
1380 ((procedure) #t)
1381 ((or) (every procedure-type? (cdr t)))
1382 (else #f)))))
1383
1384(define (procedure-name t)
1385 (and (pair? t)
1386 (case (car t)
1387 ((forall) (procedure-name (third t)))
1388 ((procedure)
1389 (let ((n (cadr t)))
1390 (cond ((string? n) (string->symbol n))
1391 ((symbol? n) n)
1392 (else #f))))
1393 (else #f))))
1394
1395(define (procedure-arguments t)
1396 (and (pair? t)
1397 (case (car t)
1398 ((forall) (procedure-arguments (third t)))
1399 ((procedure)
1400 (let ((n (second t)))
1401 (if (or (string? n) (symbol? n))
1402 (third t)
1403 (second t))))
1404 (else (bomb "procedure-arguments: not a procedure type" t)))))
1405
1406(define (procedure-results t)
1407 (and (pair? t)
1408 (case (car t)
1409 ((forall) (procedure-results (third t)))
1410 ((procedure)
1411 (let ((n (second t)))
1412 (if (or (string? n) (symbol? n))
1413 (cdddr t)
1414 (cddr t))))
1415 (else (bomb "procedure-results: not a procedure type" t)))))
1416
1417(define (procedure-argument-types t n typeenv #!optional norest)
1418 (let loop1 ((t t) (done '()))
1419 (cond ((and (pair? t)
1420 (eq? 'procedure (car t)))
1421 (let* ((vf #f)
1422 (ok #t)
1423 (alen 0)
1424 (llist
1425 ;; quite a mess
1426 (let loop ((at (if (or (string? (second t)) (symbol? (second t)))
1427 (third t)
1428 (second t)))
1429 (m n)
1430 (opt #f))
1431 (cond ((null? at)
1432 (set! ok (or opt (zero? m)))
1433 '())
1434 ((eq? '#!optional (car at))
1435 (if norest
1436 '()
1437 (loop (cdr at) m #t) ))
1438 ((eq? '#!rest (car at))
1439 (cond (norest '())
1440 (else
1441 (set! vf (and (pair? (cdr at)) (eq? 'values (cadr at))))
1442 (make-list m (rest-type (cdr at))))))
1443 ((and opt (<= m 0)) '())
1444 (else
1445 (set! ok (positive? m))
1446 (set! alen (add1 alen))
1447 (cons (car at) (loop (cdr at) (sub1 m) opt)))))))
1448 (values llist vf ok alen)))
1449 ((and (pair? t) (eq? 'forall (car t)))
1450 (loop1 (third t) done)) ; assumes typeenv has already been extracted
1451 ((assq t typeenv) =>
1452 (lambda (e)
1453 (let ((t2 (second e)))
1454 (if (and t2 (memq t2 done))
1455 (loop1 '* done) ; circularity
1456 (loop1 t2 (cons t done))))))
1457 (else (values (make-list n '*) #f #t n)))))
1458
1459(define (procedure-result-types t values-rest? args typeenv)
1460 (define (loop1 t)
1461 (cond (values-rest? args)
1462 ((assq t typeenv) => (lambda (e) (loop1 (second e))))
1463 ((and (pair? t) (eq? 'procedure (car t)))
1464 (call/cc
1465 (lambda (return)
1466 (let loop ((rt (if (or (string? (second t)) (symbol? (second t)))
1467 (cdddr t)
1468 (cddr t))))
1469 (cond ((null? rt) '())
1470 ((memq rt '(* noreturn)) (return '*))
1471 (else (cons (car rt) (loop (cdr rt)))))))))
1472 ((and (pair? t) (eq? 'forall (car t)))
1473 (loop1 (third t))) ; assumes typeenv has already been extracted
1474 (else '*)))
1475 (loop1 t))
1476
1477(define (named? t)
1478 (and (pair? t)
1479 (case (car t)
1480 ((procedure)
1481 (not (or (null? (cadr t)) (pair? (cadr t)))))
1482 ((forall) (named? (third t)))
1483 (else #f))))
1484
1485(define (rest-type r)
1486 (cond ((null? r) '*)
1487 ((eq? 'values (car r)) '*)
1488 (else (car r))))
1489
1490(define (noreturn-procedure-type? ptype)
1491 (and (pair? ptype)
1492 (case (car ptype)
1493 ((procedure)
1494 (and (list? ptype)
1495 (equal? '(noreturn)
1496 (if (pair? (second ptype))
1497 (cddr ptype)
1498 (cdddr ptype)))))
1499 ((forall)
1500 (noreturn-procedure-type? (third ptype)))
1501 (else #f))))
1502
1503(define (noreturn-type? t)
1504 (or (eq? 'noreturn t)
1505 (and (pair? t)
1506 (case (car t)
1507 ((or) (any noreturn-type? (cdr t)))
1508 ((forall) (noreturn-type? (third t)))
1509 (else #f)))))
1510
1511;;; Refinement type helpers
1512
1513(define (refinement-type? t)
1514 (and (pair? t)
1515 (case (first t)
1516 ((refine) #t)
1517 ((forall) (refinement-type? (third t)))
1518 (else #f))))
1519
1520;;; Type-environments and -variables
1521
1522(define (make-tv sym)
1523 (let* ((r (get sym '##core#tv-root))
1524 ;; ##core#tv-root is a string to make this gensym fast
1525 (new (gensym r)))
1526 (put! new '##core#tv-root r)
1527 new))
1528
1529(define (type-typeenv t)
1530 (let ((te '()))
1531 (let loop ((t t))
1532 (when (pair? t)
1533 (case (car t)
1534 ((refine)
1535 (loop (third t)))
1536 ((procedure)
1537 (cond ((or (string? (second t)) (symbol? (second t)))
1538 (for-each loop (third t))
1539 (when (pair? (cdddr t))
1540 (for-each loop (cdddr t))))
1541 (else
1542 (for-each loop (second t))
1543 (when (pair? (cddr t))
1544 (for-each loop (cddr t))))))
1545 ((forall)
1546 (set! te (append (map (lambda (tv)
1547 (if (symbol? tv)
1548 (list tv #f #f)
1549 (list (first tv) #f (second tv))))
1550 (second t))
1551 te))
1552 (loop (third t)))
1553 ((or)
1554 (for-each loop (cdr t))))))
1555 te))
1556
1557(define (trail-restore tr typeenv)
1558 (do ((tr2 trail (cdr tr2)))
1559 ((eq? tr2 tr))
1560 (let ((a (assq (car tr2) typeenv)))
1561 (set-car! (cdr a) #f))))
1562
1563(define (with-trail-restore typeenv thunk)
1564 (let* ((trail0 trail)
1565 (result (thunk)))
1566 (trail-restore trail0 typeenv)
1567 result))
1568
1569(define (resolve t typeenv)
1570 (simplify-type ;XXX do only when necessary
1571 (let resolve ((t t) (done '()))
1572 (cond ((assq t typeenv) =>
1573 (lambda (a)
1574 (let ((t2 (second a)))
1575 (if (or (not t2)
1576 (memq t done)
1577 (memq t2 done)) ; circular reference
1578 (if (third a)
1579 (resolve (third a) (cons t done))
1580 '*)
1581 (resolve t2 (cons t done))))))
1582 ((not (pair? t))
1583 (if (or (memq t value-types) (memq t basic-types))
1584 t
1585 (bomb "resolve: can't resolve unknown type-variable" t)))
1586 (else
1587 (case (car t)
1588 ((or) `(or ,@(map (cut resolve <> done) (cdr t))))
1589 ((not) `(not ,(resolve (second t) done)))
1590 ((forall refine)
1591 (list (car t) (second t) (resolve (third t) done)))
1592 ((pair list vector vector-of list-of)
1593 (cons (car t) (map (cut resolve <> done) (cdr t))))
1594 ((procedure)
1595 (let* ((name (procedure-name t))
1596 (argtypes (procedure-arguments t))
1597 (rtypes (procedure-results t)))
1598 `(procedure
1599 ,@(if name (list name) '())
1600 ,(let loop ((args argtypes))
1601 (cond ((null? args) '())
1602 ((eq? '#!rest (car args))
1603 (if (equal? '(values) (cdr args))
1604 args
1605 (cons (car args) (loop (cdr args)))))
1606 ((eq? '#!optional (car args))
1607 (cons (car args) (loop (cdr args))))
1608 (else (cons (resolve (car args) done) (loop (cdr args))))))
1609 ,@(if (eq? '* rtypes)
1610 '*
1611 (map (cut resolve <> done) rtypes)))))
1612 (else t)))))))
1613
1614
1615;;; type-db processing
1616
1617(define (load-type-database name specialize #!optional
1618 (path (repository-path)))
1619 (and-let* ((dbfile (if (not path)
1620 (and (##sys#file-exists? name #t #f #f) name)
1621 (chicken.load#find-file name path))))
1622 (debugging 'p (sprintf "loading type database `~a' ...~%" dbfile))
1623 (fluid-let ((scrutiny-debug #f))
1624 (for-each
1625 (lambda (e)
1626 (let* ((name (car e))
1627 (old (variable-mark name '##compiler#type))
1628 (specs (and (pair? (cddr e)) (cddr e)))
1629 (new
1630 (let adjust ((new (cadr e)))
1631 (if (pair? new)
1632 (cond ((and (vector? (car new))
1633 (eq? 'procedure (vector-ref (car new) 0)))
1634 (let loop ((props (cdr (vector->list (car new)))))
1635 (unless (null? props)
1636 (case (car props)
1637 ((#:pure)
1638 (mark-variable name '##compiler#pure #t)
1639 (loop (cdr props)))
1640 ((#:clean)
1641 (mark-variable name '##compiler#clean #t)
1642 (loop (cdr props)))
1643 ((#:enforce)
1644 (mark-variable name '##compiler#enforce #t)
1645 (loop (cdr props)))
1646 ((#:foldable)
1647 (mark-variable name '##compiler#foldable #t)
1648 (loop (cdr props)))
1649 ((#:predicate)
1650 (mark-variable name '##compiler#predicate (cadr props))
1651 (loop (cddr props)))
1652 (else
1653 (bomb
1654 "load-type-database: invalid procedure-type property"
1655 (car props) new)))))
1656 `(procedure ,@(cdr new)))
1657 ((eq? 'forall (car new))
1658 `(forall ,(second new) ,(adjust (third new))))
1659 (else new))
1660 new))))
1661 ;; validation is needed, even though .types-files can be considered
1662 ;; correct, because type variables have to be renamed:
1663 (let-values (((t pred pure) (validate-type new name)))
1664 (unless t
1665 (warning
1666 (sprintf "Invalid type specification for `~a':~%~%~a"
1667 name
1668 (type->pp-string new))))
1669 (when (and old (not (compatible-types? old t)))
1670 (warning
1671 (sprintf
1672 (string-append
1673 "Declared type for toplevel binding `~a'"
1674 "~%~%~a~%~%"
1675 " conflicts with previously loaded type:"
1676 "~%~%~a")
1677 name
1678 (type->pp-string new)
1679 (type->pp-string old))))
1680 (mark-variable name '##compiler#type t)
1681 (mark-variable name '##compiler#type-source 'db)
1682 (when specs
1683 (install-specializations name specs)))))
1684 (call-with-input-file dbfile read-expressions))
1685 #t)))
1686
1687(define (hash-table->list ht)
1688 (let ((len (vector-length ht)))
1689 (let loop1 ((i 0) (lst '()))
1690 (if (>= i len)
1691 lst
1692 (let loop2 ((bl (vector-ref ht i))
1693 (lst lst))
1694 (if (null? bl)
1695 (loop1 (add1 i) lst)
1696 (loop2 (cdr bl)
1697 (cons (cons (caar bl) (cdar bl)) lst))))))))
1698
1699(define (symbol<? s1 s2)
1700 (string<? (symbol->string s1)
1701 (symbol->string s2)))
1702
1703(define (emit-types-file source-file types-file db block-compilation)
1704 (with-output-to-file types-file
1705 (lambda ()
1706 (print "; GENERATED BY CHICKEN " (chicken-version) " FROM "
1707 source-file "\n")
1708 (for-each
1709 (lambda (p)
1710 (let ((sym (car p))
1711 (plist (cdr p)))
1712 (when (and (variable-visible? sym block-compilation)
1713 (memq (variable-mark sym '##compiler#type-source) '(local inference)))
1714 (let ((specs (or (variable-mark sym '##compiler#specializations) '()))
1715 (type (variable-mark sym '##compiler#type))
1716 (pred (variable-mark sym '##compiler#predicate))
1717 (pure (variable-mark sym '##compiler#pure))
1718 (clean (variable-mark sym '##compiler#clean))
1719 (enforce (variable-mark sym '##compiler#enforce))
1720 (foldable (variable-mark sym '##compiler#foldable)))
1721 (pp (cons* sym
1722 (let wrap ((type type))
1723 (if (pair? type)
1724 (case (car type)
1725 ((procedure)
1726 `(#(procedure
1727 ,@(if enforce '(#:enforce) '())
1728 ,@(if pred `(#:predicate ,pred) '())
1729 ,@(if pure '(#:pure) '())
1730 ,@(if clean '(#:clean) '())
1731 ,@(if foldable '(#:foldable) '()))
1732 ,@(cdr type)))
1733 ((forall)
1734 `(forall ,(second type) ,(wrap (third type))))
1735 (else type))
1736 type))
1737 specs))
1738 (newline)))))
1739 (sort (hash-table->list db)
1740 (lambda (a b) (symbol<? (car a) (car b)))))
1741 (print "; END OF FILE"))))
1742
1743;;
1744;; Source node tracking
1745;;
1746;; Nodes are mutated in place during specialization, which may lose line
1747;; number information if, for example, a node is changed from a
1748;; ##core#call to a class without debug info. To preserve line numbers
1749;; and allow us to print fragments of the original source, we maintain a
1750;; side table of mappings from mutated nodes to copies of the originals.
1751;;
1752
1753(define node-mutations '())
1754
1755(define (mutate-node! node expr)
1756 (set! node-mutations (alist-update! node (copy-node node) node-mutations))
1757 (copy-node! (build-node-graph expr) node))
1758
1759(define (source-node n #!optional (k values))
1760 (let ((orig (alist-ref n node-mutations eq?)))
1761 (if (not orig) (k n) (source-node orig k))))
1762
1763(define (source-node-tree n)
1764 (source-node
1765 n
1766 (lambda (n*)
1767 (make-node (node-class n*)
1768 (node-parameters n*)
1769 (map source-node-tree (node-subexpressions n*))))))
1770
1771(define (node-line-number n)
1772 (node-debug-info (source-node n)))
1773
1774(define (node-debug-info n)
1775 (case (node-class n)
1776 ((##core#call)
1777 (let ((params (node-parameters n)))
1778 (and (pair? (cdr params))
1779 (pair? (cadr params)) ; debug-info has line-number information?
1780 (source-info->line (cadr params)))))
1781 ((##core#typecase)
1782 (car (node-parameters n)))
1783 (else #f)))
1784
1785;; Mutate node for specialization
1786
1787(define (specialize-node! node args template)
1788 (let ((env '()))
1789 (define (subst x)
1790 (cond ((and (vector? x)
1791 (= 1 (vector-length x)) )
1792 (let ((y (vector-ref x 0)))
1793 (cond ((integer? y)
1794 (if (negative? y)
1795 (list-tail args (sub1 (- y)))
1796 (list-ref args (sub1 y))))
1797 ((symbol? y)
1798 (cond ((assq y env) => cdr)
1799 (else
1800 (let ((var (gensym y)))
1801 (set! env (alist-cons y var env))
1802 var)))))))
1803 ((and (vector? x)
1804 (= 2 (vector-length x))
1805 (integer? (vector-ref x 0))
1806 (eq? '... (vector-ref x 1)))
1807 (list-tail args (sub1 (vector-ref x 0))))
1808 ((not (pair? x)) x)
1809 ((eq? 'quote (car x)) x) ; to handle numeric constants
1810 (else (cons (subst (car x)) (subst (cdr x))))))
1811 (mutate-node! node (subst template))))
1812
1813
1814;;; Type-validation and -normalization
1815
1816(define (validate-type type name)
1817 ;; - returns converted type or #f
1818 ;; - also converts "(... -> ...)" types
1819 ;; - converts some typenames to struct types (u32vector, etc.)
1820 ;; - handles some type aliases
1821 ;; - drops "#!key ..." args by converting to #!rest
1822 ;; - replaces uses of "&rest"/"&optional" with "#!rest"/"#!optional"
1823 ;; - handles "(T1 -> T2 : T3)" (predicate)
1824 ;; - handles "(T1 --> T2 [: T3])" (clean)
1825 ;; - simplifies result
1826 ;; - coalesces all "forall" forms into one (remove "forall" if typevar-set is empty)
1827 ;; - renames type-variables
1828 ;; - replaces type-abbreviations
1829 (let ((ptype #f) ; (T . PT) | #f
1830 (clean #f)
1831 (typevars '())
1832 (constraints '()))
1833 (define (upto lst p)
1834 (let loop ((lst lst))
1835 (cond ((eq? lst p) '())
1836 (else (cons (car lst) (loop (cdr lst)))))))
1837 (define (memq* x lst) ; memq, but allow improper list
1838 (let loop ((lst lst))
1839 (cond ((not (pair? lst)) #f)
1840 ((eq? (car lst) x) lst)
1841 (else (loop (cdr lst))))))
1842 (define (validate-llist llist)
1843 (cond ((null? llist) '())
1844 ((symbol? llist) '(#!rest *))
1845 ((not (pair? llist)) #f)
1846 ((or (eq? '#!optional (car llist))
1847 (eq? '&optional (car llist)))
1848 (let ((l1 (validate-llist (cdr llist))))
1849 (and l1 (cons '#!optional l1))))
1850 ((or (eq? '#!rest (car llist))
1851 (eq? '&rest (car llist)))
1852 (cond ((null? (cdr llist)) '(#!rest *))
1853 ((not (pair? (cdr llist))) #f)
1854 (else
1855 (let ((l1 (validate (cadr llist))))
1856 (and l1 `(#!rest ,l1))))))
1857 ((eq? '#!key (car llist)) '(#!rest *))
1858 (else
1859 (let* ((l1 (validate (car llist)))
1860 (l2 (validate-llist (cdr llist))))
1861 (and l1 l2 (cons l1 l2))))))
1862 (define (validate t #!optional (rec #t))
1863 (cond ((memq t value-types) t)
1864 ((memq t basic-types) t)
1865 ((memq t struct-types) `(struct ,t))
1866 ((eq? t 'immediate) '(or eof null fixnum char boolean))
1867 ((eq? t 'any) '*)
1868 ((eq? t 'void) 'undefined)
1869 ((eq? t 'input-port) '(refine (input) port))
1870 ((eq? t 'output-port) '(refine (output) port))
1871 ((and (symbol? t) (##sys#get t '##compiler#type-abbreviation)))
1872 ((not (pair? t))
1873 (cond ((memq t typevars) t)
1874 (else #f)))
1875 ((eq? 'not (car t))
1876 (and (= 2 (length t))
1877 `(not ,(validate (second t)))))
1878 ((eq? 'forall (car t))
1879 (and (= 3 (length t))
1880 (list? (second t))
1881 (call/cc
1882 (lambda (return)
1883 (set! typevars
1884 (append (map (lambda (tv)
1885 (cond ((symbol? tv) tv)
1886 ((and (list? tv)
1887 (= 2 (length tv))
1888 (symbol? (car tv)))
1889 (car tv))
1890 (else (return #f))))
1891 (second t))
1892 typevars))
1893 (set! constraints
1894 (append (filter-map
1895 (lambda (tv)
1896 (and (pair? tv)
1897 (list (car tv)
1898 (let ((t (validate (cadr tv))))
1899 (unless t (return #f))
1900 t))))
1901 (second t))
1902 constraints))
1903 (validate (third t) rec)))))
1904 ((and (eq? 'quote (car t))
1905 (pair? (cdr t))
1906 (symbol? (second t))
1907 (null? (cddr t))
1908 (second t))
1909 => (lambda (v)
1910 (unless (memq v typevars)
1911 (set! typevars (cons v typevars)))
1912 v))
1913 ((eq? 'or (car t))
1914 (and (list? t)
1915 (not (null? (cdr t)))
1916 (let ((ts (map validate (cdr t))))
1917 (and (every identity ts)
1918 `(or ,@ts)))))
1919 ((eq? 'struct (car t))
1920 (and (= 2 (length t)) (symbol? (second t)) t))
1921 ((eq? 'deprecated (car t))
1922 (and (= 2 (length t)) (symbol? (second t)) t))
1923 ((eq? 'refine (car t))
1924 (and (= 3 (length t))
1925 (let ((t2 (validate (third t))))
1926 (and (value-type? t2)
1927 (list? (second t))
1928 (every symbol? (second t))
1929 (list 'refine (second t) t2)))))
1930 ((or (memq* '--> t) (memq* '-> t)) =>
1931 (lambda (p)
1932 (let* ((cleanf (eq? '--> (car p)))
1933 (ok (or (not rec) (not cleanf))))
1934 (unless rec (set! clean cleanf))
1935 (let ((cp (memq* ': p)))
1936 (cond ((not cp)
1937 (and ok
1938 (validate
1939 `(procedure ,(upto t p) ,@(cdr p))
1940 rec)))
1941 ((and (= 5 (length t))
1942 (eq? p (cdr t)) ; one argument?
1943 (eq? cp (cdddr t))) ; 4th item is ":"?
1944 (set! t (validate `(procedure (,(first t)) ,(third t)) rec))
1945 ;; we do it this way to distinguish the "outermost" predicate
1946 ;; procedure type
1947 (set! ptype (cons t (validate (cadr cp))))
1948 (and ok t))
1949 (else #f))))))
1950 ((memq (car t) '(vector-of list-of))
1951 (and (list? t)
1952 (= 2 (length t))
1953 (let ((t2 (validate (second t))))
1954 (and t2 `(,(car t) ,t2)))))
1955 ((memq (car t) '(vector list))
1956 (and (list? t)
1957 (let loop ((ts (cdr t)) (ts2 '()))
1958 (cond ((null? ts) `(,(car t) ,@(reverse ts2)))
1959 ((validate (car ts)) =>
1960 (lambda (t2) (loop (cdr ts) (cons t2 ts2))))
1961 (else #f)))))
1962 ((eq? 'pair (car t))
1963 (and (= 3 (length t))
1964 (let ((ts (map validate (cdr t))))
1965 (and (every identity ts) `(pair ,@ts)))))
1966 ((eq? 'procedure (car t))
1967 (and (pair? (cdr t))
1968 (let* ((name (if (symbol? (cadr t))
1969 (cadr t)
1970 name))
1971 (t2 (if (symbol? (cadr t)) (cddr t) (cdr t))))
1972 (and (pair? t2)
1973 (list? (car t2))
1974 (let ((ts (validate-llist (car t2))))
1975 (and ts
1976 (every identity ts)
1977 (let* ((rt2 (cdr t2))
1978 (rt (if (eq? '* rt2)
1979 rt2
1980 (and (list? rt2)
1981 (let ((rts (map validate rt2)))
1982 (and (every identity rts)
1983 rts))))))
1984 (and rt
1985 `(procedure
1986 ,@(if (and name (not rec)) (list name) '())
1987 ,ts
1988 ,@rt)))))))))
1989 (else #f)))
1990 (cond ((validate type #f) =>
1991 (lambda (type)
1992 (when (pair? typevars)
1993 (set! type
1994 `(forall
1995 ,(map (lambda (tv)
1996 (put! tv '##core#tv-root (symbol->string (strip-syntax tv)))
1997 (cond ((assq tv constraints) => identity)
1998 (else tv)))
1999 (delete-duplicates typevars eq?))
2000 ,type)))
2001 (let ((type2 (simplify-type type)))
2002 (values
2003 type2
2004 (and ptype (eq? (car ptype) type) (cdr ptype))
2005 clean))))
2006 (else (values #f #f #f)))))
2007
2008(define (check-and-validate-type type loc #!optional name)
2009 (let-values (((t pred pure) (validate-type (strip-syntax type) name)))
2010 (or t
2011 (error loc "invalid type specifier" type))))
2012
2013(define (install-specializations name specs)
2014 (define (fail spec)
2015 (error "invalid specialization format" spec name))
2016 (mark-variable
2017 name '##compiler#specializations
2018 ;;XXX it would be great if result types could refer to typevars
2019 ;; bound in the argument types, like this:
2020 ;;
2021 ;; (: with-input-from-file ((-> . *) -> . *)
2022 ;; (((forall (a) (-> a))) (a) ...code that does it single-valued-ly...))
2023 ;;
2024 ;; This would make it possible to propagate the (single) result type from
2025 ;; the thunk to the enclosing expression. Unfortunately the simplification in
2026 ;; the first validation renames typevars, so the second validation will have
2027 ;; non-matching names.
2028 (map (lambda (spec)
2029 (if (and (list? spec) (list? (first spec)))
2030 (let* ((args
2031 (map (lambda (t)
2032 (let-values (((t2 pred pure) (validate-type t #f)))
2033 (or t2
2034 (error "invalid argument type in specialization"
2035 t spec name))))
2036 (first spec)))
2037 (typevars (unzip1 (append-map type-typeenv args))))
2038 (cons
2039 args
2040 (case (length spec)
2041 ((2) (cdr spec))
2042 ((3)
2043 (cond ((list? (second spec))
2044 (cons
2045 (map (lambda (t)
2046 (let-values (((t2 pred pure) (validate-type t #f)))
2047 (or t2
2048 (error "invalid result type in specialization"
2049 t spec name))))
2050 (second spec))
2051 (cddr spec)))
2052 ((eq? '* (second spec)) (cdr spec))
2053 (else (fail spec))))
2054 (else (fail spec)))))
2055 (fail spec)))
2056 specs)))
2057
2058
2059;;; Canonicalize complex pair/list type for matching with "list-of"
2060;
2061; Returns an equivalent (list ...) form, or the original argument if no
2062; canonicalization could be done.
2063
2064(define (canonicalize-list-type t)
2065 (cond ((not (pair? t)) t)
2066 ((eq? 'pair (car t))
2067 (let ((tcar (second t))
2068 (tcdr (third t)))
2069 (let rec ((tr tcdr) (ts (list tcar)))
2070 (cond ((eq? 'null tr)
2071 `(list ,@(reverse ts)))
2072 ((and (pair? tr) (eq? 'pair (first tr)))
2073 (rec (third tr) (cons (second tr) ts)))
2074 ((and (pair? tr) (eq? 'list (first tr)))
2075 `(list ,@(reverse ts) ,@(cdr tr)))
2076 (else t)))))
2077 (else t)))
2078
2079
2080;;; Drop namespace from module-prefixed symbol:
2081
2082(define (strip-namespace sym)
2083 (let* ((s (symbol->string sym))
2084 (n (string-length s)))
2085 (let loop ((i 0))
2086 (cond ((eq? i n) sym)
2087 ((eq? (##core#inline "C_subchar" s i) #\#)
2088 (##sys#intern-symbol (##sys#substring s (fx+ i 1) n)))
2089 (else (loop (fx+ i 1)))))))
2090
2091
2092;;; hardcoded result types for certain primitives
2093
2094(define-syntax define-special-case
2095 (syntax-rules ()
2096 ((_ name handler)
2097 (##sys#put! 'name '##compiler#special-result-type handler))))
2098
2099(define-special-case ##sys#make-structure
2100 (lambda (node args loc rtypes)
2101 (or (and-let* ((subs (node-subexpressions node))
2102 ((>= (length subs) 2))
2103 (arg1 (second subs))
2104 ((eq? 'quote (node-class arg1)))
2105 (val (first (node-parameters arg1)))
2106 ((symbol? val)))
2107 ;;XXX a dirty hack - we should remove the distinct
2108 ;; "pointer-vector" type.
2109 (if (eq? 'pointer-vector val)
2110 '(pointer-vector)
2111 `((struct ,(strip-namespace val)))))
2112 rtypes)))
2113
2114(let ()
2115 (define (known-length-vector-index node args loc expected-argcount)
2116 (and-let* ((subs (node-subexpressions node))
2117 ((= (length subs) (add1 expected-argcount)))
2118 (arg1 (walked-result (second args)))
2119 ((pair? arg1))
2120 ((eq? 'vector (car arg1)))
2121 (index (third subs))
2122 ((eq? 'quote (node-class index)))
2123 (val (first (node-parameters index)))
2124 ((fixnum? val)) ; Standard type warning otherwise
2125 (vector-length (length (cdr arg1))))
2126 (if (and (>= val 0) (< val vector-length))
2127 val
2128 (begin
2129 (r-index-out-of-range loc node val vector-length "vector")
2130 #f))))
2131
2132 ;; These are a bit hacky, since they mutate the node. These special
2133 ;; cases are really only intended for determining result types...
2134 (define (vector-ref-result-type node args loc rtypes)
2135 (or (and-let* ((index (known-length-vector-index node args loc 2))
2136 (arg1 (walked-result (second args)))
2137 (vector (second (node-subexpressions node))))
2138 (mutate-node! node `(##sys#slot ,vector ',index))
2139 (list (list-ref (cdr arg1) index)))
2140 rtypes))
2141
2142 (define-special-case scheme#vector-ref vector-ref-result-type)
2143 (define-special-case ##sys#vector-ref vector-ref-result-type)
2144
2145 (define-special-case scheme#vector-set!
2146 (lambda (node args loc rtypes)
2147 (or (and-let* ((index (known-length-vector-index node args loc 3))
2148 (subs (node-subexpressions node))
2149 (vector (second subs))
2150 (new-value (fourth subs))
2151 (new-value-type (walked-result (fourth args)))
2152 (setter (if (type-always-immediate? new-value-type)
2153 '##sys#setislot
2154 '##sys#setslot)))
2155 (mutate-node! node `(,setter ,vector ',index ,new-value))
2156 '(undefined))
2157 rtypes))))
2158
2159;; TODO: Also special-case vector-length? Makes little sense though.
2160
2161
2162;;; List-related special cases
2163;
2164; Preserve known element types for:
2165;
2166; list-ref, list-tail
2167
2168(let ()
2169 (define (list-or-null a)
2170 (if (null? a) 'null `(list ,@a)))
2171
2172 ;; Split a list or pair type form at index i, calling k with the two
2173 ;; sections of the type or returning #f if it doesn't match that far.
2174 ;; Note that "list-of" is handled by "forall" entries in types.db
2175 (define (split-list-type l i k)
2176 (cond ((not (pair? l))
2177 (and (fx= i 0) (eq? l 'null) (k l l)))
2178 ((eq? (first l) 'list)
2179 (and (fx< i (length l))
2180 (receive (left right) (split-at (cdr l) i)
2181 (k (list-or-null left)
2182 (list-or-null right)))))
2183 ((eq? (first l) 'pair)
2184 (let lp ((a '()) (l l) (i i))
2185 (cond ((fx= i 0)
2186 (k (list-or-null (reverse a)) l))
2187 ((and (pair? l)
2188 (eq? (first l) 'pair))
2189 (lp (cons (second l) a)
2190 (third l)
2191 (sub1 i)))
2192 (else #f))))
2193 (else #f)))
2194
2195 ;; canonicalize-list-type will have taken care of converting (pair
2196 ;; (pair ...)) to (list ...) or (list-of ...) for proper lists.
2197 (define (proper-list-type-length t)
2198 (cond ((eq? t 'null) 0)
2199 ((and (pair? t) (eq? (car t) 'list)) (length (cdr t)))
2200 (else #f)))
2201
2202 (define (list+index-call-result-type-special-case k)
2203 (lambda (node args loc rtypes)
2204 (or (and-let* ((subs (node-subexpressions node))
2205 ((= (length subs) 3))
2206 (arg1 (walked-result (second args)))
2207 (index (third subs))
2208 ((eq? 'quote (node-class index)))
2209 (val (first (node-parameters index)))
2210 ((fixnum? val))) ; Standard type warning otherwise
2211 (cond ((negative? val)
2212 (r-index-out-of-range loc node val 'not-used "list")
2213 #f)
2214 ((split-list-type arg1 val k))
2215 ;; Warn only if it's a known proper list. This avoids
2216 ;; false warnings due to component smashing.
2217 ((proper-list-type-length arg1) =>
2218 (lambda (length)
2219 (r-index-out-of-range loc node val length "list")
2220 #f))
2221 (else #f)))
2222 rtypes)))
2223
2224 (define-special-case scheme#list-ref
2225 (list+index-call-result-type-special-case
2226 (lambda (_ result-type)
2227 (and (pair? result-type)
2228 (list (cadr result-type))))))
2229
2230 (define-special-case scheme#list-tail
2231 (list+index-call-result-type-special-case
2232 (lambda (_ result-type) (list result-type)))))
2233
2234(define-special-case scheme#list
2235 (lambda (node args loc rtypes)
2236 (if (null? (cdr args))
2237 '(null)
2238 `((list ,@(map walked-result (cdr args)))))))
2239
2240(define-special-case ##sys#list
2241 (lambda (node args loc rtypes)
2242 (if (null? (cdr args))
2243 '(null)
2244 `((list ,@(map walked-result (cdr args)))))))
2245
2246(define-special-case scheme#vector
2247 (lambda (node args loc rtypes)
2248 `((vector ,@(map walked-result (cdr args))))))
2249
2250(define-special-case ##sys#vector
2251 (lambda (node args loc rtypes)
2252 `((vector ,@(map walked-result (cdr args))))))
2253
2254(define-special-case scheme#reverse
2255 (lambda (node args loc rtypes)
2256 (or (and-let* ((subs (node-subexpressions node))
2257 ((= (length subs) 2))
2258 (arg1 (walked-result (second args)))
2259 ((pair? arg1))
2260 ((eq? (car arg1) 'list)))
2261 `((list ,@(reverse (cdr arg1)))))
2262 rtypes)))
2263
2264(let ()
2265 (define (append-special-case node args loc rtypes)
2266 (define (potentially-proper-list? l) (match-types l 'list '()))
2267
2268 (define (derive-result-type)
2269 (let lp ((args (cdr args))
2270 (index 1))
2271 (if (null? args)
2272 'null
2273 (let* ((arg1 (car args))
2274 (arg1-t (walked-result arg1)))
2275 (cond
2276 ((and (pair? arg1-t) (eq? (car arg1-t) 'list))
2277 (and-let* ((rest-t (lp (cdr args) (add1 index))))
2278 ;; decanonicalize, then recanonicalize to make it
2279 ;; easy to append a variety of types.
2280 (canonicalize-list-type
2281 (foldl (lambda (rest t) `(pair ,t ,rest))
2282 rest-t (reverse (cdr arg1-t))))))
2283
2284 ((and (pair? arg1-t) (eq? (car arg1-t) 'list-of))
2285 (and-let* ((rest-t (lp (cdr args) (add1 index))))
2286 ;; list-of's length unsurety is "contagious"
2287 (simplify-type `(or ,arg1-t ,rest-t))))
2288
2289 ;; TODO: (append (pair x (pair y z)) lst) =>
2290 ;; (pair x (pair y (or z lst)))
2291 ;; This is trickier than it sounds!
2292
2293 (else
2294 ;; The final argument may be an atom or improper list
2295 (unless (or (null? (cdr args))
2296 (potentially-proper-list? arg1-t))
2297 (r-proc-call-argument-type-mismatch
2298 loc node index arg1 'list arg1-t
2299 (variable-mark 'scheme#append '##compiler#type)))
2300 #f))))))
2301 (cond ((derive-result-type) => list)
2302 (else rtypes)))
2303
2304 (define-special-case scheme#append append-special-case)
2305 (define-special-case ##sys#append append-special-case))
2306
2307;;; Special cases for make-list/make-vector with a known size
2308;
2309; e.g. (make-list 3 #\a) => (list char char char)
2310
2311(let ()
2312
2313 (define (complex-object-constructor-result-type-special-case type)
2314 (lambda (node args loc rtypes)
2315 (or (and-let* ((subs (node-subexpressions node))
2316 (fill (case (length subs)
2317 ((2) '*)
2318 ((3) (walked-result (third args)))
2319 (else #f)))
2320 (sub2 (second subs))
2321 ((eq? 'quote (node-class sub2)))
2322 (size (first (node-parameters sub2)))
2323 ((fixnum? size))
2324 ((<= 0 size +maximal-complex-object-constructor-result-type-length+)))
2325 `((,type ,@(make-list size fill))))
2326 rtypes)))
2327
2328 (define-special-case scheme#make-vector
2329 (complex-object-constructor-result-type-special-case 'vector)))
2330
2331
2332;;; perform check over all typevar instantiations
2333;
2334; If "all" is #t all types in tlist must match, if #f then one or more.
2335
2336(define (over-all-instantiations tlist typeenv all process)
2337 (let ((insts '())
2338 (anyinst #f)
2339 (trail0 trail))
2340
2341 ;; restore trail and collect instantiations
2342 (define (restore)
2343 (ddd "restoring, trail: ~s, te: ~s" trail typeenv)
2344 (let ((is '()))
2345 (do ((tr trail (cdr tr)))
2346 ((eq? tr trail0)
2347 (set! trail tr)
2348 (when (pair? is) (set! anyinst #t))
2349 (set! insts (cons is insts)))
2350 (set! is (alist-cons
2351 (car tr)
2352 (resolve (car tr) typeenv)
2353 is))
2354 (ddd " restoring ~a, insts: ~s" (car tr) insts)
2355 (let ((a (assq (car tr) typeenv)))
2356 (set-car! (cdr a) #f)))))
2357
2358 ;; collect candidates for each typevar
2359 (define (collect)
2360 (let* ((vars (delete-duplicates (concatenate (map unzip1 insts)) eq?))
2361 (all (map (lambda (var)
2362 (cons
2363 var
2364 (filter-map
2365 (lambda (inst)
2366 (cond ((assq var inst) => cdr)
2367 ;;XXX is the following correct in all cases?
2368 (all '*)
2369 (else #f)))
2370 insts)))
2371 vars)))
2372 (ddd " collected: ~s" all)
2373 all))
2374
2375 (ddd " over-all-instantiations: ~s all: ~a" tlist all)
2376 ;; process all tlist elements
2377 (let loop ((ts (delete-duplicates tlist eq?))
2378 (ok #f))
2379 (cond ((null? ts)
2380 (cond ((or ok (null? tlist))
2381 (for-each
2382 (lambda (i)
2383 (set! trail (cons (car i) trail))
2384 (set-car! (cdr (assq (car i) typeenv))
2385 (simplify-type `(or ,@(cdr i)))))
2386 (collect))
2387 #t)
2388 (else #f)))
2389 ((process (car ts))
2390 (restore)
2391 (loop (cdr ts) #t))
2392 (all
2393 (restore)
2394 #f)
2395 (else
2396 (restore)
2397 (loop (cdr ts) ok))))))
2398
2399;;; Report helpers
2400
2401(define (multiples n)
2402 (if (= n 1) "" "s"))
2403
2404(define (string-add-indent str #!optional (indent " "))
2405 (let* ((ls (string-split str "\n" #t))
2406 (s (string-intersperse
2407 (map (lambda (l)
2408 (if (string=? "" l)
2409 l
2410 (string-append indent l)))
2411 ls)
2412 "\n")))
2413 (if (eq? #\newline (string-ref str (sub1 (string-length str))))
2414 (string-append s "\n")
2415 s)))
2416
2417(define (type->pp-string t)
2418 (define (pp-tv tv)
2419 (let ((r (get tv '##core#tv-root)))
2420 (assert r (list tv: tv))
2421 (list 'quote (string->symbol r))))
2422 (define (conv t #!optional (tv-replacements '()))
2423 (define (R t) (conv t tv-replacements))
2424 (cond
2425 ((not (pair? t))
2426 (or (alist-ref t tv-replacements eq?) t))
2427 ((refinement-type? t)
2428 (string->symbol
2429 (sprintf "~a-~a" (string-intersperse (map conc (second t)) "/") (third t))))
2430 (else
2431 (let ((tcar (and (pair? t) (car t))))
2432 (cond
2433 ((and (eq? 'forall tcar) (every symbol? (second t))) ; no constraints
2434 (let ((tvs (map (lambda (tv) (cons tv (pp-tv tv))) (second t))))
2435 (conv (third t) tvs)))
2436 ((eq? 'forall tcar) t) ; forall with constraints, do nothing
2437 ((memq tcar '(or not list vector pair list-of vector-of))
2438 `(,tcar ,@(map R (cdr t))))
2439 ((eq? 'struct tcar) t)
2440 ((eq? 'procedure tcar)
2441 (let ((args (map R (procedure-arguments t)))
2442 (res (let ((res (procedure-results t)))
2443 (if (eq? '* res)
2444 #f
2445 (map R res)))))
2446 (if (not res) ; '. *' return type not supported by ->
2447 `(procedure ,args ,@(or res '*))
2448 `(,@args ,(if (and-let* ((pn (procedure-name t))
2449 ((variable-mark pn '##compiler#pure))))
2450 '--> '->)
2451 ,@res))))
2452 (else (bomb "type->pp-string: unhandled type" t)))))))
2453 (let ((t* (conv (strip-syntax t))))
2454 (string-add-indent
2455 (string-chomp
2456 (with-output-to-string
2457 (lambda () (pp t*)))))))
2458
2459(define (fragment x)
2460 (let ((x (build-expression-tree (source-node-tree x))))
2461 (let walk ((x x) (d 0))
2462 (cond ((atom? x) (strip-syntax x))
2463 ((>= d +fragment-max-depth+) '...)
2464 ((list? x)
2465 (let* ((len (length x))
2466 (xs (if (< +fragment-max-length+ len)
2467 (append (take x +fragment-max-length+) '(...))
2468 x)))
2469 (map (cute walk <> (add1 d)) xs)))
2470 (else (strip-syntax x))))))
2471
2472(define (pp-fragment x)
2473 (string-add-indent
2474 (string-chomp
2475 (with-output-to-string
2476 (lambda ()
2477 (pp (fragment x)))))))
2478
2479(define (node-source-prefix n)
2480 (let ((line (node-line-number n)))
2481 (if (not line) "" (sprintf "In file `~a'," line))))
2482
2483(define (location-name loc #!optional (indent " "))
2484 (define (lname loc1)
2485 (if loc1
2486 (sprintf "In procedure `~a'," (real-name loc1))
2487 "In a local procedure,"))
2488 (if (null? loc)
2489 (conc "At the toplevel,\n" indent)
2490 (let rec ((loc loc)
2491 (msgs (list "")))
2492 (if (null? (cdr loc))
2493 (string-intersperse
2494 (cons (if (car loc)
2495 ;; If the first location is of format 'bar#foo'
2496 ;; consider it as being being procedure 'foo' in
2497 ;; module 'bar'.
2498 (receive (var mod) (variable-and-module (real-name (car loc)))
2499 (conc (if mod (sprintf "In module `~a',~%~a" mod indent) "")
2500 (sprintf "In procedure `~a'," var)))
2501 "In a toplevel procedure,") msgs)
2502 (conc "\n" indent))
2503 (rec (cdr loc)
2504 (cons (lname (car loc)) msgs))))))
2505
2506(define (variable-and-module name) ; -> (values var module-or-false)
2507 (let* ((str-name (if (symbol? name) (symbol->string name) name))
2508 (r (string-split str-name "#" #t)))
2509 (if (pair? (cdr r))
2510 (values (string->symbol (second r)) (string->symbol (first r)))
2511 (values (string->symbol str-name) #f))))
2512
2513(define (variable-from-module sym)
2514 (receive (var mod) (variable-and-module sym)
2515 (if mod
2516 (sprintf "`~a' from module `~a'" var mod)
2517 (sprintf "`~a'" var))))
2518
2519(define (describe-expression node)
2520 (define (p-expr n)
2521 (sprintf (string-append "This is the expression:" "~%~%" "~a")
2522 (pp-fragment n)))
2523 (define (p-node n)
2524 (cond ((and (eq? '##core#call (node-class n))
2525 (let ((pnode (first (node-subexpressions n))))
2526 (and-let* (((eq? '##core#variable (node-class pnode)))
2527 (pname (car (node-parameters pnode)))
2528 (ptype (variable-mark pname '##compiler#type)))
2529 (sprintf (string-append
2530 "It is a call to ~a which has this type:"
2531 "~%~%"
2532 "~a"
2533 "~%~%"
2534 "~a")
2535 (variable-from-module pname)
2536 (type->pp-string ptype)
2537 (p-expr n))))))
2538 ((eq? '##core#the/result (node-class n)) ; walk through
2539 (p-node (first (node-subexpressions n))))
2540 (else (p-expr n))))
2541 (p-node (source-node-tree node)))
2542
2543(define (call-node-procedure-name node)
2544 (fragment (first (node-subexpressions node))))
2545
2546(define (report2 short report-f location-node-candidates loc msg . args)
2547 (define (file-location)
2548 (any (lambda (n) (and (not (string=? "" (node-source-prefix n)))
2549 (node-source-prefix n)))
2550 location-node-candidates))
2551 (when *complain?*
2552 (report-f
2553 (conc
2554 short
2555 (string-add-indent
2556 (conc (let ((l (file-location))) (if l (conc "\n" l) "")) "\n"
2557 (location-name loc "")
2558 (sprintf "~?" msg args))
2559 " ")))
2560 (flush-output)))
2561
2562(define (report-notice reason location-node-candidates loc msg . args)
2563 (apply report2 reason ##sys#notice location-node-candidates loc msg args))
2564
2565;;; Reports
2566
2567(define (r-invalid-called-procedure-type loc call-node xptype p-node ptype)
2568 (define (variable-node-name n)
2569 (cond ((eq? '##core#the/result (node-class n))
2570 (variable-node-name (first (node-subexpressions n))))
2571 ((eq? '##core#variable (node-class n)) (car (node-parameters n)))
2572 (else #f)))
2573 (if (variable-node-name p-node)
2574 (report2
2575 "Invalid procedure"
2576 warning
2577 (list p-node call-node)
2578 loc
2579 (string-append
2580 "In procedure call:"
2581 "~%~%"
2582 "~a"
2583 "~%~%"
2584 "Variable ~a is not a procedure."
2585 "~%~%"
2586 "It has this type:"
2587 "~%~%"
2588 "~a")
2589 (pp-fragment call-node)
2590 (variable-from-module (variable-node-name p-node))
2591 (type->pp-string ptype))
2592 (report2
2593 "Invalid procedure"
2594 warning
2595 (list p-node call-node)
2596 loc
2597 (string-append
2598 "In procedure call:"
2599 "~%~%"
2600 "~a"
2601 "~%~%"
2602 "The procedure expression does not appear to be a callable."
2603 "~%~%"
2604 "~a"
2605 "~%~%"
2606 "The expected type is:"
2607 "~%~%"
2608 "~a"
2609 "~%~%"
2610 "The actual type is:"
2611 "~%~%"
2612 "~a")
2613 (pp-fragment call-node)
2614 (describe-expression p-node)
2615 (type->pp-string xptype)
2616 (type->pp-string ptype))))
2617
2618(define (r-proc-call-argument-count-mismatch loc node exp-count argc ptype)
2619 (define pname (call-node-procedure-name node))
2620 (report2
2621 "Wrong number of arguments"
2622 warning
2623 (list node)
2624 loc
2625 (string-append
2626 "In procedure call:"
2627 "~%~%"
2628 "~a"
2629 "~%~%"
2630 "Procedure `~a' is called with ~a argument~a but ~a argument~a ~a expected."
2631 "~%~%"
2632 "Procedure ~a has this type:"
2633 "~%~%"
2634 "~a")
2635 (pp-fragment node)
2636 (strip-namespace pname)
2637 argc (multiples argc)
2638 exp-count (multiples exp-count)
2639 (if (= exp-count 1) "is" "are")
2640 (variable-from-module pname)
2641 (type->pp-string ptype)))
2642
2643(define (r-proc-call-argument-type-mismatch loc node i arg-node xptype atype ptype)
2644 (define pname (call-node-procedure-name node))
2645 (report2
2646 "Invalid argument"
2647 warning
2648 (list node)
2649 loc
2650 (string-append
2651 "In procedure call:"
2652 "~%~%"
2653 "~a"
2654 "~%~%"
2655 "Argument #~a to procedure `~a' has an invalid type:"
2656 "~%~%"
2657 "~a"
2658 "~%~%"
2659 "The expected type is:"
2660 "~%~%"
2661 "~a"
2662 "~%~%"
2663 "~a"
2664 "~%~%"
2665 "Procedure ~a has this type:"
2666 "~%~%"
2667 "~a")
2668 (pp-fragment node)
2669 i
2670 (strip-namespace pname)
2671 (type->pp-string atype)
2672 (type->pp-string xptype)
2673 (describe-expression arg-node)
2674 (variable-from-module pname)
2675 (type->pp-string ptype)))
2676
2677(define (r-proc-call-argument-value-count loc call-node i arg-node atype)
2678 (define pname (call-node-procedure-name call-node))
2679 (define (p short long)
2680 (report2
2681 short
2682 warning
2683 (list arg-node call-node)
2684 loc
2685 (string-append
2686 "In procedure call:"
2687 "~%~%"
2688 "~a"
2689 "~%~%"
2690 "Argument #~a to procedure~a ~a"
2691 "~%~%"
2692 "~a")
2693 (pp-fragment call-node)
2694 i
2695 (if (zero? i) "" (sprintf " `~a'" (strip-namespace pname)))
2696 long
2697 (describe-expression arg-node)))
2698 (if (zero? (length atype))
2699 (p "Not enough argument values"
2700 "does not return any values.")
2701 (p "Too many argument values"
2702 (sprintf "returns ~a values but 1 is expected." (length atype)))))
2703
2704(define (r-index-out-of-range loc node idx obj-length obj-name)
2705 ;; Negative indices should always generate a warning
2706 (define pname (call-node-procedure-name node))
2707 (report2
2708 (if (negative? idx)
2709 (sprintf "Negative ~a index" obj-name)
2710 (sprintf "~a~a index out of range"
2711 (char-upcase (string-ref obj-name 0))
2712 (substring obj-name 1)))
2713 warning
2714 (list node)
2715 loc
2716 (string-append
2717 "In procedure call:"
2718 "~%~%"
2719 "~a"
2720 "~%~%"
2721 "Procedure ~a is called with ~a")
2722 (pp-fragment node)
2723 (variable-from-module pname)
2724 (if (negative? idx)
2725 (sprintf "a negative index ~a." idx)
2726 (sprintf "index `~a' for a ~a of length `~a'." idx obj-name obj-length))))
2727
2728(define (r-conditional-value-count-invalid loc if-node test-node atype)
2729 (define (p short long)
2730 (report2 short warning (list test-node if-node)
2731 loc
2732 (string-append
2733 "In conditional:"
2734 "~%~%"
2735 "~a"
2736 "~%~%"
2737 "The test expression ~a"
2738 "~%~%"
2739 "~a")
2740 (pp-fragment if-node)
2741 long
2742 (describe-expression test-node)))
2743 (if (zero? (length atype))
2744 (p "Zero values for conditional"
2745 "returns 0 values.")
2746 (p "Too many values for conditional"
2747 (sprintf "returns ~a values." (length atype)))))
2748
2749(define (r-let-value-count-invalid loc var let-node val-node atype)
2750 (define (p short long)
2751 (report2 short warning (list val-node let-node)
2752 loc
2753 (string-append
2754 "In let expression:"
2755 "~%~%"
2756 "~a"
2757 "~%~%"
2758 "Variable `~a' is bound to an expression that ~a"
2759 "~%~%"
2760 "~a")
2761 (pp-fragment let-node)
2762 (real-name var)
2763 long
2764 (describe-expression val-node)))
2765 (if (zero? (length atype))
2766 (p (sprintf "Let binding to `~a' has zero values" (real-name var))
2767 "returns 0 values.")
2768 (p (sprintf "Let binding to `~a' has ~a values" (real-name var) (length atype))
2769 (sprintf "returns ~a values." (length atype)))))
2770
2771(define (r-assignment-value-count-invalid loc var set-node val-node atype)
2772 (define (p short long)
2773 (report2 short warning (list val-node set-node)
2774 loc
2775 (string-append
2776 "In assignment:"
2777 "~%~%"
2778 "~a"
2779 "~%~%"
2780 "Variable `~a' is assigned from expression that ~a"
2781 "~%~%"
2782 "~a")
2783 (pp-fragment set-node)
2784 (strip-namespace var)
2785 long
2786 (describe-expression val-node)))
2787 (if (zero? (length atype))
2788 (p (sprintf "Assignment to `~a' has zero values" (strip-namespace var))
2789 "returns 0 values.")
2790 (p (sprintf "Assignment to `~a' has ~a values" (strip-namespace var) (length atype))
2791 (sprintf "returns ~a values." (length atype)))))
2792
2793(define (r-pred-call-always-true loc node pred-type atype)
2794 (define pname (call-node-procedure-name node))
2795 (report-notice
2796 "Predicate is always true"
2797 (list node)
2798 loc
2799 (string-append
2800 "In procedure call:"
2801 "~%~%"
2802 "~a"
2803 "~%~%"
2804 "The predicate will always return true."
2805 "~%~%"
2806 "Procedure ~a is a predicate for:"
2807 "~%~%"
2808 "~a"
2809 "~%~%"
2810 "The given argument has this type:"
2811 "~%~%"
2812 "~a")
2813 (pp-fragment node)
2814 (variable-from-module pname)
2815 (type->pp-string pred-type)
2816 (type->pp-string atype)))
2817
2818(define (r-pred-call-always-false loc node pred-type atype)
2819 (define pname (call-node-procedure-name node))
2820 (report-notice
2821 "Predicate is always false"
2822 (list node)
2823 loc
2824 (string-append
2825 "In procedure call:"
2826 "~%~%"
2827 "~a"
2828 "~%~%"
2829 "The predicate will always return false."
2830 "~%~%"
2831 "Procedure ~a is a predicate for:"
2832 "~%~%"
2833 "~a"
2834 "~%~%"
2835 "The given argument has this type:"
2836 "~%~%"
2837 "~a")
2838 (pp-fragment node)
2839 (variable-from-module pname)
2840 (type->pp-string pred-type)
2841 (type->pp-string atype)))
2842
2843(define (r-cond-test-always-true loc if-node test-node t)
2844 (report-notice
2845 "Test is always true"
2846 (list test-node if-node)
2847 loc
2848 (string-append
2849 "In conditional expression:"
2850 "~%~%"
2851 "~a"
2852 "~%~%"
2853 "Test condition has always true value of type:"
2854 "~%~%"
2855 "~a")
2856 (pp-fragment if-node)
2857 (type->pp-string t)))
2858
2859(define (r-cond-test-always-false loc if-node test-node)
2860 (report-notice
2861 "Test is always false"
2862 (list test-node if-node)
2863 loc
2864 (string-append
2865 "In conditional expression:"
2866 "~%~%"
2867 "~a"
2868 "~%~%"
2869 "Test condition is always false.")
2870 (pp-fragment if-node)))
2871
2872(define (r-zero-values-for-the loc node the-type)
2873 ;; (the t r) expects r returns exactly 1 value
2874 (report2
2875 "Not enough values"
2876 warning
2877 (list node)
2878 loc
2879 (string-append
2880 "In expression:"
2881 "~%~%"
2882 "~a"
2883 "~%~%"
2884 "Expression returns 0 values but is declared to return:"
2885 "~%~%"
2886 "~a")
2887 (pp-fragment node)
2888 (type->pp-string the-type)))
2889
2890(define (r-too-many-values-for-the loc node the-type rtypes)
2891 (report2
2892 "Too many values"
2893 warning
2894 (list node)
2895 loc
2896 (string-append
2897 "In expression:"
2898 "~%~%"
2899 "~a"
2900 "~%~%"
2901 "Expression returns too many values."
2902 "~%~%"
2903 "The expression returns ~a values but is declared to return:"
2904 "~%~%"
2905 "~a")
2906 (pp-fragment node)
2907 (length rtypes)
2908 (type->pp-string the-type)))
2909
2910(define (r-type-mismatch-in-the loc node atype the-type)
2911 (report2
2912 "Type mismatch"
2913 warning
2914 (list node)
2915 loc
2916 (string-append
2917 "In expression:"
2918 "~%~%"
2919 "~a"
2920 "~%~%"
2921 "Expression's declared and actual types do not match."
2922 "~%~%"
2923 "The declared type is:"
2924 "~%~%"
2925 "~a"
2926 "~%~%"
2927 "The actual type is:"
2928 "~%~%"
2929 "~a")
2930 (pp-fragment node)
2931 (type->pp-string the-type)
2932 (type->pp-string atype)))
2933
2934(define (fail-compiler-typecase loc node atype ct-types)
2935 (define (pp-type t) (string-add-indent (type->pp-string t) " "))
2936 (quit-compiling
2937 (string-append
2938 "No typecase match"
2939 "~%"
2940 "~a"
2941 "~a"
2942 "In `compiler-typecase' expression:"
2943 "~%~%"
2944 " ~a"
2945 "~%~%"
2946 " Tested expression does not match any case."
2947 "~%~%"
2948 " The expression has this type:"
2949 "~%~%"
2950 "~a"
2951 "~%~%"
2952 " The specified type cases are these:"
2953 "~%~%"
2954 "~a")
2955 (if (string=? "" (node-source-prefix node))
2956 "\n"
2957 (conc " " (node-source-prefix node) "\n "))
2958 (location-name loc)
2959 (pp-fragment node)
2960 (pp-type atype)
2961 (string-intersperse (map pp-type ct-types) "\n\n")))
2962
2963(define (r-cond-branch-value-count-mismatch loc node c-node a-node c-types a-types)
2964 (report2
2965 "Branch values mismatch"
2966 warning
2967 (list a-node node)
2968 loc
2969 (string-append
2970 "In conditional expression:"
2971 "~%~%"
2972 "~a"
2973 "~%~%"
2974 "The branches have different numbers of values."
2975 "~%~%"
2976 "The true branch returns ~a value~a:"
2977 "~%~%"
2978 "~a"
2979 "~%~%"
2980 "The false branch returns ~a value~a:"
2981 "~%~%"
2982 "~a")
2983 (pp-fragment node)
2984 (length c-types) (multiples (length c-types))
2985 (pp-fragment c-node)
2986 (length a-types) (multiples (length a-types))
2987 (pp-fragment a-node)))
2988
2989(define (r-toplevel-var-assignment-type-mismatch loc node atype var xptype value-node)
2990 (report2
2991 "Invalid assignment"
2992 warning
2993 (list node value-node)
2994 loc
2995 (string-append
2996 "In assignment:"
2997 "~%~%"
2998 "~a"
2999 "~%~%"
3000 "Variable `~a' is assigned invalid value."
3001 "~%~%"
3002 "The assigned value has this type:"
3003 "~%~%"
3004 "~a"
3005 "~%~%"
3006 "The declared type of ~a is:"
3007 "~%~%"
3008 "~a")
3009 (pp-fragment node)
3010 (strip-namespace var)
3011 (type->pp-string atype)
3012 (variable-from-module
3013 (let ((n (real-name var)))
3014 (if (symbol? n) n (string->symbol n))))
3015 (type->pp-string xptype)))
3016
3017(define (r-deprecated-identifier loc node id #!optional suggestion)
3018 (report2
3019 (sprintf "Deprecated identifier `~a'" (strip-namespace id))
3020 warning
3021 (list node)
3022 loc
3023 (string-append
3024 "In expression:"
3025 "~%~%"
3026 "~a"
3027 "~%~%"
3028 "Use of deprecated identifier ~a."
3029 "~a")
3030 (pp-fragment node) ;; TODO: parent node would be nice here
3031 (variable-from-module id)
3032 (if suggestion
3033 (sprintf "~%~%The suggested alternative is ~a."
3034 (variable-from-module suggestion))
3035 "")))
3036)