scala / scala3

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

`asInstanceOf` causes type mismatch error when used in method argument position #12739

Closed japgolly closed 3 years ago

japgolly commented 3 years ago

Compiler version

3.0.0

Minimized code

object X {

  class CA[A]
  type C = CA[_]
  val c: C = ???
  def f[A](r: CA[A]) = ()

  // works
  f(c)

  // works
  val x = c.asInstanceOf[C]
  f(x)

  // error
  f(c.asInstanceOf[C])

  // works
  f(c.asInstanceOf[c.type])
}

Output

[error] -- [E007] Type Mismatch Error: a.scala:16:18 
[error] 16 |  f(c.asInstanceOf[C])
[error]    |    ^^^^^^^^^^^^^^^^^
[error]    |    Found:    X.C
[error]    |    Required: X.CA[A]
[error]    |
[error]    |    where:    A is a type variable
[error] one error found

Expectation

No error.

odersky commented 3 years ago

This has to do with the fact that c.asInstanceOf[C] is an expression, not a value.

C[?] is not compatible with any C[A]. If the argument is a singleton value c, an exception is made and we treat C[?] as "the unknown type instance of C that is the type of c". This alone makes me cringe but we have to do it since Java also does this sort of "capture conversion". But for expressions as arguments this is unsound in general.

dwijnand commented 3 years ago

C[?] is not compatible with any C[A].

I don't quite understand this, as I would expect it to be compatible if A := ?, i.e. that unnamed type that the type alias C is using. I think I would have a better chance at understanding the unsoundness you mention if you (or someone) could mock up an example. I don't see how the 3rd example shouldn't work any different than the 2nd, for instance.

Btw, in Scala 2 the 4th example is an error while the 3rd works... 😄

japgolly commented 3 years ago

This isn't the first time I've reported simple substitution not holding (see here and here). Issues like this are 100% going to be considered Scala 3 puzzlers, which is such a shame after such great work was done to close out so many Scala 2 puzzlers and quirks.

liufengyun commented 3 years ago

This is a bug in the compiler, as the following works:

object O {

  class CA[A](var x: A)
  type C = CA[_]

  val c: CA[_] = CA(5)
  def f[A](r: CA[A]): A = r.x

  def g(): CA[_] = CA("hello")

  f(g())
  f(c.asInstanceOf[CA[_]])
}

We have two places where capture conversion kicks in. One is in typer that handles top-level wildcards. One is in TypeComparer which only works for a stable path.

The reason why the first mechanism does not work for f(c.asInstanceOf[C]) is that we forget to dealias in captureWildcards:

https://github.com/lampepfl/dotty/blob/fcd837addc5b466b055da069960e48c5d4d5c1dc/compiler/src/dotty/tools/dotc/typer/Inferencing.scala#L506-L530

With the following change, the original code compiles:

--- a/compiler/src/dotty/tools/dotc/typer/Inferencing.scala
+++ b/compiler/src/dotty/tools/dotc/typer/Inferencing.scala
@@ -503,7 +503,7 @@ object Inferencing {
     *  $i is a skolem of type `scala.internal.TypeBox`, and `CAP` is its
     *  type member. See the documentation of `TypeBox` for a rationale why we do this.
     */
-  def captureWildcards(tp: Type)(using Context): Type = tp match {
+  def captureWildcards(tp: Type)(using Context): Type = tp.dealias match {
     case tp @ AppliedType(tycon, args) if tp.hasWildcardArg =>
Swoorup commented 3 years ago

Is this related or a different issue? https://github.com/lampepfl/dotty/issues/12796 Probably unrelated since its isInstanceOf

SethTisue commented 3 years ago

Note that this isn't asInstanceOf specific, compilation also fails with e.g. def c(): C = ???; f(c()). Presumably the key difference is whether it's a stable path or not.

SethTisue commented 3 years ago

we have to do it since Java also does this sort of "capture conversion"

(For background on this remark Martin made, see https://docs.oracle.com/javase/specs/jls/se16/html/jls-5.html#jls-5.1.10 — it begins with formal language, but then following that is a nice informal motivation section, in the purple box.)