Skip to content

Commit 4ce7c06

Browse files
committed
Infer expression types in basic operations
1 parent 8e796fc commit 4ce7c06

File tree

11 files changed

+324
-125
lines changed

11 files changed

+324
-125
lines changed

lib/elixir/lib/module/types.ex

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -285,7 +285,7 @@ defmodule Module.Types do
285285
Pattern.of_head(args, guards, expected, {:infer, expected}, meta, stack, context)
286286

287287
{return_type, context} =
288-
Expr.of_expr(body, stack, context)
288+
Expr.of_expr(body, {Descr.term(), :ok}, stack, context)
289289

290290
{type_index, inferred} =
291291
add_inferred(inferred, args_types, return_type, total - 1, [])

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

Lines changed: 37 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -382,7 +382,8 @@ defmodule Module.Types.Descr do
382382
{dynamic, descr} =
383383
case :maps.take(:dynamic, descr) do
384384
:error -> {[], descr}
385-
{dynamic, descr} -> {to_quoted(:dynamic, dynamic, opts), descr}
385+
{:term, descr} -> {to_quoted(:dynamic, :term, opts), descr}
386+
{dynamic, descr} -> {to_quoted(:dynamic, difference(dynamic, descr), opts), descr}
386387
end
387388

388389
# Merge empty list and list together if they both exist
@@ -547,6 +548,41 @@ defmodule Module.Types.Descr do
547548
end
548549
end
549550

551+
@doc """
552+
Returns the intersection between two types
553+
only if they are compatible. Otherwise returns `:error`.
554+
555+
This finds the intersection between the arguments and the
556+
domain of a function. It is used to refine dynamic types
557+
as we traverse the program.
558+
"""
559+
def compatible_intersection(left, right) do
560+
{left_dynamic, left_static} =
561+
case left do
562+
:term -> {:term, :term}
563+
_ -> Map.pop(left, :dynamic, left)
564+
end
565+
566+
right_dynamic =
567+
case right do
568+
%{dynamic: dynamic} -> dynamic
569+
_ -> right
570+
end
571+
572+
cond do
573+
empty?(left_static) ->
574+
dynamic = intersection_static(unfold(left_dynamic), unfold(right_dynamic))
575+
if empty?(dynamic), do: :error, else: {:ok, dynamic(dynamic)}
576+
577+
subtype_static?(left_static, right_dynamic) ->
578+
dynamic = intersection_static(unfold(left_dynamic), unfold(right_dynamic))
579+
{:ok, union(dynamic(dynamic), left_static)}
580+
581+
true ->
582+
:error
583+
end
584+
end
585+
550586
@doc """
551587
Optimized version of `not empty?(term(), type)`.
552588
"""

0 commit comments

Comments
 (0)