From 2950963ff5be7f32b4666176b58b0651a962817a Mon Sep 17 00:00:00 2001 From: Keno Fischer Date: Tue, 29 Nov 2022 18:48:15 -0500 Subject: [PATCH] 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 https://github.com/JuliaDebug/CassetteOverlay.jl/issues/12 (cherry picked from commit 328dd578958d9c2a22ddb11970324ecd04e94314) --- base/reflection.jl | 14 +++++++++++++- test/staged.jl | 15 +++++++++++++++ 2 files changed, 28 insertions(+), 1 deletion(-) diff --git a/base/reflection.jl b/base/reflection.jl index 223d5fab040e3..25f0024c01e91 100644 --- a/base/reflection.jl +++ b/base/reflection.jl @@ -1155,13 +1155,25 @@ function may_invoke_generator(method::Method, @nospecialize(atype), sparams::Sim end end end - for i = 1:length(at.parameters) + non_va_args = method.isva ? method.nargs - 1 : method.nargs + for i = 1:non_va_args if !isdispatchelem(at.parameters[i]) if (ast_slotflag(code, 1 + i + nsparams) & SLOT_USED) != 0 return false end end end + if method.isva + # If the va argument is used, we need to ensure that all arguments that + # contribute to the va tuple are dispatchelemes + if (ast_slotflag(code, 1 + method.nargs + nsparams) & SLOT_USED) != 0 + for i = (non_va_args+1):length(at.parameters) + if !isdispatchelem(at.parameters[i]) + return false + end + end + end + end return true end diff --git a/test/staged.jl b/test/staged.jl index b99ef46a2bc1e..516baea93ec04 100644 --- a/test/staged.jl +++ b/test/staged.jl @@ -305,3 +305,18 @@ end end @test f33243() === 2 @test x33243 === 2 + +# https://github.com/JuliaDebug/CassetteOverlay.jl/issues/12 +# generated function with varargs and unfortunately placed unused slot +@generated function f_vararg_generated(args...) + :($args) +end +g_vararg_generated() = f_vararg_generated((;), (;), Base.inferencebarrier((;))) +let tup = g_vararg_generated() + @test !any(==(Any), tup) + # This is just to make sure that the test is actually testing what we want - + # the test only works if there's an unused that matches the position of the + # inferencebarrier argument above (N.B. the generator function itself + # shifts everything over by 1) + @test code_lowered(first(methods(f_vararg_generated)).generator.gen)[1].slotflags[5] == UInt8(0x00) +end