diff options
author | Adriaan Moors <adriaan.moors@typesafe.com> | 2014-02-11 19:39:39 -0800 |
---|---|---|
committer | Adriaan Moors <adriaan.moors@typesafe.com> | 2014-02-11 19:39:39 -0800 |
commit | 98320a6d5947f26e7252b025cf56a0763b39cd07 (patch) | |
tree | 34bb175cd1839ba8187e12562fa31ea5622d8a94 /src/reflect | |
parent | e3d624dd2760f2593949a1b5493f5a92fb02138a (diff) | |
parent | 47885ff665a951d2574a7ca5b2ea27c30b67c862 (diff) | |
download | scala-98320a6d5947f26e7252b025cf56a0763b39cd07.tar.gz scala-98320a6d5947f26e7252b025cf56a0763b39cd07.tar.bz2 scala-98320a6d5947f26e7252b025cf56a0763b39cd07.zip |
Merge pull request #3503 from adriaanm/rebase-3440
SI-7475 Private members aren't inheritable, findMember overhaul
Diffstat (limited to 'src/reflect')
-rw-r--r-- | src/reflect/scala/reflect/internal/Types.scala | 173 | ||||
-rw-r--r-- | src/reflect/scala/reflect/internal/tpe/FindMembers.scala | 288 | ||||
-rw-r--r-- | src/reflect/scala/reflect/runtime/JavaUniverseForce.scala | 31 |
3 files changed, 292 insertions, 200 deletions
diff --git a/src/reflect/scala/reflect/internal/Types.scala b/src/reflect/scala/reflect/internal/Types.scala index 17a58d79f6..cf405ade03 100644 --- a/src/reflect/scala/reflect/internal/Types.scala +++ b/src/reflect/scala/reflect/internal/Types.scala @@ -82,6 +82,7 @@ trait Types with tpe.GlbLubs with tpe.TypeMaps with tpe.TypeConstraints + with tpe.FindMembers with util.Collections { self: SymbolTable => import definitions._ @@ -243,18 +244,6 @@ trait Types } } - /** Same as a call to narrow unless existentials are visible - * after widening the type. In that case, narrow from the widened - * type instead of the proxy. This gives buried existentials a - * chance to make peace with the other types. See SI-5330. - */ - private def narrowForFindMember(tp: Type): Type = { - val w = tp.widen - // Only narrow on widened type when we have to -- narrow is expensive unless the target is a singleton type. - if ((tp ne w) && containsExistential(w)) w.narrow - else tp.narrow - } - /** The base class for all types */ abstract class Type extends TypeApiImpl with Annotatable[Type] { /** Types for which asSeenFrom always is the identity, no matter what @@ -989,64 +978,7 @@ trait Types * */ def findMembers(excludedFlags: Long, requiredFlags: Long): Scope = { - def findMembersInternal: Scope = { - var members: Scope = null - if (Statistics.canEnable) Statistics.incCounter(findMembersCount) - val start = if (Statistics.canEnable) Statistics.pushTimer(typeOpsStack, findMembersNanos) else null - - //Console.println("find member " + name.decode + " in " + this + ":" + this.baseClasses)//DEBUG - var required = requiredFlags - var excluded = excludedFlags | DEFERRED - var retryForDeferred = true - var self: Type = null - while (retryForDeferred) { - retryForDeferred = false - val bcs0 = baseClasses - var bcs = bcs0 - while (!bcs.isEmpty) { - val decls = bcs.head.info.decls - var entry = decls.elems - while (entry ne null) { - val sym = entry.sym - val flags = sym.flags - if ((flags & required) == required) { - val excl = flags & excluded - if (excl == 0L && - (// omit PRIVATE LOCALS unless selector class is contained in class owning the def. - (bcs eq bcs0) || - (flags & PrivateLocal) != PrivateLocal || - (bcs0.head.hasTransOwner(bcs.head)))) { - if (members eq null) members = newFindMemberScope - var others: ScopeEntry = members.lookupEntry(sym.name) - var symtpe: Type = null - while ((others ne null) && { - val other = others.sym - (other ne sym) && - ((other.owner eq sym.owner) || - (flags & PRIVATE) != 0 || { - if (self eq null) self = narrowForFindMember(this) - if (symtpe eq null) symtpe = self.memberType(sym) - !(self.memberType(other) matches symtpe) - })}) { - others = members lookupNextEntry others - } - if (others eq null) members enter sym - } else if (excl == DEFERRED) { - retryForDeferred = (excludedFlags & DEFERRED) == 0 - } - } - entry = entry.next - } // while (entry ne null) - // excluded = excluded | LOCAL - bcs = bcs.tail - } // while (!bcs.isEmpty) - required |= DEFERRED - excluded &= ~(DEFERRED.toLong) - } // while (retryForDeferred) - if (Statistics.canEnable) Statistics.popTimer(typeOpsStack, start) - if (members eq null) EmptyScope else members - } - + def findMembersInternal = new FindMembers(this, excludedFlags, requiredFlags).apply() if (this.isGround) findMembersInternal else suspendingTypeVars(typeVarsInType(this))(findMembersInternal) } @@ -1055,110 +987,13 @@ trait Types * Find member(s) in this type. If several members matching criteria are found, they are * returned in an OverloadedSymbol * - * @param name The member's name, where nme.ANYNAME means `unspecified` + * @param name The member's name * @param excludedFlags Returned members do not have these flags * @param requiredFlags Returned members do have these flags * @param stableOnly If set, return only members that are types or stable values */ - //TODO: use narrow only for modules? (correct? efficiency gain?) def findMember(name: Name, excludedFlags: Long, requiredFlags: Long, stableOnly: Boolean): Symbol = { - def findMemberInternal: Symbol = { - var member: Symbol = NoSymbol - var members: List[Symbol] = null - var lastM: ::[Symbol] = null - if (Statistics.canEnable) Statistics.incCounter(findMemberCount) - val start = if (Statistics.canEnable) Statistics.pushTimer(typeOpsStack, findMemberNanos) else null - - //Console.println("find member " + name.decode + " in " + this + ":" + this.baseClasses)//DEBUG - var membertpe: Type = null - var required = requiredFlags - var excluded = excludedFlags | DEFERRED - var continue = true - var self: Type = null - - while (continue) { - continue = false - val bcs0 = baseClasses - var bcs = bcs0 - // omit PRIVATE LOCALS unless selector class is contained in class owning the def. - def admitPrivateLocal(owner: Symbol): Boolean = { - val selectorClass = this match { - case tt: ThisType => tt.sym // SI-7507 the first base class is not necessarily the selector class. - case _ => bcs0.head - } - selectorClass.hasTransOwner(owner) - } - while (!bcs.isEmpty) { - val decls = bcs.head.info.decls - var entry = decls.lookupEntry(name) - while (entry ne null) { - val sym = entry.sym - val flags = sym.flags - if ((flags & required) == required) { - val excl = flags & excluded - if (excl == 0L && - ( - (bcs eq bcs0) || - (flags & PrivateLocal) != PrivateLocal || - admitPrivateLocal(bcs.head))) { - if (name.isTypeName || (stableOnly && sym.isStable && !sym.hasVolatileType)) { - if (Statistics.canEnable) Statistics.popTimer(typeOpsStack, start) - return sym - } else if (member eq NoSymbol) { - member = sym - } else if (members eq null) { - if ((member ne sym) && - ((member.owner eq sym.owner) || - (flags & PRIVATE) != 0 || { - if (self eq null) self = narrowForFindMember(this) - if (membertpe eq null) membertpe = self.memberType(member) - !(membertpe matches self.memberType(sym)) - })) { - lastM = new ::(sym, null) - members = member :: lastM - } - } else { - var others: List[Symbol] = members - var symtpe: Type = null - while ((others ne null) && { - val other = others.head - (other ne sym) && - ((other.owner eq sym.owner) || - (flags & PRIVATE) != 0 || { - if (self eq null) self = narrowForFindMember(this) - if (symtpe eq null) symtpe = self.memberType(sym) - !(self.memberType(other) matches symtpe) - })}) { - others = others.tail - } - if (others eq null) { - val lastM1 = new ::(sym, null) - lastM.tl = lastM1 - lastM = lastM1 - } - } - } else if (excl == DEFERRED) { - continue = true - } - } - entry = decls lookupNextEntry entry - } // while (entry ne null) - // excluded = excluded | LOCAL - bcs = if (name == nme.CONSTRUCTOR) Nil else bcs.tail - } // while (!bcs.isEmpty) - required |= DEFERRED - excluded &= ~(DEFERRED.toLong) - } // while (continue) - if (Statistics.canEnable) Statistics.popTimer(typeOpsStack, start) - if (members eq null) { - if (member == NoSymbol) if (Statistics.canEnable) Statistics.incCounter(noMemberCount) - member - } else { - if (Statistics.canEnable) Statistics.incCounter(multMemberCount) - lastM.tl = Nil - baseClasses.head.newOverloaded(this, members) - } - } + def findMemberInternal = new FindMember(this, name, excludedFlags, requiredFlags, stableOnly).apply() if (this.isGround) findMemberInternal else suspendingTypeVars(typeVarsInType(this))(findMemberInternal) diff --git a/src/reflect/scala/reflect/internal/tpe/FindMembers.scala b/src/reflect/scala/reflect/internal/tpe/FindMembers.scala new file mode 100644 index 0000000000..de54f3768e --- /dev/null +++ b/src/reflect/scala/reflect/internal/tpe/FindMembers.scala @@ -0,0 +1,288 @@ +/* NSC -- new Scala compiler + * Copyright 2005-2014 LAMP/EPFL + * @author Jason Zaugg + */ +package scala.reflect.internal +package tpe + +import Flags._ +import util.Statistics +import TypesStats._ + +trait FindMembers { + this: SymbolTable => + + /** Implementatation of `Type#{findMember, findMembers}` */ + private[internal] abstract class FindMemberBase[T](tpe: Type, name: Name, excludedFlags: Long, requiredFlags: Long) { + protected val initBaseClasses: List[Symbol] = tpe.baseClasses + + // The first base class, or the symbol of the ThisType + // e.g in: + // trait T { self: C => } + // + // The selector class of `T.this.type` is `T`, and *not* the first base class, `C`. + private[this] var _selectorClass: Symbol = null + private def selectorClass: Symbol = { + if (_selectorClass eq null) { + _selectorClass = tpe match { + case tt: ThisType => tt.sym // SI-7507 the first base class is not necessarily the selector class. + case _ => initBaseClasses.head + } + } + _selectorClass + } + + // Cache for the narrowed type of `tp` (in `tp.findMember`). + // This is needed to avoid mismatched existential types are reported in SI-5330. + private[this] var _self: Type = null + protected def self: Type = { + // TODO: use narrow only for modules? (correct? efficiency gain?) (<-- Note: this comment predates SI-5330) + if (_self eq null) _self = narrowForFindMember(tpe) + _self + } + + // Main entry point + def apply(): T = { + if (Statistics.canEnable) Statistics.incCounter(findMemberCount) + val start = if (Statistics.canEnable) Statistics.pushTimer(typeOpsStack, findMemberNanos) else null + try searchConcreteThenDeferred + finally if (Statistics.canEnable) Statistics.popTimer(typeOpsStack, start) + } + + protected def result: T + + // SLS 5.1.3 First, a concrete definition always overrides an abstract definition + private def searchConcreteThenDeferred: T = { + val deferredSeen = walkBaseClasses(requiredFlags, excludedFlags | DEFERRED) + if (deferredSeen) // OPT: the `if` avoids a second pass if the first pass didn't spot any candidates. + walkBaseClasses(requiredFlags | DEFERRED, excludedFlags & ~(DEFERRED.toLong)) + result + } + + /* + * Walk up through the decls of each base class. + * + * Called in two passes: first excluding deferred, then mandating it. + * + * @return if a potential deferred member was seen on the first pass that calls for a second pass, + and `excluded & DEFERRED != 0L` + */ + private def walkBaseClasses(required: Long, excluded: Long): Boolean = { + var bcs = initBaseClasses + + // Have we seen a candidate deferred member? + var deferredSeen = false + + // All direct parents of refinement classes in the base class sequence + // from the current `walkBaseClasses` + var refinementParents: List[Symbol] = Nil + + // Has the current `walkBaseClasses` encountered a non-refinement class? + var seenFirstNonRefinementClass = false + + val findAll = name == nme.ANYname + + while (!bcs.isEmpty) { + val currentBaseClass = bcs.head + val decls = currentBaseClass.info.decls + var entry = if (findAll) decls.elems else decls.lookupEntry(name) + while (entry ne null) { + val sym = entry.sym + val flags = sym.flags + val meetsRequirements = (flags & required) == required + if (meetsRequirements) { + val excl: Long = flags & excluded + val isExcluded: Boolean = excl != 0L + if (!isExcluded && isPotentialMember(sym, flags, currentBaseClass, seenFirstNonRefinementClass, refinementParents)) { + if (shortCircuit(sym)) return false + else addMemberIfNew(sym) + } else if (excl == DEFERRED) { + deferredSeen = true + } + } + entry = if (findAll) entry.next else decls lookupNextEntry entry + } + + // SLS 5.2 The private modifier can be used with any definition or declaration in a template. + // They are not inherited by subclasses [...] + if (currentBaseClass.isRefinementClass) + // SLS 3.2.7 A compound type T1 with . . . with Tn {R } represents objects with members as given in + // the component types T1, ..., Tn and the refinement {R } + // + // => private members should be included from T1, ... Tn. (SI-7475) + refinementParents :::= currentBaseClass.parentSymbols + else if (currentBaseClass.isClass) + seenFirstNonRefinementClass = true // only inherit privates of refinement parents after this point + + bcs = bcs.tail + } + deferredSeen + } + + /* Should this symbol be returned immediately as the sole result? */ + protected def shortCircuit(sym: Symbol): Boolean + + /* Add this member to the final result, unless an already-found member matches it. */ + protected def addMemberIfNew(sym: Symbol): Unit + + // Is `sym` a potentially member of `baseClass`? + // + // Q. When does a potential member fail to be a an actual member? + // A. if it is subsumed by an member in a subclass. + private def isPotentialMember(sym: Symbol, flags: Long, owner: Symbol, + seenFirstNonRefinementClass: Boolean, refinementParents: List[Symbol]): Boolean = { + // conservatively (performance wise) doing this with flags masks rather than `sym.isPrivate` + // to avoid multiple calls to `Symbol#flags`. + val isPrivate = (flags & PRIVATE) == PRIVATE + val isPrivateLocal = (flags & PrivateLocal) == PrivateLocal + + // TODO Is the special handling of `private[this]` vs `private` backed up by the spec? + def admitPrivate(sym: Symbol): Boolean = + (selectorClass == owner) || ( + !isPrivateLocal // private[this] only a member from within the selector class. (Optimization only? Does the spec back this up?) + && ( + !seenFirstNonRefinementClass + || refinementParents.contains(owner) + ) + ) + + (!isPrivate || admitPrivate(sym)) && (sym.name != nme.CONSTRUCTOR || owner == initBaseClasses.head) + } + + // True unless the already-found member of type `memberType` matches the candidate symbol `other`. + protected def isNewMember(member: Symbol, other: Symbol): Boolean = + ( (other ne member) + && ( (member.owner eq other.owner) // same owner, therefore overload + || (member.flags & PRIVATE) != 0 // (unqualified) private members never participate in overriding + || (other.flags & PRIVATE) != 0 // ... as overrider or overridee. + || !(memberTypeLow(member) matches memberTypeHi(other)) // do the member types match? If so, its an override. Otherwise it's an overload. + ) + ) + + // Cache for the member type of a candidate member when comparing against multiple, already-found existing members + // + // TODO this cache is probably unnecessary, `tp.memberType(sym: MethodSymbol)` is already cached internally. + private[this] var _memberTypeHiCache: Type = null + private[this] var _memberTypeHiCacheSym: Symbol = null + + protected def memberTypeHi(sym: Symbol): Type = { + if (_memberTypeHiCacheSym ne sym) { + _memberTypeHiCache = self.memberType(sym) + _memberTypeHiCacheSym = sym + } + _memberTypeHiCache + } + + // member type of the LHS of `matches` call. This is an extension point to enable a cache in + // FindMember. + protected def memberTypeLow(sym: Symbol): Type = self.memberType(sym) + + /** Same as a call to narrow unless existentials are visible + * after widening the type. In that case, narrow from the widened + * type instead of the proxy. This gives buried existentials a + * chance to make peace with the other types. See SI-5330. + */ + private def narrowForFindMember(tp: Type): Type = { + val w = tp.widen + // Only narrow on widened type when we have to -- narrow is expensive unless the target is a singleton type. + if ((tp ne w) && containsExistential(w)) w.narrow + else tp.narrow + } + } + + private[reflect] final class FindMembers(tpe: Type, excludedFlags: Long, requiredFlags: Long) + extends FindMemberBase[Scope](tpe, nme.ANYname, excludedFlags, requiredFlags) { + private[this] var _membersScope: Scope = null + private def membersScope: Scope = { + if (_membersScope eq null) _membersScope = newFindMemberScope + _membersScope + } + + protected def shortCircuit(sym: Symbol): Boolean = false + protected def result: Scope = membersScope + + protected def addMemberIfNew(sym: Symbol): Unit = { + val members = membersScope + var others = members.lookupEntry(sym.name) + var isNew = true + while ((others ne null) && isNew) { + val member = others.sym + if (!isNewMember(member, sym)) + isNew = false + others = members lookupNextEntry others // next existing member with the same name. + } + if (isNew) members.enter(sym) + } + } + + private[reflect] final class FindMember(tpe: Type, name: Name, excludedFlags: Long, requiredFlags: Long, stableOnly: Boolean) + extends FindMemberBase[Symbol](tpe, name, excludedFlags, requiredFlags) { + // Gathering the results into a hand rolled ListBuffer + // TODO Try just using a ListBuffer to see if this low-level-ness is worth it. + private[this] var member0: Symbol = NoSymbol + private[this] var members: List[Symbol] = null + private[this] var lastM: ::[Symbol] = null + + private def clearAndAddResult(sym: Symbol): Unit = { + member0 = sym + members = null + lastM = null + } + + protected def shortCircuit(sym: Symbol): Boolean = (name.isTypeName || (stableOnly && sym.isStable && !sym.hasVolatileType)) && { + clearAndAddResult(sym) + true + } + + protected def addMemberIfNew(sym: Symbol): Unit = + if (member0 eq NoSymbol) { + member0 = sym // The first found member + } else if (members eq null) { + // We've found exactly one member so far... + if (isNewMember(member0, sym)) { + // ... make that two. + lastM = new ::(sym, null) + members = member0 :: lastM + } + } else { + // Already found 2 or more members + var ms: List[Symbol] = members + + var isNew = true + while ((ms ne null) && isNew) { + val member = ms.head + if (!isNewMember(member, sym)) + isNew = false + ms = ms.tail + } + if (isNew) { + val lastM1 = new ::(sym, null) + lastM.tl = lastM1 + lastM = lastM1 + } + } + + // Cache for the member type of the first member we find. + private[this] var _member0Tpe: Type = null + private[this] def member0Tpe: Type = { + assert(member0 != null) + if (_member0Tpe eq null) _member0Tpe = self.memberType(member0) + _member0Tpe + } + + override protected def memberTypeLow(sym: Symbol): Type = + if (sym eq member0) member0Tpe else super.memberTypeLow(sym) + + // Assemble the result from the hand-rolled ListBuffer + protected def result: Symbol = if (members eq null) { + if (member0 == NoSymbol) { + if (Statistics.canEnable) Statistics.incCounter(noMemberCount) + NoSymbol + } else member0 + } else { + if (Statistics.canEnable) Statistics.incCounter(multMemberCount) + lastM.tl = Nil + initBaseClasses.head.newOverloaded(tpe, members) + } + } +} diff --git a/src/reflect/scala/reflect/runtime/JavaUniverseForce.scala b/src/reflect/scala/reflect/runtime/JavaUniverseForce.scala index fb893cbff1..0fcf215580 100644 --- a/src/reflect/scala/reflect/runtime/JavaUniverseForce.scala +++ b/src/reflect/scala/reflect/runtime/JavaUniverseForce.scala @@ -27,27 +27,8 @@ trait JavaUniverseForce { self: runtime.JavaUniverse => this.settings this.treeInfo - // inaccessible: this.scala$reflect$runtime$Gil$$gil - // inaccessible: this.uniqueLock - // inaccessible: this._skolemizationLevel - // inaccessible: this._undoLog - // inaccessible: this._intersectionWitness - // inaccessible: this._subsametypeRecursions - // inaccessible: this._pendingSubTypes - // inaccessible: this._basetypeRecursions - // inaccessible: this._pendingBaseTypes - // inaccessible: this._lubResults - // inaccessible: this._glbResults - // inaccessible: this._indent - // inaccessible: this._toStringRecursions - // inaccessible: this._toStringSubjects - // inaccessible: this.atomicIds - // inaccessible: this.atomicExistentialIds - // inaccessible: this._recursionTable - // inaccessible: this.mirrors this.rootMirror this.treeBuild - // inaccessible: this.SimpleNameOrdering this.traceSymbols this.perRunCaches this.FreshNameExtractor @@ -60,7 +41,6 @@ trait JavaUniverseForce { self: runtime.JavaUniverse => this.SubpatternsAttachment this.noPrint this.typeDebug - // inaccessible: this.maxFree this.Range // inaccessible: this.posAssigner this.ConsoleWriter @@ -116,7 +96,6 @@ trait JavaUniverseForce { self: runtime.JavaUniverse => this.pendingSuperCall this.emptyValDef this.EmptyTreeTypeSubstituter - // inaccessible: this.duplicator this.UnmappableAnnotArg this.LiteralAnnotArg this.ArrayAnnotArg @@ -127,7 +106,6 @@ trait JavaUniverseForce { self: runtime.JavaUniverse => this.UnmappableAnnotation this.ErroneousAnnotation this.ThrownException - // inaccessible: this.compactify this.tpnme this.fulltpnme this.binarynme @@ -147,7 +125,6 @@ trait JavaUniverseForce { self: runtime.JavaUniverse => this.ProperTypeKind this.TypeConKind this.inferKind - // inaccessible: this.substTypeMapCache this.UnmappableTree this.ErrorType this.WildcardType @@ -184,9 +161,6 @@ trait JavaUniverseForce { self: runtime.JavaUniverse => this.unwrapToStableClass this.unwrapWrapperTypes this.RecoverableCyclicReference - // inaccessible: this._undoLog - // inaccessible: this.numericLoBound - // inaccessible: this.numericHiBound this.TypeConstraint this.normalizeAliases this.dropSingletonType @@ -198,19 +172,16 @@ trait JavaUniverseForce { self: runtime.JavaUniverse => this.typeVarToOriginMap this.ErroneousCollector this.adaptToNewRunMap - // inaccessible: this.commonOwnerMapObj this.SubTypePair this.SymbolKind this.NoSymbol this.CyclicReference - // inaccessible: this.TypeHistory this.SymbolOps this.TermName this.TypeName this.Liftable this.Unliftable this.BooleanFlag - // inaccessible: this.CachedNames this.WeakTypeTag this.TypeTag this.Expr @@ -427,14 +398,12 @@ trait JavaUniverseForce { self: runtime.JavaUniverse => definitions.languageFeatureModule definitions.metaAnnotations definitions.AnnotationDefaultAttr - // inaccessible: definitions.erasurePhase definitions.isPhantomClass definitions.syntheticCoreClasses definitions.syntheticCoreMethods definitions.hijackedCoreClasses definitions.symbolsNotPresentInBytecode definitions.isPossibleSyntheticParent - // inaccessible: definitions.boxedValueClassesSet definitions.abbrvTag definitions.numericWeight definitions.boxedModule |