home *** CD-ROM | disk | FTP | other *** search
Text File | 1994-09-27 | 13.4 KB | 362 lines | [TEXT/CCL2] |
- ;;; This deals with declarations (let & letrec). The input is a list of
- ;;; declarations (valdefs) which may contain recursive-decl-groups, as
- ;;; introduced in dependency analysis. This function alters the list
- ;;; of non-generic type variables. Expressions containing declarations
- ;;; need to rebind the non-generic list around the decls and all expressions
- ;;; within their scope.
-
- ;;; This returns an updated decl list with recursive decl groups removed.
-
- (define (type-decls decls)
- (cond ((null? decls)
- '())
- ((is-type? 'recursive-decl-group (car decls))
- (let ((d (recursive-decl-group-decls (car decls))))
- (type-recursive d)
- (append d (type-decls (cdr decls)))))
- (else
- (type-non-recursive (car decls))
- (cons (car decls)
- (type-decls (cdr decls))))))
-
- ;;; This typechecks a mutually recursive group of declarations (valdefs).
- ;;; Generate a monomorphic variable for each declaration and unify it with
- ;;; the lhs of the decl. The variable all-vars collects all variables defined
- ;;; by the declaration group. Save the values of placeholders and ng-list
- ;;; before recursing.
-
- ;;; The type of each variable is marked as recursive.
-
- (define (type-recursive decls)
- (let ((old-ng (dynamic *non-generic-tyvars*))
- (old-placeholders (dynamic *placeholders*))
- (all-vars '())
- (new-tyvars '())
- (decls+tyvars '()))
- ;; on a type error set all types to `a' and give up.
- (setf (dynamic *placeholders*) '())
- (recover-type-error
- (lambda (r)
- (make-dummy-sigs decls)
- (setf (dynamic *placeholders*) old-placeholders)
- (funcall r))
- ;; Type the lhs of each decl and then mark each variable bound
- ;; in the decl as recursive.
- (dolist (d decls)
- (add-error-handlers d)
- (fresh-type lhs-type
- (push lhs-type (dynamic *non-generic-tyvars*))
- (push lhs-type new-tyvars)
- (type-decl-lhs d lhs-type)
- (push (tuple d lhs-type) decls+tyvars))
- (dolist (var-ref (collect-pattern-vars (valdef-lhs d)))
- (let ((var (var-ref-var var-ref)))
- (push var all-vars)
- (setf (var-type var)
- (make recursive-type (type (var-type var))
- (placeholders '())
- (rsig (var-signature var)))))))
-
- ;;; This types the decl right hand sides. Each rhs type is unified with the
- ;;; tyvar corresponding to the lhs. Before checking the signatures, the
- ;;; ng-list is restored.
-
- (dolist (d decls+tyvars)
- (let ((rhs-type (type-decl-rhs (tuple-2-1 d)))
- (lhs-type (tuple-2-2 d)))
- (type-unify lhs-type rhs-type
- (type-mismatch (tuple-2-1 d)
- "Decl type mismatch" lhs-type rhs-type))))
- (setf (dynamic *non-generic-tyvars*) old-ng)
- (let ((sig-contexts (check-user-signatures all-vars)))
-
- ;;; This generalizes the signatures of recursive decls. First, the
- ;;; context of the declaration group is computed. Any tyvar in the
- ;;; bodies with a non-empty context must appear in all signatures that
- ;;; are non-ambiguous.
-
- (let* ((all-tyvars (collect-tyvars/l new-tyvars))
- (overloaded-tyvars '()))
- (dolist (tyvar all-tyvars)
- (unless (non-generic? tyvar)
- (when (ntyvar-context tyvar)
- (push tyvar overloaded-tyvars))))
- (reconcile-sig-contexts overloaded-tyvars sig-contexts)
- ;; We should probably also emit a warning about inherently
- ;; ambiguous decls.
- (when (and overloaded-tyvars
- (apply-pattern-binding-rule? decls))
- (setf (dynamic *non-generic-tyvars*)
- (do-pattern-binding-rule
- decls overloaded-tyvars old-ng))
- (setf overloaded-tyvars '()))
- ;;; Hack Hack Kludge!!
- (let ((v '()))
- (dolist (tyvar overloaded-tyvars)
- (setf (ntyvar-context tyvar)
- (remove-dynamic-type-context (ntyvar-context tyvar)))
- (when (ntyvar-context tyvar)
- (push tyvar v)))
- (setf overloaded-tyvars v))
- ;; The next step is to compute the signatures of the defined
- ;; variables and to define all recursive placeholders. When
- ;; there is no context the placeholders become simple var refs.
- ;; and the types are simply converted.
- (cond ((null? overloaded-tyvars)
- (dolist (var all-vars)
- (let ((r (var-type var)))
- (setf (var-type var) (recursive-type-type (var-type var)))
- (dolist (p (recursive-type-placeholders r))
- (setf (recursive-placeholder-exp p)
- (**var/def var)))
- (generalize-type var))))
- ;; When the declaration has a context things get very hairy.
- ;; First, grap the recursive placeholders before generalizing the
- ;; types.
- (else
- ;; Mark the overloaded tyvars as read-only. This prevents
- ;; signature unification from changing the set of tyvars
- ;; defined in the mapping.
- (dolist (tyvar overloaded-tyvars)
- ; (when (memq (core-symbol "DynamicType")
- ; (ntyvar-context tyvar))
- ; (phase-error 'lazy-implementor
- ; "A dynamic type has leaked into a recursive declaration: ~A" decls))
- (setf (ntyvar-read-only? tyvar) '#t))
- (let ((r-placeholders '()))
- (dolist (var all-vars)
- (let ((rt (var-type var)))
- (dolist (p (recursive-type-placeholders rt))
- (push p r-placeholders))
- (setf (var-type var) (recursive-type-type rt))))
- ;; Now compute a signature for each definition and do dictionary
- ;; conversion. The var-map defines the actual parameter associated
- ;; with each of the overloaded tyvars.
- (let ((var-map (map (lambda (decl)
- (tuple (decl-var decl)
- (generalize-overloaded-type
- decl overloaded-tyvars)))
- decls)))
- ;; Finally discharge each recursive placeholder.
- (dolist (p r-placeholders)
- (let ((ref-to (recursive-placeholder-var p))
- (decl-from
- (search-enclosing-decls
- (recursive-placeholder-enclosing-decls p)
- decls)))
- (setf (recursive-placeholder-exp p)
- (recursive-call-code decl-from ref-to var-map)))
- )))))
- (setf (dynamic *placeholders*)
- (process-placeholders
- (dynamic *placeholders*) old-placeholders decls)))))))
-
- ;;; Non-recursive decls are easier. Save the placeholders, use a fresh type
- ;;; for the left hand side, check signatures, and generalize.
-
- (define (type-non-recursive decl)
- (remember-context decl
- (add-error-handlers decl)
- (fresh-type lhs-type
- (let ((old-placeholders (dynamic *placeholders*))
- (all-vars (map (lambda (x) (var-ref-var x))
- (collect-pattern-vars (valdef-lhs decl)))))
- (setf (dynamic *placeholders*) '())
- (recover-type-error
- (lambda (r)
- (make-dummy-sigs (list decl))
- (setf (dynamic *placeholders*) old-placeholders)
- (funcall r))
- (type-decl-lhs decl lhs-type)
- (let ((rhs-type (type-decl-rhs decl)))
- (type-unify lhs-type rhs-type
- (type-mismatch decl
- "Decl type mismatch" lhs-type rhs-type)))
- (check-user-signatures all-vars)
- (let ((all-tyvars (collect-tyvars lhs-type))
- (overloaded-tyvars '()))
- (dolist (tyvar all-tyvars)
- (when (ntyvar-context tyvar)
- (push tyvar overloaded-tyvars)))
- (when (and overloaded-tyvars
- (apply-pattern-binding-rule? (list decl)))
- (setf (dynamic *non-generic-tyvars*)
- (do-pattern-binding-rule
- (list decl) overloaded-tyvars (dynamic *non-generic-tyvars*)))
- (setf overloaded-tyvars '()))
- (if (null? overloaded-tyvars)
- (dolist (var all-vars)
- (generalize-type var))
- (generalize-overloaded-type decl '()))
- (setf (dynamic *placeholders*)
- (process-placeholders
- (dynamic *placeholders*) old-placeholders (list decl)))))))))
-
- ;;; These functions type check definition components.
-
- ;;; This unifies the type of the lhs pattern with a type variable.
-
- (define (type-decl-lhs object type)
- (dynamic-let ((*enclosing-decls* (cons object (dynamic *enclosing-decls*))))
- (remember-context object
- (type-check valdef lhs pat-type
- (type-unify type pat-type #f)))))
-
-
- ;;; This types the right hand side. The *enclosing-decls* variable is
- ;;; used to keep track of which decl the type checker is inside. This
- ;;; is needed for both defaulting (to find which module defaults apply)
- ;;; and recursive types to keep track of the dictionary parameter variables
- ;;; for recursive references.
-
- (define *remember-valdef* '())
-
- (define (type-decl-rhs object)
- (dynamic-let ((*enclosing-decls* (cons object (dynamic *enclosing-decls*))))
- (remember-context object
- (setf *remember-valdef* object) ; sleazy way to get a better error msg
- (type-check/unify-list valdef definitions res-type
- (type-mismatch/list object
- "Definitions have different types")
- res-type))))
-
- ;;; This is similar to typing lambda.
-
- (define-type-checker single-fun-def
- (fresh-monomorphic-types (length (single-fun-def-args object)) tyvars
- (type-check/list single-fun-def args arg-types
- (unify-list tyvars arg-types)
- (type-check/decls single-fun-def where-decls
- (type-check/unify-list single-fun-def rhs-list rhs-type
- (type-mismatch/list
- (make valdef (lhs (valdef-lhs *remember-valdef*))
- (definitions (list object)))
- "Guarded expressions have incompatible types")
- (return-type object (**arrow/l-2 arg-types rhs-type)))))))
-
-
- ;;; These functions are part of the generalization process.
-
- ;;; This function processes user signature declarations for the set of
- ;;; variables defined in a declaration. Since unification of one signature
- ;;; may change the type associated with a previously verified signature,
- ;;; signature unification is done twice unless only one variable is
- ;;; involved. The context of the signatures is returned to compare
- ;;; with the overall context of the declaration group.
-
- (define (check-user-signatures vars)
- (cond ((null? (cdr vars))
- (let* ((var (car vars))
- (sig (var-signature var)))
- (if (eq? sig '#f)
- '()
- (list (tuple var (check-var-signature var sig))))))
- (else
- (let ((sigs '()))
- (dolist (var vars)
- (let ((sig (var-signature var)))
- (unless (eq? sig '#f)
- (check-var-signature var sig))))
- (dolist (var vars)
- (let ((sig (var-signature var)))
- (unless (eq? sig '#f)
- (push (tuple var (check-var-signature var sig)) sigs))))
- sigs))))
-
-
- (define (check-var-signature var sig)
- (mlet (((sig-type sig-vars) (instantiate-gtype/newvars sig)))
- (dolist (tyvar sig-vars)
- (setf (ntyvar-read-only? tyvar) '#t))
- (type-unify (remove-recursive-type (var-type var)) sig-type
- (signature-mismatch var))
- (dolist (tyvar sig-vars)
- (setf (ntyvar-read-only? tyvar) '#f))
- sig-vars))
-
- ;;; Once the declaration context is computed, it must be compared to the
- ;;; contexts given by the user. All we need to check is that all tyvars
- ;;; constrained in the user signatures are also in the decl-context.
- ;;; All user supplied contexts are correct at this point - we just need
- ;;; to see if some ambiguous portion of the context exists.
-
- ;;; This error message needs work. We need to present the contexts.
-
- (define (reconcile-sig-contexts overloaded-tyvars sig-contexts)
- (dolist (sig sig-contexts)
- (let ((sig-vars (tuple-2-2 sig)))
- (dolist (d overloaded-tyvars)
- (when (not (memq d sig-vars))
- (type-error
- "Declaration signature has insufficiant context in declaration~%~A~%"
- (tuple-2-1 sig)))))))
-
- ;;; This is used for noisy type inference
-
- (define (report-typing var)
- (when (memq 'type (dynamic *printers*))
- (let* ((name (symbol->string (def-name var))))
- (when (not (or (string-starts? "sel-" name)
- (string-starts? "i-" name)
- (string-starts? "default-" name)
- (string-starts? "dict-" name)))
- (format '#t "~A :: ~A~%" var (var-type var))))))
-
- ;;; This is used during error recovery. When a type error occurs, all
- ;;; variables defined in the enclosing declaration are set to type `a'
- ;;; and typing is resumed.
-
- ;;; 12/93 - Changed this to use user supplied signatures when present.
-
- (define (make-dummy-sigs decls)
- (let ((dummy-type (make gtype (context '(()))
- (type (**gtyvar 0)))))
- (dolist (d decls)
- (dolist (var-ref (collect-pattern-vars (valdef-lhs d)))
- (let ((var (var-ref-var var-ref)))
- (if (eq? (var-signature var) '#f)
- (setf (var-type var) dummy-type)
- (setf (var-type var) (var-signature var))))))))
-
-
- ;;; This is used to generalize the variable signatures. If there is
- ;;; an attached signature, the signature is used. Otherwise the ntype
- ;;; is converted to a gtype.
-
- (define (generalize-type var)
- (if (eq? (var-signature var) '#f)
- (setf (var-type var) (ntype->gtype (var-type var)))
- (setf (var-type var) (var-signature var)))
- (report-typing var))
-
- ;;; For overloaded types, it is necessary to map the declaration context
- ;;; onto the generalized type. User signatures may provide different but
- ;;; equivilant contexts for different declarations in a decl goup.
-
- ;;; The overloaded-vars argument allows ambiguous contexts. This is not
- ;;; needed for non-recursive vars since the context cannot be ambiguous.
-
- (define (generalize-overloaded-type decl overloaded-vars)
- (let* ((var (decl-var decl))
- (sig (var-signature var))
- (new-tyvars '()))
- (cond ((eq? sig '#f)
- (mlet (((gtype tyvars)
- (ntype->gtype/env (var-type var) overloaded-vars)))
- (setf (var-type var) gtype)
- (setf new-tyvars tyvars)))
- (else
- (mlet (((ntype tyvars) (instantiate-gtype/newvars sig)))
- (unify ntype (var-type var))
- (setf (var-type var) sig)
- (setf new-tyvars (prune/l tyvars)))))
- (report-typing var)
- (dictionary-conversion/definition decl new-tyvars)
- new-tyvars))
-
- (define (remove-recursive-type ty)
- (if (recursive-type? ty)
- (recursive-type-type ty)
- ty))
-
-