AND and OR Blueprint nodes' connections are always evaluated (no short-circuit evaluation)

Note: This bug was original posted in the UT forum. I was asked to report it in the engine forum (forwarding me here). I mostly kept the structure from that post and added repro steps.

AND and OR nodes in Blueprint behave differently to how C++ (or other common [script] language) works.

Example code:
C++

AController* Controller = NULL;
// if (Controller && Controller->PlayerState)
if (Controller != NULL && Controller->PlayerState != NULL)
{
    // DO SOMETHING
}

This is a valid if-condition (PlayerState won’t be accessed; no crash).

UnrealScript

local Controller C;

C = none;
if (Controller != none && Controller.PlayerState != none)
{
    // DO SOMETHING
}

Similar to C++. No “accessed none” log (no crash).

Blueprint equivalent

This casues an error like this:

The log will contain an entry like this:
LogScript:Warning: Accessed None 'NewVar_0'
    Untitled_C /Temp/UEDPIE_0_Untitled_3.Untitled_3:PersistentLevel.Untitled_C_0
    Function /Temp/Untitled_3.Untitled_C:ExecuteUbergraph_Untitled:0074
PIE:Error: Error Accessed None 'NewVar_0' from node Branch in graph 'EventGraph' in blueprint Untitled

The Blueprint is trying to access the class field PlayerState of “NewVar_0” because it would process something like this (in C++):

AController* Controller = NULL;
bool b1 = Controller != NULL;
bool b2 = Controller->PlayerState != NULL;
if (b1 && b2)
{
    // DO SOMETHING
}

Controller->PlayerState would result into a crash (null pointer) which is catched by the Blueprint VM and reported as an error. The and boolean node is using UKismetMathLibrary::BooleanAND(bool, bool) which takes two evaluated boolean values. This is likely the source of the problem.

The OR node behaves the same.

if (Controller != NULL || GetController(Controller)) // GetController(ByRef)
{
    // DO SOMETHING
}

GetController would only be called if the Controller variable is not already set.

For Blueprint test, I created two pure functions which both calling a unique message and return true.

62363-bug_or_node_function.png

62364-bug_or_node_always_evaluated.png

Log:

LogBlueprintUserMessages: Func1 called.
LogBlueprintUserMessages: Func2 called.

Func2 is also processed even if there is no need for it. In C++ it would only process Func1 as it already returns true (true || ? == true).

A workaround for the AND node would be using separate Branch nodes sequentially. This is not ideal for more than 2 expressions (and you have to process/link each false case).

Original post in the UT forum:
[AND and OR nodes’ connections are always evaluated (no associativity)][5]

Additional reference:
[MSDN: Logical AND Operator: &&][6]
[MSDN: Logical OR Operator: ||][7]


Editor version:
4.8.0-2710036+++depot+UE4-UT-Releases
UT Editor Build 2710036 (10/01/2015)

Detailed description of the issue:
The associativity of the Blueprint nodes AND, OR, … are not the same like these from C++ (and other common languages). Each connected statement/node will be evaluated.

Repro Steps:

  1. Create a new level (File > New level)
  2. Open the level blueprint editor (Toolbar Blueprints > Open Level Blueprint)
  3. Create a variable of type Controller (My Blueprint > Add New > Variable)
  4. Add an Event Begin Play node to the EventGraph (or use the existing greyed-out one)
  5. Connect the above to a Branch node
  6. Drag from the Condition and create a AND boolean node.
  7. Drag and drop the new variable (NewVar_0) from the My Blueprint tab into the EventGraph
  8. Drag from that variable and create a Is Valid node
  9. Connect this node to the A pin of the branch node.
  10. Drag another time from the variable and create a Get Player State node
  11. Drag from that player state node and create another IsValid node
  12. Connect that 2nd Is Valid node to the B pin of the branch node
  13. Choose to play that level
  14. Exit the Play In Editor session (Escape) and open the output log (Window > Developer Tools > Output log

The Level Blueprint should looks like this:

The log will contain an entry like this:

LogScript:Warning: Accessed None 'NewVar_0'
    Untitled_C /Temp/UEDPIE_0_Untitled_3.Untitled_3:PersistentLevel.Untitled_C_0
    Function /Temp/Untitled_3.Untitled_C:ExecuteUbergraph_Untitled:0074
PIE:Error: Error Accessed None 'NewVar_0' from node Branch in graph 'EventGraph' in blueprint Untitled
1 Like

Hey RattleSN4K3-

I was able to reproduce the behavior you mentioned and have entered a bug report (UE-22021) for investigation.

Cheers

Getting modulo by zero log spam even when checking for a positive number due to this. Will this be addressed in the near future? Branches aren’t a reasonable solution to this.

Currently our resources are dedicated elsewhere and UE-22021 is not a priority item for us to work on. We don’t have a timeframe for when this may be addressed. Please keep in mind that with source code access, a programmer on your project can implement your own solution, and you are welcome to share that result with Epic for possible integration.

I haven’t had the opportunity to test the generation of C++ from blueprint, and I’ll probably never have time. Do you know if the C++ generated from the blueprint takes into account that all AND paths must be run?

(What I’m trying to say is…I hope it doesn’t generate something along the lines of:
if( FuncA() && FuncB() && FuncC() ) { DoThing(); }
… does it?)

Just thought I’d add here that you might want to look at using validated get functions as well (Right click variable), as they can sometimes be much cleaner. Obviously doesn’t help with the actual bug issue at hand.

215866-capture.png

Hey Gareth-

Are you referring to nativizing the blueprint? As RattleSN4K3 mentioned, the blueprint node uses UKismetMathLibrary::BooleanAND() and this is how it appears in the nativized asset as well; so this behavior would also exist with nativized blueprints.

Thanks for clearing that up . I just didn’t know enough about the conversion process. Now I do. :slight_smile:

Any update on when UE-22021 will be fixed?

Looking at the issue as of now, it was closed as “Won’t Fix” in August 2021 so unfortunately this remains an issue today and need to use the branch node trick to force the equivalent “short-circuited” behavior. These nodes continue to function like their “bitwise operator” equivalents.