This commit is contained in:
radrow 2023-04-16 16:02:09 +02:00
parent 8d7025f794
commit 3cd8c37399
4 changed files with 100 additions and 196 deletions

View File

@ -1,17 +1,13 @@
# Changelog
All notable changes to this project will be documented in this file.
All notable changes to this project shall be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
and this project adheres to [Semantic
Versioning](https://semver.org/spec/v2.0.0.html). One deviation from _Keep a
Changelog_ is that "Unreleased" may suggest a specific version bump in case of
breaking changes.
## [Unreleased]
### Added
### Changed
### Removed
### Fixed
## [8.x.x]
## [Unreleased] [8.x.x]
### Added
### Changed
- `pp_assembler` option to `pp_fate` as it is more specific.

View File

@ -834,62 +834,46 @@ global_env() ->
option_t(As, T) -> {app_t, As, {id, As, "option"}, [T]}.
map_t(As, K, V) -> {app_t, As, {id, As, "map"}, [K, V]}.
-spec infer(aeso_syntax:ast()) -> {aeso_syntax:ast(), aeso_syntax:ast(), [aeso_warnings:warning()]} | {env(), aeso_syntax:ast(), aeso_syntax:ast(), [aeso_warnings:warning()]}.
infer(Contracts) ->
infer(Contracts, []).
-type option() :: return_env | dont_unfold | no_code | debug_mode | term().
init_ets(Options) ->
-spec init_env(list(option())) -> env().
init_env(_Options) -> global_env().
-spec infer(aeso_syntax:ast(), list(option())) ->
{aeso_syntax:ast(), aeso_syntax:ast(), [aeso_warnings:warning()]} | {env(), aeso_syntax:ast(), aeso_syntax:ast(), [aeso_warnings:warning()]}.
infer([], Options) ->
create_type_errors(),
type_error({no_decls, proplists:get_value(src_file, Options, no_file)}),
destroy_and_report_type_errors(init_env(Options));
infer(Contracts, Options) ->
ets_init(), %% Init the ETS table state
try
Env = init_env(Options),
create_options(Options),
ets_new(defined_contracts, [bag]),
ets_new(type_vars, [set]),
ets_new(warnings, [bag]),
ets_new(type_vars_variance, [set]),
ets_new(functions_to_implement, [set]),
%% Set the variance for builtin types
ets_insert(type_vars_variance, {"list", [covariant]}),
ets_insert(type_vars_variance, {"option", [covariant]}),
ets_insert(type_vars_variance, {"map", [covariant, covariant]}),
ets_insert(type_vars_variance, {"oracle", [contravariant, covariant]}),
ets_insert(type_vars_variance, {"oracle_query", [covariant, covariant]}),
-spec infer([aeso_syntax:ast()]) -> {aeso_syntax:ast(), aeso_syntax:ast(), [aeso_warnings:warning()]} | {env(), aeso_syntax:ast(), aeso_syntax:ast(), [aeso_warnings:warning()]}.
infer(Modules) ->
infer(Modules, []).
when_warning(warn_unused_functions, fun() -> create_unused_functions() end),
check_modifiers(Env, Contracts),
create_type_errors(),
Contracts1 = identify_main_contract(Contracts, Options),
destroy_and_report_type_errors(Env),
{Env1, Decls} = infer1(Env, Contracts1, [], Options),
infer(Modules, Options) ->
infer(init_env(Options), Modules, Options).
infer(Env, Modules, Options) ->
ets_init(),
try
{Env1, Modules1} = infer(Env, Modules, [], Options),
when_warning(warn_unused_functions, fun() -> destroy_and_report_unused_functions() end),
when_option(warn_error, fun() -> destroy_and_report_warnings_as_type_errors() end),
WarningsUnsorted = lists:map(fun mk_warning/1, ets_tab2list(warnings)),
Warnings = aeso_warnings:sort_warnings(WarningsUnsorted),
{Env2, DeclsFolded, DeclsUnfolded} =
case proplists:get_value(dont_unfold, Options, false) of
true -> {Env1, Decls, Decls};
false -> E = on_scopes(Env1, fun(Scope) -> unfold_record_types(Env1, Scope) end),
{E, Decls, unfold_record_types(E, Decls)}
end,
case proplists:get_value(return_env, Options, false) of
false -> {DeclsFolded, DeclsUnfolded, Warnings};
true -> {Env2, DeclsFolded, DeclsUnfolded, Warnings}
{Env1, Modules1, Warnings}
end
after
clean_up_ets()
end.
-spec infer(aeso_syntax:ast(), list(option())) ->
{aeso_syntax:ast(), aeso_syntax:ast(), [aeso_warnings:warning()]} | {env(), aeso_syntax:ast(), aeso_syntax:ast(), [aeso_warnings:warning()]}.
infer(Env, [[]|Rest], Acc, Options) ->
type_error({no_decls, proplists:get_value(src_file, Options, no_file)}),
infer(Env, Rest, Acc, Options);
infer(Env, [Module|Rest], Acc, Options) ->
{Env1, Modules1} = infer1(Env, Module, [], Options),
infer(Env1, Rest, [Module1|Acc], Options);
infer(Env, [], Acc, _) ->
Modules = lists:reverse(Acc),
{Env, Modules}.
-spec infer1(env(), [aeso_syntax:decl()], [aeso_syntax:decl()], list(option())) ->
{env(), [aeso_syntax:decl()]}.
infer1(Env, [], Acc, _Options) -> {Env, lists:reverse(Acc)};
@ -2308,7 +2292,17 @@ clean_up_ets() ->
%% The interface functions behave as the standard ETS interface.
ets_init() ->
put(aeso_ast_infer_types, #{}).
put(aeso_ast_infer_types, #{}),
create_options(Options),
ets_new(defined_contracts, [bag]),
ets_new(type_vars, [set]),
ets_new(warnings, [bag]),
ets_new(functions_to_implement, [set]),
when_warning(warn_unused_functions, fun() -> create_unused_functions() end),
check_modifiers(Env, Contracts),
create_type_var_variance(),
create_type_errors(),
ok.
ets_tab_exists(Name) ->
Tabs = get(aeso_ast_infer_types),
@ -3347,6 +3341,16 @@ destroy_and_report_warnings_as_type_errors() ->
Errors = lists:map(fun mk_t_err_from_warn/1, Warnings),
aeso_errors:throw(Errors). %% No-op if Warnings == []
create_type_vars_variance() ->
ets_new(type_vars_variance, [set]),
%% Set the variance for builtin types
ets_insert(type_vars_variance, {"list", [covariant]}),
ets_insert(type_vars_variance, {"option", [covariant]}),
ets_insert(type_vars_variance, {"map", [covariant, covariant]}),
ets_insert(type_vars_variance, {"oracle", [contravariant, covariant]}),
ets_insert(type_vars_variance, {"oracle_query", [covariant, covariant]}),
ok.
%% Strip current namespace from error message for nicer printing.
unqualify(#env{ namespace = NS }, {qid, Ann, Xs}) ->
qid(Ann, unqualify1(NS, Xs));
@ -4151,4 +4155,3 @@ updates_key(Name, Updates) ->
indexed(I, Xs) ->
lists:zip(lists:seq(I, I + length(Xs) - 1), Xs).

View File

@ -8,22 +8,17 @@
-export([string/1,
string/2,
string/3,
auto_imports/1,
hash_include/2,
decl/0,
type/0,
body/0,
maybe_block/1,
run_parser/2,
run_parser/3]).
run_parser/2]).
-include("aeso_parse_lib.hrl").
-import(aeso_parse_lib, [current_file/0, set_current_file/1,
current_include_type/0, set_current_include_type/1]).
-type parse_result() :: aeso_syntax:ast() | {aeso_syntax:ast(), sets:set(include_hash())} | none().
-type include_hash() :: {string(), binary()}.
-type parse_result() :: aeso_syntax:ast() | none().
escape_errors({ok, Ok}) ->
@ -31,32 +26,21 @@ escape_errors({ok, Ok}) ->
escape_errors({error, Err}) ->
parse_error(Err).
-spec string(string()) -> parse_result().
string(String) ->
string(String, sets:new(), []).
-spec module(string()) -> parse_result().
module(String) ->
module(String, []).
-spec string(string(), aeso_compiler:options()) -> parse_result().
string(String, Opts) ->
case lists:keyfind(src_file, 1, Opts) of
{src_file, File} -> string(String, sets:add_element(File, sets:new()), Opts);
false -> string(String, sets:new(), Opts)
end.
-spec string(string(), sets:set(include_hash()), aeso_compiler:options()) -> parse_result().
string(String, Included, Opts) ->
-spec module(string(), aeso_compiler:options()) -> parse_result().
module(String, Opts) ->
AST = run_parser(file(), String, Opts),
case expand_includes(AST, Included, Opts) of
{ok, AST1} -> AST1;
{error, Err} -> parse_error(Err)
end.
add_auto_imports(AST).
run_parser(P, Inp) ->
escape_errors(parse_and_scan(P, Inp, [])).
escape_errors(scan_and_parse(P, Inp, [])).
run_parser(P, Inp, Opts) ->
escape_errors(parse_and_scan(P, Inp, Opts)).
escape_errors(scan_and_parse(P, Inp, Opts)).
parse_and_scan(P, S, Opts) ->
scan_and_parse(P, S, Opts) ->
set_current_file(proplists:get_value(src_file, Opts, no_file)),
set_current_include_type(proplists:get_value(include_type, Opts, none)),
case aeso_scan:scan(S) of
@ -560,6 +544,9 @@ pos_ann(Line, Col) ->
, {line, Line}
, {col, Col} ].
top_ann() ->
pos_ann(0, 0).
ann_pos(Ann) ->
{proplists:get_value(file, Ann),
proplists:get_value(line, Ann),
@ -681,107 +668,13 @@ bad_expr_err(Reason, E) ->
%% -- Helper functions -------------------------------------------------------
expand_includes(AST, Included, Opts) ->
add_auto_imports(AST) ->
Ann = [{origin, system}],
AST1 = [ {include, Ann, {string, Ann, File}}
|| File <- lists:usort(auto_imports(AST)) ] ++ AST,
expand_includes(AST1, Included, [], Opts).
[ {using, Ann, {con, Ann, Import}}
|| Import <- lists:usort(auto_imports(AST)) ] ++ AST.
expand_includes([], Included, Acc, Opts) ->
case lists:member(keep_included, Opts) of
false ->
{ok, lists:reverse(Acc)};
true ->
{ok, {lists:reverse(Acc), Included}}
end;
expand_includes([{include, Ann, {string, _SAnn, File}} | AST], Included, Acc, Opts) ->
case get_include_code(File, Ann, Opts) of
{ok, Code} ->
Hashed = hash_include(File, Code),
case sets:is_element(Hashed, Included) of
false ->
SrcFile = proplists:get_value(src_file, Opts, no_file),
IncludeType = case proplists:get_value(file, Ann) of
SrcFile -> direct;
_ -> indirect
end,
Opts1 = lists:keystore(src_file, 1, Opts, {src_file, File}),
Opts2 = lists:keystore(include_type, 1, Opts1, {include_type, IncludeType}),
Included1 = sets:add_element(Hashed, Included),
case parse_and_scan(file(), Code, Opts2) of
{ok, AST1} ->
expand_includes(AST1 ++ AST, Included1, Acc, Opts);
Err = {error, _} ->
Err
end;
true ->
expand_includes(AST, Included, Acc, Opts)
end;
Err = {error, _} ->
Err
end;
expand_includes([E | AST], Included, Acc, Opts) ->
expand_includes(AST, Included, [E | Acc], Opts).
read_file(File, Opts) ->
case proplists:get_value(include, Opts, {explicit_files, #{}}) of
{file_system, Paths} ->
CandidateNames = [ filename:join(Dir, File) || Dir <- Paths ],
lists:foldr(fun(F, {error, _}) -> file:read_file(F);
(_F, OK) -> OK end, {error, not_found}, CandidateNames);
{explicit_files, Files} ->
case maps:get(binary_to_list(File), Files, not_found) of
not_found -> {error, not_found};
Src -> {ok, Src}
end;
escript ->
try
Escript = escript:script_name(),
{ok, Sections} = escript:extract(Escript, []),
Archive = proplists:get_value(archive, Sections),
FileName = binary_to_list(filename:join([aesophia, priv, stdlib, File])),
case zip:extract(Archive, [{file_list, [FileName]}, memory]) of
{ok, [{_, Src}]} -> {ok, Src};
_ -> {error, not_found}
end
catch _:_ ->
{error, not_found}
end
end.
stdlib_options() ->
StdLibDir = aeso_stdlib:stdlib_include_path(),
case filelib:is_dir(StdLibDir) of
true -> [{include, {file_system, [StdLibDir]}}];
false -> [{include, escript}]
end.
get_include_code(File, Ann, Opts) ->
case {read_file(File, Opts), read_file(File, stdlib_options())} of
{{ok, Bin}, {ok, _}} ->
case filename:basename(File) == File of
true -> { error
, fail( ann_pos(Ann)
, "Illegal redefinition of standard library " ++ binary_to_list(File))};
%% If a path is provided then the stdlib takes lower priority
false -> {ok, binary_to_list(Bin)}
end;
{_, {ok, Bin}} ->
{ok, binary_to_list(Bin)};
{{ok, Bin}, _} ->
{ok, binary_to_list(Bin)};
{_, _} ->
{error, {ann_pos(Ann), include_error, File}}
end.
-spec hash_include(string() | binary(), string()) -> include_hash().
hash_include(File, Code) when is_binary(File) ->
hash_include(binary_to_list(File), Code);
hash_include(File, Code) when is_list(File) ->
{filename:basename(File), crypto:hash(sha256, Code)}.
auto_imports({comprehension_bind, _, _}) -> [<<"ListInternal.aes">>];
auto_imports({'..', _}) -> [<<"ListInternal.aes">>];
auto_imports({comprehension_bind, _, _}) -> ["ListInternal"];
auto_imports({'..', _}) -> ["ListInternal"];
auto_imports(L) when is_list(L) ->
lists:flatmap(fun auto_imports/1, L);
auto_imports(T) when is_tuple(T) ->

View File

@ -13,19 +13,20 @@
-export_type([ann_line/0, ann_col/0, ann_origin/0, ann_format/0, ann/0]).
-export_type([name/0, id/0, con/0, qid/0, qcon/0, tvar/0, op/0]).
-export_type([bin_op/0, un_op/0]).
-export_type([decl/0, letbind/0, typedef/0, pragma/0, fundecl/0]).
-export_type([top_decl/0, decl/0, letbind/0, typedef/0, pragma/0, fundecl/0]).
-export_type([arg/0, field_t/0, constructor_t/0, named_arg_t/0]).
-export_type([type/0, constant/0, expr/0, arg_expr/0, field/1, stmt/0, alt/0, lvalue/0, elim/0, pat/0]).
-export_type([ast/0]).
-type ast() :: [decl()].
-type ast() :: [top_decl()].
-type ann_line() :: integer().
-type ann_col() :: integer().
-type ann_origin() :: system | user.
-type ann_format() :: '?:' | hex | infix | prefix | elif.
-type ann() :: [ {line, ann_line()} | {col, ann_col()} | {format, ann_format()} | {origin, ann_origin()}
-type ann() :: [ {line, ann_line()} | {col, ann_col()} | {file, ann_file()}
| {format, ann_format()} | {origin, ann_origin()}
| stateful | private | payable | main | interface | entrypoint].
-type name() :: string().
@ -38,20 +39,31 @@
-type namespace_alias() :: none | con().
-type namespace_parts() :: none | {for, [id()]} | {hiding, [id()]}.
-type decl() :: {contract_main, ann(), con(), [con()], [decl()]}
| {contract_child, ann(), con(), [con()], [decl()]}
| {contract_interface, ann(), con(), [con()], [decl()]}
| {namespace, ann(), con(), [decl()]}
| {include, ann(), {string, ann(), string()}}
| {pragma, ann(), pragma()}
% Can't be toplevel
-type scoped_decl()
:: {contract_decl, ann(), con()}
| {namespace_decl, ann(), con()}
| {type_decl, ann(), id(), [tvar()]} % Only for error msgs
| {type_def, ann(), id(), [tvar()], typedef()}
| {fun_clauses, ann(), id(), type(), [letfun() | fundecl()]}
| {block, ann(), [decl()]}
| {using, ann(), con(), namespace_alias(), namespace_parts()}
| {block, ann(), [scoped_decl()]}
| fundecl()
| letfun()
| letval(). % Only for error msgs
| letval() % Only for error msgs
| decl().
% Toplevel, can be nested
-type decl()
:: {contract_main, ann(), con(), [con()], [scoped_decl()]}
| {contract_child, ann(), con(), [con()], [scoped_decl()]}
| {contract_interface, ann(), con(), [con()], [scoped_decl()]}
| {namespace, ann(), con(), [scoped_decl()]}
| {using, ann(), con(), namespace_alias(), namespace_parts()}.
% Toplevel only
-type top_decl()
:: {pragma, ann(), pragma()}
| decl().
-type compiler_version() :: [non_neg_integer()].