Julia v1.14 Release Notes

New language features

  • It is now possible to control which version of the Julia syntax will be used to parse a package by setting the compat.julia or syntax.julia_version key in Project.toml. This feature is similar to the notion of "editions" in other language ecosystems and will allow non-breaking evolution of Julia syntax in future versions. See the "Syntax Versioning" section in the code loading documentation (#60018).

  • (U+U+1D45), (U+1D4B), (U+1DB2), ˱ (U+02F1), ˲ (U+02F2), and (U+2094) can now also be used as operator suffixes, accessible as \^alpha, \^epsilon, \^ltphi, \_<, \_>, and \_schwa at the REPL (#60285).

  • The @label macro can now create labeled blocks that can be exited early with break name [value]. Use @label name expr for named blocks or @label expr for anonymous blocks. Anonymous @label blocks participate in the default break scope: a plain break or break _ exits the innermost breakable scope, whether it is a loop or an @label block. The continue statement also supports labels with continue name to continue a labeled loop (#60481).

  • typegroup blocks allow defining mutually recursive struct types that reference each other in their field types. All types in the group are resolved atomically at the end of the block (#60569).

  • Primitive types with non-byte-multiple logical widths can now be defined (#61359).

  • Introduced explicitly wrapping arithmetic operators +%, -%, *% to annotate arithmetic operations that are semantically safe to wrap/overflow. Their behavior is currently identical to the default +, -, * operators. However, in a future version, there may be opt-in support to detect unannotated wrapping in the default operators (#50790).

Language changes

  • Type{T} <: S now holds only if every type == to T is an instance of S, fixing a long-standing soundness hole where e.g. Type{Int} <: DataType held even though types like Tuple{S} where S<:Int are == (and isa) their canonical spelling without being DataTypes. In particular Type{T} is no longer a subtype of any single kind: use a union of kinds instead (e.g. Type{Int} <: Union{DataType,UnionAll} holds). isa and dispatch of type values are unaffected, and a method on Type{Int} remains more specific than one on DataType (#33136, #62141).

Compiler/Runtime improvements

  • Type inference now refines field types through conditional checks and call signatures. For example, after if !isnothing(x.field), inference knows x.field is not nothing within the branch. Similarly, after a call like func(x.field) where func(::Int) is the only matching method, inference refines x.field to Int. This works for immutable struct fields and const fields of mutable structs. Mutable (non-const) fields are not supported due to the lack of per-object memory effect tracking; for those, the recommended pattern remains storing the field value in a local variable before the check (e.g. val = x.field; if !isnothing(val) ... end) (#41199, #47574).

  • Stack traces now show full method signatures with argument types for inlined frames, matching the display of non-inlined frames (#53925).

  • Stack traces of errors raised while loading code no longer show the internals of the code loading machinery, which are collapsed to the single frame that entered loading. Frames for user code that runs during loading are unaffected. Set the JULIA_STACKTRACE_FULL_LOADING environment variable to true to show them (#52988).

  • Parallel package precompilation now coordinates CPU usage across both the precompile worker processes and the LLVM threads each spawns to compile its native image, sharing a single thread budget so idle cores are filled during the long tail without oversubscribing the machine when many packages compile at once. The total budget can be set with the new JULIA_PRECOMPILE_THREADS environment variable (#61958).

Command-line option changes

  • -P <project> is now a shorthand for --project <project> (#59867).

Multi-threading changes

  • The return type of fetch(::Task) is now inferred precisely when inference can determine the code the task was created to run (for example fetch(Threads.@spawn f(x))), instead of always being Any. Correspondingly, assigning to the result field of a Task via property syntax (t.result = v) now throws an error: the result of a task is determined by the return value of its code, and the runtime and the compiler now rely on this correspondence. To pass a value to a suspended task, use schedule(t, val) or yieldto(t, val) (#59221).

  • New functions Threads.atomic_fence_heavy and Threads.atomic_fence_light provide support for asymmetric atomic fences, speeding up atomic synchronization where one side of the synchronization runs significantly less often than the other (#60311).

  • Threads.@threads now supports array comprehensions with syntax like @threads [f(i) for i in 1:n], filtered comprehensions like @threads [f(i) for i in 1:n if condition(i)], typed comprehensions like @threads Float64[f(i) for i in 1:n], and multi-dimensional comprehensions like @threads [f(i,j) for i in 1:n, j in 1:m] (preserves dimensions). All scheduling options (:static, :dynamic, :greedy) are supported. Results preserve element order for :static and :dynamic scheduling; :greedy does not guarantee order. Non-indexable iterators are also supported (#59019).

  • The task scheduler now avoids O(nthreads) wake overhead on every @spawn, significantly reducing threading overhead particularly on highly oversubscribed machines. Benchmarks show up to 1000x reduction in spawn time in such scenarios (#61826).

  • Threads.Atomic now supports the reference form of the @atomic, @atomicswap, @atomicreplace, and @atomiconce macros (e.g. @atomic a[], @atomic a[] = v, @atomic a[] += 1), which allows the memory ordering to be specified explicitly and makes atomic read-modify-write operations syntactically clear (#62382).

Build system changes

New library functions

  • tap(f) creates a function that calls f(x) for side effects and returns x (#61340).

  • Base.set_binding_visibility! sets the declared visibility (:none, :public, or :export) of a name in a module, allowing an export or public declaration to be retracted programmatically (#62131).

  • Base.generating_output() has been made public (but not exported) to allow checking whether the current process is performing compilation for a pkgimage/sysimage (#61224).

  • Base.isfieldatomic(t, s) has been made public (but not exported); it reports whether a field s of a type t is declared @atomic.

  • Base.raw_substring is an unexported, public constructor to build a SubString without checking for valid string indices.

  • Base.unannotate(::AnnotatedString) returns the underlying un-annotated string of the input string.

  • Base.include_mapexprs(mod) is an unexported, public function returning the non-identity mapexpr functions used by include(mapexpr, …) calls while loading the package rooted at mod, keyed by (including_module, absolute_path). The table is stored inside the package image, so it survives precompilation; revision tools (e.g. Revise) use it to re-apply the original transform when an include(mapexpr, …)-ed file is edited.

New library features

  • IOContext supports a new boolean hexunsigned option that allows for printing unsigned integers in decimal instead of hexadecimal (#60267).

  • lazy"..." strings now support a flag lazy"..."c that adds compact and limit flags to the IOContext for final output-string generation (#61887).

  • The StringView type wraps an AbstractVector{UInt8} and interprets it as a UTF-8 encoded string, superseding the StringViews.jl package (#60526).

  • Package precompilation now supports running precompilation in a background task and has new interactive keyboard controls: c to cleanly cancel immediately, d to detach, i for a profile peek, v to toggle verbose mode showing elapsed time, CPU%, and memory usage, and ? for help (#60943).

  • Instances of an Enum can now be given their own docstrings within the @enum definition (#61955).

  • New methods readdir(path, DirEntry) and readdir(::DirEntry, DirEntry) return directory contents along with the type of the entries in a vector of new DirEntry objects to provide more efficient isfile etc. checks. readdir(::DirEntry) accepts a DirEntry as input and, like readdir(::AbstractString), returns a Vector{String} of names. DirEntry is exported from Base (#55358).

  • When the display height is too small to show any array entries, the text/plain array display (used e.g. by the REPL and when logging values with @info etc.) now shows as many entries as fit on a single line, truncated to the display width, instead of showing no data at all (#62543).

Standard library changes

  • codepoint(c) now succeeds for overlong encodings. Base.ismalformed, Base.isoverlong, and Base.show_invalid are now public and documented (but not exported) (#55152).

JuliaSyntaxHighlighting

LinearAlgebra

Markdown

  • Support "raw" or "inline" HTML inside Markdown data (#60629, #60632, #60732).

  • Support autolinks for email addresses (#60570).

  • Many improvements and bugfixes for rendering Markdown lists in a terminal (#55456, #60519).

  • Strikethrough text via ~strike~ or ~~through~~ is now supported by the Markdown parser (#60537).

  • Many, many bug fixes and minor tweaks; overall behavior is now much closer to CommonMark (#59977, #60502).

Profile

Random

REPL

Test

  • @test, @test_throws, and @test_broken now support a context keyword argument that provides additional information displayed on test failure. This is useful for debugging which specific case failed in parameterized tests (#60501).

  • @test_throws, @test_warn, @test_nowarn, @test_logs, and @test_deprecated now support broken and skip keyword arguments for consistency with @test (#60543).

  • New functions detect_closure_boxes and detect_closure_boxes_all find methods that allocate Core.Box in their lowered code, which can indicate performance issues from captured variables in closures (#60478).

Dates

  • unix2datetime now accepts a keyword argument localtime=true to use the host system's local time zone instead of UTC (#50296).

InteractiveUtils

  • less/@less and edit/@edit are now supported for documented variables (#53539).

  • A new @methods macro lists all methods applicable to a call expression, using the types of the given arguments, e.g. @methods isvalid('a', 1) or @methods isvalid(::AbstractChar, ::Integer) (#62311).

Dates

External dependencies

Tooling Improvements

Deprecated or removed

  • Storing into a Threads.Atomic with the plain a[] = v form (i.e. setindex!) is deprecated in favor of @atomic a[] = v. The plain form makes read-modify-write expressions such as a[] += 1 look atomic even though they expand to a separate, non-atomic load and store; use @atomic a[] += 1 or Threads.atomic_add! for an atomic update. Reading with a[] is unchanged (#62382).