# v1.7 release note section This release comes with many type inference improvements. With these improvements, Julia v1.7 will more "smartly" infer types of your program and improve the performance for free ! Mostly notably, v1.7 is able to propagate type constraints that can be derived from `isa` and `===` conditions _inter-procedurally_ (i.e. across any function calls). Certain Julia programs are written in a way that it changes the behavior depending on runtime type, and such programs may run much faster by the inferrability gain of this improvement. For example, now there is no inferrability difference between `x === nothing` and `isnothing(x)` (and so you no longer need to remember [this performance tip](https://docs.julialang.org/en/v1.6/manual/performance-tips/#Checking-for-equality-with-a-singleton)): > on v1.6 ```julia julia> code_typed((Union{Nothing,Int},); optimize=false) do x return isnothing(x) ? 0 : x end |> first CodeInfo( 1 ─ %1 = Main.isnothing(x)::Bool └── goto #3 if not %1 2 ─ return 0 3 ─ return x ) => Union{Nothing, Int64} ``` > on v1.7 ```julia julia> code_typed((Union{Nothing,Int},); optimize=false) do x return isnothing(x) ? 0 : x end |> first CodeInfo( 1 ─ %1 = Main.isnothing(x)::Bool └── goto #3 if not %1 2 ─ return 0 3 ─ return x::Int64 ) => Int64 ``` Of course, this inter-procedural constraint propagation works for arbitrary generic functions ! > on v1.6 ```julia julia> ispositive(a) = isa(a, Number) && a > 0; julia> code_typed((Union{Nothing,Int},); optimize=false) do x return ispositive(x) ? x : 0 end |> first CodeInfo( 1 ─ %1 = Main.ispositive(x)::Bool └── goto #3 if not %1 2 ─ return x::Int64 3 ─ return 0 ) => Union{Nothing, Int64} ``` > on v1.7 ```julia julia> ispositive(a) = isa(a, Number) && a > 0; julia> code_typed((Union{Nothing,Int},); optimize=false) do x return ispositive(x) ? x : 0 end |> first CodeInfo( 1 ─ %1 = Main.ispositive(x)::Bool └── goto #3 if not %1 2 ─ return x::Int64 3 ─ return 0 ) => Int64 ``` Another remarkable improvement is more eager constant-propagations. Julia v1.7 can substitute more runtime computations with pre-computed constants, and eliminate dead code by resolving conditional branches at compile-time. As an example, on v1.7, computations of special-functions can be done at compile-time ! > on v1.6: many runtime computations ```julia julia> code_typed((Int,)) do n n + sin(sum(sincos(42))) end |> first CodeInfo( 1 ─ %1 = Base.muladd_float(0.16933292771007588, 2.7557313707070068e-6, -0.0001984126982985795)::Float64 │ %2 = Base.muladd_float(0.16933292771007588, %1, 0.00833333333332249)::Float64 ... │ %28 = Base.sitofp(Float64, n)::Float64 │ %29 = Base.add_float(%28, %27)::Float64 └── return %29 ) => Float64 ``` > on v1.7: the computation of `sincos(42)` is done at compile-time ! ```julia julia> code_typed((Int,)) do n n + sin(sum(sincos(42))) end |> first CodeInfo( 1 ─ %1 = Base.sitofp(Float64, n)::Float64 │ %2 = Base.add_float(%1, -0.9678422808766897)::Float64 └── return %2 ) => Float64 ``` For those interested, here is the list of specific PRs that implement main inference improvements of this release: - inter-procedural conditional constraint propagation ([#38905](https://github.com/JuliaLang/julia/pull/38905)) - constant-propagation for union-split callsite ([#39305](https://github.com/JuliaLang/julia/pull/39305)) - constant-propagation for `invoke` callsite ([#41383](https://github.com/JuliaLang/julia/pull/41383)) - more conditional constraint propagation ([#39936](https://github.com/JuliaLang/julia/pull/39936), [#40832](https://github.com/JuliaLang/julia/pull/40832)) Actually these inference improvements were motivated by the need for [JET.jl](https://github.com/aviatesk/JET.jl), a static analyzer for Julia, that is powered by Julia compiler's type inference implementation. With the inference improvements of v1.7, JET can analyze your program more correctly and faster – as a simple measurement, [when analyzing JET itself](https://gist.github.com/aviatesk/e2ffa4bfaee60f939ef4b65449fa394b), JET takes **90** seconds to report **93** false positive errors on v1.6, but on v1.7 and higher, JET can finish the analysis withn **40** seconds and the number of false positives is reduced to **27**, thanks to both the type inference improvements and [several inferrability improvements of Julia Base](https://github.com/JuliaLang/julia/pulls?q=is%3Apr+is%3Amerged+inferrability). Having said that, JET's analysis accuracy is really not enough. In order to make it more practically useful, we need to 1.) eliminate type instabilities among Julia Base and common third-party packages, and 2.) make our type inference yet more smarter. As for the part 2., we're thinking of the following improvement ideas: - switch to a new lattice design: to simplify the addition of further improvements - design a more proper backward-analysis pass: to enable further constraint propagation - analyze lifetime and dynamic scope of objects: to optimize allocations and elide finalizers - analyze memory-effect on mutable objects: to enable scalar replacements and constant folding of mutable objects If you're interested in these inference improvements, please get in touch with us on `#internals` channel of the Julia slack. We're always looking forward to your contributions to the compiler code !