Skip to content

Commit ed7625f

Browse files
committed
finality for trait setters, annotations
traitsetter annot for all concrete trait-owned setters stumble towards juggling annotations correctly
1 parent 492aa60 commit ed7625f

File tree

4 files changed

+29
-29
lines changed

4 files changed

+29
-29
lines changed

src/compiler/scala/tools/nsc/backend/jvm/BTypesFromSymbols.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -566,7 +566,7 @@ class BTypesFromSymbols[G <: Global](val global: G) extends BTypes {
566566
(((sym.rawflags & symtab.Flags.FINAL) != 0) || isTopLevelModuleClass(sym))
567567
&& !sym.enclClass.isInterface
568568
&& !sym.isClassConstructor
569-
&& !sym.isMutable // lazy vals and vars both
569+
&& (!sym.isMutable || nme.isTraitSetterName(sym.name)) // lazy vals and vars and their setters cannot be final, but trait setters are
570570
)
571571

572572
// Primitives are "abstract final" to prohibit instantiation

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

Lines changed: 21 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@ abstract class Fields extends InfoTransform with ast.TreeDSL with TypingTransfor
3636
val phaseName: String = "fields"
3737

3838
// used for internal communication between info and tree transform of this phase -- not pickled, not in initialflags
39-
override def phaseNewFlags: Long = NEEDS_TREES | OVERRIDDEN_TRAIT_SETTER
39+
override def phaseNewFlags: Long = NEEDS_TREES | OVERRIDDEN_TRAIT_SETTER | FINAL_TRAIT_ACCESSOR
4040

4141
protected def newTransformer(unit: CompilationUnit): Transformer =
4242
new FieldsTransformer(unit)
@@ -91,25 +91,7 @@ abstract class Fields extends InfoTransform with ast.TreeDSL with TypingTransfor
9191
// NOTE: this only considers type, filter on flags first!
9292
def fieldMemoizationIn(accessorOrField: Symbol, site: Symbol) = new FieldMemoization(accessorOrField, site)
9393

94-
def filterAccessorFieldAnnotations(sym: Symbol, tp: Type) = {
95-
if ((sym.isAccessor || (sym.isValue && !sym.isMethod)) && sym.owner.isTrait) {
96-
// TODO: beansetter/beangetters...
97-
val category = if (sym.isGetter) GetterTargetClass else if (sym.isSetter) SetterTargetClass else FieldTargetClass
98-
val defaultRetention = !sym.isSetter // TODO: is this right? consider `@deprecated val x` --> in the trait: `@deprecated def x`
99-
100-
val annotations = sym.annotations filter AnnotationInfo.mkFilter(category, defaultRetention)
101-
102-
// TODO: does it matter at which phase we do this?
103-
// println(s"annotations for $sym: $annotations (out of ${sym.annotations})")
104-
105-
sym setAnnotations annotations
106-
}
107-
108-
tp
109-
}
110-
111-
112-
override def transformInfo(sym: Symbol, tp: Type): Type = synthFieldsAndAccessors(filterAccessorFieldAnnotations(sym, tp))
94+
override def transformInfo(sym: Symbol, tp: Type): Type = synthFieldsAndAccessors(tp)
11395

11496
private def newTraitSetter(getter: Symbol, clazz: Symbol) = {
11597
// Add setter for an immutable, memoizing getter
@@ -125,7 +107,6 @@ abstract class Fields extends InfoTransform with ast.TreeDSL with TypingTransfor
125107
val fieldTp = fieldTypeForGetterIn(getter, clazz.thisType)
126108
// println(s"newTraitSetter in $clazz for $getter = $setterName : $fieldTp")
127109
setter setInfo MethodType(List(setter.newSyntheticValueParam(fieldTp)), UnitTpe)
128-
setter addAnnotation TraitSetterAnnotationClass
129110
setter
130111
}
131112

@@ -146,7 +127,14 @@ abstract class Fields extends InfoTransform with ast.TreeDSL with TypingTransfor
146127
if (!(accessor hasFlag (DEFERRED | LAZY)) && fieldMemoizationIn(accessor, clazz).needsField) {
147128
// in a trait, a memoized accessor becomes deferred
148129
// (it'll receive an implementation in the first real class to extend this trait)
149-
markAccessorImplementedInSubclass(accessor)
130+
131+
// can't mark getter as FINAL in trait, but remember for when we synthetisize the impl in the subclass to make it FINAL
132+
val finality = if (accessor hasFlag FINAL) FINAL_TRAIT_ACCESSOR else 0
133+
accessor setFlag (finality | lateFINAL | DEFERRED | SYNTHESIZE_IMPL_IN_SUBCLASS)
134+
135+
// trait members cannot be final (but the synthesized ones should be)
136+
// LOCAL no longer applies (already made not-private)
137+
accessor resetFlag (FINAL | LOCAL)
150138

151139
if ((accessor hasFlag STABLE) && accessor.isGetter) // TODO: isGetter is probably redundant?
152140
newSetters += newTraitSetter(accessor, clazz)
@@ -213,7 +201,10 @@ abstract class Fields extends InfoTransform with ast.TreeDSL with TypingTransfor
213201
// mixin field accessors
214202
val mixedInFieldAndAccessors = accessorsMaybeNeedingImpl flatMap { accessor =>
215203
def cloneAccessor() = {
216-
val clonedAccessor = (accessor cloneSymbol clazz) setPos clazz.pos setFlag NEEDS_TREES resetFlag DEFERRED | SYNTHESIZE_IMPL_IN_SUBCLASS
204+
val clonedAccessor = (accessor cloneSymbol clazz) setPos clazz.pos setFlag NEEDS_TREES resetFlag DEFERRED | SYNTHESIZE_IMPL_IN_SUBCLASS | FINAL_TRAIT_ACCESSOR
205+
if (accessor hasFlag FINAL_TRAIT_ACCESSOR) {
206+
clonedAccessor setFlag FINAL | lateFINAL // lateFINAL thrown in for good measure, by analogy to makeNotPrivate
207+
}
217208
// if we don't cloneInfo, method argument symbols are shared between trait and subclasses --> lambalift proxy crash
218209
// TODO: use derive symbol variant?
219210
val clonedInfo = accessor.info.cloneInfo(clonedAccessor)
@@ -226,13 +217,15 @@ abstract class Fields extends InfoTransform with ast.TreeDSL with TypingTransfor
226217
// a trait setter for an overridden val will receive a unit body in the tree transform
227218
// (this is communicated using the DEFERRED flag)
228219
if (nme.isTraitSetterName(accessor.name)) {
229-
val overridden = isOverriddenAccessor(accessor.getterIn(accessor.owner), clazz)
220+
val getter = accessor.getterIn(accessor.owner)
221+
val overridden = isOverriddenAccessor(getter, clazz)
230222
// println(s"mixing in trait setter ${accessor.defString}: $overridden")
231223
val clone = cloneAccessor()
232224

233225
clone filterAnnotations (ai => !ai.matches(TraitSetterAnnotationClass)) // only supposed to be set in trait
234226

235227
if (overridden) clone setFlag OVERRIDDEN_TRAIT_SETTER
228+
else if (getter.isEffectivelyFinal) clone setFlag FINAL // TODO: why isn't the FINAL_TRAIT_ACCESSOR carry-over from getter enough?
236229
// println(s"mixed in trait setter ${clone.defString}")
237230

238231
List(clone)
@@ -250,6 +243,10 @@ abstract class Fields extends InfoTransform with ast.TreeDSL with TypingTransfor
250243
| (if (accessor.hasStableFlag) 0 else MUTABLE)
251244
)
252245

246+
// TODO: filter getter's annotations to exclude those only meant for the field
247+
// we must keep them around long enough to see them here, though, when we create the field
248+
field setAnnotations (accessor.annotations filter AnnotationInfo.mkFilter(FieldTargetClass, defaultRetention = true))
249+
253250
field setFlag newFlags
254251
List(cloneAccessor(), field)
255252
} else List(cloneAccessor())
@@ -276,9 +273,6 @@ abstract class Fields extends InfoTransform with ast.TreeDSL with TypingTransfor
276273
}
277274
}
278275

279-
private def markAccessorImplementedInSubclass(accessor: Symbol): Symbol =
280-
accessor setFlag (DEFERRED | SYNTHESIZE_IMPL_IN_SUBCLASS) resetFlag (FINAL | LOCAL) // already made not-private
281-
282276
private def accessorImplementedInSubclass(accessor: Symbol) =
283277
accessor hasAllFlags (ACCESSOR | SYNTHESIZE_IMPL_IN_SUBCLASS)
284278

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

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -175,6 +175,7 @@ class Flags extends ModifierFlags {
175175
final val SYNTHESIZE_IMPL_IN_SUBCLASS = 1L << 50 // Like MIXEDIN, but used in Fields
176176
final val NEEDS_TREES = 1L << 59 // Communicate from Fields' info transform to its tree transform -- this symbol needs a tree. (distinct from SYNTHESIZE_IMPL_IN_SUBCLASS)
177177
final val OVERRIDDEN_TRAIT_SETTER = 1L << 60 // Communicate from Fields' info transform to its tree transform -- this setter gets a unit body.
178+
final val FINAL_TRAIT_ACCESSOR = 1L << 61 // Communicate from Fields' info transform to its tree transform -- this accessor's synthesized implementation should be final.
178179

179180
// ------- shift definitions -------------------------------------------------------
180181
//
@@ -470,7 +471,7 @@ class Flags extends ModifierFlags {
470471
case `notPRIVATE` => "<notprivate>" // (1L << 58)
471472
case SYNTHESIZE_IMPL_IN_SUBCLASS => "<sub_synth>" // (1L << 59)
472473
case OVERRIDDEN_TRAIT_SETTER => "<overridden_trait_setter>" // (1L << 60)
473-
case 0x2000000000000000L => "" // (1L << 61)
474+
case FINAL_TRAIT_ACCESSOR => "<final_trait_acc>" // (1L << 61)
474475
case 0x4000000000000000L => "" // (1L << 62)
475476
case 0x8000000000000000L => "" // (1L << 63)
476477
case _ => ""

test/files/trait-defaults/fields.scala

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,8 @@
1+
trait T { final val bla: Int = 123 }
2+
class C extends T // bla should be final in C
3+
4+
5+
16
trait T { final val C = "S" }
27
// there should be a C method in `T$class`!
38
class C extends T { println(C) }

0 commit comments

Comments
 (0)