From eabdf44170438cbc93817d451811282b1d98ead4 Mon Sep 17 00:00:00 2001 From: Dale Wijnand Date: Wed, 4 Oct 2023 13:53:17 +0100 Subject: [PATCH 1/6] Fix the tparam bounds of exported inherited classes When trying to export M2.F, seeing M1#F from the prefix M2 doesn't change the bounds of F's T type parameter, which still refers to M1.this.A, rather than M2.A. So, we run asSeenFrom against that info, when eta-expanding the class into a hk type lambda. --- .../src/dotty/tools/dotc/typer/Namer.scala | 18 ++++++++++++++++-- tests/pos/i18569.scala | 11 +++++++++++ 2 files changed, 27 insertions(+), 2 deletions(-) create mode 100644 tests/pos/i18569.scala diff --git a/compiler/src/dotty/tools/dotc/typer/Namer.scala b/compiler/src/dotty/tools/dotc/typer/Namer.scala index a48026063415..fe419802c91d 100644 --- a/compiler/src/dotty/tools/dotc/typer/Namer.scala +++ b/compiler/src/dotty/tools/dotc/typer/Namer.scala @@ -1199,8 +1199,22 @@ class Namer { typer: Typer => if mbr.isType then val forwarderName = checkNoConflict(alias.toTypeName, isPrivate = false, span) var target = pathType.select(sym) - if target.typeParams.nonEmpty then - target = target.etaExpand(target.typeParams) + val tparams = target.typeParams + if tparams.nonEmpty then + // like `target = target.etaExpand(target.typeParams)` + // except call `asSeenFrom` to fix class type parameter bounds + // e.g. in pos/i18569: + // `Test#F` should have `M2.A` or `Test.A` as bounds, not `M1#A`. + target = HKTypeLambda(tparams.map(_.paramName))( + tl => tparams.map { + case p: Symbol => + val info = p.info.asSeenFrom(pathType, sym.owner) + HKTypeLambda.toPInfo(tl.integrate(tparams, info)) + case p => + val info = p.paramInfo + HKTypeLambda.toPInfo(tl.integrate(tparams, info)) + }, + tl => tl.integrate(tparams, target.appliedTo(tparams.map(_.paramRef)))) newSymbol( cls, forwarderName, MandatoryExportTypeFlags | (sym.flags & RetainedExportTypeFlags), diff --git a/tests/pos/i18569.scala b/tests/pos/i18569.scala new file mode 100644 index 000000000000..1457f6fbe5b6 --- /dev/null +++ b/tests/pos/i18569.scala @@ -0,0 +1,11 @@ +trait M1: + trait A + trait F[T <: A] + type G[T <: A] = F[T] + +object M2 extends M1 + +trait Test: + export M2.* + def y: F[A] + def z: G[A] From 66722b9941356e4c87e3b489507ee1b8771ad9d4 Mon Sep 17 00:00:00 2001 From: Dale Wijnand Date: Mon, 13 Nov 2023 11:42:02 +0000 Subject: [PATCH 2/6] Move implementation into TypeApplications etaExpandWithAsf --- .../tools/dotc/core/TypeApplications.scala | 16 ++++++++++++++++ .../src/dotty/tools/dotc/typer/Namer.scala | 18 ++---------------- 2 files changed, 18 insertions(+), 16 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/core/TypeApplications.scala b/compiler/src/dotty/tools/dotc/core/TypeApplications.scala index f1edd7cd8f8b..f5508eb2c060 100644 --- a/compiler/src/dotty/tools/dotc/core/TypeApplications.scala +++ b/compiler/src/dotty/tools/dotc/core/TypeApplications.scala @@ -320,6 +320,22 @@ class TypeApplications(val self: Type) extends AnyVal { } } + // Like `target.etaExpand(target.typeParams)` + // except call `asSeenFrom` to fix class type parameter bounds + // e.g. in pos/i18569: + // given prefix `M2.type` and symbol `trait F` + // the result of `prefix.select(sym)` is `M2.F` + // however F's type parameter T is `<: M1#A` rather than `<: M2.A` + // so add a call to `asSeenFrom` with prefix `M2.type` and owner `trait M1` + def etaExpandWithAsf(prefix: Type, owner: Symbol)(using Context): Type = + val tparams = self.typeParams + HKTypeLambda(tparams.map(_.paramName))( + tl => tparams.map { + case p: Symbol => HKTypeLambda.toPInfo(tl.integrate(tparams, p.info.asSeenFrom(prefix, owner))) + case p => HKTypeLambda.toPInfo(tl.integrate(tparams, p.paramInfo)) + }, + tl => tl.integrate(tparams, self.appliedTo(tparams.map(_.paramRef)))) + /** 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/typer/Namer.scala b/compiler/src/dotty/tools/dotc/typer/Namer.scala index fe419802c91d..c531d8da01a2 100644 --- a/compiler/src/dotty/tools/dotc/typer/Namer.scala +++ b/compiler/src/dotty/tools/dotc/typer/Namer.scala @@ -1199,22 +1199,8 @@ class Namer { typer: Typer => if mbr.isType then val forwarderName = checkNoConflict(alias.toTypeName, isPrivate = false, span) var target = pathType.select(sym) - val tparams = target.typeParams - if tparams.nonEmpty then - // like `target = target.etaExpand(target.typeParams)` - // except call `asSeenFrom` to fix class type parameter bounds - // e.g. in pos/i18569: - // `Test#F` should have `M2.A` or `Test.A` as bounds, not `M1#A`. - target = HKTypeLambda(tparams.map(_.paramName))( - tl => tparams.map { - case p: Symbol => - val info = p.info.asSeenFrom(pathType, sym.owner) - HKTypeLambda.toPInfo(tl.integrate(tparams, info)) - case p => - val info = p.paramInfo - HKTypeLambda.toPInfo(tl.integrate(tparams, info)) - }, - tl => tl.integrate(tparams, target.appliedTo(tparams.map(_.paramRef)))) + if target.typeParams.nonEmpty then + target = target.etaExpandWithAsf(pathType, sym.owner) newSymbol( cls, forwarderName, MandatoryExportTypeFlags | (sym.flags & RetainedExportTypeFlags), From f1fc2eb0610038dc89982ac1f129af7b8e773182 Mon Sep 17 00:00:00 2001 From: Dale Wijnand Date: Mon, 13 Nov 2023 14:27:15 +0000 Subject: [PATCH 3/6] Make etaExpandWithAsf more self-contained --- .../tools/dotc/core/TypeApplications.scala | 30 +++++++++++-------- .../src/dotty/tools/dotc/typer/Namer.scala | 2 +- 2 files changed, 18 insertions(+), 14 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/core/TypeApplications.scala b/compiler/src/dotty/tools/dotc/core/TypeApplications.scala index f5508eb2c060..c7312c75b081 100644 --- a/compiler/src/dotty/tools/dotc/core/TypeApplications.scala +++ b/compiler/src/dotty/tools/dotc/core/TypeApplications.scala @@ -322,19 +322,23 @@ class TypeApplications(val self: Type) extends AnyVal { // Like `target.etaExpand(target.typeParams)` // except call `asSeenFrom` to fix class type parameter bounds - // e.g. in pos/i18569: - // given prefix `M2.type` and symbol `trait F` - // the result of `prefix.select(sym)` is `M2.F` - // however F's type parameter T is `<: M1#A` rather than `<: M2.A` - // so add a call to `asSeenFrom` with prefix `M2.type` and owner `trait M1` - def etaExpandWithAsf(prefix: Type, owner: Symbol)(using Context): Type = - val tparams = self.typeParams - HKTypeLambda(tparams.map(_.paramName))( - tl => tparams.map { - case p: Symbol => HKTypeLambda.toPInfo(tl.integrate(tparams, p.info.asSeenFrom(prefix, owner))) - case p => HKTypeLambda.toPInfo(tl.integrate(tparams, p.paramInfo)) - }, - tl => tl.integrate(tparams, self.appliedTo(tparams.map(_.paramRef)))) + // 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 diff --git a/compiler/src/dotty/tools/dotc/typer/Namer.scala b/compiler/src/dotty/tools/dotc/typer/Namer.scala index c531d8da01a2..6621d459918b 100644 --- a/compiler/src/dotty/tools/dotc/typer/Namer.scala +++ b/compiler/src/dotty/tools/dotc/typer/Namer.scala @@ -1200,7 +1200,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(pathType, sym.owner) + target = target.etaExpandWithAsf newSymbol( cls, forwarderName, MandatoryExportTypeFlags | (sym.flags & RetainedExportTypeFlags), From 729e5ba099688a1e92c7d5cdf9611e7d1469379c Mon Sep 17 00:00:00 2001 From: Dale Wijnand Date: Tue, 28 Nov 2023 14:39:14 +0000 Subject: [PATCH 4/6] Collapse into 1 etaExpand (to rule them all) --- .../tools/dotc/core/TypeApplications.scala | 62 +++++++++---------- .../dotty/tools/dotc/core/TypeComparer.scala | 6 +- .../core/unpickleScala2/Scala2Unpickler.scala | 2 +- .../src/dotty/tools/dotc/typer/Deriving.scala | 2 +- .../src/dotty/tools/dotc/typer/Namer.scala | 2 +- .../dotty/tools/dotc/typer/RefChecks.scala | 2 +- .../src/dotty/tools/dotc/typer/Typer.scala | 2 +- 7 files changed, 39 insertions(+), 39 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/core/TypeApplications.scala b/compiler/src/dotty/tools/dotc/core/TypeApplications.scala index c7312c75b081..99735c6de7ad 100644 --- a/compiler/src/dotty/tools/dotc/core/TypeApplications.scala +++ b/compiler/src/dotty/tools/dotc/core/TypeApplications.scala @@ -33,10 +33,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`. @@ -244,7 +240,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 _ => @@ -301,45 +297,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 b23bfe9fe14b..9a6ee7296239 100644 --- a/compiler/src/dotty/tools/dotc/core/TypeComparer.scala +++ b/compiler/src/dotty/tools/dotc/core/TypeComparer.scala @@ -594,7 +594,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 } @@ -734,7 +734,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 @@ -2820,7 +2820,7 @@ class TypeComparer(@constructorOnly initctx: Context) extends ConstraintHandling tp.symbol match case cls: ClassSymbol => if cls == defn.SingletonClass then defn.AnyType - else if cls.typeParams.nonEmpty then EtaExpansion(tp) + else if cls.typeParams.nonEmpty then tp.etaExpand else tp case sym => if !ctx.erasedTypes && sym == defn.FromJavaObjectSymbol then defn.AnyType diff --git a/compiler/src/dotty/tools/dotc/core/unpickleScala2/Scala2Unpickler.scala b/compiler/src/dotty/tools/dotc/core/unpickleScala2/Scala2Unpickler.scala index 806f39ee0425..521306eb73fc 100644 --- a/compiler/src/dotty/tools/dotc/core/unpickleScala2/Scala2Unpickler.scala +++ b/compiler/src/dotty/tools/dotc/core/unpickleScala2/Scala2Unpickler.scala @@ -850,7 +850,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 6621d459918b..868fcb99dc38 100644 --- a/compiler/src/dotty/tools/dotc/typer/Namer.scala +++ b/compiler/src/dotty/tools/dotc/typer/Namer.scala @@ -1200,7 +1200,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, MandatoryExportTypeFlags | (sym.flags & RetainedExportTypeFlags), diff --git a/compiler/src/dotty/tools/dotc/typer/RefChecks.scala b/compiler/src/dotty/tools/dotc/typer/RefChecks.scala index f0914a9f6664..cf30945a7751 100644 --- a/compiler/src/dotty/tools/dotc/typer/RefChecks.scala +++ b/compiler/src/dotty/tools/dotc/typer/RefChecks.scala @@ -377,7 +377,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 165484091260..38e030ede0a9 100644 --- a/compiler/src/dotty/tools/dotc/typer/Typer.scala +++ b/compiler/src/dotty/tools/dotc/typer/Typer.scala @@ -4283,7 +4283,7 @@ class Typer(@constructorOnly nestingLevel: Int = 0) extends Namer tree1.withType(tp1) else // Eta-expand higher-kinded type - val tp1 = tree.tpe.etaExpand(tp.typeParamSymbols) + val tp1 = tree.tpe.etaExpand tree.withType(tp1) } if (ctx.mode.is(Mode.Pattern) || ctx.mode.isQuotedPattern || tree1.tpe <:< pt) tree1 From 4ff8bb1a42b85da3a940554c4fd5e2f1be6f9779 Mon Sep 17 00:00:00 2001 From: Dale Wijnand Date: Thu, 30 Nov 2023 16:32:18 +0000 Subject: [PATCH 5/6] Fix EtaExpansion extractor, to also honour prefix When looking at the info of the type parameters of a TypeRef, make sure to honour the reference's prefix. --- .../dotty/tools/dotc/core/TypeApplications.scala | 14 ++++++++------ tests/pos/i18569.reg1.scala | 11 +++++++++++ 2 files changed, 19 insertions(+), 6 deletions(-) create mode 100644 tests/pos/i18569.reg1.scala diff --git a/compiler/src/dotty/tools/dotc/core/TypeApplications.scala b/compiler/src/dotty/tools/dotc/core/TypeApplications.scala index 99735c6de7ad..dbba562ff223 100644 --- a/compiler/src/dotty/tools/dotc/core/TypeApplications.scala +++ b/compiler/src/dotty/tools/dotc/core/TypeApplications.scala @@ -37,7 +37,7 @@ object TypeApplications { * contain the bounds of the type parameters of `C`. This is necessary to be able to * contract the hk lambda to `C`. */ - private def weakerBounds(tp: HKTypeLambda, tparams: List[ParamInfo])(using Context): Boolean = + private def weakerBounds(tp: HKTypeLambda, fn: Type)(using Context): Boolean = val onlyEmptyBounds = tp.typeParams.forall(_.paramInfo == TypeBounds.empty) onlyEmptyBounds // Note: this pre-test helps efficiency. It is also necessary to workaround #9965 since in some cases @@ -46,18 +46,21 @@ object TypeApplications { // In this case, we can still return true if we know that the hk lambda bounds // are empty anyway. || { + val tparams = fn.typeParams val paramRefs = tparams.map(_.paramRef) + val prefix = fn match { case fn: TypeRef => fn.prefix case _ => NoPrefix } + val owner = fn match { case fn: TypeRef => fn.symbol.owner case _ => NoSymbol } tp.typeParams.corresponds(tparams) { (param1, param2) => - param2.paramInfo frozen_<:< param1.paramInfo.substParams(tp, paramRefs) + param2.paramInfo.asSeenFrom(prefix, owner) frozen_<:< param1.paramInfo.substParams(tp, paramRefs) } } def unapply(tp: Type)(using Context): Option[Type] = tp match - case tp @ HKTypeLambda(tparams, AppliedType(fn: Type, args)) + case tp @ HKTypeLambda(tparams, AppliedType(fn, args)) if fn.typeSymbol.isClass && tparams.hasSameLengthAs(args) && args.lazyZip(tparams).forall((arg, tparam) => arg == tparam.paramRef) - && weakerBounds(tp, fn.typeParams) => Some(fn) + && weakerBounds(tp, fn) => Some(fn) case _ => None end EtaExpansion @@ -302,7 +305,6 @@ class TypeApplications(val self: Type) extends AnyVal { 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 @@ -317,7 +319,7 @@ class TypeApplications(val self: Type) extends AnyVal { // 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 => tparams.map(p => HKTypeLambda.toPInfo(tl.integrate(tparams, p.paramInfo.asSeenFrom(self.prefix, owner)))), tl => tl.integrate(tparams, resType)) case _ => HKTypeLambda.fromParams(tparams, resType) diff --git a/tests/pos/i18569.reg1.scala b/tests/pos/i18569.reg1.scala new file mode 100644 index 000000000000..188ee36a02b2 --- /dev/null +++ b/tests/pos/i18569.reg1.scala @@ -0,0 +1,11 @@ +// Minimisation of the CI failure +// in scala-parallel-collections +// to do with how EtaExpansion is used +// by typeOfNew when typing a New tree + +trait Foo[+A]: + class Bar[B >: A] + +class Test: + def t1[X](foo: Foo[X]): Unit = + val bar = new foo.Bar() From 000de8436787c77fa5bf220dcc025d7e6df525b6 Mon Sep 17 00:00:00 2001 From: Dale Wijnand Date: Mon, 22 Jan 2024 16:52:27 +0000 Subject: [PATCH 6/6] Reuse methods for prefix and owner --- .../tools/dotc/core/TypeApplications.scala | 9 +++-- tests/neg/i7820.check | 34 +++++++++++++++++++ tests/neg/i7820.scala | 4 +-- 3 files changed, 42 insertions(+), 5 deletions(-) create mode 100644 tests/neg/i7820.check diff --git a/compiler/src/dotty/tools/dotc/core/TypeApplications.scala b/compiler/src/dotty/tools/dotc/core/TypeApplications.scala index dbba562ff223..223d3bb11515 100644 --- a/compiler/src/dotty/tools/dotc/core/TypeApplications.scala +++ b/compiler/src/dotty/tools/dotc/core/TypeApplications.scala @@ -48,9 +48,12 @@ object TypeApplications { || { val tparams = fn.typeParams val paramRefs = tparams.map(_.paramRef) - val prefix = fn match { case fn: TypeRef => fn.prefix case _ => NoPrefix } - val owner = fn match { case fn: TypeRef => fn.symbol.owner case _ => NoSymbol } + val prefix = fn.normalizedPrefix + val owner = fn.typeSymbol.maybeOwner tp.typeParams.corresponds(tparams) { (param1, param2) => + // see tests/neg/variances-constr.scala + // its B parameter should have info <: Any, using class C as the owner + // rather than info <: A, using class Inner2 as the owner param2.paramInfo.asSeenFrom(prefix, owner) frozen_<:< param1.paramInfo.substParams(tp, paramRefs) } } @@ -303,7 +306,7 @@ class TypeApplications(val self: Type) extends AnyVal { def etaExpand(using Context): Type = val tparams = self.typeParams val resType = self.appliedTo(tparams.map(_.paramRef)) - self match + 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 diff --git a/tests/neg/i7820.check b/tests/neg/i7820.check new file mode 100644 index 000000000000..14dfc910ae6d --- /dev/null +++ b/tests/neg/i7820.check @@ -0,0 +1,34 @@ +-- [E046] Cyclic Error: tests/neg/i7820.scala:1:23 --------------------------------------------------------------------- +1 |trait A1 { type F[X <: F[_, _], Y] } // error: cyclic reference involving type F + | ^ + | Cyclic reference involving type F + | + | Run with -explain-cyclic for more details. + | + | longer explanation available when compiling with `-explain` +-- [E046] Cyclic Error: tests/neg/i7820.scala:2:23 --------------------------------------------------------------------- +2 |trait A2 { type F[X <: F, Y] } // error: cyclic reference involving type F + | ^ + | Cyclic reference involving type F + | + | Run with -explain-cyclic for more details. + | + | longer explanation available when compiling with `-explain` +-- [E046] Cyclic Error: tests/neg/i7820.scala:3:23 --------------------------------------------------------------------- +3 |trait A3 { type F[X >: F, Y] } // error: cyclic reference involving type F + | ^ + | Cyclic reference involving type F + | + | Run with -explain-cyclic for more details. + | + | longer explanation available when compiling with `-explain` +-- Warning: tests/neg/i7820.scala:1:25 --------------------------------------------------------------------------------- +1 |trait A1 { type F[X <: F[_, _], Y] } // error: cyclic reference involving type F + | ^ + | `_` is deprecated for wildcard arguments of types: use `?` instead + | This construct can be rewritten automatically under -rewrite -source 3.4-migration. +-- Warning: tests/neg/i7820.scala:1:28 --------------------------------------------------------------------------------- +1 |trait A1 { type F[X <: F[_, _], Y] } // error: cyclic reference involving type F + | ^ + | `_` is deprecated for wildcard arguments of types: use `?` instead + | This construct can be rewritten automatically under -rewrite -source 3.4-migration. diff --git a/tests/neg/i7820.scala b/tests/neg/i7820.scala index 22ec8b7ac8b1..cc803aa1f3d6 100644 --- a/tests/neg/i7820.scala +++ b/tests/neg/i7820.scala @@ -1,3 +1,3 @@ trait A1 { type F[X <: F[_, _], Y] } // error: cyclic reference involving type F -trait A2 { type F[X <: F, Y] } // error: cyclic reference involving type F // error -trait A3 { type F[X >: F, Y] } // error: cyclic reference involving type F // error +trait A2 { type F[X <: F, Y] } // error: cyclic reference involving type F +trait A3 { type F[X >: F, Y] } // error: cyclic reference involving type F