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

Code before:
    src1 = get(SyntaxTree(graph1, id1), :source, nothing)
    src2 = if !copy_source
        src1
    elseif src1 isa NodeId
        _copy_ast(graph2, graph1, src1, seen, copy_source)
    elseif src1 isa Tuple
        map(i->_copy_ast(graph2, graph1, i, seen, copy_source), src1)
    else
        src1
    end
    copy_attrs!(SyntaxTree(graph2, id2), SyntaxTree(graph1, id1), true)
    setattr!(graph2, id2, :source, src2)
    if !is_leaf(graph1, id1)
        cs = NodeId[]
        for cid in children(graph1, id1)
            push!(cs, _copy_ast(graph2, graph1, cid, seen, copy_source))
        end
        setchildren!(graph2, id2, cs)
    end
    return id2
end

#-------------------------------------------------------------------------------
function set_scope_layer(ctx, ex, layer_id, force)
    k = kind(ex)
    attrs = [:scope_layer=>(force ? layer_id : get(ex, :scope_layer, layer_id))]
    if k == K"module" || k == K"toplevel" || k == K"inert"
        makenode(ctx, ex, ex, children(ex), attrs)
    elseif k == K"."
        children = [set_scope_layer(ctx, ex[1], layer_id, force), ex[2]]
        makenode(ctx, ex, ex, children, attrs)
    elseif !is_leaf(ex)
        mapchildren(e->set_scope_layer(ctx, e, layer_id, force), ctx, ex, attrs)
    else
        makeleaf(ctx, ex, ex, attrs)
    end
end

"""
    adopt_scope(ex, ref)

Copy `ex`, adopting the scope layer of `ref`.
"""
function adopt_scope(ex::SyntaxTree, scope_layer::LayerId)
    set_scope_layer(ex, ex, scope_layer, true)
end

function adopt_scope(ex::SyntaxTree, layer::ScopeLayer)
    adopt_scope(ex, layer.id)
end


Review comment (suggestion):
```suggestion
    attrs = Pair{Symbol,LayerId}[
        :scope_layer=>(force ? layer_id : get(ex, :scope_layer, layer_id))
    ]
```

if you're really trying to be strict about specializations, it can be a good idea to explicitly type most arrays, since:
1. `Pair` over-specializes just like `NamedTuple` (so the strict array type will either `convert` this away or push you to fix the type instability), and
2. Whether this vector ends up well-typed depends on inference / `Base._return_type` (esp. bad for recursive functions)

Code after:
    let copied = get(seen, id1, nothing)
        isnothing(copied) || return copied
    end
    id2 = newnode!(graph2)
    seen[id1] = id2
    src1 = get(SyntaxTree(graph1, id1), :source, nothing)
    src2 = if !copy_source
        src1
    elseif src1 isa NodeId
        _copy_ast(graph2, graph1, src1, seen, copy_source)
    elseif src1 isa Tuple
        map(i->_copy_ast(graph2, graph1, i, seen, copy_source), src1)
    else
        src1
    end
    copy_attrs!(SyntaxTree(graph2, id2), SyntaxTree(graph1, id1), true)
    setattr!(graph2, id2, :source, src2)
    if !is_leaf(graph1, id1)
        cs = NodeId[]
        for cid in children(graph1, id1)
            push!(cs, _copy_ast(graph2, graph1, cid, seen, copy_source))
        end
        setchildren!(graph2, id2, cs)
    end
    return id2
end

#-------------------------------------------------------------------------------
function set_scope_layer(ctx, ex, layer_id, force)
    k = kind(ex)
    new_layer = force ? layer_id : get(ex, :scope_layer, layer_id)

    ex2 = if k == K"module" || k == K"toplevel" || k == K"inert"
        makenode(ctx, ex, ex, children(ex))
    elseif k == K"."
        children = NodeId[set_scope_layer(ctx, ex[1], layer_id, force), ex[2]]
        makenode(ctx, ex, ex, children)
    elseif !is_leaf(ex)
        mapchildren(e->set_scope_layer(ctx, e, layer_id, force), ctx, ex)
    else
        makeleaf(ctx, ex, ex)
    end
    setattr!(ex2, :scope_layer, new_layer)
end

"""
    adopt_scope(ex, ref)

Copy `ex`, adopting the scope layer of `ref`.
"""
function adopt_scope(ex::SyntaxTree, scope_layer::LayerId)