Clause cannot be reached error
Background
I am trying to implement a couple of protocols in a module but Gradualizer is complaining saying my code is not reachable. The code works without issues, so I am confused.
Code
Here is a simplification of the source code:
defmodule Option do
defmodule Some do
@type t(elem) :: %__MODULE__{val: elem}
defstruct [:val]
defimpl Collectable do
@impl Collectable
def into(option), do: {option, fn acc, _command -> {:done, acc} end}
end
defimpl Enumerable do
@impl Enumerable
def count(_some), do: {:ok, 1}
@impl Enumerable
def member?(some, element), do: {:ok, some.val == element}
@impl Enumerable
def reduce(some, acc, fun)
def reduce(_some, {:halt, acc}, _fun), do: {:halted, acc}
def reduce(some, {:suspend, acc}, fun), do: {:suspended, acc, &reduce(some, &1, fun)}
def reduce([], {:cont, acc}, _fun), do: {:done, acc}
def reduce(%Option.Some{} = some, {:cont, acc}, fun),
do: reduce([], fun.(some.val, acc), fun)
def reduce(val, {:cont, acc}, fun), do: reduce([], fun.(val, acc), fun)
@impl Enumerable
@spec slice(%Option.Some{}) :: {:error, Enumerable.Option.Some}
def slice(_option), do: {:error, __MODULE__}
end
end
end
Here the errors:
$ mix gradient
Compiling 1 file (.ex)
Generated simple_monads app
Typechecking files...
lib/option.ex: The clause on line 9 cannot be reached
lib/option.ex: The clause on line 14 cannot be reached
lib/option.ex: The pattern %Option.Some{} on line 28 doesn't have the type any()
line 9: defimpl Collectable do
line 14: defimpl Enumerable do
line 28 does not accept any, but there is a clause for that after. By that logic, the line def reduce([], {:cont, acc}, _fun), do: {:done, acc} does not accept any as the first parameter and yet the tool wont complain.
Simply put, I cover all cases of the spec.
I would expect to see no errors.
Hi, @Fl4m3Ph03n1x!
Thanks for the new report. I'll start from the bottom:
lib/option.ex: The pattern %Option.Some{} on line 28 doesn't have the type any()
This is a known issue most recently reported in #51. There's an upstream ticket tracking it josefs/gradualizer#387.
These two, on the other hand, look similar to example of the regressions mentioned in josefs/gradualizer#359:
lib/option.ex: The clause on line 9 cannot be reached
lib/option.ex: The clause on line 14 cannot be reached
This needs a double check on our side, but at least we know where to start. If you have some extra time, could you run the check again with Gradient from this branch - https://github.com/esl/gradient/tree/use-erszcz-master? It's not ready for prime time yet, but it incorporates the WIP fixes from https://github.com/josefs/Gradualizer/pull/359.
Same issue:
lib/option.ex: The clause on line 9 cannot be reached
lib/option.ex: The clause on line 14 cannot be reached
lib/option.ex: The pattern %Option.Some{} on line 28 doesn't have the type any()
This is how my mix.exs looks:
defp deps,
do: [
{:gradient,
git: "[email protected]:esl/gradient.git",
branch: "use-erszcz-master",
only: [:dev],
runtime: false}
]
Thanks, @Fl4m3Ph03n1x :+1: This means the cannot be reached errors above are independent of https://github.com/josefs/Gradualizer/pull/359.
Hello there, any news regarding this issue? (I am just curious)
Hey, @Fl4m3Ph03n1x! https://github.com/esl/gradient/issues/61 and https://github.com/josefs/Gradualizer/issues/387 are fixed and closed, so
lib/option.ex: The pattern %Option.Some{} on line 28 doesn't have the type any()
shouldn't happen anymore. If you still see it happening with structs, please let us know here or create a new ticket.
Whether
lib/option.ex: The clause on line 9 cannot be reached
lib/option.ex: The clause on line 14 cannot be reached
are a manifestation of the list exhaustiveness checking regressions is not completely clear yet, but there's ongoing work upstream, most recently tracked as https://github.com/josefs/Gradualizer/pull/404, to fix that. Apparently, it's not trivial 😆 We're going in the right direction, though!
I've encountered a similar error but without using protocols.
defmodule Keymap.ListHelper do
def update!(list, key, fun) when is_list(list) and is_function(fun, 1) do
update!(list, key, fun, list)
end
defp update!([{key, value} | list], key, fun, _original) do
[{key, fun.(value)} | list]
end
defp update!([{_, _} = pair | list], key, fun, original) do
[pair | update!(list, key, fun, original)]
end
defp update!([], key, _fun, original) do
raise KeyError, key: key, term: original
end
end
The function is basically Keyword.update with two caveats:
- keys don't have to be atoms
- it throws on an empty list
And that last clause with throwing causes:
lib/keymap/list_helper.ex: The clause on line 14 cannot be reached
I figured you might like more data points to test :)
Hi, @tomekowal!
I figured you might like more data points to test :)
Sure, these are always welcome! Thanks for the example :)
This might be a duplicate with #32.
Ok, even with #72 the errors:
lib/option.ex: The clause on line 8 cannot be reached
lib/option.ex: The clause on line 13 cannot be reached
Are returned for this code:
$ nl -b a lib/option.ex
1 defmodule Option do
2
3 defmodule Some do
4 @type t(elem) :: %__MODULE__{val: elem}
5
6 defstruct [:val]
7
8 defimpl Collectable do
9 @impl Collectable
10 def into(option), do: {option, fn acc, _command -> {:done, acc} end}
11 end
12
13 defimpl Enumerable do
14 @impl Enumerable
15 def count(_some), do: {:ok, 1}
16
17 @impl Enumerable
18 def member?(some, element), do: {:ok, some.val == element}
19
20 @impl Enumerable
21 def reduce(some, acc, fun)
22
23 def reduce(_some, {:halt, acc}, _fun), do: {:halted, acc}
24 def reduce(some, {:suspend, acc}, fun), do: {:suspended, acc, &reduce(some, &1, fun)}
25 def reduce([], {:cont, acc}, _fun), do: {:done, acc}
26
27 def reduce(%Option.Some{} = some, {:cont, acc}, fun),
28 do: reduce([], fun.(some.val, acc), fun)
29
30 def reduce(val, {:cont, acc}, fun), do: reduce([], fun.(val, acc), fun)
31
32 @impl Enumerable
33 @spec slice(%Option.Some{}) :: {:error, Enumerable.Option.Some}
34 def slice(_option), do: {:error, __MODULE__}
35 end
36 end
37
38 end
So it means the problem is specific to protocols, i.e. using defimpl.