From 19bc1ff09fa73e13be7e3464b8440c04b657aa82 Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Wed, 22 Mar 2017 17:59:25 +0100 Subject: Disentangle Names from Seqs Structured names are not Seqs anymmore. But the Seq behavior is required in many places that mangle names. As an intermediate step we drop the Seq basetype but add Seq behavior through a decorator. Most Seq operations only work on SimpleTermNames and their TypeName analogue, will throw an exception wehn called on structured names. --- .../tools/backend/jvm/DottyBackendInterface.scala | 4 +- compiler/src/dotty/tools/dotc/core/NameInfos.scala | 6 +- compiler/src/dotty/tools/dotc/core/NameOps.scala | 2 +- compiler/src/dotty/tools/dotc/core/Names.scala | 128 +++++++++------------ .../dotc/core/classfile/ClassfileParser.scala | 2 +- .../dotty/tools/dotc/core/tasty/NameBuffer.scala | 2 +- .../dotty/tools/dotc/core/tasty/TastyName.scala | 4 +- .../dotty/tools/dotc/core/tasty/TreePickler.scala | 2 +- .../src/dotty/tools/dotc/parsing/Scanners.scala | 2 +- compiler/src/dotty/tools/dotc/parsing/Tokens.scala | 2 +- 10 files changed, 71 insertions(+), 83 deletions(-) diff --git a/compiler/src/dotty/tools/backend/jvm/DottyBackendInterface.scala b/compiler/src/dotty/tools/backend/jvm/DottyBackendInterface.scala index 77e979e4d..309d97261 100644 --- a/compiler/src/dotty/tools/backend/jvm/DottyBackendInterface.scala +++ b/compiler/src/dotty/tools/backend/jvm/DottyBackendInterface.scala @@ -544,8 +544,8 @@ class DottyBackendInterface(outputDirectory: AbstractFile, val superCallsMap: Ma def toTermName: Name = n.toTermName def dropModule: Name = n.stripModuleClassSuffix - def len: Int = n.length - def offset: Int = n.start + def len: Int = n.toSimpleName.length + def offset: Int = n.toSimpleName.start def isTermName: Boolean = n.isTermName def startsWith(s: String): Boolean = n.startsWith(s) } diff --git a/compiler/src/dotty/tools/dotc/core/NameInfos.scala b/compiler/src/dotty/tools/dotc/core/NameInfos.scala index 51c04b9e4..650a0c0a4 100644 --- a/compiler/src/dotty/tools/dotc/core/NameInfos.scala +++ b/compiler/src/dotty/tools/dotc/core/NameInfos.scala @@ -6,9 +6,11 @@ import Names._ /** Additional info associated with a name. At a minimum its kind and * a way to turn it into a string. */ -abstract class NameInfo { +abstract class NameInfo extends util.DotClass { def kind: NameInfo.Kind def mkString(underlying: TermName): String + def contains(ch: Char): Boolean = false + def ++(other: String): NameInfo = unsupported("++") } object NameInfo { @@ -30,6 +32,8 @@ object NameInfo { case class Qualified(name: TermName, separator: String) extends NameInfo { def kind = QualifiedKind def mkString(underlying: TermName) = s"$underlying$separator$name" + override def contains(ch: Char): Boolean = name.contains(ch) + override def ++(other: String): NameInfo = Qualified(name ++ other, separator) override def toString = s"Qualified($name, $separator)" } diff --git a/compiler/src/dotty/tools/dotc/core/NameOps.scala b/compiler/src/dotty/tools/dotc/core/NameOps.scala index 38eb21446..27065a50a 100644 --- a/compiler/src/dotty/tools/dotc/core/NameOps.scala +++ b/compiler/src/dotty/tools/dotc/core/NameOps.scala @@ -390,7 +390,7 @@ object NameOps { // 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 + (name.slice(start, end) ++ LOCAL_SUFFIX).asTermName } else getterName.fieldName } else name ++ LOCAL_SUFFIX diff --git a/compiler/src/dotty/tools/dotc/core/Names.scala b/compiler/src/dotty/tools/dotc/core/Names.scala index 9488aef61..9ec9e73f2 100644 --- a/compiler/src/dotty/tools/dotc/core/Names.scala +++ b/compiler/src/dotty/tools/dotc/core/Names.scala @@ -39,20 +39,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 */ - def start: Int - - /** The length of the names */ - override def length: Int - /** Is this name a type name? */ def isTypeName: Boolean @@ -71,6 +62,8 @@ object Names { /** This name downcasted to a term name */ def asTermName: TermName + def toSimpleName: SimpleTermName = this.asInstanceOf[SimpleTermName] + /** A name of the same kind as this name and with same characters as given `name` */ def fromName(name: Name): ThisName @@ -81,17 +74,6 @@ object Names { 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('$')) fromName(termName(NameTransformer.decode(toString))) @@ -103,41 +85,13 @@ object Names { /** A more efficient version of concatenation */ def ++ (other: Name): ThisName = ++ (other.toString) + def ++ (other: String): ThisName - def ++ (other: String): ThisName = fromName(termName(toString + other)) - - 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 - } - fromName(termName(cs, 0, length)) - } - - def contains(ch: Char): Boolean = { - var i = 0 - while (i < length && chrs(start + i) != ch) i += 1 - i < length - } + def replace(from: Char, to: Char): ThisName = fromName(toSimpleName.replace(from, to)) - def firstChar = chrs(start) - - // ----- Collections integration ------------------------------------- - - override protected[this] def thisCollection: WrappedString = new WrappedString(repr.toString) - override protected[this] def toCollection(repr: Name): WrappedString = new WrappedString(repr.toString) - - override protected[this] def newBuilder: Builder[Char, Name] = unsupported("newBuilder") - - override def apply(index: Int): Char = chrs(start + index) - - override def slice(from: Int, until: Int): ThisName = - fromName(termName(chrs, start + from, until - from)) + def contains(ch: Char): Boolean override def equals(that: Any) = this eq that.asInstanceOf[AnyRef] - - override def seq = toCollection(this) } abstract class TermName extends Name { @@ -226,12 +180,30 @@ object Names { ownKind == kind || !NameInfo.definesNewName(ownKind) && ownKind > kind && underlying.is(kind) } - - override protected[this] def newBuilder: Builder[Char, Name] = termNameBuilder } 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 ++ (other: String): ThisName = termName(toString + other) + + def contains(ch: Char): Boolean = { + var i = 0 + while (i < length && chrs(start + i) != ch) i += 1 + i < length + } + + override 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 + } + fromName(termName(cs, 0, length)) + } + override def hashCode: Int = start override def toString = @@ -241,8 +213,12 @@ object Names { } class TypeName(val toTermName: TermName) extends Name { - def start = toTermName.start - override def length = toTermName.length + + override def toSimpleName: SimpleTermName = toTermName.toSimpleName + + def ++ (other: String): ThisName = toTermName.++(other).toTypeName + + def contains(ch: Char): Boolean = toTermName.contains(ch) type ThisName = TypeName def isTypeName = true @@ -257,9 +233,6 @@ object Names { def without(kind: NameInfo.Kind): TypeName = toTermName.without(kind).toTypeName def is(kind: NameInfo.Kind) = toTermName.is(kind) - override protected[this] def newBuilder: Builder[Char, Name] = - termNameBuilder.mapResult(_.toTypeName) - override def toString = toTermName.toString override def debugString = toTermName.debugString + "/T" } @@ -269,8 +242,8 @@ object Names { */ class DerivedTermName(override val underlying: TermName, override val info: NameInfo) extends TermName { - def start = underlying.start - override def length = underlying.length + def ++ (other: String): ThisName = derived(info ++ other) + def contains(ch: Char): Boolean = underlying.contains(ch) || info.contains(ch) override def toString = info.mkString(underlying) override def debugString = s"${underlying.debugString}[$info]" } @@ -319,7 +292,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) @@ -385,7 +358,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) } @@ -397,7 +370,7 @@ 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) @@ -411,20 +384,31 @@ object Names { val EmptyTypeName = EmptyTermName.toTypeName // can't move CONSTRUCTOR/EMPTY_PACKAGE to `nme` because of bootstrap failures in `encode`. - val CONSTRUCTOR = termName("") - val STATIC_CONSTRUCTOR = termName("") - val EMPTY_PACKAGE = termName("") + val CONSTRUCTOR: TermName = termName("") + val STATIC_CONSTRUCTOR: TermName = termName("") + val EMPTY_PACKAGE: TermName = termName("") val dontEncode = Set[TermName](CONSTRUCTOR, EMPTY_PACKAGE) 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.fromName(termName(s.toCharArray, 0, s.length))) - def apply(): Builder[Char, Name] = termNameBuilder + def typeNameBuilder: Builder[Char, TypeName] = + StringBuilder.newBuilder.mapResult(termName(_).toTypeName) + + implicit class nameToSeq(val name: Name) extends IndexedSeqOptimized[Char, Name] { + def length = name.toSimpleName.length + def apply(n: Int) = name.toSimpleName.apply(n) + override protected[this] def newBuilder: Builder[Char, Name] = + if (name.isTypeName) typeNameBuilder else termNameBuilder + + def seq: WrappedString = new WrappedString(name.toString) + override protected[this] def thisCollection: WrappedString = seq + def startsWith(name: Name): Boolean = startsWith(name.toString) + def endsWith(name: Name): Boolean = endsWith(name.toString) + def indexOfSlice(name: Name): Int = indexOfSlice(name.toString) + def lastIndexOfSlice(name: Name): Int = lastIndexOfSlice(name.toString) + def containsSlice(name: Name): Boolean = containsSlice(name.toString) } implicit val NameOrdering: Ordering[Name] = new Ordering[Name] { diff --git a/compiler/src/dotty/tools/dotc/core/classfile/ClassfileParser.scala b/compiler/src/dotty/tools/dotc/core/classfile/ClassfileParser.scala index a0e4cc62b..131c9cf9b 100644 --- a/compiler/src/dotty/tools/dotc/core/classfile/ClassfileParser.scala +++ b/compiler/src/dotty/tools/dotc/core/classfile/ClassfileParser.scala @@ -256,7 +256,7 @@ class ClassfileParser( def subName(isDelimiter: Char => Boolean): TermName = { val start = index while (!isDelimiter(sig(index))) { index += 1 } - sig.slice(start, index) + sig.slice(start, index).asTermName } // Warning: sigToType contains nested completers which might be forced in a later run! // So local methods need their own ctx parameters. diff --git a/compiler/src/dotty/tools/dotc/core/tasty/NameBuffer.scala b/compiler/src/dotty/tools/dotc/core/tasty/NameBuffer.scala index 3ff7298ce..b05e6ef21 100644 --- a/compiler/src/dotty/tools/dotc/core/tasty/NameBuffer.scala +++ b/compiler/src/dotty/tools/dotc/core/tasty/NameBuffer.scala @@ -27,7 +27,7 @@ class NameBuffer extends TastyBuffer(10000) { def nameIndex(name: Name): NameRef = { val tname = if (name.isShadowedName) Shadowed(nameIndex(name.revertShadowed)) - else Simple(name.toTermName) + else Simple(name.toTermName.toSimpleName) nameIndex(tname) } diff --git a/compiler/src/dotty/tools/dotc/core/tasty/TastyName.scala b/compiler/src/dotty/tools/dotc/core/tasty/TastyName.scala index 26807115c..da205542e 100644 --- a/compiler/src/dotty/tools/dotc/core/tasty/TastyName.scala +++ b/compiler/src/dotty/tools/dotc/core/tasty/TastyName.scala @@ -3,7 +3,7 @@ package dotc package core package tasty -import core.Names.TermName +import core.Names.SimpleTermName import collection.mutable abstract class TastyName @@ -12,7 +12,7 @@ object TastyName { case class NameRef(index: Int) extends AnyVal - case class Simple(name: TermName) extends TastyName + case class Simple(name: SimpleTermName) 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 diff --git a/compiler/src/dotty/tools/dotc/core/tasty/TreePickler.scala b/compiler/src/dotty/tools/dotc/core/tasty/TreePickler.scala index 902d01c21..7a6b6b7ea 100644 --- a/compiler/src/dotty/tools/dotc/core/tasty/TreePickler.scala +++ b/compiler/src/dotty/tools/dotc/core/tasty/TreePickler.scala @@ -576,7 +576,7 @@ class TreePickler(pickler: TastyPickler) { } def qualifiedName(sym: Symbol)(implicit ctx: Context): TastyName = - if (sym.isRoot || sym.owner.isRoot) TastyName.Simple(sym.name.toTermName) + if (sym.isRoot || sym.owner.isRoot) TastyName.Simple(sym.name.toTermName.toSimpleName) else TastyName.Qualified(nameIndex(qualifiedName(sym.owner)), nameIndex(sym.name)) def pickleModifiers(sym: Symbol)(implicit ctx: Context): Unit = { diff --git a/compiler/src/dotty/tools/dotc/parsing/Scanners.scala b/compiler/src/dotty/tools/dotc/parsing/Scanners.scala index ff5019dc9..bcf6eb4a5 100644 --- a/compiler/src/dotty/tools/dotc/parsing/Scanners.scala +++ b/compiler/src/dotty/tools/dotc/parsing/Scanners.scala @@ -101,7 +101,7 @@ object Scanners { target.name = flushBuf(litBuf).toTermName target.token = idtoken if (idtoken == IDENTIFIER) { - val idx = target.name.start + val idx = target.name.toSimpleName.start target.token = toToken(idx) } } diff --git a/compiler/src/dotty/tools/dotc/parsing/Tokens.scala b/compiler/src/dotty/tools/dotc/parsing/Tokens.scala index d2ea9240c..96ae25c9e 100644 --- a/compiler/src/dotty/tools/dotc/parsing/Tokens.scala +++ b/compiler/src/dotty/tools/dotc/parsing/Tokens.scala @@ -129,7 +129,7 @@ abstract class TokensCommon { final val lastParen = RBRACE def buildKeywordArray(keywords: TokenSet) = { - def start(tok: Token) = tokenString(tok).toTermName.start + def start(tok: Token) = tokenString(tok).toTermName.toSimpleName.start def sourceKeywords = keywords.toList.filter { (kw: Token) => val ts = tokenString(kw) (ts != null) && !ts.contains(' ') -- cgit v1.2.3