Skip to content

Commit 77eb96f

Browse files
authored
Remove default scope layer for desugaring (#106)
When desugaring introduces a `K"Identifer"` it should always decorate it with an associates scope layer - either adopted from the users code, or an internal layer created on the fly. This ensures desugaring treats hygiene consistently with macro expansion (thus ensuring that desugaring itself is hygienic).
1 parent 0bcdba0 commit 77eb96f

File tree

4 files changed

+25
-26
lines changed

4 files changed

+25
-26
lines changed

src/ast.jl

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,21 @@ Id for scope layers in macro expansion
7070
"""
7171
const LayerId = Int
7272

73+
"""
74+
A `ScopeLayer` is a mechanism for automatic hygienic macros; every identifier
75+
is assigned to a particular layer and can only match against bindings which are
76+
themselves part of that layer.
77+
78+
Normal code contains a single scope layer, whereas each macro expansion
79+
generates a new layer.
80+
"""
81+
struct ScopeLayer
82+
id::LayerId
83+
mod::Module
84+
parent_layer::LayerId # Index of parent layer in a macro expansion. Equal to 0 for no parent
85+
is_macro_expansion::Bool # FIXME
86+
end
87+
7388
#-------------------------------------------------------------------------------
7489
# AST creation utilities
7590
_node_id(graph::SyntaxGraph, ex::SyntaxTree) = (check_compatible_graph(graph, ex); ex._id)
@@ -500,6 +515,10 @@ function adopt_scope(ex::SyntaxTree, scope_layer::LayerId)
500515
set_scope_layer(ex, ex, scope_layer, true)
501516
end
502517

518+
function adopt_scope(ex::SyntaxTree, layer::ScopeLayer)
519+
adopt_scope(ex, layer.id)
520+
end
521+
503522
function adopt_scope(ex::SyntaxTree, ref::SyntaxTree)
504523
adopt_scope(ex, ref.scope_layer)
505524
end

src/macro_expansion.jl

Lines changed: 0 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,5 @@
11
# Lowering pass 1: Macro expansion, simple normalizations and quote expansion
22

3-
"""
4-
A `ScopeLayer` is a mechanism for automatic hygienic macros; every identifier
5-
is assigned to a particular layer and can only match against bindings which are
6-
themselves part of that layer.
7-
8-
Normal code contains a single scope layer, whereas each macro expansion
9-
generates a new layer.
10-
"""
11-
struct ScopeLayer
12-
id::LayerId
13-
mod::Module
14-
parent_layer::LayerId # Index of parent layer in a macro expansion. Equal to 0 for no parent
15-
is_macro_expansion::Bool # FIXME
16-
end
17-
183
struct MacroExpansionContext{GraphType} <: AbstractLoweringContext
194
graph::GraphType
205
bindings::Bindings

src/runtime.jl

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -343,9 +343,11 @@ function (g::GeneratedFunctionStub)(world::UInt, source::Method, @nospecialize a
343343
macro_world = typemax(UInt)
344344
ctx1 = MacroExpansionContext(graph, __module__, false, macro_world)
345345

346+
layer = only(ctx1.scope_layers)
347+
346348
# Run code generator - this acts like a macro expander and like a macro
347349
# expander it gets a MacroContext.
348-
mctx = MacroContext(syntax_graph(ctx1), g.srcref, ctx1.scope_layers[end])
350+
mctx = MacroContext(syntax_graph(ctx1), g.srcref, layer)
349351
ex0 = g.gen(mctx, args...)
350352
if ex0 isa SyntaxTree
351353
if !is_compatible_graph(ctx1, ex0)
@@ -370,10 +372,10 @@ function (g::GeneratedFunctionStub)(world::UInt, source::Method, @nospecialize a
370372
# Wrap expansion in a non-toplevel lambda and run scope resolution
371373
ex2 = @ast ctx2 ex0 [K"lambda"(is_toplevel_thunk=false, toplevel_pure=true)
372374
[K"block"
373-
(string(n)::K"Identifier" for n in g.argnames)...
375+
(adopt_scope(string(n)::K"Identifier", layer) for n in g.argnames)...
374376
]
375377
[K"block"
376-
(string(n)::K"Identifier" for n in g.spnames)...
378+
(adopt_scope(string(n)::K"Identifier", layer) for n in g.spnames)...
377379
]
378380
ex2
379381
]

src/scope_analysis.jl

Lines changed: 1 addition & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -12,16 +12,9 @@ function Base.isless(a::NameKey, b::NameKey)
1212
(a.name, a.layer) < (b.name, b.layer)
1313
end
1414

15-
# Identifiers produced by lowering will have the following layer by default.
16-
#
17-
# To make new mutable variables without colliding names, lowering can
18-
# - generate new var_id's directly (like the gensyms used by the old system)
19-
# - create additional layers, though this may be unnecessary
20-
const _lowering_internal_layer = -1
21-
2215
function NameKey(ex::SyntaxTree)
2316
@chk kind(ex) == K"Identifier"
24-
NameKey(ex.name_val, get(ex, :scope_layer, _lowering_internal_layer))
17+
NameKey(ex.name_val, ex.scope_layer)
2518
end
2619

2720
#-------------------------------------------------------------------------------

0 commit comments

Comments
 (0)