Skip to content

Commit 5e6f31a

Browse files
committed
Do not leak non-abstract names in model
This patch changes the definition of `is_model_term` to align with the definition of a _value term_ in the SMT-LIB standard (value terms are particular ground terms defined in a logic for each sort (see Subsection 5.5.1) in [1].) It is now the responsibility of `Uf.assign_next` and `X.assign_value` to ensure they create value terms for all terms in the model, which involves assigning them new names of `abstract` kind. In particular, we no longer need to create abstract names in the `Uf` module. In order to make sure those names show up as the canonical representative when they exist, they are given a depth of `0`, i.e. less than any other term (except the `true` and `false` constants). Fixes #1213 Fixes #1270 [1]: The SMT-LIB Standard Version 2.7 https://smt-lib.org/papers/smt-lib-reference-v2.7-r2025-07-07.pdf
1 parent e0dfc35 commit 5e6f31a

16 files changed

+97
-86
lines changed

src/lib/reasoners/shostak.ml

Lines changed: 1 addition & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -544,12 +544,6 @@ struct
544544
| _, Ty.Tint
545545
| _, Ty.Treal -> ARITH.assign_value r distincts eq
546546
| _, Ty.Tbitv _ -> BITV.assign_value r distincts eq
547-
| Term t, Ty.Tfarray _ ->
548-
begin
549-
if List.exists (fun (t,_) -> Expr.is_model_term t) eq then None
550-
else
551-
Some (Expr.fresh_name (Expr.type_info t), false)
552-
end
553547

554548
| _, Ty.Tadt _ when not (Options.get_disable_adts()) ->
555549
ADT.assign_value r distincts eq
@@ -575,7 +569,7 @@ struct
575569
| Term t, ty -> (* case disable_adts() handled here *)
576570
if Expr.is_model_term t ||
577571
List.exists (fun (t,_) -> Expr.is_model_term t) eq then None
578-
else Some (Expr.fresh_name ty, false) (* false <-> not a case-split *)
572+
else Some (Expr.mk_abstract ty, false) (* false <-> not a case-split *)
579573
| _ ->
580574
(* There is no model-generation support for the AC symbols yet.
581575
The function [AC.assign_value] always returns [None]. *)

src/lib/reasoners/uf.ml

Lines changed: 24 additions & 60 deletions
Original file line numberDiff line numberDiff line change
@@ -1093,18 +1093,19 @@ let reinit_cache () =
10931093
(****************************************************************************)
10941094

10951095
let model_repr_of_term t env mrepr =
1096-
try ME.find t mrepr, mrepr
1097-
with Not_found ->
1098-
let mk = try ME.find t env.make with Not_found -> assert false in
1099-
let rep, _ = try MapX.find mk env.repr with Not_found -> assert false in
1100-
(* We call this function during the model generation only. At this time,
1101-
we are sure that class representatives are constant semantic values, or
1102-
uninterpreted names. *)
1103-
match X.to_model_term rep with
1104-
| Some v -> v, ME.add t v mrepr
1105-
| None ->
1106-
(* [X.to_model_term] cannot fail on constant semantic values. *)
1107-
assert false
1096+
if E.is_model_term t then t, mrepr else
1097+
try ME.find t mrepr, mrepr
1098+
with Not_found ->
1099+
let mk = try ME.find t env.make with Not_found -> assert false in
1100+
let rep, _ = try MapX.find mk env.repr with Not_found -> assert false in
1101+
(* We call this function during the model generation only. At this time,
1102+
we are sure that class representatives are constant semantic values, or
1103+
uninterpreted names. *)
1104+
match X.to_model_term rep with
1105+
| Some v -> v, ME.add t v mrepr
1106+
| None ->
1107+
(* [X.to_model_term] cannot fail on constant semantic values. *)
1108+
assert false
11081109

11091110
(* A map of expressions to terms, ordered by depth first, and then by
11101111
[Expr.compare] for expressions with same depth. This structure will
@@ -1152,25 +1153,11 @@ module Cache = struct
11521153
E.Table.add arrays_cache t values
11531154
| values ->
11541155
E.Table.replace values i v
1155-
1156-
let get_abstract_for abstracts_cache env (t : E.t) =
1157-
let r, _ = find env t in
1158-
match Shostak.HX.find abstracts_cache r with
1159-
| exception Not_found ->
1160-
let abstract = E.mk_abstract (E.type_info t) in
1161-
Shostak.HX.add abstracts_cache r abstract;
1162-
abstract
1163-
| abstract -> abstract
11641156
end
11651157

11661158
type cache = {
11671159
array_selects: Expr.t E.Table.t E.Table.t;
11681160
(** Stores all the get accesses to array names. *)
1169-
1170-
abstracts: Expr.t Shostak.HX.t;
1171-
(** Stores all the abstract values generated. This cache is necessary
1172-
to ensure we do not generate twice an abstract value for a given
1173-
symbol. *)
11741161
}
11751162

11761163
let is_destructor = function
@@ -1189,9 +1176,8 @@ let is_destructor = function
11891176
Alt-Ergo cannot produces a constant value for them. This function creates
11901177
a new abstract value in this case. *)
11911178
let compute_concrete_model_of_val cache =
1192-
let store_array_select = Cache.store_array_get cache.array_selects
1193-
and get_abstract_for = Cache.get_abstract_for cache.abstracts
1194-
in fun env t ((mdl, mrepr) as acc) ->
1179+
let store_array_select = Cache.store_array_get cache.array_selects in
1180+
fun env t ((mdl, mrepr) as acc) ->
11951181
let { E.f; xs; ty; _ } = E.term_view t in
11961182
(* TODO: We have to filter out destructors here as we don't consider
11971183
pending destructors as solvable theory symbols of the ADT theory.
@@ -1217,41 +1203,23 @@ let compute_concrete_model_of_val cache =
12171203
in
12181204
let ret_rep, mrepr = model_repr_of_term t env mrepr in
12191205
match f, arg_vals, ty with
1220-
| Sy.Name _, [], Ty.Tfarray _ ->
1221-
begin
1222-
match E.Table.find cache.array_selects t with
1223-
| exception Not_found ->
1224-
(* We have to add an abstract array in case there is no
1225-
constraint on its values. *)
1226-
E.Table.add cache.array_selects t (E.Table.create 17);
1227-
acc
1228-
| _ -> acc
1229-
end
1230-
12311206
| Sy.Op Sy.Set, _, _ -> acc
12321207

12331208
| Sy.Op Sy.Get, [a; i], _ ->
12341209
begin
1210+
let a, mrepr = model_repr_of_term a env mrepr in
1211+
let i, mrepr = model_repr_of_term i env mrepr in
12351212
let E.{ f = fa; _ } = E.term_view a in
12361213
match fa with
12371214
| Sy.Name _ ->
12381215
store_array_select a i ret_rep;
1239-
acc
1216+
mdl, mrepr
12401217
| _ ->
12411218
acc
12421219
end
12431220

12441221
| Sy.Name { hs = id; _ }, _, _ ->
1245-
let value =
1246-
match ty with
1247-
| Ty.Text _ ->
1248-
(* We cannot produce a concrete value as the type is abstract.
1249-
In this case, we produce an abstract value with the appropriate
1250-
type. *)
1251-
get_abstract_for env t
1252-
| _ -> ret_rep
1253-
in
1254-
ModelMap.(add (id, arg_tys, ty) arg_vals value mdl), mrepr
1222+
ModelMap.(add (id, arg_tys, ty) arg_vals ret_rep mdl), mrepr
12551223

12561224
| _ ->
12571225
Printer.print_err
@@ -1262,8 +1230,7 @@ let compute_concrete_model_of_val cache =
12621230

12631231
let extract_concrete_model cache =
12641232
let compute_concrete_model_of_val = compute_concrete_model_of_val cache in
1265-
let get_abstract_for = Cache.get_abstract_for cache.abstracts
1266-
in fun ~prop_model ~declared_ids env ->
1233+
fun ~prop_model ~declared_ids env ->
12671234
let terms, suspicious = terms env in
12681235
let model, mrepr =
12691236
MED.fold (fun t _mk acc -> compute_concrete_model_of_val env t acc)
@@ -1273,12 +1240,12 @@ let extract_concrete_model cache =
12731240
E.Table.fold (fun t vals mdl ->
12741241
(* We produce a fresh identifiant for abstract value in order to
12751242
prevent any capture. *)
1276-
let abstract = get_abstract_for env t in
1243+
assert (E.is_model_term t);
12771244
let ty = Expr.type_info t in
12781245
let arr_val =
12791246
E.Table.fold (fun i v arr_val ->
12801247
Expr.ArraysEx.store arr_val i v
1281-
) vals abstract
1248+
) vals t
12821249
in
12831250
let id, is_user =
12841251
let Expr.{ f; _ } = Expr.term_view t in
@@ -1309,8 +1276,5 @@ let extract_concrete_model cache =
13091276
{ Models.propositional = prop_model; model; term_values = mrepr }
13101277

13111278
let extract_concrete_model ~prop_model ~declared_ids =
1312-
let cache : cache = {
1313-
array_selects = E.Table.create 17;
1314-
abstracts = Shostak.HX.create 17;
1315-
}
1316-
in fun env -> extract_concrete_model cache ~prop_model ~declared_ids env
1279+
let cache : cache = { array_selects = E.Table.create 17 } in
1280+
fun env -> extract_concrete_model cache ~prop_model ~declared_ids env

src/lib/structures/expr.ml

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -890,8 +890,15 @@ let mk_trigger ?user:(from_user = false) ?depth ?(hyp = []) content =
890890
let mk_term s l ty =
891891
assert (match s with Sy.Lit _ | Sy.Form _ -> false | _ -> true);
892892
let d = match l with
893-
| [] ->
894-
1 (*no args ? depth = 1 (ie. current app s, ie constant)*)
893+
| [] -> (
894+
(* no args ? depth = 1 (ie. current app s, ie constant)
895+
896+
abstract constants must be smaller than other terms
897+
so that they end up in models. *)
898+
match s with
899+
| Name { ns = Abstract; _ } -> 0 (* 3rd smallest depth *)
900+
| _ -> 1
901+
)
895902
| _ ->
896903
(* if args, d is 1 + max_depth of args (equals at least to 1 *)
897904
1 + List.fold_left (fun z t -> max z t.depth) 1 l
@@ -1077,7 +1084,8 @@ let rec is_model_term e =
10771084
| Op Div, [{ f = Real _; _ }; { f = Real _; _ }] -> true
10781085
| Op Minus, [{ f = Real q; _ }; { f = Real _; _ }] -> Q.equal q Q.zero
10791086
| Op Minus, [{ f = Int i; _ }; { f = Int _; _ }] -> Z.equal i Z.zero
1080-
| (True | False | Name _ | Int _ | Real _ | Bitv _), [] -> true
1087+
| Name { ns = Abstract; _ }, [] -> true
1088+
| (True | False | Int _ | Real _ | Bitv _), [] -> true
10811089
| _ -> false
10821090

10831091
let[@inline always] is_value_term e =

src/lib/structures/expr.mli

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -437,7 +437,10 @@ val is_model_term : t -> bool
437437
A model term can be:
438438
- A constructor application involving only model terms,
439439
- A literal of a basic type (integer, real, boolean, unit or bitvector),
440-
- A name. *)
440+
- An abstract name, representing an abstract value (following SMT-LIB
441+
terminology).
442+
443+
The definition of model term maps to value terms in the SMT-LIB standard. *)
441444

442445
val save_cache: unit -> unit
443446
(** Saves the modules cache *)

tests/cram.t/run.t

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,8 +11,8 @@ appropriate here.
1111
(
1212
(define-fun x () Int 0)
1313
(define-fun y () Int 0)
14-
(define-fun a1 () (Array Int Int) (store (as @a4 (Array Int Int)) 0 0))
15-
(define-fun a2 () (Array Int Int) (as @a0 (Array Int Int)))
14+
(define-fun a1 () (Array Int Int) (store (as @a0 (Array Int Int)) 0 0))
15+
(define-fun a2 () (Array Int Int) (as @a1 (Array Int Int)))
1616
)
1717

1818
Now we will test some semantic triggers.

tests/issues/1212.default.expected

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11

22
unknown
33
(
4-
(define-fun a () (Array Int Int) (as @a3 (Array Int Int)))
5-
(define-fun b () (Array Int Int) (as @a2 (Array Int Int)))
4+
(define-fun a () (Array Int Int) (as @a2 (Array Int Int)))
5+
(define-fun b () (Array Int Int) (as @a0 (Array Int Int)))
66
)

tests/issues/1213.default.expected

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
2+
unknown
3+
(
4+
(define-fun a () (List T) nil)
5+
(define-fun b () (List T) nil)
6+
(define-fun c () (List T) (cons (as @a0 T) nil))
7+
(define-fun d () (List T) nil)
8+
)

tests/issues/1213.default.smt2

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
(set-option :produce-models true)
2+
(set-logic ALL)
3+
(declare-datatypes ((List 1)) (
4+
(par (T) (
5+
(nil)
6+
(cons (head T) (tail (List T)))
7+
))
8+
))
9+
(declare-sort T 0)
10+
(declare-const a (List T))
11+
(declare-const b (List T))
12+
(declare-const c (List T))
13+
(declare-const d (List T))
14+
(assert (= (tail a) b))
15+
(assert ((_ is cons) c))
16+
(assert (= (tail c) d))
17+
(check-sat)
18+
(get-model)

tests/issues/1270.default.expected

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
2+
unknown
3+
(
4+
(define-fun q ((arg_0 t)) Int
5+
(ite (= arg_0 (as @a1 t)) 1 (ite (= arg_0 (as @a0 t)) 0 (- 1))))
6+
(define-fun o ((arg_0 Int)) t
7+
(ite (= arg_0 0) (as @a0 t) (ite (= arg_0 1) (as @a1 t) (as @a2 t))))
8+
)

tests/issues/1270.default.smt2

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
(set-logic ALL)
2+
(set-option :produce-models true)
3+
(declare-sort t 0)
4+
(declare-fun q (t) Int)
5+
(declare-fun o (Int) t)
6+
(assert (forall ((i Int)) (or (> 0 0) (= i (q (o i))))))
7+
(check-sat)
8+
(get-model)

0 commit comments

Comments
 (0)