Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions compiler/src/dotty/tools/dotc/core/Definitions.scala
Original file line number Diff line number Diff line change
Expand Up @@ -756,7 +756,10 @@ class Definitions {
def JavaEnumType = JavaEnumClass.typeRef

@tu lazy val MethodHandleClass: ClassSymbol = requiredClass("java.lang.invoke.MethodHandle")
@tu lazy val MethodHandlesClass: TermSymbol = requiredModule("java.lang.invoke.MethodHandles")
@tu lazy val MethodHandles_lookup: Symbol = MethodHandlesClass.requiredMethod("lookup")
@tu lazy val MethodHandlesLookupClass: ClassSymbol = requiredClass("java.lang.invoke.MethodHandles.Lookup")
@tu lazy val MethodHandlesLookup_FindVarHandle: Symbol = MethodHandlesLookupClass.requiredMethod("findVarHandle")
@tu lazy val VarHandleClass: ClassSymbol = requiredClass("java.lang.invoke.VarHandle")

@tu lazy val StringBuilderClass: ClassSymbol = requiredClass("scala.collection.mutable.StringBuilder")
Expand Down
1 change: 1 addition & 0 deletions compiler/src/dotty/tools/dotc/core/NameKinds.scala
Original file line number Diff line number Diff line change
Expand Up @@ -400,6 +400,7 @@ object NameKinds {
val DirectMethName: SuffixNameKind = new SuffixNameKind(DIRECT, "$direct")
val AdaptedClosureName: SuffixNameKind = new SuffixNameKind(ADAPTEDCLOSURE, "$adapted") { override def definesNewName = true }
val SyntheticSetterName: SuffixNameKind = new SuffixNameKind(SETTER, "_$eq")
val LazyVarHandleName: SuffixNameKind = new SuffixNameKind(LAZYVALVARHANDLE, "$lzyHandle")

/** A name together with a signature. Used in Tasty trees. */
object SignedName extends NameKind(SIGNED) {
Expand Down
2 changes: 2 additions & 0 deletions compiler/src/dotty/tools/dotc/core/NameTags.scala
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,8 @@ object NameTags extends TastyFormat.NameTags {
inline val EXPLICITFIELD = 38 // An explicitly named field, introduce to avoid a clash
// with a regular field of the underlying name

inline val LAZYVALVARHANDLE = 39 // A field containing a VarHandle generated for lazy vals

def nameTagToString(tag: Int): String = tag match {
case UTF8 => "UTF8"
case QUALIFIED => "QUALIFIED"
Expand Down
69 changes: 40 additions & 29 deletions compiler/src/dotty/tools/dotc/transform/LazyVals.scala
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import core.Contexts.*
import core.Decorators.*
import core.DenotTransformers.IdentityDenotTransformer
import core.Flags.*
import core.NameKinds.{ExpandedName, LazyBitMapName, LazyLocalInitName, LazyLocalName}
import core.NameKinds.{ExpandedName, LazyBitMapName, LazyLocalInitName, LazyLocalName, LazyVarHandleName}
import core.StdNames.nme
import core.Symbols.*
import core.Types.*
Expand All @@ -28,8 +28,10 @@ class LazyVals extends MiniPhase with IdentityDenotTransformer {
* The map contains the list of the offset trees.
*/
class OffsetInfo(var defs: List[Tree], var ord: Int = 0)
class VarHandleInfo(var defs: List[Tree])

private val appendOffsetDefs = mutable.Map.empty[Symbol, OffsetInfo]
private val appendVarHandleDefs = mutable.Map.empty[Symbol, VarHandleInfo]

override def phaseName: String = LazyVals.name

Expand Down Expand Up @@ -109,12 +111,19 @@ class LazyVals extends MiniPhase with IdentityDenotTransformer {
*/
override def transformTemplate(template: Template)(using Context): Tree = {
val cls = ctx.owner.asClass
appendOffsetDefs.get(cls) match {
case None => template
case Some(data) =>
data.defs.foreach(defin => defin.symbol.addAnnotation(Annotation(defn.ScalaStaticAnnot, defin.symbol.span)))
cpy.Template(template)(body = addInFront(data.defs, template.body))
}
if ctx.settings.YlegacyLazyVals.value then
appendOffsetDefs.get(cls) match {
case None => template
case Some(data) =>
data.defs.foreach(defin => defin.symbol.addAnnotation(Annotation(defn.ScalaStaticAnnot, defin.symbol.span)))
cpy.Template(template)(body = addInFront(data.defs, template.body))
}
else
appendVarHandleDefs.get(cls) match {
case None => template
case Some(data) =>
cpy.Template(template)(body = addInFront(data.defs, template.body))
}
}

private def addInFront(prefix: List[Tree], stats: List[Tree]) = stats match {
Expand Down Expand Up @@ -328,20 +337,24 @@ class LazyVals extends MiniPhase with IdentityDenotTransformer {
* @param memberDef the transformed lazy field member definition
* @param claz the class containing this lazy val field
* @param target the target synthetic field
* @param offset the offset of the field in the storage allocation of the class
* @param varHandle the VarHandle of the field
* @param thiz a reference to the transformed class
*/
def mkThreadSafeDef(memberDef: ValOrDefDef,
claz: ClassSymbol,
target: Symbol,
offset: Tree,
varHandle: Tree,
thiz: Tree)(using Context): (DefDef, DefDef) = {
val tp = memberDef.tpe.widenDealias.resultType.widenDealias
val waiting = ref(defn.LazyValsWaitingState)
val controlState = ref(defn.LazyValsControlState)
val evaluating = Select(ref(defn.LazyValsModule), lazyNme.RLazyVals.evaluating)
val nullValue = Select(ref(defn.LazyValsModule), lazyNme.RLazyVals.nullValue)
val objCasFlag = Select(ref(defn.LazyValsModule), lazyNme.RLazyVals.objCas)
val casFlag =
typer.Applications.retypeSignaturePolymorphicFn( // must be retyped to avoid wrapping into Array[Object]
Select(varHandle, lazyNme.compareAndSet),
MethodType(List(defn.ObjectType,defn.ObjectType,defn.ObjectType), defn.BooleanType)
)
val accessorMethodSymbol = memberDef.symbol.asTerm
val lazyInitMethodName = LazyLocalInitName.fresh(memberDef.name.asTermName)
val lazyInitMethodSymbol = newSymbol(claz, lazyInitMethodName, Synthetic | Method | Private, MethodType(Nil)(_ => Nil, _ => defn.ObjectType))
Expand Down Expand Up @@ -383,12 +396,12 @@ class LazyVals extends MiniPhase with IdentityDenotTransformer {
val lockRel = {
val lockSymb = newSymbol(lazyInitMethodSymbol, lazyNme.lock, Synthetic, waiting.typeOpt)
Block(ValDef(lockSymb, ref(target).cast(waiting.typeOpt))
:: objCasFlag.appliedTo(thiz, offset, ref(lockSymb), ref(resSymb)) :: Nil,
:: casFlag.appliedTo(thiz, ref(lockSymb), ref(resSymb)) :: Nil,
ref(lockSymb).select(lazyNme.RLazyVals.waitingRelease).ensureApplied)
}
// finally block
val fin = If(
objCasFlag.appliedTo(thiz, offset, evaluating, ref(resSymb)).select(nme.UNARY_!).appliedToNone,
casFlag.appliedTo(thiz, evaluating, ref(resSymb)).select(nme.UNARY_!).appliedToNone,
lockRel,
unitLiteral
)
Expand All @@ -409,7 +422,7 @@ class LazyVals extends MiniPhase with IdentityDenotTransformer {
)
// if CAS(_, null, Evaluating)
If(
objCasFlag.appliedTo(thiz, offset, nullLiteral, evaluating),
casFlag.appliedTo(thiz, nullLiteral, evaluating),
Block(ValDef(resSymb, nullLiteral) :: ValDef(resSymbNullable, nullLiteral) :: evaluate :: Nil, // var result: AnyRef = null
Return(ref(resSymbNullable), lazyInitMethodSymbol)),
unitLiteral
Expand All @@ -425,7 +438,7 @@ class LazyVals extends MiniPhase with IdentityDenotTransformer {
ref(current).select(defn.Object_eq).appliedTo(evaluating),
// if is Evaluating then CAS(_, Evaluating, new Waiting)
Block(
objCasFlag.appliedTo(thiz, offset, ref(current), Select(New(waiting), StdNames.nme.CONSTRUCTOR).ensureApplied) :: Nil,
casFlag.appliedTo(thiz, ref(current), Select(New(waiting), StdNames.nme.CONSTRUCTOR).ensureApplied) :: Nil,
unitLiteral
),
// if not Evaluating
Expand Down Expand Up @@ -461,7 +474,6 @@ class LazyVals extends MiniPhase with IdentityDenotTransformer {
val claz = x.symbol.owner.asClass
val thizClass = Literal(Constant(claz.info))

def offsetName(id: Int) = s"${StdNames.nme.LAZY_FIELD_OFFSET}${if (x.symbol.owner.is(Module)) "_m_" else ""}$id".toTermName
val containerName = LazyLocalName.fresh(x.name.asTermName)
val containerSymbol = newSymbol(claz, containerName, x.symbol.flags &~ containerFlagsMask | containerFlags | Private, defn.ObjectType, coord = x.symbol.coord).enteredAfter(this)
containerSymbol.addAnnotation(Annotation(defn.VolatileAnnot, containerSymbol.span)) // private @volatile var _x: AnyRef
Expand All @@ -471,23 +483,22 @@ class LazyVals extends MiniPhase with IdentityDenotTransformer {
Select(ref(defn.LazyValsModule), lazyNme.RLazyVals.getOffsetStatic)
val containerTree = ValDef(containerSymbol, nullLiteral)

// create an offset for this lazy val
val offsetSymbol: TermSymbol = appendOffsetDefs.get(claz) match
case Some(info) =>
newSymbol(claz, offsetName(info.defs.size), Synthetic, defn.LongType).enteredAfter(this)
case None =>
newSymbol(claz, offsetName(0), Synthetic, defn.LongType).enteredAfter(this)
offsetSymbol.addAnnotation(Annotation(defn.ScalaStaticAnnot, offsetSymbol.span))
val fieldTree = thizClass.select(lazyNme.RLazyVals.getDeclaredField).appliedTo(Literal(Constant(containerName.mangledString)))
val offsetTree = ValDef(offsetSymbol, getOffset.appliedTo(fieldTree))
val offsetInfo = appendOffsetDefs.getOrElseUpdate(claz, new OffsetInfo(Nil))
offsetInfo.defs = offsetTree :: offsetInfo.defs
val offset = ref(offsetSymbol)
// create a VarHandle for this lazy val
val varHandleSymbol: TermSymbol = newSymbol(claz, LazyVarHandleName(containerName), Private | Synthetic, defn.VarHandleClass.typeRef).enteredAfter(this)
varHandleSymbol.addAnnotation(Annotation(defn.ScalaStaticAnnot, varHandleSymbol.span))
val getVarHandle = Apply(
Select(Apply(Select(ref(defn.MethodHandlesClass), defn.MethodHandles_lookup.name), Nil), defn.MethodHandlesLookup_FindVarHandle.name),
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Directly select the symbols?

Suggested change
Select(Apply(Select(ref(defn.MethodHandlesClass), defn.MethodHandles_lookup.name), Nil), defn.MethodHandlesLookup_FindVarHandle.name),
Select(Apply(Select(ref(defn.MethodHandlesClass), defn.MethodHandles_lookup), Nil), defn.MethodHandlesLookup_FindVarHandle),

or maybe it's

Suggested change
Select(Apply(Select(ref(defn.MethodHandlesClass), defn.MethodHandles_lookup.name), Nil), defn.MethodHandlesLookup_FindVarHandle.name),
Apply(ref(defn.MethodHandlesClass).select(defn.MethodHandles_lookup), Nil).select(defn.MethodHandlesLookup_FindVarHandle),

List(thizClass, Literal(Constant(containerName.mangledString)), Literal(Constant(defn.ObjectType)))
)
val varHandleTree = ValDef(varHandleSymbol, getVarHandle)
val varHandle = ref(varHandleSymbol)

val varHandleInfo = appendVarHandleDefs.getOrElseUpdate(claz, new VarHandleInfo(Nil))
varHandleInfo.defs = varHandleTree :: varHandleInfo.defs
val swapOver =
This(claz)

val (accessorDef, initMethodDef) = mkThreadSafeDef(x, claz, containerSymbol, offset, swapOver)
val (accessorDef, initMethodDef) = mkThreadSafeDef(x, claz, containerSymbol, varHandle, swapOver)
Thicket(containerTree, accessorDef, initMethodDef)
}

Expand Down Expand Up @@ -666,7 +677,6 @@ object LazyVals {
val waitingRelease: TermName = "countDown".toTermName
val evaluating: TermName = "Evaluating".toTermName
val nullValue: TermName = "NullValue".toTermName
val objCas: TermName = "objCAS".toTermName
val get: TermName = N.get.toTermName
val setFlag: TermName = N.setFlag.toTermName
val wait4Notification: TermName = N.wait4Notification.toTermName
Expand All @@ -687,5 +697,6 @@ object LazyVals {
val current: TermName = "current".toTermName
val lock: TermName = "lock".toTermName
val discard: TermName = "discard".toTermName
val compareAndSet: TermName = "compareAndSet".toTermName
}
}
9 changes: 6 additions & 3 deletions compiler/src/dotty/tools/dotc/transform/MoveStatics.scala
Original file line number Diff line number Diff line change
Expand Up @@ -12,13 +12,16 @@ import SymDenotations.SymDenotation
import Names.Name
import StdNames.nme
import NameOps.*
import NameKinds.LazyVarHandleName

import ast.*


import MegaPhase.*

/** Move static methods from companion to the class itself */
/** Move static methods from companion to the class itself. Also create the static constructor.
* VarHandles generated by the compiler for lazy vals are left in the original class.
*/
class MoveStatics extends MiniPhase with SymTransformer {
import ast.tpd.*

Expand All @@ -28,7 +31,7 @@ class MoveStatics extends MiniPhase with SymTransformer {

def transformSym(sym: SymDenotation)(using Context): SymDenotation =
if (sym.hasAnnotation(defn.ScalaStaticAnnot) && sym.owner.is(Flags.Module) && sym.owner.companionClass.exists &&
(sym.is(Flags.Method) || !(sym.isMutableVarOrAccessor && sym.owner.companionClass.is(Flags.Trait)))) {
(sym.is(Flags.Method) || !(sym.isMutableVarOrAccessor && sym.owner.companionClass.is(Flags.Trait)) && !sym.symbol.name.is(LazyVarHandleName))) {
sym.owner.asClass.delete(sym.symbol)
sym.owner.companionClass.asClass.enter(sym.symbol)
sym.copySymDenotation(owner = sym.owner.companionClass)
Expand Down Expand Up @@ -65,7 +68,7 @@ class MoveStatics extends MiniPhase with SymTransformer {
val moduleTmpl = module.rhs.asInstanceOf[Template]
val companionTmpl = companion.rhs.asInstanceOf[Template]
val (staticDefs, remainingDefs) = moduleTmpl.body.partition {
case memberDef: MemberDef => memberDef.symbol.isScalaStatic
case memberDef: MemberDef => memberDef.symbol.isScalaStatic && !memberDef.symbol.name.is(LazyVarHandleName)
case _ => false
}

Expand Down
9 changes: 9 additions & 0 deletions project/MiMaFilters.scala
Original file line number Diff line number Diff line change
Expand Up @@ -141,6 +141,15 @@ object MiMaFilters {
ProblemFilters.exclude[ReversedMissingMethodProblem]("scala.quoted.Quotes#reflectModule#SymbolModule.newClass"),
ProblemFilters.exclude[ReversedMissingMethodProblem]("scala.quoted.Quotes#reflectModule#SymbolModule.newModule"),

// Changes to lazy vals (added static constructors)
ProblemFilters.exclude[DirectMissingMethodProblem]("scala.Tuple.<clinit>"),
ProblemFilters.exclude[DirectMissingMethodProblem]("scala.collection.immutable.ArraySeq.<clinit>"),
ProblemFilters.exclude[DirectMissingMethodProblem]("scala.concurrent.ExecutionContext.<clinit>"),
ProblemFilters.exclude[DirectMissingMethodProblem]("scala.io.Codec.<clinit>"),
ProblemFilters.exclude[DirectMissingMethodProblem]("scala.math.BigDecimal.<clinit>"),
ProblemFilters.exclude[DirectMissingMethodProblem]("scala.sys.SystemProperties.<clinit>"),
ProblemFilters.exclude[DirectMissingMethodProblem]("scala.sys.process.Process.<clinit>"),

// Change `experimental` annotation to a final class
ProblemFilters.exclude[FinalClassProblem]("scala.annotation.experimental"),

Expand Down
21 changes: 10 additions & 11 deletions tests/printing/transformed/lazy-vals-new.check
Original file line number Diff line number Diff line change
Expand Up @@ -9,14 +9,14 @@ package <empty> {
}
@static private def <clinit>(): Unit =
{
A.OFFSET$_m_0 =
scala.runtime.LazyVals.getOffsetStatic(
classOf[Object {...}].getDeclaredField("x$lzy1"))
A.x$lzy1$lzyHandle =
java.lang.invoke.MethodHandles.lookup().findVarHandle(
classOf[Object {...}], "x$lzy1", classOf[Object])
()
}
@static @static val OFFSET$_m_0: Long =
scala.runtime.LazyVals.getOffsetStatic(
classOf[Object {...}].getDeclaredField("x$lzy1"))
@static private val x$lzy1$lzyHandle: java.lang.invoke.VarHandle =
java.lang.invoke.MethodHandles.lookup().findVarHandle(
classOf[Object {...}], "x$lzy1", classOf[Object])
private def writeReplace(): Object =
new scala.runtime.ModuleSerializationProxy(classOf[A])
@volatile private lazy var x$lzy1: Object = null
Expand All @@ -33,7 +33,7 @@ package <empty> {
val current: Object = A.x$lzy1
if current eq null then
if
scala.runtime.LazyVals.objCAS(this, A.OFFSET$_m_0, null,
A.x$lzy1$lzyHandle.compareAndSet(this, null,
scala.runtime.LazyVals.Evaluating)
then
{
Expand All @@ -49,15 +49,14 @@ package <empty> {
}
finally
if
scala.runtime.LazyVals.objCAS(this, A.OFFSET$_m_0,
A.x$lzy1$lzyHandle.compareAndSet(this,
scala.runtime.LazyVals.Evaluating, result).unary_!()
then
{
val lock: scala.runtime.LazyVals.LazyVals$Waiting =
A.x$lzy1.asInstanceOf[
scala.runtime.LazyVals.LazyVals$Waiting]
scala.runtime.LazyVals.objCAS(this, A.OFFSET$_m_0, lock,
result)
A.x$lzy1$lzyHandle.compareAndSet(this, lock, result)
lock.countDown()
}
else ()
Expand All @@ -71,7 +70,7 @@ package <empty> {
then
if current eq scala.runtime.LazyVals.Evaluating then
{
scala.runtime.LazyVals.objCAS(this, A.OFFSET$_m_0, current,
A.x$lzy1$lzyHandle.compareAndSet(this, current,
new scala.runtime.LazyVals.LazyVals$Waiting())
()
}
Expand Down
Loading