Skip to content

MapSet.new/0 threaded through recursion triggers dialyzer call_without_opaque #15673

Description

@bobergj

Existing issue

  • I have searched existing issues and could not find a duplicate.

Elixir and Erlang/OTP versions

Erlang/OTP 28 [erts-16.4.0.2] [source] [64-bit] [smp:14:14] [ds:14:14:10] [async-threads:1] [jit]

Elixir 1.20.2 (compiled with Erlang/OTP 28)

Operating system

macOS 26.5.2

Current behavior

lib/repro.ex:

defmodule RecursiveMapSetRepro do
  def check(ids), do: check(ids, MapSet.new())

  defp check([], _seen), do: false

  defp check([id | ids], seen) do
    if MapSet.member?(seen, id) do
      true
    else
      check(ids, MapSet.put(seen, id))
    end
  end
end

mix.exs:

defmodule Repro.MixProject do
  use Mix.Project

  def project do
    [
      app: :repro,
      version: "0.1.0",
      elixir: "~> 1.20",
      deps: [
        {:dialyxir, "~> 1.4", only: [:dev], runtime: false}
      ]
    ]
  end
end
mix deps.get
mix dialyzer
lib/repro.ex:8:23:call_without_opaque
Function call without opaqueness type mismatch.

Call does not have expected term of type %MapSet{:map => :sets.set(_)} (with opaque subterms) in the 1st position.

MapSet.member?(
  _seen :: %MapSet{
    :map =>
      {:set, non_neg_integer(), non_neg_integer(), pos_integer(), non_neg_integer(),
       non_neg_integer(), non_neg_integer(), tuple(), tuple()}
      | %{_ => []}
  },
  _id :: any()
)

Expected behavior

Dialyzer does not output any errors.

I've looked at #15399 and the issue discussion though, so I understand if it is not fixable. dialyxir :no_opaque option silences the error.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions