Skip to content

Commit e891b1a

Browse files
wojtekmachjosevalim
authored andcommitted
Don't add compile-time dependency on defdelegate (#10093)
The dependency doesn't seem needed in this case. We go through extra hoops to maintain dynamic invocations where `opts` is not a compile-time list and :to is not a literal. An easy way to see this optimization is: defmodule A do defdelegate f(), to: B end defmodule B do def f() do :ok end end $ touch lib/b.ex && mix compile --verbose Compiling 1 file (.ex) Compiled lib/b.ex vs: defmodule A do opts = [to: B] defdelegate f(), opts end defmodule B do def f() do :ok end end $ touch lib/b.ex && mix compile --verbose Compiling 2 files (.ex) Compiled lib/b.ex Compiled lib/a.ex
1 parent 001a5eb commit e891b1a

File tree

2 files changed

+29
-0
lines changed

2 files changed

+29
-0
lines changed

lib/elixir/lib/kernel.ex

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4881,6 +4881,18 @@ defmodule Kernel do
48814881
defmacro defdelegate(funs, opts) do
48824882
funs = Macro.escape(funs, unquote: true)
48834883

4884+
# don't add compile-time dependency on :to
4885+
opts =
4886+
with true <- is_list(opts),
4887+
{:ok, target} <- Keyword.fetch(opts, :to),
4888+
{:__aliases__, _, _} <- target do
4889+
target = Macro.expand(target, %{__CALLER__ | function: {:__info__, 1}})
4890+
Keyword.replace!(opts, :to, target)
4891+
else
4892+
_ ->
4893+
opts
4894+
end
4895+
48844896
quote bind_quoted: [funs: funs, opts: opts] do
48854897
target =
48864898
Keyword.get(opts, :to) || raise ArgumentError, "expected to: to be given as argument"

lib/elixir/test/elixir/kernel/lexical_tracker_test.exs

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -126,4 +126,21 @@ defmodule Kernel.LexicalTrackerTest do
126126
refute Foo.Bar in runtime
127127
refute Foo.Bar in compile
128128
end
129+
130+
test "defdelegate with literal does not add compile dependency" do
131+
{{compile, _structs, _runtime, _}, _binding} =
132+
Code.eval_string("""
133+
defmodule Kernel.LexicalTrackerTest.Defdelegate do
134+
defdelegate a, to: A
135+
136+
opts = [to: B]
137+
defdelegate b, opts
138+
139+
Kernel.LexicalTracker.references(__ENV__.lexical_tracker)
140+
end |> elem(3)
141+
""")
142+
143+
refute A in compile
144+
assert B in compile
145+
end
129146
end

0 commit comments

Comments
 (0)