From 5d071229baf7294b03d6d50c7685c934ef54a191 Mon Sep 17 00:00:00 2001 From: Jason Zaugg Date: Wed, 8 Mar 2017 09:19:17 +1000 Subject: [PATCH 01/15] Avoid BoxesRuntime.equals in Type.unique Like using AnyRefMap, we can optimize this datastructure with the knowledge that is only holds AnyRef-s, and we can bypass BoxesRuntime for equality/hashing. --- .../scala/reflect/internal/util/WeakHashSet.scala | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/reflect/scala/reflect/internal/util/WeakHashSet.scala b/src/reflect/scala/reflect/internal/util/WeakHashSet.scala index 412b14d3291..422a43a365d 100644 --- a/src/reflect/scala/reflect/internal/util/WeakHashSet.scala +++ b/src/reflect/scala/reflect/internal/util/WeakHashSet.scala @@ -156,7 +156,7 @@ final class WeakHashSet[A <: AnyRef](val initialCapacity: Int, val loadFactor: D case null => null.asInstanceOf[A] case _ => { val entryElem = entry.get - if (elem == entryElem) entryElem + if (elem.equals(entryElem)) entryElem else linkedListLoop(entry.tail) } } @@ -185,7 +185,7 @@ final class WeakHashSet[A <: AnyRef](val initialCapacity: Int, val loadFactor: D case null => add() case _ => { val entryElem = entry.get - if (elem == entryElem) entryElem + if (elem.equals(entryElem)) entryElem else linkedListLoop(entry.tail) } } @@ -211,9 +211,9 @@ final class WeakHashSet[A <: AnyRef](val initialCapacity: Int, val loadFactor: D @tailrec def linkedListLoop(entry: Entry[A]): Unit = entry match { - case null => add() - case _ if (elem == entry.get) => () - case _ => linkedListLoop(entry.tail) + case null => add() + case _ if elem.equals(entry.get) => () + case _ => linkedListLoop(entry.tail) } linkedListLoop(oldHead) @@ -238,7 +238,7 @@ final class WeakHashSet[A <: AnyRef](val initialCapacity: Int, val loadFactor: D @tailrec def linkedListLoop(prevEntry: Entry[A], entry: Entry[A]): Unit = entry match { case null => () - case _ if (elem == entry.get) => remove(bucket, prevEntry, entry) + case _ if elem.equals(entry.get) => remove(bucket, prevEntry, entry) case _ => linkedListLoop(entry, entry.tail) } From f717ee955c9a4018e945b3143d964303dcfa4a86 Mon Sep 17 00:00:00 2001 From: Jason Zaugg Date: Thu, 16 Mar 2017 14:19:02 +1000 Subject: [PATCH 02/15] Optimize copyTypeRef to avoid hash consing in some cases For a no-args, no-prefix type ref, we can bypass the call to TypeRef.apply, and with it, the hash-consing of the type, by asking the Symbol for its type constructor ref, which is cached. --- src/reflect/scala/reflect/internal/Types.scala | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/reflect/scala/reflect/internal/Types.scala b/src/reflect/scala/reflect/internal/Types.scala index 4401925461f..f9f301a6a5e 100644 --- a/src/reflect/scala/reflect/internal/Types.scala +++ b/src/reflect/scala/reflect/internal/Types.scala @@ -3604,7 +3604,10 @@ trait Types if (sym.isAliasType && sameLength(sym.info.typeParams, args) && !sym.lockOK) throw new RecoverableCyclicReference(sym) - TypeRef(pre, sym, args) + if ((args eq Nil) && (pre eq NoPrefix)) + sym.tpeHK // opt lean on TypeSymbol#tyconCache, rather than interning a type ref. + else + TypeRef(pre, sym, args) case _ => typeRef(pre, sym, args) } From 4a224526a583a9bff179b775e4f8825fbaad3ac0 Mon Sep 17 00:00:00 2001 From: Jason Zaugg Date: Thu, 16 Mar 2017 13:17:20 +1000 Subject: [PATCH 03/15] Optimize type comparison around AnnotatedTypes Avoids the megamorphic call to `Type#annotations` in favour of an instanceof check. --- .../scala/reflect/internal/tpe/TypeComparers.scala | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/reflect/scala/reflect/internal/tpe/TypeComparers.scala b/src/reflect/scala/reflect/internal/tpe/TypeComparers.scala index b1b5ad6f4bf..3954f6fc651 100644 --- a/src/reflect/scala/reflect/internal/tpe/TypeComparers.scala +++ b/src/reflect/scala/reflect/internal/tpe/TypeComparers.scala @@ -124,9 +124,9 @@ trait TypeComparers { // combination of { tp1, tp2 } { is, is not } an AnnotatedType - this because the // logic of "annotationsConform" is arbitrary and unknown. private def isSameType1(tp1: Type, tp2: Type): Boolean = typeRelationPreCheck(tp1, tp2) match { - case state if state.isKnown => state.booleanValue - case _ if typeHasAnnotations(tp1) || typeHasAnnotations(tp2) => sameAnnotatedTypes(tp1, tp2) - case _ => isSameType2(tp1, tp2) + case state if state.isKnown => state.booleanValue + case _ if tp1.isInstanceOf[AnnotatedType] || tp2.isInstanceOf[AnnotatedType] => sameAnnotatedTypes(tp1, tp2) + case _ => isSameType2(tp1, tp2) } private def isSameHKTypes(tp1: Type, tp2: Type) = ( @@ -315,9 +315,9 @@ trait TypeComparers { } private def isSubType1(tp1: Type, tp2: Type, depth: Depth): Boolean = typeRelationPreCheck(tp1, tp2) match { - case state if state.isKnown => state.booleanValue - case _ if typeHasAnnotations(tp1) || typeHasAnnotations(tp2) => annotationsConform(tp1, tp2) && (tp1.withoutAnnotations <:< tp2.withoutAnnotations) - case _ => isSubType2(tp1, tp2, depth) + case state if state.isKnown => state.booleanValue + case _ if tp1.isInstanceOf[AnnotatedType] || tp2.isInstanceOf[AnnotatedType] => annotationsConform(tp1, tp2) && (tp1.withoutAnnotations <:< tp2.withoutAnnotations) + case _ => isSubType2(tp1, tp2, depth) } private def isPolySubType(tp1: PolyType, tp2: PolyType): Boolean = { From 9f954e915429b062f64ea58ae5de085bfbcfb972 Mon Sep 17 00:00:00 2001 From: Jason Zaugg Date: Thu, 16 Mar 2017 00:54:43 +1000 Subject: [PATCH 04/15] Make Symbol.allOverridenSymbols tail recursive --- src/reflect/scala/reflect/internal/Symbols.scala | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/src/reflect/scala/reflect/internal/Symbols.scala b/src/reflect/scala/reflect/internal/Symbols.scala index b413f9ef30e..0f53faa419d 100644 --- a/src/reflect/scala/reflect/internal/Symbols.scala +++ b/src/reflect/scala/reflect/internal/Symbols.scala @@ -2366,15 +2366,16 @@ trait Symbols extends api.Symbols { self: SymbolTable => /** Returns all symbols overridden by this symbol. */ final def allOverriddenSymbols: List[Symbol] = { - def loop(xs: List[Symbol]): List[Symbol] = xs match { - case Nil => Nil + @tailrec + def loop(xs: List[Symbol], result: List[Symbol]): List[Symbol] = xs match { + case Nil => result case x :: xs => overriddenSymbol(x) match { - case NoSymbol => loop(xs) - case sym => sym :: loop(xs) + case NoSymbol => loop(xs, result) + case sym => loop(xs, sym :: result) } } - if (isOverridingSymbol) loop(owner.ancestors) else Nil + if (isOverridingSymbol) loop(owner.ancestors, Nil) else Nil } /** Equivalent to allOverriddenSymbols.nonEmpty, but more efficient. */ From 43f3d856e2d05ca9088f817512211fe40741df9c Mon Sep 17 00:00:00 2001 From: Jason Zaugg Date: Fri, 17 Mar 2017 13:18:02 +1000 Subject: [PATCH 05/15] Opt specializedSym, special case for WildCardType We find our way to this method with wildcard types in the search for implicit views. This commit short circuits the computation in this case, avoiding an as-seen-from operation on the low symbol's info --- .../scala/reflect/internal/Types.scala | 72 ++++++++++--------- 1 file changed, 39 insertions(+), 33 deletions(-) diff --git a/src/reflect/scala/reflect/internal/Types.scala b/src/reflect/scala/reflect/internal/Types.scala index f9f301a6a5e..fcc33df2ffc 100644 --- a/src/reflect/scala/reflect/internal/Types.scala +++ b/src/reflect/scala/reflect/internal/Types.scala @@ -3788,7 +3788,7 @@ trait Types private var uniques: util.WeakHashSet[Type] = _ private var uniqueRunId = NoRunId - protected def unique[T <: Type](tp: T): T = { + protected def unique[T <: Type](tp: T): T = { if (Statistics.canEnable) Statistics.incCounter(rawTypeCount) if (uniqueRunId != currentRunId) { uniques = util.WeakHashSet[Type](initialUniquesCapacity) @@ -4242,38 +4242,44 @@ trait Types */ protected[internal] def specializesSym(preLo: Type, symLo: Symbol, preHi: Type, symHi: Symbol, depth: Depth): Boolean = (symHi.isAliasType || symHi.isTerm || symHi.isAbstractType) && { - // only now that we know symHi is a viable candidate ^^^^^^^, do the expensive checks: ----V - require((symLo ne NoSymbol) && (symHi ne NoSymbol), ((preLo, symLo, preHi, symHi, depth))) - - val tpHi = preHi.memberInfo(symHi).substThis(preHi.typeSymbol, preLo) - - // Should we use memberType or memberInfo? - // memberType transforms (using `asSeenFrom`) `sym.tpe`, - // whereas memberInfo performs the same transform on `sym.info`. - // For term symbols, this ends up being the same thing (`sym.tpe == sym.info`). - // For type symbols, however, the `.info` of an abstract type member - // is defined by its bounds, whereas its `.tpe` is a `TypeRef` to that type symbol, - // so that `sym.tpe <:< sym.info`, but not the other way around. - // - // Thus, for the strongest (correct) result, - // we should use `memberType` on the low side. - // - // On the high side, we should use the result appropriate - // for the right side of the `<:<` above (`memberInfo`). - val tpLo = preLo.memberType(symLo) - - debuglog(s"specializesSymHi: $preHi . $symHi : $tpHi") - debuglog(s"specializesSymLo: $preLo . $symLo : $tpLo") - - if (symHi.isTerm) - (isSubType(tpLo, tpHi, depth) && - (!symHi.isStable || symLo.isStable) && // sub-member must remain stable - (!symLo.hasVolatileType || symHi.hasVolatileType || tpHi.isWildcard)) // sub-member must not introduce volatility - else if (symHi.isAbstractType) - ((tpHi.bounds containsType tpLo) && - kindsConform(symHi :: Nil, tpLo :: Nil, preLo, symLo.owner)) - else // we know `symHi.isAliasType` (see above) - tpLo =:= tpHi + val symHiInfo = symHi.info + if (symHiInfo == WildcardType) { + if (symHi.isTerm) (!symHi.isStable || symLo.isStable) // sub-member must remain stable + else true + } else { + // only now that we know symHi is a viable candidate, do the expensive checks: ----V + require((symLo ne NoSymbol) && (symHi ne NoSymbol), ((preLo, symLo, preHi, symHi, depth))) + + val tpHi = symHiInfo.asSeenFrom(preHi, symHi.owner).substThis(preHi.typeSymbol, preLo) + + // Should we use memberType or memberInfo? + // memberType transforms (using `asSeenFrom`) `sym.tpe`, + // whereas memberInfo performs the same transform on `sym.info`. + // For term symbols, this ends up being the same thing (`sym.tpe == sym.info`). + // For type symbols, however, the `.info` of an abstract type member + // is defined by its bounds, whereas its `.tpe` is a `TypeRef` to that type symbol, + // so that `sym.tpe <:< sym.info`, but not the other way around. + // + // Thus, for the strongest (correct) result, + // we should use `memberType` on the low side. + // + // On the high side, we should use the result appropriate + // for the right side of the `<:<` above (`memberInfo`). + val tpLo = preLo.memberType(symLo) + + debuglog(s"specializesSymHi: $preHi . $symHi : $tpHi") + debuglog(s"specializesSymLo: $preLo . $symLo : $tpLo") + + if (symHi.isTerm) + (isSubType(tpLo, tpHi, depth) && + (!symHi.isStable || symLo.isStable) && // sub-member must remain stable + (!symLo.hasVolatileType || symHi.hasVolatileType || tpHi.isWildcard)) // sub-member must not introduce volatility + else if (symHi.isAbstractType) + ((tpHi.bounds containsType tpLo) && + kindsConform(symHi :: Nil, tpLo :: Nil, preLo, symLo.owner)) + else // we know `symHi.isAliasType` (see above) + tpLo =:= tpHi + } } /** A function implementing `tp1` matches `tp2`. */ From fc80f8676e4fc08fa4ea2f612fb9adb536e3ea90 Mon Sep 17 00:00:00 2001 From: Jason Zaugg Date: Wed, 15 Mar 2017 22:51:44 +1000 Subject: [PATCH 06/15] Optimize boundSyms by avoiding needless call to typeOfThis This method is call in symbol substitution, which is a hot path during typechecking. This commit observes that certain types of types don't define any bound symbols, and immediately returns an empty set. These types of types are: ThisType this.type SuperType A.super.B ConstantType 42 SingleType foo.type TypeBounds >: L <: B When a type reports an empty set of bound symbols, symbol substution _does_ recurse into the compoments of the type (with `mapOver`), so we still find bound symbols, for instances, an existential type in one of the type bounds. Before this commit, calling `ThisType#boundSyms` came to the same result via the implementation inherited from `SimpleTypeProxy`, which incurred an needless call to `ThisType#typeOfThis`. --- .../scala/reflect/internal/Types.scala | 19 +++++++++++-------- 1 file changed, 11 insertions(+), 8 deletions(-) diff --git a/src/reflect/scala/reflect/internal/Types.scala b/src/reflect/scala/reflect/internal/Types.scala index fcc33df2ffc..c8452991f02 100644 --- a/src/reflect/scala/reflect/internal/Types.scala +++ b/src/reflect/scala/reflect/internal/Types.scala @@ -1085,7 +1085,8 @@ trait Types override def baseTypeSeq: BaseTypeSeq = supertype.baseTypeSeq override def baseTypeSeqDepth: Depth = supertype.baseTypeSeqDepth override def baseClasses: List[Symbol] = supertype.baseClasses - } + override def boundSyms: Set[Symbol] = emptySymbolSet + } /** A base class for types that represent a single value * (single-types and this-types). @@ -1106,13 +1107,15 @@ trait Types if (pre.isOmittablePrefix) pre.fullName + ".type" else prefixString + "type" } -/* - override def typeOfThis: Type = typeSymbol.typeOfThis - override def bounds: TypeBounds = TypeBounds(this, this) - override def prefix: Type = NoType - override def typeArgs: List[Type] = List() - override def typeParams: List[Symbol] = List() -*/ + override def boundSyms: Set[Symbol] = emptySymbolSet + + /* + override def typeOfThis: Type = typeSymbol.typeOfThis + override def bounds: TypeBounds = TypeBounds(this, this) + override def prefix: Type = NoType + override def typeArgs: List[Type] = List() + override def typeParams: List[Symbol] = List() + */ } /** An object representing an erroneous type */ From aa2ccb916998c6e2d48e177551fdb0ca89167c6d Mon Sep 17 00:00:00 2001 From: Jason Zaugg Date: Mon, 3 Apr 2017 16:22:20 +1000 Subject: [PATCH 07/15] Avoid calling member type in futile matchingSymbolInternal check If we arrive at this method with NoSymbol as the candidate symbol, avoid an as-seen-from on the low side of the comparison. --- src/reflect/scala/reflect/internal/Symbols.scala | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/reflect/scala/reflect/internal/Symbols.scala b/src/reflect/scala/reflect/internal/Symbols.scala index 0f53faa419d..72d5e7b15ac 100644 --- a/src/reflect/scala/reflect/internal/Symbols.scala +++ b/src/reflect/scala/reflect/internal/Symbols.scala @@ -2315,8 +2315,9 @@ trait Symbols extends api.Symbols { self: SymbolTable => private def matchingSymbolInternal(site: Type, candidate: Symbol): Symbol = { def qualifies(sym: Symbol) = !sym.isTerm || ((site memberType this) matches (site memberType sym)) - //OPT cut down on #closures by special casing non-overloaded case - if (candidate.isOverloaded) candidate filter qualifies + //OPT Fast past for NoSymbol. Cut down on #closures by special casing non-overloaded case + if (candidate == NoSymbol) NoSymbol + else if (candidate.isOverloaded) candidate filter qualifies else if (qualifies(candidate)) candidate else NoSymbol } From d64765ecd10cb248717dfea1ce28f7dcda2ce9c2 Mon Sep 17 00:00:00 2001 From: Jason Zaugg Date: Fri, 10 Mar 2017 13:13:46 +1000 Subject: [PATCH 08/15] Optimize TypeRef#computeHashCode Directly call hashCode on the arguments, rather than indirecting through ##. --- src/reflect/scala/reflect/internal/Types.scala | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/src/reflect/scala/reflect/internal/Types.scala b/src/reflect/scala/reflect/internal/Types.scala index c8452991f02..fe17bc84244 100644 --- a/src/reflect/scala/reflect/internal/Types.scala +++ b/src/reflect/scala/reflect/internal/Types.scala @@ -2135,14 +2135,17 @@ trait Types //OPT specialize hashCode override final def computeHashCode = { import scala.util.hashing.MurmurHash3._ - val hasArgs = args ne Nil var h = productSeed h = mix(h, pre.hashCode) h = mix(h, sym.hashCode) - if (hasArgs) - finalizeHash(mix(h, args.hashCode()), 3) - else - finalizeHash(h, 2) + var i = 0 + var elem = args + while (elem ne Nil) { + h = mix(h, elem.head.hashCode()) + elem = elem.tail + i += 1 + } + finalizeHash(h, 2 + i) } // interpret symbol's info in terms of the type's prefix and type args From d85a0879cfa2879e0b2f96f6ef56e3fd723dec59 Mon Sep 17 00:00:00 2001 From: Jason Zaugg Date: Wed, 8 Mar 2017 10:38:20 +1000 Subject: [PATCH 09/15] Optimize TypeRef#equals Avoid use of BoxesRuntime equals in favour of direct use of Object#equals. --- src/reflect/scala/reflect/internal/Types.scala | 9 +++++++++ .../scala/reflect/internal/util/Collections.scala | 13 +++++++++++++ 2 files changed, 22 insertions(+) diff --git a/src/reflect/scala/reflect/internal/Types.scala b/src/reflect/scala/reflect/internal/Types.scala index fe17bc84244..31ba5d52ee1 100644 --- a/src/reflect/scala/reflect/internal/Types.scala +++ b/src/reflect/scala/reflect/internal/Types.scala @@ -2147,6 +2147,15 @@ trait Types } finalizeHash(h, 2 + i) } + //OPT specialize equals + override final def equals(other: Any): Boolean = { + other match { + case otherTypeRef: TypeRef => + pre.equals(otherTypeRef.pre) && sym.eq(otherTypeRef.sym) && sameElementsEquals(args, otherTypeRef.args) + case _ => false + } + } + // interpret symbol's info in terms of the type's prefix and type args protected def relativeInfo: Type = appliedType(sym.info.asSeenFrom(pre, sym.owner), argsOrDummies) diff --git a/src/reflect/scala/reflect/internal/util/Collections.scala b/src/reflect/scala/reflect/internal/util/Collections.scala index a743d8962af..821f19095e7 100644 --- a/src/reflect/scala/reflect/internal/util/Collections.scala +++ b/src/reflect/scala/reflect/internal/util/Collections.scala @@ -61,6 +61,19 @@ trait Collections { head } + final def sameElementsEquals(thiss: List[AnyRef], that: List[AnyRef]): Boolean = { + // Probably immutable, so check reference identity first (it's quick anyway) + (thiss eq that) || { + var these = thiss + var those = that + while (!these.isEmpty && !those.isEmpty && these.head.equals(those.head)) { + these = these.tail + those = those.tail + } + these.isEmpty && those.isEmpty + } + } + final def collectFirst[A, B](as: List[A])(pf: PartialFunction[A, B]): Option[B] = { @tailrec def loop(rest: List[A]): Option[B] = rest match { From 8a53dd0cadfe34538a72fabc89e2ed393e96afae Mon Sep 17 00:00:00 2001 From: Jason Zaugg Date: Mon, 3 Apr 2017 16:07:34 +1000 Subject: [PATCH 10/15] Add a fast path to TreeGen to speedup synthesis of casts Unfortunately, we've had to add a lot more casts into the code to workaround corner cases in the Fields transform. This commit avoids an inefficiency in synthesizing these trees, by avoid a call to as-seen-from. --- src/reflect/scala/reflect/internal/TreeGen.scala | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/reflect/scala/reflect/internal/TreeGen.scala b/src/reflect/scala/reflect/internal/TreeGen.scala index 4a8540274b8..d312582dcbd 100644 --- a/src/reflect/scala/reflect/internal/TreeGen.scala +++ b/src/reflect/scala/reflect/internal/TreeGen.scala @@ -231,7 +231,10 @@ abstract class TreeGen { val tree = Select(pkgQualifier, sym) if (pkgQualifier.tpe == null) tree - else tree setType (qual.tpe memberType sym) + else tree setType { + if (sym.rawowner == ObjectClass || sym.rawowner == AnyClass) sym.tpeHK.normalize // opt for asInstanceOf + else (qual.tpe memberType sym) + } } } From b47c98986323530988525175ad165c3a2419ae7e Mon Sep 17 00:00:00 2001 From: Jason Zaugg Date: Thu, 9 Mar 2017 19:31:30 +1000 Subject: [PATCH 11/15] Avoid List.apply in favor of :: --- src/compiler/scala/tools/nsc/ast/parser/Parsers.scala | 2 +- .../scala/tools/nsc/backend/jvm/BTypesFromSymbols.scala | 6 +++--- .../tools/nsc/symtab/classfile/ClassfileParser.scala | 3 ++- src/compiler/scala/tools/nsc/transform/Erasure.scala | 4 ++-- src/compiler/scala/tools/nsc/typechecker/Implicits.scala | 2 +- src/compiler/scala/tools/nsc/typechecker/Typers.scala | 2 +- src/reflect/scala/reflect/internal/Types.scala | 2 +- src/reflect/scala/reflect/internal/tpe/GlbLubs.scala | 8 ++++---- 8 files changed, 15 insertions(+), 14 deletions(-) diff --git a/src/compiler/scala/tools/nsc/ast/parser/Parsers.scala b/src/compiler/scala/tools/nsc/ast/parser/Parsers.scala index d9af3cadfa4..08704066581 100644 --- a/src/compiler/scala/tools/nsc/ast/parser/Parsers.scala +++ b/src/compiler/scala/tools/nsc/ast/parser/Parsers.scala @@ -829,7 +829,7 @@ self => def mkNamed(args: List[Tree]) = if (isExpr) args map treeInfo.assignmentToMaybeNamedArg else args val arguments = right match { case Parens(args) => mkNamed(args) - case _ => List(right) + case _ => right :: Nil } if (isExpr) { if (treeInfo.isLeftAssoc(op)) { diff --git a/src/compiler/scala/tools/nsc/backend/jvm/BTypesFromSymbols.scala b/src/compiler/scala/tools/nsc/backend/jvm/BTypesFromSymbols.scala index a99e3033860..a90e834943b 100644 --- a/src/compiler/scala/tools/nsc/backend/jvm/BTypesFromSymbols.scala +++ b/src/compiler/scala/tools/nsc/backend/jvm/BTypesFromSymbols.scala @@ -610,11 +610,11 @@ class BTypesFromSymbols[G <: Global](val global: G) extends BTypes { annotatedInline = info.annotatedInline, annotatedNoInline = info.annotatedNoInline) if (methodSym.isMixinConstructor) - List((staticMethodSignature, staticMethodInfo)) + (staticMethodSignature, staticMethodInfo) :: Nil else - List((signature, info), (staticMethodSignature, staticMethodInfo)) + (signature, info) :: (staticMethodSignature, staticMethodInfo) :: Nil } else - List((signature, info)) + (signature, info) :: Nil } }).toMap diff --git a/src/compiler/scala/tools/nsc/symtab/classfile/ClassfileParser.scala b/src/compiler/scala/tools/nsc/symtab/classfile/ClassfileParser.scala index bcfd50974d0..d30e37474b1 100644 --- a/src/compiler/scala/tools/nsc/symtab/classfile/ClassfileParser.scala +++ b/src/compiler/scala/tools/nsc/symtab/classfile/ClassfileParser.scala @@ -1089,7 +1089,8 @@ abstract class ClassfileParser { cls setInfo completer mod setInfo completer mod.moduleClass setInfo loaders.moduleClassLoader - List(cls, mod.moduleClass) foreach (_.associatedFile = file) + cls.associatedFile = file + mod.moduleClass.associatedFile = file (cls, mod) } diff --git a/src/compiler/scala/tools/nsc/transform/Erasure.scala b/src/compiler/scala/tools/nsc/transform/Erasure.scala index 2266ba28030..596e1b982e1 100644 --- a/src/compiler/scala/tools/nsc/transform/Erasure.scala +++ b/src/compiler/scala/tools/nsc/transform/Erasure.scala @@ -458,7 +458,7 @@ abstract class Erasure extends InfoTransform class ComputeBridges(unit: CompilationUnit, root: Symbol) { class BridgesCursor(root: Symbol) extends overridingPairs.Cursor(root) { - override def parents = List(root.info.firstParent) + override def parents = root.info.firstParent :: Nil // Varargs bridges may need generic bridges due to the non-repeated part of the signature of the involved methods. // The vararg bridge is generated during refchecks (probably to simplify override checking), // but then the resulting varargs "bridge" method may itself need an actual erasure bridge. @@ -656,7 +656,7 @@ abstract class Erasure extends InfoTransform private def adaptMember(tree: Tree): Tree = { //Console.println("adaptMember: " + tree); tree match { - case Apply(ta @ TypeApply(sel @ Select(qual, name), List(targ)), List()) + case Apply(ta @ TypeApply(sel @ Select(qual, name), targ :: Nil), List()) if tree.symbol == Any_asInstanceOf => val qual1 = typedQualifier(qual, NOmode, ObjectTpe) // need to have an expected type, see #3037 // !!! Make pending/run/t5866b.scala work. The fix might be here and/or in unbox1. diff --git a/src/compiler/scala/tools/nsc/typechecker/Implicits.scala b/src/compiler/scala/tools/nsc/typechecker/Implicits.scala index 1dd661064ab..8432b9aa923 100644 --- a/src/compiler/scala/tools/nsc/typechecker/Implicits.scala +++ b/src/compiler/scala/tools/nsc/typechecker/Implicits.scala @@ -633,7 +633,7 @@ trait Implicits { val itree2 = if (!isView) fallback else pt match { case Function1(arg1, arg2) => typed1( - atPos(itree0.pos)(Apply(itree1, List(Ident(nme.argument) setType approximate(arg1)))), + atPos(itree0.pos)(Apply(itree1, Ident(nme.argument).setType(approximate(arg1)) :: Nil)), EXPRmode, approximate(arg2) ) match { diff --git a/src/compiler/scala/tools/nsc/typechecker/Typers.scala b/src/compiler/scala/tools/nsc/typechecker/Typers.scala index b5adf8c1731..65e44cc9e31 100644 --- a/src/compiler/scala/tools/nsc/typechecker/Typers.scala +++ b/src/compiler/scala/tools/nsc/typechecker/Typers.scala @@ -1859,7 +1859,7 @@ trait Typers extends Adaptations with Tags with TypersTracking with PatternTyper if (noSerializable) Nil else { clazz.makeSerializable() - List(TypeTree(SerializableTpe) setPos clazz.pos.focus) + TypeTree(SerializableTpe).setPos(clazz.pos.focus) :: Nil } ) }) diff --git a/src/reflect/scala/reflect/internal/Types.scala b/src/reflect/scala/reflect/internal/Types.scala index 31ba5d52ee1..23a2ab64578 100644 --- a/src/reflect/scala/reflect/internal/Types.scala +++ b/src/reflect/scala/reflect/internal/Types.scala @@ -3918,7 +3918,7 @@ trait Types && isRawIfWithoutArgs(sym) ) - def singletonBounds(hi: Type) = TypeBounds.upper(intersectionType(List(hi, SingletonClass.tpe))) + def singletonBounds(hi: Type) = TypeBounds.upper(intersectionType(hi :: SingletonClass.tpe :: Nil)) /** * A more persistent version of `Type#memberType` which does not require diff --git a/src/reflect/scala/reflect/internal/tpe/GlbLubs.scala b/src/reflect/scala/reflect/internal/tpe/GlbLubs.scala index 7e1db292132..6b8de25bfa0 100644 --- a/src/reflect/scala/reflect/internal/tpe/GlbLubs.scala +++ b/src/reflect/scala/reflect/internal/tpe/GlbLubs.scala @@ -184,8 +184,8 @@ private[internal] trait GlbLubs { /** Eliminate from list of types all elements which are a supertype * of some other element of the list. */ private def elimSuper(ts: List[Type]): List[Type] = ts match { - case List() => List() - case List(t) => List(t) + case List() => ts + case List(t) => ts case t :: ts1 => val rest = elimSuper(ts1 filter (t1 => !(t <:< t1))) if (rest exists (t1 => t1 <:< t)) rest else t :: rest @@ -195,8 +195,8 @@ private[internal] trait GlbLubs { * of some other element of the list. */ private def elimSub(ts: List[Type], depth: Depth): List[Type] = { def elimSub0(ts: List[Type]): List[Type] = ts match { - case List() => List() - case List(t) => List(t) + case List() => ts + case List(t) => ts case t :: ts1 => val rest = elimSub0(ts1 filter (t1 => !isSubType(t1, t, depth.decr))) if (rest exists (t1 => isSubType(t, t1, depth.decr))) rest else t :: rest From f985f1f3bb587ab2182dbcf00285af990be749a8 Mon Sep 17 00:00:00 2001 From: Jason Zaugg Date: Tue, 28 Mar 2017 17:58:18 +1000 Subject: [PATCH 12/15] Avoid repeating call to Symbol#typeParams in hot code --- .../scala/reflect/internal/tpe/TypeMaps.scala | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/src/reflect/scala/reflect/internal/tpe/TypeMaps.scala b/src/reflect/scala/reflect/internal/tpe/TypeMaps.scala index e0c2488839f..97471f056ef 100644 --- a/src/reflect/scala/reflect/internal/tpe/TypeMaps.scala +++ b/src/reflect/scala/reflect/internal/tpe/TypeMaps.scala @@ -101,11 +101,16 @@ private[internal] trait TypeMaps { case tr @ TypeRef(pre, sym, args) => val pre1 = this(pre) val args1 = ( - if (trackVariance && args.nonEmpty && !variance.isInvariant && sym.typeParams.nonEmpty) - mapOverArgs(args, sym.typeParams) - else + if (trackVariance && args.nonEmpty && !variance.isInvariant) { + val tparams = sym.typeParams + if (tparams.isEmpty) + args mapConserve this + else + mapOverArgs(args, tparams) + } else { args mapConserve this - ) + } + ) if ((pre1 eq pre) && (args1 eq args)) tp else copyTypeRef(tp, pre1, tr.coevolveSym(pre1), args1) case ThisType(_) => tp From 67ea707c1a4115fed932cc37e7602a408d58227b Mon Sep 17 00:00:00 2001 From: Jason Zaugg Date: Thu, 20 Apr 2017 12:50:29 +1000 Subject: [PATCH 13/15] Minor refactor of TypeRef.computeHashCode --- src/reflect/scala/reflect/internal/Types.scala | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/src/reflect/scala/reflect/internal/Types.scala b/src/reflect/scala/reflect/internal/Types.scala index 23a2ab64578..632303c9e22 100644 --- a/src/reflect/scala/reflect/internal/Types.scala +++ b/src/reflect/scala/reflect/internal/Types.scala @@ -2138,14 +2138,14 @@ trait Types var h = productSeed h = mix(h, pre.hashCode) h = mix(h, sym.hashCode) - var i = 0 - var elem = args - while (elem ne Nil) { - h = mix(h, elem.head.hashCode()) - elem = elem.tail - i += 1 + var length = 2 + var elems = args + while (elems ne Nil) { + h = mix(h, elems.head.hashCode()) + elems = elems.tail + length += 1 } - finalizeHash(h, 2 + i) + finalizeHash(h, length) } //OPT specialize equals override final def equals(other: Any): Boolean = { From 1517573edd38dd6ab01b6e75097c97e603c21ed5 Mon Sep 17 00:00:00 2001 From: Jason Zaugg Date: Thu, 20 Apr 2017 12:54:37 +1000 Subject: [PATCH 14/15] Minor refactor in elimSuper pattern match --- src/reflect/scala/reflect/internal/tpe/GlbLubs.scala | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/reflect/scala/reflect/internal/tpe/GlbLubs.scala b/src/reflect/scala/reflect/internal/tpe/GlbLubs.scala index 6b8de25bfa0..93edab99b6a 100644 --- a/src/reflect/scala/reflect/internal/tpe/GlbLubs.scala +++ b/src/reflect/scala/reflect/internal/tpe/GlbLubs.scala @@ -184,8 +184,7 @@ private[internal] trait GlbLubs { /** Eliminate from list of types all elements which are a supertype * of some other element of the list. */ private def elimSuper(ts: List[Type]): List[Type] = ts match { - case List() => ts - case List(t) => ts + case List() | List(_) => ts case t :: ts1 => val rest = elimSuper(ts1 filter (t1 => !(t <:< t1))) if (rest exists (t1 => t1 <:< t)) rest else t :: rest From 9b28b01a46f3b8804b06b4559502e2ada50ba618 Mon Sep 17 00:00:00 2001 From: Jason Zaugg Date: Wed, 26 Apr 2017 14:16:56 +1000 Subject: [PATCH 15/15] Rework changes to specializedSym Restrict the fast path to term symbols (which is the important case for performance) to allow the existing code to handle type symbols. --- src/reflect/scala/reflect/internal/Types.scala | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/reflect/scala/reflect/internal/Types.scala b/src/reflect/scala/reflect/internal/Types.scala index 632303c9e22..79fcdf84c5c 100644 --- a/src/reflect/scala/reflect/internal/Types.scala +++ b/src/reflect/scala/reflect/internal/Types.scala @@ -4258,9 +4258,9 @@ trait Types protected[internal] def specializesSym(preLo: Type, symLo: Symbol, preHi: Type, symHi: Symbol, depth: Depth): Boolean = (symHi.isAliasType || symHi.isTerm || symHi.isAbstractType) && { val symHiInfo = symHi.info - if (symHiInfo == WildcardType) { - if (symHi.isTerm) (!symHi.isStable || symLo.isStable) // sub-member must remain stable - else true + if (symHi.isTerm && symHiInfo == WildcardType) { + // OPT fast path (avoiding tpLo.mmeberType) for wildcards which appear here frequently in the search for implicit views. + !symHi.isStable || symLo.isStable // sub-member must remain stable } else { // only now that we know symHi is a viable candidate, do the expensive checks: ----V require((symLo ne NoSymbol) && (symHi ne NoSymbol), ((preLo, symLo, preHi, symHi, depth)))