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

formalsec / smtml / 556

01 Jul 2026 03:06PM UTC coverage: 44.573% (-0.04%) from 44.611%
556

push

github

filipeom
Finish migration to Yojson.Safe.t (#470)

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

2 existing lines in 1 file now uncovered.

2382 of 5344 relevant lines covered (44.57%)

38.82 hits per line

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

50.0
/src/smtml/expr.ml
1
(* SPDX-License-Identifier: MIT *)
2
(* Copyright (C) 2023-2026 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
  | Symbol of Symbol.t
14
  | List of t list
15
  | App of Symbol.t * t list
16
  | Unop of Ty.t * Ty.Unop.t * t
17
  | Binop of Ty.t * Ty.Binop.t * t * t
18
  | Triop of Ty.t * Ty.Triop.t * t * t * t
19
  | Relop of Ty.t * Ty.Relop.t * t * t
20
  | Cvtop of Ty.t * Ty.Cvtop.t * t
21
  | Naryop of Ty.t * Ty.Naryop.t * t list
22
  | Extract of t * int * int
23
  | Concat of t * t
24
  | Binder of Binder.t * t list * t
25

26
module Expr = struct
27
  type t = expr
28

29
  let list_eq (l1 : 'a list) (l2 : 'a list) : bool = List.equal phys_equal l1 l2
6✔
30

31
  let equal (e1 : expr) (e2 : expr) : bool =
32
    match (e1, e2) with
1,979✔
33
    | Val v1, Val v2 -> Value.equal v1 v2
1,013✔
34
    | Ptr { base = b1; offset = o1 }, Ptr { base = b2; offset = o2 } ->
5✔
35
      Bitvector.equal b1 b2 && phys_equal o1 o2
5✔
36
    | Symbol s1, Symbol s2 -> Symbol.equal s1 s2
429✔
37
    | List l1, List l2 -> list_eq l1 l2
4✔
38
    | App (s1, l1), App (s2, l2) -> Symbol.equal s1 s2 && list_eq l1 l2
×
39
    | Unop (t1, op1, e1), Unop (t2, op2, e2) ->
41✔
40
      Ty.equal t1 t2 && Ty.Unop.equal op1 op2 && phys_equal e1 e2
41✔
41
    | Binop (t1, op1, e1, e3), Binop (t2, op2, e2, e4) ->
124✔
42
      Ty.equal t1 t2 && Ty.Binop.equal op1 op2 && phys_equal e1 e2
109✔
43
      && phys_equal e3 e4
100✔
44
    | Relop (t1, op1, e1, e3), Relop (t2, op2, e2, e4) ->
57✔
45
      Ty.equal t1 t2 && Ty.Relop.equal op1 op2 && phys_equal e1 e2
48✔
46
      && phys_equal e3 e4
40✔
47
    | Triop (t1, op1, e1, e3, e5), Triop (t2, op2, e2, e4, e6) ->
4✔
48
      Ty.equal t1 t2 && Ty.Triop.equal op1 op2 && phys_equal e1 e2
4✔
49
      && phys_equal e3 e4 && phys_equal e5 e6
4✔
50
    | Cvtop (t1, op1, e1), Cvtop (t2, op2, e2) ->
25✔
51
      Ty.equal t1 t2 && Ty.Cvtop.equal op1 op2 && phys_equal e1 e2
25✔
52
    | Naryop (t1, op1, l1), Naryop (t2, op2, l2) ->
2✔
53
      Ty.equal t1 t2 && Ty.Naryop.equal op1 op2 && list_eq l1 l2
2✔
UNCOV
54
    | Extract (e1, h1, l1), Extract (e2, h2, l2) ->
×
UNCOV
55
      phys_equal e1 e2 && h1 = h2 && l1 = l2
×
56
    | Concat (e1, e3), Concat (e2, e4) -> phys_equal e1 e2 && phys_equal e3 e4
×
57
    | Binder (binder1, vars1, e1), Binder (binder2, vars2, e2) ->
×
58
      Binder.equal binder1 binder2 && list_eq vars1 vars2 && phys_equal e1 e2
×
59
    | ( ( Val _ | Ptr _ | Symbol _ | List _ | App _ | Unop _ | Binop _ | Triop _
×
60
        | Relop _ | Cvtop _ | Naryop _ | Extract _ | Concat _ | Binder _ )
×
61
      , _ ) ->
62
      false
63

64
  (* Optimized mixer (DJB2 variant). Inlines to simple arithmetic. *)
65
  let[@inline] combine h v = (h * 33) + v
8,303✔
66

67
  let hash (e : expr) : int =
68
    match e with
7,782✔
69
    | Val v -> Value.hash v
3,553✔
70
    | Ptr { base; offset } -> combine (Bitvector.hash base) offset.tag
21✔
71
    | Symbol s -> Symbol.hash s
1,334✔
72
    | List l -> List.fold_left (fun acc x -> combine acc x.Hc.tag) 0 l
16✔
73
    | App (s, es) ->
40✔
74
      let h_s = Symbol.hash s in
75
      List.fold_left (fun acc x -> combine acc x.Hc.tag) h_s es
40✔
76
    | Unop (ty, op, e) ->
163✔
77
      let h1 = Ty.hash ty in
78
      let h2 = combine h1 (Ty.Unop.hash op) in
163✔
79
      combine h2 e.tag
163✔
80
    | Binop (ty, op, e1, e2) ->
1,478✔
81
      let h = Ty.hash ty in
82
      let h = combine h (Ty.Binop.hash op) in
1,478✔
83
      let h = combine h e1.tag in
1,478✔
84
      combine h e2.tag
1,478✔
85
    | Triop (ty, op, e1, e2, e3) ->
44✔
86
      let h = Ty.hash ty in
87
      let h = combine h (Ty.Triop.hash op) in
44✔
88
      let h = combine h e1.tag in
44✔
89
      let h = combine h e2.tag in
44✔
90
      combine h e3.tag
44✔
91
    | Relop (ty, op, e1, e2) ->
980✔
92
      let h = Ty.hash ty in
93
      let h = combine h (Ty.Relop.hash op) in
980✔
94
      let h = combine h e1.tag in
980✔
95
      combine h e2.tag
980✔
96
    | Cvtop (ty, op, e) ->
77✔
97
      let h = Ty.hash ty in
98
      let h = combine h (Ty.Cvtop.hash op) in
77✔
99
      combine h e.tag
77✔
100
    | Naryop (ty, op, es) ->
14✔
101
      let h = Ty.hash ty in
102
      let h = combine h (Ty.Naryop.hash op) in
14✔
103
      List.fold_left (fun acc x -> combine acc x.Hc.tag) h es
14✔
104
    | Extract (e, hi, lo) ->
26✔
105
      let h = e.tag in
106
      let h = combine h hi in
107
      combine h lo
26✔
108
    | Concat (e1, e2) -> combine e1.tag e2.tag
8✔
109
    | Binder (b, vars, e) ->
28✔
110
      let h = Binder.hash b in
111
      let h_vars = List.fold_left (fun acc x -> combine acc x.Hc.tag) h vars in
28✔
112
      combine h_vars e.tag
28✔
113
end
114

115
module Hc = Hc.Make_strong [@inlined hint] (Expr)
116

117
let equal (hte1 : t) (hte2 : t) = phys_equal hte1 hte2 [@@inline]
251✔
118

119
let hash (hte : t) = hte.tag [@@inline]
10✔
120

121
module Key = struct
122
  type nonrec t = t
123

124
  let to_int hte = hash hte
6✔
125

126
  let compare x y = compare (to_int x) (to_int y)
3✔
127
end
128

129
let[@inline] make e = Hc.hashcons e
4,703✔
130

131
let[@inline] view (hte : t) = hte.node
7,550✔
132

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

135
let symbol s = make (Symbol s)
874✔
136

137
(** The return type of an expression *)
138
let rec ty (hte : t) : Ty.t =
139
  match view hte with
310✔
140
  | Val x -> Value.type_of x
58✔
141
  | Ptr _ -> Ty_bitv 32
×
142
  | Symbol x -> Symbol.type_of x
174✔
143
  | List _ -> Ty_list
×
144
  | App (sym, _) ->
×
145
    begin match sym.ty with Ty_none -> Ty_app | ty -> ty
×
146
    end
147
  | Triop (_, Ite, _, hte1, hte2) ->
×
148
    let ty1 = ty hte1 in
149
    assert (
×
150
      let ty2 = ty hte2 in
151
      Ty.equal ty1 ty2 );
×
152
    ty1
153
  | Cvtop (_, (Zero_extend m | Sign_extend m), hte) -> (
×
154
    match ty hte with Ty_bitv n -> Ty_bitv (n + m) | _ -> assert false )
1✔
155
  | Unop (ty, _, _)
21✔
156
  | Binop (ty, _, _, _)
35✔
157
  | Triop (ty, _, _, _, _)
×
158
  | Relop (ty, _, _, _)
11✔
159
  | Cvtop (ty, _, _)
2✔
160
  | Naryop (ty, _, _) ->
1✔
161
    ty
162
  | Extract (_, h, l) -> Ty_bitv (h - l + 1)
7✔
163
  | Concat (e1, e2) -> (
×
164
    match (ty e1, ty e2) with
×
165
    | Ty_bitv n1, Ty_bitv n2 -> Ty_bitv (n1 + n2)
×
166
    | t1, t2 ->
×
167
      Fmt.failwith "Invalid concat of (%a) with (%a)" Ty.pp t1 Ty.pp t2 )
168
  | Binder (_, _, e) -> ty e
×
169

170
let rec is_symbolic (v : t) : bool =
171
  match view v with
×
172
  | Val _ -> false
×
173
  | Symbol _ -> true
×
174
  | Ptr { offset; _ } -> is_symbolic offset
×
175
  | Unop (_, _, v) | Cvtop (_, _, v) | Extract (v, _, _) | Binder (_, _, v) ->
×
176
    is_symbolic v
177
  | Binop (_, _, v1, v2) | Relop (_, _, v1, v2) | Concat (v1, v2) ->
×
178
    is_symbolic v1 || is_symbolic v2
×
179
  | Triop (_, _, v1, v2, v3) ->
×
180
    is_symbolic v1 || is_symbolic v2 || is_symbolic v3
×
181
  | List vs | App (_, vs) | Naryop (_, _, vs) -> List.exists is_symbolic vs
×
182

183
let rec get_symbols_aux_loop k acc = function
184
  | [] -> k acc
10✔
185
  | hd :: tl ->
19✔
186
    get_symbols_aux (fun acc -> get_symbols_aux_loop k acc tl) acc hd
19✔
187

188
and get_symbols_aux k acc (hte : t) =
189
  match view hte with
19✔
190
  | Val _ -> k acc
3✔
191
  | Ptr { offset; _ } -> get_symbols_aux k acc offset
×
192
  | Symbol s -> k (s :: acc)
9✔
193
  | App (s, es) -> get_symbols_aux_loop k (s :: acc) es
×
194
  | List es | Naryop (_, _, es) -> get_symbols_aux_loop k acc es
×
195
  | Unop (_, _, e) | Cvtop (_, _, e) | Extract (e, _, _) ->
×
196
    get_symbols_aux k acc e
197
  | Binop (_, _, e1, e2) | Relop (_, _, e1, e2) | Concat (e1, e2) ->
×
198
    get_symbols_aux_loop k acc [ e1; e2 ]
199
  | Triop (_, _, e1, e2, e3) -> get_symbols_aux_loop k acc [ e1; e2; e3 ]
×
200
  | Binder (_, vars, e) -> get_symbols_aux_loop k acc (e :: vars)
×
201

202
let get_symbols (hte : t list) =
203
  get_symbols_aux_loop Fun.id [] hte |> List.sort_uniq Symbol.compare
3✔
204

205
let rec pp_with ~printer fmt (hte : t) =
206
  match view hte with
6✔
207
  | Val v -> Value.pp_with ~printer fmt v
×
208
  | Ptr { base; offset } ->
×
209
    Fmt.pf fmt "(Ptr %a %a)"
210
      (Bitvector.pp_with ~printer)
211
      base (pp_with ~printer) offset
212
  | Symbol s -> Fmt.pf fmt "@[<hov 1>%a@]" Symbol.pp s
4✔
213
  | List v ->
×
214
    Fmt.pf fmt "@[<hov 1>[%a]@]" (Fmt.list ~sep:Fmt.comma (pp_with ~printer)) v
×
215
  | App (s, v) ->
×
216
    Fmt.pf fmt "@[<hov 1>(%a@ %a)@]" Symbol.pp s
217
      (Fmt.list ~sep:Fmt.comma (pp_with ~printer))
×
218
      v
219
  | Unop (ty, op, e) ->
×
220
    Fmt.pf fmt "@[<hov 1>(%a.%a@ %a)@]" Ty.pp ty Ty.Unop.pp op
221
      (pp_with ~printer) e
222
  | Binop (ty, op, e1, e2) ->
2✔
223
    Fmt.pf fmt "@[<hov 1>(%a.%a@ %a@ %a)@]" Ty.pp ty Ty.Binop.pp op
224
      (pp_with ~printer) e1 (pp_with ~printer) e2
225
  | Triop (ty, op, e1, e2, e3) ->
×
226
    Fmt.pf fmt "@[<hov 1>(%a.%a@ %a@ %a@ %a)@]" Ty.pp ty Ty.Triop.pp op
227
      (pp_with ~printer) e1 (pp_with ~printer) e2 (pp_with ~printer) e3
228
  | Relop (ty, op, e1, e2) ->
×
229
    Fmt.pf fmt "@[<hov 1>(%a.%a@ %a@ %a)@]" Ty.pp ty Ty.Relop.pp op
230
      (pp_with ~printer) e1 (pp_with ~printer) e2
231
  | Cvtop (ty, op, e) ->
×
232
    Fmt.pf fmt "@[<hov 1>(%a.%a@ %a)@]" Ty.pp ty Ty.Cvtop.pp op
233
      (pp_with ~printer) e
234
  | Naryop (ty, op, es) ->
×
235
    Fmt.pf fmt "@[<hov 1>(%a.%a@ %a)@]" Ty.pp ty Ty.Naryop.pp op
236
      (Fmt.list ~sep:Fmt.sp (pp_with ~printer))
×
237
      es
238
  | Extract (e, h, l) ->
×
239
    Fmt.pf fmt "@[<hov 1>(extract@ %a@ %d@ %d)@]" (pp_with ~printer) e l h
240
  | Concat (e1, e2) ->
×
241
    Fmt.pf fmt "@[<hov 1>(++@ %a@ %a)@]" (pp_with ~printer) e1
242
      (pp_with ~printer) e2
243
  | Binder (b, vars, e) ->
×
244
    Fmt.pf fmt "@[<hov 1>(%a@ (%a)@ %a)@]" Binder.pp b
245
      (Fmt.list ~sep:Fmt.sp (pp_with ~printer))
×
246
      vars (pp_with ~printer) e
247

248
let pp fmt e = pp_with ~printer:Without_type fmt e
×
249

250
let pp_safe fmt e = pp_with ~printer:With_type_and_hexa_float fmt e
2✔
251

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

254
module Printer = struct
255
  let pp_symbols fmt syms =
256
    Fmt.list ~sep:Fmt.cut
2✔
257
      (fun fmt sym ->
258
        let t = Symbol.type_of sym in
4✔
259
        Fmt.pf fmt "(let-const %a %a)" Symbol.pp sym Ty.pp t )
4✔
260
      fmt syms
261

262
  let pp_expr fmt e =
263
    let syms = get_symbols [ e ] in
1✔
264
    if List.length syms > 0 then Fmt.pf fmt "@[<v>%a@]@\n" pp_symbols syms;
1✔
265
    pp_safe fmt e
1✔
266

267
  let pp_query fmt es =
268
    let pp_asserts fmt es =
1✔
269
      Fmt.list ~sep:Fmt.cut
1✔
270
        (fun fmt e -> Fmt.pf fmt "(assert @[<h 2>%a@])" pp_safe e)
1✔
271
        fmt es
272
    in
273
    let syms = get_symbols es in
274
    if List.length syms > 0 then Fmt.pf fmt "@[<v>%a@]@\n" pp_symbols syms;
1✔
275
    if List.length es > 0 then Fmt.pf fmt "@[<v>%a@]@\n" pp_asserts es;
1✔
276
    Fmt.string fmt "(check-sat)"
1✔
277
end
278

279
let pp_smtml = Printer.pp_query
280

281
let to_string e = Fmt.str "%a" pp e
×
282

283
let value (v : Value.t) : t = make (Val v) [@@inline]
2,271✔
284

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

287
let list l = make (List l)
5✔
288

289
let app symbol args = make (App (symbol, args))
20✔
290

291
let[@inline] binder bt vars expr = make (Binder (bt, vars, expr))
14✔
292

293
let let_in vars body = binder Let_in vars body
8✔
294

295
let forall vars body = binder Forall vars body
2✔
296

297
let exists vars body = binder Exists vars body
1✔
298

299
let raw_unop ty op hte = make (Unop (ty, op, hte)) [@@inline]
102✔
300

301
let negate_relop (hte : t) : t =
302
  let e =
6✔
303
    match view hte with
304
    | Relop (ty, Eq, e1, e2) -> Relop (ty, Ne, e1, e2)
4✔
305
    | Relop (ty, Ne, e1, e2) -> Relop (ty, Eq, e1, e2)
×
306
    | Relop (ty, Lt, e1, e2) -> Relop (ty, Le, e2, e1)
2✔
307
    | Relop (ty, LtU, e1, e2) -> Relop (ty, LeU, e2, e1)
×
308
    | Relop (ty, Le, e1, e2) -> Relop (ty, Lt, e2, e1)
×
309
    | Relop (ty, LeU, e1, e2) -> Relop (ty, LtU, e2, e1)
×
310
    | _ -> Fmt.failwith "negate_relop: not a relop."
×
311
  in
312
  make e
313

314
let rec unop ty op hte =
315
  match (op, view hte) with
118✔
316
  | Ty.Unop.(Regexp_loop _ | Regexp_star), _ -> raw_unop ty op hte
×
317
  | _, Val v -> value (Eval.unop ty op v)
27✔
318
  | Not, Unop (_, Not, hte') -> hte'
2✔
319
  | Neg, Unop (_, Neg, hte') -> hte'
1✔
320
  | Not, Binop (inner_ty, Or, a, b) ->
3✔
321
    make (Binop (inner_ty, And, unop ty Not a, unop ty Not b))
3✔
322
  | Not, Relop (Ty_fp _, _, _, _) -> raw_unop ty op hte
2✔
323
  | Not, Relop (_, _, _, _) -> negate_relop hte
6✔
324
  | Trim, Cvtop (Ty_real, ToString, _) -> hte
×
325
  | Head, List (hd :: _) -> hd
1✔
326
  | Tail, List (_ :: tl) -> make (List tl)
1✔
327
  | Reverse, List es -> make (List (List.rev es))
2✔
328
  | Length, List es -> value (Int (List.length es))
1✔
329
  | _ -> raw_unop ty op hte
72✔
330

331
let raw_binop ty op hte1 hte2 = make (Binop (ty, op, hte1, hte2)) [@@inline]
786✔
332

333
let rec binop ty op hte1 hte2 =
334
  match (op, view hte1, view hte2) with
809✔
335
  | Ty.Binop.(String_in_re | Regexp_range), _, _ -> raw_binop ty op hte1 hte2
×
336
  | op, Val v1, Val v2 -> value (Eval.binop ty op v1 v2)
70✔
337
  | Sub, Ptr { base = b1; offset = os1 }, Ptr { base = b2; offset = os2 } ->
1✔
338
    if Bitvector.equal b1 b2 then binop ty Sub os1 os2
1✔
339
    else raw_binop ty op hte1 hte2
×
340
  | Add, Ptr { base; offset }, _ ->
2✔
341
    let m = Bitvector.numbits base in
342
    make (Ptr { base; offset = binop (Ty_bitv m) Add offset hte2 })
2✔
343
  | Sub, Ptr { base; offset }, _ ->
1✔
344
    let m = Bitvector.numbits base in
345
    make (Ptr { base; offset = binop (Ty_bitv m) Sub offset hte2 })
1✔
346
  | Rem, Ptr { base; offset }, _ ->
1✔
347
    let m = Bitvector.numbits base in
348
    let rhs = value (Bitv base) in
1✔
349
    let addr = binop (Ty_bitv m) Add rhs offset in
1✔
350
    binop ty Rem addr hte2
1✔
351
  | Add, _, Ptr { base; offset } ->
1✔
352
    let m = Bitvector.numbits base in
353
    make (Ptr { base; offset = binop (Ty_bitv m) Add offset hte1 })
1✔
354
  | Sub, _, Ptr { base; offset } ->
×
355
    let m = Bitvector.numbits base in
356
    let base = value (Bitv base) in
×
357
    binop ty Sub hte1 (binop (Ty_bitv m) Add base offset)
×
358
  | (Add | Or), Val (Bitv bv), _ when Bitvector.eqz bv -> hte2
×
359
  | (And | Div | DivU | Mul | Rem | RemU), Val (Bitv bv), _
×
360
    when Bitvector.eqz bv ->
4✔
361
    hte1
1✔
362
  | (Add | Or), _, Val (Bitv bv) when Bitvector.eqz bv -> hte1
×
363
  | Add, _, Val (Real -0.) -> hte1
×
364
  | Add, Val (Real -0.), _ -> hte2
×
365
  | Add, _, Val (Num (F32 -0l)) -> hte1
1✔
366
  | Add, Val (Num (F32 -0l)), _ -> hte2
×
367
  | Add, _, Val (Num (F64 -0L)) -> hte1
×
368
  | Add, Val (Num (F64 -0L)), _ -> hte2
×
369
  | (And | Mul), _, Val (Bitv bv) when Bitvector.eqz bv -> hte2
1✔
370
  | And, Val True, _ -> hte2
×
371
  | And, _, Val True -> hte1
1✔
372
  | And, Val False, _ -> hte1
1✔
373
  | And, _, Val False -> hte2
×
374
  | Or, Val True, _ -> hte1
×
375
  | Or, _, Val True -> hte2
1✔
376
  | Or, Val False, _ -> hte2
×
377
  | Or, _, Val False -> hte1
1✔
378
  | Add, Binop (ty, Add, x, { node = Val v1; _ }), Val v2 ->
1✔
379
    let v = value (Eval.binop ty Add v1 v2) in
1✔
380
    raw_binop ty Add x v
1✔
381
  | Sub, Binop (ty, Sub, x, { node = Val v1; _ }), Val v2 ->
1✔
382
    let v = value (Eval.binop ty Add v1 v2) in
1✔
383
    raw_binop ty Sub x v
1✔
384
  | Mul, Val (Bitv bv), _ when Bitvector.eq_one bv -> hte2
×
385
  | Mul, _, Val (Bitv bv) when Bitvector.eq_one bv -> hte1
×
386
  | Mul, Binop (ty, Mul, x, { node = Val v1; _ }), Val v2 ->
1✔
387
    let v = value (Eval.binop ty Mul v1 v2) in
1✔
388
    raw_binop ty Mul x v
1✔
389
  | Add, Val v1, Binop (ty, Add, x, { node = Val v2; _ }) ->
1✔
390
    let v = value (Eval.binop ty Add v1 v2) in
1✔
391
    raw_binop ty Add v x
1✔
392
  | Mul, Val v1, Binop (ty, Mul, x, { node = Val v2; _ }) ->
1✔
393
    let v = value (Eval.binop ty Mul v1 v2) in
1✔
394
    raw_binop ty Mul v x
1✔
395
  | At, List es, Val (Int n) ->
1✔
396
    (* TODO: use another datastructure? *)
397
    begin match List.nth_opt es n with None -> assert false | Some v -> v
1✔
398
    end
399
  | (String_contains | String_prefix | String_suffix), _, Val (Str "") ->
1✔
400
    value True
401
  | List_cons, _, List es -> make (List (hte1 :: es))
1✔
402
  | List_append, List _, (List [] | Val (List [])) -> hte1
×
403
  | List_append, (List [] | Val (List [])), List _ -> hte2
×
404
  | List_append, List l0, Val (List l1) -> make (List (l0 @ List.map value l1))
1✔
405
  | List_append, Val (List l0), List l1 -> make (List (List.map value l0 @ l1))
×
406
  | List_append, List l0, List l1 -> make (List (l0 @ l1))
×
407
  | _ -> raw_binop ty op hte1 hte2
713✔
408

409
let raw_triop ty op e1 e2 e3 = make (Triop (ty, op, e1, e2, e3)) [@@inline]
24✔
410

411
let triop ty op e1 e2 e3 =
412
  match (op, view e1, view e2, view e3) with
31✔
413
  | Ty.Triop.Ite, Val True, _, _ -> e2
1✔
414
  | Ite, Val False, _, _ -> e3
1✔
415
  | Ite, _, _, _ when equal e2 e3 -> e2
1✔
416
  | op, Val v1, Val v2, Val v3 -> value (Eval.triop ty op v1 v2 v3)
4✔
417
  | Ite, _, Triop (_, Ite, c2, r1, r2), Triop (_, Ite, _, _, _) ->
×
418
    let else_ = raw_triop ty Ite e1 r2 e3 in
419
    let cond = binop Ty_bool And e1 c2 in
×
420
    raw_triop ty Ite cond r1 else_
×
421
  | _ -> raw_triop ty op e1 e2 e3
24✔
422

423
let raw_relop ty op hte1 hte2 = make (Relop (ty, op, hte1, hte2)) [@@inline]
504✔
424

425
let rec relop ty (op : Ty.Relop.t) hte1 hte2 =
426
  let both_phys_eq = phys_equal hte1 hte2 in
523✔
427
  let can_be_shortcuted =
523✔
428
    match ty with
429
    | Ty.Ty_bool | Ty_bitv _ | Ty_int | Ty_unit -> both_phys_eq
×
430
    | Ty_fp _ | Ty_app | Ty_list | Ty_real | Ty_regexp | Ty_roundingMode
×
431
    | Ty_none | Ty_str ->
×
432
      false
433
  in
434
  match (op, view hte1, view hte2) with
523✔
435
  | (Eq | Le | LeU), _, _ when can_be_shortcuted -> value True
6✔
436
  | (Ne | Lt | LtU), _, _ when can_be_shortcuted -> value False
6✔
437
  | op, Val v1, Val v2 -> value (if Eval.relop ty op v1 v2 then True else False)
21✔
438
  | Ne, Val (Real v), _ | Ne, _, Val (Real v) ->
×
439
    if Float.is_nan v || Float.is_infinite v then value True
×
440
    else if both_phys_eq then value False
×
441
    else raw_relop ty op hte1 hte2
×
442
  | _, Val (Real v), _ | _, _, Val (Real v) ->
×
443
    if Float.is_nan v || Float.is_infinite v then value False
×
444
    else
445
      (* TODO: it is possible to add a shortcut when `both_phys_eq` *)
446
      raw_relop ty op hte1 hte2
4✔
447
  | Eq, _, Val Nothing | Eq, Val Nothing, _ -> value False
×
448
  | Ne, _, Val Nothing | Ne, Val Nothing, _ -> value True
×
449
  | Eq, _, Val (App (`Op "symbol", [ Str _ ]))
×
450
  | Eq, Val (App (`Op "symbol", [ Str _ ])), _ ->
×
451
    value False
452
  | Ne, _, Val (App (`Op "symbol", [ Str _ ]))
×
453
  | Ne, Val (App (`Op "symbol", [ Str _ ])), _ ->
×
454
    value True
455
  | ( Eq
×
456
    , Symbol ({ ty = Ty_fp prec1; _ } as s1)
457
    , Symbol ({ ty = Ty_fp prec2; _ } as s2) )
458
    when both_phys_eq || (prec1 = prec2 && Symbol.equal s1 s2) ->
×
459
    raw_unop Ty_bool Not (raw_unop (Ty_fp prec1) Is_nan hte1)
×
460
  | Eq, Ptr { base = b1; offset = os1 }, Ptr { base = b2; offset = os2 } ->
1✔
461
    if both_phys_eq then value True
×
462
    else if Bitvector.equal b1 b2 then relop Ty_bool Eq os1 os2
×
463
    else value False
1✔
464
  | Ne, Ptr { base = b1; offset = os1 }, Ptr { base = b2; offset = os2 } ->
1✔
465
    if both_phys_eq then value False
×
466
    else if Bitvector.equal b1 b2 then relop Ty_bool Ne os1 os2
×
467
    else value True
1✔
468
  | ( (LtU | LeU)
1✔
469
    , Ptr { base = b1; offset = os1 }
470
    , Ptr { base = b2; offset = os2 } ) ->
471
    if both_phys_eq then value True
×
472
    else if Bitvector.equal b1 b2 then relop ty op os1 os2
×
473
    else
474
      let b1 = Value.Bitv b1 in
2✔
475
      let b2 = Value.Bitv b2 in
476
      value (if Eval.relop ty op b1 b2 then True else False)
1✔
477
  | ( op
2✔
478
    , Val (Bitv _ as n)
479
    , Ptr { base; offset = { node = Val (Bitv _ as o); _ } } ) ->
480
    let base = Eval.binop (Ty_bitv 32) Add (Bitv base) o in
481
    value (if Eval.relop ty op n base then True else False)
×
482
  | op, Ptr { base; offset = { node = Val (Bitv _ as o); _ } }, Val (Bitv _ as n)
2✔
483
    ->
484
    let base = Eval.binop (Ty_bitv 32) Add (Bitv base) o in
485
    value (if Eval.relop ty op base n then True else False)
×
486
  | op, List l1, List l2 -> relop_list op l1 l2
×
487
  | _, _, _ -> raw_relop ty op hte1 hte2
424✔
488

489
and relop_list op l1 l2 =
490
  match (op, l1, l2) with
×
491
  | Eq, [], [] -> value True
×
492
  | Eq, _, [] | Eq, [], _ -> value False
×
493
  | Eq, l1, l2 ->
×
494
    if not (List.compare_lengths l1 l2 = 0) then value False
×
495
    else
496
      List.fold_left2
×
497
        (fun acc a b ->
498
          binop Ty_bool And acc
×
499
          @@
500
          match (ty a, ty b) with
×
501
          | Ty_real, Ty_real -> relop Ty_real Eq a b
×
502
          | _ -> relop Ty_bool Eq a b )
×
503
        (value True) l1 l2
×
504
  | Ne, _, _ -> unop Ty_bool Not @@ relop_list Eq l1 l2
×
505
  | (Lt | LtU | Le | LeU), _, _ -> assert false
506

507
let raw_cvtop ty op hte = make (Cvtop (ty, op, hte)) [@@inline]
51✔
508

509
let rec cvtop theory op hte =
510
  match (op, view hte) with
75✔
511
  | Ty.Cvtop.String_to_re, _ -> raw_cvtop theory op hte
×
512
  | _, Val v -> value (Eval.cvtop theory op v)
32✔
513
  | ToBool, Cvtop (_, OfBool, hte) -> hte
1✔
514
  | OfBool, Cvtop (_, ToBool, hte) -> hte
×
515
  | String_to_float, Cvtop (Ty_real, ToString, hte) -> hte
×
516
  | Reinterpret_float, Cvtop (Ty_real, Reinterpret_int, e1) -> e1
×
517
  | Reinterpret_float, Cvtop (Ty_fp n, Reinterpret_int, e) ->
×
518
    assert (match theory with Ty_bitv m -> n = m | _ -> false);
×
519
    e
520
  | Reinterpret_int, Cvtop (Ty_int, Reinterpret_float, e1) -> e1
×
521
  | Reinterpret_int, Cvtop (Ty_bitv m, Reinterpret_float, e) ->
9✔
522
    assert (match theory with Ty_fp n -> n = m | _ -> false);
×
523
    e
524
  | Zero_extend n, Ptr { base; offset } ->
1✔
525
    let offset = cvtop theory op offset in
526
    make (Ptr { base = Bitvector.zero_extend n base; offset })
1✔
527
  | WrapI64, Ptr { base; offset } ->
1✔
528
    let offset = cvtop theory op offset in
529
    make (Ptr { base = Bitvector.extract base ~high:31 ~low:0; offset })
1✔
530
  | WrapI64, Cvtop (Ty_bitv 64, Zero_extend 32, hte) ->
×
531
    assert (Ty.equal theory (ty hte) && Ty.equal theory (Ty_bitv 32));
×
532
    hte
533
  | ToString, Cvtop (_, OfString, e1) -> e1
1✔
534
  | OfString, Cvtop (_, ToString, e1) -> e1
×
535
  | PromoteF32, Cvtop (_, DemoteF64, e1) -> e1
×
536
  | DemoteF64, Cvtop (_, PromoteF32, e1) -> e1
×
537
  | Zero_extend 0, _ -> hte
1✔
538
  | Sign_extend 0, _ -> hte
×
539
  | String_from_code, Cvtop (_, String_to_code, e1) -> e1
3✔
540
  | String_to_code, Cvtop (_, String_from_code, e1) -> e1
1✔
541
  | _ -> raw_cvtop theory op hte
25✔
542

543
let raw_naryop ty op es = make (Naryop (ty, op, es)) [@@inline]
8✔
544

545
let naryop ty op hexps =
546
  (* Get list of value or exit early *)
547
  let rec extract_values acc = function
8✔
548
    | [] -> Some (List.rev acc)
7✔
549
    | hexp :: remaining ->
19✔
550
      begin match view hexp with
551
      | Val v -> extract_values (v :: acc) remaining
18✔
552
      | _ -> None
1✔
553
      end
554
  in
555

556
  match extract_values [] hexps with
557
  | Some vlist -> value (Eval.naryop ty op vlist)
7✔
558
  | None ->
1✔
559
    begin match (ty, op) with
560
    | Ty_str, Concat ->
×
561
      let rec concat_exprs acc = function
562
        | [] -> List.rev acc
×
563
        | hexp :: remainig ->
×
564
          let acc =
565
            match view hexp with
566
            | Naryop (Ty_str, Concat, inner_hexps) ->
×
567
              List.rev_append inner_hexps acc
×
568
            | _ -> hexp :: acc
×
569
          in
570
          concat_exprs acc remainig
571
      in
572
      raw_naryop ty op (concat_exprs [] hexps)
×
573
    | _ -> raw_naryop ty op hexps
1✔
574
    end
575

576
let[@inline] raw_extract (hte : t) ~(high : int) ~(low : int) : t =
577
  make (Extract (hte, high, low))
13✔
578

579
let extract (hte : t) ~(high : int) ~(low : int) : t =
580
  match (view hte, high, low) with
36✔
581
  | Val (Bitv bv), high, low -> value (Bitv (Bitvector.extract bv ~high ~low))
26✔
582
  | ( Cvtop
2✔
583
        ( _
584
        , (Zero_extend 24 | Sign_extend 24)
1✔
585
        , ({ node = Symbol { ty = Ty_bitv 8; _ }; _ } as sym) )
586
    , 7
587
    , 0 ) ->
588
    sym
589
  | Concat (_, e), h, l when l = 0 && Ty.bitsize (ty e) = h - l + 1 -> e
2✔
590
  | Concat (e, _), 63, 32 when Ty.bitsize (ty e) = 32 -> e
×
591
  | _ ->
6✔
592
    if high - low + 1 = Ty.bitsize (ty hte) then hte
×
593
    else raw_extract hte ~high ~low
6✔
594

595
let raw_concat (msb : t) (lsb : t) : t = make (Concat (msb, lsb)) [@@inline]
4✔
596

597
(* TODO: don't rebuild so many values it generates unecessary hc lookups *)
598
let rec concat (msb : t) (lsb : t) : t =
599
  match (view msb, view lsb) with
7✔
600
  | Val (Bitv a), Val (Bitv b) -> value (Bitv (Bitvector.concat a b))
1✔
601
  | Val (Bitv _), Concat (({ node = Val (Bitv _); _ } as b), se) ->
×
602
    raw_concat (concat msb b) se
×
603
  | Extract (s1, h, m1), Extract (s2, m2, l) when equal s1 s2 && m1 = m2 + 1 ->
3✔
604
    if h - l + 1 = Ty.bitsize (ty s1) then s1 else raw_extract s1 ~high:h ~low:l
1✔
605
  | Extract (_, _, _), Concat (({ node = Extract (_, _, _); _ } as e2), e3) ->
×
606
    raw_concat (concat msb e2) e3
×
607
  | _ -> raw_concat msb lsb
3✔
608

609
let rec simplify_expr ?(in_relop = false) (hte : t) : t =
4✔
610
  match view hte with
16✔
611
  | Val _ | Symbol _ -> hte
4✔
612
  | Ptr { base; offset } ->
×
613
    let offset = simplify_expr ~in_relop offset in
614
    if not in_relop then make (Ptr { base; offset })
×
615
    else binop (Ty_bitv 32) Add (value (Bitv base)) offset
×
616
  | List es -> make @@ List (List.map (simplify_expr ~in_relop) es)
×
617
  | App (x, es) -> make @@ App (x, List.map (simplify_expr ~in_relop) es)
×
618
  | Unop (ty, op, e) ->
×
619
    let e = simplify_expr ~in_relop e in
620
    unop ty op e
×
621
  | Binop (ty, op, e1, e2) ->
6✔
622
    let e1 = simplify_expr ~in_relop e1 in
623
    let e2 = simplify_expr ~in_relop e2 in
6✔
624
    binop ty op e1 e2
6✔
625
  | Relop (ty, op, e1, e2) ->
×
626
    let e1 = simplify_expr ~in_relop:true e1 in
627
    let e2 = simplify_expr ~in_relop:true e2 in
×
628
    relop ty op e1 e2
×
629
  | Triop (ty, op, c, e1, e2) ->
×
630
    let c = simplify_expr ~in_relop c in
631
    let e1 = simplify_expr ~in_relop e1 in
×
632
    let e2 = simplify_expr ~in_relop e2 in
×
633
    triop ty op c e1 e2
×
634
  | Cvtop (ty, op, e) ->
×
635
    let e = simplify_expr ~in_relop e in
636
    cvtop ty op e
×
637
  | Naryop (ty, op, es) ->
×
638
    let es = List.map (simplify_expr ~in_relop) es in
639
    naryop ty op es
×
640
  | Extract (s, high, low) ->
×
641
    let s = simplify_expr ~in_relop s in
642
    extract s ~high ~low
×
643
  | Concat (e1, e2) ->
×
644
    let msb = simplify_expr ~in_relop e1 in
645
    let lsb = simplify_expr ~in_relop e2 in
×
646
    concat msb lsb
×
647
  | Binder _ ->
×
648
    (* Not simplifying anything atm *)
649
    hte
650

651
module Cache = Hashtbl.Make (struct
652
  type nonrec t = t
653

654
  let hash = hash
655

656
  let equal = equal
657
end)
658

659
let simplify =
660
  (* TODO: it may make sense to share the cache with simplify_expr ? *)
661
  let cache = Cache.create 512 in
662
  fun e ->
50✔
663
    match Cache.find_opt cache e with
2✔
664
    | Some simplified -> simplified
×
665
    | None ->
2✔
666
      let rec loop x =
667
        let x' = simplify_expr x in
4✔
668
        if equal x x' then begin
2✔
669
          Cache.add cache e x';
670
          x'
2✔
671
        end
672
        else loop x'
2✔
673
      in
674
      loop e
675

676
module Bool = struct
677
  open Ty
678

679
  let of_val = function
680
    | Val True -> Some true
×
681
    | Val False -> Some false
×
682
    | _ -> None
126✔
683

684
  let true_ = value True
50✔
685

686
  let false_ = value False
50✔
687

688
  let to_val b = if b then true_ else false_
×
689

690
  let v b = to_val b [@@inline]
×
691

692
  let not b =
693
    let bexpr = view b in
×
694
    match of_val bexpr with
×
695
    | Some b -> to_val (not b)
×
696
    | None -> (
×
697
      match bexpr with
698
      | Unop (Ty_bool, Not, cond) -> cond
×
699
      | _ -> unop Ty_bool Not b )
×
700

701
  let equal b1 b2 =
702
    match (view b1, view b2) with
×
703
    | Val True, Val True | Val False, Val False -> true_
×
704
    | _ -> relop Ty_bool Eq b1 b2
×
705

706
  let distinct b1 b2 =
707
    match (view b1, view b2) with
×
708
    | Val True, Val False | Val False, Val True -> true_
×
709
    | _ -> relop Ty_bool Ne b1 b2
×
710

711
  let and_ b1 b2 =
712
    match (of_val (view b1), of_val (view b2)) with
63✔
713
    | Some true, _ -> b2
×
714
    | _, Some true -> b1
×
715
    | Some false, _ | _, Some false -> false_
×
716
    | _ -> binop Ty_bool And b1 b2
63✔
717

718
  let or_ b1 b2 =
719
    match (of_val (view b1), of_val (view b2)) with
×
720
    | Some false, _ -> b2
×
721
    | _, Some false -> b1
×
722
    | Some true, _ | _, Some true -> true_
×
723
    | _ -> binop Ty_bool Or b1 b2
×
724

725
  let implies a b = binop Ty_bool Implies a b
×
726

727
  let ite c r1 r2 = triop Ty_bool Ite c r1 r2
×
728
end
729

730
module Smtlib = struct
731
  let rec pp fmt (hte : t) =
732
    match view hte with
13✔
733
    | Val v -> Value.Smtlib.pp fmt v
3✔
734
    | Ptr _ -> assert false
735
    | Symbol s -> Fmt.pf fmt "@[<hov 1>%a@]" Symbol.pp s
5✔
736
    | List _ -> assert false
737
    | App _ -> assert false
738
    | Unop (ty, op, e) ->
×
739
      Fmt.pf fmt "@[<hov 1>(%a@ %a)@]" Ty.Smtlib.pp_unop (ty, op) pp e
740
    | Binop (ty, op, e1, e2) ->
2✔
741
      Fmt.pf fmt "@[<hov 1>(%a@ %a@ %a)@]" Ty.Smtlib.pp_binop (ty, op) pp e1 pp
742
        e2
743
    | Triop _ -> assert false
744
    | Relop (ty, op, e1, e2) ->
3✔
745
      Fmt.pf fmt "@[<hov 1>(%a@ %a@ %a)@]" Ty.Smtlib.pp_relop (ty, op) pp e1 pp
746
        e2
747
    | Cvtop _ -> assert false
748
    | Naryop _ -> assert false
749
    | Extract _ -> assert false
750
    | Concat _ -> assert false
751
    | Binder _ -> assert false
752
end
753

754
let inline_symbol_values map e =
755
  let rec aux e =
2✔
756
    match view e with
2✔
757
    | Val _ -> e
×
758
    | Symbol symbol ->
2✔
759
      begin match Symbol.Map.find_opt symbol map with
760
      | None -> e
1✔
761
      | Some v -> value v
1✔
762
      end
763
    | Ptr e ->
×
764
      let offset = aux e.offset in
765
      make @@ Ptr { e with offset }
×
766
    | List vs ->
×
767
      let vs = List.map aux vs in
768
      list vs
×
769
    | App (x, vs) ->
×
770
      let vs = List.map aux vs in
771
      app x vs
×
772
    | Unop (ty, op, v) ->
×
773
      let v = aux v in
774
      unop ty op v
×
775
    | Binop (ty, op, v1, v2) ->
×
776
      let v1 = aux v1 in
777
      let v2 = aux v2 in
×
778
      binop ty op v1 v2
×
779
    | Triop (ty, op, v1, v2, v3) ->
×
780
      let v1 = aux v1 in
781
      let v2 = aux v2 in
×
782
      let v3 = aux v3 in
×
783
      triop ty op v1 v2 v3
×
784
    | Cvtop (ty, op, v) ->
×
785
      let v = aux v in
786
      cvtop ty op v
×
787
    | Relop (ty, op, v1, v2) ->
×
788
      let v1 = aux v1 in
789
      let v2 = aux v2 in
×
790
      relop ty op v1 v2
×
791
    | Naryop (ty, op, vs) ->
×
792
      let vs = List.map aux vs in
793
      naryop ty op vs
×
794
    | Extract (e, high, low) ->
×
795
      let e = aux e in
796
      extract e ~high ~low
×
797
    | Concat (e1, e2) ->
×
798
      let e1 = aux e1 in
799
      let e2 = aux e2 in
×
800
      concat e1 e2
×
801
    | Binder (b, vars, e) ->
×
802
      let e = aux e in
803
      binder b vars e
×
804
  in
805
  aux e
806

807
module Set = struct
808
  include Set.Make (Key)
809

810
  type key = Key.t
811

812
  let hash = Hashtbl.hash
813

814
  let to_list s = elements s
2✔
815

816
  let pp fmt v =
817
    Fmt.pf fmt "@[<hov 1>%a@]"
×
818
      (Fmt.iter iter ~sep:(fun fmt () -> Fmt.pf fmt "@;") pp)
×
819
      v
820

821
  let get_symbols (set : t) =
822
    fold (fun x acc -> get_symbols_aux Fun.id acc x) set []
×
823
    |> List.sort_uniq Symbol.compare
×
824

825
  let map f set =
826
    fold
×
827
      (fun elt set ->
828
        let elt = f elt in
×
829
        add elt set )
×
830
      set empty
831

832
  let inline_symbol_values symbol_map set =
833
    map (inline_symbol_values symbol_map) set
×
834
end
835

836
let rec split_conjunctions (e : t) : Set.t =
837
  match view e with
×
838
  | Binop (Ty_bool, And, e1, e2) ->
×
839
    let s1 = split_conjunctions e1 in
840
    let s2 = split_conjunctions e2 in
×
841
    Set.union s1 s2
×
842
  | _ -> Set.singleton e
×
843

844
let rec split_disjunctions (e : t) : Set.t =
845
  match view e with
×
846
  | Binop (Ty_bool, Or, e1, e2) ->
×
847
    let s1 = split_disjunctions e1 in
848
    let s2 = split_disjunctions e2 in
×
849
    Set.union s1 s2
×
850
  | _ -> 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