Skip to content

Gradual map domain with none looses static part when computing lower bound #15854

Description

@lukaszsamson

Existing issue

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

Elixir and Erlang/OTP versions

1.21.0-dev@87af1e34e

Operating system

any

Current behavior

Map domain entries are documented to be implicitly optional - a domain value none() means that the map contains no entries whose keys belong to that domain. It does not make the entire map type empty yet the current implementation breaks this assumption and loses precision of the static part:

Repro:

import Module.Types.Descr

static =
  closed_map([
    {[:integer], none()},
    {:b, {integer(), false}}
  ])

gradual =
  closed_map([
    {[:integer], dynamic()},
    {:b, {integer(), false}}
  ])

IO.puts("actual:   #{to_quoted_string(lower_bound(gradual))}")
IO.puts("expected: #{to_quoted_string(static)}")
IO.inspect(lower_bound(gradual) == static, label: "equal?")

result:

actual:   none()
expected: %{integer() => not_set(), b: integer()}
equal?: false

The same static part loss through contravariant argument handling

actual_fun = upper_bound(fun([gradual], atom()))
expected_fun = fun([static], atom())

IO.puts("actual fun:   #{to_quoted_string(actual_fun)}")
IO.puts("expected fun: #{to_quoted_string(expected_fun)}")
IO.inspect(actual_fun == expected_fun, label: "fun equal?")

result:

actual fun:   (none() -> atom())
expected fun: (%{integer() => not_set(), b: integer()} -> atom())
fun equal?: false

Another example

static =
  open_map([
    {[:tuple], none()},
    {:b, {integer(), true}}
  ])

gradual =
  open_map([
    {[:tuple], none()},
    {:b, {opt_union(integer(), dynamic(float())), true}}
  ])

IO.inspect(lower_bound(gradual) == static)
# Current: false
# Expected: true

Expected behavior

No precision loss

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

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