From 1d2b6054291ab6f71829b860d22bf5079b50c4a9 Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Fri, 28 Apr 2017 10:14:01 +0200 Subject: [PATCH 01/31] Address reviewers comments --- compiler/src/dotty/tools/dotc/core/Names.scala | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/core/Names.scala b/compiler/src/dotty/tools/dotc/core/Names.scala index 9fb583373b77..66f2580f6cd3 100644 --- a/compiler/src/dotty/tools/dotc/core/Names.scala +++ b/compiler/src/dotty/tools/dotc/core/Names.scala @@ -297,16 +297,16 @@ object Names { // because asserts are caught in exception handlers which might // cause other failures. In that case the first, important failure // is lost. - println("Backend should not call Name#toString, Name#mangledString should be used instead.") - new Error().printStackTrace() + System.err.println("Backend should not call Name#toString, Name#mangledString should be used instead.") + Thread.dumpStack() assert(false) } } new String(chrs, start, length) } - /** It's OK to take a toString if the stacktrace does not occur a method - * in GenBCode or it also contains one of the whitelisted methods below. + /** It's OK to take a toString if the stacktrace does not contain a method + * from GenBCode or it also contains one of the whitelisted methods below. */ private def toStringOK = { val trace = Thread.currentThread.getStackTrace From 98e26abc8a625b53bd621da73c7d19a3f12cb917 Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Thu, 30 Mar 2017 13:44:02 +0200 Subject: [PATCH 02/31] Revise qualified names 1. Fix problem in fullNameSeparated 2. Revise expandedName operations --- .../src/dotty/tools/dotc/core/SymDenotations.scala | 12 +++--------- 1 file changed, 3 insertions(+), 9 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/core/SymDenotations.scala b/compiler/src/dotty/tools/dotc/core/SymDenotations.scala index 6902617e0b13..07ca912dd7c5 100644 --- a/compiler/src/dotty/tools/dotc/core/SymDenotations.scala +++ b/compiler/src/dotty/tools/dotc/core/SymDenotations.scala @@ -107,7 +107,7 @@ object SymDenotations { class SymDenotation private[SymDenotations] ( symbol: Symbol, ownerIfExists: Symbol, - initName: Name, + final val name: Name, initFlags: FlagSet, initInfo: Type, initPrivateWithin: Symbol = NoSymbol) extends SingleDenotation(symbol) { @@ -131,12 +131,6 @@ object SymDenotations { 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 @@ -1218,12 +1212,12 @@ object SymDenotations { class ClassDenotation private[SymDenotations] ( symbol: Symbol, ownerIfExists: Symbol, - initName: Name, + name: Name, initFlags: FlagSet, initInfo: Type, initPrivateWithin: Symbol, initRunId: RunId) - extends SymDenotation(symbol, ownerIfExists, initName, initFlags, initInfo, initPrivateWithin) { + extends SymDenotation(symbol, ownerIfExists, name, initFlags, initInfo, initPrivateWithin) { import util.LRUCache From debdafacb2a7047b5d11973dc3b6374cbc60a4dd Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Tue, 4 Apr 2017 09:43:09 +0200 Subject: [PATCH 03/31] Use unencoded names internally Don't encode operator symbols internally. Instead, let the backend convert them when generating code. # Conflicts: # compiler/src/dotty/tools/dotc/core/Names.scala # compiler/src/dotty/tools/dotc/core/StdNames.scala # compiler/src/dotty/tools/dotc/transform/PostTyper.scala # Conflicts: # compiler/src/dotty/tools/backend/jvm/DottyBackendInterface.scala # compiler/src/dotty/tools/dotc/core/Names.scala --- .../dotty/tools/dotc/core/Definitions.scala | 4 +- .../src/dotty/tools/dotc/core/Names.scala | 40 ++-- .../src/dotty/tools/dotc/core/StdNames.scala | 62 +++---- .../tools/dotc/core/SymDenotations.scala | 6 +- .../dotc/core/tasty/TastyUnpickler.scala | 2 +- .../core/unpickleScala2/Scala2Unpickler.scala | 5 +- .../dotty/tools/dotc/parsing/Scanners.scala | 2 + .../dotc/parsing/SymbolicXMLBuilder.scala | 2 +- .../tools/dotc/printing/RefinedPrinter.scala | 2 +- .../dotc/repl/CompilingInterpreter.scala | 4 +- .../tools/dotc/transform/PostTyper.scala | 2 +- .../tools/dotc/transform/SuperAccessors.scala | 2 +- .../dotc/transform/SyntheticMethods.scala | 2 +- .../tools/dotc/transform/TreeChecker.scala | 11 +- .../src/dotty/tools/dotc/typer/Namer.scala | 14 +- .../src/dotty/tools/dotc/typer/ReTyper.scala | 2 - .../dotty/tools/dotc/typer/RefChecks.scala | 2 +- .../src/dotty/tools/dotc/typer/Typer.scala | 7 +- .../src/dotty/tools/dotc/util/Chars.scala | 6 + .../tools/dotc/util/NameTransformer.scala | 174 +++++++----------- 20 files changed, 159 insertions(+), 192 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/core/Definitions.scala b/compiler/src/dotty/tools/dotc/core/Definitions.scala index 07d6702f4965..0920ca946598 100644 --- a/compiler/src/dotty/tools/dotc/core/Definitions.scala +++ b/compiler/src/dotty/tools/dotc/core/Definitions.scala @@ -137,7 +137,7 @@ class Definitions { } private def newMethod(cls: ClassSymbol, name: TermName, info: Type, flags: FlagSet = EmptyFlags): TermSymbol = - newSymbol(cls, name.encode, flags | Method, info).asTerm + newSymbol(cls, name, flags | Method, info).asTerm private def enterMethod(cls: ClassSymbol, name: TermName, info: Type, flags: FlagSet = EmptyFlags): TermSymbol = newMethod(cls, name, info, flags).entered @@ -301,7 +301,7 @@ class Definitions { lazy val ScalaPredefModuleRef = ctx.requiredModuleRef("scala.Predef") def ScalaPredefModule(implicit ctx: Context) = ScalaPredefModuleRef.symbol - lazy val Predef_ConformsR = ScalaPredefModule.requiredClass("$less$colon$less").typeRef + lazy val Predef_ConformsR = ScalaPredefModule.requiredClass("<:<").typeRef def Predef_Conforms(implicit ctx: Context) = Predef_ConformsR.symbol lazy val Predef_conformsR = ScalaPredefModule.requiredMethodRef("$conforms") def Predef_conforms(implicit ctx: Context) = Predef_conformsR.symbol diff --git a/compiler/src/dotty/tools/dotc/core/Names.scala b/compiler/src/dotty/tools/dotc/core/Names.scala index 66f2580f6cd3..ecee2068e816 100644 --- a/compiler/src/dotty/tools/dotc/core/Names.scala +++ b/compiler/src/dotty/tools/dotc/core/Names.scala @@ -8,6 +8,7 @@ import printing.{Showable, Texts, Printer} import Texts.Text import Decorators._ import Contexts.Context +import StdNames.str import collection.IndexedSeqOptimized import collection.generic.CanBuildFrom import collection.mutable.{ Builder, StringBuilder, AnyRefMap } @@ -67,7 +68,17 @@ object Names { def isSimple: Boolean def asSimpleName: SimpleTermName def toSimpleName: SimpleTermName - def mangled: Name + + @sharable // because it's just a cache for performance + private[this] var myMangled: Name = null + + protected[Names] def mangle: ThisName + + final def mangled: ThisName = { + if (myMangled == null) myMangled = mangle + myMangled.asInstanceOf[ThisName] + } + def mangledString: String = mangled.toString def rewrite(f: PartialFunction[Name, Name]): ThisName @@ -268,7 +279,7 @@ object Names { def isSimple = true def asSimpleName = this def toSimpleName = this - def mangled = this + final def mangle = encode def rewrite(f: PartialFunction[Name, Name]): ThisName = if (f.isDefinedAt(this)) likeSpaced(f(this)) else this @@ -277,11 +288,10 @@ object Names { def mapParts(f: SimpleTermName => SimpleTermName) = f(this) def encode: SimpleTermName = - if (dontEncode(toTermName)) this else NameTransformer.encode(this) + if (dontEncode(this)) this else NameTransformer.encode(this) /** Replace \$op_name's by corresponding operator symbols. */ - def decode: SimpleTermName = - if (contains('$')) termName(NameTransformer.decode(toString)) else this + def decode: SimpleTermName = NameTransformer.decode(this) def firstPart = this def lastPart = this @@ -324,6 +334,9 @@ object Names { .contains(elem.getMethodName)) } + def sliceToString(from: Int, end: Int) = + if (end <= from) "" else new String(chrs, start + from, end - from) + def debugString: String = toString } @@ -346,7 +359,7 @@ object Names { def isSimple = toTermName.isSimple def asSimpleName = toTermName.asSimpleName def toSimpleName = toTermName.toSimpleName - def mangled = toTermName.toSimpleName.toTypeName + final def mangle = toTermName.mangle.toTypeName def rewrite(f: PartialFunction[Name, Name]): ThisName = toTermName.rewrite(f).toTypeName def collect[T](f: PartialFunction[Name, T]): Option[T] = toTermName.collect(f) @@ -382,12 +395,8 @@ object Names { 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 toSimpleName = termName(toString) + final def mangle = encode.toSimpleName def rewrite(f: PartialFunction[Name, Name]): ThisName = if (f.isDefinedAt(this)) likeSpaced(f(this)) @@ -556,8 +565,13 @@ object Names { val STATIC_CONSTRUCTOR: TermName = termName("") val EMPTY_PACKAGE: TermName = termName("") val REFINEMENT: TermName = termName("") + val LOCALDUMMY_PREFIX: TermName = termName(" 0 && name(0) == '<' && + (dontEncodeNames.contains(name) || name.startsWith(str.LOCALDUMMY_PREFIX)) implicit val NameOrdering: Ordering[Name] = new Ordering[Name] { private def compareInfos(x: NameInfo, y: NameInfo): Int = diff --git a/compiler/src/dotty/tools/dotc/core/StdNames.scala b/compiler/src/dotty/tools/dotc/core/StdNames.scala index 444d400094e7..0b636f0e1f6b 100644 --- a/compiler/src/dotty/tools/dotc/core/StdNames.scala +++ b/compiler/src/dotty/tools/dotc/core/StdNames.scala @@ -16,14 +16,15 @@ object StdNames { /** Base strings from which synthetic names are derived. */ object str { - final val SETTER_SUFFIX = "_$eq" + final val SETTER_SUFFIX = "_=" 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 MODULE_SUFFIX = "$" + //final val NAME_JOIN = "$" final val DEFAULT_GETTER = "$default$" final val LOCALDUMMY_PREFIX = ">") - val DIV = encode("/") - val EQ = encode("==") - val EQL = encode("=") - val GE = encode(">=") - val GT = encode(">") - val HASHHASH = encode("##") - val LE = encode("<=") - val LSL = encode("<<") - val LSR = encode(">>>") - val LT = encode("<") - val MINUS = encode("-") - val MOD = encode("%") - val MUL = encode("*") - val NE = encode("!=") - val OR = encode("|") + val ADD : N = "+" + val AND : N = "&" + val ASR : N = ">>" + val DIV : N = "/" + val EQ : N = "==" + val EQL : N = "=" + val GE : N = ">=" + val GT : N = ">" + val HASHHASH : N = "##" + val LE : N = "<=" + val LSL : N = "<<" + val LSR : N = ">>>" + val LT : N = "<" + val MINUS : N = "-" + val MOD : N = "%" + val MUL : N = "*" + val NE : N = "!=" + val OR : N = "|" val PLUS = ADD // technically redundant, but ADD looks funny with MINUS val SUB = MINUS // ... as does SUB with PLUS - val XOR = encode("^") - val ZAND = encode("&&") - val ZOR = encode("||") + val XOR : N = "^" + val ZAND : N = "&&" + val ZOR : N = "||" // unary operators val UNARY_PREFIX: N = "unary_" - val UNARY_~ = encode("unary_~") - val UNARY_+ = encode("unary_+") - val UNARY_- = encode("unary_-") - val UNARY_! = encode("unary_!") + val UNARY_~ : N = "unary_~" + val UNARY_+ : N = "unary_+" + val UNARY_- : N = "unary_-" + val UNARY_! : N = "unary_!" // Grouped here so Cleanup knows what tests to perform. val CommonOpNames = Set[Name](OR, XOR, AND, EQ, NE) diff --git a/compiler/src/dotty/tools/dotc/core/SymDenotations.scala b/compiler/src/dotty/tools/dotc/core/SymDenotations.scala index 07ca912dd7c5..e2786e1891f3 100644 --- a/compiler/src/dotty/tools/dotc/core/SymDenotations.scala +++ b/compiler/src/dotty/tools/dotc/core/SymDenotations.scala @@ -369,11 +369,7 @@ object SymDenotations { /** The expanded name of this denotation. */ final def expandedName(implicit ctx: Context) = 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 - legalize(name.expandedName(initial.owner)) - } + else name.expandedName(initial.owner) // need to use initial owner to disambiguate, as multiple private symbols with the same name // might have been moved from different origins into the same class diff --git a/compiler/src/dotty/tools/dotc/core/tasty/TastyUnpickler.scala b/compiler/src/dotty/tools/dotc/core/tasty/TastyUnpickler.scala index 37a3c2e762ad..eb10edb3ee0a 100644 --- a/compiler/src/dotty/tools/dotc/core/tasty/TastyUnpickler.scala +++ b/compiler/src/dotty/tools/dotc/core/tasty/TastyUnpickler.scala @@ -48,7 +48,7 @@ class TastyUnpickler(reader: TastyReader) { val result = tag match { case UTF8 => goto(end) - termName(bytes, start.index, length) + termName(bytes, start.index, length).decode case QUALIFIED | FLATTENED | EXPANDED | EXPANDPREFIX => qualifiedNameKindOfTag(tag)(readName(), readName().asSimpleName) case UNIQUE => diff --git a/compiler/src/dotty/tools/dotc/core/unpickleScala2/Scala2Unpickler.scala b/compiler/src/dotty/tools/dotc/core/unpickleScala2/Scala2Unpickler.scala index 1db3ebcb066c..766f6dd235e2 100644 --- a/compiler/src/dotty/tools/dotc/core/unpickleScala2/Scala2Unpickler.scala +++ b/compiler/src/dotty/tools/dotc/core/unpickleScala2/Scala2Unpickler.scala @@ -338,7 +338,7 @@ class Scala2Unpickler(bytes: Array[Byte], classRoot: ClassDenotation, moduleClas def atEnd = readIndex == end def readExtSymbol(): Symbol = { - val name = readNameRef() + val name = readNameRef().decode val owner = if (atEnd) loadingMirror.RootClass else readSymbolRef() def adjust(denot: Denotation) = { @@ -401,7 +401,7 @@ class Scala2Unpickler(bytes: Array[Byte], classRoot: ClassDenotation, moduleClas // println(owner.info.decls.toList.map(_.debugString).mkString("\n ")) // !!! DEBUG // } // (5) Create a stub symbol to defer hard failure a little longer. - System.err.println(i"***** missing reference, looking for $name in $owner") + System.err.println(i"***** missing reference, looking for ${name.debugString} in $owner") System.err.println(i"decls = ${owner.info.decls}") owner.info.decls.checkConsistent() if (slowSearch(name).exists) @@ -447,6 +447,7 @@ class Scala2Unpickler(bytes: Array[Byte], classRoot: ClassDenotation, moduleClas name = name.asTermName.unmangle(SuperAccessorName) flags = flags &~ Scala2SuperAccessor } + name = name.mapLast(_.decode) val mname = name.mangled def nameMatches(rootName: Name) = mname == rootName.mangled diff --git a/compiler/src/dotty/tools/dotc/parsing/Scanners.scala b/compiler/src/dotty/tools/dotc/parsing/Scanners.scala index b0fa8d760ec3..ea4122012174 100644 --- a/compiler/src/dotty/tools/dotc/parsing/Scanners.scala +++ b/compiler/src/dotty/tools/dotc/parsing/Scanners.scala @@ -7,6 +7,7 @@ import core.StdNames._, core.Comments._ import util.SourceFile import java.lang.Character.isDigit import util.Chars._ +import util.NameTransformer.avoidIllegalChars import Tokens._ import scala.annotation.{ switch, tailrec } import scala.collection.mutable @@ -622,6 +623,7 @@ object Scanners { if (ch == '`') { nextChar() finishNamed(BACKQUOTED_IDENT) + name = avoidIllegalChars(name) if (name.length == 0) error("empty quoted identifier") else if (name == nme.WILDCARD) diff --git a/compiler/src/dotty/tools/dotc/parsing/SymbolicXMLBuilder.scala b/compiler/src/dotty/tools/dotc/parsing/SymbolicXMLBuilder.scala index c99826488123..97b04669221b 100644 --- a/compiler/src/dotty/tools/dotc/parsing/SymbolicXMLBuilder.scala +++ b/compiler/src/dotty/tools/dotc/parsing/SymbolicXMLBuilder.scala @@ -54,7 +54,7 @@ class SymbolicXMLBuilder(parser: Parser, preserveWS: Boolean)(implicit ctx: Cont val __Text: TermName = "Text" val _buf: TermName = "$buf" val _md: TermName = "$md" - val _plus: TermName = "$amp$plus" + val _plus: TermName = "&+" val _tmpscope: TermName = "$tmpscope" val _xml: TermName = "xml" } diff --git a/compiler/src/dotty/tools/dotc/printing/RefinedPrinter.scala b/compiler/src/dotty/tools/dotc/printing/RefinedPrinter.scala index c3f36cc463f7..b741133c6bb9 100644 --- a/compiler/src/dotty/tools/dotc/printing/RefinedPrinter.scala +++ b/compiler/src/dotty/tools/dotc/printing/RefinedPrinter.scala @@ -60,7 +60,7 @@ class RefinedPrinter(_ctx: Context) extends PlainPrinter(_ctx) { protected val PrintableFlags = (SourceModifierFlags | Label | Module | Local).toCommonFlags override def nameString(name: Name): String = - if (ctx.settings.debugNames.value) name.debugString else name.decode.toString + if (ctx.settings.debugNames.value) name.debugString else name.toString override protected def simpleNameString(sym: Symbol): String = { val name = if (ctx.property(XprintMode).isEmpty) sym.originalName else sym.name diff --git a/compiler/src/dotty/tools/dotc/repl/CompilingInterpreter.scala b/compiler/src/dotty/tools/dotc/repl/CompilingInterpreter.scala index eed75fe886b7..98f098fbcbf3 100644 --- a/compiler/src/dotty/tools/dotc/repl/CompilingInterpreter.scala +++ b/compiler/src/dotty/tools/dotc/repl/CompilingInterpreter.scala @@ -742,7 +742,7 @@ class CompilingInterpreter( override def shouldShowResult(req: Request): Boolean = !statement.mods.is(Flags.AccessFlags) && !(isGeneratedVarName(statement.name.toString) && - req.typeOf(statement.name.encode) == "Unit") + req.typeOf(statement.name) == "Unit") } @@ -812,7 +812,7 @@ class CompilingInterpreter( /** Print out lhs instead of the generated varName */ override def resultExtractionCode(req: Request, code: PrintWriter): Unit = { code.print(" + \"" + lhs.show + ": " + - string2code(req.typeOf(helperName.encode)) + + string2code(req.typeOf(helperName)) + " = \" + " + string2code(req.fullPath(helperName)) + " + \"\\n\"") diff --git a/compiler/src/dotty/tools/dotc/transform/PostTyper.scala b/compiler/src/dotty/tools/dotc/transform/PostTyper.scala index bacb88091e50..049ccb22e00d 100644 --- a/compiler/src/dotty/tools/dotc/transform/PostTyper.scala +++ b/compiler/src/dotty/tools/dotc/transform/PostTyper.scala @@ -265,7 +265,7 @@ class PostTyper extends MacroTransform with IdentityDenotTransformer { thisTran case Import(expr, selectors) => val exprTpe = expr.tpe def checkIdent(ident: untpd.Ident): Unit = { - val name = ident.name.asTermName.encode + val name = ident.name.asTermName if (name != nme.WILDCARD && !exprTpe.member(name).exists && !exprTpe.member(name.toTypeName).exists) ctx.error(s"${ident.name} is not a member of ${expr.show}", ident.pos) } diff --git a/compiler/src/dotty/tools/dotc/transform/SuperAccessors.scala b/compiler/src/dotty/tools/dotc/transform/SuperAccessors.scala index 84a32f93bf08..0e723c58e720 100644 --- a/compiler/src/dotty/tools/dotc/transform/SuperAccessors.scala +++ b/compiler/src/dotty/tools/dotc/transform/SuperAccessors.scala @@ -107,7 +107,7 @@ class SuperAccessors(thisTransformer: DenotTransformer) { // ParamForwaders as installed ParamForwarding.scala do use super calls to vals ctx.error(s"super may be not be used on ${sym.underlyingSymbol}", sel.pos) else if (isDisallowed(sym)) - ctx.error(s"super not allowed here: use this.${sel.name.decode} instead", sel.pos) + ctx.error(s"super not allowed here: use this.${sel.name} instead", sel.pos) else if (sym is Deferred) { val member = sym.overridingSymbol(clazz) if (!mix.name.isEmpty || diff --git a/compiler/src/dotty/tools/dotc/transform/SyntheticMethods.scala b/compiler/src/dotty/tools/dotc/transform/SyntheticMethods.scala index d37e47d49ba6..c8bef28dcd66 100644 --- a/compiler/src/dotty/tools/dotc/transform/SyntheticMethods.scala +++ b/compiler/src/dotty/tools/dotc/transform/SyntheticMethods.scala @@ -82,7 +82,7 @@ class SyntheticMethods(thisTransformer: DenotTransformer) { ref(defn.runtimeMethodRef("_" + sym.name.toString)).appliedToArgs(This(clazz) :: vrefss.head) def ownName(vrefss: List[List[Tree]]): Tree = - Literal(Constant(clazz.name.stripModuleClassSuffix.decode.toString)) + Literal(Constant(clazz.name.stripModuleClassSuffix.toString)) def syntheticRHS(implicit ctx: Context): List[List[Tree]] => Tree = synthetic.name match { case nme.hashCode_ if isDerivedValueClass(clazz) => vrefss => valueHashCodeBody diff --git a/compiler/src/dotty/tools/dotc/transform/TreeChecker.scala b/compiler/src/dotty/tools/dotc/transform/TreeChecker.scala index d553868fb813..fe48667f58c6 100644 --- a/compiler/src/dotty/tools/dotc/transform/TreeChecker.scala +++ b/compiler/src/dotty/tools/dotc/transform/TreeChecker.scala @@ -25,6 +25,7 @@ import reporting.ThrowingReporter import ast.Trees._ import ast.{tpd, untpd} import util.SourcePosition +import util.Chars._ import collection.mutable import ProtoTypes._ import config.Printers @@ -51,11 +52,9 @@ class TreeChecker extends Phase with SymTransformer { private val seenClasses = collection.mutable.HashMap[String, Symbol]() private val seenModuleVals = collection.mutable.HashMap[String, Symbol]() - def isValidJVMName(name: Name) = - !name.toString.exists(c => c == '.' || c == ';' || c =='[' || c == '/') + def isValidJVMName(name: Name) = name.toString.forall(isValidJVMChar) - def isValidJVMMethodName(name: Name) = - !name.toString.exists(c => c == '.' || c == ';' || c =='[' || c == '/' || c == '<' || c == '>') + def isValidJVMMethodName(name: Name) = name.toString.forall(isValidJVMMethodChar) def printError(str: String)(implicit ctx: Context) = { ctx.echo(Console.RED + "[error] " + Console.WHITE + str) @@ -149,7 +148,7 @@ class TreeChecker extends Phase with SymTransformer { def withDefinedSym[T](tree: untpd.Tree)(op: => T)(implicit ctx: Context): T = tree match { case tree: untpd.DefTree => val sym = tree.symbol - assert(isValidJVMName(sym.name), s"${sym.fullName} name is invalid on jvm") + assert(isValidJVMName(sym.name.encode), s"${sym.name.debugString} name is invalid on jvm") everDefinedSyms.get(sym) match { case Some(t) => if (t ne tree) @@ -386,7 +385,7 @@ class TreeChecker extends Phase with SymTransformer { withDefinedSyms(ddef.tparams) { withDefinedSymss(ddef.vparamss) { if (!sym.isClassConstructor && !(sym.name eq Names.STATIC_CONSTRUCTOR)) - assert(isValidJVMMethodName(sym.name), s"${sym.name.debugString} name is invalid on jvm") + assert(isValidJVMMethodName(sym.name.encode), s"${sym.name.debugString} name is invalid on jvm") ddef.vparamss.foreach(_.foreach { vparam => assert(vparam.symbol.is(Param), diff --git a/compiler/src/dotty/tools/dotc/typer/Namer.scala b/compiler/src/dotty/tools/dotc/typer/Namer.scala index 6bfbc2b70eaf..365b19b38193 100644 --- a/compiler/src/dotty/tools/dotc/typer/Namer.scala +++ b/compiler/src/dotty/tools/dotc/typer/Namer.scala @@ -257,7 +257,7 @@ class Namer { typer: Typer => /** Add moduleClass/sourceModule to completer if it is for a module val or class */ def adjustIfModule(completer: LazyType, tree: MemberDef) = - if (tree.mods is Module) ctx.adjustModuleCompleter(completer, tree.name.encode) + if (tree.mods is Module) ctx.adjustModuleCompleter(completer, tree.name) else completer typr.println(i"creating symbol for $tree in ${ctx.mode}") @@ -280,7 +280,7 @@ class Namer { typer: Typer => tree match { case tree: TypeDef if tree.isClassDef => - val name = checkNoConflict(tree.name.encode).toTypeName + val name = checkNoConflict(tree.name).asTypeName val flags = checkFlags(tree.mods.flags &~ Implicit) val cls = recordSym(ctx.newClassSymbol( ctx.owner, name, flags, @@ -289,7 +289,7 @@ class Namer { typer: Typer => cls.completer.asInstanceOf[ClassCompleter].init() cls case tree: MemberDef => - val name = checkNoConflict(tree.name.encode) + val name = checkNoConflict(tree.name) val flags = checkFlags(tree.mods.flags) val isDeferred = lacksDefinition(tree) val deferred = if (isDeferred) Deferred else EmptyFlags @@ -566,8 +566,8 @@ class Namer { typer: Typer => /** Create links between companion object and companion class */ def createLinks(classTree: TypeDef, moduleTree: TypeDef)(implicit ctx: Context) = { - val claz = ctx.effectiveScope.lookup(classTree.name.encode) - val modl = ctx.effectiveScope.lookup(moduleTree.name.encode) + val claz = ctx.denotNamed(classTree.name).symbol + val modl = ctx.denotNamed(moduleTree.name).symbol ctx.synthesizeCompanionMethod(nme.COMPANION_CLASS_METHOD, claz, modl).entered ctx.synthesizeCompanionMethod(nme.COMPANION_MODULE_METHOD, modl, claz).entered } @@ -609,10 +609,10 @@ class Namer { typer: Typer => // matters. if (ctx.owner.is(PackageClass)) { for (cdef @ TypeDef(moduleName, _) <- moduleDef.values) { - val moduleSym = ctx.effectiveScope.lookup(moduleName.encode) + val moduleSym = ctx.effectiveScope.lookup(moduleName) if (moduleSym.isDefinedInCurrentRun) { val className = moduleName.stripModuleClassSuffix.toTypeName - val classSym = ctx.effectiveScope.lookup(className.encode) + val classSym = ctx.effectiveScope.lookup(className) if (!classSym.isDefinedInCurrentRun) { val absentClassSymbol = ctx.newClassSymbol(ctx.owner, className, EmptyFlags, _ => NoType) enterSymbol(absentClassSymbol) diff --git a/compiler/src/dotty/tools/dotc/typer/ReTyper.scala b/compiler/src/dotty/tools/dotc/typer/ReTyper.scala index 73e676b574e6..67fee03c1964 100644 --- a/compiler/src/dotty/tools/dotc/typer/ReTyper.scala +++ b/compiler/src/dotty/tools/dotc/typer/ReTyper.scala @@ -82,8 +82,6 @@ class ReTyper extends Typer { override def ensureConstrCall(cls: ClassSymbol, parents: List[Tree])(implicit ctx: Context): List[Tree] = parents - override def encodeName(tree: untpd.NameTree)(implicit ctx: Context) = tree - override def handleUnexpectedFunType(tree: untpd.Apply, fun: Tree)(implicit ctx: Context): Tree = fun.tpe match { case mt: MethodType => val args: List[Tree] = tree.args.zipWithConserve(mt.paramInfos)(typedExpr(_, _)).asInstanceOf[List[Tree]] diff --git a/compiler/src/dotty/tools/dotc/typer/RefChecks.scala b/compiler/src/dotty/tools/dotc/typer/RefChecks.scala index 91e8859bb506..ebe79cdb4f41 100644 --- a/compiler/src/dotty/tools/dotc/typer/RefChecks.scala +++ b/compiler/src/dotty/tools/dotc/typer/RefChecks.scala @@ -698,7 +698,7 @@ object RefChecks { if (!concrOvers.isEmpty) ctx.deprecationWarning( symbol.toString + " overrides concrete, non-deprecated symbol(s):" + - concrOvers.map(_.name.decode).mkString(" ", ", ", ""), tree.pos) + concrOvers.map(_.name).mkString(" ", ", ", ""), tree.pos) } } diff --git a/compiler/src/dotty/tools/dotc/typer/Typer.scala b/compiler/src/dotty/tools/dotc/typer/Typer.scala index 06a225d77ac8..85e590fd6d23 100644 --- a/compiler/src/dotty/tools/dotc/typer/Typer.scala +++ b/compiler/src/dotty/tools/dotc/typer/Typer.scala @@ -197,7 +197,7 @@ class Typer extends Namer with TypeAssigner with Applications with Implicits wit * from given `site` and `selectors`. */ def namedImportRef(imp: ImportInfo)(implicit ctx: Context): Type = { - val Name = name.toTermName.decode + val Name = name.toTermName def recur(selectors: List[untpd.Tree]): Type = selectors match { case selector :: rest => def checkUnambiguous(found: Type) = { @@ -1572,15 +1572,12 @@ class Typer extends Namer with TypeAssigner with Applications with Implicits wit !ctx.isAfterTyper) makeImplicitFunction(xtree, pt) else xtree match { - case xtree: untpd.NameTree => typedNamed(encodeName(xtree), pt) + case xtree: untpd.NameTree => typedNamed(xtree, pt) case xtree => typedUnnamed(xtree) } } } - protected def encodeName(tree: untpd.NameTree)(implicit ctx: Context): untpd.NameTree = - untpd.rename(tree, tree.name.encode) - protected def makeImplicitFunction(tree: untpd.Tree, pt: Type)(implicit ctx: Context): Tree = { val defn.FunctionOf(formals, resType, true) = pt.dealias val paramTypes = formals.map(fullyDefinedType(_, "implicit function parameter", tree.pos)) diff --git a/compiler/src/dotty/tools/dotc/util/Chars.scala b/compiler/src/dotty/tools/dotc/util/Chars.scala index 6f95b87c474b..29a88aeb13ea 100644 --- a/compiler/src/dotty/tools/dotc/util/Chars.scala +++ b/compiler/src/dotty/tools/dotc/util/Chars.scala @@ -77,6 +77,12 @@ object Chars { chtp == JCharacter.MATH_SYMBOL.toInt || chtp == JCharacter.OTHER_SYMBOL.toInt } + def isValidJVMChar(c: Char) = + !(c == '.' || c == ';' || c =='[' || c == '/') + + def isValidJVMMethodChar(c: Char) = + !(c == '.' || c == ';' || c =='[' || c == '/' || c == '<' || c == '>') + private final val otherLetters = Set[Char]('\u0024', '\u005F') // '$' and '_' private final val letterGroups = { import JCharacter._ diff --git a/compiler/src/dotty/tools/dotc/util/NameTransformer.scala b/compiler/src/dotty/tools/dotc/util/NameTransformer.scala index 52f8e6ec0bb4..1ecbebe67fc4 100644 --- a/compiler/src/dotty/tools/dotc/util/NameTransformer.scala +++ b/compiler/src/dotty/tools/dotc/util/NameTransformer.scala @@ -1,38 +1,24 @@ -/* __ *\ -** ________ ___ / / ___ Scala API ** -** / __/ __// _ | / / / _ | (c) 2003-2011, LAMP/EPFL ** -** __\ \/ /__/ __ |/ /__/ __ | http://scala-lang.org/ ** -** /____/\___/_/ |_/____/_/ | | ** -** |/ ** -\* */ - -package dotty.tools.dotc +package dotty.tools +package dotc package util import core.Names._ import core.Decorators._ +import collection.mutable +import util.Chars.isValidJVMMethodChar /** Provides functions to encode and decode Scala symbolic names. - * Also provides some constants. */ object NameTransformer { - // XXX Short term: providing a way to alter these without having to recompile - // the compiler before recompiling the compiler. - val MODULE_SUFFIX_STRING = sys.props.getOrElse("SCALA_MODULE_SUFFIX_STRING", "$") - val NAME_JOIN_STRING = sys.props.getOrElse("SCALA_NAME_JOIN_STRING", "$") - val MODULE_INSTANCE_NAME = "MODULE$" private val nops = 128 - private val ncodes = 26 * 26 - private class OpCodes(val op: Char, val code: String, val next: OpCodes) + @sharable private val op2code = new Array[String](nops) + @sharable private val str2op = new mutable.HashMap[String, Char] - private val op2code = new Array[String](nops) - private val code2op = new Array[OpCodes](ncodes) private def enterOp(op: Char, code: String) = { op2code(op) = code - val c = (code.charAt(1) - 'a') * 26 + code.charAt(2) - 'a' - code2op(c) = new OpCodes(op, code, code2op(c)) + str2op(code) = op } /* Note: decoding assumes opcodes are only ever lowercase. */ @@ -55,108 +41,76 @@ object NameTransformer { enterOp('?', "$qmark") enterOp('@', "$at") - /** Replace operator symbols by corresponding `\$opname`. + /** Expand characters that are illegal as JVM method names by `$u`, followed + * by the character's unicode expansion. + */ + def avoidIllegalChars(name: SimpleTermName) = { + var i = name.length - 1 + while (i >= 0 && isValidJVMMethodChar(name(i))) i -= 1 + if (i >= 0) + termName( + name.toString.flatMap(ch => + if (isValidJVMMethodChar(ch)) ch.toString else "$u%04X".format(ch.toInt))) + else name + } + + /** Replace operator symbols by corresponding expansion strings. * * @param name the string to encode * @return the string with all recognized opchars replaced with their encoding + * + * Operator symbols are only recognized if they make up the whole name, or + * if they make up the last part of the name which follows a `_`. */ def encode(name: SimpleTermName): SimpleTermName = { - var buf: StringBuilder = null - val len = name.length - var i = 0 - while (i < len) { - val c = name(i) - if (c < nops && (op2code(c) ne null)) { - if (buf eq null) { - buf = new StringBuilder() - buf.append(name.slice(0, i)) - } - buf.append(op2code(c)) - /* Handle glyphs that are not valid Java/JVM identifiers */ - } - else if (!Character.isJavaIdentifierPart(c)) { - if (buf eq null) { - buf = new StringBuilder() - buf.append(name.slice(0, i)) + def loop(len: Int, ops: List[String]): SimpleTermName = { + def convert = + if (ops.isEmpty) name + else { + val buf = new java.lang.StringBuilder + buf.append(chrs, name.start, len) + for (op <- ops) buf.append(op) + termName(buf.toString) } - buf.append("$u%04X".format(c.toInt)) - } - else if (buf ne null) { - buf.append(c) + if (len == 0 || name(len - 1) == '_') convert + else { + val ch = name(len - 1) + if (ch <= nops && op2code(ch) != null) + loop(len - 1, op2code(ch) :: ops) + else if (Chars.isSpecial(ch)) + loop(len - 1, ch.toString :: ops) + else name } - i += 1 } - if (buf eq null) name - else termName(buf.toString) + loop(name.length, Nil) } - /** Replace `\$opname` by corresponding operator symbol. - * - * @param name0 the string to decode - * @return the string with all recognized operator symbol encodings replaced with their name + /** Replace operator expansions by the operators themselves. + * Operator expansions are only recognized if they make up the whole name, or + * if they make up the last part of the name which follows a `_`. */ - def decode(name0: String): String = { - //System.out.println("decode: " + name);//DEBUG - val name = if (name0.endsWith("")) name0.substring(0, name0.length() - ("").length()) + "this" - else name0 - var buf: StringBuilder = null - val len = name.length() - var i = 0 - while (i < len) { - var ops: OpCodes = null - var unicode = false - val c = name charAt i - if (c == '$' && i + 2 < len) { - val ch1 = name.charAt(i + 1) - if ('a' <= ch1 && ch1 <= 'z') { - val ch2 = name.charAt(i + 2) - if ('a' <= ch2 && ch2 <= 'z') { - ops = code2op((ch1 - 'a') * 26 + ch2 - 'a') - while ((ops ne null) && !name.startsWith(ops.code, i)) ops = ops.next - if (ops ne null) { - if (buf eq null) { - buf = new StringBuilder() - buf.append(name.substring(0, i)) - } - buf.append(ops.op) - i += ops.code.length() - } - /* Handle the decoding of Unicode glyphs that are - * not valid Java/JVM identifiers */ - } else if ((len - i) >= 6 && // Check that there are enough characters left - ch1 == 'u' && - ((Character.isDigit(ch2)) || - ('A' <= ch2 && ch2 <= 'F'))) { - /* Skip past "$u", next four should be hexadecimal */ - val hex = name.substring(i + 2, i + 6) - try { - val str = Integer.parseInt(hex, 16).toChar - if (buf eq null) { - buf = new StringBuilder() - buf.append(name.substring(0, i)) - } - buf.append(str) - /* 2 for "$u", 4 for hexadecimal number */ - i += 6 - unicode = true - } catch { - case _:NumberFormatException => - /* `hex` did not decode to a hexadecimal number, so - * do nothing. */ - } - } + def decode(name: SimpleTermName): SimpleTermName = { + def loop(len: Int, ops: List[Char]): SimpleTermName = { + def convert = + if (ops.isEmpty) name + else { + val buf = new java.lang.StringBuilder + buf.append(chrs, name.start, len) + for (op <- ops) buf.append(op) + termName(buf.toString) } - } - /* If we didn't see an opcode or encoded Unicode glyph, and the - buffer is non-empty, write the current character and advance - one */ - if ((ops eq null) && !unicode) { - if (buf ne null) - buf.append(c) - i += 1 + if (len == 0 || name(len - 1) == '_') convert + else if (Chars.isSpecial(name(len - 1))) loop(len - 1, name(len - 1) :: ops) + else { + val idx = name.lastIndexOf('$', len - 1) + if (idx >= 0 && idx + 2 < len) + str2op.get(name.sliceToString(idx, len)) match { + case Some(ch) => loop(idx, ch :: ops) + case None => name + } + else name } } - //System.out.println("= " + (if (buf == null) name else buf.toString()));//DEBUG - if (buf eq null) name else buf.toString() + loop(name.length, Nil) } } From 1d97865bea24c94d2e5ae8abd6c990723cc05405 Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Tue, 4 Apr 2017 09:50:47 +0200 Subject: [PATCH 04/31] Avoid forcing diagnostic message in ErrorType I found the problem when trying to debug what went wrong with encoded names in backend. I added an assert that the toString of a name would not contain symbolic operator characters. The assert triggered because an error type forced the message (without printing to Console later). --- compiler/src/dotty/tools/dotc/core/Types.scala | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/compiler/src/dotty/tools/dotc/core/Types.scala b/compiler/src/dotty/tools/dotc/core/Types.scala index 32fb28f405cc..9d6bc0a01c55 100644 --- a/compiler/src/dotty/tools/dotc/core/Types.scala +++ b/compiler/src/dotty/tools/dotc/core/Types.scala @@ -3444,7 +3444,7 @@ object Types { abstract class FlexType extends UncachedGroundType with ValueType class ErrorType(_msg: => Message) extends FlexType { - val msg = _msg + def msg = _msg } object UnspecifiedErrorType extends ErrorType("unspecified error") From 927ff025aa7dacb6ba5d4cd05df842dcaf5cc632 Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Tue, 4 Apr 2017 13:06:37 +0200 Subject: [PATCH 05/31] var -> val --- compiler/src/dotty/tools/dotc/core/NameKinds.scala | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/compiler/src/dotty/tools/dotc/core/NameKinds.scala b/compiler/src/dotty/tools/dotc/core/NameKinds.scala index 17a7c1412001..84d5c9ff489c 100644 --- a/compiler/src/dotty/tools/dotc/core/NameKinds.scala +++ b/compiler/src/dotty/tools/dotc/core/NameKinds.scala @@ -228,7 +228,7 @@ object NameKinds { val i = skipSeparatorAndNum(name, separator) if (i > 0) { val index = name.drop(i).toString.toInt - var original = name.take(i - separator.length).asTermName + val original = name.take(i - separator.length).asTermName apply(original, index) } else name From 150b714f7384cc3621de52490e4515009fe6b6c0 Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Fri, 7 Apr 2017 10:55:31 +0200 Subject: [PATCH 06/31] Don't decode when unpickling tasty was a patch left over from a previosu attempt to bugfix. Actually goes wrong when reading string literals that are represented as UTF8 names. --- compiler/src/dotty/tools/dotc/core/tasty/TastyUnpickler.scala | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/compiler/src/dotty/tools/dotc/core/tasty/TastyUnpickler.scala b/compiler/src/dotty/tools/dotc/core/tasty/TastyUnpickler.scala index eb10edb3ee0a..37a3c2e762ad 100644 --- a/compiler/src/dotty/tools/dotc/core/tasty/TastyUnpickler.scala +++ b/compiler/src/dotty/tools/dotc/core/tasty/TastyUnpickler.scala @@ -48,7 +48,7 @@ class TastyUnpickler(reader: TastyReader) { val result = tag match { case UTF8 => goto(end) - termName(bytes, start.index, length).decode + termName(bytes, start.index, length) case QUALIFIED | FLATTENED | EXPANDED | EXPANDPREFIX => qualifiedNameKindOfTag(tag)(readName(), readName().asSimpleName) case UNIQUE => From 4cdc3884236059830d770b717ca14fcb8684f606 Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Fri, 7 Apr 2017 10:58:56 +0200 Subject: [PATCH 07/31] Invalide fullName cache when name changes Also: use "_$" instead of "~" as filler when taking the full name of a term member. If the term member is symbolic (e.g. +) the filler name would be misinterpreted as a symbolic name "~+", so would be mangled as "$tilde$plus$ instead of "~$plus". --- compiler/src/dotty/tools/dotc/core/SymDenotations.scala | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/core/SymDenotations.scala b/compiler/src/dotty/tools/dotc/core/SymDenotations.scala index e2786e1891f3..778acf6e9836 100644 --- a/compiler/src/dotty/tools/dotc/core/SymDenotations.scala +++ b/compiler/src/dotty/tools/dotc/core/SymDenotations.scala @@ -397,7 +397,7 @@ object SymDenotations { var encl = owner while (!encl.isClass && !encl.isPackageObject) { encl = encl.owner - filler += "~" + filler += "_$" } var prefix = encl.fullNameSeparated(kind) if (kind.separator == "$") @@ -499,7 +499,7 @@ object SymDenotations { * step for creating Refinement types. */ final def isRefinementClass(implicit ctx: Context): Boolean = - name.decode == tpnme.REFINE_CLASS + name == tpnme.REFINE_CLASS /** Is this symbol a package object or its module class? */ def isPackageObject(implicit ctx: Context): Boolean = { From 5f43d27992f5fcf1ea4af8843126d422d74e5b01 Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Fri, 7 Apr 2017 10:59:35 +0200 Subject: [PATCH 08/31] Eagerly update names of package members when one looks for them with a symbolic name. --- compiler/src/dotty/tools/dotc/core/SymbolLoaders.scala | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/compiler/src/dotty/tools/dotc/core/SymbolLoaders.scala b/compiler/src/dotty/tools/dotc/core/SymbolLoaders.scala index 9e19c5f05eec..4eedc4c0479c 100644 --- a/compiler/src/dotty/tools/dotc/core/SymbolLoaders.scala +++ b/compiler/src/dotty/tools/dotc/core/SymbolLoaders.scala @@ -166,7 +166,13 @@ class SymbolLoaders { override def lookupEntry(name: Name)(implicit ctx: Context): ScopeEntry = { val mangled = name.mangled val e = super.lookupEntry(mangled) - if (e != null) e + if (e != null) { + // Eagerly update symbol's name to undecoded name to avpid the name + // spearding to types. + if (name.toSimpleName != mangled && e.sym.initialDenot.name == mangled) + e.sym.initialDenot.name = name + e + } else if (_sourceModule.initialDenot.name == nme.scala_ && _sourceModule == defn.ScalaPackageVal && name.isTypeName && name.isSyntheticFunction) newScopeEntry(defn.newFunctionNTrait(name.asTypeName)) From 5875a7b0fda250f6cbe6aac9d00af06ed5333466 Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Fri, 7 Apr 2017 11:00:04 +0200 Subject: [PATCH 09/31] Fix compilation failure in LambdaLift --- compiler/src/dotty/tools/dotc/core/StdNames.scala | 2 +- compiler/src/dotty/tools/dotc/transform/LambdaLift.scala | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/core/StdNames.scala b/compiler/src/dotty/tools/dotc/core/StdNames.scala index 0b636f0e1f6b..1611aca25506 100644 --- a/compiler/src/dotty/tools/dotc/core/StdNames.scala +++ b/compiler/src/dotty/tools/dotc/core/StdNames.scala @@ -24,7 +24,7 @@ object StdNames { final val SHADOWED_PREFIX = "(shadowed)" final val AVOID_CLASH_SUFFIX = "$_avoid_name_clash_$" final val MODULE_SUFFIX = "$" - //final val NAME_JOIN = "$" + final val NAME_JOIN = "$" final val DEFAULT_GETTER = "$default$" final val LOCALDUMMY_PREFIX = " Date: Fri, 7 Apr 2017 11:00:39 +0200 Subject: [PATCH 10/31] More lenient checking of name mismatches in ClassfileParser --- .../src/dotty/tools/dotc/core/classfile/ClassfileParser.scala | 4 +++- compiler/src/dotty/tools/dotc/printing/PlainPrinter.scala | 4 +--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/core/classfile/ClassfileParser.scala b/compiler/src/dotty/tools/dotc/core/classfile/ClassfileParser.scala index eef8faf8a233..51cde0d71f38 100644 --- a/compiler/src/dotty/tools/dotc/core/classfile/ClassfileParser.scala +++ b/compiler/src/dotty/tools/dotc/core/classfile/ClassfileParser.scala @@ -92,7 +92,9 @@ class ClassfileParser( val nameIdx = in.nextChar currentClassName = pool.getClassName(nameIdx) - if (currentIsTopLevel && currentClassName != classRoot.fullName.toSimpleName) + if (currentIsTopLevel && + currentClassName != classRoot.fullName.toSimpleName && + currentClassName != classRoot.fullName.encode.toSimpleName) mismatchError(currentClassName) addEnclosingTParams() diff --git a/compiler/src/dotty/tools/dotc/printing/PlainPrinter.scala b/compiler/src/dotty/tools/dotc/printing/PlainPrinter.scala index 03e83e93e001..8aec867921eb 100644 --- a/compiler/src/dotty/tools/dotc/printing/PlainPrinter.scala +++ b/compiler/src/dotty/tools/dotc/printing/PlainPrinter.scala @@ -208,9 +208,7 @@ class PlainPrinter(_ctx: Context) extends Printer { protected def ParamRefNameString(param: ParamRef): String = ParamRefNameString(param.binder.paramNames(param.paramNum)) - /** The name of the symbol without a unique id. Under refined printing, - * the decoded original name. - */ + /** The name of the symbol without a unique id. */ protected def simpleNameString(sym: Symbol): String = nameString(sym.name) /** If -uniqid is set, the hashcode of the lambda type, after a # */ From 0311cc5ffbb302065c9745a7a0bb723e7fc3ccea Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Fri, 7 Apr 2017 11:01:17 +0200 Subject: [PATCH 11/31] Avoid unnecessary decodes --- .../dotty/tools/dotc/core/unpickleScala2/Scala2Unpickler.scala | 2 +- compiler/src/dotty/tools/dotc/printing/RefinedPrinter.scala | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/core/unpickleScala2/Scala2Unpickler.scala b/compiler/src/dotty/tools/dotc/core/unpickleScala2/Scala2Unpickler.scala index 766f6dd235e2..3a394d49f501 100644 --- a/compiler/src/dotty/tools/dotc/core/unpickleScala2/Scala2Unpickler.scala +++ b/compiler/src/dotty/tools/dotc/core/unpickleScala2/Scala2Unpickler.scala @@ -227,7 +227,7 @@ class Scala2Unpickler(bytes: Array[Byte], classRoot: ClassDenotation, moduleClas val major = readNat() val minor = readNat() if (major != MajorVersion || minor > MinorVersion) - throw new IOException("Scala signature " + classRoot.fullName.decode + + throw new IOException("Scala signature " + classRoot.fullName + " has wrong version\n expected: " + MajorVersion + "." + MinorVersion + "\n found: " + major + "." + minor + diff --git a/compiler/src/dotty/tools/dotc/printing/RefinedPrinter.scala b/compiler/src/dotty/tools/dotc/printing/RefinedPrinter.scala index b741133c6bb9..fcda277012f3 100644 --- a/compiler/src/dotty/tools/dotc/printing/RefinedPrinter.scala +++ b/compiler/src/dotty/tools/dotc/printing/RefinedPrinter.scala @@ -157,7 +157,7 @@ class RefinedPrinter(_ctx: Context) extends PlainPrinter(_ctx) { withoutPos(super.toText(tp)) case tp: SelectionProto => return "?{ " ~ toText(tp.name) ~ - (" " provided !tp.name.toSimpleName.decode.last.isLetterOrDigit) ~ + (" " provided !tp.name.toSimpleName.last.isLetterOrDigit) ~ ": " ~ toText(tp.memberProto) ~ " }" case tp: ViewProto => return toText(tp.argType) ~ " ?=>? " ~ toText(tp.resultType) From bdfd861c76a0f023423e6e18791b0f71bc024a7c Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Fri, 7 Apr 2017 11:01:30 +0200 Subject: [PATCH 12/31] Better support for debugging --- compiler/src/dotty/tools/dotc/core/TypeErasure.scala | 2 +- compiler/src/dotty/tools/dotc/transform/Pickler.scala | 2 ++ 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/compiler/src/dotty/tools/dotc/core/TypeErasure.scala b/compiler/src/dotty/tools/dotc/core/TypeErasure.scala index 1eb90b8ebe48..b8a7d2aeed97 100644 --- a/compiler/src/dotty/tools/dotc/core/TypeErasure.scala +++ b/compiler/src/dotty/tools/dotc/core/TypeErasure.scala @@ -536,7 +536,7 @@ class TypeErasure(isJava: Boolean, semiEraseVCs: Boolean, isConstructor: Boolean } } catch { case ex: AssertionError => - println(s"no sig for $tp") + println(s"no sig for $tp because of ${ex.printStackTrace()}") throw ex } diff --git a/compiler/src/dotty/tools/dotc/transform/Pickler.scala b/compiler/src/dotty/tools/dotc/transform/Pickler.scala index bf37067b1c2e..e62cb0225b89 100644 --- a/compiler/src/dotty/tools/dotc/transform/Pickler.scala +++ b/compiler/src/dotty/tools/dotc/transform/Pickler.scala @@ -11,6 +11,7 @@ import Periods._ import Phases._ import Symbols._ import Flags.Module +import reporting.ThrowingReporter import collection.mutable /** This phase pickles trees */ @@ -77,6 +78,7 @@ class Pickler extends Phase { testUnpickler( ctx.fresh .setPeriod(Period(ctx.runId + 1, FirstPhaseId)) + .setReporter(new ThrowingReporter(ctx.reporter)) .addMode(Mode.ReadPositions)) result } From 3744d70a4c8562c419796892bd2d7e2a655d79f0 Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Fri, 7 Apr 2017 14:55:22 +0200 Subject: [PATCH 13/31] Make encode and decode have more precise types The return type is now ThisName instead of Name. --- compiler/src/dotty/tools/dotc/core/Names.scala | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/core/Names.scala b/compiler/src/dotty/tools/dotc/core/Names.scala index ecee2068e816..6a8d562687eb 100644 --- a/compiler/src/dotty/tools/dotc/core/Names.scala +++ b/compiler/src/dotty/tools/dotc/core/Names.scala @@ -100,10 +100,10 @@ object Names { def toText(printer: Printer): Text = printer.toText(this) /** Replace \$op_name's by corresponding operator symbols. */ - def decode: Name + def decode: ThisName /** Replace operator symbols by corresponding \$op_name's. */ - def encode: Name + def encode: ThisName def firstPart: SimpleTermName def lastPart: SimpleTermName @@ -382,8 +382,8 @@ object Names { 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 encode: ThisName = underlying.encode.derived(info.map(_.encode)) + def decode: ThisName = underlying.decode.derived(info.map(_.decode)) def firstPart = underlying.firstPart def lastPart = info match { case qual: QualifiedInfo => qual.name From b4e8419de2880481ed27b2454d399e908a6f643b Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Fri, 7 Apr 2017 15:21:42 +0200 Subject: [PATCH 14/31] Decode names of package members on enter Avoids mutable names and the hassles associated with them. --- .../src/dotty/tools/dotc/core/SymDenotations.scala | 1 - .../src/dotty/tools/dotc/core/SymbolLoaders.scala | 12 +++--------- .../dotty/tools/dotc/core/tasty/TreeUnpickler.scala | 1 - .../dotc/core/unpickleScala2/Scala2Unpickler.scala | 1 - 4 files changed, 3 insertions(+), 12 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/core/SymDenotations.scala b/compiler/src/dotty/tools/dotc/core/SymDenotations.scala index 778acf6e9836..188f6ca06181 100644 --- a/compiler/src/dotty/tools/dotc/core/SymDenotations.scala +++ b/compiler/src/dotty/tools/dotc/core/SymDenotations.scala @@ -125,7 +125,6 @@ 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 diff --git a/compiler/src/dotty/tools/dotc/core/SymbolLoaders.scala b/compiler/src/dotty/tools/dotc/core/SymbolLoaders.scala index 4eedc4c0479c..297941cd253a 100644 --- a/compiler/src/dotty/tools/dotc/core/SymbolLoaders.scala +++ b/compiler/src/dotty/tools/dotc/core/SymbolLoaders.scala @@ -41,7 +41,7 @@ class SymbolLoaders { def enterClass( owner: Symbol, name: PreName, completer: SymbolLoader, flags: FlagSet = EmptyFlags, scope: Scope = EmptyScope)(implicit ctx: Context): Symbol = { - val cls = ctx.newClassSymbol(owner, name.toTypeName, flags, completer, assocFile = completer.sourceFileOrNull) + val cls = ctx.newClassSymbol(owner, name.toTypeName.unmangleClassName.decode, flags, completer, assocFile = completer.sourceFileOrNull) enterNew(owner, cls, completer, scope) } @@ -51,7 +51,7 @@ class SymbolLoaders { owner: Symbol, name: PreName, completer: SymbolLoader, modFlags: FlagSet = EmptyFlags, clsFlags: FlagSet = EmptyFlags, scope: Scope = EmptyScope)(implicit ctx: Context): Symbol = { val module = ctx.newModuleSymbol( - owner, name.toTermName, modFlags, clsFlags, + owner, name.toTermName.decode, modFlags, clsFlags, (module, _) => completer.proxy withDecls newScope withSourceModule (_ => module), assocFile = completer.sourceFileOrNull) enterNew(owner, module, completer, scope) @@ -166,13 +166,7 @@ class SymbolLoaders { override def lookupEntry(name: Name)(implicit ctx: Context): ScopeEntry = { val mangled = name.mangled val e = super.lookupEntry(mangled) - if (e != null) { - // Eagerly update symbol's name to undecoded name to avpid the name - // spearding to types. - if (name.toSimpleName != mangled && e.sym.initialDenot.name == mangled) - e.sym.initialDenot.name = name - e - } + if (e != null) e else if (_sourceModule.initialDenot.name == nme.scala_ && _sourceModule == defn.ScalaPackageVal && name.isTypeName && name.isSyntheticFunction) newScopeEntry(defn.newFunctionNTrait(name.asTypeName)) diff --git a/compiler/src/dotty/tools/dotc/core/tasty/TreeUnpickler.scala b/compiler/src/dotty/tools/dotc/core/tasty/TreeUnpickler.scala index 460af7c0ceea..b3b5236c509b 100644 --- a/compiler/src/dotty/tools/dotc/core/tasty/TreeUnpickler.scala +++ b/compiler/src/dotty/tools/dotc/core/tasty/TreeUnpickler.scala @@ -436,7 +436,6 @@ class TreeUnpickler(reader: TastyReader, nameAtRef: NameRef => TermName, posUnpi 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 diff --git a/compiler/src/dotty/tools/dotc/core/unpickleScala2/Scala2Unpickler.scala b/compiler/src/dotty/tools/dotc/core/unpickleScala2/Scala2Unpickler.scala index 3a394d49f501..d9c300a17920 100644 --- a/compiler/src/dotty/tools/dotc/core/unpickleScala2/Scala2Unpickler.scala +++ b/compiler/src/dotty/tools/dotc/core/unpickleScala2/Scala2Unpickler.scala @@ -460,7 +460,6 @@ class Scala2Unpickler(bytes: Array[Byte], classRoot: ClassDenotation, moduleClas //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 From 0d8e15c2aa9e831a44431bd41526c6ce8ae52c08 Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Fri, 7 Apr 2017 18:02:49 +0200 Subject: [PATCH 15/31] Drop isSimple requirement for unmangling expanded names Looks like it's not required. --- .../dotty/tools/dotc/core/unpickleScala2/Scala2Unpickler.scala | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/compiler/src/dotty/tools/dotc/core/unpickleScala2/Scala2Unpickler.scala b/compiler/src/dotty/tools/dotc/core/unpickleScala2/Scala2Unpickler.scala index d9c300a17920..06669a5b151d 100644 --- a/compiler/src/dotty/tools/dotc/core/unpickleScala2/Scala2Unpickler.scala +++ b/compiler/src/dotty/tools/dotc/core/unpickleScala2/Scala2Unpickler.scala @@ -439,7 +439,7 @@ class Scala2Unpickler(bytes: Array[Byte], classRoot: ClassDenotation, moduleClas if (name == nme.TRAIT_CONSTRUCTOR) nme.CONSTRUCTOR else name.asTermName.unmangle(Scala2MethodNameKinds) } - if ((flags is Scala2ExpandedName) && name.isSimple) { + if ((flags is Scala2ExpandedName)) { name = name.unmangle(ExpandedName) flags = flags &~ Scala2ExpandedName } From 15ff616b030a3631d13eb674759afba6cc47102f Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Fri, 7 Apr 2017 18:03:45 +0200 Subject: [PATCH 16/31] Polishing and better documentation of Names.scala # Conflicts: # compiler/src/dotty/tools/dotc/core/Names.scala --- .../backend/jvm/DottyBackendInterface.scala | 2 +- .../src/dotty/tools/dotc/core/NameKinds.scala | 2 +- .../src/dotty/tools/dotc/core/NameOps.scala | 6 +- .../src/dotty/tools/dotc/core/Names.scala | 290 ++++++++++-------- .../src/dotty/tools/dotc/core/StdNames.scala | 5 +- .../tools/dotc/parsing/JavaParsers.scala | 2 +- .../tools/dotc/transform/MoveStatics.scala | 3 +- .../tools/dotc/transform/TreeChecker.scala | 2 +- .../dotty/tools/dotc/ast/TreeInfoTest.scala | 3 +- 9 files changed, 181 insertions(+), 134 deletions(-) diff --git a/compiler/src/dotty/tools/backend/jvm/DottyBackendInterface.scala b/compiler/src/dotty/tools/backend/jvm/DottyBackendInterface.scala index 2040b39c3071..923f0a95a632 100644 --- a/compiler/src/dotty/tools/backend/jvm/DottyBackendInterface.scala +++ b/compiler/src/dotty/tools/backend/jvm/DottyBackendInterface.scala @@ -634,7 +634,7 @@ class DottyBackendInterface(outputDirectory: AbstractFile, val superCallsMap: Ma toDenot(sym)(shiftedContext).isStatic(shiftedContext) } - def isStaticConstructor: Boolean = (isStaticMember && isClassConstructor) || (sym.name eq core.Names.STATIC_CONSTRUCTOR) + def isStaticConstructor: Boolean = (isStaticMember && isClassConstructor) || (sym.name eq nme.STATIC_CONSTRUCTOR) // navigation diff --git a/compiler/src/dotty/tools/dotc/core/NameKinds.scala b/compiler/src/dotty/tools/dotc/core/NameKinds.scala index 84d5c9ff489c..735c6a30234d 100644 --- a/compiler/src/dotty/tools/dotc/core/NameKinds.scala +++ b/compiler/src/dotty/tools/dotc/core/NameKinds.scala @@ -254,7 +254,7 @@ object NameKinds { 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 + if (original == nme.DEFAULT_GETTER_INIT) original = nme.CONSTRUCTOR apply(original, index) } else name diff --git a/compiler/src/dotty/tools/dotc/core/NameOps.scala b/compiler/src/dotty/tools/dotc/core/NameOps.scala index 915bd52abb20..3ccf3f0d59ab 100644 --- a/compiler/src/dotty/tools/dotc/core/NameOps.scala +++ b/compiler/src/dotty/tools/dotc/core/NameOps.scala @@ -100,8 +100,10 @@ object NameOps { * it is also called from the backend. */ def stripModuleClassSuffix: N = likeSpaced { - val semName = - if (name.isSimple && name.endsWith("$")) name.unmangleClassName else name + val semName = name.toTermName match { + case name: SimpleTermName if name.endsWith("$") => name.unmangleClassName + case _ => name + } semName.exclude(ModuleClassName) } diff --git a/compiler/src/dotty/tools/dotc/core/Names.scala b/compiler/src/dotty/tools/dotc/core/Names.scala index 6a8d562687eb..93aefdb43abb 100644 --- a/compiler/src/dotty/tools/dotc/core/Names.scala +++ b/compiler/src/dotty/tools/dotc/core/Names.scala @@ -9,6 +9,7 @@ import Texts.Text import Decorators._ import Contexts.Context import StdNames.str +import util.Chars.isIdentifierStart import collection.IndexedSeqOptimized import collection.generic.CanBuildFrom import collection.mutable.{ Builder, StringBuilder, AnyRefMap } @@ -18,8 +19,6 @@ import util.{DotClass, SimpleMap} import config.Config import java.util.HashMap -//import annotation.volatile - object Names { import NameKinds._ @@ -33,14 +32,10 @@ object Names { implicit def eqName: Eq[Name, Name] = Eq - /** A name is essentially a string, with three differences - * 1. Names belong in one of two name spaces: they are type names or term names. - * Term names have a sub-category of "local" field names. - * The same string can correspond a name in each of the three namespaces. - * 2. Names are hash-consed. Two names - * representing the same string in the same universe are always reference identical. - * 3. Names are intended to be encoded strings. @see dotc.util.NameTransformer. - * The encoding will be applied when converting a string to a name. + /** A name if either a term name or a type name. Term names can be simple + * or derived. A simple term name is essentially an interned string stored + * in a name table. A derived term name adds a tag, and possibly a number + * or a further simple name to some other name. */ abstract class Name extends DotClass with PreName { @@ -65,8 +60,10 @@ object Names { /** This name downcasted to a term name */ def asTermName: TermName - def isSimple: Boolean + /** This name downcasted to a simple term name */ def asSimpleName: SimpleTermName + + /** This name converted to a simple term name */ def toSimpleName: SimpleTermName @sharable // because it's just a cache for performance @@ -74,6 +71,9 @@ object Names { protected[Names] def mangle: ThisName + /** This name converted to a simple term name and in addition + * with all symbolic operator characters expanded. + */ final def mangled: ThisName = { if (myMangled == null) myMangled = mangle myMangled.asInstanceOf[ThisName] @@ -81,9 +81,21 @@ object Names { def mangledString: String = mangled.toString + /** Apply rewrite rule given by `f` to some part of this name, skipping and rewrapping + * other decorators. Stops at first qualified name that's encountered. + * If `f` does not apply to any part, return name unchanged. + */ def rewrite(f: PartialFunction[Name, Name]): ThisName + + /** If partial function `f` is defined for some part of this name, apply it + * in a Some, otherwise None. Stops at first qualified name that's encountered. + */ def collect[T](f: PartialFunction[Name, T]): Option[T] + + /** Apply `f` to last simple term name making up this name */ def mapLast(f: SimpleTermName => SimpleTermName): ThisName + + /** Apply `f` to all simple term names making up this name */ def mapParts(f: SimpleTermName => SimpleTermName): ThisName /** A name in the same (term or type) namespace as this name and @@ -91,49 +103,79 @@ object Names { */ def likeSpaced(name: Name): ThisName + /** A derived name consisting of this name and the added info, unless it is + * already present in this name. + * @pre This name does not have a different info of the same kind as `info`. + */ def derived(info: NameInfo): ThisName + + /** A derived name consisting of this name and the info of `kind` */ def derived(kind: ClassifiedNameKind): ThisName = derived(kind.info) + + /** This name without any info of the given `kind`. Excepted, as always, + * is the underlying name part of a qualified name. + */ def exclude(kind: NameKind): ThisName + + /** Does this name contain an info of the given kind? Excepted, as always, + * is the underlying name part of a qualified name. + */ def is(kind: NameKind): Boolean + + /** A string showing the internal structure of this name. By contrast, `toString` + * shows the name after conversion to a simple name. + */ def debugString: String + /** Convert name to text via `printer`. */ def toText(printer: Printer): Text = printer.toText(this) - /** Replace \$op_name's by corresponding operator symbols. */ + /** Replace operator expansions by corresponding operator symbols. */ def decode: ThisName - /** Replace operator symbols by corresponding \$op_name's. */ + /** Replace operator symbols by corresponding operator expansions */ def encode: ThisName + /** The first part of this (possible qualified) name */ def firstPart: SimpleTermName + + /** The last part of this (possible qualified) name */ def lastPart: SimpleTermName - /** A more efficient version of concatenation */ + /** Append `other` to the last part of this name */ def ++ (other: Name): ThisName = ++ (other.toString) def ++ (other: String): ThisName = mapLast(n => termName(n.toString + other)) + + /** Replace all occurrences of `from` to `to` in this name */ def replace(from: Char, to: Char): ThisName = mapParts(_.replace(from, to)) + /** Is this name empty? */ def isEmpty: Boolean + /** Does (the first part of) this name start with `str`? */ def startsWith(str: String): Boolean = firstPart.startsWith(str) + + /** Does (the last part of) this name end with `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] } + /** Names for terms, can be simple or derived */ 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 + + override def isTypeName = false + override def isTermName = true + override def toTermName = this + override def asTypeName = throw new ClassCastException(this + " is not a type name") + override 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 = { + override def toTypeName: TypeName = { if (_typeName == null) synchronized { if (_typeName == null) @@ -142,7 +184,7 @@ object Names { _typeName } - def likeSpaced(name: Name): TermName = name.toTermName + override def likeSpaced(name: Name): TermName = name.toTermName def info: NameInfo = SimpleTermNameKind.info def underlying: TermName = unsupported("underlying") @@ -185,10 +227,7 @@ object Names { 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 = { + override def derived(info: NameInfo): TermName = { val thisKind = this.info.kind val thatKind = info.kind if (thisKind.tag < thatKind.tag || thatKind.definesNewName) add(info) @@ -199,73 +238,60 @@ object Names { } } - def exclude(kind: NameKind): TermName = { + override 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 = { + override 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 { + /** A simple name is essentiall an interned string */ + final 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. + /** The n'th character */ def apply(n: Int) = chrs(start + n) + /** A character in this name satisfies predicate `p` */ def exists(p: Char => Boolean): Boolean = { var i = 0 while (i < length && !p(chrs(start + i))) i += 1 i < length } + /** All characters in this name satisfy predicate `p` */ def forall(p: Char => Boolean) = !exists(!p(_)) + /** The name contains given character `ch` */ def contains(ch: Char): Boolean = { var i = 0 while (i < length && chrs(start + i) != ch) i += 1 i < length } - 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 - } - + /** The index of the last occurrence of `ch` in this name which is at most + * `start`. + */ def lastIndexOf(ch: Char, start: Int = length - 1): Int = { var i = start while (i >= 0 && apply(i) != ch) i -= 1 i } + /** The index of the last occurrence of `str` in this name */ def lastIndexOfSlice(str: String): Int = toString.lastIndexOfSlice(str) - 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) - } - - def slice(from: Int, until: Int): SimpleTermName = { - assert(0 <= from && from <= until && until <= length) - termName(chrs, start + from, until - from) + /** A slice of this name making up the characters between `from` and `until` (exclusive) */ + def slice(from: Int, end: Int): SimpleTermName = { + assert(0 <= from && from <= end && end <= length) + termName(chrs, start + from, end - from) } def drop(n: Int) = slice(n, length) @@ -273,28 +299,57 @@ object Names { def dropRight(n: Int) = slice(0, length - n) def takeRight(n: Int) = slice(length - n, length) + /** Same as slice, but as a string */ + def sliceToString(from: Int, end: Int) = + if (end <= from) "" else new String(chrs, start + from, end - from) + def head = apply(0) def last = apply(length - 1) - def isSimple = true - def asSimpleName = this - def toSimpleName = this - final def mangle = encode + override def asSimpleName = this + override def toSimpleName = this + override final def mangle = encode - def rewrite(f: PartialFunction[Name, Name]): ThisName = + override 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) + override def collect[T](f: PartialFunction[Name, T]): Option[T] = f.lift(this) + override def mapLast(f: SimpleTermName => SimpleTermName) = f(this) + override def mapParts(f: SimpleTermName => SimpleTermName) = f(this) + + override def encode: SimpleTermName = { + val dontEncode = + length >= 3 && + head == '<' && last == '>' && isIdentifierStart(apply(1)) + if (dontEncode) this else NameTransformer.encode(this) + } + + override def decode: SimpleTermName = NameTransformer.decode(this) - def encode: SimpleTermName = - if (dontEncode(this)) this else NameTransformer.encode(this) + override def isEmpty = length == 0 - /** Replace \$op_name's by corresponding operator symbols. */ - def decode: SimpleTermName = NameTransformer.decode(this) + override def startsWith(str: String): Boolean = { + var i = 0 + while (i < str.length && i < length && apply(i) == str(i)) i += 1 + i == str.length + } - def firstPart = this - def lastPart = this + 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 + } + + 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 firstPart = this + override def lastPart = this override def hashCode: Int = start @@ -340,37 +395,37 @@ object Names { def debugString: String = toString } - class TypeName(val toTermName: TermName) extends Name { + final class TypeName(val toTermName: TermName) extends Name { - def isEmpty = toTermName.isEmpty + type ThisName = TypeName - def encode = toTermName.encode.toTypeName - def decode = toTermName.decode.toTypeName - def firstPart = toTermName.firstPart - def lastPart = toTermName.lastPart + override def isTypeName = true + override def isTermName = false + override def toTypeName = this + override def asTypeName = this + override def asTermName = throw new ClassCastException(this + " is not a term name") - type ThisName = TypeName - def isTypeName = true - def isTermName = false - def toTypeName = this - def asTypeName = this - def asTermName = throw new ClassCastException(this + " is not a term name") + override def asSimpleName = toTermName.asSimpleName + override def toSimpleName = toTermName.toSimpleName + override final def mangle = toTermName.mangle.toTypeName - def isSimple = toTermName.isSimple - def asSimpleName = toTermName.asSimpleName - def toSimpleName = toTermName.toSimpleName - final def mangle = toTermName.mangle.toTypeName + override def rewrite(f: PartialFunction[Name, Name]): ThisName = toTermName.rewrite(f).toTypeName + override def collect[T](f: PartialFunction[Name, T]): Option[T] = toTermName.collect(f) + override def mapLast(f: SimpleTermName => SimpleTermName) = toTermName.mapLast(f).toTypeName + override def mapParts(f: SimpleTermName => SimpleTermName) = toTermName.mapParts(f).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 + override def likeSpaced(name: Name): TypeName = name.toTypeName - def likeSpaced(name: Name): TypeName = name.toTypeName + override def derived(info: NameInfo): TypeName = toTermName.derived(info).toTypeName + override def exclude(kind: NameKind): TypeName = toTermName.exclude(kind).toTypeName + override def is(kind: NameKind) = toTermName.is(kind) - 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 isEmpty = toTermName.isEmpty + + override def encode = toTermName.encode.toTypeName + override def decode = toTermName.decode.toTypeName + override def firstPart = toTermName.firstPart + override def lastPart = toTermName.lastPart override def toString = toTermName.toString override def debugString = toTermName.debugString + "/T" @@ -379,50 +434,50 @@ object Names { /** 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) + final case class DerivedTermName(override val underlying: TermName, override val info: NameInfo) extends TermName { - def isEmpty = false - def encode: ThisName = underlying.encode.derived(info.map(_.encode)) - def decode: ThisName = 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") + override def asSimpleName = throw new UnsupportedOperationException(s"$debugString is not a simple name") - def toSimpleName = termName(toString) - final def mangle = encode.toSimpleName + override def toSimpleName = termName(toString) + override final def mangle = encode.toSimpleName - def rewrite(f: PartialFunction[Name, Name]): ThisName = + override 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] = + override 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 = + override 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 = + override def mapParts(f: SimpleTermName => SimpleTermName): ThisName = info match { case qual: QualifiedInfo => underlying.mapParts(f).derived(qual.map(f)) case _ => underlying.mapParts(f).derived(info) } + + override def isEmpty = false + override def encode: ThisName = underlying.encode.derived(info.map(_.encode)) + override def decode: ThisName = underlying.decode.derived(info.map(_.decode)) + override def firstPart = underlying.firstPart + override 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]" } // Nametable @@ -560,19 +615,6 @@ object Names { /** 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 = termName("") - val STATIC_CONSTRUCTOR: TermName = termName("") - val EMPTY_PACKAGE: TermName = termName("") - val REFINEMENT: TermName = termName("") - val LOCALDUMMY_PREFIX: TermName = termName(" 0 && name(0) == '<' && - (dontEncodeNames.contains(name) || name.startsWith(str.LOCALDUMMY_PREFIX)) - 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 diff --git a/compiler/src/dotty/tools/dotc/core/StdNames.scala b/compiler/src/dotty/tools/dotc/core/StdNames.scala index 1611aca25506..e6378b666399 100644 --- a/compiler/src/dotty/tools/dotc/core/StdNames.scala +++ b/compiler/src/dotty/tools/dotc/core/StdNames.scala @@ -127,7 +127,7 @@ object StdNames { val DOLLAR_VALUES: N = "$values" val DOLLAR_NEW: N = "$new" val EMPTY: N = "" - val EMPTY_PACKAGE: N = Names.EMPTY_PACKAGE.toString + val EMPTY_PACKAGE: N = "" val EXCEPTION_RESULT_PREFIX: N = "exceptionResult" val EXPAND_SEPARATOR: N = str.EXPAND_SEPARATOR val IMPL_CLASS_SUFFIX: N = "$class" @@ -241,7 +241,8 @@ object StdNames { // Compiler-internal val ANYname: N = "" - val CONSTRUCTOR: N = Names.CONSTRUCTOR.toString + val CONSTRUCTOR: N = "" + val STATIC_CONSTRUCTOR: N = "" val DEFAULT_CASE: N = "defaultCase$" val EVT2U: N = "evt2u$" val EQEQ_LOCAL_VAR: N = "eqEqTemp$" diff --git a/compiler/src/dotty/tools/dotc/parsing/JavaParsers.scala b/compiler/src/dotty/tools/dotc/parsing/JavaParsers.scala index 47f201a09a0d..323308caa955 100644 --- a/compiler/src/dotty/tools/dotc/parsing/JavaParsers.scala +++ b/compiler/src/dotty/tools/dotc/parsing/JavaParsers.scala @@ -111,7 +111,7 @@ object JavaParsers { def makeTemplate(parents: List[Tree], stats: List[Tree], tparams: List[TypeDef], needsDummyConstr: Boolean) = { def pullOutFirstConstr(stats: List[Tree]): (Tree, List[Tree]) = stats match { - case (meth: DefDef) :: rest if meth.name == CONSTRUCTOR => (meth, rest) + case (meth: DefDef) :: rest if meth.name == nme.CONSTRUCTOR => (meth, rest) case first :: rest => val (constr, tail) = pullOutFirstConstr(rest) (constr, first :: tail) diff --git a/compiler/src/dotty/tools/dotc/transform/MoveStatics.scala b/compiler/src/dotty/tools/dotc/transform/MoveStatics.scala index 652320639ecf..0942c0b4af11 100644 --- a/compiler/src/dotty/tools/dotc/transform/MoveStatics.scala +++ b/compiler/src/dotty/tools/dotc/transform/MoveStatics.scala @@ -9,6 +9,7 @@ import dotty.tools.dotc.core.Decorators._ import dotty.tools.dotc.core.NameOps._ import dotty.tools.dotc.core.{Flags, Names} import dotty.tools.dotc.core.Names.Name +import dotty.tools.dotc.core.StdNames.nme import dotty.tools.dotc.core.Phases.Phase import dotty.tools.dotc.core.Symbols._ import dotty.tools.dotc.core.Types.MethodType @@ -42,7 +43,7 @@ class MoveStatics extends MiniPhaseTransform with SymTransformer { thisTransform val newBodyWithStaticConstr = if (staticFields.nonEmpty) { /* do NOT put Flags.JavaStatic here. It breaks .enclosingClass */ - val staticCostructor = ctx.newSymbol(orig.symbol, Names.STATIC_CONSTRUCTOR, Flags.Synthetic | Flags.Method | Flags.Private, MethodType(Nil, defn.UnitType)) + val staticCostructor = ctx.newSymbol(orig.symbol, nme.STATIC_CONSTRUCTOR, Flags.Synthetic | Flags.Method | Flags.Private, MethodType(Nil, defn.UnitType)) staticCostructor.addAnnotation(Annotation(defn.ScalaStaticAnnot)) staticCostructor.entered diff --git a/compiler/src/dotty/tools/dotc/transform/TreeChecker.scala b/compiler/src/dotty/tools/dotc/transform/TreeChecker.scala index fe48667f58c6..2205a104ef9c 100644 --- a/compiler/src/dotty/tools/dotc/transform/TreeChecker.scala +++ b/compiler/src/dotty/tools/dotc/transform/TreeChecker.scala @@ -384,7 +384,7 @@ class TreeChecker extends Phase with SymTransformer { override def typedDefDef(ddef: untpd.DefDef, sym: Symbol)(implicit ctx: Context) = withDefinedSyms(ddef.tparams) { withDefinedSymss(ddef.vparamss) { - if (!sym.isClassConstructor && !(sym.name eq Names.STATIC_CONSTRUCTOR)) + if (!sym.isClassConstructor && !(sym.name eq nme.STATIC_CONSTRUCTOR)) assert(isValidJVMMethodName(sym.name.encode), s"${sym.name.debugString} name is invalid on jvm") ddef.vparamss.foreach(_.foreach { vparam => diff --git a/compiler/test/dotty/tools/dotc/ast/TreeInfoTest.scala b/compiler/test/dotty/tools/dotc/ast/TreeInfoTest.scala index a55973c43db4..8869a2a9af7b 100644 --- a/compiler/test/dotty/tools/dotc/ast/TreeInfoTest.scala +++ b/compiler/test/dotty/tools/dotc/ast/TreeInfoTest.scala @@ -4,6 +4,7 @@ package ast import org.junit.Test import core.Names._ +import core.StdNames.nme import core.Types._ import core.Symbols._ import org.junit.Assert._ @@ -19,7 +20,7 @@ class TreeInfoTest extends DottyTest { val xTree = tree.find(tree => tree.symbol.name == termName("x")).get val path = defPath(xTree.symbol, tree) assertEquals(List( - ("PackageDef", EMPTY_PACKAGE), + ("PackageDef", nme.EMPTY_PACKAGE), ("TypeDef", typeName("A")), ("Template", termName("")), ("DefDef", termName("bar")), From 815c36fb1fda8561e75f93212780ef30bd664b97 Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Fri, 7 Apr 2017 18:14:47 +0200 Subject: [PATCH 17/31] Rename SimpleTermName -> SimpleName, DerivedTermName -> DerivedName --- .../dotty/tools/dotc/core/Denotations.scala | 2 +- .../src/dotty/tools/dotc/core/NameKinds.scala | 59 +++++++------- .../src/dotty/tools/dotc/core/NameOps.scala | 14 ++-- .../src/dotty/tools/dotc/core/Names.scala | 80 +++++++++---------- .../tools/dotc/core/SymDenotations.scala | 4 +- .../dotty/tools/dotc/core/SymbolLoaders.scala | 2 +- .../dotc/core/classfile/ClassfileParser.scala | 18 ++--- .../tools/dotc/core/tasty/NameBuffer.scala | 8 +- .../dotty/tools/dotc/parsing/Scanners.scala | 2 +- .../tools/dotc/util/NameTransformer.scala | 10 +-- 10 files changed, 101 insertions(+), 98 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/core/Denotations.scala b/compiler/src/dotty/tools/dotc/core/Denotations.scala index ffc58f2e8d2f..aea68c34929c 100644 --- a/compiler/src/dotty/tools/dotc/core/Denotations.scala +++ b/compiler/src/dotty/tools/dotc/core/Denotations.scala @@ -1194,7 +1194,7 @@ object Denotations { select(recur(prefix), wrap(selector)) case qn @ AnyQualifiedName(prefix, _) => recur(prefix, n => wrap(qn.info.mkString(n).toTermName)) - case path: SimpleTermName => + case path: SimpleName => def recurSimple(len: Int, wrap: TermName => Name): Denotation = { val point = path.lastIndexOf('.', len - 1) val selector = wrap(path.slice(point + 1, len).asTermName) diff --git a/compiler/src/dotty/tools/dotc/core/NameKinds.scala b/compiler/src/dotty/tools/dotc/core/NameKinds.scala index 735c6a30234d..46de7d51b1ad 100644 --- a/compiler/src/dotty/tools/dotc/core/NameKinds.scala +++ b/compiler/src/dotty/tools/dotc/core/NameKinds.scala @@ -11,6 +11,7 @@ import Decorators._ import Contexts.Context import collection.mutable +/** Defines possible kinds of NameInfo of a derived name */ object NameKinds { // These are sharable since all NameKinds are created eagerly at the start of the program @@ -20,12 +21,14 @@ object NameKinds { @sharable private val qualifiedNameKinds = new mutable.HashMap[Int, QualifiedNameKind] @sharable private val uniqueNameKinds = new mutable.HashMap[String, UniqueNameKind] + /** A class for the info stored in a derived name */ abstract class NameInfo extends DotClass { def kind: NameKind def mkString(underlying: TermName): String - def map(f: SimpleTermName => SimpleTermName): NameInfo = this + def map(f: SimpleName => SimpleName): NameInfo = this } + /** The kind of a derived name info */ abstract class NameKind(val tag: Int) extends DotClass { self => type ThisInfo <: Info class Info extends NameInfo { this: ThisInfo => @@ -34,12 +37,12 @@ object NameKinds { override def toString = infoString } def definesNewName = false - def unmangle(name: SimpleTermName): TermName = name + def unmangle(name: SimpleName): TermName = name def mkString(underlying: TermName, info: ThisInfo): String def infoString: String } - object SimpleTermNameKind extends NameKind(UTF8) { self => + object SimpleNameKind extends NameKind(UTF8) { self => type ThisInfo = Info val info = new Info def mkString(underlying: TermName, info: ThisInfo) = unsupported("mkString") @@ -51,8 +54,8 @@ object NameKinds { 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) + def unapply(name: DerivedName): Option[TermName] = name match { + case DerivedName(underlying, `info`) => Some(underlying) case _ => None } simpleNameKinds(tag) = this @@ -62,7 +65,7 @@ object NameKinds { 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 = + override def unmangle(name: SimpleName): TermName = if (name.startsWith(prefix)) apply(name.drop(prefix.length).asSimpleName) else name } @@ -70,23 +73,23 @@ object NameKinds { 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 = + override def unmangle(name: SimpleName): TermName = if (name.endsWith(suffix)) apply(name.take(name.length - suffix.length).asSimpleName) else name } trait QualifiedInfo extends NameInfo { - val name: SimpleTermName + val name: SimpleName } 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)) + case class QualInfo(val name: SimpleName) extends Info with QualifiedInfo { + override def map(f: SimpleName => SimpleName): NameInfo = new QualInfo(f(name)) override def toString = s"$infoString $name" } - def apply(qual: TermName, name: SimpleTermName): TermName = + def apply(qual: TermName, name: SimpleName): TermName = qual.derived(new QualInfo(name)) /** Overloaded version used only for ExpandedName and TraitSetterName. @@ -94,12 +97,12 @@ object NameKinds { * 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 name: SimpleName => 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)) + def unapply(name: DerivedName): Option[(TermName, SimpleName)] = name match { + case DerivedName(qual, info: this.QualInfo) => Some((qual, info.name)) case _ => None } @@ -113,8 +116,8 @@ object NameKinds { } object AnyQualifiedName { - def unapply(name: DerivedTermName): Option[(TermName, SimpleTermName)] = name match { - case DerivedTermName(qual, info: QualifiedInfo) => + def unapply(name: DerivedName): Option[(TermName, SimpleName)] = name match { + case DerivedName(qual, info: QualifiedInfo) => Some((name.underlying, info.name)) case _ => None } @@ -131,11 +134,11 @@ object NameKinds { } 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)) + def unapply(name: DerivedName): Option[(TermName, Int)] = name match { + case DerivedName(underlying, info: this.NumberedInfo) => Some((underlying, info.num)) case _ => None } - protected def skipSeparatorAndNum(name: SimpleTermName, separator: String): Int = { + protected def skipSeparatorAndNum(name: SimpleName, separator: String): Int = { var i = name.length while (i > 0 && name(i - 1).isDigit) i -= 1 if (i > separator.length && i < name.length && @@ -145,8 +148,8 @@ object NameKinds { } object AnyNumberedName { - def unapply(name: DerivedTermName): Option[(TermName, Int)] = name match { - case DerivedTermName(qual, info: NumberedInfo) => Some((qual, info.num)) + def unapply(name: DerivedName): Option[(TermName, Int)] = name match { + case DerivedName(qual, info: NumberedInfo) => Some((qual, info.num)) case _ => None } } @@ -166,8 +169,8 @@ object NameKinds { } object AnyUniqueName { - def unapply(name: DerivedTermName): Option[(TermName, String, Int)] = name match { - case DerivedTermName(qual, info: NumberedInfo) => + def unapply(name: DerivedName): Option[(TermName, String, Int)] = name match { + case DerivedName(qual, info: NumberedInfo) => info.kind match { case unique: UniqueNameKind => Some((qual, unique.separator, info.num)) case _ => None @@ -184,7 +187,7 @@ object NameKinds { private val FalseSuper = termName("$$super") private val FalseSuperLength = FalseSuper.length - override def unmangle(name: SimpleTermName): TermName = { + override def unmangle(name: SimpleName): TermName = { var i = name.lastIndexOfSlice(str.EXPAND_SEPARATOR) if (i < 0) name else { @@ -224,7 +227,7 @@ object NameKinds { val SuperArgName = new UniqueNameKind("$superArg$") val UniqueExtMethName = new UniqueNameKind("$extension") { - override def unmangle(name: SimpleTermName): TermName = { + override def unmangle(name: SimpleName): TermName = { val i = skipSeparatorAndNum(name, separator) if (i > 0) { val index = name.drop(i).toString.toInt @@ -249,7 +252,7 @@ object NameKinds { prefix.toString + str.DEFAULT_GETTER + (info.num + 1) } // TODO: Reduce code duplication with UniqueExtMethName - override def unmangle(name: SimpleTermName): TermName = { + override def unmangle(name: SimpleName): TermName = { val i = skipSeparatorAndNum(name, str.DEFAULT_GETTER) if (i > 0) { val index = name.drop(i).toString.toInt - 1 @@ -300,8 +303,8 @@ object NameKinds { 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)) + def unapply(name: DerivedName): Option[(TermName, Signature)] = name match { + case DerivedName(underlying, info: SignedInfo) => Some((underlying, info.sig)) case _ => None } diff --git a/compiler/src/dotty/tools/dotc/core/NameOps.scala b/compiler/src/dotty/tools/dotc/core/NameOps.scala index 3ccf3f0d59ab..dd8c615e5950 100644 --- a/compiler/src/dotty/tools/dotc/core/NameOps.scala +++ b/compiler/src/dotty/tools/dotc/core/NameOps.scala @@ -52,8 +52,8 @@ object NameOps { implicit class NameDecorator[N <: Name](val name: N) extends AnyVal { import nme._ - def testSimple(f: SimpleTermName => Boolean): Boolean = name match { - case name: SimpleTermName => f(name) + def testSimple(f: SimpleName => Boolean): Boolean = name match { + case name: SimpleName => f(name) case name: TypeName => name.toTermName.testSimple(f) case _ => false } @@ -83,7 +83,7 @@ object NameOps { def isOpAssignmentName: Boolean = name match { case raw.NE | raw.LE | raw.GE | EMPTY => false - case name: SimpleTermName => + case name: SimpleName => name.length > 0 && name.last == '=' && name.head != '=' && isOperatorPart(name.head) case _ => false @@ -101,7 +101,7 @@ object NameOps { */ def stripModuleClassSuffix: N = likeSpaced { val semName = name.toTermName match { - case name: SimpleTermName if name.endsWith("$") => name.unmangleClassName + case name: SimpleName if name.endsWith("$") => name.unmangleClassName case _ => name } semName.exclude(ModuleClassName) @@ -229,7 +229,7 @@ object NameOps { def compactified(implicit ctx: Context): TermName = termName(compactify(name.toString)) def unmangleClassName: N = name.toTermName match { - case name: SimpleTermName + case name: SimpleName if name.endsWith(str.MODULE_SUFFIX) && !nme.falseModuleClassNames.contains(name) => likeSpaced(name.dropRight(str.MODULE_SUFFIX.length).moduleClassName) case _ => name @@ -237,11 +237,11 @@ object NameOps { def unmangle(kind: NameKind): N = likeSpaced { name rewrite { - case unmangled: SimpleTermName => + case unmangled: SimpleName => kind.unmangle(unmangled) case ExpandedName(prefix, last) => kind.unmangle(last) rewrite { - case kernel: SimpleTermName => + case kernel: SimpleName => ExpandedName(prefix, kernel) } } diff --git a/compiler/src/dotty/tools/dotc/core/Names.scala b/compiler/src/dotty/tools/dotc/core/Names.scala index 93aefdb43abb..bf17efef8670 100644 --- a/compiler/src/dotty/tools/dotc/core/Names.scala +++ b/compiler/src/dotty/tools/dotc/core/Names.scala @@ -61,10 +61,10 @@ object Names { def asTermName: TermName /** This name downcasted to a simple term name */ - def asSimpleName: SimpleTermName + def asSimpleName: SimpleName /** This name converted to a simple term name */ - def toSimpleName: SimpleTermName + def toSimpleName: SimpleName @sharable // because it's just a cache for performance private[this] var myMangled: Name = null @@ -93,10 +93,10 @@ object Names { def collect[T](f: PartialFunction[Name, T]): Option[T] /** Apply `f` to last simple term name making up this name */ - def mapLast(f: SimpleTermName => SimpleTermName): ThisName + def mapLast(f: SimpleName => SimpleName): ThisName /** Apply `f` to all simple term names making up this name */ - def mapParts(f: SimpleTermName => SimpleTermName): ThisName + def mapParts(f: SimpleName => SimpleName): ThisName /** A name in the same (term or type) namespace as this name and * with same characters as given `name`. @@ -137,10 +137,10 @@ object Names { def encode: ThisName /** The first part of this (possible qualified) name */ - def firstPart: SimpleTermName + def firstPart: SimpleName /** The last part of this (possible qualified) name */ - def lastPart: SimpleTermName + def lastPart: SimpleName /** Append `other` to the last part of this name */ def ++ (other: Name): ThisName = ++ (other.toString) @@ -186,32 +186,32 @@ object Names { override def likeSpaced(name: Name): TermName = name.toTermName - def info: NameInfo = SimpleTermNameKind.info + def info: NameInfo = SimpleNameKind.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 => + private def getDerived(info: NameInfo): DerivedName /* | Null */= derivedNames match { + case derivedNames: SimpleMap[NameInfo, DerivedName] @unchecked => derivedNames(info) - case derivedNames: HashMap[NameInfo, DerivedTermName] @unchecked => + case derivedNames: HashMap[NameInfo, DerivedName] @unchecked => derivedNames.get(info) } - private def putDerived(info: NameInfo, name: DerivedTermName): name.type = { + private def putDerived(info: NameInfo, name: DerivedName): name.type = { derivedNames match { - case derivedNames: SimpleMap[NameInfo, DerivedTermName] @unchecked => + case derivedNames: SimpleMap[NameInfo, DerivedName] @unchecked => if (derivedNames.size < 4) this.derivedNames = derivedNames.updated(info, name) else { - val newMap = new HashMap[NameInfo, DerivedTermName] + val newMap = new HashMap[NameInfo, DerivedName] derivedNames.foreachBinding(newMap.put(_, _)) newMap.put(info, name) this.derivedNames = newMap } - case derivedNames: HashMap[NameInfo, DerivedTermName] @unchecked => + case derivedNames: HashMap[NameInfo, DerivedName] @unchecked => derivedNames.put(info, name) } name @@ -219,7 +219,7 @@ object Names { private def add(info: NameInfo): TermName = synchronized { getDerived(info) match { - case null => putDerived(info, new DerivedTermName(this, info)) + case null => putDerived(info, new DerivedName(this, info)) case derivedName => derivedName } } @@ -253,7 +253,7 @@ object Names { } /** A simple name is essentiall an interned string */ - final class SimpleTermName(val start: Int, val length: Int, @sharable private[Names] var next: SimpleTermName) extends TermName { + final class SimpleName(val start: Int, val length: Int, @sharable private[Names] var next: SimpleName) extends TermName { // `next` is @sharable because it is only modified in the synchronized block of termName. /** The n'th character */ @@ -289,7 +289,7 @@ object Names { def lastIndexOfSlice(str: String): Int = toString.lastIndexOfSlice(str) /** A slice of this name making up the characters between `from` and `until` (exclusive) */ - def slice(from: Int, end: Int): SimpleTermName = { + def slice(from: Int, end: Int): SimpleName = { assert(0 <= from && from <= end && end <= length) termName(chrs, start + from, end - from) } @@ -313,17 +313,17 @@ object Names { override def rewrite(f: PartialFunction[Name, Name]): ThisName = if (f.isDefinedAt(this)) likeSpaced(f(this)) else this override def collect[T](f: PartialFunction[Name, T]): Option[T] = f.lift(this) - override def mapLast(f: SimpleTermName => SimpleTermName) = f(this) - override def mapParts(f: SimpleTermName => SimpleTermName) = f(this) + override def mapLast(f: SimpleName => SimpleName) = f(this) + override def mapParts(f: SimpleName => SimpleName) = f(this) - override def encode: SimpleTermName = { + override def encode: SimpleName = { val dontEncode = length >= 3 && head == '<' && last == '>' && isIdentifierStart(apply(1)) if (dontEncode) this else NameTransformer.encode(this) } - override def decode: SimpleTermName = NameTransformer.decode(this) + override def decode: SimpleName = NameTransformer.decode(this) override def isEmpty = length == 0 @@ -339,7 +339,7 @@ object Names { i > str.length } - override def replace(from: Char, to: Char): SimpleTermName = { + override def replace(from: Char, to: Char): SimpleName = { val cs = new Array[Char](length) Array.copy(chrs, start, cs, 0, length) for (i <- 0 until length) { @@ -411,8 +411,8 @@ object Names { override def rewrite(f: PartialFunction[Name, Name]): ThisName = toTermName.rewrite(f).toTypeName override def collect[T](f: PartialFunction[Name, T]): Option[T] = toTermName.collect(f) - override def mapLast(f: SimpleTermName => SimpleTermName) = toTermName.mapLast(f).toTypeName - override def mapParts(f: SimpleTermName => SimpleTermName) = toTermName.mapParts(f).toTypeName + override def mapLast(f: SimpleName => SimpleName) = toTermName.mapLast(f).toTypeName + override def mapParts(f: SimpleName => SimpleName) = toTermName.mapParts(f).toTypeName override def likeSpaced(name: Name): TypeName = name.toTypeName @@ -434,7 +434,7 @@ object Names { /** A term name that's derived from an `underlying` name and that * adds `info` to it. */ - final case class DerivedTermName(override val underlying: TermName, override val info: NameInfo) + final case class DerivedName(override val underlying: TermName, override val info: NameInfo) extends TermName { override def asSimpleName = throw new UnsupportedOperationException(s"$debugString is not a simple name") @@ -456,13 +456,13 @@ object Names { case _ => underlying.collect(f) } - override def mapLast(f: SimpleTermName => SimpleTermName): ThisName = + override def mapLast(f: SimpleName => SimpleName): ThisName = info match { case qual: QualifiedInfo => underlying.derived(qual.map(f)) case _ => underlying.mapLast(f).derived(info) } - override def mapParts(f: SimpleTermName => SimpleTermName): ThisName = + override def mapParts(f: SimpleName => SimpleName): ThisName = info match { case qual: QualifiedInfo => underlying.mapParts(f).derived(qual.map(f)) case _ => underlying.mapParts(f).derived(info) @@ -496,7 +496,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[SimpleTermName](InitialHashSize) + private var table = new Array[SimpleName](InitialHashSize) /** The number of defined names. */ @sharable // because it's only mutated in synchronized block of termName @@ -524,7 +524,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): SimpleTermName = synchronized { + def termName(cs: Array[Char], offset: Int, len: Int): SimpleName = synchronized { util.Stats.record("termName") val h = hashValue(cs, offset, len) & (table.size - 1) @@ -548,7 +548,7 @@ object Names { } /** Rehash chain of names */ - def rehash(name: SimpleTermName): Unit = + def rehash(name: SimpleName): Unit = if (name != null) { val oldNext = name.next val h = hashValue(chrs, name.start, name.length) & (table.size - 1) @@ -562,7 +562,7 @@ object Names { size += 1 if (size.toDouble / table.size > fillFactor) { val oldTable = table - table = new Array[SimpleTermName](table.size * 2) + table = new Array[SimpleName](table.size * 2) for (i <- 0 until oldTable.size) rehash(oldTable(i)) } } @@ -574,7 +574,7 @@ object Names { return name name = name.next } - name = new SimpleTermName(nc, len, next) + name = new SimpleName(nc, len, next) enterChars() table(h) = name incTableSize() @@ -590,7 +590,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): SimpleTermName = { + def termName(bs: Array[Byte], offset: Int, len: Int): SimpleName = { val chars = Codec.fromUTF8(bs, offset, len) termName(chars, 0, chars.length) } @@ -602,12 +602,12 @@ object Names { termName(bs, offset, len).toTypeName /** Create a term name from a string, without encoding operators */ - def termName(s: String): SimpleTermName = termName(s.toCharArray, 0, s.length) + def termName(s: String): SimpleName = 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) - table(0) = new SimpleTermName(-1, 0, null) + table(0) = new SimpleName(-1, 0, null) /** The term name represented by the empty string */ val EmptyTermName: TermName = table(0) @@ -633,7 +633,7 @@ object Names { assert(x == y) 0 } - private def compareSimpleNames(x: SimpleTermName, y: SimpleTermName): Int = { + private def compareSimpleNames(x: SimpleName, y: SimpleName): Int = { val until = x.length min y.length var i = 0 while (i < until && x(i) == y(i)) i = i + 1 @@ -645,14 +645,14 @@ object Names { } } private def compareTermNames(x: TermName, y: TermName): Int = x match { - case x: SimpleTermName => + case x: SimpleName => y match { - case y: SimpleTermName => compareSimpleNames(x, y) + case y: SimpleName => compareSimpleNames(x, y) case _ => -1 } - case DerivedTermName(xPre, xInfo) => + case DerivedName(xPre, xInfo) => y match { - case DerivedTermName(yPre, yInfo) => + case DerivedName(yPre, yInfo) => val s = compareInfos(xInfo, yInfo) if (s == 0) compareTermNames(xPre, yPre) else s case _ => 1 diff --git a/compiler/src/dotty/tools/dotc/core/SymDenotations.scala b/compiler/src/dotty/tools/dotc/core/SymDenotations.scala index 188f6ca06181..80ed41837921 100644 --- a/compiler/src/dotty/tools/dotc/core/SymDenotations.scala +++ b/compiler/src/dotty/tools/dotc/core/SymDenotations.scala @@ -402,10 +402,10 @@ object SymDenotations { if (kind.separator == "$") // duplicate scalac's behavior: don't write a double '$$' for module class members. prefix = prefix.exclude(ModuleClassName) - def qualify(n: SimpleTermName) = + def qualify(n: SimpleName) = kind(prefix.toTermName, if (filler.isEmpty) n else termName(filler + n)) val fn = name rewrite { - case name: SimpleTermName => qualify(name) + case name: SimpleName => qualify(name) case name @ AnyQualifiedName(_, _) => qualify(name.toSimpleName) } if (isType) fn.toTypeName else fn.toTermName diff --git a/compiler/src/dotty/tools/dotc/core/SymbolLoaders.scala b/compiler/src/dotty/tools/dotc/core/SymbolLoaders.scala index 297941cd253a..24e0d9841801 100644 --- a/compiler/src/dotty/tools/dotc/core/SymbolLoaders.scala +++ b/compiler/src/dotty/tools/dotc/core/SymbolLoaders.scala @@ -186,7 +186,7 @@ class SymbolLoaders { private[core] val currentDecls: MutableScope = new PackageScope() - def isFlatName(name: SimpleTermName) = name.lastIndexOf('$', name.length - 2) >= 0 + def isFlatName(name: SimpleName) = name.lastIndexOf('$', name.length - 2) >= 0 def isFlatName(classRep: ClassRepresentation) = { val idx = classRep.name.indexOf('$') diff --git a/compiler/src/dotty/tools/dotc/core/classfile/ClassfileParser.scala b/compiler/src/dotty/tools/dotc/core/classfile/ClassfileParser.scala index 51cde0d71f38..c23ff6d2affe 100644 --- a/compiler/src/dotty/tools/dotc/core/classfile/ClassfileParser.scala +++ b/compiler/src/dotty/tools/dotc/core/classfile/ClassfileParser.scala @@ -39,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: SimpleTermName = _ // JVM name of the current class + protected var currentClassName: SimpleName = _ // JVM name of the current class protected var classTParams = Map[Name,Symbol]() classRoot.info = (new NoCompleter).withDecls(instanceScope) @@ -47,7 +47,7 @@ class ClassfileParser( private def currentIsTopLevel(implicit ctx: Context) = classRoot.owner is Flags.PackageClass - private def mismatchError(className: SimpleTermName) = + private def mismatchError(className: SimpleName) = 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 { @@ -254,14 +254,14 @@ 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: SimpleTermName, owner: Symbol = null)(implicit ctx: Context): Type = { + private def sigToType(sig: SimpleName, 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): SimpleTermName = { + def subName(isDelimiter: Char => Boolean): SimpleName = { val start = index while (!isDelimiter(sig(index))) { index += 1 } sig.slice(start, index) @@ -901,7 +901,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[SimpleTermName](len) + private val internalized = new Array[SimpleName](len) { var i = 1 while (i < starts.length) { @@ -928,12 +928,12 @@ class ClassfileParser( } /** Return the name found at given index. */ - def getName(index: Int): SimpleTermName = { + def getName(index: Int): SimpleName = { if (index <= 0 || len <= index) errorBadIndex(index) values(index) match { - case name: SimpleTermName => name + case name: SimpleName => name case null => val start = starts(index) if (in.buf(start).toInt != CONSTANT_UTF8) errorBadTag(start) @@ -948,7 +948,7 @@ class ClassfileParser( new DataInputStream(new ByteArrayInputStream(bytes, offset, len)).readUTF /** Return the name found at given index in the constant pool, with '/' replaced by '.'. */ - def getExternalName(index: Int): SimpleTermName = { + def getExternalName(index: Int): SimpleName = { if (index <= 0 || len <= index) errorBadIndex(index) @@ -977,7 +977,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): SimpleTermName = { + def getClassName(index: Int): SimpleName = { val start = starts(index) if (in.buf(start).toInt != CONSTANT_CLASS) errorBadTag(start) getExternalName(in.getChar(start + 1)) diff --git a/compiler/src/dotty/tools/dotc/core/tasty/NameBuffer.scala b/compiler/src/dotty/tools/dotc/core/tasty/NameBuffer.scala index 270d6be56d8f..d10879978b7c 100644 --- a/compiler/src/dotty/tools/dotc/core/tasty/NameBuffer.scala +++ b/compiler/src/dotty/tools/dotc/core/tasty/NameBuffer.scala @@ -4,7 +4,7 @@ package core package tasty import collection.mutable -import Names.{Name, chrs, SimpleTermName, DerivedTermName} +import Names.{Name, chrs, SimpleName, DerivedName} import NameOps.NameDecorator import NameKinds._ import Decorators._ @@ -31,7 +31,7 @@ class NameBuffer extends TastyBuffer(10000) { case AnyUniqueName(original, separator, num) => nameIndex(separator.toTermName) if (!original.isEmpty) nameIndex(original) - case DerivedTermName(original, _) => + case DerivedName(original, _) => nameIndex(original) case _ => } @@ -56,7 +56,7 @@ class NameBuffer extends TastyBuffer(10000) { val tag = name.toTermName.info.kind.tag writeByte(tag) name.toTermName match { - case name: SimpleTermName => + case name: SimpleName => val bytes = if (name.length == 0) new Array[Byte](0) else Codec.toUTF8(chrs, name.start, name.length) @@ -78,7 +78,7 @@ class NameBuffer extends TastyBuffer(10000) { withLength( { writeNameRef(original); writeNameRef(result); params.foreach(writeNameRef) }, if ((params.length + 2) * maxIndexWidth <= maxNumInByte) 1 else 2) - case DerivedTermName(original, _) => + case DerivedName(original, _) => withLength { writeNameRef(original) } } } diff --git a/compiler/src/dotty/tools/dotc/parsing/Scanners.scala b/compiler/src/dotty/tools/dotc/parsing/Scanners.scala index ea4122012174..9fcde7181526 100644 --- a/compiler/src/dotty/tools/dotc/parsing/Scanners.scala +++ b/compiler/src/dotty/tools/dotc/parsing/Scanners.scala @@ -38,7 +38,7 @@ object Scanners { var lastOffset: Offset = 0 /** the name of an identifier */ - var name: SimpleTermName = null + var name: SimpleName = null /** the string value of a literal */ var strVal: String = null diff --git a/compiler/src/dotty/tools/dotc/util/NameTransformer.scala b/compiler/src/dotty/tools/dotc/util/NameTransformer.scala index 1ecbebe67fc4..18c5b09823b2 100644 --- a/compiler/src/dotty/tools/dotc/util/NameTransformer.scala +++ b/compiler/src/dotty/tools/dotc/util/NameTransformer.scala @@ -44,7 +44,7 @@ object NameTransformer { /** Expand characters that are illegal as JVM method names by `$u`, followed * by the character's unicode expansion. */ - def avoidIllegalChars(name: SimpleTermName) = { + def avoidIllegalChars(name: SimpleName) = { var i = name.length - 1 while (i >= 0 && isValidJVMMethodChar(name(i))) i -= 1 if (i >= 0) @@ -62,8 +62,8 @@ object NameTransformer { * Operator symbols are only recognized if they make up the whole name, or * if they make up the last part of the name which follows a `_`. */ - def encode(name: SimpleTermName): SimpleTermName = { - def loop(len: Int, ops: List[String]): SimpleTermName = { + def encode(name: SimpleName): SimpleName = { + def loop(len: Int, ops: List[String]): SimpleName = { def convert = if (ops.isEmpty) name else { @@ -89,8 +89,8 @@ object NameTransformer { * Operator expansions are only recognized if they make up the whole name, or * if they make up the last part of the name which follows a `_`. */ - def decode(name: SimpleTermName): SimpleTermName = { - def loop(len: Int, ops: List[Char]): SimpleTermName = { + def decode(name: SimpleName): SimpleName = { + def loop(len: Int, ops: List[Char]): SimpleName = { def convert = if (ops.isEmpty) name else { From 5b17a4453f08fa1fd1129502840c48d2afce712b Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Sat, 8 Apr 2017 10:45:15 +0200 Subject: [PATCH 18/31] Polish and document NameKinds --- .../src/dotty/tools/dotc/core/NameKinds.scala | 88 ++++++++++++++++--- .../src/dotty/tools/dotc/core/NameOps.scala | 15 +++- .../src/dotty/tools/dotc/core/Names.scala | 6 +- .../src/dotty/tools/dotc/core/Signature.scala | 8 -- .../tools/dotc/core/tasty/NameBuffer.scala | 2 - .../dotc/core/tasty/TastyUnpickler.scala | 8 +- 6 files changed, 94 insertions(+), 33 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/core/NameKinds.scala b/compiler/src/dotty/tools/dotc/core/NameKinds.scala index 46de7d51b1ad..672858c2a3f2 100644 --- a/compiler/src/dotty/tools/dotc/core/NameKinds.scala +++ b/compiler/src/dotty/tools/dotc/core/NameKinds.scala @@ -19,6 +19,7 @@ object NameKinds { // 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 numberedNameKinds = new mutable.HashMap[Int, NumberedNameKind] @sharable private val uniqueNameKinds = new mutable.HashMap[String, UniqueNameKind] /** A class for the info stored in a derived name */ @@ -28,20 +29,39 @@ object NameKinds { def map(f: SimpleName => SimpleName): NameInfo = this } - /** The kind of a derived name info */ + /** An abstract base class of classes that define the kind of a derived name info */ abstract class NameKind(val tag: Int) extends DotClass { self => + + /** The info class defined by this kind */ type ThisInfo <: Info + + /** A simple info type; some subclasses of Kind define more refined versions */ class Info extends NameInfo { this: ThisInfo => def kind = self def mkString(underlying: TermName) = self.mkString(underlying, this) override def toString = infoString } + + /** Does this kind define logically a new name? Tested by the `rewrite` and `collect` + * combinators of names. + */ def definesNewName = false + + /** Unmangle simple name `name` into a name of this kind, or return + * original name if this is not possible. + */ def unmangle(name: SimpleName): TermName = name + + /** Turn a name of this kind consisting of an `underlying` prefix + * and the given `info` into a string. + */ def mkString(underlying: TermName, info: ThisInfo): String + + /** A string used for displaying the structure of a name */ def infoString: String } + /** The kind of SimpleNames */ object SimpleNameKind extends NameKind(UTF8) { self => type ThisInfo = Info val info = new Info @@ -49,18 +69,25 @@ object NameKinds { def infoString = unsupported("infoString") } + /** The kind of names that add a simple classification to an underlying name. + */ 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) + + /** Build a new name of this kind from an underlying name */ + def apply(underlying: TermName) = underlying.derived(info) + + /** Extractor operation for names of this kind */ def unapply(name: DerivedName): Option[TermName] = name match { case DerivedName(underlying, `info`) => Some(underlying) case _ => None } + simpleNameKinds(tag) = this } + /** The kind of names that get formed by adding a prefix to an underlying name */ 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) = @@ -70,6 +97,7 @@ object NameKinds { else name } + /** The kind of names that get formed by appending a suffix to an underlying 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 @@ -78,10 +106,16 @@ object NameKinds { else name } + /** A base trait for infos that define an additional selector name */ trait QualifiedInfo extends NameInfo { val name: SimpleName } + /** The kind of qualified names, consisting of an underlying name as a prefix, + * followed by a separator, followed by a simple selector name. + * + * A qualified names always constitutes a new name, different from its underlying name. + */ class QualifiedNameKind(tag: Int, val separator: String) extends NameKind(tag) { type ThisInfo = QualInfo @@ -89,6 +123,7 @@ object NameKinds { override def map(f: SimpleName => SimpleName): NameInfo = new QualInfo(f(name)) override def toString = s"$infoString $name" } + def apply(qual: TermName, name: SimpleName): TermName = qual.derived(new QualInfo(name)) @@ -110,11 +145,13 @@ object NameKinds { def mkString(underlying: TermName, info: ThisInfo) = s"$underlying$separator${info.name}" + def infoString = s"Qualified $separator" qualifiedNameKinds(tag) = this } + /** An extractor for qualified names of an arbitrary kind */ object AnyQualifiedName { def unapply(name: DerivedName): Option[(TermName, SimpleName)] = name match { case DerivedName(qual, info: QualifiedInfo) => @@ -123,10 +160,12 @@ object NameKinds { } } + /** A base trait for infos that contain a number */ trait NumberedInfo extends NameInfo { def num: Int } + /** The kind of numbered names consisting of an underlying name and a number */ 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 { @@ -145,8 +184,11 @@ object NameKinds { name.slice(i - separator.length, i).toString == separator) i else -1 } + + numberedNameKinds(tag) = this } + /** An extractor for numbered names of arbitrary kind */ object AnyNumberedName { def unapply(name: DerivedName): Option[(TermName, Int)] = name match { case DerivedName(qual, info: NumberedInfo) => Some((qual, info.num)) @@ -154,20 +196,28 @@ object NameKinds { } } + /** The kind of unique names that consist of an underlying name (can be empty), + * a separator indicating the class of unique name, and a unique number. + * + * A unique names always constitutes a new name, different from its underlying name. + */ 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 } + /** Generate fresh unique name of this kind with given prefix name */ def fresh(prefix: TermName = EmptyTermName)(implicit ctx: Context): TermName = ctx.freshNames.newName(prefix, this) uniqueNameKinds(separator) = this } + /** An extractor for unique names of arbitrary kind */ object AnyUniqueName { def unapply(name: DerivedName): Option[(TermName, String, Int)] = name match { case DerivedName(qual, info: NumberedInfo) => @@ -179,10 +229,16 @@ object NameKinds { } } - val QualifiedName = new QualifiedNameKind(QUALIFIED, ".") - val FlatName = new QualifiedNameKind(FLATTENED, "$") - val ExpandPrefixName = new QualifiedNameKind(EXPANDPREFIX, "$") + /** Names of the form `prefix . name` */ + val QualifiedName = new QualifiedNameKind(QUALIFIED, ".") + + /** Names of the form `prefix $ name` that are constructed as a result of flattening */ + val FlatName = new QualifiedNameKind(FLATTENED, "$") + + /** Names of the form `prefix $ name` that are prefixes of expanded names */ + val ExpandPrefixName = new QualifiedNameKind(EXPANDPREFIX, "$") + /** Expanded names of the form `prefix $$ name`. */ val ExpandedName = new QualifiedNameKind(EXPANDED, str.EXPAND_SEPARATOR) { private val FalseSuper = termName("$$super") private val FalseSuperLength = FalseSuper.length @@ -202,13 +258,16 @@ object NameKinds { } } - val TraitSetterName = new QualifiedNameKind(TRAITSETTER, str.TRAIT_SETTER_SEPARATOR) + /** Expanded names of the form `prefix $_setter_$ name`. These only occur in Scala2. */ + val TraitSetterName = new QualifiedNameKind(TRAITSETTER, str.TRAIT_SETTER_SEPARATOR) + /** Unique names of the form `prefix $ n` or `$ n $` */ val UniqueName = new UniqueNameKind("$") { override def mkString(underlying: TermName, info: ThisInfo) = if (underlying.isEmpty) "$" + info.num + "$" else super.mkString(underlying, info) } + /** Other unique names */ val InlineAccessorName = new UniqueNameKind("$_inlineAccessor_$") val TempResultName = new UniqueNameKind("ev$") val EvidenceParamName = new UniqueNameKind("evidence$") @@ -226,6 +285,9 @@ object NameKinds { val LiftedTreeName = new UniqueNameKind("liftedTree") val SuperArgName = new UniqueNameKind("$superArg$") + /** A kind of unique extension methods; Unlike other unique names, these can be + * unmangled. + */ val UniqueExtMethName = new UniqueNameKind("$extension") { override def unmangle(name: SimpleName): TermName = { val i = skipSeparatorAndNum(name, separator) @@ -238,6 +300,7 @@ object NameKinds { } } + /** Kinds of unique names generated by the pattern matcher */ 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 @@ -246,6 +309,7 @@ object NameKinds { val PatMatMatchFailName = new UniqueNameKind("matchFail") val PatMatSelectorName = new UniqueNameKind("selector") + /** The kind of names of default argument getters */ object DefaultGetterName extends NumberedNameKind(DEFAULTGETTER, "DefaultGetter") { def mkString(underlying: TermName, info: ThisInfo) = { val prefix = if (underlying.isConstructorName) nme.DEFAULT_GETTER_INIT else underlying @@ -264,11 +328,9 @@ object NameKinds { } } + /** The kind of names that also encode a variance: 0 for contravariance, 1 for covariance. */ object VariantName extends NumberedNameKind(VARIANT, "Variant") { - val varianceToPrefix = Map(-1 -> '-', 0 -> '=', 1 -> '+') - def mkString(underlying: TermName, info: ThisInfo) = { - varianceToPrefix(info.num).toString + underlying - } + def mkString(underlying: TermName, info: ThisInfo) = "-+"(info.num).toString + underlying } /** Names of the form N_. Emitted by inliner, replaced by outer path @@ -293,9 +355,9 @@ object NameKinds { val ModuleVarName = new SuffixNameKind(OBJECTVAR, "$module") val ModuleClassName = new SuffixNameKind(OBJECTCLASS, "$", optInfoString = "ModuleClass") + /** A name together with a signature. Used in Tasty trees. */ object SignedName extends NameKind(63) { - /** @param parts resultSig followed by paramsSig */ case class SignedInfo(sig: Signature) extends Info { override def toString = s"$infoString $sig" } @@ -312,10 +374,12 @@ object NameKinds { def infoString: String = "Signed" } + /** Possible name kinds of a method that comes from Scala2 pickling info. */ 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 numberedNameKindOfTag : collection.Map[Int, NumberedNameKind] = numberedNameKinds 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 dd8c615e5950..df29a32c523c 100644 --- a/compiler/src/dotty/tools/dotc/core/NameOps.scala +++ b/compiler/src/dotty/tools/dotc/core/NameOps.scala @@ -128,15 +128,24 @@ object NameOps { def errorName: N = likeSpaced(name ++ nme.ERROR) + /** Map variance value -1, +1 to 0, 1 */ + private def varianceToNat(v: Int) = (v + 1) / 2 + + /** Map 0, 1 to variance value -1, +1 */ + private def natToVariance(n: Int) = n * 2 - 1 /** Name with variance prefix: `+` for covariant, `-` for contravariant */ - def withVariance(v: Int): N = - likeSpaced { VariantName(name.exclude(VariantName).toTermName, v) } + def withVariance(v: Int): N = { + val underlying = name.exclude(VariantName) + likeSpaced( + if (v == 0) underlying + else VariantName(underlying.toTermName, varianceToNat(v))) + } /** The variance as implied by the variance prefix, or 0 if there is * no variance prefix. */ - def variance = name.collect { case VariantName(_, n) => n }.getOrElse(0) + def variance = name.collect { case VariantName(_, n) => natToVariance(n) }.getOrElse(0) def freshened(implicit ctx: Context): N = likeSpaced { name.toTermName match { diff --git a/compiler/src/dotty/tools/dotc/core/Names.scala b/compiler/src/dotty/tools/dotc/core/Names.scala index bf17efef8670..cd79e2b83301 100644 --- a/compiler/src/dotty/tools/dotc/core/Names.scala +++ b/compiler/src/dotty/tools/dotc/core/Names.scala @@ -82,13 +82,15 @@ object Names { def mangledString: String = mangled.toString /** Apply rewrite rule given by `f` to some part of this name, skipping and rewrapping - * other decorators. Stops at first qualified name that's encountered. + * other decorators. + * Stops at derived names whose kind has `definesNewName = true`. * If `f` does not apply to any part, return name unchanged. */ def rewrite(f: PartialFunction[Name, Name]): ThisName /** If partial function `f` is defined for some part of this name, apply it - * in a Some, otherwise None. Stops at first qualified name that's encountered. + * in a Some, otherwise None. + * Stops at derived names whose kind has `definesNewName = true`. */ def collect[T](f: PartialFunction[Name, T]): Option[T] diff --git a/compiler/src/dotty/tools/dotc/core/Signature.scala b/compiler/src/dotty/tools/dotc/core/Signature.scala index 4699cecf2421..fcd1e23767df 100644 --- a/compiler/src/dotty/tools/dotc/core/Signature.scala +++ b/compiler/src/dotty/tools/dotc/core/Signature.scala @@ -34,14 +34,6 @@ 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/tasty/NameBuffer.scala b/compiler/src/dotty/tools/dotc/core/tasty/NameBuffer.scala index d10879978b7c..df83b8d50573 100644 --- a/compiler/src/dotty/tools/dotc/core/tasty/NameBuffer.scala +++ b/compiler/src/dotty/tools/dotc/core/tasty/NameBuffer.scala @@ -70,8 +70,6 @@ class NameBuffer extends TastyBuffer(10000) { 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)) => diff --git a/compiler/src/dotty/tools/dotc/core/tasty/TastyUnpickler.scala b/compiler/src/dotty/tools/dotc/core/tasty/TastyUnpickler.scala index 37a3c2e762ad..67f0c763755f 100644 --- a/compiler/src/dotty/tools/dotc/core/tasty/TastyUnpickler.scala +++ b/compiler/src/dotty/tools/dotc/core/tasty/TastyUnpickler.scala @@ -57,12 +57,8 @@ class TastyUnpickler(reader: TastyReader) { val originals = until(end)(readName()) val original = if (originals.isEmpty) EmptyTermName else originals.head uniqueNameKindOfSeparator(separator)(original, num) - case DEFAULTGETTER => - DefaultGetterName(readName(), readNat()) - case VARIANT => - VariantName(readName(), readNat() - 1) - case OUTERSELECT => - OuterSelectName(readName(), readNat()) + case DEFAULTGETTER | VARIANT | OUTERSELECT => + numberedNameKindOfTag(tag)(readName(), readNat()) case SIGNED => val original = readName() val result = readName().toTypeName From f0f3ebd52219a83bb57828b83933afcefada6eb9 Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Sat, 8 Apr 2017 12:15:02 +0200 Subject: [PATCH 19/31] Update TastyFormat documentation --- .../tools/dotc/core/tasty/TastyFormat.scala | 58 ++++++++++++++----- 1 file changed, 42 insertions(+), 16 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/core/tasty/TastyFormat.scala b/compiler/src/dotty/tools/dotc/core/tasty/TastyFormat.scala index b564c09e996d..7ef92f038fab 100644 --- a/compiler/src/dotty/tools/dotc/core/tasty/TastyFormat.scala +++ b/compiler/src/dotty/tools/dotc/core/tasty/TastyFormat.scala @@ -29,16 +29,29 @@ Macro-format: Section = NameRef Length Bytes Length = Nat // length of rest of entry in bytes - Name = UTF8 Length UTF8-CodePoint* - 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 - ... + Name = UTF8 Length UTF8-CodePoint* + QUALIFIED Length qualified_NameRef selector_NameRef + FLATTENED Length qualified_NameRef selector_NameRef + EXPANDED Length qualified_NameRef selector_NameRef + EXPANDEDPREFIX Length qualified_NameRef selector_NameRef + TRAITSETTER Length qualified_NameRef selector_NameRef + UNIQUE Length separator_NameRef uniqid_Nat underlying_NameRef? + DEFAULTGETTER Length underlying_NameRef index_Nat + VARIANT Length underlying_NameRef variance_Nat // 0: Contravariant, 1: Covariant + OUTERSELECT Length underlying_NameRef nhops_Nat // a reference to `nhops` selections, followed by `underlying` + + SUPERACCESSOR Length underlying_NameRef + PROTECTEDACCESSOR Length underlying_NameRef + PROTECTEDSETTER Length underlying_NameRef + INITIALIZER Length underlying_NameRef + SHADOWED Length underlying_NameRef + AVOIDCLASH Length underlying_NameRef + DIRECT Length underlying_NameRef + FIELD Length underlying_NameRef + EXTMETH Length underlying_NameRef + OBJECTVAR Length underlying_NameRef + OBJECTCLASS Length underlying_NameRef + SIGNED Length original_NameRef resultSig_NameRef paramSig_NameRef* NameRef = Nat // ordinal number of name in name table, starting from 1. @@ -238,8 +251,7 @@ object TastyFormat { final val AVOIDCLASH = 30 final val DIRECT = 31 final val FIELD = 32 - final val SETTER = 33 - final val EXTMETH = 34 + final val EXTMETH = 33 final val OBJECTVAR = 39 final val OBJECTCLASS = 40 @@ -428,12 +440,26 @@ object TastyFormat { case QUALIFIED => "QUALIFIED" case FLATTENED => "FLATTENED" case EXPANDED => "EXPANDED" - case SIGNED => "SIGNED" - case OBJECTCLASS => "OBJECTCLASS" - case SUPERACCESSOR => "SUPERACCESSOR" + case EXPANDPREFIX => "EXPANDPREFIX" + case TRAITSETTER => "TRAITSETTER" + case UNIQUE => "UNIQUE" case DEFAULTGETTER => "DEFAULTGETTER" - case SHADOWED => "SHADOWED" case VARIANT => "VARIANT" + case OUTERSELECT => "OUTERSELECT" + + case SUPERACCESSOR => "SUPERACCESSOR" + case PROTECTEDACCESSOR => "PROTECTEDACCESSOR" + case PROTECTEDSETTER => "PROTECTEDSETTER" + case INITIALIZER => "INITIALIZER" + case SHADOWED => "SHADOWED" + case AVOIDCLASH => "AVOIDCLASH" + case DIRECT => "DIRECT" + case FIELD => "FIELD" + case EXTMETH => "EXTMETH" + case OBJECTVAR => "OBJECTVAR" + case OBJECTCLASS => "OBJECTCLASS" + + case SIGNED => "SIGNED" } def astTagToString(tag: Int): String = tag match { From 2dc02b525701871b24d5dfacbd0966caadfee8a5 Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Sat, 8 Apr 2017 12:16:48 +0200 Subject: [PATCH 20/31] Compare unmangled name when searching for roots Compare unmangled name when searching for roots in Scala2 and Tasty unpickling. Since we now load files with their unmangled name, we don't need to go through mangled names anymore. --- .../src/dotty/tools/dotc/core/SymDenotations.scala | 13 +++---------- .../dotty/tools/dotc/core/tasty/TreeUnpickler.scala | 3 +-- .../dotc/core/unpickleScala2/Scala2Unpickler.scala | 3 +-- 3 files changed, 5 insertions(+), 14 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/core/SymDenotations.scala b/compiler/src/dotty/tools/dotc/core/SymDenotations.scala index 80ed41837921..7fcac46fc49b 100644 --- a/compiler/src/dotty/tools/dotc/core/SymDenotations.scala +++ b/compiler/src/dotty/tools/dotc/core/SymDenotations.scala @@ -373,18 +373,11 @@ 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) = - initial.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 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. + * are separated by `separator` strings as indicated by the given name kind. + * Drops package objects. Represents each term in the owner chain by a simple `_$`. */ def fullNameSeparated(kind: QualifiedNameKind)(implicit ctx: Context): Name = if (symbol == NoSymbol || diff --git a/compiler/src/dotty/tools/dotc/core/tasty/TreeUnpickler.scala b/compiler/src/dotty/tools/dotc/core/tasty/TreeUnpickler.scala index b3b5236c509b..602e5f2c7fd1 100644 --- a/compiler/src/dotty/tools/dotc/core/tasty/TreeUnpickler.scala +++ b/compiler/src/dotty/tools/dotc/core/tasty/TreeUnpickler.scala @@ -417,7 +417,6 @@ class TreeUnpickler(reader: TastyReader, nameAtRef: NameRef => TermName, posUnpi val end = readEnd() var name: Name = readName() if (tag == TYPEDEF || tag == TYPEPARAM) name = name.toTypeName - val mname = name.mangled skipParams() val ttag = nextUnsharedTag val isAbsType = isAbstractType(ttag) @@ -433,7 +432,7 @@ class TreeUnpickler(reader: TastyReader, nameAtRef: NameRef => TermName, posUnpi 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.mangled == mname) match { + roots.find(root => (root.owner eq ctx.owner) && root.name == name) match { case Some(rootd) => pickling.println(i"overwriting ${rootd.symbol} # ${rootd.hashCode}") rootd.info = adjustIfModule( diff --git a/compiler/src/dotty/tools/dotc/core/unpickleScala2/Scala2Unpickler.scala b/compiler/src/dotty/tools/dotc/core/unpickleScala2/Scala2Unpickler.scala index 06669a5b151d..712148756c7e 100644 --- a/compiler/src/dotty/tools/dotc/core/unpickleScala2/Scala2Unpickler.scala +++ b/compiler/src/dotty/tools/dotc/core/unpickleScala2/Scala2Unpickler.scala @@ -449,8 +449,7 @@ class Scala2Unpickler(bytes: Array[Byte], classRoot: ClassDenotation, moduleClas } name = name.mapLast(_.decode) - val mname = name.mangled - def nameMatches(rootName: Name) = mname == rootName.mangled + def nameMatches(rootName: Name) = name == rootName 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) From 8e0c2f2f3d05b605b1363b6e81fa7642abf62c71 Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Sat, 8 Apr 2017 13:13:18 +0200 Subject: [PATCH 21/31] Use effectiveScope when creating companionLinks This was accidentally reverted from b641181. --- compiler/src/dotty/tools/dotc/transform/ResolveSuper.scala | 3 +-- compiler/src/dotty/tools/dotc/typer/Namer.scala | 4 ++-- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/transform/ResolveSuper.scala b/compiler/src/dotty/tools/dotc/transform/ResolveSuper.scala index e7936e8d95b4..de0242f79b27 100644 --- a/compiler/src/dotty/tools/dotc/transform/ResolveSuper.scala +++ b/compiler/src/dotty/tools/dotc/transform/ResolveSuper.scala @@ -19,7 +19,6 @@ 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. @@ -96,7 +95,7 @@ object ResolveSuper { def rebindSuper(base: Symbol, acc: Symbol)(implicit ctx: Context): Symbol = { var bcs = base.info.baseClasses.dropWhile(acc.owner != _).tail var sym: Symbol = NoSymbol - val SuperAccessorName(memberName) = acc.name.unexpandedName // dotty deviation: ": Name" needed otherwise pattern type is neither a subtype nor a supertype of selector type + val SuperAccessorName(memberName) = acc.name.unexpandedName ctx.debuglog(i"starting rebindsuper from $base of ${acc.showLocated}: ${acc.info} in $bcs, name = $memberName") while (bcs.nonEmpty && sym == NoSymbol) { val other = bcs.head.info.nonPrivateDecl(memberName) diff --git a/compiler/src/dotty/tools/dotc/typer/Namer.scala b/compiler/src/dotty/tools/dotc/typer/Namer.scala index 365b19b38193..1ce97098a61b 100644 --- a/compiler/src/dotty/tools/dotc/typer/Namer.scala +++ b/compiler/src/dotty/tools/dotc/typer/Namer.scala @@ -566,8 +566,8 @@ class Namer { typer: Typer => /** Create links between companion object and companion class */ def createLinks(classTree: TypeDef, moduleTree: TypeDef)(implicit ctx: Context) = { - val claz = ctx.denotNamed(classTree.name).symbol - val modl = ctx.denotNamed(moduleTree.name).symbol + val claz = ctx.effectiveScope.lookup(classTree.name) + val modl = ctx.effectiveScope.lookup(moduleTree.name) ctx.synthesizeCompanionMethod(nme.COMPANION_CLASS_METHOD, claz, modl).entered ctx.synthesizeCompanionMethod(nme.COMPANION_MODULE_METHOD, modl, claz).entered } From bb88d41930ae9be512a6858caf75c37be55dedf3 Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Sat, 8 Apr 2017 13:28:16 +0200 Subject: [PATCH 22/31] Some further polishings --- compiler/src/dotty/tools/dotc/core/NameKinds.scala | 2 +- compiler/src/dotty/tools/dotc/util/FreshNameCreator.scala | 5 ++--- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/core/NameKinds.scala b/compiler/src/dotty/tools/dotc/core/NameKinds.scala index 672858c2a3f2..b58dfa211c6b 100644 --- a/compiler/src/dotty/tools/dotc/core/NameKinds.scala +++ b/compiler/src/dotty/tools/dotc/core/NameKinds.scala @@ -206,7 +206,7 @@ object NameKinds { override def definesNewName = true def mkString(underlying: TermName, info: ThisInfo) = { - val safePrefix = str.sanitize(underlying.toString + separator) + val safePrefix = str.sanitize(underlying.toString) + separator safePrefix + info.num } diff --git a/compiler/src/dotty/tools/dotc/util/FreshNameCreator.scala b/compiler/src/dotty/tools/dotc/util/FreshNameCreator.scala index 5dbec3e5a586..5882eb4b9cd9 100644 --- a/compiler/src/dotty/tools/dotc/util/FreshNameCreator.scala +++ b/compiler/src/dotty/tools/dotc/util/FreshNameCreator.scala @@ -22,10 +22,9 @@ object FreshNameCreator { * call to this function (provided the prefix does not end in a digit). */ def newName(prefix: TermName, unique: UniqueNameKind): TermName = { - val key = str.sanitize(prefix.toString + unique.separator) + val key = str.sanitize(prefix.toString) + unique.separator counters(key) += 1 - val counter = counters(key) - prefix.derived(unique.NumberedInfo(counter)) + prefix.derived(unique.NumberedInfo(counters(key))) } } } From 2005cd94614710f906e82ad7b21a52d284d6742e Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Thu, 27 Apr 2017 17:55:42 +0200 Subject: [PATCH 23/31] Fix rebase breakage --- compiler/src/dotty/tools/dotc/core/StdNames.scala | 3 +-- compiler/src/dotty/tools/dotc/transform/LambdaLift.scala | 2 +- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/core/StdNames.scala b/compiler/src/dotty/tools/dotc/core/StdNames.scala index e6378b666399..a7f658849dd8 100644 --- a/compiler/src/dotty/tools/dotc/core/StdNames.scala +++ b/compiler/src/dotty/tools/dotc/core/StdNames.scala @@ -34,7 +34,7 @@ object StdNames { final val INTERPRETER_LINE_PREFIX = "line" final val INTERPRETER_VAR_PREFIX = "res" final val INTERPRETER_WRAPPER_SUFFIX = "$object" - final val MODULE_INSTANCE_FIELD = NameTransformer.MODULE_INSTANCE_NAME // "MODULE$" + final val MODULE_INSTANCE_FIELD = "MODULE$" final val Function = "Function" final val ImplicitFunction = "ImplicitFunction" @@ -260,7 +260,6 @@ object StdNames { val REIFY_FREE_THIS_SUFFIX: N = "$this" val REIFY_FREE_VALUE_SUFFIX: N = "$value" val REIFY_SYMDEF_PREFIX: N = "symdef$" - val MODULE_INSTANCE_FIELD: N = "MODULE$" val OUTER: N = "$outer" val REFINE_CLASS: N = "" val ROOTPKG: N = "_root_" diff --git a/compiler/src/dotty/tools/dotc/transform/LambdaLift.scala b/compiler/src/dotty/tools/dotc/transform/LambdaLift.scala index 4d6053a38ee8..81a114e03fbc 100644 --- a/compiler/src/dotty/tools/dotc/transform/LambdaLift.scala +++ b/compiler/src/dotty/tools/dotc/transform/LambdaLift.scala @@ -334,7 +334,7 @@ class LambdaLift extends MiniPhase with IdentityDenotTransformer { thisTransform private def newName(sym: Symbol)(implicit ctx: Context): Name = if (sym.isAnonymousFunction && sym.owner.is(Method, butNot = Label)) sym.name.rewrite { - case name: SimpleTermName => ExpandPrefixName(sym.owner.name.asTermName, name) + case name: SimpleName => ExpandPrefixName(sym.owner.name.asTermName, name) }.freshened else sym.name.freshened From e7b23253f1ba871b68471997b9818947838d649d Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Fri, 28 Apr 2017 13:54:55 +0200 Subject: [PATCH 24/31] Optimized scheme for mangling strings There's no need to add mangled qualified names in the name table. Instead we compute the mangled string directly and more efficiently. Also, bug fixes to make semantic names work: - When creating a fully qualified name, we need to convert the last part to a simple name. If the last part is not already a simple name or typename version of it, it should be mangled - Mangling needs to sanitize because a legal name such as might illegal when it gets a suffix or prefix. --- .../src/dotty/tools/dotc/core/NameKinds.scala | 5 +- .../src/dotty/tools/dotc/core/Names.scala | 59 ++++++++++++++----- .../tools/dotc/core/SymDenotations.scala | 2 +- 3 files changed, 49 insertions(+), 17 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/core/NameKinds.scala b/compiler/src/dotty/tools/dotc/core/NameKinds.scala index b58dfa211c6b..f8e7284c7daa 100644 --- a/compiler/src/dotty/tools/dotc/core/NameKinds.scala +++ b/compiler/src/dotty/tools/dotc/core/NameKinds.scala @@ -91,7 +91,7 @@ object NameKinds { 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 + underlying.mapLast(n => termName(prefix + str.sanitize(n.toString))).toString override def unmangle(name: SimpleName): TermName = if (name.startsWith(prefix)) apply(name.drop(prefix.length).asSimpleName) else name @@ -100,7 +100,8 @@ object NameKinds { /** The kind of names that get formed by appending a suffix to an underlying 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 + def mkString(underlying: TermName, info: ThisInfo) = + underlying.mapLast(n => termName(str.sanitize(n.toString) + suffix)).toString override def unmangle(name: SimpleName): TermName = if (name.endsWith(suffix)) apply(name.take(name.length - suffix.length).asSimpleName) else name diff --git a/compiler/src/dotty/tools/dotc/core/Names.scala b/compiler/src/dotty/tools/dotc/core/Names.scala index cd79e2b83301..ad0b895ba221 100644 --- a/compiler/src/dotty/tools/dotc/core/Names.scala +++ b/compiler/src/dotty/tools/dotc/core/Names.scala @@ -66,20 +66,13 @@ object Names { /** This name converted to a simple term name */ def toSimpleName: SimpleName - @sharable // because it's just a cache for performance - private[this] var myMangled: Name = null - - protected[Names] def mangle: ThisName - /** This name converted to a simple term name and in addition * with all symbolic operator characters expanded. */ - final def mangled: ThisName = { - if (myMangled == null) myMangled = mangle - myMangled.asInstanceOf[ThisName] - } + def mangled: ThisName - def mangledString: String = mangled.toString + /** Convert to string after mangling */ + def mangledString: String /** Apply rewrite rule given by `f` to some part of this name, skipping and rewrapping * other decorators. @@ -100,6 +93,11 @@ object Names { /** Apply `f` to all simple term names making up this name */ def mapParts(f: SimpleName => SimpleName): ThisName + /** If this a qualified name, split it into underlyng, last part, and separator + * Otherwise return an empty name, the name itself, and "") + */ + def split: (TermName, TermName, String) + /** A name in the same (term or type) namespace as this name and * with same characters as given `name`. */ @@ -252,6 +250,31 @@ object Names { thisKind == kind || !thisKind.definesNewName && thisKind.tag > kind.tag && underlying.is(kind) } + + @sharable // because it's just a cache for performance + private[Names] var myMangledString: String = null + + @sharable // because it's just a cache for performance + private[this] var myMangled: Name = null + + protected[Names] def mangle: ThisName + + final def mangled: ThisName = { + if (myMangled == null) myMangled = mangle + myMangled.asInstanceOf[ThisName] + } + + final def mangledString: String = { + if (myMangledString == null) { + val (prefix, suffix, separator) = split + val mangledSuffix = suffix.mangled.toString + myMangledString = + if (prefix.isEmpty) mangledSuffix + else str.sanitize(prefix.mangledString + separator + mangledSuffix) + } + myMangledString + } + } /** A simple name is essentiall an interned string */ @@ -317,6 +340,7 @@ object Names { override def collect[T](f: PartialFunction[Name, T]): Option[T] = f.lift(this) override def mapLast(f: SimpleName => SimpleName) = f(this) override def mapParts(f: SimpleName => SimpleName) = f(this) + override def split = (EmptyTermName, this, "") override def encode: SimpleName = { val dontEncode = @@ -391,9 +415,6 @@ object Names { .contains(elem.getMethodName)) } - def sliceToString(from: Int, end: Int) = - if (end <= from) "" else new String(chrs, start + from, end - from) - def debugString: String = toString } @@ -409,12 +430,14 @@ object Names { override def asSimpleName = toTermName.asSimpleName override def toSimpleName = toTermName.toSimpleName - override final def mangle = toTermName.mangle.toTypeName + override def mangled = toTermName.mangled.toTypeName + override def mangledString = toTermName.mangledString override def rewrite(f: PartialFunction[Name, Name]): ThisName = toTermName.rewrite(f).toTypeName override def collect[T](f: PartialFunction[Name, T]): Option[T] = toTermName.collect(f) override def mapLast(f: SimpleName => SimpleName) = toTermName.mapLast(f).toTypeName override def mapParts(f: SimpleName => SimpleName) = toTermName.mapParts(f).toTypeName + override def split = toTermName.split override def likeSpaced(name: Name): TypeName = name.toTypeName @@ -470,6 +493,14 @@ object Names { case _ => underlying.mapParts(f).derived(info) } + override def split = info match { + case info: QualifiedInfo => + (underlying, info.name, info.kind.asInstanceOf[QualifiedNameKind].separator) + case _ => + val (prefix, suffix, separator) = underlying.split + (prefix, suffix.derived(info), separator) + } + override def isEmpty = false override def encode: ThisName = underlying.encode.derived(info.map(_.encode)) override def decode: ThisName = underlying.decode.derived(info.map(_.decode)) diff --git a/compiler/src/dotty/tools/dotc/core/SymDenotations.scala b/compiler/src/dotty/tools/dotc/core/SymDenotations.scala index 7fcac46fc49b..e01dab87228c 100644 --- a/compiler/src/dotty/tools/dotc/core/SymDenotations.scala +++ b/compiler/src/dotty/tools/dotc/core/SymDenotations.scala @@ -399,7 +399,7 @@ object SymDenotations { kind(prefix.toTermName, if (filler.isEmpty) n else termName(filler + n)) val fn = name rewrite { case name: SimpleName => qualify(name) - case name @ AnyQualifiedName(_, _) => qualify(name.toSimpleName) + case name @ AnyQualifiedName(_, _) => qualify(name.mangled.toSimpleName) } if (isType) fn.toTypeName else fn.toTermName } From 1ada92b1072743e271fff67aed6403077eea6f5e Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Fri, 28 Apr 2017 15:31:51 +0200 Subject: [PATCH 25/31] Fix converting Prefix/Suffix names to strings The previous logic would have applied several suffixes in reverse. --- .../src/dotty/tools/dotc/core/NameKinds.scala | 4 +-- .../src/dotty/tools/dotc/core/Names.scala | 28 ++++++++++--------- 2 files changed, 17 insertions(+), 15 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/core/NameKinds.scala b/compiler/src/dotty/tools/dotc/core/NameKinds.scala index f8e7284c7daa..09a13e130f54 100644 --- a/compiler/src/dotty/tools/dotc/core/NameKinds.scala +++ b/compiler/src/dotty/tools/dotc/core/NameKinds.scala @@ -91,7 +91,7 @@ object NameKinds { 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 + str.sanitize(n.toString))).toString + underlying.qualToString(_.toString, n => prefix + n.toString) override def unmangle(name: SimpleName): TermName = if (name.startsWith(prefix)) apply(name.drop(prefix.length).asSimpleName) else name @@ -101,7 +101,7 @@ object NameKinds { 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.mapLast(n => termName(str.sanitize(n.toString) + suffix)).toString + underlying.qualToString(_.toString, n => n.toString + suffix) override def unmangle(name: SimpleName): TermName = if (name.endsWith(suffix)) apply(name.take(name.length - suffix.length).asSimpleName) else name diff --git a/compiler/src/dotty/tools/dotc/core/Names.scala b/compiler/src/dotty/tools/dotc/core/Names.scala index ad0b895ba221..bf14049dcdbc 100644 --- a/compiler/src/dotty/tools/dotc/core/Names.scala +++ b/compiler/src/dotty/tools/dotc/core/Names.scala @@ -93,11 +93,6 @@ object Names { /** Apply `f` to all simple term names making up this name */ def mapParts(f: SimpleName => SimpleName): ThisName - /** If this a qualified name, split it into underlyng, last part, and separator - * Otherwise return an empty name, the name itself, and "") - */ - def split: (TermName, TermName, String) - /** A name in the same (term or type) namespace as this name and * with same characters as given `name`. */ @@ -265,16 +260,24 @@ object Names { } final def mangledString: String = { - if (myMangledString == null) { - val (prefix, suffix, separator) = split - val mangledSuffix = suffix.mangled.toString - myMangledString = - if (prefix.isEmpty) mangledSuffix - else str.sanitize(prefix.mangledString + separator + mangledSuffix) - } + if (myMangledString == null) + myMangledString = qualToString(_.mangledString, _.mangled.toString) myMangledString } + /** If this a qualified name, split it into underlyng, last part, and separator + * Otherwise return an empty name, the name itself, and "") + */ + def split: (TermName, TermName, String) + + /** Convert to string as follows. If this is a qualified name + * ` `, the sanitized version of `f1() f2()`. + * Otherwise `f2` applied to this name. + */ + def qualToString(f1: TermName => String, f2: TermName => String) = { + val (first, last, sep) = split + if (first.isEmpty) f2(last) else str.sanitize(f1(first) + sep + f2(last)) + } } /** A simple name is essentiall an interned string */ @@ -437,7 +440,6 @@ object Names { override def collect[T](f: PartialFunction[Name, T]): Option[T] = toTermName.collect(f) override def mapLast(f: SimpleName => SimpleName) = toTermName.mapLast(f).toTypeName override def mapParts(f: SimpleName => SimpleName) = toTermName.mapParts(f).toTypeName - override def split = toTermName.split override def likeSpaced(name: Name): TypeName = name.toTypeName From b6b744a5fabd386c5fe8a1297fb9ee0d594d6892 Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Fri, 28 Apr 2017 16:03:33 +0200 Subject: [PATCH 26/31] Fix test to use symbolic names --- sbt-bridge/test/xsbt/ExtractUsedNamesSpecification.scala | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/sbt-bridge/test/xsbt/ExtractUsedNamesSpecification.scala b/sbt-bridge/test/xsbt/ExtractUsedNamesSpecification.scala index 6cff284feb15..5044c771872e 100644 --- a/sbt-bridge/test/xsbt/ExtractUsedNamesSpecification.scala +++ b/sbt-bridge/test/xsbt/ExtractUsedNamesSpecification.scala @@ -81,7 +81,7 @@ class ExtractUsedNamesSpecification extends Specification { // We could avoid this by checking if the untyped tree has a return type // but is it worth it? Revisit this after https://github.com/sbt/sbt/issues/1104 // has landed. - val expectedNames = standardNames ++ Set("A", "a", "$eq", "Int") + val expectedNames = standardNames ++ Set("A", "a", "=", "Int") usedNames === expectedNames } @@ -114,7 +114,7 @@ class ExtractUsedNamesSpecification extends Specification { val compilerForTesting = new ScalaCompilerForUnitTesting(nameHashing = true) val usedNames = compilerForTesting.extractUsedNamesFromSrc(src1, src2) val expectedNames = standardNames ++ Set("Test", "Test$", "B", "B$", - "Predef", "Predef$", "$qmark$qmark$qmark", "Nothing", + "Predef", "Predef$", "???", "Nothing", "lista", "List", "A", "at", "T", "X1", "X0", "as", "S", "Y", From 1c48cdb89516544d84ceec7bbf23afe1ca70fef0 Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Mon, 1 May 2017 08:57:35 +0200 Subject: [PATCH 27/31] Fix #2334: Require at least one digit after '.' in floating point literals --- .../tools/dotc/parsing/CharArrayReader.scala | 2 +- .../dotty/tools/dotc/parsing/Scanners.scala | 38 +++---------------- tests/neg/floatlits.scala | 11 ++++++ 3 files changed, 18 insertions(+), 33 deletions(-) create mode 100644 tests/neg/floatlits.scala diff --git a/compiler/src/dotty/tools/dotc/parsing/CharArrayReader.scala b/compiler/src/dotty/tools/dotc/parsing/CharArrayReader.scala index e20eb392d52c..0ecd43263aee 100644 --- a/compiler/src/dotty/tools/dotc/parsing/CharArrayReader.scala +++ b/compiler/src/dotty/tools/dotc/parsing/CharArrayReader.scala @@ -120,7 +120,7 @@ abstract class CharArrayReader { self => def isAtEnd = charOffset >= buf.length /** A new reader that takes off at the current character position */ - def lookaheadReader = new CharArrayLookaheadReader + def lookaheadReader() = new CharArrayLookaheadReader class CharArrayLookaheadReader extends CharArrayReader { val buf = self.buf diff --git a/compiler/src/dotty/tools/dotc/parsing/Scanners.scala b/compiler/src/dotty/tools/dotc/parsing/Scanners.scala index 9fcde7181526..b37d5e774c4c 100644 --- a/compiler/src/dotty/tools/dotc/parsing/Scanners.scala +++ b/compiler/src/dotty/tools/dotc/parsing/Scanners.scala @@ -180,7 +180,7 @@ object Scanners { private[this] var docstringMap: SortedMap[Int, Comment] = SortedMap.empty private[this] def addComment(comment: Comment): Unit = { - val lookahead = lookaheadReader + val lookahead = lookaheadReader() def nextPos: Int = (lookahead.getc(): @switch) match { case ' ' | '\t' => nextPos case CR | LF | FF => @@ -863,7 +863,7 @@ object Scanners { nextChar() } if (ch == 'e' || ch == 'E') { - val lookahead = lookaheadReader + val lookahead = lookaheadReader() lookahead.nextChar() if (lookahead.ch == '+' || lookahead.ch == '-') { lookahead.nextChar() @@ -907,36 +907,10 @@ object Scanners { } token = INTLIT if (base == 10 && ch == '.') { - val isDefinitelyNumber = { - val lookahead = lookaheadReader - val c = lookahead.getc() - (c: @switch) match { - /** Another digit is a giveaway. */ - case '0' | '1' | '2' | '3' | '4' | '5' | '6' | '7' | '8' | '9' => - true - - /** Backquoted idents like 22.`foo`. */ - case '`' => - false - - /** These letters may be part of a literal, or a method invocation on an Int. - */ - case 'd' | 'D' | 'f' | 'F' => - !isIdentifierPart(lookahead.getc()) - - /** A little more special handling for e.g. 5e7 */ - case 'e' | 'E' => - val ch = lookahead.getc() - !isIdentifierPart(ch) || (isDigit(ch) || ch == '+' || ch == '-') - - case x => - !isIdentifierStart(x) - } - } - if (isDefinitelyNumber) { - putChar(ch) - nextChar() - getFraction() + val lookahead = lookaheadReader() + lookahead.nextChar() + if ('0' <= lookahead.ch && lookahead.ch <= '9') { + putChar('.'); nextChar(); getFraction() } } else (ch: @switch) match { case 'e' | 'E' | 'f' | 'F' | 'd' | 'D' => diff --git a/tests/neg/floatlits.scala b/tests/neg/floatlits.scala new file mode 100644 index 000000000000..b1eb663231c3 --- /dev/null +++ b/tests/neg/floatlits.scala @@ -0,0 +1,11 @@ +object Test { + + val y = .2 // ok + + val z = 3.2 // ok + + val a = 1.0e2 + val b = 1e-3 + + val x = 2. // error: identifier expected +} From a26d0de0e78d540246a31a90c9790d4e354d84ad Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Mon, 1 May 2017 11:11:56 +0200 Subject: [PATCH 28/31] Fix -Xprompt behavior Previously, we also interpreted the newline as a significant input, which led to -Xprompt stopping only at every second error. --- .../dotc/reporting/ConsoleReporter.scala | 27 ++++++++++++------- 1 file changed, 17 insertions(+), 10 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/reporting/ConsoleReporter.scala b/compiler/src/dotty/tools/dotc/reporting/ConsoleReporter.scala index dbaa68f30fda..10ec402b8432 100644 --- a/compiler/src/dotty/tools/dotc/reporting/ConsoleReporter.scala +++ b/compiler/src/dotty/tools/dotc/reporting/ConsoleReporter.scala @@ -41,18 +41,25 @@ class ConsoleReporter( } /** Show prompt if `-Xprompt` is passed as a flag to the compiler */ - def displayPrompt()(implicit ctx: Context): Unit = { - printMessage("\na)bort, s)tack, r)esume: ") - flush() + def displayPrompt(): Unit = { + writer.println() + writer.print("a)bort, s)tack, r)esume: ") + writer.flush() if (reader != null) { - val response = reader.read().asInstanceOf[Char].toLower - if (response == 'a' || response == 's') { - Thread.dumpStack() - if (response == 'a') - sys.exit(1) + def loop(): Unit = reader.read match { + case 'a' | 'A' => + new Throwable().printStackTrace(writer) + System.exit(1) + case 's' | 'S' => + new Throwable().printStackTrace(writer) + writer.println() + writer.flush() + case 'r' | 'R' => + () + case _ => + loop() } - print("\n") - flush() + loop() } } From 72daf5ca306387e2050349701124c05ab1de3662 Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Mon, 1 May 2017 11:15:14 +0200 Subject: [PATCH 29/31] Fix error message handling We previously got: an identifier expected but `=` found even though the token that was found was a `}`! The fallacy was that in.name is not always set to something significant. I had to refactor quite a bit to avoid this. Anyway, I thought the code was a bit underwhelming with `null` interpreted and an "explanation" that simply repeated what was said in the main message. --- compiler/src/dotty/tools/dotc/parsing/Parsers.scala | 4 ++-- .../tools/dotc/reporting/diagnostic/messages.scala | 11 +++-------- .../tools/dotc/reporting/ErrorMessagesTests.scala | 3 +-- 3 files changed, 6 insertions(+), 12 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/parsing/Parsers.scala b/compiler/src/dotty/tools/dotc/parsing/Parsers.scala index ce74fbedb3c8..d207af862b43 100644 --- a/compiler/src/dotty/tools/dotc/parsing/Parsers.scala +++ b/compiler/src/dotty/tools/dotc/parsing/Parsers.scala @@ -266,7 +266,7 @@ object Parsers { def accept(token: Int): Int = { val offset = in.offset if (in.token != token) { - syntaxErrorOrIncomplete(ExpectedTokenButFound(token, in.token, in.name)) + syntaxErrorOrIncomplete(ExpectedTokenButFound(token, in.token)) } if (in.token == token) in.nextToken() offset @@ -477,7 +477,7 @@ object Parsers { in.nextToken() name } else { - syntaxErrorOrIncomplete(ExpectedTokenButFound(IDENTIFIER, in.token, in.name)) + syntaxErrorOrIncomplete(ExpectedTokenButFound(IDENTIFIER, in.token)) nme.ERROR } diff --git a/compiler/src/dotty/tools/dotc/reporting/diagnostic/messages.scala b/compiler/src/dotty/tools/dotc/reporting/diagnostic/messages.scala index b3c49a333822..a862825d0263 100644 --- a/compiler/src/dotty/tools/dotc/reporting/diagnostic/messages.scala +++ b/compiler/src/dotty/tools/dotc/reporting/diagnostic/messages.scala @@ -1057,7 +1057,7 @@ object messages { |""".stripMargin } - case class ExpectedTokenButFound(expected: Token, found: Token, foundName: TermName)(implicit ctx: Context) + case class ExpectedTokenButFound(expected: Token, found: Token)(implicit ctx: Context) extends Message(ExpectedTokenButFoundID) { val kind = "Syntax" @@ -1065,9 +1065,7 @@ object messages { if (Tokens.isIdentifier(expected)) "an identifier" else Tokens.showToken(expected) - private val foundText = - if (foundName != null) hl"`${foundName.show}`" - else Tokens.showToken(found) + private val foundText = Tokens.showToken(found) val msg = hl"""${expectedText} expected, but ${foundText} found""" @@ -1077,10 +1075,7 @@ object messages { |If you necessarily want to use $foundText as identifier, you may put it in backticks.""".stripMargin else "" - - val explanation = - s"""|The text ${foundText} may not occur at that position, the compiler expected ${expectedText}.$ifKeyword - |""".stripMargin + val explanation = s"$ifKeyword" } case class MixedLeftAndRightAssociativeOps(op1: Name, op2: Name, op2LeftAssoc: Boolean)(implicit ctx: Context) diff --git a/compiler/test/dotty/tools/dotc/reporting/ErrorMessagesTests.scala b/compiler/test/dotty/tools/dotc/reporting/ErrorMessagesTests.scala index be160ac7fba0..4676b5110f23 100644 --- a/compiler/test/dotty/tools/dotc/reporting/ErrorMessagesTests.scala +++ b/compiler/test/dotty/tools/dotc/reporting/ErrorMessagesTests.scala @@ -121,10 +121,9 @@ class ErrorMessagesTests extends ErrorMessagesTest { val defn = ictx.definitions assertMessageCount(1, messages) - val ExpectedTokenButFound(expected, found, foundName) :: Nil = messages + val ExpectedTokenButFound(expected, found) :: Nil = messages assertEquals(Tokens.IDENTIFIER, expected) assertEquals(Tokens.VAL, found) - assertEquals("val", foundName.show) } @Test def expectedToken = From 727e904cbe68640aaeba17ee87e2a64a03ad79c2 Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Mon, 1 May 2017 11:17:07 +0200 Subject: [PATCH 30/31] Don't report "not found" messages when we are looking for . --- compiler/src/dotty/tools/dotc/typer/TypeAssigner.scala | 2 +- compiler/src/dotty/tools/dotc/typer/Typer.scala | 2 ++ 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/compiler/src/dotty/tools/dotc/typer/TypeAssigner.scala b/compiler/src/dotty/tools/dotc/typer/TypeAssigner.scala index ec6fb1770000..71806facfd44 100644 --- a/compiler/src/dotty/tools/dotc/typer/TypeAssigner.scala +++ b/compiler/src/dotty/tools/dotc/typer/TypeAssigner.scala @@ -216,7 +216,7 @@ trait TypeAssigner { else if (site.derivesFrom(defn.DynamicClass) && !Dynamic.isDynamicMethod(name)) { TryDynamicCallType } else { - if (site.isErroneous) UnspecifiedErrorType + if (site.isErroneous || name.toTermName == nme.ERROR) UnspecifiedErrorType else { def kind = if (name.isTypeName) "type" else "value" def addendum = diff --git a/compiler/src/dotty/tools/dotc/typer/Typer.scala b/compiler/src/dotty/tools/dotc/typer/Typer.scala index 85e590fd6d23..e3be171f080c 100644 --- a/compiler/src/dotty/tools/dotc/typer/Typer.scala +++ b/compiler/src/dotty/tools/dotc/typer/Typer.scala @@ -354,6 +354,8 @@ class Typer extends Namer with TypeAssigner with Applications with Implicits wit // awaiting a better implicits based solution for library-supported xml return ref(defn.XMLTopScopeModuleRef) } + else if (name.toTermName == nme.ERROR) + UnspecifiedErrorType else errorType(new MissingIdent(tree, kind, name.show), tree.pos) From fcf2168a88b5f1e421eba5a68d4f69a13dd4a791 Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Mon, 1 May 2017 11:17:22 +0200 Subject: [PATCH 31/31] Fix positition of error in neg test --- tests/neg/floatlits.scala | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/neg/floatlits.scala b/tests/neg/floatlits.scala index b1eb663231c3..81ccd1c2ce3a 100644 --- a/tests/neg/floatlits.scala +++ b/tests/neg/floatlits.scala @@ -7,5 +7,5 @@ object Test { val a = 1.0e2 val b = 1e-3 - val x = 2. // error: identifier expected -} + val x = 2. +} // error: identifier expected