emmalanguage/emma

Inferred references to inaccessible types shouldn't be made explicit

Closed this issue · 2 comments

Unfortunately the following code compiles:

case class A() {
  protected case class B()
  val getB = B()
}
val a = A()
val b = a.getB

(Note, that it also compiles with private[A] instead of protected, but doesn't compile with private. Also note that code like this occurs [2] in the Scala standard library. Also see [1].)

The strange thing here is that even though the type of b can be inferred, it cannot be explicitly written out. That is, the following does not compile:

val b: a.B = a.getB

Now the problem is that some of our transformations might want to write that type out. For example, if we have f(a.getB), then ANF used to create a ValDef for a.getB and write out the type. This was fixed with b0e40f4 by leaving the type of all ValDefs empty.

However, the problem is not that easy to fix in some other situations, where we can't simply omit the type:

  • when b is part of the closure of some code that we want to compile separately, then the type of b appears as the type of a parameter of the wrapper method
  • when we are processing a lambda that originally didn't have its parameter's type specified, but we pull it into a ValDef where it cannot inferred anymore.
  • probably also when ANF introduces a DefDef

There are some solution ideas that might work:

  • We could use the singleton type of b.
  • We could improvise a construct like C++'s decltype [3], and use it on b.

[1] https://issues.scala-lang.org/browse/SI-6794
[2] See scala.collection.generic.GenericCompanion.Coll, which escapes when calling for example zipWithIndex.
[3] http://stackoverflow.com/questions/29038214/why-scala-does-not-have-a-decltype

The best we can do wrt to types IMO is the following:

  • for Symbols: always dealias and widen their type;
  • for Idents to stable symbols that are subtypes of AnyRef: singleton type;
  • for Selects of stable paths: path-dependent type;
  • for ValDefs:
    1. for parameters, ascribe with lhs.info, no type inference in this case;
    2. if lhs.info =:= rhs.tpe.dealias.widen, no need for ascription;
    3. otherwise ascribe with lhs.info (upcast);
  • in ANF:
    • substitute path-dependent types with singleton types when necessary;
    • keep track of expected type when creating symbols (esp. for call arguments).

I'm not sure how to do these changes in ANF, but I will think about it.

Fixed as best as possible with #335. Carrying around expected types would complicate transformations too much. After all we would like to avoid reimplementing Scala's type checker.