~ chicken-core (chicken-5) aa26bb8466669192b36a6c2259b4d3a65c9db272
commit aa26bb8466669192b36a6c2259b4d3a65c9db272 Author: felix <felix@call-with-current-continuation.org> AuthorDate: Tue Aug 16 00:55:43 2011 +0200 Commit: felix <felix@call-with-current-continuation.org> CommitDate: Tue Aug 16 00:55:43 2011 +0200 started with polymorphic types diff --git a/scrutinizer.scm b/scrutinizer.scm index 17da5a18..0bb6dd24 100755 --- a/scrutinizer.scm +++ b/scrutinizer.scm @@ -60,6 +60,7 @@ ; | (procedure [NAME] (VAL1 ... [#!optional VALOPT1 ...] [#!rest [VAL | values]]) . RESULTS) ; | BASIC ; | COMPLEX +; | (forall (VAR1 ...) VAL) ; | deprecated ; BASIC = * | string | symbol | char | number | boolean | list | pair | ; procedure | vector | null | eof | undefined | port | @@ -85,7 +86,7 @@ ; SPECIALIZATION = ((MVAL ... [#!rest MVAL]) [RESULTS] TEMPLATE) ; MVAL = VAL | (not VAL) | (or VAL ...) | (and VAL ...) ; TEMPLATE = #(INDEX) -; | #(-INDEX) +; | #(INDEX ...) ; | #(SYMBOL) ; | INTEGER | SYMBOL | STRING ; | (quote CONSTANT) @@ -137,7 +138,9 @@ (cond ((variable-mark id '##compiler#type) => (lambda (a) (cond - #| + #| XXX Disabled, since this would remove specializations in core library + code, where these get assigned. Still, it would be safer to + unmark assigned vars... ((and (get db id 'assigned) ; remove assigned global from type db (not (variable-mark id '##compiler#declared-type))) (mark-variable id '##compiler#type #f) @@ -184,8 +187,11 @@ (else (global-result id loc)))) (define (always-true1 t) - (cond ((and (pair? t) (eq? 'or (car t))) - (every always-true1 (cdr t))) + (cond ((pair? t) + (case (car t) + ((or) (every always-true1 (cdr t))) + ((forall) (always-true1 (third t))) + (else #t))) ((memq t '(* boolean undefined noreturn)) #f) (else #t))) @@ -220,6 +226,18 @@ " OR ")) ((struct) (sprintf "a structure of type ~a" (cadr t))) + ((forall) + (sprintf "~a (for all ~a)" + (typename (third t)) + (string-intersperse (map symbol->string (second t)) " "))) + ((pair) + (sprintf "a pair wth car ~a and cdr ~a" + (typename (second t)) + (typename (third t)))) + ((vector) + (sprintf "a vector with element type ~a" (typename (second t)))) + ((list) + (sprintf "a list with element type ~a" (typename (second t)))) (else (bomb "invalid type" t)))) (else (bomb "invalid type" t)))))) @@ -245,78 +263,97 @@ len m m (map typename results)))))) - (define (match t1 t2) + (define (match t1 t2 typeenv) + (define (match1 t1 t2) + (cond ((eq? t1 t2)) + ((and (symbol? t1) (assq t1 typeenv)) => + (lambda (e) + (if (cdr e) + (match1 (cdr e) t2) + (begin + (d " unify ~a = ~a" t1 t2) + (set-cdr! e t2) + #t)))) + ((and (symbol? t2) (assq t2 typeenv)) => + (lambda (e) + (if (cdr e) + (match1 t1 (cdr e)) + (begin + (d " unify ~a = ~a" t2 t1) + (set-cdr! e t1) + #t)))) + ((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 match1 <> t2) (cdr t1))) + ((and (pair? t2) (eq? 'or (car t2))) (any (cut match1 t1 <>) (cdr t2))) + ((and (pair? t1) (eq? 'forall (car t1))) + (match1 (third t1) t2)) ; assumes typeenv has already been extracted + ((and (pair? t2) (eq? 'forall (car t2))) + (match1 t1 (third t2))) ; assumes typeenv has already been extracted + ((eq? t1 'pair) (match1 '(pair * *) t2)) + ((eq? t2 'pair) (match1 t1 '(pair * *))) + ((eq? t1 'list) (match1 '(list *) t2)) + ((eq? t2 'list) (match1 t1 '(list *))) + ((eq? t1 'vector) (match1 '(vector *) t2)) + ((eq? t2 'vector) (match1 t1 '(vector *))) + ((eq? t1 'null) + (or (memq t2 '(null list)) + (and (pair? t2) (eq? 'list (car t2))))) + ((eq? t2 'null) + (or (memq t1 '(null list)) + (and (pair? t1) (eq? 'list (car t1))))) + ((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 typeenv) + (match-results results1 results2 typeenv)))) + ((struct) (equal? t1 t2)) + ((pair) (every match1 (cdr t1) (cdr t2))) + ((list vector) (match1 (second t1) (second t2))) + (else #f) ) ) + ((and (pair? t1) (eq? 'pair (car t1))) + (and (pair? t2) + (eq? 'list (car t2)) + (match1 (second t1) (second t2)) + (match1 (third t1) t2))) + ((and (pair? t2) (eq? 'pair (car t2))) + (and (pair? t1) + (eq? 'list (car t1)) + (match1 (second t1) (second t2)) + (match1 t1 (third t2)))) + ((and (pair? t1) (eq? 'list (car t1))) + (or (eq? 'null t2) + (and (pair? t2) + (eq? 'pair? (car t2)) + (match1 (second t1) (second t2)) + (match1 t1 (third t2))))) + ((and (pair? t2) (eq? 'list (car t2))) + (or (eq? 'null t1) + (and (pair? t1) + (eq? 'pair? (car t1)) + (match1 (second t1) (second t2)) + (match1 (third t1) t2)))) + (else #f))) (let ((m (match1 t1 t2))) (dd " 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))) - ((eq? t1 'pair) (match1 '(pair * *) t2)) - ((eq? t2 'pair) (match1 t1 '(pair * *))) - ((eq? t1 'list) (match1 '(list *) t2)) - ((eq? t2 'list) (match1 t1 '(list *))) - ((eq? t1 'vector) (match1 '(vector *) t2)) - ((eq? t2 'vector) (match1 t1 '(vector *))) - ((eq? t1 'null) - (or (memq t2 '(null list)) - (and (pair? t2) (eq? 'list (car t2))))) - ((eq? t2 'null) - (or (memq t1 '(null list)) - (and (pair? t1) (eq? 'list (car t1))))) - ((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)) - ((pair) (every match1 (cdr t1) (cdr t2))) - ((list vector) (match1 (second t1) (second t2))) - (else #f) ) ) - ((and (pair? t1) (eq? 'pair (car t1))) - (and (pair? t2) - (eq? 'list (car t2)) - (match1 (second t1) (second t2)) - (match1 (third t1) t2))) - ((and (pair? t2) (eq? 'pair (car t2))) - (and (pair? t1) - (eq? 'list (car t1)) - (match1 (second t1) (second t2)) - (match1 t1 (third t2)))) - ((and (pair? t1) (eq? 'list (car t1))) - (or (eq? 'null t2) - (and (pair? t2) - (eq? 'pair? (car t2)) - (match1 (second t1) (second t2)) - (match1 t1 (third t2))))) - ((and (pair? t2) (eq? 'list (car t2))) - (or (eq? 'null t1) - (and (pair? t1) - (eq? 'pair? (car t1)) - (match1 (second t1) (second t2)) - (match1 (third t1) t2)))) - (else #f))) - - (define (match-args args1 args2) + (define (match-args args1 args2 typeenv) (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)) '*))))) + (and (every (cut match rtype <> typeenv) head) ; match required args + (match rtype (if (pair? tail) (rest-type (cdr tail)) '*) typeenv)))) (define (optargs a) (memq a '(#!rest #!optional))) (let loop ((args1 args1) (args2 args2) (opt1 #f) (opt2 #f)) @@ -336,16 +373,17 @@ (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)) + ((match (car args1) (car args2) typeenv) + (loop (cdr args1) (cdr args2) opt1 opt2)) (else #f)))) - (define (match-results results1 results2) + (define (match-results results1 results2 typeenv) (cond ((null? results1) (atom? results2)) ((eq? '* results1)) ((eq? '* results2)) ((null? results2) #f) - ((match (car results1) (car results2)) - (match-results (cdr results1) (cdr results2))) + ((match (car results1) (car results2) typeenv) + (match-results (cdr results1) (cdr results2) typeenv)) (else #f))) (define (multiples n) @@ -419,7 +457,7 @@ (c (append (or a '()) (or b '())))) (and (pair? c) c))) - (define (call-result node args e loc params) + (define (call-result node args e loc params typeenv) (define (pname) (sprintf "~ain procedure call to `~s', " (if (and (pair? params) @@ -436,8 +474,13 @@ (pptype? (procedure-type? ptype)) (nargs (length (cdr args))) (xptype `(procedure ,(make-list nargs '*) *)) + (typeenv (or (and pptype? (type-typeenv ptype)) '())) (op #f)) - (cond ((and (not pptype?) (not (match xptype ptype))) + (define (resolve t) + (cond ((not t) '*) + ((assq t typeenv) => (lambda (a) (resolve (cdr a)))) + (else t))) + (cond ((and (not pptype?) (not (match xptype ptype typeenv))) (report loc (sprintf @@ -447,7 +490,8 @@ ptype)) (values '* #f)) (else - (let-values (((atypes values-rest) (procedure-argument-types ptype nargs))) + (let-values (((atypes values-rest) + (procedure-argument-types ptype nargs typeenv))) (d " argument-types: ~a (~a)" atypes values-rest) (unless (= (length atypes) nargs) (let ((alen (length atypes))) @@ -461,7 +505,7 @@ (atypes atypes (cdr atypes)) (i 1 (add1 i))) ((or (null? args) (null? atypes))) - (unless (match (car atypes) (car args)) + (unless (match (car atypes) (car args) typeenv) (report loc (sprintf @@ -469,14 +513,15 @@ (pname) i (car atypes) (car args))))) (when (noreturn-procedure-type? ptype) (set! noreturn #t)) - (let ((r (procedure-result-types ptype values-rest (cdr args)))) + (let ((r (procedure-result-types ptype values-rest (cdr args) typeenv))) ;;XXX we should check whether this is a standard- or extended binding (let* ((pn (procedure-name ptype))) (when pn (cond ((and (fx= 1 nargs) (variable-mark pn '##compiler#predicate)) => (lambda (pt) - (cond ((match-specialization (list pt) (cdr args) #t) + (cond ((match-specialization + (list pt) (cdr args) typeenv #t) (report-notice loc (sprintf @@ -487,7 +532,8 @@ node `(let ((#(tmp) #(1))) '#t)) (set! op (list pn pt)))) - ((match-specialization (list `(not ,pt)) (cdr args) #t) + ((match-specialization + (list `(not ,pt)) (cdr args) typeenv #t) (report-notice loc (sprintf @@ -502,7 +548,8 @@ (lambda (specs) (let loop ((specs specs)) (cond ((null? specs)) - ((match-specialization (first (car specs)) (cdr args) #f) + ((match-specialization + (first (car specs)) (cdr args) typeenv #f) (let ((spec (car specs))) (set! op (cons pn (car spec))) (let* ((r2 (and (pair? (cddr spec)) (second spec))) @@ -521,8 +568,9 @@ (when (and specialize (not op) (procedure-type? ptype)) (set-car! (node-parameters node) #t) (set! safe-calls (add1 safe-calls)))) - (d " result-types: ~a" r) - (values r op))))))) + (let ((r (if (eq? '* r) r (map resolve r)))) + (d " result-types: ~a" r) + (values r op)))))))) ;; not used in the moment (define (self-call? node loc) @@ -685,7 +733,7 @@ (b (assq var e)) ) (when (and type (not b) (not (eq? type 'deprecated)) - (not (match type rt))) + (not (match type rt '()))) ;;XXX make this an error with strict-types? (report loc @@ -741,21 +789,28 @@ (iota len))) (fn (car args)) (pn (procedure-name fn)) + (typeenv (type-typeenv `(or ,@args))) ; hack (enforces (and pn (variable-mark pn '##compiler#enforce))) (pt (and pn (variable-mark pn '##compiler#predicate)))) - (let-values (((r specialized?) (call-result n args e loc params))) + (define (resolve t) + (cond ((not t) '*) + ((assq t typeenv) => (lambda (a) (resolve (cdr a)))) + (else t))) + (let-values (((r specialized?) + (call-result n args e loc params typeenv))) (cond (specialized? (walk n e loc dest tail flow ctags) ;; keep type, as the specialization may contain icky stuff ;; like "##core#inline", etc. - r) + (resolve r)) (else (for-each (lambda (arg argr) (when (eq? '##core#variable (node-class arg)) (let* ((var (first (node-parameters arg))) (a (assq var e)) + (argr (resolve argr)) (oparg? (eq? arg (first subs))) (pred (and pt ctags @@ -766,11 +821,12 @@ ;; branch by subtracting pt from the current type ;; of var, at least in the simple case of ;; "(or ... <PT> ...)" -> "(or ... ...)" - (d " predicate `~a' indicates `~a' is ~a in flow ~a" - pn var pt (car ctags)) - (add-to-blist - var (car ctags) - (if (and a (type<=? (cdr a) pt)) (cdr a) pt))) + (let ((pt (resolve pt))) + (d " predicate `~a' indicates `~a' is ~a in flow ~a" + pn var pt (car ctags)) + (add-to-blist + var (car ctags) + (if (and a (type<=? (cdr a) pt)) (cdr a) pt)))) (a (when enforces (let ((ar (cond ((blist-type var flow) => @@ -797,7 +853,9 @@ subs (cons fn - (nth-value 0 (procedure-argument-types fn (sub1 len))))) + (nth-value + 0 + (procedure-argument-types fn (sub1 len) typeenv)))) r))))) ((##core#the) (let-values (((t _) (validate-type (first params) #f))) @@ -847,88 +905,106 @@ rn))) +;;; Simplify type specifier +; +; - coalesces "forall" and renames type-variables +; - also rename type-variables + (define (simplify-type t) - (define (simplify t) - (let ((t2 (simplify1 t))) + (let ((typeenv '())) ; ((VAR1 . NEWVAR1) ...) + (define (rename v) + (cond ((assq v typeenv) => cdr) + (else + (let ((new (gensym v))) + (set! typeenv (alist-cons v new typeenv)) + new)))) + (define (simplify t) + (call/cc + (lambda (return) + (cond ((pair? t) + (case (car t) + ((forall) + (set! typeenv + (append (map (lambda (v) (cons v (gensym v))) (second t)) typeenv)) + (simplify (third 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)))) + ((lset= eq? '(fixnum float) (cdr t)) 'number) + (else + (let* ((ts (append-map + (lambda (t) + (let ((t (simplify t))) + (cond ((and (pair? t) (eq? 'or (car t))) + (cdr t)) + ((eq? t 'undefined) (return 'undefined)) + ((eq? t 'noreturn) '()) + (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 + (dd " or-simplify: ~a" ts2) + (simplify + `(or ,@(if (any (cut eq? <> '*) ts2) '(*) ts2)))))))) ) + ((pair) + (let ((tcar (simplify (second t))) + (tcdr (simplify (third t)))) + (if (and (eq? '* tcar) (eq? '* tcdr)) + 'pair + (let rec ((tr tcdr) (ts (list tcar))) + (cond ((eq? tr 'null) `(list (or ,@(reverse ts)))) + ((and (pair? tr) (eq? 'pair (first tr))) + (rec (third tr) (cons (second tr) ts))) + (else `(pair ,tcar ,tcdr))))))) + ((vector list) + (let ((t2 (simplify (second t)))) + (if (eq? t2 '*) + (car t) + `(,(car t) ,t2)))) + ((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))) + ((assq t typeenv) => cdr) + (else t))))) + (let ((t2 (simplify t))) + (when (pair? typeenv) + (set! t2 `(forall ,(map cdr typeenv) ,t2))) (dd "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)))) - ((lset= eq? '(fixnum float) (cdr t)) 'number) - (else - (let* ((ts (append-map - (lambda (t) - (let ((t (simplify t))) - (cond ((and (pair? t) (eq? 'or (car t))) - (cdr t)) - ((eq? t 'undefined) (return 'undefined)) - ((eq? t 'noreturn) '()) - (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 - (dd " or-simplify: ~a" ts2) - (simplify - `(or ,@(if (any (cut eq? <> '*) ts2) '(*) ts2)))))))) ) - ((pair) - (let ((tcar (simplify (second t))) - (tcdr (simplify (third t)))) - (if (and (eq? '* tcar) (eq? '* tcdr)) - 'pair - (let rec ((tr tcdr) (ts (list tcar))) - (cond ((eq? tr 'null) `(list (or ,@(reverse ts)))) - ((and (pair? tr) (eq? 'pair (first tr))) - (rec (third tr) (cons (second tr) ts))) - (else `(pair ,tcar ,tcdr))))))) - ((vector list) - (let ((t2 (simplify (second t)))) - (if (eq? t2 '*) - (car t) - `(,(car t) ,t2)))) - ((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)))) - (simplify t)) + t2))) + ;;XXX this could be better done by combining non-matching arguments/llists ;; into "(or (procedure ...) (procedure ...))" @@ -972,169 +1048,241 @@ (type<=? t2 t1))) (define (type<=? t1 t2) - (cond ((eq? t1 t2)) - ((memq t2 '(* undefined))) - ((eq? 'pair t1) (type<=? '(pair * *) t2)) - ((memq t1 '(vector list)) (type<=? `(,t1 *) t2)) - ((and (eq? 'null t1) - (pair? t2) - (memq (car t2) '(pair list)))) - (else - (case t2 - ((procedure) (and (pair? t1) (eq? 'procedure (car t1)))) - ((number) (memq t1 '(fixnum float))) - ((vector list) (type<=? t1 `(,t2 *))) - ((pair) (type<=? t1 '(pair * *))) - (else - (cond ((not (pair? t1)) #f) - ((not (pair? t2)) #f) - ((eq? 'or (car t2)) - (every (cut type<=? t1 <>) (cdr t2))) - ((not (eq? (car t1) (car t2))) #f) - (else - (case (car t1) - ((or) (every (cut type<=? <> t2) (cdr t1))) - ((vector) (type<=? (second t1) (second t2))) - ((list) - (case (car t2) - ((list) (type<=? (second t1) (second t2))) - ((pair) - (and (type<=? (second t1) (second t2)) - (type<=? t1 (third t2)))) - (else #f))) - ((pair) (every type<=? (cdr t1) (cdr t2))) - ((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) - (rtype1 #f) - (rtype2 #f) - (m1 0) - (m2 0)) - (cond ((null? args1) - (and (cond ((null? args2) - (if rtype1 - (if rtype2 - (type<=? rtype1 rtype2) - #f) - #t)) - ((eq? '#!optional (car args2)) - (not rtype1)) - ((eq? '#!rest (car args2)) - (or (null? (cdr args2)) - rtype1 - (type<=? rtype1 (cadr args2)))) - (else (>= m2 m1))) - (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))))) - ((eq? (car args1) '#!optional) - (loop1 (cdr args1) args2 #f rtype2 1 m2)) - ((eq? (car args1) '#!rest) - (if (null? (cdr args1)) - (loop1 '() args2 '* rtype2 2 m2) - (loop1 '() args2 (cadr args1) rtype2 2 m2))) - ((null? args2) - (and rtype2 - (type<=? (car args1) rtype2) - (loop1 (cdr args1) '() rtype1 rtype2 m1 m2))) - ((eq? (car args2) '#!optional) - (loop1 args1 (cdr args2) rtype1 #f m1 1)) - ((eq? (car args2) '#!rest) - (if (null? (cdr args2)) - (loop1 args1 '() rtype1 '* m1 2) - (loop1 args1 '() rtype1 (cadr args2) m1 2))) - ((type<=? - (or rtype1 (car args1)) - (or rtype2 (car args2))) - (loop1 (cdr args1) (cdr args2) rtype1 rtype2 m1 m2)) - (else #f))))) - (else #f))))))))) - + (let ((typeenv '())) ; ((VAR1 . TYPE1) ...) + (cond ((eq? t1 t2)) + ((and (symbol? t1) (assq t1 typeenv)) => + (lambda (e) + (if (cdr e) + (type<=? (cdr e) t2) + (begin + (set-cdr! e t2) + #t)))) + ((and (symbol? t2) (assq t2 typeenv)) => + (lambda (e) + (if (cdr e) + (type<=? t1 (cdr e)) + (begin + (set-cdr! e t1) + #t)))) + ((memq t2 '(* undefined))) + ((eq? 'pair t1) (type<=? '(pair * *) t2)) + ((memq t1 '(vector list)) (type<=? `(,t1 *) t2)) + ((and (eq? 'null t1) + (pair? t2) + (memq (car t2) '(pair list)))) + ((and (pair? t1) (eq? 'forall (car t1))) + (set! typeenv (append (map (cut cons <> #f) (second t1)) typeenv)) + (type<=? (third t1) t2)) + ((and (pair? t2) (eq? 'forall (car t2))) + (set! typeenv (append (map (cut cons <> #f) (second t2)) typeenv)) + (type<=? t1 (third t2))) + (else + (case t2 + ((procedure) (and (pair? t1) (eq? 'procedure (car t1)))) + ((number) (memq t1 '(fixnum float))) + ((vector list) (type<=? t1 `(,t2 *))) + ((pair) (type<=? t1 '(pair * *))) + (else + (cond ((not (pair? t1)) #f) + ((not (pair? t2)) #f) + ((eq? 'or (car t2)) + (every (cut type<=? t1 <>) (cdr t2))) + ((not (eq? (car t1) (car t2))) #f) + (else + (case (car t1) + ((or) (every (cut type<=? <> t2) (cdr t1))) + ((vector) (type<=? (second t1) (second t2))) + ((list) + (case (car t2) + ((list) (type<=? (second t1) (second t2))) + ((pair) + (and (type<=? (second t1) (second t2)) + (type<=? t1 (third t2)))) + (else #f))) + ((pair) (every type<=? (cdr t1) (cdr t2))) + ((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) + (rtype1 #f) + (rtype2 #f) + (m1 0) + (m2 0)) + (cond ((null? args1) + (and (cond ((null? args2) + (if rtype1 + (if rtype2 + (type<=? rtype1 rtype2) + #f) + #t)) + ((eq? '#!optional (car args2)) + (not rtype1)) + ((eq? '#!rest (car args2)) + (or (null? (cdr args2)) + rtype1 + (type<=? rtype1 (cadr args2)))) + (else (>= m2 m1))) + (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))))) + ((eq? (car args1) '#!optional) + (loop1 (cdr args1) args2 #f rtype2 1 m2)) + ((eq? (car args1) '#!rest) + (if (null? (cdr args1)) + (loop1 '() args2 '* rtype2 2 m2) + (loop1 '() args2 (cadr args1) rtype2 2 m2))) + ((null? args2) + (and rtype2 + (type<=? (car args1) rtype2) + (loop1 (cdr args1) '() rtype1 rtype2 m1 m2))) + ((eq? (car args2) '#!optional) + (loop1 args1 (cdr args2) rtype1 #f m1 1)) + ((eq? (car args2) '#!rest) + (if (null? (cdr args2)) + (loop1 args1 '() rtype1 '* m1 2) + (loop1 args1 '() rtype1 (cadr args2) m1 2))) + ((type<=? + (or rtype1 (car args1)) + (or rtype2 (car args2))) + (loop1 (cdr args1) (cdr args2) rtype1 rtype2 m1 m2)) + (else #f))))) + (else #f)))))))))) + + +;;; various operations on procedure types (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))))))) + (case (car t) + ((forall) (procedure-type? (third t))) + ((procedure) #t) + ((or) (every procedure-type? (cdr t))) + (else #f))))) (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 #!optional norest) - (cond ((and (pair? t) (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)) - (if norest - '() - (loop (cdr at) m #t) )) - ((eq? '#!rest (car at)) - (cond (norest '()) - (else - (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 (values (make-list n '*) #f)))) - -(define (procedure-result-types t values-rest? args) - (cond (values-rest? args) - ((and (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) '()) - ((memq rt '(* noreturn)) (return '*)) - (else (cons (car rt) (loop (cdr rt))))))))) - (else '*))) + (case (car t) + ((forall) (procedure-name (third t))) + ((procedure) + (let ((n (cadr t))) + (cond ((string? n) (string->symbol n)) + ((symbol? n) n) + (else #f)))) + (else #f)))) + +(define (procedure-argument-types t n typeenv #!optional norest) + (let loop1 ((t t)) + (cond ((and (pair? t) + (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)) + (if norest + '() + (loop (cdr at) m #t) )) + ((eq? '#!rest (car at)) + (cond (norest '()) + (else + (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))) + ((and (pair? t) + (eq? 'forall (car t))) + (loop1 (third t))) ; assumes typeenv has already been extracted + ((assq t typeenv) => (lambda (e) (loop1 (cdr e)))) + (else (values (make-list n '*) #f))))) + +(define (procedure-result-types t values-rest? args typeenv) + (define (loop1 t) + (cond (values-rest? args) + ((assq t typeenv) => (lambda (e) (loop1 (cdr e)))) + ((and (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) '()) + ((memq rt '(* noreturn)) (return '*)) + (else (cons (car rt) (loop (cdr rt))))))))) + ((and (pair? t) (eq? 'forall (car t))) + (loop1 (third t))) ; assumes typeenv has already been extracted + (else '*))) + (loop1 t)) (define (named? t) (and (pair? t) - (eq? 'procedure (car t)) - (not (or (null? (cadr t)) (pair? (cadr t)))))) + (case (car t) + ((procedure) + (not (or (null? (cadr t)) (pair? (cadr t))))) + ((forall) (named? (third t))) + (else #f)))) (define (rest-type r) (cond ((null? r) '*) ((eq? 'values (car r)) '*) (else (car r)))) +(define (noreturn-procedure-type? ptype) + (and (pair? ptype) + (case (car ptype) + ((procedure) + (and (list? ptype) + (eq? 'noreturn + (if (symbol? (second ptype)) + (fourth ptype) + (third ptype))))) + ((forall) + (noreturn-procedure-type? (third ptype))) + (else #f)))) + (define (noreturn-type? t) (or (eq? 'noreturn t) (and (pair? t) - (eq? 'or (car t)) - (any noreturn-type? (cdr t))))) + (case (car t) + ((or) (any noreturn-type? (cdr t))) + ((forall) (noreturn-type? (third t))) + (else #f))))) + +(define (type-typeenv t) + (let ((te '())) + (let loop ((t t)) + (when (pair? t) + (case (car t) + ((procedure) + (cond ((or (string? (second t)) (symbol? (second t))) + (for-each loop (third t)) + (when (pair? (cdddr t)) + (for-each loop (cdddr t)))) + (else + (for-each loop (second t)) + (when (pair? (cddr t)) + (for-each loop (cddr t)))))) + ((forall) + (set! te (append (second t) te)) + (loop (third t))) + ((or and) + (for-each loop (cdr t)))))) + (map (cut cons <> #f) te))) -(define (noreturn-procedure-type? ptype) - (and (pair? ptype) - (eq? 'procedure (car ptype)) - (list? ptype) - (eq? 'noreturn - (if (symbol? (second ptype)) - (fourth ptype) - (third ptype))))) + +;;; type-db processing (define (load-type-database name #!optional (path (repository-path))) (and-let* ((dbfile (file-exists? (make-pathname path name)))) @@ -1158,21 +1306,18 @@ ((procedure?) (mark-variable name '##compiler#predicate (cadr new)) (set! new (cons 'procedure (cddr new)))))) - (cond-expand - (debugbuild - (let-values (((t _) (validate-type new name))) - (unless t - (warning "invalid type specification" name new)))) - (else)) - (when (and old (not (compatible-types? old new))) - (warning - (sprintf - "type-definition `~a' for toplevel binding `~a' conflicts with previously loaded type `~a'" - name new old))) - (mark-variable name '##compiler#type new) - (when specs - ;;XXX validate types in specs - (mark-variable name '##compiler#specializations specs)))) + (let-values (((t _) (validate-type new name))) + (unless t + (warning "invalid type specification" name new)) + (when (and old (not (compatible-types? old t))) + (warning + (sprintf + "type-definition `~a' for toplevel binding `~a' conflicts with previously loaded type `~a'" + name new old))) + (mark-variable name '##compiler#type t) + (when specs + ;;XXX validate types in specs + (mark-variable name '##compiler#specializations specs))))) (read-file dbfile))))) (define (emit-type-file filename db) @@ -1202,11 +1347,30 @@ db) (print "; END OF FILE")))) -(define (match-specialization typelist atypes exact) + +;;; matching for specialization + +(define (match-specialization typelist atypes typeenv exact) ;; - does not accept complex procedure types in typelist! ;; - "exact" means: "or"-type in atypes is not allowed (used for predicates) (define (match st t) (cond ((eq? st t)) + ((and (symbol? st) (assq st typeenv)) => + (lambda (e) + (if (cdr e) + (match (cdr e) t) + (begin + (d " unify (specialization) ~a = ~a" st t) + (set-cdr! e t) + #t)))) + ((and (symbol? t) (assq t typeenv)) => + (lambda (e) + (if (cdr e) + (match st (cdr e)) + (begin + (d " unify (specialization) ~a = ~a" t st) + (set-cdr! e st) + #t)))) ((memq st '(vector list)) (match (list st '*) t)) ((memq t '(vector list)) @@ -1221,8 +1385,12 @@ (every (cut match st <>) (cdr t))) ((and (pair? t) (eq? 'procedure (car t))) (match st 'procedure)) + ((and (pair? t) (eq? 'forall (car t))) + (match st (third t))) ; assumes typeenv has already been extracted ((pair? st) (case (car st) + ((forall) + (match (third st) t)) ; assumes typeenv has already been extracted ((not) (matchnot (cadr st) t)) ((or) (any (cut match <> t) (cdr st))) ((and) (every (cut match <> t) (cdr st))) @@ -1249,6 +1417,16 @@ (else (equal? st t)))) (define (matchnot st t) (cond ((eq? st t) #f) + ((and (symbol? st) (assq st typeenv)) => ; doesn't unify + (lambda (e) + (if (cdr e) + (matchnot (cdr e) t) + #f))) + ((and (symbol? t) (assq t typeenv)) => + (lambda (e) + (if (cdr e) + (matchnot st (cdr e)) + #f))) ((memq st '(vector list)) (matchnot (list st '*) t)) ((memq t '(vector list)) @@ -1267,9 +1445,13 @@ ((eq? 'null st) (or (not (pair? t)) (not (eq? 'list (car t))))) + ((and (pair? t) (eq? 'forall (car t))) + (match st (third t))) ; assumes typeenv has already been extracted ((pair? st) (case (car st) ;;XXX "and" not handled here + ((forall) + (match (third st) t)) ; assumes typeenv has already been extracted ((or) (every (cut matchnot <> t) (cdr t))) ((list) (and (not (eq? 'null t)) @@ -1333,7 +1515,11 @@ ;; - drops "#!key ..." args by converting to #!rest ;; - handles "(T1 -> T2 : T3)" (predicate) ;; - simplifies result - (let ((ptype #f)) ; (T . PT) | #f + ;; - coalesces all "forall" forms into one (remove "forall" if typevar-set is empty) + ;; - renames type-variables + (let ((ptype #f) ; (T . PT) | #f + (usedvars '()) + (typevars '())) (define (upto lst p) (let loop ((lst lst)) (cond ((eq? lst p) '()) @@ -1362,7 +1548,17 @@ pointer locative fixnum float pointer-vector deprecated noreturn values)) t) - ((not (pair? t)) #f) + ((not (pair? t)) + (when (memq t typevars) + (set! usedvars (cons t usedvars))) + t) + ((eq? 'forall (car t)) + (and (= 3 (length t)) + (list? (second t)) + (every symbol? (second t)) + (begin + (set! typevars (append (second t) typevars)) + (validate (third t))))) ((eq? 'or (car t)) (and (list? t) (let ((ts (map validate (cdr t)))) @@ -1420,8 +1616,15 @@ t) (else #f))))) (else #f))) - (let ((type (simplify-type (validate type #f)))) - (values type (and ptype (eq? (car ptype) type) (cdr ptype)))))) + (let ((type (validate type #f))) + (when (pair? typevars) + (set! type + `(forall ,(filter-map + (lambda (v) (and (memq v usedvars) v)) + (delete-duplicates typevars eq?)) + ,type))) + (let ((type (simplify-type type))) + (values type (and ptype (eq? (car ptype) type) (cdr ptype))))))) (define (initial-argument-types dest vars argc) (if (and dest @@ -1429,7 +1632,7 @@ (variable-mark dest '##compiler#declared-type)) (let ((ptype (variable-mark dest '##compiler#type))) (if (procedure-type? ptype) - (nth-value 0 (procedure-argument-types ptype argc #t)) + (nth-value 0 (procedure-argument-types ptype argc '() #t)) (make-list argc '*))) (make-list argc '*))) diff --git a/types.db b/types.db index 86acb838..d8c4235a 100644 --- a/types.db +++ b/types.db @@ -30,6 +30,8 @@ ; rewrite rules ; - for a description of the type-specifier syntax, see "scrutinizer.scm" (top of file) ; - in templates, "#(INTEGER)" refers to the INTEGERth argument (starting from 1) +; - in templates, "#(INTEGER ...)" refers to the INTEGERth argument (starting from 1) and +; all remaining arguments ; - in templates "#(SYMBOL)" binds X to a temporary gensym'd variable, further references ; to "#(SYMBOL)" allow backreferences to this generated identifier ; - a type of the form "(procedure! ...)" is internally treated like "(procedure ..." @@ -37,6 +39,7 @@ ; - a type of the form "(procedure? TYPE ...)" is internally treated like "(procedure ..." ; but declares the procedure as a predicate over TYPE. ; - a type of the form "(procedure!? TYPE ...)" or "(procedure?! TYPE ...)" is the obvious. +; - types in specializations are currently not validated ;; schemeTrap