git.fiddlerwoaroof.com
tdecl/instance.scm
4e987026
 ;;; tdecl/instance.scm
 
 ;;; Convert an instance decl to a definition
 
 ;;; The treatment of instances is more complex than the treatment of other
 ;;; type definitions due to the possibility of derived instances.
 ;;; Here's the plan:
 ;;;  a) instance-decls are converted to instance structures.  The type
 ;;;     information is verified but the decls are unchanged.
 ;;;  b) All instances are linked into the associated classes.
 ;;;  c) Derived instances are generated.
 ;;;  d) Instance dictionaries are generated from the decls in the instances.
 ;;;     
 
 ;;; Instances-decl to instance definition conversion
 ;;; Errors detected:
 ;;;  Class must be a class
 ;;;  Data type must be an alg
 ;;;  Tyvars must be distinct
 ;;;  Correct number of tyvars
 ;;;  Context applies only to tyvars in simple
 ;;;  C-T restriction
 
 ;;; Needs work for interface files.
 
 (define (instance->def inst-decl)
  (recover-errors '#f
   (remember-context inst-decl
     (with-slots instance-decl (context class simple decls) inst-decl
       (resolve-type simple)
       (resolve-class class)
       (let ((alg-def (tycon-def simple))
 	    (class-def (class-ref-class class)))
         (when (not (algdata? (tycon-def simple)))
 	  (signal-datatype-required (tycon-def simple)))
         (let ((tyvars (simple-tyvar-list simple)))
 	  (resolve-signature-aux tyvars context)
 	  (when (and (not (eq? *module-name* (def-module alg-def)))
 		     (not (eq? *module-name* (def-module class-def))))
 	    (signal-c-t-rule-violation class-def alg-def))
 	  (let ((old-inst (lookup-instance alg-def class-def)))
 	    (when (and (not (eq? old-inst '#f))
 		       (not (instance-special? old-inst)))
 	    (signal-multiple-instance class-def alg-def))
 	    (let ((inst (new-instance class-def alg-def tyvars)))
 	      (setf (instance-context inst) context)
 	      (setf (instance-decls inst) decls)
 	      (setf (instance-ok? inst) '#t)
 	      inst))))))))
 
 (define (signal-datatype-required def)
   (phase-error 'datatype-required
     "The synonym type ~a cannot be declared as an instance."
     (def-name def)))
 
 (define (signal-c-t-rule-violation class-def alg-def)
   (phase-error 'c-t-rule-violation
     "Instance declaration does not appear in the same module as either~%~
      the class ~a or type ~a."
     class-def alg-def))
 
 (define (signal-multiple-instance class-def alg-def)
   (phase-error 'multiple-instance
     "The type ~a has already been declared to be an instance of class ~a."
     alg-def class-def))
 
 ;;; This generates the dictionary for each instance and makes a few final
 ;;; integrity checks in the instance context.  This happens after derived
 ;;; instances are inserted.
 
 (define (expand-instance-decls inst)
   (when (instance-ok? inst)
     (check-inst-type inst)
     (with-slots instance (class algdata dictionary decls context tyvars) inst
      (let ((simple (**tycon/def algdata (map (function **tyvar) tyvars))))
       (setf (instance-gcontext inst)
 	    (gtype-context (ast->gtype/inst context simple)))
       (with-slots class (super* method-vars) class
 	;; Before computing signatures uniquify tyvar names to prevent
         ;; collision with method tyvar names
 	(let ((new-tyvars (map (lambda (tyvar) (tuple tyvar (gentyvar "tv")))
 			       (instance-tyvars inst))))
 	  (setf (instance-tyvars inst) (map (function tuple-2-2) new-tyvars))
 	  (setf (instance-context inst)
    	    (map (lambda (c)
                   (**context (context-class c)
 			     (tuple-2-2 (assq (context-tyvar c) new-tyvars))))
 		 (instance-context inst))))
 	;; Now walk over the decls & rename each method with a unique name
 	;; generated by combining the class, type, and method.  Watch for
 	;; multiple defs of methods and add defaults after all decls have
 	;; been scanned.
 	(let ((methods-used '())
 	      (new-instance-vars (map (lambda (m)
 					(tuple m (method-def-var m inst)))
 				      method-vars)))
           (dolist (decl decls)
             (setf methods-used
   	      (process-instance-decl decl new-instance-vars methods-used)))
 	  ;; now add defaults when needed
 	  (dolist (m-v new-instance-vars)
            (let* ((method-var (tuple-2-1 m-v))
 		  (definition-var (tuple-2-2 m-v))
 		  (signature (generate-method-signature inst method-var '#t)))
             (if (memq method-var methods-used)
 		(add-new-module-signature definition-var signature)
 		(let ((method-body
 		       (if (eq? (method-var-default method-var) '#f)
 			   (**abort (format '#f
      "No method declared for method ~A in instance ~A(~A)."
                               method-var class algdata))
 			   (**var/def (method-var-default method-var)))))
 		  (add-new-module-def definition-var method-body)
 		  (add-new-module-signature definition-var signature)))))
 	  (setf (instance-methods inst) new-instance-vars)
 	  (add-new-module-def dictionary
 	     (**tuple/l (append (map (lambda (m-v)
 				       (dict-method-ref
 					(tuple-2-1 m-v)	(tuple-2-2 m-v)	inst))
 				     new-instance-vars)
 				(map (lambda (c)
 				       (get-class-dict algdata c))
 				     super*))))
 	  (let ((dict-sig (generate-dictionary-signature inst)))
 	    (add-new-module-signature dictionary dict-sig))
 	  (setf (instance-decls inst) '())))))))
 
 (define (dict-method-ref method-var inst-var inst)
   (if (null? (signature-context (method-var-method-signature method-var)))
       (**var/def inst-var)
       (let* ((sig (generate-method-signature inst method-var '#f))
 	     (ctxt (signature-context sig))
 	     (ty (signature-type sig)))
 	(make overloaded-var-ref
 	      (sig (ast->gtype ctxt ty))
 	      (var inst-var)))))
 
 (define (get-class-dict algdata class)
   (let ((inst (lookup-instance algdata class)))
     (if (eq? inst '#f)
 	(**abort "Missing super class")
 	(**var/def (instance-dictionary inst)))))
 					 
 (define (process-instance-decl decl new-instance-vars methods-used)
   (if (valdef? decl)
       (rename-instance-decl decl new-instance-vars methods-used)
       (begin
        (dolist (a (annotation-decls-annotations decl))
 	(cond ((annotation-value? a)
 	       (recoverable-error 'misplaced-annotation
 		      "Misplaced annotation: ~A~%" a))
 	      (else
 	       (dolist (name (annotation-decl-names a))
                  (attach-method-annotation
 		  name (annotation-decl-annotations a) new-instance-vars)))))
        methods-used)))
 
 (define (attach-method-annotation name annotations vars)
   (cond ((null? vars)
 	 (signal-no-method name))
 	((eq? name (def-name (tuple-2-1 (car vars))))
 	 (setf (var-annotations (tuple-2-2 (car vars)))
 	       (append annotations (var-annotations (tuple-2-2 (car vars))))))
 	(else (attach-method-annotation name annotations (cdr vars)))))
 
 (define (signal-no-method name)
   (recoverable-error 'no-method "~A is not a method in this class.~%"
       name))
 
 (define (rename-instance-decl decl new-instance-vars methods-used)
   (let ((decl-vars (collect-pattern-vars (valdef-lhs decl))))
     (dolist (var decl-vars)
       (resolve-var var)
       (let ((method (var-ref-var var)))
         (when (not (eq? method *undefined-def*))
          (let ((m-v (assq method new-instance-vars)))
           (cond ((memq method methods-used)
 		 (signal-multiple-instance-def method))
 		((eq? m-v '#f)
 		 (signal-not-in-class method))
 		(else
 		 (setf (var-ref-name var) (def-name (tuple-2-2 m-v)))
 		 (setf (var-ref-var var) (tuple-2-2 m-v))
 		 (push (tuple-2-1 m-v) methods-used)))))))
     (add-new-module-decl decl)
     methods-used))
 
 (define (signal-multiple-instance-def method)
   (phase-error 'multiple-instance-def
     "The instance declaration has multiple definitions of the method ~a."
      method))
 
 (define (signal-not-in-class method)
   (phase-error 'not-in-class
     "The instance declaration includes a definition for ~a,~%~
      which is not one of the methods for this class."
     method))
 
 
 (define (method-def-var method-var inst)
   (make-new-var
     (string-append "i-"
 		   (symbol->string (print-name (instance-class inst))) "-"
 		   (symbol->string (print-name (instance-algdata inst))) "-"
 		   (symbol->string (def-name method-var)))))
 
 (define (generate-method-signature inst method-var keep-method-context?)
   (let* ((simple-type (make-instance-type inst))
 	 (class-context (instance-context inst))
 	 (class-tyvar (class-tyvar (instance-class inst)))
 	 (signature (method-var-method-signature method-var)))
     (make signature
 	  (context (if keep-method-context?
 		       (append class-context (signature-context signature))
 		       class-context))
 	  (type (substitute-tyvar (signature-type signature) class-tyvar
 				  simple-type)))))
 
 (define (make-instance-type inst)
   (**tycon/def (instance-algdata inst)
 	       (map (function **tyvar) (instance-tyvars inst))))
 
 (define (generate-dictionary-signature inst)
   (**signature (sort-inst-context-by-tyvar
 		(instance-context inst) (instance-tyvars inst))
 	       (generate-dictionary-type inst (make-instance-type inst))))
 
 (define (sort-inst-context-by-tyvar ctxt tyvars)
   (concat (map (lambda (tyvar)
 		 (extract-single-context tyvar ctxt)) tyvars)))
 
 (define (extract-single-context tyvar ctxt)
   (if (null? ctxt)
       '()
       (let ((rest (extract-single-context tyvar (cdr ctxt))))
 	(if (eq? tyvar (context-tyvar (car ctxt)))
 	    (cons (car ctxt) rest)
 	    rest))))
 
 (define (generate-dictionary-type inst simple)
   (let* ((class (instance-class inst))
 	 (algdata (instance-algdata inst))
 	 (tyvar (class-tyvar class)))
     (**tuple-type/l (append (map (lambda (method-var)
 				   ;; This ignores the context associated
 				   ;; with a method
 				   (let ((sig (method-var-method-signature
 					        method-var)))
 				     (substitute-tyvar (signature-type sig)
 						       tyvar
 						       simple)))
 				 (class-method-vars class))
 			    (map (lambda (super-class)
 				   (generate-dictionary-type
 				    (lookup-instance algdata super-class)
 				    simple))
 				 (class-super* class))))))
 
 ;;; Checks performed here:
 ;;;  Instance context must include the following:
 ;;;     Context associated with data type
 ;;;     Context associated with instances for each super class
 ;;;  All super class instances must exist
 
 (define (check-inst-type inst)
    (let* ((class (instance-class inst))
 	  (algdata (instance-algdata inst))
 	  (inst-context (instance-gcontext inst))
 	  (alg-context (gtype-context (algdata-signature algdata))))
      (when (not (full-context-implies? inst-context alg-context))
        (signal-instance-context-needs-alg-context algdata))
      (dolist (super-c (class-super class))
        (let ((super-inst (lookup-instance algdata super-c)))
 	 (cond ((eq? super-inst '#f)
 		(signal-no-super-class-instance class algdata super-c))
 	       (else
 		(when (not (full-context-implies?
 			     inst-context (instance-context super-inst)))
 		  (signal-instance-context-insufficient-for-super
 		    class algdata super-c))))))
      ))
 
 (define (signal-instance-context-needs-alg-context algdata)
   (phase-error 'instance-context-needs-alg-context
     "The instance context needs to include context defined for data type ~A."
     algdata))
 
 (define (signal-no-super-class-instance class algdata super-c)
   (fatal-error 'no-super-class-instance
     "The instance ~A(~A) requires that the instance ~A(~A) be provided."
     class algdata super-c algdata))
 
 (define (signal-instance-context-insufficient-for-super class algdata super-c)
   (phase-error 'instance-context-insufficient-for-super
     "Instance ~A(~A) does not imply super class ~A instance context."
     class algdata super-c))