Repository: godot
Issue: Supplying negative left-hand oparand to bit-shift operators produces an error (or undefined behavior)
Labels: discussion,topic:gdscript

### Tested versions

Reproducible in 4.2.1

### System information

Godot v4.2.1.stable - Linux Mint 21.2

### Issue description

Bit shift operators (>> and <<) in GDScript throw an error when providing a negative number as the left-operand:
![image](https://github.com/godotengine/godot/assets/69174851/071cdfa2-a0a0-4d8c-a016-fc02896c162a)

This makes it difficult to treat ints (signed int64s in GDScript) as raw bit data, and complicates accessing bit data directly (specifically, the left-most bit of an int).

There is no reason to prevent negative signed int64s ("negative" doesn't mean much in terms of a bit-field such as `~0` ie. all 1 bits) from having bitwise operations performed on them:
`-1 >> 1` or equivalent `~0 >> 1` (ie. `0xFFFF_FFFF_FFFF_FFFF >> 1`)
should simply return `0x7FFF_FFFF_FFFF_FFFF`.

Godot allows the treatment of ints as raw bit data in all regards (with bitwise operations: `|`, `&`, `~`, etc), but prevents this for negative numbers (ie. ints with a `1` in the left-most bit position) for bit shifting. A bitwise operation doesn't operate on a number so much as it operates on the raw bit data of the int. The use of bitwise operations in the first place generally assumes that the user would no longer like to treat the int as a number, but rather operate on the inner bit data.

I understand that C++ does not support a negative value as either operand of a bitshift operator either, but in GDScript we have no unsigned int type to work with. To implement shifting of negative int64s in the engine, the GDScript int64 can simply be converted to uint64 if it's negative before shifting.

To make matters worse, the error that prevents this in the editor does **not** produce an error at runtime, and instead produces what I assume is undefined behavior (if the behavior is the same as supplying negative signed-ints to bitshift operators in C++):
```
var num: int = 0
num -= 1
print(num)
print(num >> 1)
```
Prints:
```
-1
-1 # Something is wrong here. Undefined behavior?
```
Screenshot of explanation above:
![image](https://github.com/godotengine/godot/assets/69174851/b0dfe0a9-afe9-4941-baa2-1bae980ed7b1)

#### Common use-case that this error prevents
Say I have 2 32-bit ints that I would like to pack into a single GDScript int to save space. I can do this easily with:
```
var num1 := 0xFFFFFFFF
var num2 := 0xFFFFFFFF
var packed_data: int = num2 << 32 | num1
```  
Now, I would like to extract these values. The following intuitive code does not work when num2 has a 1-bit in the left-most (for a 32-bit int) position:

```
# Trim off the front 32-bits to extract num1. Works.
var unpacked_num1 := packed_data & 0xFFFFFFFF
# Shift the high 32-bits back to the right to extract num2. Not only does this not work, it produces
# undefined behavior as explained above (I am getting -1).
var unpacked_num2 := packed_data >> 32 # Not 0xFFFFFFFF
```

The only approach I can think of to avoid shifting negative values in this case is:

```
var unpacked_num1 := packed_data & 0xFFFFFFFF
var unpacked_num2: int
if packed_data >= 0:
  # For positive int64s, there is no issue.
  unpacked_num2 = (packed_data >> 32)
else:
  # For negative int64s, we have to shift the packed data to the right. Since we can't shift negative values:
  # 1. Invert the packed data and shift it right.
  # 2. The data is now successfully shifted, but all values are inverted, so invert them back.
  # 3. The data is now correct, but 32 left bits are 1s instead of 0s. Trim them off by bitwise-and of 0xFFFFFFFF.
  unpacked_num2 = ~(~packed_data >> 32) & 0xFFFFFFFF
```

### Steps to reproduce

```
# Editor error. Expected value of `i` is `0x7FFFF_FFFF_FFFF_FFFF` (`9223372036854775807`).
var i = -1 >> 1

# Runtime issue. Undefined behavior with no error.
var num: int = 0
num -= 1
print(num)
print(num >> 1) # Undefined behavior?
```

### Minimal reproduction project (MRP)

N/A