diff --git a/tests/run-macros/places-proto.check b/tests/run-macros/places-proto.check new file mode 100644 index 000000000000..7e9312cb0e74 --- /dev/null +++ b/tests/run-macros/places-proto.check @@ -0,0 +1,3 @@ +2 +List(3, 2, 4) +A(3, 2, 4) diff --git a/tests/run-macros/places-proto/Macros_1.scala b/tests/run-macros/places-proto/Macros_1.scala new file mode 100644 index 000000000000..9b663c726fcc --- /dev/null +++ b/tests/run-macros/places-proto/Macros_1.scala @@ -0,0 +1,49 @@ +import scala.quoted.* + +object Places: + def withPlace[T: Type, R: Type](expr: Expr[T])(body: Quotes ?=> Place[T] => Expr[R])(using Quotes): Expr[R] = { + // TODO: support applyDynamic/updateDynamic? + import quotes.reflect.* + expr match + case '{ $expr: T } => + expr.asTerm match + case placeVar: Ident if placeVar.symbol.flags.is(Flags.Mutable) => + body( + new Place[T]: + def set(x: Expr[T]): Expr[Unit] = Assign(placeVar, x.asTerm).asExprOf[Unit] + def get: Expr[T] = expr + ) + case Apply(placeApply @ Select(placeTerm, "apply"), List(indexTerm)) => + def updateWithCorrectSignature(sym: Symbol): Boolean = + true // TODO check signature + val updateSym = placeTerm.symbol.methodMember("update") + .find(updateWithCorrectSignature) + .getOrElse(report.errorAndAbort("Cannot assign to " + expr, expr)) + val placeUpdate = placeTerm.select(updateSym) + indexTerm.asExpr match + case '{ $indexExpr: idx } => + '{ + val index: idx = $indexExpr + ${ + val boundIndex = '{index}.asTerm + val place = new Place[T]: + def set(x: Expr[T]): Expr[Unit] = placeUpdate.appliedTo(boundIndex, x.asTerm).asExprOf[Unit] + def get: Expr[T] = placeApply.appliedTo(boundIndex).asExprOf[T] + body(place) + } + } + case tree => + throw new MatchError(tree.show(using Printer.TreeStructure)) + } + + + trait Place[T]: + def set(x: Expr[T]): Expr[Unit] + def get: Expr[T] + +end Places + +inline def increment(inline x: Int): Unit = ${ incrementExpr('x) } // TODO generalize to Numeric types + +private def incrementExpr(x: Expr[Int])(using Quotes): Expr[Unit] = + Places.withPlace(x) { place => place.set('{ ${place.get} + 1 }) } diff --git a/tests/run-macros/places-proto/Test_2.scala b/tests/run-macros/places-proto/Test_2.scala new file mode 100644 index 000000000000..3607e188ed53 --- /dev/null +++ b/tests/run-macros/places-proto/Test_2.scala @@ -0,0 +1,24 @@ + +@main def Test: Unit = + var x = 1 + increment(x) + println(x) + + val arr = Array(1, 2, 3) + increment(arr(0)) + increment(arr(0)) + increment(arr(2)) + println(arr.toList) + + val a = new A + increment(a(0)) + increment(a(0)) + increment(a(2)) + println(a) + + +class A: + private var x = Array(1, 2, 3) + def apply(i: Int): Int = x(i) + def update(i: Int, value: Int): Unit = { x(i) = value } + override def toString(): String = s"A(${x.mkString(", ")})"