Skip to content

Possible bug in compiler emitted warnings about incompatibles types in guard #11744

@lud

Description

@lud
Erlang/OTP 24 [erts-12.2.1] [source] [64-bit] [smp:16:16] [ds:16:16:10] [async-threads:1] [jit]

Elixir 1.13.3 (compiled with Erlang/OTP 24)

Please have a look at this demo code, notably the defguard call and how it is used:

defmodule SomeGood do
  defstruct dummy: nil
end

defmodule Demo do
  @good_modules [Somegood]

  defguard is_good(mod_or_struct)
           when (is_atom(mod_or_struct) and mod_or_struct in @good_modules) or
                  (is_map(mod_or_struct) and is_map_key(mod_or_struct, :__struct__) and
                     :erlang.map_get(:__struct__, mod_or_struct) in @good_modules)

  defguard is_bad(mod_or_struct) when not is_good(mod_or_struct)
end

defmodule Worker do
  import Demo

  def work(%contract{}) when is_good(contract) do
    :alright
  end

  def work(_) do
    :nope
  end

  def a_case(data) do
    case data do
      %contract{} when is_good(contract) -> :good_contract
      data when is_good(data) -> :good_data
      _other -> :bad_data
    end
  end
end

defmodule Dependent do
  require Demo

  def get_fun do
    fn
      %contract{} when Demo.is_bad(contract) -> :nope
      _ -> :alright
    end
  end
end

It produces the following warnings:

warning: expected Kernel.is_map_key/2 to have signature:

    :__struct__, atom() -> dynamic()

but it has signature:

    dynamic(), %{optional(dynamic()) => dynamic()} -> dynamic()

in expression:

    # lib/demo.ex:41
    is_map_key(contract, :__struct__)

Conflict found at
  lib/demo.ex:41: Dependent.get_fun/0

warning: incompatible types:

    atom() !~ map()

in expression:

    # lib/demo.ex:19
    is_map(contract)

where "contract" was given the type atom() in:

    # lib/demo.ex:19
    %contract{}

where "contract" was given the type map() in:

    # lib/demo.ex:19
    is_map(contract)

Conflict found at
  lib/demo.ex:19: Worker.work/1

warning: incompatible types:

    atom() !~ map()

in expression:

    # lib/demo.ex:29
    is_map(contract)

where "contract" was given the type atom() in:

    # lib/demo.ex:29
    %contract{}

where "contract" was given the type map() in:

    # lib/demo.ex:29
    is_map(contract)

Conflict found at
  lib/demo.ex:29: Worker.a_case/1

But to me it looks like the guards are fine.

The main guard is defined as is instead of using is_struct because I was trying to find why there was such warnings.

The warning about expected Kernel.is_map_key/2 to have signature is caused by the usage of the guard defined as defguard is_bad(mod_or_struct) when not is_good(mod_or_struct) only.

Metadata

Metadata

Assignees

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions