diff --git a/compiler/src/dotty/tools/dotc/tastyreflect/KernelImpl.scala b/compiler/src/dotty/tools/dotc/tastyreflect/KernelImpl.scala index e02ce96705dc..1ec7173e0640 100644 --- a/compiler/src/dotty/tools/dotc/tastyreflect/KernelImpl.scala +++ b/compiler/src/dotty/tools/dotc/tastyreflect/KernelImpl.scala @@ -282,6 +282,9 @@ class KernelImpl(val rootContext: core.Contexts.Context, val rootPosition: util. if (self.symbol.signature == core.Signature.NotAMethod) None else Some(self.symbol.signature) + def Select_apply(qualifier: Term, symbol: Symbol)(implicit ctx: Context): Select = + withDefaultPos(implicit ctx => tpd.Select(qualifier, Types.TermRef(qualifier.tpe, symbol))) + def Select_unique(qualifier: Term, name: String)(implicit ctx: Context): Select = { val denot = qualifier.tpe.member(name.toTermName) assert(!denot.isOverloaded, s"The symbol `$name` is overloaded. The method Select.unique can only be used for non-overloaded symbols.") diff --git a/library/src/scala/tasty/reflect/Kernel.scala b/library/src/scala/tasty/reflect/Kernel.scala index 8c10a12731de..8d0d130f9abd 100644 --- a/library/src/scala/tasty/reflect/Kernel.scala +++ b/library/src/scala/tasty/reflect/Kernel.scala @@ -329,6 +329,7 @@ trait Kernel { def Select_name(self: Select)(implicit ctx: Context): String def Select_signature(self: Select)(implicit ctx: Context): Option[Signature] + def Select_apply(qualifier: Term, symbol: Symbol)(implicit ctx: Context): Select def Select_unique(qualifier: Term, name: String)(implicit ctx: Context): Select // TODO rename, this returns an Apply and not a Select def Select_overloaded(qualifier: Term, name: String, targs: List[Type], args: List[Term])(implicit ctx: Context): Apply diff --git a/library/src/scala/tasty/reflect/TreeOps.scala b/library/src/scala/tasty/reflect/TreeOps.scala index f1403a07cb6e..e2c28c98d070 100644 --- a/library/src/scala/tasty/reflect/TreeOps.scala +++ b/library/src/scala/tasty/reflect/TreeOps.scala @@ -231,6 +231,10 @@ trait TreeOps extends Core { /** Scala term selection */ object Select { + /** Select a term member by symbol */ + def apply(qualifier: Term, symbol: Symbol)(implicit ctx: Context): Select = + kernel.Select_apply(qualifier, symbol) + /** Select a field or a non-overloaded method by name * * @note The method will produce an assertion error if the selected diff --git a/tests/run-macros/reflect-select-symbol-constructor/assert_1.scala b/tests/run-macros/reflect-select-symbol-constructor/assert_1.scala new file mode 100644 index 000000000000..140f890dd102 --- /dev/null +++ b/tests/run-macros/reflect-select-symbol-constructor/assert_1.scala @@ -0,0 +1,46 @@ +import scala.quoted._ +import scala.tasty._ + +object scalatest { + + inline def assert(condition: => Boolean): Unit = ${ assertImpl('condition, '{""}) } + + def assertImpl(cond: Expr[Boolean], clue: Expr[Any])(implicit refl: Reflection): Expr[Unit] = { + import refl._ + import util._ + + def isImplicitMethodType(tp: Type): Boolean = + Type.IsMethodType.unapply(tp).flatMap(tp => if tp.isImplicit then Some(true) else None).nonEmpty + + cond.unseal.underlyingArgument match { + case t @ Apply(sel @ Select(lhs, op), rhs :: Nil) => + let(lhs) { left => + let(rhs) { right => + val app = Apply(Select(left, sel.symbol), right :: Nil) + let(app) { result => + val l = left.seal + val r = right.seal + val b = result.seal.cast[Boolean] + val code = '{ scala.Predef.assert($b) } + code.unseal + } + } + }.seal.cast[Unit] + case Apply(f @ Apply(sel @ Select(Apply(qual, lhs :: Nil), op), rhs :: Nil), implicits) + if isImplicitMethodType(f.tpe) => + let(lhs) { left => + let(rhs) { right => + val app = Apply(Select(Apply(qual, left :: Nil), sel.symbol), right :: Nil) + let(Apply(app, implicits)) { result => + val l = left.seal + val r = right.seal + val b = result.seal.cast[Boolean] + val code = '{ scala.Predef.assert($b) } + code.unseal + } + } + }.seal.cast[Unit] + } + } + +} diff --git a/tests/run-macros/reflect-select-symbol-constructor/test_2.scala b/tests/run-macros/reflect-select-symbol-constructor/test_2.scala new file mode 100644 index 000000000000..1525360a71f7 --- /dev/null +++ b/tests/run-macros/reflect-select-symbol-constructor/test_2.scala @@ -0,0 +1,29 @@ +object Test { + import scalatest._ + + case class Box[T](v: T) { + def >(that: Box[T]): Boolean = this == that + } + + trait EqInt + implicit val eq: EqInt = new EqInt {} + + implicit class AnyOps[T](x: T) { + def === (y: T)(implicit c: EqInt) = x == y + } + + def main(args: Array[String]): Unit = { + val a = Box(Some(10)) + val five: Float = 5.0f + val six: Double = 6.0 + val ten: Int = 10 + assert(a.v === Some(10)) + assert(five < six) + assert(five > 4) + assert(ten > 5) + assert(six < 7) + assert(six > 5L) + assert(Box(6) > Box(6)) + assert(Box("h") > Box("h")) + } +}