|
|
|
@ -26,6 +26,13 @@ type transitions =
|
|
|
|
|
| PointerToDecl (** stmt to decl *)
|
|
|
|
|
| Protocol (** decl to decl *)
|
|
|
|
|
|
|
|
|
|
let is_transition_to_successor trans =
|
|
|
|
|
match trans with
|
|
|
|
|
| Body | InitExpr | ParameterName _ | Parameters | Cond
|
|
|
|
|
-> true
|
|
|
|
|
| Super | PointerToDecl | Protocol
|
|
|
|
|
-> false
|
|
|
|
|
|
|
|
|
|
(* In formulas below prefix
|
|
|
|
|
"E" means "exists a path"
|
|
|
|
|
"A" means "for all path" *)
|
|
|
|
@ -257,7 +264,11 @@ module Debug = struct
|
|
|
|
|
type eval_result = Eval_undefined | Eval_true | Eval_false
|
|
|
|
|
|
|
|
|
|
type content =
|
|
|
|
|
{ast_node: ast_node; phi: t; lcxt: CLintersContext.context; eval_result: eval_result}
|
|
|
|
|
{ ast_node: ast_node
|
|
|
|
|
; phi: t
|
|
|
|
|
; lcxt: CLintersContext.context
|
|
|
|
|
; eval_result: eval_result
|
|
|
|
|
; witness: ast_node option }
|
|
|
|
|
|
|
|
|
|
type eval_node = {id: int; content: content}
|
|
|
|
|
|
|
|
|
@ -276,7 +287,8 @@ module Debug = struct
|
|
|
|
|
; breakpoint_line: int option
|
|
|
|
|
; debugger_active: bool }
|
|
|
|
|
|
|
|
|
|
let create_content ast_node phi lcxt = {ast_node; phi; eval_result= Eval_undefined; lcxt}
|
|
|
|
|
let create_content ast_node phi lcxt =
|
|
|
|
|
{ast_node; phi; eval_result= Eval_undefined; lcxt; witness= None}
|
|
|
|
|
|
|
|
|
|
let create source_file =
|
|
|
|
|
let breakpoint_token = "INFER_BREAKPOINT" in
|
|
|
|
@ -319,10 +331,18 @@ module Debug = struct
|
|
|
|
|
| Last_occurrence n
|
|
|
|
|
-> (n, true)
|
|
|
|
|
in
|
|
|
|
|
let witness_str =
|
|
|
|
|
match eval_node.content.witness with
|
|
|
|
|
| Some witness
|
|
|
|
|
-> "\n- witness: " ^ Ctl_parser_types.ast_node_kind witness ^ " "
|
|
|
|
|
^ Ctl_parser_types.ast_node_name witness
|
|
|
|
|
| None
|
|
|
|
|
-> ""
|
|
|
|
|
in
|
|
|
|
|
let ast_str =
|
|
|
|
|
Format.asprintf "%a"
|
|
|
|
|
Format.asprintf "%a %s"
|
|
|
|
|
(pp_ast ~ast_node_to_highlight ~prettifier:(ANSITerminal.sprintf highlight_style "%s"))
|
|
|
|
|
ast_root
|
|
|
|
|
ast_root witness_str
|
|
|
|
|
in
|
|
|
|
|
L.progress "@\nNode ID: %d\tEvaluation stack level: %d\tSource line-number: %s@\n"
|
|
|
|
|
eval_node.id (Stack.length t.eval_stack)
|
|
|
|
@ -380,13 +400,16 @@ module Debug = struct
|
|
|
|
|
{t' with next_id= t.next_id + 1}
|
|
|
|
|
|
|
|
|
|
let eval_end t result =
|
|
|
|
|
let result_bool = Option.is_some result in
|
|
|
|
|
let eval_result_of_bool = function true -> Eval_true | false -> Eval_false in
|
|
|
|
|
if Stack.is_empty t.eval_stack then
|
|
|
|
|
raise (Empty_stack "Unbalanced number of eval_begin/eval_end invocations") ;
|
|
|
|
|
let evaluated_tree, eval_node, ast_node_to_display =
|
|
|
|
|
match Stack.pop_exn t.eval_stack
|
|
|
|
|
with Tree (({id= _; content} as eval_node), children), ast_node_to_display ->
|
|
|
|
|
let content' = {content with eval_result= eval_result_of_bool result} in
|
|
|
|
|
let content' =
|
|
|
|
|
{content with eval_result= eval_result_of_bool result_bool; witness= result}
|
|
|
|
|
in
|
|
|
|
|
let eval_node' = {eval_node with content= content'} in
|
|
|
|
|
(Tree (eval_node', children), eval_node', ast_node_to_display)
|
|
|
|
|
in
|
|
|
|
@ -565,48 +588,6 @@ let save_dotty_when_in_debug_mode source_file =
|
|
|
|
|
-> ()
|
|
|
|
|
|
|
|
|
|
(* Helper functions *)
|
|
|
|
|
|
|
|
|
|
let get_successor_nodes an =
|
|
|
|
|
(* get_decl_of_stmt get declarations that are directly embedded
|
|
|
|
|
as immediate children (i.e. distance 1) of an stmt (i.e., no transition).
|
|
|
|
|
TBD: check if a dual is needed for get_stmt_of_decl
|
|
|
|
|
*)
|
|
|
|
|
let get_decl_of_stmt st =
|
|
|
|
|
match st with Clang_ast_t.BlockExpr (_, _, _, d) -> [Decl d] | _ -> []
|
|
|
|
|
in
|
|
|
|
|
match an with
|
|
|
|
|
| Stmt st
|
|
|
|
|
-> let _, succs_st = Clang_ast_proj.get_stmt_tuple st in
|
|
|
|
|
let succs = List.map ~f:(fun s -> Stmt s) succs_st in
|
|
|
|
|
succs @ get_decl_of_stmt st
|
|
|
|
|
| Decl dec ->
|
|
|
|
|
match Clang_ast_proj.get_decl_context_tuple dec with
|
|
|
|
|
| Some (decl_list, _)
|
|
|
|
|
-> List.map ~f:(fun d -> Decl d) decl_list
|
|
|
|
|
| None
|
|
|
|
|
-> []
|
|
|
|
|
|
|
|
|
|
let node_to_string an =
|
|
|
|
|
match an with
|
|
|
|
|
| Decl d
|
|
|
|
|
-> Clang_ast_proj.get_decl_kind_string d
|
|
|
|
|
| Stmt s
|
|
|
|
|
-> Clang_ast_proj.get_stmt_kind_string s
|
|
|
|
|
|
|
|
|
|
let node_to_unique_string_id an =
|
|
|
|
|
match an with
|
|
|
|
|
| Decl d
|
|
|
|
|
-> let di = Clang_ast_proj.get_decl_tuple d in
|
|
|
|
|
Clang_ast_proj.get_decl_kind_string d ^ string_of_int di.Clang_ast_t.di_pointer
|
|
|
|
|
| Stmt s
|
|
|
|
|
-> let si, _ = Clang_ast_proj.get_stmt_tuple s in
|
|
|
|
|
Clang_ast_proj.get_stmt_kind_string s ^ string_of_int si.Clang_ast_t.si_pointer
|
|
|
|
|
|
|
|
|
|
(* true iff an ast node is a node of type among the list tl *)
|
|
|
|
|
let node_has_type tl an =
|
|
|
|
|
let an_alexp = ALVar.Const (node_to_string an) in
|
|
|
|
|
List.mem ~equal:ALVar.equal tl an_alexp
|
|
|
|
|
|
|
|
|
|
(* given a decl returns a stmt such that decl--->stmt via label trs *)
|
|
|
|
|
let transition_decl_to_stmt d trs =
|
|
|
|
|
let open Clang_ast_t in
|
|
|
|
@ -756,6 +737,20 @@ let next_state_via_transition an trans =
|
|
|
|
|
| _, _
|
|
|
|
|
-> []
|
|
|
|
|
|
|
|
|
|
let choose_one_witness an1 an2 =
|
|
|
|
|
if Ctl_parser_types.ast_node_equal an1 an2 then an1
|
|
|
|
|
else if Ctl_parser_types.is_node_successor_of an1 ~is_successor:an2 then an2
|
|
|
|
|
else an1
|
|
|
|
|
|
|
|
|
|
let choose_witness_opt witness_opt1 witness_opt2 =
|
|
|
|
|
match (witness_opt1, witness_opt2) with
|
|
|
|
|
| Some witness1, Some witness2
|
|
|
|
|
-> Some (choose_one_witness witness1 witness2)
|
|
|
|
|
| Some witness, None | None, Some witness
|
|
|
|
|
-> Some witness
|
|
|
|
|
| None, None
|
|
|
|
|
-> None
|
|
|
|
|
|
|
|
|
|
(* Evaluation of formulas *)
|
|
|
|
|
(* evaluate an atomic formula (i.e. a predicate) on a ast node an and a
|
|
|
|
|
linter context lcxt. That is: an, lcxt |= pred_name(params) *)
|
|
|
|
@ -857,6 +852,24 @@ let rec eval_Atomic _pred_name args an lcxt =
|
|
|
|
|
| _
|
|
|
|
|
-> L.(die ExternalError) "Undefined Predicate or wrong set of arguments: '%s'" pred_name
|
|
|
|
|
|
|
|
|
|
and eval_AND an lcxt f1 f2 =
|
|
|
|
|
match eval_formula f1 an lcxt with
|
|
|
|
|
| Some witness1 -> (
|
|
|
|
|
match eval_formula f2 an lcxt with
|
|
|
|
|
| Some witness2
|
|
|
|
|
-> Some (choose_one_witness witness1 witness2)
|
|
|
|
|
| _
|
|
|
|
|
-> None )
|
|
|
|
|
| None (* we short-circuit the AND evaluation *)
|
|
|
|
|
-> None
|
|
|
|
|
|
|
|
|
|
and eval_OR an lcxt f1 f2 = choose_witness_opt (eval_formula f1 an lcxt) (eval_formula f2 an lcxt)
|
|
|
|
|
|
|
|
|
|
and eval_Implies an lcxt f1 f2 =
|
|
|
|
|
let witness1 = if Option.is_some (eval_formula f1 an lcxt) then None else Some an in
|
|
|
|
|
let witness2 = eval_formula f2 an lcxt in
|
|
|
|
|
choose_witness_opt witness1 witness2
|
|
|
|
|
|
|
|
|
|
(* an, lcxt |= EF phi <=>
|
|
|
|
|
an, lcxt |= phi or exists an' in Successors(st): an', lcxt |= EF phi
|
|
|
|
|
|
|
|
|
@ -871,8 +884,11 @@ and eval_EF phi an lcxt trans =
|
|
|
|
|
let phi' = Or (phi, EX (trans, EF (trans, phi))) in
|
|
|
|
|
eval_formula phi' an lcxt
|
|
|
|
|
| None, _
|
|
|
|
|
-> eval_formula phi an lcxt
|
|
|
|
|
|| List.exists ~f:(fun an' -> eval_EF phi an' lcxt trans) (get_successor_nodes an)
|
|
|
|
|
-> let witness_opt = eval_formula phi an lcxt in
|
|
|
|
|
if Option.is_some witness_opt then witness_opt
|
|
|
|
|
else
|
|
|
|
|
List.fold_left (Ctl_parser_types.get_direct_successor_nodes an) ~init:witness_opt ~f:
|
|
|
|
|
(fun acc node -> choose_witness_opt (eval_EF phi node lcxt trans) acc )
|
|
|
|
|
|
|
|
|
|
(* an, lcxt |= EX phi <=> exists an' in Successors(st): an', lcxt |= phi
|
|
|
|
|
|
|
|
|
@ -882,9 +898,21 @@ and eval_EF phi an lcxt trans =
|
|
|
|
|
*)
|
|
|
|
|
and eval_EX phi an lcxt trans =
|
|
|
|
|
let succs =
|
|
|
|
|
match trans with Some l -> next_state_via_transition an l | None -> get_successor_nodes an
|
|
|
|
|
match trans with
|
|
|
|
|
| Some l
|
|
|
|
|
-> next_state_via_transition an l
|
|
|
|
|
| None
|
|
|
|
|
-> Ctl_parser_types.get_direct_successor_nodes an
|
|
|
|
|
in
|
|
|
|
|
List.exists ~f:(fun an' -> eval_formula phi an' lcxt) succs
|
|
|
|
|
let witness_opt =
|
|
|
|
|
List.fold_left succs ~init:None ~f:(fun acc node ->
|
|
|
|
|
choose_witness_opt (eval_formula phi node lcxt) acc )
|
|
|
|
|
in
|
|
|
|
|
match (witness_opt, trans) with
|
|
|
|
|
| Some _, Some trans when not (is_transition_to_successor trans)
|
|
|
|
|
-> Some an (* We want to limit the witnesses to the successors of the original node. *)
|
|
|
|
|
| _
|
|
|
|
|
-> witness_opt
|
|
|
|
|
|
|
|
|
|
(* an, lcxt |= E(phi1 U phi2) evaluated using the equivalence
|
|
|
|
|
an, lcxt |= E(phi1 U phi2) <=> an, lcxt |= phi2 or (phi1 and EX(E(phi1 U phi2)))
|
|
|
|
@ -912,11 +940,15 @@ and in_node node_type_list phi an lctx =
|
|
|
|
|
let holds_for_one_node n =
|
|
|
|
|
match lctx.CLintersContext.et_evaluation_node with
|
|
|
|
|
| Some id
|
|
|
|
|
-> String.equal id (node_to_unique_string_id an) && eval_formula phi an lctx
|
|
|
|
|
-> if String.equal id (Ctl_parser_types.ast_node_unique_string_id an) then
|
|
|
|
|
eval_formula phi an lctx
|
|
|
|
|
else None
|
|
|
|
|
| None
|
|
|
|
|
-> node_has_type [n] an && eval_formula phi an lctx
|
|
|
|
|
-> if Ctl_parser_types.ast_node_has_kind [n] an then eval_formula phi an lctx else None
|
|
|
|
|
in
|
|
|
|
|
List.exists ~f:holds_for_one_node node_type_list
|
|
|
|
|
(* This is basically an OR of formula holds in the various nodes in the list *)
|
|
|
|
|
List.fold_left node_type_list ~init:None ~f:(fun acc node ->
|
|
|
|
|
choose_witness_opt (holds_for_one_node node) acc )
|
|
|
|
|
|
|
|
|
|
(* Intuitive meaning: (an,lcxt) satifies EH[Classes] phi
|
|
|
|
|
if the node an is among the declaration specified by the list Classes and
|
|
|
|
@ -952,10 +984,12 @@ and eval_ET tl trs phi an lcxt =
|
|
|
|
|
|
|
|
|
|
and eval_ETX tl trs phi an lcxt =
|
|
|
|
|
let lcxt', tl' =
|
|
|
|
|
match (lcxt.CLintersContext.et_evaluation_node, node_has_type tl an) with
|
|
|
|
|
match (lcxt.CLintersContext.et_evaluation_node, Ctl_parser_types.ast_node_has_kind tl an) with
|
|
|
|
|
| None, true
|
|
|
|
|
-> let an_alexp = ALVar.Const (node_to_string an) in
|
|
|
|
|
( {lcxt with CLintersContext.et_evaluation_node= Some (node_to_unique_string_id an)}
|
|
|
|
|
-> let an_alexp = ALVar.Const (Ctl_parser_types.ast_node_kind an) in
|
|
|
|
|
( { lcxt with
|
|
|
|
|
CLintersContext.et_evaluation_node=
|
|
|
|
|
Some (Ctl_parser_types.ast_node_unique_string_id an) }
|
|
|
|
|
, [an_alexp] )
|
|
|
|
|
| _, _
|
|
|
|
|
-> (lcxt, tl)
|
|
|
|
@ -970,26 +1004,26 @@ and eval_ETX tl trs phi an lcxt =
|
|
|
|
|
eval_formula f an lcxt'
|
|
|
|
|
|
|
|
|
|
(* Formulas are evaluated on a AST node an and a linter context lcxt *)
|
|
|
|
|
and eval_formula f an lcxt =
|
|
|
|
|
and eval_formula f an lcxt : Ctl_parser_types.ast_node option =
|
|
|
|
|
debug_eval_begin (debug_create_payload an f lcxt) ;
|
|
|
|
|
let res =
|
|
|
|
|
match f with
|
|
|
|
|
| True
|
|
|
|
|
-> true
|
|
|
|
|
-> Some an
|
|
|
|
|
| False
|
|
|
|
|
-> false
|
|
|
|
|
-> None
|
|
|
|
|
| Atomic (name, params)
|
|
|
|
|
-> eval_Atomic name params an lcxt
|
|
|
|
|
| Not f1
|
|
|
|
|
-> not (eval_formula f1 an lcxt)
|
|
|
|
|
-> if eval_Atomic name params an lcxt then Some an else None
|
|
|
|
|
| InNode (node_type_list, f1)
|
|
|
|
|
-> in_node node_type_list f1 an lcxt
|
|
|
|
|
| Not f1 -> (
|
|
|
|
|
match eval_formula f1 an lcxt with Some _ -> None | None -> Some an )
|
|
|
|
|
| And (f1, f2)
|
|
|
|
|
-> eval_formula f1 an lcxt && eval_formula f2 an lcxt
|
|
|
|
|
-> eval_AND an lcxt f1 f2
|
|
|
|
|
| Or (f1, f2)
|
|
|
|
|
-> eval_formula f1 an lcxt || eval_formula f2 an lcxt
|
|
|
|
|
-> eval_OR an lcxt f1 f2
|
|
|
|
|
| Implies (f1, f2)
|
|
|
|
|
-> not (eval_formula f1 an lcxt) || eval_formula f2 an lcxt
|
|
|
|
|
| InNode (node_type_list, f1)
|
|
|
|
|
-> in_node node_type_list f1 an lcxt
|
|
|
|
|
-> eval_Implies an lcxt f1 f2
|
|
|
|
|
| AU (trans, f1, f2)
|
|
|
|
|
-> eval_AU f1 f2 an lcxt trans
|
|
|
|
|
| EU (trans, f1, f2)
|
|
|
|
|