aviatesk / JET.jl

An experimental code analyzer for Julia. No need for additional type annotations.
https://aviatesk.github.io/JET.jl/dev/
MIT License
708 stars 29 forks source link

Use depth-limited type printing #601

Closed charleskawczynski closed 2 months ago

charleskawczynski commented 5 months ago

Add depth-limited type printing.

This PR takes advantage of https://github.com/JuliaLang/julia/pull/49795 by truncating the printing of types.

I'm sure that not all of these changes are needed, and we probably want to change or expose changing some parameters, but this reproducer does work for me:

Full reproducer

using Revise
import JET
struct F49231{a,b,c,d,e,f,g}
    num::g
end;
f = F49231{Float64,Float32,Int,String,AbstractString,6,Float64}(1);
bar(x) = rand() > 0.5 ? x : Any[0][1]
mysum(x) = sum(y-> bar(x.num), 1:5; init=0)
a = Any[f];
mysum(a[1]) # make sure it runs
JET.@test_opt mysum(a[1])

Results, main:

JET-test failed at REPL[8]:1
  Expression: #= REPL[8]:1 =# JET.@test_opt mysum(a[1])
  ═════ 5 possible errors found ═════
  ┌ mysum(x::F49231{Float64, Float32, Int64, String, AbstractString, 6, Float64}) @ Main ./REPL[6]:1
  │┌ kwcall(::@NamedTuple{init::Int64}, ::typeof(sum), f::var"#1#2"{F49231{Float64, Float32, Int64, String, AbstractString, 6, Float64}}, a::UnitRange{Int64}) @ Base ./reducedim.jl:1011
  ││┌ sum(f::var"#1#2"{F49231{Float64, Float32, Int64, String, AbstractString, 6, Float64}}, a::UnitRange{Int64}; dims::Colon, kw::@Kwargs{init::Int64}) @ Base ./reducedim.jl:1011
  │││┌ kwcall(::@NamedTuple{init::Int64}, ::typeof(Base._sum), f::var"#1#2"{F49231{Float64, Float32, Int64, String, AbstractString, 6, Float64}}, a::UnitRange{Int64}, ::Colon) @ Base ./reducedim.jl:1015
  ││││┌ _sum(f::var"#1#2"{F49231{Float64, Float32, Int64, String, AbstractString, 6, Float64}}, a::UnitRange{Int64}, ::Colon; kw::@Kwargs{init::Int64}) @ Base ./reducedim.jl:1015
  │││││┌ kwcall(::@NamedTuple{init::Int64}, ::typeof(mapreduce), f::var"#1#2"{F49231{Float64, Float32, Int64, String, AbstractString, 6, Float64}}, op::typeof(Base.add_sum), A::UnitRange{Int64}) @ Base ./reducedim.jl:357
  ││││││┌ mapreduce(f::var"#1#2"{F49231{Float64, Float32, Int64, String, AbstractString, 6, Float64}}, op::typeof(Base.add_sum), A::UnitRange{Int64}; dims::Colon, init::Int64) @ Base ./reducedim.jl:357
  │││││││┌ _mapreduce_dim(f::var"#1#2"{F49231{Float64, Float32, Int64, String, AbstractString, 6, Float64}}, op::typeof(Base.add_sum), nt::Int64, A::UnitRange{Int64}, ::Colon) @ Base ./reducedim.jl:362
  ││││││││┌ mapfoldl_impl(f::var"#1#2"{F49231{Float64, Float32, Int64, String, AbstractString, 6, Float64}}, op::typeof(Base.add_sum), nt::Int64, itr::UnitRange{Int64}) @ Base ./reduce.jl:44
  │││││││││┌ foldl_impl(op::Base.MappingRF{var"#1#2"{F49231{Float64, Float32, Int64, String, AbstractString, 6, Float64}}, Base.BottomRF{typeof(Base.add_sum)}}, nt::Int64, itr::UnitRange{Int64}) @ Base ./reduce.jl:48
  ││││││││││┌ _foldl_impl(op::Base.MappingRF{var"#1#2"{F49231{Float64, Float32, Int64, String, AbstractString, 6, Float64}}, Base.BottomRF{typeof(Base.add_sum)}}, init::Int64, itr::UnitRange{Int64}) @ Base ./reduce.jl:58
  │││││││││││┌ (::Base.MappingRF{var"#1#2"{F49231{Float64, Float32, Int64, String, AbstractString, 6, Float64}}, Base.BottomRF{typeof(Base.add_sum)}})(acc::Int64, x::Int64) @ Base ./reduce.jl:100
  ││││││││││││┌ (::Base.BottomRF{typeof(Base.add_sum)})(acc::Int64, x::Any) @ Base ./reduce.jl:86
  │││││││││││││ runtime dispatch detected: add_sum(acc::Int64, x::Any)::Any
  ││││││││││││└────────────────────
  ││││││││││┌ _foldl_impl(op::Base.MappingRF{var"#1#2"{F49231{Float64, Float32, Int64, String, AbstractString, 6, Float64}}, Base.BottomRF{typeof(Base.add_sum)}}, init::Int64, itr::UnitRange{Int64}) @ Base ./reduce.jl:62
  │││││││││││┌ (::Base.MappingRF{var"#1#2"{F49231{Float64, Float32, Int64, String, AbstractString, 6, Float64}}, Base.BottomRF{typeof(Base.add_sum)}})(acc::Any, x::Int64) @ Base ./reduce.jl:100
  ││││││││││││┌ (::Base.BottomRF{typeof(Base.add_sum)})(::Base._InitialValue, x::Any) @ Base ./reduce.jl:85
  │││││││││││││ runtime dispatch detected: Base.reduce_first(add_sum, x::Any)::Any
  ││││││││││││└────────────────────
  ││││││││││││┌ (::Base.BottomRF{typeof(Base.add_sum)})(acc::Any, x::Any) @ Base ./reduce.jl:86
  │││││││││││││ runtime dispatch detected: add_sum(acc::Any, x::Any)::Any
  ││││││││││││└────────────────────
  ││││││││││┌ _foldl_impl(op::Base.MappingRF{var"#1#2"{F49231{Float64, Float32, Int64, String, AbstractString, 6, Float64}}, Base.BottomRF{typeof(Base.add_sum)}}, init::Int64, itr::UnitRange{Int64}) @ Base ./reduce.jl:62
  │││││││││││ runtime dispatch detected: op::Base.MappingRF{var"#1#2"{F49231{Float64, Float32, Int64, String, AbstractString, 6, Float64}}, Base.BottomRF{typeof(Base.add_sum)}}(%57::Any, %68::Int64)::Any
  ││││││││││└────────────────────
  │││││││││┌ foldl_impl(op::Base.MappingRF{var"#1#2"{F49231{Float64, Float32, Int64, String, AbstractString, 6, Float64}}, Base.BottomRF{typeof(Base.add_sum)}}, nt::Int64, itr::UnitRange{Int64}) @ Base ./reduce.jl:49
  ││││││││││┌ reduce_empty_iter(op::Base.MappingRF{var"#1#2"{F49231{Float64, Float32, Int64, String, AbstractString, 6, Float64}}, Base.BottomRF{typeof(Base.add_sum)}}, itr::UnitRange{Int64}) @ Base ./reduce.jl:383
  │││││││││││┌ reduce_empty_iter(op::Base.MappingRF{var"#1#2"{F49231{Float64, Float32, Int64, String, AbstractString, 6, Float64}}, Base.BottomRF{typeof(Base.add_sum)}}, itr::UnitRange{Int64}, ::Base.HasEltype) @ Base ./reduce.jl:384
  ││││││││││││┌ reduce_empty(op::Base.MappingRF{var"#1#2"{F49231{Float64, Float32, Int64, String, AbstractString, 6, Float64}}, Base.BottomRF{typeof(Base.add_sum)}}, ::Type{Int64}) @ Base ./reduce.jl:361
  │││││││││││││ runtime dispatch detected: Base.mapreduce_empty(%1::var"#1#2"{F49231{Float64, Float32, Int64, String, AbstractString, 6, Float64}}, [quote]::Base.BottomRF{typeof(Base.add_sum)}, ::Int64)
  ││││││││││││└────────────────────

ERROR: There was an error during testing

Results, this branch:

julia> JET.maxtypedepth[] = 2;
julia> JET.@test_opt mysum(a[1])
JET-test failed at REPL[8]:1
  Expression: #= REPL[8]:1 =# JET.@test_opt mysum(a[1])
  ═════ 5 possible errors found ═════
  ┌ mysum(x::F49231{…}) @ Main ./REPL[6]:1
  │┌ kwcall(::@NamedTuple{…}, ::typeof(sum), f::var"#1#2"{…}, a::UnitRange{…}) @ Base ./reducedim.jl:1011
  ││┌ sum(f::var"#1#2"{…}, a::UnitRange{…}; dims::Colon, kw::Base.Pairs{…}) @ Base ./reducedim.jl:1011
  │││┌ kwcall(::@NamedTuple{…}, ::typeof(Base._sum), f::var"#1#2"{…}, a::UnitRange{…}, ::Colon) @ Base ./reducedim.jl:1015
  ││││┌ _sum(f::var"#1#2"{…}, a::UnitRange{…}, ::Colon; kw::Base.Pairs{…}) @ Base ./reducedim.jl:1015
  │││││┌ kwcall(::@NamedTuple{…}, ::typeof(mapreduce), f::var"#1#2"{…}, op::typeof(Base.add_sum), A::UnitRange{…}) @ Base ./reducedim.jl:357
  ││││││┌ mapreduce(f::var"#1#2"{…}, op::typeof(Base.add_sum), A::UnitRange{…}; dims::Colon, init::Int64) @ Base ./reducedim.jl:357
  │││││││┌ _mapreduce_dim(f::var"#1#2"{…}, op::typeof(Base.add_sum), nt::Int64, A::UnitRange{…}, ::Colon) @ Base ./reducedim.jl:362
  ││││││││┌ mapfoldl_impl(f::var"#1#2"{…}, op::typeof(Base.add_sum), nt::Int64, itr::UnitRange{…}) @ Base ./reduce.jl:44
  │││││││││┌ foldl_impl(op::Base.MappingRF{…}, nt::Int64, itr::UnitRange{…}) @ Base ./reduce.jl:48
  ││││││││││┌ _foldl_impl(op::Base.MappingRF{…}, init::Int64, itr::UnitRange{…}) @ Base ./reduce.jl:58
  │││││││││││┌ (::Base.MappingRF{…})(acc::Int64, x::Int64) @ Base ./reduce.jl:100
  ││││││││││││┌ (::Base.BottomRF{…})(acc::Int64, x::Any) @ Base ./reduce.jl:86
  │││││││││││││ runtime dispatch detected: add_sum(acc::Int64, x::Any)::Any
  ││││││││││││└────────────────────
  ││││││││││┌ _foldl_impl(op::Base.MappingRF{…}, init::Int64, itr::UnitRange{…}) @ Base ./reduce.jl:62
  │││││││││││┌ (::Base.MappingRF{…})(acc::Any, x::Int64) @ Base ./reduce.jl:100
  ││││││││││││┌ (::Base.BottomRF{…})(::Base._InitialValue, x::Any) @ Base ./reduce.jl:85
  │││││││││││││ runtime dispatch detected: Base.reduce_first(add_sum, x::Any)::Any
  ││││││││││││└────────────────────
  ││││││││││││┌ (::Base.BottomRF{…})(acc::Any, x::Any) @ Base ./reduce.jl:86
  │││││││││││││ runtime dispatch detected: add_sum(acc::Any, x::Any)::Any
  ││││││││││││└────────────────────
  ││││││││││┌ _foldl_impl(op::Base.MappingRF{…}, init::Int64, itr::UnitRange{…}) @ Base ./reduce.jl:62
  │││││││││││ runtime dispatch detected: op::Base.MappingRF{var"#1#2"{F49231{Float64, Float32, Int64, String, AbstractString, 6, Float64}}, Base.BottomRF{typeof(Base.add_sum)}}(%57::Any, %68::Int64)::Any
  ││││││││││└────────────────────
  │││││││││┌ foldl_impl(op::Base.MappingRF{…}, nt::Int64, itr::UnitRange{…}) @ Base ./reduce.jl:49
  ││││││││││┌ reduce_empty_iter(op::Base.MappingRF{…}, itr::UnitRange{…}) @ Base ./reduce.jl:383
  │││││││││││┌ reduce_empty_iter(op::Base.MappingRF{…}, itr::UnitRange{…}, ::Base.HasEltype) @ Base ./reduce.jl:384
  ││││││││││││┌ reduce_empty(op::Base.MappingRF{…}, ::Type{…}) @ Base ./reduce.jl:361
  │││││││││││││ runtime dispatch detected: Base.mapreduce_empty(%1::var"#1#2"{F49231{Float64, Float32, Int64, String, AbstractString, 6, Float64}}, [quote]::Base.BottomRF{typeof(Base.add_sum)}, ::Int64)
  ││││││││││││└────────────────────

ERROR: There was an error during testing

Closes #616.

charleskawczynski commented 5 months ago

I had local changes that I wanted to commit, and I had already amended them, so I just squashed and pushed up all the fixes.

@aviatesk, could you take a second look?

Also, I'm not sure how performant this pattern is:

        buf = IOBuffer()
        _print_signature(buf, a, config; kwargs...)
        write(io, Base_type_depth_limit(buf; maxdepth=2));

. Maybe it's fine? But someone who uses IOBuffers more regularly might recognize a better way.

codecov[bot] commented 5 months ago

Codecov Report

All modified and coverable lines are covered by tests :white_check_mark:

Project coverage is 89.46%. Comparing base (93f06e2) to head (8105f34). Report is 4 commits behind head on master.

:exclamation: Current head 8105f34 differs from pull request most recent head 0f88de4. Consider uploading reports for the commit 0f88de4 to get more accurate results

Additional details and impacted files ```diff @@ Coverage Diff @@ ## master #601 +/- ## ========================================== + Coverage 89.42% 89.46% +0.03% ========================================== Files 10 10 Lines 3008 3018 +10 ========================================== + Hits 2690 2700 +10 Misses 318 318 ```

:umbrella: View full report in Codecov by Sentry.
:loudspeaker: Have feedback on the report? Share it here.

aviatesk commented 5 months ago

I'll give another review on this PR next week since I'm quite busy until then.

aviatesk commented 4 months ago

Sorry for being late to give a review. I will try to do this weekend or so.

charleskawczynski commented 4 months ago

No problem 🙂

charleskawczynski commented 3 months ago

Bump 🙂

charleskawczynski commented 2 months ago

Is there anything else needed for this PR? @aviatesk?

charleskawczynski commented 2 months ago

I’m happy to put more work into it, I’m just not sure what else is needed

aviatesk commented 2 months ago

I apologize for the delay in reviewing; I haven't had time to work on JET recently. The changes you've made look great. Thanks so much for contributing.

aviatesk commented 2 months ago

Could you please add a test case to test/ui/test_print.jl, using this commit as a reference: https://github.com/JuliaLang/julia/commit/a43ca052f2c9c29c812f02701600a9f6533507cb#diff-8d178520d08f2b4258b4bc414729ac2f1f0e65a27c176003f981cbd0b5b72e58? If it's too complicated, just let me know, and I'll take care of it.

aviatesk commented 2 months ago

And don't care test failures on 1.11 and nightly-they are known to be broken and we can proceed as far as tests on v1.10 are passing.

charleskawczynski commented 2 months ago

Could you please add a test case to test/ui/test_print.jl, using this commit as a reference: https://github.com/JuliaLang/julia/commit/a43ca052f2c9c29c812f02701600a9f6533507cb#diff-8d178520d08f2b4258b4bc414729ac2f1f0e65a27c176003f981cbd0b5b72e58? If it's too complicated, just let me know, and I'll take care of it.

I'm happy to add tests for this. The first test I have that seems to work fine is simply:

using Test
import JET
struct F49231{a,b,c,d,e,f,g}
    num::g
end;
bar(x) = rand() > 0.5 ? x : Any[0][1]
mysum(x) = sum(y-> bar(x.num), 1:5; init=0)
@testset "Depth-limited type printing" begin
    f = F49231{Float64,Float32,Int,String,AbstractString,6,Float64}(1)
    Ftype = Tuple{Vector{typeof(f)}}
    result = JET.report_opt(Ftype) do a
        mysum(a[1]) # runtime dispatch !
    end
    buf = IOBuffer()
    show(buf, result)
    s = String(take!(buf))
    @test occursin("F49231{…}", s)
end

However, I'm remembering that one thing I was a bit dissatisfied about with this PR was hard-coding maxdepth = 2. Specifically:

 function print_signature(io, sig::Signature, config; kwargs...)
     for a in sig
-        _print_signature(io, a, config; kwargs...)
+        buf = IOBuffer()
+        _print_signature(buf, a, config; kwargs...)
+        # Let's use maxdepth=2, so that unnamed
+        # functions still show types we recognize.
+        write(io, Base_type_depth_limit(buf; maxdepth=2));
     end
 end

function print_frame_sig(io, frame)
    mi = frame.linfo
    m = mi.def
    if m isa Module
        Base.show_mi(io, mi, #=from_stackframe=#true)
    else
        buf = IOBuffer()
        ioc = IOContext(buf, :backtrace=>true, :limit=>true)
        Base.StackTraces.show_spec_sig(ioc, m, mi.specTypes)
        io = IOContext(io, :backtrace=>true, :limit=>true)
        write(io, Base_type_depth_limit(buf; maxdepth=2));
    end
end

Would you be opposed to adding maxdepth to OptAnalyzer, so that users can pass it in from the jetconfigs kwargs?

aviatesk commented 2 months ago

The test case looks nice.

Would you be opposed to adding maxdepth to OptAnalyzer, so that users can pass it in from the jetconfigs kwargs?

No, it would be nice to add the proposed maxdepth configuration to jetconfigs. Implementing a new configuration might be a bit tricky though, so if it feels too complicated, you can leave things as they are for now. We can always make it configurable in a follow-up.

charleskawczynski commented 2 months ago

Great 🙂, I pushed up the test. I also made maxdepth to maxtypedepth to avoid confusion with depth. And then I moved maxtypedepth to a const Ref, so that users can interactively change it if they want to set more or less type information. I think this is ready to go, unless you have any more suggestions.

aviatesk commented 2 months ago

Got it. I'll be adding a few follow-up commits myself.

charleskawczynski commented 2 months ago

Thank you @aviatesk! This will be a quantum leap in quality of life / usability for us. We really look forward to the next release!

aviatesk commented 2 months ago

I've made a few updates. Please feel free to add any adjustments. I believe we're nearly ready to merge.

charleskawczynski commented 2 months ago

Alright, I pushed the changes, thanks again @aviatesk!

aviatesk commented 2 months ago

Thanks a lot @charleskawczynski. This features is planned to be included in the next release.

charleskawczynski commented 2 months ago

Thank you so much @aviatesk!