def check_native_compat_sym(sym): table, kind = (relations, "relation") if sym.is_relation() else (functions, "function") thing = lookup_native(sym, table, kind) # print "check_native_compat_sym: {} {}".format(sym,thing) try: if thing != None: # print "check_native_compat_sym: {} {}".format(sym,thing) z3args = [] for ds in sym.sort.dom: z3sort = lookup_native(ds, sorts, "sort") if z3sort == None: raise iu.IvyError( None, 'domain sort "{}" is uninterpreted'.format(ds)) z3args.append(z3sort.cast("0")) z3val = thing(*z3args) z3sort = z3val.sort() ns = lookup_native(sym.sort.rng, sorts, "sort") if ns == None: raise iu.IvyError( None, 'range sort "{}" is uninterpreted'.format(sym.sort.rng)) if ns != z3sort: raise iu.IvyError( None, 'range sort {}={} does not match {}'.format( sym.sort.rng, ns, z3sort)) except Exception as e: raise iu.IvyError( None, 'cannot interpret {} as {}: {}'.format( sym, ivy_logic.sig.interp[sym.name], e))
def compile_call(self): assert top_context ctx = ExprContext(lineno=self.lineno) name = self.args[0].rep if name not in top_context.actions: raise iu.IvyError(self, "call to unknown action: {}".format(name)) with ctx: args = [a.cmpl() for a in self.args[0].args] params, returns = top_context.actions[name] if len(returns) != len(self.args) - 1: raise iu.IvyError( self, "wrong number of output parameters (got {}, expecting {})".format( len(self.args) - 1, len(returns))) if len(params) != len(args): raise iu.IvyError( self, "wrong number of input parameters (got {}, expecting {})".format( len(args), len(params))) with ASTContext(self): mas = [sort_infer(a, cmpl_sort(p.sort)) for a, p in zip(args, params)] # print self.args res = CallAction(*([ivy_ast.Atom(name, mas)] + [a.cmpl() for a in self.args[1:]])) res.lineno = self.lineno ctx.code.append(res) res = ctx.extract() # print "compiled call action: {}".format(res) return res
def do_insts(ivy, insts): others = [] for instantiation in insts: pref, inst = instantiation.args defn = stack_lookup(inst.relname) if defn: # print "instantiating %s" % inst if pref != None: # ivy.define((pref.rep,inst.lineno)) ivy.declare(ObjectDecl(pref)) aparams = inst.args fparams = defn.args[0].args if len(aparams) != len(fparams): raise iu.IvyError( instantiation, "wrong number of arguments to module {}".format( inst.relname)) subst = dict((x.rep, y.rep) for x, y in zip(fparams, aparams) if not isinstance(y, Variable)) vsubst = dict((x.rep, y) for x, y in zip(fparams, aparams) if isinstance(y, Variable)) pvars = set(x.rep for x in pref.args) if pref != None else set() for v in vsubst.values(): if v.rep not in pvars: raise iu.IvyError(instantiation, "variable {} is unbound".format(v)) module = defn.args[1] inst_mod(ivy, module, pref, subst, vsubst) else: others.append(inst) if others: ivy.declare(InstantiateDecl(*others))
def check_module(): # If user specifies an isolate, check it. Else, if any isolates # are specificied in the file, check all, else check globally. missing = [] isolate = ivy_compiler.isolate.get() if isolate != None: isolates = [isolate] else: isolates = sorted(list(im.module.isolates)) if len(isolates) == 0: isolates = [None] else: if coverage.get(): missing = ivy_isolate.check_isolate_completeness() if missing: raise iu.IvyError(None,"Some assertions are not checked") for isolate in isolates: if isolate != None and isolate in im.module.isolates: idef = im.module.isolates[isolate] if len(idef.verified()) == 0 or isinstance(idef,ivy_ast.TrustedIsolateDef): continue # skip if nothing to verify if isolate: print "\nIsolate {}:".format(isolate) with im.module.copy(): ivy_isolate.create_isolate(isolate) # ,ext='ext' if opt_trusted.get(): continue check_isolate() print '' if failures > 0: raise iu.IvyError(None,"failed checks: {}".format(failures))
def check_interference(mod, new_actions, summarized_actions): calls = dict() mods = dict() mixins = dict() for actname in summarized_actions: get_calls_mods(mod, summarized_actions, actname, calls, mods, mixins) callouts = dict( ) # these are triples (midcalls,headcalls,tailcalls,bothcalls) for actname in new_actions: get_callouts(mod, new_actions, summarized_actions, actname, callouts) # iu.dbg('callouts') for actname, action in new_actions.iteritems(): if actname not in summarized_actions: for called in action.iter_calls(): if called in summarized_actions: cmods = mods[called] if cmods: things = ','.join(sorted(cmods)) raise iu.IvyError( action, "Call out to {} may have visible effect on {}". format(called, things)) if actname in callouts: for midcall in sorted(callouts[actname][0]): if midcall in calls: callbacks = calls[midcall] if callbacks: raise iu.IvyError( action, "Call to {} may cause interfering callback to {}" .format(midcall, ','.join(callbacks)))
def report_error(logic,note,ast): msg = "The verification condition is not in logic {}{} because {}.".format(logic,note,il.reason()) if il.reason() == "functions are not stratified": for sorts,asts in unstrat: msg += "\n\nNote: the following functions form a cycle:\n" for a in asts: if isinstance(a,il.Symbol): msg += ' {}\n'.format(il.sym_decl_to_str(a)) else: msg += ' {}\n'.format(iu.IvyError(a,"quantifier alternation")) raise iu.IvyError(ast,msg)
def get_strip_params(name, args, strip_map, strip_binding, ast): strip_params = strip_map_lookup(name, strip_map) if not (len(args) >= len(strip_params)): raise iu.IvyError( ast, "cannot strip isolate parameters from {}".format( presentable(name))) for sp, ap in zip(strip_params, args): if ap not in strip_binding or strip_binding[ap] != sp: raise iu.IvyError( ast, "cannot strip parameter {} from {}".format( presentable(ap), presentable(name))) return strip_params
def infer_parameters(decls): mixees = defaultdict(list) actdecls = dict() for d in decls: if d.name() == "action": for a in d.args: actdecls[a.defines()] = a for d in decls: if d.name() == "mixin": for a in d.args: mixeename = a.args[1].relname if mixeename == "init": continue if mixeename not in actdecls: raise IvyError(a, "undefined action: {}".format(mixeename)) mixees[a.args[0].relname].append(mixeename) for d in decls: if d.name() == "action": for a in d.args: am = mixees[a.defines()] if len(am) == 1 and am[0] in actdecls: mixin = a.args[1] mixee = actdecls[am[0]] nparms = len(a.args[0].args) mnparms = len(mixee.args[0].args) if len(a.formal_params) + nparms > len( mixee.formal_params) + mnparms: raise iu.IvyError( a.args[1], 'monitor has too many input parameters for {}'. format(mixee.defines())) if len(a.formal_returns) > len(mixee.formal_returns): raise iu.IvyError( a.args[1], 'monitor has too many output parameters for {}'. format(mixee.defines())) required = mnparms - nparms if len(a.formal_params) < required: raise iu.IvyError( a.args[1], 'monitor must supply at least {} explicit input parameters for {}' .format(required, mixee.defines())) xtraps = (mixee.args[0].args + mixee.formal_params)[len(a.formal_params) + nparms:] xtrars = mixee.formal_returns[len(a.formal_returns):] if xtraps or xtrars: a.formal_params.extend(xtraps) a.formal_returns.extend(xtrars) subst = dict((x.drop_prefix('fml:').rep, x.rep) for x in (xtraps + xtrars)) a.args[1] = ivy_ast.subst_prefix_atoms_ast( a.args[1], subst, None, None)
def get_cpptype_constructor(descr): """ Get a cpptype constuctor with a given descriptor. The descriptor is a string of the form "title[int][int]...", for example, bv[8] to represent bit vectors of width 8. """ title,params = parse_descr(descr) if title not in cpptypes_by_title: raise iu.IvyError(None,'unknown sort: "{}"'.format(title)) cpptype,nparams = cpptypes_by_title[title] if len(params) != nparams: raise iu.IvyError(None,'expecting {} parameter in "{}"'.format(nparams,descr)) return lambda classname: cpptype(*([classname]+params))
def __call__(self,s): if s.startswith('$'): path = s[1:].split('.') num = int(path[0])-1 if num < 0 or num >= len(self.anchor.args): raise iu.IvyError(None,'event has no argument {}'.format(s)) res = self.anchor.args[num] for field in path[1:]: if not isinstance(res,DictValue) or field not in res: raise iu.IvyError(None,'value has no field {}'.format(field)) res = res[field] return res return Symbol(s)
def report_error(logic, note, ast, unstrat): msg = "This formula is not in logic {}{} because {}".format( logic, note, il.reason()) for sorts, asts in unstrat: msg += "\n\nNote: the sort(s) " + ','.join( sorts) + ' form a function cycle using:\n' for a in asts: if isinstance(a, il.Symbol): msg += ' function {}\n'.format(a) else: msg += ' {}\n'.format(iu.IvyError(a, "quantifier alternation")) raise iu.IvyError(ast, msg)
def do_insts(ivy, insts): others = [] for instantiation in insts: pref, inst = instantiation.args defn = stack_lookup(inst.relname) if defn: # print "instantiating %s" % inst.relname aparams = inst.args fparams = defn.args[0].args if len(aparams) != len(fparams): raise iu.IvyError( instantiation, "wrong number of arguments to module {}".format( inst.relname)) subst = dict((x.rep, y.rep) for x, y in zip(fparams, aparams) if not isinstance(y, Variable)) vsubst = dict((x.rep, y) for x, y in zip(fparams, aparams) if isinstance(y, Variable)) pvars = set(x.rep for x in pref.args) if pref != None else set() for v in vsubst.values(): if v.rep not in pvars: raise iu.IvyError(instantiation, "variable {} is unbound".format(v)) module = defn.args[1] for decl in module.decls: # print "before: %s" % (decl) if vsubst: map1 = distinct_variable_renaming(used_variables_ast(pref), used_variables_ast(decl)) vpref = substitute_ast(pref, map1) vvsubst = dict( (x, map1[y.rep]) for x, y in vsubst.iteritems()) idecl = subst_prefix_atoms_ast(decl, subst, vpref, module.defined) idecl = substitute_constants_ast(idecl, vvsubst) else: idecl = subst_prefix_atoms_ast(decl, subst, pref, module.defined) if isinstance(idecl, ActionDecl): for foo in idecl.args: if not hasattr(foo.args[1], 'lineno'): print 'no lineno: {}'.format(foo) # print "after: %s" % (idecl) ivy.declare(idecl) else: others.append(inst) if others: ivy.declare(InstantiateDecl(*others))
def parse_theory(name): things = name.split('[') thy = things[0] things = things[1:] if not all(t.endswith(']') for t in things): raise iu.IvyError(None,'bad theory syntax: {}'.format(name)) prms = [int(t[:-1]) for t in things] if thy not in theory_classes: raise iu.IvyError(None,'unknown theory: {}'.format(name)) thyc = theory_classes[thy] na = thyc.num_params if len(prms) != na: raise iu.IvyError(None,'wrong number of theory parameters: {}',format(name)) return thyc(name,*prms)
def get_strip_binding(ast, strip_map, strip_binding): [get_strip_binding(arg, strip_map, strip_binding) for arg in ast.args] name = ast.rep.name if ivy_logic.is_app(ast) else ast.rep if isinstance( ast, ivy_ast.Atom) else None if name: strip_params = strip_map_lookup(name, strip_map) if not (len(ast.args) >= len(strip_params)): raise iu.IvyError( ast, "cannot strip isolate parameters from {}".format( presentable(name))) for sp, ap in zip(strip_params, ast.args): if ap in strip_binding and strip_binding[ap] != sp: raise iu.IvyError(action, "cannot strip parameter {} from {}", presentable(ap), presentable(name)) strip_binding[ap] = sp
def report_epr_error(unstrat, bad_interpreted): msg = "The verification condition is not in logic epr." for sorts, asts in unstrat: msg += "\n\nNote: the following functions form a cycle:\n" for a in asts: if isinstance(a, il.Symbol): msg += ' {}\n'.format(il.sym_decl_to_str(a)) else: msg += ' {}\n'.format(iu.IvyError(a, "skolem function")) if bad_interpreted: msg += "\n\nNote: the following interpreted functions occur over variables:\n" for sym in bad_interpreted: msg += ' {}\n'.format(il.sym_decl_to_str(sym)) raise iu.IvyError(None, msg)
def get_checked_actions(): cact = checked_action.get() if cact and 'ext:' + cact in im.module.public_actions: cact = 'ext:' + cact if cact and cact not in im.module.public_actions: raise iu.IvyError(None, '{} is not an exported action'.format(cact)) return [cact] if cact else sorted(im.module.public_actions)
def emit_sorts(header): for name, sort in il.sig.sorts.iteritems(): if name == "bool": continue if name in il.sig.interp: sort = il.sig.interp[name] if not isinstance(sort, il.EnumeratedSort): sortname = str(sort) # print "sortname: {}".format(sortname) if sortname.startswith('bv[') and sortname.endswith(']'): width = int(sortname[3:-1]) indent(header) header.append('mk_bv("{}",{});\n'.format(name, width)) continue raise iu.IvyError( None, 'sort {} has no finite interpretation'.format(name)) card = sort.card cname = varname(name) indent(header) header.append("const char *{}_values[{}]".format(cname, card) + " = {" + ','.join('"{}"'.format(x) for x in sort.extension) + "};\n") indent(header) header.append('mk_enum("{}",{},{}_values);\n'.format( name, card, cname))
def display_cex(msg, ag): if diagnose.get(): import tk_ui as ui iu.set_parameters({'mode': 'induction'}) ui.ui_main_loop(ag) exit(1) raise iu.IvyError(None, msg)
def compile_inline_call(self, args): params, returns = top_context.actions[self.rep] if len(returns) != 1: raise IvyError(self, "wrong number of return values") # TODO: right now we can't do anything with multiple returns sorts = [cmpl_sort(r.sort) for r in returns] ress = [] for sort in sorts: res = ivy_logic.Symbol('loc:' + str(len(expr_context.local_syms)), sort) expr_context.local_syms.append(res) ress.append(res()) expr_context.code.append( CallAction(*([ivy_ast.Atom(self.rep, args)] + ress))) return ivy_ast.Tuple(*ress) sort = cmpl_sort(returns[0].sort) res = ivy_logic.Symbol('loc:' + str(len(expr_context.local_syms)), sort) expr_context.local_syms.append(res) with ASTContext(self): if len(params) != len(args): raise iu.IvyError( self, "wrong number of input parameters (got {}, expecting {})". format(len(args), len(params))) args = [ sort_infer_contravariant(a, cmpl_sort(p.sort)) for a, p in zip(args, params) ] expr_context.code.append(CallAction(ivy_ast.Atom(self.rep, args), res)) return res()
def do_insts(ivy, insts): others = [] for instantiation in insts: pref, inst = instantiation.args defn = stack_lookup(inst.relname) if defn: # print "instantiating %s" % inst.relname aparams = inst.args fparams = defn.args[0].args if len(aparams) != len(fparams): raise iu.IvyError( instantiation, "wrong number of arguments to module {}".format( inst.relname)) subst = dict((x.rep, y.rep) for x, y in zip(fparams, aparams)) module = defn.args[1] for decl in module.decls: # print "before: %s" % (decl) idecl = subst_prefix_atoms_ast(decl, subst, pref, module.defined) if isinstance(idecl, ActionDecl): for foo in idecl.args: if not hasattr(foo.args[1], 'lineno'): print 'no lineno: {}'.format(foo) # print "after: %s" % (idecl) ivy.declare(idecl) else: others.append(inst) if others: ivy.declare(InstantiateDecl(*others))
def get_mixin_order(iso, mod): arcs = [(rdf.args[0].relname, rdf.args[1].relname) for rdf in mod.mixord] actions = mod.mixins.keys() for action in actions: mixins = mod.mixins[action] implements = [ m for m in mixins if isinstance(m, ivy_ast.MixinImplementDef) ] if len(implements) > 1: raise iu.IvyError(implements[1], 'Multiple implementations for {}'.format(action)) mixins = [ m for m in mixins if not isinstance(m, ivy_ast.MixinImplementDef) ] mixers = iu.topological_sort(list(set(m.mixer() for m in mixins)), arcs) keymap = dict((x, y) for y, x in enumerate(mixers)) key = lambda m: keymap[m.mixer()] before = sorted( [m for m in mixins if isinstance(m, ivy_ast.MixinBeforeDef)], key=key) after = sorted( [m for m in mixins if isinstance(m, ivy_ast.MixinAfterDef)], key=key) # order = SortOrder(arcs) # before = sorted([m for m in mixins if isinstance(m,ivy_ast.MixinBeforeDef)],order) # after = sorted([m for m in mixins if isinstance(m,ivy_ast.MixinAfterDef)],order) before.reverse() # add the before mixins in reverse order mixins = implements + before + after # print 'mixin order for action {}:' # for m in mixins: # print m.args[0] mod.mixins[action] = mixins
def emit_action(a): if isinstance(a, ia.AssignAction): vars = [ (x if isinstance(x, lg.Variable) else lg.Variable('$V' + str(idx))) for x, idx in enumerate(a.args[0].args) ] emit(' ' + a.args[0].rep.name + ' ::= ') rhs = a.args[1] if len(vars) != 0: cond = lg.And( *[lg.Eq(v, w) for v, w in zip(vars, a.args[0].args) if v != w]) rhs = lg.Ite(cond, rhs, lg.Apply(a.func, vars)) emit_expr(rhs) elif isinstance(a, ia.Sequence): emit('(') first = True for x in a.args: if not first: emit(';\n') emit_action(x) first = False emit(')') elif isinstance(a, ia.IfAction): emit('(PL.pterm.ite ') emit_expr(a.args[0]) emit_action(a.args[1]) if len(a.args) > 2: emit_action(a.args[1]) else: emit('PL.pterm.skip') else: raise iu.IvyError(a, 'action not supported yet')
def compile_app(self): args = [a.compile() for a in self.args] # handle action calls in rhs of assignment if expr_context and top_context and self.rep in top_context.actions: params, returns = top_context.actions[self.rep] if len(returns) != 1: raise IvyError(self, "wrong number of return values") # TODO: right now we can't do anything with multiple returns sorts = [cmpl_sort(r.sort) for r in returns] ress = [] for sort in sorts: res = ivy_logic.Symbol( 'loc:' + str(len(expr_context.local_syms)), sort) expr_context.local_syms.append(res) ress.append(res()) expr_context.code.append( CallAction(*([ivy_ast.Atom(self.rep, args)] + ress))) return ivy_ast.Tuple(*ress) sort = cmpl_sort(returns[0].sort) res = ivy_logic.Symbol('loc:' + str(len(expr_context.local_syms)), sort) expr_context.local_syms.append(res) with ASTContext(self): if len(params) != len(args): raise iu.IvyError( self, "wrong number of input parameters (got {}, expecting {})". format(len(args), len(params))) args = [ sort_infer(a, cmpl_sort(p.sort)) for a, p in zip(args, params) ] expr_context.code.append(CallAction(ivy_ast.Atom(self.rep, args), res)) return res() return (ivy_logic.Equals if self.rep == '=' else ivy_logic.find_polymorphic_symbol(self.rep))(*args)
def parse_descr(name): things = name.split('[') title = things[0] params = things[1:] if not all(t.endswith(']') for t in params): raise iu.IvyError(None,'bad sort descriptor: "{}"'.format(name)) return title,[int(t[:-1],0) for t in params]
def subst_subscripts_comp(s, subst): if isinstance(s, This): return s assert s != None # print 's: {} subst: {}'.format(s,subst) try: g = name_parser.findall(s) except: assert False, s # print 'g: {}'.format(g) if not g: return s pref = str_subst(g[0], subst) if isinstance(pref, This): if len(g) > 1: raise iu.IvyError( None, 'cannot substitute "this" for {} in {}'.format(g[0], s)) return pref res = pref + ''.join( ('[' + str_subst(x[1:-1], subst) + ']' if x.startswith('[') else x) for x in g[1:]) # print "res: {}".format(res) return res
def decide(s,atoms=None): # print "solving{" res = s.check() if atoms == None else s.check(atoms) if res == z3.unknown: print s.to_smt2() raise iu.IvyError(None,"Solver produced inconclusive result") # print "}" return res
def lookup_native(thing,table,kind): z3name = ivy_logic.sig.interp.get(thing.name) if z3name == None: if thing.name in iu.polymorphic_symbols: sort = thing.sort.domain[0].name if sort in ivy_logic.sig.interp and not isinstance(ivy_logic.sig.interp[sort],ivy_logic.EnumeratedSort): z3val = table(thing.name) if z3val == None: raise iu.IvyError(None,'{} is not a supported Z3 {}'.format(name,kind)) return z3val return None if isinstance(z3name,ivy_logic.EnumeratedSort): return z3name.to_z3() z3val = table(z3name) if z3val == None: raise iu.IvyError(None,'{} is not a supported Z3 {}'.format(z3name,kind)) return z3val
def decide(s): # iu.dbg('"before decide"') res = s.check() # iu.dbg('"after decide"') if res == z3.unknown: print s.to_smt2() raise iu.IvyError(None, "Solver produced inconclusive result") return res
def do_ask_pat(fun, pattern): global the_ui with uu.RunContext(the_ui): try: pat_evs = ev.parse(pattern) except: raise iu.IvyError(None, 'syntax error') fun(pat_evs)
def numeral_to_z3(num): # TODO: allow other numeric types z3sort = lookup_native(num.sort,sorts,"sort") if z3sort == None: return z3.Const(num.name,num.sort.to_z3()) # uninterpreted sort try: return z3sort.cast(str(int(num.name,0))) # allow 0x,0b, etc except: raise iu.IvyError(None,'Cannot cast "{}" to native sort {}'.format(num,z3sort))