summaryrefslogtreecommitdiff
path: root/util/type-utils.scm
blob: c9b45042a94280c2c6330dff88e306b66f5415bd (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
;;; The `prune' function removes instantiated type variables at the
;;; top level of a type.

;;; It returns an uninstantiated type variable or a type constructor.

(define-integrable (prune ntype)
  (if (ntyvar? ntype)
      (if (instantiated? ntype)
	  (prune-1 (ntyvar-value ntype))
	  ntype)
      ntype))

;;; This is because lucid can't hack inlining recursive fns.

(define (prune-1 x) (prune x))

(define-integrable (instantiated? ntyvar)
  (ntyvar-value ntyvar))
;  (not (eq? (ntyvar-value ntyvar) '#f)))  ;*** Lucid compiler bug?

(define (prune/l l)
  (map (function prune) l))


;;; These functions convert between AST types and gtypes.  Care is taken to
;;; ensure that the gtyvars are in the same order that they appear in the
;;; context.  This is needed to make dictionary conversion work right.

(define (ast->gtype context type)
  (mlet (((gcontext env) (context->gcontext context '() '()))
	 ((type env1) (type->gtype type env))
	 (gcontext-classes (arrange-gtype-classes env1 gcontext)))
    (**gtype gcontext-classes type)))

;;; This is similar except that the ordering of the tyvars is as defined in
;;; the data type.  This is used only for instance declarations and allows
;;; for simple context implication checks.  It also used by the signature
;;; of the dictionary variable.

(define (ast->gtype/inst context type)
  (mlet (((type env) (type->gtype type '()))
	 ((gcontext env1) (context->gcontext context '() env))
	 (gcontext-classes (arrange-gtype-classes env1 gcontext)))
    (**gtype gcontext-classes type)))

;;; This converts a context into gtype form [[class]]: a list of classes
;;; for each gtyvar.  This returns the context and the gtyvar environment.

(define (context->gcontext context gcontext env)
  (if (null? context)
      (values gcontext env)
      (mlet ((sym (context-tyvar (car context)))
	     (class (class-ref-class (context-class (car context))))
	     ((n new-env) (ast->gtyvar sym env))
	     (old-context (get-gtyvar-context n gcontext))
	     (new-context (merge-single-class class old-context))
	     (new-gcontext (cons (tuple n new-context) gcontext)))
	(context->gcontext (cdr context) new-gcontext new-env))))

;;; This assigns a gtyvar number to a tyvar name.

(define (ast->gtyvar sym env)
  (let ((res (assq sym env)))
    (if (eq? res '#f)
	(let ((n (length env)))
	  (values n (cons (tuple sym n) env)))
	(values (tuple-2-2 res) env))))

(define (get-gtyvar-context n gcontext)
  (cond ((null? gcontext)
	 '())
	((eqv? n (tuple-2-1 (car gcontext)))
	 (tuple-2-2 (car gcontext)))
	(else (get-gtyvar-context n (cdr gcontext)))))

(define (type->gtype type env)
  (if (tyvar? type)
      (mlet (((n env1) (ast->gtyvar (tyvar-name type) env)))
	(values (**gtyvar n) env1))
      (mlet (((types env1) (type->gtype/l (tycon-args type) env)))
	(values (**ntycon (tycon-def type) types) env1))))

(define (type->gtype/l types env)
  (if (null? types)
      (values '() env)
      (mlet (((type env1) (type->gtype (car types) env))
	     ((other-types env2) (type->gtype/l (cdr types) env1)))
	 (values (cons type other-types) env2))))

(define (arrange-gtype-classes env gcontext)
  (arrange-gtype-classes-1 0 (length env) env gcontext))

(define (arrange-gtype-classes-1 m n env gcontext)
  (if (equal? m n)
      '()
      (cons (get-gtyvar-context m gcontext)
	    (arrange-gtype-classes-1 (1+ m) n env gcontext))))

;;; These routines convert gtypes back to ordinary types.

(define (instantiate-gtype g)
 (mlet (((gtype _) (instantiate-gtype/newvars g)))
    gtype))

(define (instantiate-gtype/newvars g)
  (if (null? (gtype-context g))
      (values (gtype-type g) '())
      (let ((new-tyvars (create-new-tyvars (gtype-context g))))
	(values (copy-gtype (gtype-type g) new-tyvars) new-tyvars))))

(define (create-new-tyvars ctxts)
  (if (null? ctxts)
      '()
      (let ((tyvar (**ntyvar)))
	(setf (ntyvar-context tyvar) (car ctxts))
	(cons tyvar (create-new-tyvars (cdr ctxts))))))

(define (copy-gtype g env)
  (cond ((ntycon? g)
	 (**ntycon (ntycon-tycon g)
		   (map (lambda (g1) (copy-gtype g1 env))
			(ntycon-args g))))
	((ntyvar? g)
	 g)
	((gtyvar? g)
	 (list-ref env (gtyvar-varnum g)))
	((const-type? g)
	 (const-type-type g))))

;;; ntypes may contain synonyms.  These are expanded here.  Only the
;;; top level synonym is expanded.

(define (expand-ntype-synonym type)
  (if (and (ntycon? type)
	   (synonym? (ntycon-tycon type)))
      (let ((syn (ntycon-tycon type)))
	(expand-ntype-synonym
  	  (expand-ntype-synonym-1 (synonym-body syn)
				  (map (lambda (var val)
					 (tuple var val))
				       (synonym-args syn)
				       (ntycon-args type)))))
      type))

(define (expand-ntype-synonym-1 type env)
  (if (tyvar? type)
      (tuple-2-2 (assq (tyvar-name type) env))
      (**ntycon (tycon-def type)
		(map (lambda (ty) (expand-ntype-synonym-1 ty env))
		     (tycon-args type)))))

;;; This is used in generalization.  Note that ntyvars will remain when
;;; non-generic tyvars are encountered.

(define (ntype->gtype ntype)
  (mlet (((res _) (ntype->gtype/env ntype '())))
    res))

(define (ntype->gtype/env ntype required-vars)
  (mlet (((gtype env) (ntype->gtype-1 ntype required-vars)))
   (values 
    (make gtype (type gtype) (context (map (lambda (x) (ntyvar-context x))
					  env)))
    env)))

(define (ntype->gtype-1 ntype env)
 (let ((ntype (prune ntype)))
  (cond ((ntycon? ntype)
	 (mlet (((args env1) (ntype->gtype/l (ntycon-args ntype) env)))
	   (values (**ntycon (ntycon-tycon ntype) args) env1)))
	(else
	 (ntyvar->gtyvar ntype env)))))

(define (ntype->gtype/l types env)
  (if (null? types)
      (values '() env)
      (mlet (((type env1) (ntype->gtype-1 (car types) env))
	     ((types2 env2) (ntype->gtype/l (cdr types) env1)))
	(values (cons type types2) env2))))

(define (ntyvar->gtyvar ntyvar env)
  (if (non-generic? ntyvar)
      (values ntyvar env)
      (let ((l (list-pos ntyvar env)))
	(if (eq? l '#f)
	    (values (**gtyvar (length env)) (append env (list ntyvar)))
	    (values (**gtyvar l) env)))))
     
(define (list-pos x l)
  (list-pos-1 x l 0))

(define (list-pos-1 x l n)
  (cond ((null? l)
	 '#f)
	((eq? x (car l))
	 n)
	(else
	 (list-pos-1 x (cdr l) (1+ n)))))


;;; These utils are used in dictionary conversion.

(define (**dsel/method class method dict-code)
  (let ((pos (locate-in-list method (class-method-vars class) 0)))
    (**tuple-sel (class-dict-size class) pos dict-code)))

(define (**dsel/dict class dict-class dict-code)
  (let ((pos (locate-in-list
	      dict-class (class-super* class) (class-n-methods class))))
    (**tuple-sel (class-dict-size class) pos dict-code)))
  
(define (locate-in-list var l pos)
  (if (null? l)
      (error "Locate in list failed")
      (if (eq? var (car l))
	  pos
	  (locate-in-list var (cdr l) (1+ pos)))))

;;; These routines deal with contexts.  A context is a list classes.

;;; A context is normalized whenever class is a superclass of another.

(define (merge-contexts ctxt1 ctxt2)
  (if (null? ctxt1)
      ctxt2
      (merge-single-class (car ctxt1) (merge-contexts (cdr ctxt1) ctxt2))))

;;; This could perhaps avoid some consing but I don't imagine it would
;;; make much difference.

(define (merge-single-class class ctxt)
  (cond ((null? ctxt)
	 (list class))
	((eq? class (car ctxt))
	 ctxt)
	((memq class (class-super* (car ctxt)))
	 ctxt)
	((memq (car ctxt) (class-super* class))
	 (merge-single-class class (cdr ctxt)))
	(else
	 (cons (car ctxt) (merge-single-class class (cdr ctxt))))))

;;; This determines if ctxt2 is contained in ctxt1.

(define (context-implies? ctxt1 ctxt2)
  (or (null? ctxt2)
      (and (single-class-implies? ctxt1 (car ctxt2))
	   (context-implies? ctxt1 (cdr ctxt2)))))

(define (single-class-implies? ctxt class)
  (and (not (null? ctxt))
       (or (memq class ctxt)
	   (super-class-implies? ctxt class))))

(define (super-class-implies? ctxt class)
  (and (not (null? ctxt))
       (or (memq class (class-super* (car ctxt)))
	   (super-class-implies? (cdr ctxt) class))))

;;; This looks at the context of a full signature.

(define (full-context-implies? ctxt1 ctxt2)
  (or (null? ctxt1)
      (and (context-implies? (car ctxt1) (car ctxt2))
	   (full-context-implies? (cdr ctxt1) (cdr ctxt2)))))

;;; This is used to avoid type circularity on unification.

(define (occurs-in-type tyvar type) ; Cardelli algorithm
  (let ((type (prune type)))
    (if (ntyvar? type)
	(eq? type tyvar)
	(occurs-in-type/l tyvar (ntycon-args type)))))

; Does a tyvar occur in a list of types?
(define (occurs-in-type/l tyvar types)
  (if (null? types)
      '#f
      (or (occurs-in-type tyvar (car types))
	  (occurs-in-type/l tyvar (cdr types)))))

(define-integrable (non-generic? tyvar)
  (occurs-in-type/l tyvar (dynamic *non-generic-tyvars*)))

(define (collect-tyvars ntype)
  (collect-tyvars-1 ntype '()))

(define (collect-tyvars-1 ntype vars)
 (let ((ntype (prune ntype)))
  (if (ntyvar? ntype)
      (if (or (memq ntype vars) (non-generic? ntype))
	  vars
	  (cons ntype vars))
      (collect-tyvars/l-1 (ntycon-args ntype) vars))))

(define (collect-tyvars/l types)
  (collect-tyvars/l-1 types '()))

(define (collect-tyvars/l-1 types vars)
  (if (null? types)
      vars
      (collect-tyvars/l-1 (cdr types) (collect-tyvars-1 (car types) vars))))

;;; Random utilities

(define (decl-var decl)
  (var-ref-var (var-pat-var (valdef-lhs decl))))