~ chicken-core (chicken-5) 88fe335a357ca000b1f27e2a58ccb398d2e4cc1f
commit 88fe335a357ca000b1f27e2a58ccb398d2e4cc1f Author: felix <felix@z.(none)> AuthorDate: Wed Mar 30 19:35:53 2011 +0200 Commit: felix <felix@z.(none)> CommitDate: Wed Mar 30 19:35:53 2011 +0200 use more specific existing type when assuming diff --git a/scrutinizer.scm b/scrutinizer.scm index bd0f1e61..6f6c1a4d 100755 --- a/scrutinizer.scm +++ b/scrutinizer.scm @@ -110,11 +110,13 @@ (cond ((##sys#get id '##compiler#type) => (lambda (a) (cond - #;((and (get db id 'assigned) ; remove assigned global from type db + #| + ((and (get db id 'assigned) ; remove assigned global from type db (not (##sys#get id '##compiler#declared-type))) - (##sys#put! id '##compiler#type #f) - '(*)) - ((eq? a 'deprecated) + (##sys#put! id '##compiler#type #f) + '(*)) + |# + ((eq? a 'deprecated) (report loc (sprintf "use of deprecated library procedure `~a'" id) ) @@ -127,640 +129,643 @@ id (cadr a))) '(*)) (else (list a))))) - (else '(*)))) - (define (variable-result id e loc flow) - (cond ((find (lambda (b) - (and (eq? id (caar b)) - (memq (cdar b) flow)) ) - blist) - => (o list cdr)) - ((and (get db id 'assigned) - (not (##sys#get id '##compiler#declared-type))) - '(*)) - ((assq id e) => - (lambda (a) - (cond ((eq? 'undefined (cdr a)) - (report - loc - (sprintf "access to variable `~a' which has an undefined value" - (real-name id db))) - '(*)) - (else (list (cdr a)))))) - (else (global-result id loc)))) - (define (always-true1 t) - (cond ((and (pair? t) (eq? 'or (car t))) - (every always-true1 (cdr t))) - ((memq t '(* boolean undefined noreturn)) #f) - (else #t))) - (define (always-true t loc x) - (let ((f (always-true1 t))) - (when f - (report - loc - (sprintf - "expected value of type boolean in conditional but were given a value of\ntype `~a' which is always true:~%~%~a" - t - (pp-fragment x)))) - f)) - (define (typename t) - (case t - ((*) "anything") - ((char) "character") - (else - (cond ((symbol? t) (symbol->string t)) - ((pair? t) - (case (car t) - ((procedure) - (if (or (string? (cadr t)) (symbol? (cadr t))) - (->string (cadr t)) - (sprintf "a procedure with ~a returning ~a" - (argument-string (cadr t)) - (result-string (cddr t))))) - ((or) - (string-intersperse - (map typename (cdr t)) - " OR ")) - ((struct) - (sprintf "a structure of type ~a" (cadr t))) - (else (bomb "invalid type: ~a" t)))) - (else (bomb "invalid type: ~a" t)))))) - (define (argument-string args) - (let* ((len (length args)) - (m (multiples len))) - (if (zero? len) - "zero arguments" - (sprintf - "~a argument~a of type~a ~a" - len m m - (map typename args))))) - (define (result-string results) - (if (eq? '* results) - "an unknown number of values" - (let* ((len (length results)) - (m (multiples len))) - (if (zero? len) - "zero values" - (sprintf - "~a value~a of type~a ~a" - len m m - (map typename results)))))) - (define (simplify t) - (let ((t2 (simplify1 t))) - (d "simplify: ~a -> ~a" t t2) - t2)) - (define (simplify1 t) - (call/cc - (lambda (return) - (if (pair? t) - (case (car t) - ((or) - (cond ((= 2 (length t)) (simplify (second t))) - ((every procedure-type? (cdr t)) - (if (any (cut eq? 'procedure <>) (cdr t)) - 'procedure - (reduce - (lambda (t pt) - (let* ((name1 (and (named? t) (cadr t))) - (atypes1 (if name1 (third t) (second t))) - (rtypes1 (if name1 (cdddr t) (cddr t))) - (name2 (and (named? pt) (cadr pt))) - (atypes2 (if name2 (third pt) (second pt))) - (rtypes2 (if name2 (cdddr pt) (cddr pt)))) - (append - '(procedure) - (if (and name1 name2 (eq? name1 name2)) (list name1) '()) - (list (merge-argument-types atypes1 atypes2)) - (merge-result-types rtypes1 rtypes2)))) - #f - (cdr t)))) - (else - (let* ((ts (append-map - (lambda (t) - (let ((t (simplify t))) - (cond ((and (pair? t) (eq? 'or (car t))) - (cdr t)) - ;((eq? t 'noreturn) '()) - ((eq? t 'undefined) (return 'undefined)) - (else (list t))))) - (cdr t))) - (ts2 (let loop ((ts ts) (done '())) - (cond ((null? ts) (reverse done)) - ((eq? '* (car ts)) (return '*)) - ((any (cut type<=? (car ts) <>) (cdr ts)) - (loop (cdr ts) done)) - ((any (cut type<=? (car ts) <>) done) - (loop (cdr ts) done)) - (else (loop (cdr ts) (cons (car ts) done))))))) - (cond ((equal? ts2 (cdr t)) t) - (else - (d " or-simplify: ~a" ts2) - (simplify `(or ,@(if (any (cut eq? <> '*) ts2) '(*) ts2)))))))) ) - ((procedure) - (let* ((name (and (named? t) (cadr t))) - (rtypes (if name (cdddr t) (cddr t)))) - (append - '(procedure) - (if name (list name) '()) - (list (map simplify (if name (third t) (second t)))) - (if (eq? '* rtypes) - '* - (map simplify rtypes))))) - (else t)) - t)))) - (define (named? t) - (and (pair? t) - (eq? 'procedure (car t)) - (not (or (null? (cadr t)) (pair? (cadr t)))))) - (define (rest-type r) - (cond ((null? r) '*) - ((eq? 'values (car r)) '*) - (else (car r)))) - (define (merge-argument-types ts1 ts2) - (cond ((null? ts1) - (cond ((null? ts2) '()) - ((memq (car ts2) '(#!rest #!optional)) ts2) - (else '(#!rest)))) - ((eq? '#!rest (car ts1)) - (cond ((and (pair? ts2) (eq? '#!rest (car ts2))) - `(#!rest - ,(simplify - `(or ,(rest-type (cdr ts1)) - ,(rest-type (cdr ts2)))))) - (else '(#!rest)))) ;XXX giving up - ((eq? '#!optional (car ts1)) - (cond ((and (pair? ts2) (eq? '#!optional (car ts2))) - `(#!optional - ,(simplify `(or ,(cadr ts1) ,(cadr ts2))) - ,@(merge-argument-types (cddr ts1) (cddr ts2)))) - (else '(#!rest)))) ;XXX - (else (cons (simplify `(or ,(car ts1) ,(car ts2))) - (merge-argument-types (cdr ts1) (cdr ts2)))))) - (define (merge-result-types ts1 ts2) ;XXX possibly overly conservative - (cond ((null? ts1) ts2) - ((null? ts2) ts1) - ((or (atom? ts1) (atom? ts2)) '*) - (else (cons (simplify `(or ,(car ts1) ,(car ts2))) - (merge-result-types (cdr ts1) (cdr ts2)))))) - (define (match t1 t2) - (let ((m (match1 t1 t2))) - (d "match ~a <-> ~a -> ~a" t1 t2 m) - m)) - (define (match1 t1 t2) - (cond ((eq? t1 t2)) - ((eq? t1 '*)) - ((eq? t2 '*)) - ((eq? t1 'noreturn)) - ((eq? t2 'noreturn)) - ((and (eq? t1 'number) (memq t2 '(number fixnum float)))) - ((and (eq? t2 'number) (memq t1 '(number fixnum float)))) - ((eq? 'procedure t1) (and (pair? t2) (eq? 'procedure (car t2)))) - ((eq? 'procedure t2) (and (pair? t1) (eq? 'procedure (car t1)))) - ((and (pair? t1) (eq? 'or (car t1))) (any (cut match <> t2) (cdr t1))) - ((and (pair? t2) (eq? 'or (car t2))) (any (cut match t1 <>) (cdr t2))) - ((memq t1 '(pair list)) (memq t2 '(pair list))) - ((memq t1 '(null list)) (memq t2 '(null list))) - ((and (pair? t1) (pair? t2) (eq? (car t1) (car t2))) - (case (car t1) - ((procedure) - (let ((args1 (if (named? t1) (third t1) (second t1))) - (args2 (if (named? t2) (third t2) (second t2))) - (results1 (if (named? t1) (cdddr t1) (cddr t1))) - (results2 (if (named? t2) (cdddr t2) (cddr t2))) ) - (and (match-args args1 args2) - (match-results results1 results2)))) - ((struct) (equal? t1 t2)) - (else #f) ) ) - (else #f))) - (define (match-args args1 args2) - (d "match-args: ~s <-> ~s" args1 args2) - (define (match-rest rtype args opt) ;XXX currently ignores `opt' - (let-values (((head tail) (break (cut eq? '#!rest <>) args))) - (and (every (cut match rtype <>) head) ; match required args - (match rtype (if (pair? tail) (rest-type (cdr tail)) '*))))) - (define (optargs a) - (memq a '(#!rest #!optional))) - (let loop ((args1 args1) (args2 args2) (opt1 #f) (opt2 #f)) - (d " args ~a ~a ~a ~a" args1 args2 opt1 opt2) - (cond ((null? args1) - (or opt2 - (null? args2) - (optargs (car args2)))) - ((null? args2) - (or opt1 - (optargs (car args1)))) - ((eq? '#!optional (car args1)) - (loop (cdr args1) args2 #t opt2)) - ((eq? '#!optional (car args2)) - (loop args1 (cdr args2) opt1 #t)) - ((eq? '#!rest (car args1)) - (match-rest (rest-type (cdr args1)) args2 opt2)) - ((eq? '#!rest (car args2)) - (match-rest (rest-type (cdr args2)) args1 opt1)) - ((match (car args1) (car args2)) (loop (cdr args1) (cdr args2) opt1 opt2)) - (else #f)))) - (define (match-results results1 results2) - (cond ((null? results1) (atom? results2)) - ((eq? '* results1)) - ((eq? '* results2)) - ((null? results2) #f) - ((match (car results1) (car results2)) - (match-results (cdr results1) (cdr results2))) - (else #f))) - (define (type<=? t1 t2) - (or (eq? t1 t2) - (memq t2 '(* undefined)) - (case t2 - ((list) (memq t1 '(null pair))) - ((procedure) (and (pair? t1) (eq? 'procedure (car t1)))) - ((number) (memq t1 '(fixnum float))) - (else - (and (pair? t1) (pair? t2) - (case (car t1) - ((or) (every (cut type<=? <> t2) (cdr t1))) - ((procedure) - (let ((args1 (if (named? t1) (caddr t1) (cadr t1))) - (args2 (if (named? t2) (caddr t2) (cadr t2))) - (res1 (if (named? t1) (cdddr t1) (cddr t1))) - (res2 (if (named? t2) (cdddr t2) (cddr t2))) ) - (let loop1 ((args1 args1) - (args2 args2) - (m1 0) - (m2 0)) - (cond ((null? args1) - (and (or (null? args2) (> m2 0)) - (let loop2 ((res1 res1) (res2 res2)) - (cond ((eq? '* res2) #t) - ((null? res2) (null? res1)) - ((eq? '* res1) #f) - ((type<=? (car res1) (car res2)) - (loop2 (cdr res1) (cdr res2))) - (else #f))))) - ((null? args2) #f) - ((eq? (car args1) '#!optional) - (loop1 (cdr args1) args2 1 m2)) - ((eq? (car args2) '#!optional) - (loop1 args1 (cdr args2) m1 1)) - ((eq? (car args1) '#!rest) - (loop1 (cdr args1) args2 2 m2)) - ((eq? (car args2) '#!rest) - (loop1 args1 (cdr args2) m1 2)) - ((type<=? (car args1) (car args2)) - (loop1 (cdr args1) (cdr args2) m1 m2)) - (else #f))))))))))) - (define (most-specialized-type t1 t2) - (if (type<=? t1 t2) - t1 + (else '(*)))) + (define (blist-type id flow) + (cond ((find (lambda (b) + (and (eq? id (caar b)) + (memq (cdar b) flow)) ) + blist) + => (o list cdr)) + (else #f))) + (define (variable-result id e loc flow) + (cond ((vblist-type id flow)) + ((and (get db id 'assigned) + (not (##sys#get id '##compiler#declared-type))) + '(*)) + ((assq id e) => + (lambda (a) + (cond ((eq? 'undefined (cdr a)) + (report + loc + (sprintf "access to variable `~a' which has an undefined value" + (real-name id db))) + '(*)) + (else (list (cdr a)))))) + (else (global-result id loc)))) + (define (always-true1 t) + (cond ((and (pair? t) (eq? 'or (car t))) + (every always-true1 (cdr t))) + ((memq t '(* boolean undefined noreturn)) #f) + (else #t))) + (define (always-true t loc x) + (let ((f (always-true1 t))) + (when f + (report + loc + (sprintf + "expected value of type boolean in conditional but were given a value of\ntype `~a' which is always true:~%~%~a" + t + (pp-fragment x)))) + f)) + (define (typename t) + (case t + ((*) "anything") + ((char) "character") + (else + (cond ((symbol? t) (symbol->string t)) + ((pair? t) + (case (car t) + ((procedure) + (if (or (string? (cadr t)) (symbol? (cadr t))) + (->string (cadr t)) + (sprintf "a procedure with ~a returning ~a" + (argument-string (cadr t)) + (result-string (cddr t))))) + ((or) + (string-intersperse + (map typename (cdr t)) + " OR ")) + ((struct) + (sprintf "a structure of type ~a" (cadr t))) + (else (bomb "invalid type: ~a" t)))) + (else (bomb "invalid type: ~a" t)))))) + (define (argument-string args) + (let* ((len (length args)) + (m (multiples len))) + (if (zero? len) + "zero arguments" + (sprintf + "~a argument~a of type~a ~a" + len m m + (map typename args))))) + (define (result-string results) + (if (eq? '* results) + "an unknown number of values" + (let* ((len (length results)) + (m (multiples len))) + (if (zero? len) + "zero values" + (sprintf + "~a value~a of type~a ~a" + len m m + (map typename results)))))) + (define (simplify t) + (let ((t2 (simplify1 t))) + (d "simplify: ~a -> ~a" t t2) t2)) - (define (multiples n) - (if (= n 1) "" "s")) - (define (single what tv loc) - (if (eq? '* tv) - '* - (let ((n (length tv))) - (cond ((= 1 n) (car tv)) - ((zero? n) - (report - loc - (sprintf "expected ~a a single result, but were given zero results" what)) - 'undefined) - (else - (report - loc - (sprintf "expected ~a a single result, but were given ~a result~a" - what n (multiples n))) - (first tv)))))) - (define (report loc desc) - (when complain - (warning - (conc (location-name loc) desc)))) - (define (location-name loc) - (define (lname loc1) - (if loc1 - (sprintf "procedure `~a'" (real-name loc1)) - "unknown procedure")) - (cond ((null? loc) "at toplevel:\n ") - ((null? (cdr loc)) - (sprintf "in toplevel ~a:\n " (lname (car loc)))) - (else - (let rec ((loc loc)) - (if (null? (cdr loc)) - (location-name loc) - (sprintf "in local ~a,\n ~a" (lname (car loc)) (rec (cdr loc)))))))) - (define add-loc cons) - (define (fragment x) - (let ((x (build-expression-tree x))) - (let walk ((x x) (d 0)) - (cond ((atom? x) x) - ((>= d +fragment-max-depth+) '...) - ((list? x) - (map (cute walk <> (add1 d)) (take x (min +fragment-max-length+ (length x))))) - (else x))))) - (define (pp-fragment x) - (string-chomp - (with-output-to-string - (lambda () - (pp (fragment x)))))) - (define (call-result node args e loc params) - (define (pname) - (sprintf "~ain procedure call to `~s', " - (if (and (pair? params) (pair? (cdr params))) - (let ((n (source-info->line (cadr params)))) - (if n - (sprintf "~a: " n) - "")) - "") - (fragment (first (node-subexpressions node))))) - (d "call-result: ~a " args) - (let* ((ptype (car args)) - (nargs (length (cdr args))) - (xptype `(procedure ,(make-list nargs '*) *))) - (when (and (not (procedure-type? ptype)) - (not (match xptype ptype))) - (report - loc - (sprintf - "~aexpected a value of type `~a', but was given a value of type `~a'" - (pname) - xptype - ptype))) - (let-values (((atypes values-rest) (procedure-argument-types ptype nargs))) - (d " argument-types: ~a (~a)" atypes values-rest) - (unless (= (length atypes) nargs) - (let ((alen (length atypes))) - (report - loc - (sprintf - "~aexpected ~a argument~a, but was given ~a argument~a" - (pname) alen (multiples alen) - nargs (multiples nargs))))) - (do ((args (cdr args) (cdr args)) - (atypes atypes (cdr atypes)) - (i 1 (add1 i))) - ((or (null? args) (null? atypes))) - (unless (match (car atypes) (car args)) - (report - loc - (sprintf - "~aexpected argument #~a of type `~a', but was given an argument of type `~a'" - (pname) i (car atypes) (car args))))) - (let ((r (procedure-result-types ptype values-rest (cdr args)))) - (d " result-types: ~a" r) - (when specialize - ;;XXX we should check whether this is a standard- or extended bindng - (let ((pn (procedure-name ptype)) - (op #f)) - (when pn - (cond ((and (fx= 1 nargs) - (##sys#get pn '##compiler#predicate)) => - (lambda (pt) - (cond ((match-specialization (list pt) (cdr args)) - (specialize-node! - node - `(let ((#:tmp #(1))) '#t)) - (set! op (list pn pt))) - ((match-specialization (list `(not ,pt)) (cdr args)) - (specialize-node! - node - `(let ((#:tmp #(1))) '#f)) - (set! op (list pt `(not ,pt))))))) - ((##sys#get pn '##compiler#specializations) => - (lambda (specs) - (for-each - (lambda (spec) - (when (match-specialization (car spec) (cdr args)) - (set! op (cons pn (car spec))) - (specialize-node! node (cadr spec)))) - specs)))) - (when op - (cond ((assoc op specialization-statistics) => - (lambda (a) (set-cdr! a (add1 (cdr a))))) - (else - (set! specialization-statistics - (cons (cons op 1) - specialization-statistics)))))))) - r)))) - (define (procedure-type? t) - (or (eq? 'procedure t) - (and (pair? t) - (or (eq? 'procedure (car t)) - (and (eq? 'or (car t)) - (every procedure-type? (cdr t))))))) - (define (procedure-name t) - (and (pair? t) - (eq? 'procedure (car t)) - (let ((n (cadr t))) - (cond ((string? n) (string->symbol n)) - ((symbol? n) n) - (else #f))))) - (define (procedure-argument-types t n) - (cond ((or (memq t '(* procedure)) - (not-pair? t) - (eq? 'deprecated (car t))) - (values (make-list n '*) #f)) - ((eq? 'procedure (car t)) - (let* ((vf #f) - (llist - (let loop ((at (if (or (string? (second t)) (symbol? (second t))) - (third t) - (second t))) - (m n) - (opt #f)) - (cond ((null? at) '()) - ((eq? '#!optional (car at)) - (loop (cdr at) m #t) ) - ((eq? '#!rest (car at)) - (set! vf (and (pair? (cdr at)) (eq? 'values (cadr at)))) - (make-list m (rest-type (cdr at)))) - ((and opt (<= m 0)) '()) - (else (cons (car at) (loop (cdr at) (sub1 m) opt))))))) - (values llist vf))) - (else (bomb "not a procedure type" t)))) - (define (procedure-result-types t values-rest? args) - (cond (values-rest? args) - ((or (memq t '(* procedure)) - (not-pair? t) ) - '*) - ((eq? 'procedure (car t)) - (call/cc - (lambda (return) - (let loop ((rt (if (or (string? (second t)) (symbol? (second t))) - (cdddr t) - (cddr t)))) - (cond ((null? rt) '()) - ((eq? '* rt) (return '*)) - (else (cons (car rt) (loop (cdr rt))))))))) - (else (bomb "not a procedure type: ~a" t)))) - (define (noreturn-type? t) - (or (eq? 'noreturn t) - (and (pair? t) - (eq? 'or (car t)) - (any noreturn-type? (cdr t))))) - (define (self-call? node loc) - (case (node-class node) - ((##core#call) - (and (pair? loc) - (let ((op (first (node-subexpressions node)))) - (and (eq? '##core#variable (node-class op)) - (eq? (car loc) (first (node-parameters op))))))) - ((let) - (self-call? (last (node-subexpressions node)) loc)) - (else #f))) - (define tag - (let ((n 0)) - (lambda () - (set! n (add1 n)) - n))) - (define (invalidate-blist) - (for-each - (lambda (b) - (when (get db (caar b) 'assigned) - (d "invalidating: ~a" b) - (set-cdr! b '*))) - blist)) - (define (walk n e loc dest tail flow ctags) ; returns result specifier - (let ((subs (node-subexpressions n)) - (params (node-parameters n)) - (class (node-class n)) ) - (d "walk: ~a ~a (loc: ~a, dest: ~a, tail: ~a, flow: ~a, blist: ~a, e: ~a)" - class params loc dest tail flow blist e) - (let ((results - (case class - ((quote) (list (constant-result (first params)))) - ((##core#undefined) '(*)) - ((##core#proc) '(procedure)) - ((##core#global-ref) (global-result (first params) loc)) - ((##core#variable) (variable-result (first params) e loc flow)) - ((if) - (let* ((tags (cons (tag) (tag))) - (rt (single "in conditional" (walk (first subs) e loc #f #f flow tags) loc)) - (c (second subs)) - (a (third subs))) - (always-true rt loc n) - (let ((r1 (walk c e loc dest tail (cons (car tags) flow) #f)) - (r2 (walk a e loc dest tail (cons (cdr tags) flow) #f))) - (cond ((and (not (eq? '* r1)) (not (eq? '* r2))) - (when (and (not (any noreturn-type? r1)) - (not (any noreturn-type? r2)) - (not (= (length r1) (length r2)))) - (report - loc - (sprintf - "branches in conditional expression differ in the number of results:~%~%~a" - (pp-fragment n)))) - (map (lambda (t1 t2) (simplify `(or ,t1 ,t2))) - r1 r2)) - (else '*))))) - ((let) - ;; before CPS-conversion, `let'-nodes may have multiple bindings - (let loop ((vars params) (body subs) (e2 '())) - (if (null? vars) - (walk (car body) (append e2 e) loc dest tail flow ctags) - (let ((t (single - (sprintf "in `let' binding of `~a'" (real-name (car vars))) - (walk (car body) e loc (car vars) #f flow #f) - loc))) - (loop (cdr vars) (cdr body) (alist-cons (car vars) t e2)))))) - ((##core#lambda lambda) - (decompose-lambda-list - (first params) - (lambda (vars argc rest) - (let* ((name (if dest (list dest) '())) - (args (append (make-list argc '*) (if rest '(#!rest) '()))) - (e2 (append (map (lambda (v) (cons v '*)) - (if rest (butlast vars) vars)) - e))) - (fluid-let ((blist '())) - (let ((r (walk (first subs) - (if rest (alist-cons rest 'list e2) e2) - (add-loc dest loc) - #f #t (list (tag)) #f))) - (list - (append - '(procedure) - name - (list args) - r)))))))) - ((set! ##core#set!) - (let* ((var (first params)) - (type (##sys#get var '##compiler#type)) - (rt (single - (sprintf "in assignment to `~a'" var) - (walk (first subs) e loc var #f flow #f) - loc)) - (b (assq var e)) ) - (when (and type (not b) - (not (eq? type 'deprecated)) - (not (match type rt))) - (report - loc - (sprintf - "assignment of value of type `~a' to toplevel variable `~a' does not match declared type `~a'" - rt var type))) - (when (and b (eq? 'undefined (cdr b))) - (set-cdr! b rt)) - (when b - (set! blist (alist-cons (cons var (car flow)) rt blist))) - '(undefined))) - ((##core#primitive ##core#inline_ref) '*) - ((##core#call) - (let* ((f (fragment n)) - (len (length subs)) - (args (map (lambda (n i) - (single - (sprintf - "in ~a of procedure call `~s'" - (if (zero? i) - "operator position" - (sprintf "argument #~a" i)) - f) - (walk n e loc #f #f flow #f) loc)) - subs - (iota len))) - (fn (car args)) - (pn (procedure-name fn)) - (enforces (and pn (##sys#get pn '##compiler#enforce-argument-types))) - (pt (and pn (##sys#get pn '##compiler#predicate)))) - (let ((r (call-result n args e loc params))) - (invalidate-blist) - (for-each - (lambda (arg argr) - (when (eq? '##core#variable (node-class arg)) - (let* ((var (first (node-parameters arg))) - (a (assq var e)) - (pred (and pt ctags (not (eq? arg (car subs)))))) - (cond (pred - (d "predicate `~a' indicates `~a' is ~a in flow ~a" pn var pt - (car ctags)) - (set! blist - (alist-cons (cons var (car ctags)) pt blist))) - (a - (when enforces - ;;XXX when ctags is set, add blist entries for both flows - (let ((ar (most-specialized-type - (cdr a) - (cond ((get db var 'assigned) '*) - (else argr))))) - (d "assuming: ~a -> ~a (flow: ~a)" var ar (car flow)) - (set! blist - (alist-cons (cons var (car flow)) ar blist))))))))) - subs - (cons fn (procedure-argument-types fn (sub1 len)))) - r))) - ((##core#switch ##core#cond) - (bomb "unexpected node class: ~a" class)) - (else - (for-each (lambda (n) (walk n e loc #f #f flow #f)) subs) - '*)))) - (d " -> ~a" results) - results))) - (let ((rn (walk (first (node-subexpressions node)) '() '() #f #f (list (tag)) #f))) - (when (and (pair? specialization-statistics) - (debugging 'x "specializations:")) - (for-each - (lambda (ss) - (printf " ~a ~s~%" (cdr ss) (car ss))) - specialization-statistics)) - rn))) + (define (simplify1 t) + (call/cc + (lambda (return) + (if (pair? t) + (case (car t) + ((or) + (cond ((= 2 (length t)) (simplify (second t))) + ((every procedure-type? (cdr t)) + (if (any (cut eq? 'procedure <>) (cdr t)) + 'procedure + (reduce + (lambda (t pt) + (let* ((name1 (and (named? t) (cadr t))) + (atypes1 (if name1 (third t) (second t))) + (rtypes1 (if name1 (cdddr t) (cddr t))) + (name2 (and (named? pt) (cadr pt))) + (atypes2 (if name2 (third pt) (second pt))) + (rtypes2 (if name2 (cdddr pt) (cddr pt)))) + (append + '(procedure) + (if (and name1 name2 (eq? name1 name2)) (list name1) '()) + (list (merge-argument-types atypes1 atypes2)) + (merge-result-types rtypes1 rtypes2)))) + #f + (cdr t)))) + (else + (let* ((ts (append-map + (lambda (t) + (let ((t (simplify t))) + (cond ((and (pair? t) (eq? 'or (car t))) + (cdr t)) + ;((eq? t 'noreturn) '()) + ((eq? t 'undefined) (return 'undefined)) + (else (list t))))) + (cdr t))) + (ts2 (let loop ((ts ts) (done '())) + (cond ((null? ts) (reverse done)) + ((eq? '* (car ts)) (return '*)) + ((any (cut type<=? (car ts) <>) (cdr ts)) + (loop (cdr ts) done)) + ((any (cut type<=? (car ts) <>) done) + (loop (cdr ts) done)) + (else (loop (cdr ts) (cons (car ts) done))))))) + (cond ((equal? ts2 (cdr t)) t) + (else + (d " or-simplify: ~a" ts2) + (simplify `(or ,@(if (any (cut eq? <> '*) ts2) '(*) ts2)))))))) ) + ((procedure) + (let* ((name (and (named? t) (cadr t))) + (rtypes (if name (cdddr t) (cddr t)))) + (append + '(procedure) + (if name (list name) '()) + (list (map simplify (if name (third t) (second t)))) + (if (eq? '* rtypes) + '* + (map simplify rtypes))))) + (else t)) + t)))) + (define (named? t) + (and (pair? t) + (eq? 'procedure (car t)) + (not (or (null? (cadr t)) (pair? (cadr t)))))) + (define (rest-type r) + (cond ((null? r) '*) + ((eq? 'values (car r)) '*) + (else (car r)))) + (define (merge-argument-types ts1 ts2) + (cond ((null? ts1) + (cond ((null? ts2) '()) + ((memq (car ts2) '(#!rest #!optional)) ts2) + (else '(#!rest)))) + ((eq? '#!rest (car ts1)) + (cond ((and (pair? ts2) (eq? '#!rest (car ts2))) + `(#!rest + ,(simplify + `(or ,(rest-type (cdr ts1)) + ,(rest-type (cdr ts2)))))) + (else '(#!rest)))) ;XXX giving up + ((eq? '#!optional (car ts1)) + (cond ((and (pair? ts2) (eq? '#!optional (car ts2))) + `(#!optional + ,(simplify `(or ,(cadr ts1) ,(cadr ts2))) + ,@(merge-argument-types (cddr ts1) (cddr ts2)))) + (else '(#!rest)))) ;XXX + (else (cons (simplify `(or ,(car ts1) ,(car ts2))) + (merge-argument-types (cdr ts1) (cdr ts2)))))) + (define (merge-result-types ts1 ts2) ;XXX possibly overly conservative + (cond ((null? ts1) ts2) + ((null? ts2) ts1) + ((or (atom? ts1) (atom? ts2)) '*) + (else (cons (simplify `(or ,(car ts1) ,(car ts2))) + (merge-result-types (cdr ts1) (cdr ts2)))))) + (define (match t1 t2) + (let ((m (match1 t1 t2))) + (d "match ~a <-> ~a -> ~a" t1 t2 m) + m)) + (define (match1 t1 t2) + (cond ((eq? t1 t2)) + ((eq? t1 '*)) + ((eq? t2 '*)) + ((eq? t1 'noreturn)) + ((eq? t2 'noreturn)) + ((and (eq? t1 'number) (memq t2 '(number fixnum float)))) + ((and (eq? t2 'number) (memq t1 '(number fixnum float)))) + ((eq? 'procedure t1) (and (pair? t2) (eq? 'procedure (car t2)))) + ((eq? 'procedure t2) (and (pair? t1) (eq? 'procedure (car t1)))) + ((and (pair? t1) (eq? 'or (car t1))) (any (cut match <> t2) (cdr t1))) + ((and (pair? t2) (eq? 'or (car t2))) (any (cut match t1 <>) (cdr t2))) + ((memq t1 '(pair list)) (memq t2 '(pair list))) + ((memq t1 '(null list)) (memq t2 '(null list))) + ((and (pair? t1) (pair? t2) (eq? (car t1) (car t2))) + (case (car t1) + ((procedure) + (let ((args1 (if (named? t1) (third t1) (second t1))) + (args2 (if (named? t2) (third t2) (second t2))) + (results1 (if (named? t1) (cdddr t1) (cddr t1))) + (results2 (if (named? t2) (cdddr t2) (cddr t2))) ) + (and (match-args args1 args2) + (match-results results1 results2)))) + ((struct) (equal? t1 t2)) + (else #f) ) ) + (else #f))) + (define (match-args args1 args2) + (d "match-args: ~s <-> ~s" args1 args2) + (define (match-rest rtype args opt) ;XXX currently ignores `opt' + (let-values (((head tail) (break (cut eq? '#!rest <>) args))) + (and (every (cut match rtype <>) head) ; match required args + (match rtype (if (pair? tail) (rest-type (cdr tail)) '*))))) + (define (optargs a) + (memq a '(#!rest #!optional))) + (let loop ((args1 args1) (args2 args2) (opt1 #f) (opt2 #f)) + (d " args ~a ~a ~a ~a" args1 args2 opt1 opt2) + (cond ((null? args1) + (or opt2 + (null? args2) + (optargs (car args2)))) + ((null? args2) + (or opt1 + (optargs (car args1)))) + ((eq? '#!optional (car args1)) + (loop (cdr args1) args2 #t opt2)) + ((eq? '#!optional (car args2)) + (loop args1 (cdr args2) opt1 #t)) + ((eq? '#!rest (car args1)) + (match-rest (rest-type (cdr args1)) args2 opt2)) + ((eq? '#!rest (car args2)) + (match-rest (rest-type (cdr args2)) args1 opt1)) + ((match (car args1) (car args2)) (loop (cdr args1) (cdr args2) opt1 opt2)) + (else #f)))) + (define (match-results results1 results2) + (cond ((null? results1) (atom? results2)) + ((eq? '* results1)) + ((eq? '* results2)) + ((null? results2) #f) + ((match (car results1) (car results2)) + (match-results (cdr results1) (cdr results2))) + (else #f))) + (define (type<=? t1 t2) + (or (eq? t1 t2) + (memq t2 '(* undefined)) + (case t2 + ((list) (memq t1 '(null pair))) + ((procedure) (and (pair? t1) (eq? 'procedure (car t1)))) + ((number) (memq t1 '(fixnum float))) + (else + (and (pair? t1) (pair? t2) + (case (car t1) + ((or) (every (cut type<=? <> t2) (cdr t1))) + ((procedure) + (let ((args1 (if (named? t1) (caddr t1) (cadr t1))) + (args2 (if (named? t2) (caddr t2) (cadr t2))) + (res1 (if (named? t1) (cdddr t1) (cddr t1))) + (res2 (if (named? t2) (cdddr t2) (cddr t2))) ) + (let loop1 ((args1 args1) + (args2 args2) + (m1 0) + (m2 0)) + (cond ((null? args1) + (and (or (null? args2) (> m2 0)) + (let loop2 ((res1 res1) (res2 res2)) + (cond ((eq? '* res2) #t) + ((null? res2) (null? res1)) + ((eq? '* res1) #f) + ((type<=? (car res1) (car res2)) + (loop2 (cdr res1) (cdr res2))) + (else #f))))) + ((null? args2) #f) + ((eq? (car args1) '#!optional) + (loop1 (cdr args1) args2 1 m2)) + ((eq? (car args2) '#!optional) + (loop1 args1 (cdr args2) m1 1)) + ((eq? (car args1) '#!rest) + (loop1 (cdr args1) args2 2 m2)) + ((eq? (car args2) '#!rest) + (loop1 args1 (cdr args2) m1 2)) + ((type<=? (car args1) (car args2)) + (loop1 (cdr args1) (cdr args2) m1 m2)) + (else #f))))))))))) + (define (multiples n) + (if (= n 1) "" "s")) + (define (single what tv loc) + (if (eq? '* tv) + '* + (let ((n (length tv))) + (cond ((= 1 n) (car tv)) + ((zero? n) + (report + loc + (sprintf "expected ~a a single result, but were given zero results" what)) + 'undefined) + (else + (report + loc + (sprintf "expected ~a a single result, but were given ~a result~a" + what n (multiples n))) + (first tv)))))) + (define (report loc desc) + (when complain + (warning + (conc (location-name loc) desc)))) + (define (location-name loc) + (define (lname loc1) + (if loc1 + (sprintf "procedure `~a'" (real-name loc1)) + "unknown procedure")) + (cond ((null? loc) "at toplevel:\n ") + ((null? (cdr loc)) + (sprintf "in toplevel ~a:\n " (lname (car loc)))) + (else + (let rec ((loc loc)) + (if (null? (cdr loc)) + (location-name loc) + (sprintf "in local ~a,\n ~a" (lname (car loc)) (rec (cdr loc)))))))) + (define add-loc cons) + (define (fragment x) + (let ((x (build-expression-tree x))) + (let walk ((x x) (d 0)) + (cond ((atom? x) x) + ((>= d +fragment-max-depth+) '...) + ((list? x) + (map (cute walk <> (add1 d)) (take x (min +fragment-max-length+ (length x))))) + (else x))))) + (define (pp-fragment x) + (string-chomp + (with-output-to-string + (lambda () + (pp (fragment x)))))) + (define (call-result node args e loc params) + (define (pname) + (sprintf "~ain procedure call to `~s', " + (if (and (pair? params) (pair? (cdr params))) + (let ((n (source-info->line (cadr params)))) + (if n + (sprintf "~a: " n) + "")) + "") + (fragment (first (node-subexpressions node))))) + (d "call-result: ~a " args) + (let* ((ptype (car args)) + (nargs (length (cdr args))) + (xptype `(procedure ,(make-list nargs '*) *))) + (when (and (not (procedure-type? ptype)) + (not (match xptype ptype))) + (report + loc + (sprintf + "~aexpected a value of type `~a', but was given a value of type `~a'" + (pname) + xptype + ptype))) + (let-values (((atypes values-rest) (procedure-argument-types ptype nargs))) + (d " argument-types: ~a (~a)" atypes values-rest) + (unless (= (length atypes) nargs) + (let ((alen (length atypes))) + (report + loc + (sprintf + "~aexpected ~a argument~a, but was given ~a argument~a" + (pname) alen (multiples alen) + nargs (multiples nargs))))) + (do ((args (cdr args) (cdr args)) + (atypes atypes (cdr atypes)) + (i 1 (add1 i))) + ((or (null? args) (null? atypes))) + (unless (match (car atypes) (car args)) + (report + loc + (sprintf + "~aexpected argument #~a of type `~a', but was given an argument of type `~a'" + (pname) i (car atypes) (car args))))) + (let ((r (procedure-result-types ptype values-rest (cdr args)))) + (d " result-types: ~a" r) + (when specialize + ;;XXX we should check whether this is a standard- or extended bindng + (let ((pn (procedure-name ptype)) + (op #f)) + (when pn + (cond ((and (fx= 1 nargs) + (##sys#get pn '##compiler#predicate)) => + (lambda (pt) + (cond ((match-specialization (list pt) (cdr args)) + (specialize-node! + node + `(let ((#:tmp #(1))) '#t)) + (set! op (list pn pt))) + ((match-specialization (list `(not ,pt)) (cdr args)) + (specialize-node! + node + `(let ((#:tmp #(1))) '#f)) + (set! op (list pt `(not ,pt))))))) + ((##sys#get pn '##compiler#specializations) => + (lambda (specs) + (for-each + (lambda (spec) + (when (match-specialization (car spec) (cdr args)) + (set! op (cons pn (car spec))) + (specialize-node! node (cadr spec)))) + specs)))) + (when op + (cond ((assoc op specialization-statistics) => + (lambda (a) (set-cdr! a (add1 (cdr a))))) + (else + (set! specialization-statistics + (cons (cons op 1) + specialization-statistics)))))))) + r)))) + (define (procedure-type? t) + (or (eq? 'procedure t) + (and (pair? t) + (or (eq? 'procedure (car t)) + (and (eq? 'or (car t)) + (every procedure-type? (cdr t))))))) + (define (procedure-name t) + (and (pair? t) + (eq? 'procedure (car t)) + (let ((n (cadr t))) + (cond ((string? n) (string->symbol n)) + ((symbol? n) n) + (else #f))))) + (define (procedure-argument-types t n) + (cond ((or (memq t '(* procedure)) + (not-pair? t) + (eq? 'deprecated (car t))) + (values (make-list n '*) #f)) + ((eq? 'procedure (car t)) + (let* ((vf #f) + (llist + (let loop ((at (if (or (string? (second t)) (symbol? (second t))) + (third t) + (second t))) + (m n) + (opt #f)) + (cond ((null? at) '()) + ((eq? '#!optional (car at)) + (loop (cdr at) m #t) ) + ((eq? '#!rest (car at)) + (set! vf (and (pair? (cdr at)) (eq? 'values (cadr at)))) + (make-list m (rest-type (cdr at)))) + ((and opt (<= m 0)) '()) + (else (cons (car at) (loop (cdr at) (sub1 m) opt))))))) + (values llist vf))) + (else (bomb "not a procedure type" t)))) + (define (procedure-result-types t values-rest? args) + (cond (values-rest? args) + ((or (memq t '(* procedure)) + (not-pair? t) ) + '*) + ((eq? 'procedure (car t)) + (call/cc + (lambda (return) + (let loop ((rt (if (or (string? (second t)) (symbol? (second t))) + (cdddr t) + (cddr t)))) + (cond ((null? rt) '()) + ((eq? '* rt) (return '*)) + (else (cons (car rt) (loop (cdr rt))))))))) + (else (bomb "not a procedure type: ~a" t)))) + (define (noreturn-type? t) + (or (eq? 'noreturn t) + (and (pair? t) + (eq? 'or (car t)) + (any noreturn-type? (cdr t))))) + (define (self-call? node loc) + (case (node-class node) + ((##core#call) + (and (pair? loc) + (let ((op (first (node-subexpressions node)))) + (and (eq? '##core#variable (node-class op)) + (eq? (car loc) (first (node-parameters op))))))) + ((let) + (self-call? (last (node-subexpressions node)) loc)) + (else #f))) + (define tag + (let ((n 0)) + (lambda () + (set! n (add1 n)) + n))) + (define (invalidate-blist) + (for-each + (lambda (b) + (when (get db (caar b) 'assigned) + (d "invalidating: ~a" b) + (set-cdr! b '*))) + blist)) + (define (walk n e loc dest tail flow ctags) ; returns result specifier + (let ((subs (node-subexpressions n)) + (params (node-parameters n)) + (class (node-class n)) ) + (d "walk: ~a ~a (loc: ~a, dest: ~a, tail: ~a, flow: ~a, blist: ~a, e: ~a)" + class params loc dest tail flow blist e) + (let ((results + (case class + ((quote) (list (constant-result (first params)))) + ((##core#undefined) '(*)) + ((##core#proc) '(procedure)) + ((##core#global-ref) (global-result (first params) loc)) + ((##core#variable) (variable-result (first params) e loc flow)) + ((if) + (let* ((tags (cons (tag) (tag))) + (rt (single "in conditional" (walk (first subs) e loc #f #f flow tags) loc)) + (c (second subs)) + (a (third subs))) + (always-true rt loc n) + (let ((r1 (walk c e loc dest tail (cons (car tags) flow) #f)) + (r2 (walk a e loc dest tail (cons (cdr tags) flow) #f))) + (cond ((and (not (eq? '* r1)) (not (eq? '* r2))) + (when (and (not (any noreturn-type? r1)) + (not (any noreturn-type? r2)) + (not (= (length r1) (length r2)))) + (report + loc + (sprintf + "branches in conditional expression differ in the number of results:~%~%~a" + (pp-fragment n)))) + (map (lambda (t1 t2) (simplify `(or ,t1 ,t2))) + r1 r2)) + (else '*))))) + ((let) + ;; before CPS-conversion, `let'-nodes may have multiple bindings + (let loop ((vars params) (body subs) (e2 '())) + (if (null? vars) + (walk (car body) (append e2 e) loc dest tail flow ctags) + (let ((t (single + (sprintf "in `let' binding of `~a'" (real-name (car vars))) + (walk (car body) e loc (car vars) #f flow #f) + loc))) + (loop (cdr vars) (cdr body) (alist-cons (car vars) t e2)))))) + ((##core#lambda lambda) + (decompose-lambda-list + (first params) + (lambda (vars argc rest) + (let* ((name (if dest (list dest) '())) + (args (append (make-list argc '*) (if rest '(#!rest) '()))) + (e2 (append (map (lambda (v) (cons v '*)) + (if rest (butlast vars) vars)) + e))) + (fluid-let ((blist '())) + (let ((r (walk (first subs) + (if rest (alist-cons rest 'list e2) e2) + (add-loc dest loc) + #f #t (list (tag)) #f))) + (list + (append + '(procedure) + name + (list args) + r)))))))) + ((set! ##core#set!) + (let* ((var (first params)) + (type (##sys#get var '##compiler#type)) + (rt (single + (sprintf "in assignment to `~a'" var) + (walk (first subs) e loc var #f flow #f) + loc)) + (b (assq var e)) ) + (when (and type (not b) + (not (eq? type 'deprecated)) + (not (match type rt))) + (report + loc + (sprintf + "assignment of value of type `~a' to toplevel variable `~a' does not match declared type `~a'" + rt var type))) + (when (and b (eq? 'undefined (cdr b))) + (set-cdr! b rt)) + (when b + (set! blist (alist-cons (cons var (car flow)) rt blist))) + '(undefined))) + ((##core#primitive ##core#inline_ref) '*) + ((##core#call) + (let* ((f (fragment n)) + (len (length subs)) + (args (map (lambda (n i) + (single + (sprintf + "in ~a of procedure call `~s'" + (if (zero? i) + "operator position" + (sprintf "argument #~a" i)) + f) + (walk n e loc #f #f flow #f) loc)) + subs + (iota len))) + (fn (car args)) + (pn (procedure-name fn)) + (enforces (and pn (##sys#get pn '##compiler#enforce-argument-types))) + (pt (and pn (##sys#get pn '##compiler#predicate)))) + (let ((r (call-result n args e loc params))) + (invalidate-blist) + (for-each + (lambda (arg argr) + (when (eq? '##core#variable (node-class arg)) + (let* ((var (first (node-parameters arg))) + (a (assq var e)) + (pred (and pt ctags (not (eq? arg (car subs)))))) + (cond (pred + (d "predicate `~a' indicates `~a' is ~a in flow ~a" pn var pt + (car ctags)) + (set! blist + (alist-cons (cons var (car ctags)) pt blist))) + (a + (when enforces + ;;XXX when ctags is set, add blist entries for both flows + (let ((ar (cond ((blist-type var flow) => + (lambda (t) + (if (type<=? t argr) + t + argr))) + ((get db var 'assigned) '*) + ((type<=? (cdr a) argr) (cdr a)) + (else argr)))) + (d "assuming: ~a -> ~a (flow: ~a)" var ar (car flow)) + (set! blist + (alist-cons (cons var (car flow)) ar blist))))))))) + subs + (cons fn (procedure-argument-types fn (sub1 len)))) + r))) + ((##core#switch ##core#cond) + (bomb "unexpected node class: ~a" class)) + (else + (for-each (lambda (n) (walk n e loc #f #f flow #f)) subs) + '*)))) + (d " -> ~a" results) + results))) + (let ((rn (walk (first (node-subexpressions node)) '() '() #f #f (list (tag)) #f))) + (when (and (pair? specialization-statistics) + (debugging 'x "specializations:")) + (for-each + (lambda (ss) + (printf " ~a ~s~%" (cdr ss) (car ss))) + specialization-statistics)) + rn))) (define (load-type-database name #!optional (path (repository-path))) (and-let* ((dbfile (file-exists? (make-pathname path name))))Trap