~ chicken-core (chicken-5) /scrutinizer.scm
Trap1;;;; scrutinizer.scm - The CHICKEN Scheme compiler (local flow analysis)2;3; Copyright (c) 2009-2022, The CHICKEN Team4; All rights reserved.5;6; Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following7; conditions are met:8;9; Redistributions of source code must retain the above copyright notice, this list of conditions and the following10; disclaimer.11; Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following12; 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 promote14; 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 EXPRESS17; OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY18; AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR19; CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR20; CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR21; SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY22; THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR23; OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE24; POSSIBILITY OF SUCH DAMAGE.252627(declare28 (unit scrutinizer)29 (uses data-structures expand extras pathname port support internal))3031(module chicken.compiler.scrutinizer32 (scrutinize load-type-database emit-types-file33 validate-type check-and-validate-type install-specializations34 ;; Exported for use in the tests:35 match-types refine-types type<=?)3637(import scheme38 chicken.base39 chicken.compiler.support40 chicken.fixnum41 chicken.format42 chicken.internal43 chicken.io44 chicken.keyword45 chicken.pathname46 chicken.platform47 chicken.plist48 chicken.sort49 chicken.port50 chicken.pretty-print51 chicken.string52 chicken.syntax)5354(include "tweaks")55(include "mini-srfi-1.scm")5657(define d-depth 0)58(define scrutiny-debug #t)59(define *complain?* #f)6061(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)) )6465(define dd d)66(define ddd d)6768(define-syntax d (syntax-rules () ((_ . _) (void))))69(define-syntax dd (syntax-rules () ((_ . _) (void))))70(define-syntax ddd (syntax-rules () ((_ . _) (void))))717273;;; Walk node tree, keeping type and binding information74;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; | VALUE83; | BASIC84; | COMPLEX85; | (forall (TVAR1 ...) TYPE)86; | (refine (SYMBOL ...) VALUE)87; | deprecated88; | (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-vector93; BASIC = * | list | pair | procedure | vector | undefined | noreturn | values94; 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) | VAR102;103; global symbol properties:104;105; ##compiler#type -> TYPESPEC106; ##compiler#type-source -> 'db | 'local | 'inference107; ##compiler#predicate -> TYPESPEC108; ##compiler#specializations -> (SPECIALIZATION ...)109; ##compiler#local-specializations -> (SPECIALIZATION ...)110; ##compiler#enforce -> BOOL111; ##compiler#special-result-type -> PROCEDURE112; ##compiler#escape -> #f | 'yes | 'no113; ##compiler#type-abbreviation -> TYPESPEC114;; ##compiler#tv-root -> STRING115;116; specialization specifiers:117;118; SPECIALIZATION = ((TYPE ... [#!rest TYPE]) [RESULTS] TEMPLATE)119; TEMPLATE = #(INDEX)120; | #(INDEX ...)121; | #(SYMBOL)122; | INTEGER | SYMBOL | STRING123; | (quote CONSTANT)124; | (TEMPLATE . TEMPLATE)125;126; As an alternative to the "#!rest" and "#!optional" keywords, "&rest" or "&optional"127; may be used.128129130(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)134135(define-constant value-types136 '(string symbol keyword char null boolean true false blob eof bwp137 fixnum float number integer bignum ratnum cplxnum138 pointer-vector port pointer locative))139140(define-constant basic-types141 '(* list pair procedure vector undefined deprecated noreturn values))142143(define-constant struct-types144 '(u8vector s8vector u16vector s16vector u32vector s32vector u64vector145 s64vector f32vector f64vector thread queue environment time146 continuation lock mmap condition hash-table tcp-listener))147148(define-constant type-expansions149 '((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 *) . *))))156157(define-inline (struct-type? t)158 (and (pair? t) (eq? (car t) 'struct)))159160(define-inline (value-type? t)161 (or (struct-type? t) (memq t value-types)))162163(define specialization-statistics '())164(define trail '())165166(define (walked-result n)167 (first (node-parameters n))) ; assumes ##core#the/result node168169(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)))177178(define (scrutinize node db complain specialize strict block-compilation)179 (d "################################## SCRUTINIZE ##################################")180 (define (report loc msg . args)181 (when *complain?*182 (warning183 (conc (location-name loc)184 (sprintf "~?" msg args)))))185186 (set! *complain?* complain)187188 (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))195196 (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-type214 `(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-type220 `(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 '*)))225226 (define (global-result id loc node)227 (cond ((variable-mark id '##compiler#type) =>228 (lambda (a)229 (cond230 ((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 '(*))))238239 (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)))246247 (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 #;(report257 loc258 "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))))263264 (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)))273274 (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))278279 (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))283284 (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 (else293 (r-value-count-mismatch tv)294 (first tv))))))295296 (define add-loc cons)297298 (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)))303304 (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-type315 loc node (resolve xptype typeenv) (car args) (resolve ptype typeenv))316 (values '* #f))317 (else318 (let-values (((atypes values-rest ok alen)319 (procedure-argument-types ptype nargs typeenv)))320 (unless ok321 (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-types328 (car atypes)329 (car actualtypes)330 typeenv)331 (r-proc-call-argument-type-mismatch332 loc node i333 (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 pn343 (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-true348 loc node pt (cadr actualtypes))349 (when specialize350 (specialize-node!351 node (cdr args)352 `(let ((#(tmp) #(1))) '#t))353 (set! r '(true))354 (set! op (list pn pt))))355 ((begin356 (trail-restore trail0 typeenv)357 (match-argument-types (list `(not ,pt)) (cdr actualtypes) typeenv))358 (r-pred-call-always-false359 loc node pt (cadr actualtypes))360 (when specialize361 (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 again372 (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 (append381 (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 r2389 (third spec)390 (second spec))))391 (specialize-node! node (cdr args) rewrite)392 (when r2 (set! r r2))))393 (else394 (trail-restore trail0 tenv2)395 (loop (cdr specs))))))))))396 (when op397 (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 (else401 (set! specialization-statistics402 (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))))))))411412 (define tag413 (let ((n 0))414 (lambda ()415 (set! n (add1 n))416 n)))417418 (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 a423 (d " applying to alias: ~a -> ~a" (cdr a) type)424 (loop (cdr a))))))425426 (define (initial-argument-types dest vars argc)427 (if (and dest strict428 (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 '*)))436437 (define (walk n e loc dest flow ctags) ; returns result specifier438 (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 ((results447 (case class448 ((##core#the/result) (list (first params))) ; already walked449 ((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 (cond468 ((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 (else477 (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 for484 ;; 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-each489 (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 (else504 (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 bindings511 (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-list526 (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 dest536 (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 specialize546 dest547 (variable-mark dest '##compiler#type-source)548 (not unsafe))549 (debugging 'x "checks argument-types" dest) ;XXX550 ;; [1] this is subtle: we don't want argtype-checks to be551 ;; generated for toplevel defs other than user-declared ones.552 ;; But since the ##compiler#type-source mark is set AFTER553 ;; the lambda has been walked (see below, [2]), nothing is added.554 (generate-type-checks! n dest vars inits))555 (list556 (append557 '(procedure)558 namelst559 (list560 (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 (cons567 (cond ((eq? (cdr a) '*) '*)568 (else569 (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 (else574 (cons575 (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-invalid583 loc var n (first subs) <>)))584 (typeenv (append585 (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 this596 (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 enforcing612 (mark-variable var '##compiler#type-source 'inference)613 (mark-variable var '##compiler#type rt))))))614 (when b615 (cond ((eq? 'undefined (cdr b)) (set-cdr! b rt))616 #;(strict617 (let ((ot (or (blist-type var flow) (cdr b))))618 ;;XXX compiler-syntax for "map" will introduce619 ;; assignments that trigger this warning, so this620 ;; is currently disabled621 (unless (compatible-types? ot rt)622 (report623 loc624 "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 rt628 '*))629 (fl (car flow)))630 ;; For each outer flow F, change the var's631 ;; type to (or t <old-type@F>). Add a new632 ;; 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_1638 ;; (if foo639 ;; (set! y 'a)) ; y : symbol @ flow f_2640 ;; y) ; (1) @ flow f_1641 ;;642 ;; At point (1) the type of y can be inferred643 ;; to be (or fixnum symbol). The type of x644 ;; 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?))))))659660 (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)))664665 '(undefined)))666 ((##core#primitive) '*)667 ((##core#call)668 (let* ((f (fragment n))669 (len (length subs))670 (args (map (lambda (n2 i)671 (make-node672 '##core#the/result673 (list674 (single675 (walk n2 e loc #f flow #f)676 (cut r-proc-call-argument-value-count loc n i n2 <>)))677 (list n2)))678 subs679 (iota len)))680 (fn (walked-result (car args)))681 (pn (procedure-name fn))682 (typeenv (type-typeenv683 `(or ,@(map walked-result args)))) ; hack684 (enforces685 (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 (and693 (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 stuff701 ;; like "##core#inline", etc.702 (if (eq? '* r)703 r704 (map (cut resolve <> typeenv) r)))705 ((eq? 'quote (node-class n)) ; Call got constant folded706 (walk n e loc dest flow ctags))707 (else708 (for-each709 (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 pt716 ctags717 (not (db-get db var 'assigned))718 (not oparg?))))719 (cond (pred720 ;;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-blist726 var (car ctags)727 (if (not a) pt (refine-types a pt)))728 ;; if the variable type is an "or"-type, we can729 ;; can remove all elements that match the predicate730 ;; type731 (when a732 ;;XXX hack, again:733 (let ((at (refine-types a `(not ,pt))))734 (when at735 (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 (a739 (when enforces740 (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 ctags747 (add-to-blist var (car ctags) ar)748 (add-to-blist var (cdr ctags) ar)))))749 ((and oparg?750 (variable-mark751 var752 '##compiler#special-result-type))753 => (lambda (srt)754 (dd " hardcoded special result-type: ~a" var)755 (set! r (srt n args loc r))))))))756 subs757 (cons758 fn759 (nth-value760 0761 (procedure-argument-types fn (sub1 len) typeenv))))762 (smash)763 (if (eq? '* r)764 r765 (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 (else772 (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 1784 (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 exp790 (mutate-node! n (car subs))791 (walk n e loc dest flow ctags))792 (begin793 (trail-restore trail0 typeenv)794 (loop (cdr types) (cdr subs)))))))))795 ((##core#switch ##core#cond)796 (bomb "scrutinize: unexpected node class" class))797 (else798 (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)))803804 (let ((rn (walk (first (node-subexpressions node)) '() '() #f (list (tag)) #f)))805 (when (pair? specialization-statistics)806 (with-debugging-output807 '(o e)808 (lambda ()809 (print "specializations:")810 (for-each811 (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 errors822 (quit-compiling "some variable types do not satisfy strictness"))823 rn)))824825826;;; replace pair/vector types with components to variants with undetermined827;; component types (used for env or blist); also convert "list[-of]" types828;; into "pair", since mutation may take place829830(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) <>))))))))858859860;;; blist (binding list) helpers861;;862;; - Entries (ble) in blist have type ((symbol . fixnum) . type)863864(define ble-id caar) ; variable name : symbol865(define ble-tag cdar) ; block tag : fixnum866(define ble-type cdr) ; variable type : valid type sexp867(define ble-type-set! set-cdr!)868869870;;; Type-matching871;872; - "all" means: all elements in `or'-types in second argument must match873874(define (match-types t1 t2 #!optional (typeenv (type-typeenv `(or ,t1 ,t2))) all)875876 (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 opt2881 (null? args2)882 (optargs? (car args2))))883 ((null? args2)884 (or opt1885 (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))))897898 (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 (every901 (lambda (t)902 (or (eq? '#!optional t)903 (match1 rtype t)))904 head)905 (match1 rtype (if (pair? tail) (rest-type (cdr tail)) '*)))))906907 (define (optargs? a)908 (memq a '(#!rest #!optional)))909910 (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)))920921 (define (rawmatch1 t1 t2)922 (fluid-let ((all #f))923 (match1 t1 t2)))924925 (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))))930931 (define (match1 t1 t2)932 ;; note: the order of determining the type is important933 (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)) ; constraint941 (rawmatch1 (third e) t2))))942 ;; special case for two unbound typevars943 ((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)) ; constraint951 (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)) ; constraint964 (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 t1995 ((and (pair? t2) (eq? 'or (car t2)))996 (over-all-instantiations997 (cdr t2)998 typeenv999 all1000 (lambda (t) (match1 t1 t))))1001 ;; s.a.1002 ((and (pair? t1) (eq? 'or (car t1)))1003 (over-all-instantiations1004 (cdr t1)1005 typeenv1006 #f1007 (lambda (t) (match1 t t2)))) ; o-a-i ensures at least one element matches1008 ((and (pair? t1) (eq? 'forall (car t1)))1009 (match1 (third t1) t2)) ; assumes typeenv has already been extracted1010 ((and (pair? t2) (eq? 'forall (car t2)))1011 (match1 t1 (third t2))) ; assumes typeenv has already been extracted1012 ((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 'null1059 `(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 'null1073 `(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-instantiations1080 (cdr t1)1081 typeenv1082 #t1083 (cute match1 <> (second t2)))))1084 ((and (pair? t1) (eq? 'list-of (car t1)))1085 (and (pair? t2) (eq? 'list (car t2))1086 (over-all-instantiations1087 (cdr t2)1088 typeenv1089 #t1090 (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-instantiations1095 (cdr t1)1096 typeenv1097 #t1098 (cute match1 <> (second t2)))))1099 ((and (pair? t1) (eq? 'vector-of (car t1)))1100 (and (pair? t2) (eq? 'vector (car t2))1101 (over-all-instantiations1102 (cdr t2)1103 typeenv1104 #t1105 (cute match1 (second t1) <>))))1106 (else #f)))11071108 (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))111211131114(define (match-argument-types typelist atypes typeenv)1115 ;; this doesn't need optional: it is only used for predicate- and specialization1116 ;; matching1117 (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 (every1123 (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))))112911301131;;; Simplify type specifier1132;1133; - coalesces "forall" and renames type-variables1134; - also removes unused typevars11351136(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/cc1143 (lambda (return)1144 (cond ((pair? t)1145 (case (car t)1146 ((forall)1147 (let ((typevars (second t)))1148 (set! typeenv1149 (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! constraints1155 (append (filter-map1156 (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 'procedure1171 (foldl1172 (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 (append1180 '(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 (else1190 (let* ((ts (append-map1191 (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 t1209 (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 (else1217 (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 'pair1223 (canonicalize-list-type1224 `(pair ,tcar ,tcdr)))))1225 ((vector-of)1226 (let ((t2 (simplify (second t))))1227 (if (eq? t2 '*)1228 'vector1229 `(,(car t) ,t2))))1230 ((list-of)1231 (let ((t2 (simplify (second t))))1232 (if (eq? t2 '*)1233 'list1234 `(,(car t) ,t2))))1235 ((list)1236 (if (null? (cdr t))1237 'null1238 `(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 (append1245 '(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! t21260 `(forall ,(filter-map1261 (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)))12721273(define (maybe-expand-type t)1274 (and (symbol? t)1275 (alist-ref t type-expansions eq?)))12761277;;; Merging types12781279(define (merge-argument-types ts1 ts2)1280 ;; this could be more elegantly done by combining non-matching arguments/llists1281 ;; into "(or (procedure ...) (procedure ...))" and then simplifying1282 (cond ((null? ts1)1283 (cond ((null? ts2) '())1284 ((memq (car ts2) '(#!rest #!optional)) ts2)1285 (else '(#!rest))))1286 ((null? ts2) '(#!rest)) ;XXX giving up1287 ((eq? '#!rest (car ts1))1288 (cond ((and (pair? ts2) (eq? '#!rest (car ts2)))1289 `(#!rest1290 ,(simplify-type1291 `(or ,(rest-type (cdr ts1))1292 ,(rest-type (cdr ts2))))))1293 (else '(#!rest)))) ;XXX giving up1294 ((eq? '#!optional (car ts1))1295 (cond ((and (pair? ts2) (eq? '#!optional (car ts2)))1296 `(#!optional1297 ,(simplify-type `(or ,(cadr ts1) ,(cadr ts2)))1298 ,@(merge-argument-types (cddr ts1) (cddr ts2))))1299 (else '(#!rest)))) ;XXX1300 ((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))))))13041305(define (merge-result-types ts11 ts21) ;XXX possibly overly conservative1306 (call/cc1307 (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)))))))))131313141315(define (compatible-types? t1 t2 #!optional (te (type-typeenv `(or ,t1 ,t2))))1316 (or (type<=? t1 t2 te)1317 (type<=? t2 t1 te)))13181319(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)))13231324(define (type<=? t1 t2 #!optional (te (type-typeenv `(or ,t1 ,t2))))1325 (with-trail-restore1326 te1327 (lambda ()1328 (match-types t2 t1 te #t))))13291330;;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;;13351336(define (refine-types t1 t2)13371338 (define (refine t1 t2 te)1339 (let loop ((t1 t1) (t2 t2))1340 (cond1341 ((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 (else1365 (type-min t1 t2 te)))))13661367 (let* ((te (type-typeenv `(or ,t1 ,t2)))1368 (rt (or (refine t1 t2 te) t2)))1369 (if (eq? rt t2)1370 rt1371 (simplify-type rt))))13721373;;; various operations on procedure types13741375(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)))))13831384(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))))13941395(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)))))14051406(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)))))14161417(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 (llist1425 ;; quite a mess1426 (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 norest1436 '()1437 (loop (cdr at) m #t) ))1438 ((eq? '#!rest (car at))1439 (cond (norest '())1440 (else1441 (set! vf (and (pair? (cdr at)) (eq? 'values (cadr at))))1442 (make-list m (rest-type (cdr at))))))1443 ((and opt (<= m 0)) '())1444 (else1445 (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 extracted1451 ((assq t typeenv) =>1452 (lambda (e)1453 (let ((t2 (second e)))1454 (if (and t2 (memq t2 done))1455 (loop1 '* done) ; circularity1456 (loop1 t2 (cons t done))))))1457 (else (values (make-list n '*) #f #t n)))))14581459(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/cc1465 (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 extracted1474 (else '*)))1475 (loop1 t))14761477(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))))14841485(define (rest-type r)1486 (cond ((null? r) '*)1487 ((eq? 'values (car r)) '*)1488 (else (car r))))14891490(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))))15021503(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)))))15101511;;; Refinement type helpers15121513(define (refinement-type? t)1514 (and (pair? t)1515 (case (first t)1516 ((refine) #t)1517 ((forall) (refinement-type? (third t)))1518 (else #f))))15191520;;; Type-environments and -variables15211522(define (make-tv sym)1523 (let* ((r (get sym '##core#tv-root))1524 ;; ##core#tv-root is a string to make this gensym fast1525 (new (gensym r)))1526 (put! new '##core#tv-root r)1527 new))15281529(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 (else1542 (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))15561557(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))))15621563(define (with-trail-restore typeenv thunk)1564 (let* ((trail0 trail)1565 (result (thunk)))1566 (trail-restore trail0 typeenv)1567 result))15681569(define (resolve t typeenv)1570 (simplify-type ;XXX do only when necessary1571 (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 reference1578 (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 t1585 (bomb "resolve: can't resolve unknown type-variable" t)))1586 (else1587 (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 `(procedure1599 ,@(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 args1605 (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)))))))161316141615;;; type-db processing16161617(define (load-type-database name specialize #!optional1618 (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-each1625 (lambda (e)1626 (let* ((name (car e))1627 (old (variable-mark name '##compiler#type))1628 (specs (and (pair? (cddr e)) (cddr e)))1629 (new1630 (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 (else1653 (bomb1654 "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 considered1662 ;; correct, because type variables have to be renamed:1663 (let-values (((t pred pure) (validate-type new name)))1664 (unless t1665 (warning1666 (sprintf "Invalid type specification for `~a':~%~%~a"1667 name1668 (type->pp-string new))))1669 (when (and old (not (compatible-types? old t)))1670 (warning1671 (sprintf1672 (string-append1673 "Declared type for toplevel binding `~a'"1674 "~%~%~a~%~%"1675 " conflicts with previously loaded type:"1676 "~%~%~a")1677 name1678 (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 specs1683 (install-specializations name specs)))))1684 (call-with-input-file dbfile read-expressions))1685 #t)))16861687(define (hash-table->list ht)1688 (let ((len (vector-length ht)))1689 (let loop1 ((i 0) (lst '()))1690 (if (>= i len)1691 lst1692 (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))))))))16981699(define (symbol<? s1 s2)1700 (string<? (symbol->string s1)1701 (symbol->string s2)))17021703(define (emit-types-file source-file types-file db block-compilation)1704 (with-output-to-file types-file1705 (lambda ()1706 (print "; GENERATED BY CHICKEN " (chicken-version) " FROM "1707 source-file "\n")1708 (for-each1709 (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* sym1722 (let wrap ((type type))1723 (if (pair? type)1724 (case (car type)1725 ((procedure)1726 `(#(procedure1727 ,@(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"))))17421743;;1744;; Source node tracking1745;;1746;; Nodes are mutated in place during specialization, which may lose line1747;; number information if, for example, a node is changed from a1748;; ##core#call to a class without debug info. To preserve line numbers1749;; and allow us to print fragments of the original source, we maintain a1750;; side table of mappings from mutated nodes to copies of the originals.1751;;17521753(define node-mutations '())17541755(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))17581759(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))))17621763(define (source-node-tree n)1764 (source-node1765 n1766 (lambda (n*)1767 (make-node (node-class n*)1768 (node-parameters n*)1769 (map source-node-tree (node-subexpressions n*))))))17701771(define (node-line-number n)1772 (node-debug-info (source-node n)))17731774(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)))17841785;; Mutate node for specialization17861787(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 (else1800 (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 constants1810 (else (cons (subst (car x)) (subst (cdr x))))))1811 (mutate-node! node (subst template))))181218131814;;; Type-validation and -normalization18151816(define (validate-type type name)1817 ;; - returns converted type or #f1818 ;; - also converts "(... -> ...)" types1819 ;; - converts some typenames to struct types (u32vector, etc.)1820 ;; - handles some type aliases1821 ;; - drops "#!key ..." args by converting to #!rest1822 ;; - replaces uses of "&rest"/"&optional" with "#!rest"/"#!optional"1823 ;; - handles "(T1 -> T2 : T3)" (predicate)1824 ;; - handles "(T1 --> T2 [: T3])" (clean)1825 ;; - simplifies result1826 ;; - coalesces all "forall" forms into one (remove "forall" if typevar-set is empty)1827 ;; - renames type-variables1828 ;; - replaces type-abbreviations1829 (let ((ptype #f) ; (T . PT) | #f1830 (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 list1838 (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 (else1855 (let ((l1 (validate (cadr llist))))1856 (and l1 `(#!rest ,l1))))))1857 ((eq? '#!key (car llist)) '(#!rest *))1858 (else1859 (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/cc1882 (lambda (return)1883 (set! typevars1884 (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! constraints1894 (append (filter-map1895 (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 ok1938 (validate1939 `(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" predicate1946 ;; procedure type1947 (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 ts1976 (every identity ts)1977 (let* ((rt2 (cdr t2))1978 (rt (if (eq? '* rt2)1979 rt21980 (and (list? rt2)1981 (let ((rts (map validate rt2)))1982 (and (every identity rts)1983 rts))))))1984 (and rt1985 `(procedure1986 ,@(if (and name (not rec)) (list name) '())1987 ,ts1988 ,@rt)))))))))1989 (else #f)))1990 (cond ((validate type #f) =>1991 (lambda (type)1992 (when (pair? typevars)1993 (set! type1994 `(forall1995 ,(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 (values2003 type22004 (and ptype (eq? (car ptype) type) (cdr ptype))2005 clean))))2006 (else (values #f #f #f)))))20072008(define (check-and-validate-type type loc #!optional name)2009 (let-values (((t pred pure) (validate-type (strip-syntax type) name)))2010 (or t2011 (error loc "invalid type specifier" type))))20122013(define (install-specializations name specs)2014 (define (fail spec)2015 (error "invalid specialization format" spec name))2016 (mark-variable2017 name '##compiler#specializations2018 ;;XXX it would be great if result types could refer to typevars2019 ;; 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 from2025 ;; the thunk to the enclosing expression. Unfortunately the simplification in2026 ;; the first validation renames typevars, so the second validation will have2027 ;; non-matching names.2028 (map (lambda (spec)2029 (if (and (list? spec) (list? (first spec)))2030 (let* ((args2031 (map (lambda (t)2032 (let-values (((t2 pred pure) (validate-type t #f)))2033 (or t22034 (error "invalid argument type in specialization"2035 t spec name))))2036 (first spec)))2037 (typevars (unzip1 (append-map type-typeenv args))))2038 (cons2039 args2040 (case (length spec)2041 ((2) (cdr spec))2042 ((3)2043 (cond ((list? (second spec))2044 (cons2045 (map (lambda (t)2046 (let-values (((t2 pred pure) (validate-type t #f)))2047 (or t22048 (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)))205720582059;;; Canonicalize complex pair/list type for matching with "list-of"2060;2061; Returns an equivalent (list ...) form, or the original argument if no2062; canonicalization could be done.20632064(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)))207820792080;;; Drop namespace from module-prefixed symbol:20812082(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)))))))209020912092;;; hardcoded result types for certain primitives20932094(define-syntax define-special-case2095 (syntax-rules ()2096 ((_ name handler)2097 (##sys#put! 'name '##compiler#special-result-type handler))))20982099(define-special-case ##sys#make-structure2100 (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 distinct2108 ;; "pointer-vector" type.2109 (if (eq? 'pointer-vector val)2110 '(pointer-vector)2111 `((struct ,(strip-namespace val)))))2112 rtypes)))21132114(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 otherwise2125 (vector-length (length (cdr arg1))))2126 (if (and (>= val 0) (< val vector-length))2127 val2128 (begin2129 (r-index-out-of-range loc node val vector-length "vector")2130 #f))))21312132 ;; These are a bit hacky, since they mutate the node. These special2133 ;; 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))21412142 (define-special-case scheme#vector-ref vector-ref-result-type)2143 (define-special-case ##sys#vector-ref vector-ref-result-type)21442145 (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#setislot2154 '##sys#setslot)))2155 (mutate-node! node `(,setter ,vector ',index ,new-value))2156 '(undefined))2157 rtypes))))21582159;; TODO: Also special-case vector-length? Makes little sense though.216021612162;;; List-related special cases2163;2164; Preserve known element types for:2165;2166; list-ref, list-tail21672168(let ()2169 (define (list-or-null a)2170 (if (null? a) 'null `(list ,@a)))21712172 ;; Split a list or pair type form at index i, calling k with the two2173 ;; 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.db2175 (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)))21942195 ;; canonicalize-list-type will have taken care of converting (pair2196 ;; (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)))22012202 (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 otherwise2211 (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 avoids2216 ;; 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)))22232224 (define-special-case scheme#list-ref2225 (list+index-call-result-type-special-case2226 (lambda (_ result-type)2227 (and (pair? result-type)2228 (list (cadr result-type))))))22292230 (define-special-case scheme#list-tail2231 (list+index-call-result-type-special-case2232 (lambda (_ result-type) (list result-type)))))22332234(define-special-case scheme#list2235 (lambda (node args loc rtypes)2236 (if (null? (cdr args))2237 '(null)2238 `((list ,@(map walked-result (cdr args)))))))22392240(define-special-case ##sys#list2241 (lambda (node args loc rtypes)2242 (if (null? (cdr args))2243 '(null)2244 `((list ,@(map walked-result (cdr args)))))))22452246(define-special-case scheme#vector2247 (lambda (node args loc rtypes)2248 `((vector ,@(map walked-result (cdr args))))))22492250(define-special-case ##sys#vector2251 (lambda (node args loc rtypes)2252 `((vector ,@(map walked-result (cdr args))))))22532254(define-special-case scheme#reverse2255 (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)))22632264(let ()2265 (define (append-special-case node args loc rtypes)2266 (define (potentially-proper-list? l) (match-types l 'list '()))22672268 (define (derive-result-type)2269 (let lp ((args (cdr args))2270 (index 1))2271 (if (null? args)2272 'null2273 (let* ((arg1 (car args))2274 (arg1-t (walked-result arg1)))2275 (cond2276 ((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 it2279 ;; easy to append a variety of types.2280 (canonicalize-list-type2281 (foldl (lambda (rest t) `(pair ,t ,rest))2282 rest-t (reverse (cdr arg1-t))))))22832284 ((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))))22882289 ;; TODO: (append (pair x (pair y z)) lst) =>2290 ;; (pair x (pair y (or z lst)))2291 ;; This is trickier than it sounds!22922293 (else2294 ;; The final argument may be an atom or improper list2295 (unless (or (null? (cdr args))2296 (potentially-proper-list? arg1-t))2297 (r-proc-call-argument-type-mismatch2298 loc node index arg1 'list arg1-t2299 (variable-mark 'scheme#append '##compiler#type)))2300 #f))))))2301 (cond ((derive-result-type) => list)2302 (else rtypes)))23032304 (define-special-case scheme#append append-special-case)2305 (define-special-case ##sys#append append-special-case))23062307;;; Special cases for make-list/make-vector with a known size2308;2309; e.g. (make-list 3 #\a) => (list char char char)23102311(let ()23122313 (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)))23272328 (define-special-case scheme#make-vector2329 (complex-object-constructor-result-type-special-case 'vector)))233023312332;;; perform check over all typevar instantiations2333;2334; If "all" is #t all types in tlist must match, if #f then one or more.23352336(define (over-all-instantiations tlist typeenv all process)2337 (let ((insts '())2338 (anyinst #f)2339 (trail0 trail))23402341 ;; restore trail and collect instantiations2342 (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-cons2351 (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)))))23572358 ;; collect candidates for each typevar2359 (define (collect)2360 (let* ((vars (delete-duplicates (concatenate (map unzip1 insts)) eq?))2361 (all (map (lambda (var)2362 (cons2363 var2364 (filter-map2365 (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))23742375 (ddd " over-all-instantiations: ~s all: ~a" tlist all)2376 ;; process all tlist elements2377 (let loop ((ts (delete-duplicates tlist eq?))2378 (ok #f))2379 (cond ((null? ts)2380 (cond ((or ok (null? tlist))2381 (for-each2382 (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 (all2393 (restore)2394 #f)2395 (else2396 (restore)2397 (loop (cdr ts) ok))))))23982399;;; Report helpers24002401(define (multiples n)2402 (if (= n 1) "" "s"))24032404(define (string-add-indent str #!optional (indent " "))2405 (let* ((ls (string-split str "\n" #t))2406 (s (string-intersperse2407 (map (lambda (l)2408 (if (string=? "" l)2409 l2410 (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)))24162417(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 (cond2425 ((not (pair? t))2426 (or (alist-ref t tv-replacements eq?) t))2427 ((refinement-type? t)2428 (string->symbol2429 (sprintf "~a-~a" (string-intersperse (map conc (second t)) "/") (third t))))2430 (else2431 (let ((tcar (and (pair? t) (car t))))2432 (cond2433 ((and (eq? 'forall tcar) (every symbol? (second t))) ; no constraints2434 (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 nothing2437 ((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 #f2445 (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-indent2455 (string-chomp2456 (with-output-to-string2457 (lambda () (pp t*)))))))24582459(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))))))24712472(define (pp-fragment x)2473 (string-add-indent2474 (string-chomp2475 (with-output-to-string2476 (lambda ()2477 (pp (fragment x)))))))24782479(define (node-source-prefix n)2480 (let ((line (node-line-number n)))2481 (if (not line) "" (sprintf "In file `~a'," line))))24822483(define (location-name loc #!optional (indent " "))2484 (define (lname loc1)2485 (if loc12486 (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-intersperse2494 (cons (if (car loc)2495 ;; If the first location is of format 'bar#foo'2496 ;; consider it as being being procedure 'foo' in2497 ;; 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))))))25052506(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))))25122513(define (variable-from-module sym)2514 (receive (var mod) (variable-and-module sym)2515 (if mod2516 (sprintf "`~a' from module `~a'" var mod)2517 (sprintf "`~a'" var))))25182519(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-append2530 "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 through2539 (p-node (first (node-subexpressions n))))2540 (else (p-expr n))))2541 (p-node (source-node-tree node)))25422543(define (call-node-procedure-name node)2544 (fragment (first (node-subexpressions node))))25452546(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-f2553 (conc2554 short2555 (string-add-indent2556 (conc (let ((l (file-location))) (if l (conc "\n" l) "")) "\n"2557 (location-name loc "")2558 (sprintf "~?" msg args))2559 " ")))2560 (flush-output)))25612562(define (report-notice reason location-node-candidates loc msg . args)2563 (apply report2 reason ##sys#notice location-node-candidates loc msg args))25642565;;; Reports25662567(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 (report22575 "Invalid procedure"2576 warning2577 (list p-node call-node)2578 loc2579 (string-append2580 "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 (report22593 "Invalid procedure"2594 warning2595 (list p-node call-node)2596 loc2597 (string-append2598 "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))))26172618(define (r-proc-call-argument-count-mismatch loc node exp-count argc ptype)2619 (define pname (call-node-procedure-name node))2620 (report22621 "Wrong number of arguments"2622 warning2623 (list node)2624 loc2625 (string-append2626 "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)))26422643(define (r-proc-call-argument-type-mismatch loc node i arg-node xptype atype ptype)2644 (define pname (call-node-procedure-name node))2645 (report22646 "Invalid argument"2647 warning2648 (list node)2649 loc2650 (string-append2651 "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 i2670 (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)))26762677(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 (report22681 short2682 warning2683 (list arg-node call-node)2684 loc2685 (string-append2686 "In procedure call:"2687 "~%~%"2688 "~a"2689 "~%~%"2690 "Argument #~a to procedure~a ~a"2691 "~%~%"2692 "~a")2693 (pp-fragment call-node)2694 i2695 (if (zero? i) "" (sprintf " `~a'" (strip-namespace pname)))2696 long2697 (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)))))27032704(define (r-index-out-of-range loc node idx obj-length obj-name)2705 ;; Negative indices should always generate a warning2706 (define pname (call-node-procedure-name node))2707 (report22708 (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 warning2714 (list node)2715 loc2716 (string-append2717 "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))))27272728(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 loc2732 (string-append2733 "In conditional:"2734 "~%~%"2735 "~a"2736 "~%~%"2737 "The test expression ~a"2738 "~%~%"2739 "~a")2740 (pp-fragment if-node)2741 long2742 (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)))))27482749(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 loc2753 (string-append2754 "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 long2764 (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)))))27702771(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 loc2775 (string-append2776 "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 long2786 (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)))))27922793(define (r-pred-call-always-true loc node pred-type atype)2794 (define pname (call-node-procedure-name node))2795 (report-notice2796 "Predicate is always true"2797 (list node)2798 loc2799 (string-append2800 "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)))28172818(define (r-pred-call-always-false loc node pred-type atype)2819 (define pname (call-node-procedure-name node))2820 (report-notice2821 "Predicate is always false"2822 (list node)2823 loc2824 (string-append2825 "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)))28422843(define (r-cond-test-always-true loc if-node test-node t)2844 (report-notice2845 "Test is always true"2846 (list test-node if-node)2847 loc2848 (string-append2849 "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)))28582859(define (r-cond-test-always-false loc if-node test-node)2860 (report-notice2861 "Test is always false"2862 (list test-node if-node)2863 loc2864 (string-append2865 "In conditional expression:"2866 "~%~%"2867 "~a"2868 "~%~%"2869 "Test condition is always false.")2870 (pp-fragment if-node)))28712872(define (r-zero-values-for-the loc node the-type)2873 ;; (the t r) expects r returns exactly 1 value2874 (report22875 "Not enough values"2876 warning2877 (list node)2878 loc2879 (string-append2880 "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)))28892890(define (r-too-many-values-for-the loc node the-type rtypes)2891 (report22892 "Too many values"2893 warning2894 (list node)2895 loc2896 (string-append2897 "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)))29092910(define (r-type-mismatch-in-the loc node atype the-type)2911 (report22912 "Type mismatch"2913 warning2914 (list node)2915 loc2916 (string-append2917 "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)))29332934(define (fail-compiler-typecase loc node atype ct-types)2935 (define (pp-type t) (string-add-indent (type->pp-string t) " "))2936 (quit-compiling2937 (string-append2938 "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")))29622963(define (r-cond-branch-value-count-mismatch loc node c-node a-node c-types a-types)2964 (report22965 "Branch values mismatch"2966 warning2967 (list a-node node)2968 loc2969 (string-append2970 "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)))29882989(define (r-toplevel-var-assignment-type-mismatch loc node atype var xptype value-node)2990 (report22991 "Invalid assignment"2992 warning2993 (list node value-node)2994 loc2995 (string-append2996 "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-module3013 (let ((n (real-name var)))3014 (if (symbol? n) n (string->symbol n))))3015 (type->pp-string xptype)))30163017(define (r-deprecated-identifier loc node id #!optional suggestion)3018 (report23019 (sprintf "Deprecated identifier `~a'" (strip-namespace id))3020 warning3021 (list node)3022 loc3023 (string-append3024 "In expression:"3025 "~%~%"3026 "~a"3027 "~%~%"3028 "Use of deprecated identifier ~a."3029 "~a")3030 (pp-fragment node) ;; TODO: parent node would be nice here3031 (variable-from-module id)3032 (if suggestion3033 (sprintf "~%~%The suggested alternative is ~a."3034 (variable-from-module suggestion))3035 "")))3036)