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 8 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
12 changes: 8 additions & 4 deletions src/dotty/tools/dotc/ast/Desugar.scala
Original file line number Diff line number Diff line change
Expand Up @@ -361,9 +361,11 @@ object desugar {
}
companionDefs(parent, applyMeths ::: unapplyMeth :: defaultGetters)
}
else if (defaultGetters.nonEmpty)
companionDefs(anyRef, defaultGetters)
else Nil
else {
Copy link
Contributor

Choose a reason for hiding this comment

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

Why the change in formatting? I liked the old one better.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

This code was rewritten, it used to have more complicated logic between the commits. I have no problem changing it back.

if (defaultGetters.nonEmpty)
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] =
Expand Down Expand Up @@ -409,6 +411,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 +440,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
6 changes: 5 additions & 1 deletion src/dotty/tools/dotc/core/Denotations.scala
Original file line number Diff line number Diff line change
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 = "compaion$class"
Copy link
Contributor

Choose a reason for hiding this comment

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

Typo, should be "companion$class"


// value types (and AnyRef) are all used as terms as well
// as (at least) arguments to the @specialize annotation.
Expand Down
30 changes: 25 additions & 5 deletions src/dotty/tools/dotc/core/SymDenotations.scala
Original file line number Diff line number Diff line change
Expand Up @@ -773,11 +773,31 @@ object SymDenotations {
companionNamed(effectiveName.moduleClassName).sourceModule

/** 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 {
/*
val scalac = companionNamed(effectiveName.toTypeName)

Copy link
Contributor

Choose a reason for hiding this comment

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

Is the commented out code still needed? If not, delete as well as the enclosing {...}

if (scalac.exists) {
println(s"scalac returned companion class for $this to be $scalac")
}
*/
NoSymbol
}
}

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


/** Find companion class symbol with given name, or NoSymbol if none exists.
* Three alternative strategies:
Expand Down
2 changes: 1 addition & 1 deletion src/dotty/tools/dotc/core/SymbolLoaders.scala
Original file line number Diff line number Diff line change
Expand Up @@ -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: TermName, ret: SymDenotation, owner: SymDenotation)(implicit ctx: Context) = {
if(owner.exists && ret.exists) ctx.newSymbol(
owner = owner.symbol,
name = name,
flags = Flags.Synthetic | Flags.Private,
info = ExprType(ret.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
5 changes: 5 additions & 0 deletions src/dotty/tools/dotc/core/pickling/ClassfileParser.scala
Original file line number Diff line number Diff line change
Expand Up @@ -130,10 +130,15 @@ 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


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


/** Add type parameters of enclosing classes */
def addEnclosingTParams()(implicit ctx: Context): Unit = {
var sym = classRoot.owner
Expand Down
7 changes: 7 additions & 0 deletions src/dotty/tools/dotc/core/pickling/UnPickler.scala
Original file line number Diff line number Diff line change
Expand Up @@ -118,6 +118,13 @@ object UnPickler {
denot.owner.thisType select denot.sourceModule
else selfInfo
if (!(denot.flagsUNSAFE is JavaModule)) ensureConstructor(denot.symbol.asClass, decls)
if (denot.flagsUNSAFE is Module) {
val scalacCompanion = denot.classSymbol.scalacLinkedClass
val companionClassMethod = ctx.synthesizeCompanionMethod(nme.COMPANION_CLASS_METHOD, scalacCompanion, denot.classSymbol)
if (companionClassMethod.exists)
companionClassMethod.entered
}

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
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
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
42 changes: 37 additions & 5 deletions src/dotty/tools/dotc/typer/Namer.scala
Original file line number Diff line number Diff line change
Expand Up @@ -395,32 +395,64 @@ class Namer { typer: Typer =>
/** Create top-level symbols for statements and enter them into symbol table */
def index(stats: List[Tree])(implicit ctx: Context): Context = {

val classDef = mutable.Map[TypeName, TypeDef]()
val moduleDef = mutable.Map[TypeName, TypeDef]()

/** Merge the definitions of a synthetic companion generated by a case class
* and the real companion, if both exist.
*/
def mergeCompanionDefs() = {
val classDef = mutable.Map[TypeName, TypeDef]()
for (cdef @ TypeDef(name, _) <- stats)
if (cdef.isClassDef) classDef(name) = cdef
for (mdef @ ModuleDef(name, _) <- stats)
if (cdef.isClassDef) {
classDef(name) = cdef
cdef.attachmentOrElse(ExpandedTree, cdef) match {
case Thicket(cls :: mval :: (mcls @ TypeDef(_, _: Template)) :: crest) =>
moduleDef(name) = mcls
case _ =>
}
}
for (mdef @ ModuleDef(name, _) <- stats if !mdef.mods.is(Flags.Package)) {
val typName = name.toTypeName
val Thicket(vdef :: (mcls @ TypeDef(_, impl: Template)) :: Nil) = mdef.attachment(ExpandedTree)
moduleDef(typName) = mcls
classDef get name.toTypeName match {
case Some(cdef) =>
val Thicket(vdef :: (mcls @ TypeDef(_, impl: Template)) :: Nil) = mdef.attachment(ExpandedTree)
cdef.attachmentOrElse(ExpandedTree, cdef) match {
case Thicket(cls :: mval :: TypeDef(_, compimpl: Template) :: crest) =>
val mcls1 = cpy.TypeDef(mcls)(
rhs = cpy.Template(impl)(body = compimpl.body ++ impl.body))
mdef.putAttachment(ExpandedTree, Thicket(vdef :: mcls1 :: Nil))
moduleDef(typName) = mcls1
cdef.putAttachment(ExpandedTree, Thicket(cls :: crest))
case _ =>
}
case none =>
}
}
}
Copy link
Contributor

Choose a reason for hiding this comment

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

I think this is a bit on the complex side, in particuar I do not like the repetition of the complicated Thicket pattern matches. Can we experiment with having 4 Maps:

classDef, moduleDef as before

 val syntheticCompanionDef: Map[TypeName, TypeDef]  // a map from case classes to synthetic companions
 val coreCaseClassDef: Map[TypeName, List[Tree]] // a map from case classes to the definitions w/o synthetic companions

Using these we could streamline the traversal of ModuleDefs.

See what I mean?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Should those maps be directly updated by desugar or you'll still use attachments?
If second, you'll still need 2 pattern matches, one over classes, and one over modules to populate maps.
And then you'll need an additional pattern match to update the annotations, so you still have three pattern matches. Am I missing something?


def createLinks(classTree: TypeDef, moduleTree: TypeDef)(implicit ctx: Context) = {
val claz = ctx.denotNamed(classTree.name.encode).symbol
val modl = ctx.denotNamed(moduleTree.name.encode).symbol
ctx.synthesizeCompanionMethod(nme.COMPANION_CLASS_METHOD, claz, modl).entered
ctx.synthesizeCompanionMethod(nme.COMPANION_MODULE_METHOD, modl, claz).entered
}

def createCompanionLinks(implicit ctx: Context): Unit = {
for (cdef @ TypeDef(name, _) <- classDef.values) {
moduleDef.getOrElse(name, EmptyTree) match {
case t: TypeDef =>
createLinks(cdef, t)
case EmptyTree =>
}
}
}

stats foreach expand
mergeCompanionDefs()
(ctx /: stats) ((ctx, stat) => indexExpanded(stat)(ctx))
val ctxWithStats = (ctx /: stats) ((ctx, stat) => indexExpanded(stat)(ctx))
createCompanionLinks(ctxWithStats)
ctxWithStats
}

/** The completer of a symbol defined by a member def or import (except ClassSymbols) */
Expand Down
3 changes: 2 additions & 1 deletion src/dotty/tools/dotc/typer/RefChecks.scala
Original file line number Diff line number Diff line change
Expand Up @@ -395,7 +395,8 @@ object RefChecks {

for (member <- missing) {
val memberSym = member.symbol
def undefined(msg: String) = abstractClassError(false, member.showDcl + " is not defined" + msg)
def undefined(msg: String) =
abstractClassError(false, member.showDcl + " is not defined" + msg)
val underlying = memberSym.underlyingSymbol

// Give a specific error message for abstract vars based on why it fails:
Expand Down
2 changes: 1 addition & 1 deletion src/dotty/tools/dotc/typer/Typer.scala
Original file line number Diff line number Diff line change
Expand Up @@ -963,7 +963,7 @@ class Typer extends Namer with TypeAssigner with Applications with Implicits wit
val packageContext =
if (pkg is Package) ctx.fresh.setOwner(pkg.moduleClass).setTree(tree)
else {
ctx.error(d"$pkg is not a packge", tree.pos)
ctx.error(d"$pkg is not a package", tree.pos)
ctx
}
val stats1 = typedStats(tree.stats, pkg.moduleClass)(packageContext)
Expand Down
2 changes: 1 addition & 1 deletion test/dotc/tests.scala
Original file line number Diff line number Diff line change
Expand Up @@ -149,7 +149,7 @@ class tests extends CompilerTest {

@Test def dotc_printing = compileDir(dotcDir + "tools/dotc/printing")

@Test def dotc_reporting = compileDir(dotcDir + "tools/dotc/reporting", twice)
@Test def dotc_reporting = compileDir(dotcDir + "tools/dotc/reporting")
Copy link
Contributor

Choose a reason for hiding this comment

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

Why was the twice removed here?


@Test def dotc_typer = compileDir(dotcDir + "tools/dotc/typer", failedOther)
// error: error while loading Checking$$anon$2$,
Expand Down