From d850991cd8f782c2f811b7cbe527123cf1d6b200 Mon Sep 17 00:00:00 2001 From: Guillaume Martres Date: Tue, 1 Sep 2020 00:40:24 +0200 Subject: [PATCH] Record ordering constraint between HK type variables If `?F <: [X] => ?G[X]`, then the ordering part of the constraint should record `?F <: ?G`, otherwise constraints won't be propagated correctly. It's not clear to me if we should make a similar change in `stripParams` but note that doing so would require being a bit careful: since we use the upper bound of a type variable to determine its kind, we can't just replace it by `[X] => ?G[X]` by `AnyKind`. Fixes #9676. --- .../dotty/tools/dotc/core/OrderingConstraint.scala | 6 ++++-- .../dotty/tools/dotc/core/TypeApplications.scala | 5 +++-- .../src/dotty/tools/dotc/core/TypeComparer.scala | 4 ++-- tests/pos/i9676.scala | 13 +++++++++++++ 4 files changed, 22 insertions(+), 6 deletions(-) create mode 100644 tests/pos/i9676.scala diff --git a/compiler/src/dotty/tools/dotc/core/OrderingConstraint.scala b/compiler/src/dotty/tools/dotc/core/OrderingConstraint.scala index 53832acc82b4..84f44010e3a2 100644 --- a/compiler/src/dotty/tools/dotc/core/OrderingConstraint.scala +++ b/compiler/src/dotty/tools/dotc/core/OrderingConstraint.scala @@ -2,7 +2,7 @@ package dotty.tools package dotc package core -import Types._, Contexts._, Symbols._, Decorators._ +import Types._, Contexts._, Symbols._, Decorators._, TypeApplications._ import util.SimpleIdentityMap import collection.mutable import printing.Printer @@ -360,13 +360,15 @@ class OrderingConstraint(private val boundsMap: ParamBounds, * Q <: tp implies Q <: P and isUpper = true, or * tp <: Q implies P <: Q and isUpper = false */ - private def dependentParams(tp: Type, isUpper: Boolean): List[TypeParamRef] = tp match + private def dependentParams(tp: Type, isUpper: Boolean)(using Context): List[TypeParamRef] = tp match case param: TypeParamRef if contains(param) => param :: (if (isUpper) upper(param) else lower(param)) case tp: AndType if isUpper => dependentParams(tp.tp1, isUpper) | (dependentParams(tp.tp2, isUpper)) case tp: OrType if !isUpper => dependentParams(tp.tp1, isUpper).intersect(dependentParams(tp.tp2, isUpper)) + case EtaExpansion(tycon) => + dependentParams(tycon, isUpper) case _ => Nil diff --git a/compiler/src/dotty/tools/dotc/core/TypeApplications.scala b/compiler/src/dotty/tools/dotc/core/TypeApplications.scala index 857b4e4aae42..eaf0fa9ad420 100644 --- a/compiler/src/dotty/tools/dotc/core/TypeApplications.scala +++ b/compiler/src/dotty/tools/dotc/core/TypeApplications.scala @@ -39,8 +39,9 @@ object TypeApplications { tycon.EtaExpand(tycon.typeParamSymbols) } - def unapply(tp: Type)(using Context): Option[TypeRef] = tp match { - case tp @ HKTypeLambda(tparams, AppliedType(fn: TypeRef, args)) if (args == tparams.map(_.paramRef)) => Some(fn) + def unapply(tp: Type)(using Context): Option[Type] = tp match { + case tp @ HKTypeLambda(tparams, AppliedType(fn: Type, args)) + if args.lazyZip(tparams).forall((arg, tparam) => arg == tparam.paramRef) => Some(fn) case _ => None } } diff --git a/compiler/src/dotty/tools/dotc/core/TypeComparer.scala b/compiler/src/dotty/tools/dotc/core/TypeComparer.scala index 864f9705f753..28cb5414985c 100644 --- a/compiler/src/dotty/tools/dotc/core/TypeComparer.scala +++ b/compiler/src/dotty/tools/dotc/core/TypeComparer.scala @@ -605,7 +605,7 @@ class TypeComparer(@constructorOnly initctx: Context) extends ConstraintHandling if (tparams1.nonEmpty) return recur(tp1.EtaExpand(tparams1), tp2) || fourthTry tp2 match { - case EtaExpansion(tycon2) if tycon2.symbol.isClass && tycon2.symbol.is(JavaDefined) => + case EtaExpansion(tycon2: TypeRef) if tycon2.symbol.isClass && tycon2.symbol.is(JavaDefined) => recur(tp1, tycon2) || fourthTry case _ => fourthTry @@ -769,7 +769,7 @@ class TypeComparer(@constructorOnly initctx: Context) extends ConstraintHandling isNewSubType(tp1.parent) case tp1: HKTypeLambda => def compareHKLambda = tp1 match { - case EtaExpansion(tycon1) if tycon1.symbol.isClass && tycon1.symbol.is(JavaDefined) => + case EtaExpansion(tycon1: TypeRef) 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) diff --git a/tests/pos/i9676.scala b/tests/pos/i9676.scala new file mode 100644 index 000000000000..1703f047e30c --- /dev/null +++ b/tests/pos/i9676.scala @@ -0,0 +1,13 @@ +trait SubtypeOf[A[_], B[_]] + +object Test1 { + def instance[F[_], G[a] >: F[a]]: SubtypeOf[F, G] = new SubtypeOf[F, G] {} + + val x: SubtypeOf[List, Seq] = instance +} + +object Test2 { + def instance[G[_], F[a] <: G[a]]: SubtypeOf[F, G] = new SubtypeOf[F, G] {} + + val x: SubtypeOf[List, Seq] = instance +}