Skip to content

Commit c9a9dbc

Browse files
committed
Invent given pattern name in for comprehension
1 parent c49ff71 commit c9a9dbc

File tree

3 files changed

+105
-62
lines changed

3 files changed

+105
-62
lines changed

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

+66-55
Original file line numberDiff line numberDiff line change
@@ -13,13 +13,14 @@ import util.{Property, SourceFile, SourcePosition, SrcPos, Chars}
1313
import config.{Feature, Config}
1414
import config.Feature.{sourceVersion, migrateTo3, enabled}
1515
import config.SourceVersion.*
16-
import collection.mutable
1716
import reporting.*
1817
import printing.Formatting.hl
1918
import config.Printers
2019
import parsing.Parsers
20+
import dotty.tools.dotc.util.chaining.*
2121

2222
import scala.annotation.{unchecked as _, *}, internal.sharable
23+
import scala.collection.mutable, mutable.ListBuffer
2324

2425
object desugar {
2526
import untpd.*
@@ -272,12 +273,12 @@ object desugar {
272273
*/
273274
private def desugarContextBounds(
274275
tdef: TypeDef,
275-
evidenceBuf: mutable.ListBuffer[ValDef],
276+
evidenceBuf: ListBuffer[ValDef],
276277
evidenceFlags: FlagSet,
277278
freshName: untpd.Tree => TermName,
278279
allParamss: List[ParamClause])(using Context): TypeDef =
279280

280-
val evidenceNames = mutable.ListBuffer[TermName]()
281+
val evidenceNames = ListBuffer.empty[TermName]
281282

282283
def desugarRHS(rhs: Tree): Tree = rhs match
283284
case ContextBounds(tbounds, ctxbounds) =>
@@ -322,7 +323,7 @@ object desugar {
322323
end desugarContextBounds
323324

324325
def elimContextBounds(meth: Tree, isPrimaryConstructor: Boolean = false)(using Context): Tree =
325-
val evidenceParamBuf = mutable.ListBuffer[ValDef]()
326+
val evidenceParamBuf = ListBuffer.empty[ValDef]
326327
var seenContextBounds: Int = 0
327328
def freshName(unused: Tree) =
328329
seenContextBounds += 1 // Start at 1 like FreshNameCreator.
@@ -647,7 +648,7 @@ object desugar {
647648
* ultimately map to deferred givens.
648649
*/
649650
def typeDef(tdef: TypeDef)(using Context): Tree =
650-
val evidenceBuf = new mutable.ListBuffer[ValDef]
651+
val evidenceBuf = ListBuffer.empty[ValDef]
651652
val result = desugarContextBounds(
652653
tdef, evidenceBuf,
653654
(tdef.mods.flags.toTermFlags & AccessFlags) | Lazy | DeferredGivenFlags,
@@ -1343,7 +1344,7 @@ object desugar {
13431344
)).withSpan(tree.span)
13441345
end makePolyFunctionType
13451346

1346-
/** Invent a name for an anonympus given of type or template `impl`. */
1347+
/** Invent a name for an anonymous given of type or template `impl`. */
13471348
def inventGivenName(impl: Tree)(using Context): SimpleName =
13481349
val str = impl match
13491350
case impl: Template =>
@@ -1422,7 +1423,7 @@ object desugar {
14221423
ids.map(expand(_, false))
14231424
else {
14241425
val pats1 = if (tpt.isEmpty) pats else pats map (Typed(_, tpt))
1425-
pats1 map (makePatDef(pdef, mods, _, rhs))
1426+
pats1.map(makePatDef(original = pdef, mods, _, rhs))
14261427
}
14271428
}
14281429

@@ -1453,10 +1454,10 @@ object desugar {
14531454
* val/var/lazy val p = e
14541455
*
14551456
* Otherwise, in case there is exactly one variable x_1 in pattern
1456-
* val/var/lazy val p = e ==> val/var/lazy val x_1 = (e: @unchecked) match (case p => (x_1))
1457+
* val/var/lazy val p = e ==> val/var/lazy val x_1 = (e: @unchecked) match (case p => (x_1))
14571458
*
14581459
* in case there are zero or more than one variables in pattern
1459-
* val/var/lazy p = e ==> private[this] synthetic [lazy] val t$ = (e: @unchecked) match (case p => (x_1, ..., x_N))
1460+
* val/var/lazy p = e ==> private[this] synthetic [lazy] val t$ = (e: @unchecked) match (case p => (x_1, ..., x_N))
14601461
* val/var/def x_1 = t$._1
14611462
* ...
14621463
* val/var/def x_N = t$._N
@@ -1470,6 +1471,7 @@ object desugar {
14701471
then cpy.Ident(id)(WildcardParamName.fresh())
14711472
else id
14721473
derivedValDef(original, id1, tpt, rhs, mods)
1474+
14731475
case _ =>
14741476

14751477
def filterWildcardGivenBinding(givenPat: Bind): Boolean =
@@ -1538,7 +1540,7 @@ object desugar {
15381540
if useSelectors then Select(Ident(tmpName), nme.selectorName(n))
15391541
else Apply(Select(Ident(tmpName), nme.apply), Literal(Constant(n)) :: Nil)
15401542
val restDefs =
1541-
for (((named, tpt), n) <- vars.zipWithIndex if named.name != nme.WILDCARD)
1543+
for ((named, tpt), n) <- vars.zipWithIndex if named.name != nme.WILDCARD
15421544
yield
15431545
if mods.is(Lazy) then
15441546
DefDef(named.name.asTermName, Nil, tpt, selector(n))
@@ -2044,33 +2046,36 @@ object desugar {
20442046
makeCaseLambda(CaseDef(gen.pat, EmptyTree, body) :: Nil, matchCheckMode)
20452047
}
20462048

2047-
def hasGivenBind(pat: Tree): Boolean = pat.existsSubTree {
2048-
case pat @ Bind(_, pat1) => pat.mods.is(Given)
2049+
def hasGivenBind(pat: Tree): Boolean = pat.existsSubTree:
2050+
case pat @ Bind(_, _) => pat.mods.is(Given)
20492051
case _ => false
2050-
}
20512052

20522053
/** Does this pattern define any given bindings */
20532054
def isNestedGivenPattern(pat: Tree): Boolean = pat match
2054-
case pat @ Bind(_, pat1) => hasGivenBind(pat1)
2055+
case Bind(_, pat) => hasGivenBind(pat)
20552056
case _ => hasGivenBind(pat)
20562057

20572058
/** If `pat` is not an Identifier, a Typed(Ident, _), or a Bind, wrap
20582059
* it in a Bind with a fresh name. Return the transformed pattern, and the identifier
20592060
* that refers to the bound variable for the pattern. Wildcard Binds are
20602061
* also replaced by Binds with fresh names.
20612062
*/
2062-
def makeIdPat(pat: Tree): (Tree, Ident) = pat match {
2063-
case bind @ Bind(name, pat1) =>
2064-
if name == nme.WILDCARD then
2065-
val name = UniqueName.fresh()
2066-
(cpy.Bind(pat)(name, pat1).withMods(bind.mods), Ident(name))
2067-
else (pat, Ident(name))
2063+
def makeIdPat(pat: Tree): (Tree, Ident) = pat match
2064+
case pat @ Bind(nme.WILDCARD, body) =>
2065+
val name =
2066+
body match
2067+
case Typed(Ident(nme.WILDCARD), tpt) if pat.mods.is(Given) => inventGivenName(tpt)
2068+
case _ => UniqueName.fresh()
2069+
cpy.Bind(pat)(name, body)
2070+
.withMods(pat.mods)
2071+
->
2072+
Ident(name)
2073+
case Bind(name, _) => (pat, Ident(name))
20682074
case id: Ident if isVarPattern(id) && id.name != nme.WILDCARD => (id, id)
20692075
case Typed(id: Ident, _) if isVarPattern(id) && id.name != nme.WILDCARD => (pat, id)
20702076
case _ =>
20712077
val name = UniqueName.fresh()
20722078
(Bind(name, pat), Ident(name))
2073-
}
20742079

20752080
/** Make a pattern filter:
20762081
* rhs.withFilter { case pat => true case _ => false }
@@ -2128,11 +2133,11 @@ object desugar {
21282133
}
21292134
}
21302135

2131-
/** Is `pat` of the form `x`, `x T`, or `given T`? when used as the lhs of a generator,
2136+
/** Is `pat` of the form `x`, `x: T`, or `given T`? when used as the lhs of a generator,
21322137
* these are all considered irrefutable.
21332138
*/
21342139
def isVarBinding(pat: Tree): Boolean = pat match
2135-
case pat @ Bind(_, pat1) if pat.mods.is(Given) => isVarBinding(pat1)
2140+
case bind @ Bind(_, pat) if bind.mods.is(Given) => isVarBinding(pat)
21362141
case IdPattern(_) => true
21372142
case _ => false
21382143

@@ -2173,36 +2178,42 @@ object desugar {
21732178
case (gen: GenFrom) :: (rest @ (GenFrom(_, _, _) :: _)) =>
21742179
val cont = makeFor(mapName, flatMapName, rest, body)
21752180
Apply(rhsSelect(gen, flatMapName), makeLambda(gen, cont))
2176-
case (gen: GenFrom) :: rest
2177-
if sourceVersion.enablesBetterFors
2178-
&& rest.dropWhile(_.isInstanceOf[GenAlias]).headOption.forall(e => e.isInstanceOf[GenFrom]) // possible aliases followed by a generator or end of for
2179-
&& !rest.takeWhile(_.isInstanceOf[GenAlias]).exists(a => isNestedGivenPattern(a.asInstanceOf[GenAlias].pat)) =>
2180-
val cont = makeFor(mapName, flatMapName, rest, body)
2181-
val selectName =
2182-
if rest.exists(_.isInstanceOf[GenFrom]) then flatMapName
2183-
else mapName
2184-
val aply = Apply(rhsSelect(gen, selectName), makeLambda(gen, cont))
2185-
markTrailingMap(aply, gen, selectName)
2186-
aply
2187-
case (gen: GenFrom) :: (rest @ GenAlias(_, _) :: _) =>
2188-
val (valeqs, rest1) = rest.span(_.isInstanceOf[GenAlias])
2189-
val pats = valeqs map { case GenAlias(pat, _) => pat }
2190-
val rhss = valeqs map { case GenAlias(_, rhs) => rhs }
2191-
val (defpat0, id0) = makeIdPat(gen.pat)
2192-
val (defpats, ids) = (pats map makeIdPat).unzip
2193-
val pdefs = valeqs.lazyZip(defpats).lazyZip(rhss).map { (valeq, defpat, rhs) =>
2194-
val mods = defpat match
2195-
case defTree: DefTree => defTree.mods
2196-
case _ => Modifiers()
2197-
makePatDef(valeq, mods, defpat, rhs)
2198-
}
2199-
val rhs1 = makeFor(nme.map, nme.flatMap, GenFrom(defpat0, gen.expr, gen.checkMode) :: Nil, Block(pdefs, makeTuple(id0 :: ids).withAttachment(ForArtifact, ())))
2200-
val allpats = gen.pat :: pats
2201-
val vfrom1 = GenFrom(makeTuple(allpats), rhs1, GenCheckMode.Ignore)
2202-
makeFor(mapName, flatMapName, vfrom1 :: rest1, body)
2181+
case (gen: GenFrom) :: (tail @ GenAlias(_, _) :: _) =>
2182+
val (valeqs, suffix) = tail.span(_.isInstanceOf[GenAlias])
2183+
// possible aliases followed by a generator or end of for, when betterFors.
2184+
// exclude value definitions with a given pattern (given T = x)
2185+
val better = sourceVersion.enablesBetterFors
2186+
&& suffix.headOption.forall(_.isInstanceOf[GenFrom])
2187+
&& !valeqs.exists(a => isNestedGivenPattern(a.asInstanceOf[GenAlias].pat))
2188+
if better then
2189+
val cont = makeFor(mapName, flatMapName, enums = tail, body)
2190+
val selectName =
2191+
if suffix.exists(_.isInstanceOf[GenFrom]) then flatMapName
2192+
else mapName
2193+
Apply(rhsSelect(gen, selectName), makeLambda(gen, cont))
2194+
.tap(markTrailingMap(_, gen, selectName))
2195+
else
2196+
val (pats, rhss) = valeqs.map { case GenAlias(pat, rhs) => (pat, rhs) }.unzip
2197+
val (defpat0, id0) = makeIdPat(gen.pat)
2198+
val (defpats, ids) = pats.map(makeIdPat).unzip
2199+
val pdefs = valeqs.lazyZip(defpats).lazyZip(rhss).map: (valeq, defpat, rhs) =>
2200+
val mods = defpat match
2201+
case defTree: DefTree => defTree.mods
2202+
case _ => Modifiers()
2203+
makePatDef(original = valeq, mods, defpat, rhs)
2204+
val rhs1 =
2205+
val enums = GenFrom(defpat0, gen.expr, gen.checkMode) :: Nil
2206+
val body = Block(pdefs, makeTuple(id0 :: ids).withAttachment(ForArtifact, ()))
2207+
makeFor(nme.map, nme.flatMap, enums, body)
2208+
val allpats = gen.pat :: pats
2209+
val vfrom1 = GenFrom(makeTuple(allpats), rhs1, GenCheckMode.Ignore)
2210+
makeFor(mapName, flatMapName, enums = vfrom1 :: suffix, body)
2211+
end if
22032212
case (gen: GenFrom) :: test :: rest =>
2204-
val filtered = Apply(rhsSelect(gen, nme.withFilter), makeLambda(gen, test))
2205-
val genFrom = GenFrom(gen.pat, filtered, if sourceVersion.enablesBetterFors then GenCheckMode.Filtered else GenCheckMode.Ignore)
2213+
val genFrom =
2214+
val filtered = Apply(rhsSelect(gen, nme.withFilter), makeLambda(gen, test))
2215+
val mode = if sourceVersion.enablesBetterFors then GenCheckMode.Filtered else GenCheckMode.Ignore
2216+
GenFrom(gen.pat, filtered, mode)
22062217
makeFor(mapName, flatMapName, genFrom :: rest, body)
22072218
case GenAlias(_, _) :: _ if sourceVersion.enablesBetterFors =>
22082219
val (valeqs, rest) = enums.span(_.isInstanceOf[GenAlias])
@@ -2213,7 +2224,7 @@ object desugar {
22132224
val mods = defpat match
22142225
case defTree: DefTree => defTree.mods
22152226
case _ => Modifiers()
2216-
makePatDef(valeq, mods, defpat, rhs)
2227+
makePatDef(original = valeq, mods, defpat, rhs)
22172228
}
22182229
Block(pdefs, makeFor(mapName, flatMapName, rest, body))
22192230
case _ =>
@@ -2279,7 +2290,7 @@ object desugar {
22792290
makeFor(nme.map, nme.flatMap, enums, body) orElse tree
22802291
case PatDef(mods, pats, tpt, rhs) =>
22812292
val pats1 = if (tpt.isEmpty) pats else pats map (Typed(_, tpt))
2282-
flatTree(pats1 map (makePatDef(tree, mods, _, rhs)))
2293+
flatTree(pats1.map(makePatDef(original = tree, mods, _, rhs)))
22832294
case ext: ExtMethods =>
22842295
Block(List(ext), syntheticUnitLiteral.withSpan(ext.span))
22852296
case f: FunctionWithMods if f.hasErasedParams => makeFunctionWithValDefs(f, pt)
@@ -2396,7 +2407,7 @@ object desugar {
23962407
* without duplicates
23972408
*/
23982409
private def getVariables(tree: Tree, shouldAddGiven: Context ?=> Bind => Boolean)(using Context): List[VarInfo] = {
2399-
val buf = mutable.ListBuffer[VarInfo]()
2410+
val buf = ListBuffer.empty[VarInfo]
24002411
def seenName(name: Name) = buf exists (_._1.name == name)
24012412
def add(named: NameTree, t: Tree): Unit =
24022413
if (!seenName(named.name) && named.name.isTermName) buf += ((named, t))

compiler/src/dotty/tools/dotc/transform/CheckUnused.scala

+10-7
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ import dotty.tools.dotc.rewrites.Rewrites
1919
import dotty.tools.dotc.transform.MegaPhase.MiniPhase
2020
import dotty.tools.dotc.typer.{ImportInfo, Typer}
2121
import dotty.tools.dotc.typer.Deriving.OriginalTypeClass
22-
import dotty.tools.dotc.util.{Property, Spans, SrcPos}, Spans.Span
22+
import dotty.tools.dotc.util.{Property, Spans, SrcPos}, Spans.{NoSpan, Span}
2323
import dotty.tools.dotc.util.Chars.{isLineBreakChar, isWhitespace}
2424
import dotty.tools.dotc.util.chaining.*
2525

@@ -426,12 +426,14 @@ object CheckUnused:
426426
class PostInlining extends CheckUnused(PhaseMode.Report, "PostInlining")
427427

428428
class RefInfos:
429-
val defs = mutable.Set.empty[(Symbol, SrcPos)] // definitions
430-
val pats = mutable.Set.empty[(Symbol, SrcPos)] // pattern variables
431-
val refs = mutable.Set.empty[Symbol] // references
432-
val asss = mutable.Set.empty[Symbol] // targets of assignment
433-
val skip = mutable.Set.empty[Symbol] // methods to skip (don't warn about their params)
434-
val nowarn = mutable.Set.empty[Symbol] // marked @nowarn
429+
val defs, // definitions
430+
pats // pattern variables
431+
= mutable.Set.empty[(Symbol, SrcPos)]
432+
val refs, // references
433+
asss, // targets of assignment
434+
skip, // methods to skip (don't warn about their params)
435+
nowarn // marked @nowarn
436+
= mutable.Set.empty[Symbol]
435437
val imps = new IdentityHashMap[Import, Unit] // imports
436438
val sels = new IdentityHashMap[ImportSelector, Unit] // matched selectors
437439
def register(tree: Tree)(using Context): Unit = if inlined.isEmpty then
@@ -606,6 +608,7 @@ object CheckUnused:
606608
warnAt(pos)(UnusedSymbol.localDefs)
607609

608610
def checkPatvars() =
611+
//println(infos.pats.mkString("PATVARS\n", "\n", "\n----\n"))
609612
// convert the one non-synthetic span so all are comparable; filter NoSpan below
610613
def uniformPos(sym: Symbol, pos: SrcPos): SrcPos =
611614
if pos.span.isSynthetic then pos else pos.sourcePos.withSpan(pos.span.toSynthetic)

tests/pos/i23119.scala

+29
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)