Repository: JuliaLang/julia
File: JuliaLowering/src/macro_expansion.jl (Julia)

Code before:
        # TODO: Should every form get layerid systematically? Or only the ones
        # which expand_forms_2 needs?
        layerid = get(ex, :scope_layer, current_layer_id(ctx))
        setattr(mapchildren(e->expand_forms_1(ctx,e), ctx, ex),
                :scope_layer, layerid)
    else
        mapchildren(e->expand_forms_1(ctx,e), ctx, ex)
    end
end

has_if_generated(st::SyntaxTree) = JuliaSyntax.@stm st begin
    (_, when=is_leaf(st)||is_quoted(st)) -> false
    [K"function" _...] -> false
    ([K"=" call _], when=is_eventually_call(call)) -> false
    [K"if" [K"generated"] _ _] -> true
    _ -> any(has_if_generated, children(st))
end
split_generated(st::SyntaxTree, gen_part) = JuliaSyntax.@stm st begin
    (_, when=is_leaf(st)) -> st
    [K"if" [K"generated"] gen nongen] -> gen_part ?
        @ast(st._graph, st, [K"$" gen]) : nongen
    _ -> mapchildren(x->split_generated(x, gen_part), st._graph, st)
end

function ensure_macro_attributes(graph)
    ensure_attributes(graph,
                      var_id=IdTag,
                      scope_layer=LayerId,
                      __macro_ctx__=Nothing,
                      meta=CompileHints)
end

@fzone "JL: macroexpand" function expand_forms_1(mod::Module, ex::SyntaxTree, expr_compat_mode::Bool, macro_world::UInt)
    if kind(ex) == K"local"
        # This error assumes we're expanding the body of a top level thunk but
        # we might want to make that more explicit in the pass system.
        throw(LoweringError(ex, "local declarations have no effect outside a scope"))
    end
    graph = ensure_macro_attributes(syntax_graph(ex))
    ctx = MacroExpansionContext(graph, mod, expr_compat_mode, macro_world)
    ex2 = expand_forms_1(ctx, reparent(ctx, ex))
    graph2 = delete_attributes(graph, :__macro_ctx__)
    # TODO: Returning the context with pass-specific mutable data is a bad way
    # to carry state into the next pass. We might fix this by attaching such
    # data to the graph itself as global attributes?
    ctx2 = MacroExpansionContext(graph2, ctx.bindings, ctx.scope_layers, ctx.scope_layer_stack,
                                 expr_compat_mode, macro_world)
    return ctx2, reparent(ctx2, ex2)
end


Review comment (refactor):
What prevents this from accidentally transforming a quoted `if @generated ...`? Is it just that macro-expansion hasn't happened yet, so it's impossible to "type" `if $(Expr(:generated)) ...` ?

(side note) this pattern matching turned out quite nice, I can see why you wanted the utility 👍 

Code after:
        # TODO: Should every form get layerid systematically? Or only the ones
        # which expand_forms_2 needs?
        layerid = get(ex, :scope_layer, current_layer_id(ctx))
        setattr(mapchildren(e->expand_forms_1(ctx,e), ctx, ex),
                :scope_layer, layerid)
    else
        mapchildren(e->expand_forms_1(ctx,e), ctx, ex)
    end
end

has_if_generated(st::SyntaxTree) = JuliaSyntax.@stm st begin
    (_, when=is_leaf(st)||is_quoted(st)) -> false
    [K"function" _...] -> false
    ([K"=" call _], when=is_eventually_call(call)) -> false
    [K"if" [K"generated"] _ _] -> true
    _ -> any(has_if_generated, children(st))
end
split_generated(st::SyntaxTree, gen_part) = JuliaSyntax.@stm st begin
    (_, when=is_leaf(st)||is_quoted(st)) -> st
    [K"if" [K"generated"] gen nongen] -> if gen_part
        @ast(st._graph, st, [K"$" gen])
    else
        nongen
    end
    _ -> mapchildren(x->split_generated(x, gen_part), st._graph, st)
end

function ensure_macro_attributes(graph)
    ensure_attributes(graph,
                      var_id=IdTag,
                      scope_layer=LayerId,
                      __macro_ctx__=Nothing,
                      meta=CompileHints)
end

@fzone "JL: macroexpand" function expand_forms_1(mod::Module, ex::SyntaxTree, expr_compat_mode::Bool, macro_world::UInt)
    if kind(ex) == K"local"
        # This error assumes we're expanding the body of a top level thunk but
        # we might want to make that more explicit in the pass system.
        throw(LoweringError(ex, "local declarations have no effect outside a scope"))
    end
    graph = ensure_macro_attributes(syntax_graph(ex))
    ctx = MacroExpansionContext(graph, mod, expr_compat_mode, macro_world)
    ex2 = expand_forms_1(ctx, reparent(ctx, ex))
    graph2 = delete_attributes(graph, :__macro_ctx__)
    # TODO: Returning the context with pass-specific mutable data is a bad way
    # to carry state into the next pass. We might fix this by attaching such
    # data to the graph itself as global attributes?
    ctx2 = MacroExpansionContext(graph2, ctx.bindings, ctx.scope_layers, ctx.scope_layer_stack,
                                 expr_compat_mode, macro_world)
    return ctx2, reparent(ctx2, ex2)