From e38bdbc0ae1e4806bbc745a73f4d0bb445ba7c49 Mon Sep 17 00:00:00 2001 From: Georgi Krastev Date: Sat, 8 Jan 2022 14:20:05 +0200 Subject: [PATCH] Dealias less in OrderingConstraint.replace and AvoidMap Basically if the transformation doesn't apply to the dealias type, then return the original type instead of the dealiased one. In general it's reduce dealiasing to a minimum when typing, because it can affect type unification as shown in the tests. Unfortunately this doesn't handle the case of a singleton type alias, because `widenSingletons` also dealiases and the same fix doesn't work. --- .../dotty/tools/dotc/core/OrderingConstraint.scala | 2 +- compiler/src/dotty/tools/dotc/core/TypeOps.scala | 4 ++-- compiler/src/dotty/tools/dotc/core/Types.scala | 13 ++++++++++--- tests/neg/i14171.scala | 13 +++++++++++++ tests/pos/i14171.scala | 12 ++++++++++++ 5 files changed, 38 insertions(+), 6 deletions(-) create mode 100644 tests/neg/i14171.scala create mode 100644 tests/pos/i14171.scala diff --git a/compiler/src/dotty/tools/dotc/core/OrderingConstraint.scala b/compiler/src/dotty/tools/dotc/core/OrderingConstraint.scala index 1341fac7d735..695575e68def 100644 --- a/compiler/src/dotty/tools/dotc/core/OrderingConstraint.scala +++ b/compiler/src/dotty/tools/dotc/core/OrderingConstraint.scala @@ -454,7 +454,7 @@ class OrderingConstraint(private val boundsMap: ParamBounds, * of the parameter elsewhere in the constraint by type `tp`. */ def replace(param: TypeParamRef, tp: Type)(using Context): OrderingConstraint = - val replacement = tp.dealiasKeepAnnots.stripTypeVar + val replacement = tp.withDealiased(_.stripTypeVar) if param == replacement then this.checkNonCyclic() else assert(replacement.isValueTypeOrLambda) diff --git a/compiler/src/dotty/tools/dotc/core/TypeOps.scala b/compiler/src/dotty/tools/dotc/core/TypeOps.scala index 332129e72850..4d0e37e88a4a 100644 --- a/compiler/src/dotty/tools/dotc/core/TypeOps.scala +++ b/compiler/src/dotty/tools/dotc/core/TypeOps.scala @@ -451,8 +451,8 @@ object TypeOps: try tp match case tp: TermRef if toAvoid(tp) => - tp.info.widenExpr.dealias match { - case info: SingletonType => apply(info) + tp.info.widenExpr.withDealiased { + case singleton: SingletonType => apply(singleton) case info => range(defn.NothingType, apply(info)) } case tp: TypeRef if toAvoid(tp) => diff --git a/compiler/src/dotty/tools/dotc/core/Types.scala b/compiler/src/dotty/tools/dotc/core/Types.scala index 483745928f25..1cfd144358c5 100644 --- a/compiler/src/dotty/tools/dotc/core/Types.scala +++ b/compiler/src/dotty/tools/dotc/core/Types.scala @@ -1430,6 +1430,14 @@ object Types { */ final def dealiasKeepOpaques(using Context): Type = dealias1(keepNever, keepOpaques = true) + /** Apply `f` to this type dealiased. If the type changes, keep the change, + * otherwise return the original (not dealiased) type. + */ + final def withDealiased(f: Type => Type)(using Context): Type = + val tpd = dealias + val result = f(tpd) + if result eq tpd then this else result + /** Approximate this type with a type that does not contain skolem types. */ final def deskolemized(using Context): Type = val deskolemizer = new ApproximatingTypeMap { @@ -1478,10 +1486,9 @@ object Types { } /** Dealias, and if result is a dependent function type, drop the `apply` refinement. */ - final def dropDependentRefinement(using Context): Type = dealias match { + final def dropDependentRefinement(using Context): Type = dealias match case RefinedType(parent, nme.apply, _) => parent - case tp => tp - } + case _ => this /** The type constructor of an applied type, otherwise the type itself */ final def typeConstructor(using Context): Type = this match { diff --git a/tests/neg/i14171.scala b/tests/neg/i14171.scala new file mode 100644 index 000000000000..005746c0aaf3 --- /dev/null +++ b/tests/neg/i14171.scala @@ -0,0 +1,13 @@ +object Test: + trait MyTypeclass[F[_]] + def f[F[_]: MyTypeclass, U](t: F[U]) = () + + // using a non-singleton type makes it compile because `widenSingletons` dealiases + type MyType[T] = Nil.type + given MyTypeclass[MyType] = null + + val stream: Option[MyType[Int]] = null + for + keyStream <- stream + x = 17 // commenting this line makes it compile + yield f(keyStream) // error \ No newline at end of file diff --git a/tests/pos/i14171.scala b/tests/pos/i14171.scala new file mode 100644 index 000000000000..d2ab70179d02 --- /dev/null +++ b/tests/pos/i14171.scala @@ -0,0 +1,12 @@ +object Test: + trait MyTypeclass[F[_]] + def f[F[_]: MyTypeclass, U](t: F[U]) = () + + type MyType[T] = String + given MyTypeclass[MyType] = null + + val stream: Option[MyType[Int]] = null + for + keyStream <- stream + x = 17 + yield f(keyStream) \ No newline at end of file