Perform return type inference and application across local calls (#13984)
This commit is contained in:
@@ -88,9 +88,9 @@ start(_Type, _Args) ->
|
||||
|
||||
%% Compiler options
|
||||
{docs, true},
|
||||
{infer_signatures, true},
|
||||
{ignore_already_consolidated, false},
|
||||
{ignore_module_conflict, false},
|
||||
{infer_signatures, true},
|
||||
{on_undefined_variable, raise},
|
||||
{parser_options, [{columns, true}]},
|
||||
{debug_info, true},
|
||||
|
||||
@@ -50,7 +50,7 @@ define({Line, _S, #{module := Module} = E}, Kind, Call, Expr) ->
|
||||
Key
|
||||
end,
|
||||
|
||||
Args = [Kind, Store, elixir_locals:cache_env(E#{line := Line})],
|
||||
Args = [Kind, Store, elixir_module:cache_env(E#{line := Line})],
|
||||
{{'.', [], [elixir_def, store_definition]}, [], Args}.
|
||||
|
||||
unless_loaded(Fun, Args, Callback) ->
|
||||
|
||||
@@ -144,7 +144,6 @@ bootstrap() ->
|
||||
elixir_config:static(#{bootstrap => true}),
|
||||
elixir_config:put(docs, false),
|
||||
elixir_config:put(ignore_module_conflict, true),
|
||||
elixir_config:put(infer_signatures, false),
|
||||
elixir_config:put(on_undefined_variable, raise),
|
||||
elixir_config:put(parser_options, []),
|
||||
elixir_config:put(relative_paths, false),
|
||||
@@ -191,13 +190,13 @@ bootstrap_files() ->
|
||||
[
|
||||
<<"list/chars.ex">>,
|
||||
<<"bitwise.ex">>,
|
||||
<<"module/locals_tracker.ex">>,
|
||||
<<"module/parallel_checker.ex">>,
|
||||
<<"module/behaviour.ex">>,
|
||||
<<"module/types/helpers.ex">>,
|
||||
<<"module/types/descr.ex">>,
|
||||
<<"module/types/of.ex">>,
|
||||
<<"module/types/pattern.ex">>,
|
||||
<<"module/types/apply.ex">>,
|
||||
<<"module/types/expr.ex">>,
|
||||
<<"module/types.ex">>,
|
||||
<<"exception.ex">>,
|
||||
|
||||
@@ -37,6 +37,7 @@ fun_for(Meta, Module, Name, Arity, Kinds, External) ->
|
||||
{[{_, Kind, LocalMeta, _, _, _}], ClausesPairs} ->
|
||||
case (Kinds == all) orelse (lists:member(Kind, Kinds)) of
|
||||
true ->
|
||||
(Kind == defmacrop) andalso track_defmacrop(Module, Tuple),
|
||||
Local = {value, fun(Fun, Args) -> invoke_local(Meta, Module, Fun, Args, External) end},
|
||||
Clauses = [Clause || {_, Clause} <- ClausesPairs],
|
||||
elixir_erl:definition_to_anonymous(Kind, LocalMeta, Clauses, Local, External);
|
||||
@@ -60,6 +61,10 @@ invoke_local(Meta, Module, ErlName, Args, External) ->
|
||||
apply(Fun, Args)
|
||||
end.
|
||||
|
||||
track_defmacrop(Module, FunArity) ->
|
||||
{_, Bag} = elixir_module:data_tables(Module),
|
||||
ets:insert(Bag, {defmacrop_calls, FunArity}).
|
||||
|
||||
invoke_external(Meta, Mod, Name, Args, E) ->
|
||||
is_map(E) andalso elixir_env:trace({require, Meta, Mod, []}, E),
|
||||
apply(Mod, Name, Args).
|
||||
@@ -128,10 +133,10 @@ head_and_definition_meta(_, _Meta, _HeadDefaults, [{_, _, HeadMeta, _} | _]) ->
|
||||
%% Section for storing definitions
|
||||
|
||||
store_definition(Kind, {Call, Body}, Pos) ->
|
||||
E = elixir_locals:get_cached_env(Pos),
|
||||
E = elixir_module:get_cached_env(Pos),
|
||||
store_definition(Kind, false, Call, Body, E);
|
||||
store_definition(Kind, Key, Pos) ->
|
||||
#{module := Module} = E = elixir_locals:get_cached_env(Pos),
|
||||
#{module := Module} = E = elixir_module:get_cached_env(Pos),
|
||||
{Call, Body} = elixir_module:read_cache(Module, Key),
|
||||
store_definition(Kind, true, Call, Body, E).
|
||||
|
||||
@@ -210,7 +215,6 @@ store_definition(Meta, Kind, CheckClauses, Name, Arity, DefaultsArgs, Guards, Bo
|
||||
Clause <- def_to_clauses(Kind, Meta, Args, Guards, Body, E)],
|
||||
|
||||
DefaultsLength = length(Defaults),
|
||||
elixir_locals:record_defaults(Tuple, Kind, Module, DefaultsLength, Meta),
|
||||
check_previous_defaults(Meta, Module, Name, Arity, Kind, DefaultsLength, E),
|
||||
|
||||
store_definition(CheckClauses, Kind, Meta, Name, Arity, File,
|
||||
|
||||
@@ -55,7 +55,7 @@ import_function(Meta, Name, Arity, E) ->
|
||||
case find_import_by_name_arity(Meta, Tuple, [], E) of
|
||||
{function, Receiver} ->
|
||||
elixir_env:trace({imported_function, Meta, Receiver, Name, Arity}, E),
|
||||
elixir_locals:record_import(Tuple, Receiver, ?key(E, module), ?key(E, function)),
|
||||
elixir_import:record(Tuple, Receiver, ?key(E, module), ?key(E, function)),
|
||||
remote_function(Meta, Receiver, Name, Arity, E);
|
||||
{macro, _Receiver} ->
|
||||
false;
|
||||
@@ -74,7 +74,6 @@ import_function(Meta, Name, Arity, E) ->
|
||||
elixir_def:local_for(Meta, Name, Arity, [defmacro, defmacrop], E) of
|
||||
false ->
|
||||
elixir_env:trace({local_function, Meta, Name, Arity}, E),
|
||||
elixir_locals:record_local(Tuple, ?key(E, module), ?key(E, function), Meta, false),
|
||||
{local, Name, Arity};
|
||||
_ ->
|
||||
false
|
||||
@@ -183,10 +182,7 @@ expand_import(Meta, Name, Arity, E, Extra, AllowLocals, Trace) ->
|
||||
|
||||
%% Dispatch to the local.
|
||||
_ ->
|
||||
Trace andalso begin
|
||||
elixir_env:trace({local_macro, Meta, Name, Arity}, E),
|
||||
elixir_locals:record_local(Tuple, Module, ?key(E, function), Meta, true)
|
||||
end,
|
||||
Trace andalso elixir_env:trace({local_macro, Meta, Name, Arity}, E),
|
||||
{macro, Module, expander_macro_fun(Meta, Local, Module, Name, E)}
|
||||
end
|
||||
end.
|
||||
@@ -196,13 +192,13 @@ do_expand_import(Result, Meta, Name, Arity, Module, E, Trace) ->
|
||||
{function, Receiver} ->
|
||||
Trace andalso begin
|
||||
elixir_env:trace({imported_function, Meta, Receiver, Name, Arity}, E),
|
||||
elixir_locals:record_import({Name, Arity}, Receiver, Module, ?key(E, function))
|
||||
elixir_import:record({Name, Arity}, Receiver, Module, ?key(E, function))
|
||||
end,
|
||||
{function, Receiver, Name};
|
||||
{macro, Receiver} ->
|
||||
Trace andalso begin
|
||||
elixir_env:trace({imported_macro, Meta, Receiver, Name, Arity}, E),
|
||||
elixir_locals:record_import({Name, Arity}, Receiver, Module, ?key(E, function))
|
||||
elixir_import:record({Name, Arity}, Receiver, Module, ?key(E, function))
|
||||
end,
|
||||
{macro, Receiver, expander_macro_named(Meta, Receiver, Name, Arity, E)};
|
||||
{import, Receiver} ->
|
||||
|
||||
@@ -900,8 +900,7 @@ expand_local(Meta, Name, Args, S, #{module := Module, function := Function, cont
|
||||
|
||||
nil ->
|
||||
Arity = length(Args),
|
||||
elixir_env:trace({local_function, Meta, Name, Arity}, E),
|
||||
elixir_locals:record_local({Name, Arity}, Module, Function, Meta, false)
|
||||
elixir_env:trace({local_function, Meta, Name, Arity}, E)
|
||||
end,
|
||||
|
||||
{EArgs, SA, EA} = expand_args(Args, S, E),
|
||||
|
||||
@@ -2,7 +2,9 @@
|
||||
%% between local functions and imports.
|
||||
%% For imports dispatch, please check elixir_dispatch.
|
||||
-module(elixir_import).
|
||||
-export([import/6, import/7, special_form/2, format_error/1]).
|
||||
-export([import/6, import/7, special_form/2,
|
||||
record/4, ensure_no_local_conflict/3,
|
||||
format_error/1]).
|
||||
-compile(inline_list_funcs).
|
||||
-include("elixir.hrl").
|
||||
|
||||
@@ -135,6 +137,31 @@ calculate_key(Meta, Key, Old, New, E, Warn) ->
|
||||
{true, FinalSet, [{Key, FinalSet} | keydelete(Key, Old)]}
|
||||
end.
|
||||
|
||||
%% Record function calls for local conflicts
|
||||
|
||||
record(_Tuple, Receiver, Module, Function)
|
||||
when Function == nil; Module == Receiver -> false;
|
||||
record(Tuple, Receiver, Module, _Function) ->
|
||||
try
|
||||
{Set, _Bag} = elixir_module:data_tables(Module),
|
||||
ets:insert(Set, {{import, Tuple}, Receiver}),
|
||||
true
|
||||
catch
|
||||
error:badarg -> false
|
||||
end.
|
||||
|
||||
ensure_no_local_conflict('Elixir.Kernel', _All, _E) ->
|
||||
ok;
|
||||
ensure_no_local_conflict(Module, AllDefinitions, E) ->
|
||||
{Set, _} = elixir_module:data_tables(Module),
|
||||
|
||||
[try
|
||||
Receiver = ets:lookup_element(Set, {import, Pair}, 2),
|
||||
elixir_errors:module_error(Meta, E, ?MODULE, {import_conflict, Receiver, Pair})
|
||||
catch
|
||||
error:badarg -> false
|
||||
end || {Pair, _, Meta, _} <- AllDefinitions].
|
||||
|
||||
%% Retrieve functions and macros from modules
|
||||
|
||||
get_functions(Module, InfoCallback) ->
|
||||
@@ -229,7 +256,11 @@ format_error({special_form_conflict, {Receiver, Name, Arity}}) ->
|
||||
[elixir_aliases:inspect(Receiver), Name, Arity]);
|
||||
|
||||
format_error({no_macros, Module}) ->
|
||||
io_lib:format("could not load macros from module ~ts", [elixir_aliases:inspect(Module)]).
|
||||
io_lib:format("could not load macros from module ~ts", [elixir_aliases:inspect(Module)]);
|
||||
|
||||
format_error({import_conflict, Receiver, {Name, Arity}}) ->
|
||||
io_lib:format("imported ~ts.~ts/~B conflicts with local function",
|
||||
[elixir_aliases:inspect(Receiver), Name, Arity]).
|
||||
|
||||
%% LIST HELPERS
|
||||
|
||||
|
||||
@@ -1,146 +0,0 @@
|
||||
%% Module responsible for tracking invocations of module calls.
|
||||
-module(elixir_locals).
|
||||
-export([
|
||||
setup/1, stop/1, cache_env/1, get_cached_env/1,
|
||||
record_local/5, record_import/4, record_defaults/5,
|
||||
yank/2, reattach/6, ensure_no_import_conflict/3,
|
||||
warn_unused_local/4, ensure_no_undefined_local/3,
|
||||
format_error/1
|
||||
]).
|
||||
|
||||
-include("elixir.hrl").
|
||||
-define(cache_key, {elixir, cache_env}).
|
||||
-define(locals_key, {elixir, locals}).
|
||||
-define(locals, 'Elixir.Module.LocalsTracker').
|
||||
|
||||
setup({DataSet, _DataBag}) ->
|
||||
ets:insert(DataSet, {?cache_key, 0}),
|
||||
|
||||
case elixir_config:is_bootstrap() of
|
||||
false -> ets:insert(DataSet, {?locals_key, true});
|
||||
true -> ok
|
||||
end,
|
||||
|
||||
ok.
|
||||
|
||||
stop({DataSet, _DataBag}) ->
|
||||
ets:delete(DataSet, ?locals_key).
|
||||
|
||||
yank(Tuple, Module) ->
|
||||
if_tracker(Module, fun(Tracker) -> ?locals:yank(Tracker, Tuple) end).
|
||||
|
||||
reattach(Tuple, Kind, Module, Function, Neighbours, Meta) ->
|
||||
if_tracker(Module, fun(Tracker) -> ?locals:reattach(Tracker, Tuple, Kind, Function, Neighbours, Meta) end).
|
||||
|
||||
record_local(_Tuple, _Module, nil, _Meta, _IsMacroDispatch) ->
|
||||
ok;
|
||||
record_local(Tuple, Module, Function, Meta, IsMacroDispatch) ->
|
||||
if_tracker(Module, fun(Tracker) -> ?locals:add_local(Tracker, Function, Tuple, Meta, IsMacroDispatch), ok end).
|
||||
|
||||
record_import(_Tuple, Receiver, Module, Function)
|
||||
when Function == nil; Module == Receiver -> false;
|
||||
record_import(Tuple, Receiver, Module, Function) ->
|
||||
if_tracker(Module, fun(Tracker) -> ?locals:add_import(Tracker, Function, Receiver, Tuple), ok end).
|
||||
|
||||
record_defaults(_Tuple, _Kind, _Module, 0, _Meta) ->
|
||||
ok;
|
||||
record_defaults(Tuple, Kind, Module, Defaults, Meta) ->
|
||||
if_tracker(Module, fun(Tracker) -> ?locals:add_defaults(Tracker, Kind, Tuple, Defaults, Meta), ok end).
|
||||
|
||||
if_tracker(Module, Callback) ->
|
||||
if_tracker(Module, ok, Callback).
|
||||
|
||||
if_tracker(Module, Default, Callback) ->
|
||||
try
|
||||
{DataSet, _} = Tables = elixir_module:data_tables(Module),
|
||||
{ets:member(DataSet, ?locals_key), Tables}
|
||||
of
|
||||
{true, Tracker} -> Callback(Tracker);
|
||||
{false, _} -> Default
|
||||
catch
|
||||
error:badarg -> Default
|
||||
end.
|
||||
|
||||
%% CACHING
|
||||
|
||||
cache_env(#{line := Line, module := Module} = E) ->
|
||||
{Set, _} = elixir_module:data_tables(Module),
|
||||
Cache = elixir_env:reset_vars(E#{line := nil}),
|
||||
PrevKey = ets:lookup_element(Set, ?cache_key, 2),
|
||||
|
||||
Pos =
|
||||
case ets:lookup(Set, {cache_env, PrevKey}) of
|
||||
[{_, Cache}] ->
|
||||
PrevKey;
|
||||
_ ->
|
||||
NewKey = PrevKey + 1,
|
||||
ets:insert(Set, [{{cache_env, NewKey}, Cache}, {?cache_key, NewKey}]),
|
||||
NewKey
|
||||
end,
|
||||
|
||||
{Module, {Line, Pos}}.
|
||||
|
||||
get_cached_env({Module, {Line, Pos}}) ->
|
||||
{Set, _} = elixir_module:data_tables(Module),
|
||||
(ets:lookup_element(Set, {cache_env, Pos}, 2))#{line := Line};
|
||||
get_cached_env(Env) ->
|
||||
Env.
|
||||
|
||||
%% ERROR HANDLING
|
||||
|
||||
ensure_no_import_conflict('Elixir.Kernel', _All, _E) ->
|
||||
ok;
|
||||
ensure_no_import_conflict(Module, All, E) ->
|
||||
if_tracker(Module, ok, fun(Tracker) ->
|
||||
[elixir_errors:module_error(Meta, E, ?MODULE, {function_conflict, Error})
|
||||
|| {Meta, Error} <- ?locals:collect_imports_conflicts(Tracker, All)],
|
||||
ok
|
||||
end).
|
||||
|
||||
ensure_no_undefined_local(Module, All, E) ->
|
||||
if_tracker(Module, [], fun(Tracker) ->
|
||||
[elixir_errors:module_error(Meta, E#{function := Function, file := File}, ?MODULE, {Error, Tuple, Module})
|
||||
|| {Function, Meta, File, Tuple, Error} <- ?locals:collect_undefined_locals(Tracker, All, ?key(E, file))],
|
||||
ok
|
||||
end).
|
||||
|
||||
warn_unused_local(Module, All, Private, E) ->
|
||||
if_tracker(Module, [], fun(Tracker) ->
|
||||
{Unreachable, Warnings} = ?locals:collect_unused_locals(Tracker, All, Private),
|
||||
[elixir_errors:file_warn(Meta, E, ?MODULE, Error) || {Meta, Error} <- Warnings],
|
||||
Unreachable
|
||||
end).
|
||||
|
||||
format_error({function_conflict, {Receiver, {Name, Arity}}}) ->
|
||||
io_lib:format("imported ~ts.~ts/~B conflicts with local function",
|
||||
[elixir_aliases:inspect(Receiver), Name, Arity]);
|
||||
|
||||
format_error({unused_args, {Name, Arity}}) ->
|
||||
io_lib:format("default values for the optional arguments in ~ts/~B are never used", [Name, Arity]);
|
||||
|
||||
format_error({unused_args, {Name, Arity}, Count}) when Arity - Count == 1 ->
|
||||
io_lib:format("the default value for the last optional argument in ~ts/~B is never used", [Name, Arity]);
|
||||
|
||||
format_error({unused_args, {Name, Arity}, Count}) ->
|
||||
io_lib:format("the default values for the last ~B optional arguments in ~ts/~B are never used", [Arity - Count, Name, Arity]);
|
||||
|
||||
format_error({unused_def, {Name, Arity}, defp}) ->
|
||||
io_lib:format("function ~ts/~B is unused", [Name, Arity]);
|
||||
|
||||
format_error({unused_def, {Name, Arity}, defmacrop}) ->
|
||||
io_lib:format("macro ~ts/~B is unused", [Name, Arity]);
|
||||
|
||||
format_error({undefined_function, {F, A}, _})
|
||||
when F == '__info__', A == 1;
|
||||
F == 'behaviour_info', A == 1;
|
||||
F == 'module_info', A == 1;
|
||||
F == 'module_info', A == 0 ->
|
||||
io_lib:format("undefined function ~ts/~B (this function is auto-generated by the compiler "
|
||||
"and must always be called as a remote, as in __MODULE__.~ts/~B)", [F, A, F, A]);
|
||||
|
||||
format_error({undefined_function, {F, A}, Module}) ->
|
||||
io_lib:format("undefined function ~ts/~B (expected ~ts to define such a function or "
|
||||
"for it to be imported, but none are available)", [F, A, elixir_aliases:inspect(Module)]);
|
||||
|
||||
format_error({incorrect_dispatch, {F, A}, _Module}) ->
|
||||
io_lib:format("cannot invoke macro ~ts/~B before its definition", [F, A]).
|
||||
@@ -1,9 +1,10 @@
|
||||
-module(elixir_module).
|
||||
-export([file/1, data_tables/1, is_open/1, mode/1, delete_definition_attributes/6,
|
||||
compile/6, expand_callback/6, format_error/1, compiler_modules/0,
|
||||
write_cache/3, read_cache/2, next_counter/1, taint/1]).
|
||||
write_cache/3, read_cache/2, next_counter/1, taint/1, cache_env/1, get_cached_env/1]).
|
||||
-include("elixir.hrl").
|
||||
-define(counter_attr, {elixir, counter}).
|
||||
-define(cache_key, {elixir, cache_env}).
|
||||
|
||||
%% Stores modules currently being defined by the compiler
|
||||
|
||||
@@ -71,6 +72,29 @@ taint(Module) ->
|
||||
_:_ -> false
|
||||
end.
|
||||
|
||||
cache_env(#{line := Line, module := Module} = E) ->
|
||||
{Set, _} = data_tables(Module),
|
||||
Cache = elixir_env:reset_vars(E#{line := nil}),
|
||||
PrevKey = ets:lookup_element(Set, ?cache_key, 2),
|
||||
|
||||
Pos =
|
||||
case ets:lookup(Set, {cache_env, PrevKey}) of
|
||||
[{_, Cache}] ->
|
||||
PrevKey;
|
||||
_ ->
|
||||
NewKey = PrevKey + 1,
|
||||
ets:insert(Set, [{{cache_env, NewKey}, Cache}, {?cache_key, NewKey}]),
|
||||
NewKey
|
||||
end,
|
||||
|
||||
{Module, {Line, Pos}}.
|
||||
|
||||
get_cached_env({Module, {Line, Pos}}) ->
|
||||
{Set, _} = data_tables(Module),
|
||||
(ets:lookup_element(Set, {cache_env, Pos}, 2))#{line := Line};
|
||||
get_cached_env(Env) ->
|
||||
Env.
|
||||
|
||||
%% Compilation hook
|
||||
|
||||
compile(Meta, Module, Block, Vars, Prune, Env) ->
|
||||
@@ -146,37 +170,30 @@ compile(Meta, Module, ModuleAsCharlist, Block, Vars, Prune, E) ->
|
||||
|
||||
NifsAttribute = lists:keyfind(nifs, 1, Attributes),
|
||||
validate_nifs_attribute(NifsAttribute, AllDefinitions, Line, E),
|
||||
|
||||
Unreachable = elixir_locals:warn_unused_local(Module, AllDefinitions, NewPrivate, E),
|
||||
elixir_locals:ensure_no_undefined_local(Module, AllDefinitions, E),
|
||||
elixir_locals:ensure_no_import_conflict(Module, AllDefinitions, E),
|
||||
|
||||
%% We stop tracking locals here to avoid race conditions in case after_load
|
||||
%% evaluates code in a separate process that may write to locals table.
|
||||
elixir_locals:stop({DataSet, DataBag}),
|
||||
elixir_import:ensure_no_local_conflict(Module, AllDefinitions, E),
|
||||
make_readonly(Module),
|
||||
|
||||
(not elixir_config:is_bootstrap()) andalso
|
||||
'Elixir.Module':'__check_attributes__'(E, DataSet, DataBag),
|
||||
|
||||
RawCompileOpts = bag_lookup_element(DataBag, {accumulate, compile}, 2),
|
||||
CompileOpts = validate_compile_opts(RawCompileOpts, AllDefinitions, Unreachable, Line, E),
|
||||
Impls = bag_lookup_element(DataBag, impls, 2),
|
||||
|
||||
AfterVerify = bag_lookup_element(DataBag, {accumulate, after_verify}, 2),
|
||||
[elixir_env:trace({remote_function, [], VerifyMod, VerifyFun, 1}, CallbackE) ||
|
||||
{VerifyMod, VerifyFun} <- AfterVerify],
|
||||
|
||||
%% Compute signatures only if the module is valid.
|
||||
case ets:member(DataSet, {elixir, taint}) of
|
||||
true -> elixir_errors:compile_error(E);
|
||||
false -> ok
|
||||
end,
|
||||
%% Ensure there are no errors before we infer types
|
||||
compile_error_if_tainted(DataSet, E),
|
||||
|
||||
Signatures = case elixir_config:get(infer_signatures) of
|
||||
true -> 'Elixir.Module.Types':infer(Module, File, AllDefinitions, CallbackE);
|
||||
false -> #{}
|
||||
end,
|
||||
{Signatures, Unreachable} =
|
||||
case elixir_config:is_bootstrap() of
|
||||
true -> {#{}, []};
|
||||
false ->
|
||||
Defmacrop = bag_lookup_element(DataBag, defmacrop_calls, 2),
|
||||
'Elixir.Module.Types':infer(Module, File, AllDefinitions, NewPrivate, Defmacrop, E)
|
||||
end,
|
||||
|
||||
RawCompileOpts = bag_lookup_element(DataBag, {accumulate, compile}, 2),
|
||||
CompileOpts = validate_compile_opts(RawCompileOpts, AllDefinitions, Unreachable, Line, E),
|
||||
Impls = bag_lookup_element(DataBag, impls, 2),
|
||||
|
||||
ModuleMap = #{
|
||||
struct => get_struct(DataSet),
|
||||
@@ -186,15 +203,16 @@ compile(Meta, Module, ModuleAsCharlist, Block, Vars, Prune, E) ->
|
||||
relative_file => elixir_utils:relative_to_cwd(File),
|
||||
attributes => Attributes,
|
||||
definitions => AllDefinitions,
|
||||
unreachable => Unreachable,
|
||||
after_verify => AfterVerify,
|
||||
compile_opts => CompileOpts,
|
||||
deprecated => get_deprecated(DataBag),
|
||||
defines_behaviour => defines_behaviour(DataBag),
|
||||
impls => Impls,
|
||||
unreachable => Unreachable,
|
||||
signatures => Signatures
|
||||
},
|
||||
|
||||
compile_error_if_tainted(DataSet, E),
|
||||
Binary = elixir_erl:compile(ModuleMap),
|
||||
Autoload = proplists:get_value(autoload, CompileOpts, true),
|
||||
spawn_parallel_checker(CheckerInfo, Module, ModuleMap),
|
||||
@@ -228,6 +246,12 @@ compile(Meta, Module, ModuleAsCharlist, Block, Vars, Prune, E) ->
|
||||
elixir_code_server:call({undefmodule, Ref})
|
||||
end.
|
||||
|
||||
compile_error_if_tainted(DataSet, E) ->
|
||||
case ets:member(DataSet, {elixir, taint}) of
|
||||
true -> elixir_errors:compile_error(E);
|
||||
false -> ok
|
||||
end.
|
||||
|
||||
validate_compile_opts(Opts, Defs, Unreachable, Line, E) ->
|
||||
lists:flatmap(fun (Opt) -> validate_compile_opt(Opt, Defs, Unreachable, Line, E) end, Opts).
|
||||
|
||||
@@ -351,7 +375,6 @@ build(Module, Line, File, E) ->
|
||||
%% * {{type, Tuple}, ...}, {{opaque, Tuple}, ...}
|
||||
%% * {{callback, Tuple}, ...}, {{macrocallback, Tuple}, ...}
|
||||
%% * {{def, Tuple}, ...} (from elixir_def)
|
||||
%% * {{import, Tuple}, ...} (from elixir_locals)
|
||||
%% * {{overridable, Tuple}, ...} (from elixir_overridable)
|
||||
%%
|
||||
DataSet = ets:new(Module, [set, public]),
|
||||
@@ -367,8 +390,6 @@ build(Module, Line, File, E) ->
|
||||
%% * {overridables, ...} (from elixir_overridable)
|
||||
%% * {{default, Name}, ...} (from elixir_def)
|
||||
%% * {{clauses, Tuple}, ...} (from elixir_def)
|
||||
%% * {reattach, ...} (from elixir_locals)
|
||||
%% * {{local, Tuple}, ...} (from elixir_locals)
|
||||
%%
|
||||
DataBag = ets:new(Module, [duplicate_bag, public]),
|
||||
|
||||
@@ -395,6 +416,7 @@ build(Module, Line, File, E) ->
|
||||
{optional_callbacks, [], accumulate, []},
|
||||
|
||||
% Others
|
||||
{?cache_key, 0},
|
||||
{?counter_attr, 0}
|
||||
]),
|
||||
|
||||
@@ -411,7 +433,6 @@ build(Module, Line, File, E) ->
|
||||
%% Setup definition related modules
|
||||
Tables = {DataSet, DataBag},
|
||||
elixir_def:setup(Tables),
|
||||
elixir_locals:setup(Tables),
|
||||
Tuple = {Module, Tables, Line, File, all},
|
||||
|
||||
Ref =
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
% Holds the logic responsible for defining overridable functions and handling super.
|
||||
-module(elixir_overridable).
|
||||
-export([overridables_for/1, overridable_for/2,
|
||||
record_overridable/4, super/4,
|
||||
record_overridable/3, super/4,
|
||||
store_not_overridden/1, format_error/1]).
|
||||
-include("elixir.hrl").
|
||||
-define(overridden_pos, 5).
|
||||
-define(overridden_pos, 4).
|
||||
|
||||
overridables_for(Module) ->
|
||||
{_, Bag} = elixir_module:data_tables(Module),
|
||||
@@ -22,20 +22,20 @@ overridable_for(Module, Tuple) ->
|
||||
[] -> not_overridable
|
||||
end.
|
||||
|
||||
record_overridable(Module, Tuple, Def, Neighbours) ->
|
||||
record_overridable(Module, Tuple, Def) ->
|
||||
{Set, Bag} = elixir_module:data_tables(Module),
|
||||
|
||||
case ets:insert_new(Set, {{overridable, Tuple}, 1, Def, Neighbours, false}) of
|
||||
case ets:insert_new(Set, {{overridable, Tuple}, 1, Def, false}) of
|
||||
true ->
|
||||
ets:insert(Bag, {overridables, Tuple});
|
||||
false ->
|
||||
[{_, Count, PreviousDef, _, _}] = ets:lookup(Set, {overridable, Tuple}),
|
||||
[{_, Count, PreviousDef, _}] = ets:lookup(Set, {overridable, Tuple}),
|
||||
{{_, Kind, Meta, File, _, _}, _} = Def,
|
||||
{{_, PreviousKind, _, _, _, _}, _} = PreviousDef,
|
||||
|
||||
case is_valid_kind(Kind, PreviousKind) of
|
||||
true ->
|
||||
ets:insert(Set, {{overridable, Tuple}, Count + 1, Def, Neighbours, false});
|
||||
ets:insert(Set, {{overridable, Tuple}, Count + 1, Def, false});
|
||||
false ->
|
||||
elixir_errors:file_error(Meta, File, ?MODULE, {bad_kind, Module, Tuple, Kind})
|
||||
end
|
||||
@@ -74,7 +74,7 @@ store_not_overridden(Module) ->
|
||||
|
||||
%% Private
|
||||
|
||||
store(Set, Module, Tuple, {_, Count, Def, Neighbours, Overridden}, Hidden) ->
|
||||
store(Set, Module, Tuple, {_, Count, Def, Overridden}, Hidden) ->
|
||||
{{{def, {Name, Arity}}, Kind, Meta, File, _Check,
|
||||
{Defaults, _HasBody, _LastDefaults}}, Clauses} = Def,
|
||||
|
||||
@@ -92,8 +92,7 @@ store(Set, Module, Tuple, {_, Count, Def, Neighbours, Overridden}, Hidden) ->
|
||||
false ->
|
||||
ets:update_element(Set, {overridable, Tuple}, {?overridden_pos, true}),
|
||||
elixir_def:store_definition(none, FinalKind, Meta, FinalName, FinalArity,
|
||||
File, Module, Defaults, FinalClauses),
|
||||
elixir_locals:reattach({FinalName, FinalArity}, FinalKind, Module, Tuple, Neighbours, Meta);
|
||||
File, Module, Defaults, FinalClauses);
|
||||
true ->
|
||||
ok
|
||||
end,
|
||||
|
||||
Reference in New Issue
Block a user