diff --git a/compiler/src/dotty/tools/dotc/core/SymDenotations.scala b/compiler/src/dotty/tools/dotc/core/SymDenotations.scala index 602848a50c3f..6d1a006ed19b 100644 --- a/compiler/src/dotty/tools/dotc/core/SymDenotations.scala +++ b/compiler/src/dotty/tools/dotc/core/SymDenotations.scala @@ -1493,6 +1493,9 @@ object SymDenotations { myMemberCache } + /** Hook to do a pre-enter test. Overridden in PackageDenotation */ + protected def proceedWithEnter(sym: Symbol, mscope: MutableScope)(implicit ctx: Context): Boolean = true + /** Enter a symbol in current scope, and future scopes of same denotation. * Note: We require that this does not happen after the first time * someone does a findMember on a subclass. @@ -1510,19 +1513,13 @@ object SymDenotations { scope case _ => unforcedDecls.openForMutations } - if (this is PackageClass) { - val entry = mscope.lookupEntry(sym.name) - if (entry != null) { - if (entry.sym == sym) return - mscope.unlink(entry) - entry.sym.denot = sym.denot // to avoid stale symbols + if (proceedWithEnter(sym, mscope)) { + enterNoReplace(sym, mscope) + val nxt = this.nextInRun + if (nxt.validFor.code > this.validFor.code) { + this.nextInRun.asSymDenotation.asClass.enter(sym) } } - enterNoReplace(sym, mscope) - val nxt = this.nextInRun - if (nxt.validFor.code > this.validFor.code) { - this.nextInRun.asSymDenotation.asClass.enter(sym) - } } /** Enter a symbol in given `scope` without potentially replacing the old copy. */ @@ -1534,7 +1531,7 @@ object SymDenotations { (scope ne this.unforcedDecls) || sym.hasAnnotation(defn.ScalaStaticAnnot) || sym.name.isInlineAccessor || - isUsecase) + isUsecase, i"trying to enter $sym in $this, frozen = ${this is Frozen}") scope.enter(sym) @@ -1800,7 +1797,7 @@ object SymDenotations { /** The denotation of a package class. * It overrides ClassDenotation to take account of package objects when looking for members */ - class PackageClassDenotation private[SymDenotations] ( + final class PackageClassDenotation private[SymDenotations] ( symbol: Symbol, ownerIfExists: Symbol, name: Name, @@ -1823,15 +1820,33 @@ object SymDenotations { packageObjCache } - /** Look first for members in package; if none are found look in package object */ - override def computeNPMembersNamed(name: Name, inherited: Boolean)(implicit ctx: Context): PreDenotation = { - val denots = super.computeNPMembersNamed(name, inherited) - if (denots.exists) denots - else packageObj.moduleClass.denot match { - case pcls: ClassDenotation => pcls.computeNPMembersNamed(name, inherited) - case _ => denots + /** Looks in both the package object and the package for members. The precise algorithm + * is as follows: + * + * If this is the scala package look in the package first, and if nothing is found + * there, look in the package object second. Otherwise, look in the package object + * first, and if nothing is found there, in the package second. + * + * The reason for the special treatment of the scala package is that if we + * complete it too early, we freeze its superclass Any, so that no members can + * be entered in it. As a consequence, there should be no entry in the scala package + * object that hides a class or object in the scala package of the same name, because + * the behavior would then be unintuitive for such members. + */ + override def computeNPMembersNamed(name: Name, inherited: Boolean)(implicit ctx: Context): PreDenotation = + packageObj.moduleClass.denot match { + case pcls: ClassDenotation if !pcls.isCompleting => + if (symbol eq defn.ScalaPackageClass) { + val denots = super.computeNPMembersNamed(name, inherited) + if (denots.exists) denots else pcls.computeNPMembersNamed(name, inherited) + } + else { + val denots = pcls.computeNPMembersNamed(name, inherited) + if (denots.exists) denots else super.computeNPMembersNamed(name, inherited) + } + case _ => + super.computeNPMembersNamed(name, inherited) } - } /** The union of the member names of the package and the package object */ override def memberNames(keepOnly: NameFilter)(implicit ctx: Context): Set[Name] = { @@ -1841,6 +1856,21 @@ object SymDenotations { case _ => ownNames } } + + /** If another symbol with the same name is entered, unlink it, + * and, if symbol is a package object, invalidate the packageObj cache. + * @return `sym` is not already entered + */ + override def proceedWithEnter(sym: Symbol, mscope: MutableScope)(implicit ctx: Context): Boolean = { + val entry = mscope.lookupEntry(sym.name) + if (entry != null) { + if (entry.sym == sym) return false + mscope.unlink(entry) + entry.sym.denot = sym.denot // to avoid stale symbols + if (sym.name == nme.PACKAGE) packageObjRunId = NoRunId + } + true + } } class NoDenotation extends SymDenotation( diff --git a/compiler/src/dotty/tools/dotc/core/TypeComparer.scala b/compiler/src/dotty/tools/dotc/core/TypeComparer.scala index da6d6338789a..54b96a2530b3 100644 --- a/compiler/src/dotty/tools/dotc/core/TypeComparer.scala +++ b/compiler/src/dotty/tools/dotc/core/TypeComparer.scala @@ -726,10 +726,9 @@ class TypeComparer(initctx: Context) extends DotClass with ConstraintHandling { tycon2 match { case param2: TypeParamRef => - isMatchingApply(tp1) || { - if (canConstrain(param2)) canInstantiate(param2) - else compareLower(bounds(param2), tyconIsTypeRef = false) - } + isMatchingApply(tp1) || + canConstrain(param2) && canInstantiate(param2) || + compareLower(bounds(param2), tyconIsTypeRef = false) case tycon2: TypeRef => isMatchingApply(tp1) || compareLower(tycon2.info.bounds, tyconIsTypeRef = true) diff --git a/compiler/src/dotty/tools/dotc/core/Types.scala b/compiler/src/dotty/tools/dotc/core/Types.scala index 8ae3aa7ad43c..e7130ee2f2cd 100644 --- a/compiler/src/dotty/tools/dotc/core/Types.scala +++ b/compiler/src/dotty/tools/dotc/core/Types.scala @@ -1504,20 +1504,29 @@ object Types { case _ => NoType } assert( - (lastSymbol eq sym) || - (lastSymbol eq null) || { + (lastSymbol eq sym) + || + (lastSymbol eq null) + || { val lastDefRunId = lastDenotation match { case d: SymDenotation => d.validFor.runId case _ => lastSymbol.defRunId } (lastDefRunId != sym.defRunId) || (lastDefRunId == NoRunId) - } || - (lastSymbol.infoOrCompleter.isInstanceOf[ErrorType] || + } + || + lastSymbol.infoOrCompleter.isInstanceOf[ErrorType] + || + sym.isPackageObject // package objects can be visited before we get around to index them + || sym.owner != lastSymbol.owner && - (sym.owner.derivesFrom(lastSymbol.owner) || - selfTypeOf(sym).derivesFrom(lastSymbol.owner) || - selfTypeOf(lastSymbol).derivesFrom(sym.owner))), + (sym.owner.derivesFrom(lastSymbol.owner) + || + selfTypeOf(sym).derivesFrom(lastSymbol.owner) + || + selfTypeOf(lastSymbol).derivesFrom(sym.owner) + ), i"""data race? overwriting symbol of type $this, |long form = $toString of class $getClass, |last sym id = ${lastSymbol.id}, new sym id = ${sym.id}, diff --git a/tests/pos/i2200/Hello.scala b/tests/pos/i2200/Hello.scala new file mode 100644 index 000000000000..47e8b2024164 --- /dev/null +++ b/tests/pos/i2200/Hello.scala @@ -0,0 +1,6 @@ +package bar +import scala.language.higherKinds +class Fix[F[_]](unfix: F[Fix[F]]) +object DocTree { + def docTree(s: StreamTree[DocTree]): DocTree = new Fix(s: StreamTree[DocTree]) +} diff --git a/tests/pos/i2200/package.scala b/tests/pos/i2200/package.scala new file mode 100644 index 000000000000..3bc519b72ce3 --- /dev/null +++ b/tests/pos/i2200/package.scala @@ -0,0 +1,4 @@ +package object bar { + type StreamTree[T] = Stream[Int] + type DocTree = Fix[StreamTree] +}