From 8775dd1dc5ea3a209fc723e33eaacc3cf8712a81 Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Thu, 27 Apr 2017 18:44:06 +0200 Subject: [PATCH 1/8] Drop internalNameString and hava javaBinaryName return a string Needs updated backend # Conflicts: # compiler/src/dotty/tools/dotc/core/Names.scala --- .../dotty/tools/backend/jvm/DottyBackendInterface.scala | 8 +++----- scala-backend | 2 +- 2 files changed, 4 insertions(+), 6 deletions(-) diff --git a/compiler/src/dotty/tools/backend/jvm/DottyBackendInterface.scala b/compiler/src/dotty/tools/backend/jvm/DottyBackendInterface.scala index 8e054c9c2dd5..819f2ecab407 100644 --- a/compiler/src/dotty/tools/backend/jvm/DottyBackendInterface.scala +++ b/compiler/src/dotty/tools/backend/jvm/DottyBackendInterface.scala @@ -409,11 +409,10 @@ class DottyBackendInterface(outputDirectory: AbstractFile, val superCallsMap: Ma def newSet[K](): mutable.Set[K] = new mutable.HashSet[K] } - - val MODULE_INSTANCE_FIELD: String = nme.MODULE_INSTANCE_FIELD.toString - def internalNameString(offset: Int, length: Int): String = new String(Names.chrs, offset, length) + def dropModule(str: String) = + if (!str.isEmpty && str.last == '$') str.take(str.length - 1) else str def newTermName(prefix: String): Name = prefix.toTermName @@ -424,7 +423,6 @@ class DottyBackendInterface(outputDirectory: AbstractFile, val superCallsMap: Ma Flags.Bridge | Flags.VBridge | Flags.Private | Flags.Macro }.bits - def isQualifierSafeToElide(qual: Tree): Boolean = tpd.isIdempotentExpr(qual) def desugarIdent(i: Ident): Option[tpd.Select] = { i.tpe match { @@ -557,7 +555,7 @@ class DottyBackendInterface(outputDirectory: AbstractFile, val superCallsMap: Ma def fullName: String = sym.showFullName def simpleName: Name = sym.name def javaSimpleName: Name = toDenot(sym).name // addModuleSuffix(simpleName.dropLocal) - def javaBinaryName: Name = javaClassName.replace('.', '/').toTypeName // TODO: can we make this a string? addModuleSuffix(fullNameInternal('/')) + def javaBinaryName: String = javaClassName.replace('.', '/') // TODO: can we make this a string? addModuleSuffix(fullNameInternal('/')) def javaClassName: String = toDenot(sym).fullName.toString// addModuleSuffix(fullNameInternal('.')).toString def name: Name = sym.name def rawname: Name = { diff --git a/scala-backend b/scala-backend index ed7050296c5c..5183de95993d 160000 --- a/scala-backend +++ b/scala-backend @@ -1 +1 @@ -Subproject commit ed7050296c5c5107ae69d7a330a5706268ab1b6e +Subproject commit 5183de95993d1c32686becc910ed8d93ec0f81b7 From ca46e49e6024c9b4068444a7e37e06df88f6a609 Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Thu, 27 Apr 2017 18:55:30 +0200 Subject: [PATCH 2/8] Drop unused operations in NameHandler Also, fix a discrepancy in the signature of newWeakSet # Conflicts: # scala-backend --- .../dotty/tools/backend/jvm/DottyBackendInterface.scala | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/compiler/src/dotty/tools/backend/jvm/DottyBackendInterface.scala b/compiler/src/dotty/tools/backend/jvm/DottyBackendInterface.scala index 819f2ecab407..bd7ab19f79f3 100644 --- a/compiler/src/dotty/tools/backend/jvm/DottyBackendInterface.scala +++ b/compiler/src/dotty/tools/backend/jvm/DottyBackendInterface.scala @@ -404,7 +404,7 @@ class DottyBackendInterface(outputDirectory: AbstractFile, val superCallsMap: Ma def newAnyRefMap[K <: AnyRef, V](): mutable.AnyRefMap[K, V] = new mutable.AnyRefMap[K, V]() def newWeakMap[K, V](): mutable.WeakHashMap[K, V] = new mutable.WeakHashMap[K, V]() def recordCache[T <: Clearable](cache: T): T = cache - def newWeakSet[K <: AnyRef](): WeakHashSet[K] = new WeakHashSet[K]() + def newWeakSet[K >: Null <: AnyRef](): WeakHashSet[K] = new WeakHashSet[K]() def newMap[K, V](): mutable.HashMap[K, V] = new mutable.HashMap[K, V]() def newSet[K](): mutable.Set[K] = new mutable.HashSet[K] } @@ -540,15 +540,10 @@ class DottyBackendInterface(outputDirectory: AbstractFile, val superCallsMap: Ma def toTypeName: Name = n.toTypeName def isTypeName: Boolean = n.isTypeName def toTermName: Name = n.toTermName - def dropModule: Name = n.stripModuleClassSuffix - - def len: Int = n.toSimpleName.length - def offset: Int = n.toSimpleName.start def isTermName: Boolean = n.isTermName def startsWith(s: String): Boolean = n.startsWith(s) } - implicit def symHelper(sym: Symbol): SymbolHelper = new SymbolHelper { // names def fullName(sep: Char): String = sym.showFullName From 4641bf26c9f59e6b323f917a2e766f43fea0baf5 Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Thu, 27 Apr 2017 19:10:07 +0200 Subject: [PATCH 3/8] Remove more name ops --- .../src/dotty/tools/backend/jvm/DottyBackendInterface.scala | 3 --- 1 file changed, 3 deletions(-) diff --git a/compiler/src/dotty/tools/backend/jvm/DottyBackendInterface.scala b/compiler/src/dotty/tools/backend/jvm/DottyBackendInterface.scala index bd7ab19f79f3..9e6600b386bb 100644 --- a/compiler/src/dotty/tools/backend/jvm/DottyBackendInterface.scala +++ b/compiler/src/dotty/tools/backend/jvm/DottyBackendInterface.scala @@ -535,11 +535,8 @@ class DottyBackendInterface(outputDirectory: AbstractFile, val superCallsMap: Ma } } - implicit def nameHelper(n: Name): NameHelper = new NameHelper { - def toTypeName: Name = n.toTypeName def isTypeName: Boolean = n.isTypeName - def toTermName: Name = n.toTermName def isTermName: Boolean = n.isTermName def startsWith(s: String): Boolean = n.startsWith(s) } From 2c4ac2c74b2e44f818b98710ca162df19589fb52 Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Tue, 4 Apr 2017 09:51:32 +0200 Subject: [PATCH 4/8] [TO BE REVERTED] Gross hack to work around leaky NameHelper abstraction --- compiler/src/dotty/tools/backend/jvm/GenBCode.scala | 7 +++++-- compiler/src/dotty/tools/dotc/core/Names.scala | 11 ++++++++++- 2 files changed, 15 insertions(+), 3 deletions(-) diff --git a/compiler/src/dotty/tools/backend/jvm/GenBCode.scala b/compiler/src/dotty/tools/backend/jvm/GenBCode.scala index 25a8061ac05f..645d5e0b7fa2 100644 --- a/compiler/src/dotty/tools/backend/jvm/GenBCode.scala +++ b/compiler/src/dotty/tools/backend/jvm/GenBCode.scala @@ -50,8 +50,11 @@ class GenBCode extends Phase { new PlainDirectory(new Directory(new JFile(ctx.settings.d.value))) def run(implicit ctx: Context): Unit = { - new GenBCodePipeline(entryPoints.toList, - new DottyBackendInterface(outputDir, superCallsMap.toMap)(ctx))(ctx).run(ctx.compilationUnit.tpdTree) + val saved = Names.useMangled + Names.useMangled = true + try new GenBCodePipeline(entryPoints.toList, + new DottyBackendInterface(outputDir, superCallsMap.toMap)(ctx))(ctx).run(ctx.compilationUnit.tpdTree) + finally Names.useMangled = saved entryPoints.clear() } } diff --git a/compiler/src/dotty/tools/dotc/core/Names.scala b/compiler/src/dotty/tools/dotc/core/Names.scala index a72a028449af..4c02e5d82dd0 100644 --- a/compiler/src/dotty/tools/dotc/core/Names.scala +++ b/compiler/src/dotty/tools/dotc/core/Names.scala @@ -21,6 +21,11 @@ import java.util.HashMap object Names { import NameKinds._ + // Gross hack because the backend uses toString on arbitrary names, which means + // that the NameHelper abstraction is leaky. As long as cannot get rid of this, + // parallel compilation is impossible. + @sharable var useMangled: Boolean = false + /** A common class for things that can be turned into names. * Instances are both names and strings, the latter via a decorator. */ @@ -287,7 +292,11 @@ object Names { override def hashCode: Int = start override def toString = - if (length == 0) "" else new String(chrs, start, length) + if (length == 0) "" + else { + val n = if (useMangled) mangled.asSimpleName else this + new String(chrs, n.start, n.length) + } def debugString: String = toString } From 0735a685ee5d6146518165fc2b772fa8d3f5709c Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Thu, 27 Apr 2017 23:04:21 +0200 Subject: [PATCH 5/8] Use mangled string in backend Avoid using toString directly. --- .../backend/jvm/DottyBackendInterface.scala | 33 +++++++++---------- .../dotty/tools/backend/jvm/GenBCode.scala | 9 ++--- .../dotty/tools/dotc/core/Denotations.scala | 3 +- .../src/dotty/tools/dotc/core/Names.scala | 11 ++----- .../src/dotty/tools/dotc/core/StdNames.scala | 1 + scala-backend | 2 +- 6 files changed, 24 insertions(+), 35 deletions(-) diff --git a/compiler/src/dotty/tools/backend/jvm/DottyBackendInterface.scala b/compiler/src/dotty/tools/backend/jvm/DottyBackendInterface.scala index 9e6600b386bb..2040b39c3071 100644 --- a/compiler/src/dotty/tools/backend/jvm/DottyBackendInterface.scala +++ b/compiler/src/dotty/tools/backend/jvm/DottyBackendInterface.scala @@ -30,7 +30,7 @@ import Decorators._ import tpd._ import scala.tools.asm -import StdNames.nme +import StdNames.{nme, str} import NameOps._ import NameKinds.DefaultGetterName import dotty.tools.dotc.core @@ -161,7 +161,8 @@ class DottyBackendInterface(outputDirectory: AbstractFile, val superCallsMap: Ma def boxMethods: Map[Symbol, Symbol] = defn.ScalaValueClasses().map{x => // @darkdimius Are you sure this should be a def? (x, Erasure.Boxing.boxMethod(x.asClass)) }.toMap - def unboxMethods: Map[Symbol, Symbol] = defn.ScalaValueClasses().map(x => (x, Erasure.Boxing.unboxMethod(x.asClass))).toMap + def unboxMethods: Map[Symbol, Symbol] = + defn.ScalaValueClasses().map(x => (x, Erasure.Boxing.unboxMethod(x.asClass))).toMap override def isSyntheticArrayConstructor(s: Symbol) = { s eq defn.newArrayMethod @@ -244,7 +245,7 @@ class DottyBackendInterface(outputDirectory: AbstractFile, val superCallsMap: Ma case ClazzTag => av.visit(name, const.typeValue.toTypeKind(bcodeStore)(innerClasesStore).toASMType) case EnumTag => val edesc = innerClasesStore.typeDescriptor(const.tpe.asInstanceOf[bcodeStore.int.Type]) // the class descriptor of the enumeration class. - val evalue = const.symbolValue.name.toString // value the actual enumeration value. + val evalue = const.symbolValue.name.mangledString // value the actual enumeration value. av.visitEnum(name, edesc, evalue) } case t: TypeApply if (t.fun.symbol == Predef_classOf) => @@ -252,7 +253,7 @@ class DottyBackendInterface(outputDirectory: AbstractFile, val superCallsMap: Ma case t: tpd.Select => if (t.symbol.denot.is(Flags.Enum)) { val edesc = innerClasesStore.typeDescriptor(t.tpe.asInstanceOf[bcodeStore.int.Type]) // the class descriptor of the enumeration class. - val evalue = t.symbol.name.toString // value the actual enumeration value. + val evalue = t.symbol.name.mangledString // value the actual enumeration value. av.visitEnum(name, edesc, evalue) } else { assert(toDenot(t.symbol).name.is(DefaultGetterName)) // this should be default getter. do not emmit. @@ -262,8 +263,8 @@ class DottyBackendInterface(outputDirectory: AbstractFile, val superCallsMap: Ma for(arg <- t.elems) { emitArgument(arrAnnotV, null, arg, bcodeStore)(innerClasesStore) } arrAnnotV.visitEnd() - case Apply(fun, args) if (fun.symbol == defn.ArrayClass.primaryConstructor || - (toDenot(fun.symbol).owner == defn.ArrayClass.linkedClass && fun.symbol.name == nme_apply)) => + case Apply(fun, args) if fun.symbol == defn.ArrayClass.primaryConstructor || + toDenot(fun.symbol).owner == defn.ArrayClass.linkedClass && fun.symbol.name == nme_apply => val arrAnnotV: AnnotationVisitor = av.visitArray(name) var actualArgs = if (fun.tpe.isInstanceOf[ImplicitMethodType]) { @@ -311,7 +312,7 @@ class DottyBackendInterface(outputDirectory: AbstractFile, val superCallsMap: Ma private def emitAssocs(av: asm.AnnotationVisitor, assocs: List[(Name, Object)], bcodeStore: BCodeHelpers) (innerClasesStore: bcodeStore.BCInnerClassGen) = { for ((name, value) <- assocs) - emitArgument(av, name.toString, value.asInstanceOf[Tree], bcodeStore)(innerClasesStore) + emitArgument(av, name.mangledString, value.asInstanceOf[Tree], bcodeStore)(innerClasesStore) av.visitEnd() } @@ -360,9 +361,8 @@ class DottyBackendInterface(outputDirectory: AbstractFile, val superCallsMap: Ma else clazz.getName } - def requiredClass[T](implicit evidence: ClassTag[T]): Symbol = { + def requiredClass[T](implicit evidence: ClassTag[T]): Symbol = ctx.requiredClass(erasureString(evidence.runtimeClass).toTermName) - } def requiredModule[T](implicit evidence: ClassTag[T]): Symbol = { val moduleName = erasureString(evidence.runtimeClass) @@ -409,7 +409,7 @@ class DottyBackendInterface(outputDirectory: AbstractFile, val superCallsMap: Ma def newSet[K](): mutable.Set[K] = new mutable.HashSet[K] } - val MODULE_INSTANCE_FIELD: String = nme.MODULE_INSTANCE_FIELD.toString + val MODULE_INSTANCE_FIELD: String = str.MODULE_INSTANCE_FIELD def dropModule(str: String) = if (!str.isEmpty && str.last == '$') str.take(str.length - 1) else str @@ -539,6 +539,7 @@ class DottyBackendInterface(outputDirectory: AbstractFile, val superCallsMap: Ma def isTypeName: Boolean = n.isTypeName def isTermName: Boolean = n.isTermName def startsWith(s: String): Boolean = n.startsWith(s) + def mangledString: String = n.mangledString } implicit def symHelper(sym: Symbol): SymbolHelper = new SymbolHelper { @@ -546,13 +547,13 @@ class DottyBackendInterface(outputDirectory: AbstractFile, val superCallsMap: Ma def fullName(sep: Char): String = sym.showFullName def fullName: String = sym.showFullName def simpleName: Name = sym.name - def javaSimpleName: Name = toDenot(sym).name // addModuleSuffix(simpleName.dropLocal) + def javaSimpleName: String = toDenot(sym).name.mangledString // addModuleSuffix(simpleName.dropLocal) def javaBinaryName: String = javaClassName.replace('.', '/') // TODO: can we make this a string? addModuleSuffix(fullNameInternal('/')) - def javaClassName: String = toDenot(sym).fullName.toString// addModuleSuffix(fullNameInternal('.')).toString + def javaClassName: String = toDenot(sym).fullName.mangledString // addModuleSuffix(fullNameInternal('.')).toString def name: Name = sym.name - def rawname: Name = { + def rawname: String = { val original = toDenot(sym).initial - sym.name(ctx.withPhase(original.validFor.phaseId)) + sym.name(ctx.withPhase(original.validFor.phaseId)).mangledString } // types @@ -665,9 +666,7 @@ class DottyBackendInterface(outputDirectory: AbstractFile, val superCallsMap: Ma } def enclClass: Symbol = toDenot(sym).enclosingClass def linkedClassOfClass: Symbol = linkedClass - def linkedClass: Symbol = { - toDenot(sym)(ctx).linkedClass(ctx) - } //exitingPickler(sym.linkedClassOfClass) + def linkedClass: Symbol = toDenot(sym)(ctx).linkedClass(ctx) //exitingPickler(sym.linkedClassOfClass) def companionClass: Symbol = toDenot(sym).companionClass def companionModule: Symbol = toDenot(sym).companionModule def companionSymbol: Symbol = if (sym is Flags.Module) companionClass else companionModule diff --git a/compiler/src/dotty/tools/backend/jvm/GenBCode.scala b/compiler/src/dotty/tools/backend/jvm/GenBCode.scala index 645d5e0b7fa2..90f13f83a791 100644 --- a/compiler/src/dotty/tools/backend/jvm/GenBCode.scala +++ b/compiler/src/dotty/tools/backend/jvm/GenBCode.scala @@ -50,11 +50,8 @@ class GenBCode extends Phase { new PlainDirectory(new Directory(new JFile(ctx.settings.d.value))) def run(implicit ctx: Context): Unit = { - val saved = Names.useMangled - Names.useMangled = true - try new GenBCodePipeline(entryPoints.toList, - new DottyBackendInterface(outputDir, superCallsMap.toMap)(ctx))(ctx).run(ctx.compilationUnit.tpdTree) - finally Names.useMangled = saved + new GenBCodePipeline(entryPoints.toList, + new DottyBackendInterface(outputDir, superCallsMap.toMap)(ctx))(ctx).run(ctx.compilationUnit.tpdTree) entryPoints.clear() } } @@ -205,7 +202,7 @@ class GenBCodePipeline(val entryPoints: List[Symbol], val int: DottyBackendInter if (claszSymbol.isClass) // @DarkDimius is this test needed here? for (binary <- ctx.compilationUnit.pickled.get(claszSymbol.asClass)) { - val dataAttr = new CustomAttr(nme.TASTYATTR.toString, binary) + val dataAttr = new CustomAttr(nme.TASTYATTR.mangledString, binary) val store = if (mirrorC ne null) mirrorC else plainC store.visitAttribute(dataAttr) if (ctx.settings.emitTasty.value) { diff --git a/compiler/src/dotty/tools/dotc/core/Denotations.scala b/compiler/src/dotty/tools/dotc/core/Denotations.scala index fd42bde3660f..ffc58f2e8d2f 100644 --- a/compiler/src/dotty/tools/dotc/core/Denotations.scala +++ b/compiler/src/dotty/tools/dotc/core/Denotations.scala @@ -582,7 +582,7 @@ object Denotations { def hasUniqueSym: Boolean protected def newLikeThis(symbol: Symbol, info: Type): SingleDenotation - final def signature(implicit ctx: Context): Signature = { + final def signature(implicit ctx: Context): Signature = if (isType) Signature.NotAMethod // don't force info if this is a type SymDenotation else info match { case info: MethodicType => @@ -594,7 +594,6 @@ object Denotations { } case _ => Signature.NotAMethod } - } def derivedSingleDenotation(symbol: Symbol, info: Type)(implicit ctx: Context): SingleDenotation = if ((symbol eq this.symbol) && (info eq this.info)) this diff --git a/compiler/src/dotty/tools/dotc/core/Names.scala b/compiler/src/dotty/tools/dotc/core/Names.scala index 4c02e5d82dd0..828e800f051b 100644 --- a/compiler/src/dotty/tools/dotc/core/Names.scala +++ b/compiler/src/dotty/tools/dotc/core/Names.scala @@ -21,11 +21,6 @@ import java.util.HashMap object Names { import NameKinds._ - // Gross hack because the backend uses toString on arbitrary names, which means - // that the NameHelper abstraction is leaky. As long as cannot get rid of this, - // parallel compilation is impossible. - @sharable var useMangled: Boolean = false - /** A common class for things that can be turned into names. * Instances are both names and strings, the latter via a decorator. */ @@ -72,6 +67,7 @@ object Names { def asSimpleName: SimpleTermName def toSimpleName: SimpleTermName def mangled: Name + def mangledString: String = mangled.toString def rewrite(f: PartialFunction[Name, Name]): ThisName def collect[T](f: PartialFunction[Name, T]): Option[T] @@ -293,10 +289,7 @@ object Names { override def toString = if (length == 0) "" - else { - val n = if (useMangled) mangled.asSimpleName else this - new String(chrs, n.start, n.length) - } + else new String(chrs, start, length) def debugString: String = toString } diff --git a/compiler/src/dotty/tools/dotc/core/StdNames.scala b/compiler/src/dotty/tools/dotc/core/StdNames.scala index 92befdacbd9f..e364fe28d057 100644 --- a/compiler/src/dotty/tools/dotc/core/StdNames.scala +++ b/compiler/src/dotty/tools/dotc/core/StdNames.scala @@ -33,6 +33,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 Function = "Function" final val ImplicitFunction = "ImplicitFunction" diff --git a/scala-backend b/scala-backend index 5183de95993d..253ae6c0155a 160000 --- a/scala-backend +++ b/scala-backend @@ -1 +1 @@ -Subproject commit 5183de95993d1c32686becc910ed8d93ec0f81b7 +Subproject commit 253ae6c0155a553bef4b1a312e25fc5f18f4c5e4 From b28f7669c830ef888d7eabaf55b1fa7caa34b3a3 Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Fri, 28 Apr 2017 09:50:24 +0200 Subject: [PATCH 6/8] Change some --- .../src/dotty/tools/dotc/config/Config.scala | 7 +++++ .../src/dotty/tools/dotc/core/Names.scala | 28 ++++++++++++++++++- 2 files changed, 34 insertions(+), 1 deletion(-) diff --git a/compiler/src/dotty/tools/dotc/config/Config.scala b/compiler/src/dotty/tools/dotc/config/Config.scala index 46b1896f1add..709a5233b89e 100644 --- a/compiler/src/dotty/tools/dotc/config/Config.scala +++ b/compiler/src/dotty/tools/dotc/config/Config.scala @@ -49,6 +49,13 @@ object Config { */ final val checkNoSkolemsInInfo = false + /** Check that Name#toString is not called directly from backend by analyzing + * the stack trace of each toString call on names. This is very expensive, + * so not suitable for continuous testing. But it can be used to find a problem + * when running a specific test. + */ + final val checkBackendNames = false + /** Type comparer will fail with an assert if the upper bound * of a constrained parameter becomes Nothing. This should be turned * on only for specific debugging as normally instantiation to Nothing diff --git a/compiler/src/dotty/tools/dotc/core/Names.scala b/compiler/src/dotty/tools/dotc/core/Names.scala index 828e800f051b..2eaf8749826a 100644 --- a/compiler/src/dotty/tools/dotc/core/Names.scala +++ b/compiler/src/dotty/tools/dotc/core/Names.scala @@ -14,6 +14,7 @@ import collection.mutable.{ Builder, StringBuilder, AnyRefMap } import collection.immutable.WrappedString import collection.generic.CanBuildFrom import util.{DotClass, SimpleMap} +import config.Config import java.util.HashMap //import annotation.volatile @@ -289,7 +290,32 @@ object Names { override def toString = if (length == 0) "" - else new String(chrs, start, length) + else { + if (Config.checkBackendNames) { + if (!toStringOK) { + println("Backend should not call Name#toString, Name#mangledString should be used instead.") + new Error().printStackTrace() + assert(false) + } + } + new String(chrs, start, length) + } + + private def toStringOK = { + val trace = Thread.currentThread.getStackTrace + !trace.exists(_.getClassName.endsWith("GenBCode")) || + trace.exists(elem => + List( + "mangledString", + "toSimpleName", + "decode", + "unmangle", + "dotty$tools$dotc$core$NameOps$NameDecorator$$functionArityFor$extension", + "dotty$tools$dotc$typer$Checking$CheckNonCyclicMap$$apply", + "$plus$plus", + "readConstant") + .contains(elem.getMethodName)) + } def debugString: String = toString } From d7eea419cf352fe9562f1ae1c478e485c2839dc0 Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Fri, 28 Apr 2017 09:52:02 +0200 Subject: [PATCH 7/8] Change some toString occurrences on Name to mangledString These were caught when turning the backend check on. --- compiler/src/dotty/tools/dotc/core/SymbolLoaders.scala | 2 +- .../src/dotty/tools/dotc/core/classfile/ClassfileParser.scala | 2 +- compiler/src/dotty/tools/dotc/transform/TreeChecker.scala | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/core/SymbolLoaders.scala b/compiler/src/dotty/tools/dotc/core/SymbolLoaders.scala index 63c2817a647e..9e19c5f05eec 100644 --- a/compiler/src/dotty/tools/dotc/core/SymbolLoaders.scala +++ b/compiler/src/dotty/tools/dotc/core/SymbolLoaders.scala @@ -220,7 +220,7 @@ class SymbolLoaders { if (!sourceModule.isCompleted) sourceModule.completer.complete(sourceModule) - val packageName = if (root.isEffectiveRoot) "" else root.fullName.toString + val packageName = if (root.isEffectiveRoot) "" else root.fullName.mangledString enterFlatClasses = Some { ctx => enterFlatClasses = None diff --git a/compiler/src/dotty/tools/dotc/core/classfile/ClassfileParser.scala b/compiler/src/dotty/tools/dotc/core/classfile/ClassfileParser.scala index 9415c047fda8..eef8faf8a233 100644 --- a/compiler/src/dotty/tools/dotc/core/classfile/ClassfileParser.scala +++ b/compiler/src/dotty/tools/dotc/core/classfile/ClassfileParser.scala @@ -677,7 +677,7 @@ class ClassfileParser( for (entry <- innerClasses.values) { // create a new class member for immediate inner classes if (entry.outerName == currentClassName) { - val file = ctx.platform.classPath.findClassFile(entry.externalName.toString) getOrElse { + val file = ctx.platform.classPath.findClassFile(entry.externalName.mangledString) getOrElse { throw new AssertionError(entry.externalName) } enterClassAndModule(entry, file, entry.jflags) diff --git a/compiler/src/dotty/tools/dotc/transform/TreeChecker.scala b/compiler/src/dotty/tools/dotc/transform/TreeChecker.scala index eb7773ef3869..79de2cd88741 100644 --- a/compiler/src/dotty/tools/dotc/transform/TreeChecker.scala +++ b/compiler/src/dotty/tools/dotc/transform/TreeChecker.scala @@ -63,7 +63,7 @@ class TreeChecker extends Phase with SymTransformer { val NoSuperClass = Trait | Package def testDuplicate(sym: Symbol, registry: mutable.Map[String, Symbol], typ: String)(implicit ctx: Context) = { - val name = sym.fullName.toString + val name = sym.fullName.mangledString if (this.flatClasses && registry.contains(name)) printError(s"$typ defined twice $sym ${sym.id} ${registry(name).id}") registry(name) = sym From c3cc84fce7112c255d391bd42ad1cd2b2581e32c Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Fri, 28 Apr 2017 09:53:11 +0200 Subject: [PATCH 8/8] Document name checking --- compiler/src/dotty/tools/dotc/core/Names.scala | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/compiler/src/dotty/tools/dotc/core/Names.scala b/compiler/src/dotty/tools/dotc/core/Names.scala index 2eaf8749826a..9fb583373b77 100644 --- a/compiler/src/dotty/tools/dotc/core/Names.scala +++ b/compiler/src/dotty/tools/dotc/core/Names.scala @@ -293,6 +293,10 @@ object Names { else { if (Config.checkBackendNames) { if (!toStringOK) { + // We print the stacktrace instead of doing an assert directly, + // 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() assert(false) @@ -301,6 +305,9 @@ object Names { 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. + */ private def toStringOK = { val trace = Thread.currentThread.getStackTrace !trace.exists(_.getClassName.endsWith("GenBCode")) ||