@@ -55,23 +55,33 @@ trait Symbols extends api.Symbols { self: SymbolTable =>
55
55
def newFreeTypeSymbol (name : TypeName , flags : Long = 0L , origin : String ): FreeTypeSymbol =
56
56
new FreeTypeSymbol (name, origin) initFlags flags
57
57
58
- /** The original owner of a class. Used by the backend to generate
59
- * EnclosingMethod attributes.
58
+ /**
59
+ * This map stores previous owners of symbols when owners are modified. This allows obtaining
60
+ * the owner at each phase using phase travel and `sym.ownerAtCurrentPhase`.
61
+ *
62
+ * Invariant: for each symbol, the list of owners is sorted by the phase id at which the owner
63
+ * was set.
60
64
*/
61
- val originalOwner = perRunCaches.newMap[Symbol , Symbol ]()
65
+ val originalOwnerMap = perRunCaches.newMap[Symbol , List [( Symbol , Phase )] ]()
62
66
63
67
// TODO - don't allow the owner to be changed without checking invariants, at least
64
68
// when under some flag. Define per-phase invariants for owner/owned relationships,
65
69
// e.g. after flatten all classes are owned by package classes, there are lots and
66
70
// lots of these to be declared (or more realistically, discovered.)
67
- protected def saveOriginalOwner (sym : Symbol ) {
68
- if (originalOwner contains sym) ()
69
- else originalOwner(sym) = sym.rawowner
71
+ protected def saveOriginalOwner (sym : Symbol ): Unit = {
72
+ // some synthetic symbols have NoSymbol as owner initially
73
+ if (sym.owner != NoSymbol ) {
74
+ val l = originalOwnerMap.getOrElse(sym, Nil )
75
+ // When the owner is modified multiple times during a phase, we only store the original owner
76
+ // the first time, which is the owner at the end of the previous phase.
77
+ if (! l.exists(_._2 == phase))
78
+ originalOwnerMap(sym) = ((sym.rawowner, phase) :: l).sortBy(_._2.id)
79
+ }
70
80
}
71
81
protected def originalEnclosingMethod (sym : Symbol ): Symbol = {
72
82
if (sym.isMethod || sym == NoSymbol ) sym
73
83
else {
74
- val owner = originalOwner.getOrElse( sym, sym.rawowner)
84
+ val owner = sym.originalOwner
75
85
if (sym.isLocalDummy) owner.enclClass.primaryConstructor
76
86
else originalEnclosingMethod(owner)
77
87
}
@@ -757,8 +767,22 @@ trait Symbols extends api.Symbols { self: SymbolTable =>
757
767
* So "isModuleNotMethod" exists not for its achievement in
758
768
* brevity, but to encapsulate the relevant condition.
759
769
*/
760
- def isModuleNotMethod = isModule && ! isMethod
761
- def isStaticModule = isModuleNotMethod && isStatic
770
+ def isModuleNotMethod = {
771
+ if (isModule) {
772
+ if (phase.refChecked) this .info // force completion to make sure lateMETHOD is there.
773
+ ! isMethod
774
+ } else false
775
+ }
776
+
777
+ // After RefChecks, the `isStatic` check is mostly redundant: all non-static modules should
778
+ // be methods (and vice versa). There's a corner case on the vice-versa with mixed-in module
779
+ // symbols:
780
+ // trait T { object A }
781
+ // object O extends T
782
+ // The module symbol A is cloned into T$impl (addInterfaces), and then cloned into O (mixin).
783
+ // Since the original A is not static, it's turned into a method. The clone in O however is
784
+ // static (owned by a module), but it's also a method.
785
+ def isStaticModule = isModuleNotMethod && isStatic
762
786
763
787
final def isInitializedToDefault = ! isType && hasAllFlags(DEFAULTINIT | ACCESSOR )
764
788
final def isThisSym = isTerm && owner.thisSym == this
@@ -909,12 +933,33 @@ trait Symbols extends api.Symbols { self: SymbolTable =>
909
933
)
910
934
final def isModuleVar = hasFlag(MODULEVAR )
911
935
912
- /** Is this symbol static (i.e. with no outer instance)?
913
- * Q: When exactly is a sym marked as STATIC?
914
- * A: If it's a member of a toplevel object, or of an object contained in a toplevel object, or any number of levels deep.
915
- * http://groups.google.com/group/scala-internals/browse_thread/thread/d385bcd60b08faf6
936
+ /**
937
+ * Is this symbol static (i.e. with no outer instance)?
938
+ * Q: When exactly is a sym marked as STATIC?
939
+ * A: If it's a member of a toplevel object, or of an object contained in a toplevel object, or
940
+ * any number of levels deep.
941
+ * http://groups.google.com/group/scala-internals/browse_thread/thread/d385bcd60b08faf6
942
+ *
943
+ * TODO: should this only be invoked on class / module symbols? because there's also `isStaticMember`.
944
+ *
945
+ * Note: the result of `isStatic` changes over time.
946
+ * - Lambdalift local definitions to the class level, the `owner` field is modified.
947
+ * object T { def foo { object O } }
948
+ * After lambdalift, the OModule.isStatic is true.
949
+ *
950
+ * - After flatten, nested classes are moved to the package level. Invoking `owner` on a
951
+ * class returns a package class, for which `isStaticOwner` is true. For example,
952
+ * class C { object O }
953
+ * OModuleClass.isStatic is true after flatten. Using phase travel to get before flatten,
954
+ * method `owner` returns the class C.
955
+ *
956
+ * Why not make a stable version of `isStatic`? Maybe some parts of the compiler depend on the
957
+ * current implementation. For example
958
+ * trait T { def foo = 1 }
959
+ * The method `foo` in the implementation class T$impl will be `isStatic`, because trait
960
+ * impl classes get the `lateMODULE` flag (T$impl.isStaticOwner is true).
916
961
*/
917
- def isStatic = (this hasFlag STATIC ) || owner .isStaticOwner
962
+ def isStatic = (this hasFlag STATIC ) || ownerAtCurrentPhase .isStaticOwner
918
963
919
964
/** Is this symbol a static constructor? */
920
965
final def isStaticConstructor : Boolean =
@@ -953,10 +998,10 @@ trait Symbols extends api.Symbols { self: SymbolTable =>
953
998
954
999
/** Is this symbol defined in a block? */
955
1000
@ deprecated(" Use isLocalToBlock instead" , " 2.11.0" )
956
- final def isLocal : Boolean = owner .isTerm
1001
+ final def isLocal : Boolean = ownerAtCurrentPhase .isTerm
957
1002
958
1003
/** Is this symbol defined in a block? */
959
- final def isLocalToBlock : Boolean = owner .isTerm
1004
+ final def isLocalToBlock : Boolean = ownerAtCurrentPhase .isTerm
960
1005
961
1006
/** Is this symbol a constant? */
962
1007
final def isConstant : Boolean = isStable && isConstantType(tpe.resultType)
@@ -1106,7 +1151,7 @@ trait Symbols extends api.Symbols { self: SymbolTable =>
1106
1151
* - After lambdalift, all local method and class definitions (those not owned by a class
1107
1152
* or package class) change their owner to the enclosing class. This is done through
1108
1153
* a destructive "sym.owner = sym.owner.enclClass". The old owner is saved by
1109
- * saveOriginalOwner for the backend (needed to generate the EnclosingMethod attribute) .
1154
+ * saveOriginalOwner.
1110
1155
* - After flatten, all classes are owned by a PackageClass. This is done through a
1111
1156
* phase check (if after flatten) in the (overridden) method "def owner" in
1112
1157
* ModuleSymbol / ClassSymbol. The `rawowner` field is not modified.
@@ -1126,6 +1171,41 @@ trait Symbols extends api.Symbols { self: SymbolTable =>
1126
1171
final def safeOwner : Symbol = if (this eq NoSymbol ) NoSymbol else owner
1127
1172
final def assertOwner : Symbol = if (this eq NoSymbol ) abort(" no-symbol does not have an owner" ) else owner
1128
1173
1174
+ /**
1175
+ * The initial owner of this symbol.
1176
+ */
1177
+ def originalOwner : Symbol = {
1178
+ val owners = originalOwnerMap.getOrElse(this , Nil )
1179
+ owners.headOption.map(_._1).getOrElse(rawowner)
1180
+ }
1181
+
1182
+ /**
1183
+ * In a better world this method would not exist. It returns the symbol's owner at the current
1184
+ * phase, even after the `rawowner` field has been changed. The method `owner` only does a bit
1185
+ * of that, as explained in its documentation. Example:
1186
+ *
1187
+ * class C { def f { def g } }
1188
+ *
1189
+ * After phase lambdalift, the rawowner field of g is C. Running `exitingPickler(gSym.owner)`
1190
+ * returns C. `exitingPickler(gSym.ownerAtCurrentPhase)` returns f.
1191
+ *
1192
+ * Changing the semantics of `owner` seems to risky and a too large hit on performance.
1193
+ *
1194
+ * Note: similar to info transformers, if the owner is assigned in phase X, then
1195
+ * `ownerAtCurrentPhase` only returns that new owner in phase X+1 (not yet in X).
1196
+ */
1197
+ def ownerAtCurrentPhase : Symbol = {
1198
+ // after flatten, we have to call `owner`, since that method does the actual flattening.
1199
+ if (phase.flatClasses) owner
1200
+ else {
1201
+ val owners = originalOwnerMap.getOrElse(this , Nil )
1202
+ // each pair in owners is an owner symbol, and the phase until which it is valid.
1203
+ // we drop those that are expired in the current phase.
1204
+ val currentPhaseOwner = owners.dropWhile(_._2.id < phase.id).headOption.map(_._1)
1205
+ currentPhaseOwner.getOrElse(rawowner)
1206
+ }
1207
+ }
1208
+
1129
1209
// TODO - don't allow the owner to be changed without checking invariants, at least
1130
1210
// when under some flag. Define per-phase invariants for owner/owned relationships,
1131
1211
// e.g. after flatten all classes are owned by package classes, there are lots and
@@ -1139,7 +1219,6 @@ trait Symbols extends api.Symbols { self: SymbolTable =>
1139
1219
}
1140
1220
1141
1221
def ownerChain : List [Symbol ] = this :: owner.ownerChain
1142
- def originalOwnerChain : List [Symbol ] = this :: originalOwner.getOrElse(this , rawowner).originalOwnerChain
1143
1222
1144
1223
// Non-classes skip self and return rest of owner chain; overridden in ClassSymbol.
1145
1224
def enclClassChain : List [Symbol ] = owner.enclClassChain
0 commit comments