Merge pull request #72 from aeternity/bad-record-compiler-crash
Bad record compiler crash
This commit is contained in:
commit
98f349f67c
@ -211,7 +211,7 @@ exprAtom() ->
|
|||||||
, ?RULE(token(hex), set_ann(format, hex, setelement(1, _1, int)))
|
, ?RULE(token(hex), set_ann(format, hex, setelement(1, _1, int)))
|
||||||
, {bool, keyword(true), true}
|
, {bool, keyword(true), true}
|
||||||
, {bool, keyword(false), false}
|
, {bool, keyword(false), false}
|
||||||
, ?RULE(brace_list(?LAZY_P(field_assignment())), record(_1))
|
, ?LET_P(Fs, brace_list(?LAZY_P(field_assignment())), record(Fs))
|
||||||
, {list, [], bracket_list(Expr)}
|
, {list, [], bracket_list(Expr)}
|
||||||
, ?RULE(tok('['), Expr, binop('..'), Expr, tok(']'), _3(_2, _4))
|
, ?RULE(tok('['), Expr, binop('..'), Expr, tok(']'), _3(_2, _4))
|
||||||
, ?RULE(keyword('('), comma_sep(Expr), tok(')'), tuple_e(_1, _2))
|
, ?RULE(keyword('('), comma_sep(Expr), tok(')'), tuple_e(_1, _2))
|
||||||
@ -254,14 +254,20 @@ record_update(Ann, E, Flds) ->
|
|||||||
record([]) -> {map, [], []};
|
record([]) -> {map, [], []};
|
||||||
record(Fs) ->
|
record(Fs) ->
|
||||||
case record_or_map(Fs) of
|
case record_or_map(Fs) of
|
||||||
record -> {record, get_ann(hd(Fs)), Fs};
|
record ->
|
||||||
|
Fld = fun({field, _, [_], _} = F) -> F;
|
||||||
|
({field, Ann, LV, Id, _}) ->
|
||||||
|
bad_expr_err("Cannot use '@' in record construction", infix({lvalue, Ann, LV}, {'@', Ann}, Id));
|
||||||
|
({field, Ann, LV, _}) ->
|
||||||
|
bad_expr_err("Cannot use nested fields or keys in record construction", {lvalue, Ann, LV}) end,
|
||||||
|
{record, get_ann(hd(Fs)), lists:map(Fld, Fs)};
|
||||||
map ->
|
map ->
|
||||||
Ann = get_ann(hd(Fs ++ [{empty, []}])), %% TODO: source location for empty maps
|
Ann = get_ann(hd(Fs ++ [{empty, []}])), %% TODO: source location for empty maps
|
||||||
KV = fun({field, _, [{map_get, _, Key}], Val}) -> {Key, Val};
|
KV = fun({field, _, [{map_get, _, Key}], Val}) -> {Key, Val};
|
||||||
({field, _, LV, Id, _}) ->
|
({field, FAnn, LV, Id, _}) ->
|
||||||
bad_expr_err("Cannot use '@' in map construction", infix(LV, {op, Ann, '@'}, Id));
|
bad_expr_err("Cannot use '@' in map construction", infix({lvalue, FAnn, LV}, {'@', Ann}, Id));
|
||||||
({field, _, LV, _}) ->
|
({field, FAnn, LV, _}) ->
|
||||||
bad_expr_err("Cannot use nested fields or keys in map construction", LV) end,
|
bad_expr_err("Cannot use nested fields or keys in map construction", {lvalue, FAnn, LV}) end,
|
||||||
{map, Ann, lists:map(KV, Fs)}
|
{map, Ann, lists:map(KV, Fs)}
|
||||||
end.
|
end.
|
||||||
|
|
||||||
@ -491,11 +497,11 @@ return_error({no_file, L, C}, Err) ->
|
|||||||
return_error({F, L, C}, Err) ->
|
return_error({F, L, C}, Err) ->
|
||||||
fail(io_lib:format("In ~s at ~p:~p:\n~s", [F, L, C, Err])).
|
fail(io_lib:format("In ~s at ~p:~p:\n~s", [F, L, C, Err])).
|
||||||
|
|
||||||
-spec ret_doc_err(ann(), prettypr:document()) -> no_return().
|
-spec ret_doc_err(ann(), prettypr:document()) -> aeso_parse_lib:parser(none()).
|
||||||
ret_doc_err(Ann, Doc) ->
|
ret_doc_err(Ann, Doc) ->
|
||||||
return_error(ann_pos(Ann), prettypr:format(Doc)).
|
return_error(ann_pos(Ann), prettypr:format(Doc)).
|
||||||
|
|
||||||
-spec bad_expr_err(string(), aeso_syntax:expr()) -> no_return().
|
-spec bad_expr_err(string(), aeso_syntax:expr()) -> aeso_parse_lib:parser(none()).
|
||||||
bad_expr_err(Reason, E) ->
|
bad_expr_err(Reason, E) ->
|
||||||
ret_doc_err(get_ann(E),
|
ret_doc_err(get_ann(E),
|
||||||
prettypr:sep([prettypr:text(Reason ++ ":"),
|
prettypr:sep([prettypr:text(Reason ++ ":"),
|
||||||
|
@ -361,6 +361,7 @@ stmt_p({else, Else}) ->
|
|||||||
-spec bin_prec(aeso_syntax:bin_op()) -> {integer(), integer(), integer()}.
|
-spec bin_prec(aeso_syntax:bin_op()) -> {integer(), integer(), integer()}.
|
||||||
bin_prec('..') -> { 0, 0, 0}; %% Always printed inside '[ ]'
|
bin_prec('..') -> { 0, 0, 0}; %% Always printed inside '[ ]'
|
||||||
bin_prec('=') -> { 0, 0, 0}; %% Always printed inside '[ ]'
|
bin_prec('=') -> { 0, 0, 0}; %% Always printed inside '[ ]'
|
||||||
|
bin_prec('@') -> { 0, 0, 0}; %% Only in error messages
|
||||||
bin_prec('||') -> {200, 300, 200};
|
bin_prec('||') -> {200, 300, 200};
|
||||||
bin_prec('&&') -> {300, 400, 300};
|
bin_prec('&&') -> {300, 400, 300};
|
||||||
bin_prec('<') -> {400, 500, 500};
|
bin_prec('<') -> {400, 500, 500};
|
||||||
|
@ -329,4 +329,7 @@ failing_contracts() ->
|
|||||||
<<"The init function should return the initial state as its result and cannot read the state,\n"
|
<<"The init function should return the initial state as its result and cannot read the state,\n"
|
||||||
"but it calls\n"
|
"but it calls\n"
|
||||||
" - state (at line 13, column 13)">>]}
|
" - state (at line 13, column 13)">>]}
|
||||||
|
, {"field_parse_error",
|
||||||
|
[<<"line 6, column 1: In field_parse_error at 5:26:\n"
|
||||||
|
"Cannot use nested fields or keys in record construction: p.x\n">>]}
|
||||||
].
|
].
|
||||||
|
@ -62,7 +62,7 @@ simple_contracts_test_() ->
|
|||||||
%% Parse tests of example contracts
|
%% Parse tests of example contracts
|
||||||
[ {lists:concat(["Parse the ", Contract, " contract."]),
|
[ {lists:concat(["Parse the ", Contract, " contract."]),
|
||||||
fun() -> roundtrip_contract(Contract) end}
|
fun() -> roundtrip_contract(Contract) end}
|
||||||
|| Contract <- [counter, voting, all_syntax, '05_greeter', aeproof, multi_sig, simple_storage, withdrawal, fundme, dutch_auction] ]
|
|| Contract <- [counter, voting, all_syntax, '05_greeter', aeproof, multi_sig, simple_storage, fundme, dutch_auction] ]
|
||||||
}.
|
}.
|
||||||
|
|
||||||
parse_contract(Name) ->
|
parse_contract(Name) ->
|
||||||
|
5
test/contracts/field_parse_error.aes
Normal file
5
test/contracts/field_parse_error.aes
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
|
||||||
|
contract Fail =
|
||||||
|
record pt = {x : int, y : int}
|
||||||
|
record r = {p : pt}
|
||||||
|
function fail() = {p.x = 0, p.y = 0}
|
@ -1,56 +0,0 @@
|
|||||||
/* Example from Solidity by Example
|
|
||||||
http://solidity.readthedocs.io/en/develop/common-patterns.html
|
|
||||||
|
|
||||||
contract WithdrawalContract {
|
|
||||||
address public richest
|
|
||||||
uint public mostSent
|
|
||||||
|
|
||||||
mapping (address => uint) pendingWithdrawals
|
|
||||||
|
|
||||||
function WithdrawalContract() payable {
|
|
||||||
richest = msg.sender
|
|
||||||
mostSent = msg.value
|
|
||||||
}
|
|
||||||
|
|
||||||
function becomeRichest() payable returns (bool) {
|
|
||||||
if (msg.value > mostSent) {
|
|
||||||
pendingWithdrawals[richest] += msg.value
|
|
||||||
richest = msg.sender
|
|
||||||
mostSent = msg.value
|
|
||||||
return true
|
|
||||||
} else {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function withdraw() {
|
|
||||||
uint amount = pendingWithdrawals[msg.sender]
|
|
||||||
// Remember to zero the pending refund before
|
|
||||||
// sending to prevent re-entrancy attacks
|
|
||||||
pendingWithdrawals[msg.sender] = 0
|
|
||||||
msg.sender.transfer(amount)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
*/
|
|
||||||
|
|
||||||
contract WithdrawalContract =
|
|
||||||
|
|
||||||
record state = { richest : address,
|
|
||||||
mostSent : uint,
|
|
||||||
pendingWithdrawals : map(address, uint) }
|
|
||||||
|
|
||||||
function becomeRichest() : result(bool) =
|
|
||||||
if (call().value > state.mostSent)
|
|
||||||
let totalAmount : uint = Map.get_(state.richest, pendingWithdrawals) + call().value
|
|
||||||
{state = state{ pendingWithdrawals = Map.insert(state.richest, call().value, state.pendingWithdrawals),
|
|
||||||
richest = call().sender,
|
|
||||||
mostSent = call().value },
|
|
||||||
result = true}
|
|
||||||
else
|
|
||||||
{result = false}
|
|
||||||
|
|
||||||
function withdraw() =
|
|
||||||
let amount : uint = Map.get_(call().sender, state.pendingWithdrawals)
|
|
||||||
{ state.pendingWithdrawals = Map.insert(call().sender, 0, state.pendingWithdrawals),
|
|
||||||
transactions = spend_tx(amount, call().sender) }
|
|
Loading…
x
Reference in New Issue
Block a user