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

Dotty style downwards comparisons for implicit search and overloading resolution #6037

Merged
merged 1 commit into from Aug 6, 2018
Jump to file or symbol
Failed to load files and symbols.
+575 −1
Diff settings

Always

Just for now

@@ -98,6 +98,9 @@ trait ScalaSettings extends AbsScalaSettings
def isScala213: Boolean = source.value >= version213
private[this] val version214 = ScalaVersion("2.14.0")
def isScala214: Boolean = source.value >= version214
private[this] val version300 = ScalaVersion("3.0.0")
def isScala300: Boolean = source.value >= version300
/**
* -X "Advanced" settings
@@ -806,13 +806,36 @@ trait Infer extends Checkable {
case _ => onRight
}
}
private def isAsSpecificValueType(tpe1: Type, tpe2: Type, undef1: List[Symbol], undef2: List[Symbol]): Boolean = tpe1 match {
case PolyType(tparams1, rtpe1) =>
isAsSpecificValueType(rtpe1, tpe2, undef1 ::: tparams1, undef2)
case _ =>
tpe2 match {
case PolyType(tparams2, rtpe2) => isAsSpecificValueType(tpe1, rtpe2, undef1, undef2 ::: tparams2)
case _ => existentialAbstraction(undef1, tpe1) <:< existentialAbstraction(undef2, tpe2)
case _ if !settings.isScala300 => existentialAbstraction(undef1, tpe1) <:< existentialAbstraction(undef2, tpe2)
case _ =>
// Backport of fix for https://github.com/scala/bug/issues/2509
// from Dotty https://github.com/lampepfl/dotty/commit/89540268e6c49fb92b9ca61249e46bb59981bf5a
//
// Note that as of https://github.com/lampepfl/dotty/commit/b9f3084205bc9fcbd2a5181d3f0e539e2a20253a
// Dotty flips variances throughout, not just at the top level. We follow that behaviour here.
val e1 = existentialAbstraction(undef1, tpe1)
val e2 = existentialAbstraction(undef2, tpe2)
val flip = new TypeMap(trackVariance = true) {
def apply(tp: Type): Type = tp match {
case TypeRef(pre, sym, args) if variance > 0 && sym.typeParams.exists(_.isContravariant) =>
mapOver(TypeRef(pre, sym.flipped, args))
case _ =>
mapOver(tp)
}
}
val bt = e1.baseType(e2.typeSymbol)
val lhs = if(bt != NoType) bt else e1
flip(lhs) <:< flip(e2)
}
}
@@ -2017,6 +2017,8 @@ trait Symbols extends api.Symbols { self: SymbolTable =>
/** Internal method to clone a symbol's implementation with the given flags and no info. */
def cloneSymbolImpl(owner: Symbol, newFlags: Long): TypeOfClonedSymbol
def flipped: Symbol = this
// ------ access to related symbols --------------------------------------------------
/** The next enclosing class. */
@@ -3386,6 +3388,14 @@ trait Symbols extends api.Symbols { self: SymbolTable =>
clone
}
override lazy val flipped: ClassSymbol = {
val clone = cloneSymbol(owner)
clone.rawInfo.typeParams.foreach { sym =>
if (sym.isContravariant) sym.resetFlag(Flag.CONTRAVARIANT).setFlag(Flag.COVARIANT)
}
clone
}
override def derivedValueClassUnbox =
// (info.decl(nme.unbox)) orElse uncomment once we accept unbox methods
(info.decls.find(_ hasAllFlags PARAMACCESSOR | METHOD) getOrElse
Copy path View file
@@ -0,0 +1,7 @@
t2509-2.scala:26: error: ambiguous implicit values:
both value xb in object Test of type => X[B,Int]
and value xa in object Test of type => X[A,Boolean]
match expected type X[B,U]
val fb = f(new B)
^
one error found
Copy path View file
@@ -0,0 +1 @@
-Xsource:3.0
Copy path View file
@@ -0,0 +1,28 @@
class A
class B extends A
class C extends B
trait X[-T, U] {
val u: U
}
object XA extends X[A, Boolean] {
val u = true
}
object XB extends X[B, Int] {
val u = 23
}
object Test {
implicit def f[T, U](t: T)(implicit x: X[T, U]): U = x.u
implicit val xa: X[A, Boolean] = XA
implicit val xb: X[B, Int] = XB
val fa = f(new A)
val ffa: Boolean = fa
// Should be ambiguous
val fb = f(new B)
val ffb: Int = fb
}
Copy path View file
@@ -0,0 +1,4 @@
t2509-3.scala:31: error: value value is not a member of B
println("B: " + b.value)
^
one error found
Copy path View file
@@ -0,0 +1 @@
-Xsource:3.0
Copy path View file
@@ -0,0 +1,33 @@
class A
class B extends A
trait Y {
def value: String
}
trait X[-T] {
def y(t: T): Y
}
trait Z[-T] extends X[T]
object ZA extends Z[A] {
def y(a: A) = new Y { def value = s"${a.getClass}: AValue" }
}
object XB extends X[B] {
def y(b: B) = new Y { def value = s"S{b.getClass}: BValue" }
}
object Test {
implicit def f[T](t: T)(implicit x: X[T]): Y = x.y(t)
implicit val za: Z[A] = ZA
implicit val xb: X[B] = XB
def main(argv: Array[String]): Unit = {
val a = new A
val b = new B
println("A: " + a.value)
println("B: " + b.value)
}
}
Copy path View file
@@ -0,0 +1,7 @@
t2509-7b.scala:30: error: ambiguous implicit values:
both method make in object X of type => Both[X,X]
and method make in trait Factory of type => Both[Y,Y]
match expected type Both[Y,X]
get
^
one error found
Copy path View file
@@ -0,0 +1 @@
-Xsource:3.0
Copy path View file
@@ -0,0 +1,31 @@
class Both[-A, +B]
trait Factory[A] {
implicit def make: Both[A, A] = new Both[A, A]
}
trait X
object X extends Factory[X] {
override implicit def make: Both[X, X] = super.make
}
class Y extends X
object Y extends Factory[Y] {
// See test/files/pos/t2509-7a.scala ... discussion below
// override implicit def make: Both[Y, Y] = super.make
}
object Test {
def get(implicit ev: Both[Y, X]) = ev
// There are two possible implicits here: X.make and Y.make, neither are
// subtype of each other, so who wins?
// - Under the old scheme it's X.make because `isAsGood` sees that X.make is defined
// in X whereas Y.make is defined in Factory
// - Under the new scheme it's ambiguous because we replace contravariance by covariance
// in top-level type parameters so Y.make is treated as a subtype of X.make
// In both schemes we can get Y.make to win by uncommenting the override for make in Y
// (Y wins against X because `isDerived` also considers the subtyping relationships
// of companion classes)
get
}
Copy path View file
@@ -0,0 +1 @@
-Xsource:3.0
Copy path View file
@@ -0,0 +1,8 @@
import scala.collection.immutable._
object Test extends App {
val res0 = TreeSet(1, 2, 3, 4, 5, 6)
val res1 = res0.map(x => x)
println(res0.toList == res1.toList)
println(res1.getClass)
}
Copy path View file
@@ -0,0 +1 @@
-Xsource:3.0
Copy path View file
@@ -0,0 +1,13 @@
// See https://github.com/lampepfl/dotty/issues/2974
trait Foo[-T]
trait Bar[-T] extends Foo[T]
object Test {
implicit val fa: Foo[Any] = ???
implicit val ba: Bar[Int] = ???
def test: Unit = {
implicitly[Foo[Int]]
}
}
Copy path View file
@@ -0,0 +1 @@
-Xsource:3.0
Copy path View file
@@ -0,0 +1,33 @@
class A
class B extends A
trait Y {
def value: String
}
trait X[-T] {
def y(t: T): Y
}
trait Z[-T] extends X[T]
object XA extends X[A] {
def y(a: A) = new Y { def value = s"${a.getClass}: AValue" }
}
object ZB extends Z[B] {
def y(b: B) = new Y { def value = s"${b.getClass}: BValue" }
}
object Test {
implicit def f[T](t: T)(implicit x: X[T]): Y = x.y(t)
implicit val za: X[A] = XA
implicit val xb: Z[B] = ZB
def main(argv: Array[String]): Unit = {
val a = new A
val b = new B
println("A: " + a.value)
println("B: " + b.value)
}
}
Copy path View file
@@ -0,0 +1 @@
-Xsource:3.0
Copy path View file
@@ -0,0 +1,31 @@
class Both[-A, +B]
trait Factory[A] {
implicit def make: Both[A, A] = new Both[A, A]
}
trait X
object X extends Factory[X] {
override implicit def make: Both[X, X] = super.make
}
class Y extends X
object Y extends Factory[Y] {
// See test/files/neg/t2509-7b.scala ... discussion below
override implicit def make: Both[Y, Y] = super.make
}
object Test {
def get(implicit ev: Both[Y, X]) = ev
// There are two possible implicits here: X.make and Y.make, neither are
// subtype of each other, so who wins?
// - Under the old scheme it's X.make because `isAsGood` sees that X.make is defined
// in X whereas Y.make is defined in Factory
// - Under the new scheme it's ambiguous because we replace contravariance by covariance
// in top-level type parameters so Y.make is treated as a subtype of X.make
// In both schemes we can get Y.make to win by uncommenting the override for make in Y
// (Y wins against X because `isDerived` also considers the subtyping relationships
// of companion classes)
get
}
Copy path View file
@@ -0,0 +1,2 @@
A: class A: AValue
B: class B: BValue
Copy path View file
@@ -0,0 +1 @@
-Xsource:3.0
Copy path View file
@@ -0,0 +1,33 @@
import scala.language.implicitConversions
class A
class B extends A
trait Y {
def value: String
}
trait X[-T] {
def y(t: T): Y
}
object XA extends X[A] {
def y(a: A) = new Y { def value = s"${a.getClass}: AValue" }
}
object XB extends X[B] {
def y(b: B) = new Y { def value = s"${b.getClass}: BValue" }
}
object Test {
implicit def f[T](t: T)(implicit x: X[T]): Y = x.y(t)
implicit val xa: X[A] = XA
implicit val xb: X[B] = XB
def main(argv: Array[String]): Unit = {
val a = new A
val b = new B
println(s"A: ${a.value}")
println(s"B: ${b.value}")
}
}
Copy path View file
@@ -0,0 +1,2 @@
A: class A: AValue
B: class B: BValue
Copy path View file
@@ -0,0 +1 @@
-Xsource:3.0
Copy path View file
@@ -0,0 +1,35 @@
import scala.language.implicitConversions
class A
class B extends A
trait Y {
def value: String
}
trait X[-T] {
def y(t: T): Y
}
trait Z[-T] extends X[T]
object XA extends Z[A] {
def y(a: A) = new Y { def value = s"${a.getClass}: AValue" }
}
object ZB extends Z[B] {
def y(b: B) = new Y { def value = s"${b.getClass}: BValue" }
}
object Test {
implicit def f[T](t: T)(implicit x: X[T]): Y = x.y(t)
implicit val za: X[A] = XA
implicit val xb: Z[B] = ZB
def main(argv: Array[String]): Unit = {
val a = new A
val b = new B
println(s"A: ${a.value}")
println(s"B: ${b.value}")
}
}
Copy path View file
@@ -0,0 +1,3 @@
Inv[A] Inv[B] Inv[C] Inv[D] Inv[E]
Con[A] Con[A] Con[C] Con[C] Con[E]
Cov[E] Cov[E] Cov[E] Cov[E] Cov[E]
Copy path View file
@@ -0,0 +1 @@
-Xsource:3.0
Oops, something went wrong.
ProTip! Use n and p to navigate between commits in a pull request.