@@ -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