From 2ffdbea0770365c42e20b428e8563699e00728c3 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 ee96b24dc6b965..706570303262cb 100644 --- a/base/reflection.jl +++ b/base/reflection.jl @@ -1084,13 +1084,25 @@ function may_invoke_generator(method::Method, @nospecialize(atypes), sparams::Si 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 26e3dace1994bd..4a953ffae33b84 100644 --- a/test/staged.jl +++ b/test/staged.jl @@ -304,3 +304,18 @@ end :(global x33243 = 2) end @test_throws ErrorException f33243() + +# 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