diff --git a/community-build/community-projects/shapeless b/community-build/community-projects/shapeless index 3c11fae956e5..69ffc1f316cb 160000 --- a/community-build/community-projects/shapeless +++ b/community-build/community-projects/shapeless @@ -1 +1 @@ -Subproject commit 3c11fae956e5f12e2a69e4cdbf3b278b38bb689f +Subproject commit 69ffc1f316cb3c2c5d85a346f04276dda930d242 diff --git a/compiler/src/dotty/tools/dotc/parsing/Parsers.scala b/compiler/src/dotty/tools/dotc/parsing/Parsers.scala index eea9977aad21..89e1b79d333b 100644 --- a/compiler/src/dotty/tools/dotc/parsing/Parsers.scala +++ b/compiler/src/dotty/tools/dotc/parsing/Parsers.scala @@ -2587,35 +2587,15 @@ object Parsers { else Nil /** Pattern1 ::= Pattern2 [Ascription] - * | ‘given’ PatVar ‘:’ RefinedType */ def pattern1(location: Location = Location.InPattern): Tree = - if (in.token == GIVEN) { - val givenMod = atSpan(in.skipToken())(Mod.Given()) - atSpan(in.offset) { - in.token match { - case IDENTIFIER | USCORE if in.name.isVarPattern => - val name = in.name - in.nextToken() - accept(COLON) - val typed = ascription(Ident(nme.WILDCARD), location) - Bind(name, typed).withMods(addMod(Modifiers(), givenMod)) - case _ => - syntaxErrorOrIncomplete("pattern variable expected") - errorTermTree - } - } - } - else { - val p = pattern2() - if (in.token == COLON) { - in.nextToken() - ascription(p, location) - } - else p - } + val p = pattern2() + if in.token == COLON then + in.nextToken() + ascription(p, location) + else p - /** Pattern2 ::= [id `@'] InfixPattern + /** Pattern2 ::= [id `as'] InfixPattern */ val pattern2: () => Tree = () => infixPattern() match { case p @ Ident(name) if in.token == AT || in.isIdent(nme.as) => @@ -2623,29 +2603,28 @@ object Parsers { deprecationWarning(s"`@` bindings have been deprecated; use `as` instead", in.offset) val offset = in.skipToken() - - // compatibility for Scala2 `x @ _*` syntax infixPattern() match { - case pt @ Ident(tpnme.WILDCARD_STAR) => - if sourceVersion.isAtLeast(`3.1`) then - report.errorOrMigrationWarning( - "The syntax `x @ _*` is no longer supported; use `x : _*` instead", - in.sourcePos(startOffset(p))) + case pt @ Ident(tpnme.WILDCARD_STAR) => // compatibility for Scala2 `x @ _*` syntax + warnMigration(p) atSpan(startOffset(p), offset) { Typed(p, pt) } + case pt @ Bind(nme.WILDCARD, pt1: Typed) if pt.mods.is(Given) => + atSpan(startOffset(p), 0) { Bind(name, pt1).withMods(pt.mods) } case pt => atSpan(startOffset(p), 0) { Bind(name, pt) } } case p @ Ident(tpnme.WILDCARD_STAR) => - // compatibility for Scala2 `_*` syntax - if sourceVersion.isAtLeast(`3.1`) then - report.errorOrMigrationWarning( - "The syntax `_*` is no longer supported; use `x : _*` instead", - in.sourcePos(startOffset(p))) + warnMigration(p) atSpan(startOffset(p)) { Typed(Ident(nme.WILDCARD), p) } case p => p } + private def warnMigration(p: Tree) = + if sourceVersion.isAtLeast(`3.1`) then + report.errorOrMigrationWarning( + "The syntax `x @ _*` is no longer supported; use `x : _*` instead", + in.sourcePos(startOffset(p))) + /** InfixPattern ::= SimplePattern {id [nl] SimplePattern} */ def infixPattern(): Tree = @@ -2685,6 +2664,12 @@ object Parsers { simpleExpr() case XMLSTART => xmlLiteralPattern() + case GIVEN => + atSpan(in.offset) { + val givenMod = atSpan(in.skipToken())(Mod.Given()) + val typed = Typed(Ident(nme.WILDCARD), refinedType()) + Bind(nme.WILDCARD, typed).withMods(addMod(Modifiers(), givenMod)) + } case _ => if (isLiteral) literal(inPattern = true) else { diff --git a/docs/docs/internals/syntax.md b/docs/docs/internals/syntax.md index 9819bd4ee770..0da5fd9f6069 100644 --- a/docs/docs/internals/syntax.md +++ b/docs/docs/internals/syntax.md @@ -272,8 +272,7 @@ TypeCaseClause ::= ‘case’ InfixType ‘=>’ Type [nl] Pattern ::= Pattern1 { ‘|’ Pattern1 } Alternative(pats) Pattern1 ::= Pattern2 [‘:’ RefinedType] Bind(name, Typed(Ident(wildcard), tpe)) - | ‘given’ PatVar ‘:’ RefinedType -Pattern2 ::= [id ‘as’] InfixPattern Bind(name, pat) +Pattern2 ::= [id ‘as’] InfixPattern Bind(name, pat) InfixPattern ::= SimplePattern { id [nl] SimplePattern } InfixOp(pat, op, pat) SimplePattern ::= PatVar Ident(wildcard) | Literal Bind(name, Ident(wildcard)) @@ -281,6 +280,7 @@ SimplePattern ::= PatVar | Quoted | XmlPattern | SimplePattern1 [TypeArgs] [ArgumentPatterns] + | ‘given’ RefinedType SimplePattern1 ::= SimpleRef | SimplePattern1 ‘.’ id PatVar ::= varid diff --git a/docs/docs/reference/contextual/givens.md b/docs/docs/reference/contextual/givens.md index e4b72543545c..98f2f4504c18 100644 --- a/docs/docs/reference/contextual/givens.md +++ b/docs/docs/reference/contextual/givens.md @@ -90,6 +90,21 @@ transparent inline given mkAnnotations[A, T] as Annotations[A, T] = ${ ``` Since `mkAnnotations` is `transparent`, the type of an application is the type of its right hand side, which can be a proper subtype of the declared result type `Annotations[A, T]`. +## Pattern-Bound Given Instances + +Given instances can also appear as pattern bound-variables. Example: + +```scala +for given Context <- applicationContexts do + +pair match + case (ctx as given Context, y) => ... +``` +In the first fragment above, anonymous given instances for class `Context` are established by enumerating over `applicationContexts`. In the second fragment, a given `Context` +instance named `ctx` is established by matching against the first half of the `pair` selector. + +In each case, a pattern-bound given instance consists of `given` and a type `T`. The pattern matches exactly the same selectors as the type ascription pattern `_: T`. + ## Given Instance Initialization A given instance without type or context parameters is initialized on-demand, the first diff --git a/docs/docs/reference/metaprogramming/inline.md b/docs/docs/reference/metaprogramming/inline.md index 998da914f615..ecbd2325611e 100644 --- a/docs/docs/reference/metaprogramming/inline.md +++ b/docs/docs/reference/metaprogramming/inline.md @@ -268,10 +268,10 @@ val obj2 = choose(false) // static type is B // obj1.m() // compile-time error: `m` is not defined on `A` obj2.m() // OK ``` -Here, the inline method `choose` returns an instance of either of the two types `A` or `B`. +Here, the inline method `choose` returns an instance of either of the two types `A` or `B`. If `choose` had not been declared to be `transparent`, the result -of its expansion would always be of type `A`, even though the computed value might be of the subtype `B`. -The inline method is a "blackbox" in the sense that details of its implementation do not leak out. +of its expansion would always be of type `A`, even though the computed value might be of the subtype `B`. +The inline method is a "blackbox" in the sense that details of its implementation do not leak out. But if a `transparent` modifier is given, the expansion is the type of the expanded body. If the argument `b` is `true`, that type is `A`, otherwise it is `B`. Consequently, calling `m` on `obj2` type-checks since `obj2` has the same type as the expansion of `choose(false)`, which is `B`. @@ -571,16 +571,24 @@ would use it as follows: import scala.compiletime.summonFrom inline def setFor[T]: Set[T] = summonFrom { - case given ord: Ordering[T] => new TreeSet[T] - case _ => new HashSet[T] + case ord: Ordering[T] => new TreeSet[T](using ord) + case _ => new HashSet[T] } ``` A `summonFrom` call takes a pattern matching closure as argument. All patterns in the closure are type ascriptions of the form `identifier : Type`. -Patterns are tried in sequence. The first case with a pattern `x: T` such that -an implicit value of type `T` can be summoned is chosen. If the pattern is prefixed -with `given`, the variable `x` is bound to the implicit value for the remainder of the case. It can in turn be used as an implicit in the right hand side of the case. It is an error if one of the tested patterns gives rise to an ambiguous implicit search. +Patterns are tried in sequence. The first case with a pattern `x: T` such that an implicit value of type `T` can be summoned is chosen. + +Alternatively, one can also use a pattern-bound given instance, which avoids the explicit using clause. For instance, `setFor` could also be formulated as follows: +```scala +import scala.compiletime.summonFrom + +inline def setFor[T]: Set[T] = summonFrom { + case given Ordering[T] => new TreeSet[T] + case _ => new HashSet[T] +} +``` `summonFrom` applications must be reduced at compile time. diff --git a/tests/neg/given-pattern.scala b/tests/neg/given-pattern.scala index 9902a548f633..72f5213c9f06 100644 --- a/tests/neg/given-pattern.scala +++ b/tests/neg/given-pattern.scala @@ -4,11 +4,11 @@ class Test { import scala.collection.immutable.{TreeSet, HashSet} def f2[T](x: Ordering[T]) = { - val (given y: Ordering[T]) = x + val (given Ordering[T]) = x new TreeSet[T] // error: no implicit ordering defined for T } def f3[T](x: Ordering[T]) = { - val given y: Ordering[T] = x // error: pattern expected + val given Ordering[T] = x new TreeSet[T] // error: no implicit ordering defined for T } } \ No newline at end of file diff --git a/tests/pos-macros/i7358.scala b/tests/pos-macros/i7358.scala index 8aa6cc5d280a..1603bd6fbf49 100644 --- a/tests/pos-macros/i7358.scala +++ b/tests/pos-macros/i7358.scala @@ -8,7 +8,7 @@ transparent inline def summonT[Tp <: Tuple](using QuoteContext): Tuple = inline case _ : (hd *: tl) => { type H = hd summonFrom { - case given _ : Type[H] => summon[Type[H]] *: summonT[tl] + case given Type[H] => summon[Type[H]] *: summonT[tl] } } } diff --git a/tests/pos/given-pattern.scala b/tests/pos/given-pattern.scala index db655d093482..6267e23193e4 100644 --- a/tests/pos/given-pattern.scala +++ b/tests/pos/given-pattern.scala @@ -6,25 +6,25 @@ class Test { transparent inline def trySummon[S, T](f: PartialFunction[S, T]): T = ??? inline def setFor[T]: Set[T] = trySummon { - case given ord: Ordering[T] => new TreeSet[T] - case given _: Ordering[T] => new TreeSet[T] - case _ => new HashSet[T] + case ord as given Ordering[T] => new TreeSet[T] + case given Ordering[T] => new TreeSet[T] + case _ => new HashSet[T] } def f1[T](x: Ordering[T]) = (x, x) match { - case (given y: Ordering[T], _) => new TreeSet[T] + case (y as given Ordering[T], _) => new TreeSet[T] } def f2[T](x: Ordering[T]) = { val xs = List(x, x, x) - for given y: Ordering[T] <- xs + for y as given Ordering[T] <- xs yield new TreeSet[T] } def f3[T](x: Ordering[T]) = (x, x) match { - case (given _: Ordering[T], _) => new TreeSet[T] + case (given Ordering[T], _) => new TreeSet[T] } def f4[T](x: Ordering[T]) = { val xs = List(x, x, x) - for given _: Ordering[T] <- xs + for given Ordering[T] <- xs yield new TreeSet[T] } } \ No newline at end of file diff --git a/tests/pos/i10087.scala b/tests/pos/i10087.scala new file mode 100644 index 000000000000..7c9cd1ca24e3 --- /dev/null +++ b/tests/pos/i10087.scala @@ -0,0 +1,13 @@ +import math.Ordering +import collection.immutable.TreeSet + +def f1[T](x: Ordering[T]) = (x, x) match { + case (given Ordering[T], _) => new TreeSet[T] +} +def f4[T](x: Ordering[T]) = { + val xs = List(x, x, x) + for given Ordering[T] <- xs + yield new TreeSet[T] + for x as given Ordering[T] <- xs + yield new TreeSet[T] +} \ No newline at end of file diff --git a/tests/pos/reference/delegate-match.scala b/tests/pos/reference/delegate-match.scala index 2f1e1379ecc6..4e81c63af37b 100644 --- a/tests/pos/reference/delegate-match.scala +++ b/tests/pos/reference/delegate-match.scala @@ -5,8 +5,8 @@ class Test extends App: import scala.compiletime.summonFrom transparent inline def setFor[T]: Set[T] = summonFrom { - case given ord: Ordering[T] => new TreeSet[T] - case _ => new HashSet[T] + case ord as given Ordering[T] => new TreeSet[T] + case _ => new HashSet[T] } summon[Ordering[String]]