Skip to content
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
2 changes: 1 addition & 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,7 @@ 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 { writeByte(SUBMATCH); pickleTree(selector) }
else pickleTree(selector)
tree.cases.foreach(pickleTree)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1551,7 +1551,7 @@ class TreeUnpickler(reader: TastyReader,
readByte()
InlineMatch(readTree(), readCases(end))
}
else if nextByte == LAZY then // similarly to InlineMatch we use an arbitrary Cat.1 tag
else if nextByte == SUBMATCH then
readByte()
SubMatch(readTree(), readCases(end))
else Match(readTree(), readCases(end)))
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.
3 changes: 2 additions & 1 deletion tasty/src/dotty/tools/tasty/TastyFormat.scala
Original file line number Diff line number Diff line change
Expand Up @@ -104,7 +104,7 @@ Standard-Section: "ASTs" TopLevelStat*
INLINED Length expr_Term call_Term? ValOrDefDef* -- Inlined code from call, with given body `expr` and given bindings
LAMBDA Length meth_Term target_Type? -- Closure over method `f` of type `target` (omitted id `target` is a function type)
IF Length [INLINE] cond_Term then_Term else_Term -- inline? if cond then thenPart else elsePart
MATCH Length (IMPLICIT | [INLINE] sel_Term) CaseDef* -- (inline? sel | implicit) match caseDefs
MATCH Length (IMPLICIT | [INLINE | SUBMATCH] sel_Term) CaseDef* -- ((inline | if)? sel | implicit) match caseDefs
TRY Length expr_Term CaseDef* finalizer_Term? -- try expr catch {casdeDef} (finally finalizer)?
RETURN Length meth_ASTRef expr_Term? -- return expr?, `methASTRef` is method from which is returned
WHILE Length cond_Term body_Term -- while cond do body
Expand Down Expand Up @@ -511,6 +511,7 @@ object TastyFormat {
final val EMPTYCLAUSE = 45
final val SPLITCLAUSE = 46
final val TRACKED = 47
final val SUBMATCH = 48 // experimental.subCases

// Tree Cat. 2: tag Nat
final val firstNatTreeTag = SHAREDterm
Expand Down
Loading