Skip to content

Commit 0aa9a8f

Browse files
committed
Add map_fetch_and_put
1 parent 1e55142 commit 0aa9a8f

File tree

4 files changed

+64
-12
lines changed

4 files changed

+64
-12
lines changed

lib/elixir/lib/module/types/descr.ex

Lines changed: 31 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1429,20 +1429,46 @@ defmodule Module.Types.Descr do
14291429
end
14301430

14311431
@doc """
1432-
Adds a `key` of a given type, assuming that the descr is exclusively
1432+
Fetches and puts a `key` of a given type, assuming that the descr is exclusively
1433+
a map (or dynamic).
1434+
"""
1435+
def map_fetch_and_put(:term, _key, _type), do: :badmap
1436+
1437+
def map_fetch_and_put(descr, key, :term) when is_atom(key),
1438+
do: map_fetch_and_put_shared(descr, key, :term)
1439+
1440+
def map_fetch_and_put(descr, key, type) when is_atom(key) do
1441+
case :maps.take(:dynamic, type) do
1442+
:error -> map_fetch_and_put_shared(descr, key, type)
1443+
{dynamic, _static} -> map_fetch_and_put_shared(dynamic(descr), key, dynamic)
1444+
end
1445+
end
1446+
1447+
defp map_fetch_and_put_shared(descr, key, type) do
1448+
with {value, descr} <- map_take(descr, key, none(), &map_put_static(&1, key, type)) do
1449+
if empty?(value) do
1450+
:badkey
1451+
else
1452+
{value, descr}
1453+
end
1454+
end
1455+
end
1456+
1457+
@doc """
1458+
Puts a `key` of a given type, assuming that the descr is exclusively
14331459
a map (or dynamic).
14341460
"""
14351461
def map_put(:term, _key, _type), do: :badmap
1436-
def map_put(descr, key, :term) when is_atom(key), do: map_put_static_value(descr, key, :term)
1462+
def map_put(descr, key, :term) when is_atom(key), do: map_put_shared(descr, key, :term)
14371463

14381464
def map_put(descr, key, type) when is_atom(key) do
14391465
case :maps.take(:dynamic, type) do
1440-
:error -> map_put_static_value(descr, key, type)
1441-
{dynamic, _static} -> map_put_static_value(dynamic(descr), key, dynamic)
1466+
:error -> map_put_shared(descr, key, type)
1467+
{dynamic, _static} -> map_put_shared(dynamic(descr), key, dynamic)
14421468
end
14431469
end
14441470

1445-
defp map_put_static_value(descr, key, type) do
1471+
defp map_put_shared(descr, key, type) do
14461472
with {_value, descr} <- map_take(descr, key, :term, &map_put_static(&1, key, type)) do
14471473
{:ok, descr}
14481474
end

lib/elixir/lib/module/types/expr.ex

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -135,7 +135,8 @@ defmodule Module.Types.Expr do
135135
if fallback == none() do
136136
Enum.reduce(pairs, map_type, fn {key, type}, acc ->
137137
case map_put(acc, key, type) do
138-
{:ok, descr} -> descr
138+
{_value, descr} -> descr
139+
:badkey -> throw({:badkey, map_type, key, expr, context})
139140
:badmap -> throw({:badmap, map_type, expr, context})
140141
end
141142
end)

lib/elixir/test/elixir/module/types/descr_test.exs

Lines changed: 10 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -852,13 +852,12 @@ defmodule Module.Types.DescrTest do
852852
assert map_fetch(term(), :a) == :badmap
853853
assert map_fetch(union(open_map(), integer()), :a) == :badmap
854854

855-
assert map_fetch(closed_map(a: integer()), :a) == {false, integer()}
856-
857-
assert map_fetch(union(closed_map(a: integer()), closed_map(b: atom())), :a) ==
858-
:badkey
855+
assert map_fetch(open_map(), :a) == :badkey
856+
assert map_fetch(open_map(a: not_set()), :a) == :badkey
857+
assert map_fetch(union(closed_map(a: integer()), closed_map(b: atom())), :a) == :badkey
858+
assert map_fetch(difference(closed_map(a: integer()), closed_map(a: term())), :a) == :badkey
859859

860-
assert map_fetch(difference(closed_map(a: integer()), closed_map(a: term())), :a) ==
861-
:badkey
860+
assert map_fetch(closed_map(a: integer()), :a) == {false, integer()}
862861

863862
assert map_fetch(union(closed_map(a: integer()), closed_map(a: atom())), :a) ==
864863
{false, union(integer(), atom())}
@@ -1043,6 +1042,11 @@ defmodule Module.Types.DescrTest do
10431042
assert equal?(type, open_map(a: not_set()))
10441043
end
10451044

1045+
test "map_fetch_and_put" do
1046+
assert map_fetch_and_put(term(), :a, integer()) == :badmap
1047+
assert map_fetch_and_put(open_map(), :a, integer()) == :badkey
1048+
end
1049+
10461050
test "map_put" do
10471051
assert map_put(term(), :a, integer()) == :badmap
10481052
assert map_put(integer(), :a, integer()) == :badmap

lib/elixir/test/elixir/module/types/expr_test.exs

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -561,6 +561,27 @@ defmodule Module.Types.ExprTest do
561561
# from: types_test.ex:LINE
562562
x = :foo
563563
"""
564+
565+
assert typeerror!(
566+
(
567+
x = %{}
568+
%{x | x: :zero}
569+
)
570+
) == ~l"""
571+
expected a map within map update syntax:
572+
573+
%{x | x: :zero}
574+
575+
but got type:
576+
577+
dynamic(:foo)
578+
579+
where "x" was given the type:
580+
581+
# type: dynamic(:foo)
582+
# from: types_test.ex:LINE
583+
x = :foo
584+
"""
564585
end
565586

566587
test "updating to open maps" do

0 commit comments

Comments
 (0)