@@ -103,7 +103,7 @@ defmodule Kernel.Utils do
103103 def defstruct ( module , fields , bootstrapped? , env ) do
104104 { set , bag } = :elixir_module . data_tables ( module )
105105
106- if :ets . member ( set , :__struct__ ) do
106+ if :ets . member ( set , { :elixir , :struct } ) do
107107 raise ArgumentError ,
108108 "defstruct has already been called for " <>
109109 "#{ Kernel . inspect ( module ) } , defstruct can only be called once per module"
@@ -119,6 +119,8 @@ defmodule Kernel.Utils do
119119
120120 mapper = fn
121121 { key , val } when is_atom ( key ) ->
122+ key == :__struct__ and raise ( ArgumentError , "cannot set :__struct__ in struct definition" )
123+
122124 try do
123125 :elixir_quote . escape ( val , :none , false )
124126 rescue
@@ -129,6 +131,7 @@ defmodule Kernel.Utils do
129131 end
130132
131133 key when is_atom ( key ) ->
134+ key == :__struct__ and raise ( ArgumentError , "cannot set :__struct__ in struct definition" )
132135 { key , nil }
133136
134137 other ->
@@ -163,23 +166,24 @@ defmodule Kernel.Utils do
163166 end
164167
165168 :lists . foreach ( foreach , enforce_keys )
166- struct = :maps . put ( :__struct__ , module , :maps . from_list ( fields ) )
169+ struct = :maps . from_list ( [ __struct__: module ] ++ fields )
170+ escaped_struct = :elixir_quote . escape ( struct , :none , false )
167171
168172 body =
169173 case bootstrapped? do
170174 true ->
171175 case enforce_keys do
172176 [ ] ->
173177 quote do
174- Enum . reduce ( kv , @ __struct__ , fn { key , val } , map ->
178+ Enum . reduce ( kv , unquote ( escaped_struct ) , fn { key , val } , map ->
175179 % { map | key => val }
176180 end )
177181 end
178182
179183 _ ->
180184 quote do
181185 { map , keys } =
182- Enum . reduce ( kv , { @ __struct__ , unquote ( enforce_keys ) } , fn
186+ Enum . reduce ( kv , { unquote ( escaped_struct ) , unquote ( enforce_keys ) } , fn
183187 { key , val } , { map , keys } ->
184188 { % { map | key => val } , List . delete ( keys , key ) }
185189 end )
@@ -200,25 +204,21 @@ defmodule Kernel.Utils do
200204 quote do
201205 :lists . foldl (
202206 fn { key , val } , acc -> % { acc | key => val } end ,
203- @ __struct__ ,
207+ unquote ( escaped_struct ) ,
204208 kv
205209 )
206210 end
207211 end
208212
209213 case enforce_keys -- :maps . keys ( struct ) do
210214 [ ] ->
211- # The __struct__ attribute is during expansion and for loading remote structs
212- :ets . insert ( set , { :__struct__ , struct , nil , [ ] } )
213-
214- # The complete metadata goes into __info__(:struct)
215215 mapper = fn { key , val } ->
216216 % { field: key , default: val , required: :lists . member ( key , enforce_keys ) }
217217 end
218218
219219 :ets . insert ( set , { { :elixir , :struct } , :lists . map ( mapper , fields ) } )
220220 derive = :lists . map ( fn { _ , value } -> value end , :ets . take ( bag , { :accumulate , :derive } ) )
221- { struct , :lists . reverse ( derive ) , quote ( do: kv ) , body }
221+ { struct , :lists . reverse ( derive ) , escaped_struct , quote ( do: kv ) , body }
222222
223223 error_keys ->
224224 raise ArgumentError ,
0 commit comments