Skip to content

Commit 6cbcdc2

Browse files
authored
Merge pull request #580 from scala/backport-lts-3.3-23121
Backport "Invent given pattern name in for comprehension" to 3.3 LTS
2 parents adb08b7 + 3100c7e commit 6cbcdc2

File tree

5 files changed

+96
-8
lines changed

5 files changed

+96
-8
lines changed

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

Lines changed: 8 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1653,18 +1653,19 @@ object desugar {
16531653
* that refers to the bound variable for the pattern. Wildcard Binds are
16541654
* also replaced by Binds with fresh names.
16551655
*/
1656-
def makeIdPat(pat: Tree): (Tree, Ident) = pat match {
1657-
case bind @ Bind(name, pat1) =>
1658-
if name == nme.WILDCARD then
1659-
val name = UniqueName.fresh()
1660-
(cpy.Bind(pat)(name, pat1).withMods(bind.mods), Ident(name))
1661-
else (pat, Ident(name))
1656+
def makeIdPat(pat: Tree): (Tree, Ident) = pat match
1657+
case pat @ Bind(nme.WILDCARD, body) =>
1658+
val name =
1659+
body match
1660+
case Typed(Ident(nme.WILDCARD), tpt) if pat.mods.is(Given) => inventGivenName(tpt)
1661+
case _ => UniqueName.fresh()
1662+
(cpy.Bind(pat)(name, body).withMods(pat.mods), Ident(name))
1663+
case Bind(name, _) => (pat, Ident(name))
16621664
case id: Ident if isVarPattern(id) && id.name != nme.WILDCARD => (id, id)
16631665
case Typed(id: Ident, _) if isVarPattern(id) && id.name != nme.WILDCARD => (pat, id)
16641666
case _ =>
16651667
val name = UniqueName.fresh()
16661668
(Bind(name, pat), Ident(name))
1667-
}
16681669

16691670
/** Make a pattern filter:
16701671
* rhs.withFilter { case pat => true case _ => false }

compiler/src/dotty/tools/dotc/reporting/messages.scala

Lines changed: 20 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2087,8 +2087,27 @@ extends NamingMsg(AlreadyDefinedID):
20872087
i" in ${conflicting.associatedFile}"
20882088
else if conflicting.owner == owner then ""
20892089
else i" in ${conflicting.owner}"
2090+
def print(tpe: Type): String =
2091+
def addParams(tpe: Type): List[String] = tpe match
2092+
case tpe: MethodType =>
2093+
val s = if tpe.isContextualMethod then i"(${tpe.paramInfos}%, %) =>" else ""
2094+
s :: addParams(tpe.resType)
2095+
case tpe: PolyType =>
2096+
i"[${tpe.paramNames}%, %] =>" :: addParams(tpe.resType)
2097+
case tpe =>
2098+
i"$tpe" :: Nil
2099+
addParams(tpe).mkString(" ")
20902100
def note =
2091-
if owner.is(Method) || conflicting.is(Method) then
2101+
if conflicting.is(Given) && name.startsWith("given_") then
2102+
i"""|
2103+
|
2104+
|Provide an explicit, unique name to given definitions,
2105+
|since the names assigned to anonymous givens may clash. For example:
2106+
|
2107+
| given myGiven: ${print(atPhase(typerPhase)(conflicting.info))} // define an instance
2108+
| given myGiven @ ${print(atPhase(typerPhase)(conflicting.info))} // as a pattern variable
2109+
|"""
2110+
else if owner.is(Method) || conflicting.is(Method) then
20922111
"\n\nNote that overloaded methods must all be defined in the same group of toplevel definitions"
20932112
else ""
20942113
if conflicting.isTerm != name.isTermName then

tests/neg/i23119.check

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
-- [E161] Naming Error: tests/neg/i23119.scala:8:4 ---------------------------------------------------------------------
2+
8 | given Option[List[Int]] = Some(List(x)) // error
3+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
4+
| given_Option_List is already defined as given instance given_Option_List
5+
|
6+
| Provide an explicit, unique name to given definitions,
7+
| since the names assigned to anonymous givens may clash. For example:
8+
|
9+
| given myGiven: Option[List[String]] // define an instance
10+
| given myGiven @ Option[List[String]] // as a pattern variable
11+
-- [E161] Naming Error: tests/neg/i23119.scala:18:8 --------------------------------------------------------------------
12+
18 | given [A] => List[A] = ??? // error
13+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^
14+
| given_List_A is already defined as given instance given_List_A
15+
|
16+
| Provide an explicit, unique name to given definitions,
17+
| since the names assigned to anonymous givens may clash. For example:
18+
|
19+
| given myGiven: [A] => List[A] // define an instance
20+
| given myGiven @ [A] => List[A] // as a pattern variable

tests/neg/i23119.scala

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
//> using options -explain
2+
3+
@main def test = println:
4+
for x <- 1 to 2
5+
// works with explicit name
6+
//ols @ given Option[List[String]] = Some(List(x.toString))
7+
given Option[List[String]] = Some(List(x.toString))
8+
given Option[List[Int]] = Some(List(x)) // error
9+
yield summon[Option[List[String]]].map(ss => ss.corresponds(given_Option_List.get)((a, b) => a == b.toString))
10+
11+
// The naming clash is noticed when defining local values for "packaging":
12+
// given_Option_List is already defined as given instance given_Option_List
13+
// Previously the naming clash was noticed when extracting values in the map or do function:
14+
// duplicate pattern variable: given_Option_List
15+
16+
def also =
17+
given [A] => List[A] = ???
18+
given [A] => List[A] = ??? // error
19+
()

tests/pos/i23119.scala

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
//> using options -Wunused:patvars -Werror
2+
3+
def make: IndexedSeq[FalsePositive] =
4+
for {
5+
i <- 1 to 2
6+
given Int = i
7+
fp = FalsePositive()
8+
} yield fp
9+
10+
def broken =
11+
for
12+
i <- List(42)
13+
(x, y) = "hello" -> "world"
14+
yield
15+
s"$x, $y" * i
16+
17+
def alt: IndexedSeq[FalsePositive] =
18+
given String = "hi"
19+
for
20+
given Int <- 1 to 2
21+
j: Int = summon[Int] // simple assign because irrefutable
22+
_ = j + 1
23+
k :: Nil = j :: Nil : @unchecked // pattern in one var
24+
fp = FalsePositive(using k)
25+
yield fp
26+
27+
class FalsePositive(using Int):
28+
def usage(): Unit =
29+
println(summon[Int])

0 commit comments

Comments
 (0)