Skip to content

Commit 7d1f78a

Browse files
committed
Preserve TypeLambdas in wildApprox
wildApprox should not replace TypeParamRefs which are bound by TypeLambdas which are contained in the type being approximated. Fixes #i6238.
1 parent 0d0274b commit 7d1f78a

File tree

3 files changed

+215
-22
lines changed

3 files changed

+215
-22
lines changed

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

Lines changed: 44 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -579,47 +579,48 @@ object ProtoTypes {
579579
/** Approximate occurrences of parameter types and uninstantiated typevars
580580
* by wildcard types.
581581
*/
582-
private def wildApprox(tp: Type, theMap: WildApproxMap, seen: Set[TypeParamRef])(implicit ctx: Context): Type = tp match {
582+
private def wildApprox(tp: Type, theMap: WildApproxMap, seen: Set[TypeParamRef], internal: Set[TypeLambda])(implicit ctx: Context): Type = tp match {
583583
case tp: NamedType => // default case, inlined for speed
584584
val isPatternBoundTypeRef = tp.isInstanceOf[TypeRef] && tp.symbol.is(Flags.Case) && !tp.symbol.isClass
585585
if (isPatternBoundTypeRef) WildcardType(tp.underlying.bounds)
586586
else if (tp.symbol.isStatic || (tp.prefix `eq` NoPrefix)) tp
587-
else tp.derivedSelect(wildApprox(tp.prefix, theMap, seen))
587+
else tp.derivedSelect(wildApprox(tp.prefix, theMap, seen, internal))
588588
case tp @ AppliedType(tycon, args) =>
589-
wildApprox(tycon, theMap, seen) match {
589+
wildApprox(tycon, theMap, seen, internal) match {
590590
case _: WildcardType => WildcardType // this ensures we get a * type
591591
case tycon1 => tp.derivedAppliedType(tycon1,
592-
args.mapConserve(arg => wildApprox(arg, theMap, seen)))
592+
args.mapConserve(arg => wildApprox(arg, theMap, seen, internal)))
593593
}
594594
case tp: RefinedType => // default case, inlined for speed
595595
tp.derivedRefinedType(
596-
wildApprox(tp.parent, theMap, seen),
596+
wildApprox(tp.parent, theMap, seen, internal),
597597
tp.refinedName,
598-
wildApprox(tp.refinedInfo, theMap, seen))
598+
wildApprox(tp.refinedInfo, theMap, seen, internal))
599599
case tp: AliasingBounds => // default case, inlined for speed
600-
tp.derivedAlias(wildApprox(tp.alias, theMap, seen))
600+
tp.derivedAlias(wildApprox(tp.alias, theMap, seen, internal))
601+
case tp @ TypeParamRef(tl, _) if internal.contains(tl) => tp
601602
case tp @ TypeParamRef(poly, pnum) =>
602603
def wildApproxBounds(bounds: TypeBounds) =
603604
if (seen.contains(tp)) WildcardType
604-
else WildcardType(wildApprox(bounds, theMap, seen + tp).bounds)
605+
else WildcardType(wildApprox(bounds, theMap, seen + tp, internal).bounds)
605606
def unconstrainedApprox = wildApproxBounds(poly.paramInfos(pnum))
606607
def approxPoly =
607608
if (ctx.mode.is(Mode.TypevarsMissContext)) unconstrainedApprox
608609
else
609610
ctx.typerState.constraint.entry(tp) match {
610611
case bounds: TypeBounds => wildApproxBounds(bounds)
611612
case NoType => unconstrainedApprox
612-
case inst => wildApprox(inst, theMap, seen)
613+
case inst => wildApprox(inst, theMap, seen, internal)
613614
}
614615
approxPoly
615616
case TermParamRef(mt, pnum) =>
616-
WildcardType(TypeBounds.upper(wildApprox(mt.paramInfos(pnum), theMap, seen)))
617+
WildcardType(TypeBounds.upper(wildApprox(mt.paramInfos(pnum), theMap, seen, internal)))
617618
case tp: TypeVar =>
618-
wildApprox(tp.underlying, theMap, seen)
619+
wildApprox(tp.underlying, theMap, seen, internal)
619620
case tp: AndType =>
620621
def approxAnd = {
621-
val tp1a = wildApprox(tp.tp1, theMap, seen)
622-
val tp2a = wildApprox(tp.tp2, theMap, seen)
622+
val tp1a = wildApprox(tp.tp1, theMap, seen, internal)
623+
val tp2a = wildApprox(tp.tp2, theMap, seen, internal)
623624
def wildBounds(tp: Type) =
624625
if (tp.isInstanceOf[WildcardType]) tp.bounds else TypeBounds.upper(tp)
625626
if (tp1a.isInstanceOf[WildcardType] || tp2a.isInstanceOf[WildcardType])
@@ -630,33 +631,54 @@ object ProtoTypes {
630631
approxAnd
631632
case tp: OrType =>
632633
def approxOr = {
633-
val tp1a = wildApprox(tp.tp1, theMap, seen)
634-
val tp2a = wildApprox(tp.tp2, theMap, seen)
634+
val tp1a = wildApprox(tp.tp1, theMap, seen, internal)
635+
val tp2a = wildApprox(tp.tp2, theMap, seen, internal)
635636
if (tp1a.isInstanceOf[WildcardType] || tp2a.isInstanceOf[WildcardType])
636637
WildcardType(tp1a.bounds | tp2a.bounds)
637638
else
638639
tp.derivedOrType(tp1a, tp2a)
639640
}
640641
approxOr
641642
case tp: SelectionProto =>
642-
tp.derivedSelectionProto(tp.name, wildApprox(tp.memberProto, theMap, seen), NoViewsAllowed)
643+
tp.derivedSelectionProto(tp.name, wildApprox(tp.memberProto, theMap, seen, internal), NoViewsAllowed)
643644
case tp: ViewProto =>
644645
tp.derivedViewProto(
645-
wildApprox(tp.argType, theMap, seen),
646-
wildApprox(tp.resultType, theMap, seen))
646+
wildApprox(tp.argType, theMap, seen, internal),
647+
wildApprox(tp.resultType, theMap, seen, internal))
647648
case _: ThisType | _: BoundType => // default case, inlined for speed
648649
tp
650+
case tl: TypeLambda =>
651+
val internal1 = internal + tl
652+
val paramInfos = tl.paramInfos
653+
val paramInfos1 = tl.paramInfos.mapConserve {
654+
case tb @ TypeBounds(lo, hi) =>
655+
tb.derivedTypeBounds(
656+
wildApprox(lo, theMap, seen, internal1),
657+
wildApprox(hi, theMap, seen, internal1)
658+
)
659+
}
660+
val res = tl.resultType
661+
val res1 = wildApprox(res, theMap, seen, internal1)
662+
if ((res eq res1) && (paramInfos eq paramInfos1)) tl
663+
else {
664+
def substBounds(tl1: TypeLambda)(tb: TypeBounds): TypeBounds =
665+
tb.derivedTypeBounds(tb.lo.subst(tl, tl1), tb.hi.subst(tl, tl1))
666+
tl match {
667+
case _: HKTypeLambda => HKTypeLambda(tl.paramNames)(tl1 => paramInfos.map(substBounds(tl1)), tl1 => res1.subst(tl, tl1))
668+
case _: PolyType => PolyType(tl.paramNames)(tl1 => paramInfos.map(substBounds(tl1)), tl1 => res1.subst(tl, tl1))
669+
}
670+
}
649671
case _ =>
650-
(if (theMap != null && seen.eq(theMap.seen)) theMap else new WildApproxMap(seen))
672+
(if (theMap != null && seen.eq(theMap.seen)) theMap else new WildApproxMap(seen, internal))
651673
.mapOver(tp)
652674
}
653675

654-
final def wildApprox(tp: Type)(implicit ctx: Context): Type = wildApprox(tp, null, Set.empty)
676+
final def wildApprox(tp: Type)(implicit ctx: Context): Type = wildApprox(tp, null, Set.empty, Set.empty)
655677

656678
@sharable object AssignProto extends UncachedGroundType with MatchAlways
657679

658-
private[ProtoTypes] class WildApproxMap(val seen: Set[TypeParamRef])(implicit ctx: Context) extends TypeMap {
659-
def apply(tp: Type): Type = wildApprox(tp, this, seen)
680+
private[ProtoTypes] class WildApproxMap(val seen: Set[TypeParamRef], val internal: Set[TypeLambda])(implicit ctx: Context) extends TypeMap {
681+
def apply(tp: Type): Type = wildApprox(tp, this, seen, internal)
660682
}
661683

662684
/** Dummy tree to be used as an argument of a FunProto or ViewProto type */

tests/pos/i6238.scala

Lines changed: 121 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,121 @@
1+
object K1 {
2+
class Foo[T]
3+
4+
class Bar[F[_]]
5+
object Bar {
6+
implicit def barF[F[_]](implicit fooF: Foo[Bar[F]]): Bar[F] = null
7+
}
8+
9+
class A[T]
10+
object A {
11+
implicit def fooA[F[_[_]]](implicit barB: F[B]): Foo[F[A]] = null
12+
}
13+
14+
class B[T]
15+
object B {
16+
implicit def fooB[F[_[_]]]: Foo[F[B]] = null
17+
}
18+
}
19+
20+
object K1U {
21+
class Foo[T]
22+
23+
class Bar[F[_ <: Int]]
24+
object Bar {
25+
implicit def barF[F[_ <: Int]](implicit fooF: Foo[Bar[F]]): Bar[F] = null
26+
}
27+
28+
class A[T <: Int]
29+
object A {
30+
implicit def fooA[F[_[_ <: Int]]](implicit barB: F[B]): Foo[F[A]] = null
31+
}
32+
33+
class B[T <: Int]
34+
object B {
35+
implicit def fooB[F[_[_ <: Int]]]: Foo[F[B]] = null
36+
}
37+
}
38+
39+
object K1L {
40+
class Foo[T]
41+
42+
class Bar[F[_ >: Int]]
43+
object Bar {
44+
implicit def barF[F[_ >: Int]](implicit fooF: Foo[Bar[F]]): Bar[F] = null
45+
}
46+
47+
class A[T >: Int]
48+
object A {
49+
implicit def fooA[F[_[_ >: Int]]](implicit barB: F[B]): Foo[F[A]] = null
50+
}
51+
52+
class B[T >: Int]
53+
object B {
54+
implicit def fooB[F[_[_ >: Int]]]: Foo[F[B]] = null
55+
}
56+
}
57+
58+
object K11 {
59+
class Foo[T]
60+
61+
class Bar[F[_[_]]]
62+
object Bar {
63+
implicit def barF[F[_[_]]](implicit fooF: Foo[Bar[F]]): Bar[F] = null
64+
}
65+
66+
class A[T[_]]
67+
object A {
68+
implicit def fooA[F[_[_[_]]]](implicit barB: F[B]): Foo[F[A]] = null
69+
}
70+
71+
class B[T[_]]
72+
object B {
73+
implicit def fooB[F[_[_[_]]]]: Foo[F[B]] = null
74+
}
75+
}
76+
77+
object K2 {
78+
class Foo[T]
79+
80+
class Bar[F[_, _]]
81+
object Bar {
82+
implicit def barF[F[_, _]](implicit fooF: Foo[Bar[F]]): Bar[F] = null
83+
}
84+
85+
class A[T, U]
86+
object A {
87+
implicit def fooA[F[_[_, _]]](implicit barB: F[B]): Foo[F[A]] = null
88+
}
89+
90+
class B[T, U]
91+
object B {
92+
implicit def fooB[F[_[_, _]]]: Foo[F[B]] = null
93+
}
94+
}
95+
96+
object Test {
97+
{
98+
import K1._
99+
implicitly[Bar[A]]
100+
}
101+
102+
{
103+
import K1U._
104+
implicitly[Bar[A]]
105+
}
106+
107+
{
108+
import K1L._
109+
implicitly[Bar[A]]
110+
}
111+
112+
{
113+
import K11._
114+
implicitly[Bar[A]]
115+
}
116+
117+
{
118+
import K2._
119+
implicitly[Bar[A]]
120+
}
121+
}

tests/run/implicit-functors2.scala

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
object Utils {
2+
type Id[t] = t
3+
type Const[c] = [t] => c
4+
}
5+
6+
import Utils._
7+
8+
abstract class ErasedInstances { type FT }
9+
class ErasedProductInstances(override val toString: String) extends ErasedInstances
10+
class ErasedCoproductInstances(override val toString: String) extends ErasedInstances
11+
12+
object K1 {
13+
type Instances[F[_[_]], T[_]] = ErasedInstances { type FT = F[T] ; type C = F }
14+
}
15+
16+
class Functor[F[_]](override val toString: String)
17+
18+
object Functor {
19+
inline def apply[F[_]](implicit ff: Functor[F]): Functor[F] = ff
20+
21+
implicit val functorId: Functor[Id] = new Functor("functorId")
22+
23+
implicit def functorNested[F[_], G[_]](implicit ff: Functor[F], fg: Functor[G]): Functor[[t] => F[G[t]]] = new Functor(s"functorNested($ff, $fg)")
24+
25+
implicit def functorGen[F[_]](implicit inst: K1.Instances[Functor, F]): Functor[F] = new Functor(s"functorGen($inst")
26+
27+
implicit def functorConst[T]: Functor[Const[T]] = new Functor(s"functorConst")
28+
}
29+
30+
sealed trait Opt[+A]
31+
object Opt {
32+
implicit def optInstances[F[_[_]]](implicit fs: F[Sm], fn: F[[t] => Nn.type]): ErasedCoproductInstances { type FT = F[Opt] ; type C = F } =
33+
new ErasedCoproductInstances(s"optInstances($fs, $fn)") { type FT = F[Opt] ; type C = F }
34+
}
35+
36+
case class Sm[+A](value: A) extends Opt[A]
37+
object Sm {
38+
implicit def smInstances[F[_[_]]](implicit fi: F[Id]): ErasedProductInstances { type FT = F[Sm] ; type C = F } =
39+
new ErasedProductInstances(s"smInstances($fi)") { type FT = F[Sm] ; type C = F }
40+
}
41+
42+
case object Nn extends Opt[Nothing]
43+
44+
object Test extends App {
45+
assert(Functor[Const[Nn.type]].toString == "functorConst")
46+
assert(Functor[Sm].toString == "functorGen(smInstances(functorId)")
47+
assert(Functor[Opt].toString == "functorGen(optInstances(functorGen(smInstances(functorId), functorConst)")
48+
assert(Functor[[t] => Opt[Opt[t]]].toString == "functorNested(functorGen(optInstances(functorGen(smInstances(functorId), functorConst), functorGen(optInstances(functorGen(smInstances(functorId), functorConst))")
49+
}
50+

0 commit comments

Comments
 (0)