diff options
Diffstat (limited to 'src/compiler/scala/tools/nsc/transform/Fields.scala')
-rw-r--r-- | src/compiler/scala/tools/nsc/transform/Fields.scala | 72 |
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? |