Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support for named tuple destructuring #362

Open
wants to merge 7 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 12 additions & 1 deletion src/execution.jl
Original file line number Diff line number Diff line change
Expand Up @@ -345,7 +345,18 @@ function collectvars(ex::Expr, vars::Vector{Symbol}=Symbol[])
if isa(lhs, Symbol)
push!(vars, lhs)
elseif isa(lhs, Expr) && lhs.head == :tuple
append!(vars, lhs.args)
args = lhs.args
if !all(arg -> isa(arg, Symbol), args)
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Okay, so long as we're fixing this, let's fix this for real. There are two cases of destructuring to support: namedtuple and possibly nested tuple (which may contain namedtuple).

Let's structure the code then as:

elseif isa(lhs, Expr) && lhs.head == :tuple && length(args) == 1 && lhs.args[1].head === :parameters && all(arg->isa(arg, Symbol)), lhs.args[1].args)
    append!(vars, lhs.args[1].args)
elseif isa(lhs, Expr) && lhs.head == :tuple
    for arg in lhs.args
        if arg isa Symbol
            push!(vars, arg)
        else
            collectvars(arg, vars)
        end
    end
elseif

and let's also add a test for (a, (b, c)) = rhs as well as (a, (;x, y)) = rhs

# named tuple destructuring
if length(args) > 1 ||
!isa(first(args), Expr) ||
first(args).head !== :parameters ||
!all(arg -> isa(arg, Symbol), first(args).args)
@error "Unrecognized expression type in benchmark code: $ex"
end
args = first(args).args
end
append!(vars, args)
end
elseif (ex.head == :comprehension || ex.head == :generator)
arg = ex.args[1]
Expand Down
34 changes: 29 additions & 5 deletions test/ExecutionTests.jl
Original file line number Diff line number Diff line change
Expand Up @@ -216,10 +216,28 @@ tune!(b)
"good")
@test_throws UndefVarError local_var
@benchmark some_var = "whatever" teardown = (@test_throws UndefVarError some_var)
@benchmark foo, bar = "good", "good" setup = (foo = "bad"; bar = "bad") teardown = (@test foo ==
"good" &&
bar ==
"good")
@benchmark foo, bar = "good", "good" setup = ((foo, bar) = ("bad", "bad")) teardown = @test(
foo == "good" && bar == "good"
)
let # assignment with interpolation
two_good = "good", "good"
two_bad = "bad", "bad"
@benchmark foo, bar = $two_good setup = ((foo, bar) = $two_bad) teardown = @test(
foo == "good" && bar == "good"
)
end
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These tests are good, but if I understand correctly, I think this is missing the case where we interpolate a value from local scope with dollar, since that's part of the code that this PR changes.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've added tests for interpolated values both for the tuple and named tuple case (though I don't think the code I changed is specific to interpolation in any way).

if VERSION >= v"1.7"
@benchmark (; foo, bar) = (foo="good", bar="good") setup = (
(; foo, bar) = (foo="bad", bar="bad")
) teardown = @test(foo == "good" && bar == "good")
let # assignment with interpolation
good_nt = (foo="good", bar="good")
bad_nt = (foo="bad", bar="bad")
@benchmark foo, bar = $good_nt setup = ((; foo, bar) = $bad_nt) teardown = @test(
foo == "good" && bar == "good"
)
end
end

# test variable assignment with `@benchmark(args...)` form
@benchmark(
Expand All @@ -232,6 +250,11 @@ tune!(b)
setup = (foo = "bad"; bar = "bad"),
teardown = (@test foo == "good" && bar == "good")
)
if VERSION >= v"1.7"
@benchmark (; foo, bar) = (foo="good", bar="good") setup = (foo = "bad"; bar = "bad") teardown = @test(
foo == "good" && bar == "good"
)
end

# test kwargs separated by `,`
@benchmark(
Expand Down Expand Up @@ -327,12 +350,13 @@ str = String(take!(io))
# BenchmarkTools.DEFAULT_PARAMETERS.overhead = BenchmarkTools.estimate_overhead()
# @test time(minimum(@benchmark nothing)) == 1

@test [:x, :y, :z, :v, :w] == BenchmarkTools.collectvars(
@test [:x, :y, :z, :v, :w, :r, :s] == BenchmarkTools.collectvars(
quote
x = 1 + 3
y = 1 + x
z = (a = 4; y + a)
v, w = 1, 2
(; r, s) = (r=1, s=2, t=3)
[u^2 for u in [1, 2, 3]]
end,
)
Expand Down
Loading