comparison src/termination.sml @ 313:e0ed0d4dabc9

Termination checking
author Adam Chlipala <adamc@hcoop.net>
date Tue, 09 Sep 2008 11:46:33 -0400
parents
children a07f476d9b61
comparison
equal deleted inserted replaced
312:f387d12193ba 313:e0ed0d4dabc9
1 (* Copyright (c) 2008, Adam Chlipala
2 * All rights reserved.
3 *
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions are met:
6 *
7 * - Redistributions of source code must retain the above copyright notice,
8 * this list of conditions and the following disclaimer.
9 * - Redistributions in binary form must reproduce the above copyright notice,
10 * this list of conditions and the following disclaimer in the documentation
11 * and/or other materials provided with the distribution.
12 * - The names of contributors may not be used to endorse or promote products
13 * derived from this software without specific prior written permission.
14 *
15 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
16 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
17 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
18 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
19 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
20 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
21 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
22 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
23 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
24 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
25 * POSSIBILITY OF SUCH DAMAGE.
26 *)
27
28 structure Termination :> TERMINATION = struct
29
30 open Elab
31
32 structure E = ElabEnv
33 structure IM = IntBinaryMap
34
35 datatype pedigree =
36 Func of int
37 | Arg of int * int * con
38 | Subarg of int * int * con
39 | Rabble
40
41 fun p2s p =
42 case p of
43 Func i => "Func" ^ Int.toString i
44 | Arg (i, j, _) => "Arg" ^ Int.toString i ^ "," ^ Int.toString j
45 | Subarg (i, j, _) => "Subarg" ^ Int.toString i ^ "," ^ Int.toString j
46 | Rabble => "Rabble"
47
48 fun declOk' env (d, loc) =
49 case d of
50 DValRec vis =>
51 let
52 val nfns = length vis
53
54 val fenv = ListUtil.foldli (fn (i, (_, j, _, _), fenv) => IM.insert (fenv, j, i)) IM.empty vis
55
56 fun namesEq ((c1, _), (c2, _)) =
57 case (c1, c2) of
58 (CName s1, CName s2) => s1 = s2
59 | (CRel n1, CRel n2) => n1 = n2
60 | (CNamed n1, CNamed n2) => n1 = n2
61 | (CModProj n1, CModProj n2) => n1 = n2
62 | _ => false
63
64 fun patCon pc =
65 let
66 fun unravel (t, _) =
67 case t of
68 TCFun (_, _, _, t) => unravel t
69 | TFun (dom, _) => dom
70 | _ => raise Fail "Termination: Unexpected constructor type"
71 in
72 case pc of
73 PConVar i =>
74 let
75 val (_, t) = E.lookupENamed env i
76 in
77 unravel t
78 end
79 | PConProj (m1, ms, x) =>
80 let
81 val (str, sgn) = E.chaseMpath env (m1, ms)
82 in
83 case E.projectVal env {str = str, sgn = sgn, field = x} of
84 NONE => raise Fail "Termination: Bad constructor projection"
85 | SOME t => unravel t
86 end
87 end
88
89 fun pat penv (p, (pt, _)) =
90 let
91 fun con (i, j, pc, pt') = pat penv (Subarg (i, j, patCon pc), pt')
92
93 fun record (i, j, t, xps) =
94 case t of
95 (TRecord (CRecord (_, xts), _), _) =>
96 foldl (fn ((x, pt', _), penv) =>
97 let
98 val p' =
99 case List.find (fn (x', _) =>
100 namesEq ((CName x, ErrorMsg.dummySpan), x')) xts of
101 NONE => Rabble
102 | SOME (_, t) => Subarg (i, j, t)
103 in
104 pat penv (p', pt')
105 end) penv xps
106 | _ => foldl (fn ((_, pt', _), penv) => pat penv (Rabble, pt')) penv xps
107 in
108 case (p, pt) of
109 (_, PWild) => penv
110 | (_, PVar _) => p :: penv
111 | (_, PPrim _) => penv
112 | (_, PCon (_, _, _, NONE)) => penv
113 | (Arg (i, j, _), PCon (_, pc, _, SOME pt')) => con (i, j, pc, pt')
114 | (Subarg (i, j, _), PCon (_, pc, _, SOME pt')) => con (i, j, pc, pt')
115 | (_, PCon (_, _, _, SOME pt')) => pat penv (Rabble, pt')
116 | (Arg (i, j, t), PRecord xps) => record (i, j, t, xps)
117 | (Subarg (i, j, t), PRecord xps) => record (i, j, t, xps)
118 | (_, PRecord xps) => foldl (fn ((_, pt', _), penv) => pat penv (Rabble, pt')) penv xps
119 end
120
121 fun exp (penv, calls) e =
122 let
123 val default = (Rabble, calls)
124
125 fun apps () =
126 let
127 fun combiner calls e =
128 case #1 e of
129 EApp (e1, e2) =>
130 let
131 val (p1, ps, calls) = combiner calls e1
132 val (p2, calls) = exp (penv, calls) e2
133
134 val p = case p1 of
135 Rabble => Rabble
136 | Arg _ => Rabble
137 | Subarg (i, j, (TFun (_, ran), _)) => Subarg (i, j, ran)
138 | Subarg _ => Rabble
139 | Func _ => Rabble
140 in
141 (p, ps @ [p2], calls)
142 end
143 | ECApp (e, _) =>
144 let
145 val (p, ps, calls) = combiner calls e
146
147 val p = case p of
148 Rabble => Rabble
149 | Arg _ => Rabble
150 | Subarg (i, j, (TCFun (_, _, _, ran), _)) => Subarg (i, j, ran)
151 | Subarg _ => Rabble
152 | Func _ => Rabble
153 in
154 (p, ps, calls)
155 end
156 | _ =>
157 let
158 val (p, calls) = exp (penv, calls) e
159 in
160 (*Print.prefaces "Head" [("e", ElabPrint.p_exp env e)];
161 print (p2s p ^ "\n");*)
162 (p, [p], calls)
163 end
164
165 val (p, ps, calls) = combiner calls e
166
167 val calls =
168 case ps of
169 [] => raise Fail "Termination: Empty ps"
170 | f :: ps =>
171 case f of
172 Func i => (i, ps) :: calls
173 | _ => calls
174 in
175 (p, calls)
176 end
177 in
178 case #1 e of
179 EPrim _ => default
180 | ERel n => (List.nth (penv, n), calls)
181 | ENamed n =>
182 let
183 val p = case IM.find (fenv, n) of
184 NONE => Rabble
185 | SOME n' => Func n'
186 in
187 (p, calls)
188 end
189 | EModProj _ => default
190 | EApp _ => apps ()
191 | EAbs (_, _, _, e) =>
192 let
193 val (_, calls) = exp (Rabble :: penv, calls) e
194 in
195 (Rabble, calls)
196 end
197 | ECApp _ => apps ()
198 | ECAbs (_, _, _, e) =>
199 let
200 val (_, calls) = exp (penv, calls) e
201 in
202 (Rabble, calls)
203 end
204
205 | ERecord xets =>
206 let
207 val calls = foldl (fn ((_, e, _), calls) => #2 (exp (penv, calls) e)) calls xets
208 in
209 (Rabble, calls)
210 end
211 | EField (e, x, _) =>
212 let
213 val (p, calls) = exp (penv, calls) e
214 val p =
215 case p of
216 Subarg (i, j, (TRecord (CRecord (_, xts), _), _)) =>
217 (case List.find (fn (x', _) => namesEq (x, x')) xts of
218 NONE => Rabble
219 | SOME (_, t) => Subarg (i, j, t))
220 | _ => Rabble
221 in
222 (p, calls)
223 end
224 | ECut (e, _, _) =>
225 let
226 val (_, calls) = exp (penv, calls) e
227 in
228 (Rabble, calls)
229 end
230 | EFold _ => (Rabble, calls)
231
232 | ECase (e, pes, _) =>
233 let
234 val (p, calls) = exp (penv, calls) e
235
236 val calls = foldl (fn ((pt, e), calls) =>
237 let
238 val penv = pat penv (p, pt)
239 val (_, calls) = exp (penv, calls) e
240 in
241 calls
242 end) calls pes
243 in
244 (Rabble, calls)
245 end
246
247 | EError => (Rabble, calls)
248 | EUnif (ref (SOME e)) => exp (penv, calls) e
249 | EUnif (ref NONE) => (Rabble, calls)
250 end
251
252 fun doVali (i, (_, f, _, e), calls) =
253 let
254 fun unravel (e, j, penv) =
255 case #1 e of
256 EAbs (_, t, _, e) =>
257 unravel (e, j + 1, Arg (i, j, t) :: penv)
258 | ECAbs (_, _, _, e) =>
259 unravel (e, j, penv)
260 | _ => (j, #2 (exp (penv, calls) e))
261 in
262 unravel (e, 0, [])
263 end
264
265 val (ns, calls) = ListUtil.foldliMap doVali [] vis
266
267 fun search (ns, choices) =
268 case ns of
269 [] =>
270 let
271 val choices = rev choices
272 in
273 List.all (fn (f, args) =>
274 let
275 val recArg = List.nth (choices, f)
276
277 fun isDatatype (t, _) =
278 case t of
279 CNamed _ => true
280 | CModProj _ => true
281 | CApp (t, _) => isDatatype t
282 | _ => false
283 in
284 length args > recArg andalso
285 case List.nth (args, recArg) of
286 Subarg (i, j, t) => isDatatype t andalso j = List.nth (choices, i)
287 | _ => false
288 end) calls
289 end
290 | n :: ns' =>
291 let
292 fun search' i =
293 i < n andalso (search (ns', i :: choices) orelse search' (i + 1))
294 in
295 search' 0
296 end
297 in
298 if search (ns, []) then
299 ()
300 else
301 ErrorMsg.errorAt loc "Can't prove termination of recursive function(s)"
302 end
303
304 | DStr (_, _, _, (StrConst ds, _)) => ignore (foldl declOk env ds)
305
306 | _ => ()
307
308 and declOk (d, env) =
309 (declOk' env d;
310 E.declBinds env d)
311
312 fun check ds = ignore (foldl declOk E.empty ds)
313
314 end