|
4 | 4 |
|
5 | 5 | defmodule Module.Types do |
6 | 6 | @moduledoc false |
7 | | - alias Module.Types.{Descr, Expr, Pattern, Helpers} |
| 7 | + alias Module.Types.{Apply, Descr, Expr, Helpers, Pattern} |
8 | 8 |
|
9 | 9 | # The mode controls what happens on function application when |
10 | 10 | # there are gradual arguments. Non-gradual arguments always |
@@ -251,17 +251,27 @@ defmodule Module.Types do |
251 | 251 | context -> |
252 | 252 | {_kind, info, mapping} = Map.fetch!(context.local_sigs, fun_arity) |
253 | 253 |
|
254 | | - clauses_indexes = |
255 | | - for type_index <- pending, |
256 | | - not skip_unused_clause?(info, type_index), |
257 | | - {clause_index, ^type_index} <- mapping, |
258 | | - do: clause_index |
| 254 | + if pending != [] do |
| 255 | + {used_indexes, unused_indexes} = |
| 256 | + Enum.reduce(mapping, {[], []}, fn {clause_index, type_index}, |
| 257 | + {used_indexes, unused_indexes} -> |
| 258 | + if type_index in pending and not skip_unused_clause?(info, type_index) do |
| 259 | + {used_indexes, [clause_index | unused_indexes]} |
| 260 | + else |
| 261 | + {[clause_index | used_indexes], unused_indexes} |
| 262 | + end |
| 263 | + end) |
259 | 264 |
|
260 | | - Enum.reduce(clauses_indexes, context, fn clause_index, context -> |
261 | | - {meta, _args, _guards, _body} = Enum.fetch!(clauses, clause_index) |
262 | | - stack = %{stack | function: fun_arity} |
263 | | - Helpers.warn(__MODULE__, {:unused_clause, kind, fun_arity}, meta, stack, context) |
264 | | - end) |
| 265 | + unused_indexes = Enum.uniq(unused_indexes) -- used_indexes |
| 266 | + |
| 267 | + Enum.reduce(unused_indexes, context, fn clause_index, context -> |
| 268 | + {meta, _args, _guards, _body} = Enum.fetch!(clauses, clause_index) |
| 269 | + stack = %{stack | function: fun_arity} |
| 270 | + Helpers.warn(__MODULE__, {:unused_clause, kind, fun_arity}, meta, stack, context) |
| 271 | + end) |
| 272 | + else |
| 273 | + context |
| 274 | + end |
265 | 275 | end |
266 | 276 | end |
267 | 277 |
|
@@ -321,6 +331,70 @@ defmodule Module.Types do |
321 | 331 | stack = stack |> fresh_stack(mode, fun_arity) |> with_file_meta(meta) |
322 | 332 | base_info = {:def, kind, fun, expected} |
323 | 333 |
|
| 334 | + case clauses do |
| 335 | + [{meta, args, [], {:super, _, [_ | _]} = body}] -> |
| 336 | + default_local_handler(meta, args, body, base_info, kind, fun, expected, stack, context) |
| 337 | + |
| 338 | + _ -> |
| 339 | + infer_local_handler(clauses, base_info, kind, fun, expected, stack, context) |
| 340 | + end |
| 341 | + end |
| 342 | + |
| 343 | + defp default_local_handler(meta, args, body, base_info, kind, fun, expected, stack, context) do |
| 344 | + guards = [] |
| 345 | + previous = Pattern.init_previous() |
| 346 | + fresh_context = fresh_context(context) |
| 347 | + info = {base_info, args, guards} |
| 348 | + |
| 349 | + try do |
| 350 | + {trees, _, _, _, head_context} = |
| 351 | + Pattern.of_head(args, guards, expected, previous, info, meta, stack, fresh_context) |
| 352 | + |
| 353 | + # Compute the intersected arrows from the function call |
| 354 | + {:super, meta, call_args} = body |
| 355 | + {_kind, call_fun} = Keyword.fetch!(meta, :super) |
| 356 | + term = Descr.term() |
| 357 | + of_fun = &Expr.of_expr/5 |
| 358 | + |
| 359 | + {arrows, body_context} = |
| 360 | + Apply.local_arrows(call_fun, call_args, term, body, stack, head_context, of_fun) |
| 361 | + |
| 362 | + # For each arrow, compute the default arrow |
| 363 | + {_, mapping, inferred} = |
| 364 | + Enum.reduce(arrows, {0, [], []}, fn |
| 365 | + {clause_domain, return_type}, {index, mapping, inferred} -> |
| 366 | + of_fun = &Expr.of_expr(&1, &2, body, stack, &3) |
| 367 | + |
| 368 | + {_clause_args, clause_context} = |
| 369 | + Helpers.zip_map_reduce(call_args, clause_domain, head_context, of_fun) |
| 370 | + |
| 371 | + clause_types = Pattern.of_domain(trees, stack, clause_context) |
| 372 | + |
| 373 | + {_type_index, inferred} = |
| 374 | + add_inferred(inferred, clause_types, return_type, index - 1, []) |
| 375 | + |
| 376 | + {index + 1, [{0, index} | mapping], inferred} |
| 377 | + end) |
| 378 | + |
| 379 | + domain = |
| 380 | + case inferred do |
| 381 | + [_] -> |
| 382 | + nil |
| 383 | + |
| 384 | + _ -> |
| 385 | + inferred |
| 386 | + |> Enum.map(fn {args, _} -> args end) |
| 387 | + |> Enum.zip_with(fn types -> Enum.reduce(types, &Descr.union/2) end) |
| 388 | + end |
| 389 | + |
| 390 | + {{:infer, domain, Enum.reverse(inferred)}, mapping, restore_context(body_context, context)} |
| 391 | + rescue |
| 392 | + e -> |
| 393 | + internal_error!(e, __STACKTRACE__, kind, meta, fun, args, guards, body, stack) |
| 394 | + end |
| 395 | + end |
| 396 | + |
| 397 | + defp infer_local_handler(clauses, base_info, kind, fun, expected, stack, context) do |
324 | 398 | {_, _, _, domain, mapping, clauses_types, clauses_context} = |
325 | 399 | Enum.reduce(clauses, {0, 0, Pattern.init_previous(), [], [], [], context}, fn |
326 | 400 | {meta, args, guards, body}, |
|
0 commit comments