ID:2715614
 
BYOND Version:514
Operating System:Windows 10 Home 64-bit
Web Browser:Firefox 92.0
Applies to:Dream Maker
Status: Open

Issue hasn't been assigned a status value.
Descriptive Problem Summary:
It seems that in some cases local uses of a member variable in a proc are "cached" and can return old values even after the member variable has been changed.
The circumstances under which this seems to happen are pretty specific.
It seems that the member variable (t2 in this case) needs to be nulled / changed by a proc (break_stuff() in this case) on t2 which gets called first. Then if a variable on t2 is accessed (accessing t2 itself does NOT lead to the issue) for the FIRST time and it is an argument to a proc (as opposed to being an argument to << or assigned to a var) then the access to this variable gets the value of the object that USED to be in the variable but no longer is (t2.name in this case).
This is a confusing explanation so please inspect the code snippet.

Numbered Steps to Reproduce Problem:
Compile code below and run main(). Observe output, consult Expected/Acutal section below.

Code Snippet (if applicable) to Reproduce Problem:
proc/print(x)
world.log << x

/datum/test2
var/name = "test2"
var/datum/test/t
proc/break_stuff()
t.t2 = null

/datum/test
var/datum/test2/t2 = new
New()
t2.t = src
proc/a()
t2.break_stuff()
print("[t2.name]")
print("[isnull(t2)]")

proc/main()
var/datum/test/test = new
test.a()


Expected Results:
break_stuff() sets t2 to null and print("[t2.name]") runtimes as null.name cannot be accessed.

Actual Results:
Despite break_stuff() setting t2 to null its name is printed as "test2".
Then isnull(t2) prints 1 confirming that t2 is indeed null.

Does the problem occur:
Every time? Or how often?Every time
In other games?N/A
In other user accounts?N/A
On other computers?Yes

When does the problem NOT occur?
Using world.log<< instead of a proxy method makes the problem disappear.
Doing print("[t2.name]") twice makes the problem not appear on the second call.
Setting t2 to null through simpler mechanisms than this seems to also make the issue not happen usually.

Did the problem NOT occur in any earlier versions? If so, what was the last version that worked? (Visit http://www.byond.com/download/build to download old versions for testing.)
From a brief tst 513.1490 also has the issue, likely goes way back.

Workarounds:
I have no idea.
Ah, I know why this is, and it's not going to be a simple fix.

Internally, a "loc" var is used (herein I'll call it $loc) when accessing sub-vars, and to avoid a lot of repetitive lookups the compiler will simply shortcut most var accesses to use that whenever it can.

For example:
src.thing.foo = "bar"
src.thing.gold++

// internally this is...
$loc = src.thing
$loc.foo = "bar"
$loc.gold++

Any instance of accessing a.b will replace $loc with a, as long as a is a different var path than the last one that was used for $loc.

In the past, I probably could've gotten away with telling the compiler to simply reset this on every proc call. However since the addition of operator overloading, proc calls may now be implicit.

I'm thinking what might be needed here is a "volatile" keyword that says a certain var may always be subject to change and therefore can't be reused in this manner. Anything that might includes a volatile var in $loc should then be told to automatically reset. I'll add this to the list for 515. (Obviously of course any var might be subject to change in the method you mentioned. But generally the compiler will want to optimize what it can and usually that's a good idea, so it makes sense to give it hints in this case.)

A workaround for this situation is just to access any other member var, via either src or a different object. You don't have to do anything with the var, as long as $loc changes.
Thanks for the very quick response!

Login to reply.