Skip to content

TypedSyntax: a macro call inside an expression leaves the enclosing calls unmapped #712

Description

@jishnub

When a macro call appears inside an expression, the statements for the enclosing calls on that line — the ones the user wrote, not the macro's expansion — get no source node. Shape from ApproxFunBase's _default_Fun:

struct Coefs; coefficients::Vector{Float64}; end
function macroline(cf::Coefs, bs::Int)
    m = maximum(abs, cf.coefficients)
    maximum(abs, @view cf.coefficients[bs:end]) < 10m
end

Repro (TypedSyntax 1.5.4, Julia 1.13.0):

using TypedSyntax, JuliaSyntax
using JuliaSyntax: kind, sourcetext
macroline(Coefs(rand(3)), 1)
mi = only(Base.method_instances(macroline, (Coefs, Int), Base.get_world_counter()))
src, rt = TypedSyntax.code_typed1_tsn(mi)
tsn, mappings = TypedSyntax.tsn_and_mappings(mi, src, rt)
for (i, nodes) in enumerate(mappings)
    st = src.code[i]
    (st isa Core.ReturnNode || st isa Core.GotoIfNot || st isa Core.GotoNode) && continue
    println(i, "  ", st, " -> ", isempty(nodes) ? "(nothing)" : [string(kind(n), " `", sourcetext(n), "`") for n in nodes])
end

Output:

  1  Main.maximum                                 -> Identifier `maximum`
  2  Main.abs                                     -> Identifier `abs`
  3  Base.getproperty(_2, :coefficients)          -> . `cf.coefficients`
  4  _5 = (%1)(%2, %3)                            -> call `maximum(abs, cf.coefficients)`
  5  Main.:<                                      -> Identifier `<`
  6  Main.maximum                                 -> Identifier `maximum`
  7  Main.abs                                     -> Identifier `abs`
  8  _4 = Base.getproperty(_2, :coefficients)     -> . `cf.coefficients`
  9  _4                                           -> (nothing)
 10  Main.:(:)                                    -> (nothing)
 11  _4                                           -> (nothing)
 12  (lastindex)(%11)                             -> (nothing)
 13  (%10)(_3, %12)                               -> (nothing)
 14  (view)(%9, %13)                              -> (nothing)
 15  (%6)(%7, %14)                                -> (nothing)
 16  Main.:*                                      -> (nothing)
 17  _5                                           -> (nothing)
 18  (%16)(10, %17)                               -> (nothing)
 19  (%5)(%15, %18)                               -> (nothing)

The first line maps completely. On the second line only the leaf identifiers and cf.coefficients map; the @view expansion (lastindex, :, view) is understandably unmapped, but so are the calls around it that the source spells verbatim — maximum(abs, ...), <, 10m — so the whole expression comes back untyped and none of those callsites can be located in the source.

In the original (maximum(abs, @view cf.coefficients[bs:end]) < 10tol*maxabsc in ApproxFunBase, seen through Cthulhu) the outcome was slightly different: the maximum statement was attributed to the macrocall node itself rather than dropped, so the maximum callsite was located at the @view node. I could not reduce that variant to a standalone repro; the drop above is the reproducible form of the same thing.

Related: #710 (a macro call in argument position sends the enclosing call to the assignment target).


🤖 Filed using Claude Code. The reproduction was run locally on the stated versions and the output is quoted verbatim.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions