Macro interpolation of Symbols without `eval`

I am trying to write a macro for setting up a submodule in my package. Since this is called at precompile time, I cannot use eval (otherwise I get InitError: Evaluation into the closed module ImportEval breaks incremental compilation because the side effects will not be permanent. This is likely due to some other module mutating ImportEval with eval during precompilation - don't do this.
I set up a minimal example to illustrate where I am struggling:

a, b, c = 1, 2, 3
macro showvars(vars...)
    quote
        for v in $(esc(vars))
            # 1) desired output, but eval prohibited
            println("$v = ", eval(v))

            # 2) not desired output
            println("$v = ", v)

            # 3) errors: 'UndefVarError: `v` not defined'
            # println("$v = ", $v)

            # 4) does not work: for-loop only generates quotes
            quote
                println("$v = ", $v)
            end

            # 5) does not work: same as 2.
            $(quote
                println("$v = ", v)
            end)
        end
    end
end
@showvars c a

The desired output is

c = 3
a = 1

Is it possible to achieve the same behavior as in 1) without using eval somehow? I know I can do it by explicitly constructing the appropriate expression with Expr(...) but I feel there should be an easier, more readable way to do it.

If the variables are on the top level then you can use getfield to get the value from the module, IIRC.

As macros work on expressions, you will need to insert the variables – and not just their names as in your for loop – into the expression generated by the macro. Thus, instead of generating a for loop – which would then run when executed – you could unroll the loop and generate the desired calls to println directly, i.e., here is a working macro for your minimal example:

macro showvars(vars...)
    unrolled = [:(println("$($(QuoteNode(v))) = ", $(esc(v)))) for v in vars]
    quote
        $(unrolled...)
    end
end

When writing macros it helps a lot to check the expansion, i.e.,

julia> @macroexpand1 @showvars c a
quote
    #= REPL[72]:4 =#
    Main.println("$(:c) = ", c)
    Main.println("$(:a) = ", a)
end

Thanks, that was very helpful and solves the problem I described! I don’t fully understand when to use QuoteNode and esc, yet.

There is one more thing I need which is that vars is not a tuple of Symbols but an expression describing a tuple. That is, the macro should be called like this:

macro showvars(vars) end
@showvars (c, a)

The reason is that in my application, it will receive multiple tuples of symbols that are handled differently, e.g. @mymacro (a, b) (c, d).
I am able to do what I need to by using tupleexpr.args in the same way as vars in your unrolled loop, but maybe there is a nicer solution I was not able to find.