Macro-defined callable structs `ArgumentError: invalid type for argument number 1 in method definition`

Running

macro definestruct(name, ndims)
    return quote
        struct $name{T}
            v::Vector{T}
        end
        $name(v::Vector{T}) where T = $name{T}(v)
        nvars(::$name) = $ndims
    end
end
@definestruct(MyStruct, 6)

produces the following error:

ERROR: ArgumentError: invalid type for argument number 1 in method definition for #39#nvars at /Users/olly/.julia/dev/scratch/macro.jl:7

If I comment out the line above the nvars definition, it goes away. I don’t really understand why the error is happening. Can anyone explain?

julia> @macroexpand @definestruct(MyStruct, 6)
quote
    #= REPL[1]:3 =#
    struct MyStruct{var"#13#T"}
        #= REPL[1]:4 =#
        v::Main.Vector{var"#13#T"}
    end
    #= REPL[1]:6 =#
    (var"#11#MyStruct"(var"#14#v"::Main.Vector{var"#15#T"}) where var"#15#T") = begin
            #= REPL[1]:6 =#
            var"#11#MyStruct"{var"#15#T"}(var"#14#v")
        end
    #= REPL[1]:7 =#
    var"#12#nvars"(::var"#11#MyStruct") = begin
            #= REPL[1]:7 =#
            6
        end
end

as you can see, the (var"#11#MyStruct"(var"#14#v"::Main.Vector{var"#15#T"}) where var"#15#T") does not define something on the MyStruct.

I think you want to escape this:

julia> macro definestruct(name, ndims)
           return esc(quote
               struct $name{T}
                   v::Vector{T}
               end
               $name(v::Vector{T}) where T = $name{T}(v)
               nvars(::$name) = $ndims
           end)
       end
@definestruct (macro with 1 method)

julia> @macroexpand @definestruct(MyStruct, 6)
quote
    #= REPL[4]:3 =#
    struct MyStruct{T}
        #= REPL[4]:4 =#
        v::Vector{T}
    end
    #= REPL[4]:6 =#
    (MyStruct(v::Vector{T}) where T) = begin
            #= REPL[4]:6 =#
            MyStruct{T}(v)
        end
    #= REPL[4]:7 =#
    nvars(::MyStruct) = begin
            #= REPL[4]:7 =#
            6
        end
end
2 Likes

Thank you so much. Is there a simple explanation for what the parser was doing, and why escaping was the solution? Is it an issue of scope?

This is the standard behavior when you write a macro. The key concept is macro hygiene. The manual does a decent job at explaining it:

1 Like