InteractiveUtils: support type annotations as substitutes for values#57909
InteractiveUtils: support type annotations as substitutes for values#57909JeffBezanson merged 24 commits intoJuliaLang:masterfrom
Conversation
|
Thank you, I've wanted this for a long time! I don't really like the implementation method of using a special |
|
Does this also support |
It doesn't support matching the same |
|
Not to keep tacking on feature requests, but |
|
That's already there :) |
It is not worth the possible interference with macro-defined syntax for :(::) operands
Agreed, especially that it is leaked to the user in error messages when something goes wrong. I removed it. |
…pes` This macro broke in a few cases, and while fixing it it became clear that the macro could simply wrap around `gen_call_with_extracted_types`
d205f5f to
17468df
Compare
This caused more trouble than it is worth, best to keep the current implementation as is for now.
This is now supported. Because the previous implementation was relying on runtime evaluation to dynamically extract types in certain places, and we now do most of the work when processing the macro, a few other implementation changes were necessary. Hopefully, observable behavior should remain identical as it was before, especially when a few packages use julia> @descend schedule(::Task)
[ Info: tracking Base
schedule(t::Task) @ Base ~/julia/wip4/base/task.jl:980
980 function schedule(t::Task::Task)::Task
981 # [task] created -scheduled-> wait_time
982 maybe_record_enqueued!(t::Task)
983 enq_work(t::Task)::Task
984 end
[truncated]I also spotted a bug (reproducible on 1.11.4) where julia> @code_typed round.([1.3]; digits = 3)
ERROR: MethodError: no method matching typesof(::Vector{Float64}; digits::Int64)
This method may not support any kwargs.which requires a fair bit of refactoring, that I leave for a follow-up PR to avoid piling on more things on this one. This PR should be good for another round of review. |
topolarity
left a comment
There was a problem hiding this comment.
Didn't do a complete read-through yet, but it's looking pretty good to me - Thanks @serenity4 !
| return :($(fcn)(Core.kwcall, $tt; $(kws...))) | ||
| elseif ex0.head === :call | ||
| argtypes = Any[get_typeof(arg) for arg in ex0.args[2:end]] | ||
| if ex0.args[1] === :^ && length(ex0.args) >= 3 && isa(ex0.args[3], Int) |
There was a problem hiding this comment.
This is wild that we have to do this here 😅
There was a problem hiding this comment.
Yeah... don't look too closely at this ancient code, you may find other weird things 😅
|
I added a few minor comments on the code style, but I think the fundamental approach is correct. Great job implementing this feature! |
Co-authored-by: Shuhei Kadowaki <40514306+aviatesk@users.noreply.github.com>
…on macros (#58222) Continuing the work done at #57909, this PR adds support for the following syntax: ```julia @code_typed f(some_undef_var::Int) # some_undef_var is ignored, only the annotation is used @code_typed f(; x::Int) # same here, the name is used but not the value ``` This should allow us to copy and paste signatures found in stacktraces, such as ```julia julia> f(x; y = 3) = error() f (generic function with 1 method) julia> f(1) ERROR: Stacktrace: [1] error() @ Base ./error.jl:45 [2] f(x::Int64; y::Int64) @ Main ./REPL[40]:1 [3] top-level scope @ REPL[41]:1 julia> asin(-2) ERROR: DomainError with -2.0: asin(x) is not defined for |x| > 1. Stacktrace: [1] asin_domain_error(x::Float64) @ Base.Math ./special/trig.jl:429 [2] asin(x::Float64) @ Base.Math ./special/trig.jl:443 ``` where any function call may be copied and pasted into `@code_typed`, `@edit` etc as is, provided that the function and argument types are defined in the active module (i.e. `Main`). Thanks @topolarity for the idea. --------- Co-authored-by: Cédric Belmant <cedric.belmant@juliahub.com>
…uliaLang#57909) Extend code introspection macros (`@which`, `@code_typed` and friends) to recognize type annotations of the form `f(1, ::Float64)` as types to be forwarded as is to the relevant function. --------- Co-authored-by: Cédric Belmant <cedric.belmant@juliahub.com> Co-authored-by: Shuhei Kadowaki <40514306+aviatesk@users.noreply.github.com>
…on macros (JuliaLang#58222) Continuing the work done at JuliaLang#57909, this PR adds support for the following syntax: ```julia @code_typed f(some_undef_var::Int) # some_undef_var is ignored, only the annotation is used @code_typed f(; x::Int) # same here, the name is used but not the value ``` This should allow us to copy and paste signatures found in stacktraces, such as ```julia julia> f(x; y = 3) = error() f (generic function with 1 method) julia> f(1) ERROR: Stacktrace: [1] error() @ Base ./error.jl:45 [2] f(x::Int64; y::Int64) @ Main ./REPL[40]:1 [3] top-level scope @ REPL[41]:1 julia> asin(-2) ERROR: DomainError with -2.0: asin(x) is not defined for |x| > 1. Stacktrace: [1] asin_domain_error(x::Float64) @ Base.Math ./special/trig.jl:429 [2] asin(x::Float64) @ Base.Math ./special/trig.jl:443 ``` where any function call may be copied and pasted into `@code_typed`, `@edit` etc as is, provided that the function and argument types are defined in the active module (i.e. `Main`). Thanks @topolarity for the idea. --------- Co-authored-by: Cédric Belmant <cedric.belmant@juliahub.com>
…rospection macros (#58349) This PR includes a fix and a feature for code introspection macros (`@code_typed`, `@code_llvm` and friends *but not* `@which`, `@edit`, etc): - Fixes a bug for expressions of the form `f.(x; y = 3)`, for which keyword arguments were not properly handled and led to an internal error. - Adds support for broadcasting assignments of the form `x .= f(y)`, `x .<<= f.(y, z)`, etc. The way this was (and still is) implemented is by constructing a temporary function, `f(x1, x2, x3, ...) = <body>` and feeding that to code introspection functions. This trick doesn't apply to `@which` and `@edit`, which need to map to a single function call (we could arguably choose to target `materialize`/`materialize!`, but this behavior could be a bit surprising and difficult to support). The switch differentiating the families of macro `@code_typed`/`@code_llvm` and `@which`/`@edit` etc was further exposed as an additional argument to `gen_call_with_extracted_types` and `gen_call_with_extracted_types_and_kwargs`, which default to the previous behavior (differentiating them based on whether their name starts with `code_`). The intent is to allow other macros such as `Cthulhu.@descend` to register themselves as code introspection macros. Quick tests indicate that it works as intended, e.g. with this PR Cthulhu supports `@descend [1, 2] .+= [2, 3]` (or equivalently, as added in #57909, `@descend ::Vector{Int} .+= ::Vector{Int}`). I originally just went for the fix, and after some refactoring I realized the feature was very straightforward to implement.
This PR extends code introspection macros (
@which,@code_typedand friends) to recognize type annotations of the formf(1, ::Float64)as types to be forwarded as is to the relevant function. Here are a few examples:This has a few advantages:
@code_typed f(::Any)which will be destructured ascode_typed(f, (Any,)). Previously, macros could not express this and users had to rely on using the functional form.Varargarguments may be expressed asf(::Float64...)orf(::Vararg{Float64}), which was also not expressible previously.Task.This is implemented quite straightforwardly by recursively parsing all
::Tannotations and turning them into an intermediateInterpolatedType(T)structure, which is unwrapped asTwhen the types of the arguments are extracted before callingcode_typed.I also added support for
(::typeof(sum))(vec)forms, where a function type annotation is treated as its only instance (if we don't have a singleton type, an error is thrown), making it equivalent tosum(vec). The original motivation was to implement type-annotating callable objects such as(::Returns{Int})(args...), but as this requires changes to the underlying introspection functions I preferred to leave that out. I'd be happy to try adding support for it in a future PR if there are no objections.