@@ -26,7 +26,7 @@ export @test, @test_throws, @test_broken, @test_skip,
2626
2727export @testset
2828export @inferred
29- export detect_ambiguities, detect_unbound_args
29+ export detect_ambiguities, detect_unbound_args, detect_closure_boxes
3030export GenericString, GenericSet, GenericDict, GenericArray, GenericOrder
3131export TestSetException
3232export TestLogger, LogRecord
@@ -2527,7 +2527,7 @@ function detect_ambiguities(mods::Module...;
25272527 allowed_undefineds = nothing )
25282528 @nospecialize
25292529 ambs = Set {Tuple{Method,Method}} ()
2530- mods = collect (mods) :: Vector{ Module}
2530+ mods = Module[mods ... ]
25312531 function sortdefs (m1:: Method , m2:: Method )
25322532 ord12 = cmp (m1. file, m2. file)
25332533 if ord12 == 0
@@ -2557,6 +2557,90 @@ function detect_ambiguities(mods::Module...;
25572557 return collect (ambs)
25582558end
25592559
2560+ """
2561+ detect_closure_boxes(mod1, mod2...; recursive=false)
2562+
2563+ Return a vector of `(Method, varname)` pairs for methods defined in the specified
2564+ modules that allocate `Core.Box` in their lowered code. If `recursive=false`,
2565+ each module is treated as a root module (for example, `Base` matches
2566+ `Base.Compiler`), but submodules of non-root modules are excluded. Use
2567+ `recursive=true` to include submodules of the specified modules. The returned
2568+ `varname` is a `Symbol`, or `:unknown` when a slot name cannot be resolved.
2569+ """
2570+ function detect_closure_boxes (mods:: Module... ; recursive:: Bool = false )
2571+ @nospecialize
2572+ boxes = Tuple{Method,Symbol}[]
2573+ mods = Module[mods... ]
2574+ isempty (mods) && return boxes
2575+
2576+ function is_box_call (expr)
2577+ if ! (expr isa Expr)
2578+ return false
2579+ end
2580+ if expr. head === :call || expr. head === :new
2581+ callee = expr. args[1 ]
2582+ return callee === Core. Box || (callee isa GlobalRef && callee. mod === Core && callee. name === :Box )
2583+ end
2584+ return false
2585+ end
2586+
2587+ function slot_name (ci, slot):: Symbol
2588+ if slot isa Core. SlotNumber
2589+ idx = Int (slot. id)
2590+ if 1 <= idx <= length (ci. slotnames)
2591+ return ci. slotnames[idx]
2592+ end
2593+ end
2594+ return Symbol (string (slot))
2595+ end
2596+
2597+ function root_module (mod:: Module )
2598+ while true
2599+ parent = parentmodule (mod)
2600+ if parent === mod || parent === Main || parent === Core
2601+ return mod
2602+ end
2603+ mod = parent
2604+ end
2605+ end
2606+
2607+ function matches_module (mod:: Module )
2608+ mod in mods && return true
2609+ recursive && return is_in_mods (mod, true , mods)
2610+ return root_module (mod) in mods
2611+ end
2612+
2613+ function scan_method! (m:: Method )
2614+ matches_module (parentmodule (m)) || return
2615+ ci = try
2616+ Base. uncompressed_ast (m)
2617+ catch
2618+ return
2619+ end
2620+ for stmt in ci. code
2621+ if stmt isa Expr && stmt. head === :(= )
2622+ lhs = stmt. args[1 ]
2623+ rhs = stmt. args[2 ]
2624+ if is_box_call (rhs)
2625+ push! (boxes, (m, slot_name (ci, lhs)))
2626+ end
2627+ elseif is_box_call (stmt)
2628+ push! (boxes, (m, :unknown ))
2629+ end
2630+ end
2631+ end
2632+
2633+ Base. visit (Core. methodtable) do m
2634+ scan_method! (m)
2635+ end
2636+
2637+ sort! (boxes, by = entry -> begin
2638+ m, var = entry
2639+ (m. file, m. line, var, m. name, m. sig)
2640+ end )
2641+ return boxes
2642+ end
2643+
25602644"""
25612645 detect_unbound_args(mod1, mod2...; recursive=false, allowed_undefineds=nothing)
25622646
@@ -2583,7 +2667,7 @@ function detect_unbound_args(mods...;
25832667 allowed_undefineds= nothing )
25842668 @nospecialize mods
25852669 ambs = Set {Method} ()
2586- mods = collect (mods) :: Vector{ Module}
2670+ mods = Module[mods ... ]
25872671 function examine (mt:: Core.MethodTable )
25882672 for m in Base. MethodList (mt)
25892673 is_in_mods (parentmodule (m), recursive, mods) || continue
0 commit comments