Skip to content

Releases: elixir-lang/elixir

main-latest

17 Sep 07:21

Choose a tag to compare

Automated release for latest main.

v1.20.0-rc.0

09 Jan 19:11

Choose a tag to compare

v1.20.0-rc.0 Pre-release
Pre-release

Type system improvements

This release includes type inference of all constructs.

Type inference of function calls

Elixir now performs inference of whole functions. The best way to show the new capabilities are with examples. Take the following code:

def add_foo_and_bar(data) do
  data.foo + data.bar
end

Elixir now infers that the function expects a map as first argument, and the map must have the keys .foo and .bar whose values are either integer() or float(). The return type will be either integer() or float().

Here is another example:

def sum_to_string(a, b) do
  Integer.to_string(a + b)
end

Even though the + operator works with both integers and floats, Elixir infers that a and b must be both integers, as the result of + is given to a function that expects an integer. The inferred type information is then used during type checking to find possible typing errors.

Type inference of guards

This release also performs inference of guards! Let's see some examples:

def example(x, y) when is_list(x) and is_integer(y)

The code above correctly infers x is a list and y is an integer.

def example({:ok, x} = y) when is_binary(x) or is_integer(x)

The one above infers x is a binary or an integer, and y is a two element tuple with :ok as first element and a binary or integer as second.

def example(x) when is_map_key(x, :foo)

The code above infers x is a map which has the :foo key, represented as %{..., foo: dynamic()}. Remember the leading ... indicates the map may have other keys.

def example(x) when not is_map_key(x, :foo)

And the code above infers x does not have the :foo key (hence x.foo will raise a typing violation), which has the type: %{..., foo: not_set()}.

You can also have expressions that assert on the size of data structures:

def example(x) when tuple_size(x) < 3

Elixir will correctly track the tuple has at most two elements, and therefore accessing elem(x, 3) 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).

Complete typing of maps keys

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 dynamic().

As of Elixir v1.20, we can track all possible domains as map keys. For example, the map:

%{123 => "hello", 456.0 => :ok}

will have the type:

%{integer() => binary(), float() => :ok}

It is also possible to mix domain keys, as above, with atom keys, yielding the following:

%{integer() => integer(), root: integer()}

This system is an implementation of Typing Records, Maps, and Structs, by Giuseppe Castagna (2023).

Typing of map operations

We have typed the majority of the functions in the Map module, allowing the type system to track how keys are added, updated, and removed across all possible key types.

For example, imagine we are calling the following Map functions with a variable map, which we don't know the exact shape of, and an atom key:

Map.put(map, :key, 123)
#=> returns type %{..., key: integer()}

Map.delete(map, :key)
#=> returns type %{..., key: not_set()}

As you can see, we track when keys are set and also when they are removed.

Some operations, like Map.replace/3, only replace the key if it exists, and that is also propagated by the type system:

Map.replace(map, :key, 123)
#=> returns type %{..., key: if_set(integer())}

In other words, if the key exists, it would have been replaced by an integer value. Furthermore, whenever calling a function in the Map module and the given key is statically proven to never exist in the map, an error is emitted.

By combining full type inference with bang operations like Map.fetch!/2, Map.pop!/2, Map.replace!/3, and Map.update!/3, Elixir is able to propagate information about the desired keys. Take this module:

defmodule User do
  def name(map), do: Map.fetch!(map, :name)
end

defmodule CallsUser do
  def calls_name do
    User.name(%{})
  end
end

The code above has a type violation, which is now caught by the type system:

    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

Acknowledgements

The type system was made possible thanks to a partnership between CNRS and Remote. The development work is currently sponsored by Fresha and Tidewave.

v1.20.0-rc.0 (2026-01-09)

1. Enhancements

Elixir

  • [Calendar] Optimize date_from_iso_days by using the Neri-Schneider algorithm
  • [Enum] Add Enum.min_max sorter
  • [Integer] Add Integer.ceil_div/2
  • [IO] Add IO.iodata_empty?/1
  • [File] Skip device, named pipes, etc in File.cp_r/3 instead of erroring with reason :eio
  • [Kernel] Print intermediate results of dbg for pipes
  • [Kernel] Warn on unused requires
  • [Regex] Add Regex.import/1 to import regexes defined with /E

ExUnit

  • [ExUnit.CaptureLog] Add :formatter option for custom log formatting

Mix

  • [mix deps] Support filtering mix deps output
  • [mix test] Add mix test --dry-run

2. Hard deprecations

Elixir

  • [File] File.stream!(path, modes, lines_or_bytes) is deprecated in favor of File.stream!(path, lines_or_bytes, modes)
  • [Kernel] Matching on the size inside a bit pattern now requires the pin operator for consistency, such as <<x::size(^existing_var)>>
  • [Kernel.ParallelCompiler] Kernel.ParallelCompiler.async/1 is deprecated in favor of Kernel.ParallelCompiler.pmap/2, which is more performant and addresses known limitations

Logger

  • [Logger] Logger.*_backend functions are deprecated in favor of handlers. If you really want to keep on using backends, see the :logger_backends package
  • [Logger] Logger.enable/1 and Logger.disable/1 have been deprecated in favor of Logger.put_process_level/2 and Logger.delete_process_level/1

v1.19.5

09 Jan 11:51

Choose a tag to compare

1. Enhancements

Elixir

  • [Protocol] Optimize protocol consolidation to no longer load structs

2. Bug fixes

Elixir

  • [Kernel] Fix unnecessary recompilation when dbg_callback is modified at runtime
  • [Kernel] Fix parser crash on missing parentheses on expression following operator not in
  • [Kernel] Support fetching abstract code for modules compiled with Elixir v1.14 and earlier
  • [Protocol] Ensure protocol consolidation no longer stores outdated struct types. As a consequence, protocols types only track struct names at the moment
  • [Stream] Revert optimization which caused nested streams in Stream.flat_map/2 to crash

IEx

  • [IEx] Fix usage of #iex:break as part of multi-line prompts

Logger

  • [Logger.Backends] Do not crash on invalid metadata

v1.19-latest

07 Jun 18:18

Choose a tag to compare

Automated release for latest v1.19.

v1.19.4

27 Nov 16:46

Choose a tag to compare

1. Enhancements

Mix

  • [mix xref] Add --min-cycle-label to help projects adapt to the more precise mix xref graph 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 --min-cycle-size 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

2. Bug fixes

Elixir

  • [File] Ensure File.cp_r/3 reports non-existing destination properly (instead of source)

ExUnit

  • [ExUnit] Fix formatter crash when diffing takes too long
  • [ExUnit] Ensure parallel matches in assert propagate type information

Logger

  • [Logger] Fix regression where formatter would crash when given chardata (the crash would happen when logging non-ASCII characters)

Mix

  • [mix help] Ensure app:APP works when the project or its dependencies were not yet compiled
  • [mix escript.build] Ensure the hex application can be included in escripts

v1.19.3

13 Nov 17:12

Choose a tag to compare

1. Enhancements

Elixir

  • [Kernel] Support /E modifier for regular expressions in config files

Mix

  • [mix compile] Allow forcing specific compilers, such as --force-elixir, --force-app, etc
  • [mix help app:APP] Support showing helps for apps in Elixir and Erlang standard libraries

2. Bug fixes

ExUnit

  • [ExUnit.Case] Fix crash when formatting errors caused by a linked/trapped exit during setup_all

Mix

  • [mix compile.app] Ensure functions in the format &Mod.fun/arity can be written to .app files
  • [mix compile.app] Ensure strings with Unicode characters can be written to .app files

v1.19.2

02 Nov 12:37

Choose a tag to compare

1. Enhancements

Elixir

  • [Kernel] Measure and optimize writing of .beam files in the compiler
  • [Kernel] Optimize rare scenarios where type checking took too long

Mix

  • [mix compile] Add flag --no-check-cwd to skip compiler check to aid debugging

2. Bug fixes

Elixir

  • [IO] Fix dialyzer warning on IO.inspect :label
  • [Kernel] Ensure we warn on deprecated ~~~ unary operator

Logger

  • [Logger] Reset ansi escapes before newlines in Logger

Mix

  • [mix compile] Warn if elixirc_paths is not a list of string paths
  • [mix compile] Address regression where umbrella children were compiled too early and without respecting compilation flags
  • [mix deps.compile] Improve reliability of MIX_OS_DEPS_COMPILE_PARTITION_COUNT across mix escript.install, mix archive.install, and others

v1.19.1

20 Oct 18:28

Choose a tag to compare

1. Bug fixes

EEx

  • [EEx] Address Dialyzer warnings when invoking EEx.compile_string

Elixir

  • [Kernel] Optimize how types are computed for pretty printing
  • [Kernel] Optimize how differences are computed in the type system
  • [Macro] Do not escape options given to dbg/2
  • [Protocol] Improve protocol violation warnings

Mix

  • [mix compile] Do not attempt to touch deleted files when compilation fails and then resumed with missing files
  • [mix deps.compile] Do not spawn partitions when all dependencies are local and already compiled

v1.19.0

16 Oct 07:39

Choose a tag to compare

Official announcement: https://elixir-lang.org/blog/2025/10/16/elixir-v1-19-0-released/

1. Enhancements

Elixir

  • [Access] Add Access.values/0 for traversing maps and keyword lists values
  • [Base] Add functions to verify if an encoding is valid, such as valid16?, valid64?, and so forth
  • [Calendar] Support 2-arity options for Calendar.strftime/3 which receives the whole data type
  • [Code] Add :migrate_call_parens_on_pipe formatter option
  • [Code] Add :indentation option to Code.string_to_quoted/2
  • [Code.Fragment] Preserve more block content around cursor in container_cursor_to_quoted
  • [Code.Fragment] Add :block_keyword_or_binary_operator to Code.Fragment for more precise suggestions after operators and closing terminators
  • [Code.Fragment] Add Code.Fragment.lines/1
  • [Enum] Provide more information on Enum.OutOfBoundsError
  • [Inspect] Allow optional: :all when deriving Inspect
  • [Inspect.Algebra] Add optimistic/pessimistic groups as a simplified implementation of next_break_fits
  • [IO.ANSI] Add ANSI codes to turn off conceal and crossed_out
  • [Kernel] Raise when U+2028 and U+2029 characters are present in comments and strings to avoid line spoofing attacks
  • [Kernel] Include the line for the previous clause in errors/warnings related to conflicts between defaults on function definitions
  • [Kernel] Support min/2 and max/2 as guards
  • [Kernel.ParallelCompiler] Add each_long_verification_threshold which invokes a callback when type checking a module takes too long
  • [Kernel.ParallelCompiler] Include lines in == Compilation error in file ... == slogans
  • [Macro] Print debugging results from Macro.dbg/3 as they happen, instead of once at the end
  • [Macro] Add __escape__/1 callback so structs can escape references and other runtime data types in Macro.escape/1
  • [Module] Do not automatically load modules after their compilation, guaranteeing a more consistent compile time experience and drastically improving compilation times
  • [OptionParser] Support the :regex type
  • [OptionParser] Enhance parsing error to display available options
  • [Protocol] Type checking of protocols dispatch and implementations
  • [Regex] Add Regex.to_embed/2 which returns an embeddable representation of regex in another regex
  • [Regex] Raise error message when regexes are used as default values in struct fields for compatibility with Erlang/OTP 28
  • [Registry] Add key-based partitioning of duplicate registries
  • [String] Add String.count/2 to count occurrences of a pattern
  • [String] Update to Unicode 17.0.0

ExUnit

  • [ExUnit] Set a process label for each test
  • [ExUnit.CaptureLog] Parallelize log dispatch when multiple processes are capturing log
  • [ExUnit.Case] Add :test_group to the test context
  • [ExUnit.Doctest] Support ellipsis in doctest exceptions to match the remaining of the exception
  • [ExUnit.Doctest] Add :inspect_opts option for doctest

IEx

  • [IEx] Support multi-line prompts (due to this feature, :continuation_prompt and :alive_continuation_prompt are no longer supported as IEx configuration)
  • [IEx.Autocomplete] Functions annotated with @doc group: "Name" metadata will appear within their own groups in autocompletion

Logger

  • [Logger] Accept any enumerable in Logger.metadata/1

Mix

  • [mix] Add support for MIX_PROFILE_FLAGS to configure MIX_PROFILE
  • [mix compile] Debug the compiler and type checker PID when MIX_DEBUG=1 and compilation/verification thresholds are met
  • [mix compile] Add Mix.Tasks.Compiler.reenable/1
  • [mix deps.compile] Support MIX_OS_DEPS_COMPILE_PARTITION_COUNT for compiling deps concurrently across multiple operating system processes
  • [mix help] Add mix help Mod, mix help :mod, mix help Mod.fun, mix help Mod.fun/arity, and mix help app:package
  • [mix format] Add options to mix format to allow excluding of files
  • [mix test] Add --name-pattern option to mix test
  • [mix test] Allow to distinguish the exit status between warnings as errors and test failures
  • [mix xref graph] Add support for --format json
  • [mix xref graph] Emit a warning if --source is part of a cycle
  • [Mix] Support the :compilers option
  • [Mix.Task.Compiler] Add Mix.Task.Compiler.run/2

2. Bug fixes

Elixir

  • [Code] Return error on invalid unicode sequences in Code.string_to_quoted/2 instead of raising
  • [Code] Properly handle column annotation for in in not in expressions
  • [DateTime] Do not truncate microseconds regardless of precision in DateTime.diff/3
  • [Enum] Fix infinite loop on Enum.take/2 with negative index on empty enumerable
  • [File] Properly handle permissions errors cascading from parent in File.mkdir_p/1
  • [Inspect] Inspect ill-formed structs as maps
  • [Kernel] Properly increment metadata newline when ? is followed by a literal newline character
  • [Kernel] not_a_map.key now raises BadMapError for consistency with other map operations
  • [Protocol] defstruct/1 and defexception/1 are now disabled inside defprotocol as to not allow defining structs/exceptions alongside a protocol
  • [Regex] Fix Regex.split/2 returning too many results when the chunk being split on was empty (which can happen when using features such as /K)
  • [Stream] Ensure Stream.transform/5 respects suspend command when its inner stream halts
  • [URI] Several fixes to URI.merge/2 related to trailing slashes, trailing dots, and hostless base URIs

ExUnit

  • [ExUnit.Assertions] Fix order of pinned variables in failure reports
  • [ExUnit.Assertions] Raise if attempting to raise an assertion error with invalid message (not a binary)
  • [ExUnit.Case] Do not crash on empty test unit groups

IEx

  • [IEx] Abort pipelines when there is an error in any step along the way

Mix

  • [mix cmd] Preserve argument quoting in subcommands by no longer performing shell expansion. To revert to the previous behaviour, pass --shell before the command name
  • [mix compile] Fix bug where reverting changes to an external resource (such as HEEx template) after a compilation error would make it so the source module would not be compiled
  • [mix compile] Avoid failures when locking compilation across different users
  • [mix compile] Fix race condition when renaming files used by the compilation lock
  • [mix format] Ensure the formatter does not go over the specified limit in certain corner cases
  • [mix release] Fix RELEASE_SYS_CONFIG for Windows 11
  • [mix test] Ensure modules are preloaded in mix test --slowest-modules=N
  • [mix xref graph] Provide more consistent output by considering strong connected components only when computing graphs

3. Soft deprecations (no warnings emitted)

Elixir

  • [Inspect.Algebra] next_break_fits is deprecated in favor of optimistic/pessimistic groups
  • [Node] Node.start/2-3 is deprecated in favor of Node.start/2 with a keyword list

Mix

  • [mix compile] --no-protocol-consolidation is deprecated in favor of --no-consolidate-protocols for consistency with mix.exs configuration
  • [mix compile.protocols] Protocol consolidation is now part of compile.elixir and the task itself has no effect

4. Hard deprecations

Elixir

  • [Code] Warn if line-break characters outside of \r and \r\n are found in strings according to UX#55. This warning will be fast-tracked into an error for security reasons in Elixir v1.20, following a similar rule to bidirectional control characters. They will already raise if found in comments
  • [Code] The on_undefined_variable: :warn is deprecated. Relying on undefined variables becoming function calls will not be supported in the future
  • [File] Passing a callback as third argument to File.cp/3 is deprecated, pass it as a on_conflict: callback option instead
  • [File] Passing a callback as third argument to File.cp_r/3 is deprecated, pass it as a on_conflict: callback option instead
  • [Kernel] The struct update syntax, such as %URI{uri | path: "/foo/bar"}, now requires the given variable (or expression) to explicitly pattern match on the struct before it can be updated. This is because, thanks to the type system, pattern matching on structs can find more errors, more reliably, and we want to promote its usage. Once pattern matching is added, you may optionally convert the struct update syntax into the map update syntax %{uri | path: "/foo/bar"} with no less of typing guarantees
  • [Kernel.ParallelCompiler] Passing return_diagnostics: true as an option is required on compile, compile_to_path and require

Logger

  • [Logger] The :backends configuration is deprecated, either set the :default_handler to false or start backends in your application start callback

Mix

  • [mix] The :default_task, :preferred_cli_env, and :preferred_cli_target configuration inside def project in your mix.exs has been deprecated in favor of :default_task, :preferred_envs and :preferred_targets inside the def cli function
  • [mix do] Using commas as task separator in mix do (such as mix do foo, bar) is deprecated, use + instead (as in mix do foo + bar)

v1.19.0-rc.2

07 Oct 16:41

Choose a tag to compare

v1.19.0-rc.2 Pre-release
Pre-release

1. Enhancements

Elixir

  • [Regex] Raise error message when regexes are used as default values in struct fields for compatibility with Erlang/OTP 28
  • [Registry] Add key-based partitioning of duplicate registries

2. Bug fixes

Elixir

  • [Kernel] Address issue with type checking not completing on protocol consolidation

ExUnit

  • [ExUnit] Do not crash on empty test unit groups

Mix

  • [mix help] Add mix help app:APP
  • [mix test] Fix module preloading in mix test --slowest-modules=N