Skip to content

Commit 3e471ab

Browse files
authored
Merge pull request #10784 from dotty-staging/change-creators
Change scheme to implement creator applications
2 parents cbe50b3 + 6a93525 commit 3e471ab

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

50 files changed

+548
-353
lines changed

compiler/src/dotty/tools/backend/jvm/BytecodeWriters.scala

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -81,7 +81,8 @@ trait BytecodeWriters {
8181
trait AsmpBytecodeWriter extends BytecodeWriter {
8282
import scala.tools.asm
8383

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

8687
private def emitAsmp(jclassBytes: Array[Byte], asmpFile: dotty.tools.io.File): Unit = {
8788
val pw = asmpFile.printWriter()

compiler/src/dotty/tools/dotc/ast/MainProxies.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,7 @@ object MainProxies {
4040
}
4141

4242
private def checkNoShadowing(mainFun: Symbol)(using Context) =
43-
val cls = ctx.typer.findRef(mainFun.name.toTypeName, WildcardType, EmptyFlags, mainFun).typeSymbol
43+
val cls = ctx.typer.findRef(mainFun.name.toTypeName, WildcardType, EmptyFlags, EmptyFlags, mainFun).typeSymbol
4444
if cls.exists && cls.owner != ctx.owner then
4545
report.warning(
4646
i"""The class `${ctx.printer.fullNameString(mainFun)}` generated from `@main` will shadow the existing ${cls.showLocated}.

compiler/src/dotty/tools/dotc/ast/Trees.scala

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1013,13 +1013,15 @@ object Trees {
10131013

10141014
@sharable val EmptyTree: Thicket = genericEmptyTree
10151015
@sharable val EmptyValDef: ValDef = genericEmptyValDef
1016-
@sharable val ContextualEmptyTree: Thicket = EmptyTree() // an empty tree marking a contextual closure
1016+
@sharable val ContextualEmptyTree: Thicket = new EmptyTree() // an empty tree marking a contextual closure
10171017

10181018
// ----- Auxiliary creation methods ------------------
10191019

10201020
def Thicket(): Thicket = EmptyTree
1021-
def Thicket(x1: Tree, x2: Tree)(implicit src: SourceFile): Thicket = Thicket(x1 :: x2 :: Nil)
1022-
def Thicket(x1: Tree, x2: Tree, x3: Tree)(implicit src: SourceFile): Thicket = Thicket(x1 :: x2 :: x3 :: Nil)
1021+
def Thicket(x1: Tree, x2: Tree)(implicit src: SourceFile): Thicket = new Thicket(x1 :: x2 :: Nil)
1022+
def Thicket(x1: Tree, x2: Tree, x3: Tree)(implicit src: SourceFile): Thicket = new Thicket(x1 :: x2 :: x3 :: Nil)
1023+
def Thicket(xs: List[Tree])(implicit src: SourceFile) = new Thicket(xs)
1024+
10231025
def flatTree(xs: List[Tree])(implicit src: SourceFile): Tree = flatten(xs) match {
10241026
case x :: Nil => x
10251027
case ys => Thicket(ys)

compiler/src/dotty/tools/dotc/core/ContextOps.scala

Lines changed: 15 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -25,21 +25,22 @@ object ContextOps:
2525

2626
/** The denotation with the given `name` and all `required` flags in current context
2727
*/
28-
def denotNamed(name: Name, required: FlagSet = EmptyFlags): Denotation = inContext(ctx) {
29-
if (ctx.owner.isClass)
30-
if (ctx.outer.owner == ctx.owner) { // inner class scope; check whether we are referring to self
31-
if (ctx.scope.size == 1) {
32-
val elem = ctx.scope.lastEntry
33-
if (elem.name == name) return elem.sym.denot // return self
28+
def denotNamed(name: Name, required: FlagSet = EmptyFlags, excluded: FlagSet = EmptyFlags): Denotation =
29+
inContext(ctx) {
30+
if (ctx.owner.isClass)
31+
if (ctx.outer.owner == ctx.owner) { // inner class scope; check whether we are referring to self
32+
if (ctx.scope.size == 1) {
33+
val elem = ctx.scope.lastEntry
34+
if (elem.name == name) return elem.sym.denot // return self
35+
}
36+
val pre = ctx.owner.thisType
37+
pre.findMember(name, pre, required, excluded)
3438
}
35-
val pre = ctx.owner.thisType
36-
pre.findMember(name, pre, required, EmptyFlags)
37-
}
38-
else // we are in the outermost context belonging to a class; self is invisible here. See inClassContext.
39-
ctx.owner.findMember(name, ctx.owner.thisType, required, EmptyFlags)
40-
else
41-
ctx.scope.denotsNamed(name).filterWithFlags(required, EmptyFlags).toDenot(NoPrefix)
42-
}
39+
else // we are in the outermost context belonging to a class; self is invisible here. See inClassContext.
40+
ctx.owner.findMember(name, ctx.owner.thisType, required, excluded)
41+
else
42+
ctx.scope.denotsNamed(name).filterWithFlags(required, excluded).toDenot(NoPrefix)
43+
}
4344

4445
/** A fresh local context with given tree and owner.
4546
* Owner might not exist (can happen for self valdefs), in which case

compiler/src/dotty/tools/dotc/core/Definitions.scala

Lines changed: 4 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -300,14 +300,10 @@ class Definitions {
300300
cls.info = ClassInfo(cls.owner.thisType, cls, List(AnyType, MatchableType), newScope)
301301
cls.setFlag(NoInits | JavaDefined)
302302

303-
// The companion object doesn't really exist, so it needs to be marked as
304-
// absent. Here we need to set it before completing attempt to load Object's
305-
// classfile, which causes issue #1648.
306-
val companion = JavaLangPackageVal.info.decl(nme.Object).symbol
307-
companion.moduleClass.markAbsent()
308-
companion.markAbsent()
309-
310-
completeClass(cls)
303+
ensureConstructor(cls, cls.denot.asClass, EmptyScope)
304+
val companion = JavaLangPackageVal.info.decl(nme.Object).symbol.asTerm
305+
NamerOps.makeConstructorCompanion(companion, cls)
306+
cls
311307
}
312308
def ObjectType: TypeRef = ObjectClass.typeRef
313309

compiler/src/dotty/tools/dotc/core/Flags.scala

Lines changed: 12 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -378,7 +378,7 @@ object Flags {
378378
val (Touched @ _, _, _) = newFlags(50, "<touched>")
379379

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

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

423+
/** Symbol is a constructor proxy (either companion, or apply method) */
424+
val (ConstructorProxy @ _, _, _) = newFlags(62, "<constructor proxy>") // (could be merged with Lifted)
425+
423426
// --------- Combined Flag Sets and Conjunctions ----------------------
424427

425428
/** All possible flags */
@@ -455,15 +458,16 @@ object Flags {
455458
Scala2SpecialFlags, MutableOrOpen, Opaque, Touched, JavaStatic,
456459
OuterOrCovariant, LabelOrContravariant, CaseAccessor,
457460
Extension, NonMember, Implicit, Given, Permanent, Synthetic,
458-
SuperParamAliasOrScala2x, Inline, Macro)
461+
SuperParamAliasOrScala2x, Inline, Macro, ConstructorProxy)
459462

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

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

513517
/** Flags that can apply to a module val */
514518
val RetainedModuleValFlags: FlagSet = RetainedModuleValAndClassFlags |
@@ -539,7 +543,10 @@ object Flags {
539543
val EnumCase: FlagSet = Case | Enum
540544
val CovariantLocal: FlagSet = Covariant | Local // A covariant type parameter
541545
val ContravariantLocal: FlagSet = Contravariant | Local // A contravariant type parameter
546+
val EffectivelyErased = ConstructorProxy | Erased
547+
val ConstructorProxyModule: FlagSet = ConstructorProxy | Module
542548
val DefaultParameter: FlagSet = HasDefault | Param // A Scala 2x default parameter
549+
val DeferredInline: FlagSet = Deferred | Inline
543550
val DeferredOrLazy: FlagSet = Deferred | Lazy
544551
val DeferredOrLazyOrMethod: FlagSet = Deferred | Lazy | Method
545552
val DeferredOrTermParamOrAccessor: FlagSet = Deferred | ParamAccessor | TermParam // term symbols without right-hand sides
@@ -548,7 +555,7 @@ object Flags {
548555
val FinalOrInline: FlagSet = Final | Inline
549556
val FinalOrModuleClass: FlagSet = Final | ModuleClass // A module class or a final class
550557
val EffectivelyFinalFlags: FlagSet = Final | Private
551-
val ExcludedForwarder: Flags.FlagSet = Specialized | Lifted | Protected | JavaStatic | Private | Macro
558+
val ExcludedForwarder: Flags.FlagSet = Specialized | Lifted | Protected | JavaStatic | Private | Macro | ConstructorProxy
552559
val FinalOrSealed: FlagSet = Final | Sealed
553560
val GivenOrImplicit: FlagSet = Given | Implicit
554561
val GivenOrImplicitVal: FlagSet = GivenOrImplicit.toTermFlags

compiler/src/dotty/tools/dotc/core/NamerOps.scala

Lines changed: 99 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,12 @@
1-
package dotty.tools.dotc
1+
package dotty.tools
2+
package dotc
23
package core
34

4-
import Contexts._, Symbols._, Types._, Flags._, Scopes._, Decorators._, NameOps._
5+
import Contexts._, Symbols._, Types._, Flags._, Scopes._, Decorators._, Names._, NameOps._
56
import Denotations._
6-
import SymDenotations.LazyType, Names.Name, StdNames.nme
7+
import SymDenotations.{LazyType, SymDenotation}, StdNames.nme
8+
import config.Config
9+
import ast.untpd
710

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

63+
/** If a class has one of these flags, it does not get a constructor companion */
64+
private val NoConstructorProxyNeededFlags = Abstract | Trait | Case | Synthetic | Module
65+
66+
/** The flags of a constructor companion */
67+
private val ConstructorCompanionFlags = Synthetic | ConstructorProxy
68+
69+
/** The flags of an `apply` method that serves as a constructor proxy */
70+
val ApplyProxyFlags = Synthetic | ConstructorProxy | Inline | Method
71+
72+
/** Does symbol `cls` need constructor proxies to be generated? */
73+
def needsConstructorProxies(cls: Symbol)(using Context): Boolean =
74+
cls.isClass
75+
&& !cls.flagsUNSAFE.isOneOf(NoConstructorProxyNeededFlags)
76+
&& !cls.isAnonymousClass
77+
78+
/** The completer of a constructor proxy apply method */
79+
class ApplyProxyCompleter(constr: Symbol)(using Context) extends LazyType:
80+
def complete(denot: SymDenotation)(using Context): Unit =
81+
denot.info = constr.info
82+
83+
/** Add constructor proxy apply methods to `scope`. Proxies are for constructors
84+
* in `cls` and they reside in `modcls`.
85+
*/
86+
def addConstructorApplies(scope: MutableScope, cls: ClassSymbol, modcls: ClassSymbol)(using Context): scope.type =
87+
def proxy(constr: Symbol): Symbol =
88+
newSymbol(
89+
modcls, nme.apply, ApplyProxyFlags | (constr.flagsUNSAFE & AccessFlags),
90+
ApplyProxyCompleter(constr), coord = constr.coord)
91+
for dcl <- cls.info.decls do
92+
if dcl.isConstructor then scope.enter(proxy(dcl))
93+
scope
94+
end addConstructorApplies
95+
96+
/** The completer of a constructor companion for class `cls`, where
97+
* `modul` is the companion symbol and `modcls` is its class.
98+
*/
99+
def constructorCompanionCompleter(cls: ClassSymbol)(
100+
modul: TermSymbol, modcls: ClassSymbol)(using Context): LazyType =
101+
new LazyType with SymbolLoaders.SecondCompleter {
102+
def complete(denot: SymDenotation)(using Context): Unit =
103+
val prefix = modcls.owner.thisType
104+
denot.info = ClassInfo(
105+
prefix, modcls, defn.AnyType :: Nil,
106+
addConstructorApplies(newScope, cls, modcls), TermRef(prefix, modul))
107+
}.withSourceModule(modul)
108+
109+
/** A new symbol that is the constructor companion for class `cls` */
110+
def constructorCompanion(cls: ClassSymbol)(using Context): TermSymbol =
111+
val companion = newModuleSymbol(
112+
cls.owner, cls.name.toTermName,
113+
ConstructorCompanionFlags, ConstructorCompanionFlags,
114+
constructorCompanionCompleter(cls),
115+
coord = cls.coord,
116+
assocFile = cls.assocFile)
117+
companion.moduleClass.registerCompanion(cls)
118+
cls.registerCompanion(companion.moduleClass)
119+
companion
120+
121+
/** Add all necesssary constructor proxy symbols for members of class `cls`. This means:
122+
*
123+
* - if a member is a class that needs a constructor companion, add one,
124+
* provided no member with the same name exists.
125+
* - if `cls` is a companion object of a class that needs a constructor companion,
126+
* and `cls` does not already define or inherit an `apply` method,
127+
* add `apply` methods for all constructors of the companion class.
128+
*/
129+
def addConstructorProxies(cls: ClassSymbol)(using Context): Unit =
130+
131+
def memberExists(cls: ClassSymbol, name: TermName): Boolean =
132+
cls.baseClasses.exists(_.info.decls.lookupEntry(name) != null)
133+
for mbr <- cls.info.decls do
134+
if needsConstructorProxies(mbr)
135+
&& !mbr.asClass.unforcedRegisteredCompanion.exists
136+
&& !memberExists(cls, mbr.name.toTermName)
137+
then
138+
constructorCompanion(mbr.asClass).entered
139+
140+
if cls.is(Module)
141+
&& needsConstructorProxies(cls.linkedClass)
142+
&& !memberExists(cls, nme.apply)
143+
then
144+
addConstructorApplies(cls.info.decls.openForMutations, cls.linkedClass.asClass, cls)
145+
end addConstructorProxies
146+
147+
/** Turn `modul` into a constructor companion for class `cls` */
148+
def makeConstructorCompanion(modul: TermSymbol, cls: ClassSymbol)(using Context): Unit =
149+
val modcls = modul.moduleClass.asClass
150+
modul.setFlag(ConstructorCompanionFlags)
151+
modcls.setFlag(ConstructorCompanionFlags)
152+
modcls.info = constructorCompanionCompleter(cls)(modul, modcls)
153+
cls.registeredCompanion = modcls
154+
modcls.registeredCompanion = cls
155+
60156
end NamerOps

compiler/src/dotty/tools/dotc/core/SymDenotations.scala

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -951,7 +951,8 @@ object SymDenotations {
951951

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

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

16101611
private def baseTypeCache(using Context): BaseTypeMap = {
16111612
if !currentHasSameBaseTypesAs(myBaseTypeCachePeriod) then
1612-
myBaseTypeCache = BaseTypeMap()
1613+
myBaseTypeCache = new BaseTypeMap()
16131614
myBaseTypeCachePeriod = ctx.period
16141615
myBaseTypeCache
16151616
}
@@ -2165,6 +2166,8 @@ object SymDenotations {
21652166
if (companion.isClass && !isAbsent(canForce = false) && !companion.isAbsent(canForce = false))
21662167
myCompanion = companion
21672168

2169+
private[core] def unforcedRegisteredCompanion: Symbol = myCompanion
2170+
21682171
override def registeredCompanion(using Context) =
21692172
if !myCompanion.exists then
21702173
ensureCompleted()
@@ -2256,6 +2259,15 @@ object SymDenotations {
22562259
case d: DenotUnion => dropStale(d)
22572260
case d => d
22582261

2262+
/** Filter symbols making up a DenotUnion to remove alternatives from stale classfiles.
2263+
* This proceeds as follow:
2264+
*
2265+
* - prefer alternatives that are currently compiled over ones that have been compiled before.
2266+
* - if no alternative is compiled now, and they all come from the same file, keep all of them
2267+
* - if no alternative is compiled now, and they come from different files, keep the
2268+
* ones from the youngest file, but issue a warning that one of the class files
2269+
* should be removed from the classpath.
2270+
*/
22592271
def dropStale(multi: DenotUnion): PreDenotation =
22602272
val compiledNow = multi.filterWithPredicate(d =>
22612273
d.symbol.isDefinedInCurrentRun || d.symbol.associatedFile == null

compiler/src/dotty/tools/dotc/core/SymbolLoaders.scala

Lines changed: 12 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -359,13 +359,19 @@ abstract class SymbolLoader extends LazyType { self =>
359359
throw ex
360360
}
361361
finally {
362-
def postProcess(denot: SymDenotation) =
363-
if (!denot.isCompleted &&
364-
!denot.completer.isInstanceOf[SymbolLoaders.SecondCompleter])
365-
denot.markAbsent()
366-
postProcess(root)
362+
def postProcess(denot: SymDenotation, other: Symbol) =
363+
if !denot.isCompleted &&
364+
!denot.completer.isInstanceOf[SymbolLoaders.SecondCompleter] then
365+
if denot.is(ModuleClass) && NamerOps.needsConstructorProxies(other) then
366+
NamerOps.makeConstructorCompanion(denot.sourceModule.asTerm, other.asClass)
367+
denot.resetFlag(Touched)
368+
else
369+
denot.markAbsent()
370+
371+
val other = if root.isRoot then NoSymbol else root.scalacLinkedClass
372+
postProcess(root, other)
367373
if (!root.isRoot)
368-
postProcess(root.scalacLinkedClass.denot)
374+
postProcess(other, root.symbol)
369375
}
370376
}
371377

compiler/src/dotty/tools/dotc/core/Symbols.scala

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -54,14 +54,17 @@ object Symbols {
5454
//assert(id != 723)
5555

5656
def coord: Coord = myCoord
57+
5758
/** Set the coordinate of this class, this is only useful when the coordinate is
5859
* not known at symbol creation. This is the case for root symbols
5960
* unpickled from TASTY.
6061
*
6162
* @pre coord == NoCoord
6263
*/
6364
private[core] def coord_=(c: Coord): Unit = {
64-
assert(myCoord == NoCoord)
65+
// assert(myCoord == NoCoord)
66+
// This assertion fails for CommentPickling test.
67+
// TODO: figure out what's wrong in the setup of CommentPicklingTest and re-enable assertion.
6568
myCoord = c
6669
}
6770

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

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

498505
type MutableSymbolMap[T] = EqHashMap[Symbol, T]
506+
def MutableSymbolMap[T](): EqHashMap[Symbol, T] = EqHashMap[Symbol, T]()
507+
def MutableSymbolMap[T](initialCapacity: Int): EqHashMap[Symbol, T] = EqHashMap[Symbol, T](initialCapacity)
499508

500509
// ---- Factory methods for symbol creation ----------------------
501510
//

compiler/src/dotty/tools/dotc/core/classfile/ClassfileParser.scala

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -194,6 +194,7 @@ class ClassfileParser(
194194
addAnnotationConstructor(classInfo.asInstanceOf[TempClassInfoType])
195195

196196
setClassInfo(classRoot, classInfo, fromScala2 = false)
197+
NamerOps.addConstructorProxies(moduleRoot.classSymbol)
197198
}
198199
else if (result == Some(NoEmbedded))
199200
for (sym <- List(moduleRoot.sourceModule, moduleRoot.symbol, classRoot.symbol)) {

0 commit comments

Comments
 (0)