-
Notifications
You must be signed in to change notification settings - Fork 668
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
Implicit immutable object mutation is not detected #6881
Comments
I found these snippets: https://psalm.dev/r/bdd3b6d567<?php
/**
* @psalm-immutable
*/
final class A
{
public stdClass $b;
public function __construct()
{
$this->b = new stdClass();
}
}
$a = new A();
$a->b->c = 2; // mutates $a!
|
Does it? |
I mean, Psalm allows immutable objects to reference mutable structures: https://psalm.dev/r/7992cc4b39, so why would |
I found these snippets: https://psalm.dev/r/7992cc4b39<?php
class B {
public int $c = 1;
}
/**
* @psalm-immutable
*/
final class A
{
public B $b;
public function __construct()
{
$this->b = new B;
}
}
$a = new A();
$a->b->c = 2; // mutates $a!
|
I completely agree that immutable object should be allowed to contain mutable structures. The problem is not in The problem is that when you pass that mutable structure somewhere else and mutate it, the immutable object changes and you cannot rely on it anymore, although the instantiating code considered it to be unchangeable. Recently we found a really painful bug. We didn't clone an immutable command's mutable property in the middleware and the handler was getting a different version of the command. Maybe we could have Also, consider this example: https://psalm.dev/r/563c78d9ec. Isn't it kind of counterintuitive that |
I found these snippets: https://psalm.dev/r/563c78d9ec<?php
class B {
public int $c = 1;
}
/**
* @psalm-immutable
*/
final class A
{
public B $b;
public function __construct()
{
$this->b = new B;
}
private function mutateB(): void
{
$this->b->c = 2;
}
}
$a = new A();
$a->b->c = 2; // same thing as on line 21, but this time allowed!
|
@vudaltsov how would you answer the inlined questions in the following snippet: https://psalm.dev/r/23dd904a7a I suspect the thing that you seem to suggest cannot be implemented without ownership tracking like Rust has. Alternatively we could require all object properties of fully immutable (for the lack of better name) object to be themselves fully immutable. This way both your snippets would emit errors as neither
It could be confusing at first glance, for sure. But if you consult the definition of |
I found these snippets: https://psalm.dev/r/23dd904a7a<?php
class B { public int $c = 1; }
/** @psalm-immutable */
final class A {
public function __construct(
public B $b = new B
) {}
}
$a = new A();
$a->b->c = 2; // assuming this one is flagged
$z = $a->b;
$z->c = 10; // would this have to be flagged as well?
f($a->b); // would this?
function f(object $o): void {
$o->c = 30;
}
|
https://psalm.dev/r/bdd3b6d567
The text was updated successfully, but these errors were encountered: