Skip to content

Commit a1af355

Browse files
committed
Porting recent changes & adding fixes
1 parent d7136bb commit a1af355

File tree

5 files changed

+26
-202
lines changed

5 files changed

+26
-202
lines changed

compiler/src/dotty/tools/dotc/transform/LazyVals.scala

Lines changed: 14 additions & 188 deletions
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,6 @@ class LazyVals extends MiniPhase with IdentityDenotTransformer {
3333
*/
3434
class OldOffsetInfo(defs: List[Tree], var ord: Int) extends OffsetInfo(defs)
3535
private val appendOffsetDefs = mutable.Map.empty[Symbol, OffsetInfo]
36-
private val oldAppendOffsetDefs = mutable.Map.empty[Symbol, OldOffsetInfo]
3736

3837
override def phaseName: String = LazyVals.name
3938

@@ -59,9 +58,6 @@ class LazyVals extends MiniPhase with IdentityDenotTransformer {
5958
else nullables.toList
6059
}
6160

62-
private inline def isOldLazyVals(using ctx: Context): Boolean =
63-
import dotty.tools.dotc.config.ScalaRelease._
64-
ctx.scalaRelease <= Release3_1
6561

6662
private def initBlock(stats: List[Tree])(using Context): Block = stats match
6763
case Nil => throw new IllegalArgumentException("trying to create an empty Block")
@@ -127,7 +123,7 @@ class LazyVals extends MiniPhase with IdentityDenotTransformer {
127123
override def transformTemplate(template: Template)(using Context): Tree = {
128124
val cls = ctx.owner.asClass
129125

130-
(if isOldLazyVals then oldAppendOffsetDefs else appendOffsetDefs).get(cls) match {
126+
appendOffsetDefs.get(cls) match {
131127
case None => template
132128
case Some(data) =>
133129
data.defs.foreach(_.symbol.addAnnotation(Annotation(defn.ScalaStaticAnnot)))
@@ -295,12 +291,7 @@ class LazyVals extends MiniPhase with IdentityDenotTransformer {
295291

296292
def transformMemberDefThreadSafe(x: ValOrDefDef)(using Context): Thicket = {
297293
assert(!(x.symbol is Mutable))
298-
// generate old code for compatibility
299-
// TODO find more meaningful names than old/new
300-
if isOldLazyVals then
301-
transformMemberDefThreadSafeOld(x)
302-
else
303-
transformMemberDefThreadSafeNew(x)
294+
transformMemberDefThreadSafeNew(x)
304295
}
305296

306297
/**
@@ -309,8 +300,6 @@ class LazyVals extends MiniPhase with IdentityDenotTransformer {
309300
* private @volatile var _x: AnyRef = null
310301
* @tailrec def x: A =
311302
* _x match
312-
* case current: A =>
313-
* current
314303
* case null =>
315304
* if CAS(_x, null, Evaluating) then
316305
* var result: AnyRef = null // here, we need `AnyRef` to possibly assign `NULL`
@@ -331,6 +320,8 @@ class LazyVals extends MiniPhase with IdentityDenotTransformer {
331320
* current.awaitRelease()
332321
* x
333322
* case NULL => null
323+
* case current: A =>
324+
* current
334325
* ```
335326
* Where `Evaluating` and `NULL` are represented by `object`s and `Waiting` by a class that
336327
* allows awaiting the completion of the evaluation. Note that since tail-recursive
@@ -359,11 +350,11 @@ class LazyVals extends MiniPhase with IdentityDenotTransformer {
359350
* if current.isInstanceOf[Evaluating] then
360351
* CAS(current, Evaluating, new Waiting)
361352
* else if current.isInstanceOf[NULL] then
362-
* null
353+
* return null
363354
* else if current.isInstanceOf[Waiting] then
364355
* current.asInstanceOf[Waiting].awaitRelease()
365356
* else
366-
* current.asInstanceOf[A]
357+
* return current.asInstanceOf[A]
367358
* end while
368359
* ```
369360
*
@@ -476,23 +467,24 @@ class LazyVals extends MiniPhase with IdentityDenotTransformer {
476467
containerSymbol.setFlag(JavaStatic)
477468
val getOffset =
478469
if stat then
479-
Select(ref(helperModule), lazyNme.RLazyVals.getStaticOffset)
470+
Select(ref(helperModule), lazyNme.RLazyVals.getStaticFieldOffset)
480471
else
481-
Select(ref(helperModule), lazyNme.RLazyVals.getOffset)
472+
Select(ref(helperModule), lazyNme.RLazyVals.getOffsetStatic)
482473
val containerTree = ValDef(containerSymbol, nullLiteral)
483-
def staticOrFieldOff: Tree = getOffset.appliedTo(thizClass, Literal(Constant(containerName.toString)))
484474

485475
// create an offset for this lazy val
486476
appendOffsetDefs.get(claz) match
487477
case Some(info) =>
488478
offsetSymbol = newSymbol(claz, offsetName(info.defs.size), Synthetic, defn.LongType).enteredAfter(this)
489479
offsetSymbol.nn.addAnnotation(Annotation(defn.ScalaStaticAnnot))
490-
val offsetTree = ValDef(offsetSymbol.nn, staticOrFieldOff)
480+
val fieldTree = thizClass.select(lazyNme.RLazyVals.getDeclaredField).appliedTo(Literal(Constant(containerName.toString)))
481+
val offsetTree = ValDef(offsetSymbol.nn, getOffset.appliedTo(fieldTree))
491482
info.defs = offsetTree :: info.defs
492483
case None =>
493484
offsetSymbol = newSymbol(claz, offsetName(0), Synthetic, defn.LongType).enteredAfter(this)
494485
offsetSymbol.nn.addAnnotation(Annotation(defn.ScalaStaticAnnot))
495-
val offsetTree = ValDef(offsetSymbol.nn, staticOrFieldOff)
486+
val fieldTree = thizClass.select(lazyNme.RLazyVals.getDeclaredField).appliedTo(Literal(Constant(containerName.toString)))
487+
val offsetTree = ValDef(offsetSymbol.nn, getOffset.appliedTo(fieldTree))
496488
appendOffsetDefs += (claz -> new OffsetInfo(List(offsetTree)))
497489

498490
val waiting = requiredClass(s"$runtimeModule.${lazyNme.RLazyVals.waiting}")
@@ -513,171 +505,6 @@ class LazyVals extends MiniPhase with IdentityDenotTransformer {
513505
ref(waiting), evaluating, nullValued, swapOver)
514506
Thicket(containerTree, accessor)
515507
}
516-
517-
/** Create a threadsafe lazy accessor equivalent to such code
518-
* ```
519-
* def methodSymbol(): Int = {
520-
* while (true) {
521-
* val flag = LazyVals.get(this, bitmap_offset)
522-
* val state = LazyVals.STATE(flag, <field-id>)
523-
*
524-
* if (state == <state-3>) {
525-
* return value_0
526-
* } else if (state == <state-0>) {
527-
* if (LazyVals.CAS(this, bitmap_offset, flag, <state-1>, <field-id>)) {
528-
* try {
529-
* val result = <RHS>
530-
* value_0 = result
531-
* nullable = null
532-
* LazyVals.setFlag(this, bitmap_offset, <state-3>, <field-id>)
533-
* return result
534-
* }
535-
* catch {
536-
* case ex =>
537-
* LazyVals.setFlag(this, bitmap_offset, <state-0>, <field-id>)
538-
* throw ex
539-
* }
540-
* }
541-
* } else /* if (state == <state-1> || state == <state-2>) */ {
542-
* LazyVals.wait4Notification(this, bitmap_offset, flag, <field-id>)
543-
* }
544-
* }
545-
* }
546-
* ```
547-
*/
548-
def mkThreadSafeDefOld(methodSymbol: TermSymbol,
549-
claz: ClassSymbol,
550-
ord: Int,
551-
target: Symbol,
552-
rhs: Tree,
553-
tp: Type,
554-
offset: Tree,
555-
getFlag: Tree,
556-
stateMask: Tree,
557-
casFlag: Tree,
558-
setFlagState: Tree,
559-
waitOnLock: Tree)(using Context): DefDef = {
560-
val initState = Literal(Constant(0))
561-
val computeState = Literal(Constant(1))
562-
val computedState = Literal(Constant(3))
563-
564-
val thiz = This(claz)
565-
val fieldId = Literal(Constant(ord))
566-
567-
val flagSymbol = newSymbol(methodSymbol, lazyNme.flag, Synthetic, defn.LongType)
568-
val flagDef = ValDef(flagSymbol, getFlag.appliedTo(thiz, offset))
569-
val flagRef = ref(flagSymbol)
570-
571-
val stateSymbol = newSymbol(methodSymbol, lazyNme.state, Synthetic, defn.LongType)
572-
val stateDef = ValDef(stateSymbol, stateMask.appliedTo(ref(flagSymbol), Literal(Constant(ord))))
573-
val stateRef = ref(stateSymbol)
574-
575-
val compute = {
576-
val resultSymbol = newSymbol(methodSymbol, lazyNme.result, Synthetic, tp)
577-
val resultRef = ref(resultSymbol)
578-
val stats = (
579-
ValDef(resultSymbol, rhs) ::
580-
ref(target).becomes(resultRef) ::
581-
(nullOut(nullableFor(methodSymbol)) :+
582-
setFlagState.appliedTo(thiz, offset, computedState, fieldId))
583-
)
584-
Block(stats, Return(resultRef, methodSymbol))
585-
}
586-
587-
val retryCase = {
588-
val caseSymbol = newSymbol(methodSymbol, nme.DEFAULT_EXCEPTION_NAME, Synthetic | Case, defn.ThrowableType)
589-
val triggerRetry = setFlagState.appliedTo(thiz, offset, initState, fieldId)
590-
CaseDef(
591-
Bind(caseSymbol, ref(caseSymbol)),
592-
EmptyTree,
593-
Block(List(triggerRetry), Throw(ref(caseSymbol)))
594-
)
595-
}
596-
597-
val initialize = If(
598-
casFlag.appliedTo(thiz, offset, flagRef, computeState, fieldId),
599-
Try(compute, List(retryCase), EmptyTree),
600-
unitLiteral
601-
)
602-
603-
val condition = If(
604-
stateRef.equal(computedState),
605-
Return(ref(target), methodSymbol),
606-
If(
607-
stateRef.equal(initState),
608-
initialize,
609-
waitOnLock.appliedTo(thiz, offset, flagRef, fieldId)
610-
)
611-
)
612-
613-
val loop = WhileDo(EmptyTree, Block(List(flagDef, stateDef), condition))
614-
DefDef(methodSymbol, loop)
615-
}
616-
617-
def transformMemberDefThreadSafeOld(x: ValOrDefDef)(using Context): Thicket = {
618-
val tpe = x.tpe.widen.resultType.widen
619-
val claz = x.symbol.owner.asClass
620-
val thizClass = Literal(Constant(claz.info))
621-
val helperModule = requiredModule("scala.runtime.LazyVals")
622-
val getOffset = Select(ref(helperModule), lazyNme.RLazyVals.getOffset)
623-
val getOffsetStatic = Select(ref(helperModule), lazyNme.RLazyVals.getOffsetStatic)
624-
var offsetSymbol: TermSymbol | Null = null
625-
var flag: Tree = EmptyTree
626-
var ord = 0
627-
628-
def offsetName(id: Int) = s"${StdNames.nme.LAZY_FIELD_OFFSET}${if (x.symbol.owner.is(Module)) "_m_" else ""}$id".toTermName
629-
630-
// compute or create appropriate offsetSymbol, bitmap and bits used by current ValDef
631-
oldAppendOffsetDefs.get(claz) match {
632-
case Some(info) =>
633-
val flagsPerLong = (64 / scala.runtime.LazyVals.BITS_PER_LAZY_VAL).toInt
634-
info.ord += 1
635-
ord = info.ord % flagsPerLong
636-
val id = info.ord / flagsPerLong
637-
val offsetById = offsetName(id)
638-
if (ord != 0) // there are unused bits in already existing flag
639-
offsetSymbol = claz.info.decl(offsetById)
640-
.suchThat(sym => sym.is(Synthetic) && sym.isTerm)
641-
.symbol.asTerm
642-
else { // need to create a new flag
643-
offsetSymbol = newSymbol(claz, offsetById, Synthetic, defn.LongType).enteredAfter(this)
644-
offsetSymbol.nn.addAnnotation(Annotation(defn.ScalaStaticAnnot))
645-
val flagName = LazyBitMapName.fresh(id.toString.toTermName)
646-
val flagSymbol = newSymbol(claz, flagName, containerFlags, defn.LongType).enteredAfter(this)
647-
flag = ValDef(flagSymbol, Literal(Constant(0L)))
648-
val fieldTree = thizClass.select(lazyNme.RLazyVals.getDeclaredField).appliedTo(Literal(Constant(flagName.toString)))
649-
val offsetTree = ValDef(offsetSymbol.nn, getOffsetStatic.appliedTo(fieldTree))
650-
info.defs = offsetTree :: info.defs
651-
}
652-
653-
case None =>
654-
offsetSymbol = newSymbol(claz, offsetName(0), Synthetic, defn.LongType).enteredAfter(this)
655-
offsetSymbol.nn.addAnnotation(Annotation(defn.ScalaStaticAnnot))
656-
val flagName = LazyBitMapName.fresh("0".toTermName)
657-
val flagSymbol = newSymbol(claz, flagName, containerFlags, defn.LongType).enteredAfter(this)
658-
flag = ValDef(flagSymbol, Literal(Constant(0L)))
659-
val fieldTree = thizClass.select(lazyNme.RLazyVals.getDeclaredField).appliedTo(Literal(Constant(flagName.toString)))
660-
val offsetTree = ValDef(offsetSymbol.nn, getOffsetStatic.appliedTo(fieldTree))
661-
appendOffsetDefs += (claz -> new OffsetInfo(List(offsetTree), ord))
662-
}
663-
664-
val containerName = LazyLocalName.fresh(x.name.asTermName)
665-
val containerSymbol = newSymbol(claz, containerName, x.symbol.flags &~ containerFlagsMask | containerFlags, tpe, coord = x.symbol.coord).enteredAfter(this)
666-
667-
val containerTree = ValDef(containerSymbol, defaultValue(tpe))
668-
669-
val offset = ref(offsetSymbol.nn)
670-
val getFlag = Select(ref(helperModule), lazyNme.RLazyVals.get)
671-
val setFlag = Select(ref(helperModule), lazyNme.RLazyVals.setFlag)
672-
val wait = Select(ref(helperModule), lazyNme.RLazyVals.wait4Notification)
673-
val state = Select(ref(helperModule), lazyNme.RLazyVals.state)
674-
val cas = Select(ref(helperModule), lazyNme.RLazyVals.cas)
675-
676-
val accessor = mkThreadSafeDefOld(x.symbol.asTerm, claz, ord, containerSymbol, x.rhs, tpe, offset, getFlag, state, cas, setFlag, wait)
677-
if (flag eq EmptyTree)
678-
Thicket(containerTree, accessor)
679-
else Thicket(containerTree, flag, accessor)
680-
}
681508
}
682509

683510
object LazyVals {
@@ -694,15 +521,14 @@ object LazyVals {
694521
val evaluating: TermName = N.evaluating.toTermName
695522
val nullValued: TermName = N.nullValued.toTermName
696523
val objCas: TermName = N.objCas.toTermName
697-
val getOffset: TermName = N.getOffset.toTermName
698-
val getStaticOffset: TermName = N.getStaticOffset.toTermName
524+
val getStaticFieldOffset: TermName = N.getStaticFieldOffset.toTermName
699525
val get: TermName = N.get.toTermName
700526
val setFlag: TermName = N.setFlag.toTermName
701527
val wait4Notification: TermName = N.wait4Notification.toTermName
702528
val state: TermName = N.state.toTermName
703529
val cas: TermName = N.cas.toTermName
704530
val getOffset: TermName = N.getOffset.toTermName
705-
val getOffsetStatic: TermName = "getOffsetStatic".toTermName
531+
val getOffsetStatic: TermName = N.getOffsetStatic.toTermName
706532
val getDeclaredField: TermName = "getDeclaredField".toTermName
707533

708534
}

library/src/scala/runtime/LazyVals.scala

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -141,17 +141,18 @@ object LazyVals {
141141
unsafe.getLongVolatile(t, off)
142142
}
143143

144+
// kept for backward compatibility
144145
def getOffset(clz: Class[_], name: String): Long = {
145146
val r = unsafe.objectFieldOffset(clz.getDeclaredField(name))
146147
if (debug)
147148
println(s"getOffset($clz, $name) = $r")
148149
r
149150
}
150151

151-
def getStaticOffset(clz: Class[_], name: String): Long = {
152-
val r = unsafe.staticFieldOffset(clz.getDeclaredField(name))
152+
def getStaticFieldOffset(field: java.lang.reflect.Field): Long = {
153+
val r = unsafe.staticFieldOffset(field)
153154
if (debug)
154-
println(s"getStaticOffset($clz, $name) = $r")
155+
println(s"getStaticFieldOffset(${field.getDeclaringClass}, ${field.getName}) = $r")
155156
r
156157
}
157158

@@ -175,6 +176,7 @@ object LazyVals {
175176
final val wait4Notification = "wait4Notification"
176177
final val get = "get"
177178
final val getOffset = "getOffset"
178-
final val getStaticOffset = "getStaticOffset"
179+
final val getOffsetStatic = "getOffsetStatic"
180+
final val getStaticFieldOffset = "getStaticFieldOffset"
179181
}
180182
}

project/MiMaFilters.scala

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -19,12 +19,12 @@ object MiMaFilters {
1919
ProblemFilters.exclude[ReversedMissingMethodProblem]("scala.quoted.Quotes#reflectModule#SymbolMethods.termRef"),
2020
ProblemFilters.exclude[ReversedMissingMethodProblem]("scala.quoted.Quotes#reflectModule#TypeTreeModule.ref"),
2121

22-
ProblemFilters.exclude[DirectMissingMethodProblem]("scala.runtime.LazyVals.getStaticOffset"),
23-
ProblemFilters.exclude[DirectMissingMethodProblem]("scala.runtime.LazyVals.getStaticOffset"),
24-
ProblemFilters.exclude[DirectMissingMethodProblem]("scala.runtime.LazyVals.objCAS"),
22+
ProblemFilters.exclude[DirectMissingMethodProblem]("scala.runtime.LazyVals.getStaticFieldOffset"),
23+
ProblemFilters.exclude[DirectMissingMethodProblem]("scala.runtime.LazyVals.getOffsetStatic"),
2524
ProblemFilters.exclude[DirectMissingMethodProblem]("scala.runtime.LazyVals.objCAS"),
2625
ProblemFilters.exclude[DirectMissingMethodProblem]("scala.runtime.LazyVals#Names.evaluating"),
27-
ProblemFilters.exclude[DirectMissingMethodProblem]("scala.runtime.LazyVals#Names.getStaticOffset"),
26+
ProblemFilters.exclude[DirectMissingMethodProblem]("scala.runtime.LazyVals#Names.getStaticFieldOffset"),
27+
ProblemFilters.exclude[DirectMissingMethodProblem]("scala.runtime.LazyVals#Names.getOffsetStatic"),
2828
ProblemFilters.exclude[DirectMissingMethodProblem]("scala.runtime.LazyVals#Names.nullValued"),
2929
ProblemFilters.exclude[DirectMissingMethodProblem]("scala.runtime.LazyVals#Names.objCas"),
3030
ProblemFilters.exclude[DirectMissingMethodProblem]("scala.runtime.LazyVals#Names.waiting"),

tests/init/neg/promotion-loop.check

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,7 @@
11
-- Error: tests/init/neg/promotion-loop.scala:16:10 --------------------------------------------------------------------
22
16 | println(b) // error
33
| ^
4-
<<<<<<< HEAD
5-
| Cannot prove that the value is fully-initialized. Only initialized values may be used as arguments.
6-
=======
74
| Cannot prove that the value is fully initialized. Only initialized values may be used as arguments.
8-
>>>>>>> 91c03b1f7835d574290eba1ecf236e0f59fc2bf0
95
|
106
| The unsafe promotion may cause the following problem:
117
| Cannot prove that the value is fully initialized. Only initialized values may be used as arguments.

tests/init/neg/t3273.check

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
-- Error: tests/init/neg/t3273.scala:4:42 ------------------------------------------------------------------------------
22
4 | val num1: LazyList[Int] = 1 #:: num1.map(_ + 1) // error
33
| ^^^^^^^^^^^^^^^
4-
| Cannot prove that the value is fully-initialized. Only initialized values may be used as arguments.
4+
| Cannot prove that the value is fully initialized. Only initialized values may be used as arguments.
55
|
66
| The unsafe promotion may cause the following problem:
77
| Access non-initialized value num1. Calling trace:
@@ -10,7 +10,7 @@
1010
-- Error: tests/init/neg/t3273.scala:5:61 ------------------------------------------------------------------------------
1111
5 | val num2: LazyList[Int] = 1 #:: num2.iterator.map(_ + 1).to(LazyList) // error
1212
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
13-
| Cannot prove that the value is fully-initialized. Only initialized values may be used as arguments.
13+
| Cannot prove that the value is fully initialized. Only initialized values may be used as arguments.
1414
|
1515
| The unsafe promotion may cause the following problem:
1616
| Access non-initialized value num2. Calling trace:

0 commit comments

Comments
 (0)