Skip to content

Commit bd49ad6

Browse files
committed
Generate less code in defstruct
1 parent dadb49f commit bd49ad6

File tree

3 files changed

+59
-57
lines changed

3 files changed

+59
-57
lines changed

lib/elixir/lib/kernel.ex

Lines changed: 4 additions & 54 deletions
Original file line numberDiff line numberDiff line change
@@ -5177,56 +5177,8 @@ defmodule Kernel do
51775177
use `@type`.
51785178
"""
51795179
defmacro defstruct(fields) do
5180-
builder =
5181-
case bootstrapped?(Enum) do
5182-
true ->
5183-
quote do
5184-
case @enforce_keys do
5185-
[] ->
5186-
def __struct__(kv) do
5187-
Enum.reduce(kv, @__struct__, fn {key, val}, map ->
5188-
Map.replace!(map, key, val)
5189-
end)
5190-
end
5191-
5192-
_ ->
5193-
def __struct__(kv) do
5194-
{map, keys} =
5195-
Enum.reduce(kv, {@__struct__, @enforce_keys}, fn {key, val}, {map, keys} ->
5196-
{Map.replace!(map, key, val), List.delete(keys, key)}
5197-
end)
5198-
5199-
case keys do
5200-
[] ->
5201-
map
5202-
5203-
_ ->
5204-
raise ArgumentError,
5205-
"the following keys must also be given when building " <>
5206-
"struct #{inspect(__MODULE__)}: #{inspect(keys)}"
5207-
end
5208-
end
5209-
end
5210-
end
5211-
5212-
false ->
5213-
quote do
5214-
_ = @enforce_keys
5215-
5216-
def __struct__(kv) do
5217-
:lists.foldl(fn {key, val}, acc -> Map.replace!(acc, key, val) end, @__struct__, kv)
5218-
end
5219-
end
5220-
end
5221-
5222-
quote do
5223-
if Module.has_attribute?(__MODULE__, :__struct__) do
5224-
raise ArgumentError,
5225-
"defstruct has already been called for " <>
5226-
"#{Kernel.inspect(__MODULE__)}, defstruct can only be called once per module"
5227-
end
5228-
5229-
{struct, keys, derive} = Kernel.Utils.defstruct(__MODULE__, unquote(fields))
5180+
quote bind_quoted: [fields: fields, bootstrapped?: bootstrapped?(Enum)] do
5181+
{struct, keys, derive, body} = Kernel.Utils.defstruct(__MODULE__, fields, bootstrapped?)
52305182
@__struct__ struct
52315183
@enforce_keys keys
52325184

@@ -5235,11 +5187,9 @@ defmodule Kernel do
52355187
_ -> Protocol.__derive__(derive, __MODULE__, __ENV__)
52365188
end
52375189

5238-
def __struct__() do
5239-
@__struct__
5240-
end
5190+
def __struct__(), do: @__struct__
5191+
def __struct__(var!(kv)), do: unquote(body)
52415192

5242-
unquote(builder)
52435193
Kernel.Utils.announce_struct(__MODULE__)
52445194
struct
52455195
end

lib/elixir/lib/kernel/utils.ex

Lines changed: 51 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -100,7 +100,13 @@ defmodule Kernel.Utils do
100100
@doc """
101101
Callback for defstruct.
102102
"""
103-
def defstruct(module, fields) do
103+
def defstruct(module, fields, bootstrapped?) do
104+
if Module.has_attribute?(module, :__struct__) do
105+
raise ArgumentError,
106+
"defstruct has already been called for " <>
107+
"#{Kernel.inspect(module)}, defstruct can only be called once per module"
108+
end
109+
104110
case fields do
105111
fs when is_list(fs) ->
106112
:ok
@@ -142,12 +148,54 @@ defmodule Kernel.Utils do
142148
end
143149

144150
:lists.foreach(foreach, enforce_keys)
145-
146151
struct = :maps.put(:__struct__, module, :maps.from_list(fields))
147152

153+
body =
154+
case bootstrapped? do
155+
true ->
156+
case enforce_keys do
157+
[] ->
158+
quote do
159+
Enum.reduce(var!(kv), @__struct__, fn {key, val}, map ->
160+
Map.replace!(map, key, val)
161+
end)
162+
end
163+
164+
_ ->
165+
quote do
166+
{map, keys} =
167+
Enum.reduce(var!(kv), {@__struct__, @enforce_keys}, fn {key, val},
168+
{map, keys} ->
169+
{Map.replace!(map, key, val), List.delete(keys, key)}
170+
end)
171+
172+
case keys do
173+
[] ->
174+
map
175+
176+
_ ->
177+
raise ArgumentError,
178+
"the following keys must also be given when building " <>
179+
"struct #{inspect(__MODULE__)}: #{inspect(keys)}"
180+
end
181+
end
182+
end
183+
184+
false ->
185+
quote do
186+
_ = @enforce_keys
187+
188+
:lists.foldl(
189+
fn {key, val}, acc -> Map.replace!(acc, key, val) end,
190+
@__struct__,
191+
var!(kv)
192+
)
193+
end
194+
end
195+
148196
case enforce_keys -- :maps.keys(struct) do
149197
[] ->
150-
{struct, enforce_keys, Module.get_attribute(module, :derive)}
198+
{struct, enforce_keys, Module.get_attribute(module, :derive), body}
151199

152200
error_keys ->
153201
raise ArgumentError,

lib/elixir/lib/module.ex

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -929,6 +929,10 @@ defmodule Module do
929929
simplify_arg(Macro.expand_once(attr, env), counters, env)
930930
end
931931

932+
defp simplify_arg({:var!, _, [{var, _, atom} | _]}, counters, _env) when is_atom(atom) do
933+
{simplify_var(var, Elixir), counters}
934+
end
935+
932936
defp simplify_arg(other, counters, _env) when is_integer(other),
933937
do: autogenerated_key(counters, :int)
934938

0 commit comments

Comments
 (0)