Exporting variables from local scope introduced by macro

I want to write a macro that, before the enclosed expressions is run, activates “unsafe mode” and deactivates this mode again afterwards, kind of like

@unsafe_mode my_expr

translating to

unsafe(true)
my_expr
unsafe(false)

This is not too hard to write, but it gets trickier. The expression passed in by the user might fail, and must thus be enclosed in a try/finally block, so that unsafe(false) is always run no matter what. This is where I run into problems. If my_expr containts an assignment, I want to declare those assigned variables local outside of the try block so that the user can use them. I have not managed to do this. Here is my attempt

using MacroTools
state = false
unsafe_state() = state
unsafe_state(x) = (global state = x)

macro unsafe_comparisons(ex)
    @capture(ex, assigned_vars__ = y_) # if assigned_vars is empty do something
    quote
        previous_state = unsafe_state()
        unsafe_state(true)
        local $(esc.(assigned_vars)...)
        local res
        try
            res = ($(esc(ex)))
        finally
            unsafe_state(previous_state)
        end
        res
    end
end

julia> @unsafe a = 1
ERROR: syntax: invalid syntax in "local" declaration

I also tried

macro unsafe(ex)
    @capture(ex, assigned_vars__ = y_)
    ex1 = quote
        previous_state = unsafe_state()
        unsafe_state(true)
    end
    ex2 =Expr(:local, esc.(assigned_vars)...)
    quote 
        $ex1
        $ex2
        local res
        try
            res = ($(esc(ex)))
        finally
            unsafe_state(previous_state)
        end
        res
    end
end

julia> @unsafe a = 1
1

julia> a
ERROR: UndefVarError: a not defined

which returns the correct value but does not define a.
I’m kind of lost at this point as to what to try :confused:

Edit:
The second version actually works if the macro is used inside a function

julia> test() = (@unsafe(a = 1); a)
test (generic function with 1 method)

julia> test()
1

It does however not work if the macro is used in global scope. I suspect that when used in global scope, I have to somehow declare the assigned variables local instead of global, but I do not know how to detect this.

You could perhaps use Expr(:tryfinally, ...) as in https://github.com/KristofferC/TimerOutputs.jl/blob/b19ecbf2b044c99f783ddb7ec53c5d0d7bc235d3/src/TimerOutput.jl#L216-L221.

Thanks for your reply!
I managed to circumvent the exporting problem with a little trick, I deconstruct the full result of the user expression into the user-defined assigned variables after the try-finally block:

macro unsafe(ex)
    ex2 = if @capture(ex, assigned_vars__ = y_)
        if length(assigned_vars) == 1
            esc(assigned_vars[1])
        else
            esc.(assigned_vars[1].args)
        end
    else
        :(res)
    end
    quote
        previous_state = USE_UNSAFE_COMPARIONS[]
        unsafe_comparisons(true, verbose=false)
        local res
        try
            res = ($(esc(ex)))
        finally
            unsafe_comparisons(previous_state, verbose=false)
        end
        $ex2 = res
    end
end