Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Specialization of Type{T} within keyword arguments #27

Open
MilesCranmer opened this issue Jun 2, 2024 · 2 comments
Open

Specialization of Type{T} within keyword arguments #27

MilesCranmer opened this issue Jun 2, 2024 · 2 comments

Comments

@MilesCranmer
Copy link
Owner

MilesCranmer commented Jun 2, 2024

The following behavior occurs:

julia> using DispatchDoctor

julia> @stable f(; t::Type{T}) where {T} = one(T)
f (generic function with 1 method)

julia> f(t=Float32)
ERROR: TypeInstabilityError: Instability detected in `f`
defined at REPL[2]:1 with keyword arguments
`@NamedTuple{t::DataType}` and parameters `(:T => Float32,)`.
Inferred to be `Any`, which is not a concrete type.

I think this may be "real" and in some ways a fundamental issue with how Julia handles keyword calls. Actually Test.@inferred does the same thing:

julia> f(; t::Type{T}) where {T} = T
f (generic function with 1 method)

julia> Test.@inferred f(t=Float32)
ERROR: return type Type{Float32} does not match inferred return type DataType

Keyword arguments are passed to Julia functions as a NamedTuple via Core.kwcall. However, we have that

julia> typeof((; t=Float32))
@NamedTuple{t::DataType}

and thus Julia relies on constant propagation for this to not be type unstable, it seems. Or at least Base.promote_op can't figure it out from the types alone. The only way this is type stable is if you rely constant propagation.

In other words, if your function's return type depends on a keyword argument which is itself a type (NOT a value, it must be a DataType!), then that function seems to be type unstable, and so should be rewritten. The only way it can be type stable is if you get constant propagation.

@MilesCranmer
Copy link
Owner Author

Note also you can get around this with a trick:

julia> f(; t::Val{T}) where {T} = T
f (generic function with 1 method)

julia> Test.@inferred f(t=Val(Float32))
Float32

@MilesCranmer
Copy link
Owner Author

Also posted an issue on Julia JuliaLang/julia#54661

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant