Compare commits
21 Commits
v4.0.0-rc3
...
v4.0.0-rc5
| Author | SHA1 | Date | |
|---|---|---|---|
| 7f86b7d301 | |||
| e018c31ce1 | |||
| 9234690d31 | |||
| 214a5f0a91 | |||
| d4d3a9650a | |||
| b752965443 | |||
| 0019d92e45 | |||
| 29f2168827 | |||
| f81dc88526 | |||
| a21715a657 | |||
| 048c2ca98d | |||
| 662e5e70ef | |||
| 8e3483ced4 | |||
| 6efc390bb6 | |||
| 981027b2e7 | |||
| 11d998b739 | |||
| b481b3254b | |||
| 01a2efb7b8 | |||
| a730fcc366 | |||
| 457f9cf4ea | |||
| f34b6ed982 |
+16
-1
@@ -9,6 +9,19 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
||||
### Changed
|
||||
### Removed
|
||||
|
||||
## [4.0.0-rc5] - 2019-09-27
|
||||
### Added
|
||||
### Changed
|
||||
- Bug fixes in error reporting.
|
||||
- Bug fix in variable liveness analysis for FATE.
|
||||
### Removed
|
||||
|
||||
## [4.0.0-rc4] - 2019-09-13
|
||||
### Added
|
||||
- Handle numeric escapes, i.e. `"\x19Ethereum Signed Message:\n"`, and similar strings.
|
||||
### Changed
|
||||
### Removed
|
||||
|
||||
## [4.0.0-rc3] - 2019-09-12
|
||||
### Added
|
||||
- `Bytes.concat` and `Bytes.split` are added to be able to
|
||||
@@ -154,7 +167,9 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
||||
- Simplify calldata creation - instead of passing a compiled contract, simply
|
||||
pass a (stubbed) contract string.
|
||||
|
||||
[Unreleased]: https://github.com/aeternity/aesophia/compare/v4.0.0-rc3...HEAD
|
||||
[Unreleased]: https://github.com/aeternity/aesophia/compare/v4.0.0-rc5...HEAD
|
||||
[4.0.0-rc5]: https://github.com/aeternity/aesophia/compare/v4.0.0-rc4...v4.0.0-rc5
|
||||
[4.0.0-rc4]: https://github.com/aeternity/aesophia/compare/v4.0.0-rc3...v4.0.0-rc4
|
||||
[4.0.0-rc3]: https://github.com/aeternity/aesophia/compare/v4.0.0-rc1...v4.0.0-rc3
|
||||
[4.0.0-rc1]: https://github.com/aeternity/aesophia/compare/v3.2.0...v4.0.0-rc1
|
||||
[3.2.0]: https://github.com/aeternity/aesophia/compare/v3.1.0...v3.2.0
|
||||
|
||||
+1
-1
@@ -15,7 +15,7 @@
|
||||
{base_plt_apps, [erts, kernel, stdlib, crypto, mnesia]}
|
||||
]}.
|
||||
|
||||
{relx, [{release, {aesophia, "4.0.0-rc3"},
|
||||
{relx, [{release, {aesophia, "4.0.0-rc5"},
|
||||
[aesophia, aebytecode, getopt]},
|
||||
|
||||
{dev_mode, true},
|
||||
|
||||
@@ -783,7 +783,8 @@ check_type(_Env, Type = {uvar, _, _}, Arity) ->
|
||||
ensure_base_type(Type, Arity),
|
||||
Type;
|
||||
check_type(_Env, {args_t, Ann, Ts}, _) ->
|
||||
type_error({new_tuple_syntax, Ann, Ts}).
|
||||
type_error({new_tuple_syntax, Ann, Ts}),
|
||||
{tuple_t, Ann, Ts}.
|
||||
|
||||
ensure_base_type(Type, Arity) ->
|
||||
[ type_error({wrong_type_arguments, Type, Arity, 0}) || Arity /= 0 ],
|
||||
@@ -1155,8 +1156,6 @@ infer_expr(Env, {typed, As, Body, Type}) ->
|
||||
{typed, _, NewBody, NewType} = check_expr(Env, Body, Type1),
|
||||
{typed, As, NewBody, NewType};
|
||||
infer_expr(Env, {app, Ann, Fun, Args0}) ->
|
||||
%% TODO: fix parser to give proper annotation for normal applications!
|
||||
FunAnn = aeso_syntax:get_ann(Fun),
|
||||
NamedArgs = [ Arg || Arg = {named_arg, _, _, _} <- Args0 ],
|
||||
Args = Args0 -- NamedArgs,
|
||||
case aeso_syntax:get_ann(format, Ann) of
|
||||
@@ -1165,15 +1164,15 @@ infer_expr(Env, {app, Ann, Fun, Args0}) ->
|
||||
prefix ->
|
||||
infer_op(Env, Ann, Fun, Args, fun infer_prefix/1);
|
||||
_ ->
|
||||
NamedArgsVar = fresh_uvar(FunAnn),
|
||||
NamedArgsVar = fresh_uvar(Ann),
|
||||
NamedArgs1 = [ infer_named_arg(Env, NamedArgsVar, Arg) || Arg <- NamedArgs ],
|
||||
%% TODO: named args constraints
|
||||
NewFun={typed, _, _, FunType} = infer_expr(Env, Fun),
|
||||
NewArgs = [infer_expr(Env, A) || A <- Args],
|
||||
ArgTypes = [T || {typed, _, _, T} <- NewArgs],
|
||||
ResultType = fresh_uvar(FunAnn),
|
||||
ResultType = fresh_uvar(Ann),
|
||||
unify(Env, FunType, {fun_t, [], NamedArgsVar, ArgTypes, ResultType}, {infer_app, Fun, Args, FunType, ArgTypes}),
|
||||
{typed, FunAnn, {app, Ann, NewFun, NamedArgs1 ++ NewArgs}, dereference(ResultType)}
|
||||
{typed, Ann, {app, Ann, NewFun, NamedArgs1 ++ NewArgs}, dereference(ResultType)}
|
||||
end;
|
||||
infer_expr(Env, {'if', Attrs, Cond, Then, Else}) ->
|
||||
NewCond = check_expr(Env, Cond, {id, Attrs, "bool"}),
|
||||
@@ -1333,7 +1332,7 @@ infer_case(Env, Attrs, Pattern, ExprType, Branch, SwitchType) ->
|
||||
end,
|
||||
NewEnv = bind_vars([{Var, fresh_uvar(Ann)} || Var = {id, Ann, _} <- Vars], Env#env{ in_pattern = true }),
|
||||
NewPattern = {typed, _, _, PatType} = infer_expr(NewEnv, Pattern),
|
||||
NewBranch = check_expr(NewEnv, Branch, SwitchType),
|
||||
NewBranch = check_expr(NewEnv#env{ in_pattern = false }, Branch, SwitchType),
|
||||
unify(Env, PatType, ExprType, {case_pat, Pattern, PatType, ExprType}),
|
||||
{'case', Attrs, NewPattern, NewBranch}.
|
||||
|
||||
|
||||
+27
-16
@@ -38,7 +38,7 @@
|
||||
|
||||
-define(i(X), {immediate, X}).
|
||||
-define(a, {stack, 0}).
|
||||
-define(s, {var, -1}). %% TODO: until we have state support in FATE
|
||||
-define(s, {store, 1}).
|
||||
-define(void, {var, 9999}).
|
||||
|
||||
-define(IsState(X), (is_tuple(X) andalso tuple_size(X) =:= 2 andalso element(1, X) =:= var andalso element(2, X) < 0)).
|
||||
@@ -477,7 +477,7 @@ call_to_scode(Env, CallCode, Args) ->
|
||||
builtin_to_scode(_Env, get_state, []) ->
|
||||
[push(?s)];
|
||||
builtin_to_scode(Env, set_state, [_] = Args) ->
|
||||
call_to_scode(Env, [aeb_fate_ops:store(?s, ?a),
|
||||
call_to_scode(Env, [{'STORE', ?s, ?a},
|
||||
tuple(0)], Args);
|
||||
builtin_to_scode(Env, chain_event, Args) ->
|
||||
call_to_scode(Env, [erlang:apply(aeb_fate_ops, log, lists:duplicate(length(Args), ?a)),
|
||||
@@ -624,7 +624,7 @@ op_to_scode(string_blake2b) -> aeb_fate_ops:blake2b(?a, ?a).
|
||||
|
||||
%% PUSH and STORE ?a are the same, so we use STORE to make optimizations
|
||||
%% easier, and specialize to PUSH (which is cheaper) at the end.
|
||||
push(A) -> aeb_fate_ops:store(?a, A).
|
||||
push(A) -> {'STORE', ?a, A}.
|
||||
|
||||
tuple(0) -> push(?i({tuple, {}}));
|
||||
tuple(N) -> aeb_fate_ops:tuple(?a, N).
|
||||
@@ -689,7 +689,7 @@ pp_ann(Ind, [{i, #{ live_in := In, live_out := Out }, I} | Code]) ->
|
||||
Fmt = fun([]) -> "()";
|
||||
(Xs) -> string:join([lists:concat(["var", N]) || {var, N} <- Xs], " ")
|
||||
end,
|
||||
Op = [Ind, pp_op(I)],
|
||||
Op = [Ind, pp_op(desugar_args(I))],
|
||||
Ann = [[" % ", Fmt(In), " -> ", Fmt(Out)] || In ++ Out /= []],
|
||||
[io_lib:format("~-40s~s\n", [Op, Ann]),
|
||||
pp_ann(Ind, Code)];
|
||||
@@ -701,7 +701,7 @@ pp_op(I) ->
|
||||
|
||||
pp_arg(?i(I)) -> io_lib:format("~w", [I]);
|
||||
pp_arg({arg, N}) -> io_lib:format("arg~p", [N]);
|
||||
pp_arg({var, N}) when N < 0 -> io_lib:format("store~p", [-N]);
|
||||
pp_arg(?s) -> "store1";
|
||||
pp_arg({var, N}) -> io_lib:format("var~p", [N]);
|
||||
pp_arg(?a) -> "a".
|
||||
|
||||
@@ -741,18 +741,20 @@ ann_reads([{switch, Arg, Type, Alts, Def} | Code], Reads, Acc) ->
|
||||
ann_reads([{i, Ann, I} | Code], Reads, Acc) ->
|
||||
#{ writes_in := WritesIn, writes_out := WritesOut } = Ann,
|
||||
#{ read := Rs, write := W, pure := Pure } = attributes(I),
|
||||
Reads1 =
|
||||
%% If we write it here it's not live in (unless we also read it)
|
||||
Reads1 = Reads -- [W],
|
||||
Reads2 =
|
||||
case {W, Pure andalso not ordsets:is_element(W, Reads)} of
|
||||
%% This is a little bit dangerous: if writing to a dead variable, we ignore
|
||||
%% the reads. Relies on dead writes to be removed by the
|
||||
%% optimisations below (r_write_to_dead_var).
|
||||
{{var, _}, true} -> Reads;
|
||||
_ -> ordsets:union(Reads, Rs)
|
||||
{{var, _}, true} -> Reads1;
|
||||
_ -> ordsets:union(Reads1, Rs)
|
||||
end,
|
||||
LiveIn = ordsets:intersection(Reads1, WritesIn),
|
||||
LiveIn = ordsets:intersection(Reads2, WritesIn),
|
||||
LiveOut = ordsets:intersection(Reads, WritesOut),
|
||||
Ann1 = #{ live_in => LiveIn, live_out => LiveOut },
|
||||
ann_reads(Code, Reads1, [{i, Ann1, I} | Acc]);
|
||||
ann_reads(Code, Reads2, [{i, Ann1, I} | Acc]);
|
||||
ann_reads([], Reads, Acc) -> {Acc, Reads}.
|
||||
|
||||
%% Instruction attributes: reads, writes and purity (pure means no side-effects
|
||||
@@ -922,6 +924,7 @@ independent({i, _, I}, {i, _, J}) ->
|
||||
if WI == pc; WJ == pc -> false; %% no jumps
|
||||
not (PureI or PureJ) -> false; %% at least one is pure
|
||||
StackI and StackJ -> false; %% cannot both use the stack
|
||||
WI == WJ -> false; %% cannot write to the same register
|
||||
true ->
|
||||
%% and cannot write to each other's inputs
|
||||
not lists:member(WI, RJ) andalso
|
||||
@@ -1336,18 +1339,26 @@ unannotate({i, _Ann, I}) -> [I].
|
||||
|
||||
%% Desugar and specialize
|
||||
desugar({'ADD', ?a, ?i(1), ?a}) -> [aeb_fate_ops:inc()];
|
||||
desugar({'ADD', A, ?i(1), A}) -> [aeb_fate_ops:inc(A)];
|
||||
desugar({'ADD', A, ?i(1), A}) -> [aeb_fate_ops:inc(desugar_arg(A))];
|
||||
desugar({'ADD', ?a, ?a, ?i(1)}) -> [aeb_fate_ops:inc()];
|
||||
desugar({'ADD', A, A, ?i(1)}) -> [aeb_fate_ops:inc(A)];
|
||||
desugar({'ADD', A, A, ?i(1)}) -> [aeb_fate_ops:inc(desugar_arg(A))];
|
||||
desugar({'SUB', ?a, ?a, ?i(1)}) -> [aeb_fate_ops:dec()];
|
||||
desugar({'SUB', A, A, ?i(1)}) -> [aeb_fate_ops:dec(A)];
|
||||
desugar({'STORE', ?a, A}) -> [aeb_fate_ops:push(A)];
|
||||
desugar({'SUB', A, A, ?i(1)}) -> [aeb_fate_ops:dec(desugar_arg(A))];
|
||||
desugar({'STORE', ?a, A}) -> [aeb_fate_ops:push(desugar_arg(A))];
|
||||
desugar({switch, Arg, Type, Alts, Def}) ->
|
||||
[{switch, Arg, Type, [desugar(A) || A <- Alts], desugar(Def)}];
|
||||
[{switch, desugar_arg(Arg), Type, [desugar(A) || A <- Alts], desugar(Def)}];
|
||||
desugar(missing) -> missing;
|
||||
desugar(Code) when is_list(Code) ->
|
||||
lists:flatmap(fun desugar/1, Code);
|
||||
desugar(I) -> [I].
|
||||
desugar(I) -> [desugar_args(I)].
|
||||
|
||||
desugar_args(I) when is_tuple(I) ->
|
||||
[Op | Args] = tuple_to_list(I),
|
||||
list_to_tuple([Op | lists:map(fun desugar_arg/1, Args)]);
|
||||
desugar_args(I) -> I.
|
||||
|
||||
desugar_arg(?s) -> {var, -1};
|
||||
desugar_arg(A) -> A.
|
||||
|
||||
%% -- Phase III --------------------------------------------------------------
|
||||
%% Constructing basic blocks
|
||||
|
||||
+1
-1
@@ -312,7 +312,7 @@ map_key(Key, {ok, {_, Val}}) -> {map_key, Key, Val}.
|
||||
|
||||
elim(E, []) -> E;
|
||||
elim(E, [{proj, Ann, P} | Es]) -> elim({proj, Ann, E, P}, Es);
|
||||
elim(E, [{app, Ann, Args} | Es]) -> elim({app, Ann, E, Args}, Es);
|
||||
elim(E, [{app, _Ann, Args} | Es]) -> elim({app, aeso_syntax:get_ann(E), E, Args}, Es);
|
||||
elim(E, [{rec_upd, Ann, Flds} | Es]) -> elim(record_update(Ann, E, Flds), Es);
|
||||
elim(E, [{map_get, Ann, Key} | Es]) -> elim({map_get, Ann, E, Key}, Es);
|
||||
elim(E, [{map_get, Ann, Key, Val} | Es]) -> elim({map_get, Ann, E, Key, Val}, Es).
|
||||
|
||||
+20
-9
@@ -13,14 +13,15 @@
|
||||
override/2, push/2, pop/1]).
|
||||
|
||||
lexer() ->
|
||||
Number = fun(Digit) -> [Digit, "+(_", Digit, "+)*"] end,
|
||||
DIGIT = "[0-9]",
|
||||
HEXDIGIT = "[0-9a-fA-F]",
|
||||
LOWER = "[a-z_]",
|
||||
UPPER = "[A-Z]",
|
||||
CON = [UPPER, "[a-zA-Z0-9_]*"],
|
||||
INT = [DIGIT, "+"],
|
||||
HEX = ["0x", HEXDIGIT, "+"],
|
||||
BYTES = ["#", HEXDIGIT, "+"],
|
||||
INT = Number(DIGIT),
|
||||
HEX = ["0x", Number(HEXDIGIT)],
|
||||
BYTES = ["#", Number(HEXDIGIT)],
|
||||
WS = "[\\000-\\ ]+",
|
||||
ID = [LOWER, "[a-zA-Z0-9_']*"],
|
||||
TVAR = ["'", ID],
|
||||
@@ -53,7 +54,7 @@ lexer() ->
|
||||
, {CHAR, token(char, fun parse_char/1)}
|
||||
, {STRING, token(string, fun parse_string/1)}
|
||||
, {HEX, token(hex, fun parse_hex/1)}
|
||||
, {INT, token(int, fun list_to_integer/1)}
|
||||
, {INT, token(int, fun parse_int/1)}
|
||||
, {BYTES, token(bytes, fun parse_bytes/1)}
|
||||
|
||||
%% Identifiers (qualified first!)
|
||||
@@ -95,9 +96,11 @@ parse_char([$', C, $']) -> C.
|
||||
|
||||
unescape(Str) -> unescape(Str, []).
|
||||
|
||||
%% TODO: numeric escapes
|
||||
unescape([$"], Acc) ->
|
||||
list_to_binary(lists:reverse(Acc));
|
||||
unescape([$\\, $x, D1, D2 | Chars ], Acc) ->
|
||||
C = list_to_integer([D1, D2], 16),
|
||||
unescape(Chars, [C | Acc]);
|
||||
unescape([$\\, Code | Chars], Acc) ->
|
||||
Ok = fun(C) -> unescape(Chars, [C | Acc]) end,
|
||||
case Code of
|
||||
@@ -115,10 +118,18 @@ unescape([$\\, Code | Chars], Acc) ->
|
||||
unescape([C | Chars], Acc) ->
|
||||
unescape(Chars, [C | Acc]).
|
||||
|
||||
parse_hex("0x" ++ Chars) -> list_to_integer(Chars, 16).
|
||||
strip_underscores(S) ->
|
||||
lists:filter(fun(C) -> C /= $_ end, S).
|
||||
|
||||
parse_bytes("#" ++ Chars) ->
|
||||
N = list_to_integer(Chars, 16),
|
||||
Digits = (length(Chars) + 1) div 2,
|
||||
parse_hex("0x" ++ S) ->
|
||||
list_to_integer(strip_underscores(S), 16).
|
||||
|
||||
parse_int(S) ->
|
||||
list_to_integer(strip_underscores(S)).
|
||||
|
||||
parse_bytes("#" ++ S0) ->
|
||||
S = strip_underscores(S0),
|
||||
N = list_to_integer(S, 16),
|
||||
Digits = (length(S) + 1) div 2,
|
||||
<<N:Digits/unit:8>>.
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{application, aesophia,
|
||||
[{description, "Contract Language for aeternity"},
|
||||
{vsn, "4.0.0-rc3"},
|
||||
{vsn, "4.0.0-rc5"},
|
||||
{registered, []},
|
||||
{applications,
|
||||
[kernel,
|
||||
|
||||
@@ -152,7 +152,8 @@ compilable_contracts() ->
|
||||
"manual_stdlib_include",
|
||||
"list_comp",
|
||||
"payable",
|
||||
"unapplied_builtins"
|
||||
"unapplied_builtins",
|
||||
"underscore_number_literals"
|
||||
].
|
||||
|
||||
not_yet_compilable(fate) -> [];
|
||||
@@ -301,7 +302,14 @@ failing_contracts() ->
|
||||
<<?Pos(54, 5)
|
||||
"Let binding at line 54, column 5 must be followed by an expression">>,
|
||||
<<?Pos(58, 5)
|
||||
"Let binding at line 58, column 5 must be followed by an expression">>])
|
||||
"Let binding at line 58, column 5 must be followed by an expression">>,
|
||||
<<?Pos(63, 5)
|
||||
"Cannot unify int\n"
|
||||
" and bool\n"
|
||||
"when checking the type of the expression at line 63, column 5\n"
|
||||
" id(n) : int\n"
|
||||
"against the expected type\n"
|
||||
" bool">>])
|
||||
, ?TYPE_ERROR(init_type_error,
|
||||
[<<?Pos(7, 3)
|
||||
"Cannot unify string\n"
|
||||
|
||||
@@ -15,3 +15,5 @@ contract BasicAuth =
|
||||
entrypoint to_sign(h : hash, n : int) =
|
||||
Crypto.blake2b((h, n))
|
||||
|
||||
entrypoint weird_string() : string =
|
||||
"\x19Weird String\x42\nMore\n"
|
||||
|
||||
@@ -57,3 +57,8 @@ contract Test =
|
||||
let f() = 0
|
||||
let g() = f()
|
||||
|
||||
function id(x : 'a) : 'a = x
|
||||
|
||||
entrypoint wrong_return(n : int) : bool =
|
||||
id(n)
|
||||
|
||||
|
||||
@@ -0,0 +1,13 @@
|
||||
|
||||
contract UnderscoreNumberLiterals =
|
||||
|
||||
entrypoint ints() : list(int) =
|
||||
[ 1_999_000_000,
|
||||
19_99_00_00_00,
|
||||
0xfff_FFF_010 ]
|
||||
|
||||
entrypoint bytes() : list(bytes(4)) =
|
||||
[ #abcd_ef_00,
|
||||
#01_02_03_04,
|
||||
#aaaa_FFFF ]
|
||||
|
||||
Reference in New Issue
Block a user