Skip to content

Commit d2eb584

Browse files
KenoKristofferC
authored andcommitted
Fix generator-invocation legality check for varargs generators (#47739)
This code was introduced by me back in #31025 to speed up evaluation of generated functions that didn't make use of all of their arguments to make generation decisions. However, it neglected to take into account the possibility that the generator could be varargs. As a result, an unfortunate coincidence of an unused slot in the correct position could have allowed expansion of generators that were not supposed to be expandable. This can cause incorrect inference with all the usual consequences. However, fortunately this coincidence appears to be pretty rare. Fixes JuliaDebug/CassetteOverlay.jl#12 (cherry picked from commit 328dd57)
1 parent 19c7e2b commit d2eb584

File tree

2 files changed

+28
-1
lines changed

2 files changed

+28
-1
lines changed

base/reflection.jl

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1084,13 +1084,25 @@ function may_invoke_generator(method::Method, @nospecialize(atypes), sparams::Si
10841084
end
10851085
end
10861086
end
1087-
for i = 1:length(at.parameters)
1087+
non_va_args = method.isva ? method.nargs - 1 : method.nargs
1088+
for i = 1:non_va_args
10881089
if !isdispatchelem(at.parameters[i])
10891090
if (ast_slotflag(code, 1 + i + nsparams) & SLOT_USED) != 0
10901091
return false
10911092
end
10921093
end
10931094
end
1095+
if method.isva
1096+
# If the va argument is used, we need to ensure that all arguments that
1097+
# contribute to the va tuple are dispatchelemes
1098+
if (ast_slotflag(code, 1 + method.nargs + nsparams) & SLOT_USED) != 0
1099+
for i = (non_va_args+1):length(at.parameters)
1100+
if !isdispatchelem(at.parameters[i])
1101+
return false
1102+
end
1103+
end
1104+
end
1105+
end
10941106
return true
10951107
end
10961108

test/staged.jl

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -304,3 +304,18 @@ end
304304
:(global x33243 = 2)
305305
end
306306
@test_throws ErrorException f33243()
307+
308+
# https://github.com/JuliaDebug/CassetteOverlay.jl/issues/12
309+
# generated function with varargs and unfortunately placed unused slot
310+
@generated function f_vararg_generated(args...)
311+
:($args)
312+
end
313+
g_vararg_generated() = f_vararg_generated((;), (;), Base.inferencebarrier((;)))
314+
let tup = g_vararg_generated()
315+
@test !any(==(Any), tup)
316+
# This is just to make sure that the test is actually testing what we want -
317+
# the test only works if there's an unused that matches the position of the
318+
# inferencebarrier argument above (N.B. the generator function itself
319+
# shifts everything over by 1)
320+
@test code_lowered(first(methods(f_vararg_generated)).generator.gen)[1].slotflags[5] == UInt8(0x00)
321+
end

0 commit comments

Comments
 (0)