Skip to content

Scala 2 compat: enable reflectiveSelectable for language.reflectiveCalls. #9420

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

Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 12 additions & 1 deletion library/src/scala/Selectable.scala
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
package scala
import scala.reflect.ClassTag

/** A marker trait for objects that support structural selection via
* `selectDynamic` and `applyDynamic`
Expand All @@ -22,3 +21,15 @@ import scala.reflect.ClassTag
* types of the method in the structural type.
*/
trait Selectable extends Any

object Selectable:
/* Scala 2 compat + allowing for cross-compilation:
* enable scala.reflect.Selectable.reflectiveSelectable when there is an
* import scala.language.reflectiveCalls in scope.
*/
@deprecated(
"import scala.reflect.Selectable.reflectiveSelectable instead of scala.language.reflectiveCalls",
since = "3.0")
implicit def reflectiveSelectableFromLangReflectiveCalls(x: Any)(
using scala.languageFeature.reflectiveCalls): scala.reflect.Selectable =
scala.reflect.Selectable.reflectiveSelectable(x)
133 changes: 133 additions & 0 deletions tests/run/structural-compat.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,133 @@
// the old reflectiveCalls still works and is equivalent to
// import scala.reflect.Selectable.reflectiveSelectable
import scala.language.reflectiveCalls

object Test {
class C { type S = String; type I }
class D extends C { type I = Int }

type Foo = {
def sel0: Int
def sel1: Int => Int
def fun0(x: Int): Int

def fun1(x: Int)(y: Int): Int
def fun2(x: Int): Int => Int
def fun3(a1: Int, a2: Int, a3: Int)
(a4: Int, a5: Int, a6: Int)
(a7: Int, a8: Int, a9: Int): Int

def fun4(implicit x: Int): Int
def fun5(x: Int)(implicit y: Int): Int

def fun6(x: C, y: x.S): Int
def fun7(x: C, y: x.I): Int
def fun8(y: C): y.S
def fun9(y: C): y.I
}

class Foo1 {
def sel0: Int = 1
def sel1: Int => Int = x => x
def fun0(x: Int): Int = x

def fun1(x: Int)(y: Int): Int = x + y
def fun2(x: Int): Int => Int = y => x * y
def fun3(a1: Int, a2: Int, a3: Int)
(a4: Int, a5: Int, a6: Int)
(a7: Int, a8: Int, a9: Int): Int = -1

def fun4(implicit x: Int): Int = x
def fun5(x: Int)(implicit y: Int): Int = x + y

def fun6(x: C, y: x.S): Int = 1
def fun7(x: C, y: x.I): Int = 2
def fun8(y: C): y.S = "Hello"
def fun9(y: C): y.I = 1.asInstanceOf[y.I]
}

class Foo2 extends scala.Selectable {
def sel0: Int = 1
def sel1: Int => Int = x => x
def fun0(x: Int): Int = x

def fun1(x: Int)(y: Int): Int = x + y
def fun2(x: Int): Int => Int = y => x * y
def fun3(a1: Int, a2: Int, a3: Int)
(a4: Int, a5: Int, a6: Int)
(a7: Int, a8: Int, a9: Int): Int = -1

def fun4(implicit x: Int): Int = x
def fun5(x: Int)(implicit y: Int): Int = x + y

def fun6(x: C, y: x.S): Int = 1
def fun7(x: C, y: x.I): Int = 2
def fun8(y: C): y.S = "Hello"
def fun9(y: C): y.I = 1.asInstanceOf[y.I]
}

def basic(x: Foo): Unit ={
assert(x.sel0 == 1)
assert(x.sel1(2) == 2)
assert(x.fun0(3) == 3)

val f = x.sel1
assert(f(3) == 3)
}

def currying(x: Foo): Unit = {
assert(x.fun1(1)(2) == 3)
assert(x.fun2(1)(2) == 2)
assert(x.fun3(1, 2, 3)(4, 5, 6)(7, 8, 9) == -1)
}

def etaExpansion(x: Foo): Unit = {
val f0 = x.fun0(_)
assert(f0(2) == 2)

val f1 = x.fun0 _
assert(f1(2) == 2)

val f2 = x.fun1(1)(_)
assert(f2(2) == 3)

val f3 = x.fun1(1) _
assert(f3(2) == 3)

val f4 = x.fun1(1)
assert(f4(2) == 3)
}

def implicits(x: Foo) = {
implicit val y = 2
assert(x.fun4 == 2)
assert(x.fun5(1) == 3)
}

// Limited support for dependant methods
def dependent(x: Foo) = {
val y = new D

assert(x.fun6(y, "Hello") == 1)
// assert(x.fun7(y, 1) == 2) // error: No ClassTag available for x.I

val s = x.fun8(y)
assert((s: String) == "Hello")

// val i = x.fun9(y) // error: rejected (blows up in pickler if not rejected)
// assert((i: String) == "Hello") // error: Type mismatch: found: y.S(i); required: String
}

def main(args: Array[String]): Unit = {
basic(new Foo1)
currying(new Foo1)
etaExpansion(new Foo1)
implicits(new Foo1)
dependent(new Foo1)
basic(new Foo2)
currying(new Foo2)
etaExpansion(new Foo2)
implicits(new Foo2)
dependent(new Foo2)
}
}