Skip to content

Type inference of Array receiver with extension method #10255

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
som-snytt opened this issue Nov 9, 2020 · 5 comments
Closed

Type inference of Array receiver with extension method #10255

som-snytt opened this issue Nov 9, 2020 · 5 comments

Comments

@som-snytt
Copy link
Contributor

Minimized code

Starting scala3 REPL...
scala> import scala.util.chaining._

scala> val array = new Array[AnyRef](8).tap(xs => xs(7) = "hi")
val array: Array[AnyRef] = Array(null, null, null, null, null, null, null, hi)

scala> new Array(16)
val res0: Array[Nothing] = Array(null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null)

scala> res0.getClass.getComponentType
val res1: Class[?] = class scala.runtime.Nothing$

scala> new Array(16): Array[AnyRef]
val res2: Array[AnyRef] = Array(null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null)

scala> res2.getClass.getComponentType
val res3: Class[?] = class java.lang.Object

scala> new Array(16).tap(Array.copy(array, 0, _, 0, 8)) : Array[AnyRef]
1 |new Array(16).tap(Array.copy(array, 0, _, 0, 8)) : Array[AnyRef]
  |^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  |Found:    Array[Nothing]
  |Required: Array[AnyRef]

scala> new Array(16).tap(Array.copy(array, 0, _, 0, 8))
java.lang.ArrayStoreException: java.lang.String
        at scala.runtime.ScalaRunTime$.array_update(ScalaRunTime.scala:75)

Output in Scala 2

scala 2.13.3> import scala.util.chaining._
import scala.util.chaining._

scala 2.13.3> val array = new Array[AnyRef](8).tap(xs => xs(7) = "hi")
val array: Array[AnyRef] = Array(null, null, null, null, null, null, null, hi)

scala 2.13.3> new Array(16).tap(Array.copy(array, 0, _, 0, 8))
val res0: Array[Nothing] = Array(null, null, null, null, null, null, null, hi, null, null, null, null, null, null, null, null)

scala 2.13.3> new Array(16).tap(Array.copy(array, 0, _, 0, 8)): Array[AnyRef]
val res1: Array[AnyRef] = Array(null, null, null, null, null, null, null, hi, null, null, null, null, null, null, null, null)

scala 2.13.3> new Array(16).getClass.getComponentType
val res3: Class[_] = class java.lang.Object

scala 2.13.3> new Array(16).tap(Array.copy(array, 0, _, 0, 8)): Array[AnyRef]  // print
((scala.util.`package`.chaining.scalaUtilChainingOps[Array[AnyRef]](scala.reflect.`package`.materializeClassTag[AnyRef]().newArray(16)).tap[Unit](((x$1: Array[AnyRef]) => scala.Array.copy(array, 0, x$1, 0, 8)))): scala.Array[scala.AnyRef]) // : Array[AnyRef]

Expectation

Array type inferred as in Scala 2.

Not sure about the component type of Array[Nothing]. Best of all would be to ensure statically that the array is of length zero.

There are related issues, but this is "nothing fancy," just "plumbing." Fancy[Nothing].

Noticed at scala/scala#9275 (comment)

@odersky
Copy link
Contributor

odersky commented Nov 10, 2020

I think that dotty's behavior is reasonable. There's some funky behavior with Nothing avoidance in Scala 2 that is not repeated. It seems this kicks in here. Is there are reasonable code sample where this matters?

@som-snytt
Copy link
Contributor Author

Two changes from Scala 2 are

  1. new Array(16) is Array[Nothing] but in Scala 2 you actually get an Array[Object] instead of Array[Nothing$]
  2. new Array(16).tap(initialize): Array[AnyRef] infers new Array[AnyRef] in Scala 2 but new Array[Nothing] in Scala 3

The second expression fails because of tap. That seems like a limitation. There are related tickets about type inference and extension methods which might subsume this one.

The first difference might be a Scala 3 feature.

@smarter
Copy link
Member

smarter commented Nov 10, 2020

We should just completely forbid Array[Nothing] and force people to write down a type parameter, we know it's unsound and already forbid inferring a ClassTag[Nothing] for that reason: #1730

@som-snytt
Copy link
Contributor Author

I agree on forbidding Array[Nothing] (though there are weird APIs that take an array just for the type, and someone will go, What do you mean I can't have an empty array of nothing?).

It would be nice to infer Array[Something] when that is the expected type.

@kynthus
Copy link

kynthus commented Jan 31, 2021

I don't know if it's related, but the following 2D array initialization seems to fail.

Starting scala3 REPL...
Scala compiler version 3.0.0-M3 -- Copyright 2002-2020, LAMP/EPFL
scala> val c: Array[Array[Int]] = Array(new Array(5))
1 |val c: Array[Array[Int]] = Array(new Array(5))
  |                                 ^^^^^^^^^^^^
  |                                 Found:    Array[Nothing]
  |                                 Required: Array[Int]

Can be inferred in Scala 2.13.4.

Welcome to Scala 2.13.4 (Java HotSpot(TM) 64-Bit Server VM, Java 1.8.0_202).
Type in expressions for evaluation. Or try :help.

scala> val c: Array[Array[Int]] = Array(new Array(5))
val c: Array[Array[Int]] = Array(Array(0, 0, 0, 0, 0))

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

4 participants