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))
|