From 3dba9932fcc79ce0ea6f7c9282320c14c95d133f Mon Sep 17 00:00:00 2001 From: Jason Zaugg Date: Tue, 29 Oct 2013 16:04:17 +0100 Subject: Add a per-run cache for member symbols We are all used to calls to `definitions.PredefModule`, or `defintions.Predef_???` to grab the symbol of some well known entity. But you'll notice that some of these are lazy vals, and others are defs. Why is this so? In the presentation compiler, a member like `Predef.???` will be assigned a new symbol after the user browses into `Predef.scala`. Mistakenly using vals in definitions leads to subtle IDE bugs like SI-7678. We are able to trigger these situations in tests, as noted in the comments of that issue. Changing the vals to defs, on the other hand, has a performance penalty. Some schemes to workaround this have shown up: cache them per-implicit search, or compare method names and owners rather than symbols on hot paths in the type checker. This commit introduces a facility to cache these sort of symbols per-run, and uses it to check for `Predef.conforms` and and for the class/type tag materializers. A followup pull request (WIP: https://github.com/retronym/scala/compare/ticket/7678-2) will expand the use of to address the widespread and unsafe caching of member symbols that I found while investigating SI-7678. --- src/compiler/scala/tools/nsc/Global.scala | 3 +++ src/compiler/scala/tools/nsc/typechecker/Implicits.scala | 14 ++++---------- 2 files changed, 7 insertions(+), 10 deletions(-) (limited to 'src/compiler') diff --git a/src/compiler/scala/tools/nsc/Global.scala b/src/compiler/scala/tools/nsc/Global.scala index 1852e670e4..5f079a428b 100644 --- a/src/compiler/scala/tools/nsc/Global.scala +++ b/src/compiler/scala/tools/nsc/Global.scala @@ -1562,6 +1562,9 @@ class Global(var currentSettings: Settings, var reporter: Reporter) } } + /** Caching member symbols that are def-s in Defintions because they might change from Run to Run. */ + val runDefinitions: definitions.RunDefinitions = new definitions.RunDefinitions + /** Compile list of source files, * unless there is a problem already, * such as a plugin was passed a bad option. diff --git a/src/compiler/scala/tools/nsc/typechecker/Implicits.scala b/src/compiler/scala/tools/nsc/typechecker/Implicits.scala index 8a3bba7d0f..dc60631421 100644 --- a/src/compiler/scala/tools/nsc/typechecker/Implicits.scala +++ b/src/compiler/scala/tools/nsc/typechecker/Implicits.scala @@ -337,6 +337,9 @@ trait Implicits { val undetParams = if (isView) Nil else context.outer.undetparams val wildPt = approximate(pt) + private val runDefintions = currentRun.runDefinitions + import runDefintions.{ TagMaterializers, TagSymbols, Predef_conforms, PartialManifestClass, ManifestSymbols } + def undet_s = if (undetParams.isEmpty) "" else undetParams.mkString(" inferring ", ", ", "") def tree_s = typeDebug ptTree tree def ctx_s = fullSiteString(context) @@ -806,7 +809,7 @@ trait Implicits { private def isIneligible(info: ImplicitInfo) = ( info.isCyclicOrErroneous - || isView && isPredefMemberNamed(info.sym, nme.conforms) + || isView && (info.sym eq Predef_conforms) || shadower.isShadowed(info.name) || (!context.macrosEnabled && info.sym.isTermMacro) ) @@ -1105,13 +1108,6 @@ trait Implicits { } } - private def TagSymbols = TagMaterializers.keySet - private val TagMaterializers = Map[Symbol, Symbol]( - ClassTagClass -> materializeClassTag, - WeakTypeTagClass -> materializeWeakTypeTag, - TypeTagClass -> materializeTypeTag - ) - /** Creates a tree will produce a tag of the requested flavor. * An EmptyTree is returned if materialization fails. */ @@ -1173,8 +1169,6 @@ trait Implicits { else SearchFailure } - private val ManifestSymbols = Set[Symbol](PartialManifestClass, FullManifestClass, OptManifestClass) - /** Creates a tree that calls the relevant factory method in object * scala.reflect.Manifest for type 'tp'. An EmptyTree is returned if * no manifest is found. todo: make this instantiate take type params as well? -- cgit v1.2.3