Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Clause cannot be reached error #52

Open
Fl4m3Ph03n1x opened this issue Feb 23, 2022 · 9 comments
Open

Clause cannot be reached error #52

Fl4m3Ph03n1x opened this issue Feb 23, 2022 · 9 comments
Labels
bug Something isn't working

Comments

@Fl4m3Ph03n1x
Copy link
Contributor

Fl4m3Ph03n1x commented Feb 23, 2022

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.

@erszcz
Copy link
Member

erszcz commented Feb 23, 2022

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 josefs/Gradualizer#359.

@erszcz erszcz added the bug Something isn't working label Feb 23, 2022
@Fl4m3Ph03n1x
Copy link
Contributor Author

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}
    ]

@erszcz
Copy link
Member

erszcz commented Feb 23, 2022

Thanks, @Fl4m3Ph03n1x 👍 This means the cannot be reached errors above are independent of josefs/Gradualizer#359.

@Fl4m3Ph03n1x
Copy link
Contributor Author

Hello there, any news regarding this issue?
(I am just curious)

@erszcz
Copy link
Member

erszcz commented Apr 21, 2022

Hey, @Fl4m3Ph03n1x! #61 and josefs/Gradualizer#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 josefs/Gradualizer#404, to fix that. Apparently, it's not trivial 😆 We're going in the right direction, though!

@tomekowal
Copy link

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 :)

@erszcz
Copy link
Member

erszcz commented Apr 22, 2022

Hi, @tomekowal!

I figured you might like more data points to test :)

Sure, these are always welcome! Thanks for the example :)

@erszcz
Copy link
Member

erszcz commented Apr 27, 2022

This might be a duplicate with #32.

@erszcz
Copy link
Member

erszcz commented Apr 28, 2022

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.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug Something isn't working
Projects
None yet
Development

No branches or pull requests

3 participants