@ -47,8 +47,6 @@ module Degree = struct
( NonNegativeInt . to_int_exn d . linear * 100 ) + NonNegativeInt . to_int_exn d . log
let is_zero d = NonNegativeInt . is_zero d . linear && NonNegativeInt . is_zero d . log
let pp f d =
NonNegativeInt . pp f d . linear ;
if not ( NonNegativeInt . is_zero d . log ) then
@ -272,20 +270,22 @@ module MakePolynomial (S : NonNegativeSymbolWithDegreeKind) = struct
mult_const p1 p2 . const | > M . fold ( fun s p acc -> plus ( mult_symb ( mult p p1 ) s ) acc ) p2 . terms
let rec of_valclass : ( NonNegativeInt . t , S . t , ' t ) Bounds . valclass -> ( t , ' t ) below_above =
let rec of_valclass : ( NonNegativeInt . t , S . t , ' t ) Bounds . valclass -> ( ' t , t , ' t ) below_above =
function
| ValTop trace ->
Above trace
| Constant i ->
Below ( of_non_negative_int i )
Val ( of_non_negative_int i )
| Symbolic s -> (
match S . split_mult s with
| None ->
Below { const = NonNegativeInt . zero ; terms = M . singleton s one }
Val { const = NonNegativeInt . zero ; terms = M . singleton s one }
| Some ( s1 , s2 ) -> (
match ( of_valclass ( S . classify s1 ) , of_valclass ( S . classify s2 ) ) with
| Below s1 , Below s2 ->
Below ( mult s1 s2 )
| Val s1 , Val s2 ->
Val ( mult s1 s2 )
| Below _ , _ | _ , Below _ ->
assert false
| ( Above _ as t ) , _ | _ , ( Above _ as t ) ->
t ) )
@ -373,7 +373,7 @@ module MakePolynomial (S : NonNegativeSymbolWithDegreeKind) = struct
terms ( of_non_negative_int const )
in
fun p eval_sym ->
match subst p eval_sym with p -> Below p | exception ReturnTop s_trace -> Above s_trace
match subst p eval_sym with p -> Val p | exception ReturnTop s_trace -> Above s_trace
(* * Emit a pair ( d,t ) where d is the degree of the polynomial and t is the first term with such
@ -509,24 +509,79 @@ module TopTraces = struct
match min_elt traces with None -> [] | Some trace -> TopTrace . make_err_trace ~ depth : 0 trace
end
module UnreachableTrace = struct
type t =
| UnreachableNode of Location . t
| Call of { location : Location . t ; callee_pname : Procname . t ; callee_trace : t }
[ @@ deriving compare ]
let rec length = function
| UnreachableNode _ ->
1
| Call { callee_trace } ->
1 + length callee_trace
let compare t1 t2 = [ % compare : int * t ] ( length t1 , t1 ) ( length t2 , t2 )
let unreachable_node node_loc = UnreachableNode node_loc
let call ~ location ~ callee_pname callee_trace = Call { location ; callee_pname ; callee_trace }
let rec pp f = function
| UnreachableNode node_loc ->
F . fprintf f " UnreachableNode (%a) " Location . pp node_loc
| Call { callee_pname ; callee_trace ; location } ->
F . fprintf f " %a -> Call `%a` (%a) " pp callee_trace Procname . pp callee_pname Location . pp
location
let rec make_err_trace ~ depth trace =
match trace with
| UnreachableNode node_loc ->
[ Errlog . make_trace_element depth node_loc " Unreachable node " [] ]
| Call { location ; callee_pname ; callee_trace } ->
let desc = F . asprintf " Call to %a " Procname . pp callee_pname in
Errlog . make_trace_element depth location desc []
:: make_err_trace ~ depth : ( depth + 1 ) callee_trace
end
module UnreachableTraces = struct
include AbstractDomain . MinReprSet ( UnreachableTrace )
let make_err_trace traces =
match min_elt traces with
| None ->
[]
| Some trace ->
UnreachableTrace . make_err_trace ~ depth : 0 trace
end
module NonNegativePolynomial = struct
(* Use Below for non-Top values and Above for Top values with their trace *)
type t = ( NonNegativeNonTopPolynomial . t , TopTraces . t ) below_above
(* Use Above for Top values, Below for Unreachable values with their trace, and Val for non-negative polynomials *)
type t = ( UnreachableTraces. t , NonNegativeNonTopPolynomial. t , TopTraces . t ) below_above
type degree_with_term =
( Degree . t * NonNegativeNonTopPolynomial . t , TopTraces . t ) AbstractDomain . Types . below_above
( UnreachableTraces . t
, Degree . t * NonNegativeNonTopPolynomial . t
, TopTraces . t )
AbstractDomain . Types . below_above
let leq =
AbstractDomain . StackedUtils . leq ~ leq_below : NonNegativeNonTopPolynomial . leq
~ leq_above : TopTraces . leq
AbstractDomain . StackedUtils . leq ~ leq_below : UnreachableTraces . leq
~ leq : NonNegativeNonTopPolynomial . leq ~ leq _above: TopTraces . leq
let pp ~ hum =
let pp_below f traces =
AbstractDomain . BottomLiftedUtils . pp_bottom f ;
if not hum then F . fprintf f " : %a " UnreachableTraces . pp traces
in
let pp_above f traces =
AbstractDomain . TopLiftedUtils . pp_top f ;
if not hum then F . fprintf f " : %a " TopTraces . pp traces
in
AbstractDomain . StackedUtils . pp ~ pp_below : ( NonNegativeNonTopPolynomial . pp ~ hum ) ~ pp_above
AbstractDomain . StackedUtils . pp ~ pp : ( NonNegativeNonTopPolynomial . pp ~ hum ) ~ pp_above ~ pp_below
let pp_hum = pp ~ hum : true
@ -535,48 +590,69 @@ module NonNegativePolynomial = struct
let top = Above TopTraces . bottom
let zero = Below NonNegativeNonTopPolynomial . zero
let zero = Val NonNegativeNonTopPolynomial . zero
let one = Val NonNegativeNonTopPolynomial . one
let one = Below NonNegativeNonTopPolynomial . one
let of_unreachable node_loc =
Below ( UnreachableTraces . singleton ( UnreachableTrace . unreachable_node node_loc ) )
let of_int_exn i = Below ( NonNegativeNonTopPolynomial . of_int_exn i )
let of_int_exn i = Val ( NonNegativeNonTopPolynomial . of_int_exn i )
let make_trace_set ~ map_above =
AbstractDomain . StackedUtils . map ~ f_below : Fn . id ~ f_above : ( fun above ->
TopTraces . singleton ( map_above above ) )
AbstractDomain . StackedUtils . map
~ f_below : ( fun _ -> assert false )
~ f : Fn . id
~ f_above : ( fun above -> TopTraces . singleton ( map_above above ) )
let of_non_negative_bound ? ( degree_kind = DegreeKind . Linear ) b =
b
| > NonNegativeBoundWithDegreeKind . make degree_kind
| > NonNegativeBoundWithDegreeKind . classify | > NonNegativeNonTopPolynomial . of_valclass
(* Invariant: we always get a non-below bound from [of_valclass] *)
| > make_trace_set ~ map_above : TopTrace . unbounded_loop
let is_symbolic = function
| Above _ ->
| Below _ | Above _ ->
false
| Below p ->
| Val p ->
NonNegativeNonTopPolynomial . is_symbolic p
let is_top = function Above _ -> true | _ -> false
let is_zero = function Below p when NonNegativeNonTopPolynomial . is_zero p -> true | _ -> false
let is_unreachable = function Below _ -> true | _ -> false
let is_zero = function Val p when NonNegativeNonTopPolynomial . is_zero p -> true | _ -> false
let is_one = function Below p when NonNegativeNonTopPolynomial . is_one p -> true | _ -> false
let is_one = function Val p when NonNegativeNonTopPolynomial . is_one p -> true | _ -> false
let top_lifted_increasing ~ f p1 p2 =
AbstractDomain . StackedUtils . combine ~ dir : ` Increasing p1 p2 ~ f_below : f ~ f_above : TopTraces . join
AbstractDomain . StackedUtils . combine ~ f_below : UnreachableTraces . join ~ dir : ` Increasing p1 p2 ~ f
~ f_above : TopTraces . join
let unreachable_lifted_increasing ~ f p1 p2 =
match ( p1 , p2 ) with
| ( Below _ as below ) , Val _ | Val _ , ( Below _ as below ) ->
below
| _ ->
top_lifted_increasing ~ f p1 p2
let plus = top_lifted_increasing ~ f : NonNegativeNonTopPolynomial . plus
let mult_unreachable = unreachable_lifted_increasing ~ f : NonNegativeNonTopPolynomial . mult
let mult = top_lifted_increasing ~ f : NonNegativeNonTopPolynomial . mult
let min_default_left p1 p2 =
AbstractDomain . StackedUtils . combine ~ dir : ` Decreasing p1 p2
~ f_below : NonNegativeNonTopPolynomial . min_default_left ~ f_above : TopTraces . join
~ f : NonNegativeNonTopPolynomial . min_default_left ~ f_above : TopTraces . join
~ f_below : UnreachableTraces . join
let subst callee_pname location p eval_sym =
@ -586,37 +662,47 @@ module NonNegativePolynomial = struct
( TopTraces . map
( fun callee_trace -> TopTrace . call ~ callee_pname ~ location callee_trace )
callee_traces )
| Below p ->
| Below callee_traces ->
Below
( UnreachableTraces . map
( fun callee_trace -> UnreachableTrace . call ~ callee_pname ~ location callee_trace )
callee_traces )
| Val p ->
NonNegativeNonTopPolynomial . subst callee_pname location p eval_sym
| > make_trace_set ~ map_above : ( fun ( symbol , bound_trace ) ->
TopTrace . unbounded_symbol ~ location ~ symbol bound_trace )
let degree p =
match p with Above _ -> None | Below p -> Some ( NonNegativeNonTopPolynomial . degree p )
match p with Above _ | Below _ -> None | Val p -> Some ( NonNegativeNonTopPolynomial . degree p )
let compare_by_degree =
let cmp_above _ _ = 0 (* All Top should be considered equal *) in
AbstractDomain . StackedUtils . compare ~ cmp_above ~ cmp_below : ( fun p1 p2 ->
let cmp _ _ = 0 (* All pairs of Top/Unreachable should be considered equal *) in
AbstractDomain . StackedUtils . compare ~ cmp_above : cmp
~ cmp : ( fun p1 p2 ->
Degree . compare
( NonNegativeNonTopPolynomial . degree p1 )
( NonNegativeNonTopPolynomial . degree p2 ) )
~ cmp_below : cmp
let get_degree_with_term =
AbstractDomain . StackedUtils . map ~ f _below : NonNegativeNonTopPolynomial . degree_with_term
~ f_ a bov e: Fn . id
AbstractDomain . StackedUtils . map ~ f : NonNegativeNonTopPolynomial . degree_with_term ~ f_above : Fn . id
~ f_ below : Fn . id
let get_symbols =
AbstractDomain . StackedUtils . map ~ f_below : NonNegativeNonTopPolynomial . get_symbols ~ f_above : Fn . id
AbstractDomain . StackedUtils . map ~ f : NonNegativeNonTopPolynomial . get_symbols ~ f_above : Fn . id
~ f_below : Fn . id
let pp_degree ~ only_bigO fmt = function
| Above _ ->
Format . pp_print_string fmt " Top "
| Below ( degree , degree_term ) ->
| Below _ ->
Format . pp_print_string fmt " Unreachable "
| Val ( degree , degree_term ) ->
if only_bigO then
Format . fprintf fmt " O(%a) "
( NonNegativeNonTopPolynomial . pp ~ hum : true )
@ -634,7 +720,9 @@ module NonNegativePolynomial = struct
let polynomial_traces p =
match get_symbols p with
| Below symbols ->
| Below trace ->
UnreachableTraces . make_err_trace trace
| Val symbols ->
List . map symbols ~ f : Bounds . NonNegativeBound . make_err_trace | > Errlog . concat_traces
| Above trace ->
TopTraces . make_err_trace trace