Skip to content

Commit b65f004

Browse files
authored
Allow macrocall as function sig (#55040)
@KristofferC requested that `function @main(args) end` should work. This is currently a parse error. This PR makes it work as expected by allowing macrocall as a valid signature in function (needs to exapand to a call expr). Note that this is only the flisp changes. If this PR is accepted, an equivalent change would need to be made in JuliaSyntax.
1 parent 34aceb5 commit b65f004

File tree

4 files changed

+43
-10
lines changed

4 files changed

+43
-10
lines changed

HISTORY.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,7 @@ Language changes
5050
* It is now an error to mark a binding as both `public` and `export`ed ([#53664]).
5151
* Errors during `getfield` now raise a new `FieldError` exception type instead of the generic
5252
`ErrorException` ([#54504]).
53+
* Macros in function-signature-position no longer require parentheses. E.g. `function @main(args) ... end` is now permitted, whereas `function (@main)(args) ... end` was required in prior Julia versions.
5354

5455
Compiler/Runtime improvements
5556
-----------------------------

base/client.jl

Lines changed: 8 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -605,7 +605,7 @@ The `@main` macro may be used standalone or as part of the function definition,
605605
case, parentheses are required. In particular, the following are equivalent:
606606
607607
```
608-
function (@main)(args)
608+
function @main(args)
609609
println("Hello World")
610610
end
611611
```
@@ -624,7 +624,7 @@ imported into `Main`, it will be treated as an entrypoint in `Main`:
624624
```
625625
module MyApp
626626
export main
627-
(@main)(args) = println("Hello World")
627+
@main(args) = println("Hello World")
628628
end
629629
using .MyApp
630630
# `julia` Will execute MyApp.main at the conclusion of script execution
@@ -634,7 +634,7 @@ Note that in particular, the semantics do not attach to the method
634634
or the name:
635635
```
636636
module MyApp
637-
(@main)(args) = println("Hello World")
637+
@main(args) = println("Hello World")
638638
end
639639
const main = MyApp.main
640640
# `julia` Will *NOT* execute MyApp.main unless there is a separate `@main` annotation in `Main`
@@ -644,9 +644,6 @@ const main = MyApp.main
644644
This macro is new in Julia 1.11. At present, the precise semantics of `@main` are still subject to change.
645645
"""
646646
macro main(args...)
647-
if !isempty(args)
648-
error("`@main` is expected to be used as `(@main)` without macro arguments.")
649-
end
650647
if isdefined(__module__, :main)
651648
if Base.binding_module(__module__, :main) !== __module__
652649
error("Symbol `main` is already a resolved import in module $(__module__). `@main` must be used in the defining module.")
@@ -657,5 +654,9 @@ macro main(args...)
657654
global main
658655
global var"#__main_is_entrypoint__#"::Bool = true
659656
end)
660-
esc(:main)
657+
if !isempty(args)
658+
Expr(:call, esc(:main), map(esc, args)...)
659+
else
660+
esc(:main)
661+
end
661662
end

src/julia-parser.scm

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1329,13 +1329,13 @@
13291329

13301330
(define (valid-func-sig? paren sig)
13311331
(and (pair? sig)
1332-
(or (eq? (car sig) 'call)
1333-
(eq? (car sig) 'tuple)
1332+
(or (memq (car sig) '(call tuple))
1333+
(and (not paren) (eq? (car sig) 'macrocall))
13341334
(and paren (eq? (car sig) 'block))
13351335
(and paren (eq? (car sig) '...))
13361336
(and (eq? (car sig) '|::|)
13371337
(pair? (cadr sig))
1338-
(eq? (car (cadr sig)) 'call))
1338+
(memq (car (cadr sig)) '(call macrocall)))
13391339
(and (eq? (car sig) 'where)
13401340
(valid-func-sig? paren (cadr sig))))))
13411341

test/syntax.jl

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4050,3 +4050,34 @@ end
40504050
import ..Base
40514051
using .Base: zero, zero
40524052
end
4053+
4054+
# PR# 55040 - Macrocall as function sig
4055+
@test :(function @f()() end) == :(function (@f)() end)
4056+
4057+
function callme end
4058+
macro callmemacro(args...)
4059+
Expr(:call, esc(:callme), map(esc, args)...)
4060+
end
4061+
function @callmemacro(a::Int)
4062+
return 1
4063+
end
4064+
@callmemacro(b::Float64) = 2
4065+
function @callmemacro(a::T, b::T) where T <: Int
4066+
return 3
4067+
end
4068+
function @callmemacro(a::Int, b::Int, c::Int)::Float64
4069+
return 4
4070+
end
4071+
function @callmemacro(d::String)
4072+
(a, b, c)
4073+
# ^ Should not be accidentally parsed as an argument list
4074+
return 4
4075+
end
4076+
4077+
@test callme(1) === 1
4078+
@test callme(2.0) === 2
4079+
@test callme(3, 3) === 3
4080+
@test callme(4, 4, 4) === 4.0
4081+
4082+
# Ambiguous 1-arg anymous vs macrosig
4083+
@test_parseerror "function (@foo(a)) end"

0 commit comments

Comments
 (0)