Reference

Macros

DispatchDoctor._Macros.@stableMacro
@stable [options...] [code_block]

A macro to enforce type stability in functions. When applied, it ensures that the return type of the function is concrete. If type instability is detected, a TypeInstabilityError is thrown.

Options

  • default_mode::String="error":
    • Change the default mode from "error" to "warn" to only emit a warning, or "disable" to disable type instability checks by default.
    • To locally or globally override the mode for a package that uses DispatchDoctor, you can use the "instability_check" key in your LocalPreferences.toml (typically configured with Preferences.jl).
  • default_codegen_level::String="debug":
    • Set the code generation level to "min" to only generate a single function body for each stabilized function. The default, "debug", generates an entire duplicate function so that @code_warntype can be used.
    • To locally or globally override the code generation level for a package that uses DispatchDoctor, you can use the "instability_check_codegen_level" key in your LocalPreferences.toml.
  • default_union_limit::Int=1:
    • Sets the maximum elements in a union to be considered stable. The default is 1, meaning that all unions are considered unstable. A value of 2 would indicate that Union{Float32,Float64} is considered stable, but Union{Float16,Float32,Float64} is not.
    • To locally or globally override the union limit for a package that uses DispatchDoctor, you can use the "instability_check_union_limit" key in your LocalPreferences.toml.

Example

using DispatchDoctor: @stable

@stable function relu(x)
    if x > 0
        return x
    else
        return 0.0
    end
end

which will automatically flag any type instability:

julia> relu(1.0)
1.0

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

Extended help

You may also apply @stable to arbitrary blocks of code, such as begin or module, and have it be applied to all functions. (Just note that this skips closure functions.)

using DispatchDoctor: @stable

@stable begin
    f(x) = x
    g(x) = x > 0 ? x : 0.0
    @unstable begin
        g(x::Int) = x > 0 ? x : 0.0
    end
    module A
        h(x) = x
        include("myfile.jl")
    end
end

This @stable will apply to f, g, h, as well as all functions within myfile.jl. It skips the definition g(x::Int), meaning that when Int input is provided to g, type instability is not detected.

source

Utilities

If you wish to turn off @stable for a single function call, you can use allow_unstable:

DispatchDoctor._RuntimeChecks.allow_unstableFunction
allow_unstable(f::F) where {F<:Function}

Globally disable type DispatchDoctor instability checks within the provided function f.

This function allows you to execute a block of code where type instability checks are disabled. It ensures that the checks are re-enabled after the block is executed, even if an error occurs.

This function uses a ReentrantLock and will throw an error if used from two tasks at once.

Usage

allow_unstable() do
    # do unstable stuff
end

Arguments

  • f::F: A function to be executed with type instability checks disabled.

Returns

  • The result of the function f.

Notes

You cannot call allow_unstable from two tasks at once. An error will be thrown if you try to do so.

source

@stable will normally interact with macros by propagating them to the function definition as well as the function simulator. If you would like to change this behavior, or declare a macro as being incompatible with @stable, you can use register_macro!:

DispatchDoctor._Interactions.register_macro!Function
register_macro!(macro_name::Symbol, behavior::MacroInteractions)

Register a macro with a specified behavior in the MACRO_BEHAVIOR list.

This function adds a new macro and its associated behavior to the global list that tracks how macros should be treated when encountered during the stabilization process. The behavior can be one of CompatibleMacro, IncompatibleMacro, or DontPropagateMacro, which influences how the @stable macro interacts with the registered macro.

The default behavior for @stable is to assume CompatibleMacro unless explicitly declared.

Arguments

  • macro_name::Symbol: The symbol representing the macro to register.
  • behavior::MacroInteractions: The behavior to associate with the macro, which dictates how it should be handled.

Examples

using DispatchDoctor: register_macro!, IncompatibleMacro

register_macro!(Symbol("@mymacro"), IncompatibleMacro)
source

Internals