diff --git a/compiler/src/dotty/tools/dotc/typer/ProtoTypes.scala b/compiler/src/dotty/tools/dotc/typer/ProtoTypes.scala index 7e6e801c357f..d2736866121a 100644 --- a/compiler/src/dotty/tools/dotc/typer/ProtoTypes.scala +++ b/compiler/src/dotty/tools/dotc/typer/ProtoTypes.scala @@ -579,29 +579,30 @@ object ProtoTypes { /** Approximate occurrences of parameter types and uninstantiated typevars * by wildcard types. */ - private def wildApprox(tp: Type, theMap: WildApproxMap, seen: Set[TypeParamRef])(implicit ctx: Context): Type = tp match { + private def wildApprox(tp: Type, theMap: WildApproxMap, seen: Set[TypeParamRef], internal: Set[TypeLambda])(implicit ctx: Context): Type = tp match { case tp: NamedType => // default case, inlined for speed val isPatternBoundTypeRef = tp.isInstanceOf[TypeRef] && tp.symbol.is(Flags.Case) && !tp.symbol.isClass if (isPatternBoundTypeRef) WildcardType(tp.underlying.bounds) else if (tp.symbol.isStatic || (tp.prefix `eq` NoPrefix)) tp - else tp.derivedSelect(wildApprox(tp.prefix, theMap, seen)) + else tp.derivedSelect(wildApprox(tp.prefix, theMap, seen, internal)) case tp @ AppliedType(tycon, args) => - wildApprox(tycon, theMap, seen) match { + wildApprox(tycon, theMap, seen, internal) match { case _: WildcardType => WildcardType // this ensures we get a * type case tycon1 => tp.derivedAppliedType(tycon1, - args.mapConserve(arg => wildApprox(arg, theMap, seen))) + args.mapConserve(arg => wildApprox(arg, theMap, seen, internal))) } case tp: RefinedType => // default case, inlined for speed tp.derivedRefinedType( - wildApprox(tp.parent, theMap, seen), + wildApprox(tp.parent, theMap, seen, internal), tp.refinedName, - wildApprox(tp.refinedInfo, theMap, seen)) + wildApprox(tp.refinedInfo, theMap, seen, internal)) case tp: AliasingBounds => // default case, inlined for speed - tp.derivedAlias(wildApprox(tp.alias, theMap, seen)) + tp.derivedAlias(wildApprox(tp.alias, theMap, seen, internal)) + case tp @ TypeParamRef(tl, _) if internal.contains(tl) => tp case tp @ TypeParamRef(poly, pnum) => def wildApproxBounds(bounds: TypeBounds) = if (seen.contains(tp)) WildcardType - else WildcardType(wildApprox(bounds, theMap, seen + tp).bounds) + else WildcardType(wildApprox(bounds, theMap, seen + tp, internal).bounds) def unconstrainedApprox = wildApproxBounds(poly.paramInfos(pnum)) def approxPoly = if (ctx.mode.is(Mode.TypevarsMissContext)) unconstrainedApprox @@ -609,17 +610,17 @@ object ProtoTypes { ctx.typerState.constraint.entry(tp) match { case bounds: TypeBounds => wildApproxBounds(bounds) case NoType => unconstrainedApprox - case inst => wildApprox(inst, theMap, seen) + case inst => wildApprox(inst, theMap, seen, internal) } approxPoly case TermParamRef(mt, pnum) => - WildcardType(TypeBounds.upper(wildApprox(mt.paramInfos(pnum), theMap, seen))) + WildcardType(TypeBounds.upper(wildApprox(mt.paramInfos(pnum), theMap, seen, internal))) case tp: TypeVar => - wildApprox(tp.underlying, theMap, seen) + wildApprox(tp.underlying, theMap, seen, internal) case tp: AndType => def approxAnd = { - val tp1a = wildApprox(tp.tp1, theMap, seen) - val tp2a = wildApprox(tp.tp2, theMap, seen) + val tp1a = wildApprox(tp.tp1, theMap, seen, internal) + val tp2a = wildApprox(tp.tp2, theMap, seen, internal) def wildBounds(tp: Type) = if (tp.isInstanceOf[WildcardType]) tp.bounds else TypeBounds.upper(tp) if (tp1a.isInstanceOf[WildcardType] || tp2a.isInstanceOf[WildcardType]) @@ -630,8 +631,8 @@ object ProtoTypes { approxAnd case tp: OrType => def approxOr = { - val tp1a = wildApprox(tp.tp1, theMap, seen) - val tp2a = wildApprox(tp.tp2, theMap, seen) + val tp1a = wildApprox(tp.tp1, theMap, seen, internal) + val tp2a = wildApprox(tp.tp2, theMap, seen, internal) if (tp1a.isInstanceOf[WildcardType] || tp2a.isInstanceOf[WildcardType]) WildcardType(tp1a.bounds | tp2a.bounds) else @@ -639,24 +640,30 @@ object ProtoTypes { } approxOr case tp: SelectionProto => - tp.derivedSelectionProto(tp.name, wildApprox(tp.memberProto, theMap, seen), NoViewsAllowed) + tp.derivedSelectionProto(tp.name, wildApprox(tp.memberProto, theMap, seen, internal), NoViewsAllowed) case tp: ViewProto => tp.derivedViewProto( - wildApprox(tp.argType, theMap, seen), - wildApprox(tp.resultType, theMap, seen)) + wildApprox(tp.argType, theMap, seen, internal), + wildApprox(tp.resultType, theMap, seen, internal)) case _: ThisType | _: BoundType => // default case, inlined for speed tp + case tl: TypeLambda => + val internal1 = internal + tl + tl.derivedLambdaType( + paramInfos = tl.paramInfos.mapConserve(wildApprox(_, theMap, seen, internal1).bounds), + resType = wildApprox(tl.resType, theMap, seen, internal1) + ) case _ => - (if (theMap != null && seen.eq(theMap.seen)) theMap else new WildApproxMap(seen)) + (if (theMap != null && seen.eq(theMap.seen)) theMap else new WildApproxMap(seen, internal)) .mapOver(tp) } - final def wildApprox(tp: Type)(implicit ctx: Context): Type = wildApprox(tp, null, Set.empty) + final def wildApprox(tp: Type)(implicit ctx: Context): Type = wildApprox(tp, null, Set.empty, Set.empty) @sharable object AssignProto extends UncachedGroundType with MatchAlways - private[ProtoTypes] class WildApproxMap(val seen: Set[TypeParamRef])(implicit ctx: Context) extends TypeMap { - def apply(tp: Type): Type = wildApprox(tp, this, seen) + private[ProtoTypes] class WildApproxMap(val seen: Set[TypeParamRef], val internal: Set[TypeLambda])(implicit ctx: Context) extends TypeMap { + def apply(tp: Type): Type = wildApprox(tp, this, seen, internal) } /** Dummy tree to be used as an argument of a FunProto or ViewProto type */ 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))") +} +