summaryrefslogtreecommitdiff
path: root/type/expression-typechecking.scm
blob: f4606d1a64dcaaf2877004a220cd83d8a8ff68e7 (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
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
;;; This file contains typecheckers for all expressions except vars and
;;; declarations.

;;; From valdef-structs:
;;;   valdef, single-fun-def are in type-decls

(define-type-checker guarded-rhs
  (type-check guarded-rhs rhs rhs-type
    (type-check guarded-rhs guard guard-type
      (type-unify guard-type *bool-type*
          (type-mismatch/fixed (guarded-rhs-guard object)
	   "Guards must be of type Bool" guard-type))
      (return-type object rhs-type))))

;;; These type checkers deal with patterns.

(define-type-checker as-pat
  (type-check as-pat pattern as-type
    (setf (var-type (var-ref-var (as-pat-var object))) as-type)
    (return-type object as-type)))

(define-type-checker irr-pat
  (type-check irr-pat pattern pattern-type
    (return-type object pattern-type)))

(define-type-checker var-pat
  (fresh-type var-type
    (setf (var-type (var-ref-var (var-pat-var object))) var-type)
    (return-type object var-type)))

(define-type-checker wildcard-pat
 (fresh-type pat-type
    (return-type object pat-type)))

;;; Constant patterns create a piece of code to actually to the
;;; match: ((==) k), where k is the constant.  This code is placed in the
;;; match-fn slot of the const-pat and is used by the cfn.

(define-type-checker const-pat
 (let* ((val (const-pat-value object))
	(match-fn (**app (**var/def (core-symbol "==")) val)))
   (setf (const-pat-match-fn object) match-fn)
   (type-check const-pat match-fn match-type
     (fresh-type res-type
       (type-unify match-type (**arrow res-type *bool-type*) #f)
       (return-type object res-type)))))

(define-type-checker plus-pat
  (let* ((kp (**int (plus-pat-k object)))
	 (km (**int (- (plus-pat-k object))))
	 (match-fn (**app (**var/def (core-symbol "<=")) kp))
	 (bind-fn (**app (**var/def (core-symbol "+")) km)))
    (setf (plus-pat-match-fn object) match-fn)
    (setf (plus-pat-bind-fn object) bind-fn)
    (fresh-type res-type
      (setf (ntyvar-context res-type) (list (core-symbol "Integral")))
      (type-check plus-pat match-fn match-type
        (type-check plus-pat bind-fn bind-type
          (type-check plus-pat pattern pat-type
	    (type-unify match-type (**arrow pat-type *bool-type*) #f)
	    (type-unify bind-type (**arrow pat-type pat-type) #f)
	    (type-unify res-type pat-type #f)
	    (return-type object res-type)))))))

(define-type-checker pcon
 (type-check/list pcon pats arg-types
   (fresh-type res-type
     (let ((con-type (instantiate-gtype (con-signature (pcon-con object)))))
       (type-unify con-type (**arrow/l-2 arg-types res-type) #f)
       (return-type object res-type)))))

(define-type-checker list-pat
  (if (null? (list-pat-pats object))
      (return-type object (instantiate-gtype
			     (algdata-signature (core-symbol "List"))))
      (type-check/unify-list list-pat pats element-type
	   (type-mismatch/list object
	     "List elements have different types")
	(return-type object (**list-of element-type)))))

;;; These are in the order defined in exp-structs.scm

(define-type-checker lambda
 (with-new-tyvars
  (fresh-monomorphic-types (length (lambda-pats object)) arg-vars
    (type-check/list lambda pats arg-types
     (unify-list arg-types arg-vars)
     (type-check lambda body body-type
      (return-type object (**arrow/l-2 arg-vars body-type)))))))

(define-type-checker let
  (type-check/decls let decls
    (type-check let body let-type
      (return-type object let-type))))

(define-type-checker if
  (type-check if test-exp test-type
    (type-unify test-type *bool-type*
        (type-mismatch/fixed object
	 "The test in an if statement must be of type Bool"
	 test-type))
    (type-check if then-exp then-type
      (type-check if else-exp else-type
        (type-unify then-type else-type
              (type-mismatch object
		   "then and else clauses have different types"
		   then-type else-type))
	(return-type object then-type)))))

(define-type-checker case
 (with-new-tyvars
  (let ((case-exp object))  ; needed since object is rebound later
   (fresh-monomorphic-type arg-type
    (type-check case exp exp-type
      (type-unify arg-type exp-type #f) ; just to make it monomorphic
      (fresh-type res-type
	(dolist (object (case-alts object))
	  (recover-type-error ;;; %%% Needs work
	   (type-check alt pat pat-type
	     (type-unify pat-type arg-type
                 (type-mismatch case-exp
		  "Case patterns type conflict."
		  pat-type arg-type))
	     (type-check/decls alt where-decls
	       (type-check/unify-list alt rhs-list rhs-type
                 (type-mismatch/list case-exp
		     "Guarded expressions must have the same type")
		 (type-unify rhs-type res-type
			      (type-mismatch case-exp
		   "Case expression alternatives must have the same type"
		                 rhs-type res-type)))))))
	(return-type case-exp res-type)))))))

;;; Expressions with signatures are transformed into let expressions
;;; with signatures.  

;;;    exp :: type   is rewritten as
;;;    let temp = exp
;;;        temp :: type
;;;     in temp

(define-type-checker exp-sign
 (type-rewrite
  (let* ((temp-var (create-temp-var "TC"))
	 (decl (**valdef (**var-pat/def temp-var) '() (exp-sign-exp object)))
	 (let-exp (**let (list decl) (**var/def temp-var)))
	 (signature (exp-sign-signature object)))
      (setf (var-signature temp-var)
	    (ast->gtype (signature-context signature)
			(signature-type signature)))
      let-exp)))

;;; Rather than complicate the ast structure with a new node for dictSel
;;; we recognize the dictSel primitive as an application and treat it
;;; specially.

(define-type-checker app
 (if (and (var-ref? (app-fn object))
	  (eq? (var-ref-var (app-fn object)) (core-symbol "dictSel")))
  (type-check-dict-sel (app-arg object))
  (type-check app fn fn-type
    (type-check app arg arg-type
      (fresh-type res-type
        (fresh-type arg-type-1
          (type-unify fn-type (**arrow arg-type-1 res-type)
              (type-mismatch/fixed object
		       "Attempt to call a non-function"
		       fn-type))
	  (type-unify arg-type-1 arg-type
              (type-mismatch object
		 "Argument type mismatch" arg-type-1 arg-type))
	  (return-type object res-type)))))))

;;; This is a special hack for typing dictionary selection as used in
;;; generic tuple functions.  This extracts a dictionary from a TupleDict
;;; object and uses is to resolve the overloading of a designated
;;; expression.  The expresion must generate exactly one new context.

(define (type-check-dict-sel arg)
  (when (or (not (app? arg))
	    (not (app? (app-fn arg))))
     (dict-sel-error))
  (let* ((exp (app-fn (app-fn arg)))
	 (dict-var (app-arg (app-fn arg)))
	 (i (app-arg arg))
	 (p (dynamic *placeholders*)))
    (mlet (((object exp-type) (dispatch-type-check exp)))
	  ; check for exactly one new context
      (when (or (eq? (dynamic *placeholders*) p)
		(not (eq? (cdr (dynamic *placeholders*)) p)))
	 (dict-sel-error))
	(mlet ((placeholder (car (dynamic *placeholders*)))
	       (tyvar (placeholder-tyvar placeholder))
	       ((dict-var-ast dict-var-type) (dispatch-type-check dict-var))
	       ((index-ast index-type) (dispatch-type-check i)))
	   (setf (ntyvar-context tyvar) '())  ; prevent context from leaking out
	   (setf (dynamic *placeholders*) p)
           (type-unify dict-var-type
			  (**ntycon (core-symbol "TupleDicts") '()) #f)
	   (type-unify index-type *int-type* #f)
	   (cond ((method-placeholder? placeholder)
		  (dict-sel-error))  ; I am lazy.  This means that
		 ; dictSel must not be passed a method
		 (else
		  (setf (placeholder-exp placeholder)
			(**app (**var/def (core-symbol "dictSel"))
			       dict-var-ast index-ast))))
	   (return-type object exp-type)))))

(define (dict-sel-error)
  (fatal-error 'dict-sel-error "Bad dictSel usage."))

(define-type-checker con-ref
  (return-type object (instantiate-gtype (con-signature (con-ref-con object)))))

(define-type-checker integer-const
  (cond ((const-overloaded? object)
	 (setf (const-overloaded? object) '#f)
	 (type-rewrite (**fromInteger object)))
	(else
	 (return-type object *Integer-type*))))

(define-type-checker float-const
  (cond ((const-overloaded? object)
	 (setf (const-overloaded? object) '#f)
	 (type-rewrite (**fromRational object)))
	(else
	 (return-type object *Rational-type*))))

(define-type-checker char-const
  (return-type object *char-type*))

(define-type-checker string-const
  (return-type object *string-type*))

(define-type-checker list-exp
  (if (null? (list-exp-exps object))
      (return-type object (instantiate-gtype
			     (algdata-signature (core-symbol "List"))))
      (type-check/unify-list list-exp exps element-type
	      (type-mismatch/list object
		 "List elements do not share a common type")
	(return-type object (**list-of element-type)))))

(define-type-checker sequence
  (type-rewrite (**enumFrom (sequence-from object))))

(define-type-checker sequence-to
  (type-rewrite (**enumFromTo (sequence-to-from object)
			      (sequence-to-to object))))

(define-type-checker sequence-then
  (type-rewrite (**enumFromThen (sequence-then-from object)
				(sequence-then-then object))))

(define-type-checker sequence-then-to
  (type-rewrite (**enumFromThenTo (sequence-then-to-from object)
				  (sequence-then-to-then object)
				  (sequence-then-to-to object))))

(define-type-checker list-comp
 (with-new-tyvars
  (dolist (object (list-comp-quals object))
    (if (is-type? 'qual-generator object)
	(fresh-type pat-type
	 (push pat-type (dynamic *non-generic-tyvars*))
	 (type-check qual-generator pat pat-type-1
	   (type-unify pat-type pat-type-1 #f)
	   (type-check qual-generator exp qual-exp-type
	     (type-unify (**list-of pat-type) qual-exp-type
                            (type-mismatch/fixed object
		 "Generator expression is not a list" qual-exp-type)))))
	 (type-check qual-filter exp filter-type
	   (type-unify filter-type *bool-type*
              (type-mismatch/fixed object
		"Filter must have type Bool" filter-type)))))
  (type-check list-comp exp exp-type
     (return-type object (**list-of exp-type)))))

(define-type-checker section-l
  (type-check section-l op op-type
    (type-check section-l exp exp-type
      (fresh-type a-type
        (fresh-type b-type
          (fresh-type c-type
            (type-unify op-type (**arrow a-type b-type c-type)
                (type-mismatch/fixed object
		     "Binary function required in section" op-type))
	    (type-unify b-type exp-type
                  (type-mismatch object
		      "Argument type mismatch" b-type exp-type))
	    (return-type object (**arrow a-type c-type))))))))

(define-type-checker section-r
  (type-check section-r op op-type
    (type-check section-r exp exp-type
      (fresh-type a-type
        (fresh-type b-type
          (fresh-type c-type
            (type-unify op-type (**arrow a-type b-type c-type)
                  (type-mismatch/fixed object
			 "Binary function required" op-type))
	    (type-unify exp-type a-type
                    (type-mismatch object
			 "Argument type mismatch" a-type exp-type))
	    (return-type object (**arrow b-type c-type))))))))

(define-type-checker omitted-guard
  (return-type object *bool-type*))

(define-type-checker con-number
  (let ((arg-type (instantiate-gtype
		   (algdata-signature (con-number-type object)))))
    (type-check con-number value arg-type1
      (type-unify arg-type arg-type1 #f)
      (return-type object *int-type*))))

(define-type-checker sel
  (let ((con-type (instantiate-gtype
		   (con-signature (sel-constructor object)))))
    (mlet (((res-type exp-type1) (get-ith-type con-type (sel-slot object))))
      (type-check sel value exp-type
        (type-unify exp-type exp-type1 #f)
	(return-type object res-type)))))

(define (get-ith-type type i)
 (let ((args (ntycon-args type)))  ; must be an arrow
  (if (eq? i 0)
      (values (car args) (get-ith-type/last (cadr args)))
      (get-ith-type (cadr args) (1- i)))))

(define (get-ith-type/last type)
  (if (eq? (ntycon-tycon type) (core-symbol "Arrow"))
      (get-ith-type/last (cadr (ntycon-args type)))
      type))

(define-type-checker is-constructor
  (let ((alg-type (instantiate-gtype
		   (algdata-signature
		    (con-alg (is-constructor-constructor object))))))
    (type-check is-constructor value arg-type
      (type-unify arg-type alg-type #f)
      (return-type object *bool-type*))))

(define-type-checker cast
  (type-check cast exp _
    (fresh-type res
      (return-type object res))))

;;; This is used for overloaded methods.  The theory is to avoid supplying
;;; the context at the class level.  This type checks the variable as if it had
;;; the supplied signature.

(define-type-checker overloaded-var-ref
  (let* ((var (overloaded-var-ref-var object))
	 (gtype (overloaded-var-ref-sig object))
	 (ovar-type (var-type var)))
    (when (recursive-type? ovar-type)
	 (error
	  "Implementation error: overloaded method found a recursive type"))
    (mlet (((ntype new-vars) (instantiate-gtype/newvars gtype))
	   (object1 (insert-dict-placeholders
		     (**var/def var) new-vars object)))
	  (return-type object1 ntype))))