Skip to content

Change scheme to implement creator applications #10784

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 10 commits into from
Dec 16, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
3 changes: 2 additions & 1 deletion compiler/src/dotty/tools/backend/jvm/BytecodeWriters.scala
Original file line number Diff line number Diff line change
Expand Up @@ -81,7 +81,8 @@ trait BytecodeWriters {
trait AsmpBytecodeWriter extends BytecodeWriter {
import scala.tools.asm

private val baseDir = Directory(None.get).createDirectory() // FIXME missing directoy
private val baseDir = new Directory(None.get).createDirectory() // FIXME missing directoy
// new needed here since resolution of user-defined `apply` methods is ambiguous, and we want the constructor.

private def emitAsmp(jclassBytes: Array[Byte], asmpFile: dotty.tools.io.File): Unit = {
val pw = asmpFile.printWriter()
Expand Down
2 changes: 1 addition & 1 deletion compiler/src/dotty/tools/dotc/ast/MainProxies.scala
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ object MainProxies {
}

private def checkNoShadowing(mainFun: Symbol)(using Context) =
val cls = ctx.typer.findRef(mainFun.name.toTypeName, WildcardType, EmptyFlags, mainFun).typeSymbol
val cls = ctx.typer.findRef(mainFun.name.toTypeName, WildcardType, EmptyFlags, EmptyFlags, mainFun).typeSymbol
if cls.exists && cls.owner != ctx.owner then
report.warning(
i"""The class `${ctx.printer.fullNameString(mainFun)}` generated from `@main` will shadow the existing ${cls.showLocated}.
Expand Down
8 changes: 5 additions & 3 deletions compiler/src/dotty/tools/dotc/ast/Trees.scala
Original file line number Diff line number Diff line change
Expand Up @@ -1013,13 +1013,15 @@ object Trees {

@sharable val EmptyTree: Thicket = genericEmptyTree
@sharable val EmptyValDef: ValDef = genericEmptyValDef
@sharable val ContextualEmptyTree: Thicket = EmptyTree() // an empty tree marking a contextual closure
@sharable val ContextualEmptyTree: Thicket = new EmptyTree() // an empty tree marking a contextual closure

// ----- Auxiliary creation methods ------------------

def Thicket(): Thicket = EmptyTree
def Thicket(x1: Tree, x2: Tree)(implicit src: SourceFile): Thicket = Thicket(x1 :: x2 :: Nil)
def Thicket(x1: Tree, x2: Tree, x3: Tree)(implicit src: SourceFile): Thicket = Thicket(x1 :: x2 :: x3 :: Nil)
def Thicket(x1: Tree, x2: Tree)(implicit src: SourceFile): Thicket = new Thicket(x1 :: x2 :: Nil)
def Thicket(x1: Tree, x2: Tree, x3: Tree)(implicit src: SourceFile): Thicket = new Thicket(x1 :: x2 :: x3 :: Nil)
def Thicket(xs: List[Tree])(implicit src: SourceFile) = new Thicket(xs)

def flatTree(xs: List[Tree])(implicit src: SourceFile): Tree = flatten(xs) match {
case x :: Nil => x
case ys => Thicket(ys)
Expand Down
29 changes: 15 additions & 14 deletions compiler/src/dotty/tools/dotc/core/ContextOps.scala
Original file line number Diff line number Diff line change
Expand Up @@ -25,21 +25,22 @@ object ContextOps:

/** The denotation with the given `name` and all `required` flags in current context
*/
def denotNamed(name: Name, required: FlagSet = EmptyFlags): Denotation = inContext(ctx) {
if (ctx.owner.isClass)
if (ctx.outer.owner == ctx.owner) { // inner class scope; check whether we are referring to self
if (ctx.scope.size == 1) {
val elem = ctx.scope.lastEntry
if (elem.name == name) return elem.sym.denot // return self
def denotNamed(name: Name, required: FlagSet = EmptyFlags, excluded: FlagSet = EmptyFlags): Denotation =
inContext(ctx) {
if (ctx.owner.isClass)
if (ctx.outer.owner == ctx.owner) { // inner class scope; check whether we are referring to self
if (ctx.scope.size == 1) {
val elem = ctx.scope.lastEntry
if (elem.name == name) return elem.sym.denot // return self
}
val pre = ctx.owner.thisType
pre.findMember(name, pre, required, excluded)
}
val pre = ctx.owner.thisType
pre.findMember(name, pre, required, EmptyFlags)
}
else // we are in the outermost context belonging to a class; self is invisible here. See inClassContext.
ctx.owner.findMember(name, ctx.owner.thisType, required, EmptyFlags)
else
ctx.scope.denotsNamed(name).filterWithFlags(required, EmptyFlags).toDenot(NoPrefix)
}
else // we are in the outermost context belonging to a class; self is invisible here. See inClassContext.
ctx.owner.findMember(name, ctx.owner.thisType, required, excluded)
else
ctx.scope.denotsNamed(name).filterWithFlags(required, excluded).toDenot(NoPrefix)
}

/** A fresh local context with given tree and owner.
* Owner might not exist (can happen for self valdefs), in which case
Expand Down
12 changes: 4 additions & 8 deletions compiler/src/dotty/tools/dotc/core/Definitions.scala
Original file line number Diff line number Diff line change
Expand Up @@ -300,14 +300,10 @@ class Definitions {
cls.info = ClassInfo(cls.owner.thisType, cls, List(AnyType, MatchableType), newScope)
cls.setFlag(NoInits | JavaDefined)

// The companion object doesn't really exist, so it needs to be marked as
// absent. Here we need to set it before completing attempt to load Object's
// classfile, which causes issue #1648.
val companion = JavaLangPackageVal.info.decl(nme.Object).symbol
companion.moduleClass.markAbsent()
companion.markAbsent()

completeClass(cls)
ensureConstructor(cls, cls.denot.asClass, EmptyScope)
val companion = JavaLangPackageVal.info.decl(nme.Object).symbol.asTerm
NamerOps.makeConstructorCompanion(companion, cls)
cls
}
def ObjectType: TypeRef = ObjectClass.typeRef

Expand Down
17 changes: 12 additions & 5 deletions compiler/src/dotty/tools/dotc/core/Flags.scala
Original file line number Diff line number Diff line change
Expand Up @@ -378,7 +378,7 @@ object Flags {
val (Touched @ _, _, _) = newFlags(50, "<touched>")

/** Class has been lifted out to package level, local value has been lifted out to class level */
val (Lifted @ _, _, _) = newFlags(51, "<lifted>")
val (Lifted @ _, _, _) = newFlags(51, "<lifted>") // only used from lambda-lift (could be merged with ConstructorProxy)

/** Term member has been mixed in */
val (MixedIn @ _, _, _) = newFlags(52, "<mixedin>")
Expand Down Expand Up @@ -420,6 +420,9 @@ object Flags {
/** A denotation that is valid in all run-ids */
val (Permanent @ _, _, _) = newFlags(61, "<permanent>")

/** Symbol is a constructor proxy (either companion, or apply method) */
val (ConstructorProxy @ _, _, _) = newFlags(62, "<constructor proxy>") // (could be merged with Lifted)

// --------- Combined Flag Sets and Conjunctions ----------------------

/** All possible flags */
Expand Down Expand Up @@ -455,15 +458,16 @@ object Flags {
Scala2SpecialFlags, MutableOrOpen, Opaque, Touched, JavaStatic,
OuterOrCovariant, LabelOrContravariant, CaseAccessor,
Extension, NonMember, Implicit, Given, Permanent, Synthetic,
SuperParamAliasOrScala2x, Inline, Macro)
SuperParamAliasOrScala2x, Inline, Macro, ConstructorProxy)

/** Flags that are not (re)set when completing the denotation, or, if symbol is
* a top-level class or object, when completing the denotation once the class
* file defining the symbol is loaded (which is generally before the denotation
* is completed)
*/
val AfterLoadFlags: FlagSet = commonFlags(
FromStartFlags, AccessFlags, Final, AccessorOrSealed, LazyOrTrait, SelfName, JavaDefined, Transparent)
FromStartFlags, AccessFlags, Final, AccessorOrSealed,
Abstract, LazyOrTrait, SelfName, JavaDefined, Transparent)

/** A value that's unstable unless complemented with a Stable flag */
val UnstableValueFlags: FlagSet = Mutable | Method
Expand Down Expand Up @@ -508,7 +512,7 @@ object Flags {
val RetainedModuleValAndClassFlags: FlagSet =
AccessFlags | Package | Case |
Synthetic | JavaDefined | JavaStatic | Artifact |
Lifted | MixedIn | Specialized
Lifted | MixedIn | Specialized | ConstructorProxy

/** Flags that can apply to a module val */
val RetainedModuleValFlags: FlagSet = RetainedModuleValAndClassFlags |
Expand Down Expand Up @@ -539,7 +543,10 @@ object Flags {
val EnumCase: FlagSet = Case | Enum
val CovariantLocal: FlagSet = Covariant | Local // A covariant type parameter
val ContravariantLocal: FlagSet = Contravariant | Local // A contravariant type parameter
val EffectivelyErased = ConstructorProxy | Erased
val ConstructorProxyModule: FlagSet = ConstructorProxy | Module
val DefaultParameter: FlagSet = HasDefault | Param // A Scala 2x default parameter
val DeferredInline: FlagSet = Deferred | Inline
val DeferredOrLazy: FlagSet = Deferred | Lazy
val DeferredOrLazyOrMethod: FlagSet = Deferred | Lazy | Method
val DeferredOrTermParamOrAccessor: FlagSet = Deferred | ParamAccessor | TermParam // term symbols without right-hand sides
Expand All @@ -548,7 +555,7 @@ object Flags {
val FinalOrInline: FlagSet = Final | Inline
val FinalOrModuleClass: FlagSet = Final | ModuleClass // A module class or a final class
val EffectivelyFinalFlags: FlagSet = Final | Private
val ExcludedForwarder: Flags.FlagSet = Specialized | Lifted | Protected | JavaStatic | Private | Macro
val ExcludedForwarder: Flags.FlagSet = Specialized | Lifted | Protected | JavaStatic | Private | Macro | ConstructorProxy
val FinalOrSealed: FlagSet = Final | Sealed
val GivenOrImplicit: FlagSet = Given | Implicit
val GivenOrImplicitVal: FlagSet = GivenOrImplicit.toTermFlags
Expand Down
102 changes: 99 additions & 3 deletions compiler/src/dotty/tools/dotc/core/NamerOps.scala
Original file line number Diff line number Diff line change
@@ -1,9 +1,12 @@
package dotty.tools.dotc
package dotty.tools
package dotc
package core

import Contexts._, Symbols._, Types._, Flags._, Scopes._, Decorators._, NameOps._
import Contexts._, Symbols._, Types._, Flags._, Scopes._, Decorators._, Names._, NameOps._
import Denotations._
import SymDenotations.LazyType, Names.Name, StdNames.nme
import SymDenotations.{LazyType, SymDenotation}, StdNames.nme
import config.Config
import ast.untpd

/** Operations that are shared between Namer and TreeUnpickler */
object NamerOps:
Expand Down Expand Up @@ -57,4 +60,97 @@ object NamerOps:
else NoSymbol.assertingErrorsReported(s"no companion $name in $scope")
}

/** If a class has one of these flags, it does not get a constructor companion */
private val NoConstructorProxyNeededFlags = Abstract | Trait | Case | Synthetic | Module

/** The flags of a constructor companion */
private val ConstructorCompanionFlags = Synthetic | ConstructorProxy

/** The flags of an `apply` method that serves as a constructor proxy */
val ApplyProxyFlags = Synthetic | ConstructorProxy | Inline | Method

/** Does symbol `cls` need constructor proxies to be generated? */
def needsConstructorProxies(cls: Symbol)(using Context): Boolean =
cls.isClass
&& !cls.flagsUNSAFE.isOneOf(NoConstructorProxyNeededFlags)
&& !cls.isAnonymousClass

/** The completer of a constructor proxy apply method */
class ApplyProxyCompleter(constr: Symbol)(using Context) extends LazyType:
def complete(denot: SymDenotation)(using Context): Unit =
denot.info = constr.info

/** Add constructor proxy apply methods to `scope`. Proxies are for constructors
* in `cls` and they reside in `modcls`.
*/
def addConstructorApplies(scope: MutableScope, cls: ClassSymbol, modcls: ClassSymbol)(using Context): scope.type =
def proxy(constr: Symbol): Symbol =
newSymbol(
modcls, nme.apply, ApplyProxyFlags | (constr.flagsUNSAFE & AccessFlags),
ApplyProxyCompleter(constr), coord = constr.coord)
for dcl <- cls.info.decls do
if dcl.isConstructor then scope.enter(proxy(dcl))
scope
end addConstructorApplies

/** The completer of a constructor companion for class `cls`, where
* `modul` is the companion symbol and `modcls` is its class.
*/
def constructorCompanionCompleter(cls: ClassSymbol)(
modul: TermSymbol, modcls: ClassSymbol)(using Context): LazyType =
new LazyType with SymbolLoaders.SecondCompleter {
def complete(denot: SymDenotation)(using Context): Unit =
val prefix = modcls.owner.thisType
denot.info = ClassInfo(
prefix, modcls, defn.AnyType :: Nil,
addConstructorApplies(newScope, cls, modcls), TermRef(prefix, modul))
}.withSourceModule(modul)

/** A new symbol that is the constructor companion for class `cls` */
def constructorCompanion(cls: ClassSymbol)(using Context): TermSymbol =
val companion = newModuleSymbol(
cls.owner, cls.name.toTermName,
ConstructorCompanionFlags, ConstructorCompanionFlags,
constructorCompanionCompleter(cls),
coord = cls.coord,
assocFile = cls.assocFile)
companion.moduleClass.registerCompanion(cls)
cls.registerCompanion(companion.moduleClass)
companion

/** Add all necesssary constructor proxy symbols for members of class `cls`. This means:
*
* - if a member is a class that needs a constructor companion, add one,
* provided no member with the same name exists.
* - if `cls` is a companion object of a class that needs a constructor companion,
* and `cls` does not already define or inherit an `apply` method,
* add `apply` methods for all constructors of the companion class.
*/
def addConstructorProxies(cls: ClassSymbol)(using Context): Unit =

def memberExists(cls: ClassSymbol, name: TermName): Boolean =
cls.baseClasses.exists(_.info.decls.lookupEntry(name) != null)
for mbr <- cls.info.decls do
if needsConstructorProxies(mbr)
&& !mbr.asClass.unforcedRegisteredCompanion.exists
&& !memberExists(cls, mbr.name.toTermName)
then
constructorCompanion(mbr.asClass).entered

if cls.is(Module)
&& needsConstructorProxies(cls.linkedClass)
&& !memberExists(cls, nme.apply)
then
addConstructorApplies(cls.info.decls.openForMutations, cls.linkedClass.asClass, cls)
end addConstructorProxies

/** Turn `modul` into a constructor companion for class `cls` */
def makeConstructorCompanion(modul: TermSymbol, cls: ClassSymbol)(using Context): Unit =
val modcls = modul.moduleClass.asClass
modul.setFlag(ConstructorCompanionFlags)
modcls.setFlag(ConstructorCompanionFlags)
modcls.info = constructorCompanionCompleter(cls)(modul, modcls)
cls.registeredCompanion = modcls
modcls.registeredCompanion = cls

end NamerOps
16 changes: 14 additions & 2 deletions compiler/src/dotty/tools/dotc/core/SymDenotations.scala
Original file line number Diff line number Diff line change
Expand Up @@ -951,7 +951,8 @@ object SymDenotations {

/** An erased value or an erased inline method or field */
def isEffectivelyErased(using Context): Boolean =
is(Erased) || is(Inline) && !isRetainedInline && !hasAnnotation(defn.ScalaStaticAnnot)
isOneOf(EffectivelyErased)
|| is(Inline) && !isRetainedInline && !hasAnnotation(defn.ScalaStaticAnnot)

/** ()T and => T types should be treated as equivalent for this symbol.
* Note: For the moment, we treat Scala-2 compiled symbols as loose matching,
Expand Down Expand Up @@ -1609,7 +1610,7 @@ object SymDenotations {

private def baseTypeCache(using Context): BaseTypeMap = {
if !currentHasSameBaseTypesAs(myBaseTypeCachePeriod) then
myBaseTypeCache = BaseTypeMap()
myBaseTypeCache = new BaseTypeMap()
myBaseTypeCachePeriod = ctx.period
myBaseTypeCache
}
Expand Down Expand Up @@ -2165,6 +2166,8 @@ object SymDenotations {
if (companion.isClass && !isAbsent(canForce = false) && !companion.isAbsent(canForce = false))
myCompanion = companion

private[core] def unforcedRegisteredCompanion: Symbol = myCompanion

override def registeredCompanion(using Context) =
if !myCompanion.exists then
ensureCompleted()
Expand Down Expand Up @@ -2256,6 +2259,15 @@ object SymDenotations {
case d: DenotUnion => dropStale(d)
case d => d

/** Filter symbols making up a DenotUnion to remove alternatives from stale classfiles.
* This proceeds as follow:
*
* - prefer alternatives that are currently compiled over ones that have been compiled before.
* - if no alternative is compiled now, and they all come from the same file, keep all of them
* - if no alternative is compiled now, and they come from different files, keep the
* ones from the youngest file, but issue a warning that one of the class files
* should be removed from the classpath.
*/
def dropStale(multi: DenotUnion): PreDenotation =
val compiledNow = multi.filterWithPredicate(d =>
d.symbol.isDefinedInCurrentRun || d.symbol.associatedFile == null
Expand Down
18 changes: 12 additions & 6 deletions compiler/src/dotty/tools/dotc/core/SymbolLoaders.scala
Original file line number Diff line number Diff line change
Expand Up @@ -359,13 +359,19 @@ abstract class SymbolLoader extends LazyType { self =>
throw ex
}
finally {
def postProcess(denot: SymDenotation) =
if (!denot.isCompleted &&
!denot.completer.isInstanceOf[SymbolLoaders.SecondCompleter])
denot.markAbsent()
postProcess(root)
def postProcess(denot: SymDenotation, other: Symbol) =
if !denot.isCompleted &&
!denot.completer.isInstanceOf[SymbolLoaders.SecondCompleter] then
if denot.is(ModuleClass) && NamerOps.needsConstructorProxies(other) then
NamerOps.makeConstructorCompanion(denot.sourceModule.asTerm, other.asClass)
denot.resetFlag(Touched)
else
denot.markAbsent()

val other = if root.isRoot then NoSymbol else root.scalacLinkedClass
postProcess(root, other)
if (!root.isRoot)
postProcess(root.scalacLinkedClass.denot)
postProcess(other, root.symbol)
}
}

Expand Down
15 changes: 12 additions & 3 deletions compiler/src/dotty/tools/dotc/core/Symbols.scala
Original file line number Diff line number Diff line change
Expand Up @@ -54,14 +54,17 @@ object Symbols {
//assert(id != 723)

def coord: Coord = myCoord

/** Set the coordinate of this class, this is only useful when the coordinate is
* not known at symbol creation. This is the case for root symbols
* unpickled from TASTY.
*
* @pre coord == NoCoord
*/
private[core] def coord_=(c: Coord): Unit = {
assert(myCoord == NoCoord)
// assert(myCoord == NoCoord)
// This assertion fails for CommentPickling test.
// TODO: figure out what's wrong in the setup of CommentPicklingTest and re-enable assertion.
myCoord = c
}

Expand Down Expand Up @@ -135,8 +138,12 @@ object Symbols {
/** Does this symbol come from a currently compiled source file? */
final def isDefinedInCurrentRun(using Context): Boolean =
span.exists && defRunId == ctx.runId && {
val file = associatedFile
file != null && ctx.run.files.contains(file)
try
val file = associatedFile
file != null && ctx.run.files.contains(file)
catch case ex: StaleSymbol =>
// can happen for constructor proxy companions. Test case is pos-macros/i9484.
false
}

/** Is symbol valid in current run? */
Expand Down Expand Up @@ -496,6 +503,8 @@ object Symbols {
def currentClass(using Context): ClassSymbol = ctx.owner.enclosingClass.asClass

type MutableSymbolMap[T] = EqHashMap[Symbol, T]
def MutableSymbolMap[T](): EqHashMap[Symbol, T] = EqHashMap[Symbol, T]()
def MutableSymbolMap[T](initialCapacity: Int): EqHashMap[Symbol, T] = EqHashMap[Symbol, T](initialCapacity)

// ---- Factory methods for symbol creation ----------------------
//
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -194,6 +194,7 @@ class ClassfileParser(
addAnnotationConstructor(classInfo.asInstanceOf[TempClassInfoType])

setClassInfo(classRoot, classInfo, fromScala2 = false)
NamerOps.addConstructorProxies(moduleRoot.classSymbol)
}
else if (result == Some(NoEmbedded))
for (sym <- List(moduleRoot.sourceModule, moduleRoot.symbol, classRoot.symbol)) {
Expand Down
Loading