diff --git a/compiler/src/dotty/tools/dotc/core/Types.scala b/compiler/src/dotty/tools/dotc/core/Types.scala index ba48b6a0f2e6..7fb4b15f801e 100644 --- a/compiler/src/dotty/tools/dotc/core/Types.scala +++ b/compiler/src/dotty/tools/dotc/core/Types.scala @@ -6180,6 +6180,10 @@ object Types extends TypeUtils { variance = saved derivedLambdaType(tp)(ptypes1, this(restpe)) + protected def mapOverTypeVar(tp: TypeVar) = + val inst = tp.instanceOpt + if (inst.exists) apply(inst) else tp + def isRange(tp: Type): Boolean = tp.isInstanceOf[Range] protected def mapCapturingType(tp: Type, parent: Type, refs: CaptureSet, v: Int): Type = @@ -6217,8 +6221,7 @@ object Types extends TypeUtils { derivedTypeBounds(tp, lo1, this(tp.hi)) case tp: TypeVar => - val inst = tp.instanceOpt - if (inst.exists) apply(inst) else tp + mapOverTypeVar(tp) case tp: ExprType => derivedExprType(tp, this(tp.resultType)) @@ -6632,6 +6635,16 @@ object Types extends TypeUtils { tp.derivedLambdaType(tp.paramNames, formals, restpe) } + override protected def mapOverTypeVar(tp: TypeVar) = + val inst = tp.instanceOpt + if !inst.exists then tp + else + // We can keep the original type var if its instance is not transformed + // by the ApproximatingTypeMap. This allows for simpler bounds and for + // derivedSkolemType to retain more skolems, by keeping the info unchanged. + val res = apply(inst) + if res eq inst then tp else res + protected def reapply(tp: Type): Type = apply(tp) } diff --git a/compiler/test/dotc/pos-test-pickling.blacklist b/compiler/test/dotc/pos-test-pickling.blacklist index 3ea8b550f160..638455e7f2de 100644 --- a/compiler/test/dotc/pos-test-pickling.blacklist +++ b/compiler/test/dotc/pos-test-pickling.blacklist @@ -119,4 +119,7 @@ i7445b.scala # more aggresive reduce projection makes a difference i15525.scala +i19955a.scala +i19955b.scala +i20053b.scala diff --git a/tests/pos/i19955a.scala b/tests/pos/i19955a.scala new file mode 100644 index 000000000000..b8ea95d41d24 --- /dev/null +++ b/tests/pos/i19955a.scala @@ -0,0 +1,27 @@ + +trait Summon[R, T <: R]: + type Out +object Summon: + given [R, T <: R]: Summon[R, T] with + type Out = R + +trait DFTypeAny +trait DFBits[W <: Int] extends DFTypeAny +class DFVal[+T <: DFTypeAny] +type DFValAny = DFVal[DFTypeAny] +type DFValOf[+T <: DFTypeAny] = DFVal[T] +trait Candidate[R]: + type OutW <: Int +object Candidate: + type Aux[R, O <: Int] = Candidate[R] { type OutW = O } + given [W <: Int, R <: DFValOf[DFBits[W]]]: Candidate[R] with + type OutW = W + +extension [L](lhs: L) def foo(using es: Summon[L, lhs.type]): Unit = ??? +extension [L <: DFValAny](lhs: L)(using icL: Candidate[L]) def baz: DFValOf[DFBits[icL.OutW]] = ??? +extension [L <: DFValAny, W <: Int](lhs: L)(using icL: Candidate.Aux[L, W]) + def bazAux: DFValOf[DFBits[W]] = ??? + +val x = new DFVal[DFBits[4]] +val works = x.bazAux.foo +val fails = x.baz.foo \ No newline at end of file diff --git a/tests/pos/i19955b.scala b/tests/pos/i19955b.scala new file mode 100644 index 000000000000..99e101b312b1 --- /dev/null +++ b/tests/pos/i19955b.scala @@ -0,0 +1,17 @@ + +trait Wrap[W] + +trait IsWrapOfInt[R]: + type Out <: Int +given [W <: Int, R <: Wrap[W]]: IsWrapOfInt[R] with + type Out = Int + +trait IsInt[U <: Int] +given [U <: Int]: IsInt[U] = ??? + +extension [L](lhs: L) def get(using ev: IsWrapOfInt[L]): ev.Out = ??? +extension (lhs: Int) def isInt(using IsInt[lhs.type]): Unit = ??? + +val x: Wrap[Int] = ??? +val works = (x.get: Int).isInt +val fails = x.get.isInt diff --git a/tests/pos/i20053b.scala b/tests/pos/i20053b.scala new file mode 100644 index 000000000000..25180d56bbae --- /dev/null +++ b/tests/pos/i20053b.scala @@ -0,0 +1,22 @@ + +trait Sub[R, T >: R] +given [R, T >: R]: Sub[R, T] with {} + +trait Candidate[-R]: + type OutP +given [P]: Candidate[Option[P]] with + type OutP = P + +extension [L](lhs: L) + def ^^^[P](rhs: Option[P]) + (using es: Sub[lhs.type, Any]) + (using c: Candidate[L]) + (using check: c.type <:< Any): Option[c.OutP] = ??? + +val x: Option[Boolean] = ??? + +val z1 = x ^^^ x // Ok +val z2 = z1 ^^^ x // Ok +val zz = ^^^[Option[Boolean]](x ^^^ x)(x) // Ok + +val zzz = x ^^^ x ^^^ x // Error before changes