Skip to content

Finding macro-defined methods #31197

@timholy

Description

@timholy

Methods have a couple of fields, .file and .line, that document their origin. But when a method is defined via a macro, it gets a little ambiguous: do you attribute the definition to the macro or to the caller of the macro? There's nothing really new here: the various complaints about figuring out where a @deprecate ... command was issued from have a long and storied history. Perhaps the most important thing here is that there is a viable (but insane) strategy for finding it, see below.

Let's consider the following example:

julia> m = @which Float32(π)
Float32(::Irrational{:π}) in Base.MathConstants at irrationals.jl:167

There are a couple of interesting features here:

  • m.module is Base.MathConstants, which is the caller
  • m.file is irrationals.jl, the file in which the @irrational macro is defined. This is loaded into Base, not Base.MathConstants. m.line is likewise associated with the macro.
  • Figuring out the macro-caller's source location is tricky. For example,
julia> code = Base.uncompressed_ast(m)
CodeInfo(
1return 3.1415927f0
)

julia> code.linetable
1-element Array{Any,1}:
 Core.LineInfoNode(Base.MathConstants, :Type, Symbol("irrationals.jl"), 167, 0)

so again there doesn't seem to be any way of figuring out the caller's file.

I've found one viable strategy to find these methods:

  • Figure out which specific macro was responsible for the definition. For example, here that means parsing irrationals.jl and looking to see which top-level expression contains line 167.
  • Grab m.module's eval or include method and ask which file it's from. This is the file in which the module was created.
  • Parse that file and see if the module contains any additional include directives, so that you get a complete list of all files that were used to define that module.
  • Then for each file:
    • parse the file and look for :macrocall expressions to that particular macro
    • lower those calls and compute the method signature
    • when you find a match, return the caller LineNumberNode.

Obviously this is not a small amount of work; it would make life easier if Method contained enough information to figure this out more directly. To achieve that, it seems that CodeInfo needs some enhancements so that it can store the macro-caller's LineNumberNode. For example, allowing Vector{LineInfoNode} entries in code.linetable would be sufficient to document the origin. (Or make LineInfoNode a linked-list?)

Metadata

Metadata

Assignees

No one assigned

    Labels

    compiler:loweringSyntax lowering (compiler front end, 2nd stage)macros@macros

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions