module M

(* exception without argument *)

exception E

let p1 () ensures { false } raises { E -> true }
= raise E : unit

(* exception with an argument *)

exception F int

let p2 () ensures { false } raises { F result -> result = 1 }
= raise (F 1) : unit

let p2a () ensures { false } raises { E -> true | F _ -> false }
= raise (F (raise E : int)) : unit

(* composition of exceptions with other constructs *)

let p3 () ensures { false } raises { F result -> result = 1 }
= begin raise (F 1); raise (F 2) : unit end

let p4 () ensures { false } raises { F result -> result = 1 }
= (if True then raise (F 1) else raise (F 2)) : unit

let p5 () ensures { false } raises { E -> false | F result -> result = 1 }
= begin
    if True then raise (F 1);
    raise E : unit
  end

let p6 () ensures { false } raises { E -> true | F _ -> false }
= begin
    if False then raise (F 1);
    raise E : unit
  end

(* composition of exceptions with side-effect on a reference *)

use int.Int
use ref.Ref

val x : ref int

let p7 () ensures { false } raises { E -> !x = 1 }
= begin x := 1; raise E; x := 2 end

let p8 () ensures { false } raises { F result -> !x = 1 /\ result = 1 }
= begin x := 1; raise (F !x); x := 2 end

let p9 () ensures { false } raises { F result -> !x = 1 /\ result = 1 }
= (raise (F begin x := 1; !x end) : unit)

(* try / with *)

let p10 () ensures { result = 0 } = (try raise E : int with E -> 0 end)

let p11 () ensures { result = 1 } = (try raise (F 1) : int with F x -> x end)

let p12 () ensures { result = 2 }
= try
    begin raise E; raise (F 1); 1 end
  with E -> 2
     | F _ -> 3
  end

let p13 () ensures { !x = 2 }
= try
    begin raise E; raise (F 1); x := 1 end
  with E -> x := 2
     | F _ -> x := 3
  end

let p13a () ensures { !x <> 1 }
= try
    (if !x = 1 then raise E)
    (*{ true | E => x = 1 }*)
  with E ->
    x := 0
  end

exception E1
exception E2
exception E3

let p14 () ensures { false } raises {
  | E1 -> !x = 1 | E2 -> !x = 2 | E3 -> !x = 3
  | E -> !x <> 1 /\ !x <> 2 /\ !x <> 3 }
= begin
    if !x = 1 then raise E1;
    if !x = 2 then raise E2;
    if !x = 3 then raise E3;
    raise E : unit
  end

let p15 () ensures { false } raises { E -> !x=0 | F result -> result=0 }
= if !x = 0 then raise E else (x := 0; raise (F !x)) : unit

let p16 () ensures { !x<>0 } raises { E -> !x=1 }
  = if !x = 0 then (x:=1; raise E)

let p17 () ensures { false } raises { E -> !x=0 }
  = (x := 0; (raise E; x := 1))

end
