diff --git a/compiler/src/dotty/tools/dotc/typer/ProtoTypes.scala b/compiler/src/dotty/tools/dotc/typer/ProtoTypes.scala index 7e6e801c357f..22d771a74efd 100644 --- a/compiler/src/dotty/tools/dotc/typer/ProtoTypes.scala +++ b/compiler/src/dotty/tools/dotc/typer/ProtoTypes.scala @@ -598,6 +598,7 @@ object ProtoTypes { wildApprox(tp.refinedInfo, theMap, seen)) case tp: AliasingBounds => // default case, inlined for speed tp.derivedAlias(wildApprox(tp.alias, theMap, seen)) + case tp @ TypeParamRef(hktl: HKTypeLambda, _) if representsKind(hktl) => tp case tp @ TypeParamRef(poly, pnum) => def wildApproxBounds(bounds: TypeBounds) = if (seen.contains(tp)) WildcardType @@ -653,6 +654,18 @@ object ProtoTypes { final def wildApprox(tp: Type)(implicit ctx: Context): Type = wildApprox(tp, null, Set.empty) + final def representsKind(tl: HKTypeLambda)(implicit ctx: Context): Boolean = { + tl.paramInfos.forall { + case TypeBounds(lo, hi) => + def isKindBound(tp: Type): Boolean = + Inferencing.isFullyDefined(tp, ForceDegree.none) || (tp match { + case tp: HKTypeLambda => representsKind(tp) + case _ => false + }) + isKindBound(lo) && isKindBound(hi) + } + } + @sharable object AssignProto extends UncachedGroundType with MatchAlways private[ProtoTypes] class WildApproxMap(val seen: Set[TypeParamRef])(implicit ctx: Context) extends TypeMap { diff --git a/tests/pos/i6238.scala b/tests/pos/i6238.scala new file mode 100644 index 000000000000..9cd6d56d95ed --- /dev/null +++ b/tests/pos/i6238.scala @@ -0,0 +1,121 @@ +object K1 { + class Foo[T] + + class Bar[F[_]] + object Bar { + implicit def barF[F[_]](implicit fooF: Foo[Bar[F]]): Bar[F] = null + } + + class A[T] + object A { + implicit def fooA[F[_[_]]](implicit barB: F[B]): Foo[F[A]] = null + } + + class B[T] + object B { + implicit def fooB[F[_[_]]]: Foo[F[B]] = null + } +} + +object K1U { + class Foo[T] + + class Bar[F[_ <: Int]] + object Bar { + implicit def barF[F[_ <: Int]](implicit fooF: Foo[Bar[F]]): Bar[F] = null + } + + class A[T <: Int] + object A { + implicit def fooA[F[_[_ <: Int]]](implicit barB: F[B]): Foo[F[A]] = null + } + + class B[T <: Int] + object B { + implicit def fooB[F[_[_ <: Int]]]: Foo[F[B]] = null + } +} + +object K1L { + class Foo[T] + + class Bar[F[_ >: Int]] + object Bar { + implicit def barF[F[_ >: Int]](implicit fooF: Foo[Bar[F]]): Bar[F] = null + } + + class A[T >: Int] + object A { + implicit def fooA[F[_[_ >: Int]]](implicit barB: F[B]): Foo[F[A]] = null + } + + class B[T >: Int] + object B { + implicit def fooB[F[_[_ >: Int]]]: Foo[F[B]] = null + } +} + +object K11 { + class Foo[T] + + class Bar[F[_[_]]] + object Bar { + implicit def barF[F[_[_]]](implicit fooF: Foo[Bar[F]]): Bar[F] = null + } + + class A[T[_]] + object A { + implicit def fooA[F[_[_[_]]]](implicit barB: F[B]): Foo[F[A]] = null + } + + class B[T[_]] + object B { + implicit def fooB[F[_[_[_]]]]: Foo[F[B]] = null + } +} + +object K2 { + class Foo[T] + + class Bar[F[_, _]] + object Bar { + implicit def barF[F[_, _]](implicit fooF: Foo[Bar[F]]): Bar[F] = null + } + + class A[T, U] + object A { + implicit def fooA[F[_[_, _]]](implicit barB: F[B]): Foo[F[A]] = null + } + + class B[T, U] + object B { + implicit def fooB[F[_[_, _]]]: Foo[F[B]] = null + } +} + +object Test { + { + import K1._ + implicitly[Bar[A]] + } + + { + import K1U._ + implicitly[Bar[A]] + } + + { + import K1L._ + implicitly[Bar[A]] + } + + { + import K11._ + implicitly[Bar[A]] + } + + { + import K2._ + implicitly[Bar[A]] + } +} diff --git a/tests/run/implicit-functors2.scala b/tests/run/implicit-functors2.scala new file mode 100644 index 000000000000..9a813253e9a3 --- /dev/null +++ b/tests/run/implicit-functors2.scala @@ -0,0 +1,50 @@ +object Utils { + type Id[t] = t + type Const[c] = [t] => c +} + +import Utils._ + +abstract class ErasedInstances { type FT } +class ErasedProductInstances(override val toString: String) extends ErasedInstances +class ErasedCoproductInstances(override val toString: String) extends ErasedInstances + +object K1 { + type Instances[F[_[_]], T[_]] = ErasedInstances { type FT = F[T] ; type C = F } +} + +class Functor[F[_]](override val toString: String) + +object Functor { + inline def apply[F[_]](implicit ff: Functor[F]): Functor[F] = ff + + implicit val functorId: Functor[Id] = new Functor("functorId") + + implicit def functorNested[F[_], G[_]](implicit ff: Functor[F], fg: Functor[G]): Functor[[t] => F[G[t]]] = new Functor(s"functorNested($ff, $fg)") + + implicit def functorGen[F[_]](implicit inst: K1.Instances[Functor, F]): Functor[F] = new Functor(s"functorGen($inst") + + implicit def functorConst[T]: Functor[Const[T]] = new Functor(s"functorConst") +} + +sealed trait Opt[+A] +object Opt { + implicit def optInstances[F[_[_]]](implicit fs: F[Sm], fn: F[[t] => Nn.type]): ErasedCoproductInstances { type FT = F[Opt] ; type C = F } = + new ErasedCoproductInstances(s"optInstances($fs, $fn)") { type FT = F[Opt] ; type C = F } +} + +case class Sm[+A](value: A) extends Opt[A] +object Sm { + implicit def smInstances[F[_[_]]](implicit fi: F[Id]): ErasedProductInstances { type FT = F[Sm] ; type C = F } = + new ErasedProductInstances(s"smInstances($fi)") { type FT = F[Sm] ; type C = F } +} + +case object Nn extends Opt[Nothing] + +object Test extends App { + assert(Functor[Const[Nn.type]].toString == "functorConst") + assert(Functor[Sm].toString == "functorGen(smInstances(functorId)") + assert(Functor[Opt].toString == "functorGen(optInstances(functorGen(smInstances(functorId), functorConst)") + assert(Functor[[t] => Opt[Opt[t]]].toString == "functorNested(functorGen(optInstances(functorGen(smInstances(functorId), functorConst), functorGen(optInstances(functorGen(smInstances(functorId), functorConst))") +} +