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

Associated mutable in immutable class breaks immutability #2805

Closed
snapshotpl opened this issue Feb 13, 2020 · 9 comments
Closed

Associated mutable in immutable class breaks immutability #2805

snapshotpl opened this issue Feb 13, 2020 · 9 comments

Comments

@snapshotpl
Copy link
Contributor

https://psalm.dev/r/a6ac151a01

IMO psalm should report mutable error when not immutable class is injected.

@psalm-github-bot
Copy link

I found these snippets:

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

class Item {
  private int $i = 0;
	public function mutate(): void {
     $this->i++;
    }
  public function get(): int
  {
    return $this->i;
  }
}
  
/** 
 * @psalm-immutable
 */
class Immutable {
  private $item;
  
  public function __construct(Item $item)
  {
    $this->item = $item;
  }
  
  public function get(): int
  {
    return $this->item->get();
  }
}
$item = new Item();
$immutable = new Immutable($item);
$memory = $immutable->get();
$item->mutate();
if ($immutable->get() !== $memory) {
  // will be true
}
Psalm output (using commit ae0b1a6):

No issues!

@muglug muglug added the bug label Feb 13, 2020
@muglug muglug closed this as completed in 7d99a15 Feb 21, 2020
@weirdan
Copy link
Collaborator

weirdan commented Feb 21, 2020

@muglug The fix looks a bit too radical. IMO this should not fail: https://psalm.dev/r/8b58a212bc.

@psalm-github-bot
Copy link

I found these snippets:

https://psalm.dev/r/8b58a212bc
<?php

class Item {
  private int $i = 0;
	public function mutate(): void {
     $this->i++;
    }
  public function get(): int
  {
    return $this->i;
  }
}
  
/** 
 * @psalm-immutable
 */
class Immutable {  
  public function get(Item $item): int
  {
    return $item->get();
  }
}
$item = new Item();
$immutable = new Immutable();
$memory = $immutable->get($item);
$item->mutate();
if ($immutable->get($item) !== $memory) {
  // will be true, and it's fine
}
Psalm output (using commit 7d99a15):

ERROR: ImpureArgument - 25:27 - Cannot pass mutable value to Immutable::get

ERROR: ImpureArgument - 27:21 - Cannot pass mutable value to Immutable::get

@muglug
Copy link
Collaborator

muglug commented Feb 22, 2020

Yup, it doesn't anymore

@muglug
Copy link
Collaborator

muglug commented Mar 14, 2020

In retrospect I think this was the wrong fix. The error here is allowing the assignment $this->item = $item. That's the risky thing – instead the code should be $this->item = clone $item to prevent the reference leaking

muglug added a commit that referenced this issue Mar 14, 2020
@muglug
Copy link
Collaborator

muglug commented Mar 14, 2020

cc @sebastianbergmann and @ADmad who were caught up in this, among others. Upon further reflection the only issue is when an immutable class stores a reference to a mutable one.

@ADmad
Copy link
Contributor

ADmad commented Mar 14, 2020

@muglug The issue I had reported about was a much simpler case #2930. There's no assignment there, just new object creation.

@muglug
Copy link
Collaborator

muglug commented Mar 14, 2020

yeah, that's the point – whether or not the object created was immutable, that should not have been reported as an issue

@muglug
Copy link
Collaborator

muglug commented Sep 3, 2020

I've actually gone back totally on this: I don't believe it was a bug in the first instance. Issue was raised here: #4126

Basically there's no way for any immutable/pure promises to be broken.

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

No branches or pull requests

4 participants