In previous versions, when trying to compile a file and the file
depended on a module that was not yet compiled or was unavailable,
the parallel compiler would wait until the missing dependency shows
up. In case it did not, the compiler would "release" the file being
compiled, disabling any further missing dependency resolution.
This lead to issues in code like the following:
Code.ensure_compiled(SomeModuleThatWontBeAvailable)
%SomeStructFromMyProject{}
Because `SomeModuleThatWontBeAvailable` would never be available,
the file above would block until released. When released, the
parallel compiler would disable further lookups for that file,
meaning we would no longer wait for missing dependencies when we
attempt to load the struct.
In the majorify of cases, the struct would already be available,
which meant compilation would work. But if the file that defined
the struct was also blocked by the compiler (and consequently
released), compilation would be non-deterministic.
This patch solves the issue by removing the "release" behaviour
from the compiler. If a dependency is not available, we will
notify the file of such and move on, with the parallel compiler
still enabled. In order to guarantee this works without
introducing deadlocks, the parallel compiler now also knows which
module is being defined by a given file, building a graph and
solving dependencies from the leaves. In case it is a cyclic
graph, we will raise that a deadlock was found.
Closes#4294Closes#4055
Unreachable private functions are excluded during
compilation. As a result, if such a function had a `@spec`,
it would cause a `CompileError`. This commit fixes that by
also excluding any specs for unreachable private functions.
Closes#3144
Prior to this patch, the parallel compiler could leave spurious
processes and the module definition could leave stale data
in the elixir_modules table.
The issue has been fixed by emitting the proper exit signals
and by using monitors.
* We have created a main named table that keeps all module information
* We have reduced the amount of tables by storing internal, docs and
attributes altogether and using table looks for retriving the relevant
information
* At this moment, creating a module in Elixir defines three tables,
one for data, another for functions and another for clauses
* Detect undefined errors for compiling modules
* Log undefined errors in Task's like other OTP processes
* Normalize undefined errors from OTP processes
If we are compiling the module, we will load it with the
output of its beam file. If the module exists only in
memory, :code.which/1 will return `:in_memory`.
Closes#2533