@@ -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 =
0 commit comments