Skip to content
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
44 changes: 32 additions & 12 deletions src/desugaring.jl
Original file line number Diff line number Diff line change
Expand Up @@ -1357,8 +1357,10 @@ function expand_assignment(ctx, ex, is_const=false)
# Identifier in lhs[1] is a variable type declaration, eg
# x::T = rhs
@ast ctx ex [K"block"
[K"decl" lhs[1] lhs[2]]
is_const ? [K"const" [K"=" lhs[1] rhs]] : [K"=" lhs[1] rhs]
if kind(x) !== K"Placeholder"
[K"decl" x T]
end
is_const ? [K"const" [K"=" x rhs]] : [K"=" x rhs]
]
else
# Otherwise just a type assertion, eg
Expand Down Expand Up @@ -2169,8 +2171,17 @@ function make_lhs_decls(ctx, stmts, declkind, declmeta, ex, type_decls=true)
if type_decls
@chk numchildren(ex) == 2
name = ex[1]
@chk kind(name) == K"Identifier"
push!(stmts, makenode(ctx, ex, K"decl", name, ex[2]))
if kind(name) == K"Identifier"
push!(stmts, makenode(ctx, ex, K"decl", name, ex[2]))
else
# TODO: Currently, this ignores the LHS in `_::T = val`.
# We should probably do one of the following:
# - Throw a LoweringError if that's not too breaking
# - `convert(T, rhs)::T` and discard the result which is what
# `x::T = rhs` would do if x is never used again.
@chk kind(name) == K"Placeholder"
return
end
end
make_lhs_decls(ctx, stmts, declkind, declmeta, ex[1], type_decls)
elseif k == K"tuple" || k == K"parameters"
Expand All @@ -2196,7 +2207,7 @@ function expand_decls(ctx, ex)
# expand_assignment will create the type decls
make_lhs_decls(ctx, stmts, declkind, declmeta, binding[1], false)
push!(stmts, expand_assignment(ctx, binding))
elseif is_sym_decl(binding) || kind(binding) == K"Value"
elseif is_sym_decl(binding) || kind(binding) in (K"Value", K"Placeholder")
make_lhs_decls(ctx, stmts, declkind, declmeta, binding, true)
elseif kind(binding) == K"function"
make_lhs_decls(ctx, stmts, declkind, declmeta, binding[1], false)
Expand Down Expand Up @@ -2258,7 +2269,7 @@ end
#-------------------------------------------------------------------------------
# Expansion of function definitions

function expand_function_arg(ctx, body_stmts, arg, is_last_arg, is_kw)
function expand_function_arg(ctx, body_stmts, arg, is_last_arg, is_kw, arg_id)
ex = arg

if kind(ex) == K"="
Expand Down Expand Up @@ -2309,7 +2320,16 @@ function expand_function_arg(ctx, body_stmts, arg, is_last_arg, is_kw)
K"local"(meta=CompileHints(:is_destructured_arg, true))
[K"=" ex name]
])
elseif k == K"Identifier" || k == K"Placeholder"
elseif k == K"Placeholder"
# Lowering should be able to use placeholder args as rvalues internally,
# e.g. for kw method dispatch. Duplicate positional placeholder names
# should be allowed.
name = if is_kw
@ast ctx ex ex=>K"Identifier"
else
new_local_binding(ctx, ex, "#arg$(string(arg_id))#"; kind=:argument)
end
elseif k == K"Identifier"
name = ex
else
throw(LoweringError(ex, is_kw ? "Invalid keyword name" : "Invalid function argument"))
Expand Down Expand Up @@ -2647,7 +2667,7 @@ function keyword_function_defs(ctx, srcref, callex_srcref, name_str, typevar_nam
kwtmp = new_local_binding(ctx, keywords, "kwtmp")
for (i,arg) in enumerate(children(keywords))
(aname, atype, default, is_slurp) =
expand_function_arg(ctx, nothing, arg, i == numchildren(keywords), true)
expand_function_arg(ctx, nothing, arg, i == numchildren(keywords), true, i)
push!(kw_names, aname)
name_sym = @ast ctx aname aname=>K"Symbol"
push!(body_arg_names, aname)
Expand Down Expand Up @@ -3024,8 +3044,8 @@ function expand_function_def(ctx, ex, docs, rewrite_call=identity, rewrite_body=
first_default = 0 # index into arg_names/arg_types
arg_defaults = SyntaxList(ctx)
for (i,arg) in enumerate(args)
(aname, atype, default, is_slurp) = expand_function_arg(ctx, body_stmts, arg,
i == length(args), false)
(aname, atype, default, is_slurp) =
expand_function_arg(ctx, body_stmts, arg, i == length(args), false, i)
has_slurp |= is_slurp
push!(arg_names, aname)

Expand Down Expand Up @@ -3246,8 +3266,8 @@ function expand_opaque_closure(ctx, ex)
body_stmts = SyntaxList(ctx)
is_va = false
for (i, arg) in enumerate(children(args))
(aname, atype, default, is_slurp) = expand_function_arg(ctx, body_stmts, arg,
i == numchildren(args), false)
(aname, atype, default, is_slurp) =
expand_function_arg(ctx, body_stmts, arg, i == numchildren(args), false, i)
is_va |= is_slurp
push!(arg_names, aname)
push!(arg_types, atype)
Expand Down
17 changes: 11 additions & 6 deletions src/macro_expansion.jl
Original file line number Diff line number Diff line change
Expand Up @@ -275,7 +275,13 @@ function expand_macro(ctx, ex)
# age changes concurrently.
#
# TODO: Allow this to be passed in
if hasmethod(macfunc, Tuple{typeof(mctx), typeof.(raw_args)...}; world=ctx.macro_world)
# TODO: hasmethod always returns false for our `typemax(UInt)` meaning
# "latest world," which we shouldn't be using.
has_new_macro = ctx.macro_world === typemax(UInt) ?
hasmethod(macfunc, Tuple{typeof(mctx), typeof.(raw_args)...}) :
hasmethod(macfunc, Tuple{typeof(mctx), typeof.(raw_args)...}; world=ctx.macro_world)

if has_new_macro
macro_args = prepare_macro_args(ctx, mctx, raw_args)
expanded = try
Base.invoke_in_world(ctx.macro_world, macfunc, macro_args...)
Expand Down Expand Up @@ -376,16 +382,15 @@ function expand_forms_1(ctx::MacroExpansionContext, ex::SyntaxTree)
k = kind(ex)
if k == K"Identifier"
name_str = ex.name_val
if all(==('_'), name_str)
@ast ctx ex ex=>K"Placeholder"
elseif is_ccall_or_cglobal(name_str)
if is_ccall_or_cglobal(name_str)
# Lower special identifiers `cglobal` and `ccall` to `K"core"`
# pseudo-refs very early so that cglobal and ccall can never be
# turned into normal bindings (eg, assigned to)
@ast ctx ex name_str::K"core"
else
layerid = get(ex, :scope_layer, current_layer_id(ctx))
makeleaf(ctx, ex, ex, kind=K"Identifier", scope_layer=layerid)
k = all(==('_'), name_str) ? K"Placeholder" : K"Identifier"
scope_layer = get(ex, :scope_layer, current_layer_id(ctx))
makeleaf(ctx, ex, ex; kind=k, scope_layer)
end
elseif k == K"StrMacroName" || k == K"CmdMacroName" || k == K"macro_name"
# These can appear outside of a macrocall, e.g. in `import`
Expand Down
7 changes: 3 additions & 4 deletions src/runtime.jl
Original file line number Diff line number Diff line change
Expand Up @@ -337,10 +337,9 @@ function (g::GeneratedFunctionStub)(world::UInt, source::Method, @nospecialize a

__module__ = source.module

# Macro expansion. Looking at Core.GeneratedFunctionStub, it seems that
# macros emitted by the generator are currently expanded in the latest
# world, so do that for compatibility.
macro_world = typemax(UInt)
# Macro expansion. Note that we expand in `tls_world_age()` (see
# Core.GeneratedFunctionStub)
macro_world = Base.tls_world_age()
ctx1 = MacroExpansionContext(graph, __module__, false, macro_world)

layer = only(ctx1.scope_layers)
Expand Down
9 changes: 5 additions & 4 deletions src/syntax_macros.jl
Original file line number Diff line number Diff line change
Expand Up @@ -34,10 +34,11 @@ function Base.var"@nospecialize"(__context__::MacroContext, ex, exs...)
_apply_nospecialize(__context__, ex)
end

function Base.var"@atomic"(__context__::MacroContext, ex)
@chk kind(ex) == K"Identifier" || kind(ex) == K"::" (ex, "Expected identifier or declaration")
@ast __context__ __context__.macrocall [K"atomic" ex]
end
# TODO: support all forms that the original supports
# function Base.var"@atomic"(__context__::MacroContext, ex)
# @chk kind(ex) == K"Identifier" || kind(ex) == K"::" (ex, "Expected identifier or declaration")
# @ast __context__ __context__.macrocall [K"atomic" ex]
# end

function Base.var"@label"(__context__::MacroContext, ex)
@chk kind(ex) == K"Identifier"
Expand Down
2 changes: 1 addition & 1 deletion test/closures_ir.jl
Original file line number Diff line number Diff line change
Expand Up @@ -237,7 +237,7 @@ end
21 SourceLocation::1:10
22 (call core.svec %₁₈ %₂₀ %₂₁)
23 --- method core.nothing %₂₂
slots: [slot₁/#self#(!read) slot₂/_(!read) slot₃/g]
slots: [slot₁/#self#(!read) slot₂/#arg1#(!read) slot₃/g]
1 TestMod.#f#g##2
2 static_parameter₁
3 (new %₁ %₂)
Expand Down
10 changes: 9 additions & 1 deletion test/decls.jl
Original file line number Diff line number Diff line change
Expand Up @@ -92,9 +92,17 @@ end

# Tuple/destructuring assignments
@test JuliaLowering.include_string(test_mod, "(a0, a1, a2) = [1,2,3]") == [1,2,3]

@test JuliaLowering.include_string(test_mod, "const a,b,c = 1,2,3") === (1, 2, 3)

@testset "Placeholder decls" begin
@test JuliaLowering.include_string(test_mod, "global _ = 1") === 1
@test JuliaLowering.include_string(test_mod, "global _::Int = 1") === 1
@test JuliaLowering.include_string(test_mod, "let; local _; _ = 1; end") === 1
@test JuliaLowering.include_string(test_mod, "let; local _::Int = 1; end") === 1
@test JuliaLowering.include_string(test_mod, "let; local (a0, _, a2) = [1,2,3]; end") == [1,2,3]
@test JuliaLowering.include_string(test_mod, "let; local (a0, _::Int, a2) = [1,2,3]; end") == [1,2,3]
end

test_mod_2 = Module()
@testset "toplevel-preserving syntax" begin
JuliaLowering.include_string(test_mod_2, "if true; global v1::Bool; else const v1 = 1; end")
Expand Down
29 changes: 29 additions & 0 deletions test/functions.jl
Original file line number Diff line number Diff line change
Expand Up @@ -471,6 +471,35 @@ end
@test cl(x = 20) == 21
end

@testset "Write-only placeholder function arguments" begin
# positional arguments may be duplicate placeholders. keyword arguments can
# contain placeholders, but they must be unique
params_req = [""
"_"
"::Int"
"_, _"]
params_opt = [""
"::Int=2"
"_=2"]
params_va = ["", "_..."]
params_kw = [""
"; _"
"; _::Int"
"; _::Int=1"
"; _=1, __=2"
"; _..."
"; _=1, __..."]
local i = 0
for req in params_req, opt in params_opt, va in params_va, kw in params_kw
arg_str = join(filter(!isempty, (req, opt, va, kw)), ", ")
f_str = "function f_placeholders$i($arg_str); end"
i += 1
@testset "$f_str" begin
@test JuliaLowering.include_string(test_mod, f_str) isa Function
end
end
end

@testset "Generated functions" begin
@test JuliaLowering.include_string(test_mod, raw"""
begin
Expand Down
Loading
Loading