diff --git a/src/dotty/tools/dotc/core/TypeComparer.scala b/src/dotty/tools/dotc/core/TypeComparer.scala index ea815f6c0778..b4b1573286b6 100644 --- a/src/dotty/tools/dotc/core/TypeComparer.scala +++ b/src/dotty/tools/dotc/core/TypeComparer.scala @@ -467,16 +467,23 @@ class TypeComparer(initctx: Context) extends DotClass with ConstraintHandling { } /** If `projection` is of the form T # Apply where `T` is an instance of a Lambda class, - * and `other` is not a type lambda projection, then convert `other` to a type lambda `U`, and - * continue with `T <:< U` if `inOrder` is true and `U <:< T` otherwise. + * then + * (1) if `other` is not a (possibly projected) type lambda, convert `other` to a type lambda `U`, + * and continue with `T <:< U` if `inOrder` is true and `U <:< T` otherwise. + * (2) if `other` is a already a type lambda, + * continue with `T <:< other` if `inOrder` is true and `other <:< T` otherwise. */ def compareHK(projection: NamedType, other: Type, inOrder: Boolean) = projection.name == tpnme.Apply && { val lambda = projection.prefix.LambdaClass(forcing = true) - lambda.exists && !other.isLambda && - other.testLifted(lambda.typeParams, - if (inOrder) isSubType(projection.prefix, _) else isSubType(_, projection.prefix), - if (inOrder) Nil else classBounds(projection.prefix)) + lambda.exists && { + if (!other.isLambda) + other.testLifted(lambda.typeParams, + if (inOrder) isSubType(projection.prefix, _) else isSubType(_, projection.prefix), + if (inOrder) Nil else classBounds(projection.prefix)) + else if (inOrder) isSubType(projection.prefix, other) + else isSubType(other, projection.prefix) + } } /** The class symbols bounding the type of the `Apply` member of `tp` */ diff --git a/src/dotty/tools/dotc/typer/Implicits.scala b/src/dotty/tools/dotc/typer/Implicits.scala index a3ddca5d99db..c5755e3fa7d8 100644 --- a/src/dotty/tools/dotc/typer/Implicits.scala +++ b/src/dotty/tools/dotc/typer/Implicits.scala @@ -462,7 +462,8 @@ trait Implicits { self: Typer => } /** The expected type where parameters and uninstantiated typevars are replaced by wildcard types */ - val wildProto = implicitProto(pt, wildApprox(_)) + val approximator = new WildApproxMap(new mutable.ListBuffer[Type]) + val wildProto = implicitProto(pt, wildApprox(_, approximator)) /** Search failures; overridden in ExplainedImplicitSearch */ protected def nonMatchingImplicit(ref: TermRef): SearchFailure = NoImplicitMatches @@ -565,11 +566,23 @@ trait Implicits { self: Typer => case result: SearchSuccess => result case result: AmbiguousImplicits => result case result: SearchFailure => - searchImplicits(implicitScope(wildProto).eligible, contextual = false) + searchImplicits(implicitScope(wildProto, approximator.discarded).eligible, contextual = false) } } - def implicitScope(tp: Type): OfTypeImplicits = ctx.runInfo.implicitScope(tp, ctx) + /** The implicit scope corresponding to type `tp` and discarded parts `others`. + */ + def implicitScope(tp: Type, others: Traversable[Type]): OfTypeImplicits = { + val typeScope = ctx.runInfo.implicitScope(tp, ctx) + if (others.isEmpty) typeScope + else { + val allCompanionRefs = new TermRefSet + allCompanionRefs ++= typeScope.companionRefs + for (tp <- others) + allCompanionRefs ++= ctx.runInfo.implicitScope(tp, ctx).companionRefs + new OfTypeImplicits(tp, allCompanionRefs)(ctx) + } + } } final class ExplainedImplicitSearch(pt: Type, argument: Tree, pos: Position)(implicit ctx: Context) diff --git a/src/dotty/tools/dotc/typer/ProtoTypes.scala b/src/dotty/tools/dotc/typer/ProtoTypes.scala index 9a012c30e0b0..622fbc6167ac 100644 --- a/src/dotty/tools/dotc/typer/ProtoTypes.scala +++ b/src/dotty/tools/dotc/typer/ProtoTypes.scala @@ -378,24 +378,31 @@ object ProtoTypes { final def wildApprox(tp: Type, theMap: WildApproxMap = null)(implicit ctx: Context): Type = tp match { case tp: NamedType => // default case, inlined for speed if (tp.symbol.isStatic) tp - else tp.derivedSelect(wildApprox(tp.prefix, theMap)) + else { + val pre = wildApprox(tp.prefix, theMap) + val res = tp.derivedSelect(pre) + if ((res eq WildcardType) && (pre ne WildcardType) && + (theMap != null) && (theMap.discarded != null)) + theMap.discarded += pre + res + } case tp: RefinedType => // default case, inlined for speed tp.derivedRefinedType(wildApprox(tp.parent, theMap), tp.refinedName, wildApprox(tp.refinedInfo, theMap)) case tp: TypeAlias => // default case, inlined for speed tp.derivedTypeAlias(wildApprox(tp.alias, theMap)) case tp @ PolyParam(poly, pnum) => ctx.typerState.constraint.entry(tp) match { - case bounds: TypeBounds => wildApprox(WildcardType(bounds)) - case NoType => WildcardType(wildApprox(poly.paramBounds(pnum)).bounds) - case inst => wildApprox(inst) + case bounds: TypeBounds => wildApprox(WildcardType(bounds), theMap) + case NoType => WildcardType(wildApprox(poly.paramBounds(pnum), theMap).bounds) + case inst => wildApprox(inst, theMap) } case MethodParam(mt, pnum) => - WildcardType(TypeBounds.upper(wildApprox(mt.paramTypes(pnum)))) + WildcardType(TypeBounds.upper(wildApprox(mt.paramTypes(pnum), theMap))) case tp: TypeVar => - wildApprox(tp.underlying) + wildApprox(tp.underlying, theMap) case tp: AndType => - val tp1a = wildApprox(tp.tp1) - val tp2a = wildApprox(tp.tp2) + val tp1a = wildApprox(tp.tp1, theMap) + val tp2a = wildApprox(tp.tp2, theMap) def wildBounds(tp: Type) = if (tp.isInstanceOf[WildcardType]) tp.bounds else TypeBounds.upper(tp) if (tp1a.isInstanceOf[WildcardType] || tp2a.isInstanceOf[WildcardType]) @@ -403,23 +410,29 @@ object ProtoTypes { else tp.derivedAndType(tp1a, tp2a) case tp: OrType => - val tp1a = wildApprox(tp.tp1) - val tp2a = wildApprox(tp.tp2) + val tp1a = wildApprox(tp.tp1, theMap) + val tp2a = wildApprox(tp.tp2, theMap) if (tp1a.isInstanceOf[WildcardType] || tp2a.isInstanceOf[WildcardType]) WildcardType(tp1a.bounds | tp2a.bounds) else tp.derivedOrType(tp1a, tp2a) case tp: SelectionProto => - tp.derivedSelectionProto(tp.name, wildApprox(tp.memberProto), NoViewsAllowed) + tp.derivedSelectionProto(tp.name, wildApprox(tp.memberProto, theMap), NoViewsAllowed) case tp: ViewProto => - tp.derivedViewProto(wildApprox(tp.argType), wildApprox(tp.resultType)) + tp.derivedViewProto(wildApprox(tp.argType, theMap), wildApprox(tp.resultType, theMap)) case _: ThisType | _: BoundType | NoPrefix => // default case, inlined for speed tp case _ => (if (theMap != null) theMap else new WildApproxMap).mapOver(tp) } - private[ProtoTypes] class WildApproxMap(implicit ctx: Context) extends TypeMap { + /** A backing map for `wildApprox`. + * @param discarded A ListBuffer in which to store parts of intermediate steps of the approximation + * that have been discarded in a lookupRefined yielding a WildcardType. + * The idea is that these bits still matter as far as companion search is + * concerned, so they need to be kept somewhere. + */ + private[dotty] class WildApproxMap(val discarded: mutable.ListBuffer[Type] = null)(implicit ctx: Context) extends TypeMap { def apply(tp: Type) = wildApprox(tp, this) } diff --git a/tests/pos/i553-shuffle.scala b/tests/pos/i553-shuffle.scala new file mode 100644 index 000000000000..105379918e88 --- /dev/null +++ b/tests/pos/i553-shuffle.scala @@ -0,0 +1,8 @@ +import scala.util.Random +import scala.collection.immutable.List._ +object Test { + def test = { + val rand = new Random + rand.shuffle(List(1,2))//(List.canBuildFrom[Int]) // fails + } +}