Skip to content
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
1 change: 1 addition & 0 deletions compiler/src/dotty/tools/dotc/core/Definitions.scala
Original file line number Diff line number Diff line change
Expand Up @@ -1061,6 +1061,7 @@ class Definitions {
@tu lazy val ScalaStaticAnnot: ClassSymbol = requiredClass("scala.annotation.static")
@tu lazy val SerialVersionUIDAnnot: ClassSymbol = requiredClass("scala.SerialVersionUID")
@tu lazy val SilentIntoAnnot: ClassSymbol = requiredClass("scala.annotation.internal.$into")
@tu lazy val SilentSubMatchAnnot: ClassSymbol = requiredClass("scala.annotation.internal.$subMatch")
@tu lazy val TailrecAnnot: ClassSymbol = requiredClass("scala.annotation.tailrec")
@tu lazy val ThreadUnsafeAnnot: ClassSymbol = requiredClass("scala.annotation.threadUnsafe")
@tu lazy val ConstructorOnlyAnnot: ClassSymbol = requiredClass("scala.annotation.constructorOnly")
Expand Down
5 changes: 4 additions & 1 deletion compiler/src/dotty/tools/dotc/core/tasty/TreePickler.scala
Original file line number Diff line number Diff line change
Expand Up @@ -590,7 +590,10 @@ class TreePickler(pickler: TastyPickler, attributes: Attributes) {
if (tree.isInline)
if (selector.isEmpty) writeByte(IMPLICIT)
else { writeByte(INLINE); pickleTree(selector) }
else if tree.isSubMatch then { writeByte(LAZY); pickleTree(selector) }
else if tree.isSubMatch then
// Temporary measure until we can change TastyFormat
val annot = New(defn.SilentSubMatchAnnot.typeRef, Nil)
pickleTree(selector.annotated(annot))
else pickleTree(selector)
tree.cases.foreach(pickleTree)
}
Expand Down
9 changes: 4 additions & 5 deletions compiler/src/dotty/tools/dotc/core/tasty/TreeUnpickler.scala
Original file line number Diff line number Diff line change
Expand Up @@ -1542,7 +1542,7 @@ class TreeUnpickler(reader: TastyReader,
val tpt = ifBefore(end)(readTpt(), EmptyTree)
Closure(Nil, meth, tpt)
case MATCH =>
simplifyLub(
simplifyLub:
if (nextByte == IMPLICIT) {
readByte()
InlineMatch(EmptyTree, readCases(end))
Expand All @@ -1551,10 +1551,9 @@ class TreeUnpickler(reader: TastyReader,
readByte()
InlineMatch(readTree(), readCases(end))
}
else if nextByte == LAZY then // similarly to InlineMatch we use an arbitrary Cat.1 tag
readByte()
SubMatch(readTree(), readCases(end))
else Match(readTree(), readCases(end)))
else readTree() match
case Typed(sel, tpt) if tpt.tpe.hasAnnotation(defn.SilentSubMatchAnnot) => SubMatch(sel, readCases(end))
case sel => Match(sel, readCases(end))
case RETURN =>
val from = readSymRef()
val expr = ifBefore(end)(readTree(), EmptyTree)
Expand Down
1 change: 1 addition & 0 deletions compiler/src/dotty/tools/dotc/parsing/Parsers.scala
Original file line number Diff line number Diff line change
Expand Up @@ -3171,6 +3171,7 @@ object Parsers {
val t = inSepRegion(InCase)(postfixExpr(Location.InGuard))
t.asSubMatch
case other =>
// the guard is reinterpreted as a sub-match when there is no leading IF or ARROW token
val t = grd1.asSubMatch
grd1 = EmptyTree
t
Expand Down
21 changes: 13 additions & 8 deletions docs/_docs/reference/experimental/sub-cases.md
Original file line number Diff line number Diff line change
Expand Up @@ -36,11 +36,6 @@ e.g., `case Some(x) => x.version match ...`.
If none of the sub-cases succeed, then control flow returns to the outer match expression and proceeds as though the current case had not matched.
For example, `Some(Document("...", Version.Stable(2, 1)))` matches the first pattern, but none of its sub-cases, and we therefore obtain the result `"unsupported"`.

More generally, sub-matches also allow:
- Arbitrary nesting, e.g. sub-sub-matches are supported.
- Interleaved boolean guards, e.g. `case Some(x: Int) if x != 0 if x match ...`.
- Interleaving pattern extractors and computations for the scrutinees of sub-matches.


## Motivation

Expand All @@ -62,11 +57,21 @@ def version(d: Option[Document]) = d match
case _ => "unsupported"
```

## Details

Sub-cases allow:
- Arbitrary nesting, e.g. sub-sub-matches are supported.
- Interleaving boolean guards, e.g. `case Some(x: Int) if x != 0 if x match ...`.
- Interleaving pattern extractors and computations for the scrutinees of sub-matches.

Sub-cases are supported for:
- match clauses
- catch clauses
- partial functions

Similarly to catch clauses, match expressions with a single case can now be written on single line (without braces),
e.g., `Some(1) match case Some(x) => x`.

Exhaustivity and reachability checking conservatively assume the sub-cases to be partial, similarly boolean guards.




A sub-match is inlined iff the outer match is inlined, with the same semantics as the usual match expressions.
7 changes: 7 additions & 0 deletions library/src/scala/annotation/internal/$subMatch.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
package scala.annotation.internal

import scala.annotation.experimental

/** An internal annotation on (part of) that serves as a marker for sub matches. */
@experimental
class $subMatch extends annotation.StaticAnnotation
2 changes: 2 additions & 0 deletions project/Build.scala
Original file line number Diff line number Diff line change
Expand Up @@ -1201,6 +1201,7 @@ object Build {
file(s"${baseDirectory.value}/src/scala/annotation/internal/SourceFile.scala"),
file(s"${baseDirectory.value}/src/scala/annotation/internal/reachCapability.scala"),
file(s"${baseDirectory.value}/src/scala/annotation/internal/$$into.scala"),
file(s"${baseDirectory.value}/src/scala/annotation/internal/$$subMatch.scala"),
file(s"${baseDirectory.value}/src/scala/annotation/internal/TASTYLongSignature.java"),
file(s"${baseDirectory.value}/src/scala/annotation/internal/readOnlyCapability.scala"),
file(s"${baseDirectory.value}/src/scala/annotation/internal/unshared.scala"),
Expand Down Expand Up @@ -1339,6 +1340,7 @@ object Build {
file(s"${baseDirectory.value}/src/scala/annotation/internal/SourceFile.scala"),
file(s"${baseDirectory.value}/src/scala/annotation/internal/reachCapability.scala"),
file(s"${baseDirectory.value}/src/scala/annotation/internal/$$into.scala"),
file(s"${baseDirectory.value}/src/scala/annotation/internal/$$subMatch.scala"),
file(s"${baseDirectory.value}/src/scala/annotation/internal/TASTYLongSignature.java"),
file(s"${baseDirectory.value}/src/scala/annotation/internal/readOnlyCapability.scala"),
file(s"${baseDirectory.value}/src/scala/annotation/internal/unshared.scala"),
Expand Down
Loading