Skip to content

Commit db24586

Browse files
committed
forbid given patterns in val definitions.
Suggest that they use an alias given instead.
1 parent 5ac62a1 commit db24586

File tree

5 files changed

+151
-14
lines changed

5 files changed

+151
-14
lines changed

compiler/src/dotty/tools/dotc/ast/Desugar.scala

+42-9
Original file line numberDiff line numberDiff line change
@@ -1093,6 +1093,24 @@ object desugar {
10931093
case IdPattern(named, tpt) =>
10941094
derivedValDef(original, named, tpt, rhs, mods)
10951095
case _ =>
1096+
def givenPatternMessage(givenPat: Bind, tpt: Tree)(using Context): String =
1097+
val pat =
1098+
val givenTpt = em"${hl("given")} $tpt"
1099+
if givenPat.name == nme.WILDCARD then
1100+
givenTpt
1101+
else
1102+
em"${givenPat.name} @ $givenTpt"
1103+
em"""pattern `$pat` is not allowed here,
1104+
|please bind to a variable and use an alias given.""".stripMargin
1105+
end givenPatternMessage
1106+
1107+
def filterWildcardGivenBinding(givenPat: Bind, tpt: Tree): Boolean =
1108+
givenPat.name != nme.WILDCARD
1109+
1110+
def errorOnGivenBinding(bind: Bind, tpt: Tree)(using Context): Boolean =
1111+
report.error(givenPatternMessage(bind, tpt), bind)
1112+
false
1113+
10961114
def isTuplePattern(arity: Int): Boolean = pat match {
10971115
case Tuple(pats) if pats.size == arity =>
10981116
pats.forall(isVarPattern)
@@ -1108,13 +1126,23 @@ object desugar {
11081126
// - `pat` is a tuple of N variables or wildcard patterns like `(x1, x2, ..., xN)`
11091127
val tupleOptimizable = forallResults(rhs, isMatchingTuple)
11101128

1129+
val inAliasGenerator = original match
1130+
case _: GenAlias => true
1131+
case _ => false
1132+
11111133
val vars =
11121134
if (tupleOptimizable) // include `_`
1113-
pat match {
1114-
case Tuple(pats) =>
1115-
pats.map { case id: Ident => id -> TypeTree() }
1116-
}
1117-
else getVariables(pat) // no `_`
1135+
pat match
1136+
case Tuple(pats) => pats.map { case id: Ident => id -> TypeTree() }
1137+
else
1138+
getVariables(
1139+
tree = pat,
1140+
shouldAddGiven =
1141+
if inAliasGenerator then
1142+
filterWildcardGivenBinding
1143+
else
1144+
errorOnGivenBinding
1145+
) // no `_`
11181146

11191147
val ids = for ((named, _) <- vars) yield Ident(named.name)
11201148
val caseDef = CaseDef(pat, EmptyTree, makeTuple(ids))
@@ -1800,16 +1828,21 @@ object desugar {
18001828
/** Returns list of all pattern variables, possibly with their types,
18011829
* without duplicates
18021830
*/
1803-
private def getVariables(tree: Tree)(using Context): List[VarInfo] = {
1831+
private def getVariables(tree: Tree, shouldAddGiven: Context ?=> (Bind, Tree) => Boolean)(using Context): List[VarInfo] = {
18041832
val buf = ListBuffer[VarInfo]()
18051833
def seenName(name: Name) = buf exists (_._1.name == name)
18061834
def add(named: NameTree, t: Tree): Unit =
18071835
if (!seenName(named.name) && named.name.isTermName) buf += ((named, t))
18081836
def collect(tree: Tree): Unit = tree match {
1809-
case Bind(nme.WILDCARD, tree1) =>
1837+
case tree @ Bind(nme.WILDCARD, tree1) =>
1838+
if tree.mods.is(Given) then
1839+
val Typed(_, tpt) = tree1: @unchecked
1840+
if shouldAddGiven(tree, tpt) then
1841+
add(tree, tpt)
18101842
collect(tree1)
18111843
case tree @ Bind(_, Typed(tree1, tpt)) =>
1812-
add(tree, tpt)
1844+
if !(tree.mods.is(Given) && !shouldAddGiven(tree, tpt)) then
1845+
add(tree, tpt)
18131846
collect(tree1)
18141847
case tree @ Bind(_, tree1) =>
18151848
add(tree, TypeTree())
@@ -1827,7 +1860,7 @@ object desugar {
18271860
case SeqLiteral(elems, _) =>
18281861
elems foreach collect
18291862
case Alternative(trees) =>
1830-
for (tree <- trees; (vble, _) <- getVariables(tree))
1863+
for (tree <- trees; (vble, _) <- getVariables(tree, shouldAddGiven))
18311864
report.error(IllegalVariableInPatternAlternative(), vble.srcPos)
18321865
case Annotated(arg, _) =>
18331866
collect(arg)

tests/neg/given-pattern.scala

+5-5
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 Ordering[T]) = x
8-
new TreeSet[T] // error: no implicit ordering defined for T
7+
val (given Ordering[T]) = x // error: given Ordering[T] not allowed here
8+
new TreeSet[T] // error: no implicit ordering defined for T
99
}
1010
def f3[T](x: Ordering[T]) = {
11-
val given Ordering[T] = x
12-
new TreeSet[T] // error: no implicit ordering defined for T
11+
val given Ordering[T] = x // error: given Ordering[T] not allowed here
12+
new TreeSet[T] // error: no implicit ordering defined for T
1313
}
14-
}
14+
}

tests/neg/i11897.check

+45
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
-- Error: tests/neg/i11897.scala:11:10 ---------------------------------------------------------------------------------
2+
11 | val (x, given A) = (1, A(23)) // error
3+
| ^^^^^^^
4+
| pattern `given A` is not allowed here,
5+
| please bind to a variable and use an alias given.
6+
-- Error: tests/neg/i11897.scala:12:10 ---------------------------------------------------------------------------------
7+
12 | val (_, given B) = (true, B(false)) // error
8+
| ^^^^^^^
9+
| pattern `given B` is not allowed here,
10+
| please bind to a variable and use an alias given.
11+
-- Error: tests/neg/i11897.scala:13:8 ----------------------------------------------------------------------------------
12+
13 | val D(given C) = D(C("c")) // error
13+
| ^^^^^^^
14+
| pattern `given C` is not allowed here,
15+
| please bind to a variable and use an alias given.
16+
-- Error: tests/neg/i11897.scala:14:11 ---------------------------------------------------------------------------------
17+
14 | val F(y, given E) = F(47, E(93)) // error
18+
| ^^^^^^^
19+
| pattern `given E` is not allowed here,
20+
| please bind to a variable and use an alias given.
21+
-- Error: tests/neg/i11897.scala:15:11 ---------------------------------------------------------------------------------
22+
15 | val H(z, q @ given G) = H(47, G(101)) // error
23+
| ^^^^^^^^^^^
24+
| pattern `q @ given G` is not allowed here,
25+
| please bind to a variable and use an alias given.
26+
-- Error: tests/neg/i11897.scala:16:18 ---------------------------------------------------------------------------------
27+
16 | assert(summon[A] == A(23)) // error
28+
| ^
29+
| no implicit argument of type A was found for parameter x of method summon in object Predef
30+
-- Error: tests/neg/i11897.scala:17:18 ---------------------------------------------------------------------------------
31+
17 | assert(summon[B] == B(false)) // error
32+
| ^
33+
| no implicit argument of type B was found for parameter x of method summon in object Predef
34+
-- Error: tests/neg/i11897.scala:18:18 ---------------------------------------------------------------------------------
35+
18 | assert(summon[C] == C("c")) // error
36+
| ^
37+
| no implicit argument of type C was found for parameter x of method summon in object Predef
38+
-- Error: tests/neg/i11897.scala:19:18 ---------------------------------------------------------------------------------
39+
19 | assert(summon[E] == E(93)) // error
40+
| ^
41+
| no implicit argument of type E was found for parameter x of method summon in object Predef
42+
-- Error: tests/neg/i11897.scala:20:18 ---------------------------------------------------------------------------------
43+
20 | assert(summon[G] == G(101)) // error
44+
| ^
45+
| no implicit argument of type G was found for parameter x of method summon in object Predef

tests/neg/i11897.scala

+20
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
case class A(i: Int)
2+
case class B(b: Boolean)
3+
case class C(s: String)
4+
case class D(c: C)
5+
case class E(i: Int)
6+
case class F(i: Int, e: E)
7+
case class G(i: Int)
8+
case class H(i: Int, e: G)
9+
10+
def Test =
11+
val (x, given A) = (1, A(23)) // error
12+
val (_, given B) = (true, B(false)) // error
13+
val D(given C) = D(C("c")) // error
14+
val F(y, given E) = F(47, E(93)) // error
15+
val H(z, q @ given G) = H(47, G(101)) // error
16+
assert(summon[A] == A(23)) // error
17+
assert(summon[B] == B(false)) // error
18+
assert(summon[C] == C("c")) // error
19+
assert(summon[E] == E(93)) // error
20+
assert(summon[G] == G(101)) // error

tests/run/i10178.scala

+39
Original file line numberDiff line numberDiff line change
@@ -3,3 +3,42 @@
33
x <- Option(23)
44
given Int = x
55
do assert(summon[Int] == 23)
6+
7+
for
8+
y <- Option("ok")
9+
q @ given String = y
10+
do assert(summon[String] == "ok")
11+
12+
for
13+
z <- Option("key" -> true)
14+
(q @ given String, u @ given Boolean) = z
15+
do
16+
assert(summon[String] == "key")
17+
assert(summon[Boolean] == true)
18+
19+
for
20+
w <- Option("no" -> false)
21+
(given String, given Boolean) = w
22+
do
23+
assert(summon[String] == "no")
24+
assert(summon[Boolean] == false)
25+
26+
for
27+
given Int <- Option(23)
28+
do assert(summon[Int] == 23)
29+
30+
for
31+
q @ given String <- Option("ok")
32+
do assert(summon[String] == "ok")
33+
34+
for
35+
(q @ given String, u @ given Boolean) <- Option("key" -> true)
36+
do
37+
assert(summon[String] == "key")
38+
assert(summon[Boolean] == true)
39+
40+
for
41+
(given String, given Boolean) <- Option("no" -> false)
42+
do
43+
assert(summon[String] == "no")
44+
assert(summon[Boolean] == false)

0 commit comments

Comments
 (0)