Skip to content

Commit 2630b65

Browse files
authored
Fix BoundsError with mixed file provenance not caused by macros (#75)
Was causing several stdlib failures. MWE: ``` julia> ex = Meta.parse("begin x = 111 x = 222 end") JuliaLowering.core_lowering_hook(ex, Main, "foo.jl", 100) ``` If `core_lowering_hook` is given one filename (e.g. "none" from `@eval`), but some part of the expression contains LineNumberNodes with a different filename, we trigger the "inlined macro-expansion" logic in the debuginfo generator, which assumes new filenames are from new macro expansions atop the old filename. The violated invariant is that the list of files in this statement's flattened provenance shares some prefix with the last statement's list of files. This fix assumes there is some base file that all statements share, and normalizes different base filenames to the first it sees. Aside: Not sure if this stack logic is 100% correct given that two adjacent statements can share arbitrarily many file stack entries despite being from different macro expansions.
1 parent 7ebc13f commit 2630b65

File tree

2 files changed

+17
-7
lines changed

2 files changed

+17
-7
lines changed

src/eval.jl

Lines changed: 12 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -79,20 +79,25 @@ end
7979

8080
function add_ir_debug_info!(current_codelocs_stack, stmt)
8181
locstk = [(filename(e), source_location(e)[1]) for e in flattened_provenance(stmt)]
82-
for j in 1:max(length(locstk), length(current_codelocs_stack))
83-
if j > length(locstk) || (length(current_codelocs_stack) >= j &&
84-
current_codelocs_stack[j][1] != locstk[j][1])
85-
while length(current_codelocs_stack) >= j
82+
for j in 1:length(locstk)
83+
if j === 1 && current_codelocs_stack[j][1] != locstk[j][1]
84+
# dilemma: the filename stack here shares no prefix with that of the
85+
# previous statement, where differing filenames usually (j > 1) mean
86+
# a different macro expansion has started at this statement. guess
87+
# that both files are the same, and inherit the previous filename.
88+
locstk[j] = (current_codelocs_stack[j][1], locstk[j][2])
89+
end
90+
if j < length(current_codelocs_stack) && (j === length(locstk) ||
91+
current_codelocs_stack[j+1][1] != locstk[j+1][1])
92+
while j < length(current_codelocs_stack)
8693
info = pop!(current_codelocs_stack)
8794
push!(last(current_codelocs_stack)[2], info)
8895
end
89-
end
90-
if j > length(locstk)
91-
break
9296
elseif j > length(current_codelocs_stack)
9397
push!(current_codelocs_stack, (locstk[j][1], [], Vector{Int32}()))
9498
end
9599
end
100+
@assert length(locstk) === length(current_codelocs_stack)
96101
for (j, (file,line)) in enumerate(locstk)
97102
fn, edges, codelocs = current_codelocs_stack[j]
98103
@assert fn == file

test/hooks.jl

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,11 @@ const JL = JuliaLowering
1717
val = Core.eval(test_mod, out[1])
1818
@test val == [2,3,4]
1919
end
20+
21+
# file argument mismatch with embedded linenumbernodes shouldn't crash
22+
ex = Expr(:block, LineNumberNode(111), :(x = 1), LineNumberNode(222), :(x + 1))
23+
lwr = JuliaLowering.core_lowering_hook(ex, test_mod, "foo.jl", 333)[1]
24+
@test Core.eval(test_mod, lwr) === 2
2025
end
2126

2227
if isdefined(Core, :_lower)

0 commit comments

Comments
 (0)