diff --git a/compiler/src/dotty/tools/dotc/transform/Splicer.scala b/compiler/src/dotty/tools/dotc/transform/Splicer.scala index a6bd284da409..5725d554b815 100644 --- a/compiler/src/dotty/tools/dotc/transform/Splicer.scala +++ b/compiler/src/dotty/tools/dotc/transform/Splicer.scala @@ -111,9 +111,20 @@ object Splicer { } protected def interpretStaticMethodCall(fn: Tree, args: => List[Object])(implicit env: Env): Object = { - val (clazz, instance) = loadModule(fn.symbol.owner) - val method = getMethod(clazz, fn.symbol.name, paramsSig(fn.symbol)) - stopIfRuntimeException(method.invoke(instance, args: _*)) + if (fn.symbol == defn.NoneModuleRef.termSymbol) { + // TODO generalize + None + } else { + val (clazz, instance) = loadModule(fn.symbol.owner) + val method = getMethod(clazz, fn.symbol.name, paramsSig(fn.symbol)) + stopIfRuntimeException(method.invoke(instance, args: _*)) + } + } + + protected def interpretNew(fn: RefTree, args: => List[Result])(implicit env: Env): Object = { + val clazz = loadClass(fn.symbol.owner.fullName) + val constr = clazz.getConstructor(paramsSig(fn.symbol): _*) + constr.newInstance(args: _*).asInstanceOf[Object] } protected def unexpectedTree(tree: Tree)(implicit env: Env): Object = @@ -240,12 +251,13 @@ object Splicer { def apply(tree: Tree): Boolean = interpretTree(tree)(Map.empty) - def interpretQuote(tree: tpd.Tree)(implicit env: Env): Boolean = true - def interpretTypeQuote(tree: tpd.Tree)(implicit env: Env): Boolean = true - def interpretLiteral(value: Any)(implicit env: Env): Boolean = true - def interpretVarargs(args: List[Boolean])(implicit env: Env): Boolean = args.forall(identity) - def interpretTastyContext()(implicit env: Env): Boolean = true - def interpretStaticMethodCall(fn: tpd.Tree, args: => List[Boolean])(implicit env: Env): Boolean = args.forall(identity) + protected def interpretQuote(tree: tpd.Tree)(implicit env: Env): Boolean = true + protected def interpretTypeQuote(tree: tpd.Tree)(implicit env: Env): Boolean = true + protected def interpretLiteral(value: Any)(implicit env: Env): Boolean = true + protected def interpretVarargs(args: List[Boolean])(implicit env: Env): Boolean = args.forall(identity) + protected def interpretTastyContext()(implicit env: Env): Boolean = true + protected def interpretStaticMethodCall(fn: tpd.Tree, args: => List[Boolean])(implicit env: Env): Boolean = args.forall(identity) + protected def interpretNew(fn: RefTree, args: => List[Result])(implicit env: Env): Boolean = args.forall(identity) def unexpectedTree(tree: tpd.Tree)(implicit env: Env): Boolean = { // Assuming that top-level splices can only be in inline methods @@ -266,6 +278,7 @@ object Splicer { protected def interpretVarargs(args: List[Result])(implicit env: Env): Result protected def interpretTastyContext()(implicit env: Env): Result protected def interpretStaticMethodCall(fn: Tree, args: => List[Result])(implicit env: Env): Result + protected def interpretNew(fn: RefTree, args: => List[Result])(implicit env: Env): Result protected def unexpectedTree(tree: Tree)(implicit env: Env): Result protected final def interpretTree(tree: Tree)(implicit env: Env): Result = tree match { @@ -298,7 +311,14 @@ object Splicer { case Inlined(EmptyTree, Nil, expansion) => interpretTree(expansion) - case Typed(SeqLiteral(elems, _), _) => interpretVarargs(elems.map(e => interpretTree(e))) + case Apply(TypeApply(fun: RefTree, _), args) if fun.symbol.isConstructor && fun.symbol.owner.owner.is(Package) => + interpretNew(fun, args.map(interpretTree)) + + case Apply(fun: RefTree, args) if fun.symbol.isConstructor && fun.symbol.owner.owner.is(Package)=> + interpretNew(fun, args.map(interpretTree)) + + case Typed(SeqLiteral(elems, _), _) => + interpretVarargs(elems.map(e => interpretTree(e))) case _ => unexpectedTree(tree) diff --git a/compiler/src/dotty/tools/dotc/typer/Checking.scala b/compiler/src/dotty/tools/dotc/typer/Checking.scala index 695b47bf9a5c..42a48f09c451 100644 --- a/compiler/src/dotty/tools/dotc/typer/Checking.scala +++ b/compiler/src/dotty/tools/dotc/typer/Checking.scala @@ -678,9 +678,22 @@ trait Checking { val purityLevel = if (isFinal) Idempotent else Pure tree.tpe.widenTermRefExpr match { case tp: ConstantType if exprPurity(tree) >= purityLevel => // ok - case _ => - val allow = ctx.erasedTypes || ctx.inInlineMethod + case tp => + val allow = + ctx.erasedTypes || + ctx.inInlineMethod || + // TODO: Make None and Some constant types? + tree.symbol.eq(defn.NoneModuleRef.termSymbol) || + tree.symbol.eq(defn.SomeClass.primaryConstructor) || + (tree.symbol.name == nme.apply && tree.symbol.owner == defn.SomeClass.companionModule.moduleClass) if (!allow) ctx.error(em"$what must be a constant expression", tree.pos) + else tree match { + // TODO: add cases for type apply and multiple applies + case Apply(_, args) => + for (arg <- args) + checkInlineConformant(arg, isFinal, what) + case _ => + } } } diff --git a/compiler/test/dotc/run-test-pickling.blacklist b/compiler/test/dotc/run-test-pickling.blacklist index 39680daf65f8..6ccfdb431c33 100644 --- a/compiler/test/dotc/run-test-pickling.blacklist +++ b/compiler/test/dotc/run-test-pickling.blacklist @@ -24,6 +24,7 @@ i5119 i5119b inline-varargs-1 implicitShortcut +inline-option lazy-implicit-lists.scala lazy-implicit-nums.scala lazy-traits.scala diff --git a/tests/neg/inline-option/Macro_1.scala b/tests/neg/inline-option/Macro_1.scala new file mode 100644 index 000000000000..e78b1987f58c --- /dev/null +++ b/tests/neg/inline-option/Macro_1.scala @@ -0,0 +1,9 @@ + +import scala.quoted._ + +object Macro { + def impl(opt: Option[Int]): Expr[Int] = opt match { + case Some(i) => i.toExpr + case None => '(-1) + } +} \ No newline at end of file diff --git a/tests/neg/inline-option/Main_2.scala b/tests/neg/inline-option/Main_2.scala new file mode 100644 index 000000000000..7ae506282eba --- /dev/null +++ b/tests/neg/inline-option/Main_2.scala @@ -0,0 +1,15 @@ + +object Main { + val a: Int = 3 + size(Some(a)) // error + + val b: Option[Int] = Some(4) + size(b) // error + + inline def size(inline opt: Option[Int]): Int = ~Macro.impl(opt) + + inline def size2(inline i: Int): Int = ~Macro.impl(None) + + inline def size3(inline i: Int): Int = ~Macro.impl(Some(i)) + +} \ No newline at end of file diff --git a/tests/run/inline-option.check b/tests/run/inline-option.check new file mode 100644 index 000000000000..73ea67fc001e --- /dev/null +++ b/tests/run/inline-option.check @@ -0,0 +1,9 @@ +-1 +1 +2 +-1 +4 +5 +-1 +-1 +6 diff --git a/tests/run/inline-option/Macro_1.scala b/tests/run/inline-option/Macro_1.scala new file mode 100644 index 000000000000..6f278bcf5403 --- /dev/null +++ b/tests/run/inline-option/Macro_1.scala @@ -0,0 +1,13 @@ + +import scala.quoted._ + +object Macros { + + def impl(opt: Option[Int]): Expr[Int] = opt match { + case Some(i) => i.toExpr + case None => '(-1) + } + + def impl2(opt: Option[Option[Int]]): Expr[Int] = impl(opt.flatten) + +} diff --git a/tests/run/inline-option/Main_2.scala b/tests/run/inline-option/Main_2.scala new file mode 100644 index 000000000000..0050e2bdf89e --- /dev/null +++ b/tests/run/inline-option/Main_2.scala @@ -0,0 +1,28 @@ + +object Test { + + def main(args: Array[String]): Unit = { + println(size(None)) + println(size(Some(1))) + println(size(new Some(2))) + + println(size2(3)) + println(size3(4)) + println(size4(5)) + + println(size5(None)) + println(size5(Some(None))) + println(size5(Some(Some(6)))) + } + + inline def size(inline opt: Option[Int]): Int = ~Macros.impl(opt) + + inline def size2(inline i: Int): Int = ~Macros.impl(None) + + inline def size3(inline i: Int): Int = ~Macros.impl(Some(i)) + + inline def size4(inline i: Int): Int = ~Macros.impl2(Some(Some(i))) + + inline def size5(inline opt: Option[Option[Int]]): Int = ~Macros.impl2(opt) + +}