tag:github.com,2008:https://github.com/elixir-lang/elixir/releasesRelease notes from elixir2026-03-17T08:36:20Ztag:github.com,2008:Repository/1234714/main-latest2026-03-17T08:49:35Zmain-latest<p>Automated release for latest main.</p>github-actions[bot]tag:github.com,2008:Repository/1234714/v1.20.0-rc.32026-03-09T19:31:54Zv1.20.0-rc.3<h3>1. Enhancements</h3>
<h4>IEx</h4>
<ul>
<li>[IEx] Optimize autocompleting modules</li>
</ul>
<h3>2. Bug fixes</h3>
<h4>Elixir</h4>
<ul>
<li>[Enum] Fix <code>Enum.slice/2</code> for ranges with step > 1 sliced by step > 1</li>
<li>[File] Preserve directory permissions in <code>File.cp_r/3</code></li>
<li>[File] Fix <code>File.cp_r/3</code> infinite loop with symlink cycles</li>
<li>[File] Fix <code>File.cp_r/3</code> infinite loop when copying into subdirectory of source</li>
<li>[File] Warn when defining <code>@type record()</code>, fixes CI on Erlang/OTP 29</li>
<li>[File] Fix <code>File.Stream</code> <code>Enumerable.count</code> for files without trailing newline</li>
<li>[Float] Fix <code>Float.parse/1</code> inconsistent error handling for non-scientific notation overflow</li>
<li>[Kernel] Process fields even when structs are unknown (regression)</li>
<li>[Kernel] Improve performance on several corner cases in the type system (regression)</li>
<li>[Kernel] Fix regression when using <code>Kernel.in/2</code> in defguard (regression)</li>
</ul>github-actions[bot]tag:github.com,2008:Repository/1234714/v1.20.0-rc.22026-03-04T15:44:41Zv1.20.0-rc.2<p>Overall, the compiler finds more bugs, for free, and it has never been faster:</p>
<ul>
<li>
<p>Infers types across clauses, finding more bugs and dead code</p>
</li>
<li>
<p>Compiles ~10% faster and <a href="https://github.com/elixir-lang/elixir/pull/15087" data-hovercard-type="pull_request" data-hovercard-url="/elixir-lang/elixir/pull/15087/hovercard">has a new interpreted mode</a> (up to 5x faster, scales to the number of cores). For more information, <a href="https://github.com/josevalim/langcompilebench">follow the benchmarks</a></p>
</li>
<li>
<p>Modifying a struct definition recompiles fewer files (it no longer requires files that only pattern match or update structs to recompile)</p>
</li>
</ul>
<h3>1. Enhancements</h3>
<h4>Elixir</h4>
<ul>
<li>[Code] Add <code>module_definition: :interpreted</code> option to <code>Code</code> which allows module definitions to be evaluated instead of compiled. In some applications/architectures, this can lead to drastic improvements to compilation times. Note this does not affect the generated <code>.beam</code> file, which will have the same performance/behaviour as before</li>
<li>[Code] Make module purging opt-in and move temporary module deletion to the background to speed up compilation times</li>
<li>[Integer] Add <code>Integer.popcount/1</code></li>
<li>[Kernel] Move struct validation in patterns and updates to type checker, this means adding and remove struct fields will cause fewer files to be recompiled</li>
<li>[Kernel] Add type inference across clauses. For example, if one clause says <code>x when is_integer(x)</code>, then the next clause may no longer be an integer</li>
<li>[Kernel] Detect and warn on redundant clauses</li>
<li>[List] Add <code>List.first!/1</code> and <code>List.last!/1</code></li>
<li>Add Software Bill of Materials guide to the Documentation</li>
</ul>
<h4>Mix</h4>
<ul>
<li>[mix compile] Add <code>module_definition: :interpreted</code> option to <code>Code</code> which allows module definitions to be evaluated instead of compiled. In some applications/architectures, this can lead to drastic improvements to compilation times. Note this does not affect the generated <code>.beam</code> file, which will have the same performance/behaviour as before</li>
<li>[mix deps] Parallelize dep lock status checks during <code>deps.loadpaths</code>, improving boot times in projects with many git dependencies</li>
</ul>
<h3>2. Potential breaking changes</h3>
<h4>Elixir</h4>
<ul>
<li><code>map.foo()</code> (accessing a map field with parens) and <code>mod.foo</code> (invoking a function without parens) will now raise instead of emitting runtime warnings, aligning themselves with the type system behaviour</li>
</ul>
<h3>3. Bug fixes</h3>
<h4>IEx</h4>
<ul>
<li>[IEx] Ensure warnings emitted during IEx parsing are properly displayed/printed</li>
<li>[IEx] Ensure pry works across remote nodes</li>
</ul>
<h4>Mix</h4>
<ul>
<li>[mix compile.erlang] Topsort Erlang modules before compilation for proper dependency resolution</li>
</ul>github-actions[bot]tag:github.com,2008:Repository/1234714/v1.20.0-rc.12026-01-13T11:42:53Zv1.20.0-rc.1<h3>1. Bug fixes</h3>
<h4>Elixir</h4>
<ul>
<li>[Kernel] Improve the performance of the type system when working with large unions of open maps</li>
<li>[Kernel] Do not crash on map types with struct keys when performing type operations</li>
<li>[Kernel] Mark the outcome of bitstring types as dynamic</li>
<li>[Kernel] <code><<expr::bitstring>></code> will have type <code>binary</code> instead of <code>bitstring</code> if <code>expr</code> is a binary</li>
<li>[Kernel] Do not crash on conditional variables when calling a function on a module which is represented by a variable</li>
</ul>github-actions[bot]tag:github.com,2008:Repository/1234714/v1.20.0-rc.02026-01-09T19:11:05Zv1.20.0-rc.0<h2>Type system improvements</h2>
<p>This release includes type inference of all constructs.</p>
<h3>Type inference of function calls</h3>
<p>Elixir now performs inference of whole functions. The best way to show the new capabilities are with examples. Take the following code:</p>
<div class="highlight highlight-source-elixir notranslate position-relative overflow-auto" data-snippet-clipboard-copy-content="def add_foo_and_bar(data) do
data.foo + data.bar
end"><pre><span class="pl-k">def</span> <span class="pl-en">add_foo_and_bar</span><span class="pl-kos">(</span><span class="pl-s1">data</span><span class="pl-kos">)</span> <span class="pl-k">do</span>
<span class="pl-s1">data</span><span class="pl-c1">.</span><span class="pl-c1">foo</span> <span class="pl-c1">+</span> <span class="pl-s1">data</span><span class="pl-c1">.</span><span class="pl-c1">bar</span>
<span class="pl-k">end</span></pre></div>
<p>Elixir now infers that the function expects a <code>map</code> as first argument, and the map must have the keys <code>.foo</code> and <code>.bar</code> whose values are either <code>integer()</code> or <code>float()</code>. The return type will be either <code>integer()</code> or <code>float()</code>.</p>
<p>Here is another example:</p>
<div class="highlight highlight-source-elixir notranslate position-relative overflow-auto" data-snippet-clipboard-copy-content="def sum_to_string(a, b) do
Integer.to_string(a + b)
end"><pre><span class="pl-k">def</span> <span class="pl-en">sum_to_string</span><span class="pl-kos">(</span><span class="pl-s1">a</span><span class="pl-kos">,</span> <span class="pl-s1">b</span><span class="pl-kos">)</span> <span class="pl-k">do</span>
<span class="pl-v">Integer</span><span class="pl-c1">.</span><span class="pl-en">to_string</span><span class="pl-kos">(</span><span class="pl-s1">a</span> <span class="pl-c1">+</span> <span class="pl-s1">b</span><span class="pl-kos">)</span>
<span class="pl-k">end</span></pre></div>
<p>Even though the <code>+</code> operator works with both integers and floats, Elixir infers that <code>a</code> and <code>b</code> must be both integers, as the result of <code>+</code> is given to a function that expects an integer. The inferred type information is then used during type checking to find possible typing errors.</p>
<h3>Type inference of guards</h3>
<p>This release also performs inference of guards! Let's see some examples:</p>
<div class="highlight highlight-source-elixir notranslate position-relative overflow-auto" data-snippet-clipboard-copy-content="def example(x, y) when is_list(x) and is_integer(y)"><pre><span class="pl-k">def</span> <span class="pl-en">example</span><span class="pl-kos">(</span><span class="pl-s1">x</span><span class="pl-kos">,</span> <span class="pl-s1">y</span><span class="pl-kos">)</span> <span class="pl-k">when</span> <span class="pl-en">is_list</span><span class="pl-kos">(</span><span class="pl-s1">x</span><span class="pl-kos">)</span> <span class="pl-k">and</span> <span class="pl-en">is_integer</span><span class="pl-kos">(</span><span class="pl-s1">y</span><span class="pl-kos">)</span></pre></div>
<p>The code above correctly infers <code>x</code> is a list and <code>y</code> is an integer.</p>
<div class="highlight highlight-source-elixir notranslate position-relative overflow-auto" data-snippet-clipboard-copy-content="def example({:ok, x} = y) when is_binary(x) or is_integer(x)"><pre><span class="pl-k">def</span> <span class="pl-en">example</span><span class="pl-kos">(</span><span class="pl-kos">{</span><span class="pl-pds">:ok</span><span class="pl-kos">,</span> <span class="pl-s1">x</span><span class="pl-kos">}</span> <span class="pl-c1">=</span> <span class="pl-s1">y</span><span class="pl-kos">)</span> <span class="pl-k">when</span> <span class="pl-en">is_binary</span><span class="pl-kos">(</span><span class="pl-s1">x</span><span class="pl-kos">)</span> <span class="pl-k">or</span> <span class="pl-en">is_integer</span><span class="pl-kos">(</span><span class="pl-s1">x</span><span class="pl-kos">)</span></pre></div>
<p>The one above infers x is a binary or an integer, and <code>y</code> is a two element tuple with <code>:ok</code> as first element and a binary or integer as second.</p>
<div class="highlight highlight-source-elixir notranslate position-relative overflow-auto" data-snippet-clipboard-copy-content="def example(x) when is_map_key(x, :foo)"><pre><span class="pl-k">def</span> <span class="pl-en">example</span><span class="pl-kos">(</span><span class="pl-s1">x</span><span class="pl-kos">)</span> <span class="pl-k">when</span> <span class="pl-en">is_map_key</span><span class="pl-kos">(</span><span class="pl-s1">x</span><span class="pl-kos">,</span> <span class="pl-pds">:foo</span><span class="pl-kos">)</span></pre></div>
<p>The code above infers <code>x</code> is a map which has the <code>:foo</code> key, represented as <code>%{..., foo: dynamic()}</code>. Remember the leading <code>...</code> indicates the map may have other keys.</p>
<div class="highlight highlight-source-elixir notranslate position-relative overflow-auto" data-snippet-clipboard-copy-content="def example(x) when not is_map_key(x, :foo)"><pre><span class="pl-k">def</span> <span class="pl-en">example</span><span class="pl-kos">(</span><span class="pl-s1">x</span><span class="pl-kos">)</span> <span class="pl-k">when</span> <span class="pl-k">not</span> <span class="pl-en">is_map_key</span><span class="pl-kos">(</span><span class="pl-s1">x</span><span class="pl-kos">,</span> <span class="pl-pds">:foo</span><span class="pl-kos">)</span></pre></div>
<p>And the code above infers <code>x</code> does not have the <code>:foo</code> key (hence <code>x.foo</code> will raise a typing violation), which has the type: <code>%{..., foo: not_set()}</code>.</p>
<p>You can also have expressions that assert on the size of data structures:</p>
<div class="highlight highlight-source-elixir notranslate position-relative overflow-auto" data-snippet-clipboard-copy-content="def example(x) when tuple_size(x) < 3"><pre><span class="pl-k">def</span> <span class="pl-en">example</span><span class="pl-kos">(</span><span class="pl-s1">x</span><span class="pl-kos">)</span> <span class="pl-k">when</span> <span class="pl-en">tuple_size</span><span class="pl-kos">(</span><span class="pl-s1">x</span><span class="pl-kos">)</span> <span class="pl-c1"><</span> <span class="pl-c1">3</span></pre></div>
<p>Elixir will correctly track the tuple has at most two elements, and therefore accessing <code>elem(x, 3)</code> will emit a typing violation. In other words, Elixir can look at complex guards, infer types, and use this information to find bugs in our code, without a need to introduce type signatures (yet).</p>
<h3>Complete typing of maps keys</h3>
<p>Maps were one of the first data-structures we implemented within the Elixir type system however, up to this point, they only supported atom keys. If they had additional keys, those keys were simply marked as <code>dynamic()</code>.</p>
<p>As of Elixir v1.20, we can track all possible domains as map keys. For example, the map:</p>
<div class="highlight highlight-source-elixir notranslate position-relative overflow-auto" data-snippet-clipboard-copy-content="%{123 => "hello", 456.0 => :ok}"><pre><span class="pl-kos">%</span><span class="pl-kos">{</span><span class="pl-c1">123</span> <span class="pl-c1">=></span> <span class="pl-s">"hello"</span><span class="pl-kos">,</span> <span class="pl-c1">456.0</span> <span class="pl-c1">=></span> <span class="pl-pds">:ok</span><span class="pl-kos">}</span></pre></div>
<p>will have the type:</p>
<div class="highlight highlight-source-elixir notranslate position-relative overflow-auto" data-snippet-clipboard-copy-content="%{integer() => binary(), float() => :ok}"><pre><span class="pl-kos">%</span><span class="pl-kos">{</span><span class="pl-en">integer</span><span class="pl-kos">(</span><span class="pl-kos">)</span> <span class="pl-c1">=></span> <span class="pl-en">binary</span><span class="pl-kos">(</span><span class="pl-kos">)</span><span class="pl-kos">,</span> <span class="pl-en">float</span><span class="pl-kos">(</span><span class="pl-kos">)</span> <span class="pl-c1">=></span> <span class="pl-pds">:ok</span><span class="pl-kos">}</span></pre></div>
<p>It is also possible to mix domain keys, as above, with atom keys, yielding the following:</p>
<div class="highlight highlight-source-elixir notranslate position-relative overflow-auto" data-snippet-clipboard-copy-content="%{integer() => integer(), root: integer()}"><pre><span class="pl-kos">%</span><span class="pl-kos">{</span><span class="pl-en">integer</span><span class="pl-kos">(</span><span class="pl-kos">)</span> <span class="pl-c1">=></span> <span class="pl-en">integer</span><span class="pl-kos">(</span><span class="pl-kos">)</span><span class="pl-kos">,</span> <span class="pl-pds">root: </span><span class="pl-en">integer</span><span class="pl-kos">(</span><span class="pl-kos">)</span><span class="pl-kos">}</span></pre></div>
<p>This system is an implementation of <a href="https://www.irif.fr/~gc/papers/icfp23.pdf" rel="nofollow">Typing Records, Maps, and Structs, by Giuseppe Castagna (2023)</a>.</p>
<h3>Typing of map operations</h3>
<p>We have typed the majority of the functions in the <code>Map</code> module, allowing the type system to track how keys are added, updated, and removed across all possible key types.</p>
<p>For example, imagine we are calling the following <code>Map</code> functions with a variable <code>map</code>, which we don't know the exact shape of, and an atom key:</p>
<div class="highlight highlight-source-elixir notranslate position-relative overflow-auto" data-snippet-clipboard-copy-content="Map.put(map, :key, 123)
#=> returns type %{..., key: integer()}
Map.delete(map, :key)
#=> returns type %{..., key: not_set()}"><pre><span class="pl-v">Map</span><span class="pl-c1">.</span><span class="pl-en">put</span><span class="pl-kos">(</span><span class="pl-s1">map</span><span class="pl-kos">,</span> <span class="pl-pds">:key</span><span class="pl-kos">,</span> <span class="pl-c1">123</span><span class="pl-kos">)</span>
<span class="pl-c">#=> returns type %{..., key: integer()}</span>
<span class="pl-v">Map</span><span class="pl-c1">.</span><span class="pl-en">delete</span><span class="pl-kos">(</span><span class="pl-s1">map</span><span class="pl-kos">,</span> <span class="pl-pds">:key</span><span class="pl-kos">)</span>
<span class="pl-c">#=> returns type %{..., key: not_set()}</span></pre></div>
<p>As you can see, we track when keys are set and also when they are removed.</p>
<p>Some operations, like <code>Map.replace/3</code>, only replace the key if it exists, and that is also propagated by the type system:</p>
<div class="highlight highlight-source-elixir notranslate position-relative overflow-auto" data-snippet-clipboard-copy-content="Map.replace(map, :key, 123)
#=> returns type %{..., key: if_set(integer())}"><pre><span class="pl-v">Map</span><span class="pl-c1">.</span><span class="pl-en">replace</span><span class="pl-kos">(</span><span class="pl-s1">map</span><span class="pl-kos">,</span> <span class="pl-pds">:key</span><span class="pl-kos">,</span> <span class="pl-c1">123</span><span class="pl-kos">)</span>
<span class="pl-c">#=> returns type %{..., key: if_set(integer())}</span></pre></div>
<p>In other words, if the key exists, it would have been replaced by an integer value. Furthermore, whenever calling a function in the <code>Map</code> module and the given key is statically proven to never exist in the map, an error is emitted.</p>
<p>By combining full type inference with bang operations like <code>Map.fetch!/2</code>, <code>Map.pop!/2</code>, <code>Map.replace!/3</code>, and <code>Map.update!/3</code>, Elixir is able to propagate information about the desired keys. Take this module:</p>
<div class="highlight highlight-source-elixir notranslate position-relative overflow-auto" data-snippet-clipboard-copy-content="defmodule User do
def name(map), do: Map.fetch!(map, :name)
end
defmodule CallsUser do
def calls_name do
User.name(%{})
end
end"><pre><span class="pl-k">defmodule</span> <span class="pl-v">User</span> <span class="pl-k">do</span>
<span class="pl-k">def</span> <span class="pl-en">name</span><span class="pl-kos">(</span><span class="pl-s1">map</span><span class="pl-kos">)</span><span class="pl-kos">,</span> <span class="pl-pds">do: </span><span class="pl-v">Map</span><span class="pl-c1">.</span><span class="pl-en">fetch!</span><span class="pl-kos">(</span><span class="pl-s1">map</span><span class="pl-kos">,</span> <span class="pl-pds">:name</span><span class="pl-kos">)</span>
<span class="pl-k">end</span>
<span class="pl-k">defmodule</span> <span class="pl-v">CallsUser</span> <span class="pl-k">do</span>
<span class="pl-k">def</span> <span class="pl-en">calls_name</span> <span class="pl-k">do</span>
<span class="pl-v">User</span><span class="pl-c1">.</span><span class="pl-en">name</span><span class="pl-kos">(</span><span class="pl-kos">%</span><span class="pl-kos">{</span><span class="pl-kos">}</span><span class="pl-kos">)</span>
<span class="pl-k">end</span>
<span class="pl-k">end</span></pre></div>
<p>The code above has a type violation, which is now caught by the type system:</p>
<div class="snippet-clipboard-content notranslate position-relative overflow-auto" data-snippet-clipboard-copy-content=" warning: incompatible types given to User.name/1:
User.name(%{})
given types:
%{name: not_set()}
but expected one of:
dynamic(%{..., name: term()})
typing violation found at:
│
16 │ User.name(%{})
│ ~
│
└─ lib/calls_user.ex:7:5: CallsUser.calls_name/0"><pre lang="text" class="notranslate"><code> warning: incompatible types given to User.name/1:
User.name(%{})
given types:
%{name: not_set()}
but expected one of:
dynamic(%{..., name: term()})
typing violation found at:
│
16 │ User.name(%{})
│ ~
│
└─ lib/calls_user.ex:7:5: CallsUser.calls_name/0
</code></pre></div>
<h3>Acknowledgements</h3>
<p>The type system was made possible thanks to a partnership between <a href="https://www.cnrs.fr/" rel="nofollow">CNRS</a> and <a href="https://remote.com/" rel="nofollow">Remote</a>. The development work is currently sponsored by <a href="https://www.fresha.com/" rel="nofollow">Fresha</a> and <a href="https://tidewave.ai/" rel="nofollow">Tidewave</a>.</p>
<h2>v1.20.0-rc.0 (2026-01-09)</h2>
<h3>1. Enhancements</h3>
<h4>Elixir</h4>
<ul>
<li>[Calendar] Optimize <code>date_from_iso_days</code> by using the Neri-Schneider algorithm</li>
<li>[Enum] Add <code>Enum.min_max</code> sorter</li>
<li>[Integer] Add <code>Integer.ceil_div/2</code></li>
<li>[IO] Add <code>IO.iodata_empty?/1</code></li>
<li>[File] Skip device, named pipes, etc in <code>File.cp_r/3</code> instead of erroring with reason <code>:eio</code></li>
<li>[Kernel] Print intermediate results of <code>dbg</code> for pipes</li>
<li>[Kernel] Warn on unused requires</li>
<li>[Regex] Add <code>Regex.import/1</code> to import regexes defined with <code>/E</code></li>
</ul>
<h4>ExUnit</h4>
<ul>
<li>[ExUnit.CaptureLog] Add <code>:formatter</code> option for custom log formatting</li>
</ul>
<h4>Mix</h4>
<ul>
<li>[mix deps] Support filtering <code>mix deps</code> output</li>
<li>[mix test] Add <code>mix test --dry-run</code></li>
</ul>
<h3>2. Hard deprecations</h3>
<h4>Elixir</h4>
<ul>
<li>[File] <code>File.stream!(path, modes, lines_or_bytes)</code> is deprecated in favor of <code>File.stream!(path, lines_or_bytes, modes)</code></li>
<li>[Kernel] Matching on the size inside a bit pattern now requires the pin operator for consistency, such as <code><<x::size(^existing_var)>></code></li>
<li>[Kernel.ParallelCompiler] <code>Kernel.ParallelCompiler.async/1</code> is deprecated in favor of <code>Kernel.ParallelCompiler.pmap/2</code>, which is more performant and addresses known limitations</li>
</ul>
<h4>Logger</h4>
<ul>
<li>[Logger] <code>Logger.*_backend</code> functions are deprecated in favor of handlers. If you really want to keep on using backends, see the <code>:logger_backends</code> package</li>
<li>[Logger] <code>Logger.enable/1</code> and <code>Logger.disable/1</code> have been deprecated in favor of <code>Logger.put_process_level/2</code> and <code>Logger.delete_process_level/1</code></li>
</ul>github-actions[bot]tag:github.com,2008:Repository/1234714/v1.19.52026-01-09T11:51:16Zv1.19.5<h3>1. Enhancements</h3>
<h4>Elixir</h4>
<ul>
<li>[Protocol] Optimize protocol consolidation to no longer load structs</li>
</ul>
<h3>2. Bug fixes</h3>
<h4>Elixir</h4>
<ul>
<li>[Kernel] Fix unnecessary recompilation when <code>dbg_callback</code> is modified at runtime</li>
<li>[Kernel] Fix parser crash on missing parentheses on expression following operator <code>not in</code></li>
<li>[Kernel] Support fetching abstract code for modules compiled with Elixir v1.14 and earlier</li>
<li>[Protocol] Ensure protocol consolidation no longer stores outdated struct types. As a consequence, protocols types only track struct names at the moment</li>
<li>[Stream] Revert optimization which caused nested streams in <code>Stream.flat_map/2</code> to crash</li>
</ul>
<h4>IEx</h4>
<ul>
<li>[IEx] Fix usage of <code>#iex:break</code> as part of multi-line prompts</li>
</ul>
<h4>Logger</h4>
<ul>
<li>[Logger.Backends] Do not crash on invalid metadata</li>
</ul>github-actions[bot]tag:github.com,2008:Repository/1234714/v1.19-latest2026-01-09T11:10:02Zv1.19-latest<p>Automated release for latest v1.19.</p>github-actions[bot]tag:github.com,2008:Repository/1234714/v1.19.42025-11-27T16:46:24Zv1.19.4<h3>1. Enhancements</h3>
<h4>Mix</h4>
<ul>
<li>[mix xref] Add <code>--min-cycle-label</code> to help projects adapt to the more precise <code>mix xref graph</code> reports in Elixir v1.19. In previous versions, Elixir would break a large compilation cycle into several smaller ones, and therefore developers would check for <code>--min-cycle-size</code> on CI. However, the issue is not the size of the cycle (it has no implication in the amount of compiled files), but how many compile-time dependencies (aka compile labels) in a cycle. The new option allows developers to filter on the label parameter</li>
</ul>
<h3>2. Bug fixes</h3>
<h4>Elixir</h4>
<ul>
<li>[File] Ensure <code>File.cp_r/3</code> reports non-existing destination properly (instead of source)</li>
</ul>
<h4>ExUnit</h4>
<ul>
<li>[ExUnit] Fix formatter crash when diffing takes too long</li>
<li>[ExUnit] Ensure parallel matches in <code>assert</code> propagate type information</li>
</ul>
<h4>Logger</h4>
<ul>
<li>[Logger] Fix regression where formatter would crash when given chardata (the crash would happen when logging non-ASCII characters)</li>
</ul>
<h4>Mix</h4>
<ul>
<li>[mix help] Ensure <code>app:APP</code> works when the project or its dependencies were not yet compiled</li>
<li>[mix escript.build] Ensure the <code>hex</code> application can be included in escripts</li>
</ul>github-actions[bot]tag:github.com,2008:Repository/1234714/v1.19.32025-11-13T17:12:25Zv1.19.3<h3>1. Enhancements</h3>
<h4>Elixir</h4>
<ul>
<li>[Kernel] Support /E modifier for regular expressions in config files</li>
</ul>
<h4>Mix</h4>
<ul>
<li>[mix compile] Allow forcing specific compilers, such as <code>--force-elixir</code>, <code>--force-app</code>, etc</li>
<li>[mix help app:APP] Support showing helps for apps in Elixir and Erlang standard libraries</li>
</ul>
<h3>2. Bug fixes</h3>
<h4>ExUnit</h4>
<ul>
<li>[ExUnit.Case] Fix crash when formatting errors caused by a linked/trapped exit during <code>setup_all</code></li>
</ul>
<h4>Mix</h4>
<ul>
<li>[mix compile.app] Ensure functions in the format <code>&Mod.fun/arity</code> can be written to .app files</li>
<li>[mix compile.app] Ensure strings with Unicode characters can be written to .app files</li>
</ul>github-actions[bot]tag:github.com,2008:Repository/1234714/v1.19.22025-11-02T12:37:10Zv1.19.2<h3>1. Enhancements</h3>
<h4>Elixir</h4>
<ul>
<li>[Kernel] Measure and optimize writing of <code>.beam</code> files in the compiler</li>
<li>[Kernel] Optimize rare scenarios where type checking took too long</li>
</ul>
<h4>Mix</h4>
<ul>
<li>[mix compile] Add flag <code>--no-check-cwd</code> to skip compiler check to aid debugging</li>
</ul>
<h3>2. Bug fixes</h3>
<h4>Elixir</h4>
<ul>
<li>[IO] Fix dialyzer warning on <code>IO.inspect :label</code></li>
<li>[Kernel] Ensure we warn on deprecated <code>~~~</code> unary operator</li>
</ul>
<h4>Logger</h4>
<ul>
<li>[Logger] Reset ansi escapes before newlines in Logger</li>
</ul>
<h4>Mix</h4>
<ul>
<li>[mix compile] Warn if <code>elixirc_paths</code> is not a list of string paths</li>
<li>[mix compile] Address regression where umbrella children were compiled too early and without respecting compilation flags</li>
<li>[mix deps.compile] Improve reliability of <code>MIX_OS_DEPS_COMPILE_PARTITION_COUNT</code> across <code>mix escript.install</code>, <code>mix archive.install</code>, and others</li>
</ul>github-actions[bot]