Beispiel #1
0
    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)
Beispiel #2
0
 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
Beispiel #3
0
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()
Beispiel #4
0
    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
Beispiel #5
0
    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)
Beispiel #6
0
 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)
Beispiel #7
0
 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
Beispiel #8
0
    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)
Beispiel #9
0
    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)
Beispiel #10
0
    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
Beispiel #11
0
 def evaluate_idx(self, ctx: Context) -> int:
     if not self.idx:
         self.idx = ctx.next_pure_var_index()
     return self.idx
Beispiel #12
0
 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)
Beispiel #13
0
 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()
Beispiel #14
0
    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
Beispiel #15
0
    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)
Beispiel #16
0
    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