Skip to content

Commit 2f1d990

Browse files
authored
Merge pull request #5796 from dotty-staging/add-select-constructor
Add constructors for Select
2 parents cc59576 + 3b9306b commit 2f1d990

File tree

7 files changed

+111
-21
lines changed

7 files changed

+111
-21
lines changed

compiler/src/dotty/tools/dotc/tastyreflect/TreeOpsImpl.scala

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -401,6 +401,15 @@ trait TreeOpsImpl extends scala.tasty.reflect.TreeOps with RootPositionImpl with
401401

402402

403403
object Select extends SelectModule {
404+
def unique(qualifier: Term, name: String)(implicit ctx: Context): Select = {
405+
val denot = qualifier.tpe.member(name.toTermName)
406+
assert(!denot.isOverloaded, s"The symbol `$name` is overloaded. The method Select.unique can only be used for non-overloaded symbols.")
407+
withDefaultPos(implicit ctx => tpd.Select(qualifier, name.toTermName))
408+
}
409+
410+
def overloaded(qualifier: Term, name: String, targs: List[Type], args: List[Term])(implicit ctx: Context): Apply =
411+
withDefaultPos(implicit ctx => tpd.applyOverloaded(qualifier, name.toTermName, args, targs, Types.WildcardType).asInstanceOf[Apply])
412+
404413
def copy(original: Tree)(qualifier: Term, name: String)(implicit ctx: Context): Select =
405414
tpd.cpy.Select(original)(qualifier, name.toTermName)
406415

library/src-bootstrapped/scala/tasty/reflect/utils/TreeUtils.scala

Lines changed: 9 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -9,20 +9,17 @@ trait TreeUtils {
99
import reflect._
1010

1111
/** Bind the `rhs` to a `val` and use it in `body` */
12-
def let(rhs: Term)(bodyType: Type)(body: Term.Ident => Term): Term = {
13-
// Recover all lost type information
12+
def let(rhs: Term)(body: Term.Ident => Term): Term = {
1413
type T // TODO probably it is better to use the Sealed contruct rather than let the user create their own existential type
15-
type U // TODO probably it is better to use the Sealed contruct rather than let the user create their own existential type
16-
implicit val bodyTpe: quoted.Type[U] = bodyType.seal.asInstanceOf[quoted.Type[U]]
1714
implicit val rhsTpe: quoted.Type[T] = rhs.tpe.seal.asInstanceOf[quoted.Type[T]]
1815
val rhsExpr = rhs.seal[T]
19-
let[T, U](rhsExpr)(x => body(x.unseal.asInstanceOf[Term.Ident]).seal[U]).unseal
16+
val expr = '{
17+
val x = ~rhsExpr
18+
~{
19+
val id = ('(x)).unseal.asInstanceOf[Term.Ident]
20+
body(id).seal[Any]
21+
}
22+
}
23+
expr.unseal
2024
}
21-
22-
/** */
23-
private def let[T: quoted.Type, U: quoted.Type](rhs: Expr[T])(in: Expr[T] => Expr[U]): Expr[U] = '{
24-
val x = ~rhs
25-
~in('(x))
26-
}
27-
2825
}

library/src/scala/tasty/reflect/TreeOps.scala

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -272,8 +272,16 @@ trait TreeOps extends Core {
272272
/** Scala term selection */
273273
val Select: SelectModule
274274
abstract class SelectModule {
275-
276-
// TODO def apply(qualifier: Term, name: String, signature: Option[Signature])(implicit ctx: Context): Select
275+
/** Select a field or a non-overloaded method by name
276+
*
277+
* @note The method will produce an assertion error if the selected
278+
* method is overloaded. The method `overloaded` should be used
279+
* in that case.
280+
*/
281+
def unique(qualifier: Term, name: String)(implicit ctx: Context): Select
282+
283+
/** Call an overloaded method with the given type and term parameters */
284+
def overloaded(qualifier: Term, name: String, targs: List[Type], args: List[Term])(implicit ctx: Context): Apply
277285

278286
def copy(original: Tree)(qualifier: Term, name: String)(implicit ctx: Context): Select
279287

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
import scala.quoted._
2+
import scala.tasty._
3+
4+
object scalatest {
5+
6+
inline def assert(condition: => Boolean): Unit = ~assertImpl('(condition), '(""))
7+
8+
def assertImpl(cond: Expr[Boolean], clue: Expr[Any])(implicit refl: Reflection): Expr[Unit] = {
9+
import refl._
10+
import util._
11+
import quoted.Toolbox.Default._
12+
13+
def isImplicitMethodType(tp: Type): Boolean =
14+
Type.IsMethodType.unapply(tp).flatMap(tp => if tp.isImplicit then Some(true) else None).nonEmpty
15+
16+
cond.unseal.underlyingArgument match {
17+
case t @ Term.Apply(Term.Select(lhs, op), rhs :: Nil) =>
18+
let(lhs) { left =>
19+
let(rhs) { right =>
20+
val app = Term.Select.overloaded(left, op, Nil, right :: Nil)
21+
let(app) { result =>
22+
val l = left.seal[Any]
23+
val r = right.seal[Any]
24+
val b = result.seal[Boolean]
25+
val code = '{ scala.Predef.assert(~b) }
26+
code.unseal
27+
}
28+
}
29+
}.seal[Unit]
30+
case Term.Apply(f @ Term.Apply(Term.Select(Term.Apply(qual, lhs :: Nil), op), rhs :: Nil), implicits)
31+
if isImplicitMethodType(f.tpe) =>
32+
let(lhs) { left =>
33+
let(rhs) { right =>
34+
val app = Term.Select.overloaded(Term.Apply(qual, left :: Nil), op, Nil, right :: Nil)
35+
let(Term.Apply(app, implicits)) { result =>
36+
val l = left.seal[Any]
37+
val r = right.seal[Any]
38+
val b = result.seal[Boolean]
39+
val code = '{ scala.Predef.assert(~b) }
40+
code.unseal
41+
}
42+
}
43+
}.seal[Unit]
44+
}
45+
}
46+
47+
}
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
object Test {
2+
import scalatest._
3+
4+
case class Box[T](v: T) {
5+
def >(that: Box[T]): Boolean = this == that
6+
}
7+
8+
trait EqInt
9+
implicit val eq: EqInt = new EqInt {}
10+
11+
implicit class AnyOps[T](x: T) {
12+
def === (y: T)(implicit c: EqInt) = x == y
13+
}
14+
15+
def main(args: Array[String]): Unit = {
16+
val a = Box(Some(10))
17+
val five: Float = 5.0f
18+
val six: Double = 6.0
19+
val ten: Int = 10
20+
assert(a.v === Some(10))
21+
assert(five < six)
22+
assert(five > 4)
23+
assert(ten > 5)
24+
assert(six < 7)
25+
assert(six > 5L)
26+
assert(Box(6) > Box(6))
27+
assert(Box("h") > Box("h"))
28+
}
29+
}

tests/run-with-compiler/reflect-select-copy/assert_1.scala

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -15,9 +15,9 @@ object scalatest {
1515

1616
cond.unseal.underlyingArgument match {
1717
case Term.Apply(sel @ Term.Select(lhs, op), rhs :: Nil) =>
18-
let(lhs)(definitions.UnitType) { left =>
19-
let(rhs)(definitions.UnitType) { right =>
20-
let(Term.Apply(Term.Select.copy(sel)(left, op), right :: Nil))(definitions.UnitType) { result =>
18+
let(lhs) { left =>
19+
let(rhs) { right =>
20+
let(Term.Apply(Term.Select.copy(sel)(left, op), right :: Nil)) { result =>
2121
val l = left.seal[Any]
2222
val r = right.seal[Any]
2323
val b = result.seal[Boolean]
@@ -28,9 +28,9 @@ object scalatest {
2828
}.seal[Unit]
2929
case Term.Apply(f @ Term.Apply(Term.IsSelect(sel @ Term.Select(Term.Apply(qual, lhs :: Nil), op)), rhs :: Nil), implicits)
3030
if isImplicitMethodType(f.tpe) =>
31-
let(lhs)(definitions.UnitType) { left =>
32-
let(rhs)(definitions.UnitType) { right =>
33-
let(Term.Apply(Term.Apply(Term.Select.copy(sel)(Term.Apply(qual, left :: Nil), op), right :: Nil), implicits))(definitions.UnitType) { result =>
31+
let(lhs) { left =>
32+
let(rhs) { right =>
33+
let(Term.Apply(Term.Apply(Term.Select.copy(sel)(Term.Apply(qual, left :: Nil), op), right :: Nil), implicits)) { result =>
3434
val l = left.seal[Any]
3535
val r = right.seal[Any]
3636
val b = result.seal[Boolean]

tests/run-with-compiler/tasty-unsafe-let/quoted_1.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ object Macros {
1313
val rhsTerm = rhs.unseal
1414

1515
import reflect.util.{let => letTerm}
16-
letTerm(rhsTerm)(('[Unit]).unseal.tpe) { rhsId =>
16+
letTerm(rhsTerm) { rhsId =>
1717
body(rhsId.seal[Any].asInstanceOf[Expr[T]]).unseal // Dangerous uncheked cast!
1818
}.seal[Unit]
1919
}

0 commit comments

Comments
 (0)