def translate_unfold(self, node: ast.Call, ctx: Context) -> StmtsAndExpr: """ Translates a call to the Unfold() contract function. """ if len(node.args) != 1: raise InvalidProgramException(node, 'invalid.contract.call') if not isinstance(node.args[0], ast.Call): raise InvalidProgramException(node, 'invalid.contract.call') if get_func_name(node.args[0]) in ('Acc', 'Rd'): pred_call = node.args[0].args[0] else: pred_call = node.args[0] target_pred = self.get_target(pred_call, ctx) if (target_pred and (not isinstance(target_pred, PythonMethod) or not target_pred.predicate)): raise InvalidProgramException(node, 'invalid.contract.call') pred_stmt, pred = self.translate_expr(node.args[0], ctx, self.viper.Bool, True) if self._is_family_fold(node): # Predicate called on receiver, so it belongs to a family if ctx.ignore_family_folds: return [], None if pred_stmt: raise InvalidProgramException(node, 'purity.violated') unfold = self.viper.Unfold(pred, self.to_position(node, ctx), self.no_info(ctx)) return [unfold], None
def translate_unfolding(self, node: ast.Call, ctx: Context, impure=False) -> StmtsAndExpr: """ Translates a call to the Unfolding() contract function. """ if len(node.args) != 2: raise InvalidProgramException(node, 'invalid.contract.call') if not isinstance(node.args[0], ast.Call): raise InvalidProgramException(node, 'invalid.contract.call') if get_func_name(node.args[0]) in ('Acc', 'Rd'): pred_call = node.args[0].args[0] else: pred_call = node.args[0] target_pred = self.get_target(pred_call, ctx) if (target_pred and (not isinstance(target_pred, PythonMethod) or not target_pred.predicate)): raise InvalidProgramException(node, 'invalid.contract.call') pred_stmt, pred = self.translate_expr(node.args[0], ctx, self.viper.Bool, True) if pred_stmt: raise InvalidProgramException(node, 'purity.violated') expr_stmt, expr = self.translate_expr(node.args[1], ctx) if expr_stmt: raise InvalidProgramException(node, 'purity.violated') expr = self.unwrap(expr) unfold = self.viper.Unfolding(pred, expr, self.to_position(node, ctx), self.no_info(ctx)) return expr_stmt, unfold
def translate_acc_global(self, node: ast.Call, perm: Expr, ctx: Context) -> StmtsAndExpr: """ Translates an access permission to a global variable. """ var = self.get_target(node.args[0], ctx) if not isinstance(var, PythonGlobalVar): raise InvalidProgramException(node, 'invalid.acc') if var.is_final: raise InvalidProgramException(node, 'permission.to.final.var') pos = self.to_position(node, ctx) info = self.no_info(ctx) var_func = self.viper.FuncApp(var.sil_name, [], pos, info, self.viper.Ref, []) var_type = self.translate_type(var.type, ctx) field = self.viper.Field(GLOBAL_VAR_FIELD, var_type, pos, info) field_acc = self.viper.FieldAccess(var_func, field, pos, info) pred = self.viper.FieldAccessPredicate(field_acc, perm, pos, info) # Add type information if var.type.name not in PRIMITIVES: type_info = self.type_check(field_acc, var.type, self.no_position(ctx), ctx) pred = self.viper.And(pred, type_info, pos, info) return [], pred
def translate_may_set(self, node: ast.Call, ctx: Context) -> StmtsAndExpr: """ Translates a call to MaySet(). """ pos = self.to_position(node, ctx) info = self.no_info(ctx) stmt, rec = self.translate_expr(node.args[0], ctx) rec_type = self.get_type(node.args[0], ctx) if stmt: raise InvalidProgramException(node.args[0], 'purity.violated') if not isinstance(node.args[1], ast.Str): raise InvalidProgramException(node.args[1], 'invalid.may.set') field = rec_type.get_field(node.args[1].s) if not field: raise InvalidProgramException(node.args[1], 'invalid.may.set') may_set_pred = self.get_may_set_predicate(rec, field, ctx, pos) sil_field = self.viper.Field(field.sil_name, self.translate_type(field.type, ctx), pos, info) field_acc = self.viper.FieldAccess(rec, sil_field, pos, info) full_perm = self.viper.FullPerm(pos, info) normal_acc = self._translate_acc_field(field_acc, field.type, full_perm, pos, ctx) normal_perm = self.viper.CurrentPerm(field_acc, pos, info) have_normal_perm = self.viper.PermGtCmp(normal_perm, self.viper.NoPerm(pos, info), pos, info) result_ex = self.viper.CondExp(have_normal_perm, normal_acc, may_set_pred, pos, info) unknown = self.get_unknown_bool(ctx) result_in = self.viper.CondExp(unknown, normal_acc, may_set_pred, pos, info) return [], self.viper.InhaleExhaleExp(result_in, result_ex, pos, info)
def translate_acc_field(self, node: ast.Call, perm: Expr, ctx: Context) -> StmtsAndExpr: assert isinstance(node.args[0], ast.Attribute) field = self.get_target(node.args[0], ctx) if not isinstance(field, PythonField): raise InvalidProgramException(node, 'invalid.acc') stmt, field_acc = self.translate_expr(node.args[0], ctx) if stmt: raise InvalidProgramException(node, 'purity.violated') field_type = self.get_type(node.args[0], ctx) pred = self._translate_acc_field(field_acc, field_type, perm, self.to_position(node, ctx), ctx) return [], pred
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 translate_obligation_contractfunc_call(self, node: ast.Call, ctx: Context) -> StmtsAndExpr: """Translate a call to obligation contract function.""" func_name = get_func_name(node) if func_name == 'MustTerminate': return self._translate_must_terminate(node, ctx) elif func_name == 'MustRelease': return self._translate_must_release(node, ctx) elif func_name == 'WaitLevel': raise InvalidProgramException(node, 'invalid.wait_level.use') elif func_name == 'Level': raise InvalidProgramException(node, 'invalid.level.use') else: raise UnsupportedException(node, 'Unsupported contract function.')
def _get_method_call(self, receiver: PythonType, func_name: str, args: List[Expr], arg_types: List[PythonType], targets: List['silver.ast.LocalVarRef'], node: ast.AST, ctx: Context) -> List[Stmt]: """ Creates a method call to the method called func_name, with the given receiver and arguments. Boxes arguments if necessary. This method only handles receivers of non-union types. """ if receiver: target_cls = receiver func = target_cls.get_method(func_name) else: func = ctx.module.methods[func_name] if not func: raise InvalidProgramException(node, 'unknown.method.called') actual_args = [] for arg, param, _ in zip(args, func.get_args(), arg_types): if param.type.name == PRIMITIVE_BOOL_TYPE: actual_arg = self.to_bool(arg, ctx) elif param.type.name == '__prim__int': actual_arg = self.to_int(arg, ctx) else: actual_arg = self.to_ref(arg, ctx) actual_args.append(actual_arg) sil_name = func.sil_name call = self.create_method_call_node( ctx, sil_name, actual_args, targets, self.to_position(node, ctx), self.no_info(ctx), target_method=func, target_node=node) return call
def translate_may_create(self, node: ast.Call, ctx: Context) -> StmtsAndExpr: """ Translates a call to MayCreate(). """ pos = self.to_position(node, ctx) stmt, rec = self.translate_expr(node.args[0], ctx) rec_type = self.get_type(node.args[0], ctx) if stmt: raise InvalidProgramException(node.args[0], 'purity.violated') if not isinstance(node.args[1], ast.Str): raise InvalidProgramException(node.args[1], 'invalid.may.create') field = rec_type.get_field(node.args[1].s) if not field: raise InvalidProgramException(node.args[1], 'invalid.may.create') return [], self.get_may_set_predicate(rec, field, ctx, pos)
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_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 _raise_invalid_operation(self, error_type: str, node: ast.AST = None): """Raise InvalidProgramException.""" node = node or self._current_node raise InvalidProgramException( node, 'invalid.io_operation.' + error_type, )
def translate_unwrapped_builtin_predicate(self, node: ast.Call, ctx: Context) -> Expr: args = [] stmt, arg = self.translate_expr(node.args[0], ctx) if stmt: raise InvalidProgramException(node, 'purity.violated') perm = self.viper.FullPerm(self.no_position(ctx), self.no_info(ctx)) return self.translate_builtin_predicate(node, perm, [arg], ctx)
def _translate_triggers(self, body: ast.AST, node: ast.Call, ctx: Context) -> List['silver.ast.Trigger']: """ Assuming the given body is a tuple whose second element is a list literal containing any number of list literals containing expressions, translates those to a list of triggers. """ if (not isinstance(body, ast.Tuple) or len(body.elts) != 2): raise InvalidProgramException(node, 'invalid.trigger') trigger_node = body.elts[1] triggers = [] if not isinstance(trigger_node, ast.List): raise InvalidProgramException(node, 'invalid.trigger') outer = trigger_node if outer.elts: for el in outer.elts: trigger = [] if not isinstance(el, ast.List): raise InvalidProgramException(el, 'invalid.trigger') for inner in el.elts: if (isinstance(inner, ast.Compare) and len(inner.ops) == 1 and isinstance(inner.ops[0], ast.In)): # Use the less complex and more efficient trigger translation we # also use for the domain of the forall quantifier. assert len(inner.comparators) == 1 lhs_stmt, lhs = self.translate_expr(inner.left, ctx) part_stmt, part, valid = self._create_quantifier_contains_expr( lhs, inner.comparators[0], ctx) if part_stmt: raise InvalidProgramException( inner, 'purity.violated') if valid and not part_stmt and not lhs_stmt: trigger.append(part) continue part_stmt, part = self.translate_expr(inner, ctx) if part_stmt: raise InvalidProgramException(inner, 'purity.violated') part = self.unwrap(part) trigger.append(part) trigger = self.viper.Trigger(trigger, self.no_position(ctx), self.no_info(ctx)) triggers.append(trigger) return triggers
def translate_perm_or_int(self, node: ast.AST, ctx: Context): if isinstance(node, ast.Num): stmt, int_val = self.translate_expr(node, ctx, self.viper.Int) if stmt: raise InvalidProgramException(node, 'purity.violated') return int_val, True else: int_or_perm_val = self.translate_perm(node, ctx) return int_or_perm_val, int_or_perm_val.typ() == self.viper.Int
def translate_contract_Call(self, node: ast.Call, ctx: Context) -> Expr: if get_func_name(node) in CONTRACT_WRAPPER_FUNCS: stmt, res = self.translate_expr(node.args[0], ctx, self.viper.Bool, True) if stmt: raise InvalidProgramException(node, 'purity.violated') return res else: raise UnsupportedException(node)
def _translate_must_terminate(self, node: ast.Call, ctx: Context) -> StmtsAndExpr: """Translate a call to ``MustTerminate``.""" if ctx.obligation_context.is_translating_loop(): return self._loop_translator.translate_must_terminate(node, ctx) elif ctx.obligation_context.is_translating_posts: raise InvalidProgramException( node, 'obligation.must_terminate.in_postcondition') else: return self._method_translator.translate_must_terminate(node, ctx)
def translate_terminates_sif(self, node: ast.Call, ctx: Context) -> StmtsAndExpr: """ Translates a call to the TerminatesSif() contract function. """ cond_stmts, cond = self.translate_expr(node.args[0], ctx) rank_stmts, rank = self.translate_expr(node.args[1], ctx) if cond_stmts or rank_stmts: raise InvalidProgramException(node, 'purity.violated') return self.translator.obligation_translator._translate_must_terminate( node, ctx)
def translate_perm_Call(self, node: ast.Call, ctx: Context) -> Expr: func_name = get_func_name(node) if func_name == 'ARP': if not ctx.arp: raise UnsupportedException( node, 'ARP not supported. Use --arp flag.') if len(node.args) == 0: return self.get_arp_for_context(node, ctx) elif len(node.args) == 1: arg0_stmt, arg0 = self.translate_expr(node.args[0], ctx, self.viper.Int) if arg0_stmt: raise InvalidProgramException(node, 'purity.violated') formal_arg = self.viper.LocalVarDecl( 'count', self.viper.Int, self.to_position(node, ctx), self.no_info(ctx)) return self.viper.FuncApp('rdc', [arg0], self.to_position(node, ctx), self.no_info(ctx), self.viper.Perm, [formal_arg]) elif func_name == 'getARP': if not ctx.arp: raise UnsupportedException( node, 'ARP not supported. Use --arp flag.') if len(node.args) == 1: formal_arg = self.viper.LocalVarDecl( 'tk', self.viper.Ref, self.to_position(node, ctx), self.no_info(ctx)) arg0_stmt, arg0 = self.translate_expr(node.args[0], ctx, self.viper.Ref) if arg0_stmt: raise InvalidProgramException(node, 'purity.violated') return self.viper.FuncApp('rd_token', [arg0], self.to_position(node, ctx), self.no_info(ctx), self.viper.Perm, [formal_arg]) call_stmt, call = self.translate_expr(node, ctx, self.viper.Int) if not call_stmt: return call raise InvalidProgramException(node, 'purity.violated')
def translate_perm_Name(self, node: ast.Name, ctx: Context) -> Expr: if node.id == 'RD_PRED': if not ctx.arp: raise UnsupportedException( node, 'ARP not supported. Use --arp flag.') return self.viper.FuncApp('globalRd', [], self.to_position(node, ctx), self.no_info(ctx), self.viper.Perm, {}) stmt, res = self.translate_expr(node, ctx) if stmt: raise InvalidProgramException(node, 'purity.violated') return res
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_declassify(self, node: ast.Call, ctx: Context) -> StmtsAndExpr: """ Translates a call to the Declassify() contract function. """ stmts, expr = self.translate_expr(node.args[0], ctx) if stmts: raise InvalidProgramException(node, 'purity.violated') return [ self.viper.Declassify(expr, self.to_position(node, ctx), self.no_info(ctx)) ], None
def translate_perm_or_int(self, node: ast.AST, ctx: Context): num_class = ast.Num import sys if sys.version_info[1] >= 8: num_class = ast.Constant if isinstance(node, num_class): stmt, int_val = self.translate_expr(node, ctx, self.viper.Int) if stmt: raise InvalidProgramException(node, 'purity.violated') return int_val, True else: int_or_perm_val = self.translate_perm(node, ctx) return int_or_perm_val, int_or_perm_val.typ() == self.viper.Int
def translate_previous(self, node: ast.Call, ctx: Context) -> StmtsAndExpr: arg = node.args[0] if not isinstance(arg, ast.Name): raise InvalidProgramException(node, 'invalid.previous') loop = find_loop_for_previous(node, arg.id) if not loop: raise InvalidProgramException(node, 'invalid.previous') pos = self.to_position(node, ctx) info = self.no_info(ctx) iterator = ctx.loop_iterators[loop].ref() list_field = self.viper.Field('__previous', self.viper.SeqType(self.viper.Ref), pos, info) field_acc = self.viper.FieldAccess(iterator, list_field, pos, info) seq_type = ctx.module.global_module.classes[PSEQ_TYPE] content_type = self.get_type(node.args[0], ctx) type_lit = self.type_factory.translate_type_literal( content_type, pos, ctx) res = self.get_function_call(seq_type, '__create__', [field_acc, type_lit], [None, None], node, ctx) return [], res
def translate_low(self, node: ast.Call, ctx: Context) -> StmtsAndExpr: """ Translates a call to the Low() contract function. """ if len(node.args) != 1: raise UnsupportedException(node, "Low() requires exactly one argument") stmts, expr = self.translate_expr(node.args[0], ctx) if stmts: raise InvalidProgramException(node, 'purity.violated') info = self._create_dyn_check_info(ctx) return [], self.viper.Low(expr, None, self.to_position(node, ctx), info)
def translate_must_invoke_ctoken(self, node: ast.Call, ctx: Context) -> StmtsAndExpr: """Translate a call to ``ctoken``.""" if ctx.obligation_context.is_translating_loop(): return self._loop_translator.translate_may_invoke(node, ctx) elif ctx.obligation_context.is_translating_posts: if ctx.actual_function.name != 'Gap': raise InvalidProgramException( node, 'invalid.postcondition.ctoken_not_allowed') else: return self._method_translator.translate_may_invoke(node, ctx) else: return self._method_translator.translate_may_invoke(node, ctx)
def _translate_eval_io(self, node: ast.Call, ctx: Context) -> StmtsAndExpr: """ Translates a reference to the eval_io IOOperation as usual. Also stores the information that given argument function is among the argument functions used with eval_io in the program; this information is used to generate additional return type postconditions for the result getter of eval_io. """ if not isinstance(node.args[1], ast.Name): raise InvalidProgramException(node, 'invalid.eval.function') target = self.get_target(node.args[1], ctx) if (ctx.current_function.name != 'Eval' and (not isinstance(target, PythonMethod) or not target.pure)): raise InvalidProgramException(node, 'invalid.eval.function') stmt, res = self.translate_io_operation_call(node, ctx) func_stmt, func_arg = self.translate_expr(node.args[1], ctx) if func_stmt: raise InvalidProgramException(node, 'purity.violated') eval_io = self.get_target(node, ctx) arg_type = self.get_type(node.args[1], ctx) if arg_type.name != CALLABLE_TYPE: eval_io.func_args.append((func_arg, arg_type)) return stmt, res
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_eval(self, node: ast.Call, ctx: Context) -> StmtsAndExpr: """ Translates a call to the Eval method (which implements the eval_io IOOperation). The translation is like for other method calls, except we also inhale that the result of the operation is equal to the given function applied to the given argument. """ position = self.to_position(node, ctx) info = self.no_info(ctx) if not isinstance(node.args[1], ast.Name): raise InvalidProgramException(node, 'invalid.eval.function') target = self.get_target(node.args[1], ctx) if not target.pure: raise InvalidProgramException(node, 'invalid.eval.function') arg_stmt, arg = self.translate_expr(node.args[2], ctx) if arg_stmt: raise InvalidProgramException(node, 'purity.violated') arg_type = self.get_type(node.args[2], ctx) func_stmt, func_val = self.translate_normal_call( target, [], [arg], [arg_type], node, ctx) if func_stmt: raise InvalidProgramException(node, 'purity.violated') call_stmt, call = self.translate_normal_call_node(node, ctx) args = [] for arg in node.args: stmt, arg_val = self.translate_expr(arg, ctx) assert not stmt args.append(arg_val) eval_io = self._get_eval_io_operation(ctx) getter = self.create_result_getter(node, eval_io.get_results()[0], ctx, args, eval_io) assume = self.viper.Inhale( self.viper.EqCmp(func_val, getter, position, info), position, info) return call_stmt + [assume], call