Skip to content

Commit e96c20f

Browse files
committed
Support transparent matches with simple typed patterns
More work on pattern matching still to be done.
1 parent a125c67 commit e96c20f

14 files changed

+325
-76
lines changed

compiler/src/dotty/tools/dotc/config/Config.scala

+1-1
Original file line numberDiff line numberDiff line change
@@ -160,7 +160,7 @@ object Config {
160160
final val showCompletions = false
161161

162162
/** If set, enables tracing */
163-
final val tracingEnabled = false
163+
final val tracingEnabled = true
164164

165165
/** Initial capacity of uniques HashMap.
166166
* Note: This MUST BE a power of two to work with util.HashSet

compiler/src/dotty/tools/dotc/core/NameKinds.scala

+19-17
Original file line numberDiff line numberDiff line change
@@ -275,23 +275,25 @@ object NameKinds {
275275
}
276276

277277
/** Other unique names */
278-
val TempResultName = new UniqueNameKind("ev$")
279-
val EvidenceParamName = new UniqueNameKind("evidence$")
280-
val DepParamName = new UniqueNameKind("(param)")
281-
val LazyImplicitName = new UniqueNameKind("$_lazy_implicit_$")
282-
val LazyLocalName = new UniqueNameKind("$lzy")
283-
val LazyLocalInitName = new UniqueNameKind("$lzyINIT")
284-
val LazyFieldOffsetName = new UniqueNameKind("$OFFSET")
285-
val LazyBitMapName = new UniqueNameKind(nme.BITMAP_PREFIX.toString)
286-
val NonLocalReturnKeyName = new UniqueNameKind("nonLocalReturnKey")
287-
val WildcardParamName = new UniqueNameKind("_$")
288-
val TailLabelName = new UniqueNameKind("tailLabel")
289-
val ExceptionBinderName = new UniqueNameKind("ex")
290-
val SkolemName = new UniqueNameKind("?")
291-
val LiftedTreeName = new UniqueNameKind("liftedTree")
292-
val SuperArgName = new UniqueNameKind("$superArg$")
293-
val DocArtifactName = new UniqueNameKind("$doc")
294-
val UniqueInlineName = new UniqueNameKind("$i")
278+
val TempResultName = new UniqueNameKind("ev$")
279+
val EvidenceParamName = new UniqueNameKind("evidence$")
280+
val DepParamName = new UniqueNameKind("(param)")
281+
val LazyImplicitName = new UniqueNameKind("$_lazy_implicit_$")
282+
val LazyLocalName = new UniqueNameKind("$lzy")
283+
val LazyLocalInitName = new UniqueNameKind("$lzyINIT")
284+
val LazyFieldOffsetName = new UniqueNameKind("$OFFSET")
285+
val LazyBitMapName = new UniqueNameKind(nme.BITMAP_PREFIX.toString)
286+
val NonLocalReturnKeyName = new UniqueNameKind("nonLocalReturnKey")
287+
val WildcardParamName = new UniqueNameKind("_$")
288+
val TailLabelName = new UniqueNameKind("tailLabel")
289+
val ExceptionBinderName = new UniqueNameKind("ex")
290+
val SkolemName = new UniqueNameKind("?")
291+
val LiftedTreeName = new UniqueNameKind("liftedTree")
292+
val SuperArgName = new UniqueNameKind("$superArg$")
293+
val DocArtifactName = new UniqueNameKind("$doc")
294+
val UniqueInlineName = new UniqueNameKind("$i")
295+
val TransparentScrutineeName = new UniqueNameKind("$scrutinee")
296+
val TransparentBinderName = new UniqueNameKind("$elem")
295297

296298
/** A kind of unique extension methods; Unlike other unique names, these can be
297299
* unmangled.

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

+2
Original file line numberDiff line numberDiff line change
@@ -185,6 +185,8 @@ object Inferencing {
185185
*
186186
* Invariant refinement can be assumed if `PatternType`'s class(es) are final or
187187
* case classes (because of `RefChecks#checkCaseClassInheritanceInvariant`).
188+
*
189+
* TODO: Update so that GADT symbols can be variant, and we special case final class types in patterns
188190
*/
189191
def constrainPatternType(tp: Type, pt: Type)(implicit ctx: Context): Boolean = {
190192
def refinementIsInvariant(tp: Type): Boolean = tp match {

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

+185-36
Large diffs are not rendered by default.

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

+1
Original file line numberDiff line numberDiff line change
@@ -290,6 +290,7 @@ object PrepareTransparent {
290290

291291
def traverse(tree: Tree)(implicit ctx: Context): Unit = {
292292
tree match {
293+
case Ident(nme.WILDCARD) =>
293294
case _: Ident | _: This =>
294295
//println(i"leaf: $tree at ${tree.pos}")
295296
if (tree.symbol.exists && !isLocal(tree.symbol, inlineMethod)) {

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

+10-6
Original file line numberDiff line numberDiff line change
@@ -966,7 +966,7 @@ class Typer extends Namer
966966
assignType(cpy.Closure(tree)(env1, meth1, target), meth1, target)
967967
}
968968

969-
def typedMatch(tree: untpd.Match, pt: Type)(implicit ctx: Context) = track("typedMatch") {
969+
def typedMatch(tree: untpd.Match, pt: Type)(implicit ctx: Context): Tree = track("typedMatch") {
970970
tree.selector match {
971971
case EmptyTree =>
972972
val (protoFormals, _) = decomposeProtoFunction(pt, 1)
@@ -975,13 +975,17 @@ class Typer extends Namer
975975
case _ =>
976976
val sel1 = typedExpr(tree.selector)
977977
val selType = fullyDefinedType(sel1.tpe, "pattern selector", tree.pos).widen
978-
979-
val cases1 = harmonic(harmonize)(typedCases(tree.cases, selType, pt.notApplied))
980-
.asInstanceOf[List[CaseDef]]
981-
assignType(cpy.Match(tree)(sel1, cases1), cases1)
978+
typedMatchFinish(tree, sel1, selType, pt)
982979
}
983980
}
984981

982+
// Overridden in InlineTyper for transparent matches
983+
def typedMatchFinish(tree: untpd.Match, sel: Tree, selType: Type, pt: Type)(implicit ctx: Context): Tree = {
984+
val cases1 = harmonic(harmonize)(typedCases(tree.cases, selType, pt.notApplied))
985+
.asInstanceOf[List[CaseDef]]
986+
assignType(cpy.Match(tree)(sel, cases1), cases1)
987+
}
988+
985989
def typedCases(cases: List[untpd.CaseDef], selType: Type, pt: Type)(implicit ctx: Context) = {
986990

987991
/** gadtSyms = "all type parameters of enclosing methods that appear
@@ -1004,7 +1008,7 @@ class Typer extends Namer
10041008
accu(Set.empty, selType)
10051009
}
10061010

1007-
cases mapconserve (typedCase(_, pt, selType, gadtSyms))
1011+
cases.mapconserve(typedCase(_, pt, selType, gadtSyms))
10081012
}
10091013

10101014
/** Type a case. */

library/src/dotty/DottyPredef.scala

+4-2
Original file line numberDiff line numberDiff line change
@@ -34,8 +34,10 @@ object DottyPredef {
3434
assertFail()
3535
}
3636

37-
final def assertFail(): Unit = throw new java.lang.AssertionError("assertion failed")
38-
final def assertFail(message: => Any): Unit = throw new java.lang.AssertionError("assertion failed: " + message)
37+
def assertFail(): Unit = throw new java.lang.AssertionError("assertion failed")
38+
def assertFail(message: => Any): Unit = throw new java.lang.AssertionError("assertion failed: " + message)
3939

4040
@forceInline final def implicitly[T](implicit ev: T): T = ev
41+
42+
@forceInline def locally[T](body: => T): T = body
4143
}
+11
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
2+
object Test {
3+
def anyValue[T]: T = ???
4+
5+
transparent def test[T] = anyValue[T] match { // error
6+
case _: Byte =>
7+
case _: Char =>
8+
}
9+
10+
test[String]
11+
}

tests/neg/typelevel.scala

+9
Original file line numberDiff line numberDiff line change
@@ -55,4 +55,13 @@ object Test {
5555
class Deco2(val as: HList) extends java.lang.Cloneable with java.lang.Comparable[Deco2] {
5656
transparent def ++ (bs: HList) = concat(as, bs)
5757
}
58+
59+
def anyValue[T]: T = ???
60+
61+
transparent def test[T] = anyValue[T] match {
62+
case _: Byte =>
63+
case _: Char =>
64+
}
65+
66+
test[String]
5867
}

tests/pos/i3050.scala

+30-2
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,39 @@
11
object Test {
2+
trait Option[+T]
3+
case object None extends Option[Nothing]
4+
case class Some[+T](x: T) extends Option[T]
5+
26
transparent def openImpl(): Int =
37
Some(42) match { case Some(i) => i }
48

5-
def open(): Int = openImpl()
9+
def open() = openImpl()
10+
11+
transparent def openImpl1(): Int =
12+
new Some(42) match { case Some(i) => i }
13+
14+
def open1() = openImpl1()
615

716
transparent def openImpl2(): Int =
8-
Some(42) match { case None => 42 }
17+
None match { case None => 42 }
918

1019
def open2(): Int = openImpl2()
1120
}
21+
22+
// Same as Test, with Scala2 case classes
23+
object Test2 {
24+
transparent def openImpl(): Int =
25+
Some(42) match { case Some(i) => i }
26+
27+
def open() = openImpl()
28+
29+
transparent def openImpl1(): Int =
30+
new Some(42) match { case Some(i) => i }
31+
32+
def open1() = openImpl1()
33+
34+
transparent def openImpl2(): Int =
35+
None match { case None => 42 }
36+
37+
def open2(): Int = openImpl2()
38+
39+
}
File renamed without changes.

tests/run/lst/Lst.scala

+16-12
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,8 @@ import reflect.ClassTag
1818
class Lst[+T](val elems: Any) extends AnyVal { self =>
1919
import Lst._
2020

21+
transparent def locally[T](body: => T): T = body
22+
2123
def length: Int = elems match {
2224
case null => 0
2325
case elems: Arr => elems.length
@@ -27,7 +29,7 @@ class Lst[+T](val elems: Any) extends AnyVal { self =>
2729
def isEmpty = elems == null
2830
def nonEmpty = elems != null
2931

30-
transparent def foreach(op: => T => Unit): Unit = {
32+
transparent def foreach(op: => T => Unit): Unit = locally {
3133
def sharedOp(x: T) = op(x)
3234
elems match {
3335
case null =>
@@ -38,7 +40,7 @@ class Lst[+T](val elems: Any) extends AnyVal { self =>
3840
}
3941
}
4042

41-
transparent def foreachReversed(transparent op: T => Unit): Unit = {
43+
transparent def foreachReversed(transparent op: T => Unit): Unit = locally {
4244
def sharedOp(x: T) = op(x)
4345
elems match {
4446
case null =>
@@ -52,12 +54,14 @@ class Lst[+T](val elems: Any) extends AnyVal { self =>
5254
/** Like `foreach`, but completely inlines `op`, at the price of generating the code twice.
5355
* Should be used only of `op` is small
5456
*/
55-
transparent def foreachInlined(op: => T => Unit): Unit = elems match {
56-
case null =>
57-
case elems: Arr => def elem(i: Int) = elems(i).asInstanceOf[T]
58-
var i = 0
59-
while (i < elems.length) { op(elem(i)); i += 1 }
60-
case elem: T @unchecked => op(elem)
57+
transparent def foreachInlined(op: => T => Unit): Unit = locally {
58+
elems match {
59+
case null =>
60+
case elems: Arr => def elem(i: Int) = elems(i).asInstanceOf[T]
61+
var i = 0
62+
while (i < elems.length) { op(elem(i)); i += 1 }
63+
case elem: T @unchecked => op(elem)
64+
}
6165
}
6266

6367
def iterator(): Iterator[T] = elems match {
@@ -101,7 +105,7 @@ class Lst[+T](val elems: Any) extends AnyVal { self =>
101105
}
102106

103107
/** `f` is pulled out, not duplicated */
104-
transparent def map[U](f: => T => U): Lst[U] = {
108+
transparent def map[U](f: => T => U): Lst[U] = locally {
105109
def op(x: T) = f(x)
106110
elems match {
107111
case null => Empty
@@ -193,7 +197,7 @@ class Lst[+T](val elems: Any) extends AnyVal { self =>
193197
buf.toLst
194198
}
195199

196-
transparent def exists(p: => T => Boolean): Boolean = {
200+
transparent def exists(p: => T => Boolean): Boolean = locally {
197201
def op(x: T) = p(x)
198202
elems match {
199203
case null => false
@@ -206,7 +210,7 @@ class Lst[+T](val elems: Any) extends AnyVal { self =>
206210
}
207211
}
208212

209-
transparent def forall(p: => T => Boolean): Boolean = {
213+
transparent def forall(p: => T => Boolean): Boolean = locally {
210214
def op(x: T) = p(x)
211215
elems match {
212216
case null => true
@@ -229,7 +233,7 @@ class Lst[+T](val elems: Any) extends AnyVal { self =>
229233
elem == x
230234
}
231235

232-
transparent def foldLeft[U](z: U)(f: => (U, T) => U) = {
236+
transparent def foldLeft[U](z: U)(f: => (U, T) => U) = locally {
233237
def op(x: U, y: T) = f(x, y)
234238
elems match {
235239
case null => z
48 Bytes
Binary file not shown.
+37
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
2+
object Test extends App {
3+
def anyValue[T]: T = ???
4+
5+
transparent def defaultValue[T]: Option[Any] = anyValue[T] match {
6+
case _: Byte => Some(0: Byte)
7+
case c: Char => Some(0: Char)
8+
case d @ (_: Short) => Some(0: Short)
9+
case _: Int => Some(0)
10+
case _: Long => Some(0L)
11+
case _: Float => Some(0.0f)
12+
case _: Double => Some(0.0d)
13+
case _: Boolean => Some(false)
14+
case _: Unit => Some(())
15+
//case _: t >: Null => Some(null)
16+
case _ => None
17+
}
18+
19+
val dInt = defaultValue[Int]
20+
val dDouble = defaultValue[Double]
21+
val dBoolean = defaultValue[Boolean]
22+
val dChar = defaultValue[Char]
23+
val dString = defaultValue[String]
24+
val dAny = defaultValue[Any]
25+
println(dInt)
26+
println(dDouble)
27+
println(dBoolean)
28+
println(dChar)
29+
println(dString)
30+
println(dAny)
31+
val cInt: Int = dInt.get
32+
val cDouble: Double = dDouble.get
33+
val cBoolean: Boolean = dBoolean.get
34+
val cChar: Char = dChar.get
35+
assert(dString.isEmpty)
36+
assert(dAny.isEmpty)
37+
}

0 commit comments

Comments
 (0)