diff --git a/compiler/src/dotty/tools/dotc/core/TypeApplications.scala b/compiler/src/dotty/tools/dotc/core/TypeApplications.scala index 277cef09c36d..173945866594 100644 --- a/compiler/src/dotty/tools/dotc/core/TypeApplications.scala +++ b/compiler/src/dotty/tools/dotc/core/TypeApplications.scala @@ -34,10 +34,6 @@ object TypeApplications { */ object EtaExpansion: - def apply(tycon: Type)(using Context): Type = - assert(tycon.typeParams.nonEmpty, tycon) - tycon.etaExpand(tycon.typeParamSymbols) - /** Test that the parameter bounds in a hk type lambda `[X1,...,Xn] => C[X1, ..., Xn]` * contain the bounds of the type parameters of `C`. This is necessary to be able to * contract the hk lambda to `C`. @@ -245,7 +241,7 @@ class TypeApplications(val self: Type) extends AnyVal { def topType(using Context): Type = if self.hasSimpleKind then defn.AnyType - else etaExpand(self.typeParams) match + else self.etaExpand match case tp: HKTypeLambda => tp.derivedLambdaType(resType = tp.resultType.topType) case _ => @@ -302,45 +298,49 @@ class TypeApplications(val self: Type) extends AnyVal { /** Convert a type constructor `TC` which has type parameters `X1, ..., Xn` * to `[X1, ..., Xn] -> TC[X1, ..., Xn]`. */ - def etaExpand(tparams: List[TypeParamInfo])(using Context): Type = - HKTypeLambda.fromParams(tparams, self.appliedTo(tparams.map(_.paramRef))) - //.ensuring(res => res.EtaReduce =:= self, s"res = $res, core = ${res.EtaReduce}, self = $self, hc = ${res.hashCode}") + def etaExpand(using Context): Type = + val tparams = self.typeParams + val resType = self.appliedTo(tparams.map(_.paramRef)) + self match + case self: TypeRef if tparams.nonEmpty && self.symbol.isClass => + val prefix = self.prefix + 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(prefix, owner)))), + tl => tl.integrate(tparams, resType)) + case _ => + HKTypeLambda.fromParams(tparams, resType) /** If self is not lambda-bound, eta expand it. */ def ensureLambdaSub(using Context): Type = - if (isLambdaSub) self else EtaExpansion(self) + if isLambdaSub then self + else + assert(self.typeParams.nonEmpty, self) + self.etaExpand /** 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 if (hkParams.isEmpty) self else self match { - case self: TypeRef if self.symbol.isClass && self.typeParams.length == hkParams.length => - EtaExpansion(self) + case self: TypeRef if self.symbol.isClass && self.typeParams.hasSameLengthAs(hkParams) => + etaExpand case _ => self } } - // Like `target.etaExpand(target.typeParams)` - // except call `asSeenFrom` to fix class type parameter bounds - // e.g. from pos/i18569: - // trait M1: - // trait A - // trait F[T <: A] - // object M2 extends M1 - // Type parameter T in M2.F has an upper bound of M1#A instead of M2.A - // So we take the prefix M2.type and the F symbol's owner, M1, - // to call asSeenFrom on T's info. - def etaExpandWithAsf(using Context): Type = self match - case self: TypeRef if self.symbol.isClass => - val tparams = self.symbol.typeParams - val prefix = self.prefix - val owner = self.symbol.owner - HKTypeLambda(tparams.map(_.paramName))( - tl => tparams.map(p => HKTypeLambda.toPInfo(tl.integrate(tparams, p.info.asSeenFrom(prefix, owner)))), - tl => tl.integrate(tparams, self.appliedTo(tparams.map(_.paramRef)))) - case _ => etaExpand(typeParams) - /** Maps [Ts] => C[Ts] to C */ def etaCollapse(using Context): Type = self match case EtaExpansion(classType) => classType diff --git a/compiler/src/dotty/tools/dotc/core/TypeComparer.scala b/compiler/src/dotty/tools/dotc/core/TypeComparer.scala index db9cb60ea2fb..13c853d17924 100644 --- a/compiler/src/dotty/tools/dotc/core/TypeComparer.scala +++ b/compiler/src/dotty/tools/dotc/core/TypeComparer.scala @@ -606,7 +606,7 @@ class TypeComparer(@constructorOnly initctx: Context) extends ConstraintHandling if (base.typeSymbol == cls2) return true } else if tp1.typeParams.nonEmpty && !tp1.isAnyKind then - return recur(tp1, EtaExpansion(tp2)) + return recur(tp1, tp2.etaExpand) fourthTry } @@ -746,7 +746,7 @@ class TypeComparer(@constructorOnly initctx: Context) extends ConstraintHandling case _ => val tparams1 = tp1.typeParams if (tparams1.nonEmpty) - return recur(tp1.etaExpand(tparams1), tp2) || fourthTry + return recur(tp1.etaExpand, tp2) || fourthTry tp2 match { case EtaExpansion(tycon2: TypeRef) if tycon2.symbol.isClass && tycon2.symbol.is(JavaDefined) => recur(tp1, tycon2) || fourthTry diff --git a/compiler/src/dotty/tools/dotc/core/unpickleScala2/Scala2Unpickler.scala b/compiler/src/dotty/tools/dotc/core/unpickleScala2/Scala2Unpickler.scala index fe68d22f779a..0564ca435ba7 100644 --- a/compiler/src/dotty/tools/dotc/core/unpickleScala2/Scala2Unpickler.scala +++ b/compiler/src/dotty/tools/dotc/core/unpickleScala2/Scala2Unpickler.scala @@ -832,7 +832,7 @@ class Scala2Unpickler(bytes: Array[Byte], classRoot: ClassDenotation, moduleClas } else if args.nonEmpty then tycon.safeAppliedTo(EtaExpandIfHK(sym.typeParams, args.map(translateTempPoly))) - else if (sym.typeParams.nonEmpty) tycon.etaExpand(sym.typeParams) + else if (sym.typeParams.nonEmpty) tycon.etaExpand else tycon case TYPEBOUNDStpe => val lo = readTypeRef() diff --git a/compiler/src/dotty/tools/dotc/typer/Deriving.scala b/compiler/src/dotty/tools/dotc/typer/Deriving.scala index 6def1ecc30a8..f3be1dcff766 100644 --- a/compiler/src/dotty/tools/dotc/typer/Deriving.scala +++ b/compiler/src/dotty/tools/dotc/typer/Deriving.scala @@ -165,7 +165,7 @@ trait Deriving { // case (a) ... see description above val derivedParams = clsParams.dropRight(instanceArity) val instanceType = - if (instanceArity == clsArity) clsType.etaExpand(clsParams) + if (instanceArity == clsArity) clsType.etaExpand else { val derivedParamTypes = derivedParams.map(_.typeRef) diff --git a/compiler/src/dotty/tools/dotc/typer/Namer.scala b/compiler/src/dotty/tools/dotc/typer/Namer.scala index 44f45f4ce1ea..1bc704939eba 100644 --- a/compiler/src/dotty/tools/dotc/typer/Namer.scala +++ b/compiler/src/dotty/tools/dotc/typer/Namer.scala @@ -1197,7 +1197,7 @@ class Namer { typer: Typer => val forwarderName = checkNoConflict(alias.toTypeName, isPrivate = false, span) var target = pathType.select(sym) if target.typeParams.nonEmpty then - target = target.etaExpandWithAsf + target = target.etaExpand newSymbol( cls, forwarderName, Exported | Final, diff --git a/compiler/src/dotty/tools/dotc/typer/RefChecks.scala b/compiler/src/dotty/tools/dotc/typer/RefChecks.scala index 3f49e78c17c1..aa0832fed697 100644 --- a/compiler/src/dotty/tools/dotc/typer/RefChecks.scala +++ b/compiler/src/dotty/tools/dotc/typer/RefChecks.scala @@ -372,7 +372,7 @@ object RefChecks { */ def checkOverride(checkSubType: (Type, Type) => Context ?=> Boolean, member: Symbol, other: Symbol): Unit = def memberTp(self: Type) = - if (member.isClass) TypeAlias(member.typeRef.etaExpand(member.typeParams)) + if (member.isClass) TypeAlias(member.typeRef.etaExpand) else self.memberInfo(member) def otherTp(self: Type) = self.memberInfo(other) diff --git a/compiler/src/dotty/tools/dotc/typer/Typer.scala b/compiler/src/dotty/tools/dotc/typer/Typer.scala index 5259be9f3336..80987b0a6756 100644 --- a/compiler/src/dotty/tools/dotc/typer/Typer.scala +++ b/compiler/src/dotty/tools/dotc/typer/Typer.scala @@ -4306,7 +4306,7 @@ class Typer(@constructorOnly nestingLevel: Int = 0) extends Namer AppliedType(tree.tpe, tp.typeParams.map(Function.const(TypeBounds.empty))) else // Eta-expand higher-kinded type - tree.tpe.etaExpand(tp.typeParamSymbols) + tree.tpe.etaExpand tree.withType(tp1) } if (ctx.mode.is(Mode.Pattern) || ctx.mode.isQuotedPattern || tree1.tpe <:< pt) tree1