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