# Improving speed of runtime dispatch detector

**URL:** <https://discourse.julialang.org/t/improving-speed-of-runtime-dispatch-detector/114697>\
**Category:** Performance\
**Tags:** question\
**Created:** [May 24, 2024, 6:45pm UTC](https://discourse.julialang.org/t/improving-speed-of-runtime-dispatch-detector/114697 "2024-05-24T18:45:59Z")\
**Posts on this page:** 20\
**Page:** 3

<div class="post-metadata">

**Author:** ![danielwe](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/danielwe/32/35657_2.png) [@danielwe](https://discourse.julialang.org/u/danielwe)\
**Post date:** [May 25, 2024, 10:58pm UTC](https://discourse.julialang.org/t/improving-speed-of-runtime-dispatch-detector/114697/41 "2024-05-25T22:58:09Z")

</div>

> [@MilesCranmer](#):
>
> Should we add functionality to recursively overload `include` maybe?

At least you probably want to avoid the current half-way behavior, and either give submodules the full treatment or don’t touch them at all. I don’t think I have an opinion about which alternative is better.

---

<div class="post-metadata">

**Author:** ![danielwe](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/danielwe/32/35657_2.png) [@danielwe](https://discourse.julialang.org/u/danielwe)\
**Post date:** [May 25, 2024, 10:59pm UTC](https://discourse.julialang.org/t/improving-speed-of-runtime-dispatch-detector/114697/42 "2024-05-25T22:59:48Z")

</div>

You’re right! Total red herring. But I suppose you might want to support single-line modules as well?

---

<div class="post-metadata">

**Author:** ![MilesCranmer](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/milescranmer/32/21070_2.png) [@MilesCranmer](https://discourse.julialang.org/u/MilesCranmer)\
**Post date:** [May 25, 2024, 11:05pm UTC](https://discourse.julialang.org/t/improving-speed-of-runtime-dispatch-detector/114697/43 "2024-05-25T23:05:05Z")

</div>

Of course — It’s just an edge case I didn’t know could affect the expression structure.

And yeah I think full support of submodules makes sense.

I guess `begin` would be nice as well. That way you could wrap an entire package by putting a `@stable begin` in the main file, wrapping everything.

Not sure how to fix all of this though…

---

<div class="post-metadata">

**Author:** ![aplavin](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/aplavin/32/222056_2.png) [@aplavin](https://discourse.julialang.org/u/aplavin)\
**Post date:** [May 26, 2024, 12:23am UTC](https://discourse.julialang.org/t/improving-speed-of-runtime-dispatch-detector/114697/44 "2024-05-26T00:23:37Z")

</div>

How can one opt-out of errors thrown by `@stable` at callsite?  
That is,

```julia
@stable f(x) = ...

g(x) = ... f(x) ...

result = @allow_unstable g(123) # some argument that makes f(x) unstable

```

I don’t see any obvious way for now, but some optout is crucial to have if `@stable` is to gain adoption.

---

<div class="post-metadata">

**Author:** ![matthias314](https://avatars.discourse-cdn.com/v4/letter/m/a88e4f/32.png) [@matthias314](https://discourse.julialang.org/u/matthias314)\
**Post date:** [May 26, 2024, 1:56am UTC](https://discourse.julialang.org/t/improving-speed-of-runtime-dispatch-detector/114697/45 "2024-05-26T01:56:54Z")

</div>

Very nice idea! However, the following does not work at present (on main):

```julia
julia> @eval @stable module M
       
       f(x) = x
       
       end
ERROR: LoadError: AssertionError: module_body.head == :block

```

Some other comments:

- In my experience, calls to `Core.Compiler.return_type` (or `Base.promote_op`) cannot be nested, at least not always. If that also holds here, then applying `@stable` to a whole module has a good change of breaking something.

- How does `@stable` interact with macros like `@inline`, `@propagate_inbounds` and `@assume_effects`? It might be a good idea to address this in the documentation.

- It might help users to explain that the return type `Union{}` means that the function does not return normally (which most likely is the result of a programming mistake). Currently one gets:

```julia
julia> @stable f() = 1/'2'
f (generic function with 1 method)

julia> f()
ERROR: TypeInstabilityError: Instability detected in function `f`. Inferred to be `Union{}`, which is not a concrete type.

```

---

<div class="post-metadata">

**Author:** ![danielwe](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/danielwe/32/35657_2.png) [@danielwe](https://discourse.julialang.org/u/danielwe)\
**Post date:** [May 26, 2024, 2:29am UTC](https://discourse.julialang.org/t/improving-speed-of-runtime-dispatch-detector/114697/46 "2024-05-26T02:29:28Z")

</div>

> [@matthias314](#):
>
> the following does not work at present (on main):
> 
> ```julia
> julia> @eval @stable module M
>        
> f(x) = x
>        
> end
> ERROR: LoadError: AssertionError: module_body.head == :block
> 
> ```

Discussed above:

> [@MilesCranmer](#):
>
> your module is a single line of code — the macro expects it to be a block […] it’s a bug

---

<div class="post-metadata">

**Author:** ![MilesCranmer](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/milescranmer/32/21070_2.png) [@MilesCranmer](https://discourse.julialang.org/u/MilesCranmer)\
**Post date:** [May 26, 2024, 7:32am UTC](https://discourse.julialang.org/t/improving-speed-of-runtime-dispatch-detector/114697/47 "2024-05-26T07:32:30Z")

</div>

> [@aplavin](#):
>
> How can one opt-out of errors thrown by `@stable` at callsite?

On a branch I have a `@stable warnonly=true ...` syntax so it will still run but just warn you once. Not sure that’s what you meant

> [@matthias314](#):
>
> cannot be nested, at least not always

What do you mean by nested? Also the `promote_op` should just compile away anyways if there’s no error.

> [@matthias314](#):
>
> It might help users to explain that the return type `Union{}` means that the function does not return normally (which most likely is the result of a programming mistake).

Good idea. What does `Test.@inferred` do in this situation?

Maybe in such a case the default behavior would be to just run it, so that any errors just show up? Especially if you are doing this on entire modules, it seems like it would be annoying if all errors get captured by it.

---

<div class="post-metadata">

**Author:** ![MilesCranmer](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/milescranmer/32/21070_2.png) [@MilesCranmer](https://discourse.julialang.org/u/MilesCranmer)\
**Post date:** [May 26, 2024, 7:42am UTC](https://discourse.julialang.org/t/improving-speed-of-runtime-dispatch-detector/114697/48 "2024-05-26T07:42:02Z")

</div>

Maybe one way to disable at the call site would be to replace `Base.isconcretetype(T)` with an overload-able function that a macro could overwrite to be always true…?

So it would basically be like

```julia
result = @allow_unstable g(123) # some argument that makes f(x) unstable

```

gets transformed into

```julia
result = begin
    @eval DD.type_instability(::Type{T}) where {T} = false
    output = g(123)
    @eval DD.type_instability(::Type{T}) where {T} = !Base.isconcretetype(T)
    output
end

```

Obviously some major issues with this but it would “work”.

I’m not sure how you would do it otherwise.

Maybe there could be some global compilation flag?? You would want it to be constant, though, so that the check can still compile away.

You could also do

```julia
@stable g(x) = ...
g(x::Int) = ...

```

and the stable check will only apply to the general method, but not the one on `Int` where it’s known to be unstable.

Perhaps that’s the right solution here?

---

<div class="post-metadata">

**Author:** ![MilesCranmer](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/milescranmer/32/21070_2.png) [@MilesCranmer](https://discourse.julialang.org/u/MilesCranmer)\
**Post date:** [May 26, 2024, 9:26am UTC](https://discourse.julialang.org/t/improving-speed-of-runtime-dispatch-detector/114697/49 "2024-05-26T09:26:15Z")

</div>

Okay all the earlier ideas are implemented. You can do `@stable` on `begin...end` blocks and also have `include` within submodules work:

```julia
using DispatchDoctor

@stable begin
    f(x) = x > 0 ? x : 0.0
    g(x) = x > 0 ? x : 0.0
    module A
        module B
            include("myfile.jl") # all functions will be @stable
        end

        h(::Number) = rand(Bool) ? 1.0 : 0

        using DispatchDoctor: @unstable
        @unstable begin
            h(::Int) = rand(Bool) ? 1.0 : 0
            # ^ Can turn off for specific input types!
        end
    end
end

```

which gives

```julia
julia> f(1)
ERROR: TypeInstabilityError: Instability detected in function `f` with arguments `(Int64,)`. Inferred to be `Union{Float64, Int64}`, which is not a concrete type.
...

julia> A.h(1.0)
ERROR: TypeInstabilityError: Instability detected in function `h` with arguments `(DataType,)`. Inferred to be `Union{Float64, Int64}`, which is not a concrete type.
...

julia> A.h(1)
1.0

```

Actually now `include` just gets mapped to `_stabilizing_include` which means you can do

```julia
@stable begin
    include("myfile1.jl")
    include("myfile2.jl")
end

```

and it will work. If those files have their own `include`, those will also get mapped (since we use the `include(mapexpr::Function, m::Module, path)` feature).

> [@MilesCranmer](#):
>
> your module is a single line of code — the macro expects it to be a block

This is now fixed on main:

```julia
using DispatchDoctor

@stable module A
    f(x) = x > 0 ? x : 0.0
end

@test A.f(1.0) == 1.0
@test_throws TypeInstabilityError A.f(1)

```

> [@MilesCranmer](#):
>
> Maybe in such a case the default behavior would be to just run it, so that any errors just show up? Especially if you are doing this on entire modules, it seems like it would be annoying if all errors get captured by it.

This is now implemented for any `Union{}` return value, meaning we get:

```julia
julia> @stable my_bad_function(x) = x / "blah";

julia> my_bad_function(1)
ERROR: MethodError: no method matching /(::Int64, ::String)

```

whereas before it would throw an instability error.

> [@matthias314](#):
>
> How does `@stable` interact with macros like `@inline`, `@propagate_inbounds` and `@assume_effects`? It might be a good idea to address this in the documentation.

As of the recent change to allow arbitrary code blocks, `@stable` can now be used in combination with any other macro, and in any order. All it does is recurse through expressions until it finds a function definition. So

```julia
@stable @inline f(x, i) = x[i]

```

will get parsed like

```julia
@inline $(_stable_fnc(:(f(x, i) = x[i])))

```

which would allow for the `@inline` to tag the resultant function.

Basically it should allow other macros to work as normal.

---

<div class="post-metadata">

**Author:** ![matthias314](https://avatars.discourse-cdn.com/v4/letter/m/a88e4f/32.png) [@matthias314](https://discourse.julialang.org/u/matthias314)\
**Post date:** [May 26, 2024, 12:28pm UTC](https://discourse.julialang.org/t/improving-speed-of-runtime-dispatch-detector/114697/50 "2024-05-26T12:28:59Z")

</div>

> [@MilesCranmer](#):
>
> What do you mean by nested? Also the `promote_op` should just compile away anyways if there’s no error.

When `promote_op` analyzes a function that itself calls `promote_op`, then it may give up and return `Any`. I think this is the issue mentioned in [this JuliaCon talk](https://www.youtube.com/watch?v=2QLJOQNiZhI) at 2m25s. Unfortunately, the screen is blank in the video for some reason. (@schlichtanders could you remind us what the example was?)

For this reason I would expect something like

```julia
@stable f(x) = # something involving g(x)
@stable g(x) = # something

```

to produce problems when the function bodies become sufficiently complex. I’ve tried to produce an example, but so far it has always worked. However, I’ve run into such a problem myself when inferring the return type of functions in LinearCombinations.jl. I’ve used `Core.Compiler.return_type`, but I think `promote_op` is just wrapper around it.

---

<div class="post-metadata">

**Author:** ![MilesCranmer](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/milescranmer/32/21070_2.png) [@MilesCranmer](https://discourse.julialang.org/u/MilesCranmer)\
**Post date:** [May 26, 2024, 2:40pm UTC](https://discourse.julialang.org/t/improving-speed-of-runtime-dispatch-detector/114697/51 "2024-05-26T14:40:17Z")

</div>

> [@aplavin](#):
>
> How can one opt-out of errors thrown by `@stable` at callsite?

Ok I got one approach working for this, no macros needed:

```julia
julia> using DispatchDoctor

julia> @stable f(x) = x > 0 ? x : 0.0
f (generic function with 1 method)

julia> g(x) = f(x);

julia> g(1)
ERROR: TypeInstabilityError: Instability detected in function `f`
with arguments `(Int64,)`. Inferred to be `Union{Float64, Int64}`,
which is not a concrete type.
...

julia> allow_unstable() do
           g(1)
       end
1

```

the `allow_unstable` context manipulates a global flag (guarded by a `ReentrantLock`): [DispatchDoctor.jl/src/DispatchDoctor.jl at f00ce7b711dc2443d1a24a4b83fdc092692514f7 · MilesCranmer/DispatchDoctor.jl · GitHub](https://github.com/MilesCranmer/DispatchDoctor.jl/blob/f00ce7b711dc2443d1a24a4b83fdc092692514f7/src/DispatchDoctor.jl#L148-L151) which is checked by the function body before throwing an error:

```julia
func[:body] = quote
    let $closure() = $(func[:body]), $T = $(Base).promote_op($closure)
        if $(type_instability)($T) && !$(is_precompiling)() && $(checking_enabled)()
            $err
        end

        return $closure()::$T
    end
end

```

You will also notice the `is_precompiling` function there. That’s a quality of life thing; I just found it was annoying to get the type instability errors during precompilation. Much easier to use if I can check them by hand later.

And since the `type_instability($T)` gets hardcoded, the LLVM IR is kept nice and safe:

```julia
julia> @stable f(x) = x
f (generic function with 1 method)

julia> @code_llvm f(1)
define i64 @julia_f_560(i64 signext %0) #0 {
top:
  ret i64 %0
}

```

> [@matthias314](#):
>
> to produce problems when the function bodies become sufficiently complex. I’ve tried to produce an example, but so far it has always worked. However, I’ve run into such a problem myself when inferring the return type of functions in [LinearCombinations.jl](https://juliahub.com/ui/Packages/General/LinearCombinations).

Yeah I’m not sure either. I just wrapped some of my other Julia packages with `@stable warnonly=true begin ... end` and stuff seemed to work just fine. Maybe the most recent Julia is better at this?

> [@matthias314](#):
>
> I’ve used `Core.Compiler.return_type`, but I think `promote_op` is just wrapper around it.

The only difference I see is that `promote_op` has special behavior for `Union{}`: [julia/base/promotion.jl at b93cd432a3fd1e88d4d6a5e36f32e6fbfa0a4160 · JuliaLang/julia · GitHub](https://github.com/JuliaLang/julia/blob/b93cd432a3fd1e88d4d6a5e36f32e6fbfa0a4160/base/promotion.jl#L615-L619) which maybe (?) is necessary for special handling of MethodErrors.

Also it looks like `promote_op` is used more throughout the stdlib so I feel a bit safer using it.

But yes if there’s a better way please let me know!

---

<div class="post-metadata">

**Author:** ![matthias314](https://avatars.discourse-cdn.com/v4/letter/m/a88e4f/32.png) [@matthias314](https://discourse.julialang.org/u/matthias314)\
**Post date:** [May 26, 2024, 4:16pm UTC](https://discourse.julialang.org/t/improving-speed-of-runtime-dispatch-detector/114697/52 "2024-05-26T16:16:53Z")

</div>

I’ve tried `DispatchDoctor` with my package SmallCollections.jl. It doesn’t work as expected. Without DispatchDoctor I get

```julia
julia> @code_typed SmallBitSet([1, 2, 3])
CodeInfo(
[...]
) => SmallBitSet{UInt64}

```

and with DispatchDoctor

```julia
julia> @code_typed SmallBitSet([1, 2, 3])
CodeInfo(
[...]
) => SmallBitSet

```

which is not a concrete type. This gives an error when running the test for the package.

Here is what I did it (probably not the most efficient way): I’ve copied the source (v0.1.2) to a new directory, changed the first lines file `src/SmallCollections.jl` to

```julia
using DispatchDoctor
@stable module SmallCollections

```

Then I change into the `src` directory, include `SmallCollections.jl` and say `using .SmallCollections`.

---

<div class="post-metadata">

**Author:** ![MilesCranmer](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/milescranmer/32/21070_2.png) [@MilesCranmer](https://discourse.julialang.org/u/MilesCranmer)\
**Post date:** [May 26, 2024, 5:37pm UTC](https://discourse.julialang.org/t/improving-speed-of-runtime-dispatch-detector/114697/53 "2024-05-26T17:37:13Z")

</div>

You said there was an error when running it, what does the error message say?

Also sometimes `@code_typed` lies (see [Can we support a way to make `@code_typed` stop lying to us all? :) · Issue #32834 · JuliaLang/julia · GitHub](https://github.com/JuliaLang/julia/issues/32834)), so maybe try it with `Test.@inferred` or Cthulhu.jl?

Basically the _before_ result might not actually be true, because `@code_typed` is using incorrect specialisation rules when compared to the actual Julia compiler. `@code_typed` tends to be overly optimistic. The `@stable` macro annotates the return value by the compiler-inferred type, which might be tricking `@code_typed` to be more realistic in the second case you observed.

For full-package use I’ve been trying it out in DynamicExpressions.jl by doing

```julia
module DynamicExpressions
using DispatchDoctor
@stable begin
# package contents
end
end

```

So far the type instabilities found look to be real.

---

<div class="post-metadata">

**Author:** ![MilesCranmer](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/milescranmer/32/21070_2.png) [@MilesCranmer](https://discourse.julialang.org/u/MilesCranmer)\
**Post date:** [May 26, 2024, 7:02pm UTC](https://discourse.julialang.org/t/improving-speed-of-runtime-dispatch-detector/114697/54 "2024-05-26T19:02:46Z")

</div>

It looks like the culprit in your library is `_push`. If I annotate the return value of that function with `::SmallBitSet{U}`, the type instability error goes away. Im not sure really sure why `_push` is type unstable though, maybe something about the for-loop makes the inference struggle.

---

<div class="post-metadata">

**Author:** ![matthias314](https://avatars.discourse-cdn.com/v4/letter/m/a88e4f/32.png) [@matthias314](https://discourse.julialang.org/u/matthias314)\
**Post date:** [May 27, 2024, 2:07am UTC](https://discourse.julialang.org/t/improving-speed-of-runtime-dispatch-detector/114697/55 "2024-05-27T02:07:15Z")

</div>

You have figured it out already, but just for completeness: I do

```julia
julia> include("SmallCollections.jl") # loading modified source code as described above
julia> using .SmallCollections
julia> SmallBitSet([1,2,3])
ERROR: TypeInstabilityError: Instability detected in `SmallBitSet` defined at SmallCollections.jl:3 with arguments `(Vector{Int64},)`. Inferred to be `SmallBitSet`, which is not a concrete type.

```

I still don’t think that there is a type instablity. Two more reasons:

1. The package tests include something analogous to

```julia
julia> @inferred SmallBitSet([1,2,3])

```

(via my own macro `@test_inferred`), and this test passes.

1. Look at the following benchmark:

```julia
function f(n)
    if n <= 8
        SmallBitSet{UInt8}(1:n)
    elseif n <= 16
        SmallBitSet{UInt16}(1:n)
    elseif n <= 32
        SmallBitSet{UInt32}(1:n)
    else
        SmallBitSet{UInt64}(1:n)
    end
end

g(n) = SmallBitSet(1:n) # calls SmallBitSet{UInt}(1:n)

```

```julia
julia> n = 40; @b length(f($n))
25.530 ns (1 allocs: 16 bytes)

julia> n = 40; @b length(g($n))
3.167 ns

```

If there were an instability, then it wouldn’t be so fast and without allocations, I would say.

---

<div class="post-metadata">

**Author:** ![MilesCranmer](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/milescranmer/32/21070_2.png) [@MilesCranmer](https://discourse.julialang.org/u/MilesCranmer)\
**Post date:** [May 27, 2024, 3:08am UTC](https://discourse.julialang.org/t/improving-speed-of-runtime-dispatch-detector/114697/56 "2024-05-27T03:08:53Z")

</div>

Weird I’m not sure. It could also be because you have some `Vararg` which are not specializing, but when you write the number of args explicitly in the benchmark, Julia constant propagation saves you (I see the first `SmallBitSet` is on `args...`). See [Performance Tips · The Julia Language](https://docs.julialang.org/en/v1/manual/performance-tips/#Be-aware-of-when-Julia-avoids-specializing)

I’m really not sure other than that. Would be great if you could make a MWE of this as otherwise this is a tough point to start from.

(Also maybe you could move it to a GitHub issue so we stop sending notifications to people in this thread 😅 )

---

<div class="post-metadata">

**Author:** ![matthias314](https://avatars.discourse-cdn.com/v4/letter/m/a88e4f/32.png) [@matthias314](https://discourse.julialang.org/u/matthias314)\
**Post date:** [May 27, 2024, 3:33am UTC](https://discourse.julialang.org/t/improving-speed-of-runtime-dispatch-detector/114697/57 "2024-05-27T03:33:33Z")

</div>

This looks like an MWE:

```julia
struct SmallBitSet{U}
    mask::U
    global _SmallBitSet(mask::U) where U = new{U}(mask)
end

@stable function _push(mask::U, iter) where U
    for n in iter
        mask |= one(U) << (Int(n)-1)
    end
    _SmallBitSet(mask)
end

```

With `DispatchDoctor`:

```julia
julia> _push(UInt(0), ())
ERROR: TypeInstabilityError: Instability detected in `_push`

```

Without `DispatchDoctor`:

```julia
julia> @code_llvm _push(UInt(0), ())
; @ REPL[2]:1 within `_push`
define [1 x i64] @julia__push_145(i64 zeroext %0) #0 {
top:
; @ REPL[2]:5 within `_push`
; ┌ @ REPL[1]:3 within `_SmallBitSet`
   %unbox.fca.0.insert = insertvalue [1 x i64] zeroinitializer, i64 %0, 0
   ret [1 x i64] %unbox.fca.0.insert
; └
}

```

---

<div class="post-metadata">

**Author:** ![MilesCranmer](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/milescranmer/32/21070_2.png) [@MilesCranmer](https://discourse.julialang.org/u/MilesCranmer)\
**Post date:** [May 27, 2024, 3:48am UTC](https://discourse.julialang.org/t/improving-speed-of-runtime-dispatch-detector/114697/58 "2024-05-27T03:48:41Z")

</div>

(Please post it to the GitHub, not this thread. Also make sure it’s minimal — for example I’m sure the `-1` and `Int()` likely aren’t needed…)

---

<div class="post-metadata">

**Author:** ![MilesCranmer](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/milescranmer/32/21070_2.png) [@MilesCranmer](https://discourse.julialang.org/u/MilesCranmer)\
**Post date:** [May 27, 2024, 8:40am UTC](https://discourse.julialang.org/t/improving-speed-of-runtime-dispatch-detector/114697/59 "2024-05-27T08:40:43Z")

</div>

One more design question… should `@stable` skip unknown macros? For example I realised that `MacroTools.@capture` permits function syntax to specify a similarity search. `@stable` would break that code.

Maybe `@stable` should only propagate through macros if they are a member of an internal list defining known compatible ones (like `@inline`, `@inbounds`, etc)?

---

<div class="post-metadata">

**Author:** ![MilesCranmer](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/milescranmer/32/21070_2.png) [@MilesCranmer](https://discourse.julialang.org/u/MilesCranmer)\
**Post date:** [May 27, 2024, 2:20pm UTC](https://discourse.julialang.org/t/improving-speed-of-runtime-dispatch-detector/114697/60 "2024-05-27T14:20:03Z")

</div>

Oh wait it looks like this isn’t just your package but something stranger about Julia type inference… Sorry about that, @matthias314!

It’s weird stuff. Check this out:

```julia
function or1(x::T, iter) where {T}
    for n in iter
        x |= one(T)
    end
    x
end

# Inferred concrete:
Base.promote_op(or1, UInt64, Tuple{}) # UInt64

```

But if we put the exact same thing in a closure, this doesn’t work:

```julia

function or2(x::T, iter) where {T}
    function closure()
        for n in iter
            x |= one(T)
        end
        x
    end
    return closure()
end

# Unstable inference:
Base.promote_op(or2, UInt64, Tuple{}) # Any

```

However, if we make the contents of the closure “more stable”, we can save it:

```julia
function or3(x::T, iter) where {T}
    function closure()
        reduce(|, map(_ -> one(T), iter); init=x)
    end
    return closure()
end

Base.promote_op(or3, UInt64, Tuple{}) # UInt64

```

Type inference is weird… (Is there any `Base.@constprop`-like magic to fix this?)

Anyways, at least this issue results in a “loud” error. You will know if there is failed inference somewhere. And a failure of inference within a closure might just point to a spot where a refactor is needed (like that for loop).

[Previous page](https://discourse.julialang.org/t/improving-speed-of-runtime-dispatch-detector/114697.md?page=2)

[Next page](https://discourse.julialang.org/t/improving-speed-of-runtime-dispatch-detector/114697.md?page=4)
