summaryrefslogtreecommitdiff
path: root/src/compiler/scala/tools/nsc/transform/Fields.scala
diff options
context:
space:
mode:
Diffstat (limited to 'src/compiler/scala/tools/nsc/transform/Fields.scala')
-rw-r--r--src/compiler/scala/tools/nsc/transform/Fields.scala72
1 files changed, 45 insertions, 27 deletions
diff --git a/src/compiler/scala/tools/nsc/transform/Fields.scala b/src/compiler/scala/tools/nsc/transform/Fields.scala
index 0fe7a82b15..fbf1e8cec1 100644
--- a/src/compiler/scala/tools/nsc/transform/Fields.scala
+++ b/src/compiler/scala/tools/nsc/transform/Fields.scala
@@ -176,14 +176,25 @@ abstract class Fields extends InfoTransform with ast.TreeDSL with TypingTransfor
// NOTE: this only considers type, filter on flags first!
def fieldMemoizationIn(accessorOrField: Symbol, site: Symbol) = new FieldMemoization(accessorOrField, site)
- // drop field-targeting annotations from getters
+ // drop field-targeting annotations from getters (done during erasure because we first need to create the field symbol)
// (in traits, getters must also hold annotations that target the underlying field,
// because the latter won't be created until the trait is mixed into a class)
// TODO do bean getters need special treatment to suppress field-targeting annotations in traits?
def dropFieldAnnotationsFromGetter(sym: Symbol) =
- if (sym.isGetter && sym.owner.isTrait) {
- sym setAnnotations (sym.annotations filter AnnotationInfo.mkFilter(GetterTargetClass, defaultRetention = false))
- }
+ sym setAnnotations (sym.annotations filter AnnotationInfo.mkFilter(GetterTargetClass, defaultRetention = false))
+
+ def symbolAnnotationsTargetFieldAndGetter(sym: Symbol): Boolean = sym.isGetter && (sym.isLazy || sym.owner.isTrait)
+
+ // A trait val/var or a lazy val does not receive an underlying field symbol until this phase.
+ // Since annotations need a carrier symbol from the beginning, both field- and getter-targeting annotations
+ // are kept on the getter symbol for these until they are dropped by dropFieldAnnotationsFromGetter
+ def getterTreeAnnotationsTargetFieldAndGetter(owner: Symbol, mods: Modifiers) = mods.isLazy || owner.isTrait
+
+ // Propagate field-targeting annotations from getter to field.
+ // By the way, we must keep them around long enough to see them here (now that we have created the field),
+ // which is why dropFieldAnnotationsFromGetter is not called until erasure.
+ private def propagateFieldAnnotations(getter: Symbol, field: TermSymbol): Unit =
+ field setAnnotations (getter.annotations filter AnnotationInfo.mkFilter(FieldTargetClass, defaultRetention = true))
// can't use the referenced field since it already tracks the module's moduleClass
@@ -241,6 +252,7 @@ abstract class Fields extends InfoTransform with ast.TreeDSL with TypingTransfor
sym
}
+
private object synthFieldsAndAccessors extends TypeMap {
private def newTraitSetter(getter: Symbol, clazz: Symbol) = {
// Add setter for an immutable, memoizing getter
@@ -388,10 +400,12 @@ abstract class Fields extends InfoTransform with ast.TreeDSL with TypingTransfor
val accessorSymbolSynth = checkedAccessorSymbolSynth(tp.typeSymbol)
// expand module def in class/object (if they need it -- see modulesNeedingExpansion above)
- val expandedModulesAndLazyVals = (
+ val expandedModulesAndLazyVals =
modulesAndLazyValsNeedingExpansion flatMap { member =>
if (member.isLazy) {
- List(newLazyVarMember(member), accessorSymbolSynth.newSlowPathSymbol(member))
+ val lazyVar = newLazyVarMember(member)
+ propagateFieldAnnotations(member, lazyVar)
+ List(lazyVar, accessorSymbolSynth.newSlowPathSymbol(member))
}
// expanding module def (top-level or nested in static module)
else List(if (member.isStatic) { // implies m.isOverridingSymbol as per above filter
@@ -404,7 +418,7 @@ abstract class Fields extends InfoTransform with ast.TreeDSL with TypingTransfor
member setFlag NEEDS_TREES
newModuleVarMember(member)
})
- })
+ }
// println(s"expanded modules for $clazz: $expandedModules")
@@ -419,8 +433,9 @@ abstract class Fields extends InfoTransform with ast.TreeDSL with TypingTransfor
val clonedAccessor = (member cloneSymbol clazz) setPos clazz.pos
setMixedinAccessorFlags(member, clonedAccessor)
- if (clonedAccessor.isGetter)
- clonedAccessor setAnnotations (clonedAccessor.annotations filter AnnotationInfo.mkFilter(GetterTargetClass, defaultRetention = false))
+ // note: check original member when deciding how to triage annotations, then act on the cloned accessor
+ if (symbolAnnotationsTargetFieldAndGetter(member)) // this simplifies to member.isGetter, but the full formulation really ties the triage together
+ dropFieldAnnotationsFromGetter(clonedAccessor)
// if we don't cloneInfo, method argument symbols are shared between trait and subclasses --> lambalift proxy crash
// TODO: use derive symbol variant?
@@ -450,7 +465,11 @@ abstract class Fields extends InfoTransform with ast.TreeDSL with TypingTransfor
}
else if (member hasFlag LAZY) {
val mixedinLazy = cloneAccessor()
- val lazyVar = newLazyVarMember(mixedinLazy)
+ val lazyVar = newLazyVarMember(mixedinLazy) // link lazy var member to the mixedin lazy accessor
+
+ // propagate from original member. since mixed in one has only retained the annotations targeting the getter
+ propagateFieldAnnotations(member, lazyVar)
+
// println(s"mixing in lazy var: $lazyVar for $member")
List(lazyVar, accessorSymbolSynth.newSlowPathSymbol(mixedinLazy), newSuperLazy(mixedinLazy, site, lazyVar))
}
@@ -460,9 +479,7 @@ abstract class Fields extends InfoTransform with ast.TreeDSL with TypingTransfor
setFieldFlags(member, field)
- // filter getter's annotations to exclude those only meant for the field
- // we must keep them around long enough to see them here, though, when we create the field
- field setAnnotations (member.annotations filter AnnotationInfo.mkFilter(FieldTargetClass, defaultRetention = true))
+ propagateFieldAnnotations(member, field)
List(cloneAccessor(), field)
} else List(cloneAccessor()) // no field needed (constant-typed getter has constant as its RHS)
@@ -510,6 +527,16 @@ abstract class Fields extends InfoTransform with ast.TreeDSL with TypingTransfor
def nonStaticModuleToMethod(module: Symbol): Unit =
if (!module.isStatic) module setFlag METHOD | STABLE
+ // scala/scala-dev#219, scala/scala-dev#268
+ // Cast to avoid spurious mismatch in paths containing trait vals that have
+ // not been rebound to accessors in the subclass we're in now.
+ // For example, for a lazy val mixed into a class, the lazy var's info
+ // will not refer to symbols created during our info transformer,
+ // so if its type depends on a val that is now implemented after the info transformer,
+ // we'll get a mismatch when assigning `rhs` to `lazyVarOf(getter)`.
+ // TODO: could we rebind more aggressively? consider overriding in type equality?
+ def castHack(tree: Tree, pt: Type) = gen.mkAsInstanceOf(tree, pt)
+
class FieldsTransformer(unit: CompilationUnit) extends TypingTransformer(unit) with CheckedAccessorTreeSynthesis {
protected def typedPos(pos: Position)(tree: Tree): Tree = localTyper.typedPos(pos)(tree)
@@ -596,15 +623,6 @@ abstract class Fields extends InfoTransform with ast.TreeDSL with TypingTransfor
// synth trees for accessors/fields and trait setters when they are mixed into a class
def fieldsAndAccessors(clazz: Symbol): List[Tree] = {
- // scala/scala-dev#219
- // Cast to avoid spurious mismatch in paths containing trait vals that have
- // not been rebound to accessors in the subclass we're in now.
- // For example, for a lazy val mixed into a class, the lazy var's info
- // will not refer to symbols created during our info transformer,
- // so if its type depends on a val that is now implemented after the info transformer,
- // we'll get a mismatch when assigning `rhs` to `lazyVarOf(getter)`.
- // TODO: could we rebind more aggressively? consider overriding in type equality?
- def cast(tree: Tree, pt: Type) = gen.mkAsInstanceOf(tree, pt)
// Could be NoSymbol, which denotes an error, but it's refchecks' job to report it (this fallback is for robustness).
// This is the result of overriding a val with a def, so that no field is found in the subclass.
@@ -615,14 +633,14 @@ abstract class Fields extends InfoTransform with ast.TreeDSL with TypingTransfor
// accessor created by newMatchingModuleAccessor for a static module that does need an accessor
// (because there's a matching member in a super class)
if (getter.asTerm.referenced.isModule)
- mkAccessor(getter)(cast(Select(This(clazz), getter.asTerm.referenced), getter.info.resultType))
+ mkAccessor(getter)(castHack(Select(This(clazz), getter.asTerm.referenced), getter.info.resultType))
else {
val fieldMemoization = fieldMemoizationIn(getter, clazz)
// TODO: drop getter for constant? (when we no longer care about producing identical bytecode?)
if (fieldMemoization.constantTyped) mkAccessor(getter)(gen.mkAttributedQualifier(fieldMemoization.tp))
else fieldAccess(getter) match {
case NoSymbol => EmptyTree
- case fieldSel => mkAccessor(getter)(cast(Select(This(clazz), fieldSel), getter.info.resultType))
+ case fieldSel => mkAccessor(getter)(castHack(Select(This(clazz), fieldSel), getter.info.resultType))
}
}
@@ -636,7 +654,7 @@ abstract class Fields extends InfoTransform with ast.TreeDSL with TypingTransfor
else fieldAccess(setter) match {
case NoSymbol => EmptyTree
case fieldSel => afterOwnPhase { // the assign only type checks after our phase (assignment to val)
- mkAccessor(setter)(Assign(Select(This(clazz), fieldSel), cast(Ident(setter.firstParam), fieldSel.info)))
+ mkAccessor(setter)(Assign(Select(This(clazz), fieldSel), castHack(Ident(setter.firstParam), fieldSel.info)))
}
}
@@ -657,7 +675,7 @@ abstract class Fields extends InfoTransform with ast.TreeDSL with TypingTransfor
val selectSuper = Select(Super(This(clazz), tpnme.EMPTY), getter.name)
val lazyVar = lazyVarOf(getter)
- val rhs = cast(Apply(selectSuper, Nil), lazyVar.info)
+ val rhs = castHack(Apply(selectSuper, Nil), lazyVar.info)
synthAccessorInClass.expandLazyClassMember(lazyVar, getter, rhs)
}
@@ -708,7 +726,7 @@ abstract class Fields extends InfoTransform with ast.TreeDSL with TypingTransfor
val transformedRhs = atOwner(statSym)(transform(rhs))
if (rhs == EmptyTree) mkAccessor(statSym)(EmptyTree)
- else if (currOwner.isTrait) mkAccessor(statSym)(transformedRhs)
+ else if (currOwner.isTrait) mkAccessor(statSym)(castHack(transformedRhs, statSym.info.resultType))
else if (!currOwner.isClass) mkLazyLocalDef(vd.symbol, transformedRhs)
else {
// TODO: make `synthAccessorInClass` a field and update it in atOwner?