summaryrefslogtreecommitdiff
path: root/tdecl/instance.scm
blob: 18663397c7a9dec51eeab7ddc0c6f232323c4e86 (about) (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
;;; 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))