diff --git a/compiler/src/dotty/tools/dotc/core/TypeApplications.scala b/compiler/src/dotty/tools/dotc/core/TypeApplications.scala index 1cd1a3ad4d39..1dc81946d723 100644 --- a/compiler/src/dotty/tools/dotc/core/TypeApplications.scala +++ b/compiler/src/dotty/tools/dotc/core/TypeApplications.scala @@ -36,7 +36,7 @@ object TypeApplications { def apply(tycon: Type)(using Context): Type = assert(tycon.typeParams.nonEmpty, tycon) - tycon.EtaExpand(tycon.typeParamSymbols) + 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 @@ -71,7 +71,7 @@ object TypeApplications { */ def EtaExpandIfHK(tparams: List[TypeParamInfo], args: List[Type])(using Context): List[Type] = if (tparams.isEmpty) args - else args.zipWithConserve(tparams)((arg, tparam) => arg.EtaExpandIfHK(tparam.paramInfoOrCompleter)) + else args.zipWithConserve(tparams)((arg, tparam) => arg.etaExpandIfHK(tparam.paramInfoOrCompleter)) /** A type map that tries to reduce (part of) the result type of the type lambda `tycon` * with the given `args`(some of which are wildcard arguments represented by type bounds). @@ -245,7 +245,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 etaExpand(self.typeParams) match case tp: HKTypeLambda => tp.derivedLambdaType(resType = tp.resultType.topType) case _ => @@ -302,7 +302,7 @@ 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 = + 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}") @@ -311,7 +311,7 @@ class TypeApplications(val self: Type) extends AnyVal { if (isLambdaSub) self else EtaExpansion(self) /** Eta expand if `self` is a (non-lambda) class reference and `bound` is a higher-kinded type */ - def EtaExpandIfHK(bound: Type)(using Context): Type = { + def etaExpandIfHK(bound: Type)(using Context): Type = { val hkParams = bound.hkTypeParams if (hkParams.isEmpty) self else self match { @@ -321,6 +321,11 @@ class TypeApplications(val self: Type) extends AnyVal { } } + /** Maps [Ts] => C[Ts] to C */ + def etaCollapse(using Context): Type = self match + case EtaExpansion(classType) => classType + case _ => self + /** The type representing * * T[U1, ..., Un] diff --git a/compiler/src/dotty/tools/dotc/core/TypeComparer.scala b/compiler/src/dotty/tools/dotc/core/TypeComparer.scala index d5b97dca6164..9a3b6e3215c6 100644 --- a/compiler/src/dotty/tools/dotc/core/TypeComparer.scala +++ b/compiler/src/dotty/tools/dotc/core/TypeComparer.scala @@ -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(tparams1), 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 3e211e75b73b..f982cb5ee9bc 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(sym.typeParams) 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 8fdc468780ba..abfab1e3b981 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(clsParams) 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 5361f37c2a76..7bbf59e0fd6a 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.EtaExpand(target.typeParams) + target = target.etaExpand(target.typeParams) newSymbol( cls, forwarderName, Exported | Final, @@ -1518,7 +1518,7 @@ class Namer { typer: Typer => def typedParentType(tree: untpd.Tree): tpd.Tree = val parentTpt = typer.typedType(parent, AnyTypeConstructorProto) - val ptpe = parentTpt.tpe + val ptpe = parentTpt.tpe.dealias.etaCollapse if ptpe.typeParams.nonEmpty && ptpe.underlyingClassRef(refinementOK = false).exists then diff --git a/compiler/src/dotty/tools/dotc/typer/RefChecks.scala b/compiler/src/dotty/tools/dotc/typer/RefChecks.scala index af279844f370..06fb1fbc3b90 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(member.typeParams)) 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 66c79658b6ab..11674e6b8522 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(tp.typeParamSymbols) tree.withType(tp1) } if (ctx.mode.is(Mode.Pattern) || ctx.mode.isQuotedPattern || tree1.tpe <:< pt) tree1 diff --git a/tests/neg/i4557.scala b/tests/neg/i4557.scala index ffdf3b5be97e..74f9099c6d08 100644 --- a/tests/neg/i4557.scala +++ b/tests/neg/i4557.scala @@ -9,11 +9,11 @@ object O { type S0[X, Y] = C1[X, Y] type S1 = C1[Int] // error - class D0 extends T0 // error + class D0 extends T0 // was error, now ok class D1 extends T0[Int] class D2 extends T0[String, Int] // error - class E0 extends S0 // error + class E0 extends S0 // was error, now ok class E1 extends S0[Int] // error class E2 extends S0[String, Int] } diff --git a/tests/pos/i18623.scala b/tests/pos/i18623.scala new file mode 100644 index 000000000000..e34575c6e697 --- /dev/null +++ b/tests/pos/i18623.scala @@ -0,0 +1,15 @@ +final abstract class ForcedRecompilationToken[T] +object ForcedRecompilationToken { + implicit def default: ForcedRecompilationToken["abc"] = null +} + +class GoodNoParens[T](implicit ev: ForcedRecompilationToken[T]) +type BadNoParens[T] = GoodNoParens[T] + +// error +object A extends BadNoParens + +// ok +object B extends BadNoParens() +object C extends GoodNoParens + diff --git a/tests/pos/i18623a.scala b/tests/pos/i18623a.scala new file mode 100644 index 000000000000..043bac046896 --- /dev/null +++ b/tests/pos/i18623a.scala @@ -0,0 +1,20 @@ +final abstract class ForcedRecompilationToken[T] +object ForcedRecompilationToken { + implicit def default: ForcedRecompilationToken["abc"] = null +} + +object x { +class GoodNoParens[T](implicit ev: ForcedRecompilationToken[T]) +} +export x.GoodNoParens as BadNoParens + +// error +object A extends BadNoParens + +// ok +object B extends BadNoParens() +object C extends x.GoodNoParens + +object App extends App { + println("compiled") +} \ No newline at end of file