@@ -176,14 +176,25 @@ abstract class Fields extends InfoTransform with ast.TreeDSL with TypingTransfor
176
176
// NOTE: this only considers type, filter on flags first!
177
177
def fieldMemoizationIn (accessorOrField : Symbol , site : Symbol ) = new FieldMemoization (accessorOrField, site)
178
178
179
- // drop field-targeting annotations from getters
179
+ // drop field-targeting annotations from getters (done during erasure because we first need to create the field symbol)
180
180
// (in traits, getters must also hold annotations that target the underlying field,
181
181
// because the latter won't be created until the trait is mixed into a class)
182
182
// TODO do bean getters need special treatment to suppress field-targeting annotations in traits?
183
183
def dropFieldAnnotationsFromGetter (sym : Symbol ) =
184
- if (sym.isGetter && sym.owner.isTrait) {
185
- sym setAnnotations (sym.annotations filter AnnotationInfo .mkFilter(GetterTargetClass , defaultRetention = false ))
186
- }
184
+ sym setAnnotations (sym.annotations filter AnnotationInfo .mkFilter(GetterTargetClass , defaultRetention = false ))
185
+
186
+ def symbolAnnotationsTargetFieldAndGetter (sym : Symbol ): Boolean = sym.isGetter && (sym.isLazy || sym.owner.isTrait)
187
+
188
+ // A trait val/var or a lazy val does not receive an underlying field symbol until this phase.
189
+ // Since annotations need a carrier symbol from the beginning, both field- and getter-targeting annotations
190
+ // are kept on the getter symbol for these until they are dropped by dropFieldAnnotationsFromGetter
191
+ def getterTreeAnnotationsTargetFieldAndGetter (owner : Symbol , mods : Modifiers ) = mods.isLazy || owner.isTrait
192
+
193
+ // Propagate field-targeting annotations from getter to field.
194
+ // By the way, we must keep them around long enough to see them here (now that we have created the field),
195
+ // which is why dropFieldAnnotationsFromGetter is not called until erasure.
196
+ private def propagateFieldAnnotations (getter : Symbol , field : TermSymbol ): Unit =
197
+ field setAnnotations (getter.annotations filter AnnotationInfo .mkFilter(FieldTargetClass , defaultRetention = true ))
187
198
188
199
189
200
// 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
241
252
sym
242
253
}
243
254
255
+
244
256
private object synthFieldsAndAccessors extends TypeMap {
245
257
private def newTraitSetter (getter : Symbol , clazz : Symbol ) = {
246
258
// Add setter for an immutable, memoizing getter
@@ -388,10 +400,12 @@ abstract class Fields extends InfoTransform with ast.TreeDSL with TypingTransfor
388
400
val accessorSymbolSynth = checkedAccessorSymbolSynth(tp.typeSymbol)
389
401
390
402
// expand module def in class/object (if they need it -- see modulesNeedingExpansion above)
391
- val expandedModulesAndLazyVals = (
403
+ val expandedModulesAndLazyVals =
392
404
modulesAndLazyValsNeedingExpansion flatMap { member =>
393
405
if (member.isLazy) {
394
- List (newLazyVarMember(member), accessorSymbolSynth.newSlowPathSymbol(member))
406
+ val lazyVar = newLazyVarMember(member)
407
+ propagateFieldAnnotations(member, lazyVar)
408
+ List (lazyVar, accessorSymbolSynth.newSlowPathSymbol(member))
395
409
}
396
410
// expanding module def (top-level or nested in static module)
397
411
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
404
418
member setFlag NEEDS_TREES
405
419
newModuleVarMember(member)
406
420
})
407
- })
421
+ }
408
422
409
423
// println(s"expanded modules for $clazz: $expandedModules")
410
424
@@ -419,8 +433,9 @@ abstract class Fields extends InfoTransform with ast.TreeDSL with TypingTransfor
419
433
val clonedAccessor = (member cloneSymbol clazz) setPos clazz.pos
420
434
setMixedinAccessorFlags(member, clonedAccessor)
421
435
422
- if (clonedAccessor.isGetter)
423
- clonedAccessor setAnnotations (clonedAccessor.annotations filter AnnotationInfo .mkFilter(GetterTargetClass , defaultRetention = false ))
436
+ // note: check original member when deciding how to triage annotations, then act on the cloned accessor
437
+ if (symbolAnnotationsTargetFieldAndGetter(member)) // this simplifies to member.isGetter, but the full formulation really ties the triage together
438
+ dropFieldAnnotationsFromGetter(clonedAccessor)
424
439
425
440
// if we don't cloneInfo, method argument symbols are shared between trait and subclasses --> lambalift proxy crash
426
441
// TODO: use derive symbol variant?
@@ -450,7 +465,11 @@ abstract class Fields extends InfoTransform with ast.TreeDSL with TypingTransfor
450
465
}
451
466
else if (member hasFlag LAZY ) {
452
467
val mixedinLazy = cloneAccessor()
453
- val lazyVar = newLazyVarMember(mixedinLazy)
468
+ val lazyVar = newLazyVarMember(mixedinLazy) // link lazy var member to the mixedin lazy accessor
469
+
470
+ // propagate from original member. since mixed in one has only retained the annotations targeting the getter
471
+ propagateFieldAnnotations(member, lazyVar)
472
+
454
473
// println(s"mixing in lazy var: $lazyVar for $member")
455
474
List (lazyVar, accessorSymbolSynth.newSlowPathSymbol(mixedinLazy), newSuperLazy(mixedinLazy, site, lazyVar))
456
475
}
@@ -460,9 +479,7 @@ abstract class Fields extends InfoTransform with ast.TreeDSL with TypingTransfor
460
479
461
480
setFieldFlags(member, field)
462
481
463
- // filter getter's annotations to exclude those only meant for the field
464
- // we must keep them around long enough to see them here, though, when we create the field
465
- field setAnnotations (member.annotations filter AnnotationInfo .mkFilter(FieldTargetClass , defaultRetention = true ))
482
+ propagateFieldAnnotations(member, field)
466
483
467
484
List (cloneAccessor(), field)
468
485
} else List (cloneAccessor()) // no field needed (constant-typed getter has constant as its RHS)
0 commit comments