Skip to content

use methods to find companion class #436

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 23 commits into from
Apr 2, 2015
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
23 commits
Select commit Hold shift + click to select a range
13a05d5
#353 use methods to find companion class
DarkDimius Mar 25, 2015
d01ecb7
Add companion class methods for files read from scala & java
DarkDimius Mar 25, 2015
25af814
Fix installAfter for a Denotation List of single denotation
DarkDimius Mar 26, 2015
7baead9
Add companion link symbols early only if companion actually exists
DarkDimius Mar 26, 2015
880a6f5
Add late companion symbols in firstTransform
DarkDimius Mar 26, 2015
5e09d2d
#435 Fix conflict between package object and case class with same name
DarkDimius Mar 26, 2015
f40c498
Fix error message in typer
DarkDimius Mar 26, 2015
0a94d6e
Remove code duplication between Namer, ClassfileParser and UnPickler
DarkDimius Mar 26, 2015
7021570
Guard against absent symbols in synthesizeCompanionMethod.
DarkDimius Mar 28, 2015
f2221d0
Make companion-module links in UnPickler
DarkDimius Mar 28, 2015
595c3f6
Make companion-module links in ClassfileParser
DarkDimius Mar 28, 2015
f9910eb
Use methods to find companion modules
DarkDimius Mar 28, 2015
57027f7
Fix companion_class_method name
DarkDimius Mar 30, 2015
b653007
Workaround #440 in FirstTransform.
DarkDimius Mar 30, 2015
cdbe81e
Fix #440: entering symbol into scope also enters it into future scopes.
DarkDimius Mar 30, 2015
042c2f0
Fix #442.
DarkDimius Mar 30, 2015
84602a3
Do not synthesizeCompanionMethod twice, and do not rewrite the existi…
DarkDimius Mar 30, 2015
e907c78
companionModule needs to return ModuleVal for Module.
DarkDimius Mar 30, 2015
fdc92a6
Allow to enter private symbols into Frozen scopes.
DarkDimius Mar 30, 2015
14e0f72
Fix #443, set moduleClass of class being lazily unpickled.
DarkDimius Mar 30, 2015
34f1650
Both module and class being unpickled need to register links.
DarkDimius Mar 30, 2015
c560648
Revert "Workaround #440 in FirstTransform."
DarkDimius Apr 2, 2015
e5618d2
Simplify methods implemented in #436
DarkDimius Apr 2, 2015
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 5 additions & 2 deletions src/dotty/tools/dotc/ast/Desugar.scala
Original file line number Diff line number Diff line change
Expand Up @@ -362,9 +362,10 @@ object desugar {
companionDefs(parent, applyMeths ::: unapplyMeth :: defaultGetters)
}
else if (defaultGetters.nonEmpty)
companionDefs(anyRef, defaultGetters)
companionDefs(anyRef, defaultGetters)
else Nil


// For an implicit class C[Ts](p11: T11, ..., p1N: T1N) ... (pM1: TM1, .., pMN: TMN), the method
// synthetic implicit C[Ts](p11: T11, ..., p1N: T1N) ... (pM1: TM1, ..., pMN: TMN): C[Ts] =
// new C[Ts](p11, ..., p1N) ... (pM1, ..., pMN) =
Expand Down Expand Up @@ -409,6 +410,8 @@ object desugar {
flatTree(cdef1 :: companions ::: implicitWrappers)
}

val AccessOrSynthetic = AccessFlags | Synthetic
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Rename to SyntheticAccessor. When you set mods, its not an Or.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not setting them, I'm using this value to mask previous mods.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah, I misread. Consider the comment withdrawn.


/** Expand
*
* object name extends parents { self => body }
Expand Down Expand Up @@ -436,7 +439,7 @@ object desugar {
.withPos(tmpl.self.pos orElse tmpl.pos.startPos)
val clsTmpl = cpy.Template(tmpl)(self = clsSelf, body = tmpl.body)
val cls = TypeDef(clsName, clsTmpl)
.withMods(mods.toTypeFlags & AccessFlags | ModuleClassCreationFlags)
.withMods(mods.toTypeFlags & AccessOrSynthetic | ModuleClassCreationFlags)
Thicket(modul, classDef(cls))
}
}
Expand Down
8 changes: 6 additions & 2 deletions src/dotty/tools/dotc/core/Denotations.scala
Original file line number Diff line number Diff line change
Expand Up @@ -471,7 +471,7 @@ object Denotations {
* 2) the union of all validity periods is a contiguous
* interval.
*/
private var nextInRun: SingleDenotation = this
protected var nextInRun: SingleDenotation = this

/** The version of this SingleDenotation that was valid in the first phase
* of this run.
Expand Down Expand Up @@ -611,7 +611,10 @@ object Denotations {
val current = symbol.current
// println(s"installing $this after $phase/${phase.id}, valid = ${current.validFor}")
// printPeriods(current)
this.nextInRun = current.nextInRun
if (current.nextInRun ne current)
this.nextInRun = current.nextInRun
else
this.nextInRun = this
this.validFor = Period(ctx.runId, targetId, current.validFor.lastPhaseId)
if (current.validFor.firstPhaseId == targetId) {
// replace current with this denotation
Expand All @@ -622,6 +625,7 @@ object Denotations {
} else {
// insert this denotation after current
current.validFor = Period(ctx.runId, current.validFor.firstPhaseId, targetId - 1)
this.nextInRun = current.nextInRun
current.nextInRun = this
}
// printPeriods(this)
Expand Down
8 changes: 5 additions & 3 deletions src/dotty/tools/dotc/core/StdNames.scala
Original file line number Diff line number Diff line change
Expand Up @@ -83,8 +83,8 @@ object StdNames {
final val HASHkw: N = kw("#")
final val ATkw: N = kw("@")

val ANON_CLASS: N = "$anon"
val ANON_FUN: N = "$anonfun"
val ANON_CLASS: N = "$anon"
val ANON_FUN: N = "$anonfun"
val BITMAP_PREFIX: N = "bitmap$"
val BITMAP_NORMAL: N = BITMAP_PREFIX // initialization bitmap for public/protected lazy vals
val BITMAP_TRANSIENT: N = BITMAP_PREFIX + "trans$" // initialization bitmap for transient lazy vals
Expand Down Expand Up @@ -117,13 +117,15 @@ object StdNames {
val PROTECTED_PREFIX: N = "protected$"
val PROTECTED_SET_PREFIX: N = PROTECTED_PREFIX + "set"
val ROOT: N = "<root>"
val SHADOWED: N = "(shadowed)" // tag to be used until we have proper name kinds
val SHADOWED: N = "(shadowed)" // tag to be used until we have proper name kinds
val SINGLETON_SUFFIX: N = ".type"
val SPECIALIZED_SUFFIX: N = "$sp"
val SUPER_PREFIX: N = "super$"
val WHILE_PREFIX: N = "while$"
val DEFAULT_EXCEPTION_NAME: N = "ex$"
val INITIALIZER_PREFIX: N = "initial$"
val COMPANION_MODULE_METHOD: N = "companion$module"
val COMPANION_CLASS_METHOD: N = "companion$class"

// value types (and AnyRef) are all used as terms as well
// as (at least) arguments to the @specialize annotation.
Expand Down
56 changes: 42 additions & 14 deletions src/dotty/tools/dotc/core/SymDenotations.scala
Original file line number Diff line number Diff line change
Expand Up @@ -239,7 +239,7 @@ object SymDenotations {
final def ensureCompleted()(implicit ctx: Context): Unit = info

/** The symbols defined in this class or object.
* Careful! This coes not force the type, so is compilation order dependent.
* Careful! This does not force the type, so is compilation order dependent.
* This method should be used only in the following circumstances:
*
* 1. When accessing type parameters or type parameter accessors (both are entered before
Expand Down Expand Up @@ -766,18 +766,36 @@ object SymDenotations {
* and which is also defined in the same scope and compilation unit.
* NoSymbol if this module does not exist.
*/
final def companionModule(implicit ctx: Context): Symbol =
if (name == tpnme.ANON_CLASS)
NoSymbol // avoid forcing anon classes, this might cause cyclic reference errors
else
companionNamed(effectiveName.moduleClassName).sourceModule
final def companionModule(implicit ctx: Context): Symbol = {
if (this.flagsUNSAFE is Flags.Module) this.sourceModule
else {
val companionMethod = info.decls.denotsNamed(nme.COMPANION_MODULE_METHOD, selectPrivate).first
if (companionMethod.exists)
companionMethod.info.resultType.classSymbol.sourceModule
else
NoSymbol
}
}


/** The class with the same (type-) name as this module or module class,
* and which is also defined in the same scope and compilation unit.
* NoSymbol if this class does not exist.
*/
final def companionClass(implicit ctx: Context): Symbol =
companionNamed(effectiveName.toTypeName)
* and which is also defined in the same scope and compilation unit.
* NoSymbol if this class does not exist.
*/
final def companionClass(implicit ctx: Context): Symbol = {
val companionMethod = info.decls.denotsNamed(nme.COMPANION_CLASS_METHOD, selectPrivate).first

if (companionMethod.exists)
companionMethod.info.resultType.classSymbol
else
NoSymbol
}

final def scalacLinkedClass(implicit ctx: Context): Symbol =
if (this is ModuleClass) companionNamed(effectiveName.toTypeName)
else if (this.isClass) companionNamed(effectiveName.moduleClassName).sourceModule.moduleClass
else NoSymbol


/** Find companion class symbol with given name, or NoSymbol if none exists.
* Three alternative strategies:
Expand Down Expand Up @@ -1259,15 +1277,21 @@ object SymDenotations {
myMemberCache
}

/** Enter a symbol in current scope.
/** Enter a symbol in current scope, and future scopes of same denotation.
* Note: We require that this does not happen after the first time
* someone does a findMember on a subclass.
* @param scope The scope in which symbol should be entered.
* If this is EmptyScope, the scope is `decls`.
*/
def enter(sym: Symbol, scope: Scope = EmptyScope)(implicit ctx: Context): Unit = {
val mscope = scope match {
case scope: MutableScope => scope
case scope: MutableScope =>
// if enter gets a scope as an argument,
// than this is a scope that will eventually become decls of this symbol.
// And this should only happen if this is first time the scope of symbol
// is computed, ie symbol yet has no future.
assert(this.nextInRun == this)
scope
case _ => unforcedDecls.openForMutations
}
if (this is PackageClass) {
Expand All @@ -1279,11 +1303,15 @@ object SymDenotations {
}
}
enterNoReplace(sym, mscope)
val nxt = this.nextInRun
if (nxt.validFor.code > this.validFor.code) {
this.nextInRun.asSymDenotation.asClass.enter(sym)
}
}

/** Enter a symbol in given `scope` without potentially replacing the old copy. */
def enterNoReplace(sym: Symbol, scope: MutableScope)(implicit ctx: Context): Unit = {
require(!(this is Frozen))
require((sym.denot.flagsUNSAFE is Private) || !(this is Frozen))
scope.enter(sym)

if (myMemberFingerPrint != FingerPrint.unknown)
Expand Down
4 changes: 2 additions & 2 deletions src/dotty/tools/dotc/core/SymbolLoaders.scala
Original file line number Diff line number Diff line change
Expand Up @@ -217,7 +217,7 @@ abstract class SymbolLoader extends LazyType {
denot.markAbsent()
postProcess(root)
if (!root.isRoot)
postProcess(root.linkedClass.denot)
postProcess(root.scalacLinkedClass.denot)
}
}
}
Expand All @@ -229,7 +229,7 @@ class ClassfileLoader(val classfile: AbstractFile) extends SymbolLoader {
def description = "class file "+ classfile.toString

def rootDenots(rootDenot: ClassDenotation)(implicit ctx: Context): (ClassDenotation, ClassDenotation) = {
val linkedDenot = rootDenot.linkedClass.denot match {
val linkedDenot = rootDenot.scalacLinkedClass.denot match {
case d: ClassDenotation => d
case d =>
// this can happen if the companion if shadowed by a val or type
Expand Down
9 changes: 9 additions & 0 deletions src/dotty/tools/dotc/core/Symbols.scala
Original file line number Diff line number Diff line change
Expand Up @@ -161,6 +161,15 @@ trait Symbols { this: Context =>
owner.thisType, modcls, parents, decls, TermRef.withSymAndName(owner.thisType, module, name)),
privateWithin, coord, assocFile)

def synthesizeCompanionMethod(name: Name, target: SymDenotation, owner: SymDenotation)(implicit ctx: Context) =
if(owner.exists && target.exists && !owner.isAbsent && !target.isAbsent) {
val existing = owner.unforcedDecls.lookup(name)

existing.orElse{
ctx.newSymbol(owner.symbol, name, Flags.Synthetic | Flags.Private, ExprType(target.typeRef))
}
} else NoSymbol

/** Create a package symbol with associated package class
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

  • Add a doc comment.
  • Formatting: if (.
  • It's more common to pass owner as a symbol
  • "ret" is not descriptive.
  • In this case, I do not think the named parameters add anything. All arguments make it clear by their names already what parameter they are for.

I'd reformulate as follows:

def synthesizeCompanionMethod(target: Symbol, owner: Symbol)(implicit ctx: Context) = 
  if (target.exists && owner.exists) {
    val name = if (target is Module) nme.COMPANION_MODULE_METHOD else nme.COMPANION_CLASS_METHOD
    ctx.newSymbol(owner, name, Flags.Synthetic | Flags.Private, ExprType(target.typeRef)
  }
  else NoSymbol

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Defining method name based on flags doesn't work here: in order to not force type you will need flagsUNSAFE, and tests suggest that Module could still be unset.

* from its non-info fields and a lazy type for loading the package's members.
*/
Expand Down
11 changes: 11 additions & 0 deletions src/dotty/tools/dotc/core/pickling/ClassfileParser.scala
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,10 @@ class ClassfileParser(
if (c != classRoot.symbol) mismatchError(c)
}

if(classRoot.symbol.id == 4812) {
println("bar")
}

addEnclosingTParams()

if (unpickleOrParseInnerClasses()) return
Expand Down Expand Up @@ -130,10 +134,17 @@ class ClassfileParser(
for (i <- 0 until in.nextChar) parseMember(method = true)
classInfo = parseAttributes(classRoot.symbol, classInfo)
if (isAnnotation) addAnnotationConstructor(classInfo)

val companionClassMethod = ctx.synthesizeCompanionMethod(nme.COMPANION_CLASS_METHOD, classRoot, moduleRoot)
if (companionClassMethod.exists) companionClassMethod.entered
val companionModuleMethod = ctx.synthesizeCompanionMethod(nme.COMPANION_MODULE_METHOD, moduleRoot, classRoot)
if (companionModuleMethod.exists) companionModuleMethod.entered

setClassInfo(classRoot, classInfo)
setClassInfo(moduleRoot, staticInfo)
}


/** Add type parameters of enclosing classes */
def addEnclosingTParams()(implicit ctx: Context): Unit = {
var sym = classRoot.owner
Expand Down
24 changes: 23 additions & 1 deletion src/dotty/tools/dotc/core/pickling/UnPickler.scala
Original file line number Diff line number Diff line change
Expand Up @@ -118,6 +118,24 @@ object UnPickler {
denot.owner.thisType select denot.sourceModule
else selfInfo
if (!(denot.flagsUNSAFE is JavaModule)) ensureConstructor(denot.symbol.asClass, decls)

val scalacCompanion = denot.classSymbol.scalacLinkedClass

def registerCompanionPair(module: Symbol, claz: Symbol) = {
val companionClassMethod = ctx.synthesizeCompanionMethod(nme.COMPANION_CLASS_METHOD, claz, module)
if (companionClassMethod.exists)
companionClassMethod.entered
val companionModuleMethod = ctx.synthesizeCompanionMethod(nme.COMPANION_MODULE_METHOD, module, claz)
if (companionModuleMethod.exists)
companionModuleMethod.entered
}

if (denot.flagsUNSAFE is Module) {
registerCompanionPair(denot.classSymbol, scalacCompanion)
} else {
registerCompanionPair(scalacCompanion, denot.classSymbol)
}

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's a pity that we still need scalacLinkedClass here, because it means we cannot get rid of the associated complexity of isCoDefinedWith. I would much prefer if we got the linked class directly from the unpickling info, instead of looking at
the scope.

denot.info = ClassInfo(denot.owner.thisType, denot.classSymbol, parentRefs, decls, ost)
}
}
Expand Down Expand Up @@ -483,7 +501,11 @@ class UnPickler(bytes: Array[Byte], classRoot: ClassDenotation, moduleClassRoot:
if (isModuleRoot) {
moduleRoot setFlag flags
moduleRoot.symbol
} else ctx.newSymbol(owner, name.asTermName, flags, localMemberUnpickler, coord = start)
} else ctx.newSymbol(owner, name.asTermName, flags,
new LocalUnpickler() withModuleClass(implicit ctx =>
owner.info.decls.lookup(name.moduleClassName)
.suchThat(_ is Module).symbol)
, coord = start)
case _ =>
errorBadSignature("bad symbol tag: " + tag)
})
Expand Down
8 changes: 6 additions & 2 deletions src/dotty/tools/dotc/transform/FirstTransform.scala
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ import DenotTransformers._
import typer.Checking
import Names.Name
import NameOps._
import StdNames._


/** The first tree transform
Expand Down Expand Up @@ -72,10 +73,13 @@ class FirstTransform extends MiniPhaseTransform with IdentityDenotTransformer wi
case Nil => Nil
}

def newCompanion(name: TermName): Thicket = {
def newCompanion(name: TermName, forClass: Symbol): Thicket = {
val modul = ctx.newCompleteModuleSymbol(ctx.owner, name, Synthetic, Synthetic,
defn.ObjectClass.typeRef :: Nil, Scopes.newScope)
val mc = modul.moduleClass
if (ctx.owner.isClass) modul.enteredAfter(thisTransformer)
ctx.synthesizeCompanionMethod(nme.COMPANION_CLASS_METHOD, forClass, mc).enteredAfter(thisTransformer)
ctx.synthesizeCompanionMethod(nme.COMPANION_MODULE_METHOD, mc, forClass).enteredAfter(thisTransformer)
ModuleDef(modul, Nil)
}

Expand All @@ -89,7 +93,7 @@ class FirstTransform extends MiniPhaseTransform with IdentityDenotTransformer wi
false
}
val uniqueName = if (nameClash) objName.avoidClashName else objName
Thicket(stat :: newCompanion(uniqueName).trees)
Thicket(stat :: newCompanion(uniqueName, stat.symbol).trees)
case stat => stat
}

Expand Down
8 changes: 6 additions & 2 deletions src/dotty/tools/dotc/transform/PatternMatcher.scala
Original file line number Diff line number Diff line change
Expand Up @@ -643,8 +643,12 @@ class PatternMatcher extends MiniPhaseTransform with DenotTransformer {thisTrans
// val outer = expectedTp.typeSymbol.newMethod(vpmName.outer, newFlags = SYNTHETIC | ARTIFACT) setInfo expectedTp.prefix

val expectedClass = expectedTp.dealias.classSymbol.asClass
ExplicitOuter.ensureOuterAccessors(expectedClass)
codegen._asInstanceOf(testedBinder, expectedTp).select(ExplicitOuter.outerAccessor(expectedClass)).select(defn.Object_eq).appliedTo(expectedOuter)
val test = codegen._asInstanceOf(testedBinder, expectedTp)
val outerAccessorTested = ctx.atPhase(ctx.explicitOuterPhase.next) { implicit ctx =>
ExplicitOuter.ensureOuterAccessors(expectedClass)
test.select(ExplicitOuter.outerAccessor(expectedClass)).select(defn.Object_eq).appliedTo(expectedOuter)
}
outerAccessorTested
}
}

Expand Down
3 changes: 2 additions & 1 deletion src/dotty/tools/dotc/typer/Checking.scala
Original file line number Diff line number Diff line change
Expand Up @@ -232,7 +232,8 @@ trait Checking {

/** Check that type `tp` is stable. */
def checkStable(tp: Type, pos: Position)(implicit ctx: Context): Unit =
if (!tp.isStable) ctx.error(d"$tp is not stable", pos)
if (!tp.isStable)
ctx.error(d"$tp is not stable", pos)

/** Check that type `tp` is a legal prefix for '#'.
* @return The type itself
Expand Down
Loading