def translate_pythonvar_ref(self, var: PythonVar, module: PythonModule, node: ast.AST, ctx: Context) -> Expr: # We need a context object here if not ctx: ctx = Context() ctx.module = module return self.expr_translator.translate_pythonvar_ref(var, node, ctx)
def translate_let(self, node: ast.Call, ctx: Context, impure: bool = False) -> StmtsAndExpr: type = self.get_target(node.args[1], ctx) if not isinstance(type, PythonType) or not isinstance( node.args[2], ast.Lambda): raise InvalidProgramException(node, 'invalid.let') lambda_ = node.args[2] lambda_prefix = construct_lambda_prefix( lambda_.lineno, getattr(lambda_, 'col_offset', None)) lambda_prefix += '$' arg = lambda_.args.args[0] var = ctx.actual_function.get_variable(lambda_prefix + arg.arg) exp_stmt, exp_val = self.translate_expr(node.args[0], ctx) ctx.set_alias(arg.arg, var, None) body_stmt, body_val = self.translate_expr(lambda_.body, ctx, impure=impure) ctx.remove_alias(arg.arg) pos = self.to_position(node, ctx) info = self.no_info(ctx) let = self.viper.Let(var.decl, exp_val, body_val, pos, info) return exp_stmt + body_stmt, let
def translate_pythonvar_decl( self, var: PythonVar, module: PythonModule) -> 'silver.ast.LocalVarDecl': # We need a context object here ctx = Context() ctx.module = module return self.expr_translator.translate_pythonvar_decl(var, ctx)
def translate_exprs(self, nodes: List[ast.AST], function: PythonMethod, ctx: Context) -> Expr: """ Translates a list of nodes to a single (let-)expression if the nodes are only returns, assignments and if-blocks. First translates them to Assign- and ReturnWrappers with conditions derived from surrounding if-blocks (if any), then creates one big expression out of a list of wrappers. """ # Translate to wrapper objects wrappers = self._translate_to_wrappers(nodes, ctx) self._collect_names(wrappers, function) # Second walk through wrappers, starting at the end. Translate all of # them into one big expression. Assigns become a let, returns just the # returned value, and if something happens in an if block, we put it assert not ctx.var_aliases previous = None for wrapper in reversed(wrappers): ctx.var_aliases = wrapper.names.copy() previous = self._translate_wrapper(wrapper, previous, function, ctx) ctx.var_aliases = {} return previous
def _translate_exceptional_posts(self, method: PythonMethod, err_var: 'viper.ast.LocalVar', ctx: Context) -> List[Expr]: """ Translates the exceptional postconditions specified for 'method'. """ ctx.obligation_context.is_translating_posts = True posts = [] error = self.viper.NeCmp(err_var, self.viper.NullLit(self.no_position(ctx), self.no_info(ctx)), self.no_position(ctx), self.no_info(ctx)) error_type_conds = [] error_string = '"method only raises exceptions of type{0} {1}"'.format( 's' if len(method.declared_exceptions) > 1 else '', ', '.join([e.name for e in method.declared_exceptions])) error_type_pos = self.to_position(method.node, ctx, error_string) for exception in method.declared_exceptions: has_type = self.var_type_check(ERROR_NAME, exception, error_type_pos, ctx, inhale_exhale=False) error_type_conds.append(has_type) condition = self.viper.And(error, has_type, self.no_position(ctx), self.no_info(ctx)) assert ctx.current_contract_exception is None ctx.current_contract_exception = exception for post, aliases in method.declared_exceptions[exception]: with ctx.additional_aliases(aliases): stmt, expr = self.translate_expr(post, ctx, self.viper.Bool, True) if stmt: raise InvalidProgramException(post, 'purity.violated') expr = self.viper.Implies(condition, expr, self.to_position(post, ctx), self.no_info(ctx)) posts.append(expr) ctx.current_contract_exception = None error_type_cond = None for type in error_type_conds: if error_type_cond is None: error_type_cond = type else: error_type_cond = self.viper.Or(error_type_cond, type, error_type_pos, self.no_info(ctx)) if error_type_cond is not None: posts.append(self.viper.Implies(error, error_type_cond, error_type_pos, self.no_info(ctx))) ctx.obligation_context.is_translating_posts = False return posts
def _translate_method_body(self, method: PythonMethod, ctx: Context) -> List[Stmt]: body = [] statements = method.node.body body_start, body_end = get_body_indices(statements) # Create local variables for parameters body.extend(self._create_local_vars_for_params(method, ctx)) ctx.allow_statements = True body += flatten( [self.translate_stmt(stmt, ctx) for stmt in method.node.body[body_start:body_end]]) ctx.allow_statements = False return body
def translate_function(self, func: PythonMethod, ctx: Context) -> 'silver.ast.Function': """ Translates a pure Python function (may or not belong to a class) to a Viper function """ old_function = ctx.current_function ctx.current_function = func self.bind_type_vars(func, ctx) pos = self.to_position(func.node, ctx) if not func.type: raise InvalidProgramException(func.node, 'function.type.none') type = self.translate_type(func.type, ctx) args = self._translate_params(func, ctx) if func.declared_exceptions: raise InvalidProgramException(func.node, 'function.throws.exception') # Create preconditions pres = self._translate_pres(func, ctx) # Create postconditions posts = [] for post, aliases in func.postcondition: with ctx.additional_aliases(aliases): stmt, expr = self.translate_expr(post, ctx, self.viper.Bool) if stmt: raise InvalidProgramException(post, 'purity.violated') posts.append(expr) # Create typeof preconditions pres = self._create_typeof_pres(func, False, ctx) + pres if func.type.name not in PRIMITIVES: res_type_pos = self.to_position(func.node, ctx, '"return type is correct"') res_type = self.translate_type(func.type, ctx) result = self.viper.Result(res_type, res_type_pos, self.no_info(ctx)) check = self.type_check(result, func.type, res_type_pos, ctx) posts = [check] + posts statements = func.node.body start, end = get_body_indices(statements) # Translate body actual_body = statements[start:end] if (func.contract_only or (len(actual_body) == 1 and isinstance(actual_body[0], ast.Expr) and isinstance(actual_body[0].value, ast.Ellipsis))): body = None else: body = self.translate_exprs(actual_body, func, ctx) ctx.current_function = old_function name = func.sil_name return self.viper.Function(name, args, type, pres, posts, body, pos, self.no_info(ctx))
def _create_local_vars_for_params(self, method: PythonMethod, ctx: Context) -> List[Stmt]: """Creates LocalVarAssigns for each parameter.""" assign_stmts = [] for name, arg in method.args.items(): arg_var = ctx.current_function.create_variable(name, arg.type, self.translator) arg_assign = self.viper.LocalVarAssign(arg_var.ref(), arg.ref(), self.no_position(ctx), self.no_info(ctx)) assign_stmts.append(arg_assign) ctx.set_alias(name, arg_var, arg) return assign_stmts
def translate_main_method(self, modules: List[PythonModule], ctx: Context) -> List['silver.ast.Method']: """ Translates the global statements of the program to a single method. """ no_pos = self.no_position(ctx) no_info = self.no_info(ctx) main = self._get_main_module(modules) main_method, locals, stmts = self._create_main_method_setup(modules, ctx) method_name = main_method.sil_name # Translate statements in main module. When an import statement is encountered, # the translation will include executing the statements in the imported module. for stmt in main.node.body: stmts.extend(self.translate_stmt(stmt, ctx)) stmts += self._method_body_postamble(main_method, ctx) stmts += self._create_method_epilog(main_method, ctx) main_locals = [local.decl for local in main_method.get_locals() if not local.name.startswith('lambda')] for tb in main_method.try_blocks: main_locals.append(tb.error_var.decl) main_locals.append(tb.finally_var.decl) body = stmts res = self.create_method_node(ctx, method_name, [], [], [], [], main_locals + locals, body, no_pos, no_info, method=ctx.current_function) ctx.current_function = None return main_method, res
def _translate_pres(self, method: PythonMethod, ctx: Context) -> List[Expr]: """ Translates the preconditions specified for 'method'. """ pres = [] for pre, aliases in method.precondition: with ctx.additional_aliases(aliases): stmt, expr = self.translate_expr(pre, ctx, self.viper.Bool, True) if stmt: raise InvalidProgramException(pre, 'purity.violated') pres.append(expr) if method.cls and method.method_type is MethodType.normal: error_string = '"call receiver is not None"' pos = self.to_position(method.node, ctx, error_string) not_null = self.viper.NeCmp(next(iter(method.args.values())).ref(), self.viper.NullLit( self.no_position(ctx), self.no_info(ctx)), pos, self.no_info(ctx)) pres = [not_null] + pres return pres
def _translate_posts(self, method: PythonMethod, err_var: 'viper.ast.LocalVar', ctx: Context) -> List[Expr]: """ Translates the postconditions specified for 'method'. """ ctx.obligation_context.is_translating_posts = True posts = [] no_error = self.viper.EqCmp(err_var, self.viper.NullLit(self.no_position(ctx), self.no_info(ctx)), self.no_position(ctx), self.no_info(ctx)) for post, aliases in method.postcondition: with ctx.additional_aliases(aliases): stmt, expr = self.translate_expr(post, ctx, self.viper.Bool, True) if stmt: raise InvalidProgramException(post, 'purity.violated') if method.declared_exceptions: expr = self.viper.Implies(no_error, expr, self.to_position(post, ctx), self.no_info(ctx)) posts.append(expr) ctx.obligation_context.is_translating_posts = False return posts
def translate_handler(self, handler: PythonExceptionHandler, ctx: Context) -> List[Stmt]: """ Creates a code block representing an exception handler, to be put at the end of a Viper method """ label_name = ctx.get_label_name(handler.name) label = self.viper.Label(label_name, self.to_position(handler.node, ctx), self.no_info(ctx)) old_var_aliases = ctx.var_aliases ctx.var_aliases = handler.try_block.handler_aliases no_position = self.no_position(ctx) no_info = self.no_info(ctx) body = [] if handler.exception_name: err_var = handler.try_block.get_error_var(self.translator) if err_var.sil_name in ctx.var_aliases: err_var = ctx.var_aliases[err_var.sil_name] ctx.var_aliases[handler.exception_name] = err_var err_var.type = handler.exception body.append(self.set_var_defined(err_var, no_position, no_info)) for stmt in handler.body: body += self.translate_stmt(stmt, ctx) body_block = self.translate_block(body, self.to_position(handler.node, ctx), no_info) if handler.try_block.finally_block: next = handler.try_block.finally_name finally_var = handler.try_block.get_finally_var(self.translator) if finally_var.sil_name in ctx.var_aliases: finally_var = ctx.var_aliases[finally_var.sil_name] lhs = finally_var.ref() rhs = self.viper.IntLit(0, no_position, no_info) var_set = self.viper.LocalVarAssign(lhs, rhs, no_position, no_info) next_var_set = [var_set] else: next = 'post_' + handler.try_block.name next_var_set = [] label_name = ctx.get_label_name(next) goto_end = self.viper.Goto(label_name, self.to_position(handler.node, ctx), no_info) ctx.var_aliases = old_var_aliases return [label, body_block] + next_var_set + [goto_end]
def _create_method_epilog(self, method: PythonMethod, ctx: Context) -> List[Stmt]: """ Hook to generate the method epilog. """ end_name = ctx.get_label_name(END_LABEL) return [self.viper.Label(end_name, self.no_position(ctx), self.no_info(ctx))]
def _method_body_postamble(self, method: PythonMethod, ctx: Context) -> List[Stmt]: postamble = [] end_label = ctx.get_label_name(END_LABEL) postamble.append(self.viper.Goto(end_label, self.no_position(ctx), self.no_info(ctx))) assert not ctx.var_aliases postamble += self._translate_try_handlers(method, ctx) postamble += self.add_handlers_for_inlines(ctx) return postamble
def bind_type_vars(self, method: PythonMethod, ctx: Context) -> None: """ Binds the names of type variables of the given method and its class and superclasses to expressions denoting the values of said types. """ ctx.bound_type_vars = {} # Class type variables first. if method.cls and method.method_type is MethodType.normal: cls = method.cls while cls: for name, var in cls.type_vars.items(): self_arg = next(iter(method.args.values())).ref() literal = self.type_factory.get_ref_type_arg(self_arg, var.target_type, var.index, ctx) ctx.bound_type_vars[(var.target_type.name, name)] = literal cls = cls.superclass if isinstance(cls, GenericType): cls = cls.cls # Now method type variables. for name, var in method.type_vars.items(): if callable(var.type_expr): # var.type_expr is currently the function that will create the # type expression when given the parameter that defines the # type variable (identified by var.target_node). for i, python_var in enumerate(method.args.values()): if python_var.node is var.target_node: ref = python_var.ref() typeof = self.type_factory.typeof(ref, ctx) literal = var.type_expr(self.type_factory, typeof, ctx) var.type_expr = literal else: literal = var.type_expr ctx.bound_type_vars[(var.name,)] = literal if method.name == 'Eval': # Eval uses type parameters in ways we don't usually support; # while translating it, we're treating the type parameter V as type # object. object_type = ctx.module.global_module.classes[OBJECT_TYPE] literal = self.type_factory.translate_type_literal(object_type, self.no_position(ctx), ctx) ctx.bound_type_vars[('V',)] = literal
def _create_main_method_setup(self, modules: List[PythonModule], ctx: Context) -> Tuple[PythonMethod, List[VarDecl], List[Stmt]]: main = self._get_main_module(modules) locals, init_stmts = self._initialize_main_state(modules, main, ctx) # Create artificial main PythonMethod that contains the execution of global # statements. ctx.current_class = None method_name = ctx.module.get_fresh_name('main') main_method = PythonMethod(MAIN_METHOD_NAME, main, None, main, False, False, main.node_factory) main_method._module = main ctx.current_function = main_method ctx.current_function.try_blocks = main.try_blocks ctx.current_function.labels = main.labels ctx.current_function.precondition = main.precondition ctx.current_function.postcondition = main.postcondition ctx.current_function.loop_invariants = main.loop_invariants ctx.current_function.process(method_name, self.translator) ctx.module = main return main_method, locals, init_stmts
def translate_predicate(self, pred: PythonMethod, ctx: Context) -> 'ast.silver.Predicate': """ Translates pred to a Silver predicate. """ if not pred.type or pred.type.name != BOOL_TYPE: raise InvalidProgramException(pred.node, 'invalid.predicate') assert ctx.current_function is None ctx.current_function = pred args = [] arg_types = self.viper.TrueLit(self.no_position(ctx), self.no_info(ctx)) for name, arg in pred.args.items(): args.append(arg.decl) arg_type = self.type_check(arg.ref(), arg.type, self.no_position(ctx), ctx) arg_types = self.viper.And(arg_types, arg_type, self.no_position(ctx), self.no_info(ctx)) if len(pred.node.body) != 1: raise InvalidProgramException(pred.node, 'invalid.predicate') content = pred.node.body[0] if isinstance(content, ast.Return): content = content.value stmt, body = self.translate_expr(content, ctx, impure=True, target_type=self.viper.Bool) if stmt: raise InvalidProgramException(pred.node, 'invalid.predicate') body = self.viper.And(arg_types, body, self.no_position(ctx), self.no_info(ctx)) ctx.current_function = None return self.viper.Predicate(pred.sil_name, args, body, self.to_position(pred.node, ctx), self.no_info(ctx))
def translate_type(self, cls: PythonClass, ctx: Context) -> 'silver.ast.Type': """ Translates the given type to the corresponding Viper type (Int, Ref, ..) """ if isinstance(cls, SilverType): return cls.type elif cls.name == CALLABLE_TYPE: ctx.are_function_constants_used = True return self.viper.function_domain_type() elif cls.name in PRIMITIVES: cls = cls.try_box() return self.builtins['builtins.' + cls.name] elif cls.name == 'type': return self.type_factory.type_type() else: return self.viper.Ref
def translate_program(self, modules: List[PythonModule], sil_progs: List, selected: Set[str] = None, ignore_global: bool = False, arp: bool = False, sif=False) -> 'silver.ast.Program': ctx = Context() ctx.sif = sif ctx.current_class = None ctx.current_function = None ctx.module = modules[0] ctx.arp = arp return self.prog_translator.translate_program(modules, sil_progs, ctx, selected, ignore_global)
def translate_main_method(self, modules: List[PythonModule], ctx: Context) -> List[Method]: no_pos = self.no_position(ctx) no_info = self.no_info(ctx) main = self._get_main_module(modules) main_method, locals, stmts = self._create_main_method_setup( modules, ctx) method_name = main_method.sil_name # Create an error variable error_var = self.create_method_error_var(ctx) main_method.error_var = error_var locals.append(error_var.decl) stmts.append( self.viper.LocalVarAssign(error_var.ref(), self.viper.NullLit(no_pos, no_info), no_pos, no_info)) # Translate statements in main module. When an import statement is encountered, # the translation will include executing the statements in the imported module. for stmt in main.node.body: stmts.extend(self.translate_stmt(stmt, ctx)) stmts += self._method_body_postamble(main_method, ctx) stmts += self._create_method_epilog(main_method, ctx) main_locals = [ local.decl for local in main_method.get_locals() if not local.name.startswith('lambda') ] res = self.create_method_node(ctx, method_name, [], [], [], [], main_locals + locals, stmts, no_pos, no_info, method=ctx.current_function) ctx.current_function = None return main_method, res
def translate_method(self, method: PythonMethod, ctx: Context) -> 'silver.ast.Method': """ Translates an impure Python function (may or not belong to a class) to a Viper method """ old_function = ctx.current_function ctx.current_function = method args = self._translate_params(method, ctx) self.bind_type_vars(method, ctx) results = [res.decl for res in method.get_results()] error_var = self.create_method_error_var(ctx) error_var_decl = error_var.decl error_var_ref = error_var.ref() method.error_var = error_var pres, posts = self.extract_contract(method, ERROR_NAME, False, ctx) if method.cls and method.name == '__init__': init_pres = self._create_init_pres(method, ctx) pres = init_pres + pres if method.declared_exceptions: results.append(error_var_decl) # Translate body body = [] no_pos = self.no_position(ctx) no_info = self.no_info(ctx) if method.cls and method.method_type == MethodType.normal: body.append(self._check_self_type(method, ctx)) if method.type: # Assign null as the default return value to the return variable. assign_none = self.viper.LocalVarAssign(method.result.ref(), self.viper.NullLit(no_pos, no_info), no_pos, no_info) body.append(assign_none) if method.contract_only: false = self.viper.FalseLit(self.no_position(ctx), self.no_info(ctx)) assume_false = self.viper.Inhale(false, self.no_position(ctx), self.no_info(ctx)) body.append(assume_false) locals = [] else: body.append(self.viper.LocalVarAssign(error_var_ref, self.viper.NullLit(self.no_position(ctx), self.no_info(ctx)), self.no_position(ctx), self.no_info(ctx))) if method.declared_exceptions: locals = [] else: locals = [error_var_decl] body += self._translate_method_body(method, ctx) for arg in method.get_args(): ctx.remove_alias(arg.name) body += self._method_body_postamble(method, ctx) locals += [local.decl for local in method.get_locals() if not local.name.startswith('lambda')] body += self._create_method_epilog(method, ctx) name = method.sil_name nodes = self.create_method_node( ctx, name, args, results, pres, posts, locals, body, self.to_position(method.node, ctx), self.no_info(ctx), method=method) ctx.current_function = old_function return nodes
def translate_predicate_family( self, root: PythonMethod, preds: List[PythonMethod], ctx: Context ) -> Tuple[List['ast.silver.Predicate'], List['ast.silver.Method']]: """ Translates the methods in preds, whose root (which they all override) is root, to a family-predicate in Silver. """ no_info = self.no_info(ctx) dependencies = {} for pred in preds: value = {pred.overrides} if pred.overrides else set() dependencies[pred] = value sorted = toposort_flatten(dependencies, False) name = root.sil_name args = [] self_var_ref = root.args[next(iter(root.args))].ref() true_lit = self.viper.TrueLit(self.no_position(ctx), self.no_info(ctx)) arg_types = true_lit self.bind_type_vars(root, ctx) for arg in root.args.values(): args.append(arg.decl) arg_type = self.type_check(arg.ref(), arg.type, self.no_position(ctx), ctx) arg_types = self.viper.And(arg_types, arg_type, self.no_position(ctx), self.no_info(ctx)) unknown_type_condition = true_lit self_framing_check_methods = [] body = None assert not ctx.var_aliases for instance in sorted: ctx.var_aliases = {} assert not ctx.current_function if instance.type.name != BOOL_TYPE: raise InvalidProgramException(instance.node, 'invalid.predicate') ctx.current_function = instance ctx.module = instance.module self.bind_type_vars(instance, ctx) # Replace variables in instance by variables in root, since we use the # parameter names from root. for root_name, current_name in zip(root.args.keys(), instance.args.keys()): root_var = root.args[root_name] # For the receiver parameter, we need it to have the same sil_name as # that of the root, but the type of the current instance when translating # it, otherwise some fields/functions/predicates may not be found. if root_name == next(iter(root.args.keys())): root_var = copy.copy(root_var) root_var.type = instance.cls ctx.set_alias(current_name, root_var) actual_body_start = 0 while (actual_body_start < len(instance.node.body) and isinstance( instance.node.body[actual_body_start], ast.Expr) and isinstance(instance.node.body[actual_body_start].value, ast.Str)): actual_body_start += 1 if len(instance.node.body[actual_body_start:]) != 1: raise InvalidProgramException(instance.node, 'invalid.predicate') content = instance.node.body[actual_body_start] if isinstance(content, ast.Return): content = content.value stmt, current = self.translate_expr(content, ctx, impure=True, target_type=self.viper.Bool) if stmt: raise InvalidProgramException(instance.node, 'invalid.predicate') instance_pos = self.to_position(instance.node, ctx) has_type = self.type_factory.type_check(self_var_ref, instance.cls, instance_pos, ctx) implication = self.viper.Implies(has_type, current, instance_pos, no_info) self_var_type_expr = self.type_factory.typeof(self_var_ref, ctx) instance_type = self.type_factory.translate_type_literal( instance.cls, instance_pos, ctx, alias=self_var_type_expr) type_not_instance = self.viper.NeCmp(self_var_type_expr, instance_type, instance_pos, no_info) unknown_type_condition = self.viper.And(unknown_type_condition, type_not_instance, instance_pos, no_info) ctx.current_function = None if body: body = self.viper.And(body, implication, self.to_position(root.node, ctx), no_info) else: body = implication self_frame_method_name = root.module.get_fresh_name(root.name + '_' + instance.name + 'frame_check') pos_with_rule = self.to_position( instance.node, ctx, rules=rules.PRED_FAM_MEMBER_NOT_FRAMED) current_with_rule = self.viper.And(true_lit, current, pos_with_rule, no_info) self_frame_method = self.viper.Method( self_frame_method_name, args, [], [arg_types, has_type, current_with_rule], [], [], None, self.to_position(root.node, ctx), no_info) self_framing_check_methods.append(self_frame_method) # Dirty hack to artificially create a dependency from this predicate to its well-formedness check; # the call is never used, but the creation of the call while translating the predicate will be tracked. self.viper.MethodCall(self_frame_method_name, [], [], self.to_position(root.node, ctx), no_info) root_pos = self.to_position(root.node, ctx) all_preds = [] if not (root.name == 'invariant' and root.cls.name == 'Lock'): root_pos_with_rule = self.to_position( root.node, ctx, rules=rules.PRED_FAM_FOLD_UNKNOWN_RECEIVER) rest_pred_name = root.module.get_fresh_name(root.name + '_abstract_rest') rest_pred = self.viper.Predicate(rest_pred_name, args, None, root_pos, no_info) all_preds.append(rest_pred) rest_pred_acc = self.viper.PredicateAccess( [arg.localVar() for arg in args], rest_pred_name, root_pos_with_rule, no_info) rest_pred_acc_pred = self.viper.PredicateAccessPredicate( rest_pred_acc, self.viper.FullPerm(root_pos_with_rule, no_info), root_pos_with_rule, no_info) body = self.viper.And( body, self.viper.Implies(unknown_type_condition, rest_pred_acc_pred, root_pos_with_rule, no_info), root_pos_with_rule, no_info) ctx.var_aliases = {} body = self.viper.And(arg_types, body, root_pos, no_info) family_pred = self.viper.Predicate(name, args, body, root_pos, no_info) all_preds.append(family_pred) return all_preds, self_framing_check_methods
def translate_finally(self, block: PythonTryBlock, ctx: Context) -> List[Stmt]: """ Creates a code block representing the finally-block belonging to block, to be put at the end of a Viper method. """ pos = self.to_position(block.node, ctx) info = self.no_info(ctx) label_name = ctx.get_label_name(block.finally_name) label = self.viper.Label(label_name, self.to_position(block.node, ctx), self.no_info(ctx)) loop = get_parent_of_type(block.node, (ast.While, ast.For)) body = [label] if block.finally_block: for stmt in block.finally_block: body += self.translate_stmt(stmt, ctx) else: # With-block ctx_type = self.get_type(block.with_item.context_expr, ctx) ctx_var = block.with_var exit_type = ctx_type.get_method('__exit__').type exit_res = ctx.current_function.create_variable('exit_res', exit_type, self.translator) type_class = ctx.module.global_module.classes['type'] exception_class = ctx.module.global_module.classes['Exception'] tb_class = ctx.module.global_module.classes['traceback'] # The __exit__ method takes three arguments: type, value and # traceback. type_var = ctx.actual_function.create_variable('t', type_class, self.translator) value_var = ctx.actual_function.create_variable('e', exception_class, self.translator) traceback_var = ctx.actual_function.create_variable('tb', tb_class, self.translator) body.append(self._assign_exit_vars(block, type_var, value_var, traceback_var, pos, ctx)) exit_call = self.get_method_call(ctx_type, '__exit__', [ctx_var.ref(), type_var.ref(), value_var.ref(), traceback_var.ref()], [ctx_type, None, None, None], [exit_res.ref()], block.with_item.context_expr, ctx) body.extend(exit_call) finally_var = block.get_finally_var(self.translator) if finally_var.sil_name in ctx.var_aliases: finally_var = ctx.var_aliases[finally_var.sil_name] tries = get_surrounding_try_blocks(ctx.actual_function.try_blocks, block.node) tries_in_same_loop = [] if loop is not None: tries_in_same_loop = [t for t in tries if get_parent_of_type(t.node, (ast.While, ast.For)) is loop] post_label = ctx.get_label_name(block.post_name) goto_post = self.viper.Goto(post_label, pos, info) end_label = ctx.get_label_name(END_LABEL) goto_end = self.viper.Goto(end_label, pos, info) empty_stmt = self.translate_block([], pos, info) except_block = [] return_block = [] for current in tries: if not return_block and current.finally_block: # Propagate finally var value var_next = current.get_finally_var(self.translator) if var_next.sil_name in ctx.var_aliases: var_next = ctx.var_aliases[var_next.sil_name] next_assign = self.viper.LocalVarAssign(var_next.ref(), finally_var.ref(), pos, info) # Goto finally block next_label = ctx.get_label_name(current.finally_name) goto_next = self.viper.Goto(next_label, pos, info) return_block = [next_assign, goto_next] for handler in current.handlers: # If handler applies, # goto handler err_var = block.get_error_var(self.translator) if err_var.sil_name in ctx.var_aliases: err_var = ctx.var_aliases[err_var.sil_name] condition = self.var_type_check(err_var.sil_name, handler.exception, self.to_position(handler.node, ctx), ctx, inhale_exhale=False) label_name = ctx.get_label_name(handler.name) goto = self.viper.Goto(label_name, pos, info) if_handler = self.viper.If(condition, goto, empty_stmt, pos, info) except_block.append(if_handler) if current.finally_block: # Propagate finally var value # Goto finally block except_block += return_block break break_block = [] for current in tries_in_same_loop: if not break_block and current.finally_block: # Propagate finally var value var_next = current.get_finally_var(self.translator) if var_next.sil_name in ctx.var_aliases: var_next = ctx.var_aliases[var_next.sil_name] next_assign = self.viper.LocalVarAssign(var_next.ref(), finally_var.ref(), pos, info) # Goto finally block next_label = ctx.get_label_name(current.finally_name) goto_next = self.viper.Goto(next_label, pos, info) break_block = [next_assign, goto_next] continue_block = [next_assign, goto_next] if not return_block: return_block = [goto_end] if loop and not break_block: goto_break = self.viper.Goto(loop.post_label, pos, info) goto_continue = self.viper.Goto(loop.end_label, pos, info) break_block = [goto_break] continue_block = [goto_continue] if ctx.actual_function.declared_exceptions: # Assign error to error output var error_var = ctx.error_var.ref() block_error_var = block.get_error_var(self.translator) if block_error_var.sil_name in ctx.var_aliases: block_error_var = ctx.var_aliases[block_error_var.sil_name] assign = self.viper.LocalVarAssign( error_var, block_error_var.ref(), pos, info) except_block.append(assign) except_block.append(goto_end) else: error_string = '"method raises no exceptions"' error_pos = self.to_position(block.node, ctx, error_string) false = self.viper.FalseLit(error_pos, info) assert_false = self.viper.Exhale(false, error_pos, info) except_block.append(assert_false) except_block.append(goto_end) except_block = self.translate_block(except_block, pos, info) return_block = self.translate_block(return_block, pos, info) if loop: break_block = self.translate_block(break_block, pos, info) continue_block = self.translate_block(continue_block, pos, info) number_one = self.viper.IntLit(1, pos, info) number_two = self.viper.IntLit(2, pos, info) number_three = self.viper.IntLit(3, pos, info) number_four = self.viper.IntLit(4, pos, info) is_one = self.viper.EqCmp(finally_var.ref(), number_one, pos, info) is_two = self.viper.EqCmp(finally_var.ref(), number_two, pos, info) is_three = self.viper.EqCmp(finally_var.ref(), number_three, pos, info) is_four = self.viper.EqCmp(finally_var.ref(), number_four, pos, info) if_return = self.viper.If(is_one, return_block, goto_post, pos, info) if_except = self.viper.If(is_two, except_block, if_return, pos, info) top_level_if = if_except if loop is not None: if_break = self.viper.If(is_three, break_block, if_except, pos, info) if_continue = self.viper.If(is_four, continue_block, if_break, pos, info) top_level_if = if_continue body += [top_level_if] return body
def translate_predicate_family(self, root: PythonMethod, preds: List[PythonMethod], ctx: Context) -> 'ast.silver.Predicate': """ Translates the methods in preds, whose root (which they all override) is root, to a family-predicate in Silver. """ dependencies = {} for pred in preds: value = {pred.overrides} if pred.overrides else set() dependencies[pred] = value sorted = toposort_flatten(dependencies, False) name = root.sil_name args = [] self_var_ref = root.args[next(iter(root.args))].ref() arg_types = self.viper.TrueLit(self.no_position(ctx), self.no_info(ctx)) self.bind_type_vars(root, ctx) for arg in root.args.values(): args.append(arg.decl) arg_type = self.type_check(arg.ref(), arg.type, self.no_position(ctx), ctx) arg_types = self.viper.And(arg_types, arg_type, self.no_position(ctx), self.no_info(ctx)) body = None assert not ctx.var_aliases for instance in sorted: ctx.var_aliases = {} assert not ctx.current_function if instance.type.name != BOOL_TYPE: raise InvalidProgramException(instance.node, 'invalid.predicate') ctx.current_function = instance ctx.module = instance.module self.bind_type_vars(instance, ctx) # Replace variables in instance by variables in root, since we use the # parameter names from root. for root_name, current_name in zip(root.args.keys(), instance.args.keys()): root_var = root.args[root_name] # For the receiver parameter, we need it to have the same sil_name as # that of the root, but the type of the current instance when translating # it, otherwise some fields/functions/predicates may not be found. if root_name == next(iter(root.args.keys())): root_var = copy.copy(root_var) root_var.type = instance.cls ctx.set_alias(current_name, root_var) actual_body_start = 0 while (actual_body_start < len(instance.node.body) and isinstance( instance.node.body[actual_body_start], ast.Expr) and isinstance(instance.node.body[actual_body_start].value, ast.Str)): actual_body_start += 1 if len(instance.node.body[actual_body_start:]) != 1: raise InvalidProgramException(instance.node, 'invalid.predicate') content = instance.node.body[actual_body_start] if isinstance(content, ast.Return): content = content.value stmt, current = self.translate_expr(content, ctx, impure=True, target_type=self.viper.Bool) if stmt: raise InvalidProgramException(instance.node, 'invalid.predicate') has_type = self.type_factory.type_check( self_var_ref, instance.cls, self.to_position(instance.node, ctx), ctx) implication = self.viper.Implies( has_type, current, self.to_position(instance.node, ctx), self.no_info(ctx)) ctx.current_function = None if body: body = self.viper.And(body, implication, self.to_position(root.node, ctx), self.no_info(ctx)) else: body = implication ctx.var_aliases = {} body = self.viper.And(arg_types, body, self.no_position(ctx), self.no_info(ctx)) return self.viper.Predicate(name, args, body, self.to_position(root.node, ctx), self.no_info(ctx))
def translate_forall(self, node: ast.Call, ctx: Context, impure=False) -> StmtsAndExpr: domain_node = node.args[0] lambda_ = node.args[1] variables = [] lambda_prefix = construct_lambda_prefix( lambda_.lineno, getattr(lambda_, 'col_offset', None)) lambda_prefix += '$' arg = lambda_.args.args[0] var = ctx.actual_function.get_variable(lambda_prefix + arg.arg) variables.append(var.decl) ctx.set_alias(arg.arg, var, None) if isinstance(lambda_.body, ast.Tuple): if not len(lambda_.body.elts) == 2: raise InvalidProgramException(node, 'invalid.forall') body_stmt, rhs = self.translate_expr(lambda_.body.elts[0], ctx, self.viper.Bool, impure) triggers = self._translate_triggers(lambda_.body, node, ctx) else: body_type = self.get_type(lambda_.body, ctx) if not body_type or body_type.name != BOOL_TYPE: raise InvalidProgramException(node, 'invalid.forall') body_stmt, rhs = self.translate_expr(lambda_.body, ctx, self.viper.Bool, impure) triggers = [] ctx.remove_alias(arg.arg) if body_stmt: raise InvalidProgramException(node, 'purity.violated') dom_stmt, lhs, always_use = self._create_quantifier_contains_expr( var.ref(), domain_node, ctx) if dom_stmt: raise InvalidProgramException(domain_node, 'purity.violated') lhs = self.unwrap(lhs) implication = self.viper.Implies(lhs, rhs, self.to_position(node, ctx), self.no_info(ctx)) if always_use or not triggers: # Add lhs of the implication, which the user cannot write directly # in this exact form. # If we always do this, we apparently deactivate the automatically # generated triggers and things are actually worse. # Change: We always do this now. try: # Depending on the collection expression, this doesn't always # work (malformed trigger); in that case, we just don't do it. lhs_trigger = self.viper.Trigger([lhs], self.no_position(ctx), self.no_info(ctx)) triggers = [lhs_trigger] + triggers except Exception: pass var_type_check = self.type_check(var.ref(), var.type, self.no_position(ctx), ctx, False) implication = self.viper.Implies(var_type_check, implication, self.to_position(node, ctx), self.no_info(ctx)) forall = self.viper.Forall(variables, triggers, implication, self.to_position(node, ctx), self.no_info(ctx)) return dom_stmt, forall