diff options
Diffstat (limited to 'compiler/src/dotty/tools/dotc/core')
30 files changed, 1345 insertions, 963 deletions
diff --git a/compiler/src/dotty/tools/dotc/core/Contexts.scala b/compiler/src/dotty/tools/dotc/core/Contexts.scala index 8707b66f9..b299de434 100644 --- a/compiler/src/dotty/tools/dotc/core/Contexts.scala +++ b/compiler/src/dotty/tools/dotc/core/Contexts.scala @@ -174,9 +174,6 @@ object Contexts { protected def freshNames_=(freshNames: FreshNameCreator) = _freshNames = freshNames def freshNames: FreshNameCreator = _freshNames - def freshName(prefix: String = ""): String = freshNames.newName(prefix) - def freshName(prefix: Name): String = freshName(prefix.toString) - /** A map in which more contextual properties can be stored */ private var _moreProperties: Map[Key[Any], Any] = _ protected def moreProperties_=(moreProperties: Map[Key[Any], Any]) = _moreProperties = moreProperties @@ -297,7 +294,7 @@ object Contexts { /** Is this a context that introduces a non-empty scope? */ def isNonEmptyScopeContext: Boolean = - (this.scope ne outer.scope) && this.scope.nonEmpty + (this.scope ne outer.scope) && !this.scope.isEmpty /** Leave message in diagnostics buffer if it exists */ def diagnose(str: => String) = diff --git a/compiler/src/dotty/tools/dotc/core/Definitions.scala b/compiler/src/dotty/tools/dotc/core/Definitions.scala index 8ff6a2ea9..a97589d73 100644 --- a/compiler/src/dotty/tools/dotc/core/Definitions.scala +++ b/compiler/src/dotty/tools/dotc/core/Definitions.scala @@ -68,7 +68,7 @@ class Definitions { enterTypeField(cls, name, flags | ClassTypeParamCreationFlags, scope) private def enterSyntheticTypeParam(cls: ClassSymbol, paramFlags: FlagSet, scope: MutableScope, suffix: String = "T0") = - enterTypeParam(cls, suffix.toTypeName.expandedName(cls), ExpandedName | paramFlags, scope) + enterTypeParam(cls, suffix.toTypeName.expandedName(cls), paramFlags, scope) // NOTE: Ideally we would write `parentConstrs: => Type*` but SIP-24 is only // implemented in Dotty and not in Scala 2. @@ -109,7 +109,7 @@ class Definitions { * def apply(implicit $x0: T0, ..., $x{N_1}: T{N-1}): R * } */ - private def newFunctionNTrait(name: TypeName) = { + def newFunctionNTrait(name: TypeName) = { val completer = new LazyType { def complete(denot: SymDenotation)(implicit ctx: Context): Unit = { val cls = denot.asClass.classSymbol @@ -120,7 +120,7 @@ class Definitions { enterTypeParam(cls, name ++ "$T" ++ i.toString, Contravariant, decls) val resParam = enterTypeParam(cls, name ++ "$R", Covariant, decls) val (methodType, parentTraits) = - if (name.startsWith(tpnme.ImplicitFunction)) { + if (name.firstPart.startsWith(str.ImplicitFunction)) { val superTrait = FunctionType(arity).appliedTo(argParams.map(_.typeRef) ::: resParam.typeRef :: Nil) (ImplicitMethodType, ctx.normalizeToClassRefs(superTrait :: Nil, cls, decls)) @@ -725,12 +725,11 @@ class Definitions { /** If type `ref` refers to a class in the scala package, its name, otherwise EmptyTypeName */ def scalaClassName(ref: Type)(implicit ctx: Context): TypeName = scalaClassName(ref.classSymbol) - private def isVarArityClass(cls: Symbol, prefix: Name) = { - val name = scalaClassName(cls) - name.startsWith(prefix) && - name.length > prefix.length && - name.drop(prefix.length).forall(_.isDigit) - } + private def isVarArityClass(cls: Symbol, prefix: String) = + scalaClassName(cls).testSimple(name => + name.startsWith(prefix) && + name.length > prefix.length && + name.drop(prefix.length).forall(_.isDigit)) def isBottomClass(cls: Symbol) = cls == NothingClass || cls == NullClass @@ -760,9 +759,9 @@ class Definitions { */ def isSyntheticFunctionClass(cls: Symbol) = scalaClassName(cls).isSyntheticFunction - def isAbstractFunctionClass(cls: Symbol) = isVarArityClass(cls, tpnme.AbstractFunction) - def isTupleClass(cls: Symbol) = isVarArityClass(cls, tpnme.Tuple) - def isProductClass(cls: Symbol) = isVarArityClass(cls, tpnme.Product) + def isAbstractFunctionClass(cls: Symbol) = isVarArityClass(cls, str.AbstractFunction) + def isTupleClass(cls: Symbol) = isVarArityClass(cls, str.Tuple) + def isProductClass(cls: Symbol) = isVarArityClass(cls, str.Product) /** Returns the erased class of the function class `cls` * - FunctionN for N > 22 becomes FunctionXXL @@ -924,23 +923,6 @@ class Definitions { // ----- Initialization --------------------------------------------------- - /** Give the scala package a scope where a FunctionN trait is automatically - * added when someone looks for it. - */ - private def makeScalaSpecial()(implicit ctx: Context) = { - val oldInfo = ScalaPackageClass.classInfo - val oldDecls = oldInfo.decls - val newDecls = new MutableScope(oldDecls) { - override def lookupEntry(name: Name)(implicit ctx: Context): ScopeEntry = { - val res = super.lookupEntry(name) - if (res == null && name.isTypeName && name.isSyntheticFunction) - newScopeEntry(newFunctionNTrait(name.asTypeName)) - else res - } - } - ScalaPackageClass.info = oldInfo.derivedClassInfo(decls = newDecls) - } - /** Lists core classes that don't have underlying bytecode, but are synthesized on-the-fly in every reflection universe */ lazy val syntheticScalaClasses = List( AnyClass, @@ -968,8 +950,6 @@ class Definitions { def init()(implicit ctx: Context) = { this.ctx = ctx if (!_isInitialized) { - makeScalaSpecial() - // force initialization of every symbol that is synthesized or hijacked by the compiler val forced = syntheticCoreClasses ++ syntheticCoreMethods ++ ScalaValueClasses() diff --git a/compiler/src/dotty/tools/dotc/core/Denotations.scala b/compiler/src/dotty/tools/dotc/core/Denotations.scala index 7341b96af..fd42bde36 100644 --- a/compiler/src/dotty/tools/dotc/core/Denotations.scala +++ b/compiler/src/dotty/tools/dotc/core/Denotations.scala @@ -4,8 +4,9 @@ package core import SymDenotations.{ SymDenotation, ClassDenotation, NoDenotation } import Contexts.{Context, ContextBase} -import Names.{Name, PreName} -import Names.TypeName +import Names._ +import NameOps._ +import NameKinds._ import StdNames._ import Symbols.NoSymbol import Symbols._ @@ -1171,27 +1172,42 @@ object Denotations { * if generateStubs is set, generates stubs for missing top-level symbols */ def staticRef(path: Name, generateStubs: Boolean = true)(implicit ctx: Context): Denotation = { - def recur(path: Name, len: Int): Denotation = { - val point = path.lastIndexOf('.', len - 1) - val owner = - if (point > 0) recur(path.toTermName, point).disambiguate(_.info.isParameterless) - else if (path.isTermName) defn.RootClass.denot - else defn.EmptyPackageClass.denot + def select(prefix: Denotation, selector: Name): Denotation = { + val owner = prefix.disambiguate(_.info.isParameterless) if (owner.exists) { - val name = path slice (point + 1, len) - val result = owner.info.member(name) - if (result ne NoDenotation) result + val result = owner.info.member(selector) + if (result.exists) result else { val alt = - if (generateStubs) missingHook(owner.symbol.moduleClass, name) + if (generateStubs) missingHook(owner.symbol.moduleClass, selector) else NoSymbol - if (alt.exists) alt.denot - else MissingRef(owner, name) + if (alt.exists) alt.denot else MissingRef(owner, selector) } } else owner } - recur(path, path.length) + def recur(path: Name, wrap: TermName => Name = identity): Denotation = path match { + case path: TypeName => + recur(path.toTermName, n => n.toTypeName) + case ModuleClassName(underlying) => + recur(underlying, n => wrap(ModuleClassName(n))) + case QualifiedName(prefix, selector) => + select(recur(prefix), wrap(selector)) + case qn @ AnyQualifiedName(prefix, _) => + recur(prefix, n => wrap(qn.info.mkString(n).toTermName)) + case path: SimpleTermName => + def recurSimple(len: Int, wrap: TermName => Name): Denotation = { + val point = path.lastIndexOf('.', len - 1) + val selector = wrap(path.slice(point + 1, len).asTermName) + val prefix = + if (point > 0) recurSimple(point, identity) + else if (selector.isTermName) defn.RootClass.denot + else defn.EmptyPackageClass.denot + select(prefix, selector) + } + recurSimple(path.length, wrap) + } + recur(path) } /** If we are looking for a non-existing term name in a package, diff --git a/compiler/src/dotty/tools/dotc/core/Flags.scala b/compiler/src/dotty/tools/dotc/core/Flags.scala index 29f1078a2..84072cd50 100644 --- a/compiler/src/dotty/tools/dotc/core/Flags.scala +++ b/compiler/src/dotty/tools/dotc/core/Flags.scala @@ -272,9 +272,6 @@ object Flags { */ final val Synthetic = commonFlag(18, "<synthetic>") - /** Symbol's name is expanded */ - final val ExpandedName = commonFlag(19, "<expandedname>") - /** A covariant type variable / an outer accessor */ final val CovariantOrOuter = commonFlag(20, "") final val Covariant = typeFlag(20, "<covariant>") @@ -308,19 +305,18 @@ object Flags { final val CaseAccessor = termFlag(25, "<caseaccessor>") /** A binding for a type parameter of a base class or trait. - * TODO: Replace with combination of isType, ExpandedName, and Override? */ final val BaseTypeArg = typeFlag(25, "<basetypearg>") final val CaseAccessorOrBaseTypeArg = CaseAccessor.toCommonFlags /** A super accessor */ - final val SuperAccessor = termFlag(26, "<superaccessor>") + final val Scala2SuperAccessor = termFlag(26, "<superaccessor>") /** An unpickled Scala 2.x class */ final val Scala2x = typeFlag(26, "<scala-2.x>") - final val SuperAccessorOrScala2x = SuperAccessor.toCommonFlags + final val SuperAccessorOrScala2x = Scala2x.toCommonFlags /** A method that has default params */ final val DefaultParameterized = termFlag(27, "<defaultparam>") @@ -409,9 +405,6 @@ object Flags { final val Scala2ExistentialCommon = commonFlag(55, "<existential>") final val Scala2Existential = Scala2ExistentialCommon.toTypeFlags - /** An overloaded symbol (Scala 2.x only) */ - final val Scala2Overloaded = termFlag(56, "<overloaded>") - /** A module variable (Scala 2.x only) */ final val Scala2ModuleVar = termFlag(57, "<modulevar>") @@ -424,6 +417,13 @@ object Flags { /** A method that is known to have inherited default parameters */ final val InheritedDefaultParams = termFlag(60, "<inherited-default-param>") + /** Translation of Scala2's EXPANDEDNAME flag. This flag is never stored in + * symbols, is only used locally when reading the flags of a Scala2 symbol. + * It's therefore safe to share the code with `InheritedDefaultParams` because + * the latter is never present in Scala2 unpickle info. + */ + final val Scala2ExpandedName = InheritedDefaultParams.toCommonFlags + /** A method that is known to have no default parameters */ final val NoDefaultParams = termFlag(61, "<no-default-param>") @@ -452,7 +452,7 @@ object Flags { final val FromStartFlags = Module | Package | Deferred | MethodOrHKCommon | Param | ParamAccessor | Scala2ExistentialCommon | Mutable.toCommonFlags | InSuperCall | Touched | JavaStatic | - CovariantOrOuter | ContravariantOrLabel | ExpandedName | CaseAccessorOrBaseTypeArg | + CovariantOrOuter | ContravariantOrLabel | CaseAccessorOrBaseTypeArg | Fresh | Frozen | Erroneous | ImplicitCommon | Permanent | Synthetic | SuperAccessorOrScala2x | Inline @@ -475,7 +475,7 @@ object Flags { /** Flags that are passed from a type parameter of a class to a refinement symbol * that sets the type parameter */ - final val RetainedTypeArgFlags = VarianceFlags | ExpandedName | Protected | Local + final val RetainedTypeArgFlags = VarianceFlags | Protected | Local /** Modules always have these flags set */ final val ModuleCreationFlags = ModuleVal | Lazy | Final | Stable @@ -502,7 +502,7 @@ object Flags { */ final val RetainedModuleValAndClassFlags: FlagSet = AccessFlags | Package | Case | - Synthetic | ExpandedName | JavaDefined | JavaStatic | Artifact | + Synthetic | JavaDefined | JavaStatic | Artifact | Erroneous | Lifted | MixedIn | Specialized /** Flags that can apply to a module val */ @@ -550,9 +550,6 @@ object Flags { /** A private accessor */ final val PrivateAccessor = allOf(Private, Accessor) - /** A type parameter with synthesized name */ - final val ExpandedTypeParam = allOf(ExpandedName, TypeParam) - /** An inline method */ final val InlineMethod = allOf(Inline, Method) @@ -578,7 +575,7 @@ object Flags { final val FinalOrInline = Final | Inline /** If symbol of a type alias has these flags, prefer the alias */ - final val AliasPreferred = TypeParam | BaseTypeArg | ExpandedName + final val AliasPreferred = TypeParam | BaseTypeArg /** A covariant type parameter instance */ final val LocalCovariant = allOf(Local, Covariant) diff --git a/compiler/src/dotty/tools/dotc/core/NameKinds.scala b/compiler/src/dotty/tools/dotc/core/NameKinds.scala new file mode 100644 index 000000000..0f08e4701 --- /dev/null +++ b/compiler/src/dotty/tools/dotc/core/NameKinds.scala @@ -0,0 +1,317 @@ +package dotty.tools +package dotc +package core + +import Names._ +import NameOps._ +import StdNames._ +import util.DotClass +import tasty.TastyFormat._ +import Decorators._ +import Contexts.Context +import collection.mutable + +object NameKinds { + + // These are sharable since all NameKinds are created eagerly at the start of the program + // before any concurrent threads are forked. for this to work, NameKinds should never + // be created lazily or in modules that start running after compilers are forked. + @sharable private val simpleNameKinds = new mutable.HashMap[Int, ClassifiedNameKind] + @sharable private val qualifiedNameKinds = new mutable.HashMap[Int, QualifiedNameKind] + @sharable private val uniqueNameKinds = new mutable.HashMap[String, UniqueNameKind] + + abstract class NameInfo extends DotClass { + def kind: NameKind + def mkString(underlying: TermName): String + def map(f: SimpleTermName => SimpleTermName): NameInfo = this + } + + abstract class NameKind(val tag: Int) extends DotClass { self => + type ThisInfo <: Info + class Info extends NameInfo { this: ThisInfo => + def kind = self + def mkString(underlying: TermName) = self.mkString(underlying, this) + override def toString = infoString + } + def definesNewName = false + def unmangle(name: SimpleTermName): TermName = name + def mkString(underlying: TermName, info: ThisInfo): String + def infoString: String + } + + object SimpleTermNameKind extends NameKind(UTF8) { self => + type ThisInfo = Info + val info = new Info + def mkString(underlying: TermName, info: ThisInfo) = unsupported("mkString") + def infoString = unsupported("infoString") + } + + abstract class ClassifiedNameKind(tag: Int, val infoString: String) extends NameKind(tag) { + type ThisInfo = Info + val info = new Info + def apply(qual: TermName) = + qual.derived(info) + def unapply(name: DerivedTermName): Option[TermName] = name match { + case DerivedTermName(underlying, `info`) => Some(underlying) + case _ => None + } + simpleNameKinds(tag) = this + } + + class PrefixNameKind(tag: Int, prefix: String, optInfoString: String = "") + extends ClassifiedNameKind(tag, if (optInfoString.isEmpty) s"Prefix $prefix" else optInfoString) { + def mkString(underlying: TermName, info: ThisInfo) = + underlying.mapLast(n => termName(prefix + n.toString)).toString + override def unmangle(name: SimpleTermName): TermName = + if (name.startsWith(prefix)) apply(name.drop(prefix.length).asSimpleName) + else name + } + + class SuffixNameKind(tag: Int, suffix: String, optInfoString: String = "") + extends ClassifiedNameKind(tag, if (optInfoString.isEmpty) s"Suffix $suffix" else optInfoString) { + def mkString(underlying: TermName, info: ThisInfo) = underlying.toString ++ suffix + override def unmangle(name: SimpleTermName): TermName = + if (name.endsWith(suffix)) apply(name.take(name.length - suffix.length).asSimpleName) + else name + } + + trait QualifiedInfo extends NameInfo { + val name: SimpleTermName + } + + class QualifiedNameKind(tag: Int, val separator: String) + extends NameKind(tag) { + type ThisInfo = QualInfo + case class QualInfo(val name: SimpleTermName) extends Info with QualifiedInfo { + override def map(f: SimpleTermName => SimpleTermName): NameInfo = new QualInfo(f(name)) + override def toString = s"$infoString $name" + } + def apply(qual: TermName, name: SimpleTermName): TermName = + qual.derived(new QualInfo(name)) + + /** Overloaded version used only for ExpandedName and TraitSetterName. + * Needed because the suffix of an expanded name may itself be expanded. + * For example, look at javap of scala.App.initCode + */ + def apply(qual: TermName, name: TermName): TermName = name rewrite { + case name: SimpleTermName => apply(qual, name) + case AnyQualifiedName(_, _) => apply(qual, name.toSimpleName) + } + + def unapply(name: DerivedTermName): Option[(TermName, SimpleTermName)] = name match { + case DerivedTermName(qual, info: this.QualInfo) => Some((qual, info.name)) + case _ => None + } + + override def definesNewName = true + + def mkString(underlying: TermName, info: ThisInfo) = + s"$underlying$separator${info.name}" + def infoString = s"Qualified $separator" + + qualifiedNameKinds(tag) = this + } + + object AnyQualifiedName { + def unapply(name: DerivedTermName): Option[(TermName, SimpleTermName)] = name match { + case DerivedTermName(qual, info: QualifiedInfo) => + Some((name.underlying, info.name)) + case _ => None + } + } + + trait NumberedInfo extends NameInfo { + def num: Int + } + + abstract class NumberedNameKind(tag: Int, val infoString: String) extends NameKind(tag) { self => + type ThisInfo = NumberedInfo + case class NumberedInfo(val num: Int) extends Info with NameKinds.NumberedInfo { + override def toString = s"$infoString $num" + } + def apply(qual: TermName, num: Int) = + qual.derived(new NumberedInfo(num)) + def unapply(name: DerivedTermName): Option[(TermName, Int)] = name match { + case DerivedTermName(underlying, info: this.NumberedInfo) => Some((underlying, info.num)) + case _ => None + } + protected def skipSeparatorAndNum(name: SimpleTermName, separator: String): Int = { + var i = name.length + while (i > 0 && name(i - 1).isDigit) i -= 1 + if (i > separator.length && i < name.length && + name.slice(i - separator.length, i).toString == separator) i + else -1 + } + } + + object AnyNumberedName { + def unapply(name: DerivedTermName): Option[(TermName, Int)] = name match { + case DerivedTermName(qual, info: NumberedInfo) => Some((qual, info.num)) + case _ => None + } + } + + case class UniqueNameKind(val separator: String) + extends NumberedNameKind(UNIQUE, s"Unique $separator") { + override def definesNewName = true + def mkString(underlying: TermName, info: ThisInfo) = { + val safePrefix = str.sanitize(underlying.toString + separator) + safePrefix + info.num + } + + def fresh(prefix: TermName = EmptyTermName)(implicit ctx: Context): TermName = + ctx.freshNames.newName(prefix, this) + + uniqueNameKinds(separator) = this + } + + object AnyUniqueName { + def unapply(name: DerivedTermName): Option[(TermName, String, Int)] = name match { + case DerivedTermName(qual, info: NumberedInfo) => + info.kind match { + case unique: UniqueNameKind => Some((qual, unique.separator, info.num)) + case _ => None + } + case _ => None + } + } + + val QualifiedName = new QualifiedNameKind(QUALIFIED, ".") + val FlatName = new QualifiedNameKind(FLATTENED, "$") + val ExpandPrefixName = new QualifiedNameKind(EXPANDPREFIX, "$") + + val ExpandedName = new QualifiedNameKind(EXPANDED, str.EXPAND_SEPARATOR) { + private val FalseSuper = termName("$$super") + private val FalseSuperLength = FalseSuper.length + + override def unmangle(name: SimpleTermName): TermName = { + var i = name.lastIndexOfSlice(str.EXPAND_SEPARATOR) + if (i < 0) name + else { + // Hack to make super accessors from traits work. They would otherwise fail because of #765 + // The problem is that in `x$$super$$plus` the expansion prefix needs to be `x` + // instead of `x$$super`. + if (i > FalseSuperLength && name.slice(i - FalseSuperLength, i) == FalseSuper) + i -= FalseSuper.length + + apply(name.take(i).asTermName, name.drop(i + str.EXPAND_SEPARATOR.length).asSimpleName) + } + } + } + + val TraitSetterName = new QualifiedNameKind(TRAITSETTER, str.TRAIT_SETTER_SEPARATOR) + + val UniqueName = new UniqueNameKind("$") { + override def mkString(underlying: TermName, info: ThisInfo) = + if (underlying.isEmpty) "$" + info.num + "$" else super.mkString(underlying, info) + } + + val InlineAccessorName = new UniqueNameKind("$_inlineAccessor_$") + val TempResultName = new UniqueNameKind("ev$") + val EvidenceParamName = new UniqueNameKind("evidence$") + val DepParamName = new UniqueNameKind("<param>") + val LazyImplicitName = new UniqueNameKind("$_lazy_implicit_$") + val LazyLocalName = new UniqueNameKind("$lzy") + val LazyLocalInitName = new UniqueNameKind("$lzyINIT") + val LazyFieldOffsetName = new UniqueNameKind("$OFFSET") + val LazyBitMapName = new UniqueNameKind(nme.BITMAP_PREFIX.toString) + val NonLocalReturnKeyName = new UniqueNameKind("nonLocalReturnKey") + val WildcardParamName = new UniqueNameKind("_$") + val TailLabelName = new UniqueNameKind("tailLabel") + val ExceptionBinderName = new UniqueNameKind("ex") + val SkolemName = new UniqueNameKind("?") + val LiftedTreeName = new UniqueNameKind("liftedTree") + + val UniqueExtMethName = new UniqueNameKind("$extension") { + override def unmangle(name: SimpleTermName): TermName = { + val i = skipSeparatorAndNum(name, separator) + if (i > 0) { + val index = name.drop(i).toString.toInt + var original = name.take(i - separator.length).asTermName + apply(original, index) + } + else name + } + } + + val PatMatStdBinderName = new UniqueNameKind("x") + val PatMatPiName = new UniqueNameKind("pi") // FIXME: explain what this is + val PatMatPName = new UniqueNameKind("p") // FIXME: explain what this is + val PatMatOName = new UniqueNameKind("o") // FIXME: explain what this is + val PatMatCaseName = new UniqueNameKind("case") + val PatMatMatchFailName = new UniqueNameKind("matchFail") + val PatMatSelectorName = new UniqueNameKind("selector") + + object DefaultGetterName extends NumberedNameKind(DEFAULTGETTER, "DefaultGetter") { + def mkString(underlying: TermName, info: ThisInfo) = { + val prefix = if (underlying.isConstructorName) nme.DEFAULT_GETTER_INIT else underlying + prefix.toString + str.DEFAULT_GETTER + (info.num + 1) + } + // TODO: Reduce code duplication with UniqueExtMethName + override def unmangle(name: SimpleTermName): TermName = { + val i = skipSeparatorAndNum(name, str.DEFAULT_GETTER) + if (i > 0) { + val index = name.drop(i).toString.toInt - 1 + var original = name.take(i - str.DEFAULT_GETTER.length).asTermName + if (original == nme.DEFAULT_GETTER_INIT) original = Names.CONSTRUCTOR + apply(original, index) + } + else name + } + } + + object VariantName extends NumberedNameKind(VARIANT, "Variant") { + val varianceToPrefix = Map(-1 -> '-', 0 -> '=', 1 -> '+') + def mkString(underlying: TermName, info: ThisInfo) = { + varianceToPrefix(info.num).toString + underlying + } + } + + /** Names of the form N_<outer>. Emitted by inliner, replaced by outer path + * in ExplicitOuter. + */ + object OuterSelectName extends NumberedNameKind(OUTERSELECT, "OuterSelect") { + def mkString(underlying: TermName, info: ThisInfo) = { + assert(underlying.isEmpty) + info.num + "_<outer>" + } + } + + val SuperAccessorName = new PrefixNameKind(SUPERACCESSOR, "super$") + val InitializerName = new PrefixNameKind(INITIALIZER, "initial$") + val ShadowedName = new PrefixNameKind(SHADOWED, "(shadowed)") + val ProtectedAccessorName = new PrefixNameKind(PROTECTEDACCESSOR, "protected$") + val ProtectedSetterName = new PrefixNameKind(PROTECTEDSETTER, "protected$set") // dubious encoding, kept for Scala2 compatibility + val AvoidClashName = new SuffixNameKind(AVOIDCLASH, "$_avoid_name_clash_$") + val DirectName = new SuffixNameKind(DIRECT, "$direct") + val FieldName = new SuffixNameKind(FIELD, "$$local") + val ExtMethName = new SuffixNameKind(EXTMETH, "$extension") + val ModuleVarName = new SuffixNameKind(OBJECTVAR, "$module") + val ModuleClassName = new SuffixNameKind(OBJECTCLASS, "$", optInfoString = "ModuleClass") + + object SignedName extends NameKind(63) { + + /** @param parts resultSig followed by paramsSig */ + case class SignedInfo(sig: Signature) extends Info { + override def toString = s"$infoString $sig" + } + type ThisInfo = SignedInfo + + def apply(qual: TermName, sig: Signature) = + qual.derived(new SignedInfo(sig)) + def unapply(name: DerivedTermName): Option[(TermName, Signature)] = name match { + case DerivedTermName(underlying, info: SignedInfo) => Some((underlying, info.sig)) + case _ => None + } + + def mkString(underlying: TermName, info: ThisInfo): String = unsupported("mkString") + def infoString: String = "Signed" + } + + val Scala2MethodNameKinds: List[NameKind] = + List(DefaultGetterName, ExtMethName, UniqueExtMethName, ProtectedAccessorName, ProtectedSetterName) + + def simpleNameKindOfTag : collection.Map[Int, ClassifiedNameKind] = simpleNameKinds + def qualifiedNameKindOfTag : collection.Map[Int, QualifiedNameKind] = qualifiedNameKinds + def uniqueNameKindOfSeparator: collection.Map[String, UniqueNameKind] = uniqueNameKinds +}
\ No newline at end of file diff --git a/compiler/src/dotty/tools/dotc/core/NameOps.scala b/compiler/src/dotty/tools/dotc/core/NameOps.scala index 240ad359b..915bd52ab 100644 --- a/compiler/src/dotty/tools/dotc/core/NameOps.scala +++ b/compiler/src/dotty/tools/dotc/core/NameOps.scala @@ -4,11 +4,12 @@ package core import java.security.MessageDigest import scala.annotation.switch import scala.io.Codec -import Names._, StdNames._, Contexts._, Symbols._, Flags._ +import Names._, StdNames._, Contexts._, Symbols._, Flags._, NameKinds._ import Decorators.PreNamedString import util.{Chars, NameTransformer} import Chars.isOperatorPart import Definitions._ +import config.Config object NameOps { @@ -48,212 +49,97 @@ object NameOps { } } - class PrefixNameExtractor(pre: TermName) { - def apply(name: TermName): TermName = pre ++ name - def unapply(name: TermName): Option[TermName] = - if (name startsWith pre) Some(name.drop(pre.length).asTermName) else None - } - - object SuperAccessorName extends PrefixNameExtractor(nme.SUPER_PREFIX) - object InitializerName extends PrefixNameExtractor(nme.INITIALIZER_PREFIX) - implicit class NameDecorator[N <: Name](val name: N) extends AnyVal { import nme._ - def likeTyped(n: PreName): N = + def testSimple(f: SimpleTermName => Boolean): Boolean = name match { + case name: SimpleTermName => f(name) + case name: TypeName => name.toTermName.testSimple(f) + case _ => false + } + + def likeSpaced(n: PreName): N = (if (name.isTermName) n.toTermName else n.toTypeName).asInstanceOf[N] def isConstructorName = name == CONSTRUCTOR || name == TRAIT_CONSTRUCTOR def isStaticConstructorName = name == STATIC_CONSTRUCTOR - def isExceptionResultName = name startsWith EXCEPTION_RESULT_PREFIX - def isImplClassName = name endsWith IMPL_CLASS_SUFFIX - def isLocalDummyName = name startsWith LOCALDUMMY_PREFIX - def isLoopHeaderLabel = (name startsWith WHILE_PREFIX) || (name startsWith DO_WHILE_PREFIX) - def isProtectedAccessorName = name startsWith PROTECTED_PREFIX - def isReplWrapperName = name containsSlice INTERPRETER_IMPORT_WRAPPER - def isTraitSetterName = name containsSlice TRAIT_SETTER_SEPARATOR - def isSetterName = name endsWith SETTER_SUFFIX - def isSingletonName = name endsWith SINGLETON_SUFFIX - def isModuleClassName = name endsWith MODULE_SUFFIX - def isAvoidClashName = name endsWith AVOID_CLASH_SUFFIX - def isImportName = name startsWith IMPORT - def isFieldName = name endsWith LOCAL_SUFFIX - def isShadowedName = name.length > 0 && name.head == '(' && name.startsWith(nme.SHADOWED) - def isDefaultGetterName = name.isTermName && name.asTermName.defaultGetterIndex >= 0 - def isScala2LocalSuffix = name.endsWith(" ") - def isModuleVarName(name: Name): Boolean = - name.stripAnonNumberSuffix endsWith MODULE_VAR_SUFFIX - def isSelectorName = name.startsWith(" ") && name.tail.forall(_.isDigit) - def isLazyLocal = name.endsWith(nme.LAZY_LOCAL) - def isOuterSelect = name.endsWith(nme.OUTER_SELECT) - def isInlineAccessor = name.startsWith(nme.INLINE_ACCESSOR_PREFIX) + def isLocalDummyName = name startsWith str.LOCALDUMMY_PREFIX + def isReplWrapperName = name.toString contains str.INTERPRETER_IMPORT_WRAPPER + def isSetterName = name endsWith str.SETTER_SUFFIX + def isScala2LocalSuffix = testSimple(_.endsWith(" ")) + def isSelectorName = testSimple(n => n.startsWith("_") && n.drop(1).forall(_.isDigit)) /** Is name a variable name? */ - def isVariableName: Boolean = name.length > 0 && { - val first = name.head - (((first.isLower && first.isLetter) || first == '_') - && (name != false_) - && (name != true_) - && (name != null_)) + def isVariableName: Boolean = testSimple { n => + n.length > 0 && { + val first = n.head + (((first.isLower && first.isLetter) || first == '_') + && (n != false_) + && (n != true_) + && (n != null_)) + } } def isOpAssignmentName: Boolean = name match { case raw.NE | raw.LE | raw.GE | EMPTY => false - case _ => + case name: SimpleTermName => name.length > 0 && name.last == '=' && name.head != '=' && isOperatorPart(name.head) - } - - /** If the name ends with $nn where nn are - * all digits, strip the $ and the digits. - * Otherwise return the argument. - */ - def stripAnonNumberSuffix: Name = { - var pos = name.length - while (pos > 0 && name(pos - 1).isDigit) - pos -= 1 - - if (pos > 0 && pos < name.length && name(pos - 1) == '$') - name take (pos - 1) - else - name + case _ => + false } /** Convert this module name to corresponding module class name */ - def moduleClassName: TypeName = (name ++ tpnme.MODULE_SUFFIX).toTypeName + def moduleClassName: TypeName = name.derived(ModuleClassName).toTypeName /** Convert this module class name to corresponding source module name */ - def sourceModuleName: TermName = stripModuleClassSuffix.toTermName - - /** If name ends in module class suffix, drop it */ - def stripModuleClassSuffix: Name = - if (isModuleClassName) name dropRight MODULE_SUFFIX.length else name - - /** Append a suffix so that this name does not clash with another name in the same scope */ - def avoidClashName: TermName = (name ++ AVOID_CLASH_SUFFIX).toTermName + def sourceModuleName: TermName = name.toTermName.exclude(ModuleClassName) - /** If name ends in "avoid clash" suffix, drop it */ - def stripAvoidClashSuffix: Name = - if (isAvoidClashName) name dropRight AVOID_CLASH_SUFFIX.length else name + /** If name ends in module class suffix, drop it. This + * method needs to work on mangled as well as unmangled names because + * it is also called from the backend. + */ + def stripModuleClassSuffix: N = likeSpaced { + val semName = + if (name.isSimple && name.endsWith("$")) name.unmangleClassName else name + semName.exclude(ModuleClassName) + } /** If flags is a ModuleClass but not a Package, add module class suffix */ - def adjustIfModuleClass(flags: Flags.FlagSet): N = { + def adjustIfModuleClass(flags: Flags.FlagSet): N = likeSpaced { if (flags is (ModuleClass, butNot = Package)) name.asTypeName.moduleClassName - else stripAvoidClashSuffix - }.asInstanceOf[N] - - /** The superaccessor for method with given name */ - def superName: TermName = (nme.SUPER_PREFIX ++ name).toTermName - - /** The expanded name of `name` relative to given class `base`. - */ - def expandedName(base: Symbol, separator: Name)(implicit ctx: Context): N = - expandedName(if (base is Flags.ExpandedName) base.name else base.fullNameSeparated("$"), separator) - - def expandedName(base: Symbol)(implicit ctx: Context): N = expandedName(base, nme.EXPAND_SEPARATOR) - - /** The expanded name of `name` relative to `basename` with given `separator` - */ - def expandedName(prefix: Name, separator: Name = nme.EXPAND_SEPARATOR): N = - name.fromName(prefix ++ separator ++ name).asInstanceOf[N] - - def expandedName(prefix: Name): N = expandedName(prefix, nme.EXPAND_SEPARATOR) - - /** Revert the expanded name. Note: This currently gives incorrect results - * if the normal name contains `nme.EXPAND_SEPARATOR`, i.e. two consecutive '$' - * signs. This can happen for instance if a super accessor is paired with - * an encoded name, e.g. super$$plus$eq. See #765. - */ - def unexpandedName: N = { - var idx = name.lastIndexOfSlice(nme.EXPAND_SEPARATOR) - - // Hack to make super accessors from traits work. They would otherwise fail because of #765 - // TODO: drop this once we have more robust name handling - if (idx > FalseSuperLength && name.slice(idx - FalseSuperLength, idx) == FalseSuper) - idx -= FalseSuper.length - - if (idx < 0) name else (name drop (idx + nme.EXPAND_SEPARATOR.length)).asInstanceOf[N] + else name.toTermName.exclude(AvoidClashName) } - def expandedPrefix: N = { - val idx = name.lastIndexOfSlice(nme.EXPAND_SEPARATOR) - assert(idx >= 0) - name.take(idx).asInstanceOf[N] + def expandedName(base: Symbol, kind: QualifiedNameKind = ExpandedName)(implicit ctx: Context): N = { + val prefix = + if (base.name.is(ExpandedName)) base.name else base.fullNameSeparated(ExpandPrefixName) + likeSpaced { kind(prefix.toTermName, name.toTermName) } } - def shadowedName: N = likeTyped(nme.SHADOWED ++ name) - - def revertShadowed: N = likeTyped(name.drop(nme.SHADOWED.length)) - - def implClassName: N = likeTyped(name ++ tpnme.IMPL_CLASS_SUFFIX) + /** Revert the expanded name. */ + def unexpandedName: N = likeSpaced { + name.rewrite { case ExpandedName(_, unexp) => unexp } + } - def errorName: N = likeTyped(name ++ nme.ERROR) + def implClassName: N = likeSpaced(name ++ tpnme.IMPL_CLASS_SUFFIX) - def directName: N = likeTyped(name ++ DIRECT_SUFFIX) + def errorName: N = likeSpaced(name ++ nme.ERROR) - def freshened(implicit ctx: Context): N = - likeTyped( - if (name.isModuleClassName) name.stripModuleClassSuffix.freshened.moduleClassName - else likeTyped(ctx.freshName(name ++ NameTransformer.NAME_JOIN_STRING))) /** Name with variance prefix: `+` for covariant, `-` for contravariant */ def withVariance(v: Int): N = - if (hasVariance) dropVariance.withVariance(v) - else v match { - case -1 => likeTyped('-' +: name) - case 1 => likeTyped('+' +: name) - case 0 => name - } - - /** Does name have a `+`/`-` variance prefix? */ - def hasVariance: Boolean = - name.nonEmpty && name.head == '+' || name.head == '-' - - /** Drop variance prefix if name has one */ - def dropVariance: N = if (hasVariance) likeTyped(name.tail) else name + likeSpaced { VariantName(name.exclude(VariantName).toTermName, v) } /** The variance as implied by the variance prefix, or 0 if there is * no variance prefix. */ - def variance = name.head match { - case '-' => -1 - case '+' => 1 - case _ => 0 - } + def variance = name.collect { case VariantName(_, n) => n }.getOrElse(0) - /** Translate a name into a list of simple TypeNames and TermNames. - * In all segments before the last, type/term is determined by whether - * the following separator char is '.' or '#'. The last segment - * is of the same type as the original name. - * - * Examples: - * - * package foo { - * object Lorax { object Wog ; class Wog } - * class Lorax { object Zax ; class Zax } - * } - * - * f("foo.Lorax".toTermName) == List("foo": Term, "Lorax": Term) // object Lorax - * f("foo.Lorax".toTypeName) == List("foo": Term, "Lorax": Type) // class Lorax - * f("Lorax.Wog".toTermName) == List("Lorax": Term, "Wog": Term) // object Wog - * f("Lorax.Wog".toTypeName) == List("Lorax": Term, "Wog": Type) // class Wog - * f("Lorax#Zax".toTermName) == List("Lorax": Type, "Zax": Term) // object Zax - * f("Lorax#Zax".toTypeName) == List("Lorax": Type, "Zax": Type) // class Zax - * - * Note that in actual scala syntax you cannot refer to object Zax without an - * instance of Lorax, so Lorax#Zax could only mean the type. One might think - * that Lorax#Zax.type would work, but this is not accepted by the parser. - * For the purposes of referencing that object, the syntax is allowed. - */ - def segments: List[Name] = { - def mkName(name: Name, follow: Char): Name = - if (follow == '.') name.toTermName else name.toTypeName - - name.indexWhere(ch => ch == '.' || ch == '#') match { - case -1 => - if (name.isEmpty) scala.Nil else name :: scala.Nil - case idx => - mkName(name take idx, name(idx)) :: (name drop (idx + 1)).segments + def freshened(implicit ctx: Context): N = likeSpaced { + name.toTermName match { + case ModuleClassName(original) => ModuleClassName(original.freshened) + case name => UniqueName.fresh(name) } } @@ -263,7 +149,7 @@ object NameOps { * - (-1) otherwise */ def functionArity: Int = - functionArityFor(tpnme.Function) max functionArityFor(tpnme.ImplicitFunction) + functionArityFor(str.Function) max functionArityFor(str.ImplicitFunction) /** Is a function name * - FunctionN for N >= 0 @@ -276,7 +162,7 @@ object NameOps { * - ImplicitFunctionN for N >= 0 * - false otherwise */ - def isImplicitFunction: Boolean = functionArityFor(tpnme.ImplicitFunction) >= 0 + def isImplicitFunction: Boolean = functionArityFor(str.ImplicitFunction) >= 0 /** Is a synthetic function name * - FunctionN for N > 22 @@ -284,24 +170,18 @@ object NameOps { * - false otherwise */ def isSyntheticFunction: Boolean = { - functionArityFor(tpnme.Function) > MaxImplementedFunctionArity || - functionArityFor(tpnme.ImplicitFunction) >= 0 + functionArityFor(str.Function) > MaxImplementedFunctionArity || + functionArityFor(str.ImplicitFunction) >= 0 } /** Parsed function arity for function with some specific prefix */ - private def functionArityFor(prefix: Name): Int = { + private def functionArityFor(prefix: String): Int = { if (name.startsWith(prefix)) try name.toString.substring(prefix.length).toInt catch { case _: NumberFormatException => -1 } else -1 } - /** The number of hops specified in an outer-select name */ - def outerSelectHops: Int = { - require(isOuterSelect) - name.dropRight(nme.OUTER_SELECT.length).toString.toInt - } - /** The name of the generic runtime operation corresponding to an array operation */ def genericArrayOp: TermName = name match { case nme.apply => nme.array_apply @@ -338,100 +218,61 @@ object NameOps { val methodTags: Seq[Name] = (methodTargs zip methodTarsNames).sortBy(_._2).map(x => typeToTag(x._1)) val classTags: Seq[Name] = (classTargs zip classTargsNames).sortBy(_._2).map(x => typeToTag(x._1)) - name.fromName(name ++ nme.specializedTypeNames.prefix ++ + name.likeSpaced(name ++ nme.specializedTypeNames.prefix ++ methodTags.fold(nme.EMPTY)(_ ++ _) ++ nme.specializedTypeNames.separator ++ classTags.fold(nme.EMPTY)(_ ++ _) ++ nme.specializedTypeNames.suffix) } /** If name length exceeds allowable limit, replace part of it by hash */ def compactified(implicit ctx: Context): TermName = termName(compactify(name.toString)) - } - // needed??? - private val Boxed = Map[TypeName, TypeName]( - tpnme.Boolean -> jtpnme.BoxedBoolean, - tpnme.Byte -> jtpnme.BoxedByte, - tpnme.Char -> jtpnme.BoxedCharacter, - tpnme.Short -> jtpnme.BoxedShort, - tpnme.Int -> jtpnme.BoxedInteger, - tpnme.Long -> jtpnme.BoxedLong, - tpnme.Float -> jtpnme.BoxedFloat, - tpnme.Double -> jtpnme.BoxedDouble) + def unmangleClassName: N = name.toTermName match { + case name: SimpleTermName + if name.endsWith(str.MODULE_SUFFIX) && !nme.falseModuleClassNames.contains(name) => + likeSpaced(name.dropRight(str.MODULE_SUFFIX.length).moduleClassName) + case _ => name + } + + def unmangle(kind: NameKind): N = likeSpaced { + name rewrite { + case unmangled: SimpleTermName => + kind.unmangle(unmangled) + case ExpandedName(prefix, last) => + kind.unmangle(last) rewrite { + case kernel: SimpleTermName => + ExpandedName(prefix, kernel) + } + } + } + + def unmangle(kinds: List[NameKind]): N = { + val unmangled = (name /: kinds)(_.unmangle(_)) + if (unmangled eq name) name else unmangled.unmangle(kinds) + } + } implicit class TermNameDecorator(val name: TermName) extends AnyVal { import nme._ - def setterName: TermName = - if (name.isFieldName) name.fieldToGetter.setterName - else name ++ SETTER_SUFFIX + def setterName: TermName = name.exclude(FieldName) ++ str.SETTER_SUFFIX def getterName: TermName = - if (name.isFieldName) fieldToGetter - else setterToGetter + name.exclude(FieldName).mapLast(n => + if (n.endsWith(str.SETTER_SUFFIX)) n.take(n.length - str.SETTER_SUFFIX.length).asSimpleName + else n) def fieldName: TermName = if (name.isSetterName) { - if (name.isTraitSetterName) { - // has form <$-separated-trait-name>$_setter_$ `name`_$eq - val start = name.indexOfSlice(TRAIT_SETTER_SEPARATOR) + TRAIT_SETTER_SEPARATOR.length - val end = name.indexOfSlice(SETTER_SUFFIX) - name.slice(start, end) ++ LOCAL_SUFFIX - } else getterName.fieldName + if (name.is(TraitSetterName)) { + val TraitSetterName(_, original) = name + original.fieldName + } + else getterName.fieldName } - else name ++ LOCAL_SUFFIX - - private def setterToGetter: TermName = { - assert(name.endsWith(SETTER_SUFFIX), name + " is referenced as a setter but has wrong name format") - name.take(name.length - SETTER_SUFFIX.length).asTermName - } - - def fieldToGetter: TermName = { - assert(name.isFieldName) - name.take(name.length - LOCAL_SUFFIX.length).asTermName - } - - /** Nominally, name$default$N, encoded for <init> - * @param Post the parameters position. - * @note Default getter name suffixes start at 1, so `pos` has to be adjusted by +1 - */ - def defaultGetterName(pos: Int): TermName = { - val prefix = if (name.isConstructorName) DEFAULT_GETTER_INIT else name - prefix ++ DEFAULT_GETTER ++ (pos + 1).toString - } - - /** Nominally, name from name$default$N, CONSTRUCTOR for <init> */ - def defaultGetterToMethod: TermName = { - val p = name.indexOfSlice(DEFAULT_GETTER) - if (p >= 0) { - val q = name.take(p).asTermName - // i.e., if (q.decoded == CONSTRUCTOR.toString) CONSTRUCTOR else q - if (q == DEFAULT_GETTER_INIT) CONSTRUCTOR else q - } else name - } - - /** If this is a default getter, its index (starting from 0), else -1 */ - def defaultGetterIndex: Int = { - var i = name.length - while (i > 0 && name(i - 1).isDigit) i -= 1 - if (i > 0 && i < name.length && name.take(i).endsWith(DEFAULT_GETTER)) - name.drop(i).toString.toInt - 1 - else - -1 - } + else FieldName(name) def stripScala2LocalSuffix: TermName = - if (name.isScala2LocalSuffix) name.init.asTermName else name - - /** The name of an accessor for protected symbols. */ - def protectedAccessorName: TermName = - PROTECTED_PREFIX ++ name.unexpandedName - - /** The name of a setter for protected symbols. Used for inherited Java fields. */ - def protectedSetterName: TermName = - PROTECTED_SET_PREFIX ++ name.unexpandedName - - def moduleVarName: TermName = - name ++ MODULE_VAR_SUFFIX + if (name.isScala2LocalSuffix) name.asSimpleName.dropRight(1) else name /** The name unary_x for a prefix operator x */ def toUnaryName: TermName = name match { @@ -441,65 +282,5 @@ object NameOps { case raw.BANG => UNARY_! case _ => name } - - /** The name of a method which stands in for a primitive operation - * during structural type dispatch. - */ - def primitiveInfixMethodName: TermName = name match { - case OR => takeOr - case XOR => takeXor - case AND => takeAnd - case EQ => testEqual - case NE => testNotEqual - case ADD => add - case SUB => subtract - case MUL => multiply - case DIV => divide - case MOD => takeModulo - case LSL => shiftSignedLeft - case LSR => shiftLogicalRight - case ASR => shiftSignedRight - case LT => testLessThan - case LE => testLessOrEqualThan - case GE => testGreaterOrEqualThan - case GT => testGreaterThan - case ZOR => takeConditionalOr - case ZAND => takeConditionalAnd - case _ => NO_NAME - } - - /** Postfix/prefix, really. - */ - def primitivePostfixMethodName: TermName = name match { - case UNARY_! => takeNot - case UNARY_+ => positive - case UNARY_- => negate - case UNARY_~ => complement - case `toByte` => toByte - case `toShort` => toShort - case `toChar` => toCharacter - case `toInt` => toInteger - case `toLong` => toLong - case `toFloat` => toFloat - case `toDouble` => toDouble - case _ => NO_NAME - } - - def primitiveMethodName: TermName = - primitiveInfixMethodName match { - case NO_NAME => primitivePostfixMethodName - case name => name - } - - def lazyLocalName = name ++ nme.LAZY_LOCAL - def nonLazyName = { - assert(name.isLazyLocal) - name.dropRight(nme.LAZY_LOCAL.length) - } - - def inlineAccessorName = nme.INLINE_ACCESSOR_PREFIX ++ name ++ "$" } - - private final val FalseSuper = "$$super".toTermName - private val FalseSuperLength = FalseSuper.length } diff --git a/compiler/src/dotty/tools/dotc/core/Names.scala b/compiler/src/dotty/tools/dotc/core/Names.scala index 11f0b55a8..a72a02844 100644 --- a/compiler/src/dotty/tools/dotc/core/Names.scala +++ b/compiler/src/dotty/tools/dotc/core/Names.scala @@ -10,13 +10,16 @@ import Decorators._ import Contexts.Context import collection.IndexedSeqOptimized import collection.generic.CanBuildFrom -import collection.mutable.{ Builder, StringBuilder } +import collection.mutable.{ Builder, StringBuilder, AnyRefMap } import collection.immutable.WrappedString import collection.generic.CanBuildFrom -import util.DotClass +import util.{DotClass, SimpleMap} +import java.util.HashMap + //import annotation.volatile object Names { + import NameKinds._ /** A common class for things that can be turned into names. * Instances are both names and strings, the latter via a decorator. @@ -37,20 +40,11 @@ object Names { * 3. Names are intended to be encoded strings. @see dotc.util.NameTransformer. * The encoding will be applied when converting a string to a name. */ - abstract class Name extends DotClass - with PreName - with collection.immutable.Seq[Char] - with IndexedSeqOptimized[Char, Name] { + abstract class Name extends DotClass with PreName { /** A type for names of the same kind as this name */ type ThisName <: Name - /** The start index in the character array */ - val start: Int - - /** The length of the names */ - override val length: Int - /** Is this name a type name? */ def isTypeName: Boolean @@ -69,120 +63,244 @@ object Names { /** This name downcasted to a term name */ def asTermName: TermName - /** Create a new name of same kind as this one, in the given - * basis, with `len` characters taken from `cs` starting at `offset`. - */ - def fromChars(cs: Array[Char], offset: Int, len: Int): ThisName + def isSimple: Boolean + def asSimpleName: SimpleTermName + def toSimpleName: SimpleTermName + def mangled: Name - /** Create new name of same kind as this name and with same - * characters as given `name`. - */ - def fromName(name: Name): ThisName = fromChars(chrs, name.start, name.length) + def rewrite(f: PartialFunction[Name, Name]): ThisName + def collect[T](f: PartialFunction[Name, T]): Option[T] + def mapLast(f: SimpleTermName => SimpleTermName): ThisName + def mapParts(f: SimpleTermName => SimpleTermName): ThisName - /** Create new name of same kind as this name with characters from - * the given string + /** A name in the same (term or type) namespace as this name and + * with same characters as given `name`. */ - def fromString(str: String): ThisName = { - val cs = str.toCharArray - fromChars(cs, 0, cs.length) - } + def likeSpaced(name: Name): ThisName - override def toString = - if (length == 0) "" else new String(chrs, start, length) + def derived(info: NameInfo): ThisName + def derived(kind: ClassifiedNameKind): ThisName = derived(kind.info) + def exclude(kind: NameKind): ThisName + def is(kind: NameKind): Boolean + def debugString: String def toText(printer: Printer): Text = printer.toText(this) - /** Write to UTF8 representation of this name to given character array. - * Start copying to index `to`. Return index of next free byte in array. - * Array must have enough remaining space for all bytes - * (i.e. maximally 3*length bytes). - */ - final def copyUTF8(bs: Array[Byte], offset: Int): Int = { - val bytes = Codec.toUTF8(chrs, start, length) - scala.compat.Platform.arraycopy(bytes, 0, bs, offset, bytes.length) - offset + bytes.length - } - /** Replace \$op_name's by corresponding operator symbols. */ - def decode: Name = - if (contains('$')) fromString(NameTransformer.decode(toString)) - else this + def decode: Name /** Replace operator symbols by corresponding \$op_name's. */ - def encode: Name = - if (dontEncode(toTermName)) this else NameTransformer.encode(this) + def encode: Name + + def firstPart: SimpleTermName + def lastPart: SimpleTermName /** A more efficient version of concatenation */ def ++ (other: Name): ThisName = ++ (other.toString) + def ++ (other: String): ThisName = mapLast(n => termName(n.toString + other)) + def replace(from: Char, to: Char): ThisName = mapParts(_.replace(from, to)) + + def isEmpty: Boolean + + def startsWith(str: String): Boolean = firstPart.startsWith(str) + def endsWith(str: String): Boolean = lastPart.endsWith(str) + + override def hashCode = System.identityHashCode(this) + override def equals(that: Any) = this eq that.asInstanceOf[AnyRef] + } - def ++ (other: String): ThisName = { - val s = toString + other - fromChars(s.toCharArray, 0, s.length) + abstract class TermName extends Name { + type ThisName = TermName + def isTypeName = false + def isTermName = true + def toTermName = this + def asTypeName = throw new ClassCastException(this + " is not a type name") + def asTermName = this + + @sharable // because it is only modified in the synchronized block of toTypeName. + @volatile private[this] var _typeName: TypeName = null + + def toTypeName: TypeName = { + if (_typeName == null) + synchronized { + if (_typeName == null) + _typeName = new TypeName(this) + } + _typeName } - def replace(from: Char, to: Char): ThisName = { - val cs = new Array[Char](length) - Array.copy(chrs, start, cs, 0, length) - for (i <- 0 until length) { - if (cs(i) == from) cs(i) = to + def likeSpaced(name: Name): TermName = name.toTermName + + def info: NameInfo = SimpleTermNameKind.info + def underlying: TermName = unsupported("underlying") + + @sharable // because of synchronized block in `and` + private var derivedNames: AnyRef /* SimpleMap | j.u.HashMap */ = + SimpleMap.Empty[NameInfo] + + private def getDerived(info: NameInfo): DerivedTermName /* | Null */= derivedNames match { + case derivedNames: SimpleMap[NameInfo, DerivedTermName] @unchecked => + derivedNames(info) + case derivedNames: HashMap[NameInfo, DerivedTermName] @unchecked => + derivedNames.get(info) + } + + private def putDerived(info: NameInfo, name: DerivedTermName): name.type = { + derivedNames match { + case derivedNames: SimpleMap[NameInfo, DerivedTermName] @unchecked => + if (derivedNames.size < 4) + this.derivedNames = derivedNames.updated(info, name) + else { + val newMap = new HashMap[NameInfo, DerivedTermName] + derivedNames.foreachBinding(newMap.put(_, _)) + newMap.put(info, name) + this.derivedNames = newMap + } + case derivedNames: HashMap[NameInfo, DerivedTermName] @unchecked => + derivedNames.put(info, name) + } + name + } + + private def add(info: NameInfo): TermName = synchronized { + getDerived(info) match { + case null => putDerived(info, new DerivedTermName(this, info)) + case derivedName => derivedName + } + } + + private def rewrap(underlying: TermName) = + if (underlying eq this.underlying) this else underlying.add(info) + + /** Return derived name with given `info` and the current + * name as underlying name. + */ + def derived(info: NameInfo): TermName = { + val thisKind = this.info.kind + val thatKind = info.kind + if (thisKind.tag < thatKind.tag || thatKind.definesNewName) add(info) + else if (thisKind.tag > thatKind.tag) rewrap(underlying.derived(info)) + else { + assert(info == this.info) + this } - fromChars(cs, 0, length) } + def exclude(kind: NameKind): TermName = { + val thisKind = this.info.kind + if (thisKind.tag < kind.tag || thisKind.definesNewName) this + else if (thisKind.tag > kind.tag) rewrap(underlying.exclude(kind)) + else underlying + } + + def is(kind: NameKind): Boolean = { + val thisKind = this.info.kind + thisKind == kind || + !thisKind.definesNewName && thisKind.tag > kind.tag && underlying.is(kind) + } + } + + class SimpleTermName(val start: Int, val length: Int, @sharable private[Names] var next: SimpleTermName) extends TermName { + // `next` is @sharable because it is only modified in the synchronized block of termName. + + def apply(n: Int) = chrs(start + n) + + def exists(p: Char => Boolean): Boolean = { + var i = 0 + while (i < length && !p(chrs(start + i))) i += 1 + i < length + } + + def forall(p: Char => Boolean) = !exists(!p(_)) + def contains(ch: Char): Boolean = { var i = 0 while (i < length && chrs(start + i) != ch) i += 1 i < length } - def firstChar = chrs(start) + def isEmpty = length == 0 + + override def startsWith(str: String): Boolean = { + var i = 0 + while (i < str.length && i < length && apply(i) == str(i)) i += 1 + i == str.length + } + + override def endsWith(str: String): Boolean = { + var i = 1 + while (i <= str.length && i <= length && apply(length - i) == str(str.length - i)) i += 1 + i > str.length + } - // ----- Collections integration ------------------------------------- + def lastIndexOf(ch: Char, start: Int = length - 1): Int = { + var i = start + while (i >= 0 && apply(i) != ch) i -= 1 + i + } - override protected[this] def thisCollection: WrappedString = new WrappedString(repr.toString) - override protected[this] def toCollection(repr: Name): WrappedString = new WrappedString(repr.toString) + def lastIndexOfSlice(str: String): Int = toString.lastIndexOfSlice(str) - override protected[this] def newBuilder: Builder[Char, Name] = unsupported("newBuilder") + override def replace(from: Char, to: Char): SimpleTermName = { + val cs = new Array[Char](length) + Array.copy(chrs, start, cs, 0, length) + for (i <- 0 until length) { + if (cs(i) == from) cs(i) = to + } + termName(cs, 0, length) + } - override def apply(index: Int): Char = chrs(start + index) + def slice(from: Int, until: Int): SimpleTermName = { + assert(0 <= from && from <= until && until <= length) + termName(chrs, start + from, until - from) + } - override def slice(from: Int, until: Int): ThisName = - fromChars(chrs, start + from, until - from) + def drop(n: Int) = slice(n, length) + def take(n: Int) = slice(0, n) + def dropRight(n: Int) = slice(0, length - n) + def takeRight(n: Int) = slice(length - n, length) - override def equals(that: Any) = this eq that.asInstanceOf[AnyRef] + def head = apply(0) + def last = apply(length - 1) - override def seq = toCollection(this) - } + def isSimple = true + def asSimpleName = this + def toSimpleName = this + def mangled = this - class TermName(val start: Int, val length: Int, @sharable private[Names] var next: TermName) extends Name { - // `next` is @sharable because it is only modified in the synchronized block of termName. - type ThisName = TermName - def isTypeName = false - def isTermName = true + def rewrite(f: PartialFunction[Name, Name]): ThisName = + if (f.isDefinedAt(this)) likeSpaced(f(this)) else this + def collect[T](f: PartialFunction[Name, T]): Option[T] = f.lift(this) + def mapLast(f: SimpleTermName => SimpleTermName) = f(this) + def mapParts(f: SimpleTermName => SimpleTermName) = f(this) - @sharable // because it is only modified in the synchronized block of toTypeName. - @volatile private[this] var _typeName: TypeName = null + def encode: SimpleTermName = + if (dontEncode(toTermName)) this else NameTransformer.encode(this) - def toTypeName: TypeName = { - if (_typeName == null) - synchronized { - if (_typeName == null) - _typeName = new TypeName(start, length, this) - } - _typeName - } - def toTermName = this - def asTypeName = throw new ClassCastException(this + " is not a type name") - def asTermName = this + /** Replace \$op_name's by corresponding operator symbols. */ + def decode: SimpleTermName = + if (contains('$')) termName(NameTransformer.decode(toString)) else this + + def firstPart = this + def lastPart = this override def hashCode: Int = start - override protected[this] def newBuilder: Builder[Char, Name] = termNameBuilder + override def toString = + if (length == 0) "" else new String(chrs, start, length) - def fromChars(cs: Array[Char], offset: Int, len: Int): TermName = termName(cs, offset, len) + def debugString: String = toString } - class TypeName(val start: Int, val length: Int, val toTermName: TermName) extends Name { + class TypeName(val toTermName: TermName) extends Name { + + def isEmpty = toTermName.isEmpty + + def encode = toTermName.encode.toTypeName + def decode = toTermName.decode.toTypeName + def firstPart = toTermName.firstPart + def lastPart = toTermName.lastPart + type ThisName = TypeName def isTypeName = true def isTermName = false @@ -190,12 +308,77 @@ object Names { def asTypeName = this def asTermName = throw new ClassCastException(this + " is not a term name") - override def hashCode: Int = -start + def isSimple = toTermName.isSimple + def asSimpleName = toTermName.asSimpleName + def toSimpleName = toTermName.toSimpleName + def mangled = toTermName.toSimpleName.toTypeName - override protected[this] def newBuilder: Builder[Char, Name] = - termNameBuilder.mapResult(_.toTypeName) + def rewrite(f: PartialFunction[Name, Name]): ThisName = toTermName.rewrite(f).toTypeName + def collect[T](f: PartialFunction[Name, T]): Option[T] = toTermName.collect(f) + def mapLast(f: SimpleTermName => SimpleTermName) = toTermName.mapLast(f).toTypeName + def mapParts(f: SimpleTermName => SimpleTermName) = toTermName.mapParts(f).toTypeName - def fromChars(cs: Array[Char], offset: Int, len: Int): TypeName = typeName(cs, offset, len) + def likeSpaced(name: Name): TypeName = name.toTypeName + + def derived(info: NameInfo): TypeName = toTermName.derived(info).toTypeName + def exclude(kind: NameKind): TypeName = toTermName.exclude(kind).toTypeName + def is(kind: NameKind) = toTermName.is(kind) + + override def toString = toTermName.toString + override def debugString = toTermName.debugString + "/T" + } + + /** A term name that's derived from an `underlying` name and that + * adds `info` to it. + */ + case class DerivedTermName(override val underlying: TermName, override val info: NameInfo) + extends TermName { + def isEmpty = false + def encode: Name = underlying.encode.derived(info.map(_.encode)) + def decode: Name = underlying.decode.derived(info.map(_.decode)) + def firstPart = underlying.firstPart + def lastPart = info match { + case qual: QualifiedInfo => qual.name + case _ => underlying.lastPart + } + override def toString = info.mkString(underlying) + override def debugString = s"${underlying.debugString}[$info]" + + def isSimple = false + def asSimpleName = throw new UnsupportedOperationException(s"$debugString is not a simple name") + + private[this] var simpleName: SimpleTermName = null + def toSimpleName = { + if (simpleName == null) simpleName = termName(toString) + simpleName + } + def mangled = toSimpleName + + def rewrite(f: PartialFunction[Name, Name]): ThisName = + if (f.isDefinedAt(this)) likeSpaced(f(this)) + else info match { + case qual: QualifiedInfo => this + case _ => underlying.rewrite(f).derived(info) + } + + def collect[T](f: PartialFunction[Name, T]): Option[T] = + if (f.isDefinedAt(this)) Some(f(this)) + else info match { + case qual: QualifiedInfo => None + case _ => underlying.collect(f) + } + + def mapLast(f: SimpleTermName => SimpleTermName): ThisName = + info match { + case qual: QualifiedInfo => underlying.derived(qual.map(f)) + case _ => underlying.mapLast(f).derived(info) + } + + def mapParts(f: SimpleTermName => SimpleTermName): ThisName = + info match { + case qual: QualifiedInfo => underlying.mapParts(f).derived(qual.map(f)) + case _ => underlying.mapParts(f).derived(info) + } } // Nametable @@ -214,7 +397,7 @@ object Names { /** Hashtable for finding term names quickly. */ @sharable // because it's only mutated in synchronized block of termName - private var table = new Array[TermName](InitialHashSize) + private var table = new Array[SimpleTermName](InitialHashSize) /** The number of defined names. */ @sharable // because it's only mutated in synchronized block of termName @@ -242,7 +425,7 @@ object Names { /** Create a term name from the characters in cs[offset..offset+len-1]. * Assume they are already encoded. */ - def termName(cs: Array[Char], offset: Int, len: Int): TermName = synchronized { + def termName(cs: Array[Char], offset: Int, len: Int): SimpleTermName = synchronized { util.Stats.record("termName") val h = hashValue(cs, offset, len) & (table.size - 1) @@ -266,7 +449,7 @@ object Names { } /** Rehash chain of names */ - def rehash(name: TermName): Unit = + def rehash(name: SimpleTermName): Unit = if (name != null) { val oldNext = name.next val h = hashValue(chrs, name.start, name.length) & (table.size - 1) @@ -280,7 +463,7 @@ object Names { size += 1 if (size.toDouble / table.size > fillFactor) { val oldTable = table - table = new Array[TermName](table.size * 2) + table = new Array[SimpleTermName](table.size * 2) for (i <- 0 until oldTable.size) rehash(oldTable(i)) } } @@ -292,7 +475,7 @@ object Names { return name name = name.next } - name = new TermName(nc, len, next) + name = new SimpleTermName(nc, len, next) enterChars() table(h) = name incTableSize() @@ -308,7 +491,7 @@ object Names { /** Create a term name from the UTF8 encoded bytes in bs[offset..offset+len-1]. * Assume they are already encoded. */ - def termName(bs: Array[Byte], offset: Int, len: Int): TermName = { + def termName(bs: Array[Byte], offset: Int, len: Int): SimpleTermName = { val chars = Codec.fromUTF8(bs, offset, len) termName(chars, 0, chars.length) } @@ -320,53 +503,75 @@ object Names { termName(bs, offset, len).toTypeName /** Create a term name from a string, without encoding operators */ - def termName(s: String): TermName = termName(s.toCharArray, 0, s.length) + def termName(s: String): SimpleTermName = termName(s.toCharArray, 0, s.length) /** Create a type name from a string, without encoding operators */ def typeName(s: String): TypeName = typeName(s.toCharArray, 0, s.length) - /** The term name represented by the empty string */ - val EmptyTermName = new TermName(-1, 0, null) + table(0) = new SimpleTermName(-1, 0, null) - table(0) = EmptyTermName + /** The term name represented by the empty string */ + val EmptyTermName: TermName = table(0) /** The type name represented by the empty string */ val EmptyTypeName = EmptyTermName.toTypeName // can't move CONSTRUCTOR/EMPTY_PACKAGE to `nme` because of bootstrap failures in `encode`. - val CONSTRUCTOR = termName("<init>") - val STATIC_CONSTRUCTOR = termName("<clinit>") - val EMPTY_PACKAGE = termName("<empty>") - - val dontEncode = Set(CONSTRUCTOR, EMPTY_PACKAGE) + val CONSTRUCTOR: TermName = termName("<init>") + val STATIC_CONSTRUCTOR: TermName = termName("<clinit>") + val EMPTY_PACKAGE: TermName = termName("<empty>") + val REFINEMENT: TermName = termName("<refinement>") - def termNameBuilder: Builder[Char, TermName] = - StringBuilder.newBuilder.mapResult(termName) - - implicit val nameCanBuildFrom: CanBuildFrom[Name, Char, Name] = new CanBuildFrom[Name, Char, Name] { - def apply(from: Name): Builder[Char, Name] = - StringBuilder.newBuilder.mapResult(s => from.fromChars(s.toCharArray, 0, s.length)) - def apply(): Builder[Char, Name] = termNameBuilder - } + val dontEncode = Set(CONSTRUCTOR, EMPTY_PACKAGE, REFINEMENT) implicit val NameOrdering: Ordering[Name] = new Ordering[Name] { + private def compareInfos(x: NameInfo, y: NameInfo): Int = + if (x.kind.tag != y.kind.tag) x.kind.tag - y.kind.tag + else x match { + case x: QualifiedInfo => + y match { + case y: QualifiedInfo => + compareSimpleNames(x.name, y.name) + } + case x: NumberedInfo => + y match { + case y: NumberedInfo => + x.num - y.num + } + case _ => + assert(x == y) + 0 + } + private def compareSimpleNames(x: SimpleTermName, y: SimpleTermName): Int = { + val until = x.length min y.length + var i = 0 + while (i < until && x(i) == y(i)) i = i + 1 + if (i < until) { + if (x(i) < y(i)) -1 + else /*(x(i) > y(i))*/ 1 + } else { + x.length - y.length + } + } + private def compareTermNames(x: TermName, y: TermName): Int = x match { + case x: SimpleTermName => + y match { + case y: SimpleTermName => compareSimpleNames(x, y) + case _ => -1 + } + case DerivedTermName(xPre, xInfo) => + y match { + case DerivedTermName(yPre, yInfo) => + val s = compareInfos(xInfo, yInfo) + if (s == 0) compareTermNames(xPre, yPre) else s + case _ => 1 + } + } def compare(x: Name, y: Name): Int = { if (x.isTermName && y.isTypeName) 1 else if (x.isTypeName && y.isTermName) -1 else if (x eq y) 0 - else { - val until = x.length min y.length - var i = 0 - - while (i < until && x(i) == y(i)) i = i + 1 - - if (i < until) { - if (x(i) < y(i)) -1 - else /*(x(i) > y(i))*/ 1 - } else { - x.length - y.length - } - } + else compareTermNames(x.toTermName, y.toTermName) } } } diff --git a/compiler/src/dotty/tools/dotc/core/Scopes.scala b/compiler/src/dotty/tools/dotc/core/Scopes.scala index 6090079e5..205798474 100644 --- a/compiler/src/dotty/tools/dotc/core/Scopes.scala +++ b/compiler/src/dotty/tools/dotc/core/Scopes.scala @@ -32,7 +32,7 @@ object Scopes { * This value must be a power of two, so that the index of an element can * be computed as element.hashCode & (hashTable.length - 1) */ - private final val MinHash = 8 + final val MinHashedScopeSize = 8 /** The maximal permissible number of recursions when creating * a hashtable @@ -60,7 +60,7 @@ object Scopes { * or to delete them. These methods are provided by subclass * MutableScope. */ - abstract class Scope extends DotClass with printing.Showable with Iterable[Symbol] { + abstract class Scope extends DotClass with printing.Showable { /** The last scope-entry from which all others are reachable via `prev` */ private[dotc] def lastEntry: ScopeEntry @@ -76,18 +76,37 @@ object Scopes { /** The symbols in this scope in the order they were entered; * inherited from outer ones first. */ - def toList: List[Symbol] + def toList(implicit ctx: Context): List[Symbol] /** Return all symbols as an iterator in the order they were entered in this scope. */ - def iterator: Iterator[Symbol] = toList.iterator + def iterator(implicit ctx: Context): Iterator[Symbol] = toList.iterator + + /** Is the scope empty? */ + def isEmpty: Boolean = lastEntry eq null + + def foreach[U](p: Symbol => U)(implicit ctx: Context): Unit = toList foreach p + + def filter(p: Symbol => Boolean)(implicit ctx: Context): List[Symbol] = { + ensureComplete() + var syms: List[Symbol] = Nil + var e = lastEntry + while ((e ne null) && e.owner == this) { + val sym = e.sym + if (p(sym)) syms = sym :: syms + e = e.prev + } + syms + } + + def find(p: Symbol => Boolean)(implicit ctx: Context): Symbol = filter(p) match { + case sym :: _ => sym + case _ => NoSymbol + } /** Returns a new mutable scope with the same content as this one. */ def cloneScope(implicit ctx: Context): MutableScope - /** Is the scope empty? */ - override def isEmpty: Boolean = lastEntry eq null - /** Lookup a symbol entry matching given name. */ def lookupEntry(name: Name)(implicit ctx: Context): ScopeEntry @@ -144,6 +163,12 @@ object Scopes { final def toText(printer: Printer): Text = printer.toText(this) def checkConsistent()(implicit ctx: Context) = () + + /** Ensure that all elements of this scope have been entered. + * Overridden by SymbolLoaders.PackageLoader#PackageScope, where it + * makes sure that all names with `$`'s have been added. + */ + protected def ensureComplete()(implicit ctx: Context): Unit = () } /** A subclass of Scope that defines methods for entering and @@ -155,9 +180,10 @@ object Scopes { class MutableScope protected[Scopes](initElems: ScopeEntry, initSize: Int, val nestingLevel: Int = 0) extends Scope { + /** Scope shares elements with `base` */ protected[Scopes] def this(base: Scope)(implicit ctx: Context) = { this(base.lastEntry, base.size, base.nestingLevel + 1) - ensureCapacity(MinHash)(ctx) // WTH? it seems the implicit is not in scope for a secondary constructor call. + ensureCapacity(MinHashedScopeSize)(ctx) // WTH? it seems the implicit is not in scope for a secondary constructor call. } def this() = this(null, 0, 0) @@ -178,6 +204,8 @@ object Scopes { */ private var elemsCache: List[Symbol] = null + protected def newScopeLikeThis() = new MutableScope() + /** Clone scope, taking care not to force the denotations of any symbols in the scope. */ def cloneScope(implicit ctx: Context): MutableScope = { @@ -187,7 +215,7 @@ object Scopes { entries += e e = e.prev } - val scope = newScope + val scope = newScopeLikeThis() for (i <- entries.length - 1 to 0 by -1) { val e = entries(i) scope.newScopeEntry(e.name, e.sym) @@ -197,7 +225,7 @@ object Scopes { /** create and enter a scope entry with given name and symbol */ protected def newScopeEntry(name: Name, sym: Symbol)(implicit ctx: Context): ScopeEntry = { - ensureCapacity(if (hashTable ne null) hashTable.length else MinHash) + ensureCapacity(if (hashTable ne null) hashTable.length else MinHashedScopeSize) val e = new ScopeEntry(name, sym, this) e.prev = lastEntry lastEntry = e @@ -338,8 +366,9 @@ object Scopes { /** Returns all symbols as a list in the order they were entered in this scope. * Does _not_ include the elements of inherited scopes. */ - override final def toList: List[Symbol] = { + override final def toList(implicit ctx: Context): List[Symbol] = { if (elemsCache eq null) { + ensureComplete() elemsCache = Nil var e = lastEntry while ((e ne null) && e.owner == this) { @@ -351,6 +380,7 @@ object Scopes { } override def implicitDecls(implicit ctx: Context): List[TermRef] = { + ensureComplete() var irefs = new mutable.ListBuffer[TermRef] var e = lastEntry while (e ne null) { @@ -365,25 +395,13 @@ object Scopes { /** Vanilla scope - symbols are stored in declaration order. */ - final def sorted: List[Symbol] = toList - - override def foreach[U](p: Symbol => U): Unit = toList foreach p - - override def filter(p: Symbol => Boolean): List[Symbol] = { - var syms: List[Symbol] = Nil - var e = lastEntry - while ((e ne null) && e.owner == this) { - val sym = e.sym - if (p(sym)) syms = sym :: syms - e = e.prev - } - syms - } + final def sorted(implicit ctx: Context): List[Symbol] = toList override def openForMutations: MutableScope = this /** Check that all symbols in this scope are in their correct hashtable buckets. */ override def checkConsistent()(implicit ctx: Context) = { + ensureComplete() var e = lastEntry while (e != null) { var e1 = lookupEntry(e.name) @@ -407,9 +425,6 @@ object Scopes { scope } - /** Create new scope for the members of package `pkg` */ - def newPackageScope(pkgClass: Symbol): MutableScope = newScope - /** Transform scope of members of `owner` using operation `op` * This is overridden by the reflective compiler to avoid creating new scopes for packages */ @@ -425,7 +440,7 @@ object Scopes { override private[dotc] def lastEntry = null override def size = 0 override def nestingLevel = 0 - override def toList = Nil + override def toList(implicit ctx: Context) = Nil override def cloneScope(implicit ctx: Context): MutableScope = unsupported("cloneScope") override def lookupEntry(name: Name)(implicit ctx: Context): ScopeEntry = null override def lookupNextEntry(entry: ScopeEntry)(implicit ctx: Context): ScopeEntry = null diff --git a/compiler/src/dotty/tools/dotc/core/Signature.scala b/compiler/src/dotty/tools/dotc/core/Signature.scala index fcd1e2376..4699cecf2 100644 --- a/compiler/src/dotty/tools/dotc/core/Signature.scala +++ b/compiler/src/dotty/tools/dotc/core/Signature.scala @@ -34,6 +34,14 @@ import scala.annotation.tailrec case class Signature(paramsSig: List[TypeName], resSig: TypeName) { import Signature._ +/* FIXME does not compile under dotty, we get a missing param error + def checkUnqual(name: TypeName) = name mapParts { part => + assert(!part.contains('.'), name) + part + } + paramsSig.foreach(checkUnqual) + checkUnqual(resSig) +*/ /** Two names are consistent if they are the same or one of them is tpnme.Uninstantiated */ private def consistent(name1: TypeName, name2: TypeName) = name1 == name2 || name1 == tpnme.Uninstantiated || name2 == tpnme.Uninstantiated diff --git a/compiler/src/dotty/tools/dotc/core/StdNames.scala b/compiler/src/dotty/tools/dotc/core/StdNames.scala index e7928fd09..92befdacb 100644 --- a/compiler/src/dotty/tools/dotc/core/StdNames.scala +++ b/compiler/src/dotty/tools/dotc/core/StdNames.scala @@ -15,6 +15,34 @@ object StdNames { /** Base strings from which synthetic names are derived. */ + object str { + final val SETTER_SUFFIX = "_$eq" + final val EXPAND_SEPARATOR = "$$" + final val TRAIT_SETTER_SEPARATOR = "$_setter_$" + final val SUPER_PREFIX = "super$" + final val INITIALIZER_PREFIX = "initial$" + final val SHADOWED_PREFIX = "(shadowed)" + final val AVOID_CLASH_SUFFIX = "$_avoid_name_clash_$" + final val MODULE_SUFFIX = NameTransformer.MODULE_SUFFIX_STRING + final val DEFAULT_GETTER = "$default$" + final val LOCALDUMMY_PREFIX = "<local " // owner of local blocks + final val ANON_CLASS = "$anon" + final val ANON_FUN = "$anonfun" + + final val INTERPRETER_IMPORT_WRAPPER = "$iw" + final val INTERPRETER_LINE_PREFIX = "line" + final val INTERPRETER_VAR_PREFIX = "res" + final val INTERPRETER_WRAPPER_SUFFIX = "$object" + + final val Function = "Function" + final val ImplicitFunction = "ImplicitFunction" + final val AbstractFunction = "AbstractFunction" + final val Tuple = "Tuple" + final val Product = "Product" + + def sanitize(str: String) = str.replaceAll("""[<>]""", """\$""") + } + abstract class DefinedNames[N <: Name] { protected implicit def fromString(s: String): N protected def fromName(name: Name): N = fromString(name.toString) @@ -84,44 +112,30 @@ object StdNames { final val HASHkw: N = kw("#") final val ATkw: N = kw("@") - val ANON_CLASS: N = "$anon" - val ANON_FUN: N = "$anonfun" - val BITMAP_PREFIX: N = "bitmap$" + val ANON_CLASS: N = str.ANON_CLASS + val ANON_FUN: N = str.ANON_FUN + val BITMAP_PREFIX: N = "bitmap$" // @darkdimius: $bitmap? Also, the next 4 names are unused. val BITMAP_NORMAL: N = BITMAP_PREFIX // initialization bitmap for public/protected lazy vals val BITMAP_TRANSIENT: N = BITMAP_PREFIX + "trans$" // initialization bitmap for transient lazy vals val BITMAP_CHECKINIT: N = BITMAP_PREFIX + "init$" // initialization bitmap for checkinit values val BITMAP_CHECKINIT_TRANSIENT: N = BITMAP_PREFIX + "inittrans$" // initialization bitmap for transient checkinit values - val DEFAULT_GETTER: N = "$default$" - val DEFAULT_GETTER_INIT: N = NameTransformer.encode("<init>") + val DEFAULT_GETTER: N = str.DEFAULT_GETTER + val DEFAULT_GETTER_INIT: N = "$lessinit$greater" val DO_WHILE_PREFIX: N = "doWhile$" + val DOLLAR_VALUES: N = "$values" + val DOLLAR_NEW: N = "$new" val EMPTY: N = "" val EMPTY_PACKAGE: N = Names.EMPTY_PACKAGE.toString - val EVIDENCE_PARAM_PREFIX: N = "evidence$" - val DEP_PARAM_PREFIX = "<param>" val EXCEPTION_RESULT_PREFIX: N = "exceptionResult" - val EXPAND_SEPARATOR: N = "$$" + val EXPAND_SEPARATOR: N = str.EXPAND_SEPARATOR val IMPL_CLASS_SUFFIX: N = "$class" val IMPORT: N = "<import>" - val INLINE_ACCESSOR_PREFIX = "$inlineAccessor$" - val INTERPRETER_IMPORT_WRAPPER: N = "$iw" - val INTERPRETER_LINE_PREFIX: N = "line" - val INTERPRETER_VAR_PREFIX: N = "res" - val INTERPRETER_WRAPPER_SUFFIX: N = "$object" - val LOCALDUMMY_PREFIX: N = "<local " // owner of local blocks val MODULE_SUFFIX: N = NameTransformer.MODULE_SUFFIX_STRING - val AVOID_CLASH_SUFFIX: N = "$_avoid_name_clash_$" - val MODULE_VAR_SUFFIX: N = "$module" val NAME_JOIN: N = NameTransformer.NAME_JOIN_STRING - val USCORE_PARAM_PREFIX: N = "_$" val OPS_PACKAGE: N = "<special-ops>" val OVERLOADED: N = "<overloaded>" val PACKAGE: N = "package" - val PACKAGE_CLS: N = "package$" - val PROTECTED_PREFIX: N = "protected$" - val PROTECTED_SET_PREFIX: N = PROTECTED_PREFIX + "set" val ROOT: N = "<root>" - val SHADOWED: N = "(shadowed)" // tag to be used until we have proper name kinds - val SINGLETON_SUFFIX: N = ".type" val SPECIALIZED_SUFFIX: N = "$sp" val SUPER_PREFIX: N = "super$" val WHILE_PREFIX: N = "while$" @@ -129,11 +143,7 @@ object StdNames { val INITIALIZER_PREFIX: N = "initial$" val COMPANION_MODULE_METHOD: N = "companion$module" val COMPANION_CLASS_METHOD: N = "companion$class" - val TRAIT_SETTER_SEPARATOR: N = "$_setter_$" - val DIRECT_SUFFIX: N = "$direct" - val LAZY_IMPLICIT_PREFIX: N = "$lazy_implicit$" - val DOLLAR_VALUES: N = "$values" - val DOLLAR_NEW: N = "$new" + val TRAIT_SETTER_SEPARATOR: N = str.TRAIT_SETTER_SEPARATOR // value types (and AnyRef) are all used as terms as well // as (at least) arguments to the @specialize annotation. @@ -167,7 +177,6 @@ object StdNames { // fictions we use as both types and terms final val ERROR: N = "<error>" - final val ERRORenc: N = encode("<error>") final val NO_NAME: N = "<none>" // formerly NOSYMBOL final val WILDCARD: N = "_" @@ -181,23 +190,18 @@ object StdNames { final val REIFY_TREECREATOR_PREFIX: N = "$treecreator" final val REIFY_TYPECREATOR_PREFIX: N = "$typecreator" - final val AbstractFunction: N = "AbstractFunction" final val Any: N = "Any" final val AnyVal: N = "AnyVal" final val ExprApi: N = "ExprApi" - final val Function: N = "Function" - final val ImplicitFunction: N = "ImplicitFunction" final val Mirror: N = "Mirror" final val Nothing: N = "Nothing" final val Null: N = "Null" final val Object: N = "Object" final val PartialFunction: N = "PartialFunction" final val PrefixType: N = "PrefixType" - final val Product: N = "Product" final val Serializable: N = "Serializable" final val Singleton: N = "Singleton" final val Throwable: N = "Throwable" - final val Tuple: N = "Tuple" final val ClassfileAnnotation: N = "ClassfileAnnotation" final val ClassManifest: N = "ClassManifest" @@ -241,11 +245,8 @@ object StdNames { val EVT2U: N = "evt2u$" val EQEQ_LOCAL_VAR: N = "eqEqTemp$" val FAKE_LOCAL_THIS: N = "this$" - val LAZY_LOCAL: N = "$lzy" - val LAZY_LOCAL_INIT: N = "$lzyINIT" val LAZY_FIELD_OFFSET: N = "OFFSET$" val LAZY_SLOW_SUFFIX: N = "$lzycompute" - val LOCAL_SUFFIX: N = "$$local" val UNIVERSE_BUILD_PREFIX: N = "$u.build." val UNIVERSE_BUILD: N = "$u.build" val UNIVERSE_PREFIX: N = "$u." @@ -259,13 +260,10 @@ object StdNames { val REIFY_SYMDEF_PREFIX: N = "symdef$" val MODULE_INSTANCE_FIELD: N = NameTransformer.MODULE_INSTANCE_NAME // "MODULE$" val OUTER: N = "$outer" - val OUTER_LOCAL: N = "$outer " - val OUTER_SELECT: N = "_<outer>" // emitted by inliner, replaced by outer path in explicitouter val REFINE_CLASS: N = "<refinement>" val ROOTPKG: N = "_root_" val SELECTOR_DUMMY: N = "<unapply-selector>" val SELF: N = "$this" - val SETTER_SUFFIX: N = encode("_=") val SKOLEM: N = "<skolem>" val SPECIALIZED_INSTANCE: N = "specInstance$" val THIS: N = "_$this" @@ -440,7 +438,6 @@ object StdNames { val lang: N = "lang" val length: N = "length" val lengthCompare: N = "lengthCompare" - val liftedTree: N = "liftedTree" val `macro` : N = "macro" val macroThis : N = "_this" val macroContext : N = "c" @@ -458,7 +455,6 @@ object StdNames { val ne: N = "ne" val newFreeTerm: N = "newFreeTerm" val newFreeType: N = "newFreeType" - val newNestedSymbol: N = "newNestedSymbol" val newScopeWith: N = "newScopeWith" val next: N = "next" val nmeNewTermName: N = "newTermName" @@ -541,6 +537,11 @@ object StdNames { val synthSwitch: N = "$synthSwitch" val _scope: N = "$scope" + val nothingClass: N = "Nothing$" + val nullClass: N = "Null$" + + val falseModuleClassNames = Set(nothingClass, nullClass, nothingRuntimeClass, nullRuntimeClass) + // unencoded operators object raw { final val AMP : N = "&" @@ -661,22 +662,6 @@ object StdNames { val isBoxedNumberOrBoolean: N = "isBoxedNumberOrBoolean" val isBoxedNumber: N = "isBoxedNumber" - - val reflPolyCacheName: N = "reflPoly$Cache" - val reflClassCacheName: N = "reflClass$Cache" - val reflParamsCacheName: N = "reflParams$Cache" - val reflMethodCacheName: N = "reflMethod$Cache" - val reflMethodName: N = "reflMethod$Method" - - private val reflectionCacheNames = Set[N]( - reflPolyCacheName, - reflClassCacheName, - reflParamsCacheName, - reflMethodCacheName, - reflMethodName - ) - - def isReflectionCacheName(name: Name) = reflectionCacheNames exists (name startsWith _) } class ScalaTermNames extends ScalaNames[TermName] { @@ -723,7 +708,7 @@ object StdNames { } def localDummyName(clazz: Symbol)(implicit ctx: Context): TermName = - LOCALDUMMY_PREFIX ++ clazz.name ++ ">" + termName(str.LOCALDUMMY_PREFIX + clazz.name + ">") def newBitmapName(bitmapPrefix: TermName, n: Int): TermName = bitmapPrefix ++ n.toString diff --git a/compiler/src/dotty/tools/dotc/core/SymDenotations.scala b/compiler/src/dotty/tools/dotc/core/SymDenotations.scala index 602848a50..1e0beb5f3 100644 --- a/compiler/src/dotty/tools/dotc/core/SymDenotations.scala +++ b/compiler/src/dotty/tools/dotc/core/SymDenotations.scala @@ -4,7 +4,7 @@ package core import Periods._, Contexts._, Symbols._, Denotations._, Names._, NameOps._, Annotations._ import Types._, Flags._, Decorators._, DenotTransformers._, StdNames._, Scopes._, Comments._ -import NameOps._ +import NameOps._, NameKinds._ import Scopes.Scope import collection.mutable import collection.immutable.BitSet @@ -107,7 +107,7 @@ object SymDenotations { class SymDenotation private[SymDenotations] ( symbol: Symbol, ownerIfExists: Symbol, - final val name: Name, + initName: Name, initFlags: FlagSet, initInfo: Type, initPrivateWithin: Symbol = NoSymbol) extends SingleDenotation(symbol) { @@ -125,11 +125,18 @@ object SymDenotations { // ------ Getting and setting fields ----------------------------- + private[this] var myName = initName private[this] var myFlags: FlagSet = adaptFlags(initFlags) private[this] var myInfo: Type = initInfo private[this] var myPrivateWithin: Symbol = initPrivateWithin private[this] var myAnnotations: List[Annotation] = Nil + /** The name of the symbol */ + def name = myName + + /** Update the name; only called when unpickling top-level classes */ + def name_=(n: Name) = myName = n + /** The owner of the symbol; overridden in NoDenotation */ def owner: Symbol = ownerIfExists @@ -252,7 +259,7 @@ object SymDenotations { */ def effectiveName(implicit ctx: Context) = if (this is ModuleClass) name.stripModuleClassSuffix - else name.stripAvoidClashSuffix + else name.exclude(AvoidClashName) /** The privateWithin boundary, NoSymbol if no boundary is given. */ @@ -367,7 +374,7 @@ object SymDenotations { /** The expanded name of this denotation. */ final def expandedName(implicit ctx: Context) = - if (is(ExpandedName) || isConstructor) name + if (name.is(ExpandedName) || isConstructor) name else { def legalize(name: Name): Name = // JVM method names may not contain `<' or `>' characters if (is(Method)) name.replace('<', '(').replace('>', ')') else name @@ -377,49 +384,50 @@ object SymDenotations { // might have been moved from different origins into the same class /** The name with which the denoting symbol was created */ - final def originalName(implicit ctx: Context) = { - val d = initial - if (d is ExpandedName) d.name.unexpandedName else d.name // !!!DEBUG, was: effectiveName - } + final def originalName(implicit ctx: Context) = + initial.effectiveName /** The encoded full path name of this denotation, where outer names and inner names * are separated by `separator` strings. * Never translates expansions of operators back to operator symbol. - * Drops package objects. Represents terms in the owner chain by a simple `~`. + * Drops package objects. Represents each term in the owner chain by a simple `~`. * (Note: scalac uses nothing to represent terms, which can cause name clashes * between same-named definitions in different enclosing methods. Before this commit * we used `$' but this can cause ambiguities with the class separator '$'). * A separator "" means "flat name"; the real separator in this case is "$" and * enclosing packages do not form part of the name. */ - def fullNameSeparated(separator: String)(implicit ctx: Context): Name = { - var sep = separator - var stopAtPackage = false - if (sep.isEmpty) { - sep = "$" - stopAtPackage = true - } + def fullNameSeparated(kind: QualifiedNameKind)(implicit ctx: Context): Name = if (symbol == NoSymbol || owner == NoSymbol || owner.isEffectiveRoot || - stopAtPackage && owner.is(PackageClass)) name + kind == FlatName && owner.is(PackageClass)) name else { + var filler = "" var encl = owner while (!encl.isClass && !encl.isPackageObject) { encl = encl.owner - sep += "~" + filler += "~" + } + var prefix = encl.fullNameSeparated(kind) + if (kind.separator == "$") + // duplicate scalac's behavior: don't write a double '$$' for module class members. + prefix = prefix.exclude(ModuleClassName) + def qualify(n: SimpleTermName) = + kind(prefix.toTermName, if (filler.isEmpty) n else termName(filler + n)) + val fn = name rewrite { + case name: SimpleTermName => qualify(name) + case name @ AnyQualifiedName(_, _) => qualify(name.toSimpleName) } - if (owner.is(ModuleClass, butNot = Package) && sep == "$") sep = "" // duplicate scalac's behavior: don't write a double '$$' for module class members. - val fn = encl.fullNameSeparated(separator) ++ sep ++ name if (isType) fn.toTypeName else fn.toTermName } - } + /** The encoded flat name of this denotation, where joined names are separated by `separator` characters. */ - def flatName(implicit ctx: Context): Name = fullNameSeparated("") + def flatName(implicit ctx: Context): Name = fullNameSeparated(FlatName) /** `fullName` where `.' is the separator character */ - def fullName(implicit ctx: Context): Name = fullNameSeparated(".") + def fullName(implicit ctx: Context): Name = fullNameSeparated(QualifiedName) // ----- Tests ------------------------------------------------- @@ -460,13 +468,13 @@ object SymDenotations { /** Is this symbol an anonymous class? */ final def isAnonymousClass(implicit ctx: Context): Boolean = - isClass && (initial.name startsWith tpnme.ANON_CLASS) + isClass && (initial.name startsWith str.ANON_CLASS) final def isAnonymousFunction(implicit ctx: Context) = - this.symbol.is(Method) && (initial.name startsWith nme.ANON_FUN) + this.symbol.is(Method) && (initial.name startsWith str.ANON_FUN) final def isAnonymousModuleVal(implicit ctx: Context) = - this.symbol.is(ModuleVal) && (initial.name startsWith nme.ANON_CLASS) + this.symbol.is(ModuleVal) && (initial.name startsWith str.ANON_CLASS) /** Is this a companion class method or companion object method? * These methods are generated by Symbols#synthesizeCompanionMethod @@ -505,8 +513,10 @@ object SymDenotations { /** Is this symbol a package object or its module class? */ def isPackageObject(implicit ctx: Context): Boolean = { - val poName = if (isType) nme.PACKAGE_CLS else nme.PACKAGE - (name.toTermName == poName) && (owner is Package) && (this is Module) + val nameMatches = + if (isType) name == tpnme.PACKAGE.moduleClassName + else name == nme.PACKAGE + nameMatches && (owner is Package) && (this is Module) } /** Is this symbol an abstract type? */ @@ -922,14 +932,15 @@ object SymDenotations { * and which is also defined in the same scope and compilation unit. * NoSymbol if this class does not exist. */ - final def companionClass(implicit ctx: Context): Symbol = { - val companionMethod = info.decls.denotsNamed(nme.COMPANION_CLASS_METHOD, selectPrivate).first - - if (companionMethod.exists) - companionMethod.info.resultType.classSymbol - else - NoSymbol - } + final def companionClass(implicit ctx: Context): Symbol = + if (is(Package)) NoSymbol + else { + val companionMethod = info.decls.denotsNamed(nme.COMPANION_CLASS_METHOD, selectPrivate).first + if (companionMethod.exists) + companionMethod.info.resultType.classSymbol + else + NoSymbol + } final def scalacLinkedClass(implicit ctx: Context): Symbol = if (this is ModuleClass) companionNamed(effectiveName.toTypeName) @@ -1198,9 +1209,7 @@ object SymDenotations { /** If denotation is private, remove the Private flag and expand the name if necessary */ def ensureNotPrivate(implicit ctx: Context) = if (is(Private)) - copySymDenotation( - name = expandedName, - initFlags = this.flags &~ Private | ExpandedName) + copySymDenotation(name = expandedName, initFlags = this.flags &~ Private) else this } @@ -1209,18 +1218,19 @@ object SymDenotations { class ClassDenotation private[SymDenotations] ( symbol: Symbol, ownerIfExists: Symbol, - name: Name, + initName: Name, initFlags: FlagSet, initInfo: Type, initPrivateWithin: Symbol, initRunId: RunId) - extends SymDenotation(symbol, ownerIfExists, name, initFlags, initInfo, initPrivateWithin) { + extends SymDenotation(symbol, ownerIfExists, initName, initFlags, initInfo, initPrivateWithin) { import util.LRUCache // ----- denotation fields and accessors ------------------------------ - if (initFlags is (Module, butNot = Package)) assert(name.isModuleClassName, s"module naming inconsistency: $name") + if (initFlags is (Module, butNot = Package)) + assert(name.is(ModuleClassName), s"module naming inconsistency: ${name.debugString}") /** The symbol asserted to have type ClassSymbol */ def classSymbol: ClassSymbol = symbol.asInstanceOf[ClassSymbol] @@ -1493,6 +1503,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 +1523,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. */ @@ -1533,8 +1540,8 @@ object SymDenotations { !(this is Frozen) || (scope ne this.unforcedDecls) || sym.hasAnnotation(defn.ScalaStaticAnnot) || - sym.name.isInlineAccessor || - isUsecase) + sym.name.is(InlineAccessorName) || + isUsecase, i"trying to enter $sym in $this, frozen = ${this is Frozen}") scope.enter(sym) @@ -1755,13 +1762,13 @@ object SymDenotations { } } - private[this] var fullNameCache: SimpleMap[String, Name] = SimpleMap.Empty - override final def fullNameSeparated(separator: String)(implicit ctx: Context): Name = { - val cached = fullNameCache(separator) + private[this] var fullNameCache: SimpleMap[QualifiedNameKind, Name] = SimpleMap.Empty + override final def fullNameSeparated(kind: QualifiedNameKind)(implicit ctx: Context): Name = { + val cached = fullNameCache(kind) if (cached != null) cached else { - val fn = super.fullNameSeparated(separator) - fullNameCache = fullNameCache.updated(separator, fn) + val fn = super.fullNameSeparated(kind) + fullNameCache = fullNameCache.updated(kind, fn) fn } } @@ -1773,8 +1780,8 @@ object SymDenotations { def constrNamed(cname: TermName) = info.decls.denotsNamed(cname).last.symbol // denotsNamed returns Symbols in reverse order of occurrence if (this.is(ImplClass)) constrNamed(nme.TRAIT_CONSTRUCTOR) // ignore normal constructor - else - constrNamed(nme.CONSTRUCTOR).orElse(constrNamed(nme.TRAIT_CONSTRUCTOR)) + else if (this.is(Package)) NoSymbol + else constrNamed(nme.CONSTRUCTOR).orElse(constrNamed(nme.TRAIT_CONSTRUCTOR)) } /** The parameter accessors of this class. Term and type accessors, @@ -1800,7 +1807,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 +1830,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 +1866,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/SymbolLoaders.scala b/compiler/src/dotty/tools/dotc/core/SymbolLoaders.scala index 79f8a6a45..e4d2d446f 100644 --- a/compiler/src/dotty/tools/dotc/core/SymbolLoaders.scala +++ b/compiler/src/dotty/tools/dotc/core/SymbolLoaders.scala @@ -14,6 +14,7 @@ import Contexts._, Symbols._, Flags._, SymDenotations._, Types._, Scopes._, util import StdNames._, NameOps._ import Decorators.{PreNamedString, StringInterpolators} import classfile.ClassfileParser +import util.Stats import scala.util.control.NonFatal object SymbolLoaders { @@ -148,23 +149,79 @@ class SymbolLoaders { override def sourceModule(implicit ctx: Context) = _sourceModule def description = "package loader " + classpath.name - private[core] val currentDecls: MutableScope = newScope + private var enterFlatClasses: Option[Context => Unit] = None + + Stats.record("package scopes") + + /** The scope of a package. This is different from a normal scope + * in three aspects: + * + * 1. Names of scope entries are kept in mangled form. + * 2. Some function types in the `scala` package are synthesized. + */ + final class PackageScope extends MutableScope { + override def newScopeEntry(name: Name, sym: Symbol)(implicit ctx: Context): ScopeEntry = + super.newScopeEntry(name.mangled, sym) + + override def lookupEntry(name: Name)(implicit ctx: Context): ScopeEntry = { + val mangled = name.mangled + val e = super.lookupEntry(mangled) + if (e != null) e + else if (_sourceModule.initialDenot.name == nme.scala_ && _sourceModule == defn.ScalaPackageVal && + name.isTypeName && name.isSyntheticFunction) + newScopeEntry(defn.newFunctionNTrait(name.asTypeName)) + else if (isFlatName(mangled.toSimpleName) && enterFlatClasses.isDefined) { + Stats.record("package scopes with flatnames entered") + enterFlatClasses.get(ctx) + lookupEntry(name) + } + else e + } + + override def ensureComplete()(implicit ctx: Context) = + for (enter <- enterFlatClasses) enter(ctx) + + override def newScopeLikeThis() = new PackageScope + } + + private[core] val currentDecls: MutableScope = new PackageScope() + + def isFlatName(name: SimpleTermName) = name.lastIndexOf('$', name.length - 2) >= 0 + + def isFlatName(classRep: ClassPath#ClassRep) = { + val idx = classRep.name.indexOf('$') + idx >= 0 && idx < classRep.name.length - 1 + } + + def maybeModuleClass(classRep: ClassPath#ClassRep) = classRep.name.last == '$' + + private def enterClasses(root: SymDenotation, flat: Boolean)(implicit ctx: Context) = { + def isAbsent(classRep: ClassPath#ClassRep) = + !root.unforcedDecls.lookup(classRep.name.toTypeName).exists + + if (!root.isRoot) { + for (classRep <- classpath.classes) + if (!maybeModuleClass(classRep) && isFlatName(classRep) == flat && + (!flat || isAbsent(classRep))) // on 2nd enter of flat names, check that the name has not been entered before + initializeFromClassPath(root.symbol, classRep) + for (classRep <- classpath.classes) + if (maybeModuleClass(classRep) && isFlatName(classRep) == flat && + isAbsent(classRep)) + initializeFromClassPath(root.symbol, classRep) + } + } def doComplete(root: SymDenotation)(implicit ctx: Context): Unit = { assert(root is PackageClass, root) - def maybeModuleClass(classRep: ClassPath#ClassRep) = classRep.name.last == '$' val pre = root.owner.thisType root.info = ClassInfo(pre, root.symbol.asClass, Nil, currentDecls, pre select sourceModule) if (!sourceModule.isCompleted) sourceModule.completer.complete(sourceModule) - if (!root.isRoot) { - for (classRep <- classpath.classes) - if (!maybeModuleClass(classRep)) - initializeFromClassPath(root.symbol, classRep) - for (classRep <- classpath.classes) - if (maybeModuleClass(classRep) && !root.unforcedDecls.lookup(classRep.name.toTypeName).exists) - initializeFromClassPath(root.symbol, classRep) + enterFlatClasses = Some { ctx => + enterFlatClasses = None + enterClasses(root, flat = true)(ctx) } + enterClasses(root, flat = false) if (!root.isEmptyPackage) for (pkg <- classpath.packages) enterPackage(root.symbol, pkg) diff --git a/compiler/src/dotty/tools/dotc/core/Symbols.scala b/compiler/src/dotty/tools/dotc/core/Symbols.scala index 95ff1cb75..e0d9aca2b 100644 --- a/compiler/src/dotty/tools/dotc/core/Symbols.scala +++ b/compiler/src/dotty/tools/dotc/core/Symbols.scala @@ -19,6 +19,7 @@ import util.Positions._ import DenotTransformers._ import StdNames._ import NameOps._ +import NameKinds.LazyImplicitName import ast.tpd.Tree import ast.TreeTypeMap import Constants.Constant @@ -260,7 +261,7 @@ trait Symbols { this: Context => /** Create a synthetic lazy implicit value */ def newLazyImplicit(info: Type) = - newSymbol(owner, freshName(nme.LAZY_IMPLICIT_PREFIX).toTermName, Lazy, info) + newSymbol(owner, LazyImplicitName.fresh(), Lazy, info) /** Create a symbol representing a selftype declaration for class `cls`. */ def newSelfSym(cls: ClassSymbol, name: TermName = nme.WILDCARD, selfInfo: Type = NoType): TermSymbol = diff --git a/compiler/src/dotty/tools/dotc/core/TypeApplications.scala b/compiler/src/dotty/tools/dotc/core/TypeApplications.scala index 23c3f96cc..82051b66c 100644 --- a/compiler/src/dotty/tools/dotc/core/TypeApplications.scala +++ b/compiler/src/dotty/tools/dotc/core/TypeApplications.scala @@ -11,6 +11,7 @@ import util.Stats._ import util.common._ import Names._ import NameOps._ +import NameKinds._ import Flags._ import StdNames.tpnme import util.Positions.Position @@ -391,9 +392,12 @@ class TypeApplications(val self: Type) extends AnyVal { if (!args.exists(_.isInstanceOf[TypeBounds])) { val followAlias = Config.simplifyApplications && { dealiased.resType match { - case AppliedType(tyconBody, _) => - sameLength(dealiased.typeParams, tyconBody.typeParams) - // Reducing is safe for type inference, as kind arity of type constructor does not change + case AppliedType(tyconBody, dealiasedArgs) => + // Reduction should not affect type inference when it's + // just eta-reduction (ignoring variance annotations). + // See i2201*.scala for examples where more aggressive + // reduction would break type inference. + dealiased.paramRefs == dealiasedArgs case _ => false } } @@ -461,11 +465,6 @@ class TypeApplications(val self: Type) extends AnyVal { self case _ => val v = tparam.paramVariance - /* Not neeeded. - if (v > 0 && !(tparam is Local) && !(tparam is ExpandedTypeParam)) TypeBounds.upper(self) - else if (v < 0 && !(tparam is Local) && !(tparam is ExpandedTypeParam)) TypeBounds.lower(self) - else - */ TypeAlias(self, v) } @@ -507,13 +506,14 @@ class TypeApplications(val self: Type) extends AnyVal { */ final def baseTypeWithArgs(base: Symbol)(implicit ctx: Context): Type = ctx.traceIndented(s"btwa ${self.show} wrt $base", core, show = true) { def default = self.baseTypeRef(base).appliedTo(baseArgInfos(base)) + def isExpandedTypeParam(sym: Symbol) = sym.is(TypeParam) && sym.name.is(ExpandedName) self match { case tp: TypeRef => tp.info match { case TypeBounds(_, hi) => hi.baseTypeWithArgs(base) case _ => default } - case tp @ RefinedType(parent, name, _) if !tp.member(name).symbol.is(ExpandedTypeParam) => + case tp @ RefinedType(parent, name, _) if !isExpandedTypeParam(tp.member(name).symbol) => tp.wrapIfMember(parent.baseTypeWithArgs(base)) case tp: TermRef => tp.underlying.baseTypeWithArgs(base) diff --git a/compiler/src/dotty/tools/dotc/core/TypeComparer.scala b/compiler/src/dotty/tools/dotc/core/TypeComparer.scala index da6d63387..54b96a253 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 83fb70aa1..c8c1886cc 100644 --- a/compiler/src/dotty/tools/dotc/core/Types.scala +++ b/compiler/src/dotty/tools/dotc/core/Types.scala @@ -7,6 +7,7 @@ import Symbols._ import Flags._ import Names._ import StdNames._, NameOps._ +import NameKinds.{ShadowedName, SkolemName} import Scopes._ import Constants._ import Contexts._ @@ -107,10 +108,11 @@ object Types { final def isValueTypeOrLambda: Boolean = isValueType || this.isInstanceOf[TypeLambda] /** Does this type denote a stable reference (i.e. singleton type)? */ - @tailrec final def isStable(implicit ctx: Context): Boolean = stripTypeVar match { - case tp: TermRef => tp.termSymbol.isStable && tp.prefix.isStable + final def isStable(implicit ctx: Context): Boolean = stripTypeVar match { + case tp: TermRef => tp.termSymbol.isStable && tp.prefix.isStable || tp.info.isStable case _: SingletonType | NoPrefix => true case tp: RefinedOrRecType => tp.parent.isStable + case tp: ExprType => tp.resultType.isStable case _ => false } @@ -1504,20 +1506,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}, @@ -1584,7 +1595,7 @@ object Types { } protected def asMemberOf(prefix: Type, allowPrivate: Boolean)(implicit ctx: Context): Denotation = - if (name.isShadowedName) prefix.nonPrivateMember(name.revertShadowed) + if (name.is(ShadowedName)) prefix.nonPrivateMember(name.exclude(ShadowedName)) else if (!allowPrivate) prefix.nonPrivateMember(name) else prefix.member(name) @@ -1720,7 +1731,7 @@ object Types { * the public name. */ def shadowed(implicit ctx: Context): NamedType = - NamedType(prefix, name.shadowedName) + NamedType(prefix, name.derived(ShadowedName)) override def equals(that: Any) = that match { case that: NamedType => @@ -1793,8 +1804,8 @@ object Types { override def newLikeThis(prefix: Type)(implicit ctx: Context): TermRef = fixDenot(TermRef.withSig(prefix, name, sig), prefix) - override def shadowed(implicit ctx: Context): NamedType = - fixDenot(TermRef.withSig(prefix, name.shadowedName, sig), prefix) + override def shadowed(implicit ctx: Context): NamedType = + fixDenot(TermRef.withSig(prefix, name.derived(ShadowedName), sig), prefix) override def equals(that: Any) = that match { case that: TermRefWithSignature => @@ -2990,9 +3001,9 @@ object Types { override def hashCode: Int = identityHash override def equals(that: Any) = this eq that.asInstanceOf[AnyRef] - private var myRepr: String = null - def repr(implicit ctx: Context) = { - if (myRepr == null) myRepr = ctx.freshName("?") + private var myRepr: Name = null + def repr(implicit ctx: Context): Name = { + if (myRepr == null) myRepr = SkolemName.fresh() myRepr } diff --git a/compiler/src/dotty/tools/dotc/core/classfile/ClassfileParser.scala b/compiler/src/dotty/tools/dotc/core/classfile/ClassfileParser.scala index da875c906..27afa4d09 100644 --- a/compiler/src/dotty/tools/dotc/core/classfile/ClassfileParser.scala +++ b/compiler/src/dotty/tools/dotc/core/classfile/ClassfileParser.scala @@ -5,6 +5,7 @@ package classfile import Contexts._, Symbols._, Types._, Names._, StdNames._, NameOps._, Scopes._, Decorators._ import SymDenotations._, unpickleScala2.Scala2Unpickler._, Constants._, Annotations._, util.Positions._ +import NameKinds.{ModuleClassName, DefaultGetterName} import ast.tpd._ import java.io.{ File, IOException } import java.lang.Integer.toHexString @@ -25,6 +26,8 @@ class ClassfileParser( classRoot: ClassDenotation, moduleRoot: ClassDenotation)(ictx: Context) { + //println(s"parsing ${classRoot.name.debugString} ${moduleRoot.name.debugString}") + import ClassfileConstants._ import ClassfileParser._ @@ -36,7 +39,7 @@ class ClassfileParser( protected val staticScope: MutableScope = newScope // the scope of all static definitions protected var pool: ConstantPool = _ // the classfile's constant pool - protected var currentClassName: Name = _ // JVM name of the current class + protected var currentClassName: SimpleTermName = _ // JVM name of the current class protected var classTParams = Map[Name,Symbol]() classRoot.info = (new NoCompleter).withDecls(instanceScope) @@ -44,8 +47,8 @@ class ClassfileParser( private def currentIsTopLevel(implicit ctx: Context) = classRoot.owner is Flags.PackageClass - private def mismatchError(c: Symbol) = - throw new IOException(s"class file '${in.file}' has location not matching its contents: contains $c") + private def mismatchError(className: SimpleTermName) = + throw new IOException(s"class file '${in.file}' has location not matching its contents: contains class $className") def run()(implicit ctx: Context): Option[Embedded] = try { ctx.debuglog("[class] >> " + classRoot.fullName) @@ -89,10 +92,8 @@ class ClassfileParser( val nameIdx = in.nextChar currentClassName = pool.getClassName(nameIdx) - if (currentIsTopLevel) { - val c = pool.getClassSymbol(nameIdx) - if (c != classRoot.symbol) mismatchError(c) - } + if (currentIsTopLevel && currentClassName != classRoot.fullName.toSimpleName) + mismatchError(currentClassName) addEnclosingTParams() @@ -239,21 +240,21 @@ class ClassfileParser( final def objToAny(tp: Type)(implicit ctx: Context) = if (tp.isDirectRef(defn.ObjectClass) && !ctx.phase.erasedTypes) defn.AnyType else tp - private def sigToType(sig: TermName, owner: Symbol = null)(implicit ctx: Context): Type = { + private def sigToType(sig: SimpleTermName, owner: Symbol = null)(implicit ctx: Context): Type = { var index = 0 val end = sig.length def accept(ch: Char): Unit = { assert(sig(index) == ch, (sig(index), ch)) index += 1 } - def subName(isDelimiter: Char => Boolean): TermName = { + def subName(isDelimiter: Char => Boolean): SimpleTermName = { val start = index while (!isDelimiter(sig(index))) { index += 1 } sig.slice(start, index) } // Warning: sigToType contains nested completers which might be forced in a later run! // So local methods need their own ctx parameters. - def sig2type(tparams: immutable.Map[Name,Symbol], skiptvs: Boolean)(implicit ctx: Context): Type = { + def sig2type(tparams: immutable.Map[Name, Symbol], skiptvs: Boolean)(implicit ctx: Context): Type = { val tag = sig(index); index += 1 (tag: @switch) match { case BYTE_TAG => defn.ByteType @@ -590,7 +591,7 @@ class ClassfileParser( def addDefaultGetter(attr: Symbol, n: Int) = ctx.newSymbol( owner = moduleRoot.symbol, - name = nme.CONSTRUCTOR.defaultGetterName(n), + name = DefaultGetterName(nme.CONSTRUCTOR, n), flags = attr.flags & Flags.AccessFlags, info = defn.NothingType).entered @@ -647,7 +648,10 @@ class ClassfileParser( * and implicitly current class' superclasses. */ private def enterOwnInnerClasses()(implicit ctx: Context): Unit = { - def className(name: Name): Name = name.drop(name.lastIndexOf('.') + 1) + def className(name: Name): Name = { + val name1 = name.toSimpleName + name1.drop(name1.lastIndexOf('.') + 1) + } def enterClassAndModule(entry: InnerClassEntry, file: AbstractFile, jflags: Int) = { ctx.base.loaders.enterClassAndModule( @@ -883,7 +887,7 @@ class ClassfileParser( private val len = in.nextChar private val starts = new Array[Int](len) private val values = new Array[AnyRef](len) - private val internalized = new Array[TermName](len) + private val internalized = new Array[SimpleTermName](len) { var i = 1 while (i < starts.length) { @@ -910,12 +914,12 @@ class ClassfileParser( } /** Return the name found at given index. */ - def getName(index: Int): TermName = { + def getName(index: Int): SimpleTermName = { if (index <= 0 || len <= index) errorBadIndex(index) values(index) match { - case name: TermName => name + case name: SimpleTermName => name case null => val start = starts(index) if (in.buf(start).toInt != CONSTANT_UTF8) errorBadTag(start) @@ -926,7 +930,7 @@ class ClassfileParser( } /** Return the name found at given index in the constant pool, with '/' replaced by '.'. */ - def getExternalName(index: Int): TermName = { + def getExternalName(index: Int): SimpleTermName = { if (index <= 0 || len <= index) errorBadIndex(index) @@ -943,9 +947,9 @@ class ClassfileParser( val start = starts(index) if (in.buf(start).toInt != CONSTANT_CLASS) errorBadTag(start) val name = getExternalName(in.getChar(start + 1)) - if (name.isModuleClassName && (name ne nme.nothingRuntimeClass) && (name ne nme.nullRuntimeClass)) + if (name.endsWith("$") && (name ne nme.nothingRuntimeClass) && (name ne nme.nullRuntimeClass)) // Null$ and Nothing$ ARE classes - c = ctx.requiredModule(name.sourceModuleName) + c = ctx.requiredModule(name.dropRight(1)) else c = classNameToSymbol(name) values(index) = c } @@ -955,7 +959,7 @@ class ClassfileParser( /** Return the external name of the class info structure found at 'index'. * Use 'getClassSymbol' if the class is sure to be a top-level class. */ - def getClassName(index: Int): TermName = { + def getClassName(index: Int): SimpleTermName = { val start = starts(index) if (in.buf(start).toInt != CONSTANT_CLASS) errorBadTag(start) getExternalName(in.getChar(start + 1)) @@ -995,7 +999,7 @@ class ClassfileParser( val start = starts(index) if (in.buf(start).toInt != CONSTANT_CLASS) errorBadTag(start) val name = getExternalName(in.getChar(start + 1)) - if (name(0) == ARRAY_TAG) { + if (name.firstPart(0) == ARRAY_TAG) { c = sigToType(name) values(index) = c } else { diff --git a/compiler/src/dotty/tools/dotc/core/tasty/DottyUnpickler.scala b/compiler/src/dotty/tools/dotc/core/tasty/DottyUnpickler.scala index 2c93819d5..28916a781 100644 --- a/compiler/src/dotty/tools/dotc/core/tasty/DottyUnpickler.scala +++ b/compiler/src/dotty/tools/dotc/core/tasty/DottyUnpickler.scala @@ -19,12 +19,12 @@ object DottyUnpickler { class TreeSectionUnpickler(posUnpickler: Option[PositionUnpickler]) extends SectionUnpickler[TreeUnpickler]("ASTs") { - def unpickle(reader: TastyReader, tastyName: TastyName.Table) = - new TreeUnpickler(reader, tastyName, posUnpickler) + def unpickle(reader: TastyReader, nameAtRef: NameTable) = + new TreeUnpickler(reader, nameAtRef, posUnpickler) } class PositionsSectionUnpickler extends SectionUnpickler[PositionUnpickler]("Positions") { - def unpickle(reader: TastyReader, tastyName: TastyName.Table) = + def unpickle(reader: TastyReader, nameAtRef: NameTable) = new PositionUnpickler(reader) } } diff --git a/compiler/src/dotty/tools/dotc/core/tasty/NameBuffer.scala b/compiler/src/dotty/tools/dotc/core/tasty/NameBuffer.scala index 3ff7298ce..270d6be56 100644 --- a/compiler/src/dotty/tools/dotc/core/tasty/NameBuffer.scala +++ b/compiler/src/dotty/tools/dotc/core/tasty/NameBuffer.scala @@ -4,84 +4,83 @@ package core package tasty import collection.mutable -import Names.{Name, chrs} -import Decorators._, NameOps._ +import Names.{Name, chrs, SimpleTermName, DerivedTermName} +import NameOps.NameDecorator +import NameKinds._ +import Decorators._ import TastyBuffer._ import scala.io.Codec -import TastyName._ import TastyFormat._ class NameBuffer extends TastyBuffer(10000) { import NameBuffer._ - private val nameRefs = new mutable.LinkedHashMap[TastyName, NameRef] + private val nameRefs = new mutable.LinkedHashMap[Name, NameRef] - def nameIndex(name: TastyName): NameRef = nameRefs.get(name) match { - case Some(ref) => - ref - case None => - val ref = NameRef(nameRefs.size) - nameRefs(name) = ref - ref - } def nameIndex(name: Name): NameRef = { - val tname = - if (name.isShadowedName) Shadowed(nameIndex(name.revertShadowed)) - else Simple(name.toTermName) - nameIndex(tname) - } - - def nameIndex(str: String): NameRef = nameIndex(str.toTermName) - - def fullNameIndex(name: Name): NameRef = { - val pos = name.lastIndexOf('.') - if (pos > 0) - nameIndex(Qualified(fullNameIndex(name.take(pos)), nameIndex(name.drop(pos + 1)))) - else - nameIndex(name) + val name1 = name.toTermName + nameRefs.get(name1) match { + case Some(ref) => + ref + case None => + name1 match { + case SignedName(original, Signature(params, result)) => + nameIndex(original); nameIndex(result); params.foreach(nameIndex) + case AnyQualifiedName(prefix, name) => + nameIndex(prefix); nameIndex(name) + case AnyUniqueName(original, separator, num) => + nameIndex(separator.toTermName) + if (!original.isEmpty) nameIndex(original) + case DerivedTermName(original, _) => + nameIndex(original) + case _ => + } + val ref = NameRef(nameRefs.size) + nameRefs(name1) = ref + ref + } } private def withLength(op: => Unit, lengthWidth: Int = 1): Unit = { val lengthAddr = currentAddr for (i <- 0 until lengthWidth) writeByte(0) op - val length = currentAddr.index - lengthAddr.index - 1 + val length = currentAddr.index - lengthAddr.index - lengthWidth putNat(lengthAddr, length, lengthWidth) } - def writeNameRef(ref: NameRef) = writeNat(ref.index) + def writeNameRef(ref: NameRef): Unit = writeNat(ref.index) + def writeNameRef(name: Name): Unit = writeNameRef(nameRefs(name.toTermName)) - def pickleName(name: TastyName): Unit = name match { - case Simple(name) => - val bytes = - if (name.length == 0) new Array[Byte](0) - else Codec.toUTF8(chrs, name.start, name.length) - writeByte(UTF8) - writeNat(bytes.length) - writeBytes(bytes, bytes.length) - case Qualified(qualified, selector) => - writeByte(QUALIFIED) - withLength { writeNameRef(qualified); writeNameRef(selector) } - case Signed(original, params, result) => - writeByte(SIGNED) - withLength( + def pickleNameContents(name: Name): Unit = { + val tag = name.toTermName.info.kind.tag + writeByte(tag) + name.toTermName match { + case name: SimpleTermName => + val bytes = + if (name.length == 0) new Array[Byte](0) + else Codec.toUTF8(chrs, name.start, name.length) + writeNat(bytes.length) + writeBytes(bytes, bytes.length) + case AnyQualifiedName(prefix, name) => + withLength { writeNameRef(prefix); writeNameRef(name) } + case AnyUniqueName(original, separator, num) => + withLength { + writeNameRef(separator.toTermName) + writeNat(num) + if (!original.isEmpty) writeNameRef(original) + } + case VariantName(original, sign) => + withLength { writeNameRef(original); writeNat(sign + 1) } + case AnyNumberedName(original, num) => + withLength { writeNameRef(original); writeNat(num) } + case SignedName(original, Signature(params, result)) => + withLength( { writeNameRef(original); writeNameRef(result); params.foreach(writeNameRef) }, if ((params.length + 2) * maxIndexWidth <= maxNumInByte) 1 else 2) - case Expanded(prefix, original) => - writeByte(EXPANDED) - withLength { writeNameRef(prefix); writeNameRef(original) } - case ModuleClass(module) => - writeByte(OBJECTCLASS) - withLength { writeNameRef(module) } - case SuperAccessor(accessed) => - writeByte(SUPERACCESSOR) - withLength { writeNameRef(accessed) } - case DefaultGetter(method, paramNumber) => - writeByte(DEFAULTGETTER) - withLength { writeNameRef(method); writeNat(paramNumber) } - case Shadowed(original) => - writeByte(SHADOWED) - withLength { writeNameRef(original) } + case DerivedTermName(original, _) => + withLength { writeNameRef(original) } + } } override def assemble(): Unit = { @@ -89,7 +88,7 @@ class NameBuffer extends TastyBuffer(10000) { for ((name, ref) <- nameRefs) { assert(ref.index == i) i += 1 - pickleName(name) + pickleNameContents(name) } } } diff --git a/compiler/src/dotty/tools/dotc/core/tasty/TastyBuffer.scala b/compiler/src/dotty/tools/dotc/core/tasty/TastyBuffer.scala index 13bc95028..40782f534 100644 --- a/compiler/src/dotty/tools/dotc/core/tasty/TastyBuffer.scala +++ b/compiler/src/dotty/tools/dotc/core/tasty/TastyBuffer.scala @@ -26,6 +26,9 @@ object TastyBuffer { * the value of 4 gives a maximal array size of 256M. */ final val AddrWidth = 4 + + /** An address referring to a serialized name */ + case class NameRef(index: Int) extends AnyVal } import TastyBuffer._ diff --git a/compiler/src/dotty/tools/dotc/core/tasty/TastyFormat.scala b/compiler/src/dotty/tools/dotc/core/tasty/TastyFormat.scala index 8b2255e94..f03e279c6 100644 --- a/compiler/src/dotty/tools/dotc/core/tasty/TastyFormat.scala +++ b/compiler/src/dotty/tools/dotc/core/tasty/TastyFormat.scala @@ -33,11 +33,11 @@ Macro-format: QUALIFIED Length qualified_NameRef selector_NameRef SIGNED Length original_NameRef resultSig_NameRef paramSig_NameRef* EXPANDED Length original_NameRef + UNIQUE Length separator_NameRef num_Nat original_NameRef? OBJECTCLASS Length module_NameRef SUPERACCESSOR Length accessed_NameRef DEFAULTGETTER Length method_NameRef paramNumber_Nat SHADOWED Length original_NameRef - MANGLED Length mangle_NameRef name_NameRef ... NameRef = Nat // ordinal number of name in name table, starting from 1. @@ -222,12 +222,30 @@ object TastyFormat { final val UTF8 = 1 final val QUALIFIED = 2 - final val SIGNED = 3 + final val FLATTENED = 3 final val EXPANDED = 4 - final val OBJECTCLASS = 5 - final val SUPERACCESSOR = 6 - final val DEFAULTGETTER = 7 - final val SHADOWED = 8 + final val EXPANDPREFIX = 5 + final val TRAITSETTER = 6 + final val UNIQUE = 10 + final val DEFAULTGETTER = 11 + final val VARIANT = 12 + final val OUTERSELECT = 13 + + final val SUPERACCESSOR = 20 + final val PROTECTEDACCESSOR = 21 + final val PROTECTEDSETTER = 22 + final val INITIALIZER = 23 + final val SHADOWED = 24 + final val AVOIDCLASH = 30 + final val DIRECT = 31 + final val FIELD = 32 + final val SETTER = 33 + final val EXTMETH = 34 + final val OBJECTVAR = 39 + final val OBJECTCLASS = 40 + + final val SIGNED = 63 + final val firstInternalTag = 64 // AST tags @@ -411,11 +429,14 @@ object TastyFormat { def nameTagToString(tag: Int): String = tag match { case UTF8 => "UTF8" case QUALIFIED => "QUALIFIED" - case SIGNED => "SIGNED" + case FLATTENED => "FLATTENED" case EXPANDED => "EXPANDED" + case SIGNED => "SIGNED" case OBJECTCLASS => "OBJECTCLASS" case SUPERACCESSOR => "SUPERACCESSOR" case DEFAULTGETTER => "DEFAULTGETTER" + case SHADOWED => "SHADOWED" + case VARIANT => "VARIANT" } def astTagToString(tag: Int): String = tag match { diff --git a/compiler/src/dotty/tools/dotc/core/tasty/TastyName.scala b/compiler/src/dotty/tools/dotc/core/tasty/TastyName.scala deleted file mode 100644 index 26807115c..000000000 --- a/compiler/src/dotty/tools/dotc/core/tasty/TastyName.scala +++ /dev/null @@ -1,30 +0,0 @@ -package dotty.tools -package dotc -package core -package tasty - -import core.Names.TermName -import collection.mutable - -abstract class TastyName - -object TastyName { - - case class NameRef(index: Int) extends AnyVal - - case class Simple(name: TermName) extends TastyName - case class Qualified(qualified: NameRef, selector: NameRef) extends TastyName - case class Signed(original: NameRef, params: List[NameRef], result: NameRef) extends TastyName - case class Expanded(prefix: NameRef, original: NameRef) extends TastyName - case class ModuleClass(module: NameRef) extends TastyName - case class SuperAccessor(accessed: NameRef) extends TastyName - case class DefaultGetter(method: NameRef, num: Int) extends TastyName - case class Shadowed(original: NameRef) extends TastyName - - class Table extends (NameRef => TastyName) { - private val names = new mutable.ArrayBuffer[TastyName] - def add(name: TastyName) = names += name - def apply(ref: NameRef) = names(ref.index) - def contents: Iterable[TastyName] = names - } -} diff --git a/compiler/src/dotty/tools/dotc/core/tasty/TastyPickler.scala b/compiler/src/dotty/tools/dotc/core/tasty/TastyPickler.scala index c844d522e..cc2e4dd58 100644 --- a/compiler/src/dotty/tools/dotc/core/tasty/TastyPickler.scala +++ b/compiler/src/dotty/tools/dotc/core/tasty/TastyPickler.scala @@ -9,10 +9,11 @@ import TastyBuffer._ import java.util.UUID import core.Symbols.Symbol import ast.tpd +import Decorators._ class TastyPickler { - private val sections = new mutable.ArrayBuffer[(TastyName.NameRef, TastyBuffer)] + private val sections = new mutable.ArrayBuffer[(NameRef, TastyBuffer)] val uuid = UUID.randomUUID() private val headerBuffer = { @@ -28,7 +29,7 @@ class TastyPickler { val nameBuffer = new NameBuffer def newSection(name: String, buf: TastyBuffer) = - sections += ((nameBuffer.nameIndex(name), buf)) + sections += ((nameBuffer.nameIndex(name.toTermName), buf)) def assembleParts(): Array[Byte] = { def lengthWithLength(buf: TastyBuffer) = { diff --git a/compiler/src/dotty/tools/dotc/core/tasty/TastyPrinter.scala b/compiler/src/dotty/tools/dotc/core/tasty/TastyPrinter.scala index ce3722ff1..a5c870881 100644 --- a/compiler/src/dotty/tools/dotc/core/tasty/TastyPrinter.scala +++ b/compiler/src/dotty/tools/dotc/core/tasty/TastyPrinter.scala @@ -4,34 +4,24 @@ package tasty import Contexts._, Decorators._ import printing.Texts._ -import TastyName._ +import Names.Name import StdNames._ import TastyUnpickler._ -import TastyBuffer.Addr +import TastyBuffer.{Addr, NameRef} import util.Positions.{Position, offsetToInt} import collection.mutable class TastyPrinter(bytes: Array[Byte])(implicit ctx: Context) { val unpickler = new TastyUnpickler(bytes) - import unpickler.{tastyName, unpickle} + import unpickler.{nameAtRef, unpickle} - def nameToString(name: TastyName): String = name match { - case Simple(name) => name.toString - case Qualified(qual, name) => nameRefToString(qual) + "." + nameRefToString(name) - case Signed(original, params, result) => - i"${nameRefToString(original)}@${params.map(nameRefToString)}%,%:${nameRefToString(result)}" - case Expanded(prefix, original) => s"$prefix${nme.EXPAND_SEPARATOR}$original" - case ModuleClass(original) => nameRefToString(original) + "/MODULECLASS" - case SuperAccessor(accessed) => nameRefToString(accessed) + "/SUPERACCESSOR" - case DefaultGetter(meth, num) => nameRefToString(meth) + "/DEFAULTGETTER" + num - case Shadowed(original) => nameRefToString(original) + "/SHADOWED" - } + def nameToString(name: Name): String = name.debugString - def nameRefToString(ref: NameRef): String = nameToString(tastyName(ref)) + def nameRefToString(ref: NameRef): String = nameToString(nameAtRef(ref)) def printNames() = - for ((name, idx) <- tastyName.contents.zipWithIndex) { + for ((name, idx) <- nameAtRef.contents.zipWithIndex) { val index = "%4d: ".format(idx) println(index + nameToString(name)) } @@ -46,7 +36,7 @@ class TastyPrinter(bytes: Array[Byte])(implicit ctx: Context) { class TreeSectionUnpickler extends SectionUnpickler[Unit]("ASTs") { import TastyFormat._ - def unpickle(reader: TastyReader, tastyName: TastyName.Table): Unit = { + def unpickle(reader: TastyReader, tastyName: NameTable): Unit = { import reader._ var indent = 0 def newLine() = { @@ -116,7 +106,7 @@ class TastyPrinter(bytes: Array[Byte])(implicit ctx: Context) { } class PositionSectionUnpickler extends SectionUnpickler[Unit]("Positions") { - def unpickle(reader: TastyReader, tastyName: TastyName.Table): Unit = { + def unpickle(reader: TastyReader, tastyName: NameTable): Unit = { print(s"${reader.endAddr.index - reader.currentAddr.index}") val positions = new PositionUnpickler(reader).positions println(s" position bytes:") diff --git a/compiler/src/dotty/tools/dotc/core/tasty/TastyReader.scala b/compiler/src/dotty/tools/dotc/core/tasty/TastyReader.scala index e583c4793..af5e78891 100644 --- a/compiler/src/dotty/tools/dotc/core/tasty/TastyReader.scala +++ b/compiler/src/dotty/tools/dotc/core/tasty/TastyReader.scala @@ -4,7 +4,6 @@ package core package tasty import TastyBuffer._ -import TastyName.NameRef import collection.mutable /** A byte array buffer that can be filled with bytes or natural numbers in TASTY format, diff --git a/compiler/src/dotty/tools/dotc/core/tasty/TastyUnpickler.scala b/compiler/src/dotty/tools/dotc/core/tasty/TastyUnpickler.scala index 8a1f58acd..37a3c2e76 100644 --- a/compiler/src/dotty/tools/dotc/core/tasty/TastyUnpickler.scala +++ b/compiler/src/dotty/tools/dotc/core/tasty/TastyUnpickler.scala @@ -4,14 +4,23 @@ package tasty import scala.collection.mutable import TastyFormat._ -import Names.{Name, termName} +import TastyBuffer.NameRef +import Names.{Name, TermName, termName, EmptyTermName} +import NameKinds._ import java.util.UUID object TastyUnpickler { class UnpickleException(msg: String) extends Exception(msg) abstract class SectionUnpickler[R](val name: String) { - def unpickle(reader: TastyReader, tastyName: TastyName.Table): R + def unpickle(reader: TastyReader, nameAtRef: NameTable): R + } + + class NameTable extends (NameRef => TermName) { + private val names = new mutable.ArrayBuffer[TermName] + def add(name: TermName) = names += name + def apply(ref: NameRef) = names(ref.index) + def contents: Iterable[TermName] = names } } @@ -23,18 +32,15 @@ class TastyUnpickler(reader: TastyReader) { def this(bytes: Array[Byte]) = this(new TastyReader(bytes)) private val sectionReader = new mutable.HashMap[String, TastyReader] - val tastyName = new TastyName.Table + val nameAtRef = new NameTable - def check(cond: Boolean, msg: => String) = + private def check(cond: Boolean, msg: => String) = if (!cond) throw new UnpickleException(msg) - def readString(): String = { - val TastyName.Simple(name) = tastyName(readNameRef()) - name.toString - } + private def readName(): TermName = nameAtRef(readNameRef()) + private def readString(): String = readName().toString - def readName(): TastyName = { - import TastyName._ + private def readNameContents(): TermName = { val tag = readByte() val length = readNat() val start = currentAddr @@ -42,24 +48,30 @@ class TastyUnpickler(reader: TastyReader) { val result = tag match { case UTF8 => goto(end) - Simple(termName(bytes, start.index, length)) - case QUALIFIED => - Qualified(readNameRef(), readNameRef()) - case SIGNED => - val original = readNameRef() - val result = readNameRef() - val params = until(end)(readNameRef()) - Signed(original, params, result) - case EXPANDED => - Expanded(readNameRef(), readNameRef()) - case OBJECTCLASS => - ModuleClass(readNameRef()) - case SUPERACCESSOR => - SuperAccessor(readNameRef()) + termName(bytes, start.index, length) + case QUALIFIED | FLATTENED | EXPANDED | EXPANDPREFIX => + qualifiedNameKindOfTag(tag)(readName(), readName().asSimpleName) + case UNIQUE => + val separator = readName().toString + val num = readNat() + val originals = until(end)(readName()) + val original = if (originals.isEmpty) EmptyTermName else originals.head + uniqueNameKindOfSeparator(separator)(original, num) case DEFAULTGETTER => - DefaultGetter(readNameRef(), readNat()) - case SHADOWED => - Shadowed(readNameRef()) + DefaultGetterName(readName(), readNat()) + case VARIANT => + VariantName(readName(), readNat() - 1) + case OUTERSELECT => + OuterSelectName(readName(), readNat()) + case SIGNED => + val original = readName() + val result = readName().toTypeName + val params = until(end)(readName().toTypeName) + var sig = Signature(params, result) + if (sig == Signature.NotAMethod) sig = Signature.NotAMethod + SignedName(original, sig) + case _ => + simpleNameKindOfTag(tag)(readName()) } assert(currentAddr == end, s"bad name $result $start $currentAddr $end") result @@ -77,10 +89,10 @@ class TastyUnpickler(reader: TastyReader) { new UUID(readUncompressedLong(), readUncompressedLong()) } - val uuid = readHeader() + private val uuid = readHeader() locally { - until(readEnd()) { tastyName.add(readName()) } + until(readEnd()) { nameAtRef.add(readNameContents()) } while (!isAtEnd) { val secName = readString() val secEnd = readEnd() @@ -91,5 +103,5 @@ class TastyUnpickler(reader: TastyReader) { def unpickle[R](sec: SectionUnpickler[R]): Option[R] = for (reader <- sectionReader.get(sec.name)) yield - sec.unpickle(reader, tastyName) + sec.unpickle(reader, nameAtRef) } diff --git a/compiler/src/dotty/tools/dotc/core/tasty/TreePickler.scala b/compiler/src/dotty/tools/dotc/core/tasty/TreePickler.scala index 902d01c21..5d33738c2 100644 --- a/compiler/src/dotty/tools/dotc/core/tasty/TreePickler.scala +++ b/compiler/src/dotty/tools/dotc/core/tasty/TreePickler.scala @@ -9,16 +9,18 @@ import TastyFormat._ import Contexts._, Symbols._, Types._, Names._, Constants._, Decorators._, Annotations._, StdNames.tpnme, NameOps._ import collection.mutable import typer.Inliner -import NameOps._ +import NameOps._, NameKinds._ import StdNames.nme import TastyBuffer._ import TypeApplications._ +import transform.SymUtils._ +import config.Config class TreePickler(pickler: TastyPickler) { val buf = new TreeBuffer pickler.newSection("ASTs", buf) import buf._ - import pickler.nameBuffer.{nameIndex, fullNameIndex} + import pickler.nameBuffer.nameIndex import ast.tpd._ private val symRefs = new mutable.HashMap[Symbol, Addr] @@ -52,28 +54,8 @@ class TreePickler(pickler: TastyPickler) { } private def pickleName(name: Name): Unit = writeNat(nameIndex(name).index) - private def pickleName(name: TastyName): Unit = writeNat(nameIndex(name).index) - private def pickleNameAndSig(name: Name, sig: Signature) = { - val Signature(params, result) = sig - pickleName(TastyName.Signed(nameIndex(name), params.map(fullNameIndex), fullNameIndex(result))) - } - - private def pickleName(sym: Symbol)(implicit ctx: Context): Unit = { - def encodeSuper(name: Name): TastyName.NameRef = - if (sym is Flags.SuperAccessor) { - val SuperAccessorName(n) = name - nameIndex(TastyName.SuperAccessor(nameIndex(n))) - } - else nameIndex(name) - val nameRef = - if (sym is Flags.ExpandedName) - nameIndex( - TastyName.Expanded( - nameIndex(sym.name.expandedPrefix), - encodeSuper(sym.name.unexpandedName))) - else encodeSuper(sym.name) - writeNat(nameRef.index) - } + private def pickleNameAndSig(name: Name, sig: Signature) = + pickleName(SignedName(name.toTermName, sig)) private def pickleSymRef(sym: Symbol)(implicit ctx: Context) = symRefs.get(sym) match { case Some(label) => @@ -126,7 +108,7 @@ class TreePickler(pickler: TastyPickler) { writeLongInt(java.lang.Double.doubleToRawLongBits(c.doubleValue)) case StringTag => writeByte(STRINGconst) - writeNat(nameIndex(c.stringValue).index) + pickleName(c.stringValue.toTermName) case NullTag => writeByte(NULLconst) case ClazzTag => @@ -160,7 +142,7 @@ class TreePickler(pickler: TastyPickler) { withLength { pickleType(tycon); args.foreach(pickleType(_)) } case ConstantType(value) => pickleConstant(value) - case tpe: TypeRef if tpe.info.isAlias && tpe.symbol.is(Flags.AliasPreferred) => + case tpe: TypeRef if tpe.info.isAlias && tpe.symbol.isAliasPreferred => pickleType(tpe.superType) case tpe: WithFixedSym => val sym = tpe.symbol @@ -178,7 +160,7 @@ class TreePickler(pickler: TastyPickler) { } if (sym.is(Flags.Package)) { writeByte(if (tpe.isType) TYPEREFpkg else TERMREFpkg) - pickleName(qualifiedName(sym)) + pickleName(sym.fullName) } else if (sym is Flags.BindDefinedType) { registerDef(sym) @@ -278,7 +260,7 @@ class TreePickler(pickler: TastyPickler) { def picklePackageRef(pkg: Symbol)(implicit ctx: Context): Unit = { writeByte(TERMREFpkg) - pickleName(qualifiedName(pkg)) + pickleName(pkg.fullName) } def pickleMethodic(tag: Int, tpe: LambdaType)(implicit ctx: Context) = { @@ -312,7 +294,7 @@ class TreePickler(pickler: TastyPickler) { registerDef(sym) writeByte(tag) withLength { - pickleName(sym) + pickleName(sym.name) pickleParams tpt match { case templ: Template => pickleTree(tpt) @@ -370,7 +352,7 @@ class TreePickler(pickler: TastyPickler) { case Select(qual, name) => writeByte(if (name.isTypeName) SELECTtpt else SELECT) val realName = tree.tpe match { - case tp: NamedType if tp.name.isShadowedName => tp.name + case tp: NamedType if tp.name.is(ShadowedName) => tp.name case _ => name } val sig = tree.tpe.signature @@ -575,10 +557,6 @@ class TreePickler(pickler: TastyPickler) { pickleName(id.name) } - def qualifiedName(sym: Symbol)(implicit ctx: Context): TastyName = - if (sym.isRoot || sym.owner.isRoot) TastyName.Simple(sym.name.toTermName) - else TastyName.Qualified(nameIndex(qualifiedName(sym.owner)), nameIndex(sym.name)) - def pickleModifiers(sym: Symbol)(implicit ctx: Context): Unit = { import Flags._ val flags = sym.flags diff --git a/compiler/src/dotty/tools/dotc/core/tasty/TreeUnpickler.scala b/compiler/src/dotty/tools/dotc/core/tasty/TreeUnpickler.scala index 4db995e10..2908c541e 100644 --- a/compiler/src/dotty/tools/dotc/core/tasty/TreeUnpickler.scala +++ b/compiler/src/dotty/tools/dotc/core/tasty/TreeUnpickler.scala @@ -5,6 +5,7 @@ package tasty import Contexts._, Symbols._, Types._, Scopes._, SymDenotations._, Names._, NameOps._ import StdNames._, Denotations._, Flags._, Constants._, Annotations._ +import NameKinds._ import util.Positions._ import ast.{tpd, Trees, untpd} import Trees._ @@ -15,15 +16,15 @@ import scala.collection.mutable.ListBuffer import scala.collection.{ mutable, immutable } import config.Printers.pickling import typer.Checking +import config.Config /** Unpickler for typed trees * @param reader the reader from which to unpickle * @param tastyName the nametable * @param posUNpicklerOpt the unpickler for positions, if it exists */ -class TreeUnpickler(reader: TastyReader, tastyName: TastyName.Table, posUnpicklerOpt: Option[PositionUnpickler]) { +class TreeUnpickler(reader: TastyReader, nameAtRef: NameRef => TermName, posUnpicklerOpt: Option[PositionUnpickler]) { import TastyFormat._ - import TastyName._ import TreeUnpickler._ import tpd._ @@ -74,20 +75,6 @@ class TreeUnpickler(reader: TastyReader, tastyName: TastyName.Table, posUnpickle new TreeReader(reader).readTopLevel()(ctx.addMode(Mode.AllowDependentFunctions)) } - def toTermName(tname: TastyName): TermName = tname match { - case Simple(name) => name - case Qualified(qual, name) => toTermName(qual) ++ "." ++ toTermName(name) - case Signed(original, params, result) => toTermName(original) - case Shadowed(original) => toTermName(original).shadowedName - case Expanded(prefix, original) => toTermName(original).expandedName(toTermName(prefix)) - case ModuleClass(original) => toTermName(original).moduleClassName.toTermName - case SuperAccessor(accessed) => toTermName(accessed).superName - case DefaultGetter(meth, num) => ??? - } - - def toTermName(ref: NameRef): TermName = toTermName(tastyName(ref)) - def toTypeName(ref: NameRef): TypeName = toTermName(ref).toTypeName - class Completer(owner: Symbol, reader: TastyReader) extends LazyType { import reader._ def complete(denot: SymDenotation)(implicit ctx: Context): Unit = { @@ -165,17 +152,7 @@ class TreeUnpickler(reader: TastyReader, tastyName: TastyName.Table, posUnpickle else tag } - def readName(): TermName = toTermName(readNameRef()) - - def readNameSplitSig()(implicit ctx: Context): Any /* TermName | (TermName, Signature) */ = - tastyName(readNameRef()) match { - case Signed(original, params, result) => - var sig = Signature(params map toTypeName, toTypeName(result)) - if (sig == Signature.NotAMethod) sig = Signature.NotAMethod - (toTermName(original), sig) - case name => - toTermName(name) - } + def readName(): TermName = nameAtRef(readNameRef()) // ------ Reading types ----------------------------------------------------- @@ -305,9 +282,9 @@ class TreeUnpickler(reader: TastyReader, tastyName: TastyName.Table, posUnpickle val name = readName().toTypeName TypeRef(readType(), name) case TERMREF => - readNameSplitSig() match { - case name: TermName => TermRef.all(readType(), name) - case (name: TermName, sig: Signature) => TermRef.withSig(readType(), name, sig) + readName() match { + case SignedName(name, sig) => TermRef.withSig(readType(), name, sig) + case name => TermRef.all(readType(), name) } case THIS => ThisType.raw(readType().asInstanceOf[TypeRef]) @@ -438,9 +415,9 @@ class TreeUnpickler(reader: TastyReader, tastyName: TastyName.Table, posUnpickle val start = currentAddr val tag = readByte() val end = readEnd() - val rawName = tastyName(readNameRef()) - var name: Name = toTermName(rawName) + var name: Name = readName() if (tag == TYPEDEF || tag == TYPEPARAM) name = name.toTypeName + val mname = name.mangled skipParams() val ttag = nextUnsharedTag val isAbsType = isAbstractType(ttag) @@ -451,19 +428,15 @@ class TreeUnpickler(reader: TastyReader, tastyName: TastyName.Table, posUnpickle val rhsIsEmpty = noRhs(end) if (!rhsIsEmpty) skipTree() val (givenFlags, annots, privateWithin) = readModifiers(end) - def nameFlags(tname: TastyName): FlagSet = tname match { - case TastyName.Expanded(_, original) => ExpandedName | nameFlags(tastyName(original)) - case TastyName.SuperAccessor(_) => Flags.SuperAccessor - case _ => EmptyFlags - } pickling.println(i"creating symbol $name at $start with flags $givenFlags") - val flags = normalizeFlags(tag, givenFlags | nameFlags(rawName), name, isAbsType, rhsIsEmpty) + val flags = normalizeFlags(tag, givenFlags, name, isAbsType, rhsIsEmpty) def adjustIfModule(completer: LazyType) = if (flags is Module) ctx.adjustModuleCompleter(completer, name) else completer val sym = - roots.find(root => (root.owner eq ctx.owner) && root.name == name) match { + roots.find(root => (root.owner eq ctx.owner) && root.name.mangled == mname) match { case Some(rootd) => pickling.println(i"overwriting ${rootd.symbol} # ${rootd.hashCode}") + rootd.name = name rootd.info = adjustIfModule( new Completer(ctx.owner, subReader(start, end)) with SymbolLoaders.SecondCompleter) rootd.flags = flags &~ Touched // allow one more completion @@ -886,7 +859,7 @@ class TreeUnpickler(reader: TastyReader, tastyName: TastyName.Table, posUnpickle val localCtx = if (name == nme.CONSTRUCTOR) ctx.addMode(Mode.InSuperCall) else ctx val qual = readTerm()(localCtx) - val unshadowed = if (name.isShadowedName) name.revertShadowed else name + val unshadowed = name.exclude(ShadowedName) untpd.Select(qual, unshadowed).withType(tpf(qual.tpe.widenIfUnstable)) } @@ -905,9 +878,9 @@ class TreeUnpickler(reader: TastyReader, tastyName: TastyName.Table, posUnpickle case SELECT => def readRest(name: Name, sig: Signature) = completeSelect(name, TermRef.withSig(_, name.asTermName, sig)) - readNameSplitSig match { - case name: Name => readRest(name, Signature.NotAMethod) - case (name: Name, sig: Signature) => readRest(name, sig) + readName() match { + case SignedName(name, sig) => readRest(name, sig) + case name => readRest(name, Signature.NotAMethod) } case SELECTtpt => val name = readName().toTypeName @@ -1059,7 +1032,13 @@ class TreeUnpickler(reader: TastyReader, tastyName: TastyName.Table, posUnpickle } def readCases(end: Addr)(implicit ctx: Context): List[CaseDef] = - collectWhile(nextByte == CASEDEF && currentAddr != end) { readCase()(ctx.fresh.setNewScope) } + collectWhile((nextByte == CASEDEF || nextByte == SHARED) && currentAddr != end) { + if (nextByte == SHARED) { + readByte() + forkAt(readAddr()).readCase()(ctx.fresh.setNewScope) + } + else readCase()(ctx.fresh.setNewScope) + } def readCase()(implicit ctx: Context): CaseDef = { val start = currentAddr diff --git a/compiler/src/dotty/tools/dotc/core/unpickleScala2/PickleBuffer.scala b/compiler/src/dotty/tools/dotc/core/unpickleScala2/PickleBuffer.scala index 6ee9f1f9e..2a789dca9 100644 --- a/compiler/src/dotty/tools/dotc/core/unpickleScala2/PickleBuffer.scala +++ b/compiler/src/dotty/tools/dotc/core/unpickleScala2/PickleBuffer.scala @@ -224,12 +224,12 @@ object PickleBuffer { DEFAULTPARAM -> (DefaultParameterized, Trait), BRIDGE -> Bridge, ACCESSOR -> Accessor, - SUPERACCESSOR -> SuperAccessor, + SUPERACCESSOR -> Scala2SuperAccessor, PARAMACCESSOR -> ParamAccessor, MODULEVAR -> Scala2ModuleVar, LAZY -> Lazy, MIXEDIN -> (MixedIn, Scala2Existential), - EXPANDEDNAME -> ExpandedName, + EXPANDEDNAME -> Scala2ExpandedName, IMPLCLASS -> (Scala2PreSuper, ImplClass), SPECIALIZED -> Specialized, VBRIDGE -> VBridge, diff --git a/compiler/src/dotty/tools/dotc/core/unpickleScala2/Scala2Unpickler.scala b/compiler/src/dotty/tools/dotc/core/unpickleScala2/Scala2Unpickler.scala index cf99bb022..1db3ebcb0 100644 --- a/compiler/src/dotty/tools/dotc/core/unpickleScala2/Scala2Unpickler.scala +++ b/compiler/src/dotty/tools/dotc/core/unpickleScala2/Scala2Unpickler.scala @@ -9,6 +9,7 @@ import java.lang.Double.longBitsToDouble import Contexts._, Symbols._, Types._, Scopes._, SymDenotations._, Names._, NameOps._ import StdNames._, Denotations._, NameOps._, Flags._, Constants._, Annotations._ +import NameKinds.{Scala2MethodNameKinds, SuperAccessorName, ExpandedName} import dotty.tools.dotc.typer.ProtoTypes.{FunProtoTyped, FunProto} import util.Positions._ import dotty.tools.dotc.ast.{tpd, Trees, untpd}, ast.tpd._ @@ -18,6 +19,7 @@ import printing.Printer import io.AbstractFile import util.common._ import typer.Checking.checkNonCyclic +import transform.SymUtils._ import PickleBuffer._ import PickleFormat._ import Decorators._ @@ -361,7 +363,7 @@ class Scala2Unpickler(bytes: Array[Byte], classRoot: ClassDenotation, moduleClas } def slowSearch(name: Name): Symbol = - owner.info.decls.find(_.name == name).getOrElse(NoSymbol) + owner.info.decls.find(_.name == name) def nestedObjectSymbol: Symbol = { // If the owner is overloaded (i.e. a method), it's not possible to select the @@ -420,10 +422,10 @@ class Scala2Unpickler(bytes: Array[Byte], classRoot: ClassDenotation, moduleClas // symbols that were pickled with Pickler.writeSymInfo val nameref = readNat() - val name0 = at(nameref, readName) + var name = at(nameref, readName) val owner = readSymbolRef() - var flags = unpickleScalaFlags(readLongNat(), name0.isTypeName) + var flags = unpickleScalaFlags(readLongNat(), name.isTypeName) if (flags is DefaultParameter) { // DefaultParameterized flag now on method, not parameter //assert(flags is Param, s"$name0 in $owner") @@ -431,18 +433,33 @@ class Scala2Unpickler(bytes: Array[Byte], classRoot: ClassDenotation, moduleClas owner.setFlag(DefaultParameterized) } - val name1 = name0.adjustIfModuleClass(flags) - val name = if (name1 == nme.TRAIT_CONSTRUCTOR) nme.CONSTRUCTOR else name1 + name = name.adjustIfModuleClass(flags) + if (flags is Method) { + name = + if (name == nme.TRAIT_CONSTRUCTOR) nme.CONSTRUCTOR + else name.asTermName.unmangle(Scala2MethodNameKinds) + } + if ((flags is Scala2ExpandedName) && name.isSimple) { + name = name.unmangle(ExpandedName) + flags = flags &~ Scala2ExpandedName + } + if (flags is Scala2SuperAccessor) { + name = name.asTermName.unmangle(SuperAccessorName) + flags = flags &~ Scala2SuperAccessor + } - def isClassRoot = (name == classRoot.name) && (owner == classRoot.owner) && !(flags is ModuleClass) - def isModuleClassRoot = (name == moduleClassRoot.name) && (owner == moduleClassRoot.owner) && (flags is Module) - def isModuleRoot = (name == moduleClassRoot.name.sourceModuleName) && (owner == moduleClassRoot.owner) && (flags is Module) + val mname = name.mangled + def nameMatches(rootName: Name) = mname == rootName.mangled + def isClassRoot = nameMatches(classRoot.name) && (owner == classRoot.owner) && !(flags is ModuleClass) + def isModuleClassRoot = nameMatches(moduleClassRoot.name) && (owner == moduleClassRoot.owner) && (flags is Module) + def isModuleRoot = nameMatches(moduleClassRoot.name.sourceModuleName) && (owner == moduleClassRoot.owner) && (flags is Module) //if (isClassRoot) println(s"classRoot of $classRoot found at $readIndex, flags = $flags") // !!! DEBUG //if (isModuleRoot) println(s"moduleRoot of $moduleRoot found at $readIndex, flags = $flags") // !!! DEBUG //if (isModuleClassRoot) println(s"moduleClassRoot of $moduleClassRoot found at $readIndex, flags = $flags") // !!! DEBUG def completeRoot(denot: ClassDenotation, completer: LazyType): Symbol = { + denot.name = name denot.setFlag(flags) denot.resetFlag(Touched) // allow one more completion denot.info = completer @@ -470,7 +487,7 @@ class Scala2Unpickler(bytes: Array[Byte], classRoot: ClassDenotation, moduleClas var flags1 = flags if (flags is TypeParam) { name1 = name1.expandedName(owner) - flags1 |= owner.typeParamCreationFlags | ExpandedName + flags1 |= owner.typeParamCreationFlags } ctx.newSymbol(owner, name1, flags1, localMemberUnpickler, coord = start) case CLASSsym => @@ -546,9 +563,9 @@ class Scala2Unpickler(bytes: Array[Byte], classRoot: ClassDenotation, moduleClas else tp1 if (denot.isConstructor) addConstructorTypeParams(denot) if (atEnd) { - assert(!(denot is SuperAccessor), denot) + assert(!denot.isSuperAccessor, denot) } else { - assert(denot is (SuperAccessor | ParamAccessor), denot) + assert(denot.is(ParamAccessor) || denot.isSuperAccessor, denot) def disambiguate(alt: Symbol) = { // !!! DEBUG ctx.debugTraceIndented(s"disambiguating ${denot.info} =:= ${denot.owner.thisType.memberInfo(alt)} ${denot.owner}") { denot.info matches denot.owner.thisType.memberInfo(alt) @@ -1022,7 +1039,7 @@ class Scala2Unpickler(bytes: Array[Byte], classRoot: ClassDenotation, moduleClas val rhs = readTreeRef() val params = until(end, readIdentRef) val ldef = DefDef(symbol.asTerm, rhs) - def isCaseLabel(sym: Symbol) = sym.name.startsWith(nme.CASEkw) + def isCaseLabel(sym: Symbol) = sym.name.startsWith(nme.CASEkw.toString) if (isCaseLabel(symbol)) ldef else Block(ldef :: Nil, Apply(Ident(symbol.termRef), Nil)) |