diff options
author | Paul Phillips <paulp@improving.org> | 2012-11-14 10:54:27 -0800 |
---|---|---|
committer | Paul Phillips <paulp@improving.org> | 2012-11-15 09:02:21 -0800 |
commit | 768a4082a6090835afef34ee38c2c398da335b01 (patch) | |
tree | 017b375f540b6ce7d3ba97c281c9ca54d6eb28e5 /src/compiler | |
parent | 4444369ddf39176dcfae4bcf1901697a6ccdfd42 (diff) | |
download | scala-768a4082a6090835afef34ee38c2c398da335b01.tar.gz scala-768a4082a6090835afef34ee38c2c398da335b01.tar.bz2 scala-768a4082a6090835afef34ee38c2c398da335b01.zip |
Fix for overly eager package object initialization.
A subtle change in the order in which symbol attributes were
inspected (now you know why I avoid vals in the compiler) led to a
cycle during initialization for slick. I'm afraid I don't know how
to reproduce the issue outside of slick and sbt, so I added some
logging instead.
After some challenges juggling general correctness and cycle
avoidance, I resorted to improving and documenting the logic
as well. I predict reviewer will be pleased.
Diffstat (limited to 'src/compiler')
-rw-r--r-- | src/compiler/scala/tools/nsc/typechecker/Contexts.scala | 41 |
1 files changed, 30 insertions, 11 deletions
diff --git a/src/compiler/scala/tools/nsc/typechecker/Contexts.scala b/src/compiler/scala/tools/nsc/typechecker/Contexts.scala index 78380ad054..a8d7de6362 100644 --- a/src/compiler/scala/tools/nsc/typechecker/Contexts.scala +++ b/src/compiler/scala/tools/nsc/typechecker/Contexts.scala @@ -761,25 +761,44 @@ trait Contexts { self: Analyzer => * info of the package object. However to avoid cycles we'll check * what other ways we can before pushing that way. */ - def isInPackageObject(sym: Symbol, pkg: Symbol) = { - val pkgClass = if (pkg.isTerm) pkg.moduleClass else pkg + def isInPackageObject(sym: Symbol, pkg: Symbol): Boolean = { + def uninitialized(what: String) = { + log(s"Cannot look for $sym in package object of $pkg; $what is not initialized.") + false + } + def pkgClass = if (pkg.isTerm) pkg.moduleClass else pkg def matchesInfo = ( - pkg.isInitialized && { - // need to be careful here to not get a cyclic reference during bootstrap + // need to be careful here to not get a cyclic reference during bootstrap + if (pkg.isInitialized) { val module = pkg.info member nme.PACKAGEkw - module.isInitialized && (module.info.member(sym.name).alternatives contains sym) + if (module.isInitialized) + module.info.member(sym.name).alternatives contains sym + else + uninitialized("" + module) } + else uninitialized("" + pkg) ) def inPackageObject(sym: Symbol) = ( - !sym.isPackage - && !sym.owner.isPackageClass - && (sym.owner ne NoSymbol) - && (sym.owner.owner == pkgClass || matchesInfo) + // To be in the package object, one of these must be true: + // 1) sym.owner is a package object class, and sym.owner.owner is the package class for `pkg` + // 2) sym.owner is inherited by the correct package object class + // We try to establish 1) by inspecting the owners directly, and then we try + // to rule out 2), and only if both those fail do we resort to looking in the info. + !sym.isPackage && (sym.owner ne NoSymbol) && ( + if (sym.owner.isPackageObjectClass) + sym.owner.owner == pkgClass + else + !sym.owner.isPackageClass && matchesInfo + ) ) + // An overloaded symbol might not have the expected owner! + // The alternatives must be inspected directly. pkgClass.isPackageClass && ( - if (sym.isOverloaded) sym.alternatives forall inPackageObject - else inPackageObject(sym) + if (sym.isOverloaded) + sym.alternatives forall (isInPackageObject(_, pkg)) + else + inPackageObject(sym) ) } |