Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Bitwise assignment doesn't change type #1340

Open
bugreportuser opened this issue Feb 17, 2019 · 13 comments
Open

Bitwise assignment doesn't change type #1340

bugreportuser opened this issue Feb 17, 2019 · 13 comments

Comments

@bugreportuser
Copy link
Contributor

If $x is false, using |= true should make it 1.

Example (https://getpsalm.org/r/11805c0b24):

<?php
$x = false;
$x |= rand(0, 1) !== 2;
$x |= true;
$x |= 1;
if ($x) {
    echo $x;
}

Example output:

Psalm output (using commit a428b34):

ERROR: TypeDoesNotContainType - 6:5 - Found a contradiction when evaluating $x and trying to reconcile type 'false' to !falsy
@muglug muglug added the bug label Feb 17, 2019
muglug added a commit that referenced this issue Feb 18, 2019
@muglug
Copy link
Collaborator

muglug commented Feb 18, 2019

I've fixed this for ints - not sure about bools

@bugreportuser
Copy link
Contributor Author

Thanks. Are you willing to add support for bools later or at least a warning?

@muglug
Copy link
Collaborator

muglug commented Feb 18, 2019

Yeah, going to keep this open because it's a valid use case

@muglug muglug added enhancement and removed bug labels Feb 18, 2019
@SignpostMarv
Copy link
Contributor

Just checking in; is the following example a separate issue to this one? https://psalm.dev/r/222baddf7b

@bugreportuser
Copy link
Contributor Author

@SignpostMarv It's a separate issue but similar to #2139.

@orklah
Copy link
Collaborator

orklah commented Nov 30, 2020

This has been reworked since and now boolean operations with bitwise operators emits an error. Not sure we can still consider this a valid use case

@bugreportuser
Copy link
Contributor Author

You're right. I've had to avoid this for other tools too, so this issue isn't a priority. It's surprising php hasn't started supporting bitwise operators for booleans since it would be very useful

@tvdijen
Copy link
Contributor

tvdijen commented Sep 8, 2021

This issue still exists, yet the error messages have changed over time;
https://psalm.dev/r/aa2d948c03

@psalm-github-bot
Copy link

I found these snippets:

https://psalm.dev/r/aa2d948c03
<?php

$enabled = false;
$classes = ['SomeClass', 'SomeOtherClass'];
foreach ($classes as $class) {
    $enabled |= class_exists($class);
}

return $enabled;
Psalm output (using commit 3fcfc94):

ERROR: PossiblyFalseOperand - 6:5 - Left operand cannot be falsable, got false|float|int

ERROR: InvalidOperand - 6:17 - Cannot perform a numeric operation with a non-numeric type bool

@AndrolGenhald
Copy link
Collaborator

AndrolGenhald commented Feb 18, 2022

I went and implemented this with the strictBooleanOperands config, but then I decided it's a really bad idea and scrapped it.

I figured for consistency we should support all the boolean operators:

$bool = random_int(0, 1) ? true : false;

/** @psalm-check-type-exact $var = 1|2 */
$var = $bool + 1;
/** @psalm-check-type-exact $var = -1|-2 */
$var = $bool - 2;
/** @psalm-check-type-exact $var = 0|1 */
$var = $bool % 3;
/** @psalm-check-type-exact $var = 0|2 */
$var = $bool * 2;
/** @psalm-check-type-exact $var = 0|1 */
$var = $bool ** 3;
/** @psalm-check-type-exact $var = 4|5 */
$var = $bool | 4;
/** @psalm-check-type-exact $var = 2|3 */
$var = $bool ^ 3;
/** @psalm-check-type-exact $var = 0|1 */
$var = $bool & 3;
/** @psalm-check-type-exact $var = 0|4 */
$var = $bool << 2;
/** @psalm-check-type-exact $var = 0|1 */
$var = $bool >> 0;

But if we do that, then this doesn't show any issues:

/** @var string */
$str = "some string";
$pos = strpos($str, "z") + 1;

And I think catching that is more important than allowing |= on booleans. I think the real solution here is for PHP to add logical assignment operators &&= and ||= (ie someone should pick up php/php-src#4535), but until then I think casting is the way to go.

Should we go ahead and close this @orklah?

@psalm-github-bot
Copy link

psalm-github-bot bot commented Feb 18, 2022

I found these snippets:

https://psalm.dev/r/daa21fb9b5
<?php

/** @var string */
$str = "some string";
$pos = strpos($str, "z") + 1;
Psalm output (using commit 2d83a4b):

ERROR: PossiblyFalseOperand - 5:8 - Left operand cannot be falsable, got false|int<0, max>
https://psalm.dev/r/bdb02a1b35
<?php
$x = (int) false;
$x |= (int) true;
$x |= 1;
$x &= (int) (rand(0, 1) !== 1);
if ($x) {
  echo $x;
}
Psalm output (using commit 2d83a4b):

No issues!

@orklah
Copy link
Collaborator

orklah commented Feb 20, 2022

I'm not sure why solving boolean operations make the strpos operation go without Issue, can you explain that?

AndrolGenhald added a commit to AndrolGenhald/psalm that referenced this issue Feb 20, 2022
@AndrolGenhald
Copy link
Collaborator

I pushed my attempt here so you can look at it. The problem is that the bool to int cast happens before the PossiblyFalseOperand is emitted. I hadn't considered to try both allowing the cast and emitting the issue, but I thought the point was more to make Psalm allow this pattern without any issues rather than to still show issues but have the correct types.

Especially after the casting improvements in #7696 I think the best solution for now is just for users to cast when doing this, it will result in correct types and no issues from Psalm.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

6 participants