Skip to content

Commit a2fb70f

Browse files
committed
Fix #10178: Put the wildcard demon back in the bottle
This was a very interesting bug. Why did `any2stringadd` appear out of the blue? The first observation was that it was resolved by an import of an identifier with `_` as its name. Why? Because we have a standard predefined import ```scala import Predef.{any2stringadd => _, _} ``` The correct encoding for the removal `_` is as an empty tree, but by mistake we generated an import with a `_` ident instead. So this counted as a renaming of `any2stringadd` to the identifier `_`. How did we end up with an identifier `_` that needed to be resolved? This was a second bug in Desugar, method `makeIdPat`. Here we needed to create binds for parts of a pattern. If the pattern was already a Bind, we used that one instead. But the `Bind` in question was anonymous, using `_` as the pattern variable. So we did not replace that by a fresh identifier, but used `_` as the identifier instead. However, `_` is treated specially as a binder; it does not generate a symbol in the enclosing scope. So resolving the `_` identifier did not see the enclosing bind and searched further out instead, until it found the "renamed" import. Beautiful! It shows that treating wildcards as some sort of identifiers is fraught with surprises.
1 parent 1ab76c1 commit a2fb70f

File tree

3 files changed

+13
-3
lines changed

3 files changed

+13
-3
lines changed

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

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1456,10 +1456,15 @@ object desugar {
14561456

14571457
/** If `pat` is not an Identifier, a Typed(Ident, _), or a Bind, wrap
14581458
* it in a Bind with a fresh name. Return the transformed pattern, and the identifier
1459-
* that refers to the bound variable for the pattern.
1459+
* that refers to the bound variable for the pattern. Wildcard Binds are
1460+
* also replaced by Binds with fresh names.
14601461
*/
14611462
def makeIdPat(pat: Tree): (Tree, Ident) = pat match {
1462-
case Bind(name, _) => (pat, Ident(name))
1463+
case Bind(name, pat1) =>
1464+
if name == nme.WILDCARD then
1465+
val name = UniqueName.fresh()
1466+
(cpy.Bind(pat)(name, pat1), Ident(name))
1467+
else (pat, Ident(name))
14631468
case id: Ident if isVarPattern(id) && id.name != nme.WILDCARD => (id, id)
14641469
case Typed(id: Ident, _) if isVarPattern(id) && id.name != nme.WILDCARD => (pat, id)
14651470
case _ =>

compiler/src/dotty/tools/dotc/typer/ImportInfo.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ object ImportInfo {
2626
:: untpd.ImportSelector(untpd.Ident(nme.EMPTY)) // ... and also all given members
2727
:: Nil
2828
if ref.isPredef then // do not import any2stringadd
29-
selectors = untpd.ImportSelector(untpd.Ident(nme.any2stringadd), untpd.Ident(nme.WILDCARD))
29+
selectors = untpd.ImportSelector(untpd.Ident(nme.any2stringadd), untpd.EmptyTree)
3030
:: selectors
3131

3232
def sym(using Context) =

tests/run/i10178.scala

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
@main def Test: Unit =
2+
for
3+
x <- Option(23)
4+
given Int = x
5+
do assert(summon[Int] == 23)

0 commit comments

Comments
 (0)