From 1b7cc7d69a9c4bf8c29834fbe5eb8f4f5cafce01 Mon Sep 17 00:00:00 2001 From: Nicolas Stucki Date: Fri, 6 Nov 2020 09:50:48 +0100 Subject: [PATCH 1/3] Remove outdated spec doc The current version is maintained in `dotty.tools.dotc.quoted.Matcher` --- .../other-new-features/quoted-pattern-spec.md | 111 ------------------ .../other-new-features/quoted-pattern-spec.md | 111 ------------------ 2 files changed, 222 deletions(-) delete mode 100644 docs/docs/reference/other-new-features/quoted-pattern-spec.md delete mode 100644 scala3doc/dotty-docs/docs/docs/reference/other-new-features/quoted-pattern-spec.md diff --git a/docs/docs/reference/other-new-features/quoted-pattern-spec.md b/docs/docs/reference/other-new-features/quoted-pattern-spec.md deleted file mode 100644 index ad52b0e7a0dc..000000000000 --- a/docs/docs/reference/other-new-features/quoted-pattern-spec.md +++ /dev/null @@ -1,111 +0,0 @@ ---- -layout: doc-page -title: "Pattern Matching on Quoted Code" ---- - - -## Overview - -Any top-level quote `'{ ... }` in a pattern position will become a quoted pattern. Inside quoted pattern parts of the code can be spliced with `$` which extracts that part of the code. -Splices can be of two forms: -* A splice `${ ... : Expr[T] }` that can be placed in any expression position. -* A splice `${ ... : Bind[T] }` that can be placed on names of `val`s, `var`s or `def`s - -```scala -def foo(x: Expr[Int])(using tasty.Reflect): Expr[Int] = x match { - case '{ val $a: Int = $x; (${Bind(`a`)}: Int) + 1 } => '{ $x + 1 } // TODO needs fix for #6328, `a` is currently not in scope while typing -} -``` -In the example above we have `$a` which provides a `Bind[Int]`, `$x` which provides an `Expr[Int]` and `${Bind(`a`)}` which probides an `Expr[Int]` that is pattern matched against `Bind(`a`)` to check that it is a reference to `a`. - -Quoted patterns are transformed during typer to a call of `scala.internal.quoted.Expr.unapply` which splits the quoted code into the patterns and a reifiable quote that will be used as witnesses at runtime. - -```scala -def foo(x: Expr[Int])(using tasty.Reflect): Expr[Int] = x match { - case scala.internal.quoted.Expr.unapply[Tuple3[Bind[Int], Expr[Int], Expr[Int]]](Tuple3(a, x, Bind(`a`), y))('{ @patternBindHole val a: Int = patternHole[Int]; patternHole[Int] + 1 }) => -} -``` - - -## Runtime semantics - -At runtime to a `quoted.Expr` can be matched to another using `scala.internal.quoted.Expr.unapply`. - -```scala -def unapply[Tup <: Tuple](scrutineeExpr: Expr[Any])(implicit patternExpr: Expr[Any], reflection: Reflection): Option[Tup] -``` - -The `scrutineeExpr` is a normal quoted expression while `patternExpr` may contain holes representing splices. -The result of pattern matching is `None` if the expressions are not equivalent, otherwise it returns `Some` (some tuple) containing the contents of the matched holes. - -Let's define some abstractions on the possible results of this pattern matching using the alias `Matching`: -```scala -type Matching = Option[Tuple] -type Env - -def notMatched = None -def matched = Some(()) // aka Some(Tuple0()) -def matched[T](x: T) = Some(Tuple1(x)) -extension (x: Matching) def && (y: Matching) = if (x == None || y == None) None else Some(x.get ++ y.get) -def fold[T](m: Mattching*)(using Env): Matching = m.fold(matched)(_ && _) - -// `a =#= b` stands for `a` matches `b` -extension (scrutinee: Tree) def =#= pattern: Tree)(using Env): Matching // described by cases in the tables below - -def envWith(equiv: (Symbol, Symbol)*)(using Env): Env // Adds to the current environment the fact that s1 from the scrutinee is equivalent to s2 in the pattern - -def equivalent(s1: Symbol, s2: Symbol)(using Env): Env -``` - -The implementation of `=#=` - -| Tree | Pattern | Returns | -| :-----------------------: | :-------------------------: | :---------- | -| Term `a` | `patternHole[X]` | `match(quoted.Expr[X]('a))` if type of `a` is a subtype of `X` -| `val a: A` | `@patternBindHole val x: X` | `match(quoted.Bind[X](a.sym)) && '{val a: A} =#= '{val x: X}` -| Literal `a` | Literal `x` | `matched` if value of `a` is equal to the value of `x` -| `a` | `x` | `matched` if `equivalent(a.sym, x.sym)` -| `a.b` | `x.y` | `'a =#= 'x` if `equivalent(b.sym, y.sym)` -| `a: A` | `x: X` | `'a =#= 'x && '[A] =#= '[X]` -| `fa(.. ai ..)` | `fx(.. xi ..)` | `'fa =#= 'fx && fold(.. 'ai =#= 'xi) ..)` -| `fa[.. Ai ..]` | `fx[.. Xi ..]` | `'fa =#= 'fx && fold(.. '[Ai] =#= '[Xi] ..)` -| `{.. ai ..}` | `{.. xi ..}` | `fold(.. 'ai =#= 'xi ..)` -| `if (a) b else c` | `if (x) y else z` | `'a =#= 'x && 'b =#= 'y && 'c =#= 'z` -| `while (a) b` | `while (x) y` | `'a =#= 'x && 'b =#= 'y` -| Assignment `a = b` | Assignment `x = y` | `'b =#= 'y` if `'a =#= 'x.nonEmpty` -| Named argument
`n = a` | Named argument
`m = x` | `'a =#= 'x` -| `Seq(.. ai ..): _*` | `Seq(.. xi ..): _*` | `fold(.. 'ai =#= 'xi ..)` -| `new A` | `new X` | `'[A] =#= '[X]` -| `this` | `this` | `matched` if both refer to the same symbol -| `a.super[B]` | `x.super[Y]` | `'a =#= 'x` if `B` equals `Y` -| `val a: A = b`
`lazy val a: A = b`
`var a: A = b` | `val x: X = y`
`lazy val x: X = y`
`var x: X = y` | `'[A] =#= '[X] && 'b =#= 'y given envWith(a.sym -> b.sym)` -| `def a[..Ai..](.. bij: Bij ..): A = c` | `def x[..Xi..](.. yij: Yij ..): X = z` | `fold(..'[Ai] =#= '[Xi]..) && fold(.. 'bij =#= 'yij && '[Bij] =#= '[Yij] ..) && '[A] =#= '[X] && 'c =#= 'z given envWith(a.sym -> b.sym, .. bij.sym -> yij.sym ..)` -| `(.. ai: Ai ..) => b` | `(.. xi: Xi ..) => y` | `fold(.. 'ai =#= 'xi && '[Ai] =#= '[Xi] ..) && 'b =#= 'y given envWith(.. ai.sym -> xi.sym ..)` -| `a match { .. bi .. }` | `x match { .. yi .. }` | `'a =#= 'x && fold(.. 'bi =#= 'yi ..)` -| `try a catch { .. bi .. } finally ci` | `try x catch { .. yi .. } finally z` | `'a =#= 'x && fold(.. 'bi =#= 'yi ..) && 'c =#= 'z` -| | | -| `case a if b => c` | `case x if y => z` | `'a =#= 'x && 'b =#= 'y && c =#= z` -| | | -| Inferred `A` | Inferred `X` | `matched` if `A <:< X` -| `A[.. Bi ..]` | `X[.. Yi ..]` | `matched` if `('[A] && '[X] && fold(.. '[Bi] =#= '[Yi] ..)).nonEmpty` -| `A @annot` | `X` | `'[A] =#= '[X]` -| `A` | `X @annot` | `'[A] && '[X]` -| | | `notMatched` - - -| Pattern inside the quote | Pattern | Returns | -| :-------------------------: |:--------------------------: | :------------- | -| Value `a` | Value `x` | `'a =#= 'x` -| `a: A` | `x: X` | `'[A] && '[X]` -| `a @ b` | `x @ y` | `'b =#= 'y given envWith(a.sym -> b.sym)` -| Unapply `a(..bi..)(..ci..)` | Unapply `x(..yi..)(..zi..)` | `'a =#= 'x && fold(.. 'bi =#= 'yi ..) && fold(.. 'ci =#= 'zi ..)` -| `.. | ai | ..` | `.. | xi | ..` | `fold(.. 'ai =#= 'xi ..)` -| `_` | `_` | `matched` -| | | `notMatched` - - - - -## Quoted Patterns transformation - -Coming soon... diff --git a/scala3doc/dotty-docs/docs/docs/reference/other-new-features/quoted-pattern-spec.md b/scala3doc/dotty-docs/docs/docs/reference/other-new-features/quoted-pattern-spec.md deleted file mode 100644 index ad52b0e7a0dc..000000000000 --- a/scala3doc/dotty-docs/docs/docs/reference/other-new-features/quoted-pattern-spec.md +++ /dev/null @@ -1,111 +0,0 @@ ---- -layout: doc-page -title: "Pattern Matching on Quoted Code" ---- - - -## Overview - -Any top-level quote `'{ ... }` in a pattern position will become a quoted pattern. Inside quoted pattern parts of the code can be spliced with `$` which extracts that part of the code. -Splices can be of two forms: -* A splice `${ ... : Expr[T] }` that can be placed in any expression position. -* A splice `${ ... : Bind[T] }` that can be placed on names of `val`s, `var`s or `def`s - -```scala -def foo(x: Expr[Int])(using tasty.Reflect): Expr[Int] = x match { - case '{ val $a: Int = $x; (${Bind(`a`)}: Int) + 1 } => '{ $x + 1 } // TODO needs fix for #6328, `a` is currently not in scope while typing -} -``` -In the example above we have `$a` which provides a `Bind[Int]`, `$x` which provides an `Expr[Int]` and `${Bind(`a`)}` which probides an `Expr[Int]` that is pattern matched against `Bind(`a`)` to check that it is a reference to `a`. - -Quoted patterns are transformed during typer to a call of `scala.internal.quoted.Expr.unapply` which splits the quoted code into the patterns and a reifiable quote that will be used as witnesses at runtime. - -```scala -def foo(x: Expr[Int])(using tasty.Reflect): Expr[Int] = x match { - case scala.internal.quoted.Expr.unapply[Tuple3[Bind[Int], Expr[Int], Expr[Int]]](Tuple3(a, x, Bind(`a`), y))('{ @patternBindHole val a: Int = patternHole[Int]; patternHole[Int] + 1 }) => -} -``` - - -## Runtime semantics - -At runtime to a `quoted.Expr` can be matched to another using `scala.internal.quoted.Expr.unapply`. - -```scala -def unapply[Tup <: Tuple](scrutineeExpr: Expr[Any])(implicit patternExpr: Expr[Any], reflection: Reflection): Option[Tup] -``` - -The `scrutineeExpr` is a normal quoted expression while `patternExpr` may contain holes representing splices. -The result of pattern matching is `None` if the expressions are not equivalent, otherwise it returns `Some` (some tuple) containing the contents of the matched holes. - -Let's define some abstractions on the possible results of this pattern matching using the alias `Matching`: -```scala -type Matching = Option[Tuple] -type Env - -def notMatched = None -def matched = Some(()) // aka Some(Tuple0()) -def matched[T](x: T) = Some(Tuple1(x)) -extension (x: Matching) def && (y: Matching) = if (x == None || y == None) None else Some(x.get ++ y.get) -def fold[T](m: Mattching*)(using Env): Matching = m.fold(matched)(_ && _) - -// `a =#= b` stands for `a` matches `b` -extension (scrutinee: Tree) def =#= pattern: Tree)(using Env): Matching // described by cases in the tables below - -def envWith(equiv: (Symbol, Symbol)*)(using Env): Env // Adds to the current environment the fact that s1 from the scrutinee is equivalent to s2 in the pattern - -def equivalent(s1: Symbol, s2: Symbol)(using Env): Env -``` - -The implementation of `=#=` - -| Tree | Pattern | Returns | -| :-----------------------: | :-------------------------: | :---------- | -| Term `a` | `patternHole[X]` | `match(quoted.Expr[X]('a))` if type of `a` is a subtype of `X` -| `val a: A` | `@patternBindHole val x: X` | `match(quoted.Bind[X](a.sym)) && '{val a: A} =#= '{val x: X}` -| Literal `a` | Literal `x` | `matched` if value of `a` is equal to the value of `x` -| `a` | `x` | `matched` if `equivalent(a.sym, x.sym)` -| `a.b` | `x.y` | `'a =#= 'x` if `equivalent(b.sym, y.sym)` -| `a: A` | `x: X` | `'a =#= 'x && '[A] =#= '[X]` -| `fa(.. ai ..)` | `fx(.. xi ..)` | `'fa =#= 'fx && fold(.. 'ai =#= 'xi) ..)` -| `fa[.. Ai ..]` | `fx[.. Xi ..]` | `'fa =#= 'fx && fold(.. '[Ai] =#= '[Xi] ..)` -| `{.. ai ..}` | `{.. xi ..}` | `fold(.. 'ai =#= 'xi ..)` -| `if (a) b else c` | `if (x) y else z` | `'a =#= 'x && 'b =#= 'y && 'c =#= 'z` -| `while (a) b` | `while (x) y` | `'a =#= 'x && 'b =#= 'y` -| Assignment `a = b` | Assignment `x = y` | `'b =#= 'y` if `'a =#= 'x.nonEmpty` -| Named argument
`n = a` | Named argument
`m = x` | `'a =#= 'x` -| `Seq(.. ai ..): _*` | `Seq(.. xi ..): _*` | `fold(.. 'ai =#= 'xi ..)` -| `new A` | `new X` | `'[A] =#= '[X]` -| `this` | `this` | `matched` if both refer to the same symbol -| `a.super[B]` | `x.super[Y]` | `'a =#= 'x` if `B` equals `Y` -| `val a: A = b`
`lazy val a: A = b`
`var a: A = b` | `val x: X = y`
`lazy val x: X = y`
`var x: X = y` | `'[A] =#= '[X] && 'b =#= 'y given envWith(a.sym -> b.sym)` -| `def a[..Ai..](.. bij: Bij ..): A = c` | `def x[..Xi..](.. yij: Yij ..): X = z` | `fold(..'[Ai] =#= '[Xi]..) && fold(.. 'bij =#= 'yij && '[Bij] =#= '[Yij] ..) && '[A] =#= '[X] && 'c =#= 'z given envWith(a.sym -> b.sym, .. bij.sym -> yij.sym ..)` -| `(.. ai: Ai ..) => b` | `(.. xi: Xi ..) => y` | `fold(.. 'ai =#= 'xi && '[Ai] =#= '[Xi] ..) && 'b =#= 'y given envWith(.. ai.sym -> xi.sym ..)` -| `a match { .. bi .. }` | `x match { .. yi .. }` | `'a =#= 'x && fold(.. 'bi =#= 'yi ..)` -| `try a catch { .. bi .. } finally ci` | `try x catch { .. yi .. } finally z` | `'a =#= 'x && fold(.. 'bi =#= 'yi ..) && 'c =#= 'z` -| | | -| `case a if b => c` | `case x if y => z` | `'a =#= 'x && 'b =#= 'y && c =#= z` -| | | -| Inferred `A` | Inferred `X` | `matched` if `A <:< X` -| `A[.. Bi ..]` | `X[.. Yi ..]` | `matched` if `('[A] && '[X] && fold(.. '[Bi] =#= '[Yi] ..)).nonEmpty` -| `A @annot` | `X` | `'[A] =#= '[X]` -| `A` | `X @annot` | `'[A] && '[X]` -| | | `notMatched` - - -| Pattern inside the quote | Pattern | Returns | -| :-------------------------: |:--------------------------: | :------------- | -| Value `a` | Value `x` | `'a =#= 'x` -| `a: A` | `x: X` | `'[A] && '[X]` -| `a @ b` | `x @ y` | `'b =#= 'y given envWith(a.sym -> b.sym)` -| Unapply `a(..bi..)(..ci..)` | Unapply `x(..yi..)(..zi..)` | `'a =#= 'x && fold(.. 'bi =#= 'yi ..) && fold(.. 'ci =#= 'zi ..)` -| `.. | ai | ..` | `.. | xi | ..` | `fold(.. 'ai =#= 'xi ..)` -| `_` | `_` | `matched` -| | | `notMatched` - - - - -## Quoted Patterns transformation - -Coming soon... From 24ae305364b410936e0c422795ddde792d404fd1 Mon Sep 17 00:00:00 2001 From: Nicolas Stucki Date: Fri, 6 Nov 2020 09:56:01 +0100 Subject: [PATCH 2/3] Remove unsused annotation `@patternBindHole` was replaced by `@patternType` --- compiler/src/dotty/tools/dotc/ast/Desugar.scala | 4 ++-- .../src/dotty/tools/dotc/typer/QuotesAndSplices.scala | 8 ++++---- .../src-bootstrapped/scala/internal/quoted/Patterns.scala | 5 ----- 3 files changed, 6 insertions(+), 11 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/ast/Desugar.scala b/compiler/src/dotty/tools/dotc/ast/Desugar.scala index bcc0cdc8c235..43f8fa908eb9 100644 --- a/compiler/src/dotty/tools/dotc/ast/Desugar.scala +++ b/compiler/src/dotty/tools/dotc/ast/Desugar.scala @@ -919,8 +919,8 @@ object desugar { def quotedPatternTypeDef(tree: TypeDef)(using Context): TypeDef = { assert(ctx.mode.is(Mode.QuotedPattern)) if (tree.name.startsWith("$") && !tree.isBackquoted) { - val patternBindHoleAnnot = New(ref(defn.InternalQuotedPatterns_patternTypeAnnot.typeRef)).withSpan(tree.span) - val mods = tree.mods.withAddedAnnotation(patternBindHoleAnnot) + val patternTypeAnnot = New(ref(defn.InternalQuotedPatterns_patternTypeAnnot.typeRef)).withSpan(tree.span) + val mods = tree.mods.withAddedAnnotation(patternTypeAnnot) tree.withMods(mods) } else tree diff --git a/compiler/src/dotty/tools/dotc/typer/QuotesAndSplices.scala b/compiler/src/dotty/tools/dotc/typer/QuotesAndSplices.scala index 4c9566fa93b5..b88d4a641f5e 100644 --- a/compiler/src/dotty/tools/dotc/typer/QuotesAndSplices.scala +++ b/compiler/src/dotty/tools/dotc/typer/QuotesAndSplices.scala @@ -197,7 +197,7 @@ trait QuotesAndSplices { ) /** Split a typed quoted pattern is split into its type bindings, pattern expression and inner patterns. - * Type definitions with `@patternBindHole` will be inserted in the pattern expression for each type binding. + * Type definitions with `@patternType` will be inserted in the pattern expression for each type binding. * * A quote pattern * ``` @@ -208,7 +208,7 @@ trait QuotesAndSplices { * ( * Map(<$t>: Symbol -> <$t @ _>: Bind), * <'{ - * @scala.internal.Quoted.patternBindHole type $t + * @scala.internal.Quoted.patternType type $t * scala.internal.Quoted.patternHole[List[$t]] * }>: Tree, * List(: Tree) @@ -366,7 +366,7 @@ trait QuotesAndSplices { * ``` * * For each type splice we will create a new type binding in the pattern match ($t @ _ in this case) - * and a corresponding type in the quoted pattern as a hole (@patternBindHole type $t in this case). + * and a corresponding type in the quoted pattern as a hole (@patternType type $t in this case). * All these generated types are inserted at the start of the quoted code. * * After typing the tree will resemble @@ -388,7 +388,7 @@ trait QuotesAndSplices { * (implicit t @ _, ls @ _: Expr[List[$t]]) // from the spliced patterns * )( * '{ // Runtime quote Matcher.unapply uses to mach against. Expression directly inside the quoted pattern without the splices - * @scala.internal.Quoted.patternBindHole type $t + * @scala.internal.Quoted.patternType type $t * scala.internal.Quoted.patternHole[List[$t]] * }, * true, // If there is at least one type splice. Used to instantiate the context with or without GADT constraints diff --git a/library/src-bootstrapped/scala/internal/quoted/Patterns.scala b/library/src-bootstrapped/scala/internal/quoted/Patterns.scala index e4520beba1b1..8e1b3468ddc2 100644 --- a/library/src-bootstrapped/scala/internal/quoted/Patterns.scala +++ b/library/src-bootstrapped/scala/internal/quoted/Patterns.scala @@ -16,11 +16,6 @@ object Patterns { /** A higher order splice in a quoted pattern is desugared by the compiler into a call to this method */ def higherOrderHole[U](args: Any*): U = ??? - // TODO remove - /** A splice of a name in a quoted pattern is desugared by wrapping getting this annotation */ - @compileTimeOnly("Illegal reference to `scala.internal.quoted.CompileTime.patternBindHole`") - class patternBindHole extends Annotation - /** A splice of a name in a quoted pattern is that marks the definition of a type splice */ @compileTimeOnly("Illegal reference to `scala.internal.quoted.CompileTime.patternType`") class patternType extends Annotation From 281798460e09552f279d588b669c94938756776c Mon Sep 17 00:00:00 2001 From: Nicolas Stucki Date: Fri, 6 Nov 2020 09:59:01 +0100 Subject: [PATCH 3/3] Update path in error message --- .../scala/internal/quoted/Patterns.scala | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/library/src-bootstrapped/scala/internal/quoted/Patterns.scala b/library/src-bootstrapped/scala/internal/quoted/Patterns.scala index 8e1b3468ddc2..fa5d11f6cdf5 100644 --- a/library/src-bootstrapped/scala/internal/quoted/Patterns.scala +++ b/library/src-bootstrapped/scala/internal/quoted/Patterns.scala @@ -5,23 +5,23 @@ import scala.annotation.{Annotation, compileTimeOnly} object Patterns { /** A splice in a quoted pattern is desugared by the compiler into a call to this method */ - @compileTimeOnly("Illegal reference to `scala.internal.quoted.CompileTime.patternHole`") + @compileTimeOnly("Illegal reference to `scala.internal.quoted.Patterns.patternHole`") def patternHole[T]: T = ??? - @compileTimeOnly("Illegal reference to `scala.internal.quoted.CompileTime.patternHigherOrderHole`") + @compileTimeOnly("Illegal reference to `scala.internal.quoted.Patterns.patternHigherOrderHole`") /** A higher order splice in a quoted pattern is desugared by the compiler into a call to this method */ def patternHigherOrderHole[U](pat: Any, args: Any*): U = ??? - @compileTimeOnly("Illegal reference to `scala.internal.quoted.CompileTime.higherOrderHole`") + @compileTimeOnly("Illegal reference to `scala.internal.quoted.Patterns.higherOrderHole`") /** A higher order splice in a quoted pattern is desugared by the compiler into a call to this method */ def higherOrderHole[U](args: Any*): U = ??? /** A splice of a name in a quoted pattern is that marks the definition of a type splice */ - @compileTimeOnly("Illegal reference to `scala.internal.quoted.CompileTime.patternType`") + @compileTimeOnly("Illegal reference to `scala.internal.quoted.Patterns.patternType`") class patternType extends Annotation /** A type pattern that must be aproximated from above */ - @compileTimeOnly("Illegal reference to `scala.internal.quoted.CompileTime.fromAbove`") + @compileTimeOnly("Illegal reference to `scala.internal.quoted.Patterns.fromAbove`") class fromAbove extends Annotation }