Skip to content

Commit 2ea9761

Browse files
committed
Fix #10087: Change syntax of given instances in patterns
See the reference doc pages in this commit for details
1 parent ac4e29d commit 2ea9761

File tree

8 files changed

+72
-49
lines changed

8 files changed

+72
-49
lines changed

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

Lines changed: 15 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -2587,35 +2587,15 @@ object Parsers {
25872587
else Nil
25882588

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

2618-
/** Pattern2 ::= [id `@'] InfixPattern
2598+
/** Pattern2 ::= [id `as'] InfixPattern
26192599
*/
26202600
val pattern2: () => Tree = () => infixPattern() match {
26212601
case p @ Ident(name) if in.token == AT || in.isIdent(nme.as) =>
@@ -2624,14 +2604,15 @@ object Parsers {
26242604

26252605
val offset = in.skipToken()
26262606

2627-
// compatibility for Scala2 `x @ _*` syntax
26282607
infixPattern() match {
2629-
case pt @ Ident(tpnme.WILDCARD_STAR) =>
2608+
case pt @ Ident(tpnme.WILDCARD_STAR) => // compatibility for Scala2 `x @ _*` syntax
26302609
if sourceVersion.isAtLeast(`3.1`) then
26312610
report.errorOrMigrationWarning(
26322611
"The syntax `x @ _*` is no longer supported; use `x : _*` instead",
26332612
in.sourcePos(startOffset(p)))
26342613
atSpan(startOffset(p), offset) { Typed(p, pt) }
2614+
case pt @ Bind(nme.WILDCARD, pt1: Typed) if pt.mods.is(Given) =>
2615+
atSpan(startOffset(p), 0) { Bind(name, pt1).withMods(pt.mods) }
26352616
case pt =>
26362617
atSpan(startOffset(p), 0) { Bind(name, pt) }
26372618
}
@@ -2685,6 +2666,12 @@ object Parsers {
26852666
simpleExpr()
26862667
case XMLSTART =>
26872668
xmlLiteralPattern()
2669+
case GIVEN =>
2670+
atSpan(in.offset) {
2671+
val givenMod = atSpan(in.skipToken())(Mod.Given())
2672+
val typed = Typed(Ident(nme.WILDCARD), refinedType())
2673+
Bind(nme.WILDCARD, typed).withMods(addMod(Modifiers(), givenMod))
2674+
}
26882675
case _ =>
26892676
if (isLiteral) literal(inPattern = true)
26902677
else {

docs/docs/internals/syntax.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -272,15 +272,15 @@ TypeCaseClause ::= ‘case’ InfixType ‘=>’ Type [nl]
272272
273273
Pattern ::= Pattern1 { ‘|’ Pattern1 } Alternative(pats)
274274
Pattern1 ::= Pattern2 [‘:’ RefinedType] Bind(name, Typed(Ident(wildcard), tpe))
275-
| ‘given’ PatVar ‘:’ RefinedType
276-
Pattern2 ::= [id ‘as’] InfixPattern Bind(name, pat)
275+
Pattern2 ::= [id ‘as’] InfixPattern Bind(name, pat)
277276
InfixPattern ::= SimplePattern { id [nl] SimplePattern } InfixOp(pat, op, pat)
278277
SimplePattern ::= PatVar Ident(wildcard)
279278
| Literal Bind(name, Ident(wildcard))
280279
| ‘(’ [Patterns] ‘)’ Parens(pats) Tuple(pats)
281280
| Quoted
282281
| XmlPattern
283282
| SimplePattern1 [TypeArgs] [ArgumentPatterns]
283+
| ‘given’ RefinedType
284284
SimplePattern1 ::= SimpleRef
285285
| SimplePattern1 ‘.’ id
286286
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 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/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)