def process_examples(self, examples: List[Example], equal_output: Callable[[Any, Any], bool]): try: all_ok = all([ self.process_example(example, equal_output) for example in examples ]) if all_ok: return ok() else: blames = self._get_blames() if len(blames) == 0: return bad() else: return bad(why=blames) except PruningException as e: node = e.node # Blame should include all children of node blame_nodes = {child for child in dfs(node)} # Blame should also include all non-children non-leaf nodes with constraints for prog_node in dfs(self._prog): if prog_node.is_param(): blame_nodes.add(node) elif prog_node.is_apply() and len( prog_node.production.constraints) > 0: blame_nodes.add(node) return bad([[Blame(node, node.production) for node in blame_nodes]])
def test_blame(self): enode0 = builder.make_enum('SmallInt', '-3') enode1 = builder.make_enum('SmallInt', '-2') snode = builder.make_apply('sqrt', [enode0]) inode = builder.make_apply('id', [snode]) interp = FooInterpreter() handler = AssertionViolationHandler(spec, interp) with self.assertRaises(AssertionViolation) as cm: interp.eval(inode, []) type_error = cm.exception blames = handler.handle_interpreter_error(type_error) self.assertIsNotNone(blames) self.assertEqual(len(blames), 2) for blame in blames: self.assertNotIn(Blame(inode, inode.production), blame) self.assertIn(Blame(snode, snode.production), blame) self.assertTrue((Blame(enode0, enode0.production) in blame) or (Blame(enode0, enode1.production) in blame))
def _analyze_enum(self, prod: Production, error: AssertionViolation) -> List[List[Blame]]: blames = list() arg_node = error.arg blame_base = self._compute_blame_base(error) blames.append(blame_base + [Blame(arg_node, prod)]) # for alt_prod in self._spec.get_productions_with_lhs(prod.lhs): # alt_node = AtomNode(alt_prod) # # Inputs doesn't matter here as we don't have any ParamNode # value = self._interp.eval(alt_node, []) # if not error.reason(value): # blames.append(blame_base + [Blame(arg_node, alt_prod)]) return blames
def _compute_blame_base(self, error: AssertionViolation) -> List[Blame]: node = error.node capture_set = set(error.captures) # Exclude the failed child itself capture_set.discard(error.index) capture_nodes = [node.children[x] for x in capture_set] blame_nodes = [node] for capture_node in capture_nodes: for child in dfs(capture_node): blame_nodes.append(child) return [Blame(n, n.production) for n in blame_nodes]
def test_blame_with_capture(self): enode0 = builder.make_enum('SmallInt', '-2') cnode = builder.make_apply('const', [enode0]) enode1 = builder.make_enum('SmallInt', '-3') enode2 = builder.make_enum('SmallInt', '3') dnode = builder.make_apply('idiv', [cnode, enode1]) interp = FooInterpreter() handler = AssertionViolationHandler(spec, interp) with self.assertRaises(AssertionViolation) as cm: interp.eval(dnode, []) type_error = cm.exception blames = handler.handle_interpreter_error(type_error) self.assertIsNotNone(blames) self.assertEqual(len(blames), 2) for blame in blames: self.assertIn(Blame(dnode, dnode.production), blame) self.assertTrue((Blame(enode1, enode1.production) in blame) or (Blame(enode1, enode2.production) in blame)) # These nodes are captured in our assertion self.assertIn(Blame(cnode, cnode.production), blame) self.assertIn(Blame(enode0, enode0.production), blame)
def process_example(self, example: Example, equal_output: Callable[[Any, Any], bool], prune: str, abstract_eval_memory): """check whether an example program is consistent with the input-output spec """ prune = AbstractPrune(self._interp, example, prune, abstract_eval_memory) if prune.is_unsat(self._prog): # If abstract semantics cannot be satisfiable, perform blame analysis blame_nodes = prune.get_blame_nodes() if blame_nodes is not None: self._blames_collection.add( frozenset([Blame(n, n.production) for n in blame_nodes])) return False else: # If abstract semantics is satisfiable, start interpretation constraint_interpreter = ConstraintInterpreter( self._interp, example.input) interpreter_output = constraint_interpreter.interpret(self._prog) return equal_output(interpreter_output, example.output)
def gen_blame(prod): return frozenset([ Blame(node=n, production=(prod if n is node else n.production)) for n in base_nodes ])