Skip to content

Commit b955f9a

Browse files
authored
Merge pull request #12660 from dotty-staging/scala2-tparam-wrong-owner
Scala2Unpickler: Don't trust the owner field of tparams
2 parents 5ac62a1 + 7535dd9 commit b955f9a

File tree

5 files changed

+89
-10
lines changed

5 files changed

+89
-10
lines changed

compiler/src/dotty/tools/dotc/core/unpickleScala2/Scala2Unpickler.scala

+37-10
Original file line numberDiff line numberDiff line change
@@ -495,7 +495,27 @@ class Scala2Unpickler(bytes: Array[Byte], classRoot: ClassDenotation, moduleClas
495495
sym.setFlag(Scala2x)
496496
if (!(isRefinementClass(sym) || isUnpickleRoot(sym) || sym.is(Scala2Existential))) {
497497
val owner = sym.owner
498-
if (owner.isClass)
498+
val canEnter =
499+
owner.isClass &&
500+
(!sym.is(TypeParam) ||
501+
owner.infoOrCompleter.match
502+
case completer: ClassUnpickler =>
503+
// Type parameters seen after class initialization are not
504+
// actually type parameters of the current class but of some
505+
// external class because of the bizarre way in which Scala 2
506+
// pickles them (see
507+
// https://github.com/scala/scala/blob/aa31e3e6bb945f5d69740d379ede1cd514904109/src/compiler/scala/tools/nsc/symtab/classfile/Pickler.scala#L181-L197).
508+
// Make sure we don't enter them in the class otherwise the
509+
// compiler will get very confused (testcase in sbt-test/scala2-compat/i12641).
510+
// Note: I don't actually know if these stray type parameters
511+
// can also show up before initialization, if that's the case
512+
// we'll need to study more closely how Scala 2 handles type
513+
// parameter unpickling and try to emulate it.
514+
!completer.isInitialized
515+
case _ =>
516+
true)
517+
518+
if (canEnter)
499519
owner.asClass.enter(sym, symScope(owner))
500520
}
501521
sym
@@ -625,23 +645,30 @@ class Scala2Unpickler(bytes: Array[Byte], classRoot: ClassDenotation, moduleClas
625645
object localMemberUnpickler extends LocalUnpickler
626646

627647
class ClassUnpickler(infoRef: Int) extends LocalUnpickler with TypeParamsCompleter {
628-
private def readTypeParams()(using Context): List[TypeSymbol] = {
648+
private var myTypeParams: List[TypeSymbol] = null
649+
650+
private def readTypeParams()(using Context): Unit = {
629651
val tag = readByte()
630652
val end = readNat() + readIndex
631-
if (tag == POLYtpe) {
632-
val unusedRestpeRef = readNat()
633-
until(end, () => readSymbolRef()(using ctx)).asInstanceOf[List[TypeSymbol]]
634-
}
635-
else Nil
653+
myTypeParams =
654+
if (tag == POLYtpe) {
655+
val unusedRestpeRef = readNat()
656+
until(end, () => readSymbolRef()(using ctx)).asInstanceOf[List[TypeSymbol]]
657+
} else Nil
636658
}
637-
private def loadTypeParams(using Context) =
659+
private def loadTypeParams()(using Context) =
638660
atReadPos(index(infoRef), () => readTypeParams()(using ctx))
639661

662+
/** Have the type params of this class already been unpickled? */
663+
def isInitialized: Boolean = myTypeParams ne null
664+
640665
/** Force reading type params early, we need them in setClassInfo of subclasses. */
641-
def init()(using Context): List[TypeSymbol] = loadTypeParams
666+
def init()(using Context): List[TypeSymbol] =
667+
if !isInitialized then loadTypeParams()
668+
myTypeParams
642669

643670
override def completerTypeParams(sym: Symbol)(using Context): List[TypeSymbol] =
644-
loadTypeParams
671+
init()
645672
}
646673

647674
def rootClassUnpickler(start: Coord, cls: Symbol, module: Symbol, infoRef: Int): ClassUnpickler =
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
import cek.Async
2+
3+
4+
object demo {
5+
6+
def test1[F[_]](ev: Async[F]): Unit = ???
7+
def test2[F[_]](ev: Async[F]): Unit = ???
8+
9+
}
+13
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
val scala3Version = sys.props("plugin.scalaVersion")
2+
val scala2Version = sys.props("plugin.scala2Version")
3+
4+
lazy val lib = project.in(file("lib"))
5+
.settings(
6+
scalaVersion := scala2Version
7+
)
8+
9+
lazy val app = project.in(file("app"))
10+
.dependsOn(lib)
11+
.settings(
12+
scalaVersion := scala3Version
13+
)
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
package cek
2+
3+
trait Async[F[_]]
4+
5+
object Async {
6+
trait WriterTAsync[F[_], L1]
7+
extends Async[({ type LL[A] = WriterT[F, L1, A] })#LL]
8+
with MonadCancel.WriterTMonadCancel[F, L1] {
9+
10+
override def delegate = super.delegate
11+
}
12+
13+
}
14+
15+
case class WriterT[F[_], L0, V]()
16+
17+
trait MonadError[F[_]]
18+
trait MonadCancel[F[_]]
19+
20+
object MonadCancel {
21+
22+
trait WriterTMonadCancel[F[_], L2]
23+
extends MonadCancel[({ type LL[A] = WriterT[F, L2, A] })#LL] {
24+
25+
def delegate: MonadError[({ type LL[A] = WriterT[F, L2, A] })#LL] =
26+
???
27+
28+
}
29+
}

sbt-test/scala2-compat/i12641/test

+1
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
> app/compile

0 commit comments

Comments
 (0)