annotate src/iflow.sml @ 1208:b5a4c5407ae0

Checking known() correctly, according to a pair of examples
author Adam Chlipala <adamc@hcoop.net>
date Tue, 06 Apr 2010 10:39:15 -0400
parents ae3036773768
children 775357041e48
rev   line source
adamc@1200 1 (* Copyright (c) 2010, Adam Chlipala
adamc@1200 2 * All rights reserved.
adamc@1200 3 *
adamc@1200 4 * Redistribution and use in source and binary forms, with or without
adamc@1200 5 * modification, are permitted provided that the following conditions are met:
adamc@1200 6 *
adamc@1200 7 * - Redistributions of source code must retain the above copyright notice,
adamc@1200 8 * this list of conditions and the following disclaimer.
adamc@1200 9 * - Redistributions in binary form must reproduce the above copyright notice,
adamc@1200 10 * this list of conditions and the following disclaimer in the documentation
adamc@1200 11 * and/or other materials provided with the distribution.
adamc@1200 12 * - The names of contributors may not be used to endorse or promote products
adamc@1200 13 * derived from this software without specific prior written permission.
adamc@1200 14 *
adamc@1200 15 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
adamc@1200 16 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
adamc@1200 17 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
adamc@1200 18 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
adamc@1200 19 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
adamc@1200 20 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
adamc@1200 21 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
adamc@1200 22 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
adamc@1200 23 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
adamc@1200 24 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
adamc@1200 25 * POSSIBILITY OF SUCH DAMAGE.
adamc@1200 26 *)
adamc@1200 27
adamc@1200 28 structure Iflow :> IFLOW = struct
adamc@1200 29
adamc@1200 30 open Mono
adamc@1200 31
adamc@1207 32 structure IS = IntBinarySet
adamc@1202 33 structure IM = IntBinaryMap
adamc@1202 34
adamc@1200 35 structure SS = BinarySetFn(struct
adamc@1200 36 type ord_key = string
adamc@1200 37 val compare = String.compare
adamc@1200 38 end)
adamc@1200 39
adamc@1200 40 val writers = ["htmlifyInt_w",
adamc@1200 41 "htmlifyFloat_w",
adamc@1200 42 "htmlifyString_w",
adamc@1200 43 "htmlifyBool_w",
adamc@1200 44 "htmlifyTime_w",
adamc@1200 45 "attrifyInt_w",
adamc@1200 46 "attrifyFloat_w",
adamc@1200 47 "attrifyString_w",
adamc@1200 48 "attrifyChar_w",
adamc@1200 49 "urlifyInt_w",
adamc@1200 50 "urlifyFloat_w",
adamc@1200 51 "urlifyString_w",
adamc@1200 52 "urlifyBool_w"]
adamc@1200 53
adamc@1200 54 val writers = SS.addList (SS.empty, writers)
adamc@1200 55
adamc@1200 56 type lvar = int
adamc@1200 57
adamc@1200 58 datatype exp =
adamc@1200 59 Const of Prim.t
adamc@1200 60 | Var of int
adamc@1200 61 | Lvar of lvar
adamc@1200 62 | Func of string * exp list
adamc@1200 63 | Recd of (string * exp) list
adamc@1200 64 | Proj of exp * string
adamc@1200 65 | Finish
adamc@1200 66
adamc@1200 67 datatype reln =
adamc@1207 68 Known
adamc@1207 69 | Sql of string
adamc@1200 70 | Eq
adamc@1200 71
adamc@1200 72 datatype prop =
adamc@1200 73 True
adamc@1200 74 | False
adamc@1200 75 | Unknown
adamc@1200 76 | And of prop * prop
adamc@1200 77 | Or of prop * prop
adamc@1200 78 | Reln of reln * exp list
adamc@1200 79 | Select of int * lvar * lvar * prop * exp
adamc@1200 80
adamc@1200 81 local
adamc@1207 82 open Print
adamc@1207 83 val string = PD.string
adamc@1207 84 in
adamc@1207 85
adamc@1207 86 fun p_exp e =
adamc@1207 87 case e of
adamc@1207 88 Const p => Prim.p_t p
adamc@1207 89 | Var n => string ("x" ^ Int.toString n)
adamc@1207 90 | Lvar n => string ("X" ^ Int.toString n)
adamc@1207 91 | Func (f, es) => box [string (f ^ "("),
adamc@1207 92 p_list p_exp es,
adamc@1207 93 string ")"]
adamc@1207 94 | Recd xes => box [string "{",
adamc@1207 95 p_list (fn (x, e) => box [string "x",
adamc@1207 96 space,
adamc@1207 97 string "=",
adamc@1207 98 space,
adamc@1207 99 p_exp e]) xes,
adamc@1207 100 string "}"]
adamc@1207 101 | Proj (e, x) => box [p_exp e,
adamc@1207 102 string ("." ^ x)]
adamc@1207 103 | Finish => string "FINISH"
adamc@1207 104
adamc@1207 105 fun p_reln r es =
adamc@1207 106 case r of
adamc@1207 107 Known =>
adamc@1207 108 (case es of
adamc@1207 109 [e] => box [string "known(",
adamc@1207 110 p_exp e,
adamc@1207 111 string ")"]
adamc@1207 112 | _ => raise Fail "Iflow.p_reln: Known")
adamc@1207 113 | Sql s => box [string (s ^ "("),
adamc@1207 114 p_list p_exp es,
adamc@1207 115 string ")"]
adamc@1207 116 | Eq =>
adamc@1207 117 (case es of
adamc@1207 118 [e1, e2] => box [p_exp e1,
adamc@1207 119 space,
adamc@1207 120 string "=",
adamc@1207 121 space,
adamc@1207 122 p_exp e2]
adamc@1207 123 | _ => raise Fail "Iflow.p_reln: Eq")
adamc@1207 124
adamc@1207 125 fun p_prop p =
adamc@1207 126 case p of
adamc@1207 127 True => string "True"
adamc@1207 128 | False => string "False"
adamc@1207 129 | Unknown => string "??"
adamc@1207 130 | And (p1, p2) => box [string "(",
adamc@1207 131 p_prop p1,
adamc@1207 132 string ")",
adamc@1207 133 space,
adamc@1207 134 string "&&",
adamc@1207 135 space,
adamc@1207 136 string "(",
adamc@1207 137 p_prop p2,
adamc@1207 138 string ")"]
adamc@1207 139 | Or (p1, p2) => box [string "(",
adamc@1207 140 p_prop p1,
adamc@1207 141 string ")",
adamc@1207 142 space,
adamc@1207 143 string "||",
adamc@1207 144 space,
adamc@1207 145 string "(",
adamc@1207 146 p_prop p2,
adamc@1207 147 string ")"]
adamc@1207 148 | Reln (r, es) => p_reln r es
adamc@1207 149 | Select (n1, n2, n3, p, e) => box [string ("select(x" ^ Int.toString n1
adamc@1207 150 ^ ",X" ^ Int.toString n2
adamc@1207 151 ^ ",X" ^ Int.toString n3
adamc@1207 152 ^ "){"),
adamc@1207 153 p_prop p,
adamc@1207 154 string "}{",
adamc@1207 155 p_exp e,
adamc@1207 156 string "}"]
adamc@1207 157
adamc@1207 158 end
adamc@1207 159
adamc@1207 160 local
adamc@1202 161 val count = ref 1
adamc@1200 162 in
adamc@1200 163 fun newLvar () =
adamc@1200 164 let
adamc@1200 165 val n = !count
adamc@1200 166 in
adamc@1200 167 count := n + 1;
adamc@1200 168 n
adamc@1200 169 end
adamc@1200 170 end
adamc@1200 171
adamc@1200 172 fun subExp (v, lv) =
adamc@1200 173 let
adamc@1200 174 fun sub e =
adamc@1200 175 case e of
adamc@1200 176 Const _ => e
adamc@1200 177 | Var v' => if v' = v then Lvar lv else e
adamc@1200 178 | Lvar _ => e
adamc@1200 179 | Func (f, es) => Func (f, map sub es)
adamc@1200 180 | Recd xes => Recd (map (fn (x, e) => (x, sub e)) xes)
adamc@1200 181 | Proj (e, s) => Proj (sub e, s)
adamc@1200 182 | Finish => Finish
adamc@1200 183 in
adamc@1200 184 sub
adamc@1200 185 end
adamc@1200 186
adamc@1200 187 fun subProp (v, lv) =
adamc@1200 188 let
adamc@1200 189 fun sub p =
adamc@1200 190 case p of
adamc@1200 191 True => p
adamc@1200 192 | False => p
adamc@1200 193 | Unknown => p
adamc@1200 194 | And (p1, p2) => And (sub p1, sub p2)
adamc@1200 195 | Or (p1, p2) => Or (sub p1, sub p2)
adamc@1200 196 | Reln (r, es) => Reln (r, map (subExp (v, lv)) es)
adamc@1200 197 | Select (v1, lv1, lv2, p, e) => Select (v1, lv1, lv2, sub p, subExp (v, lv) e)
adamc@1200 198 in
adamc@1200 199 sub
adamc@1200 200 end
adamc@1200 201
adamc@1200 202 fun isKnown e =
adamc@1200 203 case e of
adamc@1200 204 Const _ => true
adamc@1200 205 | Func (_, es) => List.all isKnown es
adamc@1200 206 | Recd xes => List.all (isKnown o #2) xes
adamc@1200 207 | Proj (e, _) => isKnown e
adamc@1200 208 | _ => false
adamc@1200 209
adamc@1200 210 fun isFinish e =
adamc@1200 211 case e of
adamc@1200 212 Finish => true
adamc@1200 213 | _ => false
adamc@1200 214
adamc@1208 215 val unif = ref (IM.empty : exp IM.map)
adamc@1208 216
adamc@1208 217 fun reset () = unif := IM.empty
adamc@1208 218 fun save () = !unif
adamc@1208 219 fun restore x = unif := x
adamc@1208 220
adamc@1200 221 fun simplify e =
adamc@1200 222 case e of
adamc@1200 223 Const _ => e
adamc@1200 224 | Var _ => e
adamc@1208 225 | Lvar n =>
adamc@1208 226 (case IM.find (!unif, n) of
adamc@1208 227 NONE => e
adamc@1208 228 | SOME e => simplify e)
adamc@1200 229 | Func (f, es) =>
adamc@1200 230 let
adamc@1200 231 val es = map simplify es
adamc@1200 232 in
adamc@1200 233 if List.exists isFinish es then
adamc@1200 234 Finish
adamc@1200 235 else
adamc@1200 236 Func (f, es)
adamc@1200 237 end
adamc@1200 238 | Recd xes =>
adamc@1200 239 let
adamc@1200 240 val xes = map (fn (x, e) => (x, simplify e)) xes
adamc@1200 241 in
adamc@1200 242 if List.exists (isFinish o #2) xes then
adamc@1200 243 Finish
adamc@1200 244 else
adamc@1200 245 Recd xes
adamc@1200 246 end
adamc@1200 247 | Proj (e, s) =>
adamc@1200 248 (case simplify e of
adamc@1200 249 Recd xes =>
adamc@1200 250 getOpt (ListUtil.search (fn (x, e') => if x = s then SOME e' else NONE) xes, Recd xes)
adamc@1200 251 | e' =>
adamc@1200 252 if isFinish e' then
adamc@1200 253 Finish
adamc@1200 254 else
adamc@1200 255 Proj (e', s))
adamc@1200 256 | Finish => Finish
adamc@1200 257
adamc@1202 258 fun decomp fals or =
adamc@1200 259 let
adamc@1200 260 fun decomp p k =
adamc@1200 261 case p of
adamc@1200 262 True => k []
adamc@1202 263 | False => fals
adamc@1200 264 | Unknown => k []
adamc@1200 265 | And (p1, p2) =>
adamc@1200 266 decomp p1 (fn ps1 =>
adamc@1200 267 decomp p2 (fn ps2 =>
adamc@1200 268 k (ps1 @ ps2)))
adamc@1200 269 | Or (p1, p2) =>
adamc@1200 270 or (decomp p1 k, fn () => decomp p2 k)
adamc@1200 271 | Reln x => k [x]
adamc@1200 272 | Select _ => k []
adamc@1200 273 in
adamc@1200 274 decomp
adamc@1200 275 end
adamc@1200 276
adamc@1202 277 fun lvarIn lv =
adamc@1202 278 let
adamc@1202 279 fun lvi e =
adamc@1202 280 case e of
adamc@1202 281 Const _ => false
adamc@1202 282 | Var _ => false
adamc@1202 283 | Lvar lv' => lv' = lv
adamc@1202 284 | Func (_, es) => List.exists lvi es
adamc@1202 285 | Recd xes => List.exists (lvi o #2) xes
adamc@1202 286 | Proj (e, _) => lvi e
adamc@1202 287 | Finish => false
adamc@1202 288 in
adamc@1202 289 lvi
adamc@1202 290 end
adamc@1202 291
adamc@1202 292 fun eq' (e1, e2) =
adamc@1202 293 case (e1, e2) of
adamc@1202 294 (Const p1, Const p2) => Prim.equal (p1, p2)
adamc@1202 295 | (Var n1, Var n2) => n1 = n2
adamc@1202 296
adamc@1202 297 | (Lvar n1, _) =>
adamc@1202 298 (case IM.find (!unif, n1) of
adamc@1202 299 SOME e1 => eq' (e1, e2)
adamc@1202 300 | NONE =>
adamc@1202 301 case e2 of
adamc@1202 302 Lvar n2 =>
adamc@1202 303 (case IM.find (!unif, n2) of
adamc@1202 304 SOME e2 => eq' (e1, e2)
adamc@1202 305 | NONE => n1 = n2
adamc@1208 306 orelse (unif := IM.insert (!unif, n2, e1);
adamc@1202 307 true))
adamc@1202 308 | _ =>
adamc@1202 309 if lvarIn n1 e2 then
adamc@1202 310 false
adamc@1202 311 else
adamc@1202 312 (unif := IM.insert (!unif, n1, e2);
adamc@1202 313 true))
adamc@1202 314
adamc@1202 315 | (_, Lvar n2) =>
adamc@1202 316 (case IM.find (!unif, n2) of
adamc@1202 317 SOME e2 => eq' (e1, e2)
adamc@1202 318 | NONE =>
adamc@1202 319 if lvarIn n2 e1 then
adamc@1202 320 false
adamc@1202 321 else
adamc@1202 322 (unif := IM.insert (!unif, n2, e1);
adamc@1202 323 true))
adamc@1202 324
adamc@1202 325 | (Func (f1, es1), Func (f2, es2)) => f1 = f2 andalso ListPair.allEq eq' (es1, es2)
adamc@1202 326 | (Recd xes1, Recd xes2) => ListPair.allEq (fn ((x1, e1), (x2, e2)) => x1 = x2 andalso eq' (e1, e2)) (xes1, xes2)
adamc@1202 327 | (Proj (e1, s1), Proj (e2, s2)) => eq' (e1, e2) andalso s1 = s2
adamc@1202 328 | (Finish, Finish) => true
adamc@1202 329 | _ => false
adamc@1202 330
adamc@1202 331 fun eq (e1, e2) =
adamc@1202 332 let
adamc@1203 333 val saved = save ()
adamc@1202 334 in
adamc@1202 335 if eq' (simplify e1, simplify e2) then
adamc@1202 336 true
adamc@1202 337 else
adamc@1203 338 (restore saved;
adamc@1202 339 false)
adamc@1202 340 end
adamc@1202 341
adamc@1202 342 exception Imply of prop * prop
adamc@1202 343
adamc@1208 344 val debug = ref false
adamc@1208 345
adamc@1208 346 (* Congruence closure *)
adamc@1208 347 structure Cc :> sig
adamc@1208 348 type t
adamc@1208 349 val empty : t
adamc@1208 350 val assert : t * exp * exp -> t
adamc@1208 351 val query : t * exp * exp -> bool
adamc@1208 352 val allPeers : t * exp -> exp list
adamc@1208 353 end = struct
adamc@1208 354
adamc@1208 355 fun eq' (e1, e2) =
adamc@1208 356 case (e1, e2) of
adamc@1208 357 (Const p1, Const p2) => Prim.equal (p1, p2)
adamc@1208 358 | (Var n1, Var n2) => n1 = n2
adamc@1208 359 | (Lvar n1, Lvar n2) => n1 = n2
adamc@1208 360 | (Func (f1, es1), Func (f2, es2)) => f1 = f2 andalso ListPair.allEq eq' (es1, es2)
adamc@1208 361 | (Recd xes1, Recd xes2) => length xes1 = length xes2 andalso
adamc@1208 362 List.all (fn (x2, e2) =>
adamc@1208 363 List.exists (fn (x1, e1) => x1 = x2 andalso eq' (e1, e2)) xes2) xes1
adamc@1208 364 | (Proj (e1, x1), Proj (e2, x2)) => eq' (e1, e2) andalso x1 = x2
adamc@1208 365 | (Finish, Finish) => true
adamc@1208 366 | _ => false
adamc@1208 367
adamc@1208 368 fun eq (e1, e2) = eq' (simplify e1, simplify e2)
adamc@1208 369
adamc@1208 370 type t = (exp * exp) list
adamc@1208 371
adamc@1208 372 val empty = []
adamc@1208 373
adamc@1208 374 fun lookup (t, e) =
adamc@1208 375 case List.find (fn (e', _) => eq (e', e)) t of
adamc@1208 376 NONE => e
adamc@1208 377 | SOME (_, e2) => lookup (t, e2)
adamc@1208 378
adamc@1208 379 fun assert (t, e1, e2) =
adamc@1208 380 let
adamc@1208 381 val r1 = lookup (t, e1)
adamc@1208 382 val r2 = lookup (t, e2)
adamc@1208 383 in
adamc@1208 384 if eq (r1, r2) then
adamc@1208 385 t
adamc@1208 386 else
adamc@1208 387 (r1, r2) :: t
adamc@1208 388 end
adamc@1208 389
adamc@1208 390 open Print
adamc@1208 391
adamc@1208 392 fun query (t, e1, e2) =
adamc@1208 393 (if !debug then
adamc@1208 394 prefaces "CC query" [("e1", p_exp (simplify e1)),
adamc@1208 395 ("e2", p_exp (simplify e2)),
adamc@1208 396 ("t", p_list (fn (e1, e2) => box [p_exp (simplify e1),
adamc@1208 397 space,
adamc@1208 398 PD.string "->",
adamc@1208 399 space,
adamc@1208 400 p_exp (simplify e2)]) t)]
adamc@1208 401 else
adamc@1208 402 ();
adamc@1208 403 eq (lookup (t, e1), lookup (t, e2)))
adamc@1208 404
adamc@1208 405 fun allPeers (t, e) =
adamc@1208 406 let
adamc@1208 407 val r = lookup (t, e)
adamc@1208 408 in
adamc@1208 409 r :: List.mapPartial (fn (e1, e2) =>
adamc@1208 410 let
adamc@1208 411 val r' = lookup (t, e2)
adamc@1208 412 in
adamc@1208 413 if eq (r, r') then
adamc@1208 414 SOME e1
adamc@1208 415 else
adamc@1208 416 NONE
adamc@1208 417 end) t
adamc@1208 418 end
adamc@1208 419
adamc@1208 420 end
adamc@1208 421
adamc@1208 422 fun rimp cc ((r1, es1), (r2, es2)) =
adamc@1202 423 case (r1, r2) of
adamc@1202 424 (Sql r1', Sql r2') =>
adamc@1202 425 r1' = r2' andalso
adamc@1202 426 (case (es1, es2) of
adamc@1202 427 ([Recd xes1], [Recd xes2]) =>
adamc@1202 428 let
adamc@1203 429 val saved = save ()
adamc@1202 430 in
adamc@1202 431 if List.all (fn (f, e2) =>
adamc@1203 432 case ListUtil.search (fn (f', e1) => if f' = f then SOME e1 else NONE) xes1 of
adamc@1203 433 NONE => true
adamc@1203 434 | SOME e1 => eq (e1, e2)) xes2 then
adamc@1202 435 true
adamc@1202 436 else
adamc@1203 437 (restore saved;
adamc@1202 438 false)
adamc@1202 439 end
adamc@1202 440 | _ => false)
adamc@1202 441 | (Eq, Eq) =>
adamc@1202 442 (case (es1, es2) of
adamc@1202 443 ([x1, y1], [x2, y2]) =>
adamc@1202 444 let
adamc@1203 445 val saved = save ()
adamc@1202 446 in
adamc@1202 447 if eq (x1, x2) andalso eq (y1, y2) then
adamc@1202 448 true
adamc@1202 449 else
adamc@1203 450 (restore saved;
adamc@1208 451 if eq (x1, y2) andalso eq (y1, x2) then
adamc@1208 452 true
adamc@1208 453 else
adamc@1208 454 (restore saved;
adamc@1208 455 false))
adamc@1202 456 end
adamc@1202 457 | _ => false)
adamc@1207 458 | (Known, Known) =>
adamc@1207 459 (case (es1, es2) of
adamc@1208 460 ([Var v], [e2]) =>
adamc@1207 461 let
adamc@1208 462 fun matches e =
adamc@1208 463 case e of
adamc@1208 464 Var v' => v' = v
adamc@1208 465 | Proj (e, _) => matches e
adamc@1207 466 | _ => false
adamc@1207 467 in
adamc@1208 468 List.exists matches (Cc.allPeers (cc, e2))
adamc@1207 469 end
adamc@1207 470 | _ => false)
adamc@1202 471 | _ => false
adamc@1202 472
adamc@1202 473 fun imply (p1, p2) =
adamc@1208 474 let
adamc@1208 475 fun doOne doKnown =
adamc@1208 476 decomp true (fn (e1, e2) => e1 andalso e2 ()) p1
adamc@1208 477 (fn hyps =>
adamc@1208 478 decomp false (fn (e1, e2) => e1 orelse e2 ()) p2
adamc@1208 479 (fn goals =>
adamc@1208 480 let
adamc@1208 481 val cc = foldl (fn (p, cc) =>
adamc@1208 482 case p of
adamc@1208 483 (Eq, [e1, e2]) => Cc.assert (cc, e1, e2)
adamc@1208 484 | _ => cc) Cc.empty hyps
adamc@1202 485
adamc@1208 486 fun gls goals onFail =
adamc@1208 487 case goals of
adamc@1208 488 [] => true
adamc@1208 489 | g :: goals =>
adamc@1208 490 case (doKnown, g) of
adamc@1208 491 (false, (Known, _)) => gls goals onFail
adamc@1208 492 | _ =>
adamc@1208 493 let
adamc@1208 494 fun hps hyps =
adamc@1208 495 case hyps of
adamc@1208 496 [] => onFail ()
adamc@1208 497 | h :: hyps =>
adamc@1208 498 let
adamc@1208 499 val saved = save ()
adamc@1208 500 in
adamc@1208 501 if rimp cc (h, g) then
adamc@1208 502 let
adamc@1208 503 val changed = IM.numItems (!unif)
adamc@1208 504 <> IM.numItems saved
adamc@1208 505 in
adamc@1208 506 gls goals (fn () => (restore saved;
adamc@1208 507 changed andalso hps hyps))
adamc@1208 508 end
adamc@1208 509 else
adamc@1208 510 hps hyps
adamc@1208 511 end
adamc@1208 512 in
adamc@1208 513 (case g of
adamc@1208 514 (Eq, [e1, e2]) => Cc.query (cc, e1, e2)
adamc@1208 515 | _ => false)
adamc@1208 516 orelse hps hyps
adamc@1208 517 end
adamc@1208 518 in
adamc@1208 519 gls goals (fn () => false)
adamc@1208 520 end))
adamc@1208 521 in
adamc@1208 522 reset ();
adamc@1208 523 doOne false;
adamc@1208 524 doOne true
adamc@1208 525 end
adamc@1200 526
adamc@1200 527 fun patCon pc =
adamc@1200 528 case pc of
adamc@1200 529 PConVar n => "C" ^ Int.toString n
adamc@1200 530 | PConFfi {mod = m, datatyp = d, con = c, ...} => m ^ "." ^ d ^ "." ^ c
adamc@1200 531
adamc@1202 532
adamc@1200 533
adamc@1200 534 datatype chunk =
adamc@1200 535 String of string
adamc@1200 536 | Exp of Mono.exp
adamc@1200 537
adamc@1200 538 fun chunkify e =
adamc@1200 539 case #1 e of
adamc@1200 540 EPrim (Prim.String s) => [String s]
adamc@1207 541 | EStrcat (e1, e2) =>
adamc@1207 542 let
adamc@1207 543 val chs1 = chunkify e1
adamc@1207 544 val chs2 = chunkify e2
adamc@1207 545 in
adamc@1207 546 case chs2 of
adamc@1207 547 String s2 :: chs2' =>
adamc@1207 548 (case List.last chs1 of
adamc@1207 549 String s1 => List.take (chs1, length chs1 - 1) @ String (s1 ^ s2) :: chs2'
adamc@1207 550 | _ => chs1 @ chs2)
adamc@1207 551 | _ => chs1 @ chs2
adamc@1207 552 end
adamc@1200 553 | _ => [Exp e]
adamc@1200 554
adamc@1201 555 type 'a parser = chunk list -> ('a * chunk list) option
adamc@1201 556
adamc@1201 557 fun always v chs = SOME (v, chs)
adamc@1201 558
adamc@1202 559 fun parse p s =
adamc@1202 560 case p (chunkify s) of
adamc@1201 561 SOME (v, []) => SOME v
adamc@1201 562 | _ => NONE
adamc@1201 563
adamc@1201 564 fun const s chs =
adamc@1201 565 case chs of
adamc@1201 566 String s' :: chs => if String.isPrefix s s' then
adamc@1201 567 SOME ((), if size s = size s' then
adamc@1201 568 chs
adamc@1201 569 else
adamc@1201 570 String (String.extract (s', size s, NONE)) :: chs)
adamc@1201 571 else
adamc@1201 572 NONE
adamc@1201 573 | _ => NONE
adamc@1201 574
adamc@1201 575 fun follow p1 p2 chs =
adamc@1201 576 case p1 chs of
adamc@1201 577 NONE => NONE
adamc@1201 578 | SOME (v1, chs) =>
adamc@1201 579 case p2 chs of
adamc@1201 580 NONE => NONE
adamc@1201 581 | SOME (v2, chs) => SOME ((v1, v2), chs)
adamc@1201 582
adamc@1201 583 fun wrap p f chs =
adamc@1201 584 case p chs of
adamc@1201 585 NONE => NONE
adamc@1201 586 | SOME (v, chs) => SOME (f v, chs)
adamc@1201 587
adamc@1201 588 fun alt p1 p2 chs =
adamc@1201 589 case p1 chs of
adamc@1201 590 NONE => p2 chs
adamc@1201 591 | v => v
adamc@1201 592
adamc@1207 593 fun altL ps =
adamc@1207 594 case rev ps of
adamc@1207 595 [] => (fn _ => NONE)
adamc@1207 596 | p :: ps =>
adamc@1207 597 foldl (fn (p1, p2) => alt p1 p2) p ps
adamc@1207 598
adamc@1204 599 fun opt p chs =
adamc@1204 600 case p chs of
adamc@1204 601 NONE => SOME (NONE, chs)
adamc@1204 602 | SOME (v, chs) => SOME (SOME v, chs)
adamc@1204 603
adamc@1201 604 fun skip cp chs =
adamc@1201 605 case chs of
adamc@1201 606 String "" :: chs => skip cp chs
adamc@1201 607 | String s :: chs' => if cp (String.sub (s, 0)) then
adamc@1201 608 skip cp (String (String.extract (s, 1, NONE)) :: chs')
adamc@1201 609 else
adamc@1201 610 SOME ((), chs)
adamc@1201 611 | _ => SOME ((), chs)
adamc@1201 612
adamc@1201 613 fun keep cp chs =
adamc@1201 614 case chs of
adamc@1201 615 String "" :: chs => keep cp chs
adamc@1201 616 | String s :: chs' =>
adamc@1201 617 let
adamc@1201 618 val (befor, after) = Substring.splitl cp (Substring.full s)
adamc@1201 619 in
adamc@1201 620 if Substring.isEmpty befor then
adamc@1201 621 NONE
adamc@1201 622 else
adamc@1201 623 SOME (Substring.string befor,
adamc@1201 624 if Substring.isEmpty after then
adamc@1201 625 chs'
adamc@1201 626 else
adamc@1201 627 String (Substring.string after) :: chs')
adamc@1201 628 end
adamc@1201 629 | _ => NONE
adamc@1201 630
adamc@1204 631 fun ws p = wrap (follow (skip (fn ch => ch = #" "))
adamc@1204 632 (follow p (skip (fn ch => ch = #" ")))) (#1 o #2)
adamc@1204 633
adamc@1204 634 fun log name p chs =
adamc@1206 635 (if !debug then
adamc@1206 636 case chs of
adamc@1207 637 String s :: _ => print (name ^ ": " ^ s ^ "\n")
adamc@1206 638 | _ => print (name ^ ": blocked!\n")
adamc@1206 639 else
adamc@1206 640 ();
adamc@1204 641 p chs)
adamc@1201 642
adamc@1201 643 fun list p chs =
adamc@1207 644 altL [wrap (follow p (follow (ws (const ",")) (list p)))
adamc@1207 645 (fn (v, ((), ls)) => v :: ls),
adamc@1207 646 wrap (ws p) (fn v => [v]),
adamc@1207 647 always []] chs
adamc@1201 648
adamc@1201 649 val ident = keep (fn ch => Char.isAlphaNum ch orelse ch = #"_")
adamc@1201 650
adamc@1201 651 val t_ident = wrap ident (fn s => if String.isPrefix "T_" s then
adamc@1201 652 String.extract (s, 2, NONE)
adamc@1201 653 else
adamc@1201 654 raise Fail "Iflow: Bad table variable")
adamc@1201 655 val uw_ident = wrap ident (fn s => if String.isPrefix "uw_" s then
adamc@1201 656 String.extract (s, 3, NONE)
adamc@1201 657 else
adamc@1201 658 raise Fail "Iflow: Bad uw_* variable")
adamc@1201 659
adamc@1201 660 val sitem = wrap (follow t_ident
adamc@1201 661 (follow (const ".")
adamc@1201 662 uw_ident))
adamc@1201 663 (fn (t, ((), f)) => (t, f))
adamc@1201 664
adamc@1206 665 datatype Rel =
adamc@1206 666 Exps of exp * exp -> prop
adamc@1206 667 | Props of prop * prop -> prop
adamc@1206 668
adamc@1204 669 datatype sqexp =
adamc@1206 670 SqConst of Prim.t
adamc@1206 671 | Field of string * string
adamc@1206 672 | Binop of Rel * sqexp * sqexp
adamc@1207 673 | SqKnown of sqexp
adamc@1207 674 | Inj of Mono.exp
adamc@1204 675
adamc@1207 676 val sqbrel = altL [wrap (const "=") (fn () => Exps (fn (e1, e2) => Reln (Eq, [e1, e2]))),
adamc@1207 677 wrap (const "AND") (fn () => Props And),
adamc@1207 678 wrap (const "OR") (fn () => Props Or)]
adamc@1204 679
adamc@1204 680 datatype ('a, 'b) sum = inl of 'a | inr of 'b
adamc@1204 681
adamc@1206 682 fun int chs =
adamc@1206 683 case chs of
adamc@1206 684 String s :: chs' =>
adamc@1206 685 let
adamc@1206 686 val (befor, after) = Substring.splitl Char.isDigit (Substring.full s)
adamc@1206 687 in
adamc@1206 688 if Substring.isEmpty befor then
adamc@1206 689 NONE
adamc@1206 690 else case Int64.fromString (Substring.string befor) of
adamc@1206 691 NONE => NONE
adamc@1206 692 | SOME n => SOME (n, if Substring.isEmpty after then
adamc@1206 693 chs'
adamc@1206 694 else
adamc@1206 695 String (Substring.string after) :: chs')
adamc@1206 696 end
adamc@1206 697 | _ => NONE
adamc@1206 698
adamc@1206 699 val prim = wrap (follow (wrap int Prim.Int) (opt (const "::int8"))) #1
adamc@1206 700
adamc@1207 701 fun known' chs =
adamc@1207 702 case chs of
adamc@1207 703 Exp (EFfi ("Basis", "sql_known"), _) :: chs => SOME ((), chs)
adamc@1207 704 | _ => NONE
adamc@1207 705
adamc@1207 706 fun sqlify chs =
adamc@1207 707 case chs of
adamc@1207 708 Exp (EFfiApp ("Basis", f, [e]), _) :: chs =>
adamc@1207 709 if String.isPrefix "sqlify" f then
adamc@1207 710 SOME (e, chs)
adamc@1207 711 else
adamc@1207 712 NONE
adamc@1207 713 | _ => NONE
adamc@1207 714
adamc@1204 715 fun sqexp chs =
adamc@1206 716 log "sqexp"
adamc@1207 717 (altL [wrap prim SqConst,
adamc@1207 718 wrap sitem Field,
adamc@1207 719 wrap known SqKnown,
adamc@1207 720 wrap sqlify Inj,
adamc@1207 721 wrap (follow (ws (const "("))
adamc@1207 722 (follow (wrap
adamc@1207 723 (follow sqexp
adamc@1207 724 (alt
adamc@1207 725 (wrap
adamc@1207 726 (follow (ws sqbrel)
adamc@1207 727 (ws sqexp))
adamc@1207 728 inl)
adamc@1207 729 (always (inr ()))))
adamc@1207 730 (fn (e1, sm) =>
adamc@1207 731 case sm of
adamc@1207 732 inl (bo, e2) => Binop (bo, e1, e2)
adamc@1207 733 | inr () => e1))
adamc@1207 734 (const ")")))
adamc@1207 735 (fn ((), (e, ())) => e)])
adamc@1207 736 chs
adamc@1206 737
adamc@1207 738 and known chs = wrap (follow known' (follow (const "(") (follow sqexp (const ")"))))
adamc@1207 739 (fn ((), ((), (e, ()))) => e) chs
adamc@1207 740
adamc@1207 741 val select = log "select"
adamc@1207 742 (wrap (follow (const "SELECT ") (list sitem))
adamc@1207 743 (fn ((), ls) => ls))
adamc@1201 744
adamc@1201 745 val fitem = wrap (follow uw_ident
adamc@1201 746 (follow (const " AS ")
adamc@1201 747 t_ident))
adamc@1201 748 (fn (t, ((), f)) => (t, f))
adamc@1201 749
adamc@1207 750 val from = log "from"
adamc@1207 751 (wrap (follow (const "FROM ") (list fitem))
adamc@1207 752 (fn ((), ls) => ls))
adamc@1201 753
adamc@1204 754 val wher = wrap (follow (ws (const "WHERE ")) sqexp)
adamc@1204 755 (fn ((), ls) => ls)
adamc@1204 756
adamc@1207 757 val query = log "query"
adamc@1207 758 (wrap (follow (follow select from) (opt wher))
adamc@1207 759 (fn ((fs, ts), wher) => {Select = fs, From = ts, Where = wher}))
adamc@1201 760
adamc@1207 761 fun queryProp env rv oe e =
adamc@1202 762 case parse query e of
adamc@1207 763 NONE => (print ("Warning: Information flow checker can't parse SQL query at "
adamc@1207 764 ^ ErrorMsg.spanToString (#2 e) ^ "\n");
adamc@1207 765 Unknown)
adamc@1201 766 | SOME r =>
adamc@1202 767 let
adamc@1202 768 val p =
adamc@1202 769 foldl (fn ((t, v), p) =>
adamc@1202 770 And (p,
adamc@1202 771 Reln (Sql t,
adamc@1202 772 [Recd (foldl (fn ((v', f), fs) =>
adamc@1202 773 if v' = v then
adamc@1202 774 (f, Proj (Proj (Lvar rv, v), f)) :: fs
adamc@1202 775 else
adamc@1202 776 fs) [] (#Select r))])))
adamc@1202 777 True (#From r)
adamc@1205 778
adamc@1205 779 fun expIn e =
adamc@1205 780 case e of
adamc@1206 781 SqConst p => inl (Const p)
adamc@1206 782 | Field (v, f) => inl (Proj (Proj (Lvar rv, v), f))
adamc@1205 783 | Binop (bo, e1, e2) =>
adamc@1206 784 inr (case (bo, expIn e1, expIn e2) of
adamc@1206 785 (Exps f, inl e1, inl e2) => f (e1, e2)
adamc@1206 786 | (Props f, inr p1, inr p2) => f (p1, p2)
adamc@1206 787 | _ => Unknown)
adamc@1207 788 | SqKnown e =>
adamc@1207 789 inr (case expIn e of
adamc@1207 790 inl e => Reln (Known, [e])
adamc@1207 791 | _ => Unknown)
adamc@1207 792 | Inj e =>
adamc@1207 793 let
adamc@1207 794 fun deinj (e, _) =
adamc@1207 795 case e of
adamc@1207 796 ERel n => List.nth (env, n)
adamc@1207 797 | EField (e, f) => Proj (deinj e, f)
adamc@1207 798 | _ => raise Fail "Iflow: non-variable injected into query"
adamc@1207 799 in
adamc@1207 800 inl (deinj e)
adamc@1207 801 end
adamc@1205 802
adamc@1205 803 val p = case #Where r of
adamc@1205 804 NONE => p
adamc@1205 805 | SOME e =>
adamc@1205 806 case expIn e of
adamc@1205 807 inr p' => And (p, p')
adamc@1205 808 | _ => p
adamc@1202 809 in
adamc@1202 810 case oe of
adamc@1202 811 NONE => p
adamc@1202 812 | SOME oe =>
adamc@1205 813 And (p, foldl (fn ((v, f), p) =>
adamc@1205 814 Or (p,
adamc@1205 815 Reln (Eq, [oe, Proj (Proj (Lvar rv, v), f)])))
adamc@1205 816 False (#Select r))
adamc@1202 817 end
adamc@1200 818
adamc@1202 819 fun evalExp env (e as (_, loc), st as (nv, p, sent)) =
adamc@1200 820 let
adamc@1200 821 fun default () =
adamc@1200 822 (Var nv, (nv+1, p, sent))
adamc@1200 823
adamc@1200 824 fun addSent (p, e, sent) =
adamc@1200 825 if isKnown e then
adamc@1200 826 sent
adamc@1200 827 else
adamc@1202 828 (loc, e, p) :: sent
adamc@1200 829 in
adamc@1200 830 case #1 e of
adamc@1200 831 EPrim p => (Const p, st)
adamc@1200 832 | ERel n => (List.nth (env, n), st)
adamc@1200 833 | ENamed _ => default ()
adamc@1200 834 | ECon (_, pc, NONE) => (Func (patCon pc, []), st)
adamc@1200 835 | ECon (_, pc, SOME e) =>
adamc@1200 836 let
adamc@1200 837 val (e, st) = evalExp env (e, st)
adamc@1200 838 in
adamc@1200 839 (Func (patCon pc, [e]), st)
adamc@1200 840 end
adamc@1200 841 | ENone _ => (Func ("None", []), st)
adamc@1200 842 | ESome (_, e) =>
adamc@1200 843 let
adamc@1200 844 val (e, st) = evalExp env (e, st)
adamc@1200 845 in
adamc@1200 846 (Func ("Some", [e]), st)
adamc@1200 847 end
adamc@1200 848 | EFfi _ => default ()
adamc@1200 849 | EFfiApp (m, s, es) =>
adamc@1200 850 if m = "Basis" andalso SS.member (writers, s) then
adamc@1200 851 let
adamc@1200 852 val (es, st) = ListUtil.foldlMap (evalExp env) st es
adamc@1200 853 in
adamc@1200 854 (Func ("unit", []), (#1 st, p, foldl (fn (e, sent) => addSent (#2 st, e, sent)) sent es))
adamc@1200 855 end
adamc@1200 856 else if Settings.isEffectful (m, s) andalso not (Settings.isBenignEffectful (m, s)) then
adamc@1200 857 default ()
adamc@1200 858 else
adamc@1200 859 let
adamc@1200 860 val (es, st) = ListUtil.foldlMap (evalExp env) st es
adamc@1200 861 in
adamc@1200 862 (Func (m ^ "." ^ s, es), st)
adamc@1200 863 end
adamc@1200 864 | EApp _ => default ()
adamc@1200 865 | EAbs _ => default ()
adamc@1200 866 | EUnop (s, e1) =>
adamc@1200 867 let
adamc@1200 868 val (e1, st) = evalExp env (e1, st)
adamc@1200 869 in
adamc@1200 870 (Func (s, [e1]), st)
adamc@1200 871 end
adamc@1200 872 | EBinop (s, e1, e2) =>
adamc@1200 873 let
adamc@1200 874 val (e1, st) = evalExp env (e1, st)
adamc@1200 875 val (e2, st) = evalExp env (e2, st)
adamc@1200 876 in
adamc@1200 877 (Func (s, [e1, e2]), st)
adamc@1200 878 end
adamc@1200 879 | ERecord xets =>
adamc@1200 880 let
adamc@1200 881 val (xes, st) = ListUtil.foldlMap (fn ((x, e, _), st) =>
adamc@1200 882 let
adamc@1200 883 val (e, st) = evalExp env (e, st)
adamc@1200 884 in
adamc@1200 885 ((x, e), st)
adamc@1200 886 end) st xets
adamc@1200 887 in
adamc@1200 888 (Recd xes, st)
adamc@1200 889 end
adamc@1200 890 | EField (e, s) =>
adamc@1200 891 let
adamc@1200 892 val (e, st) = evalExp env (e, st)
adamc@1200 893 in
adamc@1200 894 (Proj (e, s), st)
adamc@1200 895 end
adamc@1200 896 | ECase _ => default ()
adamc@1200 897 | EStrcat (e1, e2) =>
adamc@1200 898 let
adamc@1200 899 val (e1, st) = evalExp env (e1, st)
adamc@1200 900 val (e2, st) = evalExp env (e2, st)
adamc@1200 901 in
adamc@1200 902 (Func ("cat", [e1, e2]), st)
adamc@1200 903 end
adamc@1200 904 | EError _ => (Finish, st)
adamc@1200 905 | EReturnBlob {blob = b, mimeType = m, ...} =>
adamc@1200 906 let
adamc@1200 907 val (b, st) = evalExp env (b, st)
adamc@1200 908 val (m, st) = evalExp env (m, st)
adamc@1200 909 in
adamc@1200 910 (Finish, (#1 st, p, addSent (#2 st, b, addSent (#2 st, m, sent))))
adamc@1200 911 end
adamc@1200 912 | ERedirect (e, _) =>
adamc@1200 913 let
adamc@1200 914 val (e, st) = evalExp env (e, st)
adamc@1200 915 in
adamc@1200 916 (Finish, (#1 st, p, addSent (#2 st, e, sent)))
adamc@1200 917 end
adamc@1200 918 | EWrite e =>
adamc@1200 919 let
adamc@1200 920 val (e, st) = evalExp env (e, st)
adamc@1200 921 in
adamc@1200 922 (Func ("unit", []), (#1 st, p, addSent (#2 st, e, sent)))
adamc@1200 923 end
adamc@1200 924 | ESeq (e1, e2) =>
adamc@1200 925 let
adamc@1200 926 val (_, st) = evalExp env (e1, st)
adamc@1200 927 in
adamc@1200 928 evalExp env (e2, st)
adamc@1200 929 end
adamc@1200 930 | ELet (_, _, e1, e2) =>
adamc@1200 931 let
adamc@1200 932 val (e1, st) = evalExp env (e1, st)
adamc@1200 933 in
adamc@1200 934 evalExp (e1 :: env) (e2, st)
adamc@1200 935 end
adamc@1200 936 | EClosure (n, es) =>
adamc@1200 937 let
adamc@1200 938 val (es, st) = ListUtil.foldlMap (evalExp env) st es
adamc@1200 939 in
adamc@1200 940 (Func ("Cl" ^ Int.toString n, es), st)
adamc@1200 941 end
adamc@1200 942
adamc@1200 943 | EQuery {query = q, body = b, initial = i, ...} =>
adamc@1200 944 let
adamc@1200 945 val (_, st) = evalExp env (q, st)
adamc@1200 946 val (i, st) = evalExp env (i, st)
adamc@1200 947
adamc@1200 948 val r = #1 st
adamc@1200 949 val acc = #1 st + 1
adamc@1200 950 val st' = (#1 st + 2, #2 st, #3 st)
adamc@1200 951
adamc@1200 952 val (b, st') = evalExp (Var acc :: Var r :: env) (b, st')
adamc@1200 953
adamc@1200 954 val r' = newLvar ()
adamc@1200 955 val acc' = newLvar ()
adamc@1207 956 val qp = queryProp env r' NONE q
adamc@1200 957
adamc@1200 958 val doSubExp = subExp (r, r') o subExp (acc, acc')
adamc@1200 959 val doSubProp = subProp (r, r') o subProp (acc, acc')
adamc@1200 960
adamc@1200 961 val p = doSubProp (#2 st')
adamc@1200 962 val p = And (p, qp)
adamc@1200 963 val p = Select (r, r', acc', p, doSubExp b)
adamc@1200 964 in
adamc@1202 965 (Var r, (#1 st + 1, And (#2 st, p), map (fn (loc, e, p) => (loc,
adamc@1202 966 doSubExp e,
adamc@1202 967 And (qp, doSubProp p))) (#3 st')))
adamc@1200 968 end
adamc@1200 969 | EDml _ => default ()
adamc@1200 970 | ENextval _ => default ()
adamc@1200 971 | ESetval _ => default ()
adamc@1200 972
adamc@1200 973 | EUnurlify _ => default ()
adamc@1200 974 | EJavaScript _ => default ()
adamc@1200 975 | ESignalReturn _ => default ()
adamc@1200 976 | ESignalBind _ => default ()
adamc@1200 977 | ESignalSource _ => default ()
adamc@1200 978 | EServerCall _ => default ()
adamc@1200 979 | ERecv _ => default ()
adamc@1200 980 | ESleep _ => default ()
adamc@1200 981 | ESpawn _ => default ()
adamc@1200 982 end
adamc@1200 983
adamc@1200 984 fun check file =
adamc@1200 985 let
adamc@1207 986 val exptd = foldl (fn ((d, _), exptd) =>
adamc@1207 987 case d of
adamc@1207 988 DExport (_, _, n, _, _, _) => IS.add (exptd, n)
adamc@1207 989 | _ => exptd) IS.empty file
adamc@1207 990
adamc@1202 991 fun decl ((d, _), (vals, pols)) =
adamc@1200 992 case d of
adamc@1207 993 DVal (_, n, _, e, _) =>
adamc@1200 994 let
adamc@1207 995 val isExptd = IS.member (exptd, n)
adamc@1207 996
adamc@1207 997 fun deAbs (e, env, nv, p) =
adamc@1200 998 case #1 e of
adamc@1207 999 EAbs (_, _, _, e) => deAbs (e, Var nv :: env, nv + 1,
adamc@1207 1000 if isExptd then
adamc@1207 1001 And (p, Reln (Known, [Var nv]))
adamc@1207 1002 else
adamc@1207 1003 p)
adamc@1207 1004 | _ => (e, env, nv, p)
adamc@1200 1005
adamc@1207 1006 val (e, env, nv, p) = deAbs (e, [], 1, True)
adamc@1200 1007
adamc@1207 1008 val (e, (_, p, sent)) = evalExp env (e, (nv, p, []))
adamc@1200 1009 in
adamc@1207 1010 (sent @ vals, pols)
adamc@1200 1011 end
adamc@1202 1012
adamc@1207 1013 | DPolicy (PolQuery e) => (vals, queryProp [] 0 (SOME (Var 0)) e :: pols)
adamc@1202 1014
adamc@1202 1015 | _ => (vals, pols)
adamc@1202 1016
adamc@1203 1017 val () = reset ()
adamc@1202 1018
adamc@1202 1019 val (vals, pols) = foldl decl ([], []) file
adamc@1200 1020 in
adamc@1207 1021 app (fn (loc, e, p) =>
adamc@1207 1022 let
adamc@1207 1023 val p = And (p, Reln (Eq, [Var 0, e]))
adamc@1207 1024 in
adamc@1208 1025 if List.exists (fn pol => if imply (p, pol) then
adamc@1208 1026 (if !debug then
adamc@1208 1027 Print.prefaces "Match"
adamc@1208 1028 [("Hyp", p_prop p),
adamc@1208 1029 ("Goal", p_prop pol)]
adamc@1208 1030 else
adamc@1208 1031 ();
adamc@1208 1032 true)
adamc@1208 1033 else
adamc@1208 1034 false) pols then
adamc@1207 1035 ()
adamc@1207 1036 else
adamc@1207 1037 (ErrorMsg.errorAt loc "The information flow policy may be violated here.";
adamc@1207 1038 Print.preface ("The state satisifes this predicate:", p_prop p))
adamc@1207 1039 end) vals
adamc@1200 1040 end
adamc@1200 1041
adamc@1200 1042 end