Skip to content

Commit d961fef

Browse files
committed
Store inferred expression types on byte code
1 parent 4ce7c06 commit d961fef

File tree

6 files changed

+207
-92
lines changed

6 files changed

+207
-92
lines changed

lib/elixir/lib/module/types.ex

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -281,12 +281,19 @@ defmodule Module.Types do
281281
context = fresh_context(context)
282282

283283
try do
284-
{args_types, context} =
284+
{trees, context} =
285285
Pattern.of_head(args, guards, expected, {:infer, expected}, meta, stack, context)
286286

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

290+
args_types =
291+
if stack.mode == :traversal do
292+
expected
293+
else
294+
Pattern.of_domain(trees, expected, context)
295+
end
296+
290297
{type_index, inferred} =
291298
add_inferred(inferred, args_types, return_type, total - 1, [])
292299

@@ -302,7 +309,7 @@ defmodule Module.Types do
302309
end)
303310

304311
inferred = {:infer, Enum.reverse(clauses_types)}
305-
{inferred, mapping, restore_context(context, clauses_context)}
312+
{inferred, mapping, restore_context(clauses_context, context)}
306313
end
307314

308315
# We check for term equality of types as an optimization
@@ -399,7 +406,7 @@ defmodule Module.Types do
399406
%{context | vars: %{}, failed: false}
400407
end
401408

402-
defp restore_context(%{vars: vars, failed: failed}, later_context) do
409+
defp restore_context(later_context, %{vars: vars, failed: failed}) do
403410
%{later_context | vars: vars, failed: failed}
404411
end
405412

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

Lines changed: 77 additions & 63 deletions
Original file line numberDiff line numberDiff line change
@@ -139,10 +139,13 @@ defmodule Module.Types.Expr do
139139
{right_type, context} = of_expr(right_expr, @expected_expr, stack, context)
140140

141141
# We do not raise on underscore in case someone writes _ = raise "omg"
142-
case left_expr do
143-
{:_, _, ctx} when is_atom(ctx) -> {right_type, context}
144-
_ -> Pattern.of_match(left_expr, right_type, expr, {:match, right_type}, stack, context)
145-
end
142+
context =
143+
case left_expr do
144+
{:_, _, ctx} when is_atom(ctx) -> context
145+
_ -> Pattern.of_match(left_expr, right_type, expr, {:match, right_type}, stack, context)
146+
end
147+
148+
{right_type, context}
146149
end
147150

148151
# %{map | ...}
@@ -244,9 +247,9 @@ defmodule Module.Types.Expr do
244247
of_expr(post, expected_expr, stack, context)
245248
end
246249

247-
def of_expr({:cond, _meta, [[{:do, clauses}]]}, expected_expr, stack, context) do
250+
def of_expr({:cond, _meta, [[{:do, clauses}]]}, expected_expr, stack, original) do
248251
clauses
249-
|> reduce_non_empty({none(), context}, fn
252+
|> reduce_non_empty({none(), original}, fn
250253
{:->, meta, [[head], body]}, {acc, context}, last? ->
251254
{head_type, context} = of_expr(head, @term_expected, stack, context)
252255

@@ -269,7 +272,7 @@ defmodule Module.Types.Expr do
269272
end
270273

271274
{body_type, context} = of_expr(body, expected_expr, stack, context)
272-
{union(body_type, acc), context}
275+
{union(body_type, acc), reset_vars(context, original)}
273276
end)
274277
|> dynamic_unless_static(stack)
275278
end
@@ -301,53 +304,64 @@ defmodule Module.Types.Expr do
301304
end
302305

303306
# TODO: here
304-
def of_expr({:try, _meta, [[do: body] ++ blocks]}, _expected_expr, stack, context) do
305-
{body_type, context} = of_expr(body, @expected_expr, stack, context)
306-
initial = if Keyword.has_key?(blocks, :else), do: none(), else: body_type
307-
308-
blocks
309-
|> Enum.reduce({initial, context}, fn
310-
{:rescue, clauses}, acc_context ->
311-
Enum.reduce(clauses, acc_context, fn
312-
{:->, _, [[{:in, meta, [var, exceptions]} = expr], body]}, {acc, context} ->
313-
{type, context} = of_rescue(var, exceptions, body, expr, [], meta, stack, context)
314-
{union(type, acc), context}
315-
316-
{:->, meta, [[var], body]}, {acc, context} ->
317-
hint = [:anonymous_rescue]
318-
{type, context} = of_rescue(var, [], body, var, hint, meta, stack, context)
319-
{union(type, acc), context}
320-
end)
307+
def of_expr({:try, _meta, [[do: body] ++ blocks]}, _expected_expr, stack, original) do
308+
{type, context} = of_expr(body, @expected_expr, stack, original)
309+
{after_block, blocks} = Keyword.pop(blocks, :after)
310+
{else_block, blocks} = Keyword.pop(blocks, :else)
311+
312+
{type, context} =
313+
if else_block do
314+
of_clauses(else_block, [type], {:try_else, type}, stack, {none(), context})
315+
else
316+
{type, context}
317+
end
321318

322-
{:after, body}, {acc, context} ->
323-
{_type, context} = of_expr(body, @expected_expr, stack, context)
324-
{acc, context}
319+
{type, context} =
320+
blocks
321+
|> Enum.reduce({type, reset_vars(context, original)}, fn
322+
{:rescue, clauses}, acc_context ->
323+
Enum.reduce(clauses, acc_context, fn
324+
{:->, _, [[{:in, meta, [var, exceptions]} = expr], body]}, {acc, context} ->
325+
{type, context} = of_rescue(var, exceptions, body, expr, [], meta, stack, context)
326+
{union(type, acc), context}
327+
328+
{:->, meta, [[var], body]}, {acc, context} ->
329+
hint = [:anonymous_rescue]
330+
{type, context} = of_rescue(var, [], body, var, hint, meta, stack, context)
331+
{union(type, acc), context}
332+
end)
325333

326-
{:catch, clauses}, acc_context ->
327-
of_clauses(clauses, [@try_catch, dynamic()], :try_catch, stack, acc_context)
334+
{:catch, clauses}, {acc, context} ->
335+
of_clauses(clauses, [@try_catch, dynamic()], :try_catch, stack, {acc, context})
336+
end)
337+
|> dynamic_unless_static(stack)
328338

329-
{:else, clauses}, acc_context ->
330-
of_clauses(clauses, [body_type], {:try_else, body_type}, stack, acc_context)
331-
end)
332-
|> dynamic_unless_static(stack)
339+
if after_block do
340+
{_type, context} = of_expr(after_block, @expected_expr, stack, context)
341+
{type, context}
342+
else
343+
{type, context}
344+
end
333345
end
334346

347+
@timeout_type union(integer(), atom([:infinity]))
348+
335349
# TODO: here
336-
def of_expr({:receive, _meta, [blocks]}, expected_expr, stack, context) do
350+
def of_expr({:receive, _meta, [blocks]}, expected_expr, stack, original) do
337351
blocks
338-
|> Enum.reduce({none(), context}, fn
352+
|> Enum.reduce({none(), original}, fn
339353
{:do, {:__block__, _, []}}, acc_context ->
340354
acc_context
341355

342356
{:do, clauses}, acc_context ->
343357
of_clauses(clauses, [dynamic()], :receive, stack, acc_context)
344358

345359
{:after, [{:->, meta, [[timeout], body]}] = after_expr}, {acc, context} ->
346-
{timeout_type, context} = of_expr(timeout, {integer(), after_expr}, stack, context)
360+
{timeout_type, context} = of_expr(timeout, {@timeout_type, after_expr}, stack, context)
347361
{body_type, context} = of_expr(body, expected_expr, stack, context)
348362

349-
if integer_type?(timeout_type) do
350-
{union(body_type, acc), context}
363+
if compatible?(timeout_type, @timeout_type) do
364+
{union(body_type, acc), reset_vars(context, original)}
351365
else
352366
error = {:badtimeout, timeout_type, timeout, context}
353367
{union(body_type, acc), error(__MODULE__, error, meta, stack, context)}
@@ -391,10 +405,10 @@ defmodule Module.Types.Expr do
391405

392406
# TODO: with pat <- expr do expr end
393407
# TODO: here
394-
def of_expr({:with, _meta, [_ | _] = clauses}, _expected_expr, stack, context) do
408+
def of_expr({:with, _meta, [_ | _] = clauses}, _expected_expr, stack, original) do
395409
{clauses, [options]} = Enum.split(clauses, -1)
396-
context = Enum.reduce(clauses, context, &with_clause(&1, stack, &2))
397-
context = Enum.reduce(options, context, &with_option(&1, stack, &2))
410+
context = Enum.reduce(clauses, original, &with_clause(&1, stack, &2))
411+
context = Enum.reduce(options, context, &with_option(&1, stack, &2, original))
398412
{dynamic(), context}
399413
end
400414

@@ -527,11 +541,11 @@ defmodule Module.Types.Expr do
527541

528542
## Try
529543

530-
defp of_rescue(var, exceptions, body, expr, hints, meta, stack, context) do
544+
defp of_rescue(var, exceptions, body, expr, hints, meta, stack, original) do
531545
args = [__exception__: @atom_true]
532546

533547
{structs, context} =
534-
Enum.map_reduce(exceptions, context, fn exception, context ->
548+
Enum.map_reduce(exceptions, original, fn exception, context ->
535549
# Exceptions are not validated in the compiler,
536550
# to avoid export dependencies. So we do it here.
537551
if Code.ensure_loaded?(exception) and function_exported?(exception, :__struct__, 0) do
@@ -557,7 +571,8 @@ defmodule Module.Types.Expr do
557571
context
558572
end
559573

560-
of_expr(body, @expected_expr, stack, context)
574+
{type, context} = of_expr(body, @expected_expr, stack, context)
575+
{type, reset_vars(context, original)}
561576
end
562577

563578
## Comprehensions
@@ -567,8 +582,7 @@ defmodule Module.Types.Expr do
567582
{pattern, guards} = extract_head([left])
568583
{type, context} = of_expr(right, @expected_expr, stack, context)
569584

570-
{_type, context} =
571-
Pattern.of_match(pattern, guards, dynamic(), expr, :for, stack, context)
585+
context = Pattern.of_match(pattern, guards, dynamic(), expr, :for, stack, context)
572586

573587
{_type, context} =
574588
Apply.remote(Enumerable, :count, [right], [type], expr, stack, context)
@@ -579,8 +593,7 @@ defmodule Module.Types.Expr do
579593
defp for_clause({:<<>>, _, [{:<-, meta, [left, right]}]} = expr, stack, context) do
580594
{right_type, context} = of_expr(right, {binary(), expr}, stack, context)
581595

582-
{_pattern_type, context} =
583-
Pattern.of_match(left, binary(), expr, :for, stack, context)
596+
context = Pattern.of_match(left, binary(), expr, :for, stack, context)
584597

585598
if binary_type?(right_type) do
586599
context
@@ -630,7 +643,7 @@ defmodule Module.Types.Expr do
630643

631644
defp with_clause({:<-, _meta, [left, right]} = expr, stack, context) do
632645
{pattern, guards} = extract_head([left])
633-
{_type, context} = Pattern.of_match(pattern, guards, dynamic(), expr, :with, stack, context)
646+
context = Pattern.of_match(pattern, guards, dynamic(), expr, :with, stack, context)
634647
{_, context} = of_expr(right, @expected_expr, stack, context)
635648
context
636649
end
@@ -640,12 +653,12 @@ defmodule Module.Types.Expr do
640653
context
641654
end
642655

643-
defp with_option({:do, body}, stack, context) do
656+
defp with_option({:do, body}, stack, context, original) do
644657
{_type, context} = of_expr(body, @expected_expr, stack, context)
645-
context
658+
reset_vars(context, original)
646659
end
647660

648-
defp with_option({:else, clauses}, stack, context) do
661+
defp with_option({:else, clauses}, stack, context, _original) do
649662
{_, context} = of_clauses(clauses, [dynamic()], :with_else, stack, {none(), context})
650663
context
651664
end
@@ -678,15 +691,17 @@ defmodule Module.Types.Expr do
678691
defp dynamic_unless_static({_, _} = output, %{mode: :static}), do: output
679692
defp dynamic_unless_static({type, context}, %{mode: _}), do: {dynamic(type), context}
680693

681-
defp of_clauses(clauses, expected, info, %{mode: mode} = stack, {acc, context}) do
682-
%{failed: failed?, vars: vars} = context
694+
defp of_clauses(clauses, expected, info, %{mode: mode} = stack, {acc, original}) do
695+
%{failed: failed?} = original
683696

684-
Enum.reduce(clauses, {acc, context}, fn {:->, meta, [head, body]}, {acc, context} ->
685-
{failed?, context} = reset_context(context, vars, failed?)
697+
Enum.reduce(clauses, {acc, original}, fn {:->, meta, [head, body]}, {acc, context} ->
698+
{failed?, context} = reset_failed(context, failed?)
686699
{patterns, guards} = extract_head(head)
687-
{_types, context} = Pattern.of_head(patterns, guards, expected, info, meta, stack, context)
700+
701+
{_trees, context} = Pattern.of_head(patterns, guards, expected, info, meta, stack, context)
702+
688703
{body, context} = of_expr(body, @expected_expr, stack, context)
689-
context = set_failed(context, failed?)
704+
context = context |> set_failed(failed?) |> reset_vars(original)
690705

691706
if mode == :traversal do
692707
{dynamic(), context}
@@ -696,15 +711,14 @@ defmodule Module.Types.Expr do
696711
end)
697712
end
698713

699-
defp reset_context(%{failed: true} = context, vars, false),
700-
do: {true, %{context | failed: false, vars: vars}}
701-
702-
defp reset_context(context, vars, _),
703-
do: {false, %{context | vars: vars}}
714+
defp reset_failed(%{failed: true} = context, false), do: {true, %{context | failed: false}}
715+
defp reset_failed(context, _), do: {false, context}
704716

705717
defp set_failed(%{failed: false} = context, true), do: %{context | failed: true}
706718
defp set_failed(context, _bool), do: context
707719

720+
defp reset_vars(context, %{vars: vars}), do: %{context | vars: vars}
721+
708722
defp extract_head([{:when, _meta, args}]) do
709723
case Enum.split(args, -1) do
710724
{patterns, [guards]} -> {patterns, flatten_when(guards)}

0 commit comments

Comments
 (0)