diff --git a/src/aeso_ast_infer_types.erl b/src/aeso_ast_infer_types.erl index ea9c1ae..b80ecfc 100644 --- a/src/aeso_ast_infer_types.erl +++ b/src/aeso_ast_infer_types.erl @@ -106,6 +106,7 @@ , in_pattern = false :: boolean() , stateful = false :: boolean() , current_function = none :: none | aeso_syntax:id() + , what = top :: top | namespace | contract }). -type env() :: #env{}. @@ -162,7 +163,7 @@ check_tvar(#env{ typevars = TVars}, T = {tvar, _, X}) -> -spec bind_fun(name(), type() | typesig(), env()) -> env(). bind_fun(X, Type, Env) -> - case lookup_name(Env, [X]) of + case lookup_env(Env, term, [], [X]) of false -> force_bind_fun(X, Type, Env); {_QId, {Ann1, _}} -> type_error({duplicate_definition, X, [Ann1, aeso_syntax:get_ann(Type)]}), @@ -171,9 +172,14 @@ bind_fun(X, Type, Env) -> -spec force_bind_fun(name(), type() | typesig(), env()) -> env(). force_bind_fun(X, Type, Env) -> - Ann = aeso_syntax:get_ann(Type), + Ann = aeso_syntax:get_ann(Type), + NoCode = get_option(no_code, false), + Entry = case X == "init" andalso Env#env.what == contract andalso not NoCode of + true -> {reserved_init, Ann, Type}; + false -> {Ann, Type} + end, on_current_scope(Env, fun(Scope = #scope{ funs = Funs }) -> - Scope#scope{ funs = [{X, {Ann, Type}} | Funs] } + Scope#scope{ funs = [{X, Entry} | Funs] } end). -spec bind_funs([{name(), type() | typesig()}], env()) -> env(). @@ -253,34 +259,32 @@ possible_scopes(#env{ namespace = Current}, Name) -> Qual = lists:droplast(Name), [ lists:sublist(Current, I) ++ Qual || I <- lists:seq(0, length(Current)) ]. --spec lookup_name(env(), qname()) -> false | {qname(), fun_info()}. -lookup_name(Env, Name) -> - lookup_env(Env, term, Name). - -spec lookup_type(env(), type_id()) -> false | {qname(), type_info()}. lookup_type(Env, Id) -> - lookup_env(Env, type, qname(Id)). + lookup_env(Env, type, aeso_syntax:get_ann(Id), qname(Id)). --spec lookup_env(env(), term, qname()) -> false | {qname(), fun_info()}; - (env(), type, qname()) -> false | {qname(), type_info()}. -lookup_env(Env, Kind, Name) -> +-spec lookup_env(env(), term, aeso_syntax:ann(), qname()) -> false | {qname(), fun_info()}; + (env(), type, aeso_syntax:ann(), qname()) -> false | {qname(), type_info()}. +lookup_env(Env, Kind, Ann, Name) -> Var = case Name of [X] when Kind == term -> proplists:get_value(X, Env#env.vars, false); _ -> false end, case Var of - {Ann, Type} -> {Name, {Ann, Type}}; + {Ann1, Type} -> {Name, {Ann1, Type}}; false -> Names = [ Qual ++ [lists:last(Name)] || Qual <- possible_scopes(Env, Name) ], - case [ Res || QName <- Names, Res <- [lookup_env1(Env, Kind, QName)], Res /= false] of + case [ Res || QName <- Names, Res <- [lookup_env1(Env, Kind, Ann, QName)], Res /= false] of [] -> false; [Res] -> Res; - Many -> type_error({ambiguous_name, [{qid, Ann, Q} || {Q, {Ann, _}} <- Many]}) + Many -> + type_error({ambiguous_name, [{qid, A, Q} || {Q, {A, _}} <- Many]}), + false end end. --spec lookup_env1(env(), type | term, qname()) -> false | {qname(), fun_info()}. -lookup_env1(#env{ namespace = Current, scopes = Scopes }, Kind, QName) -> +-spec lookup_env1(env(), type | term, aeso_syntax:ann(), qname()) -> false | {qname(), fun_info()}. +lookup_env1(#env{ namespace = Current, scopes = Scopes }, Kind, Ann, QName) -> Qual = lists:droplast(QName), Name = lists:last(QName), AllowPrivate = lists:prefix(Qual, Current), @@ -295,9 +299,12 @@ lookup_env1(#env{ namespace = Current, scopes = Scopes }, Kind, QName) -> %% Look up the unqualified name case proplists:get_value(Name, Defs, false) of false -> false; - {Ann, _} = E -> + {reserved_init, Ann1, Type} -> + type_error({cannot_call_init_function, Ann}), + {QName, {Ann1, Type}}; %% Return the type to avoid an extra not-in-scope error + {Ann1, _} = E -> %% Check that it's not private (or we can see private funs) - case not is_private(Ann) orelse AllowPrivate of + case not is_private(Ann1) orelse AllowPrivate of true -> {QName, E}; false -> false end @@ -532,7 +539,7 @@ map_t(As, K, V) -> {app_t, As, {id, As, "map"}, [K, V]}. infer(Contracts) -> infer(Contracts, []). --type option() :: return_env | dont_unfold. +-type option() :: return_env | dont_unfold | no_code | term(). -spec init_env(list(option())) -> env(). init_env(_Options) -> global_env(). @@ -541,6 +548,7 @@ init_env(_Options) -> global_env(). infer(Contracts, Options) -> ets_init(), %% Init the ETS table state try + io:format("Options = ~p\n", [Options]), Env = init_env(Options), create_options(Options), ets_new(type_vars, [set]), @@ -602,7 +610,8 @@ infer_constant({letval, Attrs,_Pattern, Type, E}) -> %% infer_contract takes a proplist mapping global names to types, and %% a list of definitions. -spec infer_contract(env(), contract | namespace, [aeso_syntax:decl()]) -> {env(), [aeso_syntax:decl()]}. -infer_contract(Env, What, Defs) -> +infer_contract(Env0, What, Defs) -> + Env = Env0#env{ what = What }, Kind = fun({type_def, _, _, _, _}) -> type; ({letfun, _, _, _, _, _}) -> function; ({fun_decl, _, _, _}) -> prototype; @@ -830,8 +839,8 @@ is_string_type({bytes_t, _, N}) -> N > 32; is_string_type(_) -> false. -spec check_constructor_overlap(env(), aeso_syntax:con(), type()) -> ok | no_return(). -check_constructor_overlap(Env, Con = {con, _, Name}, NewType) -> - case lookup_name(Env, Name) of +check_constructor_overlap(Env, Con = {con, Ann, Name}, NewType) -> + case lookup_env(Env, term, Ann, Name) of false -> ok; {_, {Ann, Type}} -> OldType = case Type of {type_sig, _, _, _, T} -> T; @@ -965,7 +974,7 @@ lookup_name(Env, As, Name) -> lookup_name(Env, As, Name, []). lookup_name(Env, As, Id, Options) -> - case lookup_name(Env, qname(Id)) of + case lookup_env(Env, term, As, qname(Id)) of false -> type_error({unbound_variable, Id}), {Id, fresh_uvar(As)}; diff --git a/src/aeso_compiler.erl b/src/aeso_compiler.erl index eeba90c..014d17b 100644 --- a/src/aeso_compiler.erl +++ b/src/aeso_compiler.erl @@ -144,7 +144,7 @@ string_to_code(ContractString, Options) -> Ast = parse(ContractString, Options), pp_sophia_code(Ast, Options), pp_ast(Ast, Options), - {TypeEnv, TypedAst} = aeso_ast_infer_types:infer(Ast, [return_env]), + {TypeEnv, TypedAst} = aeso_ast_infer_types:infer(Ast, [return_env | Options]), pp_typed_ast(TypedAst, Options), case proplists:get_value(backend, Options, aevm) of aevm -> diff --git a/test/aeso_abi_tests.erl b/test/aeso_abi_tests.erl index c6ce469..3042f92 100644 --- a/test/aeso_abi_tests.erl +++ b/test/aeso_abi_tests.erl @@ -80,11 +80,11 @@ encode_decode_sophia_string(SophiaType, String) -> , " record r = {x : an_alias(int), y : variant}\n" , " datatype variant = Red | Blue(map(string, int))\n" , " entrypoint foo : arg_type => arg_type\n" ], - case aeso_compiler:check_call(lists:flatten(Code), "foo", [String], []) of + case aeso_compiler:check_call(lists:flatten(Code), "foo", [String], [no_code]) of {ok, _, {[Type], _}, [Arg]} -> io:format("Type ~p~n", [Type]), Data = encode(Arg), - case aeso_compiler:to_sophia_value(Code, "foo", ok, Data, []) of + case aeso_compiler:to_sophia_value(Code, "foo", ok, Data, [no_code]) of {ok, Sophia} -> lists:flatten(io_lib:format("~s", [prettypr:format(aeso_pretty:expr(Sophia))])); {error, Err} -> @@ -152,7 +152,7 @@ oracle_test() -> " Oracle.get_question(o, q)\n", {ok, _, {[word, word], {list, string}}, [16#123, 16#456]} = aeso_compiler:check_call(Contract, "question", ["ok_111111111111111111111111111111ZrdqRz9", - "oq_1111111111111111111111111111113AFEFpt5"], []), + "oq_1111111111111111111111111111113AFEFpt5"], [no_code]), ok. @@ -174,7 +174,7 @@ encode_decode_calldata(FunName, Types, Args, RetType) -> encode_decode_calldata_(Code, FunName, Args, RetVMType) -> {ok, Calldata} = aeso_compiler:create_calldata(Code, FunName, Args, []), - {ok, _, {ArgTypes, RetType}, _} = aeso_compiler:check_call(Code, FunName, Args, [{backend, aevm}]), + {ok, _, {ArgTypes, RetType}, _} = aeso_compiler:check_call(Code, FunName, Args, [{backend, aevm}, no_code]), ?assertEqual(RetType, RetVMType), CalldataType = {tuple, [word, {tuple, ArgTypes}]}, {ok, {_Hash, ArgTuple}} = aeb_heap:from_binary(CalldataType, Calldata),