Here's a minimum broken example in Elixir 1.3:
defmodule Foo do
@type t :: %__MODULE__{x: non_neg_integer}
defstruct x: 0
@spec test(t) :: t
def test(%__MODULE__{} = foo), do: test2(foo)
@spec test2(t) :: t
defp test2(%__MODULE__{} = foo), do: %__MODULE__{foo | x: 5}
end
This fails to type check with: foo.ex:9: The variable _@1 can never match since previous clauses completely covered the type #{'__struct__':='Elixir.Foo', _=>_}
.
I've searched and searched and cannot for the life of me find an explanation of what this means, or how to fix it.
If you simplify the code to:
defmodule Foo do
@type t :: %__MODULE__{x: non_neg_integer}
defstruct x: 0
@spec set_x_to_5(t) :: t
def set_x_to_5(%__MODULE__{} = foo), do: %__MODULE__{foo | x: 5}
end
and then decompile the resulting .beam file, you get:
set_x_to_5(#{'__struct__' := 'Elixir.Foo'} = foo@1) ->
case foo@1 of
_@1 = #{'__struct__' := 'Elixir.Foo'} -> _@1#{x := 5};
_@1 -> erlang:error({badstruct, 'Elixir.Foo', _@1})
end.
If you see the case
statement Elixir generated for %__MODULE__{foo | x: 5}
carefully, you'll see that it contains a branch that can never match because __struct__
is guaranteed to be Foo
inside that function. This is generated by Elixir because Elixir throws an error if you use the %Struct{map | ...}
syntax with a map
of a different struct:
iex(1)> defmodule Foo, do: defstruct [:x]
iex(2)> defmodule Bar, do: defstruct [:x]
iex(3)> %Foo{%Bar{x: 1} | x: 2}
** (BadStructError) expected a struct named Foo, got: %Bar{x: 1}
To fix this, you can remove the __MODULE__
part and just do:
%{foo | x: 5}
The result will be the same and you'll get no warnings.