SRL Archive - NOTA

Excuse me while I rant


Bixby Sayz

2011-04-14 18:15:16

Didn't want to spam up the rest of the forums so thought I'd put it in here. Just wanted to bitch and whine and stamp my feet. Oh and pout too!

I seem to be always bumping my head into subtle "gotchas" when scripting, wasting hours trying to track down what is going on.

Here's an example:
program new;

var
BoolVar: Boolean;

function BoolFunc: Boolean;
begin
Result := False; // The "magic" happens here
if not BoolVar then
begin
WriteLn('BoolVar is False inside function (without an assignment)');
Exit;
end;
// Some other code here which we never get to
Result := True;
end;

begin
ClearDebug;
BoolVar := True;
BoolVar := BoolFunc;
WriteLn(BoolToStr(BoolVar));
end.
And the output looks like:
BoolVar is False inside function (without an assignment)
False
Successfully executed.
Bugger me! Took me an entire day to find that one.

HarryJames

2011-04-14 18:17:29

You've assigned BoolVar as a global variable, which is automatically set to false.
If you want for it to start false, and then come out true (where the value could've been true to start with, change it to this.

program new;

var
BoolVar: Boolean;

function BoolFunc: Boolean;
begin
BoolVar := False; // The "magic" happens here
if not BoolVar then
begin
WriteLn('BoolVar is False inside function (without an assignment)');
Result := False;
Exit;
end;
// Some other code here which we never get to
Result := True;
end;

begin
ClearDebug;
BoolVar := True;
BoolVar := BoolFunc;
WriteLn(BoolToStr(BoolVar));
end.

Bixby Sayz

2011-04-14 18:30:59

No, my point is BoolVar was changing to False and I could not figure out why when I knew I had explicitly set it to True and never made an assignment to it anywhere else.

(This is a contrived example to avoid posting 50 lines of actual code)

HarryJames

2011-04-14 18:33:34

Ohh, so you've figured out the problem, that was a silly mistake but you had spent ages trying to fix?

Edits!: Ignore me, I'm tired and now going to sleep.
Night!

Bixby Sayz

2011-04-14 18:49:58

Yeah.

Currently I'm trying to figure out an issue using the Arrays in Players.

Something along the lines of: (again this is contrived)

// SomeIntValue = 500150, SomeOtherIntValue = 500000
with Players[CurrentPlayer] do
Arrays[0][12] := Arrays[0][12] + (SomeIntValue - SomeOtherIntValue);

and it always gets assigned 0 even though all the values are nonzero. Huh?

I wound up having to do this:
TempInt := Arrays[0][12];
IncEx(TempInt, (SomeIntValue - SomeOtherIntValue));
Arrays[0][12] := TempInt;
With absolutely no idea why I should have to do that.

Gu3st...

2011-04-14 19:05:13

IMO, it shouldn't be like that… Maybe this is a bug?

IIRC, it didn't use to be like that before? I'm not sure, but I can't make any sense of it the way it's done now…

i luffs yeww

2011-04-14 19:10:14

Change it to Arrays[0][12] := Players[CurrentPlayer].Arrays[0][12] + (SomeIntValue - SomeOtherIntValue);

I think that would work. The whole "with X do whatever" thing isn't greedy, in the sense that it will only do the very foremost assignment, iirc. I may very well be wrong though.

Bixby Sayz

2011-04-14 20:47:19

Didn't fix it, but I'm not surprised.

I have been unable to recreate it with a simpler demo script like I did the example above, so I have something else going on.

Driving me crazy tho. I print out all the values just before and after the assignment so I can see what is going on. Everything looks right, but it doesn't get assigned.

Off-Topic: This is one of those times having a debugger to step through the code and examine vars would be invaluable. Unfortunately it uses reflection so I can't just flip it over to SCAR and debug it.


Edit: Was looking at the code and was able to come up with a simple demo script that reproduces the error. And discovered a fix. My head is still sore from beating my head on the desk when I discovered the issue.

This does not work:
program new;

{$i SRL\SRL.scar}
var
IntVar: Integer;

begin
SetupSRL;
NumberOfPlayers(1);
CurrentPlayer := 0;
with Players[CurrentPlayer] do
begin
SetLength(Arrays[0], 1); Arrays[0][0] := 5;
SetLength(Arrays[1], 1); Arrays[1][0] := 10;
SetLength(Arrays[2], 1);
ClearDebug;

// 5 = 10 - 5
IntVar := (Arrays[1][0] - Arrays[0][0]);
Arrays[2][0] := Arrays[2][0] + IntVar;
WriteLn(Arrays[2][0]);
end;
end.

This does:
program new;

{$i SRL\SRL.scar}
var
IntVar: Integer;

begin
SetupSRL;
NumberOfPlayers(1);
CurrentPlayer := 0;
with Players[CurrentPlayer] do
begin
SetLength(Arrays[0], 1); Arrays[0][0] := 5;
SetLength(Arrays[1], 1); Arrays[1][0] := 10;
SetLength(Arrays[2], 1); Arrays[2][0] := 0; // This assignment fixes it
ClearDebug;

// 5 = 10 - 5
IntVar := (Arrays[1][0] - Arrays[0][0]);
Arrays[2][0] := Players[CurrentPlayer].Arrays[2][0] + IntVar;
WriteLn(Arrays[2][0]);
end;
end.
While variants can hold integers, they are NOT integers. A subtle but important difference. Unlike an integer the initial value is not 0. Trying to add an integer value to an undefined value is…undefined.

If you explicitly assign a value of 0 to the variant then suddenly integer math starts working.

Sigh. Another day gone trying to find that.

i luffs yeww

2011-04-14 21:09:15

Well something's up.


type
TUser = record
Arrays: array of TVariantArray; // * For custom arrays.
end;

var
Players: array of TUser;
some_int, some_other_int: Integer;

begin
SetArrayLength(Players, 1);
SetArrayLength(Players[0].Arrays, 10);
SetArrayLength(Players[0].Arrays[0], 10);
some_int := 500150;
some_other_int := 500000;
Players[0].Arrays[0][0] := 5;
with Players[0] do
Arrays[0][0] := Arrays[0][0] + (some_int - some_other_int);
Writeln(Players[0].Arrays[0][0]);
end.

works just fine for me.

So either you're doing something wrong, or something is wrong somewhere else. Don't know what it could be though.

lordsaturn

2011-04-14 22:40:02

@Bixby

What operating system are you using?

Bixby Sayz

2011-04-14 23:09:04

Tried this on an XP and Windows 7 machine with the same result.

On the XP machine reinstalled Simba and now Disguise does nothing. Ugh.

i luffs yeww

2011-04-14 23:43:52

Did you try my example? Because it works fine for me.

Gu3st...

2011-04-15 03:53:45

var
B : Boolean;

function Bool : Boolean;
begin
WriteLn(B);
Result := False;
WriteLn(B);
end;

begin
B := True;
B := Bool;
end.

Results in this:
Compiled succesfully in 32 ms.
True
False
Successfully executed.

cause

2011-04-15 04:09:41

Bixby this is because when your declaring a variable you give it a memory address to some memory that hasn't necessarily been reset to 0.

Gu3st...

2011-04-15 04:48:01

File a bug report, Bixby

Wizzup?

2011-04-15 05:07:51

Seems like a bug in Pascalscript I encountered ages ago with SCAR, even before Simba existed. Something with temp variables to work around broken assignments. Yes, seems like the same one. If you have a reproducible test case I can file a bug at Pascalscript.

i luffs yeww

2011-04-15 05:29:27

From: Wizzup?
Seems like a bug in Pascalscript I encountered ages ago with SCAR, even before Simba existed. Something with temp variables to work around broken assignments. Yes, seems like the same one. If you have a reproducible test case I can file a bug at Pascalscript.


? :x It works just fine for me?

Gu3st...

2011-04-15 05:43:34

From: i luffs yeww
? :x It works just fine for me?

Try this:
From: Zyt3x
var
B : Boolean;

function Bool : Boolean;
begin
WriteLn(B);
Result := False;
WriteLn(B);
end;

begin
B := True;
B := Bool;
end.

Results in this:
Compiled succesfully in 32 ms.
True
False
Successfully executed.

i luffs yeww

2011-04-15 06:12:42

I'm obviously stupid because I see no problem with that? You set B to True, it prints True. You set B to False, it prints False.

nvm

Narcle

2011-04-15 06:27:08

From: i luffs yeww
I'm obviously stupid because I see no problem with that? You set B to True, it prints True. You set B to False, it prints False.

nvm


Well what I see is the function didn't end yet to set B to false. It should still be true true inside the function then true false after.

E:
heh

nielsie95

2011-04-15 07:24:53

Pretty serious bug :(

Markus

2011-04-15 09:00:48

Not a bug, just badly documented behaviour.
Boolvar == result, result just gets aliased to boolvar. Which means if you set result to false, you set boolvar to false since they are the same variable but just with different names.

@Wizzup: this has NOTHING to do with the TPA assignment bug,


program new;

var
BoolVar: Boolean;

function BoolFunc: Boolean;
begin
if result then
begin
WriteLn('Hey look, the result is true ;)');
Result := False;
Exit;
end else
writeln('Hmm, result == boolvar is false..');
// Some other code here which we never get to
Result := False;
end;

begin
ClearDebug;
BoolVar := True;
BoolVar := BoolFunc;
BoolVar := BoolFunc;
WriteLn(BoolToStr(BoolVar));
end.
Presto, it works.

More clear demo:

program StringDemo;
var hoi : string;
function doei : string;
begin
writeln('Result = '+result);
end;
begin
hoi := 'Hoi!';
Doei;
hoi := doei;
end.

@OP: If you really want to have fun with subtle gotcha's, try messing around with words, bytes and assignments like that. For example, assign a byte to a word, then bitshift it:

program new;
var
AF : word;
A : byte;
begin
A := 255; //max byte
writeln(Padr('A = ', 40)+inttostr(a));
inc(a);
writeln(Padr('Hey look, A just had an overflow: ', 40)+inttostr(a));
A := 255; //set it back
AF := A shl 5;
writeln(Padr('AF is now: ', 40)+inttostr(af));
AF := 255 shl 5;
writeln(Padr('But what should it be? It''s: ', 40)+inttostr(AF));
AF := word(a) shl 5
Writeln(Padr('Now we try this leet workaround: ', 40)+inttostr(af));
AF := 255 shl 5 and $ff
writeln(Padr('Explanation: the assignment is a byte: ', 40)+inttostr(AF));
end.

That took me hours to find out and gave me a LOT of bugs in my z80 emu.

nielsie95

2011-04-15 09:08:14

This is a bug..

Markus

2011-04-15 09:19:03

Actually it's undefined behaviour, the pascal standard says nothing about this.

Bixby Sayz

2011-04-15 09:21:59

From: nielsie95
This is a bug..


No just strange behavior that can throw you for a loop until you understand what is going on.

From: Markus
For example, assign a byte to a word, then bitshift it:.


OMG That would take forever to figure out what is going on.

i luffs yeww

2011-04-15 09:22:40

From: Markus
Actually it's undefined behaviour, the pascal standard says nothing about this.


I'm agreeing with Markus..

Gu3st...

2011-04-15 10:15:08

I agree with nielsie95… In Markus' StringDemo he never sets "result" to anything, thus "doei" should never result anything…

It would make more sense if we were talking about pointers of some sort here, but that's not the case

Bixby Sayz

2011-04-15 10:18:55

I have no idea. All I know is if I set an initial value of 0 it works. If I do not, it doesn't. And that is all I need to know.

nielsie95

2011-04-15 10:23:25

If the Pascal Standard doesn't say anything about this, I guess you're right it's not a bug. Personally, I'd rather see it changed, but it looks like it's been like this for quite some time (SCAR 3.15 is the oldest I could find to test with). Looks like Pascal Script just handles it as an extra var parameter.

Still might be a good idea to file a bug report, though?

Wizzup?

2011-04-15 11:04:40

Eh, undefined behaviour is a bug by definition. Plus, I don't see how this behaviour is not ``defined''. The variable is simply to be assigned AFTER the call, not before it, not in it, not at the moment the result is set; it can still change afterwards. It's not proper behaviour. Standards are defined inductively. In this case, it should simply call the procedure, return the value and then assign B the value. That's how it should work, that's how the assignment is defined. First evaluate the method call, then assign. That's how imperative languages work.

Markus, I was not talking about a TPA bug, I don't even know what PS bug you are referring to right now?