diff --git a/src/signatures.jl b/src/signatures.jl index 56aaa1a..fa503ed 100644 --- a/src/signatures.jl +++ b/src/signatures.jl @@ -253,9 +253,19 @@ end # try to normalize `def` to `GlobalRef` representation function normalize_defsig(@nospecialize(def), mod::Module) if def isa QuoteNode - # A quoted Symbol (e.g. the callee of `:foo(x)`) is already a name; only - # other wrapped values (functions, types, modules) need `nameof`. - def = def.value isa Symbol ? def.value : nameof(def.value) + val = def.value + if val isa Symbol + # A quoted Symbol (e.g. the callee of `:foo(x)`) is already a name. + def = val + elseif val isa Union{Function, DataType, UnionAll, Module} + # The quoted values that have a name (e.g. a `const` callee that + # JuliaInterpreter folded into a `QuoteNode`). + def = nameof(val) + else + # Any other quoted value, e.g. an instance of a callable struct that a macro + # interpolated as the callee of a call, is not a name: leave it as it is. + return def + end end if def isa Symbol def = GlobalRef(mod, def) diff --git a/test/signatures.jl b/test/signatures.jl index ff33d3c..2c91692 100644 --- a/test/signatures.jl +++ b/test/signatures.jl @@ -626,6 +626,37 @@ end @test LoweredCodeUtils.identify_framemethod_calls(frame) isa Any # must not throw end +# A callable value that is neither a function, a type nor a module: it has no `nameof`. +module QuotedCallee +struct Offset + value::Float64 +end +(offset::Offset)(x) = x + offset.value +end + +@testset "normalize_defsig with a quoted callable value as callee" begin + # A macro may interpolate an arbitrary value as the callee of a call, + # `$(QuoteNode(Offset(2.0)))(x)`, which lowers to a `:call` whose callee is that + # `QuoteNode`. Only functions, types and modules have a `nameof`; any other quoted + # value is not a name and must be left as it is instead of throwing. + for val in (QuotedCallee.Offset(2.0), 1, "s", Union{Int,String}) + qn = QuoteNode(val) + @test LoweredCodeUtils.normalize_defsig(qn, QuotedCallee) === qn + end + for (val, name) in ((sum, :sum), (Int, :Int64), (Base, :Base)) + @test LoweredCodeUtils.normalize_defsig(QuoteNode(val), QuotedCallee) === GlobalRef(QuotedCallee, name) + end + + ex = :(apply_offset(x) = $(QuoteNode(QuotedCallee.Offset(2.0)))(x)) + methodinfos, selfcalls = LoweredCodeUtils.identify_framemethod_calls(Frame(QuotedCallee, ex)) + @test haskey(methodinfos, GlobalRef(QuotedCallee, :apply_offset)) + @test isempty(selfcalls) + signatures = MethodInfoKey[] + methoddefs!(signatures, Frame(QuotedCallee, ex)) + @test length(signatures) == 1 + @test Base.invokelatest(QuotedCallee.apply_offset, 3.0) == 5.0 +end + module FoldedCallees struct Callable end end