From 69b04d122d4f4527110a031bb2f271e3e807b8e8 Mon Sep 17 00:00:00 2001 From: Georgi Krastev Date: Tue, 29 Dec 2020 15:12:22 +0200 Subject: [PATCH 1/2] Correct bounds for higher-kinded GADT skolems --- .../tools/nsc/typechecker/PatternTypers.scala | 12 +++---- .../scala/reflect/internal/tpe/TypeMaps.scala | 15 ++++----- test/files/neg/hk-existential-lb.check | 2 +- test/files/pos/t12295.scala | 33 +++++++++++++++++++ 4 files changed, 47 insertions(+), 15 deletions(-) create mode 100644 test/files/pos/t12295.scala diff --git a/src/compiler/scala/tools/nsc/typechecker/PatternTypers.scala b/src/compiler/scala/tools/nsc/typechecker/PatternTypers.scala index 192483b4ce3c..74bfd0491a14 100644 --- a/src/compiler/scala/tools/nsc/typechecker/PatternTypers.scala +++ b/src/compiler/scala/tools/nsc/typechecker/PatternTypers.scala @@ -195,16 +195,16 @@ trait PatternTypers { def skolems = try skolemBuffer.toList finally skolemBuffer.clear() def apply(tp: Type): Type = mapOver(tp) match { - case tp @ TypeRef(NoPrefix, tpSym, Nil) if eligible(tpSym) => - val bounds = ( - if (variance.isInvariant) tpSym.tpeHK.bounds - else if (variance.isPositive) TypeBounds.upper(tpSym.tpeHK) - else TypeBounds.lower(tpSym.tpeHK) + case TypeRef(NoPrefix, tpSym, Nil) if eligible(tpSym) => + val bounds = genPolyType(tpSym.typeParams, + if (variance.isInvariant) tpSym.tpe.bounds + else if (variance.isPositive) TypeBounds.upper(tpSym.tpe) + else TypeBounds.lower(tpSym.tpe) ) // origin must be the type param so we can deskolemize val skolem = context.owner.newGADTSkolem(freshTypeName("?" + tpSym.name), tpSym, bounds) skolemBuffer += skolem - logResult(s"Created gadt skolem $skolem: ${skolem.tpe_*} to stand in for $tpSym")(skolem.tpe_*) + logResult(s"Created gadt skolem $skolem: ${skolem.tpeHK} to stand in for $tpSym")(skolem.tpeHK) case tp1 => tp1 } } diff --git a/src/reflect/scala/reflect/internal/tpe/TypeMaps.scala b/src/reflect/scala/reflect/internal/tpe/TypeMaps.scala index 5604e7d88e86..a501b78b92a7 100644 --- a/src/reflect/scala/reflect/internal/tpe/TypeMaps.scala +++ b/src/reflect/scala/reflect/internal/tpe/TypeMaps.scala @@ -345,18 +345,17 @@ private[internal] trait TypeMaps { val tp1 = mapOver(tp) if (variance.isInvariant) tp1 else tp1 match { - case TypeRef(pre, sym, args) if tparams.contains(sym) && occurCount(sym) == 1 => - val repl = if (variance.isPositive) dropSingletonType(tp1.upperBound) else tp1.lowerBound + case TypeRef(_, sym, typeArgs) if tparams.contains(sym) && occurCount(sym) == 1 => def msg = { val word = if (variance.isPositive) "upper" else "lower" s"Widened lone occurrence of $tp1 inside existential to $word bound" } - if (!repl.typeSymbol.isBottomClass && ! anyContains.collect(repl)) - debuglogResult(msg)(repl) - else - tp1 - case _ => - tp1 + + val widened = if (variance.isPositive) dropSingletonType(tp1.upperBound) else tp1.lowerBound + if (widened.typeSymbol.isBottomClass || anyContains.collect(widened)) tp1 + else debuglogResult(msg)(appliedType(genPolyType(sym.typeParams, widened), typeArgs)) + case other => + other } } override def mapOver(tp: Type): Type = tp match { diff --git a/test/files/neg/hk-existential-lb.check b/test/files/neg/hk-existential-lb.check index 287da95c412f..4779aa9cc784 100644 --- a/test/files/neg/hk-existential-lb.check +++ b/test/files/neg/hk-existential-lb.check @@ -1,7 +1,7 @@ hk-existential-lb.scala:5: error: type mismatch; found : Functor[Option] required: Functor[_[x] >: List[x]] -Note: Option <: Any, but class Functor is invariant in type F. +Note: Option <: [x]Any, but class Functor is invariant in type F. You may wish to define F as +F instead. (SLS 4.5) val someF: Functor[F] forSome { type F[x] >: List[x] } = new Functor[Option] ^ diff --git a/test/files/pos/t12295.scala b/test/files/pos/t12295.scala new file mode 100644 index 000000000000..24150cb97103 --- /dev/null +++ b/test/files/pos/t12295.scala @@ -0,0 +1,33 @@ +object Test { + sealed trait Foo[+A] // sealed or not doesn't matter + case class ImplA[A](a: A) extends Foo[A] + case class ImplAny[A](a: Any) extends Foo[A] + + trait Bar[+G[_]] // must be covariant + + def err[F[_]](): Unit = { + val x: Foo[Foo[Bar[F]]] = ??? + + x match { + case ImplAny(ImplAny(_)) => ??? + case ImplAny(ImplA(_)) => ??? + case ImplA(_) => ??? + case ImplAny(_) => ??? + case _ => ??? + } + + x match { + case ImplA(ImplA(_)) => ??? + case ImplA(ImplAny(_)) => ??? + case ImplA(y) => y.toString + case ImplA(y) => y match { + case ImplA(_) => ??? + case _ => ??? + } + case ImplA(y) => y + case _ => ??? + } + + () + } +} From cd13abc76838c05302012e29ea38344b9ea42818 Mon Sep 17 00:00:00 2001 From: Georgi Krastev Date: Tue, 5 Jan 2021 20:50:39 +0200 Subject: [PATCH 2/2] Preserve kind of relativeInfo --- src/reflect/scala/reflect/internal/Types.scala | 2 +- .../scala/reflect/internal/tpe/TypeMaps.scala | 15 ++++++++------- test/files/run/repl-kind.check | 4 ++-- 3 files changed, 11 insertions(+), 10 deletions(-) diff --git a/src/reflect/scala/reflect/internal/Types.scala b/src/reflect/scala/reflect/internal/Types.scala index dc10202117c4..1cf542d9979b 100644 --- a/src/reflect/scala/reflect/internal/Types.scala +++ b/src/reflect/scala/reflect/internal/Types.scala @@ -2438,7 +2438,7 @@ trait Types // interpret symbol's info in terms of the type's prefix and type args - protected def relativeInfo: Type = appliedType(sym.info.asSeenFrom(pre, sym.owner), argsOrDummies) + protected def relativeInfo: Type = appliedType(sym.info.asSeenFrom(pre, sym.owner), args) // @M: propagate actual type params (args) to `tp`, by replacing // formal type parameters with actual ones. If tp is higher kinded, diff --git a/src/reflect/scala/reflect/internal/tpe/TypeMaps.scala b/src/reflect/scala/reflect/internal/tpe/TypeMaps.scala index a501b78b92a7..5604e7d88e86 100644 --- a/src/reflect/scala/reflect/internal/tpe/TypeMaps.scala +++ b/src/reflect/scala/reflect/internal/tpe/TypeMaps.scala @@ -345,17 +345,18 @@ private[internal] trait TypeMaps { val tp1 = mapOver(tp) if (variance.isInvariant) tp1 else tp1 match { - case TypeRef(_, sym, typeArgs) if tparams.contains(sym) && occurCount(sym) == 1 => + case TypeRef(pre, sym, args) if tparams.contains(sym) && occurCount(sym) == 1 => + val repl = if (variance.isPositive) dropSingletonType(tp1.upperBound) else tp1.lowerBound def msg = { val word = if (variance.isPositive) "upper" else "lower" s"Widened lone occurrence of $tp1 inside existential to $word bound" } - - val widened = if (variance.isPositive) dropSingletonType(tp1.upperBound) else tp1.lowerBound - if (widened.typeSymbol.isBottomClass || anyContains.collect(widened)) tp1 - else debuglogResult(msg)(appliedType(genPolyType(sym.typeParams, widened), typeArgs)) - case other => - other + if (!repl.typeSymbol.isBottomClass && ! anyContains.collect(repl)) + debuglogResult(msg)(repl) + else + tp1 + case _ => + tp1 } } override def mapOver(tp: Type): Type = tp match { diff --git a/test/files/run/repl-kind.check b/test/files/run/repl-kind.check index d18f1af0b3b2..099619c2fc21 100644 --- a/test/files/run/repl-kind.check +++ b/test/files/run/repl-kind.check @@ -14,8 +14,8 @@ Either's kind is F[+A1,+A2] This is a type constructor: a 1st-order-kinded type. scala> :k -v scala.collection.SortedMapOps -scala.collection.SortedMapOps's kind is X[K,+V,+CC[X,Y] <: scala.collection.Map[X,Y] with scala.collection.SortedMapOps[X, Y, CC, _],+C <: scala.collection.SortedMapOps[K,V,CC,C]] -* -> * -(+)-> (* -> * -> *(scala.collection.Map[X,Y] with scala.collection.SortedMapOps[X, Y, CC, _])) -(+)-> *(scala.collection.SortedMapOps[K,V,CC,C]) -(+)-> * +scala.collection.SortedMapOps's kind is X[K,+V,+CC[X,Y] <: [X, Y]scala.collection.Map[X,Y] with scala.collection.SortedMapOps[X, Y, CC, _],+C <: scala.collection.SortedMapOps[K,V,CC,C]] +* -> * -(+)-> (* -> * -> *([X, Y]scala.collection.Map[X,Y] with scala.collection.SortedMapOps[X, Y, CC, _])) -(+)-> *(scala.collection.SortedMapOps[K,V,CC,C]) -(+)-> * This is a type constructor that takes type constructor(s): a higher-kinded type. scala> :kind -v Tuple2