diff --git a/compiler/src/dotty/tools/backend/jvm/DottyBackendInterface.scala b/compiler/src/dotty/tools/backend/jvm/DottyBackendInterface.scala index 8e054c9c2dd5..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) @@ -404,16 +404,15 @@ 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] } + val MODULE_INSTANCE_FIELD: String = str.MODULE_INSTANCE_FIELD - - 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 { @@ -537,32 +535,25 @@ 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 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) + def mangledString: String = n.mangledString } - implicit def symHelper(sym: Symbol): SymbolHelper = new SymbolHelper { // names 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 javaBinaryName: Name = javaClassName.replace('.', '/').toTypeName // TODO: can we make this a string? addModuleSuffix(fullNameInternal('/')) - def javaClassName: String = toDenot(sym).fullName.toString// addModuleSuffix(fullNameInternal('.')).toString + 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.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 @@ -675,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 25a8061ac05f..90f13f83a791 100644 --- a/compiler/src/dotty/tools/backend/jvm/GenBCode.scala +++ b/compiler/src/dotty/tools/backend/jvm/GenBCode.scala @@ -202,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/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/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 a72a028449af..9fb583373b77 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 @@ -67,6 +68,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] @@ -287,7 +289,40 @@ object Names { override def hashCode: Int = start override def toString = - if (length == 0) "" else new String(chrs, start, length) + if (length == 0) "" + 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) + } + } + 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")) || + 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 } 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/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 diff --git a/scala-backend b/scala-backend index ed7050296c5c..253ae6c0155a 160000 --- a/scala-backend +++ b/scala-backend @@ -1 +1 @@ -Subproject commit ed7050296c5c5107ae69d7a330a5706268ab1b6e +Subproject commit 253ae6c0155a553bef4b1a312e25fc5f18f4c5e4