Skip to content

Fix #9033, fix #9056: Add EmptyTuple #9049

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
Jun 8, 2020
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
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ class Apply {
def setup(): Unit = {
val size = sizeAndIndex.split(' ')(0).toInt
index = sizeAndIndex.split(' ')(1).toInt
tuple = "elem" *: ()
tuple = "elem" *: Tuple()

for (i <- 1 until size)
tuple = "elem" *: tuple
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ class Concat {
var tuple2: Tuple = _

def tupleOfSize(n: Int): Tuple = {
var t: Tuple = ()
var t: Tuple = Tuple()
for (i <- 1 to n)
t = "elem" *: t
t
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ class Cons {

@Setup
def setup(): Unit = {
tuple = ()
tuple = Tuple()

for (i <- 1 to size)
tuple = "elem" *: tuple
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ class Conversions {

@Setup
def setup(): Unit = {
tuple = ()
tuple = Tuple()

for (i <- 1 to size)
tuple = "elem" *: tuple
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ class Map {

@Setup
def setup(): Unit = {
tuple = ()
tuple = Tuple()

for (i <- 1 to size)
tuple = "elem" *: tuple
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ class Tail {

@Setup
def setup(): Unit = {
tuple = "elem" *: ()
tuple = "elem" *: Tuple()

for (i <- 1 until size)
tuple = "elem" *: tuple
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,11 +11,11 @@ class TupleOps {

@Setup
def setup(): Unit = {
tuple1 = ()
tuple1 = Tuple()
for (i <- 1 until 15)
tuple1 = s"elem$i" *: tuple1

tuple2 = ()
tuple2 = Tuple()
for (i <- 1 until 10)
tuple2 = s"elem$i" *: tuple2

Expand All @@ -25,23 +25,23 @@ class TupleOps {

def tupleFlatMap(tuple: Tuple, f: [A] => A => Tuple): Tuple = {
def tailRecFlatMap(t: Tuple, acc: Tuple): Tuple = t match {
case () => acc
case Tuple() => acc
case x *: rest => tailRecFlatMap(rest, acc ++ f(x))
}
tailRecFlatMap(tuple, ())
tailRecFlatMap(tuple, Tuple())
}

def tupleReverse(tuple: Tuple): Tuple = {
def tailRecReverse(t: Tuple, acc: Tuple): Tuple = t match {
case () => acc
case Tuple() => acc
case x *: rest => tailRecReverse(rest, x *: acc)
}
tailRecReverse(tuple, ())
tailRecReverse(tuple, Tuple())
}

def tupleMerge(tuple1: Tuple, tuple2: Tuple): Tuple = (tuple1, tuple2) match {
case (_, ()) => tuple1
case ((), _) => tuple2
case (_, Tuple()) => tuple1
case (Tuple(), _) => tuple2
case (x *: xs, y *: ys) =>
if (x.asInstanceOf[Int] <= y.asInstanceOf[Int]) x *: tupleMerge(xs, tuple2)
else y *: tupleMerge(tuple1, ys)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,8 @@ class Zip {

@Setup
def setup(): Unit = {
tuple1 = ()
tuple2 = ()
tuple1 = Tuple()
tuple2 = Tuple()

for (i <- 1 to size) {
tuple1 = "el" *: tuple1
Expand Down
2 changes: 1 addition & 1 deletion bench-run/src/main/scala/tuples/Drop.scala
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ class Drop {

@Setup
def setup(): Unit = {
tuple = ()
tuple = Tuple()
half = size / 2

for (i <- 1 to size)
Expand Down
2 changes: 1 addition & 1 deletion bench-run/src/main/scala/tuples/Split.scala
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ class Split {

@Setup
def setup(): Unit = {
tuple = ()
tuple = Tuple()
half = size / 2

for (i <- 1 to size)
Expand Down
2 changes: 1 addition & 1 deletion bench-run/src/main/scala/tuples/Take.scala
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ class Take {

@Setup
def setup(): Unit = {
tuple = ()
tuple = Tuple()
half = size / 2

for (i <- 1 to size)
Expand Down
2 changes: 1 addition & 1 deletion compiler/src/dotty/tools/dotc/ast/tpd.scala
Original file line number Diff line number Diff line change
Expand Up @@ -1337,7 +1337,7 @@ object tpd extends Trees.Instance[Type] with TypedTreeInfo {

/** Creates the nested pairs type tree repesentation of the type trees in `ts` */
def nestedPairsTypeTree(ts: List[Tree])(implicit ctx: Context): Tree =
ts.foldRight[Tree](TypeTree(defn.UnitType))((x, acc) => AppliedTypeTree(TypeTree(defn.PairClass.typeRef), x :: acc :: Nil))
ts.foldRight[Tree](TypeTree(defn.EmptyTupleModule.termRef))((x, acc) => AppliedTypeTree(TypeTree(defn.PairClass.typeRef), x :: acc :: Nil))

/** Replaces all positions in `tree` with zero-extent positions */
private def focusPositions(tree: Tree)(implicit ctx: Context): Tree = {
Expand Down
14 changes: 9 additions & 5 deletions compiler/src/dotty/tools/dotc/core/Definitions.scala
Original file line number Diff line number Diff line change
Expand Up @@ -749,11 +749,12 @@ class Definitions {
@tu lazy val TupleTypeRef: TypeRef = ctx.requiredClassRef("scala.Tuple")
def TupleClass(implicit ctx: Context): ClassSymbol = TupleTypeRef.symbol.asClass
@tu lazy val Tuple_cons: Symbol = TupleClass.requiredMethod("*:")
@tu lazy val EmptyTupleModule: Symbol = ctx.requiredModule("scala.EmptyTuple")
@tu lazy val NonEmptyTupleTypeRef: TypeRef = ctx.requiredClassRef("scala.NonEmptyTuple")
def NonEmptyTupleClass(implicit ctx: Context): ClassSymbol = NonEmptyTupleTypeRef.symbol.asClass
lazy val NonEmptyTuple_tail: Symbol = NonEmptyTupleClass.requiredMethod("tail")

@tu lazy val PairClass: ClassSymbol = ctx.requiredClass("scala.*:")

@tu lazy val TupleXXLClass: ClassSymbol = ctx.requiredClass("scala.runtime.TupleXXL")
def TupleXXLModule(implicit ctx: Context): Symbol = TupleXXLClass.companionModule

Expand All @@ -770,6 +771,9 @@ class Definitions {
lazy val RuntimeTuple_concat: Symbol = RuntimeTupleModule.requiredMethod("concat")
lazy val RuntimeTuple_toArray: Symbol = RuntimeTupleModule.requiredMethod("toArray")
lazy val RuntimeTuple_productToArray: Symbol = RuntimeTupleModule.requiredMethod("productToArray")
lazy val RuntimeTuple_isInstanceOfTuple: Symbol = RuntimeTupleModule.requiredMethod("isInstanceOfTuple")
lazy val RuntimeTuple_isInstanceOfEmptyTuple: Symbol = RuntimeTupleModule.requiredMethod("isInstanceOfEmptyTuple")
lazy val RuntimeTuple_isInstanceOfNonEmptyTuple: Symbol = RuntimeTupleModule.requiredMethod("isInstanceOfNonEmptyTuple")

@tu lazy val TupledFunctionTypeRef: TypeRef = ctx.requiredClassRef("scala.TupledFunction")
def TupledFunctionClass(implicit ctx: Context): ClassSymbol = TupledFunctionTypeRef.symbol.asClass
Expand Down Expand Up @@ -1185,11 +1189,11 @@ class Definitions {
}

def tupleTypes(tp: Type, bound: Int = Int.MaxValue)(implicit ctx: Context): Option[List[Type]] = {
@tailrec def rec(tp: Type, acc: List[Type], bound: Int): Option[List[Type]] = tp.normalized match {
@tailrec def rec(tp: Type, acc: List[Type], bound: Int): Option[List[Type]] = tp.normalized.dealias match {
case _ if bound < 0 => Some(acc.reverse)
case tp: AppliedType if defn.PairClass == tp.classSymbol => rec(tp.args(1), tp.args.head :: acc, bound - 1)
case tp: AppliedType if defn.isTupleClass(tp.tycon.classSymbol) => Some(acc.reverse ::: tp.args)
case tp if tp.classSymbol == defn.UnitClass => Some(acc.reverse)
case tp: TermRef if tp.symbol == defn.EmptyTupleModule => Some(acc.reverse)
case _ => None
}
rec(tp.stripTypeVar, Nil, bound)
Expand Down Expand Up @@ -1300,7 +1304,7 @@ class Definitions {
def syntheticParent(tparams: List[TypeSymbol]): Type =
if (tparams.isEmpty) TupleTypeRef
else TypeOps.nestedPairs(tparams.map(_.typeRef))
if (isTupleClass(cls) || cls == UnitClass) parents :+ syntheticParent(tparams)
if (isTupleClass(cls)) parents :+ syntheticParent(tparams)
else parents
}

Expand Down Expand Up @@ -1397,7 +1401,7 @@ class Definitions {
.updated(AnyClass, ObjectClass)
.updated(AnyValClass, ObjectClass)
.updated(SingletonClass, ObjectClass)
.updated(TupleClass, ObjectClass)
.updated(TupleClass, ProductClass)
.updated(NonEmptyTupleClass, ProductClass)

// ----- Initialization ---------------------------------------------------
Expand Down
2 changes: 1 addition & 1 deletion compiler/src/dotty/tools/dotc/core/TypeOps.scala
Original file line number Diff line number Diff line change
Expand Up @@ -729,6 +729,6 @@ object TypeOps:
}

def nestedPairs(ts: List[Type])(using Context): Type =
ts.foldRight(defn.UnitType: Type)(defn.PairClass.typeRef.appliedTo(_, _))
ts.foldRight(defn.EmptyTupleModule.termRef: Type)(defn.PairClass.typeRef.appliedTo(_, _))

end TypeOps
Original file line number Diff line number Diff line change
Expand Up @@ -2005,7 +2005,10 @@ class ReflectionCompilerInterface(val rootContext: core.Contexts.Context) extend
def Definitions_NothingType: Type = defn.NothingType
def Definitions_NullType: Type = defn.NullType
def Definitions_StringType: Type = defn.StringType

def Definitions_TupleType: Type = defn.TupleTypeRef
def Definitions_EmptyTupleType: Type = defn.EmptyTupleModule.termRef
def Definitions_NonEmptyTupleType: Type = defn.NonEmptyTupleClass.typeRef
def Definitions_TupleConsType: Type = defn.PairClass.typeRef

///////////////
// IMPLICITS //
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -69,8 +69,8 @@ class TupleOptimizations extends MiniPhase with IdentityDenotTransformer {
val size = tpes.size
assert(size > 0)
if (size == 1)
// ()
Literal(Constant(()))
// scala.EmptyTuple
ref(defn.EmptyTupleModule.termRef)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe define a type in Definitions for defn.EmptyTupleModule.termRef.

else if (size <= 5)
// val t = tup.asInstanceOf[TupleN[...]]
// TupleN-1(t._2, ..., t._n)
Expand Down Expand Up @@ -197,8 +197,8 @@ class TupleOptimizations extends MiniPhase with IdentityDenotTransformer {

private def knownTupleFromIterator(size: Int, it: Tree)(implicit ctx: Context): Tree =
if (size == 0)
// Unit for empty tuple
Literal(Constant(())) // TODO should this code be here? Or assert(size > specializedSize)
// EmptyTuple for empty tuple
ref(defn.EmptyTupleModule.termRef) // TODO should this code be here? Or assert(size > specializedSize)
else if (size <= MaxTupleArity) {
// TupleN(it.next(), ..., it.next())

Expand Down
12 changes: 12 additions & 0 deletions compiler/src/dotty/tools/dotc/transform/TypeTestsCasts.scala
Original file line number Diff line number Diff line change
Expand Up @@ -305,11 +305,17 @@ object TypeTestsCasts {
*
* expr.isInstanceOf[A | B] ~~> expr.isInstanceOf[A] | expr.isInstanceOf[B]
* expr.isInstanceOf[A & B] ~~> expr.isInstanceOf[A] & expr.isInstanceOf[B]
* expr.isInstanceOf[Tuple] ~~> scala.runtime.Tuple.isInstanceOfTuple(expr)
* expr.isInstanceOf[EmptyTuple] ~~> scala.runtime.Tuple.isInstanceOfEmptyTuple(expr)
* expr.isInstanceOf[NonEmptyTuple] ~~> scala.runtime.Tuple.isInstanceOfNonEmptyTuple(expr)
* expr.isInstanceOf[*:[_, _]] ~~> scala.runtime.Tuple.isInstanceOfNonEmptyTuple(expr)
*
* The transform happens before erasure of `testType`, thus cannot be merged
* with `transformIsInstanceOf`, which depends on erased type of `testType`.
*/
def transformTypeTest(expr: Tree, testType: Type, flagUnrelated: Boolean): Tree = testType.dealias match {
case tref: TermRef if tref.symbol == defn.EmptyTupleModule =>
ref(defn.RuntimeTuple_isInstanceOfEmptyTuple).appliedTo(expr)
case _: SingletonType =>
expr.isInstance(testType).withSpan(tree.span)
case OrType(tp1, tp2) =>
Expand All @@ -330,6 +336,12 @@ object TypeTestsCasts {
derivedTree(e, defn.Any_isInstanceOf, e.tpe)
.and(isArrayTest(e))
}
case tref: TypeRef if tref.symbol == defn.TupleClass =>
ref(defn.RuntimeTuple_isInstanceOfTuple).appliedTo(expr)
case tref: TypeRef if tref.symbol == defn.NonEmptyTupleClass =>
ref(defn.RuntimeTuple_isInstanceOfNonEmptyTuple).appliedTo(expr)
case AppliedType(tref: TypeRef, _) if tref.symbol == defn.PairClass =>
ref(defn.RuntimeTuple_isInstanceOfNonEmptyTuple).appliedTo(expr)
case _ =>
transformIsInstanceOf(expr, erasure(testType), flagUnrelated)
}
Expand Down
24 changes: 14 additions & 10 deletions compiler/src/dotty/tools/dotc/transform/TypeUtils.scala
Original file line number Diff line number Diff line change
Expand Up @@ -31,27 +31,31 @@ object TypeUtils {
case ps => ps.reduceLeft(AndType(_, _))
}

/** The arity of this tuple type, which can be made up of Unit, TupleX and `*:` pairs,
/** The arity of this tuple type, which can be made up of EmptyTuple, TupleX and `*:` pairs,
* or -1 if this is not a tuple type.
*/
def tupleArity(implicit ctx: Context): Int = self match {
case AppliedType(tycon, _ :: tl :: Nil) if tycon.isRef(defn.PairClass) =>
val arity = tl.tupleArity
if (arity < 0) arity else arity + 1
case tp1 =>
if (tp1.isRef(defn.UnitClass)) 0
else if (defn.isTupleClass(tp1.classSymbol)) tp1.dealias.argInfos.length
else -1
case self: TermRef if self.symbol == defn.EmptyTupleModule =>
0
case self if defn.isTupleClass(self.classSymbol) =>
self.dealias.argInfos.length
case _ =>
-1
}

/** The element types of this tuple type, which can be made up of Unit, TupleX and `*:` pairs */
/** The element types of this tuple type, which can be made up of EmptyTuple, TupleX and `*:` pairs */
def tupleElementTypes(implicit ctx: Context): List[Type] = self match {
case AppliedType(tycon, hd :: tl :: Nil) if tycon.isRef(defn.PairClass) =>
hd :: tl.tupleElementTypes
case tp1 =>
if (tp1.isRef(defn.UnitClass)) Nil
else if (defn.isTupleClass(tp1.classSymbol)) tp1.dealias.argInfos
else throw new AssertionError("not a tuple")
case self: TermRef if self.symbol == defn.EmptyTupleModule =>
Nil
case self if defn.isTupleClass(self.classSymbol) =>
self.dealias.argInfos
case _ =>
throw new AssertionError("not a tuple")
}

/** The `*:` equivalent of an instance of a Tuple class */
Expand Down
4 changes: 3 additions & 1 deletion compiler/src/dotty/tools/dotc/typer/QuotesAndSplices.scala
Original file line number Diff line number Diff line change
Expand Up @@ -432,7 +432,9 @@ trait QuotesAndSplices {
}
}

val splicePat = typed(untpd.Tuple(splices.map(x => untpd.TypedSplice(replaceBindingsInTree.transform(x)))).withSpan(quoted.span), patType)
val splicePat =
if splices.isEmpty then ref(defn.EmptyTupleModule.termRef)
else typed(untpd.Tuple(splices.map(x => untpd.TypedSplice(replaceBindingsInTree.transform(x)))).withSpan(quoted.span), patType)

val unapplySym = if (tree.quoted.isTerm) defn.InternalQuotedExpr_unapply else defn.InternalQuotedType_unapply
val quoteClass = if (tree.quoted.isTerm) defn.QuotedExprClass else defn.QuotedTypeClass
Expand Down
2 changes: 1 addition & 1 deletion compiler/src/dotty/tools/dotc/typer/Typer.scala
Original file line number Diff line number Diff line change
Expand Up @@ -2309,7 +2309,7 @@ class Typer extends Namer
else List.fill(arity)(defn.AnyType)
val elems = tree.trees.lazyZip(pts).map(typed(_, _))
if (ctx.mode.is(Mode.Type))
elems.foldRight(TypeTree(defn.UnitType): Tree)((elemTpt, elemTpts) =>
elems.foldRight(TypeTree(defn.EmptyTupleModule.termRef): Tree)((elemTpt, elemTpts) =>
AppliedTypeTree(TypeTree(defn.PairClass.typeRef), List(elemTpt, elemTpts)))
.withSpan(tree.span)
else {
Expand Down
Loading