Esempio n. 1
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
Esempio n. 2
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