diff --git a/compiler/src/dotty/tools/dotc/core/TypeComparer.scala b/compiler/src/dotty/tools/dotc/core/TypeComparer.scala index acb545efdca4..3f562bdd9025 100644 --- a/compiler/src/dotty/tools/dotc/core/TypeComparer.scala +++ b/compiler/src/dotty/tools/dotc/core/TypeComparer.scala @@ -605,11 +605,11 @@ class TypeComparer(initctx: Context) extends ConstraintHandling[AbsentContext] w if (tparams1.nonEmpty) return recur(tp1.EtaExpand(tparams1), tp2) || fourthTry tp2 match { - case EtaExpansion(tycon2) if tycon2.symbol.isClass => - return recur(tp1, tycon2) + case EtaExpansion(tycon2) if tycon2.symbol.isClass && tycon2.symbol.is(JavaDefined) => + recur(tp1, tycon2) || fourthTry case _ => + fourthTry } - fourthTry } compareTypeLambda case OrType(tp21, tp22) => @@ -773,7 +773,10 @@ class TypeComparer(initctx: Context) extends ConstraintHandling[AbsentContext] w isNewSubType(tp1.parent) case tp1: HKTypeLambda => def compareHKLambda = tp1 match { - case EtaExpansion(tycon1) => recur(tycon1, tp2) + case EtaExpansion(tycon1) if tycon1.symbol.isClass && tycon1.symbol.is(JavaDefined) => + // It's a raw type that was mistakenly eta-expanded to a hk-type. + // This can happen because we do not cook types coming from Java sources + recur(tycon1, tp2) case _ => tp2 match { case tp2: HKTypeLambda => false // this case was covered in thirdTry case _ => tp2.typeParams.hasSameLengthAs(tp1.paramRefs) && isSubType(tp1.resultType, tp2.appliedTo(tp1.paramRefs)) @@ -2591,7 +2594,11 @@ class ExplainingTypeComparer(initctx: Context) extends TypeComparer(initctx) { } override def isSubType(tp1: Type, tp2: Type, approx: ApproxState): Boolean = - traceIndented(s"${show(tp1)} <:< ${show(tp2)}${if (Config.verboseExplainSubtype) s" ${tp1.getClass} ${tp2.getClass}" else ""} $approx ${if (frozenConstraint) " frozen" else ""}") { + def moreInfo = + if Config.verboseExplainSubtype || ctx.settings.verbose.value + then s" ${tp1.getClass} ${tp2.getClass}" + else "" + traceIndented(s"${show(tp1)} <:< ${show(tp2)}$moreInfo $approx ${if (frozenConstraint) " frozen" else ""}") { super.isSubType(tp1, tp2, approx) } diff --git a/tests/pending/pos/i4908.scala b/tests/pending/pos/i4908.scala new file mode 100644 index 000000000000..c0465e024c9e --- /dev/null +++ b/tests/pending/pos/i4908.scala @@ -0,0 +1,41 @@ +object Test { + trait A + trait B + trait TestConstructor1 { type F[X <: A] + trait FromSet[C[_ <: A with B]] + + trait MSetLike[X <: A with B, This <: MSet[X] with MSetLike[X, This]] { + def to[C[X <: A with B] <: MSet[X] with MSetLike[X, C[X]]](fi: FromSet[C]): C[X] = ??? + } + trait MSet[X <: A with B] extends MSetLike[X, MSet[X]] + object MSetFactory extends FromSet[MSet] + } + + trait TestConstructor4[D] { + trait TestConstructor5[E] { + trait FromSet[C[_ <: D with E]] + + trait MSetLike[X <: D with E, This <: MSet[X] with MSetLike[X, This]] { + def to[C[X <: D with E] <: MSet[X] with MSetLike[X, C[X]]](fi: FromSet[C]): C[X] = ??? + } + trait MSet[X <: D with E] extends MSetLike[X, MSet[X]] + object MSetFactory extends FromSet[MSet] + } + } + + type C = A & B + val v1: TestConstructor1 => Unit = { f => + type P[a <: A] = f.F[a] + + type P1[c <: C] = f.MSet[c] + (f.MSetFactory: f.FromSet[f.MSet]): Unit + (x: P1[C]) => x.to(f.MSetFactory) + } + + def f3(f: TestConstructor4[A])(g: f.TestConstructor5[B]): Unit = { + type P1[c <: C] = g.MSet[c] + (g.MSetFactory: g.FromSet[g.MSet]): Unit + (x: P1[C]) => x.to(g.MSetFactory) + (x: P1[C]) => x.to[g.MSet](g.MSetFactory) + } +} \ No newline at end of file diff --git a/tests/pos/i4742.scala b/tests/pos/i4742.scala new file mode 100644 index 000000000000..d59d4bd403bc --- /dev/null +++ b/tests/pos/i4742.scala @@ -0,0 +1,6 @@ +import scala.reflect.ClassTag + +class Test { + def foo[T <: String: ClassTag](f: T => Int) = 1 + def bar(f: String => Int) = foo(f) +} \ No newline at end of file diff --git a/tests/pos/i4854.scala b/tests/pos/i4854.scala new file mode 100644 index 000000000000..f6a6127bf946 --- /dev/null +++ b/tests/pos/i4854.scala @@ -0,0 +1,5 @@ +class C { + def f(x: Int): Unit = () + val f: String => Unit = s => () + f("a") +} \ No newline at end of file diff --git a/tests/pos/i4867.scala b/tests/pos/i4867.scala new file mode 100644 index 000000000000..5b36df39f8dc --- /dev/null +++ b/tests/pos/i4867.scala @@ -0,0 +1,16 @@ +object UnionMapping { + private def parse(string: String): Int | Double = { + if(string.contains(".")) + string.toDouble + else + string.toInt + } + + def test_number = { + val strings: Seq[String] = Seq("123", "2.0", "42") + // Works + val asdf: Seq[AnyVal] = strings.map(parse(_)) + // Fails to compile + val union: Seq[Int | Double] = strings.map(parse(_)) + } +} \ No newline at end of file diff --git a/tests/pos/i4906.scala b/tests/pos/i4906.scala new file mode 100644 index 000000000000..a76415cef348 --- /dev/null +++ b/tests/pos/i4906.scala @@ -0,0 +1,36 @@ +object Test { + trait A + trait TestConstructor1 { type F[_ <: A] } + trait TestConstructor2[D] { + type F[_ <: D] + class G[X <: D] + trait TestConstructor3[E] { + type G[_ <: D & E] + class H[X <: D & E] + } + } + + val v1: TestConstructor1 => Unit = { f => + type P[a <: A] = f.F[a] // OK + } + + val v2: TestConstructor2[A] => Unit = { f => + type P[a <: A] = f.F[a] // Error! Type argument a does not conform to upper bound D + } + + def f2(f: TestConstructor2[A]): Unit = { + type P[a <: A] = f.F[a] // Error! Type argument a does not conform to upper bound D + } + + val v3: (f: TestConstructor2[A]) => (g: f.TestConstructor3[A]) => Unit = ??? // ok + val v4: (f: TestConstructor2[A]) => (g: f.TestConstructor3[A]) => Unit = {f => ???} + val v5: (f: TestConstructor2[A]) => (g: f.TestConstructor3[A]) => Unit = {(f: TestConstructor2[A]) => ???} + // } + def f3(f: TestConstructor2[A], g: f.TestConstructor3[A]): Unit = { + type P[a <: A] = f.F[a] // Error! Type argument a does not conform to upper bound D + type Q[a <: A] = g.G[a] + // type R[a <: A] = (f.F & g.G)[a] // compiler error + type R[a <: A] = ([X <: A] =>> f.F[X] & g.G[X])[a] + type S[a <: A] = f.G[a] & g.H[a] + } +}