summaryrefslogtreecommitdiff
path: root/src/property.ml
diff options
context:
space:
mode:
Diffstat (limited to 'src/property.ml')
-rw-r--r--src/property.ml228
1 files changed, 228 insertions, 0 deletions
diff --git a/src/property.ml b/src/property.ml
new file mode 100644
index 00000000..83594f4f
--- /dev/null
+++ b/src/property.ml
@@ -0,0 +1,228 @@
+(**************************************************************************)
+(* Sail *)
+(* *)
+(* Copyright (c) 2013-2017 *)
+(* Kathyrn Gray *)
+(* Shaked Flur *)
+(* Stephen Kell *)
+(* Gabriel Kerneis *)
+(* Robert Norton-Wright *)
+(* Christopher Pulte *)
+(* Peter Sewell *)
+(* Alasdair Armstrong *)
+(* Brian Campbell *)
+(* Thomas Bauereiss *)
+(* Anthony Fox *)
+(* Jon French *)
+(* Dominic Mulligan *)
+(* Stephen Kell *)
+(* Mark Wassell *)
+(* *)
+(* All rights reserved. *)
+(* *)
+(* This software was developed by the University of Cambridge Computer *)
+(* Laboratory as part of the Rigorous Engineering of Mainstream Systems *)
+(* (REMS) project, funded by EPSRC grant EP/K008528/1. *)
+(* *)
+(* Redistribution and use in source and binary forms, with or without *)
+(* modification, are permitted provided that the following conditions *)
+(* are met: *)
+(* 1. Redistributions of source code must retain the above copyright *)
+(* notice, this list of conditions and the following disclaimer. *)
+(* 2. Redistributions in binary form must reproduce the above copyright *)
+(* notice, this list of conditions and the following disclaimer in *)
+(* the documentation and/or other materials provided with the *)
+(* distribution. *)
+(* *)
+(* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' *)
+(* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED *)
+(* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A *)
+(* PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR *)
+(* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, *)
+(* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT *)
+(* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF *)
+(* USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND *)
+(* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, *)
+(* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT *)
+(* OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF *)
+(* SUCH DAMAGE. *)
+(**************************************************************************)
+
+open Ast
+open Ast_util
+open Parser_combinators
+
+let find_properties (Defs defs) =
+ let rec find_prop acc = function
+ | DEF_pragma (("property" | "counterexample") as prop_type, command, l) :: defs ->
+ begin match Util.find_next (function DEF_spec _ -> true | _ -> false) defs with
+ | _, Some (DEF_spec vs, defs) ->
+ find_prop ((prop_type, command, l, vs) :: acc) defs
+ | _, _ ->
+ raise (Reporting.err_general l "Property is not attached to any function signature")
+ end
+ | def :: defs -> find_prop acc defs
+ | [] -> acc
+ in
+ find_prop [] defs
+ |> List.map (fun ((_, _, _, vs) as prop) -> (id_of_val_spec vs, prop))
+ |> List.fold_left (fun m (id, prop) -> Bindings.add id prop m) Bindings.empty
+
+let add_property_guards props (Defs defs) =
+ let open Type_check in
+ let rec add_property_guards' acc = function
+ | (DEF_fundef (FD_aux (FD_function (r_opt, t_opt, e_opt, funcls), fd_aux) as fdef) as def) :: defs ->
+ begin match Bindings.find_opt (id_of_fundef fdef) props with
+ | Some (_, _, pragma_l, VS_aux (VS_val_spec (TypSchm_aux (TypSchm_ts (quant, _), _), _, _, _), _)) ->
+ begin match quant_split quant with
+ | _, [] -> add_property_guards' (def :: acc) defs
+ | _, constraints ->
+ let add_constraints_to_funcl (FCL_aux (FCL_Funcl (id, Pat_aux (pexp, pexp_aux)), fcl_aux)) =
+ let add_guard exp =
+ (* FIXME: Use an assert *)
+ let exp' = mk_exp (E_block [mk_exp (E_app (mk_id "sail_assume", [mk_exp (E_constraint (List.fold_left nc_and nc_true constraints))]));
+ strip_exp exp])
+ in
+ try Type_check.check_exp (env_of exp) exp' (typ_of exp) with
+ | Type_error (_, l, err) ->
+ let msg =
+ "\nType error when generating guard for a property.\n\
+ When generating guards we convert type quantifiers from the function signature\n\
+ into runtime checks so it must be possible to reconstruct the quantifier from\n\
+ the function arguments. For example:\n\n\
+ \
+ function f : forall 'n, 'n <= 100. (x: int('n)) -> bool\n\n\
+ \
+ would cause the runtime check x <= 100 to be added to the function body.\n\
+ To fix this error, ensure that all quantifiers have corresponding function arguments.\n"
+ in
+ raise (Reporting.err_typ pragma_l (Type_error.string_of_type_error err ^ msg))
+ in
+ let mk_funcl p = FCL_aux (FCL_Funcl (id, Pat_aux (p, pexp_aux)), fcl_aux) in
+ match pexp with
+ | Pat_exp (pat, exp) ->
+ mk_funcl (Pat_exp (pat, add_guard exp))
+ | Pat_when (pat, guard, exp) ->
+ mk_funcl (Pat_when (pat, guard, add_guard exp))
+ in
+
+ let funcls = List.map add_constraints_to_funcl funcls in
+ let fdef = FD_aux (FD_function (r_opt, t_opt, e_opt, funcls), fd_aux) in
+
+ add_property_guards' (DEF_fundef fdef :: acc) defs
+ end
+ | None -> add_property_guards' (def :: acc) defs
+ end
+
+ | def :: defs -> add_property_guards' (def :: acc) defs
+ | [] -> List.rev acc
+ in
+ Defs (add_property_guards' [] defs)
+
+let rewrite defs =
+ add_property_guards (find_properties defs) defs
+
+type event = Overflow | Assertion | Assumption | Match | Return
+
+type query =
+ | Q_all of event (* All events of type are true *)
+ | Q_exist of event (* Some event of type is true *)
+ | Q_not of query
+ | Q_and of query list
+ | Q_or of query list
+
+let default_query =
+ Q_or [Q_and [Q_not (Q_exist Assertion); Q_all Return; Q_not (Q_exist Match)]; Q_exist Overflow; Q_not (Q_all Assumption)]
+
+module Event = struct
+ type t = event
+ let compare e1 e2 =
+ match e1, e2 with
+ | Overflow, Overflow -> 0
+ | Assertion, Assertion -> 0
+ | Assumption, Assumption -> 0
+ | Match, Match -> 0
+ | Return, Return -> 0
+ | Overflow, _ -> 1
+ | _, Overflow -> -1
+ | Assertion, _ -> 1
+ | _, Assertion -> -1
+ | Assumption, _ -> 1
+ | _, Assumption -> -1
+ | Match, _ -> 1
+ | _, Match -> -1
+end
+
+let string_of_event = function
+ | Overflow -> "overflow"
+ | Assertion -> "assertion"
+ | Assumption -> "assumption"
+ | Match -> "match_failure"
+ | Return -> "return"
+
+let rec string_of_query = function
+ | Q_all ev -> "A " ^ string_of_event ev
+ | Q_exist ev -> "E " ^ string_of_event ev
+ | Q_not q -> "~(" ^ string_of_query q ^ ")"
+ | Q_and qs -> "(" ^ Util.string_of_list " & " string_of_query qs ^ ")"
+ | Q_or qs -> "(" ^ Util.string_of_list " | " string_of_query qs ^ ")"
+
+let parse_query =
+ let amp = token (function Str.Delim "&" -> Some () | _ -> None) in
+ let bar = token (function Str.Delim "|" -> Some () | _ -> None) in
+ let lparen = token (function Str.Delim "(" -> Some () | _ -> None) in
+ let rparen = token (function Str.Delim ")" -> Some () | _ -> None) in
+ let quant = token (function Str.Text ("A" | "all") -> Some (fun x -> Q_all x)
+ | Str.Text ("E" | "exist") -> Some (fun x -> Q_exist x)
+ | _ -> None) in
+ let event = token (function Str.Text "overflow" -> Some Overflow
+ | Str.Text "assertion" -> Some Assertion
+ | Str.Text "assumption" -> Some Assumption
+ | Str.Text "match_failure" -> Some Match
+ | Str.Text "return"-> Some Return
+ | _ -> None) in
+ let tilde = token (function Str.Delim "~" -> Some () | _ -> None) in
+
+ let rec exp0 () =
+ pchoose (exp1 () >>= fun x -> bar >>= fun _ -> exp0 () >>= fun y -> preturn (Q_or [x; y]))
+ (exp1 ())
+ and exp1 () =
+ pchoose (exp2 () >>= fun x -> amp >>= fun _ -> exp1 () >>= fun y -> preturn (Q_and [x; y]))
+ (exp2 ())
+ and exp2 () =
+ pchoose (tilde >>= fun _ -> exp3 () >>= fun x -> preturn (Q_not x))
+ (exp3 ())
+ and exp3 () =
+ pchoose (lparen >>= fun _ -> exp0 () >>= fun x -> rparen >>= fun _ -> preturn x)
+ (quant >>= fun f -> event >>= fun ev -> preturn (f ev))
+ in
+ parse (exp0 ()) "[ \n\t]+\\|(\\|)\\|&\\||\\|~"
+
+type pragma = {
+ query : query;
+ litmus : string list;
+ }
+
+let default_pragma = {
+ query = default_query;
+ litmus = [];
+ }
+
+let parse_pragma l input =
+ let key = Str.regexp ":[a-z]+" in
+ let tokens = Str.full_split key input in
+ let rec process_toks pragma = function
+ | Str.Delim ":query" :: Str.Text query :: rest ->
+ begin match parse_query query with
+ | Some q -> process_toks { pragma with query = q } rest
+ | None ->
+ raise (Reporting.err_general l ("Could not parse query " ^ String.trim query))
+ end
+ | Str.Delim ":litmus" :: rest ->
+ let args, rest = Util.take_drop (function Str.Text _ -> true | _ -> false) rest in
+ process_toks { pragma with litmus = List.map (function Str.Text t -> t | _ -> assert false) args } rest
+ | [] -> pragma
+ | _ ->
+ raise (Reporting.err_general l "Could not parse pragma")
+ in
+ process_toks default_pragma tokens