scala / scala3

The Scala 3 compiler, also known as Dotty.
https://dotty.epfl.ch
Apache License 2.0
5.82k stars 1.05k forks source link

Inference of inaccessible types #4228

Open Blaisorblade opened 6 years ago

Blaisorblade commented 6 years ago

Type inference sometimes produces inaccessible types, but should widen then to visible ones. An example from 2012: https://groups.google.com/d/msg/scala-internals/0j8laVNTQsI/B6fu1e4KJ2sJ

package a {
  private[a] trait Impl { def f = 5 }
  class Vis1 extends Object with Impl
  class Vis2 extends Object with Impl

  object Vis {
    def f = new Vis1
    def g = new Vis2
    def lub = List(f, g)  // this should be (and is) a List[Impl]
  }
}
package b {
  object Elsewhere {
    import a.Vis._
    def lub = List(f, g) // this should not be (but is) a List[a.Impl]
  }
}

Running the REPL with the output confirms the issue is still there:

> repl -classpath out
[...]
scala> a.Vis.lub
val res0: List[a.Impl] = List(a.Vis1@67e28be3, a.Vis2@e344ad3)
scala> b.Elsewhere.lub
val res1: List[a.Impl] = List(a.Vis1@63716833, a.Vis2@573284a5)

(BTW, the other example in that email is instead fixed, arguably better in Dotty than in Scalac):

scala> def f = { trait A ; trait B extends A ; trait C extends B ; new C { } }; lazy val g = f
def f: Object
val g: Object = <lazy>
Blaisorblade commented 6 years ago

BTW, I suspect a related issue came up in https://github.com/lampepfl/dotty/pull/4133#pullrequestreview-105511057, though I might be confused — this one doesn't crash Dotty, just produce questionable results.

smarter commented 6 years ago

We already disallow public classes extending private classes in some situations:

object A {
  private trait Foo
  class Bar extends Foo
}
-- Error: try/i4228b.scala:3:8 -------------------------------------------------
3 |  class Bar extends Foo
  |        ^
  |        non-private class Bar refers to private trait Foo
  |        in its type signature Object with A.Foo{...}

We could extend this to private-qualified superclasses and to classes defined directly in packages.

smarter commented 6 years ago

It might be possible to use https://github.com/olafurpg/scala-experiments to check how frequently projects have public classes extending private classes and if there are actual usecases for this.

Blaisorblade commented 6 years ago

Here was one use of extending private classes — the goal was to reuse implementation: https://github.com/scala/scala/commit/51ec62a8c3

Your comment in #4133 shows another (smaller) instance.

In both cases, widening inferred types to accessible ones seems a possible alternative in principle, though I'm not sure how hard it is to do in practice. I think this is yet-another-instance of a well-known problem with ML modules, the "avoidance problem" (@odersky mentioned it days ago), but I am not too familiar with solutions.