Skip to content

Commit d4151d2

Browse files
committed
Fix generator-invocation legality check for varargs generators
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
1 parent 02aa0b0 commit d4151d2

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
@@ -1165,13 +1165,25 @@ function may_invoke_generator(method::Method, @nospecialize(atype), sparams::Sim
11651165
end
11661166
end
11671167
end
1168-
for i = 1:length(at.parameters)
1168+
non_va_args = method.isva ? method.nargs : method.nargs - 1
1169+
for i = 1:non_va_args
11691170
if !isdispatchelem(at.parameters[i])
11701171
if (ast_slotflag(code, 1 + i + nsparams) & SLOT_USED) != 0
11711172
return false
11721173
end
11731174
end
11741175
end
1176+
if method.isva
1177+
# If the va argument is used, we need to ensure that all arguments that
1178+
# contribute to the va tuple are dispatchelemes
1179+
if (ast_slotflag(code, 1 + method.nargs + nsparams) & SLOT_USED) != 0
1180+
for i = (non_va_args+1):length(at.parameters)
1181+
if !isdispatchelem(at.parameters[i])
1182+
return false
1183+
end
1184+
end
1185+
end
1186+
end
11751187
return true
11761188
end
11771189

test/staged.jl

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

0 commit comments

Comments
 (0)