From 65b171c3abb1d8c5cd50e10f6722f0a39fd9ad46 Mon Sep 17 00:00:00 2001 From: Yichen Xu Date: Mon, 9 Aug 2021 02:26:43 +0800 Subject: [PATCH 01/14] Approximate GADT lower bound for HKTs --- compiler/src/dotty/tools/dotc/core/TypeComparer.scala | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/compiler/src/dotty/tools/dotc/core/TypeComparer.scala b/compiler/src/dotty/tools/dotc/core/TypeComparer.scala index eee0e802528c..038330c2f6a1 100644 --- a/compiler/src/dotty/tools/dotc/core/TypeComparer.scala +++ b/compiler/src/dotty/tools/dotc/core/TypeComparer.scala @@ -1167,6 +1167,15 @@ class TypeComparer(@constructorOnly initctx: Context) extends ConstraintHandling else fallback(tycon2bounds.lo) + def compareTyConGadtBounds: Boolean = + tycon2 match + case tycon2: TypeRef => + val tycon2sym = tycon2.symbol + tycon2sym.onGadtBounds { bounds2 => + compareLower(bounds2, tyconIsTypeRef = false) + } + case _ => false + tycon2 match { case param2: TypeParamRef => isMatchingApply(tp1) || @@ -1174,6 +1183,7 @@ class TypeComparer(@constructorOnly initctx: Context) extends ConstraintHandling compareLower(bounds(param2), tyconIsTypeRef = false) case tycon2: TypeRef => isMatchingApply(tp1) || + compareTyConGadtBounds || defn.isCompiletimeAppliedType(tycon2.symbol) && compareCompiletimeAppliedType(tp2, tp1, fromBelow = true) || { tycon2.info match { case info2: TypeBounds => From 23f2d7591e9c930b6e645bf99522f2ced7ef834c Mon Sep 17 00:00:00 2001 From: Yichen Xu Date: Mon, 9 Aug 2021 02:28:18 +0800 Subject: [PATCH 02/14] Properly record the usage of GADT bounds --- .../dotty/tools/dotc/core/TypeComparer.scala | 20 ++++++++++--------- 1 file changed, 11 insertions(+), 9 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/core/TypeComparer.scala b/compiler/src/dotty/tools/dotc/core/TypeComparer.scala index 038330c2f6a1..a964a56c0b3d 100644 --- a/compiler/src/dotty/tools/dotc/core/TypeComparer.scala +++ b/compiler/src/dotty/tools/dotc/core/TypeComparer.scala @@ -1167,14 +1167,16 @@ class TypeComparer(@constructorOnly initctx: Context) extends ConstraintHandling else fallback(tycon2bounds.lo) - def compareTyConGadtBounds: Boolean = - tycon2 match - case tycon2: TypeRef => - val tycon2sym = tycon2.symbol - tycon2sym.onGadtBounds { bounds2 => - compareLower(bounds2, tyconIsTypeRef = false) - } - case _ => false + def byGadtBounds: Boolean = + { + tycon2 match + case tycon2: TypeRef => + val tycon2sym = tycon2.symbol + tycon2sym.onGadtBounds { bounds2 => + compareLower(bounds2, tyconIsTypeRef = false) + } + case _ => false + } && { GADTused = true; true } tycon2 match { case param2: TypeParamRef => @@ -1183,7 +1185,7 @@ class TypeComparer(@constructorOnly initctx: Context) extends ConstraintHandling compareLower(bounds(param2), tyconIsTypeRef = false) case tycon2: TypeRef => isMatchingApply(tp1) || - compareTyConGadtBounds || + byGadtBounds || defn.isCompiletimeAppliedType(tycon2.symbol) && compareCompiletimeAppliedType(tp2, tp1, fromBelow = true) || { tycon2.info match { case info2: TypeBounds => From e4edcea8042615b714d5b44ace49cdb3f026da99 Mon Sep 17 00:00:00 2001 From: Yichen Xu Date: Wed, 18 Aug 2021 15:01:05 +0800 Subject: [PATCH 03/14] Fix the injectivity issue --- compiler/src/dotty/tools/dotc/core/TypeComparer.scala | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/compiler/src/dotty/tools/dotc/core/TypeComparer.scala b/compiler/src/dotty/tools/dotc/core/TypeComparer.scala index a964a56c0b3d..37a46ff1d52b 100644 --- a/compiler/src/dotty/tools/dotc/core/TypeComparer.scala +++ b/compiler/src/dotty/tools/dotc/core/TypeComparer.scala @@ -9,6 +9,7 @@ import StdNames.nme import TypeOps.refineUsingParent import collection.mutable import util.Stats +import scala.util.DynamicVariable import config.Config import config.Feature.migrateTo3 import config.Printers.{constr, subtyping, gadts, matchTypes, noPrinter} @@ -60,6 +61,11 @@ class TypeComparer(@constructorOnly initctx: Context) extends ConstraintHandling /** Indicates whether the subtype check used GADT bounds */ private var GADTused: Boolean = false + /** Indicates whether we have touched HKT GADT bounds */ + private val touchedHKGADT: DynamicVariable[Boolean] = new DynamicVariable(false) + + private def HKGADTtouched[T](body: => T): T = touchedHKGADT.withValue(true) { body } + private var myInstance: TypeComparer = this def currentInstance: TypeComparer = myInstance @@ -1092,6 +1098,7 @@ class TypeComparer(@constructorOnly initctx: Context) extends ConstraintHandling val tyconIsInjective = (tycon1sym.isClass || tycon2sym.isClass) && (!touchedGADTs || gadtIsInstantiated) + && !touchedHKGADT.value inFrozenGadtIf(!tyconIsInjective) { if tycon1sym == tycon2sym && tycon1sym.isAliasType then @@ -1173,7 +1180,7 @@ class TypeComparer(@constructorOnly initctx: Context) extends ConstraintHandling case tycon2: TypeRef => val tycon2sym = tycon2.symbol tycon2sym.onGadtBounds { bounds2 => - compareLower(bounds2, tyconIsTypeRef = false) + HKGADTtouched { compareLower(bounds2, tyconIsTypeRef = false) } } case _ => false } && { GADTused = true; true } From ab6aa685b8fe9770318c054d7f0f9976c20359a8 Mon Sep 17 00:00:00 2001 From: Yichen Xu Date: Wed, 18 Aug 2021 15:11:36 +0800 Subject: [PATCH 04/14] Add pos testcase --- tests/pos/gadt-hkt-bounds.scala | 7 +++++++ 1 file changed, 7 insertions(+) create mode 100644 tests/pos/gadt-hkt-bounds.scala diff --git a/tests/pos/gadt-hkt-bounds.scala b/tests/pos/gadt-hkt-bounds.scala new file mode 100644 index 000000000000..7b9fb976fbfa --- /dev/null +++ b/tests/pos/gadt-hkt-bounds.scala @@ -0,0 +1,7 @@ +type Const = [X] =>> Int + +trait Expr[F[_]] +case class ConstExpr() extends Expr[Const] + +def foo[F[_], A](e: Expr[F]): F[A] = e match + case _: ConstExpr => 0 From 196033e7d0ee221e618ecf915d2c3fe44a34cf15 Mon Sep 17 00:00:00 2001 From: Yichen Xu Date: Wed, 18 Aug 2021 15:15:23 +0800 Subject: [PATCH 05/14] Add neg test --- tests/neg/gadt-hkt-hi-bounds.scala | 8 ++++++++ 1 file changed, 8 insertions(+) create mode 100644 tests/neg/gadt-hkt-hi-bounds.scala diff --git a/tests/neg/gadt-hkt-hi-bounds.scala b/tests/neg/gadt-hkt-hi-bounds.scala new file mode 100644 index 000000000000..0aec1fe74113 --- /dev/null +++ b/tests/neg/gadt-hkt-hi-bounds.scala @@ -0,0 +1,8 @@ +type Const = [X] =>> Int + +trait Expr[-F[_]] +case class ConstExpr() extends Expr[Const] + +def foo[F[_], A](e: Expr[F]) = e match + case _: ConstExpr => + val i: Int = ??? : F[A] // limitation // error From d551238552bd9e6ea4da19034b6c0ac5816f41a3 Mon Sep 17 00:00:00 2001 From: Yichen Xu Date: Wed, 18 Aug 2021 15:15:29 +0800 Subject: [PATCH 06/14] Update pos test --- tests/pos/gadt-hkt-bounds.scala | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/pos/gadt-hkt-bounds.scala b/tests/pos/gadt-hkt-bounds.scala index 7b9fb976fbfa..177b7fe044f3 100644 --- a/tests/pos/gadt-hkt-bounds.scala +++ b/tests/pos/gadt-hkt-bounds.scala @@ -1,6 +1,6 @@ type Const = [X] =>> Int -trait Expr[F[_]] +trait Expr[+F[_]] case class ConstExpr() extends Expr[Const] def foo[F[_], A](e: Expr[F]): F[A] = e match From 7cda5b5a5d196e7a0af9adacd8e4bc10a70762d2 Mon Sep 17 00:00:00 2001 From: Yichen Xu Date: Tue, 24 Aug 2021 01:55:48 +0800 Subject: [PATCH 07/14] Rename HKGadt state and use primitive boolean --- .../src/dotty/tools/dotc/core/TypeComparer.scala | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/core/TypeComparer.scala b/compiler/src/dotty/tools/dotc/core/TypeComparer.scala index 37a46ff1d52b..3c724907818e 100644 --- a/compiler/src/dotty/tools/dotc/core/TypeComparer.scala +++ b/compiler/src/dotty/tools/dotc/core/TypeComparer.scala @@ -62,9 +62,13 @@ class TypeComparer(@constructorOnly initctx: Context) extends ConstraintHandling private var GADTused: Boolean = false /** Indicates whether we have touched HKT GADT bounds */ - private val touchedHKGADT: DynamicVariable[Boolean] = new DynamicVariable(false) + private var HKGADTtouched: Boolean = false - private def HKGADTtouched[T](body: => T): T = touchedHKGADT.withValue(true) { body } + private def touchHKGadt[T](body: => T): T = + val savedHKGADTtouched = HKGADTtouched + val res = body + HKGADTtouched = savedHKGADTtouched + res private var myInstance: TypeComparer = this def currentInstance: TypeComparer = myInstance @@ -1098,7 +1102,7 @@ class TypeComparer(@constructorOnly initctx: Context) extends ConstraintHandling val tyconIsInjective = (tycon1sym.isClass || tycon2sym.isClass) && (!touchedGADTs || gadtIsInstantiated) - && !touchedHKGADT.value + && !HKGADTtouched inFrozenGadtIf(!tyconIsInjective) { if tycon1sym == tycon2sym && tycon1sym.isAliasType then @@ -1180,7 +1184,7 @@ class TypeComparer(@constructorOnly initctx: Context) extends ConstraintHandling case tycon2: TypeRef => val tycon2sym = tycon2.symbol tycon2sym.onGadtBounds { bounds2 => - HKGADTtouched { compareLower(bounds2, tyconIsTypeRef = false) } + touchHKGadt { compareLower(bounds2, tyconIsTypeRef = false) } } case _ => false } && { GADTused = true; true } From dfbe8d4f378e5c88475eaa6edbfa72045867ae60 Mon Sep 17 00:00:00 2001 From: Yichen Xu Date: Tue, 24 Aug 2021 11:18:12 +0800 Subject: [PATCH 08/14] Fix touchHKGadt --- compiler/src/dotty/tools/dotc/core/TypeComparer.scala | 1 + 1 file changed, 1 insertion(+) diff --git a/compiler/src/dotty/tools/dotc/core/TypeComparer.scala b/compiler/src/dotty/tools/dotc/core/TypeComparer.scala index 3c724907818e..226de5ab7670 100644 --- a/compiler/src/dotty/tools/dotc/core/TypeComparer.scala +++ b/compiler/src/dotty/tools/dotc/core/TypeComparer.scala @@ -66,6 +66,7 @@ class TypeComparer(@constructorOnly initctx: Context) extends ConstraintHandling private def touchHKGadt[T](body: => T): T = val savedHKGADTtouched = HKGADTtouched + HKGADTtouched = true val res = body HKGADTtouched = savedHKGADTtouched res From abb4155c01da54601d40ee412b31fe9105366c2a Mon Sep 17 00:00:00 2001 From: Yichen Xu Date: Wed, 25 Aug 2021 12:29:40 +0800 Subject: [PATCH 09/14] Utilize frozenGadt instead of using a new state variable --- .../src/dotty/tools/dotc/core/TypeComparer.scala | 14 ++------------ 1 file changed, 2 insertions(+), 12 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/core/TypeComparer.scala b/compiler/src/dotty/tools/dotc/core/TypeComparer.scala index 226de5ab7670..bd95d7323334 100644 --- a/compiler/src/dotty/tools/dotc/core/TypeComparer.scala +++ b/compiler/src/dotty/tools/dotc/core/TypeComparer.scala @@ -61,16 +61,6 @@ class TypeComparer(@constructorOnly initctx: Context) extends ConstraintHandling /** Indicates whether the subtype check used GADT bounds */ private var GADTused: Boolean = false - /** Indicates whether we have touched HKT GADT bounds */ - private var HKGADTtouched: Boolean = false - - private def touchHKGadt[T](body: => T): T = - val savedHKGADTtouched = HKGADTtouched - HKGADTtouched = true - val res = body - HKGADTtouched = savedHKGADTtouched - res - private var myInstance: TypeComparer = this def currentInstance: TypeComparer = myInstance @@ -1103,7 +1093,7 @@ class TypeComparer(@constructorOnly initctx: Context) extends ConstraintHandling val tyconIsInjective = (tycon1sym.isClass || tycon2sym.isClass) && (!touchedGADTs || gadtIsInstantiated) - && !HKGADTtouched + && !frozenGadt inFrozenGadtIf(!tyconIsInjective) { if tycon1sym == tycon2sym && tycon1sym.isAliasType then @@ -1185,7 +1175,7 @@ class TypeComparer(@constructorOnly initctx: Context) extends ConstraintHandling case tycon2: TypeRef => val tycon2sym = tycon2.symbol tycon2sym.onGadtBounds { bounds2 => - touchHKGadt { compareLower(bounds2, tyconIsTypeRef = false) } + inFrozenGadt { compareLower(bounds2, tyconIsTypeRef = false) } } case _ => false } && { GADTused = true; true } From 28c63814af708c273e835bee9ad2fbac76d523ab Mon Sep 17 00:00:00 2001 From: Yichen Xu Date: Thu, 26 Aug 2021 00:46:02 +0800 Subject: [PATCH 10/14] Remove unused import --- compiler/src/dotty/tools/dotc/core/TypeComparer.scala | 1 - 1 file changed, 1 deletion(-) diff --git a/compiler/src/dotty/tools/dotc/core/TypeComparer.scala b/compiler/src/dotty/tools/dotc/core/TypeComparer.scala index bd95d7323334..c2c1e08d154a 100644 --- a/compiler/src/dotty/tools/dotc/core/TypeComparer.scala +++ b/compiler/src/dotty/tools/dotc/core/TypeComparer.scala @@ -9,7 +9,6 @@ import StdNames.nme import TypeOps.refineUsingParent import collection.mutable import util.Stats -import scala.util.DynamicVariable import config.Config import config.Feature.migrateTo3 import config.Printers.{constr, subtyping, gadts, matchTypes, noPrinter} From 2da3c588a00452c9b775d318c2ce2987b33ab4ba Mon Sep 17 00:00:00 2001 From: Yichen Xu Date: Thu, 26 Aug 2021 23:47:32 +0800 Subject: [PATCH 11/14] Remove workaround --- compiler/src/dotty/tools/dotc/core/TypeComparer.scala | 1 - 1 file changed, 1 deletion(-) diff --git a/compiler/src/dotty/tools/dotc/core/TypeComparer.scala b/compiler/src/dotty/tools/dotc/core/TypeComparer.scala index c2c1e08d154a..6fda9b304411 100644 --- a/compiler/src/dotty/tools/dotc/core/TypeComparer.scala +++ b/compiler/src/dotty/tools/dotc/core/TypeComparer.scala @@ -1092,7 +1092,6 @@ class TypeComparer(@constructorOnly initctx: Context) extends ConstraintHandling val tyconIsInjective = (tycon1sym.isClass || tycon2sym.isClass) && (!touchedGADTs || gadtIsInstantiated) - && !frozenGadt inFrozenGadtIf(!tyconIsInjective) { if tycon1sym == tycon2sym && tycon1sym.isAliasType then From 0b323dc8a51b55e6ef10d2ff06764343787da1d3 Mon Sep 17 00:00:00 2001 From: Yichen Xu Date: Wed, 25 Aug 2021 14:05:37 +0800 Subject: [PATCH 12/14] Fix inFrozenGadtIf --- compiler/src/dotty/tools/dotc/core/TypeComparer.scala | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/compiler/src/dotty/tools/dotc/core/TypeComparer.scala b/compiler/src/dotty/tools/dotc/core/TypeComparer.scala index 6fda9b304411..b86722a7ba98 100644 --- a/compiler/src/dotty/tools/dotc/core/TypeComparer.scala +++ b/compiler/src/dotty/tools/dotc/core/TypeComparer.scala @@ -172,7 +172,7 @@ class TypeComparer(@constructorOnly initctx: Context) extends ConstraintHandling private inline def inFrozenGadtIf[T](cond: Boolean)(inline op: T): T = { val savedFrozenGadt = frozenGadt - frozenGadt = cond + frozenGadt ||= cond try op finally frozenGadt = savedFrozenGadt } From 8b45a005530281c6ed59f552eecc5f624bdb2a65 Mon Sep 17 00:00:00 2001 From: Yichen Xu Date: Sun, 29 Aug 2021 10:47:33 +0800 Subject: [PATCH 13/14] Also approximate with HKT's GADT higher bounds --- .../dotty/tools/dotc/core/TypeComparer.scala | 20 ++++++++++++++----- tests/{neg => pos}/gadt-hkt-hi-bounds.scala | 2 +- ...-bounds.scala => gadt-hkt-lo-bounds.scala} | 0 3 files changed, 16 insertions(+), 6 deletions(-) rename tests/{neg => pos}/gadt-hkt-hi-bounds.scala (73%) rename tests/pos/{gadt-hkt-bounds.scala => gadt-hkt-lo-bounds.scala} (100%) diff --git a/compiler/src/dotty/tools/dotc/core/TypeComparer.scala b/compiler/src/dotty/tools/dotc/core/TypeComparer.scala index b86722a7ba98..b0ee7c524f20 100644 --- a/compiler/src/dotty/tools/dotc/core/TypeComparer.scala +++ b/compiler/src/dotty/tools/dotc/core/TypeComparer.scala @@ -1225,11 +1225,21 @@ class TypeComparer(@constructorOnly initctx: Context) extends ConstraintHandling isSubType(bounds(param1).hi.applyIfParameterized(args1), tp2, approx.addLow) case tycon1: TypeRef => val sym = tycon1.symbol - !sym.isClass && { - defn.isCompiletimeAppliedType(sym) && compareCompiletimeAppliedType(tp1, tp2, fromBelow = false) || - recur(tp1.superType, tp2) || - tryLiftedToThis1 - } + + def byGadtBounds: Boolean = + { + sym.onGadtBounds { bounds1 => + inFrozenGadt { isSubType(bounds1.hi.applyIfParameterized(args1), tp2, approx.addLow) } + } + } && { GADTused = true; true } + + { + !sym.isClass && { + defn.isCompiletimeAppliedType(sym) && compareCompiletimeAppliedType(tp1, tp2, fromBelow = false) || + recur(tp1.superType, tp2) || + tryLiftedToThis1 + } + } || byGadtBounds case tycon1: TypeProxy => recur(tp1.superType, tp2) case _ => diff --git a/tests/neg/gadt-hkt-hi-bounds.scala b/tests/pos/gadt-hkt-hi-bounds.scala similarity index 73% rename from tests/neg/gadt-hkt-hi-bounds.scala rename to tests/pos/gadt-hkt-hi-bounds.scala index 0aec1fe74113..fca06f375b6b 100644 --- a/tests/neg/gadt-hkt-hi-bounds.scala +++ b/tests/pos/gadt-hkt-hi-bounds.scala @@ -5,4 +5,4 @@ case class ConstExpr() extends Expr[Const] def foo[F[_], A](e: Expr[F]) = e match case _: ConstExpr => - val i: Int = ??? : F[A] // limitation // error + val i: Int = ??? : F[A] diff --git a/tests/pos/gadt-hkt-bounds.scala b/tests/pos/gadt-hkt-lo-bounds.scala similarity index 100% rename from tests/pos/gadt-hkt-bounds.scala rename to tests/pos/gadt-hkt-lo-bounds.scala From d1a04e22f8fd35a2344260515d4dff57a3f46f86 Mon Sep 17 00:00:00 2001 From: Yichen Xu Date: Mon, 30 Aug 2021 21:08:26 +0800 Subject: [PATCH 14/14] Remove redundant braces --- .../dotty/tools/dotc/core/TypeComparer.scala | 19 ++++++++----------- 1 file changed, 8 insertions(+), 11 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/core/TypeComparer.scala b/compiler/src/dotty/tools/dotc/core/TypeComparer.scala index b0ee7c524f20..d167d55310a1 100644 --- a/compiler/src/dotty/tools/dotc/core/TypeComparer.scala +++ b/compiler/src/dotty/tools/dotc/core/TypeComparer.scala @@ -1227,19 +1227,16 @@ class TypeComparer(@constructorOnly initctx: Context) extends ConstraintHandling val sym = tycon1.symbol def byGadtBounds: Boolean = - { - sym.onGadtBounds { bounds1 => - inFrozenGadt { isSubType(bounds1.hi.applyIfParameterized(args1), tp2, approx.addLow) } - } + sym.onGadtBounds { bounds1 => + inFrozenGadt { isSubType(bounds1.hi.applyIfParameterized(args1), tp2, approx.addLow) } } && { GADTused = true; true } - { - !sym.isClass && { - defn.isCompiletimeAppliedType(sym) && compareCompiletimeAppliedType(tp1, tp2, fromBelow = false) || - recur(tp1.superType, tp2) || - tryLiftedToThis1 - } - } || byGadtBounds + + !sym.isClass && { + defn.isCompiletimeAppliedType(sym) && compareCompiletimeAppliedType(tp1, tp2, fromBelow = false) || + recur(tp1.superType, tp2) || + tryLiftedToThis1 + }|| byGadtBounds case tycon1: TypeProxy => recur(tp1.superType, tp2) case _ =>