Collection<covariant Animal> (call-site covariance)covariant to a type
argument lets a function accept Collection<Cat> where Collection<Animal>
is written, without making the class itself @template-covariant. The cost
moves to that parameter: add() expects never, because a write would let
a Dog into a collection of Cats.
Declaration-site @template-covariant is generics_template_covariant.
References:
- PHPStan phpdoc-types: Generics (Collection<covariant Type>)
- PHPStan "A guide to call-site generic variance"| Analyzer | Version | Result | Diagnostics |
|---|---|---|---|
| phan | 6.0.7Phan 6.0.7 php-ast version 1.1.3 PHP version used to run Phan: 8.5.9 | Unrecognized | phpdoc_advanced_phpstan_call_site_covariant.php:91: Saw possibly unextractable annotation for a fragment of comment '* @param Collection<covariant Animal> $animals': after Collection, did not see an element name (will guess based on comment order) [PhanUnextractableAnnotationElementName] phpdoc_advanced_phpstan_call_site_covariant.php:91: Saw a token Phan may have failed to parse after '* @param Collection<covariant Animal> $animals': after Collection, saw '<' [PhanUnextractableAnnotationSuffix] phpdoc_advanced_phpstan_call_site_covariant.php:98: Saw possibly unextractable annotation for a fragment of comment '* @param Collection<covariant Animal> $animals': after Collection, did not see an element name (will guess based on comment order) [PhanUnextractableAnnotationElementName] phpdoc_advanced_phpstan_call_site_covariant.php:98: Saw a token Phan may have failed to parse after '* @param Collection<covariant Animal> $animals': after Collection, saw '<' [PhanUnextractableAnnotationSuffix]
|
| phpstan | 2.2.15PHPStan - PHP Static Analysis Tool 2.2.15 | Enforced reported Lv.5+ | phpdoc_advanced_phpstan_call_site_covariant.php:102: Parameter #1 $item of method Conformance\Tests\PhpdocAdvancedPhpstanCallSiteCovariant\Collection<covariant Conformance\Tests\PhpdocAdvancedPhpstanCallSiteCovariant\Animal>::add() expects never, Conformance\Tests\PhpdocAdvancedPhpstanCallSiteCovariant\Dog given. [reported-from-level=5] phpdoc_advanced_phpstan_call_site_covariant.php:122: Parameter #1 $animals of function Conformance\Tests\PhpdocAdvancedPhpstanCallSiteCovariant\takesCovariant expects Conformance\Tests\PhpdocAdvancedPhpstanCallSiteCovariant\Collection<covariant Conformance\Tests\PhpdocAdvancedPhpstanCallSiteCovariant\Animal>, Conformance\Tests\PhpdocAdvancedPhpstanCallSiteCovariant\Collection<mixed> given. [reported-from-level=9] phpdoc_advanced_phpstan_call_site_covariant.php:127: Parameter #1 $animals of function Conformance\Tests\PhpdocAdvancedPhpstanCallSiteCovariant\takesInvariant expects Conformance\Tests\PhpdocAdvancedPhpstanCallSiteCovariant\Collection<Conformance\Tests\PhpdocAdvancedPhpstanCallSiteCovariant\Animal>, Conformance\Tests\PhpdocAdvancedPhpstanCallSiteCovariant\Collection<Conformance\Tests\PhpdocAdvancedPhpstanCallSiteCovariant\Cat> given. [reported-from-level=5] With strict-rulesphpdoc_advanced_phpstan_call_site_covariant.php:102: Parameter #1 $item of method Conformance\Tests\PhpdocAdvancedPhpstanCallSiteCovariant\Collection<covariant Conformance\Tests\PhpdocAdvancedPhpstanCallSiteCovariant\Animal>::add() expects never, Conformance\Tests\PhpdocAdvancedPhpstanCallSiteCovariant\Dog given. phpdoc_advanced_phpstan_call_site_covariant.php:122: Parameter #1 $animals of function Conformance\Tests\PhpdocAdvancedPhpstanCallSiteCovariant\takesCovariant expects Conformance\Tests\PhpdocAdvancedPhpstanCallSiteCovariant\Collection<covariant Conformance\Tests\PhpdocAdvancedPhpstanCallSiteCovariant\Animal>, Conformance\Tests\PhpdocAdvancedPhpstanCallSiteCovariant\Collection<mixed> given. phpdoc_advanced_phpstan_call_site_covariant.php:127: Parameter #1 $animals of function Conformance\Tests\PhpdocAdvancedPhpstanCallSiteCovariant\takesInvariant expects Conformance\Tests\PhpdocAdvancedPhpstanCallSiteCovariant\Collection<Conformance\Tests\PhpdocAdvancedPhpstanCallSiteCovariant\Animal>, Conformance\Tests\PhpdocAdvancedPhpstanCallSiteCovariant\Collection<Conformance\Tests\PhpdocAdvancedPhpstanCallSiteCovariant\Cat> given.
|
| psalm | 6.18.0Psalm 6.18.0@536dd6236b39a5115385b0307b42d6d1ad431a02 next: 7.0.0-beta22 |
Incidental (1/2) | phpdoc_advanced_phpstan_call_site_covariant.php:121: Argument 1 of Conformance\Tests\PhpdocAdvancedPhpstanCallSiteCovariant\takesCovariant expects Conformance\Tests\PhpdocAdvancedPhpstanCallSiteCovariant\Collection<Conformance\Tests\PhpdocAdvancedPhpstanCallSiteCovariant\Animal>, but Conformance\Tests\PhpdocAdvancedPhpstanCallSiteCovariant\Collection<Conformance\Tests\PhpdocAdvancedPhpstanCallSiteCovariant\Cat> provided [InvalidArgument] phpdoc_advanced_phpstan_call_site_covariant.php:122: Argument 1 of Conformance\Tests\PhpdocAdvancedPhpstanCallSiteCovariant\takesCovariant expects Conformance\Tests\PhpdocAdvancedPhpstanCallSiteCovariant\Collection<Conformance\Tests\PhpdocAdvancedPhpstanCallSiteCovariant\Animal>, but parent type Conformance\Tests\PhpdocAdvancedPhpstanCallSiteCovariant\Collection<mixed> provided [MixedArgumentTypeCoercion] phpdoc_advanced_phpstan_call_site_covariant.php:123: Argument 1 of Conformance\Tests\PhpdocAdvancedPhpstanCallSiteCovariant\writeCovariant expects Conformance\Tests\PhpdocAdvancedPhpstanCallSiteCovariant\Collection<Conformance\Tests\PhpdocAdvancedPhpstanCallSiteCovariant\Animal>, but Conformance\Tests\PhpdocAdvancedPhpstanCallSiteCovariant\Collection<Conformance\Tests\PhpdocAdvancedPhpstanCallSiteCovariant\Cat> provided [InvalidArgument] phpdoc_advanced_phpstan_call_site_covariant.php:127: Argument 1 of Conformance\Tests\PhpdocAdvancedPhpstanCallSiteCovariant\takesInvariant expects Conformance\Tests\PhpdocAdvancedPhpstanCallSiteCovariant\Collection<Conformance\Tests\PhpdocAdvancedPhpstanCallSiteCovariant\Animal>, but Conformance\Tests\PhpdocAdvancedPhpstanCallSiteCovariant\Collection<Conformance\Tests\PhpdocAdvancedPhpstanCallSiteCovariant\Cat> provided [InvalidArgument]
Expectation diffLine 121: Expected valid value to be accepted, got ["Argument 1 of Conformance\\Tests\\PhpdocAdvancedPhpstanCallSiteCovariant\\takesCovariant expects Conformance\\Tests\\PhpdocAdvancedPhpstanCallSiteCovariant\\Collection<Conformance\\Tests\\PhpdocAdvancedPhpstanCallSiteCovariant\\Animal>, but Conformance\\Tests\\PhpdocAdvancedPhpstanCallSiteCovariant\\Collection<Conformance\\Tests\\PhpdocAdvancedPhpstanCallSiteCovariant\\Cat> provided [InvalidArgument]"] Line 123: Expected valid value to be accepted, got ["Argument 1 of Conformance\\Tests\\PhpdocAdvancedPhpstanCallSiteCovariant\\writeCovariant expects Conformance\\Tests\\PhpdocAdvancedPhpstanCallSiteCovariant\\Collection<Conformance\\Tests\\PhpdocAdvancedPhpstanCallSiteCovariant\\Animal>, but Conformance\\Tests\\PhpdocAdvancedPhpstanCallSiteCovariant\\Collection<Conformance\\Tests\\PhpdocAdvancedPhpstanCallSiteCovariant\\Cat> provided [InvalidArgument]"] Notes: Ignores the `covariant` keyword and treats the parameter as invariant Collection<Animal>. Collection<Cat> is rejected on the valid calls; the mixed rejection is the leftover invariant check, not call-site covariance. |
| mago | 1.50.0mago 1.50.0 | Enforced | phpdoc_advanced_phpstan_call_site_covariant.php:102: Possible argument type mismatch for argument #1 of `Conformance\Tests\PhpdocAdvancedPhpstanCallSiteCovariant\Collection::add`: expected `never`, but possibly received `Conformance\Tests\PhpdocAdvancedPhpstanCallSiteCovariant\Dog`. [possibly-invalid-argument] phpdoc_advanced_phpstan_call_site_covariant.php:122: Argument type mismatch for argument #1 of `Conformance\Tests\PhpdocAdvancedPhpstanCallSiteCovariant\takesCovariant`: expected `Conformance\Tests\PhpdocAdvancedPhpstanCallSiteCovariant\Collection<Conformance\Tests\PhpdocAdvancedPhpstanCallSiteCovariant\Animal>`, but provided type `Conformance\Tests\PhpdocAdvancedPhpstanCallSiteCovariant\Collection<mixed>` is less specific. [less-specific-nested-argument-type] phpdoc_advanced_phpstan_call_site_covariant.php:127: Argument type mismatch for argument #1 of `Conformance\Tests\PhpdocAdvancedPhpstanCallSiteCovariant\takesInvariant`: expected `Conformance\Tests\PhpdocAdvancedPhpstanCallSiteCovariant\Collection<Conformance\Tests\PhpdocAdvancedPhpstanCallSiteCovariant\Animal>`, but provided type `Conformance\Tests\PhpdocAdvancedPhpstanCallSiteCovariant\Collection<Conformance\Tests\PhpdocAdvancedPhpstanCallSiteCovariant\Cat>` is less specific. [less-specific-argument]
|
| mir | 0.78.0mir 0.78.0 | Unrecognized | phpdoc_advanced_phpstan_call_site_covariant.php:93: UndefinedDocblockClass: Docblock type 'Conformance\Tests\PhpdocAdvancedPhpstanCallSiteCovariant\covariant Animal' does not exist [MIR1505] phpdoc_advanced_phpstan_call_site_covariant.php:100: UndefinedDocblockClass: Docblock type 'Conformance\Tests\PhpdocAdvancedPhpstanCallSiteCovariant\covariant Animal' does not exist [MIR1505] phpdoc_advanced_phpstan_call_site_covariant.php:120: InvalidArgument: Argument $animals of takesCovariant() expects 'Conformance\Tests\PhpdocAdvancedPhpstanCallSiteCovariant\Collection<Conformance\Tests\PhpdocAdvancedPhpstanCallSiteCovariant\covariant Animal>', got 'Conformance\Tests\PhpdocAdvancedPhpstanCallSiteCovariant\Collection<Conformance\Tests\PhpdocAdvancedPhpstanCallSiteCovariant\Animal>' [MIR0201] phpdoc_advanced_phpstan_call_site_covariant.php:121: InvalidArgument: Argument $animals of takesCovariant() expects 'Conformance\Tests\PhpdocAdvancedPhpstanCallSiteCovariant\Collection<Conformance\Tests\PhpdocAdvancedPhpstanCallSiteCovariant\covariant Animal>', got 'Conformance\Tests\PhpdocAdvancedPhpstanCallSiteCovariant\Collection<Conformance\Tests\PhpdocAdvancedPhpstanCallSiteCovariant\Cat>' [MIR0201] phpdoc_advanced_phpstan_call_site_covariant.php:123: InvalidArgument: Argument $animals of writeCovariant() expects 'Conformance\Tests\PhpdocAdvancedPhpstanCallSiteCovariant\Collection<Conformance\Tests\PhpdocAdvancedPhpstanCallSiteCovariant\covariant Animal>', got 'Conformance\Tests\PhpdocAdvancedPhpstanCallSiteCovariant\Collection<Conformance\Tests\PhpdocAdvancedPhpstanCallSiteCovariant\Cat>' [MIR0201] phpdoc_advanced_phpstan_call_site_covariant.php:127: InvalidArgument: Argument $animals of takesInvariant() expects 'Conformance\Tests\PhpdocAdvancedPhpstanCallSiteCovariant\Collection<Conformance\Tests\PhpdocAdvancedPhpstanCallSiteCovariant\Animal>', got 'Conformance\Tests\PhpdocAdvancedPhpstanCallSiteCovariant\Collection<Conformance\Tests\PhpdocAdvancedPhpstanCallSiteCovariant\Cat>' [MIR0201]
Expectation diffLine 120: Expected valid value to be accepted, got ["InvalidArgument: Argument $animals of takesCovariant() expects 'Conformance\\Tests\\PhpdocAdvancedPhpstanCallSiteCovariant\\Collection<Conformance\\Tests\\PhpdocAdvancedPhpstanCallSiteCovariant\\covariant Animal>', got 'Conformance\\Tests\\PhpdocAdvancedPhpstanCallSiteCovariant\\Collection<Conformance\\Tests\\PhpdocAdvancedPhpstanCallSiteCovariant\\Animal>' [MIR0201]"] Line 121: Expected valid value to be accepted, got ["InvalidArgument: Argument $animals of takesCovariant() expects 'Conformance\\Tests\\PhpdocAdvancedPhpstanCallSiteCovariant\\Collection<Conformance\\Tests\\PhpdocAdvancedPhpstanCallSiteCovariant\\covariant Animal>', got 'Conformance\\Tests\\PhpdocAdvancedPhpstanCallSiteCovariant\\Collection<Conformance\\Tests\\PhpdocAdvancedPhpstanCallSiteCovariant\\Cat>' [MIR0201]"] Line 123: Expected valid value to be accepted, got ["InvalidArgument: Argument $animals of writeCovariant() expects 'Conformance\\Tests\\PhpdocAdvancedPhpstanCallSiteCovariant\\Collection<Conformance\\Tests\\PhpdocAdvancedPhpstanCallSiteCovariant\\covariant Animal>', got 'Conformance\\Tests\\PhpdocAdvancedPhpstanCallSiteCovariant\\Collection<Conformance\\Tests\\PhpdocAdvancedPhpstanCallSiteCovariant\\Cat>' [MIR0201]"] Notes: Reads `covariant Animal` as a single class name (with the space). Collection<Animal> and Collection<Cat> both fail to match Collection<covariant Animal>. |
| phpantom | 0.10.0phpantom_lsp 0.10.0 | Not enforced | No diagnostics reported.
|
| intelephense | 1.18.5intelephense 1.18.5 | Unrecognized | phpdoc_advanced_phpstan_call_site_covariant.php:91: PHPDoc undefined type 'Conformance\Tests\PhpdocAdvancedPhpstanCallSiteCovariant\covariant'. [P1133] phpdoc_advanced_phpstan_call_site_covariant.php:98: PHPDoc undefined type 'Conformance\Tests\PhpdocAdvancedPhpstanCallSiteCovariant\covariant'. [P1133]
Notes: Reports an undefined type `covariant` and does not apply call-site covariance. |
| phpactor | 2026.06.23.0Phpactor 2026.06.23.0 | Not enforced | No diagnostics reported.
|
| phpy | 1.0.19274phpy 1.0.19274 | Not enforced | No diagnostics reported.
|
| qodana | 262.9437.196Qodana 262.9437.196 | Not enforced | No diagnostics reported.
Notes: Silent on Collection<covariant Animal>: no undefined-type complaint on `covariant`, Collection<Cat> is accepted, and add() is not restricted to never. |
| noverify | 0.5.5NoVerify, version 0.5.5: built on: 2025.04.22 14:36:46 OS: r-kuchinskas arm64 Commit: 4774b82f8adc53fbef38f95e97af4b953b4d1529 | Unrecognized ⚠ 3 false positives | phpdoc_advanced_phpstan_call_site_covariant.php:44: param $item miss matched with phpdoc type <<T>> [funcParamTypeMissMatch] phpdoc_advanced_phpstan_call_site_covariant.php:93: param $animals miss matched with phpdoc type <<Collection<covariant Animal>>> [funcParamTypeMissMatch] phpdoc_advanced_phpstan_call_site_covariant.php:100: param $animals miss matched with phpdoc type <<Collection<covariant Animal>>> [funcParamTypeMissMatch] phpdoc_advanced_phpstan_call_site_covariant.php:108: param $animals miss matched with phpdoc type <<Collection<Animal>>> [funcParamTypeMissMatch] phpdoc_advanced_phpstan_call_site_covariant.php:116: param $animals miss matched with phpdoc type <<Collection<Animal>>> [funcParamTypeMissMatch]
Expectation diffLine 44: Unexpected errors ["param $item miss matched with phpdoc type <<T>> [funcParamTypeMissMatch]"] Line 108: Unexpected errors ["param $animals miss matched with phpdoc type <<Collection<Animal>>> [funcParamTypeMissMatch]"] Line 116: Unexpected errors ["param $animals miss matched with phpdoc type <<Collection<Animal>>> [funcParamTypeMissMatch]"] |
| steins | 0.1.8steins 0.1.8 (2026-09-21 revision 76306a9) | Not enforced | No diagnostics reported.
|
| phpstan-strict | 2.2.15PHPStan - PHP Static Analysis Tool 2.2.15 | Enforced | phpdoc_advanced_phpstan_call_site_covariant.php:102: Parameter #1 $item of method Conformance\Tests\PhpdocAdvancedPhpstanCallSiteCovariant\Collection<covariant Conformance\Tests\PhpdocAdvancedPhpstanCallSiteCovariant\Animal>::add() expects never, Conformance\Tests\PhpdocAdvancedPhpstanCallSiteCovariant\Dog given. phpdoc_advanced_phpstan_call_site_covariant.php:122: Parameter #1 $animals of function Conformance\Tests\PhpdocAdvancedPhpstanCallSiteCovariant\takesCovariant expects Conformance\Tests\PhpdocAdvancedPhpstanCallSiteCovariant\Collection<covariant Conformance\Tests\PhpdocAdvancedPhpstanCallSiteCovariant\Animal>, Conformance\Tests\PhpdocAdvancedPhpstanCallSiteCovariant\Collection<mixed> given. phpdoc_advanced_phpstan_call_site_covariant.php:127: Parameter #1 $animals of function Conformance\Tests\PhpdocAdvancedPhpstanCallSiteCovariant\takesInvariant expects Conformance\Tests\PhpdocAdvancedPhpstanCallSiteCovariant\Collection<Conformance\Tests\PhpdocAdvancedPhpstanCallSiteCovariant\Animal>, Conformance\Tests\PhpdocAdvancedPhpstanCallSiteCovariant\Collection<Conformance\Tests\PhpdocAdvancedPhpstanCallSiteCovariant\Cat> given.
|
| psalm-next | 7.0.0-beta22Psalm 7.0.0-beta22@de27e5724ee5997a1e3baee4d394ca6f4e46eba0 | Incidental (1/2) | phpdoc_advanced_phpstan_call_site_covariant.php:121: Argument 1 of Conformance\Tests\PhpdocAdvancedPhpstanCallSiteCovariant\takesCovariant expects Conformance\Tests\PhpdocAdvancedPhpstanCallSiteCovariant\Collection<Conformance\Tests\PhpdocAdvancedPhpstanCallSiteCovariant\Animal>, but Conformance\Tests\PhpdocAdvancedPhpstanCallSiteCovariant\Collection<Conformance\Tests\PhpdocAdvancedPhpstanCallSiteCovariant\Cat> provided [InvalidArgument] phpdoc_advanced_phpstan_call_site_covariant.php:122: Argument 1 of Conformance\Tests\PhpdocAdvancedPhpstanCallSiteCovariant\takesCovariant expects Conformance\Tests\PhpdocAdvancedPhpstanCallSiteCovariant\Collection<Conformance\Tests\PhpdocAdvancedPhpstanCallSiteCovariant\Animal>, but parent type Conformance\Tests\PhpdocAdvancedPhpstanCallSiteCovariant\Collection<mixed> provided [MixedArgumentTypeCoercion] phpdoc_advanced_phpstan_call_site_covariant.php:123: Argument 1 of Conformance\Tests\PhpdocAdvancedPhpstanCallSiteCovariant\writeCovariant expects Conformance\Tests\PhpdocAdvancedPhpstanCallSiteCovariant\Collection<Conformance\Tests\PhpdocAdvancedPhpstanCallSiteCovariant\Animal>, but Conformance\Tests\PhpdocAdvancedPhpstanCallSiteCovariant\Collection<Conformance\Tests\PhpdocAdvancedPhpstanCallSiteCovariant\Cat> provided [InvalidArgument] phpdoc_advanced_phpstan_call_site_covariant.php:127: Argument 1 of Conformance\Tests\PhpdocAdvancedPhpstanCallSiteCovariant\takesInvariant expects Conformance\Tests\PhpdocAdvancedPhpstanCallSiteCovariant\Collection<Conformance\Tests\PhpdocAdvancedPhpstanCallSiteCovariant\Animal>, but Conformance\Tests\PhpdocAdvancedPhpstanCallSiteCovariant\Collection<Conformance\Tests\PhpdocAdvancedPhpstanCallSiteCovariant\Cat> provided [InvalidArgument]
Expectation diffLine 121: Expected valid value to be accepted, got ["Argument 1 of Conformance\\Tests\\PhpdocAdvancedPhpstanCallSiteCovariant\\takesCovariant expects Conformance\\Tests\\PhpdocAdvancedPhpstanCallSiteCovariant\\Collection<Conformance\\Tests\\PhpdocAdvancedPhpstanCallSiteCovariant\\Animal>, but Conformance\\Tests\\PhpdocAdvancedPhpstanCallSiteCovariant\\Collection<Conformance\\Tests\\PhpdocAdvancedPhpstanCallSiteCovariant\\Cat> provided [InvalidArgument]"] Line 123: Expected valid value to be accepted, got ["Argument 1 of Conformance\\Tests\\PhpdocAdvancedPhpstanCallSiteCovariant\\writeCovariant expects Conformance\\Tests\\PhpdocAdvancedPhpstanCallSiteCovariant\\Collection<Conformance\\Tests\\PhpdocAdvancedPhpstanCallSiteCovariant\\Animal>, but Conformance\\Tests\\PhpdocAdvancedPhpstanCallSiteCovariant\\Collection<Conformance\\Tests\\PhpdocAdvancedPhpstanCallSiteCovariant\\Cat> provided [InvalidArgument]"] Notes: Ignores the `covariant` keyword and treats the parameter as invariant Collection<Animal>. Collection<Cat> is rejected on the valid calls; the mixed rejection is the leftover invariant check, not call-site covariance. |
<?php
declare(strict_types=1);
namespace Conformance\Tests\PhpdocAdvancedPhpstanCallSiteCovariant;
/**
* `Collection<covariant Animal>` (call-site covariance)
*
* A PHPStan call-site variance spelling. Attaching `covariant` to a type
* argument lets a function accept `Collection<Cat>` where `Collection<Animal>`
* is written, without making the class itself `@template-covariant`. The cost
* moves to that parameter: `add()` expects `never`, because a write would let
* a Dog into a collection of Cats.
*
* Declaration-site `@template-covariant` is `generics_template_covariant`.
*
* References:
* - PHPStan phpdoc-types: Generics (`Collection<covariant Type>`)
* - PHPStan "A guide to call-site generic variance"
*/
class Animal
{
}
class Cat extends Animal
{
}
class Dog extends Animal
{
}
/**
* @template T
*/
final class Collection
{
/**
* @param T $item
*/
public function __construct(
public mixed $item,
) {
}
/**
* @param T $item
*/
public function add($item): void
{
$this->item = $item;
}
/**
* @return T
*/
public function get(): mixed
{
return $this->item;
}
}
/**
* @return Collection<Animal>
*/
function animals(): Collection
{
return new Collection(new Animal());
}
/**
* @return Collection<Cat>
*/
function cats(): Collection
{
return new Collection(new Cat());
}
/**
* @param mixed $item
* @return Collection<mixed>
*/
function mixeds(mixed $item = null): Collection
{
return new Collection($item);
}
/**
* @param Collection<covariant Animal> $animals
*/
function takesCovariant(Collection $animals): void // T: Collection<covariant Animal>
{
}
/**
* @param Collection<covariant Animal> $animals
*/
function writeCovariant(Collection $animals): void // T: Collection<covariant Animal>
{
$animals->add(new Dog()); // E?: add() on a covariant projection expects never
}
/**
* @param Collection<Animal> $animals
*/
function writeInvariant(Collection $animals): void
{
$animals->add(new Dog()); // V: the same write is valid on invariant Collection<Animal>
}
/**
* @param Collection<Animal> $animals
*/
function takesInvariant(Collection $animals): void
{
}
takesCovariant(animals()); // V
takesCovariant(cats()); // V: Collection<Cat> satisfies Collection<covariant Animal>
takesCovariant(mixeds()); // E?: mixed is not a subtype of Animal
writeCovariant(cats()); // V
writeInvariant(animals()); // V
// Contrast: without the keyword, Collection is invariant.
takesInvariant(cats()); // E?[noise]: Collection<Cat> is not Collection<Animal>