;;; The `prune' function removes instantiated type variables at the ;;; top level of a type. ;;; It returns an uninstantiated type variable or a type constructor. (define-integrable (prune ntype) (if (ntyvar? ntype) (if (instantiated? ntype) (prune-1 (ntyvar-value ntype)) ntype) ntype)) ;;; This is because lucid can't hack inlining recursive fns. (define (prune-1 x) (prune x)) (define-integrable (instantiated? ntyvar) (ntyvar-value ntyvar)) ; (not (eq? (ntyvar-value ntyvar) '#f))) ;*** Lucid compiler bug? (define (prune/l l) (map (function prune) l)) ;;; These functions convert between AST types and gtypes. Care is taken to ;;; ensure that the gtyvars are in the same order that they appear in the ;;; context. This is needed to make dictionary conversion work right. (define (ast->gtype context type) (mlet (((gcontext env) (context->gcontext context '() '())) ((type env1) (type->gtype type env)) (gcontext-classes (arrange-gtype-classes env1 gcontext))) (**gtype gcontext-classes type))) ;;; This is similar except that the ordering of the tyvars is as defined in ;;; the data type. This is used only for instance declarations and allows ;;; for simple context implication checks. It also used by the signature ;;; of the dictionary variable. (define (ast->gtype/inst context type) (mlet (((type env) (type->gtype type '())) ((gcontext env1) (context->gcontext context '() env)) (gcontext-classes (arrange-gtype-classes env1 gcontext))) (**gtype gcontext-classes type))) ;;; This converts a context into gtype form [[class]]: a list of classes ;;; for each gtyvar. This returns the context and the gtyvar environment. (define (context->gcontext context gcontext env) (if (null? context) (values gcontext env) (mlet ((sym (context-tyvar (car context))) (class (class-ref-class (context-class (car context)))) ((n new-env) (ast->gtyvar sym env)) (old-context (get-gtyvar-context n gcontext)) (new-context (merge-single-class class old-context)) (new-gcontext (cons (tuple n new-context) gcontext))) (context->gcontext (cdr context) new-gcontext new-env)))) ;;; This assigns a gtyvar number to a tyvar name. (define (ast->gtyvar sym env) (let ((res (assq sym env))) (if (eq? res '#f) (let ((n (length env))) (values n (cons (tuple sym n) env))) (values (tuple-2-2 res) env)))) (define (get-gtyvar-context n gcontext) (cond ((null? gcontext) '()) ((eqv? n (tuple-2-1 (car gcontext))) (tuple-2-2 (car gcontext))) (else (get-gtyvar-context n (cdr gcontext))))) (define (type->gtype type env) (if (tyvar? type) (mlet (((n env1) (ast->gtyvar (tyvar-name type) env))) (values (**gtyvar n) env1)) (mlet (((types env1) (type->gtype/l (tycon-args type) env))) (values (**ntycon (tycon-def type) types) env1)))) (define (type->gtype/l types env) (if (null? types) (values '() env) (mlet (((type env1) (type->gtype (car types) env)) ((other-types env2) (type->gtype/l (cdr types) env1))) (values (cons type other-types) env2)))) (define (arrange-gtype-classes env gcontext) (arrange-gtype-classes-1 0 (length env) env gcontext)) (define (arrange-gtype-classes-1 m n env gcontext) (if (equal? m n) '() (cons (get-gtyvar-context m gcontext) (arrange-gtype-classes-1 (1+ m) n env gcontext)))) ;;; These routines convert gtypes back to ordinary types. (define (instantiate-gtype g) (mlet (((gtype _) (instantiate-gtype/newvars g))) gtype)) (define (instantiate-gtype/newvars g) (if (null? (gtype-context g)) (values (gtype-type g) '()) (let ((new-tyvars (create-new-tyvars (gtype-context g)))) (values (copy-gtype (gtype-type g) new-tyvars) new-tyvars)))) (define (create-new-tyvars ctxts) (if (null? ctxts) '() (let ((tyvar (**ntyvar))) (setf (ntyvar-context tyvar) (car ctxts)) (cons tyvar (create-new-tyvars (cdr ctxts)))))) (define (copy-gtype g env) (cond ((ntycon? g) (**ntycon (ntycon-tycon g) (map (lambda (g1) (copy-gtype g1 env)) (ntycon-args g)))) ((ntyvar? g) g) ((gtyvar? g) (list-ref env (gtyvar-varnum g))) ((const-type? g) (const-type-type g)))) ;;; ntypes may contain synonyms. These are expanded here. Only the ;;; top level synonym is expanded. (define (expand-ntype-synonym type) (if (and (ntycon? type) (synonym? (ntycon-tycon type))) (let ((syn (ntycon-tycon type))) (expand-ntype-synonym (expand-ntype-synonym-1 (synonym-body syn) (map (lambda (var val) (tuple var val)) (synonym-args syn) (ntycon-args type))))) type)) (define (expand-ntype-synonym-1 type env) (if (tyvar? type) (tuple-2-2 (assq (tyvar-name type) env)) (**ntycon (tycon-def type) (map (lambda (ty) (expand-ntype-synonym-1 ty env)) (tycon-args type))))) ;;; This is used in generalization. Note that ntyvars will remain when ;;; non-generic tyvars are encountered. (define (ntype->gtype ntype) (mlet (((res _) (ntype->gtype/env ntype '()))) res)) (define (ntype->gtype/env ntype required-vars) (mlet (((gtype env) (ntype->gtype-1 ntype required-vars))) (values (make gtype (type gtype) (context (map (lambda (x) (ntyvar-context x)) env))) env))) (define (ntype->gtype-1 ntype env) (let ((ntype (prune ntype))) (cond ((ntycon? ntype) (mlet (((args env1) (ntype->gtype/l (ntycon-args ntype) env))) (values (**ntycon (ntycon-tycon ntype) args) env1))) (else (ntyvar->gtyvar ntype env))))) (define (ntype->gtype/l types env) (if (null? types) (values '() env) (mlet (((type env1) (ntype->gtype-1 (car types) env)) ((types2 env2) (ntype->gtype/l (cdr types) env1))) (values (cons type types2) env2)))) (define (ntyvar->gtyvar ntyvar env) (if (non-generic? ntyvar) (values ntyvar env) (let ((l (list-pos ntyvar env))) (if (eq? l '#f) (values (**gtyvar (length env)) (append env (list ntyvar))) (values (**gtyvar l) env))))) (define (list-pos x l) (list-pos-1 x l 0)) (define (list-pos-1 x l n) (cond ((null? l) '#f) ((eq? x (car l)) n) (else (list-pos-1 x (cdr l) (1+ n))))) ;;; These utils are used in dictionary conversion. (define (**dsel/method class method dict-code) (let ((pos (locate-in-list method (class-method-vars class) 0))) (**tuple-sel (class-dict-size class) pos dict-code))) (define (**dsel/dict class dict-class dict-code) (let ((pos (locate-in-list dict-class (class-super* class) (class-n-methods class)))) (**tuple-sel (class-dict-size class) pos dict-code))) (define (locate-in-list var l pos) (if (null? l) (error "Locate in list failed") (if (eq? var (car l)) pos (locate-in-list var (cdr l) (1+ pos))))) ;;; These routines deal with contexts. A context is a list classes. ;;; A context is normalized whenever class is a superclass of another. (define (merge-contexts ctxt1 ctxt2) (if (null? ctxt1) ctxt2 (merge-single-class (car ctxt1) (merge-contexts (cdr ctxt1) ctxt2)))) ;;; This could perhaps avoid some consing but I don't imagine it would ;;; make much difference. (define (merge-single-class class ctxt) (cond ((null? ctxt) (list class)) ((eq? class (car ctxt)) ctxt) ((memq class (class-super* (car ctxt))) ctxt) ((memq (car ctxt) (class-super* class)) (merge-single-class class (cdr ctxt))) (else (cons (car ctxt) (merge-single-class class (cdr ctxt)))))) ;;; This determines if ctxt2 is contained in ctxt1. (define (context-implies? ctxt1 ctxt2) (or (null? ctxt2) (and (single-class-implies? ctxt1 (car ctxt2)) (context-implies? ctxt1 (cdr ctxt2))))) (define (single-class-implies? ctxt class) (and (not (null? ctxt)) (or (memq class ctxt) (super-class-implies? ctxt class)))) (define (super-class-implies? ctxt class) (and (not (null? ctxt)) (or (memq class (class-super* (car ctxt))) (super-class-implies? (cdr ctxt) class)))) ;;; This looks at the context of a full signature. (define (full-context-implies? ctxt1 ctxt2) (or (null? ctxt1) (and (context-implies? (car ctxt1) (car ctxt2)) (full-context-implies? (cdr ctxt1) (cdr ctxt2))))) ;;; This is used to avoid type circularity on unification. (define (occurs-in-type tyvar type) ; Cardelli algorithm (let ((type (prune type))) (if (ntyvar? type) (eq? type tyvar) (occurs-in-type/l tyvar (ntycon-args type))))) ; Does a tyvar occur in a list of types? (define (occurs-in-type/l tyvar types) (if (null? types) '#f (or (occurs-in-type tyvar (car types)) (occurs-in-type/l tyvar (cdr types))))) (define-integrable (non-generic? tyvar) (occurs-in-type/l tyvar (dynamic *non-generic-tyvars*))) (define (collect-tyvars ntype) (collect-tyvars-1 ntype '())) (define (collect-tyvars-1 ntype vars) (let ((ntype (prune ntype))) (if (ntyvar? ntype) (if (or (memq ntype vars) (non-generic? ntype)) vars (cons ntype vars)) (collect-tyvars/l-1 (ntycon-args ntype) vars)))) (define (collect-tyvars/l types) (collect-tyvars/l-1 types '())) (define (collect-tyvars/l-1 types vars) (if (null? types) vars (collect-tyvars/l-1 (cdr types) (collect-tyvars-1 (car types) vars)))) ;;; Random utilities (define (decl-var decl) (var-ref-var (var-pat-var (valdef-lhs decl))))