diff --git a/src/aeso_ast_infer_types.erl b/src/aeso_ast_infer_types.erl index c1ee415..0470f07 100644 --- a/src/aeso_ast_infer_types.erl +++ b/src/aeso_ast_infer_types.erl @@ -150,17 +150,20 @@ check_tvar(#env{ typevars = TVars}, T = {tvar, _, X}) -> -spec bind_fun(name(), type() | typesig(), env()) -> env(). bind_fun(X, Type, Env) -> - Ann = aeso_syntax:get_ann(Type), case lookup_name(Env, [X]) of - false -> - on_current_scope(Env, fun(Scope = #scope{ funs = Funs }) -> - Scope#scope{ funs = [{X, {Ann, Type}} | Funs] } - end); + false -> force_bind_fun(X, Type, Env); {_QId, {Ann1, _}} -> - type_error({duplicate_definition, X, [Ann1, Ann]}), + type_error({duplicate_definition, X, [Ann1, aeso_syntax:get_ann(Type)]}), Env end. +-spec force_bind_fun(name(), type() | typesig(), env()) -> env(). +force_bind_fun(X, Type, Env) -> + Ann = aeso_syntax:get_ann(Type), + on_current_scope(Env, fun(Scope = #scope{ funs = Funs }) -> + Scope#scope{ funs = [{X, {Ann, Type}} | Funs] } + end). + -spec bind_funs([{name(), type() | typesig()}], env()) -> env(). bind_funs([], Env) -> Env; bind_funs([{Id, Type} | Rest], Env) -> @@ -182,8 +185,18 @@ bind_state(Env) -> {S, _} -> {qid, Ann, S}; false -> Unit end, - bind_funs([{"state", State}, - {"put", {fun_t, Ann, [], [State], Unit}}], Env). + Event = + case lookup_type(Env, {id, Ann, "event"}) of + {E, _} -> {qid, Ann, E}; + false -> {id, Ann, "event"} %% will cause type error if used(?) + end, + Env1 = bind_funs([{"state", State}, + {"put", {fun_t, Ann, [], [State], Unit}}], Env), + + %% A bit of a hack: we bind Chain.event with the local event type. + Env2 = force_bind_fun("event", {fun_t, Ann, [], [Event], Unit}, + Env1#env{ namespace = ["Chain"] }), + Env2#env{ namespace = Env1#env.namespace }. -spec bind_field(name(), field_info(), env()) -> env(). bind_field(X, Info, Env = #env{ fields = Fields }) -> @@ -325,7 +338,6 @@ global_env() -> Bool = {id, Ann, "bool"}, String = {id, Ann, "string"}, Address = {id, Ann, "address"}, - Event = {id, Ann, "event"}, Hash = {id, Ann, "hash"}, Bits = {id, Ann, "bits"}, Oracle = fun(Q, R) -> {app_t, Ann, {id, Ann, "oracle"}, [Q, R]} end, @@ -376,8 +388,7 @@ global_env() -> {"timestamp", Int}, {"block_height", Int}, {"difficulty", Int}, - {"gas_limit", Int}, - {"event", Fun1(Event, Unit)}]) + {"gas_limit", Int}]) , types = MkDefs([{"ttl", 0}]) }, ContractScope = #scope @@ -558,13 +569,13 @@ infer_contract(Env, What, Defs) -> end, Get = fun(K) -> [ Def || Def <- Defs, Kind(Def) == K ] end, {Env1, TypeDefs} = check_typedefs(Env, Get(type)), + create_type_errors(), Env2 = case What of namespace -> Env1; contract -> bind_state(Env1) %% bind state and put end, {ProtoSigs, Decls} = lists:unzip([ check_fundecl(Env1, Decl) || Decl <- Get(prototype) ]), - create_type_errors(), Env3 = bind_funs(ProtoSigs, Env2), Functions = Get(function), %% Check for duplicates in Functions (we turn it into a map below) @@ -780,8 +791,9 @@ check_reserved_entrypoints(Funs) -> check_fundecl(Env, {fun_decl, Ann, Id = {id, _, Name}, Type = {fun_t, _, _, _, _}}) -> Type1 = {fun_t, _, Named, Args, Ret} = check_type(Env, Type), {{Name, {type_sig, Ann, Named, Args, Ret}}, {fun_decl, Ann, Id, Type1}}; -check_fundecl(_, {fun_decl, _Attrib, {id, _, Name}, Type}) -> - error({fundecl_must_have_funtype, Name, Type}). +check_fundecl(Env, {fun_decl, Ann, Id = {id, _, Name}, Type}) -> + type_error({fundecl_must_have_funtype, Ann, Id, Type}), + {{Name, {type_sig, Ann, [], [], Type}}, check_type(Env, Type)}. infer_nonrec(Env, LetFun) -> create_constraints(), diff --git a/test/aeso_compiler_tests.erl b/test/aeso_compiler_tests.erl index 9f6f9d4..d558b5d 100644 --- a/test/aeso_compiler_tests.erl +++ b/test/aeso_compiler_tests.erl @@ -76,7 +76,9 @@ compilable_contracts() -> "builtin_map_get_bug", "nodeadcode", "deadcode", - "variant_types" + "variant_types", + "state_handling", + "events" ]. %% Contracts that should produce type errors diff --git a/test/contracts/state_handling.aes b/test/contracts/state_handling.aes index 737e644..7fdd196 100644 --- a/test/contracts/state_handling.aes +++ b/test/contracts/state_handling.aes @@ -1,19 +1,22 @@ contract Remote = - function look_at : (state) => () + record rstate = { i : int, s : string, m : map(int, int) } + + function look_at : (rstate) => () function return_s : (bool) => string function return_m : (bool) => map(int, int) - function get : (state) => state - function get_i : (state) => int - function get_s : (state) => string - function get_m : (state) => map(int, int) + function get : (rstate) => rstate + function get_i : (rstate) => int + function get_s : (rstate) => string + function get_m : (rstate) => map(int, int) - function fun_update_i : (state, int) => state - function fun_update_s : (state, string) => state - function fun_update_m : (state, map(int, int)) => state - function fun_update_mk : (state, int, int) => state + function fun_update_i : (rstate, int) => rstate + function fun_update_s : (rstate, string) => rstate + function fun_update_m : (rstate, map(int, int)) => rstate + function fun_update_mk : (rstate, int, int) => rstate contract StateHandling = - record state = { i : int, s : string, m : map(int, int) } + + type state = Remote.rstate function init(r : Remote, i : int) = let state0 = { i = 0, s = "undefined", m = {} }