The previous implementation was generating an unnecessary
intermediate function when using `&super/arity`.
In addition, capture expressions using `super` with sequential
value placeholders (`&n`) were not optimized like other functions.
For example, the capture expression `&foo(&1, &2, &3)` is
optimized to `&foo/3` because the value placeholders (`&n`) are
sequential `&1, &2, &3`.
The current approach discards the __aliases__ tuple and
do a simple check that allows some strange behavior like:
```elixir
iex(1)> alias :lists, as: :"Elixir.foobar"
:lists
iex(2)> :"Elixir.foobar"
:"Elixir.foobar"
iex(3)> alias :"Elixir.foobar", as: Baz
:"Elixir.foobar"
iex(3)> Baz
:lists
```
This is important when you may want to check if a module
is available but if that module is running a similar check
on you, you don't want the compiler to fail.
This change fixes inconsistencies when building structs: if the
struct was built using the map syntax the `:__struct__` key
was overridden by the given module name; however, if the struct
was built using `struct/1,2` the `:__struct__` key was kept.
For example:
defmodule X do
defstruct [:x]
end
defmodule Y do
defdelegate __struct__, to: X
defdelegate __struct__(args), to: X
end
iex> %Y{}
%Y{x: nil}
iex> struct(Y)
%X{x: nil}
Closes#8800
During the bitstring expansion, guard checks were avoided due to
a special case for inlining binaries during interpolation. These
changes ensure guards are checked.
__struct__/0,1 is not meant to return an AST. Using
Macro.to_string/1 might lead to incorrect error messages:
defmodule MyStruct do
def __struct__, do: {:ok, :one, :two}
def __struct__(_), do: {:ok, :one, :two}
end
iex> %MyStruct{}
** (CompileError) iex:2: expected MyStruct.__struct__/1 to
return a map with a :__struct__ key that holds the name
of the struct (atom), got: ok
And vice-versa.
Overriding a macro as a function (and vice-versa) might produce
undesired effects.
For example, it's not clear if the following code should or
should not raise:
defmodule Foo do
def foo, do: bar()
defmacro bar, do: :ok
defoverridable bar: 0
def bar, do: :ok
end
On top of that, `super` does not currently work for such cases.
This is part of an ongoing effort to forbid overriding macros
with functions and vice-versa. New checks will require the
overridable information to be stored more efficiently.
Elixir's with is expanded into a series of nested cases. For
example, this code:
with {:ok, a} <- fun_a(),
{:ok, b} <- fun_b() do
wa = fun_w(a)
wb = fun_w(b)
else
error -> {:c, error}
end
would be expanded to something similar to this:
case fun_a() do
{:ok, a} ->
case fun_b() do
{:ok, b} ->
wa = fun_w(a)
wb = fun_w(b)
var1 ->
case var1 do
error -> {:c, error}
var2 -> error({with_clause, var2})
end
end
var1 ->
case var1 do
error -> {:c, error}
var2 -> error({with_clause, var2})
end
end
The generated code can be optimized for else clauses that
only contain a single catch-all clause (a variable that would
bind everything). Applying this optimization, the code of the
example above would be:
case fun_a() do
{:ok, a} ->
case fun_b() do
{:ok, b} ->
wa = fun_w(a)
wb = fun_w(b)
error ->
{:c, error}
end
error ->
{:c, error}
end
Binary/bitstring matching allows to dynamically define the
`size` of the binary in certain conditions:
* if the `size` variable is defined prior to the pattern
match:
iex> size = 8
iex> <<a::size(size), rest::binary>> = "hello"
iex> a
104
* if the `size` variable is matched within the same
binary/bitstring match, prior to its use:
iex> <<name_size::size(8), name::binary-size(name_size), _rest::binary>> = <<5, "Frank the Walrus">>
iex> name
"Frank"
Other cases are considered illegal patterns, for example:
{name_size, <<name::binary-size(name_size), _rest::binary>>} = {5, "Frank the Walrus"}
This commit raises a `CompileError: undefined variable ...` for such cases.