Skip to content

Fix #2397: Upgrade hasMatchingMember #2402

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 2 commits into from
May 9, 2017
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
59 changes: 34 additions & 25 deletions compiler/src/dotty/tools/dotc/core/TypeComparer.scala
Original file line number Diff line number Diff line change
Expand Up @@ -881,35 +881,44 @@ class TypeComparer(initctx: Context) extends DotClass with ConstraintHandling {
* Normalization is as follows: If `tp2` contains a skolem to its refinement type,
* rebase both itself and the member info of `tp` on a freshly created skolem type.
*/
protected def hasMatchingMember(name: Name, tp1: Type, tp2: RefinedType): Boolean = {
val rinfo2 = tp2.refinedInfo
val mbr = tp1.member(name)

def qualifies(m: SingleDenotation) = isSubType(m.info, rinfo2)

def memberMatches: Boolean = mbr match { // inlined hasAltWith for performance
case mbr: SingleDenotation => qualifies(mbr)
case _ => mbr hasAltWith qualifies
}

// special case for situations like:
// class C { type T }
// val foo: C
// foo.type <: C { type T {= , <: , >:} foo.T }
def selfReferentialMatch = tp1.isInstanceOf[SingletonType] && {
rinfo2 match {
case rinfo2: TypeBounds =>
val mbr1 = tp1.select(name)
!defn.isBottomType(tp1.widen) &&
(mbr1 =:= rinfo2.hi || (rinfo2.hi ne rinfo2.lo) && mbr1 =:= rinfo2.lo)
protected def hasMatchingMember(name: Name, tp1: Type, tp2: RefinedType): Boolean =
/*>|>*/ ctx.traceIndented(i"hasMatchingMember($tp1 . $name :? ${tp2.refinedInfo}), mbr: ${tp1.member(name).info}", subtyping) /*<|<*/ {
val rinfo2 = tp2.refinedInfo

// If the member is an abstract type, compare the member itself
// instead of its bounds. This case is needed situations like:
//
// class C { type T }
// val foo: C
// foo.type <: C { type T {= , <: , >:} foo.T }
//
// or like:
//
// class C[T]
// C[_] <: C[TV]
//
// where TV is a type variable. See i2397.scala for an example of the latter.
def matchAbstractTypeMember(info1: Type) = info1 match {
case TypeBounds(lo, hi) if lo ne hi =>
tp2.refinedInfo match {
case rinfo2: TypeBounds =>
val ref1 = tp1.widenExpr.select(name)
(rinfo2.variance > 0 || isSubType(rinfo2.lo, ref1)) &&
(rinfo2.variance < 0 || isSubType(ref1, rinfo2.hi))
case _ =>
false
}
case _ => false
}
}

/*>|>*/ ctx.traceIndented(i"hasMatchingMember($tp1 . $name :? ${tp2.refinedInfo}) ${mbr.info.show} $rinfo2", subtyping) /*<|<*/ {
memberMatches || selfReferentialMatch
def qualifies(m: SingleDenotation) =
isSubType(m.info, rinfo2) || matchAbstractTypeMember(m.info)

tp1.member(name) match { // inlined hasAltWith for performance
case mbr: SingleDenotation => qualifies(mbr)
case mbr => mbr hasAltWith qualifies
}
}
}

final def ensureStableSingleton(tp: Type): SingletonType = tp.stripTypeVar match {
case tp: SingletonType if tp.isStable => tp
Expand Down
6 changes: 4 additions & 2 deletions compiler/src/dotty/tools/dotc/transform/PatternMatcher.scala
Original file line number Diff line number Diff line change
Expand Up @@ -1341,8 +1341,10 @@ class PatternMatcher extends MiniPhaseTransform with DenotTransformer {
tree match {
case Typed(unapply, _) => apply(unapply, binder)
case UnApply(unfun, implicits, args) =>
val castedBinder = ref(binder).ensureConforms(tree.tpe)
val synth = if (implicits.isEmpty) unfun.appliedTo(castedBinder) else unfun.appliedTo(castedBinder).appliedToArgs(implicits)
val mt @ MethodType(_) = unfun.tpe.widen
val castedBinder = ref(binder).ensureConforms(mt.paramInfos.head)
var synth = unfun.appliedTo(castedBinder)
if (implicits.nonEmpty) synth = synth.appliedToArgs(implicits)
new ExtractorCallRegular(alignPatterns(tree, synth.tpe), synth, args, synth.tpe)
}
}
Expand Down
21 changes: 21 additions & 0 deletions tests/neg/t5729.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
trait T[X]
object Test {
def join(in: Seq[T[_]]): Int = ???
def join[S](in: Seq[T[S]]): String = ???
join(null: Seq[T[_]]) // error: ambiguous
}

object C {
def join(in: Seq[List[_]]): Int = error("TODO")
def join[S](in: Seq[List[S]]): String = error("TODO")

join(Seq[List[Int]]()) // error: ambiguous
//
// ./a.scala:13: error: ambiguous reference to overloaded definition,
// both method join in object C of type [S](in: Seq[List[S]])String
// and method join in object C of type (in: Seq[List[_]])Int
// match argument types (Seq[List[Int]])
// join(Seq[List[Int]]())
// ^
// one error found
}
16 changes: 16 additions & 0 deletions tests/pos/i2397.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
class Foo[A]

object Test {
def foo[T](x: Foo[T]) = x

foo((new Foo[Int]: Foo[_]))
}

import java.nio.file._
import java.util.stream.Collectors

object Foo {
Files.walk(Paths.get("")).collect(Collectors.toList[Path])
}


6 changes: 0 additions & 6 deletions tests/pos/t5729.scala

This file was deleted.