def havoc_state_except_self(self, state: State, res: List[Stmt], ctx: Context, pos=None): """ Havocs all contract state except self and self-allocated. """ with ctx.inline_scope(None): def inlined_pre_state(name: str) -> str: return ctx.inline_prefix + mangled.pre_state_var_name(name) old_state_for_performs = self.state(inlined_pre_state, ctx) for val in old_state_for_performs.values(): ctx.new_local_vars.append(val.var_decl(ctx, pos)) self.copy_state(ctx.current_state, old_state_for_performs, res, ctx, unless=lambda n: not self.is_allocation(n)) self.copy_state(ctx.current_old_state, old_state_for_performs, res, ctx, unless=self.is_allocation) self.havoc_state(state, res, ctx, pos, unless=self._is_self) with ctx.state_scope(ctx.current_state, old_state_for_performs): self.expression_translator.assume_own_resources_stayed_constant( res, ctx, pos)
def _add_local_var(self, node: ast.Name, ctx: Context): """ Adds the local variable to the context. """ pos = self.to_position(node, ctx) variable_name = node.id mangled_name = ctx.new_local_var_name(variable_name) var = TranslatedPureIndexedVar(variable_name, mangled_name, node.type, self.viper_ast, pos) ctx.locals[variable_name] = var
def havoc_var(viper_ast: ViperAST, viper_type, ctx: Context): if ctx.is_pure_function: pure_idx = ctx.next_pure_var_index() function_result = viper_ast.Result(struct_type(viper_ast)) return struct_get_idx(viper_ast, function_result, pure_idx, viper_type) else: havoc_name = ctx.new_local_var_name('havoc') havoc = viper_ast.LocalVarDecl(havoc_name, viper_type) ctx.new_local_vars.append(havoc) return havoc.localVar()
def fail_if(self, cond: Expr, stmts: List[Stmt], res: List[Expr], ctx: Context, pos=None, info=None): assert isinstance(ctx.success_var, TranslatedPureIndexedVar) cond_var = TranslatedPureIndexedVar('cond', 'cond', VYPER_BOOL, self.viper_ast, pos, info) cond_local_var = cond_var.local_var(ctx) assign = self.viper_ast.EqCmp(cond_local_var, cond, pos, info) expr = self.viper_ast.Implies(ctx.pure_conds, assign, pos, info) if ctx.pure_conds else assign res.append(expr) # Fail if the condition is true fail_cond = self.viper_ast.And( ctx.pure_conds, cond_local_var, pos, info) if ctx.pure_conds else cond_local_var old_success_idx = ctx.success_var.evaluate_idx(ctx) ctx.success_var.new_idx() assign = self.viper_ast.EqCmp(ctx.success_var.local_var(ctx), self.viper_ast.FalseLit(), pos, info) expr = self.viper_ast.Implies(ctx.pure_conds, assign, pos, info) if ctx.pure_conds else assign res.append(expr) ctx.pure_success.append((fail_cond, ctx.success_var.evaluate_idx(ctx))) ctx.success_var.idx = old_success_idx # If we did not fail, we know that the condition is not true negated_local_var = self.viper_ast.Not(cond_local_var, pos, info) ctx.pure_conds = self.viper_ast.And(ctx.pure_conds, negated_local_var, pos, info)\ if ctx.pure_conds else negated_local_var
def translate_If(self, node: ast.If, res: List[Expr], ctx: Context): pre_locals = self._local_variable_snapshot(ctx) pre_conds = ctx.pure_conds pos = self.to_position(node, ctx) cond = self.expression_translator.translate(node.test, res, ctx) cond_var = TranslatedPureIndexedVar('cond', 'cond', VYPER_BOOL, self.viper_ast, pos) assign = self.viper_ast.EqCmp(cond_var.local_var(ctx), cond, pos) expr = self.viper_ast.Implies(ctx.pure_conds, assign, pos) if ctx.pure_conds else assign res.append(expr) cond_local_var = cond_var.local_var(ctx) # "Then" branch with ctx.new_local_scope(): # Update condition ctx.pure_conds = self.viper_ast.And( pre_conds, cond_local_var, pos) if pre_conds else cond_local_var with self.assignment_translator.assume_everything_has_wrapped_information( ): self.translate_stmts(node.body, res, ctx) # Get current state of local variable state after "then"-branch then_locals = self._local_variable_snapshot(ctx) # Reset indices of local variables in context self._reset_variable_to_snapshot(pre_locals, ctx) # "Else" branch with ctx.new_local_scope(): # Update condition negated_local_var = self.viper_ast.Not(cond_local_var, pos) ctx.pure_conds = self.viper_ast.And( pre_conds, negated_local_var, pos) if pre_conds else negated_local_var with self.assignment_translator.assume_everything_has_wrapped_information( ): self.translate_stmts(node.orelse, res, ctx) # Get current state of local variable state after "else"-branch else_locals = self._local_variable_snapshot(ctx) # Update condition ctx.pure_conds = pre_conds # Merge variable changed in the "then" or "else" branch self._merge_snapshots(cond_local_var, then_locals, else_locals, res, ctx)
def translate_Attribute(self, node: ast.Attribute, _: List[Stmt], ctx: Context) -> Expr: pos = self.to_position(node, ctx) assert isinstance(node.value, ast.Name) interface = ctx.current_program.interfaces[node.value.id] with ctx.program_scope(interface): self_address = ctx.self_address or helpers.self_address( self.viper_ast) return self._resource(node.attr, [self_address], ctx, pos)
def translate_Subscript(self, node: ast.Subscript, res: List[Stmt], ctx: Context) -> Expr: pos = self.to_position(node, ctx) other_address = self.specification_translator.translate( node.index, res, ctx) if isinstance(node.value, ast.Attribute): assert isinstance(node.value.value, ast.Name) interface = ctx.current_program.interfaces[node.value.value.id] with ctx.program_scope(interface): return self._resource(node.value.attr, [other_address], ctx, pos) elif isinstance(node.value, ast.Name): return self._resource(node.value.id, [other_address], ctx, pos) else: assert False
def havoc_state(self, state: State, res: List[Stmt], ctx: Context, pos=None, unless=None): havocs = [] for var in state.values(): if unless and unless(var.name): continue havoc_name = ctx.new_local_var_name('havoc') havoc_var = self.viper_ast.LocalVarDecl(havoc_name, var.var_decl(ctx).typ(), pos) ctx.new_local_vars.append(havoc_var) havocs.append( self.viper_ast.LocalVarAssign(var.local_var(ctx), havoc_var.localVar(), pos)) self.seqn_with_info(havocs, "Havoc state", res)
def translate_ReceiverCall(self, node: ast.ReceiverCall, res: List[Stmt], ctx: Context) -> Expr: pos = self.to_position(node, ctx) if isinstance(node.receiver, ast.Name): interface = ctx.current_program.interfaces[node.receiver.id] address = ctx.self_address or helpers.self_address(self.viper_ast) elif isinstance(node.receiver, ast.Subscript): assert isinstance(node.receiver.value, ast.Attribute) assert isinstance(node.receiver.value.value, ast.Name) interface_name = node.receiver.value.value.id interface = ctx.current_program.interfaces[interface_name] address = self.specification_translator.translate( node.receiver.index, res, ctx) else: assert False with ctx.program_scope(interface): args = [ self.specification_translator.translate(arg, res, ctx) for arg in node.args ] args.append(address) return self._resource(node.name, args, ctx, pos)
def translate(self, function: VyperFunction, ctx: Context) -> Function: with ctx.function_scope(): pos = self.to_position(function.node, ctx) ctx.function = function ctx.is_pure_function = True args = { name: self._translate_pure_non_local_var(var, ctx) for name, var in function.args.items() } state = { names.SELF: TranslatedVar(names.SELF, mangled.present_state_var_name(names.SELF), types.AnyStructType(), self.viper_ast, pos) } ctx.present_state = state ctx.old_state = ctx.present_state ctx.pre_state = ctx.present_state ctx.issued_state = ctx.present_state ctx.current_state = ctx.present_state ctx.current_old_state = ctx.present_state ctx.args = args.copy() ctx.locals = {} ctx.success_var = TranslatedPureIndexedVar(names.SUCCESS, mangled.SUCCESS_VAR, types.VYPER_BOOL, self.viper_ast) ctx.return_label = None ctx.revert_label = None ctx.result_var = TranslatedPureIndexedVar( names.RESULT, mangled.RESULT_VAR, function.type.return_type, self.viper_ast) body = [] # State type assumptions for state_var in ctx.present_state.values(): type_assumptions = self.type_translator.type_assumptions( state_var.local_var(ctx), state_var.type, ctx) body.extend(type_assumptions) # Assume type assumptions for self address self_address = helpers.self_address(self.viper_ast) self_address_ass = self.type_translator.type_assumptions( self_address, types.VYPER_ADDRESS, ctx) body.extend(self_address_ass) # Assume type assumptions for arguments for var in function.args.values(): local_var = args[var.name].local_var(ctx) assumptions = self.type_translator.type_assumptions( local_var, var.type, ctx) body.extend(assumptions) # If we do not encounter an exception we will return success ctx.success_var.new_idx() body.append( self.viper_ast.EqCmp(ctx.success_var.local_var(ctx), self.viper_ast.TrueLit())) self.statement_translator.translate_stmts(function.node.body, body, ctx) viper_struct_type = helpers.struct_type(self.viper_ast) function_result = self.viper_ast.Result(viper_struct_type) def partial_unfinished_cond_expression(cond_and_idx): cond, idx = cond_and_idx def unfinished_cond_expression(expr): val = helpers.struct_get_idx(self.viper_ast, function_result, idx, viper_type, pos) return self.viper_ast.CondExp(cond, val, expr) return unfinished_cond_expression # Generate success variable viper_type = self.viper_ast.Bool unfinished_cond_expressions = list( map(partial_unfinished_cond_expression, ctx.pure_success)) value = reduce(lambda expr, func: func(expr), reversed(unfinished_cond_expressions), self.viper_ast.TrueLit()) # Set success variable at slot 0 success_var = helpers.struct_pure_get_success( self.viper_ast, function_result, pos) success_cond_expr = self.viper_ast.CondExp( value, self.viper_ast.TrueLit(), self.viper_ast.FalseLit()) body.append(self.viper_ast.EqCmp(success_var, success_cond_expr)) # Generate result variable viper_type = self.viper_ast.Bool default_value = self.viper_ast.TrueLit() if function.type.return_type: viper_type = self.type_translator.translate( function.type.return_type, ctx) default_value = self.type_translator.default_value( function.node, function.type.return_type, body, ctx) unfinished_cond_expressions = list( map(partial_unfinished_cond_expression, ctx.pure_returns)) value = reduce(lambda expr, func: func(expr), reversed(unfinished_cond_expressions), default_value) # Set result variable at slot 1 result_var = helpers.struct_pure_get_result( self.viper_ast, function_result, viper_type, pos) body.append(self.viper_ast.EqCmp(result_var, value)) # Arguments have to be TranslatedVar. Therefore, transform the non-local TranslatedPureIndexedVar to # local TranslatedVar. arg_transform = [] new_args = {} for arg_name, arg in args.items(): assert isinstance(arg, TranslatedPureIndexedVar) if not arg.is_local: lhs = arg.local_var(ctx) new_arg = TranslatedVar(arg.name, arg.mangled_name, arg.type, arg.viper_ast, arg.pos, arg.info, is_local=True) rhs = new_arg.local_var(ctx) arg_transform.append(self.viper_ast.EqCmp(lhs, rhs)) new_args[arg_name] = new_arg body = arg_transform + body args_list = [ arg.var_decl(ctx) for arg in chain(state.values(), new_args.values()) ] viper_name = mangled.pure_function_name(function.name) function = self.viper_ast.Function( viper_name, args_list, helpers.struct_type(self.viper_ast), [], body, None, pos) return function
def evaluate_idx(self, ctx: Context) -> int: if not self.idx: self.idx = ctx.next_pure_var_index() return self.idx
def translate_Raise(self, node: ast.Raise, res: List[Expr], ctx: Context): pos = self.to_position(node, ctx) self.fail_if(self.viper_ast.TrueLit(pos), [], res, ctx, pos) ctx.pure_conds = self.viper_ast.FalseLit(pos)
def translate_Continue(self, node: ast.Continue, res: List[Expr], ctx: Context): ctx.pure_continues.append( (ctx.pure_conds, self._local_variable_snapshot(ctx))) ctx.pure_conds = self.viper_ast.FalseLit()
def translate_For(self, node: ast.For, res: List[Expr], ctx: Context): pos = self.to_position(node, ctx) pre_conds = ctx.pure_conds self._add_local_var(node.target, ctx) loop_var_name = node.target.id loop_var = ctx.locals[loop_var_name] assert isinstance(loop_var, TranslatedPureIndexedVar) array = self.expression_translator.translate_top_level_expression( node.iter, res, ctx) loop_var.is_local = False array_var = TranslatedPureIndexedVar('array', 'array', node.iter.type, self.viper_ast, pos, is_local=False) res.append(self.viper_ast.EqCmp(array_var.local_var(ctx), array, pos)) array_local_var = array_var.local_var(ctx) times = node.iter.type.size lpos = self.to_position(node.target, ctx) rpos = self.to_position(node.iter, ctx) if times > 0: has_numeric_array = types.is_numeric(node.target.type) loop_invariants = ctx.function.loop_invariants.get(node) if loop_invariants: # loop-array expression ctx.loop_arrays[loop_var_name] = array_local_var # New variable loop-idx loop_idx_var = TranslatedPureIndexedVar( 'idx', 'idx', VYPER_UINT256, self.viper_ast, pos) ctx.loop_indices[loop_var_name] = loop_idx_var loop_idx_local_var = loop_idx_var.local_var(ctx) # Havoc used variables loop_used_var = {} for var_name in ctx.function.analysis.loop_used_names.get( loop_var_name, []): if var_name == loop_var_name: continue var = ctx.locals.get(var_name) if var and isinstance(var, TranslatedPureIndexedVar): # Create new variable mangled_name = ctx.new_local_var_name(var.name) new_var = TranslatedPureIndexedVar( var.name, mangled_name, var.type, var.viper_ast, var.pos, var.info, var.is_local) copy_expr = self.viper_ast.EqCmp( new_var.local_var(ctx), var.local_var(ctx), rpos) res.append(copy_expr) loop_used_var[var.name] = new_var # Havoc old var var.new_idx() var.is_local = False # Assume loop 0 <= index < |array| loop_idx_ge_zero = self.viper_ast.GeCmp( loop_idx_local_var, self.viper_ast.IntLit(0), rpos) times_lit = self.viper_ast.IntLit(times) loop_idx_lt_array_size = self.viper_ast.LtCmp( loop_idx_local_var, times_lit, rpos) loop_idx_assumption = self.viper_ast.And( loop_idx_ge_zero, loop_idx_lt_array_size, rpos) res.append(loop_idx_assumption) # Set loop variable to array[index] array_at = self.viper_ast.SeqIndex(array_local_var, loop_idx_local_var, rpos) if has_numeric_array: array_at = helpers.w_wrap(self.viper_ast, array_at) set_loop_var = self.viper_ast.EqCmp(loop_var.local_var(ctx), array_at, lpos) res.append(set_loop_var) with ctx.old_local_variables_scope(loop_used_var): with ctx.state_scope(ctx.current_state, ctx.current_state): for loop_invariant in loop_invariants: cond_pos = self.to_position(loop_invariant, ctx) inv = self.specification_translator.translate_pre_or_postcondition( loop_invariant, res, ctx) inv_var = TranslatedPureIndexedVar( 'inv', 'inv', VYPER_BOOL, self.viper_ast, cond_pos) inv_local_var = inv_var.local_var(ctx) assign = self.viper_ast.EqCmp( inv_local_var, inv, cond_pos) expr = self.viper_ast.Implies( pre_conds, assign, cond_pos) if pre_conds else assign res.append(expr) ctx.pure_conds = self.viper_ast.And(ctx.pure_conds, inv_local_var, pos)\ if ctx.pure_conds else inv_local_var # Store state before loop body pre_locals = self._local_variable_snapshot(ctx) pre_loop_iteration_conds = ctx.pure_conds # Loop Body with ctx.break_scope(): with ctx.continue_scope(): with self.assignment_translator.assume_everything_has_wrapped_information( ): with ctx.new_local_scope(): self.translate_stmts(node.body, res, ctx) ctx.pure_conds = pre_loop_iteration_conds # After loop body increase idx loop_idx_inc = self.viper_ast.Add( loop_idx_local_var, self.viper_ast.IntLit(1), pos) loop_idx_var.new_idx() loop_idx_local_var = loop_idx_var.local_var(ctx) res.append( self.viper_ast.EqCmp(loop_idx_local_var, loop_idx_inc, pos)) # Break if we reached the end of the array we iterate over loop_idx_eq_times = self.viper_ast.EqCmp( loop_idx_local_var, times_lit, pos) cond_var = TranslatedPureIndexedVar( 'cond', 'cond', VYPER_BOOL, self.viper_ast, pos) cond_local_var = cond_var.local_var(ctx) res.append( self.viper_ast.EqCmp(cond_local_var, loop_idx_eq_times, pos)) break_cond = self.viper_ast.And(ctx.pure_conds, cond_local_var, pos)\ if ctx.pure_conds else cond_local_var ctx.pure_breaks.append( (break_cond, self._local_variable_snapshot(ctx))) # Assume that only one of the breaks happened only_one_break_cond = self._xor_conds( [x for x, _ in ctx.pure_breaks]) assert only_one_break_cond is not None if pre_conds is None: res.append(only_one_break_cond) else: res.append( self.viper_ast.Implies(pre_conds, only_one_break_cond)) # Merge snapshots prev_snapshot = pre_locals for cond, snapshot in reversed(ctx.pure_breaks): prev_snapshot = self._merge_snapshots( cond, snapshot, prev_snapshot, res, ctx) else: # Unroll loop if we have no loop invariants pre_loop_locals = self._local_variable_snapshot(ctx) with ctx.break_scope(): for i in range(times): pre_loop_iteration_conds = ctx.pure_conds with ctx.continue_scope(): idx = self.viper_ast.IntLit(i, lpos) array_at = self.viper_ast.SeqIndex( array_local_var, idx, rpos) if has_numeric_array: array_at = helpers.w_wrap( self.viper_ast, array_at) loop_var.new_idx() var_set = self.viper_ast.EqCmp( loop_var.local_var(ctx), array_at, lpos) res.append(var_set) pre_it_locals = self._local_variable_snapshot(ctx) with self.assignment_translator.assume_everything_has_wrapped_information( ): with ctx.new_local_scope(): self.translate_stmts(node.body, res, ctx) post_it_locals = self._local_variable_snapshot(ctx) ctx.pure_continues.append( (ctx.pure_conds, post_it_locals)) prev_snapshot = pre_it_locals for cond, snapshot in reversed(ctx.pure_continues): prev_snapshot = self._merge_snapshots( cond, snapshot, prev_snapshot, res, ctx) ctx.pure_conds = pre_loop_iteration_conds post_loop_locals = self._local_variable_snapshot(ctx) ctx.pure_breaks.append((ctx.pure_conds, post_loop_locals)) prev_snapshot = pre_loop_locals for cond, snapshot in reversed(ctx.pure_breaks): prev_snapshot = self._merge_snapshots( cond, snapshot, prev_snapshot, res, ctx) ctx.pure_conds = pre_conds
def type_assumptions(self, node, type: VyperType, ctx: Context) -> List[Expr]: """ Computes the assumptions for either array length or number bounds of nested structures. If mode == 0: constructs bounds If mode == 1: constructs array lengths """ def construct(type, node): ret = [] # If we encounter a bounded primitive type we add the following assumption: # x >= lower # x <= upper # where x is said integer if types.is_bounded(type): lower = self.viper_ast.IntLit(type.lower) upper = self.viper_ast.IntLit(type.upper) lcmp = self.viper_ast.LeCmp(lower, node) ucmp = self.viper_ast.LeCmp(node, upper) # If the no_overflows config option is enabled, we only assume non-negativity for uints if ctx.program.config.has_option(names.CONFIG_NO_OVERFLOWS): if types.is_unsigned(type): bounds = lcmp else: bounds = self.viper_ast.TrueLit() else: bounds = self.viper_ast.And(lcmp, ucmp) ret.append(bounds) elif type == types.NON_NEGATIVE_INT: lower = self.viper_ast.IntLit(0) lcmp = self.viper_ast.LeCmp(lower, node) ret.append(lcmp) # If we encounter a map, we add the following assumptions: # forall k: Key :: construct(map_get(k)) # forall k: Key :: map_get(k) <= map_sum() # where constuct constructs the assumption for the values contained # in the map (may be empty) elif isinstance(type, MapType): key_type = self.translate(type.key_type, ctx) value_type = self.translate(type.value_type, ctx) quant_var_name = ctx.new_quantified_var_name() quant_decl = self.viper_ast.LocalVarDecl( quant_var_name, key_type) quant = quant_decl.localVar() new_node = helpers.map_get(self.viper_ast, node, quant, key_type, value_type) trigger = self.viper_ast.Trigger([new_node]) sub_ret = construct(type.value_type, new_node) for r in sub_ret: quantifier = self.viper_ast.Forall([quant_decl], [trigger], r) ret.append(quantifier) if types.is_unsigned(type.value_type): mp_sum = helpers.map_sum(self.viper_ast, node, key_type) r = self.viper_ast.LeCmp(new_node, mp_sum) quantifier = self.viper_ast.Forall([quant_decl], [trigger], r) ret.append(quantifier) # If we encounter an array, we add the follwing assumptions: # |array| == array_size # forall i: Int :: 0 <= i && i < |array| ==> construct(array[i]) # where construct recursively constructs the assumptions for nested arrays and maps elif isinstance(type, ArrayType): array_len = self.viper_ast.SeqLength(node) size = self.viper_ast.IntLit(type.size) if type.is_strict: comp = self.viper_ast.EqCmp(array_len, size) else: comp = self.viper_ast.LeCmp(array_len, size) ret.append(comp) quant_var_name = ctx.new_quantified_var_name() quant_decl = self.viper_ast.LocalVarDecl( quant_var_name, self.viper_ast.Int) quant = quant_decl.localVar() new_node = helpers.array_get(self.viper_ast, node, quant, type.element_type) trigger = self.viper_ast.Trigger([new_node]) leq = self.viper_ast.LeCmp(self.viper_ast.IntLit(0), quant) le = self.viper_ast.LtCmp(quant, self.viper_ast.SeqLength(node)) bounds = self.viper_ast.And(leq, le) sub_ret = construct(type.element_type, new_node) for r in sub_ret: implies = self.viper_ast.Implies(bounds, r) quantifier = self.viper_ast.Forall([quant_decl], [trigger], implies) ret.append(quantifier) elif isinstance(type, (ContractType, InterfaceType)): return construct(types.VYPER_ADDRESS, node) # If we encounter a struct type we simply add the necessary assumptions for # all struct members # Additionally, we add an assumption about the type tag elif isinstance(type, StructType): for member_name, member_type in type.member_types.items(): viper_type = self.translate(member_type, ctx) get = helpers.struct_get(self.viper_ast, node, member_name, viper_type, type) ret.extend(construct(member_type, get)) type_tag = self.viper_ast.IntLit( mangled.struct_type_tag(type.name, type.kind)) get_tag = helpers.struct_type_tag(self.viper_ast, node) ret.append(self.viper_ast.EqCmp(get_tag, type_tag)) return ret with ctx.quantified_var_scope(): return construct(type, node)
def translate(self, function: VyperFunction, ctx: Context) -> List[Function]: with ctx.function_scope(): with ctx.lemma_scope(): pos = self.to_position(function.node, ctx) ctx.function = function args = { name: self._translate_non_local_var(var, ctx) for name, var in function.args.items() } ctx.present_state = {} ctx.old_state = {} ctx.pre_state = {} ctx.issued_state = {} ctx.current_state = {} ctx.current_old_state = {} ctx.args = args.copy() ctx.locals = {} ctx.success_var = None ctx.return_label = None ctx.revert_label = None ctx.result_var = None preconditions = [] # Type assumptions for arguments for var in function.args.values(): local_var = args[var.name].local_var(ctx) assumptions = self.type_translator.type_assumptions( local_var, var.type, ctx) preconditions.extend(assumptions) # Explicit preconditions for precondition in function.preconditions: stmts = [] preconditions.append( self.specification_translator. translate_pre_or_postcondition(precondition, stmts, ctx)) assert not stmts # If we assume uninterpreted function and assert the interpreted ones, the lemma has no body body = None if function.is_interpreted( ) else self.viper_ast.TrueLit() postconditions = [] interpreted_postconditions = [] if function.is_interpreted(): # Without a body, we must ensure that "result == true" viper_result = self.viper_ast.Result( self.viper_ast.Bool, pos) postconditions.append( self.viper_ast.EqCmp(viper_result, self.viper_ast.TrueLit(), pos)) for idx, stmt in enumerate(function.node.body): assert isinstance(stmt, ast.ExprStmt) expr = stmt.value stmts = [] post = self.specification_translator.translate( expr, stmts, ctx) post_pos = self.to_position(stmt, ctx, rules=rules.LEMMA_FAIL) viper_result = self.viper_ast.Result( self.viper_ast.Bool, post_pos) postconditions.append( self.viper_ast.EqCmp(viper_result, post, post_pos)) if function.is_interpreted(): # If the function is interpreted, generate also an interpreted version of the lemma step with ctx.interpreted_scope(): interpreted_post = self.specification_translator.translate( expr, stmts, ctx) interpreted_postconditions.append( self.viper_ast.EqCmp(viper_result, interpreted_post, post_pos)) assert not stmts args_list = [arg.var_decl(ctx) for arg in args.values()] viper_name = mangled.lemma_name(function.name) viper_functions = [ self.viper_ast.Function(viper_name, args_list, self.viper_ast.Bool, preconditions, postconditions, body, pos) ] if function.is_interpreted(): # If we have interpreted postconditions, generate a second function. # This second function has always a body, the same arguments and the same preconditions, but uses # interpreted mul, div and mod instead of the uninterpreted $Int-functions in the postconditions. viper_functions.append( self.viper_ast.Function("interpreted$" + viper_name, args_list, self.viper_ast.Bool, preconditions, interpreted_postconditions, self.viper_ast.TrueLit(), pos)) return viper_functions