Skip to content

Commit ed9d001

Browse files
committed
Normalize when constant folding
It can happen that what is really a constant type is still in the form of an unreduced MatchType. We need to normalize the type to reveal the constant. The new version typeclass-derivation2.scala test needs this fix to compile without errors.
1 parent 574f4bb commit ed9d001

File tree

3 files changed

+71
-17
lines changed

3 files changed

+71
-17
lines changed

compiler/src/dotty/tools/dotc/typer/ConstFold.scala

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ object ConstFold {
2020
def apply(tree: Tree)(implicit ctx: Context): Tree = finish(tree) {
2121
tree match {
2222
case Apply(Select(xt, op), yt :: Nil) =>
23-
xt.tpe.widenTermRefExpr match {
23+
xt.tpe.widenTermRefExpr.normalized match {
2424
case ConstantType(x) =>
2525
yt.tpe.widenTermRefExpr match {
2626
case ConstantType(y) => foldBinop(op, x, y)
@@ -42,7 +42,7 @@ object ConstFold {
4242
*/
4343
def apply(tree: Tree, pt: Type)(implicit ctx: Context): Tree =
4444
finish(apply(tree)) {
45-
tree.tpe.widenTermRefExpr match {
45+
tree.tpe.widenTermRefExpr.normalized match {
4646
case ConstantType(x) => x convertTo pt
4747
case _ => null
4848
}

compiler/src/dotty/tools/dotc/typer/Inliner.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -606,7 +606,7 @@ class Inliner(call: tpd.Tree, rhsToInline: tpd.Tree)(implicit ctx: Context) {
606606
}
607607

608608
object ConstantValue {
609-
def unapply(tree: Tree)(implicit ctx: Context): Option[Any] = tree.tpe.widenTermRefExpr match {
609+
def unapply(tree: Tree)(implicit ctx: Context): Option[Any] = tree.tpe.widenTermRefExpr.normalized match {
610610
case ConstantType(Constant(x)) => Some(x)
611611
case _ => None
612612
}

tests/run/typeclass-derivation2.scala

Lines changed: 68 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -3,18 +3,65 @@ import scala.collection.mutable
33
// Generic deriving infrastructure
44
object Deriving {
55

6+
/** The shape of an ADT in a sum of products representation */
67
enum Shape {
8+
9+
/** A sum with alternative types `Alts` */
710
case Cases[Alts <: Tuple]
11+
12+
/** A product type `T` with element types `Elems` */
813
case Case[T, Elems <: Tuple]
914
}
1015

11-
case class GenericCase[+T](ordinal: Int, elems: Array[Object])
16+
/** A generic representation of a case in an ADT
17+
* @param ordinal The ordinal value of the case in the list of the ADT's cases
18+
* @param elems The elements of the case
19+
*/
20+
class GenericCase[+T](val ordinal: Int, val elems: Product) {
21+
22+
/** A generic case with elements given as an array */
23+
def this(ordinal: Int, elems: Array[AnyRef]) =
24+
this(ordinal, new ArrayProduct(elems))
25+
26+
/** A generic case with an initial empty array of `numElems` elements, to be filled in. */
27+
def this(ordinal: Int, numElems: Int) =
28+
this(ordinal, new Array[AnyRef](numElems))
29+
30+
/** A generic case with no elements */
31+
def this(ordinal: Int) =
32+
this(ordinal, EmptyProduct)
33+
34+
/** The `n`'th element of this generic case */
35+
def apply(n: Int): Any = elems.productElement(n)
36+
}
37+
38+
/** Helper class to turn arrays into products */
39+
private class ArrayProduct(val elems: Array[AnyRef]) extends Product {
40+
def canEqual(that: Any): Boolean = true
41+
def productElement(n: Int) = elems(n)
42+
def productArity = elems.length
43+
override def productIterator: Iterator[Any] = elems.iterator
44+
def update(n: Int, x: Any) = elems(n) = x.asInstanceOf[AnyRef]
45+
}
46+
47+
/** Helper object */
48+
private object EmptyProduct extends Product {
49+
def canEqual(that: Any): Boolean = true
50+
def productElement(n: Int) = throw new IndexOutOfBoundsException
51+
def productArity = 0
52+
}
1253

54+
/** A class for mapping between an ADT value and
55+
* the generic case that represents the value
56+
*/
1357
abstract class GenericMapper[T] {
1458
def toGenericCase(x: T): GenericCase[T]
1559
def fromGenericCase(c: GenericCase[T]): T
1660
}
1761

62+
/** Every generic derivation starts with a typeclass instance of this type.
63+
* It informs that type `T` has shape `S` and is backed by the extended generic mapper.
64+
*/
1865
abstract class HasShape[T, S <: Shape] extends GenericMapper[T]
1966
}
2067

@@ -34,13 +81,15 @@ object Lst {
3481
Shape.Case[Nil.type, Unit]
3582
)]
3683

84+
val NilCase = new GenericCase[Nil.type](1)
85+
3786
implicit def lstShape[T]: HasShape[Lst[T], LstShape[T]] = new {
3887
def toGenericCase(xs: Lst[T]): GenericCase[Lst[T]] = xs match {
39-
case Cons(x, xs1) => GenericCase[Cons[T]](0, Array(x.asInstanceOf, xs1))
40-
case Nil => GenericCase[Nil.type](1, Array())
41-
}
88+
case xs: Cons[T] => new GenericCase[Cons[T]](0, xs)
89+
case Nil => NilCase
90+
}
4291
def fromGenericCase(c: GenericCase[Lst[T]]): Lst[T] = c.ordinal match {
43-
case 0 => Cons[T](c.elems(0).asInstanceOf, c.elems(1).asInstanceOf)
92+
case 0 => Cons[T](c(0).asInstanceOf, c(1).asInstanceOf)
4493
case 1 => Nil
4594
}
4695
}
@@ -61,9 +110,9 @@ object Pair {
61110

62111
implicit def pairShape[T]: HasShape[Pair[T], PairShape[T]] = new {
63112
def toGenericCase(xy: Pair[T]) =
64-
GenericCase[Pair[T]](0, Array(xy._1.asInstanceOf, xy._2.asInstanceOf))
113+
new GenericCase[Pair[T]](0, xy)
65114
def fromGenericCase(c: GenericCase[Pair[T]]): Pair[T] =
66-
Pair(c.elems(0).asInstanceOf, c.elems(1).asInstanceOf)
115+
Pair(c(0).asInstanceOf, c(1).asInstanceOf)
67116
}
68117

69118
// two clauses that could be generated from a `derives` clause
@@ -84,7 +133,7 @@ object Eq {
84133
case eq: Eq[T] => eq.eql(x, y)
85134
}
86135

87-
inline def eqlElems[Elems <: Tuple](xs: Array[Object], ys: Array[Object], n: Int): Boolean =
136+
inline def eqlElems[Elems <: Tuple](xs: GenericCase[_], ys: GenericCase[_], n: Int): Boolean =
88137
inline erasedValue[Elems] match {
89138
case _: (elem *: elems1) =>
90139
tryEql[elem](xs(n).asInstanceOf, ys(n).asInstanceOf) &&
@@ -94,7 +143,7 @@ object Eq {
94143
}
95144

96145
inline def eqlCase[T, Elems <: Tuple](mapper: GenericMapper[T], x: T, y: T) =
97-
eqlElems[Elems](mapper.toGenericCase(x).elems, mapper.toGenericCase(y).elems, 0)
146+
eqlElems[Elems](mapper.toGenericCase(x), mapper.toGenericCase(y), 0)
98147

99148
inline def eqlCases[T, Alts <: Tuple](mapper: GenericMapper[T], x: T, y: T): Boolean =
100149
inline erasedValue[Alts] match {
@@ -141,7 +190,7 @@ object Pickler {
141190
case pkl: Pickler[T] => pkl.pickle(buf, x)
142191
}
143192

144-
inline def pickleElems[Elems <: Tuple](buf: mutable.ListBuffer[Int], elems: Array[AnyRef], n: Int): Unit =
193+
inline def pickleElems[Elems <: Tuple](buf: mutable.ListBuffer[Int], elems: GenericCase[_], n: Int): Unit =
145194
inline erasedValue[Elems] match {
146195
case _: (elem *: elems1) =>
147196
tryPickle[elem](buf, elems(n).asInstanceOf[elem])
@@ -150,7 +199,7 @@ object Pickler {
150199
}
151200

152201
inline def pickleCase[T, Elems <: Tuple](mapper: GenericMapper[T], buf: mutable.ListBuffer[Int], x: T): Unit =
153-
pickleElems[Elems](buf, mapper.toGenericCase(x).elems, 0)
202+
pickleElems[Elems](buf, mapper.toGenericCase(x), 0)
154203

155204
inline def pickleCases[T, Alts <: Tuple](mapper: GenericMapper[T], buf: mutable.ListBuffer[Int], x: T, n: Int): Unit =
156205
inline erasedValue[Alts] match {
@@ -178,9 +227,14 @@ object Pickler {
178227
}
179228

180229
inline def unpickleCase[T, Elems <: Tuple](mapper: GenericMapper[T], buf: mutable.ListBuffer[Int], ordinal: Int): T = {
181-
val elems = new Array[Object](constValue[Tuple.Size[Elems]])
182-
unpickleElems[Elems](buf, elems, 0)
183-
mapper.fromGenericCase(GenericCase(ordinal, elems))
230+
inline val size = constValue[Tuple.Size[Elems]]
231+
inline if (size == 0)
232+
mapper.fromGenericCase(new GenericCase[T](ordinal))
233+
else {
234+
val elems = new Array[Object](size)
235+
unpickleElems[Elems](buf, elems, 0)
236+
mapper.fromGenericCase(new GenericCase[T](ordinal, elems))
237+
}
184238
}
185239

186240
inline def unpickleCases[T, Alts <: Tuple](mapper: GenericMapper[T], buf: mutable.ListBuffer[Int], ordinal: Int, n: Int): T =

0 commit comments

Comments
 (0)