From f11cc5eabb0d42d687ae5e850f358f8aee18471f Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Tue, 9 May 2017 12:12:49 +0200 Subject: [PATCH 1/2] 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. --- .../dotty/tools/dotc/core/TypeComparer.scala | 59 +++++++++++-------- .../tools/dotc/transform/PatternMatcher.scala | 6 +- tests/neg/t5729.scala | 21 +++++++ tests/pos/i2397.scala | 9 +++ tests/pos/t5729.scala | 6 -- 5 files changed, 68 insertions(+), 33 deletions(-) create mode 100644 tests/neg/t5729.scala create mode 100644 tests/pos/i2397.scala delete mode 100644 tests/pos/t5729.scala diff --git a/compiler/src/dotty/tools/dotc/core/TypeComparer.scala b/compiler/src/dotty/tools/dotc/core/TypeComparer.scala index 36c134a95d2f..7d7bd2b160b0 100644 --- a/compiler/src/dotty/tools/dotc/core/TypeComparer.scala +++ b/compiler/src/dotty/tools/dotc/core/TypeComparer.scala @@ -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 diff --git a/compiler/src/dotty/tools/dotc/transform/PatternMatcher.scala b/compiler/src/dotty/tools/dotc/transform/PatternMatcher.scala index 447a003e7d1f..4472c9f5e170 100644 --- a/compiler/src/dotty/tools/dotc/transform/PatternMatcher.scala +++ b/compiler/src/dotty/tools/dotc/transform/PatternMatcher.scala @@ -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) } } diff --git a/tests/neg/t5729.scala b/tests/neg/t5729.scala new file mode 100644 index 000000000000..c7d42ee238ea --- /dev/null +++ b/tests/neg/t5729.scala @@ -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 +} diff --git a/tests/pos/i2397.scala b/tests/pos/i2397.scala new file mode 100644 index 000000000000..4fce9cde4640 --- /dev/null +++ b/tests/pos/i2397.scala @@ -0,0 +1,9 @@ +class Foo[A] + +object Test { + def foo[T](x: Foo[T]) = x + + foo((new Foo[Int]: Foo[_])) +} + + diff --git a/tests/pos/t5729.scala b/tests/pos/t5729.scala deleted file mode 100644 index 944aa04d8c0a..000000000000 --- a/tests/pos/t5729.scala +++ /dev/null @@ -1,6 +0,0 @@ -trait T[X] -object Test { - def join(in: Seq[T[_]]): Int = ??? - def join[S](in: Seq[T[S]]): String = ??? - join(null: Seq[T[_]]) -} From be1312a12c8ca6b0cf4cb1334076cbe4e2655171 Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Tue, 9 May 2017 13:01:54 +0200 Subject: [PATCH 2/2] Add original test --- tests/pos/i2397.scala | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/tests/pos/i2397.scala b/tests/pos/i2397.scala index 4fce9cde4640..20ad37abd7c2 100644 --- a/tests/pos/i2397.scala +++ b/tests/pos/i2397.scala @@ -6,4 +6,11 @@ object Test { foo((new Foo[Int]: Foo[_])) } +import java.nio.file._ +import java.util.stream.Collectors + +object Foo { + Files.walk(Paths.get("")).collect(Collectors.toList[Path]) +} +