Skip to content

Commit 38686ed

Browse files
committed
Elide conversion of receiver in DropForMap
1 parent 7093dad commit 38686ed

File tree

2 files changed

+78
-8
lines changed

2 files changed

+78
-8
lines changed

compiler/src/dotty/tools/dotc/transform/localopt/DropForMap.scala

Lines changed: 24 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,9 @@ package dotty.tools.dotc
22
package transform.localopt
33

44
import dotty.tools.dotc.ast.tpd.*
5-
import dotty.tools.dotc.core.Decorators.*
65
import dotty.tools.dotc.core.Contexts.*
6+
import dotty.tools.dotc.core.Decorators.*
7+
import dotty.tools.dotc.core.Flags.*
78
import dotty.tools.dotc.core.StdNames.*
89
import dotty.tools.dotc.core.Symbols.*
910
import dotty.tools.dotc.core.Types.*
@@ -25,14 +26,20 @@ class DropForMap extends MiniPhase:
2526
override def description: String = DropForMap.description
2627

2728
override def transformApply(tree: Apply)(using Context): Tree =
28-
if !tree.hasAttachment(desugar.TrailingForMap) then tree
29-
else tree match
30-
case aply @ Apply(MapCall(f), List(Lambda(List(param), body)))
31-
if f.tpe =:= aply.tpe => // make sure that the type of the expression won't change
32-
f // drop the map call
29+
tree.removeAttachment(desugar.TrailingForMap) match
30+
case Some(_) =>
31+
tree match
32+
case aply @ Apply(MapCall(f), List(Lambda(List(param), body))) =>
33+
if f.tpe =:= aply.tpe then // make sure that the type of the expression won't change
34+
return f // drop the map call
35+
else
36+
f match
37+
case Converted(r) if r.tpe =:= aply.tpe =>
38+
return r // drop the map call and the conversion
39+
case _ =>
3340
case _ =>
34-
tree.removeAttachment(desugar.TrailingForMap)
35-
tree
41+
case _ =>
42+
tree
3643

3744
private object Lambda:
3845
def unapply(tree: Tree)(using Context): Option[(List[ValDef], Tree)] =
@@ -49,6 +56,15 @@ class DropForMap extends MiniPhase:
4956
case TypeApply(fn, _) => unapply(fn)
5057
case _ => None
5158

59+
private object Converted:
60+
def unapply(tree: Tree)(using Context): Option[Tree] = tree match
61+
case Apply(fn @ Apply(_, _), _) => unapply(fn)
62+
case Apply(fn, r :: Nil)
63+
if fn.symbol.is(Implicit) || fn.symbol.name == nme.apply && fn.symbol.owner.derivesFrom(defn.ConversionClass)
64+
=> Some(r)
65+
case TypeApply(fn, _) => unapply(fn)
66+
case _ => None
67+
5268
object DropForMap:
5369
val name: String = "dropForMap"
5470
val description: String = "Drop unused trailing map calls in for comprehensions"

tests/run/i23409.scala

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
2+
//> using options -preview
3+
4+
// dropForMap should be aware of conversions to receiver
5+
6+
import language.implicitConversions
7+
8+
trait Func[F[_]]:
9+
def map[A, B](fa: F[A])(f: A => B): F[B]
10+
11+
object Func:
12+
trait Ops[F[_], A]:
13+
type T <: Func[F]
14+
def t: T
15+
def fa: F[A]
16+
def map[B](f: A => B): F[B] = t.map[A, B](fa)(f)
17+
18+
object OldStyle:
19+
implicit def cv[F[_], A](fa0: F[A])(using Func[F]): Ops[F, A] { type T = Func[F] } =
20+
new Ops[F, A]:
21+
type T = Func[F]
22+
def t: T = summon[Func[F]]
23+
def fa = fa0
24+
25+
object NewStyle:
26+
given [F[_], A] => Func[F] => Conversion[F[A], Ops[F, A] { type T = Func[F] }]:
27+
def apply(fa0: F[A]): Ops[F, A] { type T = Func[F] } =
28+
new Ops[F, A]:
29+
type T = Func[F]
30+
def t: T = summon[Func[F]]
31+
def fa = fa0
32+
end Func
33+
34+
def works =
35+
for i <- List(42) yield i
36+
37+
class C[A]
38+
object C:
39+
given Func[C]:
40+
def map[A, B](fa: C[A])(f: A => B): C[B] = ??? // must be elided
41+
42+
def implicitlyConverted() = println:
43+
import Func.OldStyle.given
44+
//C().map(x => x) --> C()
45+
for x <- C() yield x
46+
47+
def usingConversion() = println:
48+
import Func.NewStyle.given
49+
//C().map(x => x) --> C()
50+
for x <- C() yield x
51+
52+
@main def Test =
53+
implicitlyConverted()
54+
usingConversion()

0 commit comments

Comments
 (0)