Page MenuHomeFeedback Tracker

New "if ... throw" implementation is broken
Closed, ResolvedPublic

Description

Now this is critical. We have changed 450 files to adapt the new throw command and figured out that it does behave wrong. Even though the if statement results in false, it still executes the code in the throw, but does not throw. Sounds weird, but try it.

Details

Legacy ID
2855551444
Severity
None
Resolution
No Bug
Reproducibility
Always
Category
Scripting
Steps To Reproduce

// Broken one, throws NOPE, but still prints U and V
Foo =
{
systemChat _this;
false
};

try
{
if (false) throw ("U" call Foo);
if (false) throw ("V" call Foo);

throw "NOPE";
}
catch
{
systemChat (str _exception);
};

// Throws only "NOPE", like it should. It still evaluates "U" and "V" but that does obviously nothing. Which is probably the reason no one noticed that before
try
{
if (false) throw "U";
if (false) throw "V";

throw "NOPE";
}
catch
{
systemChat (str _exception);
};

Writes "HELLO" to the systemChat, even though it shouldnt
This prooves the script engine still evaluates the throw content
// even if not necessary
try
{
if (false) throw (systemChat "HELLO");
}
catch
{

};

Event Timeline

Eichi edited Steps To Reproduce. (Show Details)Dec 4 2015, 12:11 AM
Eichi set Category to Scripting.
Eichi set Reproducibility to Always.
Eichi set Severity to None.
Eichi set Resolution to No Bug.
Eichi set Legacy ID to 2855551444.May 8 2016, 1:13 PM

I'm afraid this is not a bug but expected behaviour. You pass an argument to throw command which is a result of the function call. With any other command the compiler will behave in the same way, it will first call the function to obtain return value and only then use the value as the argument for the command.

You probably did not expect the function to be executed at all, but there is no reason why it shouldn't, it is inside try {} scope that has been entered. In other cases when you have throw inside then {} scope, it is different because then {} scope is only entered when "if" before that evaluates to true. And since it is false in your example it never has a chance.

Compare this to getVariable with devault value. Same thing would happen, if default value is a function call, it will first call the function.

a = 10;
b = missionNamespace getVariable ["a", 123 call abc];

the abc function will always run, regardless if a is or isn't defined. I hope this clears it up and sorry for the time you have wasted on changing.

Grim added a subscriber: Grim.May 8 2016, 1:13 PM
Grim added a comment.Dec 4 2015, 12:54 AM

if(false){do something here anyway. ????!?!??!?!?}?

Bohemia added a subscriber: Bohemia.May 8 2016, 1:13 PM

I really was expecting this to be a programming logic mistake.

You are saying its expected behaviour for performance reasons ???
So the the script engine is executing the call function inside the throw, regardless if is needed or not.
In general use throw statements in general aren't executed often.
Evaluating the throw all the time will cause overhead, especially when using a loop.

This behaviour will cause confusion with most programmers and honestly should just be removed from Arma imo

---------

So the only use for
IF THROW is for when returning static text to an exception, otherwise its completely useless.

Eichi added a subscriber: Eichi.May 8 2016, 1:13 PM
Eichi added a comment.Dec 4 2015, 1:08 AM

"but there is no reason why it shouldn't"
-> if (false)

Or in human words:

"If you must not do that, do it anyways".

So basically you're telling us, that this unlogical behavior, that doesn't occur in every proper programming language in the world, is by design? What's the sense of that "if" then, if the script engine completely disregards it?

The engine evaluates the throw before the if, just in case it needs to throw something? This means the engine is reading the code backwards.

I really can't handle this anymore. Even Visual Basic is more advanced than this.

Sorry for the hate, but this really makes me upset.

There are 3 templates for IF command now

IF BOOL THEN CODE (ELSE CODE)
IF BOOL THEN ARRAY and
IF BOOL THROW ANYTHING

compiler must find a match in templates for your expression otherwise it throws an error. It reads the code forward but it needs to know all the arguments like for example is it a CODE after THEN? Because you can use variable, doesnt have to be block with curly brackets.

if (true) then BIS_fnc_whatever

BIS_fnc_whatever needs to be evaluated. So when you use throw like this, its argument needs to be evaluated just as well.

Also let me remind you that original throw functionality has not changed a bit, you can still

if (false) then {throw (123 call abc)}

and abc will never execute.

PS just to make sure everyone understands this

if (false) throw <whatever>

throw above does not throw anything, command never executes.

"You are saying its expected behaviour for performance reasons ???"

It is just normal behaviour.

abc =
{
systemchat str _this;
{}
};

if (false) then (123 call abc);

Expression after then is still evaluated and function is called. This is normal.

So an example of normal behaviour is
IF Statement which is always false that executes code -- Brillant thanks :)

You could just replace the if statement with
123 call abc;

It would accomplish the exact same thing with less overhead from the IF.
The only reasons to use IF THEN (), is to confuse/troll people reading sqf code.

---------------------

Honestly this sounds like a sqf script engine design quirk.
I still think that the IF THROW behaviour goes against normal logic & it will just confuse people. In its current state i would just remove it.

---------------------

But thanks for the info about getVariable & default values.
I need to double check my code for this now aswell, just incase.

Eichi added a comment.Dec 4 2015, 9:43 AM

"You tell your car not to drive, but it drives anyways" - That's not a problem. This is normal. And normal behavior.

I agree with Torndeco. Better remove this unlogical construct, as it is completely useless and will only break things.

commy2 added a subscriber: commy2.May 8 2016, 1:13 PM

Downvoted.
This is expected behavior. The expression:
("U" call Foo)
is evaluated before:
if (false) throw
, because it has round brackets around it.

It's return value (always "false") is thrown and passed as _expression. Can be easily tested by replacing the first
if (false)
with
if (true)
The current "try throw catch"-syntax is useless in my opinion. Even with the recent "improvements". It should be avoided altogether.