diff --git a/src/aeso_ast_infer_types.erl b/src/aeso_ast_infer_types.erl index 9376129..565c34d 100644 --- a/src/aeso_ast_infer_types.erl +++ b/src/aeso_ast_infer_types.erl @@ -1290,6 +1290,16 @@ infer_expr(Env, {block, Attrs, Stmts}) -> BlockType = fresh_uvar(Attrs), NewStmts = infer_block(Env, Attrs, Stmts, BlockType), {typed, Attrs, {block, Attrs, NewStmts}, BlockType}; +infer_expr(_Env, {record_or_map_error, Attrs, Fields}) -> + type_error({mixed_record_and_map, {record, Attrs, Fields}}), + Type = fresh_uvar(Attrs), + {typed, Attrs, {record, Attrs, []}, Type}; +infer_expr(Env, {record_or_map_error, Attrs, Expr, []}) -> + type_error({empty_record_or_map_update, {record, Attrs, Expr, []}}), + infer_expr(Env, Expr); +infer_expr(Env, {record_or_map_error, Attrs, Expr, Fields}) -> + type_error({mixed_record_and_map, {record, Attrs, Expr, Fields}}), + infer_expr(Env, Expr); infer_expr(Env, {lam, Attrs, Args, Body}) -> ArgTypes = [fresh_uvar(As) || {arg, As, _, _} <- Args], ArgPatterns = [{typed, As, Pat, check_type(Env, T)} || {arg, As, Pat, T} <- Args], @@ -2456,6 +2466,14 @@ mk_error({compiler_version_mismatch, Ann, Version, Op, Bound}) -> "because it does not satisfy the constraint" " ~s ~s ~s\n", [PrintV(Version), Op, PrintV(Bound)]), mk_t_err(pos(Ann), Msg); +mk_error({empty_record_or_map_update, Expr}) -> + Msg = io_lib:format("Empty record/map update\n~s", + [pp_expr(" ", Expr)]), + mk_t_err(pos(Expr), Msg); +mk_error({mixed_record_and_map, Expr}) -> + Msg = io_lib:format("Mixed record fields and map keys in\n~s", + [pp_expr(" ", Expr)]), + mk_t_err(pos(Expr), Msg); mk_error(Err) -> Msg = io_lib:format("Unknown error: ~p\n", [Err]), mk_t_err(pos(0, 0), Msg). diff --git a/src/aeso_parser.erl b/src/aeso_parser.erl index 06a3c43..e03224c 100644 --- a/src/aeso_parser.erl +++ b/src/aeso_parser.erl @@ -349,7 +349,9 @@ record(Fs) -> bad_expr_err("Cannot use '@' in map construction", infix({lvalue, FAnn, LV}, {'@', Ann}, Id)); ({field, FAnn, LV, _}) -> 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)}; + record_or_map_error -> + {record_or_map_error, get_ann(hd(Fs)), Fs} end. record_or_map(Fields) -> @@ -361,9 +363,7 @@ record_or_map(Fields) -> case lists:usort(lists:map(Kind, Fields)) of [proj] -> record; [map_get] -> map; - _ -> - [{field, Ann, _, _} | _] = Fields, - bad_expr_err("Mixed record fields and map keys in", {record, Ann, Fields}) + _ -> record_or_map_error %% Defer error until type checking end. field_assignment() -> diff --git a/src/aeso_syntax.erl b/src/aeso_syntax.erl index 4eb52ef..66104b2 100644 --- a/src/aeso_syntax.erl +++ b/src/aeso_syntax.erl @@ -100,9 +100,8 @@ | {list, ann(), [expr()]} | {list_comp, ann(), expr(), [comprehension_exp()]} | {typed, ann(), expr(), type()} - | {record, ann(), [field(expr())]} - | {record, ann(), expr(), [field(expr())]} %% record update - | {map, ann(), expr(), [field(expr())]} %% map update + | {record_or_map(), ann(), [field(expr())]} + | {record_or_map(), ann(), expr(), [field(expr())]} %% record/map update | {map, ann(), [{expr(), expr()}]} | {map_get, ann(), expr(), expr()} | {map_get, ann(), expr(), expr(), expr()} @@ -111,6 +110,8 @@ | id() | qid() | con() | qcon() | constant(). +-type record_or_map() :: record | map | record_or_map_error. + -type comprehension_exp() :: [ {comprehension_bind, id(), expr()} | {comprehension_if, ann(), expr()} | letbind() ]. diff --git a/test/aeso_compiler_tests.erl b/test/aeso_compiler_tests.erl index 50130e6..c5912cd 100644 --- a/test/aeso_compiler_tests.erl +++ b/test/aeso_compiler_tests.erl @@ -595,6 +595,17 @@ failing_contracts() -> [<>]) + , ?TYPE_ERROR(bad_records, + [<>, + <>, + <> + ]) ]. -define(Path(File), "code_errors/" ??File). diff --git a/test/contracts/bad_records.aes b/test/contracts/bad_records.aes new file mode 100644 index 0000000..529e6f9 --- /dev/null +++ b/test/contracts/bad_records.aes @@ -0,0 +1,5 @@ +contract BadRecord = + entrypoint foo() = + let r = {x = 0, [0] = 1} + r{x = 0, [0] = 1} + r{}