Skip to content

Add inline tuples #5186

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

Closed
wants to merge 2 commits into from
Closed
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
32 changes: 30 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,37 @@ 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 =>
// TODO: Make None and Some constant types?
def isCaseClassApply(sym: Symbol): Boolean = {
sym.name == nme.apply && (
tree.symbol.owner == defn.SomeClass.companionModule.moduleClass ||
defn.isTupleClass(tree.symbol.owner.companionClass)
)
}
def isCaseClassNew(sym: Symbol): Boolean = {
sym.isPrimaryConstructor && (
sym.eq(defn.SomeClass.primaryConstructor) ||
defn.isTupleClass(tree.symbol.owner)
)
}
def isCaseObject(sym: Symbol): Boolean = {
tree.symbol.eq(defn.NoneModuleRef.termSymbol)
}
val allow =
ctx.erasedTypes ||
ctx.inInlineMethod ||
isCaseClassApply(tree.symbol) ||
isCaseClassNew(tree.symbol) ||
isCaseObject(tree.symbol)
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
3 changes: 3 additions & 0 deletions compiler/test/dotc/run-test-pickling.blacklist
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,9 @@ i5119
i5119b
inline-varargs-1
implicitShortcut
inline-option
inline-tuples-1
inline-tuples-2
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))

}
27 changes: 27 additions & 0 deletions tests/neg/inline-tuples-1/Macro_1.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@

import scala.quoted._

object Macros {
def tup1(tup: Tuple1[Int]): Expr[Int] = tup.productIterator.map(_.asInstanceOf[Int]).sum.toExpr
def tup2(tup: Tuple2[Int, Int]): Expr[Int] = tup.productIterator.map(_.asInstanceOf[Int]).sum.toExpr
def tup3(tup: Tuple3[Int, Int, Int]): Expr[Int] = tup.productIterator.map(_.asInstanceOf[Int]).sum.toExpr
def tup4(tup: Tuple4[Int, Int, Int, Int]): Expr[Int] = tup.productIterator.map(_.asInstanceOf[Int]).sum.toExpr
def tup5(tup: Tuple5[Int, Int, Int, Int, Int]): Expr[Int] = tup.productIterator.map(_.asInstanceOf[Int]).sum.toExpr
def tup6(tup: Tuple6[Int, Int, Int, Int, Int, Int]): Expr[Int] = tup.productIterator.map(_.asInstanceOf[Int]).sum.toExpr
def tup7(tup: Tuple7[Int, Int, Int, Int, Int, Int, Int]): Expr[Int] = tup.productIterator.map(_.asInstanceOf[Int]).sum.toExpr
def tup8(tup: Tuple8[Int, Int, Int, Int, Int, Int, Int, Int]): Expr[Int] = tup.productIterator.map(_.asInstanceOf[Int]).sum.toExpr
def tup9(tup: Tuple9[Int, Int, Int, Int, Int, Int, Int, Int, Int]): Expr[Int] = tup.productIterator.map(_.asInstanceOf[Int]).sum.toExpr
def tup10(tup: Tuple10[Int, Int, Int, Int, Int, Int, Int, Int, Int, Int]): Expr[Int] = tup.productIterator.map(_.asInstanceOf[Int]).sum.toExpr
def tup11(tup: Tuple11[Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int]): Expr[Int] = tup.productIterator.map(_.asInstanceOf[Int]).sum.toExpr
def tup12(tup: Tuple12[Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int]): Expr[Int] = tup.productIterator.map(_.asInstanceOf[Int]).sum.toExpr
def tup13(tup: Tuple13[Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int]): Expr[Int] = tup.productIterator.map(_.asInstanceOf[Int]).sum.toExpr
def tup14(tup: Tuple14[Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int]): Expr[Int] = tup.productIterator.map(_.asInstanceOf[Int]).sum.toExpr
def tup15(tup: Tuple15[Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int]): Expr[Int] = tup.productIterator.map(_.asInstanceOf[Int]).sum.toExpr
def tup16(tup: Tuple16[Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int]): Expr[Int] = tup.productIterator.map(_.asInstanceOf[Int]).sum.toExpr
def tup17(tup: Tuple17[Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int]): Expr[Int] = tup.productIterator.map(_.asInstanceOf[Int]).sum.toExpr
def tup18(tup: Tuple18[Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int]): Expr[Int] = tup.productIterator.map(_.asInstanceOf[Int]).sum.toExpr
def tup19(tup: Tuple19[Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int]): Expr[Int] = tup.productIterator.map(_.asInstanceOf[Int]).sum.toExpr
def tup20(tup: Tuple20[Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int]): Expr[Int] = tup.productIterator.map(_.asInstanceOf[Int]).sum.toExpr
def tup21(tup: Tuple21[Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int]): Expr[Int] = tup.productIterator.map(_.asInstanceOf[Int]).sum.toExpr
def tup22(tup: Tuple22[Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int]): Expr[Int] = tup.productIterator.map(_.asInstanceOf[Int]).sum.toExpr
}
Loading