diff --git a/library/src/scala/Selectable.scala b/library/src/scala/Selectable.scala index 332cab87b662..01f8dd902549 100644 --- a/library/src/scala/Selectable.scala +++ b/library/src/scala/Selectable.scala @@ -1,5 +1,4 @@ package scala -import scala.reflect.ClassTag /** A marker trait for objects that support structural selection via * `selectDynamic` and `applyDynamic` @@ -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) diff --git a/tests/run/structural-compat.scala b/tests/run/structural-compat.scala new file mode 100644 index 000000000000..600f3dd1cba1 --- /dev/null +++ b/tests/run/structural-compat.scala @@ -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) + } +}