/[svn]/typing/typer.ml
ViewVC logotype

Contents of /typing/typer.ml

Parent Directory Parent Directory | Revision Log Revision Log


Revision 64 - (show annotations)
Tue Jul 10 17:02:32 2007 UTC (5 years, 10 months ago) by abate
File size: 20005 byte(s)
[r2002-10-30 03:08:01 by cvscast] Empty log message

Original author: cvscast
Date: 2002-10-30 03:08:09+00:00
1 (* I. Transform the abstract syntax of types and patterns into
2 the internal form *)
3
4 open Location
5 open Ast
6
7 exception Pattern of string
8 exception NonExhaustive of Types.descr
9 exception MultipleLabel of Types.label
10 exception Constraint of Types.descr * Types.descr * string
11 exception ShouldHave of Types.descr * string
12 exception WrongLabel of Types.descr * Types.label
13 exception UnboundId of string
14
15 let raise_loc loc exn = raise (Location (loc,exn))
16
17 (* Internal representation as a graph (desugar recursive types and regexp),
18 to compute freevars, etc... *)
19
20 type ti = {
21 id : int;
22 mutable loc' : loc;
23 mutable fv : string SortedList.t option;
24 mutable descr': descr;
25 mutable type_node: Types.node option;
26 mutable pat_node: Patterns.node option
27 }
28 and descr =
29 [ `Alias of string * ti
30 | `Type of Types.descr
31 | `Or of ti * ti
32 | `And of ti * ti * bool
33 | `Diff of ti * ti
34 | `Times of ti * ti
35 | `Arrow of ti * ti
36 | `Record of Types.label * bool * ti
37 | `Capture of Patterns.capture
38 | `Constant of Patterns.capture * Types.const
39 ]
40
41
42
43 module S = struct type t = string let compare = compare end
44 module StringMap = Map.Make(S)
45 module StringSet = Set.Make(S)
46
47 let mk' =
48 let counter = ref 0 in
49 fun loc ->
50 incr counter;
51 let rec x = {
52 id = !counter;
53 loc' = loc;
54 fv = None;
55 descr' = `Alias ("__dummy__", x);
56 type_node = None;
57 pat_node = None
58 } in
59 x
60
61 let cons loc d =
62 let x = mk' loc in
63 x.descr' <- d;
64 x
65
66 (* Note:
67 Compilation of Regexp is implemented as a ``rewriting'' of
68 the parsed syntax, in order to be able to print its result
69 (for debugging for instance)
70
71 It would be possible (and a little more efficient) to produce
72 directly ti nodes.
73 *)
74
75 module Regexp = struct
76 let memo = Hashtbl.create 51
77 let defs = ref []
78 let name =
79 let c = ref 0 in
80 fun () ->
81 incr c;
82 "#" ^ (string_of_int !c)
83
84 let rec seq_vars accu = function
85 | Epsilon | Elem _ -> accu
86 | Seq (r1,r2) | Alt (r1,r2) -> seq_vars (seq_vars accu r1) r2
87 | Star r | WeakStar r -> seq_vars accu r
88 | SeqCapture (v,r) -> seq_vars (StringSet.add v accu) r
89
90 let rec propagate vars = function
91 | Epsilon -> `Epsilon
92 | Elem x -> `Elem (vars,x)
93 | Seq (r1,r2) -> `Seq (propagate vars r1,propagate vars r2)
94 | Alt (r1,r2) -> `Alt (propagate vars r1, propagate vars r2)
95 | Star r -> `Star (propagate vars r)
96 | WeakStar r -> `WeakStar (propagate vars r)
97 | SeqCapture (v,x) -> propagate (StringSet.add v vars) x
98
99 let cup r1 r2 =
100 match (r1,r2) with
101 | (_, `Empty) -> r1
102 | (`Empty, _) -> r2
103 | (`Res t1, `Res t2) -> `Res (mk noloc (Or (t1,t2)))
104
105 let rec compile fin e seq : [`Res of Ast.ppat | `Empty] =
106 if List.mem seq e then `Empty
107 else
108 let e = seq :: e in
109 match seq with
110 | [] ->
111 `Res fin
112 | `Epsilon :: rest ->
113 compile fin e rest
114 | `Elem (vars,x) :: rest ->
115 let capt = StringSet.fold
116 (fun v t -> mk noloc (And (t, (mk noloc (Capture v)), true)))
117 vars x in
118 `Res (mk noloc (Prod (capt, guard_compile fin rest)))
119 | `Seq (r1,r2) :: rest ->
120 compile fin e (r1 :: r2 :: rest)
121 | `Alt (r1,r2) :: rest ->
122 cup (compile fin e (r1::rest)) (compile fin e (r2::rest))
123 | `Star r :: rest -> cup (compile fin e (r::seq)) (compile fin e rest)
124 | `WeakStar r :: rest -> cup (compile fin e rest) (compile fin e (r::seq))
125
126 and guard_compile fin seq =
127 try Hashtbl.find memo seq
128 with
129 Not_found ->
130 let n = name () in
131 let v = mk noloc (PatVar n) in
132 Hashtbl.add memo seq v;
133 let d = compile fin [] seq in
134 (match d with
135 | `Empty -> assert false
136 | `Res d -> defs := (n,d) :: !defs);
137 v
138
139
140 let atom_nil = Types.mk_atom "nil"
141 let constant_nil v t =
142 mk noloc (And (t, (mk noloc (Constant (v, Types.Atom atom_nil))), true))
143
144 let compile regexp queue : ppat =
145 let vars = seq_vars StringSet.empty regexp in
146 let fin = StringSet.fold constant_nil vars queue in
147 let n = guard_compile fin [propagate StringSet.empty regexp] in
148 Hashtbl.clear memo;
149 let d = !defs in
150 defs := [];
151 mk noloc (Recurs (n,d))
152 end
153
154 let compile_regexp = Regexp.compile
155
156
157 let rec compile env { loc = loc; descr = d } : ti =
158 match (d : Ast.ppat') with
159 | PatVar s ->
160 (try StringMap.find s env
161 with Not_found ->
162 raise_loc loc (Pattern ("Undefined type variable " ^ s))
163 )
164 | Recurs (t, b) -> compile (compile_many env b) t
165 | Regexp (r,q) -> compile env (Regexp.compile r q)
166 | Internal t -> cons loc (`Type t)
167 | Or (t1,t2) -> cons loc (`Or (compile env t1, compile env t2))
168 | And (t1,t2,e) -> cons loc (`And (compile env t1, compile env t2,e))
169 | Diff (t1,t2) -> cons loc (`Diff (compile env t1, compile env t2))
170 | Prod (t1,t2) -> cons loc (`Times (compile env t1, compile env t2))
171 | Arrow (t1,t2) -> cons loc (`Arrow (compile env t1, compile env t2))
172 | Record (l,o,t) -> cons loc (`Record (l,o,compile env t))
173 | Constant (x,v) -> cons loc (`Constant (x,v))
174 | Capture x -> cons loc (`Capture x)
175
176 and compile_many env b =
177 let b = List.map (fun (v,t) -> (v,t,mk' t.loc)) b in
178 let env =
179 List.fold_left (fun env (v,t,x) -> StringMap.add v x env) env b in
180 List.iter (fun (v,t,x) -> x.descr' <- `Alias (v, compile env t)) b;
181 env
182
183
184 let rec comp_fv seen s =
185 match s.fv with
186 | Some l -> l
187 | None ->
188 let l =
189 match s.descr' with
190 | `Alias (_,x) -> if List.memq s seen then [] else comp_fv (s :: seen) x
191 | `Or (s1,s2)
192 | `And (s1,s2,_)
193 | `Diff (s1,s2)
194 | `Times (s1,s2)
195 | `Arrow (s1,s2) -> SortedList.cup (comp_fv seen s1) (comp_fv seen s2)
196 | `Record (l,opt,s) -> comp_fv seen s
197 | `Type _ -> []
198 | `Capture x
199 | `Constant (x,_) -> [x]
200 in
201 if seen = [] then s.fv <- Some l;
202 l
203
204
205 let fv = comp_fv []
206
207 let rec typ seen s : Types.descr =
208 match s.descr' with
209 | `Alias (v,x) ->
210 if List.memq s seen then
211 raise_loc s.loc'
212 (Pattern
213 ("Unguarded recursion on variable " ^ v ^ " in this type"))
214 else typ (s :: seen) x
215 | `Type t -> t
216 | `Or (s1,s2) -> Types.cup (typ seen s1) (typ seen s2)
217 | `And (s1,s2,_) -> Types.cap (typ seen s1) (typ seen s2)
218 | `Diff (s1,s2) -> Types.diff (typ seen s1) (typ seen s2)
219 | `Times (s1,s2) -> Types.times (typ_node s1) (typ_node s2)
220 | `Arrow (s1,s2) -> Types.arrow (typ_node s1) (typ_node s2)
221 | `Record (l,o,s) -> Types.record l o (typ_node s)
222 | `Capture _ | `Constant _ -> assert false
223
224 and typ_node s : Types.node =
225 match s.type_node with
226 | Some x -> x
227 | None ->
228 let x = Types.make () in
229 s.type_node <- Some x;
230 let t = typ [] s in
231 Types.define x t;
232 x
233
234 let type_node s = Types.internalize (typ_node s)
235
236 let rec pat seen s : Patterns.descr =
237 if fv s = [] then Patterns.constr (type_node s) else
238 match s.descr' with
239 | `Alias (v,x) ->
240 if List.memq s seen then
241 raise_loc s.loc'
242 (Pattern
243 ("Unguarded recursion on variable " ^ v ^ " in this pattern"))
244 else pat (s :: seen) x
245 | `Or (s1,s2) -> Patterns.cup (pat seen s1) (pat seen s2)
246 | `And (s1,s2,e) -> Patterns.cap (pat seen s1) (pat seen s2) e
247 | `Diff (s1,s2) when fv s2 = [] ->
248 let s2 = Types.cons (Types.neg (Types.descr (type_node s2)))in
249 Patterns.cap (pat seen s1) (Patterns.constr s2) true
250 | `Diff _ ->
251 raise_loc s.loc' (Pattern "Difference not allowed in patterns")
252 | `Times (s1,s2) -> Patterns.times (pat_node s1) (pat_node s2)
253 | `Record (l,false,s) -> Patterns.record l (pat_node s)
254 | `Record _ ->
255 raise_loc s.loc'
256 (Pattern "Optional field not allowed in record patterns")
257 | `Capture x -> Patterns.capture x
258 | `Constant (x,c) -> Patterns.constant x c
259 | `Arrow _ ->
260 raise_loc s.loc' (Pattern "Arrow not allowed in patterns")
261 | `Type _ -> assert false
262
263 and pat_node s : Patterns.node =
264 match s.pat_node with
265 | Some x -> x
266 | None ->
267 let x = Patterns.make (fv s) in
268 s.pat_node <- Some x;
269 let t = pat [] s in
270 Patterns.define x t;
271 x
272
273 let global_types = ref StringMap.empty
274
275 let mk_typ e =
276 if fv e = [] then type_node e
277 else raise_loc e.loc' (Pattern "Capture variables are not allowed in types")
278
279
280 let typ e =
281 mk_typ (compile !global_types e)
282
283 let pat e =
284 let e = compile !global_types e in
285 pat_node e
286
287 let register_global_types b =
288 let env = compile_many !global_types b in
289 List.iter (fun (v,_) ->
290 let d = Types.descr (mk_typ (StringMap.find v env)) in
291 let d = Types.normalize d in
292 Types.Print.register_global v d
293 ) b;
294 global_types := env
295
296
297 (* II. Build skeleton *)
298
299 module Fv = StringSet
300
301 let rec expr { loc = loc; descr = d } =
302 let (fv,td) =
303 match d with
304 | DebugTyper t -> (Fv.empty, Typed.DebugTyper (typ t))
305 | Forget (e,t) ->
306 let (fv,e) = expr e and t = typ t in
307 (fv, Typed.Forget (e,t))
308 | Var s -> (Fv.singleton s, Typed.Var s)
309 | Apply (e1,e2) ->
310 let (fv1,e1) = expr e1 and (fv2,e2) = expr e2 in
311 (Fv.union fv1 fv2, Typed.Apply (e1,e2))
312 | Abstraction a ->
313 let iface = List.map (fun (t1,t2) -> (typ t1, typ t2)) a.fun_iface in
314 let t = List.fold_left
315 (fun accu (t1,t2) -> Types.cap accu (Types.arrow t1 t2))
316 Types.any iface in
317 let iface = List.map
318 (fun (t1,t2) -> (Types.descr t1, Types.descr t2))
319 iface in
320 let (fv0,body) = branches a.fun_body in
321 let fv = match a.fun_name with
322 | None -> fv0
323 | Some f -> Fv.remove f fv0 in
324 (fv,
325 Typed.Abstraction
326 { Typed.fun_name = a.fun_name;
327 Typed.fun_iface = iface;
328 Typed.fun_body = body;
329 Typed.fun_typ = t;
330 Typed.fun_fv = Fv.elements fv0
331 }
332 )
333 | Cst c -> (Fv.empty, Typed.Cst c)
334 | Pair (e1,e2) ->
335 let (fv1,e1) = expr e1 and (fv2,e2) = expr e2 in
336 (Fv.union fv1 fv2, Typed.Pair (e1,e2))
337 | Dot (e,l) ->
338 let (fv,e) = expr e in
339 (fv, Typed.Dot (e,l))
340 | RecordLitt r ->
341 let fv = ref Fv.empty in
342 let r = List.sort (fun (l1,_) (l2,_) -> compare l1 l2) r in
343 let r = List.map
344 (fun (l,e) ->
345 let (fv2,e) = expr e in fv := Fv.union !fv fv2; (l,e))
346 r in
347 let rec check = function
348 | (l1,_) :: (l2,_) :: _ when l1 = l2 ->
349 raise_loc loc (MultipleLabel l1)
350 | _ :: rem -> check rem
351 | _ -> () in
352 check r;
353 (!fv, Typed.RecordLitt r)
354 | Op (op,le) ->
355 let (fvs,ltes) = List.split (List.map expr le) in
356 let fv = List.fold_left Fv.union Fv.empty fvs in
357 (fv, Typed.Op (op,ltes))
358 | Match (e,b) ->
359 let (fv1,e) = expr e
360 and (fv2,b) = branches b in
361 (Fv.union fv1 fv2, Typed.Match (e, b))
362 | Map (e,b) ->
363 let (fv1,e) = expr e
364 and (fv2,b) = branches b in
365 (Fv.union fv1 fv2, Typed.Map (e, b))
366 | Try (e,b) ->
367 let (fv1,e) = expr e
368 and (fv2,b) = branches b in
369 (Fv.union fv1 fv2, Typed.Try (e, b))
370 in
371 fv,
372 { Typed.exp_loc = loc;
373 Typed.exp_typ = Types.empty;
374 Typed.exp_descr = td;
375 }
376
377 and branches b =
378 let fv = ref Fv.empty in
379 let accept = ref Types.empty in
380 let b = List.map
381 (fun (p,e) ->
382 let (fv2,e) = expr e in
383 fv := Fv.union !fv fv2;
384 let p = pat p in
385 accept := Types.cup !accept (Types.descr (Patterns.accept p));
386 { Typed.br_used = false;
387 Typed.br_pat = p;
388 Typed.br_body = e }
389 ) b in
390 (!fv,
391 {
392 Typed.br_typ = Types.empty;
393 Typed.br_branches = b;
394 Typed.br_accept = !accept;
395 Typed.br_compiled = None;
396 }
397 )
398
399 module Env = StringMap
400
401 open Typed
402
403
404 let check loc t s msg =
405 if not (Types.subtype t s) then raise_loc loc (Constraint (t, s, msg))
406
407 let rec type_check env e constr precise =
408 (* Format.fprintf Format.std_formatter "constr=%a precise=%b@\n"
409 Types.Print.print_descr constr precise;
410 *)
411 let d = type_check' e.exp_loc env e.exp_descr constr precise in
412 e.exp_typ <- Types.cup e.exp_typ d;
413 d
414
415 and type_check' loc env e constr precise = match e with
416 | Forget (e,t) ->
417 let t = Types.descr t in
418 ignore (type_check env e t false);
419 t
420 | Abstraction a ->
421 let t =
422 try Types.Arrow.check_strenghten a.fun_typ constr
423 with Not_found ->
424 raise_loc loc
425 (ShouldHave
426 (constr, "but the interface of the abstraction is not compatible"))
427 in
428 let env = match a.fun_name with
429 | None -> env
430 | Some f -> Env.add f a.fun_typ env in
431 List.iter
432 (fun (t1,t2) ->
433 ignore (type_check_branches loc env true t1 a.fun_body t2 false)
434 ) a.fun_iface;
435 t
436
437 | Match (e,b) ->
438 let t = type_check env e b.br_accept true in
439 type_check_branches loc env true t b constr precise
440
441 | Try (e,b) ->
442 let te = type_check env e constr precise in
443 let tb = type_check_branches loc env false Types.any b constr precise in
444 Types.cup te tb
445
446 | Pair (e1,e2) ->
447 let rects = Types.Product.get constr in
448 if Types.Product.is_empty rects then
449 raise_loc loc (ShouldHave (constr,"but it is a pair."));
450 let pi1 = Types.Product.pi1 rects in
451
452 let t1 = type_check env e1 (Types.Product.pi1 rects)
453 (precise || (Types.Product.need_second rects))in
454 let rects = Types.Product.restrict_1 rects t1 in
455 let t2 = type_check env e2 (Types.Product.pi2 rects) precise in
456 if precise then
457 Types.times (Types.cons t1) (Types.cons t2)
458 else
459 constr
460
461 | RecordLitt r ->
462 let rconstr = Types.Record.get constr in
463 if Types.Record.is_empty rconstr then
464 raise_loc loc (ShouldHave (constr,"but it is a record."));
465
466 let (rconstr,res) =
467 List.fold_left
468 (fun (rconstr,res) (l,e) ->
469 let rconstr = Types.Record.restrict_label_present rconstr l in
470 let pi = Types.Record.project_field rconstr l in
471 if Types.Record.is_empty rconstr then
472 raise_loc loc
473 (ShouldHave (constr,(Printf.sprintf
474 "Field %s is not allowed here."
475 (Types.label_name l)
476 )
477 ));
478 let t = type_check env e pi true in
479 let rconstr = Types.Record.restrict_field rconstr l t in
480
481 let res =
482 if precise
483 then Types.cap res (Types.record l false (Types.cons t))
484 else res in
485 (rconstr,res)
486 ) (rconstr, if precise then Types.Record.any else constr) r
487 in
488 res
489
490 | Map (e,b) ->
491 let t = type_check env e (Sequence.star b.br_accept) true in
492
493 let constr' = Sequence.approx (Types.cap Sequence.any constr) in
494 let exact = Types.subtype (Sequence.star constr') constr in
495 (*
496 Format.fprintf Format.std_formatter
497 "(Map) constr = %a@; exact = %b\n@." Types.Print.print_descr constr exact;
498 *)
499 (* Note:
500 - could be more precise by integrating the decomposition
501 of constr inside Sequence.map.
502 *)
503 let res =
504 Sequence.map
505 (fun t ->
506 type_check_branches loc env true t b constr' (precise || (not exact)))
507 t in
508 if not exact then check loc res constr "";
509 if precise then res else constr
510 | Op ("@", [e1;e2]) ->
511 let constr' = Sequence.star
512 (Sequence.approx (Types.cap Sequence.any constr)) in
513 let exact = Types.subtype constr' constr in
514 if exact then
515 let t1 = type_check env e1 constr' precise
516 and t2 = type_check env e2 constr' precise in
517 if precise then Sequence.concat t1 t2 else constr
518 else
519 (* Note:
520 the knownledge of t1 may makes it useless to
521 check t2 with 'precise' ... *)
522 let t1 = type_check env e1 constr' true
523 and t2 = type_check env e2 constr' true in
524 let res = Sequence.concat t1 t2 in
525 check loc res constr "";
526 if precise then res else constr
527 | Op ("flatten", [e]) ->
528 let constr' = Sequence.star
529 (Sequence.approx (Types.cap Sequence.any constr)) in
530 let sconstr' = Sequence.star constr' in
531 let exact = Types.subtype constr' constr in
532 if exact then
533 let t = type_check env e sconstr' precise in
534 if precise then Sequence.flatten t else constr
535 else
536 let t = type_check env e sconstr' true in
537 let res = Sequence.flatten t in
538 check loc res constr "";
539 if precise then res else constr
540 | _ ->
541 let t : Types.descr = compute_type' loc env e in
542 check loc t constr "";
543 t
544
545 and compute_type env e =
546 type_check env e Types.any true
547
548 and compute_type' loc env = function
549 | DebugTyper t -> Types.descr t
550 | Var s ->
551 (try Env.find s env
552 with Not_found -> raise_loc loc (UnboundId s)
553 )
554 | Apply (e1,e2) ->
555 let t1 = type_check env e1 Types.Arrow.any true in
556 let t1 = Types.Arrow.get t1 in
557 let dom = Types.Arrow.domain t1 in
558 if Types.Arrow.need_arg t1 then
559 let t2 = type_check env e2 dom true in
560 Types.Arrow.apply t1 t2
561 else
562 (ignore (type_check env e2 dom false); Types.Arrow.apply_noarg t1)
563 | Cst c -> Types.constant c
564 | Dot (e,l) ->
565 let t = type_check env e Types.Record.any true in
566 (try (Types.Record.project t l)
567 with Not_found -> raise_loc loc (WrongLabel(t,l)))
568 | Op (op, el) ->
569 let args = List.map (fun e -> (e.exp_loc, compute_type env e)) el in
570 type_op loc op args
571 | Map (e,b) ->
572 let t = compute_type env e in
573 Sequence.map (fun t -> type_check_branches loc env true t b Types.any true) t
574
575 (* We keep these cases here to allow comparison and benchmarking ...
576 Just comment the corresponding cases in type_check' to
577 activate these ones.
578 *)
579 | Pair (e1,e2) ->
580 let t1 = compute_type env e1
581 and t2 = compute_type env e2 in
582 Types.times (Types.cons t1) (Types.cons t2)
583 | RecordLitt r ->
584 List.fold_left
585 (fun accu (l,e) ->
586 let t = compute_type env e in
587 let t = Types.record l false (Types.cons t) in
588 Types.cap accu t
589 ) Types.Record.any r
590
591
592 | _ -> assert false
593
594 and type_check_branches loc env exh targ brs constr precise =
595 if Types.is_empty targ then Types.empty
596 else (
597 brs.br_typ <- Types.cup brs.br_typ targ;
598 branches_aux loc env exh targ
599 (if precise then Types.empty else constr)
600 constr precise brs.br_branches
601 )
602
603 and branches_aux loc env exh targ tres constr precise = function
604 | [] -> if exh then raise_loc loc (NonExhaustive targ) else tres
605 | b :: rem ->
606 let p = b.br_pat in
607 let acc = Types.descr (Patterns.accept p) in
608
609 let targ' = Types.cap targ acc in
610 if Types.is_empty targ'
611 then branches_aux loc env exh targ tres constr precise rem
612 else
613 ( b.br_used <- true;
614 let res = Patterns.filter targ' p in
615 let env' = List.fold_left
616 (fun env (x,t) -> Env.add x (Types.descr t) env)
617 env res in
618 let t = type_check env' b.br_body constr precise in
619 let tres = if precise then Types.cup t tres else tres in
620 let targ'' = Types.diff targ acc in
621 if (Types.non_empty targ'') then
622 branches_aux loc env exh targ'' tres constr precise rem
623 else
624 tres
625 )
626
627 and type_op loc op args =
628 match (op,args) with
629 | "+", [loc1,t1; loc2,t2] ->
630 type_int_binop Intervals.add loc1 t1 loc2 t2
631 | "-", [loc1,t1; loc2,t2] ->
632 type_int_binop Intervals.sub loc1 t1 loc2 t2
633 | ("*" | "/"), [loc1,t1; loc2,t2] ->
634 type_int_binop (fun i1 i2 -> Intervals.any) loc1 t1 loc2 t2
635 | "@", [loc1,t1; loc2,t2] ->
636 check loc1 t1 Sequence.any
637 "The first argument of @ must be a sequence";
638 Sequence.concat t1 t2
639 | "flatten", [loc1,t1] ->
640 check loc1 t1 Sequence.seqseq
641 "The argument of flatten must be a sequence of sequences";
642 Sequence.flatten t1
643 | "load_xml", [loc1,t1] ->
644 check loc1 t1 Sequence.string
645 "The argument of load_xml must be a string (filename)";
646 Types.any
647 | "raise", [loc1,t1] ->
648 Types.empty
649 | _ -> assert false
650
651 and type_int_binop f loc1 t1 loc2 t2 =
652 if not (Types.Int.is_int t1) then
653 raise_loc loc1
654 (Constraint
655 (t1,Types.Int.any,
656 "The first argument must be an integer"));
657 if not (Types.Int.is_int t2) then
658 raise_loc loc2
659 (Constraint
660 (t2,Types.Int.any,
661 "The second argument must be an integer"));
662 Types.Int.put
663 (f (Types.Int.get t1) (Types.Int.get t2));
664
665

CVS Admin">CVS Admin
ViewVC Help
Powered by ViewVC 1.1.5