Skip to content

Commit cf8fbd8

Browse files
authored
Merge pull request #10091 from dotty-staging/fix-#10087
Fix #10087 : Change syntax of given instances in patterns
2 parents 9ec34f8 + 718dacd commit cf8fbd8

File tree

10 files changed

+82
-61
lines changed

10 files changed

+82
-61
lines changed

compiler/src/dotty/tools/dotc/parsing/Parsers.scala

Lines changed: 23 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -2588,65 +2588,44 @@ object Parsers {
25882588
else Nil
25892589

25902590
/** Pattern1 ::= Pattern2 [Ascription]
2591-
* | ‘given’ PatVar ‘:’ RefinedType
25922591
*/
25932592
def pattern1(location: Location = Location.InPattern): Tree =
2594-
if (in.token == GIVEN) {
2595-
val givenMod = atSpan(in.skipToken())(Mod.Given())
2596-
atSpan(in.offset) {
2597-
in.token match {
2598-
case IDENTIFIER | USCORE if in.name.isVarPattern =>
2599-
val name = in.name
2600-
in.nextToken()
2601-
accept(COLON)
2602-
val typed = ascription(Ident(nme.WILDCARD), location)
2603-
Bind(name, typed).withMods(addMod(Modifiers(), givenMod))
2604-
case _ =>
2605-
syntaxErrorOrIncomplete("pattern variable expected")
2606-
errorTermTree
2607-
}
2608-
}
2609-
}
2610-
else {
2611-
val p = pattern2()
2612-
if (in.token == COLON) {
2613-
in.nextToken()
2614-
ascription(p, location)
2615-
}
2616-
else p
2617-
}
2593+
val p = pattern2()
2594+
if in.token == COLON then
2595+
in.nextToken()
2596+
ascription(p, location)
2597+
else p
26182598

2619-
/** Pattern2 ::= [id `@'] InfixPattern
2599+
/** Pattern2 ::= [id `as'] InfixPattern
26202600
*/
26212601
val pattern2: () => Tree = () => infixPattern() match {
26222602
case p @ Ident(name) if in.token == AT || in.isIdent(nme.as) =>
26232603
if in.token == AT && sourceVersion.isAtLeast(`3.1`) then
26242604
deprecationWarning(s"`@` bindings have been deprecated; use `as` instead", in.offset)
26252605

26262606
val offset = in.skipToken()
2627-
2628-
// compatibility for Scala2 `x @ _*` syntax
26292607
infixPattern() match {
2630-
case pt @ Ident(tpnme.WILDCARD_STAR) =>
2631-
if sourceVersion.isAtLeast(`3.1`) then
2632-
report.errorOrMigrationWarning(
2633-
"The syntax `x @ _*` is no longer supported; use `x : _*` instead",
2634-
in.sourcePos(startOffset(p)))
2608+
case pt @ Ident(tpnme.WILDCARD_STAR) => // compatibility for Scala2 `x @ _*` syntax
2609+
warnMigration(p)
26352610
atSpan(startOffset(p), offset) { Typed(p, pt) }
2611+
case pt @ Bind(nme.WILDCARD, pt1: Typed) if pt.mods.is(Given) =>
2612+
atSpan(startOffset(p), 0) { Bind(name, pt1).withMods(pt.mods) }
26362613
case pt =>
26372614
atSpan(startOffset(p), 0) { Bind(name, pt) }
26382615
}
26392616
case p @ Ident(tpnme.WILDCARD_STAR) =>
2640-
// compatibility for Scala2 `_*` syntax
2641-
if sourceVersion.isAtLeast(`3.1`) then
2642-
report.errorOrMigrationWarning(
2643-
"The syntax `_*` is no longer supported; use `x : _*` instead",
2644-
in.sourcePos(startOffset(p)))
2617+
warnMigration(p)
26452618
atSpan(startOffset(p)) { Typed(Ident(nme.WILDCARD), p) }
26462619
case p =>
26472620
p
26482621
}
26492622

2623+
private def warnMigration(p: Tree) =
2624+
if sourceVersion.isAtLeast(`3.1`) then
2625+
report.errorOrMigrationWarning(
2626+
"The syntax `x @ _*` is no longer supported; use `x : _*` instead",
2627+
in.sourcePos(startOffset(p)))
2628+
26502629
/** InfixPattern ::= SimplePattern {id [nl] SimplePattern}
26512630
*/
26522631
def infixPattern(): Tree =
@@ -2686,6 +2665,12 @@ object Parsers {
26862665
simpleExpr()
26872666
case XMLSTART =>
26882667
xmlLiteralPattern()
2668+
case GIVEN =>
2669+
atSpan(in.offset) {
2670+
val givenMod = atSpan(in.skipToken())(Mod.Given())
2671+
val typed = Typed(Ident(nme.WILDCARD), refinedType())
2672+
Bind(nme.WILDCARD, typed).withMods(addMod(Modifiers(), givenMod))
2673+
}
26892674
case _ =>
26902675
if (isLiteral) literal(inPattern = true)
26912676
else {

docs/docs/internals/syntax.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -269,15 +269,15 @@ TypeCaseClause ::= ‘case’ InfixType ‘=>’ Type [nl]
269269
270270
Pattern ::= Pattern1 { ‘|’ Pattern1 } Alternative(pats)
271271
Pattern1 ::= Pattern2 [‘:’ RefinedType] Bind(name, Typed(Ident(wildcard), tpe))
272-
| ‘given’ PatVar ‘:’ RefinedType
273-
Pattern2 ::= [id ‘as’] InfixPattern Bind(name, pat)
272+
Pattern2 ::= [id ‘as’] InfixPattern Bind(name, pat)
274273
InfixPattern ::= SimplePattern { id [nl] SimplePattern } InfixOp(pat, op, pat)
275274
SimplePattern ::= PatVar Ident(wildcard)
276275
| Literal Bind(name, Ident(wildcard))
277276
| ‘(’ [Patterns] ‘)’ Parens(pats) Tuple(pats)
278277
| Quoted
279278
| XmlPattern
280279
| SimplePattern1 [TypeArgs] [ArgumentPatterns]
280+
| ‘given’ RefinedType
281281
SimplePattern1 ::= SimpleRef
282282
| SimplePattern1 ‘.’ id
283283
PatVar ::= varid

docs/docs/reference/contextual/givens.md

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -90,6 +90,21 @@ transparent inline given mkAnnotations[A, T] as Annotations[A, T] = ${
9090
```
9191
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]`.
9292

93+
## Pattern-Bound Given Instances
94+
95+
Given instances can also appear as pattern bound-variables. Example:
96+
97+
```scala
98+
for given Context <- applicationContexts do
99+
100+
pair match
101+
case (ctx as given Context, y) => ...
102+
```
103+
In the first fragment above, anonymous given instances for class `Context` are established by enumerating over `applicationContexts`. In the second fragment, a given `Context`
104+
instance named `ctx` is established by matching against the first half of the `pair` selector.
105+
106+
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`.
107+
93108
## Given Instance Initialization
94109

95110
A given instance without type or context parameters is initialized on-demand, the first

docs/docs/reference/metaprogramming/inline.md

Lines changed: 16 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -268,10 +268,10 @@ val obj2 = choose(false) // static type is B
268268
// obj1.m() // compile-time error: `m` is not defined on `A`
269269
obj2.m() // OK
270270
```
271-
Here, the inline method `choose` returns an instance of either of the two types `A` or `B`.
271+
Here, the inline method `choose` returns an instance of either of the two types `A` or `B`.
272272
If `choose` had not been declared to be `transparent`, the result
273-
of its expansion would always be of type `A`, even though the computed value might be of the subtype `B`.
274-
The inline method is a "blackbox" in the sense that details of its implementation do not leak out.
273+
of its expansion would always be of type `A`, even though the computed value might be of the subtype `B`.
274+
The inline method is a "blackbox" in the sense that details of its implementation do not leak out.
275275
But if a `transparent` modifier is given, the expansion is the type of the expanded body. If the argument `b`
276276
is `true`, that type is `A`, otherwise it is `B`. Consequently, calling `m` on `obj2`
277277
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:
571571
import scala.compiletime.summonFrom
572572

573573
inline def setFor[T]: Set[T] = summonFrom {
574-
case given ord: Ordering[T] => new TreeSet[T]
575-
case _ => new HashSet[T]
574+
case ord: Ordering[T] => new TreeSet[T](using ord)
575+
case _ => new HashSet[T]
576576
}
577577
```
578578
A `summonFrom` call takes a pattern matching closure as argument. All patterns
579579
in the closure are type ascriptions of the form `identifier : Type`.
580580

581-
Patterns are tried in sequence. The first case with a pattern `x: T` such that
582-
an implicit value of type `T` can be summoned is chosen. If the pattern is prefixed
583-
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.
581+
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.
582+
583+
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:
584+
```scala
585+
import scala.compiletime.summonFrom
586+
587+
inline def setFor[T]: Set[T] = summonFrom {
588+
case given Ordering[T] => new TreeSet[T]
589+
case _ => new HashSet[T]
590+
}
591+
```
584592

585593
`summonFrom` applications must be reduced at compile time.
586594

tests/neg/given-pattern.scala

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,11 +4,11 @@ class Test {
44
import scala.collection.immutable.{TreeSet, HashSet}
55

66
def f2[T](x: Ordering[T]) = {
7-
val (given y: Ordering[T]) = x
7+
val (given Ordering[T]) = x
88
new TreeSet[T] // error: no implicit ordering defined for T
99
}
1010
def f3[T](x: Ordering[T]) = {
11-
val given y: Ordering[T] = x // error: pattern expected
11+
val given Ordering[T] = x
1212
new TreeSet[T] // error: no implicit ordering defined for T
1313
}
1414
}

tests/pos-macros/i7358.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ transparent inline def summonT[Tp <: Tuple](using QuoteContext): Tuple = inline
88
case _ : (hd *: tl) => {
99
type H = hd
1010
summonFrom {
11-
case given _ : Type[H] => summon[Type[H]] *: summonT[tl]
11+
case given Type[H] => summon[Type[H]] *: summonT[tl]
1212
}
1313
}
1414
}

tests/pos/given-pattern.scala

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -6,25 +6,25 @@ class Test {
66
transparent inline def trySummon[S, T](f: PartialFunction[S, T]): T = ???
77

88
inline def setFor[T]: Set[T] = trySummon {
9-
case given ord: Ordering[T] => new TreeSet[T]
10-
case given _: Ordering[T] => new TreeSet[T]
11-
case _ => new HashSet[T]
9+
case ord as given Ordering[T] => new TreeSet[T]
10+
case given Ordering[T] => new TreeSet[T]
11+
case _ => new HashSet[T]
1212
}
1313

1414
def f1[T](x: Ordering[T]) = (x, x) match {
15-
case (given y: Ordering[T], _) => new TreeSet[T]
15+
case (y as given Ordering[T], _) => new TreeSet[T]
1616
}
1717
def f2[T](x: Ordering[T]) = {
1818
val xs = List(x, x, x)
19-
for given y: Ordering[T] <- xs
19+
for y as given Ordering[T] <- xs
2020
yield new TreeSet[T]
2121
}
2222
def f3[T](x: Ordering[T]) = (x, x) match {
23-
case (given _: Ordering[T], _) => new TreeSet[T]
23+
case (given Ordering[T], _) => new TreeSet[T]
2424
}
2525
def f4[T](x: Ordering[T]) = {
2626
val xs = List(x, x, x)
27-
for given _: Ordering[T] <- xs
27+
for given Ordering[T] <- xs
2828
yield new TreeSet[T]
2929
}
3030
}

tests/pos/i10087.scala

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
import math.Ordering
2+
import collection.immutable.TreeSet
3+
4+
def f1[T](x: Ordering[T]) = (x, x) match {
5+
case (given Ordering[T], _) => new TreeSet[T]
6+
}
7+
def f4[T](x: Ordering[T]) = {
8+
val xs = List(x, x, x)
9+
for given Ordering[T] <- xs
10+
yield new TreeSet[T]
11+
for x as given Ordering[T] <- xs
12+
yield new TreeSet[T]
13+
}

tests/pos/reference/delegate-match.scala

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,8 @@ class Test extends App:
55
import scala.compiletime.summonFrom
66

77
transparent inline def setFor[T]: Set[T] = summonFrom {
8-
case given ord: Ordering[T] => new TreeSet[T]
9-
case _ => new HashSet[T]
8+
case ord as given Ordering[T] => new TreeSet[T]
9+
case _ => new HashSet[T]
1010
}
1111

1212
summon[Ordering[String]]

0 commit comments

Comments
 (0)