Elixir and Erlang/OTP versions
Erlang/OTP 28 [erts-16.2] [source] [64-bit] [smp:12:12] [ds:12:12:10] [async-threads:1] [jit]
Interactive Elixir (1.20.0-rc.4)
Operating system
any
Current behavior
tuple/1, closed_map/1, open_map/1, non_empty_list/1,2 discard the static component of gradual element types, producing dynamic-only containers. This breaks subtype relation
Repro:
import Module.Types.Descr
# NOTE static and dynamic part
x = union(atom([:ok]), dynamic(integer()))
%{atom: {:union, %{ok: []}}, dynamic: %{atom: {:union, %{ok: []}}, bitmap: 8}}
# subtype holds
subtype?(atom([:ok]), x)
true
# NOTE only dynamic
tuple([x])
%{dynamic: %{tuple: {:closed, [%{atom: {:union, %{ok: []}}, bitmap: 8}]}}}
closed_map(a: x)
%{dynamic: %{map: {:closed, [a: %{atom: {:union, %{ok: []}}, bitmap: 8}]}}}
open_map(a: x)
%{dynamic: %{map: {:open, [a: %{atom: {:union, %{ok: []}}, bitmap: 8}]}}}
non_empty_list(x)
%{dynamic: %{list: {%{atom: {:union, %{ok: []}}, bitmap: 8}, %{bitmap: 4}}}}
# subtype does not hold
subtype?(tuple([atom([:ok])]), tuple([x]))
false
subtype?(closed_map(a: atom([:ok])), closed_map(a: x))
false
subtype?(open_map(a: atom([:ok])), open_map(a: x))
false
subtype?(non_empty_list(atom([:ok])), non_empty_list(x))
false
Cause: All three constructors call :maps.take(:dynamic, value) on element types and use only the dynamic part, discarding _static:
- tuple_descr/3
- map_descr_pairs/4
- list_pop_dynamic/1
Expected behavior
Constructors should split the element into static and dynamic parts, building both a static container and a dynamic container. There are tests asserting existing dynamic hoisting behavior:
dynamic(open_map(a: integer())) == open_map(a: dynamic(integer()))
Elixir and Erlang/OTP versions
Erlang/OTP 28 [erts-16.2] [source] [64-bit] [smp:12:12] [ds:12:12:10] [async-threads:1] [jit]
Interactive Elixir (1.20.0-rc.4)
Operating system
any
Current behavior
tuple/1,closed_map/1,open_map/1,non_empty_list/1,2discard the static component of gradual element types, producing dynamic-only containers. This breaks subtype relationRepro:
Cause: All three constructors call :maps.take(:dynamic, value) on element types and use only the dynamic part, discarding _static:
elixir/lib/elixir/lib/module/types/descr.ex
Line 4931 in 03b45e5
elixir/lib/elixir/lib/module/types/descr.ex
Line 4827 in 03b45e5
elixir/lib/elixir/lib/module/types/descr.ex
Line 4224 in 03b45e5
Expected behavior
Constructors should split the element into static and dynamic parts, building both a static container and a dynamic container. There are tests asserting existing dynamic hoisting behavior: