diff --git a/compiler/src/dotty/tools/dotc/core/tasty/TreePickler.scala b/compiler/src/dotty/tools/dotc/core/tasty/TreePickler.scala index 20a405271078..9e8567f57ddc 100644 --- a/compiler/src/dotty/tools/dotc/core/tasty/TreePickler.scala +++ b/compiler/src/dotty/tools/dotc/core/tasty/TreePickler.scala @@ -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) } diff --git a/compiler/src/dotty/tools/dotc/core/tasty/TreeUnpickler.scala b/compiler/src/dotty/tools/dotc/core/tasty/TreeUnpickler.scala index 7ed71dde2455..95c9731a0679 100644 --- a/compiler/src/dotty/tools/dotc/core/tasty/TreeUnpickler.scala +++ b/compiler/src/dotty/tools/dotc/core/tasty/TreeUnpickler.scala @@ -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))) diff --git a/compiler/src/dotty/tools/dotc/parsing/Parsers.scala b/compiler/src/dotty/tools/dotc/parsing/Parsers.scala index 16afbe9ad433..77369c828113 100644 --- a/compiler/src/dotty/tools/dotc/parsing/Parsers.scala +++ b/compiler/src/dotty/tools/dotc/parsing/Parsers.scala @@ -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 diff --git a/docs/_docs/reference/experimental/sub-cases.md b/docs/_docs/reference/experimental/sub-cases.md index de80a8cfb752..227fc6f36709 100644 --- a/docs/_docs/reference/experimental/sub-cases.md +++ b/docs/_docs/reference/experimental/sub-cases.md @@ -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 @@ -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. diff --git a/tasty/src/dotty/tools/tasty/TastyFormat.scala b/tasty/src/dotty/tools/tasty/TastyFormat.scala index 37e3a3acfdab..e3c410dc32b3 100644 --- a/tasty/src/dotty/tools/tasty/TastyFormat.scala +++ b/tasty/src/dotty/tools/tasty/TastyFormat.scala @@ -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 @@ -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