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

formalsec / smtml / 555

01 Jul 2026 02:55PM UTC coverage: 44.611% (-1.8%) from 46.445%
555

push

github

filipeom
Use explicit result return in smtml parsing functions

It already bit me in the ass by not treating exceptions in injector.
With explicit types I'm forced to treat them and also it allows me to
use monadic syntax.

17 of 48 new or added lines in 9 files covered. (35.42%)

368 existing lines in 3 files now uncovered.

2384 of 5344 relevant lines covered (44.61%)

38.81 hits per line

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

50.39
/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,959✔
33
    | Val v1, Val v2 -> Value.equal v1 v2
1,010✔
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
427✔
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) ->
40✔
40
      Ty.equal t1 t2 && Ty.Unop.equal op1 op2 && phys_equal e1 e2
40✔
41
    | Binop (t1, op1, e1, e3), Binop (t2, op2, e2, e4) ->
122✔
42
      Ty.equal t1 t2 && Ty.Binop.equal op1 op2 && phys_equal e1 e2
107✔
43
      && phys_equal e3 e4
99✔
44
    | Relop (t1, op1, e1, e3), Relop (t2, op2, e2, e4) ->
56✔
45
      Ty.equal t1 t2 && Ty.Relop.equal op1 op2 && phys_equal e1 e2
49✔
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✔
54
    | Extract (e1, h1, l1), Extract (e2, h2, l2) ->
1✔
55
      phys_equal e1 e2 && h1 = h2 && l1 = l2
1✔
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 _
×
UNCOV
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,306✔
66

67
  let hash (e : expr) : int =
68
    match e with
7,786✔
69
    | Val v -> Value.hash v
3,554✔
70
    | Ptr { base; offset } -> combine (Bitvector.hash base) offset.tag
21✔
71
    | Symbol s -> Symbol.hash s
1,336✔
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) ->
164✔
77
      let h1 = Ty.hash ty in
78
      let h2 = combine h1 (Ty.Unop.hash op) in
164✔
79
      combine h2 e.tag
164✔
80
    | Binop (ty, op, e1, e2) ->
1,479✔
81
      let h = Ty.hash ty in
82
      let h = combine h (Ty.Binop.hash op) in
1,479✔
83
      let h = combine h e1.tag in
1,479✔
84
      combine h e2.tag
1,479✔
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) ->
25✔
105
      let h = e.tag in
106
      let h = combine h hi in
107
      combine h lo
25✔
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