Unexpected Boolean flag behavior in Julia 1.12 vs 1.10

I have encountered a strange issue while migrating code from Julia 1.10 to Julia 1.12.3. It seems like a variable assignment within a function is not being reflected correctly in the caller’s scope, or I am fundamentally misunderstanding a change in the recent version.

Here is a minimal working example:

str = "[1 232.4;\n 2 40;\n 2 40];"
write("my_file.txt", str)

function readFile(str::String)
    flag = false

    for line in eachline("my_file.txt")
        if !flag && occursin("[", line)
            flag = true
            println("Flag value after first change: ",  flag)
        end

        if flag
            flag = parseLine(line, flag, "[", "]")
            println("Flag returned from parseLine: ", flag)
        end
    end
end

function parseLine(line::String, flag::Bool, start::String, last::String)
    if occursin(start, line)
        line = split(line, start)[end]
    end

    if occursin(last, line)
        flag = false
        println("Flag updated inside parseLine: ",  flag)
    end

    return flag
end

readFile(str)

The problematic output:

Flag value after first change: true
Flag returned from parseLine: true
Flag returned from parseLine: true
Flag updated inside parseLine: false
Flag returned from parseLine: true      <-- Why is this true?

The last line indicates that even though parseLine explicitly returned false, the flag variable in the readFile function remains true.

Two strange observations:

  1. Adding an else block fixes it: If I modify parseLine to explicitly set flag = true in an else branch, the output is correct (the flag becomes false in the caller scope).
function parseLine(line::String, flag::Bool, start::String, last::String)
    if occursin(start, line)
        line = split(line, start)[end]
    end

    if occursin(last, line)
        flag = false
        println("Flag updated inside parseLine: ",  flag)
    else
        flag = true
    end

    return flag
end
  1. Commenting out the split logic:
if occursin(start, line)
   line = split(line, start)[end]
end

also fixes it.

I am certain this logic worked in Julia 1.10. Has there been a change in how local bindings or string manipulations (like split) interact with function returns in Julia 1.12?

Any help or explanation would be greatly appreciated!

2 Likes

This is possibly a manifestation of a compiler bug

2 Likes

yes, this is a bug that is fixed on master and will be backported to the next 1.12 patch release

2 Likes

1.11 does the correct thing too:

julia> readFile(str) # 1.11.9
Flag value after first change: true
Flag returned from parseLine: true
Flag returned from parseLine: true
Flag updated inside parseLine: false
Flag returned from parseLine: false

Can confirm 1.12 is the anomaly…:

julia> readFile(str) # 1.12.4
Flag value after first change: true
Flag returned from parseLine: true
Flag returned from parseLine: true
Flag updated inside parseLine: false
Flag returned from parseLine: true

…and that nightly fixes this particular example at least:

julia> readFile(str) # nightly 1.14.0-DEV.1694
Flag value after first change: true
Flag returned from parseLine: true
Flag returned from parseLine: true
Flag updated inside parseLine: false
Flag returned from parseLine: false

Wonder where in the compilation process goes wrong, interestingly the @code_lowered doesn’t show a misscoped variable as it appeared to be.

1 Like