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

formalsec / smtml / 443

15 Dec 2025 12:13PM UTC coverage: 45.286% (-0.05%) from 45.333%
443

push

github

filipeom
Add `Expr.Set.to_list` for compatability with OCaml 4.14

0 of 1 new or added line in 1 file covered. (0.0%)

150 existing lines in 1 file now uncovered.

879 of 1941 relevant lines covered (45.29%)

7.45 hits per line

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

31.77
/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
500✔
36
    | Val v1, Val v2 -> Value.equal v1 v2
472✔
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) ->
1✔
49
      Ty.equal t1 t2 && Ty.Relop.equal op1 op2 && phys_equal e1 e2
1✔
50
      && phys_equal e3 e4
1✔
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,022✔
71
    match e with
72
    | Val v -> h v
858✔
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)
9✔
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

UNCOV
100
  let compare x y = compare (to_int x) (to_int y)
×
101
end
102

103
let[@inline] make e = Hc.hashcons e
761✔
104

105
let[@inline] view (hte : t) = hte.node
559✔
106

UNCOV
107
let[@inline] compare (hte1 : t) (hte2 : t) = compare hte1.tag hte2.tag
×
108

109
let symbol s = make (Symbol s)
27✔
110

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

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

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

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

UNCOV
219
let pp_list fmt (es : t list) = Fmt.hovbox (Fmt.list ~sep:Fmt.comma pp) fmt es
×
220

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

UNCOV
244
let to_string e = Fmt.str "%a" pp e
×
245

246
let value (v : Value.t) : t = make (Val v) [@@inline]
665✔
247

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

UNCOV
250
let loc l = make (Loc l)
×
251

252
let list l = make (List l)
5✔
253

254
let app symbol args = make (App (symbol, args))
×
255

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

258
let let_in vars body = binder Let_in vars body
×
259

260
let forall vars body = binder Forall vars body
×
261

UNCOV
262
let exists vars body = binder Exists vars body
×
263

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

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

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

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

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

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

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

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

394
let raw_relop ty op hte1 hte2 = make (Relop (ty, op, hte1, hte2)) [@@inline]
5✔
395

396
let rec relop ty (op : Ty.Relop.t) hte1 hte2 =
397
  let both_phys_eq = phys_equal hte1 hte2 in
81✔
398
  let can_be_shortcuted =
81✔
399
    match ty with
400
    | Ty.Ty_bool | Ty_bitv _ | Ty_int | Ty_unit -> both_phys_eq
×
UNCOV
401
    | Ty_fp _ | Ty_app | Ty_list | Ty_real | Ty_regexp | Ty_roundingMode
×
UNCOV
402
    | Ty_none | Ty_str ->
×
403
      false
404
  in
405
  match (op, view hte1, view hte2) with
81✔
406
  | (Eq | Le | Ge | LeU | GeU), _, _ when can_be_shortcuted -> value True
3✔
407
  | (Ne | Lt | Gt | LtU | GtU), _, _ when can_be_shortcuted -> value False
3✔
408
  | op, Val v1, Val v2 -> value (if Eval.relop ty op v1 v2 then True else False)
21✔
409
  | Ne, Val (Real v), _ | Ne, _, Val (Real v) ->
×
410
    if Float.is_nan v || Float.is_infinite v then value True
×
411
    else if both_phys_eq then value False
×
412
    else raw_relop ty op hte1 hte2
×
UNCOV
413
  | _, Val (Real v), _ | _, _, Val (Real v) ->
×
UNCOV
414
    if Float.is_nan v || Float.is_infinite v then value False
×
415
    else
416
      (* TODO: it is possible to add a shortcut when `both_phys_eq` *)
417
      raw_relop ty op hte1 hte2
×
418
  | Eq, _, Val Nothing | Eq, Val Nothing, _ -> value False
×
419
  | Ne, _, Val Nothing | Ne, Val Nothing, _ -> value True
×
UNCOV
420
  | Eq, _, Val (App (`Op "symbol", [ Str _ ]))
×
421
  | Eq, Val (App (`Op "symbol", [ Str _ ])), _ ->
×
422
    value False
UNCOV
423
  | Ne, _, Val (App (`Op "symbol", [ Str _ ]))
×
424
  | Ne, Val (App (`Op "symbol", [ Str _ ])), _ ->
×
425
    value True
UNCOV
426
  | ( Eq
×
427
    , Symbol ({ ty = Ty_fp prec1; _ } as s1)
428
    , Symbol ({ ty = Ty_fp prec2; _ } as s2) )
UNCOV
429
    when both_phys_eq || (prec1 = prec2 && Symbol.equal s1 s2) ->
×
430
    raw_unop Ty_bool Not (raw_unop (Ty_fp prec1) Is_nan hte1)
×
431
  | Eq, Ptr { base = b1; offset = os1 }, Ptr { base = b2; offset = os2 } ->
1✔
UNCOV
432
    if both_phys_eq then value True
×
UNCOV
433
    else if Bitvector.equal b1 b2 then relop Ty_bool Eq os1 os2
×
434
    else value False
1✔
435
  | Ne, Ptr { base = b1; offset = os1 }, Ptr { base = b2; offset = os2 } ->
1✔
UNCOV
436
    if both_phys_eq then value False
×
UNCOV
437
    else if Bitvector.equal b1 b2 then relop Ty_bool Ne os1 os2
×
438
    else value True
1✔
439
  | ( (LtU | LeU)
1✔
440
    , Ptr { base = b1; offset = os1 }
441
    , Ptr { base = b2; offset = os2 } ) ->
UNCOV
442
    if both_phys_eq then value True
×
UNCOV
443
    else if Bitvector.equal b1 b2 then relop ty op os1 os2
×
444
    else
445
      let b1 = Value.Bitv b1 in
2✔
446
      let b2 = Value.Bitv b2 in
447
      value (if Eval.relop ty op b1 b2 then True else False)
1✔
448
  | ( op
2✔
449
    , Val (Bitv _ as n)
450
    , Ptr { base; offset = { node = Val (Bitv _ as o); _ } } ) ->
451
    let base = Eval.binop (Ty_bitv 32) Add (Bitv base) o in
452
    value (if Eval.relop ty op n base then True else False)
1✔
453
  | op, Ptr { base; offset = { node = Val (Bitv _ as o); _ } }, Val (Bitv _ as n)
2✔
454
    ->
455
    let base = Eval.binop (Ty_bitv 32) Add (Bitv base) o in
456
    value (if Eval.relop ty op base n then True else False)
1✔
UNCOV
457
  | op, List l1, List l2 -> relop_list op l1 l2
×
458
  | Gt, _, _ -> relop ty Lt hte2 hte1
2✔
459
  | GtU, _, _ -> relop ty LtU hte2 hte1
1✔
460
  | Ge, _, _ -> relop ty Le hte2 hte1
1✔
UNCOV
461
  | GeU, _, _ -> relop ty LeU hte2 hte1
×
462
  | _, _, _ -> raw_relop ty op hte1 hte2
5✔
463

464
and relop_list op l1 l2 =
465
  match (op, l1, l2) with
×
466
  | Eq, [], [] -> value True
×
467
  | Eq, _, [] | Eq, [], _ -> value False
×
UNCOV
468
  | Eq, l1, l2 ->
×
469
    if not (List.compare_lengths l1 l2 = 0) then value False
×
470
    else
471
      List.fold_left2
×
472
        (fun acc a b ->
473
          binop Ty_bool And acc
×
474
          @@
475
          match (ty a, ty b) with
×
476
          | Ty_real, Ty_real -> relop Ty_real Eq a b
×
477
          | _ -> relop Ty_bool Eq a b )
×
UNCOV
478
        (value True) l1 l2
×
UNCOV
479
  | Ne, _, _ -> unop Ty_bool Not @@ relop_list Eq l1 l2
×
480
  | (Lt | LtU | Gt | GtU | Le | LeU | Ge | GeU), _, _ -> assert false
481

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

484
let rec cvtop theory op hte =
485
  match (op, view hte) with
28✔
486
  | Ty.Cvtop.String_to_re, _ -> raw_cvtop theory op hte
×
487
  | _, Val v -> value (Eval.cvtop theory op v)
23✔
UNCOV
488
  | String_to_float, Cvtop (Ty_real, ToString, hte) -> hte
×
UNCOV
489
  | ( Reinterpret_float
×
490
    , Cvtop (Ty_real, Reinterpret_int, { node = Symbol { ty = Ty_int; _ }; _ })
491
    ) ->
492
    hte
493
  | Zero_extend n, Ptr { base; offset } ->
1✔
494
    let offset = cvtop theory op offset in
495
    make (Ptr { base = Bitvector.zero_extend n base; offset })
1✔
496
  | WrapI64, Ptr { base; offset } ->
1✔
497
    let offset = cvtop theory op offset in
498
    make (Ptr { base = Bitvector.extract base ~high:31 ~low:0; offset })
1✔
UNCOV
499
  | WrapI64, Cvtop (Ty_bitv 64, Zero_extend 32, hte) ->
×
UNCOV
500
    assert (Ty.equal theory (ty hte) && Ty.equal theory (Ty_bitv 32));
×
501
    hte
502
  | _ -> raw_cvtop theory op hte
3✔
503

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

506
let naryop ty op es =
UNCOV
507
  if List.for_all (fun e -> match view e with Val _ -> true | _ -> false) es
×
508
  then
509
    let vs =
7✔
510
      List.map (fun e -> match view e with Val v -> v | _ -> assert false) es
18✔
511
    in
512
    value (Eval.naryop ty op vs)
7✔
513
  else
UNCOV
514
    match (ty, op, List.map view es) with
×
UNCOV
515
    | ( Ty_str
×
516
      , Concat
517
      , [ Naryop (Ty_str, Concat, l1); Naryop (Ty_str, Concat, l2) ] ) ->
518
      raw_naryop Ty_str Concat (l1 @ l2)
519
    | Ty_str, Concat, [ Naryop (Ty_str, Concat, htes); hte ] ->
×
520
      raw_naryop Ty_str Concat (htes @ [ make hte ])
×
521
    | Ty_str, Concat, [ hte; Naryop (Ty_str, Concat, htes) ] ->
×
UNCOV
522
      raw_naryop Ty_str Concat (make hte :: htes)
×
UNCOV
523
    | _ -> raw_naryop ty op es
×
524

525
let[@inline] raw_extract (hte : t) ~(high : int) ~(low : int) : t =
526
  make (Extract (hte, high, low))
7✔
527

528
let extract (hte : t) ~(high : int) ~(low : int) : t =
529
  match (view hte, high, low) with
12✔
530
  | Val (Bitv bv), high, low ->
3✔
531
    let high = (high * 8) - 1 in
532
    let low = low * 8 in
533
    value (Bitv (Bitvector.extract bv ~high ~low))
3✔
534
  | ( Cvtop
2✔
535
        ( _
536
        , (Zero_extend 24 | Sign_extend 24)
1✔
537
        , ({ node = Symbol { ty = Ty_bitv 8; _ }; _ } as sym) )
538
    , 1
539
    , 0 ) ->
540
    sym
541
  | Concat (_, e), h, l when Ty.size (ty e) = h - l -> e
2✔
542
  | Concat (e, _), 8, 4 when Ty.size (ty e) = 4 -> e
×
543
  | _ ->
5✔
UNCOV
544
    if high - low = Ty.size (ty hte) then hte else raw_extract hte ~high ~low
×
545

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

548
(* TODO: don't rebuild so many values it generates unecessary hc lookups *)
549
let rec concat (msb : t) (lsb : t) : t =
550
  match (view msb, view lsb) with
6✔
551
  | Val (Bitv a), Val (Bitv b) -> value (Bitv (Bitvector.concat a b))
1✔
UNCOV
552
  | Val (Bitv _), Concat (({ node = Val (Bitv _); _ } as b), se) ->
×
UNCOV
553
    raw_concat (concat msb b) se
×
554
  | Extract (s1, h, m1), Extract (s2, m2, l) when equal s1 s2 && m1 = m2 ->
3✔
555
    if h - l = Ty.size (ty s1) then s1 else raw_extract s1 ~high:h ~low:l
1✔
UNCOV
556
  | Extract (_, _, _), Concat (({ node = Extract (_, _, _); _ } as e2), e3) ->
×
UNCOV
557
    raw_concat (concat msb e2) e3
×
558
  | _ -> raw_concat msb lsb
2✔
559

560
let rec simplify_expr ?(in_relop = false) (hte : t) : t =
4✔
561
  match view hte with
16✔
UNCOV
562
  | Val _ | Symbol _ | Loc _ -> hte
×
563
  | Ptr { base; offset } ->
×
564
    let offset = simplify_expr ~in_relop offset in
565
    if not in_relop then make (Ptr { base; offset })
×
566
    else binop (Ty_bitv 32) Add (value (Bitv base)) offset
×
567
  | List es -> make @@ List (List.map (simplify_expr ~in_relop) es)
×
UNCOV
568
  | App (x, es) -> make @@ App (x, List.map (simplify_expr ~in_relop) es)
×
569
  | Unop (ty, op, e) ->
×
570
    let e = simplify_expr ~in_relop e in
UNCOV
571
    unop ty op e
×
572
  | Binop (ty, op, e1, e2) ->
6✔
573
    let e1 = simplify_expr ~in_relop e1 in
574
    let e2 = simplify_expr ~in_relop e2 in
6✔
575
    binop ty op e1 e2
6✔
576
  | Relop (ty, op, e1, e2) ->
×
577
    let e1 = simplify_expr ~in_relop:true e1 in
578
    let e2 = simplify_expr ~in_relop:true e2 in
×
UNCOV
579
    relop ty op e1 e2
×
580
  | Triop (ty, op, c, e1, e2) ->
×
581
    let c = simplify_expr ~in_relop c in
582
    let e1 = simplify_expr ~in_relop e1 in
×
583
    let e2 = simplify_expr ~in_relop e2 in
×
UNCOV
584
    triop ty op c e1 e2
×
585
  | Cvtop (ty, op, e) ->
×
586
    let e = simplify_expr ~in_relop e in
UNCOV
587
    cvtop ty op e
×
588
  | Naryop (ty, op, es) ->
×
589
    let es = List.map (simplify_expr ~in_relop) es in
UNCOV
590
    naryop ty op es
×
591
  | Extract (s, high, low) ->
×
592
    let s = simplify_expr ~in_relop s in
UNCOV
593
    extract s ~high ~low
×
594
  | Concat (e1, e2) ->
×
595
    let msb = simplify_expr ~in_relop e1 in
596
    let lsb = simplify_expr ~in_relop e2 in
×
UNCOV
597
    concat msb lsb
×
UNCOV
598
  | Binder _ ->
×
599
    (* Not simplifying anything atm *)
600
    hte
601

602
module Cache = Hashtbl.Make (struct
603
  type nonrec t = t
604

605
  let hash = hash
606

607
  let equal = equal
608
end)
609

610
let simplify =
611
  (* TODO: it may make sense to share the cache with simplify_expr ? *)
612
  let cache = Cache.create 512 in
613
  fun e ->
4✔
614
    match Cache.find_opt cache e with
2✔
UNCOV
615
    | Some simplified -> simplified
×
616
    | None ->
2✔
617
      let rec loop x =
618
        let x' = simplify_expr x in
4✔
619
        if equal x x' then begin
2✔
620
          Cache.add cache e x';
621
          x'
2✔
622
        end
623
        else loop x'
2✔
624
      in
625
      loop e
626

627
module Bool = struct
628
  open Ty
629

630
  let of_val = function
631
    | Val True -> Some true
×
UNCOV
632
    | Val False -> Some false
×
UNCOV
633
    | _ -> None
×
634

635
  let true_ = value True
4✔
636

637
  let false_ = value False
4✔
638

639
  let to_val b = if b then true_ else false_
×
640

UNCOV
641
  let v b = to_val b [@@inline]
×
642

643
  let not b =
644
    let bexpr = view b in
×
645
    match of_val bexpr with
×
UNCOV
646
    | Some b -> to_val (not b)
×
647
    | None -> (
×
648
      match bexpr with
UNCOV
649
      | Unop (Ty_bool, Not, cond) -> cond
×
UNCOV
650
      | _ -> unop Ty_bool Not b )
×
651

652
  let equal b1 b2 =
653
    match (view b1, view b2) with
×
UNCOV
654
    | Val True, Val True | Val False, Val False -> true_
×
UNCOV
655
    | _ -> relop Ty_bool Eq b1 b2
×
656

657
  let distinct b1 b2 =
658
    match (view b1, view b2) with
×
UNCOV
659
    | Val True, Val False | Val False, Val True -> true_
×
UNCOV
660
    | _ -> relop Ty_bool Ne b1 b2
×
661

662
  let and_ b1 b2 =
663
    match (of_val (view b1), of_val (view b2)) with
×
664
    | Some true, _ -> b2
×
665
    | _, Some true -> b1
×
UNCOV
666
    | Some false, _ | _, Some false -> false_
×
UNCOV
667
    | _ -> binop Ty_bool And b1 b2
×
668

669
  let or_ b1 b2 =
670
    match (of_val (view b1), of_val (view b2)) with
×
671
    | Some false, _ -> b2
×
672
    | _, Some false -> b1
×
UNCOV
673
    | Some true, _ | _, Some true -> true_
×
674
    | _ -> binop Ty_bool Or b1 b2
×
675

UNCOV
676
  let ite c r1 r2 = triop Ty_bool Ite c r1 r2
×
677
end
678

679
module Make (T : sig
680
  type elt
681

682
  val ty : Ty.t
683

684
  val value : elt -> Value.t
685
end) =
686
struct
687
  open Ty
688

689
  let v i = value (T.value i)
×
690

691
  let sym x = symbol Symbol.(x @: T.ty)
×
692

693
  let ( ~- ) e = unop T.ty Neg e
×
694

695
  let ( = ) e1 e2 = relop Ty_bool Eq e1 e2
×
696

697
  let ( != ) e1 e2 = relop Ty_bool Ne e1 e2
×
698

699
  let ( > ) e1 e2 = relop T.ty Gt e1 e2
×
700

701
  let ( >= ) e1 e2 = relop T.ty Ge e1 e2
×
702

703
  let ( < ) e1 e2 = relop T.ty Lt e1 e2
×
704

UNCOV
705
  let ( <= ) e1 e2 = relop T.ty Le e1 e2
×
706
end
707

708
module Bitv = struct
709
  open Ty
710

711
  module I8 = Make (struct
712
    type elt = int
713

714
    let ty = Ty_bitv 8
715

UNCOV
716
    let value i = Value.Bitv (Bitvector.of_int8 i)
×
717
  end)
718

719
  module I32 = Make (struct
720
    type elt = int32
721

722
    let ty = Ty_bitv 32
723

UNCOV
724
    let value i = Value.Bitv (Bitvector.of_int32 i)
×
725
  end)
726

727
  module I64 = Make (struct
728
    type elt = int64
729

730
    let ty = Ty_bitv 64
731

UNCOV
732
    let value i = Value.Bitv (Bitvector.of_int64 i)
×
733
  end)
734
end
735

736
module Fpa = struct
737
  open Ty
738

739
  module F32 = struct
740
    include Make (struct
741
      type elt = float
742

743
      let ty = Ty_fp 32
744

UNCOV
745
      let value f = Value.Num (F32 (Int32.bits_of_float f))
×
746
    end)
747

748
    (* Redeclare equality due to incorrect theory annotation *)
749
    let ( = ) e1 e2 = relop (Ty_fp 32) Eq e1 e2
×
750

UNCOV
751
    let ( != ) e1 e2 = relop (Ty_fp 32) Ne e1 e2
×
752
  end
753

754
  module F64 = struct
755
    include Make (struct
756
      type elt = float
757

758
      let ty = Ty_fp 64
759

UNCOV
760
      let value f = Value.Num (F64 (Int64.bits_of_float f))
×
761
    end)
762

763
    (* Redeclare equality due to incorrect theory annotation *)
764
    let ( = ) e1 e2 = relop (Ty_fp 64) Eq e1 e2
×
765

UNCOV
766
    let ( != ) e1 e2 = relop (Ty_fp 64) Ne e1 e2
×
767
  end
768
end
769

770
module Smtlib = struct
771
  let rec pp fmt (hte : t) =
772
    match view hte with
13✔
773
    | Val v -> Value.Smtlib.pp fmt v
3✔
774
    | Ptr _ -> assert false
775
    | Loc _ -> assert false
776
    | Symbol s -> Fmt.pf fmt "@[<hov 1>%a@]" Symbol.pp s
5✔
777
    | List _ -> assert false
778
    | App _ -> assert false
UNCOV
779
    | Unop (ty, op, e) ->
×
780
      Fmt.pf fmt "@[<hov 1>(%a@ %a)@]" Ty.Smtlib.pp_unop (ty, op) pp e
781
    | Binop (ty, op, e1, e2) ->
2✔
782
      Fmt.pf fmt "@[<hov 1>(%a@ %a@ %a)@]" Ty.Smtlib.pp_binop (ty, op) pp e1 pp
783
        e2
784
    | Triop _ -> assert false
785
    | Relop (ty, op, e1, e2) ->
3✔
786
      Fmt.pf fmt "@[<hov 1>(%a@ %a@ %a)@]" Ty.Smtlib.pp_relop (ty, op) pp e1 pp
787
        e2
788
    | Cvtop _ -> assert false
789
    | Naryop _ -> assert false
790
    | Extract _ -> assert false
791
    | Concat _ -> assert false
792
    | Binder _ -> assert false
793
end
794

795
let inline_symbol_values map e =
796
  let rec aux e =
2✔
797
    match view e with
2✔
798
    | Val _ | Loc _ -> e
×
799
    | Symbol symbol -> Option.value ~default:e (Symbol.Map.find_opt symbol map)
2✔
800
    | Ptr e ->
×
801
      let offset = aux e.offset in
UNCOV
802
      make @@ Ptr { e with offset }
×
803
    | List vs ->
×
804
      let vs = List.map aux vs in
UNCOV
805
      list vs
×
806
    | App (x, vs) ->
×
807
      let vs = List.map aux vs in
UNCOV
808
      app x vs
×
809
    | Unop (ty, op, v) ->
×
810
      let v = aux v in
UNCOV
811
      unop ty op v
×
812
    | Binop (ty, op, v1, v2) ->
×
813
      let v1 = aux v1 in
814
      let v2 = aux v2 in
×
UNCOV
815
      binop ty op v1 v2
×
816
    | Triop (ty, op, v1, v2, v3) ->
×
817
      let v1 = aux v1 in
818
      let v2 = aux v2 in
×
819
      let v3 = aux v3 in
×
UNCOV
820
      triop ty op v1 v2 v3
×
821
    | Cvtop (ty, op, v) ->
×
822
      let v = aux v in
UNCOV
823
      cvtop ty op v
×
824
    | Relop (ty, op, v1, v2) ->
×
825
      let v1 = aux v1 in
826
      let v2 = aux v2 in
×
UNCOV
827
      relop ty op v1 v2
×
828
    | Naryop (ty, op, vs) ->
×
829
      let vs = List.map aux vs in
UNCOV
830
      naryop ty op vs
×
831
    | Extract (e, high, low) ->
×
832
      let e = aux e in
UNCOV
833
      extract e ~high ~low
×
834
    | Concat (e1, e2) ->
×
835
      let e1 = aux e1 in
836
      let e2 = aux e2 in
×
UNCOV
837
      concat e1 e2
×
838
    | Binder (b, vars, e) ->
×
839
      let e = aux e in
UNCOV
840
      binder b vars e
×
841
  in
842
  aux e
843

844
module Set = struct
845
  include Set.Make (Key)
846

847
  type key = Key.t
848

849
  let hash = Hashtbl.hash
850

NEW
851
  let to_list s = elements s
×
852

853
  let pp fmt v =
UNCOV
854
    Fmt.pf fmt "@[<hov 1>%a@]"
×
855
      (Fmt.iter iter ~sep:(fun fmt () -> Fmt.pf fmt "@;") pp)
×
856
      v
857

858
  let get_symbols (set : t) =
859
    let tbl = Hashtbl.create 64 in
×
860
    let rec symbols hte =
×
861
      match view hte with
×
862
      | Val _ | Loc _ -> ()
×
863
      | Ptr { offset; _ } -> symbols offset
×
864
      | Symbol s -> Hashtbl.replace tbl s ()
×
UNCOV
865
      | List es -> List.iter symbols es
×
866
      | App (_, es) -> List.iter symbols es
×
867
      | Unop (_, _, e1) -> symbols e1
×
UNCOV
868
      | Binop (_, _, e1, e2) ->
×
869
        symbols e1;
870
        symbols e2
×
871
      | Triop (_, _, e1, e2, e3) ->
×
872
        symbols e1;
873
        symbols e2;
×
874
        symbols e3
×
875
      | Relop (_, _, e1, e2) ->
×
876
        symbols e1;
877
        symbols e2
×
UNCOV
878
      | Cvtop (_, _, e) -> symbols e
×
879
      | Naryop (_, _, es) -> List.iter symbols es
×
880
      | Extract (e, _, _) -> symbols e
×
UNCOV
881
      | Concat (e1, e2) ->
×
882
        symbols e1;
UNCOV
883
        symbols e2
×
UNCOV
884
      | Binder (_, vars, e) ->
×
885
        List.iter symbols vars;
UNCOV
886
        symbols e
×
887
    in
888
    iter symbols set;
UNCOV
889
    Hashtbl.fold (fun k () acc -> k :: acc) tbl []
×
890

891
  let map f set =
UNCOV
892
    fold
×
893
      (fun elt set ->
UNCOV
894
        let elt = f elt in
×
895
        add elt set )
×
896
      set empty
897

898
  let inline_symbol_values symbol_map set =
899
    map (inline_symbol_values symbol_map) set
×
900
end
901

902
let rec split_conjunctions (e : t) : Set.t =
903
  match view e with
×
904
  | Binop (Ty_bool, And, e1, e2) ->
×
905
    let s1 = split_conjunctions e1 in
UNCOV
906
    let s2 = split_conjunctions e2 in
×
907
    Set.union s1 s2
×
908
  | _ -> Set.singleton e
×
909

910
let rec split_disjunctions (e : t) : Set.t =
911
  match view e with
×
912
  | Binop (Ty_bool, Or, e1, e2) ->
×
913
    let s1 = split_disjunctions e1 in
UNCOV
914
    let s2 = split_disjunctions e2 in
×
UNCOV
915
    Set.union s1 s2
×
UNCOV
916
  | _ -> 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

© 2025 Coveralls, Inc