def __init__(self, ctx: SynthCtx, state: [EVar], assumptions: [Exp], q: Query, k, hints: [Exp] = [], freebies: [Exp] = [], ops: [Op] = [], funcs: {str: TFunc} = {}): assert all( v in state for v in free_vars(q) ), "Oops, query looks malformed due to {}:\n{}\nfree_vars({})".format( [v for v in free_vars(q) if v not in state], pprint(q), repr(q)) super().__init__() self.ctx = ctx self.state = state self.assumptions = assumptions q = shallow_copy(q) q.ret = wrap_naked_statevars(q.ret, OrderedSet(state)) self.q = q self.hints = hints self.freebies = freebies self.ops = ops self.k = k self.funcs = OrderedDict(funcs)
def codegen(self, e: Exp, concretization_functions: {str: Exp}, out: EVar) -> Stm: """Return statements that write the result of `e` to `out`. The returned statements must declare the variable `out`; it will not be declared by the caller. This function also requires the `concretization_functions` that describe the invariants for variables in `e`. """ if isinstance(e, EMakeMinTreeMultiset) or isinstance( e, EMakeMaxTreeMultiset): assert out.type == self.rep_type(e.type) extended_concretization_functions = dict(concretization_functions) extended_concretization_functions[out.id] = e dummy_out = EVar(out.id).with_type(e.type) return seq([ SDecl(out, None), self.implement_stmt(SCall(dummy_out, "add_all", (e.e, )), extended_concretization_functions) ]) elif isinstance(e, ETreeMultisetElems): elem_type = e.type.elem_type x = fresh_var(elem_type, "x") from cozy.syntax_tools import shallow_copy xs = shallow_copy(e.e).replace_type(e.type) return seq([ SDecl(out, EEmptyList().with_type(out.type)), SForEach(x, xs, SCall(out, "add", (x, ))) ]) elif isinstance(e, ETreeMultisetPeek): return SDecl(out, e) else: raise NotImplementedError(e)
def visit_SDecl(self, s): var = s.var assert isinstance(var, EVar) if not hasattr(var, "type"): # This should not be necessary, but it guards against sloppiness # elsewhere. var = shallow_copy(var).with_type(s.val.type) return self.declare(var, s.val)
def add_implicit_handle_assumptions(spec : Spec) -> Spec: """ At the start of every method, for all reachable handles (i.e. those stored on the data structure plus those in arguments): If two different handles have the same address, then they have the same value. """ spec = shallow_copy(spec) new_methods = [] for m in spec.methods: handles = reachable_handles_at_method(spec, m) new_assumptions = implicit_handle_assumptions_for_method(handles, m) m = shallow_copy(m) m.assumptions = list(m.assumptions) + new_assumptions new_methods.append(m) spec.methods = new_methods return spec
def _add_subquery(self, sub_q : Query, used_by : Stm) -> Stm: with task("adding query", query=sub_q.name): sub_q = shallow_copy(sub_q) with task("checking whether we need more handle assumptions"): new_a = implicit_handle_assumptions_for_method( reachable_handles_at_method(self.spec, sub_q), sub_q) if not valid(EImplies(EAll(sub_q.assumptions), EAll(new_a))): event("we do!") sub_q.assumptions = list(itertools.chain(sub_q.assumptions, new_a)) with task("simplifying"): orig_a = sub_q.assumptions orig_a_size = sum(a.size() for a in sub_q.assumptions) orig_ret_size = sub_q.ret.size() sub_q.assumptions = tuple(simplify_or_ignore(a) for a in sub_q.assumptions) sub_q.ret = simplify(sub_q.ret) a_size = sum(a.size() for a in sub_q.assumptions) ret_size = sub_q.ret.size() event("|assumptions|: {} -> {}".format(orig_a_size, a_size)) event("|ret|: {} -> {}".format(orig_ret_size, ret_size)) if a_size > orig_a_size: print("NO, BAD SIMPLIFICATION") print("original") for a in orig_a: print(" - {}".format(pprint(a))) print("simplified") for a in sub_q.assumptions: print(" - {}".format(pprint(a))) assert False state_vars = self.abstract_state funcs = self.extern_funcs qq = find_one(self.query_specs, lambda qq: dedup_queries.value and queries_equivalent(qq, sub_q, state_vars=state_vars, extern_funcs=funcs)) if qq is not None: event("subgoal {} is equivalent to {}".format(sub_q.name, qq.name)) arg_reorder = [[x[0] for x in sub_q.args].index(a) for (a, t) in qq.args] class Repl(BottomUpRewriter): def visit_ECall(self, e): args = tuple(self.visit(a) for a in e.args) if e.func == sub_q.name: args = tuple(args[idx] for idx in arg_reorder) return ECall(qq.name, args).with_type(e.type) else: return ECall(e.func, args).with_type(e.type) used_by = Repl().visit(used_by) else: self.add_query(sub_q) return used_by
def implement_add(self, target, args): assert target.type == self target = shallow_copy(target).with_type(self.rep_type()) new_elem, = args return seq([ SAssign( EGetField(new_elem, self.next_ptr).with_type(self.t), target), SAssign( EGetField(new_elem, self.prev_ptr).with_type(self.t), self.null), SIf( ENot(equal(target, self.null)), SAssign( EGetField(target, self.prev_ptr).with_type(self.t), new_elem), SNoOp()), SAssign(target, new_elem) ])
def construct_concrete(self, e: Exp, out: Exp): assert out.type == self, "{} : {}".format(pprint(e), pprint(e.type)) out = shallow_copy(out).with_type(self.rep_type()) assert isinstance(e, EMakeMap2) # TODO? k = fresh_var(self.k, "k") return seq([ SAssign( EVectorGet(out, ENum(i).with_type(INT)).with_type(self.v), construct_value(self.v)) for (i, k) in enumerate(self.all_keys) ] + [ SForEach( k, e.e, SAssign( EVectorGet(out, k).with_type(self.v), ELet(k, e.value).with_type(self.v))) ])
def for_each(self, id, iter, body): assert iter.type == self # assert isinstance(iter, EVar), pprint(iter) iter = shallow_copy(iter).with_type(self.rep_type()) assert id.type == self.t next = fresh_name("next") return seq([ SDecl(id.id, iter), SWhile( ENot(equal(id, self.null)), seq([ SDecl(next, EGetField(id, self.next_ptr).with_type(id.type)), body, SAssign(id, EVar(next).with_type(id.type)) ])) ])
def __init__(self, ctx: SynthCtx, state: [EVar], assumptions: [Exp], q: Query, k, hints: [Exp] = [], examples: [dict] = None): super().__init__() self.ctx = ctx self.state = state self.assumptions = assumptions self.q = shallow_copy(q) assert all( v in state for v in free_vars(q) ), "Oops, query looks malformed due to {}:\n{}\nfree_vars({})".format( [v for v in free_vars(q) if v not in state], pprint(q), repr(q)) q.ret = wrap_naked_statevars(q.ret, OrderedSet(state)) self.hints = hints self.examples = examples self.k = k
def implement_remove(self, target, args): assert target.type == self target = shallow_copy(target).with_type(self.rep_type()) elem, = args prev = EGetField(elem, self.prev_ptr).with_type(self.t) next = EGetField(elem, self.next_ptr).with_type(self.t) return seq([ SIf(equal(elem, target), SAssign(target, next), SNoOp()), SIf( ENot(equal(prev, self.null)), SAssign( EGetField(prev, self.next_ptr).with_type(self.t), next), SNoOp()), SIf( ENot(equal(next, self.null)), SAssign( EGetField(next, self.prev_ptr).with_type(self.t), prev), SNoOp()), SAssign(next, self.null), SAssign(prev, self.null) ])
def __init__(self, state: [EVar], assumptions: [Exp], q: Query, context: Context, solutions_q, hints: [Exp] = [], freebies: [Exp] = [], ops: [Op] = [], improve_count=None): super().__init__() self.state = state self.assumptions = assumptions q = shallow_copy(q) q.ret = wrap_naked_statevars(q.ret, OrderedSet(state)) self.q = q self.context = context self.hints = hints self.freebies = freebies self.ops = ops self.solutions_q = solutions_q self.improve_count = improve_count
def construct_concrete(self, e: Exp, out: Exp): print(pprint(e)) if self == out.type: out = shallow_copy(out).with_type(self.rep_type()) x = fresh_var(self.t, "x") return seq([ SAssign(out, self.null), SForEach( x, e, seq([ SAssign( EGetField(x, self.next_ptr).with_type(self.t), out), SAssign( EGetField(x, self.prev_ptr).with_type(self.t), self.null), SIf( ENot(equal(out, self.null)), SAssign( EGetField(out, self.prev_ptr).with_type(self.t), x), SNoOp()), SAssign(out, x) ])) ])
def _add_subquery(self, sub_q: Query, used_by: Stm) -> Stm: print("Adding new query {}...".format(sub_q.name)) # orig_ret = sub_q.ret # print("rewritng ret for {}".format(pprint(orig_ret))) sub_q = shallow_copy(sub_q) sub_q.assumptions += tuple( implicit_handle_assumptions_for_method( reachable_handles_at_method(self.spec, sub_q), sub_q)) sub_q.ret = simplify(sub_q.ret) # sub_q = rewrite_ret(sub_q, simplify) # if sub_q.ret != orig_ret: # print("rewrote ret") # print(" --> {}".format(pprint(sub_q.ret))) qq = find_one( self.query_specs, lambda qq: dedup_queries.value and queries_equivalent(qq, sub_q)) if qq is not None: print("########### subgoal {} is equivalent to {}".format( sub_q.name, qq.name)) arg_reorder = [[x[0] for x in sub_q.args].index(a) for (a, t) in qq.args] class Repl(BottomUpRewriter): def visit_ECall(self, e): args = tuple(self.visit(a) for a in e.args) if e.func == sub_q.name: args = tuple(args[idx] for idx in arg_reorder) return ECall(qq.name, args).with_type(e.type) else: return ECall(e.func, args).with_type(e.type) used_by = Repl().visit(used_by) else: self.add_query(sub_q) return used_by
def _add_subquery(self, sub_q: Query, used_by: Stm) -> Stm: """Add a query that helps maintain some other state. Parameters: sub_q - the specification of the helper query used_by - the statement that calls `sub_q` If a query already exists that is equivalent to `sub_q`, this method returns `used_by` rewritten to use the existing query and does not add the query to the implementation. Otherwise it returns `used_by` unchanged. """ with task("adding query", query=sub_q.name): sub_q = shallow_copy(sub_q) with task("checking whether we need more handle assumptions"): new_a = implicit_handle_assumptions( reachable_handles_at_method(self.spec, sub_q)) if not valid(EImplies(EAll(sub_q.assumptions), EAll(new_a))): event("we do!") sub_q.assumptions = list( itertools.chain(sub_q.assumptions, new_a)) with task("repairing state var boundaries"): extra_available_state = [ e for v, e in self._concretization_functions ] sub_q.ret = repair_well_formedness( strip_EStateVar(sub_q.ret), self.context_for_method(sub_q), extra_available_state) with task("simplifying"): orig_a = sub_q.assumptions orig_a_size = sum(a.size() for a in sub_q.assumptions) orig_ret_size = sub_q.ret.size() sub_q.assumptions = tuple( simplify_or_ignore(a) for a in sub_q.assumptions) sub_q.ret = simplify(sub_q.ret) a_size = sum(a.size() for a in sub_q.assumptions) ret_size = sub_q.ret.size() event("|assumptions|: {} -> {}".format(orig_a_size, a_size)) event("|ret|: {} -> {}".format(orig_ret_size, ret_size)) if a_size > orig_a_size: print("NO, BAD SIMPLIFICATION") print("original") for a in orig_a: print(" - {}".format(pprint(a))) print("simplified") for a in sub_q.assumptions: print(" - {}".format(pprint(a))) assert False state_vars = self.abstract_state funcs = self.extern_funcs qq = find_one( self.query_specs, lambda qq: dedup_queries.value and queries_equivalent(qq, sub_q, state_vars=state_vars, extern_funcs=funcs, assumptions=EAll(self.abstract_invariants))) if qq is not None: event("subgoal {} is equivalent to {}".format( sub_q.name, qq.name)) arg_reorder = [[x[0] for x in sub_q.args].index(a) for (a, t) in qq.args] class Repl(BottomUpRewriter): def visit_ECall(self, e): args = tuple(self.visit(a) for a in e.args) if e.func == sub_q.name: args = tuple(args[idx] for idx in arg_reorder) return ECall(qq.name, args).with_type(e.type) else: return ECall(e.func, args).with_type(e.type) used_by = Repl().visit(used_by) else: self.add_query(sub_q) return used_by
def find_one(self, target): print("finding one {} : {}".format(pprint(target), pprint(target.type))) assert target.type == self target = shallow_copy(target).with_type(self.rep_type()) return target