diff --git a/CHANGELOG.md b/CHANGELOG.md index 218866c..1241859 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] ### Added +- Added support for `EXIT` opcode via `exit : (string) => 'a` function (behaves same as `ABORT`, but consumes all gas). - Compiler warnings for the follwing: shadowing, negative spends, division by zero, unused functions, unused includes, unused stateful annotations, unused variables, unused parameters, unused user-defined type, dead return value. - The pipe operator |> ``` diff --git a/docs/sophia_features.md b/docs/sophia_features.md index aa9e229..2d839e1 100644 --- a/docs/sophia_features.md +++ b/docs/sophia_features.md @@ -862,6 +862,16 @@ function require(b : bool, err : string) = if(!b) abort(err) ``` +Aside from that, there is an almost equivalent function `exit` + +```sophia +exit(reason : string) : 'a +``` + +Just like `abort`, it breaks the execution with the given reason. The difference +however is in the gas consumption — while `abort` returns unused gas, a call to +`exit` burns it all. + ## Delegation signature Some chain operations (`Oracle.` and `AENS.`) have an diff --git a/src/aeso_ast_infer_types.erl b/src/aeso_ast_infer_types.erl index 61b26ab..9acf1a3 100644 --- a/src/aeso_ast_infer_types.erl +++ b/src/aeso_ast_infer_types.erl @@ -539,8 +539,9 @@ global_env() -> %% TTL constructors {"RelativeTTL", Fun1(Int, TTL)}, {"FixedTTL", Fun1(Int, TTL)}, - %% Abort + %% Abort/exit {"abort", Fun1(String, A)}, + {"exit", Fun1(String, A)}, {"require", Fun([Bool, String], Unit)}]) , types = MkDefs( [{"int", 0}, {"bool", 0}, {"char", 0}, {"string", 0}, {"address", 0}, diff --git a/src/aeso_ast_to_fcode.erl b/src/aeso_ast_to_fcode.erl index d0328c9..bea8572 100644 --- a/src/aeso_ast_to_fcode.erl +++ b/src/aeso_ast_to_fcode.erl @@ -242,7 +242,7 @@ builtins() -> MkName = fun(NS, Fun) -> list_to_atom(string:to_lower(string:join(NS ++ [Fun], "_"))) end, - Scopes = [{[], [{"abort", 1}, {"require", 2}]}, + Scopes = [{[], [{"abort", 1}, {"require", 2}, {"exit", 1}]}, {["Chain"], [{"spend", 2}, {"balance", 1}, {"block_hash", 1}, {"coinbase", none}, {"timestamp", none}, {"block_height", none}, {"difficulty", none}, {"gas_limit", none}, {"bytecode_hash", 1}, {"create", variable}, {"clone", variable}]}, diff --git a/src/aeso_fcode_to_fate.erl b/src/aeso_fcode_to_fate.erl index 67a4b5c..ffaf85e 100644 --- a/src/aeso_fcode_to_fate.erl +++ b/src/aeso_fcode_to_fate.erl @@ -507,6 +507,8 @@ builtin_to_scode(Env, bytes_split, [_, _] = Args) -> call_to_scode(Env, aeb_fate_ops:bytes_split(?a, ?a, ?a), Args); builtin_to_scode(Env, abort, [_] = Args) -> call_to_scode(Env, aeb_fate_ops:abort(?a), Args); +builtin_to_scode(Env, exit, [_] = Args) -> + call_to_scode(Env, aeb_fate_ops:exit(?a), Args); builtin_to_scode(Env, chain_spend, [_, _] = Args) -> call_to_scode(Env, [aeb_fate_ops:spend(?a, ?a), tuple(0)], Args); @@ -1486,6 +1488,7 @@ r_write_to_dead_var({i, Ann, I}, Code) -> r_write_to_dead_var(_, _) -> false. op_view({'ABORT', R}) -> {'ABORT', none, [R]}; +op_view({'EXIT', R}) -> {'EXIT', none, [R]}; op_view(T) when is_tuple(T) -> [Op, R | As] = tuple_to_list(T), CheckReads = fun(Rs, X) -> case [] == Rs -- [dst, src] of true -> X; false -> false end end, diff --git a/test/contracts/all_syntax.aes b/test/contracts/all_syntax.aes index ced918a..513f3be 100644 --- a/test/contracts/all_syntax.aes +++ b/test/contracts/all_syntax.aes @@ -80,3 +80,4 @@ contract AllSyntax = let sh : shakespeare(shakespeare(int)) = {wolfgang = state} sh{wolfgang.wolfgang = sh.wolfgang} // comment + exit("hope you had fun reading this")