git.fiddlerwoaroof.com
Raw Blame History
;;; Before classes are converted, the super class relation is computed.
;;; This sets up the super and super* field of each class and
;;; checks for the following errors:
;;;  Wrong tyvar in context
;;;  cyclic class structure
;;;  Non-class in context

(define (compute-super-classes modules)
  (let ((all-classes '()))
    (walk-modules modules
     (lambda ()       
      (dolist (c (module-classes *module*))
       (remember-context c
	(with-slots class-decl (super-classes class class-var) c
	  (let* ((def (class-ref-class class))
		 (local-ctxts '())
		 (super '()))
	    (dolist (context super-classes)
              (with-slots context (class tyvar) context
		(when (not (eq? class-var tyvar))
		  (signal-super-class-tyvar-error class class-var tyvar))
		(resolve-class class)
		(let ((super-def (class-ref-class class)))
		  (when (not (eq? super-def *undefined-def*))
		    (push super-def super)
		    (when (eq? *unit* (def-unit super-def))
		      (push super-def local-ctxts))))))
	    (update-slots class def
	       (super super)
	       (tyvar class-var))
	    (push (cons def local-ctxts) all-classes)))))))
    (multiple-value-bind (status sorted) (topsort all-classes)
      (when (eq? status 'cyclic)
	(signal-cyclic-class-structure sorted))
      (dolist (c sorted)
        (let* ((super (class-super c))
	       (super* super))
	   (dolist (s super)
	     (setf super* (set-union super* (class-super* s)))
	     (setf (class-super* c) super*)))))))

(define (signal-super-class-tyvar-error class class-var tyvar)
  (recoverable-error 'super-class-tyvar-error
    "The context for class ~A must only refer to type variable ~A.~%~
     Type variable ~A cannot be used here."
    (class-ref-name class) class-var tyvar))

(define (signal-cyclic-class-structure classes)
  (fatal-error 'cyclic-class-structure
    "There is a cycle in the superclass relation involving these classes:~%~a"
    classes))


;;;  This sets up the following fields in the class entry:
;;;    instances '()
;;;    defaults = ast for defaults
;;;    kind
;;;    methods
;;;    signatures
;;;    method-vars
;;;    selectors
;;;  Each method is initialized with
;;;    class
;;;    signature
;;;    type
;;;  Errors detected:
;;;   signature doesnt reference class 

(define (class->def class-decl)
 (remember-context class-decl
   (let* ((class (class-ref-class (class-decl-class class-decl)))
	  (decls (class-decl-decls class-decl)))
     (setf (class-instances class) '())
     (setf (class-kind class) (find-class-kind class))
     (init-methods class decls)  ; sets up defaults, method signatures
     (setf (class-n-methods class) (length (class-method-vars class)))
     (setf (class-dict-size class)
	   (+ (class-n-methods class) (length (class-super* class))))
     class)))

(define (find-class-kind class)
  (cond ((not (module-prelude? *module*))
	 'other)
	((memq class
	       (list (core-symbol "Eq") (core-symbol "Ord")
		     (core-symbol "Text") (core-symbol "Binary")
		     (core-symbol "Ix") (core-symbol "Enum")))
	 'Standard)
	((memq class
	       (list (core-symbol "Num") (core-symbol "Real")
		     (core-symbol "Integral") (core-symbol "Fractional")
		     (core-symbol "Floating") (core-symbol "RealFrac")
		     (core-symbol "RealFloat")))
		     'Numeric)
	(else
	 'other)))

(define (init-methods class decls)
 (let* ((tyvar (class-tyvar class))
        (class-context (**context (**class/def class) tyvar)))
  (dolist (decl decls)
   (remember-context decl
    (cond ((is-type? 'signdecl decl)
	   (let* ((signature (signdecl-signature decl))
		  (vars (resolve-signature signature)))
	     (when (not (memq tyvar vars))
	       (signal-class-sig-ignores-type signature))
	     ;; Note: signature does not include defined class yet
	     (dolist (context (signature-context signature))
               (when (eq? tyvar (context-tyvar context))
		 (signal-method-constrains-class-tyvar context)))
	     (setf signature (rename-class-sig-vars signature tyvar))
	     (let ((gtype (ast->gtype (cons class-context
					    (signature-context signature))
				      (signature-type signature))))
 	       (dolist (var-ref (signdecl-vars decl))
	         (let ((var (var-ref-var var-ref)))
		   (setf (var-type var) gtype)
		   (setf (method-var-method-signature var) signature))))))
	  (else  ; decl must be a default definition
	   (let ((vars (collect-pattern-vars (valdef-lhs decl))))
	     (dolist (var-ref vars)
	       (resolve-var var-ref)
               (let* ((method-name (var-ref-name var-ref))
		      (method-var (var-ref-var var-ref)))
		 (when (not (eq? method-var *undefined-def*))
		  (if (and (method-var? method-var)
			   (eq? (method-var-class method-var) class))
		   (let ((default-var
			   (make-new-var
			     (string-append
			       "default-"
			       (symbol->string (def-name method-var))))))
		     (setf (var-ref-var var-ref) default-var)
		     (setf (var-ref-name var-ref) (def-name default-var))
		     (when (not (eq? (method-var-default method-var) '#f))
		       (signal-multiple-definition-of-default method-name))
		     (setf (method-var-default method-var) default-var)
		     (let* ((sig (method-var-method-signature method-var))
			    (context (cons class-context
					   (signature-context sig)))
			    (new-sig (**signature context
						  (signature-type sig))))
		       (add-new-module-signature default-var new-sig)))
		   (signal-default-not-in-class method-var class)))))
	     (add-new-module-decl decl))))))))

(define (signal-class-sig-ignores-type signature)
  (phase-error 'class-sig-ignores-type
    "The method signature ~a does not reference the overloaded type."
    signature))


;;; *** I don't understand this message.

(define (signal-method-constrains-class-tyvar context)
  (phase-error 'method-constrains-class-tyvar
    "Individual methods may not further constrain a class: ~A" context))


;;; *** I don't understand this message.

(define (signal-multiple-definition-of-default method-name)
  (phase-error 'multiple-definition-of-default
   "More that one default for ~A."
   method-name))


;;; *** I don't understand this message.

(define (signal-default-not-in-class method-var class)
  (phase-error 'default-not-in-class
	       "~A is not a method in class ~A."
	       method-var class))

	   
(define (create-selector-functions class)
  (let ((res '()))
    (dolist (c (cons class (class-super* class)))
      (dolist (m (class-method-vars c))
	(let* ((var (make-new-var
	        (string-append "sel-"
			       (symbol->string (def-name class))
			       "/"
			       (symbol->string (def-name m)))))
	       (sel-body (create-selector-code class m)))
	  (setf (var-selector-fn? var) '#t)
	  (push (tuple m var) res)
	  (when (not (eq? (module-type *module*) 'interface))
	     (add-new-module-def var sel-body)))))
    res))

(define (create-selector-code c m)
  (let ((var (create-local-definition '|d|)))
    (setf (var-force-strict? var) '#t)
    (let ((body (create-selector-code-1 c m (**var/def var))))
      (**lambda/pat (list (**var-pat/def var)) body))))

(define (create-selector-code-1 class method d)
  (let ((mcl (method-var-class method)))
    (cond ((eq? mcl class)
	   (**dsel/method class method d))
	  (else
	   (**dsel/method mcl method (**dsel/dict class mcl d))))))
	     
;;; The following code is for the alpha conversion of method
;;; signatures.  The class tyvar is unchanged; all others are renamed.
;;; This is needed because all method types are combined to form the
;;; dictionary signature and aliasing among different tyvars should be
;;; prevented.

(define (rename-class-sig-vars signature tyvar)
  (mlet (((new-context env1)
	  (rename-context-vars (signature-context signature)
			       (list (tuple tyvar tyvar))))
	 ((new-type _)
	  (rename-type-vars (signature-type signature) env1)))
      (**signature new-context new-type)))

(define (rename-context-vars contexts env)
  (if (null? contexts)
      (values '() env)
      (mlet (((new-tyvar env1)
	      (rename-sig-tyvar (context-tyvar (car contexts)) env))
	     ((rest env2)
	      (rename-context-vars (cdr contexts) env1)))
       (values (cons (**context (context-class (car contexts)) new-tyvar) rest)
	       env2))))

(define (rename-type-vars type env)
  (if (tyvar? type)
      (mlet (((tyvar env1)
	      (rename-sig-tyvar (tyvar-name type) env)))
	 (values (**tyvar tyvar) env1))
      (mlet (((new-types env1) (rename-type-vars/l (tycon-args type) env)))
        (values (**tycon/def (tycon-def type) new-types) env1))))

(define (rename-type-vars/l types env)
  (if (null? types)
      (values '() env)
      (mlet (((type1 env1) (rename-type-vars (car types) env))
	     ((new-types env2) (rename-type-vars/l (cdr types) env1)))
          (values (cons type1 new-types) env2))))

(define (rename-sig-tyvar tyvar env)
  (let ((res (assq tyvar env)))
    (if (eq? res '#f)
	(let ((new-tyvar (gentyvar (symbol->string tyvar))))
	  (values new-tyvar (cons (tuple tyvar new-tyvar) env)))
	(values (tuple-2-2 res) env))))

(define *tyvar-counter* 0)

;;; This generates a new interned tyvar name

(define (gentyvar root)
  (incf *tyvar-counter*)
  (string->symbol (format '#f "~A-~A" root *tyvar-counter*)))