结构的透析器错误

时间:2016-08-06 04:11:32

标签: types elixir dialyzer

以下是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

无法使用foo.ex:9: The variable _@1 can never match since previous clauses completely covered the type #{'__struct__':='Elixir.Foo', _=>_}键入支票。

我进行了搜索和搜索,并且不能为我的生活找到解释这意味着什么,或者如何解决它。

1 个答案:

答案 0 :(得分:7)

如果您将代码简化为:

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

然后反编译生成的.beam文件,你得到:

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.

如果您仔细查看为case生成的%__MODULE__{foo | x: 5}语句Elixir,您会发现它包含一个永不匹配的分支,因为__struct__保证为{{ 1}}在那个函数里面。这是由Elixir生成的,因为如果将Foo语法与%Struct{map | ...}不同的结构使用,Elixir会抛出错误:

map

要解决此问题,您可以删除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} 部分,然后执行以下操作:

__MODULE__

结果将是相同的,您将不会收到任何警告。