From 0ad1cd816bc1537ad332addabb0ff6c293e3e0a0 Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Fri, 24 Mar 2017 13:20:01 +0100 Subject: Add default getter names Plus various bug fixes and filling in missing functionality --- compiler/src/dotty/tools/dotc/core/NameInfos.scala | 17 +++- compiler/src/dotty/tools/dotc/core/NameOps.scala | 69 +++++++++---- compiler/src/dotty/tools/dotc/core/Names.scala | 111 ++++++++++++++++----- compiler/src/dotty/tools/dotc/core/StdNames.scala | 5 + .../dotty/tools/dotc/core/tasty/NameBuffer.scala | 2 + .../tools/dotc/core/tasty/TreeUnpickler.scala | 2 +- .../dotc/core/unpickleScala2/Scala2Unpickler.scala | 5 +- .../dotty/tools/dotc/transform/ResolveSuper.scala | 11 +- .../dotty/tools/dotc/transform/TreeChecker.scala | 4 +- 9 files changed, 169 insertions(+), 57 deletions(-) (limited to 'compiler') diff --git a/compiler/src/dotty/tools/dotc/core/NameInfos.scala b/compiler/src/dotty/tools/dotc/core/NameInfos.scala index daaa5c4e1..c5b83b0dc 100644 --- a/compiler/src/dotty/tools/dotc/core/NameInfos.scala +++ b/compiler/src/dotty/tools/dotc/core/NameInfos.scala @@ -2,6 +2,7 @@ package dotty.tools.dotc package core import Names._ +import NameOps._ import StdNames._ /** Additional info associated with a name. At a minimum its kind and @@ -20,6 +21,7 @@ object NameInfo { val TermNameKind = 0 val QualifiedKind = 1 val ModuleClassKind = 2 + val DefaultGetterKind = 3 val qualifier: Map[String, SimpleTermName => Qualified] = Map("." -> Select, @@ -43,7 +45,7 @@ object NameInfo { def kind = QualifiedKind override def map(f: SimpleTermName => SimpleTermName): NameInfo = newLikeThis(f(name)) def mkString(underlying: TermName) = s"$underlying$separator$name" - override def toString = s"$getClass($name)" + override def toString = s"${getClass.getSimpleName}($name)" } case class Select(val name: SimpleTermName) extends Qualified { @@ -66,6 +68,19 @@ object NameInfo { def newLikeThis(name: SimpleTermName) = TraitSetter(name) } + trait Numbered extends NameInfo { + def num: Int + override def toString = s"${getClass.getSimpleName}($num)" + } + + case class DefaultGetter(val num: Int) extends Numbered { + def kind = DefaultGetterKind + def mkString(underlying: TermName) = { + val prefix = if (underlying.isConstructorName) nme.DEFAULT_GETTER_INIT else underlying + prefix.toString + nme.DEFAULT_GETTER + (num + 1) + } + } + val ModuleClass = new NameInfo { def kind = ModuleClassKind def mkString(underlying: TermName) = underlying + "$" diff --git a/compiler/src/dotty/tools/dotc/core/NameOps.scala b/compiler/src/dotty/tools/dotc/core/NameOps.scala index 65669a871..eb284168a 100644 --- a/compiler/src/dotty/tools/dotc/core/NameOps.scala +++ b/compiler/src/dotty/tools/dotc/core/NameOps.scala @@ -102,8 +102,10 @@ object NameOps { 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) + case _ => + false } /** If the name ends with $nn where nn are @@ -164,7 +166,8 @@ object NameOps { def expandedName(prefix: Name, separator: Name = nme.EXPAND_SEPARATOR): N = likeTyped( if (Config.semanticNames) - prefix.derived(NameInfo.qualifier(separator.toString)(name.asSimpleName)) + prefix.derived(NameInfo.qualifier(separator.toString)(name.toSimpleName)) + // note: expanded name may itself be expanded. For example, look at javap of scala.App.initCode else prefix ++ separator ++ name) def expandedName(prefix: Name): N = expandedName(prefix, nme.EXPAND_SEPARATOR) @@ -247,7 +250,7 @@ object NameOps { */ def unmangleClassName: N = if (Config.semanticNames && name.isSimple && name.isTypeName) - if (name.endsWith(MODULE_SUFFIX)) + if (name.endsWith(MODULE_SUFFIX) && !tpnme.falseModuleClassNames.contains(name.asTypeName)) likeTyped(name.dropRight(MODULE_SUFFIX.length).moduleClassName) else name else name @@ -404,44 +407,60 @@ object NameOps { 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).asTermName + val start = name.lastPart.indexOfSlice(TRAIT_SETTER_SEPARATOR) + TRAIT_SETTER_SEPARATOR.length + val end = name.lastPart.indexOfSlice(SETTER_SUFFIX) + name.mapLast(n => (n.slice(start, end) ++ LOCAL_SUFFIX).asSimpleName) } else getterName.fieldName } - else name ++ LOCAL_SUFFIX + else name.mapLast(n => (n ++ LOCAL_SUFFIX).asSimpleName) 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 + name.mapLast(n => n.take(n.length - SETTER_SUFFIX.length).asSimpleName) } def fieldToGetter: TermName = { assert(name.isFieldName) - name.take(name.length - LOCAL_SUFFIX.length).asTermName + name.mapLast(n => n.take(n.length - LOCAL_SUFFIX.length).asSimpleName) } /** Nominally, name$default$N, encoded for * @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 - } + def defaultGetterName(pos: Int): TermName = + if (Config.semanticNames) name.derived(NameInfo.DefaultGetter(pos)) + else { + val prefix = if (name.isConstructorName) DEFAULT_GETTER_INIT else name + prefix ++ DEFAULT_GETTER ++ (pos + 1).toString + } /** Nominally, name from name$default$N, CONSTRUCTOR for */ - 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 + def defaultGetterToMethod: TermName = + if (Config.semanticNames) + name rewrite { + case DerivedTermName(methName, NameInfo.DefaultGetter(_)) => methName + } + else mangledDefaultGetterToMethod + + def mangledDefaultGetterToMethod: 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 = { + def defaultGetterIndex: Int = + if (Config.semanticNames) + name collect { + case DerivedTermName(methName, NameInfo.DefaultGetter(num)) => num + } getOrElse -1 + else mangledDefaultGetterIndex + + def mangledDefaultGetterIndex: 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)) @@ -529,6 +548,14 @@ object NameOps { } def inlineAccessorName = nme.INLINE_ACCESSOR_PREFIX ++ name ++ "$" + + def unmangleMethodName: TermName = + if (Config.semanticNames && name.isSimple) { + val idx = name.mangledDefaultGetterIndex + if (idx >= 0) name.mangledDefaultGetterToMethod.defaultGetterName(idx) + else name + } + else name } private final val FalseSuper = "$$super".toTermName diff --git a/compiler/src/dotty/tools/dotc/core/Names.scala b/compiler/src/dotty/tools/dotc/core/Names.scala index 84b2fab0c..22d0588f4 100644 --- a/compiler/src/dotty/tools/dotc/core/Names.scala +++ b/compiler/src/dotty/tools/dotc/core/Names.scala @@ -66,6 +66,9 @@ object Names { def asSimpleName: SimpleTermName def toSimpleName: SimpleTermName 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 /** A name of the same kind as this name and with same characters as given `name` */ def likeKinded(name: Name): ThisName @@ -88,9 +91,8 @@ object Names { /** A more efficient version of concatenation */ def ++ (other: Name): ThisName = ++ (other.toString) - def ++ (other: String): ThisName - - def replace(from: Char, to: Char): ThisName = likeKinded(asSimpleName.replace(from, to)) + def ++ (other: String): ThisName = mapLast(n => termName(n.toString + other)) + def replace(from: Char, to: Char): ThisName = mapParts(_.replace(from, to)) def isEmpty: Boolean @@ -198,8 +200,6 @@ object Names { def apply(n: Int) = chrs(start + n) - def ++ (other: String): SimpleTermName = termName(toString + other) - private def contains(ch: Char): Boolean = { var i = 0 while (i < length && chrs(start + i) != ch) i += 1 @@ -220,19 +220,29 @@ object Names { i > str.length } - override def replace(from: Char, to: Char): ThisName = { + 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 } - likeKinded(termName(cs, 0, length)) + termName(cs, 0, length) } def isSimple = true def asSimpleName = this def toSimpleName = this - def rewrite(f: PartialFunction[Name, Name]): ThisName = likeKinded(f(this)) + def rewrite(f: PartialFunction[Name, Name]): ThisName = + if (f.isDefinedAt(this)) likeKinded(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) + + /*def exists(p: Char => Boolean): Boolean = { + var i = 0 + while (i < length && !p(chrs(start + i))) i += 1 + i < length + }*/ def encode: SimpleTermName = if (dontEncode(toTermName)) this else NameTransformer.encode(this) @@ -254,8 +264,6 @@ object Names { class TypeName(val toTermName: TermName) extends Name { - def ++ (other: String): ThisName = toTermName.++(other).toTypeName - def isEmpty = toTermName.isEmpty def encode = toTermName.encode.toTypeName @@ -274,6 +282,9 @@ object Names { def asSimpleName = toTermName.asSimpleName def toSimpleName = toTermName.toSimpleName 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 likeKinded(name: Name): TypeName = name.toTypeName @@ -298,10 +309,6 @@ object Names { case qual: NameInfo.Qualified => qual.name case _ => underlying.lastPart } - def ++ (other: String): ThisName = info match { - case qual: NameInfo.Qualified => underlying.derived(qual.map(_ ++ other)) - case _ => (underlying ++ other).derived(info) - } override def toString = info.mkString(underlying) override def debugString = s"${underlying.debugString}[$info]" @@ -315,6 +322,25 @@ object Names { case qual: NameInfo.Qualified => 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: NameInfo.Qualified => None + case _ => underlying.collect(f) + } + + def mapLast(f: SimpleTermName => SimpleTermName): ThisName = + info match { + case qual: NameInfo.Qualified => underlying.derived(qual.map(f)) + case _ => underlying.mapLast(f).derived(info) + } + + def mapParts(f: SimpleTermName => SimpleTermName): ThisName = + info match { + case qual: NameInfo.Qualified => underlying.mapParts(f).derived(qual.map(f)) + case _ => underlying.mapParts(f).derived(info) + } } // Nametable @@ -478,23 +504,54 @@ object Names { } implicit val NameOrdering: Ordering[Name] = new Ordering[Name] { + private def compareInfos(x: NameInfo, y: NameInfo): Int = + if (x.kind != y.kind) x.kind - y.kind + else x match { + case x: NameInfo.Qualified => + y match { + case y: NameInfo.Qualified => + val s = x.separator.compareTo(y.separator) + if (s == 0) compareSimpleNames(x.name, y.name) else s + } + case x: NameInfo.Numbered => + y match { + case y: NameInfo.Numbered => + 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/StdNames.scala b/compiler/src/dotty/tools/dotc/core/StdNames.scala index 9e05d4bce..1a65556ba 100644 --- a/compiler/src/dotty/tools/dotc/core/StdNames.scala +++ b/compiler/src/dotty/tools/dotc/core/StdNames.scala @@ -546,6 +546,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 = "&" diff --git a/compiler/src/dotty/tools/dotc/core/tasty/NameBuffer.scala b/compiler/src/dotty/tools/dotc/core/tasty/NameBuffer.scala index 7ac505a20..b45255eb8 100644 --- a/compiler/src/dotty/tools/dotc/core/tasty/NameBuffer.scala +++ b/compiler/src/dotty/tools/dotc/core/tasty/NameBuffer.scala @@ -36,6 +36,8 @@ class NameBuffer extends TastyBuffer(10000) { case _: NameInfo.Expand => Expanded } tcon(nameIndex(prefix, toTasty), nameIndex(qual.name)) + case DerivedTermName(prefix, NameInfo.DefaultGetter(num)) => + DefaultGetter(nameIndex(prefix, toTasty), num) case name1 => if (name1.isShadowedName) Shadowed(nameIndex(name1.revertShadowed, toTasty)) else toTasty(name1.asSimpleName) diff --git a/compiler/src/dotty/tools/dotc/core/tasty/TreeUnpickler.scala b/compiler/src/dotty/tools/dotc/core/tasty/TreeUnpickler.scala index 06165c6f4..d4269d6e4 100644 --- a/compiler/src/dotty/tools/dotc/core/tasty/TreeUnpickler.scala +++ b/compiler/src/dotty/tools/dotc/core/tasty/TreeUnpickler.scala @@ -90,7 +90,7 @@ class TreeUnpickler(reader: TastyReader, tastyName: TastyName.Table, posUnpickle case Shadowed(original) => toTermName(original).shadowedName case ModuleClass(original) => toTermName(original).moduleClassName.toTermName case SuperAccessor(accessed) => toTermName(accessed).superName - case DefaultGetter(meth, num) => ??? + case DefaultGetter(meth, num) => toTermName(meth).defaultGetterName(num) } private def qualTermName(qual: NameRef, name: NameRef, sep: String) = diff --git a/compiler/src/dotty/tools/dotc/core/unpickleScala2/Scala2Unpickler.scala b/compiler/src/dotty/tools/dotc/core/unpickleScala2/Scala2Unpickler.scala index a49379327..084b8d098 100644 --- a/compiler/src/dotty/tools/dotc/core/unpickleScala2/Scala2Unpickler.scala +++ b/compiler/src/dotty/tools/dotc/core/unpickleScala2/Scala2Unpickler.scala @@ -433,7 +433,10 @@ class Scala2Unpickler(bytes: Array[Byte], classRoot: ClassDenotation, moduleClas val name1 = name0.adjustIfModuleClass(flags) val name2 = if (name1 == nme.TRAIT_CONSTRUCTOR) nme.CONSTRUCTOR else name1 - val name = if (flags is ModuleClass) name2.unmangleClassName else name2 + val name = + if (flags is ModuleClass) name2.unmangleClassName + else if (flags is Method) name2.asTermName.unmangleMethodName + else name2 def isClassRoot = (name == classRoot.name) && (owner == classRoot.owner) && !(flags is ModuleClass) def isModuleClassRoot = (name == moduleClassRoot.name) && (owner == moduleClassRoot.owner) && (flags is Module) diff --git a/compiler/src/dotty/tools/dotc/transform/ResolveSuper.scala b/compiler/src/dotty/tools/dotc/transform/ResolveSuper.scala index 3a301167d..b6c28f570 100644 --- a/compiler/src/dotty/tools/dotc/transform/ResolveSuper.scala +++ b/compiler/src/dotty/tools/dotc/transform/ResolveSuper.scala @@ -18,6 +18,7 @@ import util.Positions._ import Names._ import collection.mutable import ResolveSuper._ +import config.Config /** This phase adds super accessors and method overrides where * linearization differs from Java's rule for default methods in interfaces. @@ -95,10 +96,12 @@ object ResolveSuper { var bcs = base.info.baseClasses.dropWhile(acc.owner != _).tail var sym: Symbol = NoSymbol val unexpandedAccName = - if (acc.is(ExpandedName)) // Cannot use unexpandedName because of #765. t2183.scala would fail if we did. - acc.name - .drop(acc.name.indexOfSlice(nme.EXPAND_SEPARATOR ++ nme.SUPER_PREFIX)) - .drop(nme.EXPAND_SEPARATOR.length) + if (acc.is(ExpandedName)) + if (Config.semanticNames) acc.name.unexpandedName + else // Cannot use unexpandedName because of #765. t2183.scala would fail if we did. + acc.name + .drop(acc.name.indexOfSlice(nme.EXPAND_SEPARATOR ++ nme.SUPER_PREFIX)) + .drop(nme.EXPAND_SEPARATOR.length) else acc.name val SuperAccessorName(memberName) = unexpandedAccName: Name // dotty deviation: ": Name" needed otherwise pattern type is neither a subtype nor a supertype of selector type ctx.debuglog(i"starting rebindsuper from $base of ${acc.showLocated}: ${acc.info} in $bcs, name = $memberName") diff --git a/compiler/src/dotty/tools/dotc/transform/TreeChecker.scala b/compiler/src/dotty/tools/dotc/transform/TreeChecker.scala index ebb5b605b..199fac82b 100644 --- a/compiler/src/dotty/tools/dotc/transform/TreeChecker.scala +++ b/compiler/src/dotty/tools/dotc/transform/TreeChecker.scala @@ -50,10 +50,10 @@ class TreeChecker extends Phase with SymTransformer { private val seenModuleVals = collection.mutable.HashMap[String, Symbol]() def isValidJVMName(name: Name) = - !name.exists(c => c == '.' || c == ';' || c =='[' || c == '/') + !name.toString.exists(c => c == '.' || c == ';' || c =='[' || c == '/') def isValidJVMMethodName(name: Name) = - !name.exists(c => c == '.' || c == ';' || c =='[' || c == '/' || c == '<' || c == '>') + !name.toString.exists(c => c == '.' || c == ';' || c =='[' || c == '/' || c == '<' || c == '>') def printError(str: String)(implicit ctx: Context) = { ctx.echo(Console.RED + "[error] " + Console.WHITE + str) -- cgit v1.2.3