Skip to content

Commit f11cc5e

Browse files
committed
Fix #2397: Upgrade hasMatchingMember
The previous "self-referential" special case needs to be generalized. The fix caused some changes elsewhere. - `t5279` now fails with an overloading ambiguity, which is expected. - pattern matching code generation failed in 3 tests, had to be upgraded. The errors did not trigger before because instances of unapply type parameters were coarser.
1 parent 5093126 commit f11cc5e

File tree

5 files changed

+68
-33
lines changed

5 files changed

+68
-33
lines changed

compiler/src/dotty/tools/dotc/core/TypeComparer.scala

Lines changed: 34 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -881,35 +881,44 @@ class TypeComparer(initctx: Context) extends DotClass with ConstraintHandling {
881881
* Normalization is as follows: If `tp2` contains a skolem to its refinement type,
882882
* rebase both itself and the member info of `tp` on a freshly created skolem type.
883883
*/
884-
protected def hasMatchingMember(name: Name, tp1: Type, tp2: RefinedType): Boolean = {
885-
val rinfo2 = tp2.refinedInfo
886-
val mbr = tp1.member(name)
887-
888-
def qualifies(m: SingleDenotation) = isSubType(m.info, rinfo2)
889-
890-
def memberMatches: Boolean = mbr match { // inlined hasAltWith for performance
891-
case mbr: SingleDenotation => qualifies(mbr)
892-
case _ => mbr hasAltWith qualifies
893-
}
894-
895-
// special case for situations like:
896-
// class C { type T }
897-
// val foo: C
898-
// foo.type <: C { type T {= , <: , >:} foo.T }
899-
def selfReferentialMatch = tp1.isInstanceOf[SingletonType] && {
900-
rinfo2 match {
901-
case rinfo2: TypeBounds =>
902-
val mbr1 = tp1.select(name)
903-
!defn.isBottomType(tp1.widen) &&
904-
(mbr1 =:= rinfo2.hi || (rinfo2.hi ne rinfo2.lo) && mbr1 =:= rinfo2.lo)
884+
protected def hasMatchingMember(name: Name, tp1: Type, tp2: RefinedType): Boolean =
885+
/*>|>*/ ctx.traceIndented(i"hasMatchingMember($tp1 . $name :? ${tp2.refinedInfo}), mbr: ${tp1.member(name).info}", subtyping) /*<|<*/ {
886+
val rinfo2 = tp2.refinedInfo
887+
888+
// If the member is an abstract type, compare the member itself
889+
// instead of its bounds. This case is needed situations like:
890+
//
891+
// class C { type T }
892+
// val foo: C
893+
// foo.type <: C { type T {= , <: , >:} foo.T }
894+
//
895+
// or like:
896+
//
897+
// class C[T]
898+
// C[_] <: C[TV]
899+
//
900+
// where TV is a type variable. See i2397.scala for an example of the latter.
901+
def matchAbstractTypeMember(info1: Type) = info1 match {
902+
case TypeBounds(lo, hi) if lo ne hi =>
903+
tp2.refinedInfo match {
904+
case rinfo2: TypeBounds =>
905+
val ref1 = tp1.widenExpr.select(name)
906+
(rinfo2.variance > 0 || isSubType(rinfo2.lo, ref1)) &&
907+
(rinfo2.variance < 0 || isSubType(ref1, rinfo2.hi))
908+
case _ =>
909+
false
910+
}
905911
case _ => false
906912
}
907-
}
908913

909-
/*>|>*/ ctx.traceIndented(i"hasMatchingMember($tp1 . $name :? ${tp2.refinedInfo}) ${mbr.info.show} $rinfo2", subtyping) /*<|<*/ {
910-
memberMatches || selfReferentialMatch
914+
def qualifies(m: SingleDenotation) =
915+
isSubType(m.info, rinfo2) || matchAbstractTypeMember(m.info)
916+
917+
tp1.member(name) match { // inlined hasAltWith for performance
918+
case mbr: SingleDenotation => qualifies(mbr)
919+
case mbr => mbr hasAltWith qualifies
920+
}
911921
}
912-
}
913922

914923
final def ensureStableSingleton(tp: Type): SingletonType = tp.stripTypeVar match {
915924
case tp: SingletonType if tp.isStable => tp

compiler/src/dotty/tools/dotc/transform/PatternMatcher.scala

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1341,8 +1341,10 @@ class PatternMatcher extends MiniPhaseTransform with DenotTransformer {
13411341
tree match {
13421342
case Typed(unapply, _) => apply(unapply, binder)
13431343
case UnApply(unfun, implicits, args) =>
1344-
val castedBinder = ref(binder).ensureConforms(tree.tpe)
1345-
val synth = if (implicits.isEmpty) unfun.appliedTo(castedBinder) else unfun.appliedTo(castedBinder).appliedToArgs(implicits)
1344+
val mt @ MethodType(_) = unfun.tpe.widen
1345+
val castedBinder = ref(binder).ensureConforms(mt.paramInfos.head)
1346+
var synth = unfun.appliedTo(castedBinder)
1347+
if (implicits.nonEmpty) synth = synth.appliedToArgs(implicits)
13461348
new ExtractorCallRegular(alignPatterns(tree, synth.tpe), synth, args, synth.tpe)
13471349
}
13481350
}

tests/neg/t5729.scala

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
trait T[X]
2+
object Test {
3+
def join(in: Seq[T[_]]): Int = ???
4+
def join[S](in: Seq[T[S]]): String = ???
5+
join(null: Seq[T[_]]) // error: ambiguous
6+
}
7+
8+
object C {
9+
def join(in: Seq[List[_]]): Int = error("TODO")
10+
def join[S](in: Seq[List[S]]): String = error("TODO")
11+
12+
join(Seq[List[Int]]()) // error: ambiguous
13+
//
14+
// ./a.scala:13: error: ambiguous reference to overloaded definition,
15+
// both method join in object C of type [S](in: Seq[List[S]])String
16+
// and method join in object C of type (in: Seq[List[_]])Int
17+
// match argument types (Seq[List[Int]])
18+
// join(Seq[List[Int]]())
19+
// ^
20+
// one error found
21+
}

tests/pos/i2397.scala

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
class Foo[A]
2+
3+
object Test {
4+
def foo[T](x: Foo[T]) = x
5+
6+
foo((new Foo[Int]: Foo[_]))
7+
}
8+
9+

tests/pos/t5729.scala

Lines changed: 0 additions & 6 deletions
This file was deleted.

0 commit comments

Comments
 (0)