The mechanism by which the Scala compiler checks variance annotations

I’m interested in how Scala 3 compiler checks variance annotations. In the book Programming in Scala, the Fifth Edition description starts on page 406. I will paste some of the descriptions.

Every position is classified as negative or positive or neutral. Type parameters annotated with + may only be used in positive positions, while type parameters annotated with - may only be used in negative positions. A type parameter with no variance annotation may be used in any position, and is, therefore, the only kind of type parameter that can be used
in neutral positions of the class body.

To classify the positions, the compiler starts from the declaration of a type parameter and then moves inward through deeper nesting levels. Positions at the top level of the declaring class are classified as positive. By default, positions at deeper nesting levels are classified the same as those at enclosing levels, but there are a handful of exceptions where the classification changes. Method value parameter positions are classified to the flipped classification relative to positions outside the method, where the flip of a positive classification is negative, the flip of a negative classification is positive, and the flip of a neutral classification is still neutral.

Besides method value parameter positions, the current classification is also flipped at the type parameters of methods. A classification is sometimes flipped at the type argument position of a type, such as the Arg in C[Arg] , depending on the variance of the corresponding type parameter. If C ’s type parameter is annotated with a + then the classification stays the same. If C ’s type parameter is annotated with a - , then the current classification is flipped. If C ’s type parameter has no variance annotation then the current classification is changed to neutral.

Then example is given. I understand why W is classified as a minus because of:

the current classification is also flipped at the type parameters of methods.

and also why volume method parameter T and Cat[U+, T-] value parameter of listener are classified as - because:

Method value parameter positions are classified to the flipped classification relative to positions outside the method

varianceAlgorithm

But I don’t understand how type arguments in type constructors are classified. So U+ and T- in listener: Cat[U+, T-]- and in Cat[Cat[U+, T-]-, U+]+ return type of meow method: U+ and T- inside nested Cat[U+, T-] and U+ inside outer Cat type constructor. The explanation is:

Looking at the result type of meow, the position of the first Cat[U, T] argument is negative because Cat ’s first type parameter, T, is annotated with a - . The type U inside this argument is again in a positive position (two flips), whereas the type T inside that argument is still in a negative position.

Are type arguments in type constructor at the top level of declared class the same as in class type? Why U+ inside nested Cat was flipped two times? Why T in meow method result still negative? And why neither of the type parameters in type of listener Cat[U+, T-] aren’t flipped?