summaryrefslogtreecommitdiff
path: root/type/type-decl.scm
blob: 790c0ca688769c4bfbc5f59eb6f3cbad67f61d13 (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
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
;;; 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 *dict-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)
        (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 '()))))))

;;; 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)
	      (when (and (ntyvar-context tyvar) (not (non-generic? 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 '()))
	 ;; 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)
		     (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
  (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 (type-decl-rhs object)
 (dynamic-let ((*enclosing-decls* (cons object (dynamic *enclosing-decls*))))
  (remember-context object
   (type-check/unify-list valdef definitions res-type
       (type-mismatch/list object
	   "Right hand sides 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 object
			"Bodies 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.

(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)))
	  (setf (var-type var) dummy-type))))))


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