/*++ Module Name: dl_mk_array_instantiation.h Abstract: Transforms predicates so that array invariants can be discovered. Motivation : Given a predicate P(a), no quantifier-free solution can express that P(a) <=> forall i, P(a[i]) = 0 Solution : Introduce a fresh variable i, and transform P(a) into P!inst(i, a). Now, (P!inst(i,a) := a[i] = 0) <=> P(a) := forall i, a[i] = 0. Transformation on Horn rules: P(a, args) /\ phi(a, args, args') => P'(args') (for simplicity, assume there are no arrays in args'). Is transformed into: (/\_r in read_indices(phi) P!inst(r, a, args)) /\ phi(a, args, args') => P'(args') Limitations : This technique can only discover invariants on arrays that depend on one quantifier. Related work : Techniques relying on adding quantifiers and eliminating them. See dl_mk_quantifier_abstraction and dl_mk_quantifier_instantiation Implementation: The implementation follows the solution suggested above, with more options. The addition of options implies that in the simple case described above, we in fact have P(a) transformed into P(i, a[i], a). 1) Dealing with multiple quantifiers -> The options fixedpoint.xform.instantiate_arrays.nb_quantifier gives the number of quantifiers per array. 2) Enforcing the instantiation -> We suggest an option (enforce_instantiation) to enforce this abstraction. This transforms P(a) into P(i, a[i]). This enforces the solver to limit the space search at the cost of imprecise results. This option corresponds to fixedpoint.xform.instantiate_arrays.enforce 3) Adding slices in the mix -> We wish to have the possibility to further restrict the search space: we want to smash cells, given a smashing rule. For example, in for loops j=0; j P'(...) is transformed into (/\_r in read_indices(phi) P!inst(id_r, a[r], a) /\ GetId(r) = id_r) /\ phi(a, ...) => P'(...). Note : when no slicing is done, GetId(i) = i. This option corresponds to fixedpoint.xform.instantiate_arrays.slice_technique Although we described GetId as returning integers, there is no reason to restrict the type of ids to integers. A more direct method, for the 0<=i > selects; expr_equiv_class eq_classes; unsigned cnt;//Index for new variables obj_map done_selects; expr_ref_vector ownership; //Helper functions void instantiate_rule(const rule& r, rule_set & dest);//Instantiates the rule void retrieve_selects(expr* e);//Retrieves all selects (fills the selects and eq_classes members) expr_ref rewrite_select(expr*array, expr*select);//Rewrites select(a, args) to select(array, args) expr_ref_vector retrieve_all_selects(expr*array);//Retrieves all selects linked to a given array (using eq classes and selects) expr_ref_vector instantiate_pred(app*old_pred);//Returns all the instantiation of a given predicate expr_ref create_pred(app*old_pred, expr_ref_vector& new_args);//Creates a predicate expr_ref create_head(app* old_head);//Creates the new head var * mk_select_var(expr* select); /*Given the old predicate, and the new arguments for the new predicate, returns the new setId arguments. By default getId(P(x, y, a, b), (x, y, a[i], a[j], a, b[k], b[l], b)) (nb_quantifier=2, enforce=false) returns (i,j,k,l) So that the final created predicate is P!inst(x, y, a[i], a[j], a, b[k], b[l], b, i, j, k, l) */ expr_ref_vector getId(app*old_pred, const expr_ref_vector& new_args); public: mk_array_instantiation(context & ctx, unsigned priority); rule_set * operator()(rule_set const & source) override; ~mk_array_instantiation() override{} }; }; #endif /* DL_MK_ARRAY_INSTANTIATION_H_ */