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 unreachable for integer return value rather than any #84

Open
baldwindavid opened this issue May 26, 2022 · 2 comments
Open

Clause unreachable for integer return value rather than any #84

baldwindavid opened this issue May 26, 2022 · 2 comments
Labels
bug Something isn't working enhancement New feature or request

Comments

@baldwindavid
Copy link

baldwindavid commented May 26, 2022

I'm testing out gradient on a baldwindavid/ex_waiter#3 and get an error that a clause cannot be reached when setting the return type as an integer on a function. It does work if the return type is any. The function and desired spec is commented out at... https://github.com/baldwindavid/ex_waiter/pull/3/files#diff-a502442c0bd637c012b9c3bd3788d431af95331497257e34e1a53bb8a5446f49R332

@erszcz
Copy link
Member

erszcz commented May 27, 2022

There are two remaining FIXMEs in the linked PR, let me go over them one by one.

  # FIXME: It seems the second attempt/1 clause cannot be reached.
  @spec attempt(%Waiter{}) :: {:ok, %Waiter{}} | {:error, %Waiter{}}
  defp attempt(%Waiter{attempt_num: num, num_attempts: num} = waiter) do

This one seems to be caused by a known limitation in the type checker, i.e. by it not being able to understand variable binds in the struct pattern and the fact that both fields must match. In other words, the problem is on the type checker side, not in the checked code, so it's a false positive.

  # FIXME: Error when the return type is set to integer()
  # Error: lib/ex_waiter.ex: The clause on line XXX cannot be reached
  @spec delay_default(%Waiter{}) :: timeout()
  defp delay_default(%Waiter{} = waiter) do
    waiter.attempt_num * 10
  end

This one seems to be an issue we were not aware of before. Thanks, @baldwindavid!

Details follow - delay_default and its spec are translated to the following Erlang:

168 -spec delay_default(#{'__struct__' :=
169                           'Elixir.ExWaiter.Waiter',
170                       attempt_num := term(), attempts := term(),
171                       checker_fn := term(), delay := term(),
172                       'exception_on_retries_exhausted?' := term(),
173                       'fulfilled?' := term(), num_attempts := term(),
174                       on_failure := term(), on_success := term(),
175                       returning := term(), total_delay := term(),
176                       value := term()}) -> timeout().

276 delay_default(#{'__struct__' :=
277                     'Elixir.ExWaiter.Waiter'} =
278                   _waiter@1) ->
279     case _waiter@1 of
280         #{attempt_num := _@1} -> _@1;
281         _@1 when erlang:is_map(_@1) ->
282             erlang:error({badkey, attempt_num, _@1});
283         _@1 -> _@1:attempt_num()
284     end
285         * 10.

When type checking that as Erlang the error message pinpoints the exact clause that cannot match:

Typechecking files...
src/ex_waiter.erl: The clause on line 281 at column 9 cannot be reached

This means we're not type checking the generated code properly. Thanks again, @baldwindavid, for taking the time to try Gradient and Gradualizer out. We've got some homework to do based on this report :)

@erszcz erszcz added bug Something isn't working enhancement New feature or request labels May 27, 2022
@baldwindavid
Copy link
Author

Thanks for checking into the report! This was the perfect size small codebase to try out the package and it works quite well. I'm looking forward to seeing it develop and hope to use it on a large codebase at some point. I've mostly been using the TypeCheck library, which is very powerful, but just really hard to beat a static type checker that uses regular Elixir typespecs.

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

No branches or pull requests

2 participants