Compiler infers a type that has no syntax

I’m opening a discussion here since I’m not sure whether it’s a feature request or a bug.

How do I explicitly add a type annotation for Main.all in the program below?

class Outer {
  object inner {
    class Foo
    def foo = new Foo
  }
}

object Main {
  val a = new Outer
  val b = new Outer
  def all = List(a.inner.foo, b.inner.foo)
}

When compiling this program with -Xprint:typer, we see the inferred type of Main.all is

    def all: List[Outer#inner.Foo] = scala.collection.immutable.List.apply[Outer#inner.Foo](Main.this.a.inner.foo, Main.this.b.inner.foo)

When I copy-paste the inferred type into the program and try to compile again I get a syntax error

infer.scala:11: error: ']' expected but '.' found.
  def all: List[Outer#inner.Foo] = List(a.inner.foo, b.inner.foo)
                           ^
one error found

I failed to come up with a wider type that’s not Any. The compiler is capable of inferring Outer#inner.Foo so it seems like this type is “just” missing surface syntax :thinking:

4 Likes

I’ve hit this issue in the past as well, very annoying. It happens both in Scalac and Dotty.

Note that neither of the following workarounds type check, whereas it seems that they should:

class Outer {
  type inner = inner.type
  ...
}
object Main {
  ...
  def all: List[Outer#inner#Foo] = List(a.inner.foo, b.inner.foo)
}
class Outer {
  type Foo = inner.Foo
  ...
}

object Main {
  ...
  def all: List[Outer#Foo] = List(a.inner.foo, b.inner.foo)
}
1 Like

Dotty infers List[(a.inner.type | b.inner.type)#Foo] which I would never have thought of but works as syntax too :).

Oddly enough it does work if you List[Outer#Foo](a.inner.foo, b.inner.foo).

Besides this strange quirk it seems mostly a parser issue to me. A . in a type after a # is not allowed.

Interesting. That’s a bug and I think it should be reported and fixed.

There are types in Scala that can’t be expressed in source code. Besides your example I know two more

trait MyTrait

val x: ??? = new MyTrait {
  implicit val n: Int = 10
} // val x: MyTrait{implicit val n: Int}

import x._
implicitly[Int]
val a: ??? = { trait A { def f(x:this.type): this.type = x  }; new A{} } 
//val a: AnyRef{def f(x: this.type): this.type}
a.f(a)

(@rssh told me about the second example)

2 Likes

This is hilarious. I tried to make the type using structural types, but it ends up being infinitely self recursive.

1 Like