diff --git a/tests/run-separate-compilation/gestalt-optional-inline/Macro_1.scala b/tests/run-separate-compilation/gestalt-optional-inline/Macro_1.scala new file mode 100644 index 000000000000..ee0b6520b170 --- /dev/null +++ b/tests/run-separate-compilation/gestalt-optional-inline/Macro_1.scala @@ -0,0 +1,18 @@ +// Port of https://github.com/liufengyun/gestalt/blob/master/macros/src/main/scala/gestalt/macros/Optional.scala +// using only `inline` + +final class Optional[+A >: Null](val value: A) extends AnyVal { + def get: A = value + def isEmpty = value == null + + inline def getOrElse[B >: A](alt: => B): B = + if (isEmpty) alt else value + + // f is by name to beta-reduce it + inline def map[B >: Null](f: => A => B): Optional[B] = { + if (isEmpty) new Optional(null) + else new Optional(f(value)) + } + + override def toString = if (isEmpty) "" else s"$value" +} diff --git a/tests/run-separate-compilation/gestalt-optional-inline/Test_2.scala b/tests/run-separate-compilation/gestalt-optional-inline/Test_2.scala new file mode 100644 index 000000000000..039091974350 --- /dev/null +++ b/tests/run-separate-compilation/gestalt-optional-inline/Test_2.scala @@ -0,0 +1,81 @@ +// Port of https://github.com/liufengyun/gestalt/blob/master/macros/src/test/scala/gestalt/macros/OptionalTest.scala + +object Test { + + class C + + def main(args: Array[String]): Unit = { + getOrElseTest() + mapTest() + simpleGetOrElseTest() + `don't duplicate side-effects of the prefix test`() + hygieneTest() + `owner chain corruptionTest`() + `typed/untyped mixup test`() + `the final thingTest`() + } + + def getOrElseTest(): Unit = { + val opt = new Optional[String]("hello") + assert(opt.getOrElse("world") == "hello") + + val opt2 = new Optional[String](null) + assert(opt2.getOrElse("hello") == "hello") + } + + def mapTest(): Unit = { + val opt = new Optional[String]("hello") + assert(opt.map(_ + " world") == new Optional("hello world")) + + val opt2 = new Optional[String](null) + assert(opt2.map(_ + " world") == new Optional(null)) + } + + def simpleGetOrElseTest(): Unit = { + val c1 = new C + val c2 = new C + val c3 = new C + var sideEffect = 0 + + val x = new Optional(c1) + val x1 = x.getOrElse(c2) + assert(x1 == c1) + assert(sideEffect == 0) + + val y = new Optional(null) + val y1 = y.getOrElse({ sideEffect += 1; c3 }) + assert(y1 == c3) + assert(sideEffect == 1) + } + + def `don't duplicate side-effects of the prefix test`(): Unit = { + val c1 = new C + val c2 = new C + var sideEffect = 0 + + def x = { sideEffect += 1; new Optional(c1) } + val x1 = x.getOrElse(c2) + assert(sideEffect == 1) + } + + def hygieneTest(): Unit = { + val temp = 100 + new Optional(if (temp < 100) new C else null).getOrElse(new C) + } + + def `owner chain corruptionTest`(): Unit = { + def foo(x: => Optional[C]) = x + foo({ val y = new Optional(null); y }).getOrElse(new C) + } + + def `typed/untyped mixup test`(): Unit = { + val x1 = new Optional(new C) + val x2 = x1.map(_.toString) + } + + def `the final thingTest`(): Unit = { + def foo(f: => C): C = f + val x1 = new Optional(new C) + val x2 = x1.map(x => foo({ val y = x; y })) + } +} diff --git a/tests/run-separate-compilation/gestalt-optional-staging/Macro_1.scala b/tests/run-separate-compilation/gestalt-optional-staging/Macro_1.scala new file mode 100644 index 000000000000..19aff30b7315 --- /dev/null +++ b/tests/run-separate-compilation/gestalt-optional-staging/Macro_1.scala @@ -0,0 +1,30 @@ +// Port of https://github.com/liufengyun/gestalt/blob/master/macros/src/main/scala/gestalt/macros/Optional.scala +// using staging macros (only quotes and splices) + +import scala.quoted._ + +final class Optional[+A >: Null](val value: A) extends AnyVal { + def get: A = value + def isEmpty = value == null + + inline def getOrElse[B >: A](alt: => B): B = ~Optional.getOrElseImpl('(this), '(alt)) + + inline def map[B >: Null](f: A => B): Optional[B] = ~Optional.mapImpl('(this), '(f)) + + override def toString = if (isEmpty) "" else s"$value" +} + +object Optional { + + // FIXME fix issue #5097 and enable private + /*private*/ def getOrElseImpl[T >: Null](opt: Expr[Optional[T]], alt: Expr[T]): Expr[T] = '{ + if ((~opt).isEmpty) ~alt else (~opt).value + } + + // FIXME fix issue #5097 and enable private + /*private*/ def mapImpl[A >: Null, B >: Null : Type](opt: Expr[Optional[A]], f: Expr[A => B]): Expr[Optional[B]] = '{ + if ((~opt).isEmpty) new Optional(null) + else new Optional(~f('((~opt).value))) + } + +} diff --git a/tests/run-separate-compilation/gestalt-optional-staging/Test_2.scala b/tests/run-separate-compilation/gestalt-optional-staging/Test_2.scala new file mode 100644 index 000000000000..039091974350 --- /dev/null +++ b/tests/run-separate-compilation/gestalt-optional-staging/Test_2.scala @@ -0,0 +1,81 @@ +// Port of https://github.com/liufengyun/gestalt/blob/master/macros/src/test/scala/gestalt/macros/OptionalTest.scala + +object Test { + + class C + + def main(args: Array[String]): Unit = { + getOrElseTest() + mapTest() + simpleGetOrElseTest() + `don't duplicate side-effects of the prefix test`() + hygieneTest() + `owner chain corruptionTest`() + `typed/untyped mixup test`() + `the final thingTest`() + } + + def getOrElseTest(): Unit = { + val opt = new Optional[String]("hello") + assert(opt.getOrElse("world") == "hello") + + val opt2 = new Optional[String](null) + assert(opt2.getOrElse("hello") == "hello") + } + + def mapTest(): Unit = { + val opt = new Optional[String]("hello") + assert(opt.map(_ + " world") == new Optional("hello world")) + + val opt2 = new Optional[String](null) + assert(opt2.map(_ + " world") == new Optional(null)) + } + + def simpleGetOrElseTest(): Unit = { + val c1 = new C + val c2 = new C + val c3 = new C + var sideEffect = 0 + + val x = new Optional(c1) + val x1 = x.getOrElse(c2) + assert(x1 == c1) + assert(sideEffect == 0) + + val y = new Optional(null) + val y1 = y.getOrElse({ sideEffect += 1; c3 }) + assert(y1 == c3) + assert(sideEffect == 1) + } + + def `don't duplicate side-effects of the prefix test`(): Unit = { + val c1 = new C + val c2 = new C + var sideEffect = 0 + + def x = { sideEffect += 1; new Optional(c1) } + val x1 = x.getOrElse(c2) + assert(sideEffect == 1) + } + + def hygieneTest(): Unit = { + val temp = 100 + new Optional(if (temp < 100) new C else null).getOrElse(new C) + } + + def `owner chain corruptionTest`(): Unit = { + def foo(x: => Optional[C]) = x + foo({ val y = new Optional(null); y }).getOrElse(new C) + } + + def `typed/untyped mixup test`(): Unit = { + val x1 = new Optional(new C) + val x2 = x1.map(_.toString) + } + + def `the final thingTest`(): Unit = { + def foo(f: => C): C = f + val x1 = new Optional(new C) + val x2 = x1.map(x => foo({ val y = x; y })) + } +}