Description
Compiler version
Scala 3.1.1
Minimized code
Consider this code where the lines with l2
and l3
fails compilation.
case object A
case class B()
case class C()
type Union = A.type | B | C
val a: List[A.type] = ???
val b: List[B] = ???
val c: List[C] = ???
val l1: List[Union] = a ++ b // OK
val l2: List[Union] = a ++ b ++ c // [E007] Found: List[Object], Required: List[Union]
val l3: List[Union] = (a: List[Union]) ++ b ++ c // [E007] Found: List[Object], Required: List[Union]
val l4: List[Union] = (a: List[Union]) ++ (b ++ c) // OK
Output
[error] -- [E007] Type Mismatch Error: /path/to/Test.scala:22:26
[error] 22 | val l2: List[Union] = a ++ b ++ c
[error] | ^^^^^^^^^^^
[error] | Found: List[Object]
[error] | Required: List[Union]
[error] -- [E007] Type Mismatch Error: /path/to/Test.scala:23:26
[error] 23 | val l3: List[Union] = (a: List[Union]) ++ b ++ c
[error] | ^^^^^^^^^^^^^^^^^^^^^^^^^^
[error] | Found: List[Object]
[error] | Required: List[Union]
Expectation
I am aware that the compiler does not infer union types but instead infers Matchable
. However, it seems odd that val l1
compiles when l2
and l3
don't.
The problem might be that (a ++ b)
is inferred to List[Matchable]
and therefore (a ++ b) ++ c
gets tagged as no-more-specific-than-List[Matchable]
and therefore fails the type ascription List[Union]
. It seems (a ++ b)
should be tagged as no-more-specific-than-List[A.type | B]
and therefore (a ++ b) ++ c
should be tagged as no-more-specific-than-List[(A.type | B) | C]
, which either infers to List[Matchable]
with no ascription, or passes the List[Union]
ascription.
Adding the extra set of parenthesis for l4
basically reduces the case to be similar to l1
and things compile.
Things (almost) work if A is a class and not an object
It is important to note that the problem is only present if A
is an object. Changing A
to be a class changes the behaviour:
case class A()
case class B()
case class C()
type Union = A | B | C
val a: List[A] = ???
val b: List[B] = ???
val c: List[C] = ???
val l1: List[Union] = a ++ b // OK
val l2: List[Union] = a ++ b ++ c // [E007] Found: List[Object], Required: List[Test1.Union]
val l3: List[Union] = (a: List[Union]) ++ b ++ c // OK
Now, l2
still fails compilation but the type ascription in l3
fixes the issue. The problem with l2
looks similar to the problem reported in #11449.
The output is:
[error] -- [E007] Type Mismatch Error: /path/to/Test.scala:10:26
[error] 10 | val l2: List[Union] = a ++ b ++ c
[error] | ^^^^^^^^^^^
[error] | Found: List[Object]
[error] | Required: List[Union]