Skip to content

Commit b48bb2d

Browse files
committed
Higher-kinded type variable unification.
Can cause ambiguous implicits, so is under the compiler flag -Xsource:2.13 Fixes scala/bug#10185 Fixes scala/bug#10195 Fixes scala/bug#10197 Fixes scala/bug#10213 Fixes scala/bug#10238 Fixes scala/bug#10372 Presents an alternative fix to scala/bug#6895.
1 parent e1e8d05 commit b48bb2d

29 files changed

+553
-5
lines changed

src/reflect/mima-filters/2.12.0.forwards.excludes

+2-1
Original file line numberDiff line numberDiff line change
@@ -13,4 +13,5 @@ ProblemFilters.exclude[MissingClassProblem]("scala.reflect.io.FileZipArchive$Laz
1313
ProblemFilters.exclude[DirectMissingMethodProblem]("scala.reflect.io.ZipArchive.closeZipFile")
1414
ProblemFilters.exclude[MissingClassProblem]("scala.reflect.io.FileZipArchive$LeakyEntry")
1515

16-
ProblemFilters.exclude[DirectMissingMethodProblem]("scala.reflect.runtime.SynchronizedSymbols#SynchronizedSymbol.exists")
16+
ProblemFilters.exclude[DirectMissingMethodProblem]("scala.reflect.runtime.SynchronizedSymbols#SynchronizedSymbol.exists")
17+
ProblemFilters.exclude[DirectMissingMethodProblem]("scala.reflect.runtime.Settings.isScala213")

src/reflect/scala/reflect/internal/Types.scala

+1-1
Original file line numberDiff line numberDiff line change
@@ -3325,7 +3325,7 @@ trait Types
33253325
)
33263326
override def etaExpand: Type = (
33273327
if (!isHigherKinded) this
3328-
else logResult("Normalizing HK $this")(typeFun(params, applyArgs(params map (_.typeConstructor))))
3328+
else logResult(s"Normalizing HK $this")(typeFun(params, applyArgs(params map (_.typeConstructor))))
33293329
)
33303330
override def typeSymbol = origin.typeSymbol
33313331

src/reflect/scala/reflect/internal/settings/MutableSettings.scala

+1
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,7 @@ abstract class MutableSettings extends AbsSettings {
6161

6262
def isScala211: Boolean
6363
def isScala212: Boolean
64+
def isScala213: Boolean
6465
}
6566

6667
object MutableSettings {

src/reflect/scala/reflect/internal/tpe/TypeComparers.scala

+27-2
Original file line numberDiff line numberDiff line change
@@ -365,7 +365,32 @@ trait TypeComparers {
365365

366366
// @assume tp1.isHigherKinded || tp2.isHigherKinded
367367
def isHKSubType(tp1: Type, tp2: Type, depth: Depth): Boolean = {
368-
def isSub(ntp1: Type, ntp2: Type) = (ntp1.withoutAnnotations, ntp2.withoutAnnotations) match {
368+
369+
def isSubHKTypeVar(tp1: Type, tp2: Type) = (tp1, tp2) match {
370+
case (tv1 @ TypeVar(_, _), tv2 @ TypeVar(_, _)) =>
371+
reporter.warning(tv1.typeSymbol.pos,
372+
sm"""|compiler bug: Unexpected code path: testing two type variables for subtype relation:
373+
| ${tv1} <:< ${tv2}
374+
|Please report bug at https://github.com/scala/bug/issues
375+
""".trim)
376+
false
377+
case (tp1, tv2 @ TypeVar(_, _)) =>
378+
val ntp1 = tp1.normalize
379+
(tv2.params corresponds ntp1.typeParams)(methodHigherOrderTypeParamsSubVariance) &&
380+
{ tv2.addLoBound(ntp1); true }
381+
case (tv1 @ TypeVar(_, _), tp2) =>
382+
val ntp2 = tp2.normalize
383+
(ntp2.typeParams corresponds tv1.params)(methodHigherOrderTypeParamsSubVariance) &&
384+
{ tv1.addHiBound(ntp2); true }
385+
case _ =>
386+
false
387+
}
388+
389+
def isSub(tp1: Type, tp2: Type) =
390+
settings.isScala213 && isSubHKTypeVar(tp1, tp2) ||
391+
isSub2(tp1.normalize, tp2.normalize) // @M! normalize reduces higher-kinded case to PolyType's
392+
393+
def isSub2(ntp1: Type, ntp2: Type) = (ntp1, ntp2) match {
369394
case (TypeRef(_, AnyClass, _), _) => false // avoid some warnings when Nothing/Any are on the other side
370395
case (_, TypeRef(_, NothingClass, _)) => false
371396
case (pt1: PolyType, pt2: PolyType) => isPolySubType(pt1, pt2) // @assume both .isHigherKinded (both normalized to PolyType)
@@ -381,7 +406,7 @@ trait TypeComparers {
381406
|| (if (isNoArgStaticClassTypeRef(tp1) && isNoArgStaticClassTypeRef(tp2))
382407
tp1.typeSymbolDirect.isNonBottomSubClass(tp2.typeSymbolDirect) // OPT faster than comparing eta-expanded types
383408
else
384-
isSub(tp1.normalize, tp2.normalize) && annotationsConform(tp1, tp2) // @M! normalize reduces higher-kinded case to PolyType's
409+
isSub(tp1.withoutAnnotations, tp2.withoutAnnotations) && annotationsConform(tp1, tp2)
385410
)
386411
)
387412
}

src/reflect/scala/reflect/runtime/Settings.scala

+1
Original file line numberDiff line numberDiff line change
@@ -54,4 +54,5 @@ private[reflect] class Settings extends MutableSettings {
5454
val maxClassfileName = new IntSetting(255)
5555
def isScala211 = true
5656
def isScala212 = true
57+
def isScala213 = false
5758
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
hk-typevar-unification.scala:16: error: inferred kinds of the type arguments ([_]Foo[_]) do not conform to the expected kinds of the type parameters (type F).
2+
[_]Foo[_]'s type parameters do not match type F's expected parameters:
3+
type _ (in class Foo) is invariant, but type _ is declared covariant
4+
g(tcFoo)
5+
^
6+
hk-typevar-unification.scala:16: error: type mismatch;
7+
found : TC[Foo]
8+
required: TC[F]
9+
g(tcFoo)
10+
^
11+
two errors found
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
-Xsource:2.13
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
trait TC[F[_]]
2+
class A
3+
class Foo[_]
4+
5+
object Test {
6+
7+
def f[F[_ <: A]](tc: TC[F]): Unit = ()
8+
def g[F[+_]] (tc: TC[F]): Unit = ()
9+
10+
val tcFoo: TC[Foo] = new TC[Foo] {}
11+
12+
// incompatible bounds
13+
f(tcFoo)
14+
15+
// incompatible variance
16+
g(tcFoo)
17+
}

test/files/pos/patmat-hk.flags

+1
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
-Xsource:2.13

test/files/pos/patmat-hk.scala

+13
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
case class Foo[F[_]]()
2+
3+
case class APair[F[_], G[_], A](f: F[A], g: G[A])
4+
5+
object Test {
6+
Foo[({ type L[a] = (a, Int) })#L]() match {
7+
case Foo() => ()
8+
}
9+
10+
APair[({ type L[a] = (Boolean, a) })#L, ({ type L[a] = a => Int })#L, String]((true, "two"), _.length) match {
11+
case APair((b, s), f) => ()
12+
}
13+
}

test/files/pos/t10185.flags

+1
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
-Xsource:2.13

test/files/pos/t10185.scala

+10
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
sealed trait Foo[A, F[_ <: A]]
2+
case class Bar[A, F[_ <: A]]() extends Foo[A, F]
3+
4+
class F[S <: String]
5+
6+
object Test {
7+
def f(foo: Foo[String, F]): Unit = foo match {
8+
case Bar() => ()
9+
}
10+
}

test/files/pos/t10195.flags

+1
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
-Xsource:2.13

test/files/pos/t10195.scala

+11
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
sealed trait Foo[F[_]]
2+
case class Bar[F[_]]() extends Foo[F]
3+
4+
object Test {
5+
6+
val foo: Foo[({ type Out[X] = String })#Out] = ???
7+
8+
foo match {
9+
case Bar() =>
10+
}
11+
}

test/files/pos/t10197.flags

+1
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
-Xsource:2.13

test/files/pos/t10197.scala

+38
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
import scala.language.higherKinds
2+
3+
final case class Getter[S, A](get: S => A)
4+
5+
final case class Wrap[F[_], A](value: F[A])
6+
7+
object Wrap {
8+
// Helper to defer specifying second argument to Wrap.
9+
// Basically a type lambda specialized for Wrap.
10+
// Wr[F]#ap[A] =:= Wrap[F, A]
11+
type Wr[F[_]] = { type ap[A] = Wrap[F, A] }
12+
13+
implicit def unwrapper[F[_], A]: Getter[Wrap[F, A], F[A]] =
14+
Getter(w => w.value)
15+
}
16+
17+
object Test {
18+
import Wrap._
19+
20+
type Foo[A] = List[A]
21+
type Bar[A] = String
22+
23+
type WrapFoo1[A] = Wrap[Foo, A]
24+
type WrapBar1[A] = Wrap[Bar, A]
25+
26+
implicitly[Getter[WrapFoo1[Int], Foo[Int]]]
27+
implicitly[Getter[WrapBar1[Int], Bar[Int]]]
28+
29+
type WrapFoo2[A] = Wr[Foo]#ap[A]
30+
type WrapBar2[A] = Wr[Bar]#ap[A]
31+
32+
// here's evidence that the new types are the same as the old ones
33+
implicitly[WrapFoo2[Int] =:= WrapFoo1[Int]]
34+
implicitly[WrapBar2[Int] =:= WrapBar1[Int]]
35+
36+
implicitly[Getter[WrapFoo2[Int], Foo[Int]]]
37+
implicitly[Getter[WrapBar2[Int], Bar[Int]]]
38+
}

test/files/pos/t10213.flags

+1
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
-Xsource:2.13

test/files/pos/t10213.scala

+53
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
import scala.language.higherKinds
2+
3+
final case class Coproduct[F[_], G[_], A](run: Either[F[A], G[A]])
4+
5+
object Coproduct {
6+
7+
sealed trait Builder {
8+
type Out[_]
9+
}
10+
11+
sealed trait :++:[F[_], G[_]] extends Builder {
12+
type Out[A] = Coproduct[F, G, A]
13+
}
14+
15+
sealed trait :+:[F[_], B <: Builder] extends Builder {
16+
type Out[A] = Coproduct[F, B#Out, A]
17+
}
18+
}
19+
20+
trait Inject[F[_], H[_]] {
21+
def inj[A](fa: F[A]): H[A]
22+
}
23+
24+
object Inject {
25+
import Coproduct._
26+
27+
implicit def reflexiveInject[F[_]]: Inject[F, F] =
28+
new Inject[F, F] {
29+
def inj[A](fa: F[A]): F[A] = fa
30+
}
31+
32+
implicit def injectLeft[F[_], G[_]]: Inject[F, (F :++: G)#Out] =
33+
new Inject[F, (F :++: G)#Out] {
34+
def inj[A](fa: F[A]): Coproduct[F, G, A] = Coproduct(Left(fa))
35+
}
36+
37+
implicit def injectRight[F[_], G[_], H[_]](implicit I: Inject[F, H]): Inject[F, (G :++: H)#Out] =
38+
new Inject[F, (G :++: H)#Out] {
39+
def inj[A](fa: F[A]): Coproduct[G, H , A] = Coproduct(Right(I.inj(fa)))
40+
}
41+
}
42+
43+
object Test1 {
44+
import Coproduct.{:++:, :+:}
45+
46+
class Foo[A]
47+
class Bar[A]
48+
class Baz[A]
49+
50+
implicitly[Inject[Baz, (Foo :+: Bar :++: Baz)#Out]]
51+
52+
implicitly[Inject[Baz, ({ type Out[A] = Coproduct[Foo, ({ type Out1[a] = Coproduct[Bar, Baz, a] })#Out1, A] })#Out]]
53+
}

test/files/pos/t10238.flags

+1
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
-Xsource:2.13

test/files/pos/t10238.scala

+36
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
object Test {
2+
3+
// Data types
4+
5+
type Id[A] = A
6+
7+
class MaybeT[F[_], A]
8+
9+
type Maybe[A] = MaybeT[Id, A]
10+
11+
type MaybeMaybe[A] = MaybeT[Maybe, A]
12+
13+
14+
// Typeclass
15+
16+
trait Monad[F[_]]
17+
18+
19+
// Instances
20+
21+
implicit val monadId: Monad[Id] = ???
22+
23+
implicit def monadMaybeT[F[_]: Monad]: Monad[({ type λ[A] = MaybeT[F, A] })#λ] = ???
24+
25+
implicit val monadOption: Monad[Option] = ???
26+
27+
28+
// Implicit search tests
29+
30+
implicitly[Monad[Id]]
31+
implicitly[Monad[({ type λ[A] = A })#λ]]
32+
implicitly[Monad[Maybe]]
33+
implicitly[Monad[({ type λ[A] = MaybeT[Id, A] })#λ]]
34+
implicitly[Monad[MaybeMaybe]]
35+
implicitly[Monad[({ type λ[A] = MaybeT[Maybe, A] })#λ]]
36+
}

test/files/pos/t10372.flags

+1
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
-Xsource:2.13

test/files/pos/t10372.scala

+16
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
import scala.language.higherKinds
2+
import scala.language.implicitConversions
3+
4+
object Test {
5+
class Expected[T, Func[_]]
6+
implicit def conv[T, Func[_]](i : Int) : Expected[T, Func] = ???
7+
type FuncId[T] = T
8+
9+
object DoesNotCompile {
10+
class Bla {
11+
type Alias[T] = Expected[T, FuncId]
12+
def bla[T](expected : Alias[T]) : Unit = {}
13+
}
14+
(new Bla).bla(2)
15+
}
16+
}

test/files/pos/t6895b-2.flags

+1
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
-Xsource:2.13

test/files/pos/t6895b-2.scala

+39
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
trait Foo[F[_]]
2+
trait Bar[F[_], A]
3+
4+
trait Or[A, B]
5+
6+
class Test {
7+
implicit def orFoo[A]: Foo[({type L[X] = Or[A, X]})#L] = ???
8+
implicit def barFoo[F[_]](implicit f: Foo[F]): Foo[({type L[X] = Bar[F, X]})#L] = ???
9+
10+
// Now we can define a couple of type aliases:
11+
type StringOr[X] = Or[String, X]
12+
type BarStringOr[X] = Bar[StringOr, X]
13+
14+
// ok
15+
implicitly[Foo[BarStringOr]]
16+
barFoo[StringOr](null) : Foo[BarStringOr]
17+
barFoo(null) : Foo[BarStringOr]
18+
19+
// nok
20+
implicitly[Foo[({type L[X] = Bar[StringOr, X]})#L]]
21+
// Let's write the application explicitly, and then
22+
// compile with just this line enabled and -explaintypes.
23+
barFoo(null) : Foo[({type L[X] = Bar[StringOr, X]})#L]
24+
25+
// Foo[[X]Bar[F,X]] <: Foo[[X]Bar[[X]Or[String,X],X]]?
26+
// Bar[[X]Or[String,X],X] <: Bar[F,X]?
27+
// F[_] <: Or[String,_]?
28+
// false
29+
// false
30+
// false
31+
32+
// Note that the type annotation above is typechecked as
33+
// Foo[[X]Bar[[X]Or[String,X],X]], ie the type alias `L`
34+
// is eta expanded.
35+
//
36+
// This is done so that it does not escape its defining scope.
37+
// However, one this is done, higher kinded inference
38+
// no longer is able to unify F with `StringOr` (scala/bug#2712)
39+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
Some(1)
2+
Some(1)
3+
Some((hi,5))
4+
Some((hi,5))
5+
Some(X)
6+
Some(X)
7+
Some(X)
8+
Some(X)
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
-Xsource:2.13

0 commit comments

Comments
 (0)