Skip to content

Allow None and Some to be inline parameters #5181

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Oct 4, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
40 changes: 30 additions & 10 deletions compiler/src/dotty/tools/dotc/transform/Splicer.scala
Original file line number Diff line number Diff line change
Expand Up @@ -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 =
Expand Down Expand Up @@ -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
Expand All @@ -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 {
Expand Down Expand Up @@ -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)
Expand Down
17 changes: 15 additions & 2 deletions compiler/src/dotty/tools/dotc/typer/Checking.scala
Original file line number Diff line number Diff line change
Expand Up @@ -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 _ =>
}
}
}

Expand Down
1 change: 1 addition & 0 deletions compiler/test/dotc/run-test-pickling.blacklist
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ i5119
i5119b
inline-varargs-1
implicitShortcut
inline-option
lazy-implicit-lists.scala
lazy-implicit-nums.scala
lazy-traits.scala
Expand Down
9 changes: 9 additions & 0 deletions tests/neg/inline-option/Macro_1.scala
Original file line number Diff line number Diff line change
@@ -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)
}
}
15 changes: 15 additions & 0 deletions tests/neg/inline-option/Main_2.scala
Original file line number Diff line number Diff line change
@@ -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))

}
9 changes: 9 additions & 0 deletions tests/run/inline-option.check
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
-1
1
2
-1
4
5
-1
-1
6
13 changes: 13 additions & 0 deletions tests/run/inline-option/Macro_1.scala
Original file line number Diff line number Diff line change
@@ -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)

}
28 changes: 28 additions & 0 deletions tests/run/inline-option/Main_2.scala
Original file line number Diff line number Diff line change
@@ -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)

}