Skip to content

Commit cf08f25

Browse files
committed
Merge pull request scala#1380 from scalamacros/ticket/6277
SI-6277 fix for isXXX methods in reflection
2 parents 01b6826 + 2fb507b commit cf08f25

27 files changed

+164
-64
lines changed

src/compiler/scala/tools/nsc/symtab/SymbolLoaders.scala

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -219,7 +219,7 @@ abstract class SymbolLoaders {
219219
/**
220220
* Load contents of a package
221221
*/
222-
class PackageLoader(classpath: ClassPath[platform.BinaryRepr]) extends SymbolLoader {
222+
class PackageLoader(classpath: ClassPath[platform.BinaryRepr]) extends SymbolLoader with FlagAgnosticCompleter {
223223
protected def description = "package loader "+ classpath.name
224224

225225
protected def doComplete(root: Symbol) {
@@ -242,7 +242,7 @@ abstract class SymbolLoaders {
242242
}
243243
}
244244

245-
class ClassfileLoader(val classfile: AbstractFile) extends SymbolLoader {
245+
class ClassfileLoader(val classfile: AbstractFile) extends SymbolLoader with FlagAssigningCompleter {
246246
private object classfileParser extends ClassfileParser {
247247
val global: SymbolLoaders.this.global.type = SymbolLoaders.this.global
248248
}
@@ -267,7 +267,7 @@ abstract class SymbolLoaders {
267267
override def sourcefile: Option[AbstractFile] = classfileParser.srcfile
268268
}
269269

270-
class MsilFileLoader(msilFile: MsilFile) extends SymbolLoader {
270+
class MsilFileLoader(msilFile: MsilFile) extends SymbolLoader with FlagAssigningCompleter {
271271
private def typ = msilFile.msilType
272272
private object typeParser extends clr.TypeParser {
273273
val global: SymbolLoaders.this.global.type = SymbolLoaders.this.global
@@ -277,14 +277,14 @@ abstract class SymbolLoaders {
277277
protected def doComplete(root: Symbol) { typeParser.parse(typ, root) }
278278
}
279279

280-
class SourcefileLoader(val srcfile: AbstractFile) extends SymbolLoader {
280+
class SourcefileLoader(val srcfile: AbstractFile) extends SymbolLoader with FlagAssigningCompleter {
281281
protected def description = "source file "+ srcfile.toString
282282
override def fromSource = true
283283
override def sourcefile = Some(srcfile)
284284
protected def doComplete(root: Symbol): Unit = global.currentRun.compileLate(srcfile)
285285
}
286286

287-
object moduleClassLoader extends SymbolLoader {
287+
object moduleClassLoader extends SymbolLoader with FlagAssigningCompleter {
288288
protected def description = "module class loader"
289289
protected def doComplete(root: Symbol) { root.sourceModule.initialize }
290290
}

src/compiler/scala/tools/nsc/symtab/classfile/ClassfileParser.scala

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -844,7 +844,7 @@ abstract class ClassfileParser {
844844
GenPolyType(ownTypeParams, tpe)
845845
} // sigToType
846846

847-
class TypeParamsType(override val typeParams: List[Symbol]) extends LazyType {
847+
class TypeParamsType(override val typeParams: List[Symbol]) extends LazyType with FlagAgnosticCompleter {
848848
override def complete(sym: Symbol) { throw new AssertionError("cyclic type dereferencing") }
849849
}
850850

@@ -1228,7 +1228,7 @@ abstract class ClassfileParser {
12281228
}
12291229
}
12301230

1231-
class LazyAliasType(alias: Symbol) extends LazyType {
1231+
class LazyAliasType(alias: Symbol) extends LazyType with FlagAgnosticCompleter {
12321232
override def complete(sym: Symbol) {
12331233
sym setInfo createFromClonedSymbols(alias.initialize.typeParams, alias.tpe)(typeFun)
12341234
}

src/compiler/scala/tools/nsc/symtab/clr/TypeParser.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -64,7 +64,7 @@ abstract class TypeParser {
6464
busy = false
6565
}
6666

67-
class TypeParamsType(override val typeParams: List[Symbol]) extends LazyType {
67+
class TypeParamsType(override val typeParams: List[Symbol]) extends LazyType with FlagAgnosticCompleter {
6868
override def complete(sym: Symbol) { throw new AssertionError("cyclic type dereferencing") }
6969
}
7070

src/compiler/scala/tools/nsc/transform/AddInterfaces.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -132,7 +132,7 @@ abstract class AddInterfaces extends InfoTransform { self: Erasure =>
132132
* - for every interface member of iface: its implementation method, if one is needed
133133
* - every former member of iface that is implementation only
134134
*/
135-
private class LazyImplClassType(iface: Symbol) extends LazyType {
135+
private class LazyImplClassType(iface: Symbol) extends LazyType with FlagAgnosticCompleter {
136136
/** Compute the decls of implementation class implClass,
137137
* given the decls ifaceDecls of its interface.
138138
*/

src/compiler/scala/tools/nsc/typechecker/Namers.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1506,7 +1506,7 @@ trait Namers extends MethodSynthesis {
15061506

15071507
/** A class representing a lazy type with known type parameters.
15081508
*/
1509-
class PolyTypeCompleter(tparams: List[TypeDef], restp: TypeCompleter, owner: Tree, ctx: Context) extends LockingTypeCompleter {
1509+
class PolyTypeCompleter(tparams: List[TypeDef], restp: TypeCompleter, owner: Tree, ctx: Context) extends LockingTypeCompleter with FlagAgnosticCompleter {
15101510
private val ownerSym = owner.symbol
15111511
override val typeParams = tparams map (_.symbol) //@M
15121512
override val tree = restp.tree

src/reflect/scala/reflect/api/Symbols.scala

Lines changed: 1 addition & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -214,22 +214,7 @@ trait Symbols { self: Universe =>
214214

215215
/** A list of annotations attached to this Symbol.
216216
*/
217-
// we cannot expose the `annotations` method because it doesn't auto-initialize a symbol (see SI-5423)
218-
// there was an idea to use the `isCompilerUniverse` flag and auto-initialize symbols in `annotations` whenever this flag is false
219-
// but it doesn't work, because the unpickler (that is shared between reflective universes and global universes) is very picky about initialization
220-
// scala.reflect.internal.Types$TypeError: bad reference while unpickling scala.collection.immutable.Nil: type Nothing not found in scala.type not found.
221-
// at scala.reflect.internal.pickling.UnPickler$Scan.toTypeError(UnPickler.scala:836)
222-
// at scala.reflect.internal.pickling.UnPickler$Scan$LazyTypeRef.complete(UnPickler.scala:849) // auto-initialize goes boom
223-
// at scala.reflect.internal.Symbols$Symbol.info(Symbols.scala:1140)
224-
// at scala.reflect.internal.Symbols$Symbol.initialize(Symbols.scala:1272) // this triggers auto-initialize
225-
// at scala.reflect.internal.Symbols$Symbol.annotations(Symbols.scala:1438) // unpickler first tries to get pre-existing annotations
226-
// at scala.reflect.internal.Symbols$Symbol.addAnnotation(Symbols.scala:1458) // unpickler tries to add the annotation being read
227-
// at scala.reflect.internal.pickling.UnPickler$Scan.readSymbolAnnotation(UnPickler.scala:489) // unpickler detects an annotation
228-
// at scala.reflect.internal.pickling.UnPickler$Scan.run(UnPickler.scala:88)
229-
// at scala.reflect.internal.pickling.UnPickler.unpickle(UnPickler.scala:37)
230-
// at scala.reflect.runtime.JavaMirrors$JavaMirror.unpickleClass(JavaMirrors.scala:253) // unpickle from within a reflexive mirror
231-
// def annotations: List[Annotation]
232-
def getAnnotations: List[Annotation]
217+
def annotations: List[Annotation]
233218

234219
/** For a class: the module or case class factory with the same name in the same package.
235220
* For a module: the class with the same name in the same package.

src/reflect/scala/reflect/internal/Flags.scala

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -296,6 +296,12 @@ class Flags extends ModifierFlags {
296296
/** These flags are pickled */
297297
final val PickledFlags = InitialFlags & ~FlagsNotPickled
298298

299+
/** If we have a top-level class or module
300+
* and someone asks us for a flag not in TopLevelPickledFlags,
301+
* then we don't need unpickling to give a definite answer.
302+
*/
303+
final val TopLevelPickledFlags = PickledFlags & ~(MODULE | METHOD | PACKAGE | PARAM | EXISTENTIAL)
304+
299305
def getterFlags(fieldFlags: Long): Long = ACCESSOR + (
300306
if ((fieldFlags & MUTABLE) != 0) fieldFlags & ~MUTABLE & ~PRESUPER
301307
else fieldFlags & ~PRESUPER | STABLE

src/reflect/scala/reflect/internal/HasFlags.scala

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -99,6 +99,7 @@ trait HasFlags {
9999
def isLabel = hasAllFlags(LABEL | METHOD) && !hasAccessorFlag
100100
def isLazy = hasFlag(LAZY)
101101
def isLifted = hasFlag(LIFTED)
102+
def isMacro = hasFlag(MACRO)
102103
def isMutable = hasFlag(MUTABLE)
103104
def isOverride = hasFlag(OVERRIDE)
104105
def isParamAccessor = hasFlag(PARAMACCESSOR)
@@ -109,6 +110,7 @@ trait HasFlags {
109110
def isProtectedLocal = hasAllFlags(ProtectedLocal)
110111
def isPublic = hasNoFlags(PRIVATE | PROTECTED) && !hasAccessBoundary
111112
def isSealed = hasFlag(SEALED)
113+
def isSpecialized = hasFlag(SPECIALIZED)
112114
def isSuperAccessor = hasFlag(SUPERACCESSOR)
113115
def isSynthetic = hasFlag(SYNTHETIC)
114116
def isTrait = hasFlag(TRAIT) && !hasFlag(PARAM)

src/reflect/scala/reflect/internal/Importers.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -104,7 +104,7 @@ trait Importers extends api.Importers { self: SymbolTable =>
104104
mysym setFlag Flags.LOCKED
105105
mysym setInfo {
106106
val mytypeParams = sym.typeParams map importSymbol
107-
new LazyPolyType(mytypeParams) {
107+
new LazyPolyType(mytypeParams) with FlagAgnosticCompleter {
108108
override def complete(s: Symbol) {
109109
val result = sym.info match {
110110
case from.PolyType(_, res) => res

src/reflect/scala/reflect/internal/Symbols.scala

Lines changed: 78 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,16 @@ trait Symbols extends api.Symbols { self: SymbolTable =>
5555
def newFreeTypeSymbol(name: TypeName, flags: Long = 0L, origin: String): FreeTypeSymbol =
5656
new FreeTypeSymbol(name, origin) initFlags flags
5757

58+
/** Determines whether the given information request should trigger the given symbol's completer.
59+
* See comments to `Symbol.needsInitialize` for details.
60+
*/
61+
protected def shouldTriggerCompleter(symbol: Symbol, completer: Type, isFlagRelated: Boolean, mask: Long) =
62+
completer match {
63+
case null => false
64+
case _: FlagAgnosticCompleter => !isFlagRelated
65+
case _ => abort(s"unsupported completer: $completer of class ${if (completer != null) completer.getClass else null} for symbol ${symbol.fullName}")
66+
}
67+
5868
/** The original owner of a class. Used by the backend to generate
5969
* EnclosingMethod attributes.
6070
*/
@@ -67,7 +77,7 @@ trait Symbols extends api.Symbols { self: SymbolTable =>
6777
def isParamWithDefault: Boolean = this.hasDefault
6878
def isByNameParam: Boolean = this.isValueParameter && (this hasFlag BYNAMEPARAM)
6979
def isImplementationArtifact: Boolean = (this hasFlag BRIDGE) || (this hasFlag VBRIDGE) || (this hasFlag ARTIFACT)
70-
def isJava: Boolean = this hasFlag JAVA
80+
def isJava: Boolean = isJavaDefined
7181
def isVal: Boolean = isTerm && !isModule && !isMethod && !isMutable
7282
def isVar: Boolean = isTerm && !isModule && !isMethod && isMutable
7383

@@ -88,7 +98,6 @@ trait Symbols extends api.Symbols { self: SymbolTable =>
8898
def toTypeIn(site: Type): Type = site.memberType(this)
8999
def toTypeConstructor: Type = typeConstructor
90100
def setTypeSignature(tpe: Type): this.type = { setInfo(tpe); this }
91-
def getAnnotations: List[AnnotationInfo] = { initialize; annotations }
92101
def setAnnotations(annots: AnnotationInfo*): this.type = { setAnnotations(annots.toList); this }
93102

94103
def getter: Symbol = getter(owner)
@@ -218,9 +227,10 @@ trait Symbols extends api.Symbols { self: SymbolTable =>
218227
val m = newModuleSymbol(clazz.name.toTermName, clazz.pos, MODULE | newFlags)
219228
connectModuleToClass(m, clazz.asInstanceOf[ClassSymbol])
220229
}
221-
final def newModule(name: TermName, pos: Position = NoPosition, newFlags: Long = 0L): ModuleSymbol = {
222-
val m = newModuleSymbol(name, pos, newFlags | MODULE)
223-
val clazz = newModuleClass(name.toTypeName, pos, m getFlag ModuleToClassFlags)
230+
final def newModule(name: TermName, pos: Position = NoPosition, newFlags0: Long = 0L): ModuleSymbol = {
231+
val newFlags = newFlags0 | MODULE
232+
val m = newModuleSymbol(name, pos, newFlags)
233+
val clazz = newModuleClass(name.toTypeName, pos, newFlags & ModuleToClassFlags)
224234
connectModuleToClass(m, clazz)
225235
}
226236

@@ -238,9 +248,10 @@ trait Symbols extends api.Symbols { self: SymbolTable =>
238248
final def newModuleSymbol(name: TermName, pos: Position = NoPosition, newFlags: Long = 0L): ModuleSymbol =
239249
newTermSymbol(name, pos, newFlags).asInstanceOf[ModuleSymbol]
240250

241-
final def newModuleAndClassSymbol(name: Name, pos: Position, flags: FlagSet): (ModuleSymbol, ClassSymbol) = {
242-
val m = newModuleSymbol(name, pos, flags | MODULE)
243-
val c = newModuleClass(name.toTypeName, pos, m getFlag ModuleToClassFlags)
251+
final def newModuleAndClassSymbol(name: Name, pos: Position, flags0: FlagSet): (ModuleSymbol, ClassSymbol) = {
252+
val flags = flags0 | MODULE
253+
val m = newModuleSymbol(name, pos, flags)
254+
val c = newModuleClass(name.toTypeName, pos, flags & ModuleToClassFlags)
244255
connectModuleToClass(m, c)
245256
(m, c)
246257
}
@@ -489,14 +500,12 @@ trait Symbols extends api.Symbols { self: SymbolTable =>
489500
def isAliasType = false
490501
def isAbstractType = false
491502
def isSkolem = false
492-
def isMacro = this hasFlag MACRO
493503

494504
/** A Type, but not a Class. */
495505
def isNonClassType = false
496506

497507
/** The bottom classes are Nothing and Null, found in Definitions. */
498508
def isBottomClass = false
499-
def isSpecialized = this hasFlag SPECIALIZED
500509

501510
/** These are all tests for varieties of ClassSymbol, which has these subclasses:
502511
* - ModuleClassSymbol
@@ -585,11 +594,20 @@ trait Symbols extends api.Symbols { self: SymbolTable =>
585594
&& owner.isPackageClass
586595
&& nme.isReplWrapperName(name)
587596
)
588-
final def getFlag(mask: Long): Long = flags & mask
597+
final def getFlag(mask: Long): Long = {
598+
if (!isCompilerUniverse && needsInitialize(isFlagRelated = true, mask = mask)) initialize
599+
flags & mask
600+
}
589601
/** Does symbol have ANY flag in `mask` set? */
590-
final def hasFlag(mask: Long): Boolean = (flags & mask) != 0
602+
final def hasFlag(mask: Long): Boolean = {
603+
if (!isCompilerUniverse && needsInitialize(isFlagRelated = true, mask = mask)) initialize
604+
(flags & mask) != 0
605+
}
591606
/** Does symbol have ALL the flags in `mask` set? */
592-
final def hasAllFlags(mask: Long): Boolean = (flags & mask) == mask
607+
final def hasAllFlags(mask: Long): Boolean = {
608+
if (!isCompilerUniverse && needsInitialize(isFlagRelated = true, mask = mask)) initialize
609+
(flags & mask) == mask
610+
}
593611

594612
def setFlag(mask: Long): this.type = { _rawflags |= mask ; this }
595613
def resetFlag(mask: Long): this.type = { _rawflags &= ~mask ; this }
@@ -1138,7 +1156,10 @@ trait Symbols extends api.Symbols { self: SymbolTable =>
11381156
/** See comment in HasFlags for how privateWithin combines with flags.
11391157
*/
11401158
private[this] var _privateWithin: Symbol = _
1141-
def privateWithin = _privateWithin
1159+
def privateWithin = {
1160+
if (!isCompilerUniverse && needsInitialize(isFlagRelated = false, mask = 0)) initialize
1161+
_privateWithin
1162+
}
11421163
def privateWithin_=(sym: Symbol) { _privateWithin = sym }
11431164
def setPrivateWithin(sym: Symbol): this.type = { privateWithin_=(sym) ; this }
11441165

@@ -1329,6 +1350,46 @@ trait Symbols extends api.Symbols { self: SymbolTable =>
13291350
this
13301351
}
13311352

1353+
/** Called when the programmer requests information that might require initialization of the underlying symbol.
1354+
*
1355+
* `isFlagRelated` and `mask` describe the nature of this information.
1356+
* isFlagRelated = true means that the programmer needs particular bits in flags.
1357+
* isFlagRelated = false means that the request is unrelated to flags (annotations or privateWithin).
1358+
*
1359+
* In our current architecture, symbols for top-level classes and modules
1360+
* are created as dummies. Package symbols just call newClass(name) or newModule(name) and
1361+
* consider their job done.
1362+
*
1363+
* In order for such a dummy to provide meaningful info (e.g. a list of its members),
1364+
* it needs to go through unpickling. Unpickling is a process of reading Scala metadata
1365+
* from ScalaSignature annotations and assigning it to symbols and types.
1366+
*
1367+
* A single unpickling session takes a top-level class or module, parses the ScalaSignature annotation
1368+
* and then reads metadata for the unpicklee, its companion (if any) and all their members recursively
1369+
* (i.e. the pickle not only contains info about directly nested classes/modules, but also about
1370+
* classes/modules nested into those and so on).
1371+
*
1372+
* Unpickling is triggered automatically whenever typeSignature (info in compiler parlance) is called.
1373+
* This happens because package symbols assign completer thunks to the dummies they create.
1374+
* Therefore metadata loading happens lazily and transparently.
1375+
*
1376+
* Almost transparently. Unfortunately metadata isn't limited to just signatures (i.e. lists of members).
1377+
* It also includes flags (which determine e.g. whether a class is sealed or not), annotations and privateWithin.
1378+
* This gives rise to unpleasant effects like in SI-6277, when a flag test called on an uninitialize symbol
1379+
* produces incorrect results.
1380+
*
1381+
* One might think that the solution is simple: automatically call the completer whenever one needs
1382+
* flags, annotations and privateWithin - just like it's done for typeSignature. Unfortunately, this
1383+
* leads to weird crashes in scalac, and currently we can't attempt to fix the core of the compiler
1384+
* risk stability a few weeks before the final release.
1385+
*
1386+
* However we do need to fix this for runtime reflection, since it's not something we'd like to
1387+
* expose to reflection users. Therefore a proposed solution is to check whether we're in a
1388+
* runtime reflection universe and if yes then to commence initialization.
1389+
*/
1390+
protected def needsInitialize(isFlagRelated: Boolean, mask: Long) =
1391+
!isInitialized && (flags & LOCKED) == 0 && shouldTriggerCompleter(this, if (infos ne null) infos.info else null, isFlagRelated, mask)
1392+
13321393
/** Was symbol's type updated during given phase? */
13331394
final def isUpdatedAt(pid: Phase#Id): Boolean = {
13341395
assert(isCompilerUniverse)
@@ -1491,8 +1552,10 @@ trait Symbols extends api.Symbols { self: SymbolTable =>
14911552
/** After the typer phase (before, look at the definition's Modifiers), contains
14921553
* the annotations attached to member a definition (class, method, type, field).
14931554
*/
1494-
def annotations: List[AnnotationInfo] =
1555+
def annotations: List[AnnotationInfo] = {
1556+
if (!isCompilerUniverse && needsInitialize(isFlagRelated = false, mask = 0)) initialize
14951557
_annotations
1558+
}
14961559

14971560
def setAnnotations(annots: List[AnnotationInfo]): this.type = {
14981561
_annotations = annots

src/reflect/scala/reflect/internal/Types.scala

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3445,6 +3445,16 @@ trait Types extends api.Types { self: SymbolTable =>
34453445
override def kind = "LazyType"
34463446
}
34473447

3448+
/** A marker trait representing an as-yet unevaluated type
3449+
* which doesn't assign flags to the underlying symbol.
3450+
*/
3451+
trait FlagAgnosticCompleter extends LazyType
3452+
3453+
/** A marker trait representing an as-yet unevaluated type
3454+
* which assigns flags to the underlying symbol.
3455+
*/
3456+
trait FlagAssigningCompleter extends LazyType
3457+
34483458
abstract class LazyPolyType(override val typeParams: List[Symbol]) extends LazyType {
34493459
override def safeToString =
34503460
(if (typeParams.isEmpty) "" else typeParamsString(this)) + super.safeToString

src/reflect/scala/reflect/internal/pickling/UnPickler.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -848,7 +848,7 @@ abstract class UnPickler /*extends scala.reflect.generic.UnPickler*/ {
848848
}
849849

850850
/** A lazy type which when completed returns type at index `i`. */
851-
private class LazyTypeRef(i: Int) extends LazyType {
851+
private class LazyTypeRef(i: Int) extends LazyType with FlagAgnosticCompleter {
852852
private val definedAtRunId = currentRunId
853853
private val p = phase
854854
override def complete(sym: Symbol) : Unit = try {

0 commit comments

Comments
 (0)