Allow pattern matching in left-hand sides

This commit is contained in:
Ulf Norell 2020-01-14 10:08:27 +01:00
parent fdba063337
commit 238ffdc8e0
14 changed files with 183 additions and 133 deletions

View File

@ -129,7 +129,7 @@ encode_anon_args(Types) ->
encode_args(Args) -> [ encode_arg(A) || A <- Args ]. encode_args(Args) -> [ encode_arg(A) || A <- Args ].
encode_arg({arg, _, Id, T}) -> encode_arg({typed, _, Id, T}) ->
#{name => encode_type(Id), #{name => encode_type(Id),
type => encode_type(T)}. type => encode_type(T)}.

View File

@ -679,7 +679,7 @@ process_block(Ann, [Decl | Decls]) ->
process_block(Ann, Rest)]; process_block(Ann, Rest)];
{letfun, Ann1, Id = {id, _, Name}, _, _, _} -> {letfun, Ann1, Id = {id, _, Name}, _, _, _} ->
{Clauses, Rest} = lists:splitwith(IsThis(Name), [Decl | Decls]), {Clauses, Rest} = lists:splitwith(IsThis(Name), [Decl | Decls]),
[{fun_clauses, Ann1, Id, {id, [{origin, system}], "_"}, Clauses} | [{fun_clauses, Ann1, Id, {id, [{origin, system} | Ann1], "_"}, Clauses} |
process_block(Ann, Rest)] process_block(Ann, Rest)]
end. end.
@ -815,9 +815,9 @@ check_type(Env, T) ->
check_type(Env, T = {tvar, _, _}, Arity) -> check_type(Env, T = {tvar, _, _}, Arity) ->
[ type_error({higher_kinded_typevar, T}) || Arity /= 0 ], [ type_error({higher_kinded_typevar, T}) || Arity /= 0 ],
check_tvar(Env, T); check_tvar(Env, T);
check_type(_Env, X = {id, _, "_"}, Arity) -> check_type(_Env, X = {id, Ann, "_"}, Arity) ->
ensure_base_type(X, Arity), ensure_base_type(X, Arity),
X; fresh_uvar(Ann);
check_type(Env, X = {Tag, _, _}, Arity) when Tag == con; Tag == qcon; Tag == id; Tag == qid -> check_type(Env, X = {Tag, _, _}, Arity) when Tag == con; Tag == qcon; Tag == id; Tag == qid ->
case lookup_type(Env, X) of case lookup_type(Env, X) of
{Q, {_, Def}} -> {Q, {_, Def}} ->
@ -1011,44 +1011,49 @@ infer_letrec(Env, Defs) ->
infer_letfun(Env, {fun_clauses, Ann, Fun = {id, _, Name}, Type, Clauses}) -> infer_letfun(Env, {fun_clauses, Ann, Fun = {id, _, Name}, Type, Clauses}) ->
Type1 = check_type(Env, Type), Type1 = check_type(Env, Type),
{NameSigs, Clauses1} = lists:unzip([ infer_letfun(Env, Clause) || Clause <- Clauses ]), {NameSigs, Clauses1} = lists:unzip([ infer_letfun1(Env, Clause) || Clause <- Clauses ]),
{_, Sigs = [Sig | _]} = lists:unzip(NameSigs), {_, Sigs = [Sig | _]} = lists:unzip(NameSigs),
_ = [ begin _ = [ begin
ClauseT = typesig_to_fun_t(ClauseSig), ClauseT = typesig_to_fun_t(ClauseSig),
unify(Env, ClauseT, Type1, {check_typesig, Name, ClauseT, Type1}) unify(Env, ClauseT, Type1, {check_typesig, Name, ClauseT, Type1})
end || ClauseSig <- Sigs ], end || ClauseSig <- Sigs ],
{{Name, Sig}, desugar_clauses(Ann, Fun, Sig, Clauses1)}; {{Name, Sig}, desugar_clauses(Ann, Fun, Sig, Clauses1)};
infer_letfun(Env0, {letfun, Attrib, Fun = {id, NameAttrib, Name}, Args, What, Body}) -> infer_letfun(Env, LetFun = {letfun, Ann, Fun, _, _, _}) ->
{{Name, Sig}, Clause} = infer_letfun1(Env, LetFun),
{{Name, Sig}, desugar_clauses(Ann, Fun, Sig, [Clause])}.
infer_letfun1(Env0, {letfun, Attrib, Fun = {id, NameAttrib, Name}, Args, What, Body}) ->
Env = Env0#env{ stateful = aeso_syntax:get_ann(stateful, Attrib, false), Env = Env0#env{ stateful = aeso_syntax:get_ann(stateful, Attrib, false),
current_function = Fun }, current_function = Fun },
check_unique_arg_names(Fun, Args), {NewEnv, {typed, _, {tuple, _, TypedArgs}, {tuple_t, _, ArgTypes}}} = infer_pattern(Env, {tuple, [{origin, system} | NameAttrib], Args}),
ArgTypes = [{ArgName, check_type(Env, arg_type(ArgAnn, T))} || {arg, ArgAnn, ArgName, T} <- Args],
ExpectedType = check_type(Env, arg_type(NameAttrib, What)), ExpectedType = check_type(Env, arg_type(NameAttrib, What)),
NewBody={typed, _, _, ResultType} = check_expr(bind_vars(ArgTypes, Env), Body, ExpectedType), NewBody={typed, _, _, ResultType} = check_expr(NewEnv, Body, ExpectedType),
NewArgs = [{arg, A1, {id, A2, ArgName}, T}
|| {{_, T}, {arg, A1, {id, A2, ArgName}, _}} <- lists:zip(ArgTypes, Args)],
NamedArgs = [], NamedArgs = [],
TypeSig = {type_sig, Attrib, none, NamedArgs, [T || {arg, _, _, T} <- NewArgs], ResultType}, TypeSig = {type_sig, Attrib, none, NamedArgs, ArgTypes, ResultType},
{{Name, TypeSig}, {{Name, TypeSig},
{letfun, Attrib, {id, NameAttrib, Name}, NewArgs, ResultType, NewBody}}. {letfun, Attrib, {id, NameAttrib, Name}, TypedArgs, ResultType, NewBody}}.
desugar_clauses(Ann, Fun, {type_sig, _, _, _, ArgTypes, RetType}, Clauses) -> desugar_clauses(Ann, Fun, {type_sig, _, _, _, ArgTypes, RetType}, Clauses) ->
NoAnn = [{origin, system}], NeedDesugar =
Args = [ {arg, NoAnn, {id, NoAnn, "x#" ++ integer_to_list(I)}, Type} case Clauses of
|| {I, Type} <- indexed(1, ArgTypes) ], [{letfun, _, _, As, _, _}] -> lists:any(fun({typed, _, {id, _, _}, _}) -> false; (_) -> true end, As);
ArgTuple = {tuple, NoAnn, [X || {arg, _, X, _} <- Args]}, _ -> true
ArgType = {tuple_t, NoAnn, ArgTypes}, end,
{letfun, Ann, Fun, Args, RetType, case NeedDesugar of
{switch, NoAnn, {typed, NoAnn, ArgTuple, ArgType}, false -> [Clause] = Clauses, Clause;
[ {'case', AnnC, {tuple, AnnC, [ {typed, AnnA, Pat, PatT} || {arg, AnnA, Pat, PatT} <- ArgsC ]}, Body} true ->
|| {letfun, AnnC, _, ArgsC, _, Body} <- Clauses ]}}. NoAnn = [{origin, system}],
Args = [ {typed, NoAnn, {id, NoAnn, "x#" ++ integer_to_list(I)}, Type}
check_unique_arg_names(Fun, Args) -> || {I, Type} <- indexed(1, ArgTypes) ],
Name = fun({arg, _, {id, _, X}, _}) -> X end, Tuple = fun([X]) -> X;
Names = lists:map(Name, Args), (As) -> {typed, NoAnn, {tuple, NoAnn, As}, {tuple_t, NoAnn, ArgTypes}}
Dups = lists:usort(Names -- lists:usort(Names)), end,
[ type_error({repeated_arg, Fun, Arg}) || Arg <- Dups ], {letfun, Ann, Fun, Args, RetType,
ok. {typed, NoAnn,
{switch, NoAnn, Tuple(Args),
[ {'case', AnnC, Tuple(ArgsC), Body}
|| {letfun, AnnC, _, ArgsC, _, Body} <- Clauses ]}, RetType}}
end.
print_typesig({Name, TypeSig}) -> print_typesig({Name, TypeSig}) ->
?PRINT_TYPES("Inferred ~s : ~s\n", [Name, pp(TypeSig)]). ?PRINT_TYPES("Inferred ~s : ~s\n", [Name, pp(TypeSig)]).
@ -1141,9 +1146,9 @@ get_call_chains(Graph, Visited, Queue, Stop, Acc) ->
end. end.
check_expr(Env, Expr, Type) -> check_expr(Env, Expr, Type) ->
E = {typed, _, _, Type1} = infer_expr(Env, Expr), {typed, Ann, Expr1, Type1} = infer_expr(Env, Expr),
unify(Env, Type1, Type, {check_expr, Expr, Type1, Type}), unify(Env, Type1, Type, {check_expr, Expr, Type1, Type}),
E. {typed, Ann, Expr1, Type}. %% Keep the user-given type
infer_expr(_Env, Body={bool, As, _}) -> infer_expr(_Env, Body={bool, As, _}) ->
{typed, As, Body, {id, As, "bool"}}; {typed, As, Body, {id, As, "bool"}};
@ -1428,7 +1433,7 @@ infer_pattern(Env, Pattern) ->
end, end,
NewEnv = bind_vars([{Var, fresh_uvar(Ann1)} || Var = {id, Ann1, _} <- Vars], Env#env{ in_pattern = true }), NewEnv = bind_vars([{Var, fresh_uvar(Ann1)} || Var = {id, Ann1, _} <- Vars], Env#env{ in_pattern = true }),
NewPattern = infer_expr(NewEnv, Pattern), NewPattern = infer_expr(NewEnv, Pattern),
{NewEnv, NewPattern}. {NewEnv#env{ in_pattern = Env#env.in_pattern }, NewPattern}.
infer_case(Env, Attrs, Pattern, ExprType, Branch, SwitchType) -> infer_case(Env, Attrs, Pattern, ExprType, Branch, SwitchType) ->
{NewEnv, NewPattern = {typed, _, _, PatType}} = infer_pattern(Env, Pattern), {NewEnv, NewPattern = {typed, _, _, PatType}} = infer_pattern(Env, Pattern),

View File

@ -415,9 +415,12 @@ type_to_fcode(Env, Sub, {fun_t, _, Named, Args, Res}) ->
type_to_fcode(_Env, _Sub, Type) -> type_to_fcode(_Env, _Sub, Type) ->
error({todo, Type}). error({todo, Type}).
-spec args_to_fcode(env(), [aeso_syntax:arg()]) -> [{var_name(), ftype()}]. -spec args_to_fcode(env(), [aeso_syntax:pattern()]) -> [{var_name(), ftype()}].
args_to_fcode(Env, Args) -> args_to_fcode(Env, Args) ->
[ {Name, type_to_fcode(Env, Type)} || {arg, _, {id, _, Name}, Type} <- Args ]. [ case Arg of
{id, _, Name} -> {Name, type_to_fcode(Env, Type)};
_ -> internal_error({bad_arg, Arg}) %% Pattern matching has been moved to the rhs at this point
end || {typed, _, Arg, Type} <- Args ].
-define(make_let(X, Expr, Body), -define(make_let(X, Expr, Body),
make_let(Expr, fun(X) -> Body end)). make_let(Expr, fun(X) -> Body end)).
@ -722,7 +725,7 @@ validate_aens_resolve_type(Ann, {app_t, _, _, [Type]}, {variant, [[], [FType]]})
ensure_first_order_entrypoint(Ann, Id = {id, _, Name}, Args, Ret, FArgs, FRet) -> ensure_first_order_entrypoint(Ann, Id = {id, _, Name}, Args, Ret, FArgs, FRet) ->
[ ensure_first_order(FT, {invalid_entrypoint, higher_order, Ann1, Id, {argument, X, T}}) [ ensure_first_order(FT, {invalid_entrypoint, higher_order, Ann1, Id, {argument, X, T}})
|| {{arg, Ann1, X, T}, {_, FT}} <- lists:zip(Args, FArgs) ], || {{typed, Ann1, X, T}, {_, FT}} <- lists:zip(Args, FArgs) ],
[ ensure_first_order(FRet, {invalid_entrypoint, higher_order, Ann, Id, {result, Ret}}) [ ensure_first_order(FRet, {invalid_entrypoint, higher_order, Ann, Id, {result, Ret}})
|| Name /= "init" ], %% init can return higher-order values, since they're written to the store || Name /= "init" ], %% init can return higher-order values, since they're written to the store
%% rather than being returned. %% rather than being returned.
@ -968,7 +971,11 @@ stmts_to_fcode(Env, [{letval, _, {typed, _, {id, _, X}, _}, Expr} | Stmts]) ->
stmts_to_fcode(Env, [{letval, Ann, Pat, Expr} | Stmts]) -> stmts_to_fcode(Env, [{letval, Ann, Pat, Expr} | Stmts]) ->
expr_to_fcode(Env, {switch, Ann, Expr, [{'case', Ann, Pat, {block, Ann, Stmts}}]}); expr_to_fcode(Env, {switch, Ann, Expr, [{'case', Ann, Pat, {block, Ann, Stmts}}]});
stmts_to_fcode(Env, [{letfun, Ann, {id, _, X}, Args, _Type, Expr} | Stmts]) -> stmts_to_fcode(Env, [{letfun, Ann, {id, _, X}, Args, _Type, Expr} | Stmts]) ->
{'let', X, expr_to_fcode(Env, {lam, Ann, Args, Expr}), LamArgs = [ case Arg of
{typed, Ann1, Id, T} -> {arg, Ann1, Id, T};
_ -> internal_error({bad_arg, Arg}) %% pattern matching has been desugared
end || Arg <- Args ],
{'let', X, expr_to_fcode(Env, {lam, Ann, LamArgs, Expr}),
stmts_to_fcode(bind_var(Env, X), Stmts)}; stmts_to_fcode(bind_var(Env, X), Stmts)};
stmts_to_fcode(Env, [Expr]) -> stmts_to_fcode(Env, [Expr]) ->
expr_to_fcode(Env, Expr); expr_to_fcode(Env, Expr);

View File

@ -131,7 +131,7 @@ contract_to_icode([Decl | Code], Icode) ->
ast_id({id, _, Id}) -> Id; ast_id({id, _, Id}) -> Id;
ast_id({qid, _, Id}) -> Id. ast_id({qid, _, Id}) -> Id.
ast_args([{arg, _, Name, Type}|Rest], Acc, Icode) -> ast_args([{typed, _, Name, Type}|Rest], Acc, Icode) ->
ast_args(Rest, [{ast_id(Name), ast_typerep1(Type, Icode)}| Acc], Icode); ast_args(Rest, [{ast_id(Name), ast_typerep1(Type, Icode)}| Acc], Icode);
ast_args([], Acc, _Icode) -> lists:reverse(Acc). ast_args([], Acc, _Icode) -> lists:reverse(Acc).
@ -355,7 +355,9 @@ ast_body({block, As, [{letval, _, Pat, E} | Rest]}, Icode) ->
#switch{expr = E1, #switch{expr = E1,
cases = [{Pat1, Rest1}]}; cases = [{Pat1, Rest1}]};
ast_body({block, As, [{letfun, Ann, F, Args, _Type, Expr} | Rest]}, Icode) -> ast_body({block, As, [{letfun, Ann, F, Args, _Type, Expr} | Rest]}, Icode) ->
ast_body({block, As, [{letval, Ann, F, {lam, Ann, Args, Expr}} | Rest]}, Icode); ToArg = fun({typed, Ann1, Id, T}) -> {arg, Ann1, Id, T} end, %% Pattern matching has been desugared
LamArgs = lists:map(ToArg, Args),
ast_body({block, As, [{letval, Ann, F, {lam, Ann, LamArgs, Expr}} | Rest]}, Icode);
ast_body({block,_,[]}, _Icode) -> ast_body({block,_,[]}, _Icode) ->
#tuple{cpts=[]}; #tuple{cpts=[]};
ast_body({block,_,[E]}, Icode) -> ast_body({block,_,[E]}, Icode) ->
@ -804,10 +806,10 @@ check_entrypoint_type(Ann, Name, Args, Ret) ->
true -> ok true -> ok
end end, end end,
[ CheckFirstOrder(T, {invalid_entrypoint, higher_order, Ann1, Name, {argument, X, T}}) [ CheckFirstOrder(T, {invalid_entrypoint, higher_order, Ann1, Name, {argument, X, T}})
|| {arg, Ann1, X, T} <- Args ], || {typed, Ann1, X, T} <- Args ],
CheckFirstOrder(Ret, {invalid_entrypoint, higher_order, Ann, Name, {result, Ret}}), CheckFirstOrder(Ret, {invalid_entrypoint, higher_order, Ann, Name, {result, Ret}}),
[ CheckMonomorphic(T, {invalid_entrypoint, polymorphic, Ann1, Name, {argument, X, T}}) [ CheckMonomorphic(T, {invalid_entrypoint, polymorphic, Ann1, Name, {argument, X, T}})
|| {arg, Ann1, X, T} <- Args ], || {typed, Ann1, X, T} <- Args ],
CheckMonomorphic(Ret, {invalid_entrypoint, polymorphic, Ann, Name, {result, Ret}}). CheckMonomorphic(Ret, {invalid_entrypoint, polymorphic, Ann, Name, {result, Ret}}).
check_oracle_type(Ann, Type = ?oracle_t(QType, RType)) -> check_oracle_type(Ann, Type = ?oracle_t(QType, RType)) ->

View File

@ -389,8 +389,8 @@ decode_calldata(ContractString, FunName, Calldata, Options0) ->
#{ typed_ast := TypedAst, type_env := TypeEnv} = Code, #{ typed_ast := TypedAst, type_env := TypeEnv} = Code,
{ok, Args, _} = get_decode_type(FunName, TypedAst), {ok, Args, _} = get_decode_type(FunName, TypedAst),
DropArg = fun({arg, _, _, T}) -> T; (T) -> T end, GetType = fun({typed, _, _, T}) -> T; (T) -> T end,
ArgTypes = lists:map(DropArg, Args), ArgTypes = lists:map(GetType, Args),
Type0 = {tuple_t, [], ArgTypes}, Type0 = {tuple_t, [], ArgTypes},
%% user defined data types such as variants needed to match against %% user defined data types such as variants needed to match against
Type = aeso_ast_infer_types:unfold_types_in_type(TypeEnv, Type0, [unfold_record_types, unfold_variant_types]), Type = aeso_ast_infer_types:unfold_types_in_type(TypeEnv, Type0, [unfold_record_types, unfold_variant_types]),

View File

@ -176,14 +176,15 @@ valdef() ->
fundef() -> fundef() ->
choice( choice(
[ ?RULE(id(), args(), tok('='), body(), {letfun, get_ann(_1), _1, _2, type_wildcard(), _4}) [ ?RULE(id(), args(), tok('='), body(), {letfun, get_ann(_1), _1, _2, type_wildcard(get_ann(_1)), _4})
, ?RULE(id(), args(), tok(':'), type(), tok('='), body(), {letfun, get_ann(_1), _1, _2, _4, _6}) , ?RULE(id(), args(), tok(':'), type(), tok('='), body(), {letfun, get_ann(_1), _1, _2, _4, _6})
]). ]).
args() -> paren_list(arg()). args() -> paren_list(pattern()).
lam_args() -> paren_list(arg()).
arg() -> choice( arg() -> choice(
?RULE(id(), {arg, get_ann(_1), _1, type_wildcard()}), ?RULE(id(), {arg, get_ann(_1), _1, type_wildcard(get_ann(_1))}),
?RULE(id(), tok(':'), type(), {arg, get_ann(_1), _1, _3})). ?RULE(id(), tok(':'), type(), {arg, get_ann(_1), _1, _3})).
%% -- Types ------------------------------------------------------------------ %% -- Types ------------------------------------------------------------------
@ -254,7 +255,7 @@ expr100() ->
Expr100 = ?LAZY_P(expr100()), Expr100 = ?LAZY_P(expr100()),
Expr200 = ?LAZY_P(expr200()), Expr200 = ?LAZY_P(expr200()),
choice( choice(
[ ?RULE(args(), keyword('=>'), body(), {lam, _2, _1, _3}) %% TODO: better location [ ?RULE(lam_args(), keyword('=>'), body(), {lam, _2, _1, _3}) %% TODO: better location
, {'if', keyword('if'), parens(Expr100), Expr200, right(tok(else), Expr100)} , {'if', keyword('if'), parens(Expr100), Expr200, right(tok(else), Expr100)}
, ?RULE(Expr200, optional(right(tok(':'), type())), , ?RULE(Expr200, optional(right(tok(':'), type())),
case _2 of case _2 of
@ -500,8 +501,8 @@ infix(L, Op, R) -> set_ann(format, infix, {app, get_ann(L), Op, [L, R]}).
prefixes(Ops, E) -> lists:foldr(fun prefix/2, E, Ops). prefixes(Ops, E) -> lists:foldr(fun prefix/2, E, Ops).
prefix(Op, E) -> set_ann(format, prefix, {app, get_ann(Op), Op, [E]}). prefix(Op, E) -> set_ann(format, prefix, {app, get_ann(Op), Op, [E]}).
type_wildcard() -> type_wildcard(Ann) ->
{id, [{origin, system}], "_"}. {id, [{origin, system} | Ann], "_"}.
block_e(Stmts) -> block_e(Stmts) ->
group_ifs(Stmts, []). group_ifs(Stmts, []).

View File

@ -200,7 +200,7 @@ name({typed, _, Name, _}) -> name(Name).
letdecl(Let, {letval, _, P, E}) -> letdecl(Let, {letval, _, P, E}) ->
block_expr(0, hsep([text(Let), expr(P), text("=")]), E); block_expr(0, hsep([text(Let), expr(P), text("=")]), E);
letdecl(Let, {letfun, _, F, Args, T, E}) -> letdecl(Let, {letfun, _, F, Args, T, E}) ->
block_expr(0, hsep([text(Let), typed(beside(name(F), args(Args)), T), text("=")]), E). block_expr(0, hsep([text(Let), typed(beside(name(F), expr({tuple, [], Args})), T), text("=")]), E).
-spec args([aeso_syntax:arg()]) -> doc(). -spec args([aeso_syntax:arg()]) -> doc().
args(Args) -> args(Args) ->

View File

@ -50,7 +50,7 @@
-type letbind() -type letbind()
:: {letval, ann(), pat(), expr()} :: {letval, ann(), pat(), expr()}
| {letfun, ann(), id(), [arg()], type(), expr()}. | {letfun, ann(), id(), [pat()], type(), expr()}.
-type arg() :: {arg, ann(), id(), type()}. -type arg() :: {arg, ann(), id(), type()}.

View File

@ -50,7 +50,7 @@ fold(Alg = #alg{zero = Zero, plus = Plus, scoped = Scoped}, Fun, K, X) ->
{fun_decl, _, _, T} -> Type(T); {fun_decl, _, _, T} -> Type(T);
{letval, _, P, E} -> Scoped(BindExpr(P), Expr(E)); {letval, _, P, E} -> Scoped(BindExpr(P), Expr(E));
{letfun, _, F, Xs, T, E} -> Sum([BindExpr(F), Type(T), Expr(Xs ++ [E])]); {letfun, _, F, Xs, T, E} -> Sum([BindExpr(F), Type(T), Expr(Xs ++ [E])]);
{fun_clauses, _, _, T, Cs} -> Sum([Type(T), Decl(Cs)]); {fun_clauses, _, _, T, Cs} -> Sum([Type(T) | [Decl(C) || C <- Cs]]);
%% typedef() %% typedef()
{alias_t, T} -> Type(T); {alias_t, T} -> Type(T);
{record_t, Fs} -> Type(Fs); {record_t, Fs} -> Type(Fs);

View File

@ -164,7 +164,8 @@ compilable_contracts() ->
"unapplied_builtins", "unapplied_builtins",
"underscore_number_literals", "underscore_number_literals",
"qualified_constructor", "qualified_constructor",
"let_patterns" "let_patterns",
"lhs_matching"
]. ].
not_yet_compilable(fate) -> []; not_yet_compilable(fate) -> [];
@ -299,9 +300,22 @@ failing_contracts() ->
"Repeated name x in pattern\n" "Repeated name x in pattern\n"
" x :: x (at line 26, column 7)">>, " x :: x (at line 26, column 7)">>,
<<?Pos(44, 14) <<?Pos(44, 14)
"Repeated argument x to function repeated_arg (at line 44, column 14).">>, "Repeated names x, y in pattern\n"
<<?Pos(44, 14) " (x : int, y, x : string, y : bool) (at line 44, column 14)">>,
"Repeated argument y to function repeated_arg (at line 44, column 14).">>, <<?Pos(44, 39)
"Cannot unify int\n"
" and string\n"
"when checking the type of the expression at line 44, column 39\n"
" x : int\n"
"against the expected type\n"
" string">>,
<<?Pos(44, 72)
"Cannot unify int\n"
" and string\n"
"when checking the type of the expression at line 44, column 72\n"
" x : int\n"
"against the expected type\n"
" string">>,
<<?Pos(14, 24) <<?Pos(14, 24)
"No record type with fields y, z (at line 14, column 24)">>, "No record type with fields y, z (at line 14, column 24)">>,
<<?Pos(15, 26) <<?Pos(15, 26)
@ -367,73 +381,7 @@ failing_contracts() ->
<<?Pos(3, 13) <<?Pos(3, 13)
"Nested namespace not allowed\nNamespace 'Foo' at line 3, column 13 not defined at top level.">>]) "Nested namespace not allowed\nNamespace 'Foo' at line 3, column 13 not defined at top level.">>])
, ?TYPE_ERROR(bad_address_literals, , ?TYPE_ERROR(bad_address_literals,
[<<?Pos(32, 5) [<<?Pos(11, 5)
"The type bytes(32) is not a contract type\n"
"when checking that the contract literal\n"
" ct_Ez6MyeTMm17YnTnDdHTSrzMEBKmy7Uz2sXu347bTDPgVH2ifJ\n"
"has the type\n"
" bytes(32)">>,
<<?Pos(30, 5)
"The type oracle(int, bool) is not a contract type\n"
"when checking that the contract literal\n"
" ct_Ez6MyeTMm17YnTnDdHTSrzMEBKmy7Uz2sXu347bTDPgVH2ifJ\n"
"has the type\n"
" oracle(int, bool)">>,
<<?Pos(28, 5)
"The type address is not a contract type\n"
"when checking that the contract literal\n"
" ct_Ez6MyeTMm17YnTnDdHTSrzMEBKmy7Uz2sXu347bTDPgVH2ifJ\n"
"has the type\n"
" address">>,
<<?Pos(25, 5)
"Cannot unify oracle_query('a, 'b)\n"
" and Remote\n"
"when checking the type of the expression at line 25, column 5\n"
" oq_2oRvyowJuJnEkxy58Ckkw77XfWJrmRgmGaLzhdqb67SKEL1gPY :\n"
" oracle_query('a, 'b)\n"
"against the expected type\n"
" Remote">>,
<<?Pos(23, 5)
"Cannot unify oracle_query('c, 'd)\n"
" and bytes(32)\n"
"when checking the type of the expression at line 23, column 5\n"
" oq_2oRvyowJuJnEkxy58Ckkw77XfWJrmRgmGaLzhdqb67SKEL1gPY :\n"
" oracle_query('c, 'd)\n"
"against the expected type\n"
" bytes(32)">>,
<<?Pos(21, 5)
"Cannot unify oracle_query('e, 'f)\n"
" and oracle(int, bool)\n"
"when checking the type of the expression at line 21, column 5\n"
" oq_2oRvyowJuJnEkxy58Ckkw77XfWJrmRgmGaLzhdqb67SKEL1gPY :\n"
" oracle_query('e, 'f)\n"
"against the expected type\n"
" oracle(int, bool)">>,
<<?Pos(18, 5)
"Cannot unify oracle('g, 'h)\n"
" and Remote\n"
"when checking the type of the expression at line 18, column 5\n"
" ok_2YNyxd6TRJPNrTcEDCe9ra59SVUdp9FR9qWC5msKZWYD9bP9z5 :\n"
" oracle('g, 'h)\n"
"against the expected type\n"
" Remote">>,
<<?Pos(16, 5)
"Cannot unify oracle('i, 'j)\n"
" and bytes(32)\n"
"when checking the type of the expression at line 16, column 5\n"
" ok_2YNyxd6TRJPNrTcEDCe9ra59SVUdp9FR9qWC5msKZWYD9bP9z5 :\n"
" oracle('i, 'j)\n"
"against the expected type\n"
" bytes(32)">>,
<<?Pos(14, 5)
"Cannot unify oracle('k, 'l)\n"
" and oracle_query(int, bool)\n"
"when checking the type of the expression at line 14, column 5\n"
" ok_2YNyxd6TRJPNrTcEDCe9ra59SVUdp9FR9qWC5msKZWYD9bP9z5 :\n"
" oracle('k, 'l)\n"
"against the expected type\n"
" oracle_query(int, bool)">>,
<<?Pos(11, 5)
"Cannot unify address\n" "Cannot unify address\n"
" and oracle(int, bool)\n" " and oracle(int, bool)\n"
"when checking the type of the expression at line 11, column 5\n" "when checking the type of the expression at line 11, column 5\n"
@ -454,6 +402,72 @@ failing_contracts() ->
" ak_2gx9MEFxKvY9vMG5YnqnXWv1hCsX7rgnfvBLJS4aQurustR1rt : address\n" " ak_2gx9MEFxKvY9vMG5YnqnXWv1hCsX7rgnfvBLJS4aQurustR1rt : address\n"
"against the expected type\n" "against the expected type\n"
" bytes(32)">>, " bytes(32)">>,
<<?Pos(14, 5)
"Cannot unify oracle('a, 'b)\n"
" and oracle_query(int, bool)\n"
"when checking the type of the expression at line 14, column 5\n"
" ok_2YNyxd6TRJPNrTcEDCe9ra59SVUdp9FR9qWC5msKZWYD9bP9z5 :\n"
" oracle('a, 'b)\n"
"against the expected type\n"
" oracle_query(int, bool)">>,
<<?Pos(16, 5)
"Cannot unify oracle('c, 'd)\n"
" and bytes(32)\n"
"when checking the type of the expression at line 16, column 5\n"
" ok_2YNyxd6TRJPNrTcEDCe9ra59SVUdp9FR9qWC5msKZWYD9bP9z5 :\n"
" oracle('c, 'd)\n"
"against the expected type\n"
" bytes(32)">>,
<<?Pos(18, 5)
"Cannot unify oracle('e, 'f)\n"
" and Remote\n"
"when checking the type of the expression at line 18, column 5\n"
" ok_2YNyxd6TRJPNrTcEDCe9ra59SVUdp9FR9qWC5msKZWYD9bP9z5 :\n"
" oracle('e, 'f)\n"
"against the expected type\n"
" Remote">>,
<<?Pos(21, 5)
"Cannot unify oracle_query('g, 'h)\n"
" and oracle(int, bool)\n"
"when checking the type of the expression at line 21, column 5\n"
" oq_2oRvyowJuJnEkxy58Ckkw77XfWJrmRgmGaLzhdqb67SKEL1gPY :\n"
" oracle_query('g, 'h)\n"
"against the expected type\n"
" oracle(int, bool)">>,
<<?Pos(23, 5)
"Cannot unify oracle_query('i, 'j)\n"
" and bytes(32)\n"
"when checking the type of the expression at line 23, column 5\n"
" oq_2oRvyowJuJnEkxy58Ckkw77XfWJrmRgmGaLzhdqb67SKEL1gPY :\n"
" oracle_query('i, 'j)\n"
"against the expected type\n"
" bytes(32)">>,
<<?Pos(25, 5)
"Cannot unify oracle_query('k, 'l)\n"
" and Remote\n"
"when checking the type of the expression at line 25, column 5\n"
" oq_2oRvyowJuJnEkxy58Ckkw77XfWJrmRgmGaLzhdqb67SKEL1gPY :\n"
" oracle_query('k, 'l)\n"
"against the expected type\n"
" Remote">>,
<<?Pos(28, 5)
"The type address is not a contract type\n"
"when checking that the contract literal\n"
" ct_Ez6MyeTMm17YnTnDdHTSrzMEBKmy7Uz2sXu347bTDPgVH2ifJ\n"
"has the type\n"
" address">>,
<<?Pos(30, 5)
"The type oracle(int, bool) is not a contract type\n"
"when checking that the contract literal\n"
" ct_Ez6MyeTMm17YnTnDdHTSrzMEBKmy7Uz2sXu347bTDPgVH2ifJ\n"
"has the type\n"
" oracle(int, bool)">>,
<<?Pos(32, 5)
"The type bytes(32) is not a contract type\n"
"when checking that the contract literal\n"
" ct_Ez6MyeTMm17YnTnDdHTSrzMEBKmy7Uz2sXu347bTDPgVH2ifJ\n"
"has the type\n"
" bytes(32)">>,
<<?Pos(34, 5), <<?Pos(34, 5),
"The type address is not a contract type\n" "The type address is not a contract type\n"
"when checking that the call to\n" "when checking that the call to\n"
@ -569,7 +583,7 @@ failing_contracts() ->
"Failed to resolve byte array lengths in call to Bytes.split with argument of type\n" "Failed to resolve byte array lengths in call to Bytes.split with argument of type\n"
" - 'f (at line 12, column 20)\n" " - 'f (at line 12, column 20)\n"
"and result types\n" "and result types\n"
" - 'e (at line 13, column 5)\n" " - 'e (at line 12, column 25)\n"
" - bytes(20) (at line 12, column 29)">>, " - bytes(20) (at line 12, column 29)">>,
<<?Pos(16, 5) <<?Pos(16, 5)
"Failed to resolve byte array lengths in call to Bytes.split with argument of type\n" "Failed to resolve byte array lengths in call to Bytes.split with argument of type\n"
@ -582,7 +596,7 @@ failing_contracts() ->
" - 'b (at line 18, column 20)\n" " - 'b (at line 18, column 20)\n"
"and result types\n" "and result types\n"
" - bytes(20) (at line 18, column 25)\n" " - bytes(20) (at line 18, column 25)\n"
" - 'a (at line 19, column 5)">>]) " - 'a (at line 18, column 37)">>])
, ?TYPE_ERROR(wrong_compiler_version, , ?TYPE_ERROR(wrong_compiler_version,
[<<?Pos(1, 1) [<<?Pos(1, 1)
"Cannot compile with this version of the compiler,\n" "Cannot compile with this version of the compiler,\n"

View File

@ -16,7 +16,7 @@ simple_contracts_test_() ->
" function id(x) = x\n", " function id(x) = x\n",
?assertMatch( ?assertMatch(
[{contract, _, {con, _, "Identity"}, [{contract, _, {con, _, "Identity"},
[{letfun, _, {id, _, "id"}, [{arg, _, {id, _, "x"}, {id, _, "_"}}], {id, _, "_"}, [{letfun, _, {id, _, "id"}, [{id, _, "x"}], {id, _, "_"},
{id, _, "x"}}]}], parse_string(Text)), {id, _, "x"}}]}], parse_string(Text)),
ok ok
end}, end},

View File

@ -11,7 +11,7 @@ contract Factorial =
stateful entrypoint set_worker(worker) = put(state{worker = worker}) stateful entrypoint set_worker(worker) = put(state{worker = worker})
entrypoint fac(x : int) : int = entrypoint
if(x == 0) 1 fac : int => int
else x * state.worker.fac(x - 1) fac(0) = 1
fac(x) = x * state.worker.fac(x - 1)

View File

@ -0,0 +1,22 @@
contract LHSMatching =
function from_some(Some(x)) = x
function
length : list('a) => int
length([]) = 0
length(_ :: xs) = 1 + length(xs)
function
append([], ys) = ys
append(x :: xs, ys) = x :: append(xs, ys)
function local_match(xs : list('a)) =
let null([]) = true
let null(_ :: _) = false
!null(xs)
entrypoint main() =
from_some(Some([0]))
++ append([length([true]), 2, 3], [4, 5, 6])
++ [7 | if (local_match([false]))]

View File

@ -8,10 +8,9 @@ contract Stack =
entrypoint init(ss : list(string)) = { stack = ss, size = length(ss) } entrypoint init(ss : list(string)) = { stack = ss, size = length(ss) }
function length(xs) = function
switch(xs) length([]) = 0
[] => 0 length(_ :: xs) = length(xs) + 1
_ :: xs => length(xs) + 1
stateful entrypoint pop() : string = stateful entrypoint pop() : string =
switch(state.stack) switch(state.stack)