diff --git a/compiler/src/dotty/tools/dotc/core/TypeApplications.scala b/compiler/src/dotty/tools/dotc/core/TypeApplications.scala index ab216bfd147d..64b9a1f082ba 100644 --- a/compiler/src/dotty/tools/dotc/core/TypeApplications.scala +++ b/compiler/src/dotty/tools/dotc/core/TypeApplications.scala @@ -310,6 +310,34 @@ class TypeApplications(val self: Type) extends AnyVal { def ensureLambdaSub(using Context): Type = if (isLambdaSub) self else EtaExpansion(self) + + /** Convert a type constructor `TC` which has type parameters `X1, ..., Xn` + * to `[X1, ..., Xn] -> TC[X1, ..., Xn]`. + */ + def etaExpand(using Context): Type = + val tparams = self.typeParams + val resType = self.appliedTo(tparams.map(_.paramRef)) + self.dealias match + case self: TypeRef if tparams.nonEmpty && self.symbol.isClass => + val owner = self.symbol.owner + // Calling asSeenFrom on the type parameter infos is important + // so that class type references within another prefix have + // their type parameters' info fixed. + // e.g. from pos/i18569: + // trait M1: + // trait A + // trait F[T <: A] + // object M2 extends M1 + // Type parameter T in M1.F has an upper bound of M1#A + // But eta-expanding M2.F should have type parameters with an upper-bound of M2.A. + // So we take the prefix M2.type and the F symbol's owner, M1, + // to call asSeenFrom on T's info. + HKTypeLambda(tparams.map(_.paramName))( + tl => tparams.map(p => HKTypeLambda.toPInfo(tl.integrate(tparams, p.paramInfo.asSeenFrom(self.prefix, owner)))), + tl => tl.integrate(tparams, resType)) + case _ => + HKTypeLambda.fromParams(tparams, resType) + /** Eta expand if `self` is a (non-lambda) class reference and `bound` is a higher-kinded type */ def EtaExpandIfHK(bound: Type)(using Context): Type = { val hkParams = bound.hkTypeParams diff --git a/compiler/src/dotty/tools/dotc/core/Types.scala b/compiler/src/dotty/tools/dotc/core/Types.scala index f2e000c2db73..f0ca272cce36 100644 --- a/compiler/src/dotty/tools/dotc/core/Types.scala +++ b/compiler/src/dotty/tools/dotc/core/Types.scala @@ -6073,7 +6073,7 @@ object Types extends TypeUtils { protected def range(lo: Type, hi: Type): Type = if variance > 0 then hi else if variance < 0 then - if (lo eq defn.NothingType) && hi.hasSimpleKind then + if (lo eq defn.NothingType) then // Approximate by Nothing & hi instead of just Nothing, in case the // approximated type is used as the prefix of another type (this would // lead to a type with a `NoDenotation` denot and a possible @@ -6084,8 +6084,14 @@ object Types extends TypeUtils { // example if Nothing is the type of a parameter being depended on in // a MethodType) // - // Test case in tests/pos/i23530.scala - AndType(lo, hi) + // Test case in tests/pos/i23530.scala (and tests/pos/i23627.scala for + // the higher-kinded case which requires eta-expansion) + hi.etaExpand match + case expandedHi: HKTypeLambda => + expandedHi.derivedLambdaType(resType = AndType(lo, expandedHi.resType)) + case _ => + // simple-kinded case + AndType(lo, hi) else lo else if lo `eq` hi then lo diff --git a/tests/pos/i23627.scala b/tests/pos/i23627.scala new file mode 100644 index 000000000000..1480a80c9c00 --- /dev/null +++ b/tests/pos/i23627.scala @@ -0,0 +1,18 @@ +trait TestContainer: + trait TestPath[T]: + type AbsMember + + extension (path: TestPath[?]) + infix def ext(color: path.AbsMember): Unit = ??? + infix def ext(other: Int): Unit = ??? + +object Repro: + val dc2: TestContainer = ??? + import dc2.TestPath + + def transition(path: TestPath[?])(using DummyImplicit): TestPath[?] = ??? + + def test: Unit = + val di: TestPath[?] = ??? + // error + val z1 = transition(di).ext(1)