1 (* HCoop
Domtool (http
://hcoop
.sourceforge
.net
/)
2 * Copyright (c
) 2006, Adam Chlipala
4 * This program is free software
; you can redistribute it
and/or
5 * modify it under the terms
of the GNU General Public License
6 * as published by the Free Software Foundation
; either version
2
7 * of the License
, or (at your option
) any later version
.
9 * This program is distributed
in the hope that it will be useful
,
10 * but WITHOUT ANY WARRANTY
; without even the implied warranty
of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE
. See the
12 * GNU General Public License for more details
.
14 * You should have received a copy
of the GNU General Public License
15 * along
with this program
; if not
, write to the Free Software
16 * Foundation
, Inc
., 51 Franklin Street
, Fifth Floor
, Boston
, MA
02110-1301, USA
.
19 (* Domtool configuration language
type checking
*)
21 structure Tycheck
:> TYCHECK
= struct
25 structure SS
= StringSet
26 structure SM
= StringMap
28 type env
= SS
.set
* typ SM
.map
29 val empty
: env
= (SS
.add (SS
.singleton
"int", "string"),
32 fun lookupType (ts
, _
) name
= SS
.member (ts
, name
)
33 fun lookupVal (_
, vs
) name
= SM
.find (vs
, name
)
35 fun bindType (ts
, vs
) name
= (SS
.add (ts
, name
), vs
)
36 fun bindVal (ts
, vs
) (name
, t
) = (ts
, SM
.insert (vs
, name
, t
))
41 fun resetUnif () = unifCount
:= 0
48 str (chr (ord #
"A" + c
))
50 "UNIF" ^
Int.toString (c
- 26)
53 TUnif (name
, ref NONE
)
57 exception UnequalDomains
59 fun eqRecord
f (r1
, r2
) =
60 (SM
.appi (fn (k
, v1
) =>
61 case SM
.find (r2
, k
) of
62 NONE
=> raise UnequalDomains
67 raise UnequalDomains
) r1
;
68 SM
.appi (fn (k
, v2
) =>
69 case SM
.find (r1
, k
) of
70 NONE
=> raise UnequalDomains
75 raise UnequalDomains
) r2
;
77 handle UnequalDomains
=> false
79 fun eqPred ((p1
, _
), (p2
, _
)) =
81 (CRoot
, CRoot
) => true
82 |
(CConst s1
, CConst s2
) => s1
= s2
83 |
(CPrefix p1
, CPrefix p2
) => eqPred (p1
, p2
)
84 |
(CNot p1
, CNot p2
) => eqPred (p1
, p2
)
85 |
(CAnd (p1
, q1
), CAnd (p2
, q2
)) =>
86 eqPred (p1
, p2
) andalso eqPred (q1
, q2
)
90 fun eqTy (t1All
as (t1
, _
), t2All
as (t2
, _
)) =
92 (TBase s1
, TBase s2
) => s1
= s2
93 |
(TList t1
, TList t2
) => eqTy (t1
, t2
)
94 |
(TArrow (d1
, r1
), TArrow (d2
, r2
)) =>
95 eqTy (d1
, d2
) andalso eqTy (r1
, r2
)
97 |
(TAction (p1
, d1
, r1
), TAction (p2
, d2
, r2
)) =>
98 eqPred (p1
, p2
) andalso eqRecord
eqTy (d1
, d2
)
99 andalso eqRecord
eqTy (r1
, r2
)
101 |
(TNested (p1
, q1
), TNested (p2
, q2
)) =>
102 eqPred (p1
, p2
) andalso eqPred (q1
, q2
)
104 |
(TUnif (_
, ref (SOME t1
)), _
) => eqTy (t1
, t2All
)
105 |
(_
, TUnif (_
, ref (SOME t2
))) => eqTy (t1All
, t2
)
107 |
(TUnif (_
, r1
), TUnif (_
, r2
)) => r1
= r2
109 |
(TError
, TError
) => true
113 datatype unification_error
=
114 UnifyPred
of pred
* pred
115 | UnifyTyp
of typ
* typ
116 | UnifyOccurs
of string * typ
118 exception Unify
of unification_error
120 datatype type_error
=
121 WrongType
of string * exp
* typ
* typ
* unification_error option
122 | WrongForm
of string * string * exp
* typ
* unification_error option
123 | UnboundVariable
of string
124 | WrongPred
of string * pred
* pred
126 fun preface (s
, d
) = printd (PD
.hovBox (PD
.PPS
.Rel
0,
127 [PD
.string s
, PD
.space
1, d
]))
129 fun describe_unification_error t ue
=
131 UnifyPred (p1
, p2
) =>
132 (print
"Reason: Incompatible contexts.\n";
133 preface ("Have:", p_pred p1
);
134 preface ("Need:", p_pred p2
))
135 |
UnifyTyp (t1
, t2
) =>
139 (print
"Reason: Incompatible types.\n";
140 preface ("Have:", p_typ t1
);
141 preface ("Need:", p_typ t2
))
142 |
UnifyOccurs (name
, t
') =>
146 (print
"Reason: Occurs check failed for ";
151 fun describe_type_error loc te
=
153 WrongType (place
, e
, t1
, t2
, ueo
) =>
154 (ErrorMsg
.error (SOME loc
) (place ^
" has wrong type.");
155 preface (" Expression:", p_exp e
);
156 preface ("Actual type:", p_typ t1
);
157 preface ("Needed type:", p_typ t2
);
158 Option
.app (describe_unification_error t1
) ueo
)
159 |
WrongForm (place
, form
, e
, t
, ueo
) =>
160 (ErrorMsg
.error (SOME loc
) (place ^
" has a non-" ^ form ^
" type.");
161 preface ("Expression:", p_exp e
);
162 preface (" Type:", p_typ t
);
163 Option
.app (describe_unification_error t
) ueo
)
164 | UnboundVariable name
=>
165 ErrorMsg
.error (SOME loc
) ("Unbound variable " ^ name ^
".\n")
166 |
WrongPred (place
, p1
, p2
) =>
167 (ErrorMsg
.error (SOME loc
) ("Context incompatibility for " ^ place ^
".");
168 preface ("Have:", p_pred p1
);
169 preface ("Need:", p_pred p2
))
171 fun predImplies (p1All
as (p1
, _
), p2All
as (p2
, _
)) =
173 (_
, CPrefix (CRoot
, _
)) => true
174 |
(CNot (CPrefix (CRoot
, _
), _
), _
) => true
176 |
(CRoot
, CRoot
) => true
178 |
(CConst s1
, CConst s2
) => s1
= s2
180 |
(CPrefix p1
, CPrefix p2
) => predImplies (p1
, p2
)
181 |
(_
, CPrefix p2
) => predImplies (p1All
, p2
)
183 |
(CNot p1
, CNot p2
) => predImplies (p2
, p1
)
185 |
(_
, CAnd (p1
, p2
)) => predImplies (p1All
, p1
) andalso predImplies (p1All
, p2
)
186 |
(CAnd (p1
, p2
), _
) => predImplies (p1
, p2All
) orelse predImplies (p2
, p2All
)
190 fun predSimpl (pAll
as (p
, loc
)) =
194 | CPrefix p
=> (CPrefix (predSimpl p
), loc
)
195 | CNot p
=> (CNot (predSimpl p
), loc
)
198 val p1
' = predSimpl p1
199 val p2
' = predSimpl p2
202 (CAnd (c1
, c2
), _
) => predSimpl (CAnd (c1
, (CAnd (c2
, p2
'), loc
)), loc
)
203 | _
=> if predImplies (p2
', p1
') then
206 (CAnd (p1
', p2
'), loc
)
209 fun subPred (p1
, p2
) =
210 if predImplies (p1
, p2
) then
213 raise (Unify (UnifyPred (p1
, p2
)))
215 fun subRecord
f (r1
, r2
) =
216 SM
.appi (fn (k
, v2
) =>
217 case SM
.find (r1
, k
) of
218 NONE
=> raise UnequalDomains
219 | SOME v1
=> f (v1
, v2
)) r2
221 fun occurs
u (t
, _
) =
224 | TList t
=> occurs u t
225 |
TArrow (d
, r
) => occurs u d
orelse occurs u r
226 |
TAction (_
, d
, r
) =>
227 List.exists (occurs u
) (SM
.listItems d
)
228 orelse List.exists (occurs u
) (SM
.listItems r
)
231 |
TUnif (_
, ref (SOME t
)) => occurs u t
232 |
TUnif (_
, u
') => u
= u
'
234 fun subTyp (t1All
as (t1
, _
), t2All
as (t2
, _
)) =
236 (TBase s1
, TBase s2
) =>
240 raise Unify (UnifyTyp (t1All
, t2All
))
241 |
(TList t1
, TList t2
) => subTyp (t1
, t2
)
242 |
(TArrow (d1
, r1
), TArrow (d2
, r2
)) =>
246 |
(TAction (p1
, d1
, r1
), TAction (p2
, d2
, r2
)) =>
248 subRecord
subTyp (d2
, d1
);
249 subRecord
subTyp (r1
, r2
);
250 subRecord
subTyp (r2
, r1
))
251 handle UnequalDomains
=> raise Unify (UnifyTyp (t1All
, t2All
)))
253 |
(TNested (d1
, r1
), TNested (d2
, r2
)) =>
257 |
(TUnif (_
, ref (SOME t1
)), _
) => subTyp (t1
, t2All
)
258 |
(_
, TUnif (_
, ref (SOME t2
))) => subTyp (t1All
, t2
)
260 |
(TUnif (_
, r1
), TUnif (_
, r2
)) =>
266 |
(TUnif (name
, r
), _
) =>
267 if occurs r t2All
then
268 raise (Unify (UnifyOccurs (name
, t2All
)))
272 |
(_
, TUnif (name
, r
)) =>
273 if occurs r t1All
then
274 raise (Unify (UnifyOccurs (name
, t1All
)))
281 | _
=> raise Unify (UnifyTyp (t1All
, t2All
))
288 fun whnorm (tAll
as (t
, loc
)) =
290 TUnif (_
, ref (SOME tAll
)) => whnorm tAll
293 fun checkTyp
G (tAll
as (t
, loc
)) =
295 val err
= ErrorMsg
.error (SOME loc
)
299 if lookupType G name
then
302 (err ("Unbound type name " ^ name
);
304 | TList t
=> (TList (checkTyp G t
), loc
)
305 |
TArrow (d
, r
) => (TArrow (checkTyp G d
, checkTyp G r
), loc
)
306 |
TAction (p
, d
, r
) => (TAction (p
, SM
.map (checkTyp G
) d
,
307 SM
.map (checkTyp G
) r
), loc
)
309 | TError
=> raise Fail
"TError in parser-generated type"
310 | TUnif _
=> raise Fail
"TUnif in parser-generated type"
313 fun checkExp
G (eAll
as (e
, loc
)) =
315 val dte
= describe_type_error loc
318 EInt _
=> (TBase
"int", loc
)
319 | EString _
=> (TBase
"string", loc
)
322 val t
= (newUnif (), loc
)
324 foldl (fn (e
', ret
) =>
326 val t
' = checkExp G e
'
330 (TList (TError
, loc
), loc
)
334 (dte (WrongType ("List element",
340 end) (TList t
, loc
) es
347 NONE
=> (newUnif (), loc
)
348 | SOME t
=> checkTyp G t
350 val G
' = bindVal
G (x
, t
)
351 val t
' = checkExp G
' e
353 (TArrow (t
, t
'), loc
)
356 (case lookupVal G x
of
357 NONE
=> (dte (UnboundVariable x
);
360 |
EApp (func
, arg
) =>
362 val dom
= (newUnif (), loc
)
363 val ran
= (newUnif (), loc
)
365 val tf
= checkExp G func
366 val ta
= checkExp G arg
368 (subTyp (tf
, (TArrow (dom
, ran
), loc
));
371 dte (WrongType ("Function argument",
378 (dte (WrongForm ("Function to be applied",
390 (TAction ((CPrefix (CRoot
, loc
), loc
),
392 SM
.insert (SM
.empty
, evar
, t
)),
395 |
EGet (x
, evar
, rest
) =>
397 val xt
= (newUnif (), loc
)
398 val G
' = bindVal
G (x
, xt
)
400 val rt
= whnorm (checkExp G
' rest
)
403 (TAction (p
, d
, r
), _
) =>
404 (case SM
.find (d
, evar
) of
405 NONE
=> (TAction (p
, SM
.insert (d
, evar
, xt
), r
), loc
)
409 dte (WrongType ("Retrieved environment variable",
416 | _
=> (dte (WrongForm ("Body of environment variable read",
424 | ESeq
[] => raise Fail
"Empty ESeq"
425 | ESeq
[e1
] => checkExp G e1
426 |
ESeq (e1
:: rest
) =>
428 val e2
= (ESeq rest
, loc
)
430 val t1
= whnorm (checkExp G e1
)
431 val t2
= whnorm (checkExp G e2
)
434 (TAction (p1
, d1
, r1
), _
) =>
436 (TAction (p2
, d2
, r2
), _
) =>
438 val p
' = predSimpl (CAnd (p1
, p2
), loc
)
440 val d
' = SM
.foldli (fn (name
, t
, d
') =>
441 case SM
.find (r1
, name
) of
443 (case SM
.find (d
', name
) of
444 NONE
=> SM
.insert (d
', name
, t
)
448 dte (WrongType ("Shared environment variable",
457 dte (WrongType ("Shared environment variable",
465 val r
' = SM
.foldli (fn (name
, t
, r
') => SM
.insert (r
', name
, t
))
468 (TAction (p
', d
', r
'), loc
)
471 | _
=> (dte (WrongForm ("Action to be sequenced",
478 | _
=> (dte (WrongForm ("Action to be sequenced",
488 val rt
= whnorm (checkExp G e
)
491 (TAction (p
, d
, _
), _
) =>
492 (TAction (p
, d
, SM
.empty
), loc
)
494 | _
=> (dte (WrongForm ("Body of local action",
504 val t1
= whnorm (checkExp G e1
)
505 val t2
= whnorm (checkExp G e2
)
508 (TNested (pd
, pr
), _
) =>
510 (TAction (p
, d
, r
), _
) =>
511 if predImplies (pd
, p
) then
512 (TAction (pr
, d
, r
), loc
)
514 (dte (WrongPred ("nested action",
520 (dte (WrongForm ("Body of nested action",
528 (dte (WrongForm ("Container of nested action",
536 | ESkip
=> (TAction ((CPrefix (CRoot
, loc
), loc
),
537 SM
.empty
, SM
.empty
), loc
)
542 fun ununif (tAll
as (t
, loc
)) =
545 | TList t
=> (TList (ununif t
), loc
)
546 |
TArrow (d
, r
) => (TArrow (ununif d
, ununif r
), loc
)
547 |
TAction (p
, d
, r
) => (TAction (p
, SM
.map ununif d
, SM
.map ununif r
), loc
)
548 |
TUnif (_
, ref (SOME t
)) => ununif t
552 |
TUnif (_
, ref NONE
) => raise Ununif
554 fun hasError (t
, _
) =
557 | TList t
=> hasError t
558 |
TArrow (d
, r
) => hasError d
orelse hasError r
559 |
TAction (p
, d
, r
) => List.exists
hasError (SM
.listItems d
)
560 orelse List.exists
hasError (SM
.listItems r
)
563 |
TUnif (_
, ref (SOME t
)) => hasError t
564 |
TUnif (_
, ref NONE
) => false
567 fun checkUnit
G (eAll
as (_
, loc
)) =
570 val t
= checkExp G eAll
577 (ErrorMsg
.error (SOME loc
) "Unification variables remain in type:";
582 fun checkDecl
G (d
, _
, loc
) =
584 DExternType name
=> bindType G name
585 |
DExternVal (name
, t
) => bindVal
G (name
, checkTyp G t
)
587 fun checkFile G
tInit (ds
, eo
) =
589 val G
' = foldl (fn (d
, G
) => checkDecl G d
) G ds
593 |
SOME (e
as (_
, loc
)) =>
595 val t
= checkExp G
' e
599 (ErrorMsg
.error (SOME loc
) "Bad type for final expression of source file.";
600 preface ("Actual:", p_typ t
);
601 preface ("Needed:", p_typ tInit
))