Skip to content

Commit 95716c6

Browse files
authored
support for shadowing of global variabls (#122)
1 parent 40b7bcb commit 95716c6

File tree

4 files changed

+59
-8
lines changed

4 files changed

+59
-8
lines changed

CHANGELOG.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,9 @@
11
# News
22

3+
## v1.0.2 - 2025-02-07
4+
5+
- Support shadowing of globals, e.g. `@resumable function f(); a=a+1; end;` where `a` is already an existing global.
6+
37
## v1.0.1 - 2024-11-23
48

59
- Better macro hygiene (e.g. for better support in Pluto.jl)

Project.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ license = "MIT"
55
desc = "C# sharp style generators a.k.a. semi-coroutines for Julia."
66
authors = ["Ben Lauwens and volunteer maintainers"]
77
repo = "https://github.com/JuliaDynamics/ResumableFunctions.jl.git"
8-
version = "1.0.1"
8+
version = "1.0.2"
99

1010
[deps]
1111
Logging = "56ddb016-857b-54e1-b83d-db4d58db5568"

src/utils.jl

Lines changed: 8 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -247,8 +247,8 @@ end
247247
# We solve this problem by renaming all variables.
248248
#
249249
# We use a ScopeTracker type to keep track of the things that are already
250-
# renamned. It is basically just a Vector{Dict{Symbol, Symbol}},
251-
# representing a stack, where the top records the renamend variables in the
250+
# renamed. It is basically just a Vector{Dict{Symbol, Symbol}},
251+
# representing a stack, where the top records the renamed variables in the
252252
# current scope.
253253
#
254254
# The renaming is done as follows. If we encounter an assignment of the form
@@ -261,7 +261,7 @@ end
261261
# This is done in lookup_lhs!. Note that some construction, like `let`, create
262262
# a new variable in a new scope. This is handled by the `new` keyword.
263263
#
264-
# For any other symbol y (which is not the left hand side of an assignmetn),
264+
# For any other symbol y (which is not the left hand side of an assignment),
265265
# there are the following two cases:
266266
# 1) y has been seen before in some scope. Then we replace y accordingly.
267267
# 2) y has not been seen before, then we don't rename it.
@@ -467,13 +467,14 @@ function scoping(expr::Expr, scope)
467467
return quote $(res...) end
468468
end
469469

470-
# the LHS is a symbol or a tuple of symbols
471-
expr.args[1] = lookup_lhs!(expr.args[1], scope)
472-
473-
# now transform the RHS, this can be anything
470+
# first transform the RHS (this can be anything) to check for shadowing of globals
474471
for i in 2:length(expr.args)
475472
expr.args[i] = scoping(expr.args[i], scope)
476473
end
474+
475+
# only then deal with the LHS (it is a symbol or a tuple of symbols)
476+
expr.args[1] = lookup_lhs!(expr.args[1], scope)
477+
477478
return expr
478479
end
479480
if expr.head === :macrocall

test/test_globals.jl

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
using ResumableFunctions
2+
using Test
3+
4+
a = 1
5+
g() = 1
6+
h(x) = x+1
7+
8+
@resumable function f1()
9+
b = a
10+
b = b+1
11+
@yield b
12+
end
13+
14+
@test collect(f1()) == [2]
15+
16+
@resumable function f2()
17+
a = a
18+
a = a+1
19+
@yield a
20+
end
21+
22+
@test collect(f2()) == [2]
23+
24+
@resumable function f3()
25+
g = g()
26+
g = g+1
27+
@yield g
28+
end
29+
30+
@test collect(f3()) == [2]
31+
32+
@resumable function f4()
33+
a = h(a)
34+
a = a+1
35+
@yield a
36+
end
37+
38+
@test collect(f4()) == [3]
39+
40+
@resumable function f5()
41+
g = h(g())
42+
g = g+1
43+
@yield g
44+
end
45+
46+
@test collect(f5()) == [3]

0 commit comments

Comments
 (0)