godotengine / godot

Godot Engine – Multi-platform 2D and 3D game engine
https://godotengine.org
MIT License
89.04k stars 20.19k forks source link

GDScript bitwise AND evaluating to Nil when using variables #44411

Closed briansemrau closed 3 years ago

briansemrau commented 3 years ago

Godot version:

4.0.dev 94b15bfb89

OS/device including version:

Windows 10

Issue description:

I'm trying to use bitwise AND in a project to check flags. However, this operator does not seem to work unless using integer literals.

Edit: From my comment below, using this operator can also cause a crash if an Array or Dictionary is declared earlier. Probably can use other types that use refs.

Steps to reproduce:

Using the code:

var one: int = 1
var also_one: int = 1

print(1 & 1)
print(one & 1)
print(1 & one)
print(one & also_one)

prints the following:

1 Null Null Null

Minimal reproduction project:

GDSBitAndMinRep.zip

Calinou commented 3 years ago

I can reproduce this on master Git 6ccc6b6e2 but not on 3.2 Git fd6b3060c.

On master, I get:

1 Null Null Null

On 3.2.x, I get:

1 1 1 1

briansemrau commented 3 years ago

I'm actually able to produce a crash using this operator.

Using the script:

extends Node

func _ready():

  var dict: Dictionary = { # this can also be an Array, can be empty
      "key": "value"
  }

  var one: int = 1  # must have static type, inferred or defined

  var myval = one & 1  # must use bitwise AND with at least one variable

I get the crash log:

CrashHandlerException: Program crashed Dumping the backtrace. Please include this when reporting the bug on https://github.com/godotengine/godot/issues [0] _atomic_conditional_increment_impl (C:\Users\brian\Documents\GitHub\mygodot4\core\templates\safe_refcount.cpp:65) [1] atomic_conditional_increment (C:\Users\brian\Documents\GitHub\mygodot4\core\templates\safe_refcount.cpp:116) [2] SafeRefCount::ref (C:\Users\brian\Documents\GitHub\mygodot4\core\templates\safe_refcount.h:175) [3] Dictionary::_ref (C:\Users\brian\Documents\GitHub\mygodot4\core\variant\dictionary.cpp:155) [4] Dictionary::Dictionary (C:\Users\brian\Documents\GitHub\mygodot4\core\variant\dictionary.cpp:264) [5] Variant::reference (C:\Users\brian\Documents\GitHub\mygodot4\core\variant\variant.cpp:1146) [6] Variant::operator= (C:\Users\brian\Documents\GitHub\mygodot4\core\variant\variant.cpp:2691) [7] GDScriptFunction::call (C:\Users\brian\Documents\GitHub\mygodot4\modules\gdscript\gdscript_vm.cpp:1031) [8] GDScriptInstance::call (C:\Users\brian\Documents\GitHub\mygodot4\modules\gdscript\gdscript.cpp:1555) [9] ScriptInstance::call (C:\Users\brian\Documents\GitHub\mygodot4\core\object\script_language.cpp:314) [10] Node::_notification (C:\Users\brian\Documents\GitHub\mygodot4\scene\main\node.cpp:147) [11] Node::_notificationv (C:\Users\brian\Documents\GitHub\mygodot4\scene\main\node.h:45) [12] Object::notification (C:\Users\brian\Documents\GitHub\mygodot4\core\object\object.cpp:795) [13] Node::_propagate_ready (C:\Users\brian\Documents\GitHub\mygodot4\scene\main\node.cpp:189) [14] Node::_propagate_ready (C:\Users\brian\Documents\GitHub\mygodot4\scene\main\node.cpp:181)

briansemrau commented 3 years ago

If anyone else is currently trying to use the bitwise AND operator on master, a workaround is to do this in the meantime:

# var output = valueA & valueB
# do this instead:
var output = ~(~valueA | ~valueB)
lyuma commented 3 years ago

Thank you. I was going to report this but you all got to it before me.

~(~valueA | ~valueB) is a nice snippet.

Much better than the silly workaround function I was using (I got it from stackoverflow heh):

static func bitand(a : int, b : int) -> int:
    var c : int = 0
    for x in range(64):
        c += c
        if (a < 0):
            if (b < 0):
                c += 1
        a += a
        b += b
    return c

One question I had though... my code had a case where & works and produces a result equal to 0. For example, due to another bug, Error values get random high order bits set, so I needed to and them with 0xffffffff So it attempts to correct for it with this code:

    var e = get_tree().multiplayer.send_bytes(my_packet, NetworkedMultiplayerPeer.TARGET_PEER_BROADCAST, NetworkedMultiplayerPeer.TRANSFER_MODE_UNRELIABLE)
    if (e & 0xffffffff) != OK:
        printerr("send_audio_packet: send_bytes failed! %s" % e)

And in this case, the printerr never happens.

... though who knows what other bugs are out there. Does null == 0?

briansemrau commented 3 years ago

@lyuma In my code, the bitwise-and often evaluated to null which caused if-statement conditions to never pass. This may be happening to you. Though I'm not sure if null == 0 is true. If that's not what you meant, it's possible (I'm speculating) that the & operator is using the wrong operand values and it happened to use an int. This would produce a number as an output, even if meaningless.

By the way, you don't need to use bitwise-and in your example use case. You can just use if e: .... If e is non-zero it should evaluate as true. (unless it's different for negative numbers? I forget the standard)