@@ -341,14 +341,30 @@ defmodule Module.Types.Expr do
341341 def of_expr ( { :for , meta , [ _ | _ ] = args } , stack , context ) do
342342 { clauses , [ [ { :do , block } | opts ] ] } = Enum . split ( args , - 1 )
343343 context = Enum . reduce ( clauses , context , & for_clause ( & 1 , stack , & 2 ) )
344- context = Enum . reduce ( opts , context , & for_option ( & 1 , meta , stack , & 2 ) )
345344
345+ # We don't need to type check uniq, as it is a compile-time boolean.
346+ # We handle reduce and into accordingly instead.
346347 if Keyword . has_key? ( opts , :reduce ) do
348+ reduce = Keyword . fetch! ( opts , :reduce )
349+ { _ , context } = of_expr ( reduce , stack , context )
347350 { _ , context } = of_clauses ( block , [ dynamic ( ) ] , :for_reduce , stack , { none ( ) , context } )
348351 { dynamic ( ) , context }
349352 else
350- { _type , context } = of_expr ( block , stack , context )
351- { dynamic ( ) , context }
353+ into = Keyword . get ( opts , :into , [ ] )
354+ { into_wrapper , context } = for_into ( into , meta , stack , context )
355+ { block_type , context } = of_expr ( block , stack , context )
356+
357+ for_type =
358+ for type <- into_wrapper do
359+ case type do
360+ :binary -> binary ( )
361+ :list -> list ( block_type )
362+ :dynamic -> dynamic ( )
363+ end
364+ end
365+ |> Enum . reduce ( & union / 2 )
366+
367+ { for_type , context }
352368 end
353369 end
354370
@@ -504,35 +520,35 @@ defmodule Module.Types.Expr do
504520 context
505521 end
506522
507- defp for_option ( { :into , expr } , _meta , _stack , context ) when is_list ( expr ) or is_binary ( expr ) do
508- context
509- end
510-
511- defp for_option ( { :into , expr } , meta , stack , context ) do
512- { type , context } = of_expr ( expr , stack , context )
523+ @ into_compile union ( binary ( ) , empty_list ( ) )
513524
514- meta =
515- case expr do
516- { _ , meta , _ } -> meta
517- _ -> meta
518- end
525+ defp for_into ( [ ] , _meta , _stack , context ) ,
526+ do: { [ :list ] , context }
519527
520- wrapped_expr = { :__block__ , [ type_check: :into ] ++ meta , [ expr ] }
521-
522- { _type , context } =
523- Apply . remote ( Collectable , :into , [ expr ] , [ type ] , wrapped_expr , stack , context )
528+ defp for_into ( binary , _meta , _stack , context ) when is_binary ( binary ) ,
529+ do: { [ :binary ] , context }
524530
525- context
526- end
531+ # TODO: Use the collectable protocol for the output
532+ defp for_into ( into , meta , stack , context ) do
533+ { type , context } = of_expr ( into , stack , context )
527534
528- defp for_option ( { :reduce , expr } , _meta , stack , context ) do
529- { _type , context } = of_expr ( expr , stack , context )
530- context
531- end
535+ if subtype? ( type , @ into_compile ) do
536+ case { binary_type? ( type ) , empty_list_type? ( type ) } do
537+ { false , true } -> { [ :list ] , context }
538+ { true , false } -> { [ :binary ] , context }
539+ { _ , _ } -> { [ :binary , :list ] , context }
540+ end
541+ else
542+ meta =
543+ case into do
544+ { _ , meta , _ } -> meta
545+ _ -> meta
546+ end
532547
533- defp for_option ( { :uniq , _ } , _meta , _stack , context ) do
534- # This option is verified to be a boolean at compile-time
535- context
548+ expr = { :__block__ , [ type_check: :into ] ++ meta , [ into ] }
549+ { _type , context } = Apply . remote ( Collectable , :into , [ into ] , [ type ] , expr , stack , context )
550+ { [ :dynamic ] , context }
551+ end
536552 end
537553
538554 ## With
0 commit comments