def emit_action_gen(header, impl, name, action, classname): global indent_level caname = varname(name) upd = action.update(im.module, None) pre = tr.reverse_image(ilu.true_clauses(), ilu.true_clauses(), upd) pre_clauses = ilu.trim_clauses(pre) pre_clauses = ilu.and_clauses( pre_clauses, ilu.Clauses([df.to_constraint() for df in im.module.concepts])) pre = pre_clauses.to_formula() syms = [ x for x in ilu.used_symbols_ast(pre) if x.name not in il.sig.symbols ] header.append("class " + caname + "_gen : public gen {\n public:\n") for sym in syms: if not sym.name.startswith('__ts') and sym not in pre_clauses.defidx: declare_symbol(header, sym) header.append(" {}_gen();\n".format(caname)) impl.append(caname + "_gen::" + caname + "_gen(){\n") indent_level += 1 emit_sig(impl) for sym in syms: emit_decl(impl, sym) indent(impl) impl.append('add("(assert {})");\n'.format( slv.formula_to_z3(pre).sexpr().replace('\n', '\\\n'))) indent_level -= 1 impl.append("}\n") header.append(" bool generate(" + classname + "&);\n};\n") impl.append("bool " + caname + "_gen::generate(" + classname + "& obj) {\n push();\n") indent_level += 1 pre_used = ilu.used_symbols_ast(pre) for sym in all_state_symbols(): if sym in pre_used and sym not in pre_clauses.defidx: # skip symbols not used in constraint if slv.solver_name(sym) != None: # skip interpreted symbols global is_derived if sym not in is_derived: emit_set(impl, sym) for sym in syms: if not sym.name.startswith('__ts') and sym not in pre_clauses.defidx: emit_randomize(impl, sym) impl.append(""" bool res = solve(); if (res) { """) indent_level += 1 for sym in syms: if not sym.name.startswith('__ts') and sym not in pre_clauses.defidx: emit_eval(impl, sym) indent_level -= 2 impl.append(""" } pop(); obj.___ivy_gen = this; return res; } """)
def emit_action_gen(header,impl,name,action,classname): global indent_level caname = varname(name) upd = action.update(im.module,None) pre = tr.reverse_image(ilu.true_clauses(),ilu.true_clauses(),upd) pre_clauses = ilu.trim_clauses(pre) pre_clauses = ilu.and_clauses(pre_clauses,ilu.Clauses([df.to_constraint() for df in im.module.concepts])) pre = pre_clauses.to_formula() syms = [x for x in ilu.used_symbols_ast(pre) if x.name not in il.sig.symbols] header.append("class " + caname + "_gen : public gen {\n public:\n") for sym in syms: if not sym.name.startswith('__ts') and sym not in pre_clauses.defidx: declare_symbol(header,sym) header.append(" {}_gen();\n".format(caname)) impl.append(caname + "_gen::" + caname + "_gen(){\n"); indent_level += 1 emit_sig(impl) for sym in syms: emit_decl(impl,sym) indent(impl) impl.append('add("(assert {})");\n'.format(slv.formula_to_z3(pre).sexpr().replace('\n','\\\n'))) indent_level -= 1 impl.append("}\n"); header.append(" bool generate(" + classname + "&);\n};\n"); impl.append("bool " + caname + "_gen::generate(" + classname + "& obj) {\n push();\n") indent_level += 1 pre_used = ilu.used_symbols_ast(pre) for sym in all_state_symbols(): if sym in pre_used and sym not in pre_clauses.defidx: # skip symbols not used in constraint if slv.solver_name(sym) != None: # skip interpreted symbols global is_derived if sym not in is_derived: emit_set(impl,sym) for sym in syms: if not sym.name.startswith('__ts') and sym not in pre_clauses.defidx: emit_randomize(impl,sym) impl.append(""" bool res = solve(); if (res) { """) indent_level += 1 for sym in syms: if not sym.name.startswith('__ts') and sym not in pre_clauses.defidx: emit_eval(impl,sym) indent_level -= 2 impl.append(""" } pop(); obj.___ivy_gen = this; return res; } """)
def __init__(self, axioms, definitions, schemata=None): """ A proof checker starts with sets of axioms, definitions and schemata - axioms is a list of ivy_ast.LabeledFormula - definitions is a list of ivy_ast.LabeledFormula - schemata is a map from string names to ivy_ast.LabeledFormula The schemata argument is optional and is included for backward compatibility with ivy_mc. """ self.axioms = [normalize_goal(ax) for ax in axioms] self.definitions = dict( (d.formula.defines().name, normalize_goal(d)) for d in definitions) self.schemata = dict( (x, normalize_goal(y)) for x, y in schemata.iteritems()) if schemata is not None else dict() for ax in axioms: if ax.label is not None: self.schemata[ax.name] = ax self.stale = set() # set of symbols that are not fresh for lf in axioms + definitions: self.stale.update(lu.used_symbols_ast(lf.formula)) for goal in schemata.values(): vocab = goal_vocab(goal) self.stale.update(vocab.symbols)
def mine_constants(mod,trans,invariant): res = defaultdict(list) for c in ilu.used_symbols_ast(invariant): if not il.is_function_sort(c.sort) and tr.is_skolem(c): res[c.sort].append(c) # iu.dbg('res') return res
def show_sym(v,decd,val): if all(x in inv_env or not my_is_skolem(x) and not tr.is_new(x) and x not in env for x in ilu.used_symbols_ast(decd)): expr = ilu.rename_ast(decd,inv_env) if not (expr in self.current and self.current[expr] == val): print ' {} = {}'.format(expr,val) self.current[expr] = val
def fmla_vocab(fmla): """ Get the free vocabulary of a formula, including sorts, symbols and variables """ things = lu.used_sorts_ast(fmla) things.update(lu.used_symbols_ast(fmla)) things.update(lu.used_variables_ast(fmla)) return things
def prev_expr(stvarset,expr,sort_constants): if any(sym in stvarset or tr.is_skolem(sym) and not sym in sort_constants[sym.sort] for sym in ilu.symbols_ast(expr)): return None news = [sym for sym in ilu.used_symbols_ast(expr) if tr.is_new(sym)] if news: rn = dict((sym,tr.new_of(sym)) for sym in news) return ilu.rename_ast(expr,rn) return None
def clauses_model_to_diagram(clauses1,ignore = None, implied = None,model = None,axioms=None,weaken=True,numerals=True): """ Return a diagram of a model of clauses1 or None. The function "ignore", if provided, returns true for symbols that should be ignored in the diagram. """ # print "clauses_model_to_diagram clauses1 = {}".format(clauses1) if axioms == None: axioms = true_clauses() h = model_if_none(and_clauses(clauses1,axioms),implied,model) ignore = ignore if ignore != None else lambda x: False res = model_facts(h,(lambda x: False),clauses1,upclose=True) # why not pass axioms? # print "clauses_model_to_diagram res = {}".format(res) # find representative elements # find representatives of universe elements if numerals: reps = numeral_assign(res,h) else: reps = dict() for c in used_constants_clauses(clauses1): # print "constant: {}".format(c) mc = get_model_constant(h.model,ivy_logic.Constant(c)) # print "value: {}".format(mc) if mc.rep not in reps or reps[mc.rep].rep.is_skolem() and not c.is_skolem(): reps[mc.rep] = ivy_logic.Constant(c) for s in h.sorts(): for e in h.sort_universe(s): if e.rep not in reps: reps[e.rep] = e.rep.skolem()() # print "clauses_model_to_diagram reps = {}".format(reps) # filter out clauses using universe elements without reps # res = [cls for cls in res if all(c in reps for c in used_constants_clause(cls))] # replace universe elements with their reps # print "clauses_model_to_diagram res = {}".format(res) res = substitute_constants_clauses(res,reps) # filter defined skolems # this caused a bug in the leader example. the generated diagram did not satisfy clauses1 res.fmlas = [f for f in res.fmlas if not any((x.is_skolem() and x in clauses1.defidx) for x in used_symbols_ast(f))] # print "clauses_model_to_diagram res = {}".format(res) uc = Clauses([[ivy_logic._eq_lit(ivy_logic.Variable('X',c.get_sort()),reps[c.rep]) for c in h.sort_universe(s)] for s in h.sorts()]) # print "clauses_model_to_diagram uc = {}".format(uc) # uc = true_clauses() if weaken: res = unsat_core(res,and_clauses(uc,axioms),clauses1) # implied not used here # print "clauses_model_to_diagram res = {}".format(res) # print "foo = {}".format(unsat_core(and_clauses(uc,axioms),true_clauses(),clauses1)) # filter out non-rep skolems repset = set(c.rep for e,c in reps.iteritems()) # print "clauses_model_to_diagram repset = {}".format(repset) ign = lambda x,ignore=ignore: (ignore(x) and not x in repset) res = Clauses([cl for cl in res.fmlas if not any(ign(c) for c in used_symbols_ast(cl))]) # print "clauses_model_to_diagram res = {}".format(res) return res
def mine_constants2(mod,trans,invariant): defnd = set(dfn.defines() for dfn in trans.defs) res = defaultdict(list) syms = ilu.used_symbols_ast(invariant) syms.update(ilu.used_symbols_clauses(trans)) for c in syms: if not il.is_function_sort(c.sort): res[c.sort].append(c) # iu.dbg('res') return res
def state_changed(self,recomp=True): cs = self.concept_session cs.cache.clear() vocab = list(ilu.used_symbols_asts([c.formula for c in self.nodes])) fsyms = list(s for s in ilu.used_symbols_ast(cs._to_formula()) if not s.is_skolem()) vocab += list(all_symbols()) + fsyms cs.domain = replace_concept_domain_vocabulary(cs.domain,set(vocab)) for concept in self.new_relations: add_domain_concept(self.concept_domain.concepts,concept) if recomp: self.recompute()
def state_changed(self, recomp=True): cs = self.concept_session cs.cache.clear() vocab = list(ilu.used_symbols_asts([c.formula for c in self.nodes])) fsyms = list(s for s in ilu.used_symbols_ast(cs._to_formula()) if not s.is_skolem()) vocab += list(all_symbols()) + fsyms cs.domain = replace_concept_domain_vocabulary(cs.domain, set(vocab)) for concept in self.new_relations: add_domain_concept(self.concept_domain.concepts, concept) if recomp: self.recompute()
def match_get(match, sym, env, default=None): """ get the value of a symbol in a match, checking that no symbols are captured in env """ val = match.get(sym, None) if val is not None: vocab = lu.used_symbols_ast(val) vocab.update(lu.variables_ast(val)) for v in vocab: if v in env: raise_capture(v) return val return default
def __init__(self,defn): self.defn = defn self.dependencies = used_symbols_ast(defn.args[1])
def __init__(self, defn): self.defn = defn self.dependencies = used_symbols_ast(defn.args[1])
def is_immutable_expr(expr): res = not any(my_is_skolem(sym) or tr.is_new(sym) or sym in stvarset for sym in ilu.used_symbols_ast(expr)) return res
def to_aiger(mod,ext_act): erf = il.Symbol('err_flag',il.find_sort('bool')) errconds = [] add_err_flag_mod(mod,erf,errconds) # we use a special state variable __init to indicate the initial state ext_acts = [mod.actions[x] for x in sorted(mod.public_actions)] ext_act = ia.EnvAction(*ext_acts) init_var = il.Symbol('__init',il.find_sort('bool')) init = add_err_flag(ia.Sequence(*([a for n,a in mod.initializers]+[ia.AssignAction(init_var,il.And())])),erf,errconds) action = ia.Sequence(ia.AssignAction(erf,il.Or()),ia.IfAction(init_var,ext_act,init)) # get the invariant to be proved, replacing free variables with # skolems. First, we apply any proof tactics. pc = ivy_proof.ProofChecker(mod.axioms,mod.definitions,mod.schemata) pmap = dict((lf.id,p) for lf,p in mod.proofs) conjs = [] for lf in mod.labeled_conjs: if lf.id in pmap: proof = pmap[lf.id] subgoals = pc.admit_proposition(lf,proof) conjs.extend(subgoals) else: conjs.append(lf) invariant = il.And(*[il.drop_universals(lf.formula) for lf in conjs]) # iu.dbg('invariant') skolemizer = lambda v: ilu.var_to_skolem('__',il.Variable(v.rep,v.sort)) vs = ilu.used_variables_in_order_ast(invariant) sksubs = dict((v.rep,skolemizer(v)) for v in vs) invariant = ilu.substitute_ast(invariant,sksubs) invar_syms = ilu.used_symbols_ast(invariant) # compute the transition relation stvars,trans,error = action.update(mod,None) # print 'action : {}'.format(action) # print 'annotation: {}'.format(trans.annot) annot = trans.annot # match_annotation(action,annot,MatchHandler()) indhyps = [il.close_formula(il.Implies(init_var,lf.formula)) for lf in mod.labeled_conjs] # trans = ilu.and_clauses(trans,indhyps) # save the original symbols for trace orig_syms = ilu.used_symbols_clauses(trans) orig_syms.update(ilu.used_symbols_ast(invariant)) # TODO: get the axioms (or maybe only the ground ones?) # axioms = mod.background_theory() # rn = dict((sym,tr.new(sym)) for sym in stvars) # next_axioms = ilu.rename_clauses(axioms,rn) # return ilu.and_clauses(axioms,next_axioms) funs = set() for df in trans.defs: funs.update(ilu.used_symbols_ast(df.args[1])) for fmla in trans.fmlas: funs.update(ilu.used_symbols_ast(fmla)) # funs = ilu.used_symbols_clauses(trans) funs.update(ilu.used_symbols_ast(invariant)) funs = set(sym for sym in funs if il.is_function_sort(sym.sort)) iu.dbg('[str(fun) for fun in funs]') # Propositionally abstract # step 1: get rid of definitions of non-finite symbols by turning # them into constraints new_defs = [] new_fmlas = [] for df in trans.defs: if len(df.args[0].args) == 0 and is_finite_sort(df.args[0].sort): new_defs.append(df) else: fmla = df.to_constraint() new_fmlas.append(fmla) trans = ilu.Clauses(new_fmlas+trans.fmlas,new_defs) # step 2: get rid of ite's over non-finite sorts, by introducing constraints cnsts = [] new_defs = [elim_ite(df,cnsts) for df in trans.defs] new_fmlas = [elim_ite(fmla,cnsts) for fmla in trans.fmlas] trans = ilu.Clauses(new_fmlas+cnsts,new_defs) # step 3: eliminate quantfiers using finite instantiations from_asserts = il.And(*[il.Equals(x,x) for x in ilu.used_symbols_ast(il.And(*errconds)) if tr.is_skolem(x) and not il.is_function_sort(x.sort)]) iu.dbg('from_asserts') invar_syms.update(ilu.used_symbols_ast(from_asserts)) sort_constants = mine_constants(mod,trans,il.And(invariant,from_asserts)) sort_constants2 = mine_constants2(mod,trans,invariant) print '\ninstantiations:' trans,invariant = Qelim(sort_constants,sort_constants2)(trans,invariant,indhyps) # print 'after qe:' # print 'trans: {}'.format(trans) # print 'invariant: {}'.format(invariant) # step 4: instantiate the axioms using patterns # We have to condition both the transition relation and the # invariant on the axioms, so we define a boolean symbol '__axioms' # to represent the axioms. axs = instantiate_axioms(mod,stvars,trans,invariant,sort_constants,funs) ax_conj = il.And(*axs) ax_var = il.Symbol('__axioms',ax_conj.sort) ax_def = il.Definition(ax_var,ax_conj) invariant = il.Implies(ax_var,invariant) trans = ilu.Clauses(trans.fmlas+[ax_var],trans.defs+[ax_def]) # step 5: eliminate all non-propositional atoms by replacing with fresh booleans # An atom with next-state symbols is converted to a next-state symbol if possible stvarset = set(stvars) prop_abs = dict() # map from atoms to proposition variables global prop_abs_ctr # sigh -- python lameness prop_abs_ctr = 0 # counter for fresh symbols new_stvars = [] # list of fresh symbols # get the propositional abstraction of an atom def new_prop(expr): res = prop_abs.get(expr,None) if res is None: prev = prev_expr(stvarset,expr,sort_constants) if prev is not None: # print 'stvar: old: {} new: {}'.format(prev,expr) pva = new_prop(prev) res = tr.new(pva) new_stvars.append(pva) prop_abs[expr] = res # prevent adding this again to new_stvars else: global prop_abs_ctr res = il.Symbol('__abs[{}]'.format(prop_abs_ctr),expr.sort) # print '{} = {}'.format(res,expr) prop_abs[expr] = res prop_abs_ctr += 1 return res # propositionally abstract an expression global mk_prop_fmlas mk_prop_fmlas = [] def mk_prop_abs(expr): if il.is_quantifier(expr) or len(expr.args) > 0 and any(not is_finite_sort(a.sort) for a in expr.args): return new_prop(expr) return expr.clone(map(mk_prop_abs,expr.args)) # apply propositional abstraction to the transition relation new_defs = map(mk_prop_abs,trans.defs) new_fmlas = [mk_prop_abs(il.close_formula(fmla)) for fmla in trans.fmlas] # find any immutable abstract variables, and give them a next definition def my_is_skolem(x): res = tr.is_skolem(x) and x not in invar_syms return res def is_immutable_expr(expr): res = not any(my_is_skolem(sym) or tr.is_new(sym) or sym in stvarset for sym in ilu.used_symbols_ast(expr)) return res for expr,v in prop_abs.iteritems(): if is_immutable_expr(expr): new_stvars.append(v) print 'new state: {}'.format(expr) new_defs.append(il.Definition(tr.new(v),v)) trans = ilu.Clauses(new_fmlas+mk_prop_fmlas,new_defs) # apply propositional abstraction to the invariant invariant = mk_prop_abs(invariant) # create next-state symbols for atoms in the invariant (is this needed?) rn = dict((sym,tr.new(sym)) for sym in stvars) mk_prop_abs(ilu.rename_ast(invariant,rn)) # this is to pick up state variables from invariant # update the state variables by removing the non-finite ones and adding the fresh state booleans stvars = [sym for sym in stvars if is_finite_sort(sym.sort)] + new_stvars # iu.dbg('trans') # iu.dbg('stvars') # iu.dbg('invariant') # exit(0) # For each state var, create a variable that corresponds to the input of its latch # Also, havoc all the state bits except the init flag at the initial time. This # is needed because in aiger, all latches start at 0! def fix(v): return v.prefix('nondet') def curval(v): return v.prefix('curval') def initchoice(v): return v.prefix('initchoice') stvars_fix_map = dict((tr.new(v),fix(v)) for v in stvars) stvars_fix_map.update((v,curval(v)) for v in stvars if v != init_var) trans = ilu.rename_clauses(trans,stvars_fix_map) # iu.dbg('trans') new_defs = trans.defs + [il.Definition(ilu.sym_inst(tr.new(v)),ilu.sym_inst(fix(v))) for v in stvars] new_defs.extend(il.Definition(curval(v),il.Ite(init_var,v,initchoice(v))) for v in stvars if v != init_var) trans = ilu.Clauses(trans.fmlas,new_defs) # Turn the transition constraint into a definition cnst_var = il.Symbol('__cnst',il.find_sort('bool')) new_defs = list(trans.defs) new_defs.append(il.Definition(tr.new(cnst_var),fix(cnst_var))) new_defs.append(il.Definition(fix(cnst_var),il.Or(cnst_var,il.Not(il.And(*trans.fmlas))))) stvars.append(cnst_var) trans = ilu.Clauses([],new_defs) # Input are all the non-defined symbols. Output indicates invariant is false. # iu.dbg('trans') def_set = set(df.defines() for df in trans.defs) def_set.update(stvars) # iu.dbg('def_set') used = ilu.used_symbols_clauses(trans) used.update(ilu.symbols_ast(invariant)) inputs = [sym for sym in used if sym not in def_set and not il.is_interpreted_symbol(sym)] fail = il.Symbol('__fail',il.find_sort('bool')) outputs = [fail] # iu.dbg('trans') # make an aiger aiger = Encoder(inputs,stvars,outputs) comb_defs = [df for df in trans.defs if not tr.is_new(df.defines())] invar_fail = il.Symbol('invar__fail',il.find_sort('bool')) # make a name for invariant fail cond comb_defs.append(il.Definition(invar_fail,il.Not(invariant))) aiger.deflist(comb_defs) for df in trans.defs: if tr.is_new(df.defines()): aiger.set(tr.new_of(df.defines()),aiger.eval(df.args[1])) miter = il.And(init_var,il.Not(cnst_var),il.Or(invar_fail,il.And(fix(erf),il.Not(fix(cnst_var))))) aiger.set(fail,aiger.eval(miter)) # aiger.sub.debug() # make a decoder for the abstract propositions decoder = dict((y,x) for x,y in prop_abs.iteritems()) for sym in aiger.inputs + aiger.latches: if sym not in decoder and sym in orig_syms: decoder[sym] = sym cnsts = set(sym for syms in sort_constants.values() for sym in syms) return aiger,decoder,annot,cnsts,action,stvarset
def clauses_model_to_diagram(clauses1, ignore=None, implied=None, model=None, axioms=None, weaken=True): """ Return a diagram of a model of clauses1 or None. The function "ignore", if provided, returns true for symbols that should be ignored in the diagram. """ print "clauses_model_to_diagram clauses1 = {}".format(clauses1) if axioms == None: axioms = true_clauses h = model_if_none(and_clauses(clauses1, axioms), implied, model) ignore = ignore if ignore != None else lambda x: False res = model_facts(h, (lambda x: False), clauses1, upclose=True) # why not pass axioms? print "clauses_model_to_diagram res = {}".format(res) # find representative elements # find representatives of universe elements reps = dict() for c in used_constants_clauses(clauses1): # print "constant: {}".format(c) mc = get_model_constant(h.model, ivy_logic.Constant(c)) # print "value: {}".format(mc) if mc.rep not in reps or reps[ mc.rep].rep.is_skolem() and not c.is_skolem(): reps[mc.rep] = ivy_logic.Constant(c) for s in h.sorts(): for e in h.sort_universe(s): if e.rep not in reps: reps[e.rep] = e.rep.skolem()() print "clauses_model_to_diagram reps = {}".format(reps) # filter out clauses using universe elements without reps # res = [cls for cls in res if all(c in reps for c in used_constants_clause(cls))] # replace universe elements with their reps print "clauses_model_to_diagram res = {}".format(res) res = substitute_constants_clauses(res, reps) # filter defined skolems # this caused a bug in the leader example. the generated diagram did not satisfy clauses1 res.fmlas = [ f for f in res.fmlas if not any((x.is_skolem() and x in clauses1.defidx) for x in used_symbols_ast(f)) ] print "clauses_model_to_diagram res = {}".format(res) uc = Clauses([[ ivy_logic._eq_lit(ivy_logic.Variable('X', c.get_sort()), reps[c.rep]) for c in h.sort_universe(s) ] for s in h.sorts()]) print "clauses_model_to_diagram uc = {}".format(uc) # uc = true_clauses() if weaken: res = unsat_core(res, and_clauses(uc, axioms), clauses1) # implied not used here print "clauses_model_to_diagram res = {}".format(res) # print "foo = {}".format(unsat_core(and_clauses(uc,axioms),true_clauses(),clauses1)) # filter out non-rep skolems repset = set(c.rep for e, c in reps.iteritems()) print "clauses_model_to_diagram repset = {}".format(repset) ign = lambda x, ignore=ignore: (ignore(x) and not x in repset) res = Clauses([ cl for cl in res.fmlas if not any(ign(c) for c in used_symbols_ast(cl)) ]) print "clauses_model_to_diagram res = {}".format(res) return res
def isolate_component(mod, isolate_name, extra_with=[], extra_strip=None): if isolate_name not in mod.isolates: raise iu.IvyError(None, "undefined isolate: {}".format(isolate_name)) isolate = mod.isolates[isolate_name] verified = set(a.relname for a in (isolate.verified() + tuple(extra_with))) present = set(a.relname for a in isolate.present()) present.update(verified) if not interpret_all_sorts: for type_name in list(ivy_logic.sig.interp): if not (type_name in present or any( startswith_eq_some(itp.label.rep, present, mod) for itp in mod.interps[type_name] if itp.label)): del ivy_logic.sig.interp[type_name] delegates = set(s.delegated() for s in mod.delegates if not s.delegee()) delegated_to = dict( (s.delegated(), s.delegee()) for s in mod.delegates if s.delegee()) derived = set(df.args[0].func.name for df in mod.concepts) for name in present: if (name not in mod.hierarchy and name not in ivy_logic.sig.sorts and name not in derived and name not in ivy_logic.sig.interp and name not in mod.actions and name not in ivy_logic.sig.symbols): raise iu.IvyError( None, "{} is not an object, action, sort, definition, or interpreted function" .format(name)) impl_mixins = defaultdict(list) # delegate all the stub actions to their implementations global implementation_map implementation_map = {} for actname, ms in mod.mixins.iteritems(): implements = [ m for m in ms if isinstance(m, ivy_ast.MixinImplementDef) ] impl_mixins[actname].extend(implements) before_after = [ m for m in ms if not isinstance(m, ivy_ast.MixinImplementDef) ] del ms[:] ms.extend(before_after) for m in implements: for foo in (m.mixee(), m.mixer()): if foo not in mod.actions: raise IvyError(m, 'action {} not defined'.format(foo)) action = mod.actions[m.mixee()] if not (isinstance(action, ia.Sequence) and len(action.args) == 0): raise IvyError( m, 'multiple implementations of action {}'.format(m.mixee())) action = ia.apply_mixin(m, mod.actions[m.mixer()], action) mod.actions[m.mixee()] = action implementation_map[m.mixee()] = m.mixer() new_actions = {} use_mixin = lambda name: startswith_some(name, present, mod) mod_mixin = lambda m: m if startswith_some(name, verified, mod ) else m.prefix_calls('ext:') all_mixins = lambda m: True no_mixins = lambda m: False after_mixins = lambda m: isinstance(m, ivy_ast.MixinAfterDef) before_mixins = lambda m: isinstance(m, ivy_ast.MixinBeforeDef) delegated_to_verified = lambda n: n in delegated_to and startswith_eq_some( delegated_to[n], verified, mod) ext_assumes = lambda m: before_mixins(m) and not delegated_to_verified( m.mixer()) int_assumes = lambda m: after_mixins(m) and not delegated_to_verified( m.mixer()) ext_assumes_no_ver = lambda m: not delegated_to_verified(m.mixer()) summarized_actions = set() for actname, action in mod.actions.iteritems(): ver = startswith_eq_some(actname, verified, mod) pre = startswith_eq_some(actname, present, mod) if pre: if not ver: assert hasattr(action, 'lineno') assert hasattr(action, 'formal_params'), action ext_action = action.assert_to_assume().prefix_calls('ext:') assert hasattr(ext_action, 'lineno') assert hasattr(ext_action, 'formal_params'), ext_action if actname in delegates: int_action = action.prefix_calls('ext:') assert hasattr(int_action, 'lineno') assert hasattr(int_action, 'formal_params'), int_action else: int_action = ext_action assert hasattr(int_action, 'lineno') assert hasattr(int_action, 'formal_params'), int_action else: int_action = ext_action = action assert hasattr(int_action, 'lineno') assert hasattr(int_action, 'formal_params'), int_action # internal version of the action has mixins checked ea = no_mixins if ver else int_assumes new_actions[actname] = add_mixins(mod, actname, int_action, ea, use_mixin, lambda m: m) # external version of the action assumes mixins are ok, unless they # are delegated to a currently verified object ea = ext_assumes if ver else ext_assumes_no_ver new_action = add_mixins(mod, actname, ext_action, ea, use_mixin, mod_mixin) new_actions['ext:' + actname] = new_action # TODO: external version is public if action public *or* called from opaque # public_actions.add('ext:'+actname) else: # TODO: here must check that summarized action does not # have a call dependency on the isolated module summarized_actions.add(actname) action = summarize_action(action) new_actions[actname] = add_mixins(mod, actname, action, after_mixins, use_mixin, mod_mixin) new_actions['ext:' + actname] = add_mixins(mod, actname, action, all_mixins, use_mixin, mod_mixin) # figure out what is exported: exported = set() for e in mod.exports: if not e.scope() and startswith_eq_some(e.exported(), present, mod): # global scope exported.add('ext:' + e.exported()) for actname, action in mod.actions.iteritems(): if not startswith_some(actname, present, mod): for c in action.iter_calls(): if (startswith_some(c, present, mod) or any( startswith_some(m.mixer(), present, mod) for m in mod.mixins[c])): exported.add('ext:' + c) # print "exported: {}".format(exported) # We allow objects to reference any symbols in global scope, and # we keep axioms declared in global scope. Because of the way # thigs are named, this gives a different condition for keeping # symbols and axioms (in particular, axioms in global scope have # label None). Maybe this needs to be cleaned up. keep_sym = lambda name: (iu.ivy_compose_character not in name or startswith_eq_some(name, present)) keep_ax = lambda name: (name is None or startswith_eq_some( name.rep, present, mod)) check_pr = lambda name: (name is None or startswith_eq_some( name.rep, verified, mod)) prop_deps = get_prop_dependencies(mod) # filter the conjectures new_conjs = [c for c in mod.labeled_conjs if keep_ax(c.label)] del mod.labeled_conjs[:] mod.labeled_conjs.extend(new_conjs) # filter the inits new_inits = [c for c in mod.labeled_inits if keep_ax(c.label)] del mod.labeled_inits[:] mod.labeled_inits.extend(new_inits) # filter the axioms dropped_axioms = [a for a in mod.labeled_axioms if not keep_ax(a.label)] mod.labeled_axioms = [a for a in mod.labeled_axioms if keep_ax(a.label)] mod.labeled_props = [a for a in mod.labeled_props if keep_ax(a.label)] # convert the properties not being verified to axioms mod.labeled_axioms.extend( [a for a in mod.labeled_props if not check_pr(a.label)]) mod.labeled_props = [a for a in mod.labeled_props if check_pr(a.label)] # filter definitions mod.concepts = [ c for c in mod.concepts if startswith_eq_some(c.args[0].func.name, present, mod) ] # filter the signature # keep only the symbols referenced in the remaining # formulas asts = [] for x in [ mod.labeled_axioms, mod.labeled_props, mod.labeled_inits, mod.labeled_conjs ]: asts += [y.formula for y in x] asts += mod.concepts asts += [action for action in new_actions.values()] sym_names = set(x.name for x in lu.used_symbols_asts(asts)) if filter_symbols.get() or cone_of_influence.get(): old_syms = list(mod.sig.symbols) for sym in old_syms: if sym not in sym_names: del mod.sig.symbols[sym] # check that any dropped axioms do not refer to the isolate's signature # and any properties have dependencies present def pname(s): return s.label if s.label else "" if enforce_axioms.get(): for a in dropped_axioms: for x in lu.used_symbols_ast(a.formula): if x.name in sym_names: raise iu.IvyError( a, "relevant axiom {} not enforced".format(pname(a))) for actname, action in mod.actions.iteritems(): if startswith_eq_some(actname, present, mod): for c in action.iter_calls(): called = mod.actions[c] if not startswith_eq_some(c, present, mod): if not (type(called) == ia.Sequence and not called.args): raise iu.IvyError( None, "No implementation for action {}".format(c)) for p, ds in prop_deps: for d in ds: if not startswith_eq_some(d, present, mod): raise iu.IvyError( p, "property {} depends on abstracted object {}".format( pname(p), d)) # for x,y in new_actions.iteritems(): # print iu.pretty(ia.action_def_to_str(x,y)) # check for interference # iu.dbg('list(summarized_actions)') check_interference(mod, new_actions, summarized_actions) # After checking, we can put in place the new action definitions mod.public_actions.clear() mod.public_actions.update(exported) mod.actions.clear() mod.actions.update(new_actions) # TODO: need a better way to filter signature # new_syms = set(s for s in mod.sig.symbols if keep_sym(s)) # for s in list(mod.sig.symbols): # if s not in new_syms: # del mod.sig.symbols[s] # strip the isolate parameters strip_isolate(mod, isolate, impl_mixins, extra_strip) # collect the initial condition init_cond = ivy_logic.And(*(lf.formula for lf in mod.labeled_inits)) mod.init_cond = lu.formula_to_clauses(init_cond)