diff --git a/lib/elixir/lib/code.ex b/lib/elixir/lib/code.ex index 97fec1c9ce..5a8204e1ea 100644 --- a/lib/elixir/lib/code.ex +++ b/lib/elixir/lib/code.ex @@ -1634,13 +1634,19 @@ defmodule Code do nil :proceed -> - loaded = - Module.ParallelChecker.verify(fn -> - :elixir_compiler.string(charlist, file, fn _, _ -> :ok end) - end) + try do + loaded = + Module.ParallelChecker.verify(fn -> + :elixir_compiler.string(charlist, file, fn _, _ -> :ok end) + end) - :elixir_code_server.cast({:required, file}) - loaded + :elixir_code_server.cast({:required, file}) + loaded + catch + kind, reason -> + :elixir_code_server.call({:release, file}) + :erlang.raise(kind, reason, __STACKTRACE__) + end end end diff --git a/lib/elixir/src/elixir_code_server.erl b/lib/elixir/src/elixir_code_server.erl index e2baa51c33..bb563d6e4a 100644 --- a/lib/elixir/src/elixir_code_server.erl +++ b/lib/elixir/src/elixir_code_server.erl @@ -59,6 +59,9 @@ handle_call({acquire, Path}, From, Config) -> handle_call(required, _From, Config) -> {reply, [F || {F, true} <- maps:to_list(Config#elixir_code_server.required)], Config}; +handle_call({release, Path}, _From, Config) -> + {reply, ok, release(Path, Config)}; + handle_call(retrieve_compiler_module, _From, Config) -> case Config#elixir_code_server.mod_pool of {Used, [Mod | Unused], Counter} -> @@ -140,6 +143,20 @@ terminate(_Reason, _Config) -> code_change(_Old, Config, _Extra) -> {ok, Config}. +release(Path, Config) -> + Current = Config#elixir_code_server.required, + case maps:find(Path, Current) of + {ok, []} -> + Released = maps:remove(Path, Current), + Config#elixir_code_server{required=Released}; + {ok, [Next | Waiting]} -> + _ = gen_server:reply(Next, proceed), + Released = maps:put(Path, Waiting, Current), + Config#elixir_code_server{required=Released}; + error -> + Config + end. + compiler_module(I) -> list_to_atom("elixir_compiler_" ++ integer_to_list(I)). diff --git a/lib/elixir/test/elixir/code_test.exs b/lib/elixir/test/elixir/code_test.exs index 18d024511d..036c516401 100644 --- a/lib/elixir/test/elixir/code_test.exs +++ b/lib/elixir/test/elixir/code_test.exs @@ -514,6 +514,25 @@ defmodule CodeTest do Code.unrequire_files([fixture_path("code_sample.exs")]) end + test "require_file/1 releases the file when compilation fails" do + path = tmp_path("bad_require_#{System.unique_integer([:positive])}.ex") + + try do + File.write!(path, ~s|raise "boom"|) + + assert_raise RuntimeError, "boom", fn -> + Code.require_file(path) + end + + assert_raise RuntimeError, "boom", fn -> + Code.require_file(path) + end + after + File.rm(path) + Code.unrequire_files([path]) + end + end + test "string_to_quoted!/2 errors take lines/columns/indentation into account" do assert_exception( SyntaxError,