@@ -1028,50 +1028,126 @@ defmodule Module.Types.Descr do
10281028
10291029 defp fun_apply_with_strategy ( fun_static , fun_dynamic , arguments ) do
10301030 args_dynamic? = are_arguments_dynamic? ( arguments )
1031+ arity = length ( arguments )
10311032
10321033 # For non-dynamic function and arguments, just return the static result
10331034 if fun_dynamic == nil and not args_dynamic? do
1034- fun_apply_static ( fun_static , arguments , :static , args_dynamic? )
1035- else
1036- # For dynamic cases, combine static and dynamic results
1037- { static_args , dynamic_args , maybe_empty? } =
1038- if args_dynamic? do
1039- { materialize_arguments ( arguments , :up ) , materialize_arguments ( arguments , :down ) , true }
1035+ with { :ok , static_domain , static_arrows } <- fun_normalize ( fun_static , arity , :static ) do
1036+ if subtype? ( domain_descr ( arguments ) , static_domain ) do
1037+ { :ok , fun_apply_static ( arguments , static_arrows , false ) }
10401038 else
1041- { arguments , arguments , false }
1039+ :badarg
10421040 end
1041+ end
1042+ else
1043+ normalized =
1044+ case fun_normalize ( fun_static , arity , :static ) do
1045+ { :ok , static_domain , static_arrows } when fun_dynamic == nil ->
1046+ # TODO: Should dynamic_arrows be [] or static_arrows
1047+ { :ok , static_domain , static_arrows , static_arrows }
1048+
1049+ { :ok , static_domain , static_arrows } when fun_dynamic != nil ->
1050+ case fun_normalize ( fun_dynamic , arity , :dynamic ) do
1051+ { :ok , dynamic_domain , dynamic_arrows } ->
1052+ domain = union ( dynamic_domain , dynamic ( union ( static_domain , dynamic_domain ) ) )
1053+ { :ok , domain , static_arrows , dynamic_arrows }
10431054
1044- case fun_apply_static ( fun_static , static_args , :static , false ) do
1045- { :ok , res1 } when fun_dynamic == nil ->
1046- # We need to compute which parts are dynamic
1047- with { :ok , res2 } <- fun_apply_static ( fun_static , dynamic_args , :static , maybe_empty? ) do
1048- { :ok , union ( res1 , dynamic ( res2 ) ) }
1049- end
1055+ _ ->
1056+ # TODO: Should dynamic_arrows be [] or static_arrows
1057+ { :ok , static_domain , static_arrows , static_arrows }
1058+ end
10501059
1051- { :ok , res1 } when fun_dynamic != nil ->
1052- # If static succeeded, the dynamic part can fail, we don't care
1053- case fun_apply_static ( fun_dynamic , dynamic_args , :dynamic , maybe_empty? ) do
1054- { :ok , res2 } -> { :ok , union ( res1 , dynamic ( res2 ) ) }
1055- _ -> { :ok , res1 }
1056- end
1060+ :badfun ->
1061+ case fun_normalize ( fun_dynamic , arity , :dynamic ) do
1062+ { :ok , dynamic_domain , dynamic_arrows } ->
1063+ # TODO: Should static_arrows be [] or dynamic_arrows
1064+ { :ok , dynamic_domain , [ ] , dynamic_arrows }
10571065
1058- :badfun ->
1059- # Then the dynamic call has to succeed
1060- result =
1061- if fun_dynamic do
1062- fun_apply_static ( fun_dynamic , dynamic_args , :dynamic , maybe_empty? )
1063- else
1064- fun_apply_static ( fun_static , dynamic_args , :static , maybe_empty? )
1066+ error ->
1067+ error
10651068 end
10661069
1067- with { :ok , descr } <- result do
1068- { :ok , dynamic ( descr ) }
1069- end
1070+ error ->
1071+ error
1072+ end
10701073
1071- # badarg/badarity
1072- error ->
1073- error
1074+ with { :ok , domain , static_arrows , dynamic_arrows } <- normalized do
1075+ if subtype? ( domain_descr ( arguments ) , domain ) do
1076+ # For dynamic cases, combine static and dynamic results
1077+ { static_args , dynamic_args , maybe_empty? } =
1078+ if args_dynamic? do
1079+ { materialize_arguments ( arguments , :up ) , materialize_arguments ( arguments , :down ) ,
1080+ true }
1081+ else
1082+ { arguments , arguments , false }
1083+ end
1084+
1085+ { :ok ,
1086+ union (
1087+ fun_apply_static ( static_args , static_arrows , false ) ,
1088+ dynamic ( fun_apply_static ( dynamic_args , dynamic_arrows , maybe_empty? ) )
1089+ ) }
1090+ else
1091+ :badarg
1092+ end
10741093 end
1094+
1095+ # case fun_normalize(fun_static, arity, :static) do
1096+ # {:ok, static_domain, static_arrow} when fun_dynamic == nil ->
1097+ # with {:ok, res1} <-
1098+ # new_fun_apply_static(static_domain, static_arrows, static_args, false),
1099+ # {:ok, res2} <-
1100+ # new_fun_apply_static(static_domain, static_arrows, dynamic_args, maybe_empty?) do
1101+ # {:ok, union(res1, dynamic(res2))}
1102+ # end
1103+
1104+ # {:ok, static_domain, static_arrows} when fun_dynamic != nil ->
1105+ # case fun_normalize(fun_dynamic, arity, :dynamic) do
1106+ # {:ok, dynamic_domain, dynamic_arrows} ->
1107+ # with {:ok, res1} <-
1108+ # checked_fun_apply_static(
1109+ # union(dynamic_domain, static_domain),
1110+ # static_domain,
1111+ # static_arrows,
1112+ # static_args,
1113+ # false
1114+ # ) do
1115+ # case checked_fun_apply_static(
1116+ # dynamic_domain,
1117+ # dynamic_domain,
1118+ # dynamic_arrows,
1119+ # dynamic_args,
1120+ # maybe_empty?
1121+ # ) do
1122+ # {:ok, res2} ->
1123+ # {:ok, union(res1, dynamic(res2))}
1124+
1125+ # _ ->
1126+ # {:ok, res1}
1127+ # end
1128+ # end
1129+
1130+ # _ ->
1131+ # new_fun_apply_static(static_domain, static_arrows, static_args, false)
1132+ # end
1133+
1134+ # :badfun ->
1135+ # # Then the dynamic call has to succeed
1136+ # result =
1137+ # if fun_dynamic do
1138+ # fun_apply_static(fun_dynamic, dynamic_args, :dynamic, maybe_empty?)
1139+ # else
1140+ # fun_apply_static(fun_static, dynamic_args, :static, maybe_empty?)
1141+ # end
1142+
1143+ # with {:ok, descr} <- result do
1144+ # {:ok, dynamic(descr)}
1145+ # end
1146+
1147+ # # badarg/badarity
1148+ # error ->
1149+ # error
1150+ # end
10751151 end
10761152 end
10771153
@@ -1081,46 +1157,76 @@ defmodule Module.Types.Descr do
10811157
10821158 defp are_arguments_dynamic? ( arguments ) , do: Enum . any? ( arguments , & match? ( % { dynamic: _ } , & 1 ) )
10831159
1084- defp fun_apply_static ( % { fun: fun_bdd } , arguments , mode , maybe_empty? ) do
1085- arity = length ( arguments )
1086-
1087- with { :ok , domain , arrows } <- fun_normalize ( fun_bdd , arity , mode ) do
1088- type_args = domain_descr ( arguments )
1160+ defp fun_apply_static ( arguments , arrows , maybe_empty? ) do
1161+ type_args = domain_descr ( arguments )
10891162
1090- cond do
1091- # Optization: short-circuits when inner loop is none() or outer loop is term()
1092- maybe_empty? and empty? ( type_args ) ->
1093- result =
1094- Enum . reduce_while ( arrows , none ( ) , fn intersection_of_arrows , acc ->
1095- Enum . reduce_while ( intersection_of_arrows , term ( ) , fn
1096- { _dom , _ret } , acc when acc == @ none -> { :halt , acc }
1097- { _dom , ret } , acc -> { :cont , intersection ( acc , ret ) }
1098- end )
1099- |> case do
1100- :term -> { :halt , :term }
1101- inner -> { :cont , union ( inner , acc ) }
1102- end
1103- end )
1104-
1105- { :ok , result }
1106-
1107- subtype? ( type_args , domain ) ->
1108- result =
1109- Enum . reduce ( arrows , none ( ) , fn intersection_of_arrows , acc ->
1110- aux_apply ( acc , type_args , term ( ) , intersection_of_arrows )
1111- end )
1112-
1113- { :ok , result }
1114-
1115- true ->
1116- :badarg
1117- end
1163+ # Optimization: short-circuits when inner loop is none() or outer loop is term()
1164+ if maybe_empty? and empty? ( type_args ) do
1165+ Enum . reduce_while ( arrows , none ( ) , fn intersection_of_arrows , acc ->
1166+ Enum . reduce_while ( intersection_of_arrows , term ( ) , fn
1167+ { _dom , _ret } , acc when acc == @ none -> { :halt , acc }
1168+ { _dom , ret } , acc -> { :cont , intersection ( acc , ret ) }
1169+ end )
1170+ |> case do
1171+ :term -> { :halt , :term }
1172+ inner -> { :cont , union ( inner , acc ) }
1173+ end
1174+ end )
1175+ else
1176+ Enum . reduce ( arrows , none ( ) , fn intersection_of_arrows , acc ->
1177+ aux_apply ( acc , type_args , term ( ) , intersection_of_arrows )
1178+ end )
11181179 end
11191180 end
11201181
1121- defp fun_apply_static ( % { } , _arguments , _mode , _maybe_empty? ) do
1122- :badfun
1123- end
1182+ # defp fun_apply_static(descr, arguments, mode, maybe_empty?) do
1183+ # arity = length(arguments)
1184+
1185+ # with {:ok, domain, arrows} <- fun_normalize(descr, arity, mode) do
1186+ # new_fun_apply_static(domain, arrows, arguments, maybe_empty?)
1187+ # end
1188+ # end
1189+
1190+ # defp checked_fun_apply_static(check_domain, domain, arrows, arguments, maybe_empty?) do
1191+ # if subtype?(domain_descr(arguments), check_domain) do
1192+ # new_fun_apply_static(domain, arrows, arguments, maybe_empty?)
1193+ # else
1194+ # :badarg
1195+ # end
1196+ # end
1197+
1198+ # defp new_fun_apply_static(domain, arrows, arguments, maybe_empty?) do
1199+ # type_args = domain_descr(arguments)
1200+
1201+ # cond do
1202+ # # Optimization: short-circuits when inner loop is none() or outer loop is term()
1203+ # maybe_empty? and empty?(type_args) ->
1204+ # result =
1205+ # Enum.reduce_while(arrows, none(), fn intersection_of_arrows, acc ->
1206+ # Enum.reduce_while(intersection_of_arrows, term(), fn
1207+ # {_dom, _ret}, acc when acc == @none -> {:halt, acc}
1208+ # {_dom, ret}, acc -> {:cont, intersection(acc, ret)}
1209+ # end)
1210+ # |> case do
1211+ # :term -> {:halt, :term}
1212+ # inner -> {:cont, union(inner, acc)}
1213+ # end
1214+ # end)
1215+
1216+ # {:ok, result}
1217+
1218+ # subtype?(type_args, domain) ->
1219+ # result =
1220+ # Enum.reduce(arrows, none(), fn intersection_of_arrows, acc ->
1221+ # aux_apply(acc, type_args, term(), intersection_of_arrows)
1222+ # end)
1223+
1224+ # {:ok, result}
1225+
1226+ # true ->
1227+ # :badarg
1228+ # end
1229+ # end
11241230
11251231 # Helper function for function application that handles the application of
11261232 # function arrows to input types.
@@ -1199,7 +1305,7 @@ defmodule Module.Types.Descr do
11991305 #
12001306 # This function is used internally by `fun_apply`, and others to
12011307 # ensure consistent handling of function types in all operations.
1202- defp fun_normalize ( bdd , arity , mode ) do
1308+ defp fun_normalize ( % { fun: bdd } , arity , mode ) do
12031309 { domain , arrows , bad_arities } =
12041310 Enum . reduce ( fun_get ( bdd ) , { term ( ) , [ ] , [ ] } , fn
12051311 { [ { args , _ } | _ ] = pos_funs , neg_funs } , { domain , arrows , bad_arities } ->
@@ -1235,6 +1341,10 @@ defmodule Module.Types.Descr do
12351341 end
12361342 end
12371343
1344+ defp fun_normalize ( % { } , _arity , _mode ) do
1345+ :badfun
1346+ end
1347+
12381348 # Checks if a function type is empty.
12391349 #
12401350 # A function type is empty if:
0 commit comments