Skip to content

Commit

Permalink
Infer expression types in basic operations
Browse files Browse the repository at this point in the history
  • Loading branch information
josevalim committed Jan 2, 2025
1 parent 8e796fc commit 4ce7c06
Show file tree
Hide file tree
Showing 11 changed files with 324 additions and 125 deletions.
2 changes: 1 addition & 1 deletion lib/elixir/lib/module/types.ex
Original file line number Diff line number Diff line change
Expand Up @@ -285,7 +285,7 @@ defmodule Module.Types do
Pattern.of_head(args, guards, expected, {:infer, expected}, meta, stack, context)

{return_type, context} =
Expr.of_expr(body, stack, context)
Expr.of_expr(body, {Descr.term(), :ok}, stack, context)

{type_index, inferred} =
add_inferred(inferred, args_types, return_type, total - 1, [])
Expand Down
38 changes: 37 additions & 1 deletion lib/elixir/lib/module/types/descr.ex
Original file line number Diff line number Diff line change
Expand Up @@ -382,7 +382,8 @@ defmodule Module.Types.Descr do
{dynamic, descr} =
case :maps.take(:dynamic, descr) do
:error -> {[], descr}
{dynamic, descr} -> {to_quoted(:dynamic, dynamic, opts), descr}
{:term, descr} -> {to_quoted(:dynamic, :term, opts), descr}
{dynamic, descr} -> {to_quoted(:dynamic, difference(dynamic, descr), opts), descr}
end

# Merge empty list and list together if they both exist
Expand Down Expand Up @@ -547,6 +548,41 @@ defmodule Module.Types.Descr do
end
end

@doc """
Returns the intersection between two types
only if they are compatible. Otherwise returns `:error`.
This finds the intersection between the arguments and the
domain of a function. It is used to refine dynamic types
as we traverse the program.
"""
def compatible_intersection(left, right) do
{left_dynamic, left_static} =
case left do
:term -> {:term, :term}
_ -> Map.pop(left, :dynamic, left)
end

right_dynamic =
case right do
%{dynamic: dynamic} -> dynamic
_ -> right
end

cond do
empty?(left_static) ->
dynamic = intersection_static(unfold(left_dynamic), unfold(right_dynamic))
if empty?(dynamic), do: :error, else: {:ok, dynamic(dynamic)}

subtype_static?(left_static, right_dynamic) ->
dynamic = intersection_static(unfold(left_dynamic), unfold(right_dynamic))
{:ok, union(dynamic(dynamic), left_static)}

true ->
:error
end
end

@doc """
Optimized version of `not empty?(term(), type)`.
"""
Expand Down
Loading

0 comments on commit 4ce7c06

Please sign in to comment.