Skip to content

Commit 67baf68

Browse files
committed
Properly propagate if_set on Map operations
1 parent 297bd84 commit 67baf68

File tree

6 files changed

+29
-34
lines changed

6 files changed

+29
-34
lines changed

lib/elixir/lib/map.ex

Lines changed: 16 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -408,8 +408,8 @@ defmodule Map do
408408
iex> Map.replace(%{a: 1, b: 2}, :a, 3)
409409
%{a: 3, b: 2}
410410
411-
iex> Map.replace(%{a: 1}, :b, 2)
412-
%{a: 1}
411+
iex> Map.replace(%{"a" => 1}, "b", 2)
412+
%{"a" => 1}
413413
414414
"""
415415
@doc since: "1.11.0"
@@ -467,8 +467,8 @@ defmodule Map do
467467
iex> Map.replace_lazy(%{a: 1, b: 2}, :a, fn v -> v * 4 end)
468468
%{a: 4, b: 2}
469469
470-
iex> Map.replace_lazy(%{a: 1, b: 2}, :c, fn v -> v * 4 end)
471-
%{a: 1, b: 2}
470+
iex> Map.replace_lazy(%{"a" => 1, "b" => 2}, "c", fn v -> v * 4 end)
471+
%{"a" => 1, "b" => 2}
472472
473473
"""
474474
@doc since: "1.14.0"
@@ -571,15 +571,13 @@ defmodule Map do
571571
572572
## Examples
573573
574-
iex> Map.get(%{}, :a)
575-
nil
576-
iex> Map.get(%{a: 1}, :a)
574+
iex> Map.get(%{"a" => 1}, "a")
577575
1
578-
iex> Map.get(%{a: 1}, :b)
576+
iex> Map.get(%{"a" => 1}, "b")
579577
nil
580-
iex> Map.get(%{a: 1}, :b, 3)
578+
iex> Map.get(%{"a" => 1}, "b", 3)
581579
3
582-
iex> Map.get(%{a: nil}, :a, 1)
580+
iex> Map.get(%{"a" => nil}, "a", 1)
583581
nil
584582
585583
"""
@@ -608,15 +606,11 @@ defmodule Map do
608606
609607
## Examples
610608
611-
iex> map = %{a: 1}
612-
iex> fun = fn ->
613-
...> # some expensive operation here
614-
...> 13
615-
...> end
616-
iex> Map.get_lazy(map, :a, fun)
609+
iex> Map.get_lazy(%{a: 1}, :a, fn -> :expensive_value end)
617610
1
618-
iex> Map.get_lazy(map, :b, fun)
619-
13
611+
612+
iex> Map.get_lazy(%{"a" => 1}, "b", fn -> :expensive_value end)
613+
:expensive_value
620614
621615
"""
622616
@spec get_lazy(map, key, (-> value)) :: value
@@ -808,15 +802,11 @@ defmodule Map do
808802
809803
## Examples
810804
811-
iex> map = %{a: 1}
812-
iex> fun = fn ->
813-
...> # some expensive operation here
814-
...> 13
815-
...> end
816-
iex> Map.pop_lazy(map, :a, fun)
805+
iex> Map.pop_lazy(%{a: 1}, :a, fn -> :expensive_value end)
817806
{1, %{}}
818-
iex> Map.pop_lazy(map, :b, fun)
819-
{13, %{a: 1}}
807+
808+
iex> Map.pop_lazy(%{"a" => 1}, "b", fn -> :expensive_value end)
809+
{:expensive_value, %{"a" => 1}}
820810
821811
"""
822812
@spec pop_lazy(map, key, (-> value)) :: {value, map}

lib/elixir/lib/module/types/apply.ex

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -519,7 +519,9 @@ defmodule Module.Types.Apply do
519519
end
520520

521521
defp remote_apply(Map, :replace, _info, [map, key, value] = args_types, stack) do
522-
case map_update(map, key, value, false, false) do
522+
fun = fn optional?, _type -> if optional?, do: if_set(value), else: value end
523+
524+
case map_update_fun(map, key, fun, false, false) do
523525
{_value, descr, _errors} -> {:ok, return(descr, args_types, stack)}
524526
:badmap -> {:error, badremote(Map, :replace, args_types)}
525527
{:error, _errors} -> {:error, {:badkeydomain, map, key, "do nothing"}}
@@ -671,7 +673,9 @@ defmodule Module.Types.Apply do
671673
end
672674

673675
defp remote_apply(:maps, :update, _info, [key, value, map] = args_types, stack) do
674-
case map_update(map, key, value, false, false) do
676+
fun = fn optional?, _type -> if optional?, do: if_set(value), else: value end
677+
678+
case map_update_fun(map, key, fun, false, false) do
675679
{_value, descr, _errors} -> {:ok, return(descr, args_types, stack)}
676680
:badmap -> {:error, badremote(:maps, :update, args_types)}
677681
{:error, _errors} -> {:error, {:badkeydomain, map, key, "raise"}}
@@ -1061,9 +1065,9 @@ defmodule Module.Types.Apply do
10611065
_ -> {map, fun}
10621066
end
10631067

1064-
fun_apply = fn _optional?, arg_type ->
1068+
fun_apply = fn optional?, arg_type ->
10651069
case fun_apply(fun, [arg_type]) do
1066-
{:ok, res} -> res
1070+
{:ok, res} -> if optional?, do: if_set(res), else: res
10671071
reason -> throw({:badapply, reason, [arg_type]})
10681072
end
10691073
end

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

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -272,6 +272,7 @@ defmodule Module.Types.Descr do
272272
# If type contains a :dynamic part, :optional gets added there.
273273
def if_set(type) do
274274
case type do
275+
%{dynamic: :term} -> %{dynamic: term_or_optional()}
275276
%{dynamic: dyn} -> Map.put(%{type | dynamic: Map.put(dyn, :optional, 1)}, :optional, 1)
276277
_ -> Map.put(type, :optional, 1)
277278
end

lib/elixir/test/elixir/map_test.exs

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -216,8 +216,7 @@ defmodule MapTest do
216216
map = %{c: 3, b: 2, a: 1}
217217
assert Map.replace(map, :b, 10) == %{c: 3, b: 10, a: 1}
218218
assert Map.replace(map, :a, 1) == map
219-
assert Map.replace(map, :x, 1) == map
220-
assert Map.replace(%{}, :x, 1) == %{}
219+
assert Map.replace(Process.get(:unused, map), :x, 1) == map
221220
end
222221

223222
test "replace!/3" do

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

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -795,6 +795,7 @@ defmodule Module.Types.DescrTest do
795795
refute subtype?(if_set(none()), term())
796796
refute subtype?(if_set(term()), term())
797797
assert subtype?(if_set(term()), if_set(term()))
798+
refute subtype?(if_set(term()), if_set(dynamic(term())))
798799
end
799800

800801
test "list" do

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

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -954,7 +954,7 @@ defmodule Module.Types.MapTest do
954954
closed_map(key: atom([:value]))
955955

956956
assert typecheck!([x], Map.replace(x, :key, :value)) ==
957-
dynamic(open_map(key: atom([:value])))
957+
dynamic(open_map(key: if_set(atom([:value]))))
958958

959959
# If one of them succeeds, we are still fine!
960960
assert typecheck!(
@@ -1001,7 +1001,7 @@ defmodule Module.Types.MapTest do
10011001
dynamic(closed_map(key: atom([:value])))
10021002

10031003
assert typecheck!([x], Map.replace_lazy(x, :key, fn _ -> :value end)) ==
1004-
dynamic(open_map(key: atom([:value])))
1004+
dynamic(open_map(key: if_set(atom([:value]))))
10051005

10061006
# If one of them succeeds, we are still fine!
10071007
assert typecheck!(

0 commit comments

Comments
 (0)