1
- /* NSC -- new Scala compiler
2
- * Copyright 2005-2013 LAMP/EPFL and Lightbend, Inc
3
- */
1
+ // Copyright 2005-2017 LAMP/EPFL and Lightbend, Inc
4
2
5
3
package scala .tools .nsc
6
4
package transform
@@ -95,75 +93,77 @@ trait AccessorSynthesis extends Transform with ast.TreeDSL {
95
93
}
96
94
97
95
case class BitmapInfo (symbol : Symbol , mask : Literal ) {
98
- def storageClass : ClassSymbol = symbol.info.typeSymbol.asClass
96
+ def select (on : This ): Tree = Select (on, symbol)
97
+ def applyToMask (on : This , op : Name ): Tree = Apply (member(select(on), op), List (mask))
98
+ def member (bitmapRef : Tree , name : Name ): Tree = Select (bitmapRef, getMember(storageClass, name))
99
+ def convert (bitmapRef : Tree ): Tree = Apply (member(bitmapRef, newTermName(" to" + storageClass.name)), Nil )
100
+
101
+ def isLong : Boolean = storageClass == LongClass
102
+ def isBoolean : Boolean = storageClass == BooleanClass
103
+
104
+ lazy val storageClass : ClassSymbol = symbol.info.typeSymbol.asClass
99
105
}
100
106
101
107
102
108
// TODO: better way to communicate from info transform to tree transform?
103
109
private [this ] val _bitmapInfo = perRunCaches.newMap[Symbol , BitmapInfo ]
104
110
private [this ] val _slowPathFor = perRunCaches.newMap[Symbol , Symbol ]()
105
111
106
- def checkedAccessorSymbolSynth (clz : Symbol ) =
107
- if (settings.checkInit) new CheckInitAccessorSymbolSynth { val clazz = clz }
108
- else new CheckedAccessorSymbolSynth { val clazz = clz }
109
-
110
- // base trait, with enough functionality for lazy vals -- CheckInitAccessorSymbolSynth adds logic for -Xcheckinit
111
- trait CheckedAccessorSymbolSynth {
112
- protected val clazz : Symbol
113
-
114
- protected def defaultPos = clazz.pos.focus
115
- protected def isTrait = clazz.isTrait
116
- protected def hasTransientAnnot (field : Symbol ) = field.accessedOrSelf hasAnnotation TransientAttr
117
-
118
- def needsBitmap (sym : Symbol ): Boolean = ! (isTrait || sym.isDeferred) && sym.isMethod && sym.isLazy && ! sym.isSpecialized
119
-
112
+ def checkedAccessorSymbolSynth (clz : Symbol ): CheckedAccessorSymbolSynth =
113
+ new CheckedAccessorSymbolSynth (clz)
120
114
121
- /** Examines the symbol and returns a name indicating what brand of
122
- * bitmap it requires. The possibilities are the BITMAP_* vals
123
- * defined in StdNames. If it needs no bitmap, nme.NO_NAME.
115
+ // base trait, with enough functionality for generating bitmap symbols for lazy vals and -Xcheckinit fields
116
+ class CheckedAccessorSymbolSynth (val clazz : Symbol ) {
117
+ /**
118
+ * Note: fields of classes inheriting DelayedInit are not checked.
119
+ * This is because they are neither initialized in the constructor
120
+ * nor do they have a setter (not if they are vals anyway). The usual
121
+ * logic for setting bitmaps does therefore not work for such fields.
122
+ * That's why they are excluded.
124
123
*
125
- * bitmaps for checkinit fields are not inherited
126
124
*/
127
- protected def bitmapCategory (sym : Symbol ): Name = {
128
- // ensure that nested objects are transformed TODO: still needed?
129
- sym.initialize
125
+ private [this ] val doCheckInit = settings.checkInit.value && ! (clazz isSubClass DelayedInitClass )
130
126
131
- import nme ._
132
-
133
- if (needsBitmap(sym) && sym.isLazy)
134
- if (hasTransientAnnot(sym)) BITMAP_TRANSIENT else BITMAP_NORMAL
135
- else NO_NAME
136
- }
137
-
138
-
139
- def bitmapFor (sym : Symbol ): BitmapInfo = _bitmapInfo(sym)
140
- protected def hasBitmap (sym : Symbol ): Boolean = _bitmapInfo isDefinedAt sym
127
+ private [AccessorSynthesis ] def bitmapFor (field : Symbol ): BitmapInfo = _bitmapInfo(field)
128
+ protected def bitmapOf (field : Symbol ): Option [BitmapInfo ] = _bitmapInfo.get(field)
141
129
142
130
143
131
/** Fill the map from fields to bitmap infos.
132
+ * This is called for all fields in each transformed class (by the fields info transformer),
133
+ * after the fields inherited from traits have been added.
144
134
*
145
- * Instead of field symbols, the map keeps their getter symbols. This makes code generation easier later.
135
+ * bitmaps for checkinit fields are not inherited
146
136
*/
147
- def computeBitmapInfos (decls : List [Symbol ]): List [Symbol ] = {
148
- def doCategory (fields : List [Symbol ], category : Name ) = {
149
- val nbFields = fields.length // we know it's > 0
137
+ def computeBitmapInfos (fields : List [Symbol ]): List [Symbol ] = {
138
+ def bitmapCategory (field : Symbol ): Name = {
139
+ import nme ._
140
+
141
+ if (field.isLazy)
142
+ if (field hasAnnotation TransientAttr ) BITMAP_TRANSIENT else BITMAP_NORMAL
143
+ else if (doCheckInit && ! (field hasFlag DEFAULTINIT | PRESUPER ))
144
+ if (field hasAnnotation TransientAttr ) BITMAP_CHECKINIT_TRANSIENT else BITMAP_CHECKINIT
145
+ else NO_NAME
146
+ }
147
+
148
+ def allocateBitmaps (fieldsWithBitmaps : List [Symbol ], category : Name ) = {
149
+ val nbFields = fieldsWithBitmaps.length // we know it's > 0
150
150
val (bitmapClass, bitmapCapacity) =
151
- if (nbFields == 1 ) (BooleanClass , 1 )
152
- else if (nbFields <= 8 ) (ByteClass , 8 )
153
- else if (nbFields <= 32 ) (IntClass , 32 )
154
- else (LongClass , 64 )
151
+ if (nbFields == 1 ) (BooleanClass , 1 )
152
+ else if (nbFields <= 8 ) (ByteClass , 8 )
153
+ else if (nbFields <= 32 ) (IntClass , 32 )
154
+ else (LongClass , 64 )
155
155
156
156
// 0-based index of highest bit, divided by bits per bitmap
157
157
// note that this is only ever > 0 when bitmapClass == LongClass
158
158
val maxBitmapNumber = (nbFields - 1 ) / bitmapCapacity
159
159
160
160
// transient fields get their own category
161
- val isTransientCategory = fields.head hasAnnotation TransientAttr
161
+ val isTransientCategory = nme.isTransientBitmap(category)
162
162
163
163
val bitmapSyms =
164
164
(0 to maxBitmapNumber).toArray map { bitmapNumber =>
165
165
val bitmapSym = (
166
- clazz.newVariable(nme.newBitmapName(category, bitmapNumber).toTermName, defaultPos )
166
+ clazz.newVariable(nme.newBitmapName(category, bitmapNumber).toTermName, clazz.pos.focus )
167
167
setInfo bitmapClass.tpe
168
168
setFlag PrivateLocal | NEEDS_TREES
169
169
)
@@ -175,7 +175,7 @@ trait AccessorSynthesis extends Transform with ast.TreeDSL {
175
175
bitmapSym
176
176
}
177
177
178
- fields .zipWithIndex foreach { case (f, idx) =>
178
+ fieldsWithBitmaps .zipWithIndex foreach { case (f, idx) =>
179
179
val bitmapIdx = idx / bitmapCapacity
180
180
val offsetInBitmap = idx % bitmapCapacity
181
181
val mask =
@@ -188,60 +188,23 @@ trait AccessorSynthesis extends Transform with ast.TreeDSL {
188
188
bitmapSyms
189
189
}
190
190
191
- decls groupBy bitmapCategory flatMap {
192
- case (category, fields) if category != nme.NO_NAME && fields.nonEmpty => doCategory (fields, category)
191
+ fields groupBy bitmapCategory flatMap {
192
+ case (category, fields) if category != nme.NO_NAME && fields.nonEmpty => allocateBitmaps (fields, category)
193
193
case _ => Nil
194
194
} toList
195
195
}
196
196
197
197
def slowPathFor (lzyVal : Symbol ): Symbol = _slowPathFor(lzyVal)
198
198
199
199
def newSlowPathSymbol (lzyVal : Symbol ): Symbol = {
200
- val pos = if (lzyVal.pos != NoPosition ) lzyVal.pos else defaultPos // TODO: is the else branch ever taken?
200
+ val pos = if (lzyVal.pos != NoPosition ) lzyVal.pos else clazz.pos.focus // TODO: is the else branch ever taken?
201
201
val sym = clazz.newMethod(nme.newLazyValSlowComputeName(lzyVal.name.toTermName), pos, PRIVATE ) setInfo MethodType (Nil , lzyVal.tpe.resultType)
202
202
_slowPathFor(lzyVal) = sym
203
203
sym
204
204
}
205
205
206
206
}
207
207
208
- trait CheckInitAccessorSymbolSynth extends CheckedAccessorSymbolSynth {
209
- /** Does this field require an initialized bit?
210
- * Note: fields of classes inheriting DelayedInit are not checked.
211
- * This is because they are neither initialized in the constructor
212
- * nor do they have a setter (not if they are vals anyway). The usual
213
- * logic for setting bitmaps does therefore not work for such fields.
214
- * That's why they are excluded.
215
- * Note: The `checkinit` option does not check if transient fields are initialized.
216
- */
217
- protected def needsInitFlag (sym : Symbol ): Boolean =
218
- sym.isGetter &&
219
- ! ( sym.isInitializedToDefault
220
- || isConstantType(sym.info.finalResultType) // scala/bug#4742
221
- || sym.hasFlag(PARAMACCESSOR | SPECIALIZED | LAZY )
222
- || sym.accessed.hasFlag(PRESUPER )
223
- || sym.isOuterAccessor
224
- || (sym.owner isSubClass DelayedInitClass )
225
- || (sym.accessed hasAnnotation TransientAttr ))
226
-
227
- /** Examines the symbol and returns a name indicating what brand of
228
- * bitmap it requires. The possibilities are the BITMAP_* vals
229
- * defined in StdNames. If it needs no bitmap, nme.NO_NAME.
230
- *
231
- * bitmaps for checkinit fields are not inherited
232
- */
233
- override protected def bitmapCategory (sym : Symbol ): Name = {
234
- import nme ._
235
-
236
- super .bitmapCategory(sym) match {
237
- case NO_NAME if needsInitFlag(sym) && ! sym.isDeferred =>
238
- if (hasTransientAnnot(sym)) BITMAP_CHECKINIT_TRANSIENT else BITMAP_CHECKINIT
239
- case category => category
240
- }
241
- }
242
-
243
- override def needsBitmap (sym : Symbol ): Boolean = super .needsBitmap(sym) || ! (isTrait || sym.isDeferred) && needsInitFlag(sym)
244
- }
245
208
246
209
247
210
// synthesize trees based on info gathered during info transform
@@ -250,47 +213,34 @@ trait AccessorSynthesis extends Transform with ast.TreeDSL {
250
213
// (they are persisted even between phases because the -Xcheckinit logic runs during constructors)
251
214
// TODO: can we use attachments instead of _bitmapInfo and _slowPathFor?
252
215
trait CheckedAccessorTreeSynthesis extends AccessorTreeSynthesis {
253
-
254
216
// note: we deal in getters here, not field symbols
255
- trait SynthCheckedAccessorsTreesInClass extends CheckedAccessorSymbolSynth {
217
+ class SynthCheckedAccessorsTreesInClass ( clazz : Symbol ) extends CheckedAccessorSymbolSynth (clazz) {
256
218
def isUnitGetter (sym : Symbol ) = sym.tpe.resultType.typeSymbol == UnitClass
257
219
def thisRef = gen.mkAttributedThis(clazz)
258
220
221
+
259
222
/** Return an (untyped) tree of the form 'clazz.this.bitmapSym & mask (==|!=) 0', the
260
223
* precise comparison operator depending on the value of 'equalToZero'.
261
224
*/
262
- def mkTest (field : Symbol , equalToZero : Boolean = true ): Tree = {
263
- val bitmap = bitmapFor(field)
264
- val bitmapTree = thisRef DOT bitmap.symbol
265
-
266
- if (bitmap.storageClass == BooleanClass ) {
267
- if (equalToZero) NOT (bitmapTree) else bitmapTree
268
- } else {
269
- val lhs = bitmapTree GEN_& (bitmap.mask, bitmap.storageClass)
270
- if (equalToZero) lhs GEN_== (ZERO , bitmap.storageClass)
271
- else lhs GEN_!= (ZERO , bitmap.storageClass)
272
- }
273
- }
225
+ def mkTest (bm : BitmapInfo , equalToZero : Boolean = true ): Tree =
226
+ if (bm.isBoolean)
227
+ if (equalToZero) NOT (bm.select(thisRef)) else bm.select(thisRef)
228
+ else
229
+ Apply (bm.member(bm.applyToMask(thisRef, nme.AND ), if (equalToZero) nme.EQ else nme.NE ), List (ZERO ))
274
230
275
231
/** Return an (untyped) tree of the form 'Clazz.this.bmp = Clazz.this.bmp | mask'. */
276
- def mkSetFlag (valSym : Symbol ): Tree = {
277
- val bitmap = bitmapFor(valSym)
278
- def x = thisRef DOT bitmap.symbol
279
-
280
- Assign (x,
281
- if (bitmap.storageClass == BooleanClass ) TRUE
232
+ def mkSetFlag (bitmap : BitmapInfo ): Tree =
233
+ Assign (bitmap.select(thisRef),
234
+ if (bitmap.isBoolean) TRUE
282
235
else {
283
- val or = Apply (Select (x, getMember(bitmap.storageClass, nme.OR )), List (bitmap.mask))
284
- // NOTE: bitwise or (`|`) on two bytes yields and Int (TODO: why was this not a problem when this ran during mixins?)
285
- // TODO: need this to make it type check -- is there another way??
286
- if (bitmap.storageClass != LongClass ) Apply (Select (or, newTermName(" to" + bitmap.storageClass.name)), Nil )
287
- else or
288
- }
289
- )
290
- }
236
+ val ored = bitmap.applyToMask(thisRef, nme.OR )
237
+ // NOTE: Unless the bitmap is a Long, we must convert explicitly to avoid widening
238
+ // For example, bitwise OR (`|`) on two bytes yields and Int
239
+ if (bitmap.isLong) ored else bitmap.convert(ored)
240
+ })
291
241
}
292
242
293
- class SynthLazyAccessorsIn (protected val clazz : Symbol ) extends SynthCheckedAccessorsTreesInClass {
243
+ class SynthLazyAccessorsIn (clazz : Symbol ) extends SynthCheckedAccessorsTreesInClass (clazz) {
294
244
/**
295
245
* The compute method (slow path) looks like:
296
246
*
@@ -334,8 +284,9 @@ trait AccessorSynthesis extends Transform with ast.TreeDSL {
334
284
val selectVar = if (isUnit) UNIT else Select (thisRef, lazyVar)
335
285
val storeRes = if (isUnit) rhsAtSlowDef else Assign (selectVar, fields.castHack(rhsAtSlowDef, lazyVar.info))
336
286
337
- def needsInit = mkTest(lazyAccessor)
338
- val doInit = Block (List (storeRes), mkSetFlag(lazyAccessor))
287
+ val bitmap = bitmapFor(lazyVar)
288
+ def needsInit = mkTest(bitmap)
289
+ val doInit = Block (List (storeRes), mkSetFlag(bitmap))
339
290
// the slow part of double-checked locking (TODO: is this the most efficient pattern? https://github.come/scala/scala-dev/issues/204)
340
291
val slowPathRhs = Block (gen.mkSynchronized(thisRef)(If (needsInit, doInit, EmptyTree )) :: Nil , selectVar)
341
292
@@ -349,50 +300,52 @@ trait AccessorSynthesis extends Transform with ast.TreeDSL {
349
300
}
350
301
}
351
302
352
- class SynthInitCheckedAccessorsIn (protected val clazz : Symbol ) extends SynthCheckedAccessorsTreesInClass with CheckInitAccessorSymbolSynth {
303
+ class SynthInitCheckedAccessorsIn (clazz : Symbol ) extends SynthCheckedAccessorsTreesInClass (clazz) {
304
+
305
+ // Add statements to the body of a constructor to set the 'init' bit for each field initialized in the constructor
353
306
private object addInitBitsTransformer extends Transformer {
354
- private def checkedGetter (lhs : Tree )(pos : Position ) = {
355
- val getter = clazz.info decl lhs.symbol.getterName suchThat (_.isGetter)
356
- if (hasBitmap(getter) && needsInitFlag(getter)) {
357
- debuglog(" adding checked getter for: " + getter + " " + lhs.symbol.flagString)
358
- List (typedPos(pos)(mkSetFlag(getter)))
359
- }
360
- else Nil
361
- }
362
307
override def transformStats (stats : List [Tree ], exprOwner : Symbol ) = {
363
- // !!! Ident(self) is never referenced, is it supposed to be confirming
364
- // that self is anything in particular?
365
- super .transformStats(
366
- stats flatMap {
367
- case stat@ Assign (lhs@ Select (This (_), _), rhs) => stat :: checkedGetter(lhs)(stat.pos.focus)
368
- // remove initialization for default values -- TODO is this case ever hit? constructors does not generate Assigns with EmptyTree for the rhs AFAICT
369
- case Apply (lhs@ Select (Ident (self), _), EmptyTree .asList) if lhs.symbol.isSetter => Nil
370
- case stat => List (stat)
371
- },
372
- exprOwner
373
- )
308
+ val checkedStats = stats flatMap {
309
+ // Mark field as initialized after an assignment
310
+ case stat@ Assign (lhs@ Select (This (_), _), _) =>
311
+ stat :: bitmapOf(lhs.symbol).toList.map(bitmap => typedPos(stat.pos.focus)(mkSetFlag(bitmap)))
312
+
313
+ // remove initialization for default values
314
+ // TODO is this case ever hit? constructors does not generate Assigns with EmptyTree for the rhs AFAICT
315
+ // !!! Ident(self) is never referenced, is it supposed to be confirming
316
+ // that self is anything in particular?
317
+ case Apply (lhs@ Select (Ident (self), _), EmptyTree .asList) if lhs.symbol.isSetter => Nil
318
+ case stat => List (stat)
319
+ }
320
+
321
+ super .transformStats(checkedStats, exprOwner)
374
322
}
375
323
}
376
324
325
+ private [this ] val isTrait = clazz.isTrait
326
+ // We only act on concrete methods, and traits only need to have their constructor rewritten
327
+ def needsWrapping (dd : DefDef ) =
328
+ dd.rhs != EmptyTree && (! isTrait || dd.symbol.isConstructor)
329
+
377
330
/** Make getters check the initialized bit, and the class constructor & setters are changed to set the initialized bits. */
378
- def wrapRhsWithInitChecks (sym : Symbol )(rhs : Tree ): Tree = {
379
- // Add statements to the body of a constructor to set the 'init' bit for each field initialized in the constructor
331
+ def wrapRhsWithInitChecks (sym : Symbol )(rhs : Tree ): Tree =
380
332
if (sym.isConstructor) addInitBitsTransformer transform rhs
381
- else if (isTrait || rhs == EmptyTree ) rhs
382
- else if (needsInitFlag(sym)) // getter
383
- mkCheckedAccessorRhs(if (isUnitGetter(sym)) UNIT else rhs, rhs.pos, sym)
384
- else if (sym.isSetter) {
385
- val getter = sym.getterIn(clazz)
386
- if (needsInitFlag(getter)) Block (List (rhs, typedPos(rhs.pos.focus)(mkSetFlag(getter))), UNIT )
387
- else rhs
333
+ else if ((sym hasFlag ACCESSOR ) && ! (sym hasFlag LAZY )) {
334
+ val field = clazz.info.decl(sym.localName)
335
+ if (field == NoSymbol ) rhs
336
+ else bitmapOf(field) match {
337
+ case Some (bitmap) =>
338
+ if (sym.isGetter) mkCheckedAccessorRhs(if (isUnitGetter(sym)) UNIT else rhs, rhs.pos, bitmap) // TODO: why not always use rhs?
339
+ else Block (List (rhs, typedPos(rhs.pos.focus)(mkSetFlag(bitmap))), UNIT )
340
+ case _ => rhs
341
+ }
388
342
}
389
343
else rhs
390
- }
391
344
392
- private def mkCheckedAccessorRhs (retVal : Tree , pos : Position , getter : Symbol ): Tree = {
345
+ private def mkCheckedAccessorRhs (retVal : Tree , pos : Position , bitmap : BitmapInfo ): Tree = {
393
346
val msg = s " Uninitialized field: ${clazz.sourceFile}: ${pos.line}"
394
347
val result =
395
- IF (mkTest(getter , equalToZero = false )).
348
+ IF (mkTest(bitmap , equalToZero = false )).
396
349
THEN (retVal).
397
350
ELSE (Throw (NewFromConstructor (UninitializedFieldConstructor , LIT (msg))))
398
351
0 commit comments