Skip to content

Union type with object used with generic type infers wrong type even with type ascription #14642

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

Closed
martingd opened this issue Mar 8, 2022 · 5 comments · Fixed by #17512
Closed

Comments

@martingd
Copy link

martingd commented Mar 8, 2022

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]
@martingd martingd added itype:bug stat:needs triage Every issue needs to have an "area" and "itype" label labels Mar 8, 2022
@prolativ prolativ added area:typer and removed stat:needs triage Every issue needs to have an "area" and "itype" label labels Mar 8, 2022
@prolativ
Copy link
Contributor

prolativ commented Mar 8, 2022

Interestingly even

a ++ (b: List[Union])

is typed as List[Object] even though according to the signature of map the type parameter of List in the result should be taken from the type of b, which is given explicitly.

@smarter smarter assigned mbovel and unassigned smarter Mar 8, 2022
@soronpo
Copy link
Contributor

soronpo commented Mar 8, 2022

This looks like the default widening behavior of Scalac. #8231

@martingd
Copy link
Author

martingd commented Mar 8, 2022

@soronpo Isn't #8231 about widening of singleton types? Not sure I see this is the issue I run into here.

@soronpo
Copy link
Contributor

soronpo commented Mar 8, 2022

It is, and I'm not a compiler expert, but I think they may rely on the same mechanism. The good thing is that unions are new (exist only in Scala 3) and therefore setting the widening behavior can be more flexible than literal widening.

@bjornregnell
Copy link
Contributor

l3 seems to work now in 3.2.1 but l2 is still an error:

scala> 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
-- [E007] Type Mismatch Error: ------------------------------------------------------------------------------------------------
9 |val l2: List[Union] = a ++ b ++ c                  // [E007] Found: List[Object], Required: List[Union]
  |                      ^^^^^^^^^^^
  |                      Found:    List[Object]
  |                      Required: List[Union]
  |
  | longer explanation available when compiling with `-explain`
1 error found

ckipp01 added a commit to ckipp01/dotty that referenced this issue May 15, 2023
nicolasstucki added a commit that referenced this issue May 16, 2023
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging a pull request may close this issue.

6 participants