Repository: JuliaLang/julia
File: JuliaLowering/test/scopes.jl (Julia)

Code before:
        @test !global_assigned
    end

    @testset "soft scope isn't top level" begin
        ex = quote
            begin
                for i in 1:1; global soft_assigned_explicit_global = 1; end
                for i in 1:1; soft_assigned_explicit_global = 2; end
            end
        end
        expr_eval(test_mod, enable_softscope(ex))
        @test test_mod.soft_assigned_explicit_global === 1
    end
end

# Distinct from the stateful "existing global" check (probably to get around the
# case where the global only becomes existing within the expression being
# lowered)
@testset "assignments at top level can influence assignment resolution in soft scopes" begin
    for soft_mode in (true, false),
        s1 in (inner_neutral, (e)->inner_neutral(inner_neutral(e))),
        g_assign in (:(assigned_global = false), :(global assigned_global = false))

        inner_assign_islocal = s1(Expr(
            :block,
            :(assigned_global = true),
            Expr(:(=), :out, Expr(:islocal, :assigned_global))))

        for ex in (Expr(:block, :(local out), inner_assign_islocal, g_assign, :out),
                   Expr(:block, :(local out), g_assign, inner_assign_islocal, :out))

            if soft_mode
                ex = enable_softscope(ex)
                ok = expr_eval(test_mod, ex) === false
                !ok && error("expected assignment to global\n", ex)
                @test ok
            else
                # some of these produce warning in flisp
                ok = expr_eval(test_mod, ex) === true
                !ok && error("expected assignment to local\n", ex)
                @test ok
            end
            Base.delete_binding(test_mod, :assigned_global)
        end
    end
end

# Note: Certain flisp (un)hygiene behaviour is yet to be implemented.
# In flisp, with no escaping:
# - Top-level functions are unhygienic and declared in the macro's module
# - Top level globals are unhygienic and declared in the calling module

Review comment (suggestion):
Might be nice to encode these skip / error cases into `results`, but pretty orthogonal to this PR

Code after:
        (true,  decls_neutral, wrap_none,    wrap_neutral) => (true,true),
        (true,  decls_none,    wrap_hard,    wrap_func   ) => (true,false),
        (true,  decls_none,    wrap_hard,    wrap_hard   ) => (true,false),
        (true,  decls_none,    wrap_hard,    wrap_neutral) => (true,false),
        (true,  decls_none,    wrap_neutral, wrap_func   ) => (true,false),
        (true,  decls_none,    wrap_neutral, wrap_hard   ) => (true,false),
        (true,  decls_none,    wrap_neutral, wrap_neutral) => (true,true),
        (true,  decls_none,    wrap_none,    wrap_func   ) => (true,false),
        (true,  decls_none,    wrap_none,    wrap_hard   ) => (true,false),
        (true,  decls_none,    wrap_none,    wrap_neutral) => (true,true),
    )
    expected_s(b::Bool) = b ? "assignment to outer var" : "brand-new var"

    tmp_test_mod = Module()
    tmp_test_mod_2 = Module()

    for ((soft_mode, decls_s, middle_s, assign_s), results) in expected_outer_vals,
            (lhs_i, lhs) in enumerate(lhs_names)

        ex = decls_s(middle_s(assign_s(:($lhs = true))), lhs)
        soft_mode && (ex = enable_softscope(ex))

        if lhs in (:argname, :spname) && decls_s !== decls_func
            continue
        elseif lhs === :spname
            @test_throws LoweringError expr_eval(tmp_test_mod, ex)
        else
            expected = results[lhs_i]
            reference_ok = reference_eval(tmp_test_mod_2, ex) === expected
            !reference_ok && @error("flisp produced unexpected result; fix that or JL scope tests:\n",
                                   "expected $(expected_s(expected)), got $(expected_s(!expected))\n", ex)
            ok = expr_eval(tmp_test_mod, ex) === expected
            !ok && @error("expected $(expected_s(expected)), got $(expected_s(!expected))\n", ex)
            @test ok
        end

        if lhs === :gname
            Base.delete_binding(tmp_test_mod, :gname)
            Base.delete_binding(tmp_test_mod_2, :gname)
        end
    end
end

@testset "global declarations at top level are ignored in assignment resolution" begin
    suggest_global(e) = :(begin; global declared_unassigned_global; $e; end)
    for soft_mode in (true, false), scope in (wrap_func, wrap_hard, wrap_neutral)
        ex = scope(:(declared_unassigned_global = true))
        soft_mode && (ex = enable_softscope(ex))
        expr_eval(test_mod, ex)
        global_assigned = @invokelatest isdefined(test_mod, :declared_unassigned_global)
        global_assigned && error("global should not be assigned. settings: $soft_mode $scope\n")