@after_verify hooks are invoked right after the current module is verified for
undefined functions, deprecations, etc. A module is always verified after
it is compiled. In Mix projects, a module is also verified when any of its
runtime dependencies change. Therefore this is useful to perform verification
of the current module while avoiding compile-time dependencies.
Here are some sample use cases:
* Ecto can use this validate associations consistently and effectively
* Phoenix can use this to verify routes
* Surface can use this to verify component attributes
Before this patch, this code would add a compile-time
dependency to Bar and Baz:
defmodule Foo do
@mods [Bar, Baz]
def example(arg) when arg in @mods, do: arg
end
That's because Bar and Baz were read in the module body,
even though ultimately they are only expanded and used
inside a function.
This patch postpones the expansion of aliases until they
are used. So if `@mods` is only used inside functions,
no compile-time deps are added.
Closes#11714.
Previously, the internal Elixir expansion pass worked directly on
Macro.Env. However, this poised an issue, if we want to track
more information in the pass, we ended up exposing it on
Macro.Env, making it larger, and potentially slowing down
operations such as __ENV__ serialization.
This commit refactors the expansion pass to work with two
structures, the Macro.Env struct and a #elixir_ex{} record.
The Elixir compiler spawns a separate process per file.
When a file has to wait on another module, Elixir tracks
in the compiler that the file is waiting.
However, every time a module is defined, the Elixir compiler
spawns a separate process to compile to .beam, and this
process may expand structs in the typespec. Since this
new process is no longer the original file process, Elixir
was not able to track its waiting time.
This PR address this issue by passing the original file_pid
to the .beam compiler process. Note though that, if we
change typespecs to be compiled in the original file process,
this change is no longer required, but at the moment there are
no plans to make such change.
Closes#11036.
Prior to this commit, compilation would copy the module map
(which is the map with the module definitions, attriubutes,
etc) multiple times. In particular, the module execution
process would pass it to the compiler. Then the module
execution process would send it to the parallel compiler
coordinator, which would pass it to the parallel checker
coordinator and then to each individual parallel checker.
That's four copies, which can be quite expensive since it
has the whole Elixir AST.
This commit makes it so it is copied only once. The map
is assembled inside the compiler process and then copied
to the checker process.
This has yielded good improvements in Elixir's test suite.
Before:
Finished in 27.3 seconds (14.4s on load, 7.0s async, 5.8s sync)
After:
Finished in 22.0 seconds (9.4s on load, 6.7s async, 5.8s sync)
Instead of incrementing the version for each variable individually we increment it globally for all variables. This means we can uniquely identify each variable assignment with only the version.
Currently the callbacks on :elixir_compiler are called before the warnings of unused aliases and imports are emitted, causing the messages to not consumed during the compilation. This commit changes `:elixir_lexical.run/2` to `:elixir_lexical.run/3` splitting the callback in two, one during the execution to evaluate the forms and another to be called after all the warnings are emitted.
Fixes#9440