• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

formalsec / smtml / 425

23 Nov 2025 06:33PM UTC coverage: 45.662% (-5.9%) from 51.566%
425

push

github

filipeom
Refactor exception handling in operator evaluation

Previously, during operator evaluation we would raise generic failures
for following three reasons:

- A theory was unsupported
- An operator was not supported by a given theory, and
- A failure in the evaluation of an operator

This would make exception handling difficult for apps that need to
gracefully handle failures. For this reason, we refactored the above
exceptions to instead raise a `Eval_error` of type `error_kind`.

Additionally, the refactor included:

- Fixing a couple of bugs when raising the `Type_error` exception.
- Adding all the helpers used for value coercion at the top of the file.
  I think it's more organised this way. Only time will tell.

BREAKING: Exception API changed for the `Eval` module

213 of 273 new or added lines in 1 file covered. (78.02%)

286 existing lines in 5 files now uncovered.

879 of 1925 relevant lines covered (45.66%)

7.5 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

32.3
/src/smtml/expr.ml
1
(* SPDX-License-Identifier: MIT *)
2
(* Copyright (C) 2023-2024 formalsec *)
3
(* Written by the Smtml programmers *)
4

5
type t = expr Hc.hash_consed
6

7
and expr =
8
  | Val of Value.t
9
  | Ptr of
10
      { base : Bitvector.t
11
      ; offset : t
12
      }
13
  | Loc of Loc.t
14
  | Symbol of Symbol.t
15
  | List of t list
16
  | App of Symbol.t * t list
17
  | Unop of Ty.t * Ty.Unop.t * t
18
  | Binop of Ty.t * Ty.Binop.t * t * t
19
  | Triop of Ty.t * Ty.Triop.t * t * t * t
20
  | Relop of Ty.t * Ty.Relop.t * t * t
21
  | Cvtop of Ty.t * Ty.Cvtop.t * t
22
  | Naryop of Ty.t * Ty.Naryop.t * t list
23
  | Extract of t * int * int
24
  | Concat of t * t
25
  | Binder of Binder.t * t list * t
26

27
module Expr = struct
28
  type t = expr
29

30
  let list_eq (l1 : 'a list) (l2 : 'a list) : bool =
31
    if List.compare_lengths l1 l2 = 0 then List.for_all2 phys_equal l1 l2
4✔
32
    else false
×
33

34
  let equal (e1 : expr) (e2 : expr) : bool =
35
    match (e1, e2) with
499✔
36
    | Val v1, Val v2 -> Value.equal v1 v2
470✔
37
    | Loc a, Loc b -> Loc.compare a b = 0
×
38
    | Ptr { base = b1; offset = o1 }, Ptr { base = b2; offset = o2 } ->
4✔
39
      Bitvector.equal b1 b2 && phys_equal o1 o2
4✔
40
    | Symbol s1, Symbol s2 -> Symbol.equal s1 s2
6✔
41
    | List l1, List l2 -> list_eq l1 l2
4✔
42
    | App (s1, l1), App (s2, l2) -> Symbol.equal s1 s2 && list_eq l1 l2
×
43
    | Unop (t1, op1, e1), Unop (t2, op2, e2) ->
1✔
44
      Ty.equal t1 t2 && Ty.Unop.equal op1 op2 && phys_equal e1 e2
1✔
45
    | Binop (t1, op1, e1, e3), Binop (t2, op2, e2, e4) ->
12✔
46
      Ty.equal t1 t2 && Ty.Binop.equal op1 op2 && phys_equal e1 e2
12✔
47
      && phys_equal e3 e4
12✔
48
    | Relop (t1, op1, e1, e3), Relop (t2, op2, e2, e4) ->
2✔
49
      Ty.equal t1 t2 && Ty.Relop.equal op1 op2 && phys_equal e1 e2
2✔
50
      && phys_equal e3 e4
2✔
51
    | Triop (t1, op1, e1, e3, e5), Triop (t2, op2, e2, e4, e6) ->
×
52
      Ty.equal t1 t2 && Ty.Triop.equal op1 op2 && phys_equal e1 e2
×
53
      && phys_equal e3 e4 && phys_equal e5 e6
×
54
    | Cvtop (t1, op1, e1), Cvtop (t2, op2, e2) ->
×
55
      Ty.equal t1 t2 && Ty.Cvtop.equal op1 op2 && phys_equal e1 e2
×
56
    | Naryop (t1, op1, l1), Naryop (t2, op2, l2) ->
×
57
      Ty.equal t1 t2 && Ty.Naryop.equal op1 op2 && list_eq l1 l2
×
58
    | Extract (e1, h1, l1), Extract (e2, h2, l2) ->
×
59
      phys_equal e1 e2 && h1 = h2 && l1 = l2
×
60
    | Concat (e1, e3), Concat (e2, e4) -> phys_equal e1 e2 && phys_equal e3 e4
×
61
    | Binder (binder1, vars1, e1), Binder (binder2, vars2, e2) ->
×
62
      Binder.equal binder1 binder2 && list_eq vars1 vars2 && phys_equal e1 e2
×
63
    | ( ( Val _ | Ptr _ | Loc _ | Symbol _ | List _ | App _ | Unop _ | Binop _
×
64
        | Triop _ | Relop _ | Cvtop _ | Naryop _ | Extract _ | Concat _
×
65
        | Binder _ )
×
66
      , _ ) ->
67
      false
68

69
  let hash (e : expr) : int =
70
    let h x = Hashtbl.hash x in
1,023✔
71
    match e with
72
    | Val v -> h v
856✔
73
    | Ptr { base; offset } -> h (base, offset.tag)
22✔
74
    | Loc l -> h l
×
75
    | Symbol s -> h s
48✔
76
    | List v -> h v
16✔
77
    | App (x, es) -> h (x, es)
×
78
    | Unop (ty, op, e) -> h (ty, op, e.tag)
7✔
79
    | Cvtop (ty, op, e) -> h (ty, op, e.tag)
6✔
80
    | Binop (ty, op, e1, e2) -> h (ty, op, e1.tag, e2.tag)
38✔
81
    | Relop (ty, op, e1, e2) -> h (ty, op, e1.tag, e2.tag)
12✔
82
    | Triop (ty, op, e1, e2, e3) -> h (ty, op, e1.tag, e2.tag, e3.tag)
×
83
    | Naryop (ty, op, es) -> h (ty, op, es)
×
84
    | Extract (e, hi, lo) -> h (e.tag, hi, lo)
14✔
85
    | Concat (e1, e2) -> h (e1.tag, e2.tag)
4✔
86
    | Binder (b, vars, e) -> h (b, vars, e.tag)
×
87
end
88

89
module Hc = Hc.Make [@inlined hint] (Expr)
90

91
let equal (hte1 : t) (hte2 : t) = phys_equal hte1 hte2 [@@inline]
221✔
92

93
let hash (hte : t) = hte.tag [@@inline]
4✔
94

95
module Key = struct
96
  type nonrec t = t
97

98
  let to_int hte = hash hte
×
99
end
100

101
let[@inline] make e = Hc.hashcons e
761✔
102

103
let[@inline] view (hte : t) = hte.node
569✔
104

105
let[@inline] compare (hte1 : t) (hte2 : t) = compare hte1.tag hte2.tag
×
106

107
let symbol s = make (Symbol s)
27✔
108

109
(** The return type of an expression *)
110
let rec ty (hte : t) : Ty.t =
111
  match view hte with
13✔
112
  | Val x -> Value.type_of x
×
113
  | Ptr _ -> Ty_bitv 32
×
114
  | Loc _ -> Ty_app
×
115
  | Symbol x -> Symbol.type_of x
10✔
116
  | List _ -> Ty_list
×
117
  | App (sym, _) -> begin match sym.ty with Ty_none -> Ty_app | ty -> ty end
×
118
  | Triop (_, Ite, _, hte1, hte2) ->
×
119
    let ty1 = ty hte1 in
120
    assert (
×
121
      let ty2 = ty hte2 in
122
      Ty.equal ty1 ty2 );
×
123
    ty1
UNCOV
124
  | Cvtop (_, (Zero_extend m | Sign_extend m), hte) -> (
×
125
    match ty hte with Ty_bitv n -> Ty_bitv (n + m) | _ -> assert false )
1✔
126
  | Unop (ty, _, _)
×
127
  | Binop (ty, _, _, _)
×
UNCOV
128
  | Triop (ty, _, _, _, _)
×
129
  | Relop (ty, _, _, _)
×
130
  | Cvtop (ty, _, _)
×
UNCOV
131
  | Naryop (ty, _, _) ->
×
132
    ty
133
  | Extract (_, h, l) -> Ty_bitv ((h - l) * 8)
2✔
134
  | Concat (e1, e2) -> (
×
135
    match (ty e1, ty e2) with
×
UNCOV
136
    | Ty_bitv n1, Ty_bitv n2 -> Ty_bitv (n1 + n2)
×
137
    | t1, t2 ->
×
138
      Fmt.failwith "Invalid concat of (%a) with (%a)" Ty.pp t1 Ty.pp t2 )
UNCOV
139
  | Binder (_, _, e) -> ty e
×
140

141
let rec is_symbolic (v : t) : bool =
142
  match view v with
×
143
  | Val _ | Loc _ -> false
×
144
  | Symbol _ -> true
×
145
  | Ptr { offset; _ } -> is_symbolic offset
×
146
  | Unop (_, _, v) | Cvtop (_, _, v) | Extract (v, _, _) | Binder (_, _, v) ->
×
147
    is_symbolic v
148
  | Binop (_, _, v1, v2) | Relop (_, _, v1, v2) | Concat (v1, v2) ->
×
149
    is_symbolic v1 || is_symbolic v2
×
150
  | Triop (_, _, v1, v2, v3) ->
×
151
    is_symbolic v1 || is_symbolic v2 || is_symbolic v3
×
152
  | List vs | App (_, vs) | Naryop (_, _, vs) -> List.exists is_symbolic vs
×
153

154
let get_symbols (hte : t list) =
155
  let tbl = Hashtbl.create 64 in
1✔
156
  let rec symbols (hte : t) =
1✔
157
    match view hte with
13✔
UNCOV
158
    | Val _ | Loc _ -> ()
×
UNCOV
159
    | Ptr { offset; _ } -> symbols offset
×
160
    | Symbol s -> Hashtbl.replace tbl s ()
5✔
UNCOV
161
    | List es -> List.iter symbols es
×
162
    | App (_, es) -> List.iter symbols es
×
163
    | Unop (_, _, e1) -> symbols e1
×
164
    | Binop (_, _, e1, e2) ->
2✔
165
      symbols e1;
166
      symbols e2
2✔
167
    | Triop (_, _, e1, e2, e3) ->
×
168
      symbols e1;
UNCOV
169
      symbols e2;
×
UNCOV
170
      symbols e3
×
171
    | Relop (_, _, e1, e2) ->
3✔
172
      symbols e1;
173
      symbols e2
3✔
174
    | Cvtop (_, _, e) -> symbols e
×
UNCOV
175
    | Naryop (_, _, es) -> List.iter symbols es
×
UNCOV
176
    | Extract (e, _, _) -> symbols e
×
UNCOV
177
    | Concat (e1, e2) ->
×
178
      symbols e1;
179
      symbols e2
×
180
    | Binder (_, vars, e) ->
×
181
      List.iter symbols vars;
UNCOV
182
      symbols e
×
183
  in
184
  List.iter symbols hte;
185
  Hashtbl.fold (fun k () acc -> k :: acc) tbl []
1✔
186

187
let rec pp fmt (hte : t) =
UNCOV
188
  match view hte with
×
UNCOV
189
  | Val v -> Value.pp fmt v
×
UNCOV
190
  | Ptr { base; offset } -> Fmt.pf fmt "(Ptr %a %a)" Bitvector.pp base pp offset
×
UNCOV
191
  | Loc l -> Fmt.pf fmt "(loc %a)" Loc.pp l
×
192
  | Symbol s -> Fmt.pf fmt "@[<hov 1>%a@]" Symbol.pp s
×
193
  | List v -> Fmt.pf fmt "@[<hov 1>[%a]@]" (Fmt.list ~sep:Fmt.comma pp) v
×
194
  | App (s, v) ->
×
195
    Fmt.pf fmt "@[<hov 1>(%a@ %a)@]" Symbol.pp s (Fmt.list ~sep:Fmt.comma pp) v
×
196
  | Unop (ty, op, e) ->
×
197
    Fmt.pf fmt "@[<hov 1>(%a.%a@ %a)@]" Ty.pp ty Ty.Unop.pp op pp e
198
  | Binop (ty, op, e1, e2) ->
×
199
    Fmt.pf fmt "@[<hov 1>(%a.%a@ %a@ %a)@]" Ty.pp ty Ty.Binop.pp op pp e1 pp e2
200
  | Triop (ty, op, e1, e2, e3) ->
×
201
    Fmt.pf fmt "@[<hov 1>(%a.%a@ %a@ %a@ %a)@]" Ty.pp ty Ty.Triop.pp op pp e1 pp
202
      e2 pp e3
UNCOV
203
  | Relop (ty, op, e1, e2) ->
×
204
    Fmt.pf fmt "@[<hov 1>(%a.%a@ %a@ %a)@]" Ty.pp ty Ty.Relop.pp op pp e1 pp e2
UNCOV
205
  | Cvtop (ty, op, e) ->
×
206
    Fmt.pf fmt "@[<hov 1>(%a.%a@ %a)@]" Ty.pp ty Ty.Cvtop.pp op pp e
207
  | Naryop (ty, op, es) ->
×
208
    Fmt.pf fmt "@[<hov 1>(%a.%a@ (%a))@]" Ty.pp ty Ty.Naryop.pp op
209
      (Fmt.list ~sep:Fmt.comma pp)
×
210
      es
211
  | Extract (e, h, l) -> Fmt.pf fmt "@[<hov 1>(extract@ %a@ %d@ %d)@]" pp e l h
×
UNCOV
212
  | Concat (e1, e2) -> Fmt.pf fmt "@[<hov 1>(++@ %a@ %a)@]" pp e1 pp e2
×
213
  | Binder (b, vars, e) ->
×
UNCOV
214
    Fmt.pf fmt "@[<hov 1>(%a@ (%a)@ %a)@]" Binder.pp b (Fmt.list ~sep:Fmt.sp pp)
×
215
      vars pp e
216

217
let pp_list fmt (es : t list) = Fmt.hovbox (Fmt.list ~sep:Fmt.comma pp) fmt es
×
218

219
let pp_smtml fmt (es : t list) : unit =
UNCOV
220
  let def_num_printer = Num.get_default_printer () in
×
221
  Num.set_default_printer `Hexadecimal;
×
UNCOV
222
  Bitvector.set_default_printer `WithType;
×
UNCOV
223
  let pp_symbols fmt syms =
×
224
    Fmt.list ~sep:Fmt.cut
×
225
      (fun fmt sym ->
226
        let t = Symbol.type_of sym in
×
227
        Fmt.pf fmt "(let-const %a %a)" Symbol.pp sym Ty.pp t )
×
228
      fmt syms
229
  in
230
  let pp_asserts fmt es =
231
    Fmt.list ~sep:Fmt.cut
×
UNCOV
232
      (fun fmt e -> Fmt.pf fmt "(assert @[<h 2>%a@])" pp e)
×
233
      fmt es
234
  in
235
  let syms = get_symbols es in
236
  if List.length syms > 0 then Fmt.pf fmt "@[<v>%a@]@\n" pp_symbols syms;
×
UNCOV
237
  if List.length es > 0 then Fmt.pf fmt "@[<v>%a@]@\n" pp_asserts es;
×
UNCOV
238
  Fmt.string fmt "(check-sat)";
×
UNCOV
239
  Num.set_default_printer def_num_printer;
×
240
  Bitvector.set_default_printer `Pretty
×
241

242
let to_string e = Fmt.str "%a" pp e
×
243

244
let value (v : Value.t) : t = make (Val v) [@@inline]
663✔
245

246
let ptr base offset = make (Ptr { base = Bitvector.of_int32 base; offset })
7✔
247

UNCOV
248
let loc l = make (Loc l)
×
249

250
let list l = make (List l)
5✔
251

UNCOV
252
let app symbol args = make (App (symbol, args))
×
253

254
let[@inline] binder bt vars expr = make (Binder (bt, vars, expr))
×
255

UNCOV
256
let let_in vars body = binder Let_in vars body
×
257

UNCOV
258
let forall vars body = binder Forall vars body
×
259

260
let exists vars body = binder Exists vars body
×
261

262
let raw_unop ty op hte = make (Unop (ty, op, hte)) [@@inline]
4✔
263

264
let normalize_eq_or_ne op (ty', e1, e2) =
265
  let make_relop lhs rhs = Relop (ty', op, lhs, rhs) in
×
266
  let ty1, ty2 = (ty e1, ty e2) in
×
267
  if not (Ty.equal ty1 ty2) then make_relop e1 e2
×
268
  else begin
×
269
    match ty1 with
270
    | Ty_bitv m ->
×
271
      let binop = make (Binop (ty1, Sub, e1, e2)) in
UNCOV
272
      let zero = make (Val (Bitv (Bitvector.make Z.zero m))) in
×
273
      make_relop binop zero
×
274
    | Ty_int ->
×
275
      let binop = make (Binop (ty1, Sub, e1, e2)) in
UNCOV
276
      let zero = make (Val (Int Int.zero)) in
×
277
      make_relop binop zero
×
278
    | Ty_real ->
×
279
      let binop = make (Binop (ty1, Sub, e1, e2)) in
280
      let zero = make (Val (Real 0.)) in
×
281
      make_relop binop zero
×
UNCOV
282
    | _ -> make_relop e1 e2
×
283
  end
284

285
let negate_relop (hte : t) : t =
286
  let e =
×
287
    match view hte with
UNCOV
288
    | Relop (ty, Eq, e1, e2) -> normalize_eq_or_ne Ne (ty, e1, e2)
×
289
    | Relop (ty, Ne, e1, e2) -> normalize_eq_or_ne Eq (ty, e1, e2)
×
UNCOV
290
    | Relop (ty, Lt, e1, e2) -> Relop (ty, Le, e2, e1)
×
UNCOV
291
    | Relop (ty, LtU, e1, e2) -> Relop (ty, LeU, e2, e1)
×
UNCOV
292
    | Relop (ty, Le, e1, e2) -> Relop (ty, Lt, e2, e1)
×
UNCOV
293
    | Relop (ty, LeU, e1, e2) -> Relop (ty, LtU, e2, e1)
×
UNCOV
294
    | Relop (ty, Gt, e1, e2) -> Relop (ty, Le, e1, e2)
×
UNCOV
295
    | Relop (ty, GtU, e1, e2) -> Relop (ty, LeU, e1, e2)
×
296
    | Relop (ty, Ge, e1, e2) -> Relop (ty, Lt, e1, e2)
×
UNCOV
297
    | Relop (ty, GeU, e1, e2) -> Relop (ty, LtU, e1, e2)
×
UNCOV
298
    | _ -> Fmt.failwith "negate_relop: not a relop."
×
299
  in
300
  make e
301

302
let unop ty op hte =
303
  match (op, view hte) with
34✔
304
  | Ty.Unop.(Regexp_loop _ | Regexp_star), _ -> raw_unop ty op hte
×
305
  | _, Val v -> value (Eval.unop ty op v)
23✔
306
  | Not, Unop (_, Not, hte') -> hte'
1✔
307
  | Not, Relop (Ty_fp _, _, _, _) -> raw_unop ty op hte
2✔
308
  | Not, Relop (_, _, _, _) -> negate_relop hte
×
309
  | Neg, Unop (_, Neg, hte') -> hte'
1✔
UNCOV
310
  | Trim, Cvtop (Ty_real, ToString, _) -> hte
×
311
  | Head, List (hd :: _) -> hd
1✔
312
  | Tail, List (_ :: tl) -> make (List tl)
1✔
313
  | Reverse, List es -> make (List (List.rev es))
2✔
314
  | Length, List es -> value (Int (List.length es))
1✔
315
  | _ -> raw_unop ty op hte
2✔
316

317
let raw_binop ty op hte1 hte2 = make (Binop (ty, op, hte1, hte2)) [@@inline]
25✔
318

319
let rec binop ty op hte1 hte2 =
320
  match (op, view hte1, view hte2) with
100✔
321
  | Ty.Binop.(String_in_re | Regexp_range), _, _ -> raw_binop ty op hte1 hte2
×
322
  | op, Val v1, Val v2 -> value (Eval.binop ty op v1 v2)
68✔
323
  | Sub, Ptr { base = b1; offset = os1 }, Ptr { base = b2; offset = os2 } ->
1✔
324
    if Bitvector.equal b1 b2 then binop ty Sub os1 os2
1✔
325
    else raw_binop ty op hte1 hte2
×
326
  | Add, Ptr { base; offset }, _ ->
2✔
327
    let m = Bitvector.numbits base in
328
    make (Ptr { base; offset = binop (Ty_bitv m) Add offset hte2 })
2✔
329
  | Sub, Ptr { base; offset }, _ ->
1✔
330
    let m = Bitvector.numbits base in
331
    make (Ptr { base; offset = binop (Ty_bitv m) Sub offset hte2 })
1✔
332
  | Rem, Ptr { base; offset }, _ ->
1✔
333
    let m = Bitvector.numbits base in
334
    let rhs = value (Bitv base) in
1✔
335
    let addr = binop (Ty_bitv m) Add rhs offset in
1✔
336
    binop ty Rem addr hte2
1✔
337
  | Add, _, Ptr { base; offset } ->
1✔
338
    let m = Bitvector.numbits base in
339
    make (Ptr { base; offset = binop (Ty_bitv m) Add offset hte1 })
1✔
340
  | Sub, _, Ptr { base; offset } ->
×
341
    let m = Bitvector.numbits base in
342
    let base = value (Bitv base) in
×
343
    binop ty Sub hte1 (binop (Ty_bitv m) Add base offset)
×
344
  | (Add | Or), Val (Bitv bv), _ when Bitvector.eqz bv -> hte2
×
345
  | (And | Div | DivU | Mul | Rem | RemU), Val (Bitv bv), _
×
346
    when Bitvector.eqz bv ->
3✔
347
    hte1
1✔
UNCOV
348
  | (Add | Or), _, Val (Bitv bv) when Bitvector.eqz bv -> hte1
×
349
  | (And | Mul), _, Val (Bitv bv) when Bitvector.eqz bv -> hte2
1✔
350
  | Add, Binop (ty, Add, x, { node = Val v1; _ }), Val v2 ->
1✔
351
    let v = value (Eval.binop ty Add v1 v2) in
1✔
352
    raw_binop ty Add x v
1✔
353
  | Sub, Binop (ty, Sub, x, { node = Val v1; _ }), Val v2 ->
1✔
354
    let v = value (Eval.binop ty Add v1 v2) in
1✔
355
    raw_binop ty Sub x v
1✔
356
  | Mul, Val (Bitv bv), _ when Bitvector.eq_one bv -> hte2
×
UNCOV
357
  | Mul, _, Val (Bitv bv) when Bitvector.eq_one bv -> hte1
×
358
  | Mul, Binop (ty, Mul, x, { node = Val v1; _ }), Val v2 ->
1✔
359
    let v = value (Eval.binop ty Mul v1 v2) in
1✔
360
    raw_binop ty Mul x v
1✔
361
  | Add, Val v1, Binop (ty, Add, x, { node = Val v2; _ }) ->
1✔
362
    let v = value (Eval.binop ty Add v1 v2) in
1✔
363
    raw_binop ty Add v x
1✔
364
  | Mul, Val v1, Binop (ty, Mul, x, { node = Val v2; _ }) ->
1✔
365
    let v = value (Eval.binop ty Mul v1 v2) in
1✔
366
    raw_binop ty Mul v x
1✔
367
  | At, List es, Val (Int n) ->
1✔
368
    (* TODO: use another datastructure? *)
369
    begin match List.nth_opt es n with None -> assert false | Some v -> v
1✔
370
    end
371
  | List_cons, _, List es -> make (List (hte1 :: es))
1✔
UNCOV
372
  | List_append, List _, (List [] | Val (List [])) -> hte1
×
373
  | List_append, (List [] | Val (List [])), List _ -> hte2
×
374
  | List_append, List l0, Val (List l1) -> make (List (l0 @ List.map value l1))
1✔
UNCOV
375
  | List_append, Val (List l0), List l1 -> make (List (List.map value l0 @ l1))
×
UNCOV
376
  | List_append, List l0, List l1 -> make (List (l0 @ l1))
×
377
  | _ -> raw_binop ty op hte1 hte2
14✔
378

UNCOV
379
let raw_triop ty op e1 e2 e3 = make (Triop (ty, op, e1, e2, e3)) [@@inline]
×
380

381
let triop ty op e1 e2 e3 =
382
  match (op, view e1, view e2, view e3) with
6✔
383
  | Ty.Triop.Ite, Val True, _, _ -> e2
1✔
384
  | Ite, Val False, _, _ -> e3
1✔
385
  | op, Val v1, Val v2, Val v3 -> value (Eval.triop ty op v1 v2 v3)
4✔
UNCOV
386
  | Ite, _, Triop (_, Ite, c2, r1, r2), Triop (_, Ite, _, _, _) ->
×
387
    let else_ = raw_triop ty Ite e1 r2 e3 in
388
    let cond = binop Ty_bool And e1 c2 in
×
UNCOV
389
    raw_triop ty Ite cond r1 else_
×
390
  | _ -> raw_triop ty op e1 e2 e3
×
391

392
let raw_relop ty op hte1 hte2 = make (Relop (ty, op, hte1, hte2)) [@@inline]
7✔
393

394
let rec relop ty op hte1 hte2 =
395
  match (op, view hte1, view hte2) with
86✔
396
  | op, Val v1, Val v2 -> value (if Eval.relop ty op v1 v2 then True else False)
29✔
UNCOV
397
  | Ty.Relop.Ne, Val (Real v), _ | Ne, _, Val (Real v) ->
×
UNCOV
398
    if Float.is_nan v || Float.is_infinite v then value True
×
UNCOV
399
    else raw_relop ty op hte1 hte2
×
UNCOV
400
  | _, Val (Real v), _ | _, _, Val (Real v) ->
×
UNCOV
401
    if Float.is_nan v || Float.is_infinite v then value False
×
UNCOV
402
    else raw_relop ty op hte1 hte2
×
UNCOV
403
  | Eq, _, Val Nothing | Eq, Val Nothing, _ -> value False
×
404
  | Ne, _, Val Nothing | Ne, Val Nothing, _ -> value True
×
405
  | Eq, _, Val (App (`Op "symbol", [ Str _ ]))
×
UNCOV
406
  | Eq, Val (App (`Op "symbol", [ Str _ ])), _ ->
×
407
    value False
UNCOV
408
  | Ne, _, Val (App (`Op "symbol", [ Str _ ]))
×
UNCOV
409
  | Ne, Val (App (`Op "symbol", [ Str _ ])), _ ->
×
410
    value True
UNCOV
411
  | ( Eq
×
412
    , Symbol ({ ty = Ty_fp prec1; _ } as s1)
413
    , Symbol ({ ty = Ty_fp prec2; _ } as s2) )
UNCOV
414
    when prec1 = prec2 && Symbol.equal s1 s2 ->
×
UNCOV
415
    raw_unop Ty_bool Not (raw_unop (Ty_fp prec1) Is_nan hte1)
×
416
  | Eq, Ptr { base = b1; offset = os1 }, Ptr { base = b2; offset = os2 } ->
2✔
417
    if Bitvector.equal b1 b2 then relop Ty_bool Eq os1 os2 else value False
1✔
418
  | Ne, Ptr { base = b1; offset = os1 }, Ptr { base = b2; offset = os2 } ->
2✔
419
    if Bitvector.equal b1 b2 then relop Ty_bool Ne os1 os2 else value True
1✔
420
  | ( (LtU | LeU)
2✔
421
    , Ptr { base = b1; offset = os1 }
422
    , Ptr { base = b2; offset = os2 } ) ->
423
    if Bitvector.equal b1 b2 then relop ty op os1 os2
2✔
424
    else
425
      let b1 = Value.Bitv b1 in
2✔
426
      let b2 = Value.Bitv b2 in
427
      value (if Eval.relop ty op b1 b2 then True else False)
1✔
428
  | ( op
2✔
429
    , Val (Bitv _ as n)
430
    , Ptr { base; offset = { node = Val (Bitv _ as o); _ } } ) ->
431
    let base = Eval.binop (Ty_bitv 32) Add (Bitv base) o in
432
    value (if Eval.relop ty op n base then True else False)
1✔
433
  | op, Ptr { base; offset = { node = Val (Bitv _ as o); _ } }, Val (Bitv _ as n)
2✔
434
    ->
435
    let base = Eval.binop (Ty_bitv 32) Add (Bitv base) o in
436
    value (if Eval.relop ty op base n then True else False)
1✔
437
  | op, List l1, List l2 -> relop_list op l1 l2
×
438
  | Gt, _, _ -> relop ty Lt hte2 hte1
2✔
439
  | GtU, _, _ -> relop ty LtU hte2 hte1
1✔
440
  | Ge, _, _ -> relop ty Le hte2 hte1
1✔
441
  | GeU, _, _ -> relop ty LeU hte2 hte1
1✔
442
  | _, _, _ -> raw_relop ty op hte1 hte2
7✔
443

444
and relop_list op l1 l2 =
445
  match (op, l1, l2) with
×
446
  | Eq, [], [] -> value True
×
447
  | Eq, _, [] | Eq, [], _ -> value False
×
448
  | Eq, l1, l2 ->
×
449
    if not (List.compare_lengths l1 l2 = 0) then value False
×
450
    else
451
      List.fold_left2
×
452
        (fun acc a b ->
453
          binop Ty_bool And acc
×
454
          @@
UNCOV
455
          match (ty a, ty b) with
×
456
          | Ty_real, Ty_real -> relop Ty_real Eq a b
×
457
          | _ -> relop Ty_bool Eq a b )
×
UNCOV
458
        (value True) l1 l2
×
459
  | Ne, _, _ -> unop Ty_bool Not @@ relop_list Eq l1 l2
×
460
  | (Lt | LtU | Gt | GtU | Le | LeU | Ge | GeU), _, _ -> assert false
461

462
let raw_cvtop ty op hte = make (Cvtop (ty, op, hte)) [@@inline]
3✔
463

464
let rec cvtop theory op hte =
465
  match (op, view hte) with
28✔
UNCOV
466
  | Ty.Cvtop.String_to_re, _ -> raw_cvtop theory op hte
×
467
  | _, Val v -> value (Eval.cvtop theory op v)
23✔
UNCOV
468
  | String_to_float, Cvtop (Ty_real, ToString, hte) -> hte
×
UNCOV
469
  | ( Reinterpret_float
×
470
    , Cvtop (Ty_real, Reinterpret_int, { node = Symbol { ty = Ty_int; _ }; _ })
471
    ) ->
472
    hte
473
  | Zero_extend n, Ptr { base; offset } ->
1✔
474
    let offset = cvtop theory op offset in
475
    make (Ptr { base = Bitvector.zero_extend n base; offset })
1✔
476
  | WrapI64, Ptr { base; offset } ->
1✔
477
    let offset = cvtop theory op offset in
478
    make (Ptr { base = Bitvector.extract base ~high:31 ~low:0; offset })
1✔
UNCOV
479
  | WrapI64, Cvtop (Ty_bitv 64, Zero_extend 32, hte) ->
×
UNCOV
480
    assert (Ty.equal theory (ty hte) && Ty.equal theory (Ty_bitv 32));
×
481
    hte
482
  | _ -> raw_cvtop theory op hte
3✔
483

UNCOV
484
let raw_naryop ty op es = make (Naryop (ty, op, es)) [@@inline]
×
485

486
let naryop ty op es =
UNCOV
487
  if List.for_all (fun e -> match view e with Val _ -> true | _ -> false) es
×
488
  then
489
    let vs =
7✔
490
      List.map (fun e -> match view e with Val v -> v | _ -> assert false) es
18✔
491
    in
492
    value (Eval.naryop ty op vs)
7✔
493
  else
494
    match (ty, op, List.map view es) with
×
495
    | ( Ty_str
×
496
      , Concat
497
      , [ Naryop (Ty_str, Concat, l1); Naryop (Ty_str, Concat, l2) ] ) ->
498
      raw_naryop Ty_str Concat (l1 @ l2)
499
    | Ty_str, Concat, [ Naryop (Ty_str, Concat, htes); hte ] ->
×
UNCOV
500
      raw_naryop Ty_str Concat (htes @ [ make hte ])
×
501
    | Ty_str, Concat, [ hte; Naryop (Ty_str, Concat, htes) ] ->
×
UNCOV
502
      raw_naryop Ty_str Concat (make hte :: htes)
×
503
    | _ -> raw_naryop ty op es
×
504

505
let[@inline] raw_extract (hte : t) ~(high : int) ~(low : int) : t =
506
  make (Extract (hte, high, low))
7✔
507

508
let extract (hte : t) ~(high : int) ~(low : int) : t =
509
  match (view hte, high, low) with
12✔
510
  | Val (Bitv bv), high, low ->
3✔
511
    let high = (high * 8) - 1 in
512
    let low = low * 8 in
513
    value (Bitv (Bitvector.extract bv ~high ~low))
3✔
514
  | ( Cvtop
2✔
515
        ( _
516
        , (Zero_extend 24 | Sign_extend 24)
1✔
517
        , ({ node = Symbol { ty = Ty_bitv 8; _ }; _ } as sym) )
518
    , 1
519
    , 0 ) ->
520
    sym
521
  | Concat (_, e), h, l when Ty.size (ty e) = h - l -> e
2✔
UNCOV
522
  | Concat (e, _), 8, 4 when Ty.size (ty e) = 4 -> e
×
523
  | _ ->
5✔
UNCOV
524
    if high - low = Ty.size (ty hte) then hte else raw_extract hte ~high ~low
×
525

526
let raw_concat (msb : t) (lsb : t) : t = make (Concat (msb, lsb)) [@@inline]
2✔
527

528
(* TODO: don't rebuild so many values it generates unecessary hc lookups *)
529
let rec concat (msb : t) (lsb : t) : t =
530
  match (view msb, view lsb) with
6✔
531
  | Val (Bitv a), Val (Bitv b) -> value (Bitv (Bitvector.concat a b))
1✔
532
  | Val (Bitv _), Concat (({ node = Val (Bitv _); _ } as b), se) ->
×
UNCOV
533
    raw_concat (concat msb b) se
×
534
  | Extract (s1, h, m1), Extract (s2, m2, l) when equal s1 s2 && m1 = m2 ->
3✔
535
    if h - l = Ty.size (ty s1) then s1 else raw_extract s1 ~high:h ~low:l
1✔
UNCOV
536
  | Extract (_, _, _), Concat (({ node = Extract (_, _, _); _ } as e2), e3) ->
×
UNCOV
537
    raw_concat (concat msb e2) e3
×
538
  | _ -> raw_concat msb lsb
2✔
539

540
let rec simplify_expr ?(in_relop = false) (hte : t) : t =
4✔
541
  match view hte with
16✔
542
  | Val _ | Symbol _ | Loc _ -> hte
×
543
  | Ptr { base; offset } ->
×
544
    let offset = simplify_expr ~in_relop offset in
UNCOV
545
    if not in_relop then make (Ptr { base; offset })
×
UNCOV
546
    else binop (Ty_bitv 32) Add (value (Bitv base)) offset
×
547
  | List es -> make @@ List (List.map (simplify_expr ~in_relop) es)
×
548
  | App (x, es) -> make @@ App (x, List.map (simplify_expr ~in_relop) es)
×
549
  | Unop (ty, op, e) ->
×
550
    let e = simplify_expr ~in_relop e in
551
    unop ty op e
×
552
  | Binop (ty, op, e1, e2) ->
6✔
553
    let e1 = simplify_expr ~in_relop e1 in
554
    let e2 = simplify_expr ~in_relop e2 in
6✔
555
    binop ty op e1 e2
6✔
UNCOV
556
  | Relop (ty, op, e1, e2) ->
×
557
    let e1 = simplify_expr ~in_relop:true e1 in
UNCOV
558
    let e2 = simplify_expr ~in_relop:true e2 in
×
UNCOV
559
    relop ty op e1 e2
×
UNCOV
560
  | Triop (ty, op, c, e1, e2) ->
×
561
    let c = simplify_expr ~in_relop c in
UNCOV
562
    let e1 = simplify_expr ~in_relop e1 in
×
UNCOV
563
    let e2 = simplify_expr ~in_relop e2 in
×
UNCOV
564
    triop ty op c e1 e2
×
UNCOV
565
  | Cvtop (ty, op, e) ->
×
566
    let e = simplify_expr ~in_relop e in
UNCOV
567
    cvtop ty op e
×
UNCOV
568
  | Naryop (ty, op, es) ->
×
569
    let es = List.map (simplify_expr ~in_relop) es in
570
    naryop ty op es
×
UNCOV
571
  | Extract (s, high, low) ->
×
572
    let s = simplify_expr ~in_relop s in
UNCOV
573
    extract s ~high ~low
×
UNCOV
574
  | Concat (e1, e2) ->
×
575
    let msb = simplify_expr ~in_relop e1 in
UNCOV
576
    let lsb = simplify_expr ~in_relop e2 in
×
UNCOV
577
    concat msb lsb
×
UNCOV
578
  | Binder _ ->
×
579
    (* Not simplifying anything atm *)
580
    hte
581

582
module Cache = Hashtbl.Make (struct
583
  type nonrec t = t
584

585
  let hash = hash
586

587
  let equal = equal
588
end)
589

590
let simplify =
591
  (* TODO: it may make sense to share the cache with simplify_expr ? *)
592
  let cache = Cache.create 512 in
593
  fun e ->
4✔
594
    match Cache.find_opt cache e with
2✔
595
    | Some simplified -> simplified
×
596
    | None ->
2✔
597
      let rec loop x =
598
        let x' = simplify_expr x in
4✔
599
        if equal x x' then begin
2✔
600
          Cache.add cache e x';
601
          x'
2✔
602
        end
603
        else loop x'
2✔
604
      in
605
      loop e
606

607
module Bool = struct
608
  open Ty
609

610
  let of_val = function
611
    | Val True -> Some true
×
612
    | Val False -> Some false
×
613
    | _ -> None
×
614

615
  let true_ = value True
4✔
616

617
  let false_ = value False
4✔
618

619
  let to_val b = if b then true_ else false_
×
620

621
  let v b = to_val b [@@inline]
×
622

623
  let not b =
624
    let bexpr = view b in
×
625
    match of_val bexpr with
×
626
    | Some b -> to_val (not b)
×
UNCOV
627
    | None -> (
×
628
      match bexpr with
UNCOV
629
      | Unop (Ty_bool, Not, cond) -> cond
×
UNCOV
630
      | _ -> unop Ty_bool Not b )
×
631

632
  let equal b1 b2 =
UNCOV
633
    match (view b1, view b2) with
×
UNCOV
634
    | Val True, Val True | Val False, Val False -> true_
×
UNCOV
635
    | _ -> relop Ty_bool Eq b1 b2
×
636

637
  let distinct b1 b2 =
UNCOV
638
    match (view b1, view b2) with
×
UNCOV
639
    | Val True, Val False | Val False, Val True -> true_
×
UNCOV
640
    | _ -> relop Ty_bool Ne b1 b2
×
641

642
  let and_ b1 b2 =
643
    match (of_val (view b1), of_val (view b2)) with
×
UNCOV
644
    | Some true, _ -> b2
×
UNCOV
645
    | _, Some true -> b1
×
UNCOV
646
    | Some false, _ | _, Some false -> false_
×
UNCOV
647
    | _ -> binop Ty_bool And b1 b2
×
648

649
  let or_ b1 b2 =
UNCOV
650
    match (of_val (view b1), of_val (view b2)) with
×
UNCOV
651
    | Some false, _ -> b2
×
UNCOV
652
    | _, Some false -> b1
×
UNCOV
653
    | Some true, _ | _, Some true -> true_
×
UNCOV
654
    | _ -> binop Ty_bool Or b1 b2
×
655

UNCOV
656
  let ite c r1 r2 = triop Ty_bool Ite c r1 r2
×
657
end
658

659
module Make (T : sig
660
  type elt
661

662
  val ty : Ty.t
663

664
  val value : elt -> Value.t
665
end) =
666
struct
667
  open Ty
668

669
  let v i = value (T.value i)
×
670

UNCOV
671
  let sym x = symbol Symbol.(x @: T.ty)
×
672

673
  let ( ~- ) e = unop T.ty Neg e
×
674

675
  let ( = ) e1 e2 = relop Ty_bool Eq e1 e2
×
676

677
  let ( != ) e1 e2 = relop Ty_bool Ne e1 e2
×
678

UNCOV
679
  let ( > ) e1 e2 = relop T.ty Gt e1 e2
×
680

681
  let ( >= ) e1 e2 = relop T.ty Ge e1 e2
×
682

683
  let ( < ) e1 e2 = relop T.ty Lt e1 e2
×
684

UNCOV
685
  let ( <= ) e1 e2 = relop T.ty Le e1 e2
×
686
end
687

688
module Bitv = struct
689
  open Ty
690

691
  module I8 = Make (struct
692
    type elt = int
693

694
    let ty = Ty_bitv 8
695

UNCOV
696
    let value i = Value.Bitv (Bitvector.of_int8 i)
×
697
  end)
698

699
  module I32 = Make (struct
700
    type elt = int32
701

702
    let ty = Ty_bitv 32
703

704
    let value i = Value.Bitv (Bitvector.of_int32 i)
×
705
  end)
706

707
  module I64 = Make (struct
708
    type elt = int64
709

710
    let ty = Ty_bitv 64
711

UNCOV
712
    let value i = Value.Bitv (Bitvector.of_int64 i)
×
713
  end)
714
end
715

716
module Fpa = struct
717
  open Ty
718

719
  module F32 = struct
720
    include Make (struct
721
      type elt = float
722

723
      let ty = Ty_fp 32
724

725
      let value f = Value.Num (F32 (Int32.bits_of_float f))
×
726
    end)
727

728
    (* Redeclare equality due to incorrect theory annotation *)
729
    let ( = ) e1 e2 = relop (Ty_fp 32) Eq e1 e2
×
730

731
    let ( != ) e1 e2 = relop (Ty_fp 32) Ne e1 e2
×
732
  end
733

734
  module F64 = struct
735
    include Make (struct
736
      type elt = float
737

738
      let ty = Ty_fp 64
739

UNCOV
740
      let value f = Value.Num (F64 (Int64.bits_of_float f))
×
741
    end)
742

743
    (* Redeclare equality due to incorrect theory annotation *)
744
    let ( = ) e1 e2 = relop (Ty_fp 64) Eq e1 e2
×
745

UNCOV
746
    let ( != ) e1 e2 = relop (Ty_fp 64) Ne e1 e2
×
747
  end
748
end
749

750
module Smtlib = struct
751
  let rec pp fmt (hte : t) =
752
    match view hte with
13✔
753
    | Val v -> Value.Smtlib.pp fmt v
3✔
754
    | Ptr _ -> assert false
755
    | Loc _ -> assert false
756
    | Symbol s -> Fmt.pf fmt "@[<hov 1>%a@]" Symbol.pp s
5✔
757
    | List _ -> assert false
758
    | App _ -> assert false
UNCOV
759
    | Unop (ty, op, e) ->
×
760
      Fmt.pf fmt "@[<hov 1>(%a@ %a)@]" Ty.Smtlib.pp_unop (ty, op) pp e
761
    | Binop (ty, op, e1, e2) ->
2✔
762
      Fmt.pf fmt "@[<hov 1>(%a@ %a@ %a)@]" Ty.Smtlib.pp_binop (ty, op) pp e1 pp
763
        e2
764
    | Triop _ -> assert false
765
    | Relop (ty, op, e1, e2) ->
3✔
766
      Fmt.pf fmt "@[<hov 1>(%a@ %a@ %a)@]" Ty.Smtlib.pp_relop (ty, op) pp e1 pp
767
        e2
768
    | Cvtop _ -> assert false
769
    | Naryop _ -> assert false
770
    | Extract _ -> assert false
771
    | Concat _ -> assert false
772
    | Binder _ -> assert false
773
end
774

775
let inline_symbol_values map e =
776
  let rec aux e =
2✔
777
    match view e with
2✔
UNCOV
778
    | Val _ | Loc _ -> e
×
779
    | Symbol symbol -> Option.value ~default:e (Symbol.Map.find_opt symbol map)
2✔
UNCOV
780
    | Ptr e ->
×
781
      let offset = aux e.offset in
UNCOV
782
      make @@ Ptr { e with offset }
×
UNCOV
783
    | List vs ->
×
784
      let vs = List.map aux vs in
UNCOV
785
      list vs
×
UNCOV
786
    | App (x, vs) ->
×
787
      let vs = List.map aux vs in
788
      app x vs
×
UNCOV
789
    | Unop (ty, op, v) ->
×
790
      let v = aux v in
UNCOV
791
      unop ty op v
×
792
    | Binop (ty, op, v1, v2) ->
×
793
      let v1 = aux v1 in
794
      let v2 = aux v2 in
×
UNCOV
795
      binop ty op v1 v2
×
UNCOV
796
    | Triop (ty, op, v1, v2, v3) ->
×
797
      let v1 = aux v1 in
UNCOV
798
      let v2 = aux v2 in
×
UNCOV
799
      let v3 = aux v3 in
×
UNCOV
800
      triop ty op v1 v2 v3
×
UNCOV
801
    | Cvtop (ty, op, v) ->
×
802
      let v = aux v in
UNCOV
803
      cvtop ty op v
×
UNCOV
804
    | Relop (ty, op, v1, v2) ->
×
805
      let v1 = aux v1 in
UNCOV
806
      let v2 = aux v2 in
×
807
      relop ty op v1 v2
×
UNCOV
808
    | Naryop (ty, op, vs) ->
×
809
      let vs = List.map aux vs in
UNCOV
810
      naryop ty op vs
×
UNCOV
811
    | Extract (e, high, low) ->
×
812
      let e = aux e in
UNCOV
813
      extract e ~high ~low
×
UNCOV
814
    | Concat (e1, e2) ->
×
815
      let e1 = aux e1 in
UNCOV
816
      let e2 = aux e2 in
×
UNCOV
817
      concat e1 e2
×
UNCOV
818
    | Binder (b, vars, e) ->
×
819
      let e = aux e in
UNCOV
820
      binder b vars e
×
821
  in
822
  aux e
823

824
module Set = struct
825
  include PatriciaTree.MakeHashconsedSet (Key) ()
826

827
  let hash = to_int
828

829
  let pp fmt v =
UNCOV
830
    Fmt.pf fmt "@[<hov 1>%a@]"
×
UNCOV
831
      (pretty ~pp_sep:(fun fmt () -> Fmt.pf fmt "@;") pp)
×
832
      v
833

834
  let get_symbols (set : t) =
UNCOV
835
    let tbl = Hashtbl.create 64 in
×
UNCOV
836
    let rec symbols hte =
×
UNCOV
837
      match view hte with
×
UNCOV
838
      | Val _ | Loc _ -> ()
×
UNCOV
839
      | Ptr { offset; _ } -> symbols offset
×
UNCOV
840
      | Symbol s -> Hashtbl.replace tbl s ()
×
UNCOV
841
      | List es -> List.iter symbols es
×
UNCOV
842
      | App (_, es) -> List.iter symbols es
×
UNCOV
843
      | Unop (_, _, e1) -> symbols e1
×
UNCOV
844
      | Binop (_, _, e1, e2) ->
×
845
        symbols e1;
UNCOV
846
        symbols e2
×
UNCOV
847
      | Triop (_, _, e1, e2, e3) ->
×
848
        symbols e1;
UNCOV
849
        symbols e2;
×
UNCOV
850
        symbols e3
×
UNCOV
851
      | Relop (_, _, e1, e2) ->
×
852
        symbols e1;
UNCOV
853
        symbols e2
×
UNCOV
854
      | Cvtop (_, _, e) -> symbols e
×
UNCOV
855
      | Naryop (_, _, es) -> List.iter symbols es
×
UNCOV
856
      | Extract (e, _, _) -> symbols e
×
UNCOV
857
      | Concat (e1, e2) ->
×
858
        symbols e1;
UNCOV
859
        symbols e2
×
UNCOV
860
      | Binder (_, vars, e) ->
×
861
        List.iter symbols vars;
UNCOV
862
        symbols e
×
863
    in
864
    iter symbols set;
UNCOV
865
    Hashtbl.fold (fun k () acc -> k :: acc) tbl []
×
866

867
  let map f set =
UNCOV
868
    fold
×
869
      (fun elt set ->
UNCOV
870
        let elt = f elt in
×
UNCOV
871
        add elt set )
×
872
      set empty
873

874
  let inline_symbol_values symbol_map set =
UNCOV
875
    map (inline_symbol_values symbol_map) set
×
876
end
877

878
let rec split_conjunctions (e : t) : Set.t =
UNCOV
879
  match view e with
×
UNCOV
880
  | Binop (Ty_bool, And, e1, e2) ->
×
881
    let s1 = split_conjunctions e1 in
UNCOV
882
    let s2 = split_conjunctions e2 in
×
UNCOV
883
    Set.union s1 s2
×
UNCOV
884
  | _ -> Set.singleton e
×
885

886
let rec split_disjunctions (e : t) : Set.t =
UNCOV
887
  match view e with
×
UNCOV
888
  | Binop (Ty_bool, Or, e1, e2) ->
×
889
    let s1 = split_disjunctions e1 in
UNCOV
890
    let s2 = split_disjunctions e2 in
×
UNCOV
891
    Set.union s1 s2
×
UNCOV
892
  | _ -> Set.singleton e
×
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc