Skip to content

Commit 43e2b9d

Browse files
committed
Move locals to types
1 parent 3601ad7 commit 43e2b9d

File tree

12 files changed

+182
-150
lines changed

12 files changed

+182
-150
lines changed

lib/elixir/lib/module.ex

Lines changed: 2 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1415,15 +1415,7 @@ defmodule Module do
14151415
def delete_definition(module, {name, arity})
14161416
when is_atom(module) and is_atom(name) and is_integer(arity) do
14171417
assert_not_readonly!(__ENV__.function, module)
1418-
1419-
case :elixir_def.take_definition(module, {name, arity}) do
1420-
false ->
1421-
false
1422-
1423-
_ ->
1424-
:elixir_locals.yank({name, arity}, module)
1425-
true
1426-
end
1418+
:elixir_def.take_definition(module, {name, arity}) != false
14271419
end
14281420

14291421
@doc """
@@ -1452,8 +1444,7 @@ defmodule Module do
14521444
"overridable because it was not defined"
14531445

14541446
clause ->
1455-
neighbours = :elixir_locals.yank(tuple, module)
1456-
:elixir_overridable.record_overridable(module, tuple, clause, neighbours)
1447+
:elixir_overridable.record_overridable(module, tuple, clause)
14571448
end
14581449

14591450
other ->

lib/elixir/lib/module/types.ex

Lines changed: 95 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -9,17 +9,34 @@ defmodule Module.Types do
99

1010
@doc false
1111
def infer(module, file, defs, private, used, env) do
12-
finder = &List.keyfind(defs, &1, 0)
13-
handler = &local_handler(&1, &2, &3, finder)
12+
finder = &:lists.keyfind(&1, 1, defs)
13+
14+
handler = fn meta, fun_arity, stack, context ->
15+
case local_handler(meta, fun_arity, stack, context, finder) do
16+
false ->
17+
undefined_function!(:undefined_function, meta, fun_arity, stack, env)
18+
false
19+
20+
{kind, _, _} = triplet ->
21+
if (kind == :defmacro or kind == :defmacrop) and not Keyword.has_key?(meta, :super) do
22+
undefined_function!(:incorrect_dispatch, meta, fun_arity, stack, env)
23+
false
24+
else
25+
triplet
26+
end
27+
end
28+
end
29+
1430
stack = stack(:infer, file, module, {:__info__, 1}, :all, env, handler)
1531
context = context(%{})
1632

1733
{types, %{local_sigs: local_sigs}} =
18-
for {fun_arity, kind, _meta, _clauses} = def <- defs,
34+
for {fun_arity, kind, meta, _clauses} = def <- defs,
1935
kind == :def or kind == :defmacro,
2036
reduce: {[], context} do
2137
{types, context} ->
22-
{_kind, inferred, context} = local_handler(fun_arity, stack, context, fn _ -> def end)
38+
{_kind, inferred, context} =
39+
local_handler(meta, fun_arity, stack, context, fn _ -> def end)
2340

2441
if kind == :def and fun_arity not in @no_infer do
2542
{[{fun_arity, inferred} | types], context}
@@ -37,6 +54,12 @@ defmodule Module.Types do
3754
{Map.new(types), unreachable}
3855
end
3956

57+
defp undefined_function!(reason, meta, {fun, arity}, stack, env) do
58+
env = %{env | function: stack.function, file: stack.file}
59+
tuple = {reason, {fun, arity}, stack.module}
60+
:elixir_errors.module_error(Helpers.with_span(meta, fun), env, __MODULE__, tuple)
61+
end
62+
4063
defp warn_unused_def({_fun_arity, _kind, false, _}, _reachable, _used, _env) do
4164
:ok
4265
end
@@ -80,14 +103,16 @@ defmodule Module.Types do
80103

81104
@doc false
82105
def warnings(module, file, defs, no_warn_undefined, cache) do
83-
finder = &List.keyfind(defs, &1, 0)
84-
handler = &local_handler(&1, &2, &3, finder)
106+
finder = &:lists.keyfind(&1, 1, defs)
107+
handler = &local_handler(&1, &2, &3, &4, finder)
85108
stack = stack(:dynamic, file, module, {:__info__, 1}, no_warn_undefined, cache, handler)
86109
context = context(%{})
87110

88111
context =
89-
Enum.reduce(defs, context, fn {fun_arity, _kind, _meta, _clauses} = def, context ->
90-
{_kind, _inferred, context} = local_handler(fun_arity, stack, context, fn _ -> def end)
112+
Enum.reduce(defs, context, fn {fun_arity, _kind, meta, _clauses} = def, context ->
113+
{_kind, _inferred, context} =
114+
local_handler(meta, fun_arity, stack, context, fn _ -> def end)
115+
91116
context
92117
end)
93118

@@ -112,7 +137,7 @@ defmodule Module.Types do
112137
end
113138
end
114139

115-
defp local_handler(fun_arity, stack, context, finder) do
140+
defp local_handler(_meta, fun_arity, stack, context, finder) do
116141
case context.local_sigs do
117142
%{^fun_arity => {kind, inferred, _mapping}} ->
118143
{kind, inferred, context}
@@ -121,47 +146,58 @@ defmodule Module.Types do
121146
{kind, :none, context}
122147

123148
local_sigs ->
124-
{{fun, arity}, kind, meta, clauses} =
125-
finder.(fun_arity) || raise "could not find #{inspect(fun_arity)}"
126-
127-
expected = List.duplicate(Descr.dynamic(), arity)
128-
stack = stack |> fresh_stack(fun_arity) |> with_file_meta(meta)
129-
context = put_in(context.local_sigs, Map.put(local_sigs, fun_arity, kind))
130-
131-
{_, _, mapping, clauses_types, clauses_context} =
132-
Enum.reduce(clauses, {0, 0, [], [], context}, fn
133-
{meta, args, guards, body}, {index, total, mapping, inferred, context} ->
134-
context = fresh_context(context)
135-
136-
try do
137-
{args_types, context} =
138-
Pattern.of_head(args, guards, expected, :default, meta, stack, context)
139-
140-
{return_type, context} =
141-
Expr.of_expr(body, stack, context)
142-
143-
{type_index, inferred} =
144-
add_inferred(inferred, args_types, return_type, total - 1, [])
145-
146-
if type_index == -1 do
147-
{index + 1, total + 1, [{index, total} | mapping], inferred, context}
148-
else
149-
{index + 1, total, [{index, type_index} | mapping], inferred, context}
150-
end
151-
rescue
152-
e ->
153-
internal_error!(e, __STACKTRACE__, kind, meta, fun, args, guards, body, stack)
154-
end
155-
end)
156-
157-
inferred = {:infer, Enum.reverse(clauses_types)}
158-
triplet = {kind, inferred, mapping}
159-
context = restore_context(context, clauses_context)
160-
context = update_in(context.local_sigs, &Map.put(&1, fun_arity, triplet))
161-
{kind, inferred, context}
149+
case finder.(fun_arity) do
150+
{fun_arity, kind, meta, clauses} ->
151+
context = put_in(context.local_sigs, Map.put(local_sigs, fun_arity, kind))
152+
153+
{inferred, mapping, context} =
154+
local_handler(fun_arity, kind, meta, clauses, stack, context)
155+
156+
context =
157+
update_in(context.local_sigs, &Map.put(&1, fun_arity, {kind, inferred, mapping}))
158+
159+
{kind, inferred, context}
160+
161+
false ->
162+
false
163+
end
162164
end
163165
end
164166

167+
defp local_handler({fun, arity} = fun_arity, kind, meta, clauses, stack, context) do
168+
expected = List.duplicate(Descr.dynamic(), arity)
169+
stack = stack |> fresh_stack(fun_arity) |> with_file_meta(meta)
170+
171+
{_, _, mapping, clauses_types, clauses_context} =
172+
Enum.reduce(clauses, {0, 0, [], [], context}, fn
173+
{meta, args, guards, body}, {index, total, mapping, inferred, context} ->
174+
context = fresh_context(context)
175+
176+
try do
177+
{args_types, context} =
178+
Pattern.of_head(args, guards, expected, :default, meta, stack, context)
179+
180+
{return_type, context} =
181+
Expr.of_expr(body, stack, context)
182+
183+
{type_index, inferred} =
184+
add_inferred(inferred, args_types, return_type, total - 1, [])
185+
186+
if type_index == -1 do
187+
{index + 1, total + 1, [{index, total} | mapping], inferred, context}
188+
else
189+
{index + 1, total, [{index, type_index} | mapping], inferred, context}
190+
end
191+
rescue
192+
e ->
193+
internal_error!(e, __STACKTRACE__, kind, meta, fun, args, guards, body, stack)
194+
end
195+
end)
196+
197+
inferred = {:infer, Enum.reverse(clauses_types)}
198+
{inferred, mapping, restore_context(context, clauses_context)}
199+
end
200+
165201
# We check for term equality of types as an optimization
166202
# to reduce the amount of check we do at runtime.
167203
defp add_inferred([{args, existing_return} | tail], args, return, index, acc),
@@ -306,4 +342,16 @@ defmodule Module.Types do
306342

307343
def format_error({:unused_def, {name, arity}, :defmacrop}),
308344
do: "macro #{name}/#{arity} is unused"
345+
346+
def format_error({:undefined_function, {f, a}, _})
347+
when {f, a} in [__info__: 1, behaviour_info: 1, module_info: 1, module_info: 0],
348+
do:
349+
"undefined function #{f}/#{a} (this function is auto-generated by the compiler and must always be called as a remote, as in __MODULE__.#{f}/#{a})"
350+
351+
def format_error({:undefined_function, {f, a}, module}),
352+
do:
353+
"undefined function #{f}/#{a} (expected #{inspect(module)} to define such a function or for it to be imported, but none are available)"
354+
355+
def format_error({:incorrect_dispatch, {f, a}, _module}),
356+
do: "cannot invoke macro #{f}/#{a} before its definition"
309357
end

lib/elixir/lib/module/types/apply.ex

Lines changed: 35 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -508,30 +508,35 @@ defmodule Module.Types.Apply do
508508
@doc """
509509
Deal with local functions.
510510
"""
511-
def local(fun, args_types, expr, stack, context) do
511+
def local(fun, args_types, {_, meta, _} = expr, stack, context) do
512512
fun_arity = {fun, length(args_types)}
513-
{kind, info, context} = stack.local_handler.(fun_arity, stack, context)
514513

515-
case apply_signature(info, args_types, stack) do
516-
{:ok, indexes, type} ->
517-
context =
518-
if stack != :infer and kind == :defp do
519-
update_in(context.local_used[fun_arity], fn current ->
520-
if info == :none do
521-
[]
514+
case stack.local_handler.(meta, fun_arity, stack, context) do
515+
false ->
516+
{dynamic(), context}
517+
518+
{kind, info, context} ->
519+
case apply_signature(info, args_types, stack) do
520+
{:ok, indexes, type} ->
521+
context =
522+
if stack != :infer and kind == :defp do
523+
update_in(context.local_used[fun_arity], fn current ->
524+
if info == :none do
525+
[]
526+
else
527+
(current || used_from_clauses(info)) -- indexes
528+
end
529+
end)
522530
else
523-
(current || used_from_clauses(info)) -- indexes
531+
context
524532
end
525-
end)
526-
else
527-
context
528-
end
529533

530-
{type, context}
534+
{type, context}
531535

532-
{:error, domain, clauses} ->
533-
error = {:badlocal, expr, args_types, domain, clauses, context}
534-
{error_type(), error(error, elem(expr, 1), stack, context)}
536+
{:error, domain, clauses} ->
537+
error = {:badlocal, expr, args_types, domain, clauses, context}
538+
{error_type(), error(error, with_span(meta, fun), stack, context)}
539+
end
535540
end
536541
end
537542

@@ -544,15 +549,20 @@ defmodule Module.Types.Apply do
544549
@doc """
545550
Deal with local captures.
546551
"""
547-
def local_capture(fun, arity, _meta, stack, context) do
552+
def local_capture(fun, arity, meta, stack, context) do
548553
fun_arity = {fun, arity}
549-
{kind, _info, context} = stack.local_handler.(fun_arity, stack, context)
550554

551-
if stack != :infer and kind == :defp do
552-
# Mark all clauses as used, as the function is being exported.
553-
{fun(), put_in(context.local_used[fun_arity], [])}
554-
else
555-
{fun(), context}
555+
case stack.local_handler.(meta, fun_arity, stack, context) do
556+
false ->
557+
{fun(), context}
558+
559+
{kind, _info, context} ->
560+
if stack != :infer and kind == :defp do
561+
# Mark all clauses as used, as the function is being exported.
562+
{fun(), put_in(context.local_used[fun_arity], [])}
563+
else
564+
{fun(), context}
565+
end
556566
end
557567
end
558568

lib/elixir/lib/module/types/helpers.ex

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,13 @@ defmodule Module.Types.Helpers do
3838
def get_meta({_, meta, _}), do: meta
3939
def get_meta(_other), do: []
4040

41+
@doc """
42+
Attaches span information.
43+
"""
44+
def with_span(meta, name) do
45+
:elixir_env.calculate_span(meta, name)
46+
end
47+
4148
## Warnings
4249

4350
@doc """

lib/elixir/src/elixir_def.erl

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -217,7 +217,6 @@ store_definition(Meta, Kind, CheckClauses, Name, Arity, DefaultsArgs, Guards, Bo
217217
Clause <- def_to_clauses(Kind, Meta, Args, Guards, Body, E)],
218218

219219
DefaultsLength = length(Defaults),
220-
elixir_locals:record_defaults(Tuple, Kind, Module, DefaultsLength, Meta),
221220
check_previous_defaults(Meta, Module, Name, Arity, Kind, DefaultsLength, E),
222221

223222
store_definition(CheckClauses, Kind, Meta, Name, Arity, File,

lib/elixir/src/elixir_dispatch.erl

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -74,7 +74,6 @@ import_function(Meta, Name, Arity, E) ->
7474
elixir_def:local_for(Meta, Name, Arity, [defmacro, defmacrop], E) of
7575
false ->
7676
elixir_env:trace({local_function, Meta, Name, Arity}, E),
77-
elixir_locals:record_local(Tuple, ?key(E, module), ?key(E, function), Meta, false),
7877
{local, Name, Arity};
7978
_ ->
8079
false
@@ -183,10 +182,7 @@ expand_import(Meta, Name, Arity, E, Extra, AllowLocals, Trace) ->
183182

184183
%% Dispatch to the local.
185184
{_, {_Kind, Fun}} ->
186-
Trace andalso begin
187-
elixir_env:trace({local_macro, Meta, Name, Arity}, E),
188-
elixir_locals:record_local(Tuple, Module, ?key(E, function), Meta, true)
189-
end,
185+
Trace andalso elixir_env:trace({local_macro, Meta, Name, Arity}, E),
190186
{macro, Module, expander_macro_fun(Meta, Fun, Module, Name, E)}
191187
end
192188
end.

lib/elixir/src/elixir_expand.erl

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -900,8 +900,7 @@ expand_local(Meta, Name, Args, S, #{module := Module, function := Function, cont
900900

901901
nil ->
902902
Arity = length(Args),
903-
elixir_env:trace({local_function, Meta, Name, Arity}, E),
904-
elixir_locals:record_local({Name, Arity}, Module, Function, Meta, false)
903+
elixir_env:trace({local_function, Meta, Name, Arity}, E)
905904
end,
906905

907906
{EArgs, SA, EA} = expand_args(Args, S, E),

0 commit comments

Comments
 (0)