Skip to content

Commit e87b935

Browse files
committed
Streamline subpatterns for domain keys and list heads
1 parent b6a0097 commit e87b935

3 files changed

Lines changed: 30 additions & 47 deletions

File tree

lib/elixir/lib/module/types.ex

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -446,8 +446,8 @@ defmodule Module.Types do
446446
warnings: [],
447447
# All vars and their types
448448
vars: %{},
449-
# Stores special metadata need by heads in patterns
450-
heads: %{},
449+
# Stores special metadata used by list heads and domain keys in patterns
450+
subpatterns: %{},
451451
# Variables that are specific to the current environment/conditional
452452
conditional_vars: nil,
453453
# Track metadata specific to matches and guards

lib/elixir/lib/module/types/of.ex

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -169,8 +169,12 @@ defmodule Module.Types.Of do
169169
Preserves `context` in first argument while
170170
resetting it to the vars in the second argument.
171171
"""
172-
def reset_vars(context, %{heads: heads, vars: vars, conditional_vars: conditional_vars}),
173-
do: %{context | heads: heads, vars: vars, conditional_vars: conditional_vars}
172+
def reset_vars(context, %{
173+
subpatterns: subpatterns,
174+
vars: vars,
175+
conditional_vars: conditional_vars
176+
}),
177+
do: %{context | subpatterns: subpatterns, vars: vars, conditional_vars: conditional_vars}
174178

175179
@doc """
176180
Executes the args with acc using conditional variables.
@@ -615,8 +619,8 @@ defmodule Module.Types.Of do
615619
{:unknown, compatible_size(actual, expr, stack, context)}
616620
end
617621

618-
defp specifier_size(match_or_guard, {:size, _, [arg]} = expr, stack, {_, context}) do
619-
{actual, context} = Module.Types.Pattern.of_size(match_or_guard, arg, expr, stack, context)
622+
defp specifier_size(_match_or_guard, {:size, _, [arg]} = expr, stack, {_, context}) do
623+
{actual, context} = Module.Types.Pattern.of_guard(arg, integer(), expr, stack, context)
620624
{:unknown, compatible_size(actual, expr, stack, context)}
621625
end
622626

lib/elixir/lib/module/types/pattern.ex

Lines changed: 20 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -354,15 +354,9 @@ defmodule Module.Types.Pattern do
354354
end
355355
end
356356

357-
defp of_pattern_var([{:head, key} | rest], type, context) do
358-
case list_hd(type) do
359-
{:ok, head} ->
360-
%{^key => tree} = context.heads
361-
of_pattern_var(rest, intersection(head, of_pattern_tree(tree, context)), context)
362-
363-
_ ->
364-
:error
365-
end
357+
defp of_pattern_var([{:subpattern, key} | rest], type, context) do
358+
%{^key => subpattern} = context.subpatterns
359+
of_pattern_var(rest, intersection(type, subpattern), context)
366360
end
367361

368362
defp of_pattern_var([:tail | rest], type, context) do
@@ -463,15 +457,6 @@ defmodule Module.Types.Pattern do
463457
{float(), context}
464458
end
465459

466-
@doc """
467-
Handle `size` in binary modifiers.
468-
469-
They behave like guards, so we need to take into account their scope.
470-
"""
471-
def of_size(_match_or_guard, arg, expr, stack, context) do
472-
of_guard(arg, integer(), expr, stack, context)
473-
end
474-
475460
## Patterns
476461

477462
# :atom
@@ -543,16 +528,7 @@ defmodule Module.Types.Pattern do
543528
true -> {:intersection, [Enum.reduce(static, &intersection/2) | dynamic]}
544529
end
545530

546-
# If the path has domain keys or head, then it is imprecise,
547-
# and we need to keep filtering the match variable itself.
548-
imprecise? =
549-
Enum.any?(path, fn
550-
{:domain, _} -> true
551-
:head -> true
552-
_ -> false
553-
end)
554-
555-
if dynamic == [] and not imprecise? do
531+
if dynamic == [] do
556532
{return, context}
557533
else
558534
context = of_var(var, version, [%{root: return, expr: match}], context)
@@ -714,7 +690,8 @@ defmodule Module.Types.Pattern do
714690
{key_type, context} = of_pattern(key, [%{root: :key, expr: key}], stack, context)
715691
true = is_descr(key_type)
716692

717-
{_value_type, context} = of_pattern(value, [{:domain, key_type} | path], stack, context)
693+
{_value_type, context} =
694+
of_subpattern(value, [{:domain, key_type} | path], stack, context)
718695

719696
# A domain key cannot restrict the map in any way,
720697
# because we are matching only on one possible value
@@ -757,18 +734,7 @@ defmodule Module.Types.Pattern do
757734

758735
result =
759736
Enum.reduce(prefix, {[], [], context}, fn arg, {static, dynamic, context} ->
760-
# These cases don't need to store head information because
761-
# they have no intersection
762-
{type, context} =
763-
if is_number(arg) or is_binary(arg) or is_atom(arg) or arg == [] or is_var(arg) do
764-
of_pattern(arg, [:head | path], stack, context)
765-
else
766-
%{heads: heads} = context
767-
key = map_size(heads)
768-
context = %{context | heads: Map.put(heads, key, nil)}
769-
{type, context} = of_pattern(arg, [{:head, key} | path], stack, context)
770-
{type, put_in(context.heads[key], type)}
771-
end
737+
{type, context} = of_subpattern(arg, [:head | path], stack, context)
772738

773739
if is_descr(type) do
774740
{[type | static], dynamic, context}
@@ -789,6 +755,19 @@ defmodule Module.Types.Pattern do
789755
end
790756
end
791757

758+
# These cases don't need to store information because they have no intersection
759+
defp of_subpattern(arg, path, stack, context)
760+
when is_number(arg) or is_binary(arg) or is_atom(arg) or arg == [] or is_var(arg) do
761+
of_pattern(arg, path, stack, context)
762+
end
763+
764+
defp of_subpattern(arg, path, stack, %{subpatterns: subpatterns} = context) do
765+
key = map_size(subpatterns)
766+
context = %{context | subpatterns: Map.put(subpatterns, key, nil)}
767+
{type, context} = of_pattern(arg, [{:subpattern, key} | path], stack, context)
768+
{type, put_in(context.subpatterns[key], of_pattern_tree(type, context))}
769+
end
770+
792771
## Guards
793772
#
794773
# Whenever we have a or/orelse, we need to build multiple environments

0 commit comments

Comments
 (0)