diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml index 953fa83aebe4..e90bb795c382 100644 --- a/.github/workflows/ci.yaml +++ b/.github/workflows/ci.yaml @@ -200,11 +200,11 @@ jobs: )" steps: - ###################################################################################### - ## WARNING: DO NOT CHANGE THE JAVA VERSION HERE. SCALA IS DISTRIBUTED USING JAVA 8. ## - ###################################################################################### - - name: Set JDK 8 as default - run: echo "/usr/lib/jvm/java-8-openjdk-amd64/bin" >> $GITHUB_PATH + ############################################################################################################ + ## WARNING: DO NOT CHANGE THE JAVA VERSION HERE. SCALA 3.8.0+ IS DISTRIBUTED USING JAVA 17. ## + ############################################################################################################ + - name: Set JDK 17 as default # some projects depend on javalib elements removed in newer versions + run: echo "/usr/lib/jvm/java-17-openjdk-amd64/bin" >> $GITHUB_PATH - name: Reset existing repo run: | git config --global --add safe.directory $GITHUB_WORKSPACE @@ -257,11 +257,11 @@ jobs: )" steps: - ###################################################################################### - ## WARNING: DO NOT CHANGE THE JAVA VERSION HERE. SCALA IS DISTRIBUTED USING JAVA 8. ## - ###################################################################################### - - name: Set JDK 8 as default - run: echo "/usr/lib/jvm/java-8-openjdk-amd64/bin" >> $GITHUB_PATH + ############################################################################################################ + ## WARNING: DO NOT CHANGE THE JAVA VERSION HERE. SCALA 3.8.0+ IS DISTRIBUTED USING JAVA 17. ## + ############################################################################################################ + - name: Set JDK 17 as default # some projects depend on javalib elements removed in newer versions + run: echo "/usr/lib/jvm/java-17-openjdk-amd64/bin" >> $GITHUB_PATH - name: Reset existing repo run: | git config --global --add safe.directory $GITHUB_WORKSPACE @@ -314,11 +314,11 @@ jobs: )" steps: - ###################################################################################### - ## WARNING: DO NOT CHANGE THE JAVA VERSION HERE. SCALA IS DISTRIBUTED USING JAVA 8. ## - ###################################################################################### - - name: Set JDK 8 as default - run: echo "/usr/lib/jvm/java-8-openjdk-amd64/bin" >> $GITHUB_PATH + ############################################################################################################ + ## WARNING: DO NOT CHANGE THE JAVA VERSION HERE. SCALA 3.8.0+ IS DISTRIBUTED USING JAVA 17. ## + ############################################################################################################ + - name: Set JDK 17 as default + run: echo "/usr/lib/jvm/java-17-openjdk-amd64/bin" >> $GITHUB_PATH - name: Reset existing repo run: | git config --global --add safe.directory $GITHUB_WORKSPACE @@ -415,11 +415,11 @@ jobs: SONATYPE_USER: ${{ secrets.SONATYPE_USER_ORGSCALALANG }} steps: - ###################################################################################### - ## WARNING: DO NOT CHANGE THE JAVA VERSION HERE. SCALA IS DISTRIBUTED USING JAVA 8. ## - ###################################################################################### - - name: Set JDK 8 as default - run: echo "/usr/lib/jvm/java-8-openjdk-amd64/bin" >> $GITHUB_PATH + ############################################################################################## + ## WARNING: DO NOT CHANGE THE JAVA VERSION HERE. SCALA 3.8.0+ IS DISTRIBUTED USING JAVA 17. ## + ############################################################################################## + - name: Set JDK 17 as default + run: echo "/usr/lib/jvm/java-17-openjdk-amd64/bin" >> $GITHUB_PATH - name: Reset existing repo run: | git config --global --add safe.directory $GITHUB_WORKSPACE diff --git a/community-build/src/scala/dotty/communitybuild/projects.scala b/community-build/src/scala/dotty/communitybuild/projects.scala index 20cc90101eb5..f5f223bafbe7 100644 --- a/community-build/src/scala/dotty/communitybuild/projects.scala +++ b/community-build/src/scala/dotty/communitybuild/projects.scala @@ -127,6 +127,11 @@ object projects: s""";set $project/Compile/doc/sources ++= ($project/Compile/doc/dotty.tools.sbtplugin.DottyPlugin.autoImport.tastyFiles).value ;$project/doc""" ).mkString(" ") + private def removeRelease8(projects: String*): String = + projects.map(project => + s"""set $project/Compile/scalacOptions := ($project/Compile/scalacOptions).value.filterNot(opt => opt == "-release" || opt == "8")""" + ).mkString("; ") + private def aggregateDoc(in: String)(projects: String*) = val tastyFiles = (in +: projects).map(p => s"($p/Compile/doc/dotty.tools.sbtplugin.DottyPlugin.autoImport.tastyFiles).value").mkString(" ++ ") @@ -445,7 +450,10 @@ object projects: lazy val discipline = SbtCommunityProject( project = "discipline", - sbtTestCommand = "coreJVM/test;coreJS/test", + sbtTestCommand = List( + removeRelease8("core.jvm", "core.js"), + "coreJVM/test;coreJS/test" + ).mkString("; "), sbtPublishCommand = "set every credentials := Nil;coreJVM/publishLocal;coreJS/publishLocal", scalacOptions = SbtCommunityProject.scalacOptions.filter(_ != "-Wsafe-init"), ) @@ -544,7 +552,12 @@ object projects: lazy val scissLucre = SbtCommunityProject( project = "Lucre", - sbtTestCommand = "adjunctJVM/test;baseJVM/test;confluentJVM/test;coreJVM/test;dataJVM/test;exprJVM/test;geomJVM/test;lucre-bdb/test;testsJVM/test", + sbtTestCommand = + val subprojects = List("adjunct.jvm", "base.jvm", "confluent.jvm", "core.jvm", "data.jvm", "expr.jvm", "geom.jvm", "bdb", "tests.jvm") + List( + subprojects.map(name => s"""set ($name/Compile/compile/scalacOptions) := ($name/Compile/compile/scalacOptions).value.filterNot(opt => opt == "-release" || opt == "8")"""), + List("adjunctJVM/test;baseJVM/test;confluentJVM/test;coreJVM/test;dataJVM/test;exprJVM/test;geomJVM/test;lucre-bdb/test;testsJVM/test") + ).flatten.mkString("; "), extraSbtArgs = List("-Dde.sciss.lucre.ShortTests=true"), sbtPublishCommand = "adjunctJVM/publishLocal;baseJVM/publishLocal;confluentJVM/publishLocal;coreJVM/publishLocal;dataJVM/publishLocal;exprJVM/publishLocal;geomJVM/publishLocal;lucre-bdb/publishLocal", ) @@ -615,7 +628,11 @@ object projects: lazy val fs2 = SbtCommunityProject( project = "fs2", - sbtTestCommand = "coreJVM/test; coreJS/test", // io/test requires JDK9+ + sbtTestCommand = + List( + removeRelease8("coreJVM", "coreJS"), + "coreJVM/test; coreJS/test;" + ).mkString("; "), sbtPublishCommand = "coreJVM/publishLocal; coreJS/publishLocal", scalacOptions = SbtCommunityProject.scalacOptions.filter(_ != "-Wsafe-init"), ) diff --git a/community-build/test/scala/dotty/communitybuild/CommunityBuildTest.scala b/community-build/test/scala/dotty/communitybuild/CommunityBuildTest.scala index e2cc88c04d83..f83078f26c36 100644 --- a/community-build/test/scala/dotty/communitybuild/CommunityBuildTest.scala +++ b/community-build/test/scala/dotty/communitybuild/CommunityBuildTest.scala @@ -58,7 +58,8 @@ end CommunityBuildTestB @Category(Array(classOf[TestCategory])) class CommunityBuildTestC: @Test def akka = projects.akka.run() - @Test def betterfiles = projects.betterfiles.run() + // Disabled because `javax.xml.bind` is not available since java 11 + // @Test def betterfiles = projects.betterfiles.run() @Test def cask = projects.cask.run() // Temporarily disabled until problem discovered in comments to #9449 is fixed // @Test def dottyCpsAsync = projects.dottyCpsAsync.run() @@ -75,7 +76,8 @@ class CommunityBuildTestC: @Test def oslib = projects.oslib.run() // @Test def oslibWatch = projects.oslibWatch.run() @Test def parboiled2 = projects.parboiled2.run() - @Test def playJson = projects.playJson.run() + // Disabled because `javax.xml.bind` is not available since java 11 + // @Test def playJson = projects.playJson.run() @Test def pprint = projects.pprint.run() //@Test def protoquill = projects.protoquill.run() @Test def requests = projects.requests.run() diff --git a/compiler/src/dotty/tools/dotc/config/ScalaSettingsProperties.scala b/compiler/src/dotty/tools/dotc/config/ScalaSettingsProperties.scala index b50fe0b93023..069b383fd8a9 100644 --- a/compiler/src/dotty/tools/dotc/config/ScalaSettingsProperties.scala +++ b/compiler/src/dotty/tools/dotc/config/ScalaSettingsProperties.scala @@ -10,6 +10,8 @@ object ScalaSettingsProperties: private lazy val minTargetVersion = classfileVersionMap.keysIterator.min private lazy val maxTargetVersion = classfileVersionMap.keysIterator.max + private val minReleaseVersion = 17 + def supportedTargetVersions: List[String] = (minTargetVersion to maxTargetVersion).toList.map(_.toString) @@ -17,8 +19,8 @@ object ScalaSettingsProperties: if scala.util.Properties.isJavaAtLeast("9") then val jdkVersion = JDK9Reflectors.runtimeVersionMajor(JDK9Reflectors.runtimeVersion()).intValue() val maxVersion = Math.min(jdkVersion, maxTargetVersion) - (minTargetVersion to maxVersion).toList.map(_.toString) - else List(minTargetVersion).map(_.toString) + (minReleaseVersion to maxVersion).toList.map(_.toString) + else List(minReleaseVersion).map(_.toString) def supportedScalaReleaseVersions: List[String] = ScalaRelease.values.toList.map(_.show) diff --git a/compiler/src/dotty/tools/dotc/core/Definitions.scala b/compiler/src/dotty/tools/dotc/core/Definitions.scala index dbe1602e2d82..1b3f2a1e0a8f 100644 --- a/compiler/src/dotty/tools/dotc/core/Definitions.scala +++ b/compiler/src/dotty/tools/dotc/core/Definitions.scala @@ -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") diff --git a/compiler/src/dotty/tools/dotc/transform/LazyVals.scala b/compiler/src/dotty/tools/dotc/transform/LazyVals.scala index 76bc09e07540..cfb9122e9e23 100644 --- a/compiler/src/dotty/tools/dotc/transform/LazyVals.scala +++ b/compiler/src/dotty/tools/dotc/transform/LazyVals.scala @@ -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 methodHandlesLookupDef: Tree, var varHandleDefs: List[Tree]) private val appendOffsetDefs = mutable.Map.empty[Symbol, OffsetInfo] + private val appendVarHandleDefs = mutable.Map.empty[Symbol, VarHandleInfo] override def phaseName: String = LazyVals.name @@ -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.methodHandlesLookupDef +: data.varHandleDefs, template.body)) + } } private def addInFront(prefix: List[Tree], stats: List[Tree]) = stats match { @@ -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)) @@ -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 ) @@ -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 @@ -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 @@ -461,33 +474,37 @@ 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 containerSymbol.addAnnotations(x.symbol.annotations) // pass annotations from original definition containerSymbol.removeAnnotation(defn.ScalaStaticAnnot) - val getOffset = - 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) + val varHandleInfo = appendVarHandleDefs.getOrElseUpdate(claz, new VarHandleInfo(EmptyTree, Nil)) + varHandleInfo.methodHandlesLookupDef match + case EmptyTree => + val lookupSym: TermSymbol = newSymbol(claz, (s"${claz.name}${lazyNme.methodHandleLookupSuffix}").toTermName, Synthetic, defn.MethodHandlesLookupClass.typeRef).enteredAfter(this) + lookupSym.addAnnotation(Annotation(defn.ScalaStaticAnnot, lookupSym.span)) + varHandleInfo.methodHandlesLookupDef = + ValDef(lookupSym, Apply(Select(ref(defn.MethodHandlesClass), defn.MethodHandles_lookup.name), Nil)) + case _ => + + // create a VarHandle for this lazy val + val varHandleSymbol: TermSymbol = newSymbol(claz, s"$containerName${lazyNme.lzyHandleSuffix}".toTermName, Synthetic, defn.VarHandleClass.typeRef).enteredAfter(this) + varHandleSymbol.addAnnotation(Annotation(defn.ScalaStaticAnnot, varHandleSymbol.span)) + val getVarHandle = Apply( + Select(ref(varHandleInfo.methodHandlesLookupDef.symbol), defn.MethodHandlesLookup_FindVarHandle.name), + List(thizClass, Literal(Constant(containerName.mangledString)), Literal(Constant(defn.ObjectType))) + ) + val varHandleTree = ValDef(varHandleSymbol, getVarHandle) + val varHandle = ref(varHandleSymbol) + varHandleInfo.varHandleDefs = varHandleTree :: varHandleInfo.varHandleDefs 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) } @@ -666,7 +683,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 @@ -687,5 +703,13 @@ object LazyVals { val current: TermName = "current".toTermName val lock: TermName = "lock".toTermName val discard: TermName = "discard".toTermName + val compareAndSet: TermName = "compareAndSet".toTermName + val lzyHandleSuffix: String = "$$lzyHandle" + val methodHandleLookupSuffix: String = "$$methodHandleLookup" } + + extension (sym: Symbol) def isVarHandleForLazy(using Context) = + sym.name.endsWith(lazyNme.lzyHandleSuffix) + extension (sym: Symbol) def isMethodLookupForLazy(using Context) = + sym.name.endsWith(lazyNme.methodHandleLookupSuffix) } diff --git a/compiler/src/dotty/tools/dotc/transform/MoveStatics.scala b/compiler/src/dotty/tools/dotc/transform/MoveStatics.scala index b3ec05501b5b..a0db7343901c 100644 --- a/compiler/src/dotty/tools/dotc/transform/MoveStatics.scala +++ b/compiler/src/dotty/tools/dotc/transform/MoveStatics.scala @@ -12,13 +12,18 @@ import SymDenotations.SymDenotation import Names.Name import StdNames.nme import NameOps.* +import LazyVals.{isVarHandleForLazy, isMethodLookupForLazy} import ast.* +import scala.collection.mutable 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 (more in LazyVals.scala) are left in the original class. + * MethodSymbols.lookup() generated by the compiler is extracted to be a local static constructor value. + */ class MoveStatics extends MiniPhase with SymTransformer { import ast.tpd.* @@ -28,7 +33,8 @@ 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.isVarHandleForLazy && !sym.symbol.isMethodLookupForLazy)) { sym.owner.asClass.delete(sym.symbol) sym.owner.companionClass.asClass.enter(sym.symbol) sym.copySymDenotation(owner = sym.owner.companionClass) @@ -40,7 +46,11 @@ class MoveStatics extends MiniPhase with SymTransformer { val (classes, others) = trees.partition(x => x.isInstanceOf[TypeDef] && x.symbol.isClass) val pairs = classes.groupBy(_.symbol.name.stripModuleClassSuffix).asInstanceOf[Map[Name, List[TypeDef]]] - def rebuild(orig: TypeDef, newBody: List[Tree]): Tree = { + /** Rebuilds a template with static ValDefs from `newBody`` initialized in a static constructor. + * `localStaticDefs`, used for initialisation of other static values, + * are moved entirely into the static constructor. + */ + def rebuild(orig: TypeDef, newBody: List[Tree], localStaticDefs: List[ValDef]): Tree = { val staticFields = newBody.filter(x => x.isInstanceOf[ValDef] && x.symbol.hasAnnotation(defn.ScalaStaticAnnot)).asInstanceOf[List[ValDef]] val newBodyWithStaticConstr = if (staticFields.nonEmpty) { @@ -49,8 +59,24 @@ class MoveStatics extends MiniPhase with SymTransformer { staticCostructor.addAnnotation(Annotation(defn.ScalaStaticAnnot, staticCostructor.span)) staticCostructor.entered - val staticAssigns = staticFields.map(x => Assign(ref(x.symbol), x.rhs.changeOwner(x.symbol, staticCostructor))) - tpd.DefDef(staticCostructor, Block(staticAssigns, tpd.unitLiteral)) :: newBody + val symbolRemap = localStaticDefs.map { x => + x.symbol.owner.asClass.delete(x.symbol) + x.changeOwner(x.symbol.owner, staticCostructor) + (x.symbol, x.symbol.copy(owner = staticCostructor)) + }.toMap + + val localValDefs = localStaticDefs.map ( vDef => + val newSymbol = symbolRemap(vDef.symbol) + ValDef(newSymbol.asTerm, vDef.rhs.changeOwner(vDef.symbol, newSymbol)) + ) + + val localValDefMapper = new TreeMap: + override def transform(tree: Tree)(using Context): Tree = tree match + case ident @ Ident(tp) if symbolRemap.contains(ident.symbol) => ref(symbolRemap(ident.symbol)) + case _ => super.transform(tree) + + val staticAssigns = staticFields.map(x => Assign(ref(x.symbol), localValDefMapper.transform(x.rhs).changeOwner(x.symbol, staticCostructor))) + tpd.DefDef(staticCostructor, Block(localValDefs ++ staticAssigns, tpd.unitLiteral)) :: newBody } else newBody @@ -64,12 +90,24 @@ class MoveStatics extends MiniPhase with SymTransformer { else { 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 _ => false + + val staticDefs = mutable.ListBuffer[Tree]() + val staticTiedDefs = mutable.ListBuffer[Tree]() + val localStaticDefs = mutable.ListBuffer[ValDef]() + val remainingDefs = mutable.ListBuffer[Tree]() + + moduleTmpl.body.foreach { + case valDef: ValDef if valDef.symbol.isMethodLookupForLazy && valDef.symbol.isScalaStatic => + localStaticDefs.addOne(valDef) + case memberDef: MemberDef if memberDef.symbol.isScalaStatic => + if memberDef.symbol.isVarHandleForLazy then + staticTiedDefs.addOne(memberDef) + else + staticDefs.addOne(memberDef) + case other => remainingDefs.addOne(other) } - rebuild(companion, companionTmpl.body ++ staticDefs) :: rebuild(module, remainingDefs) :: Nil + rebuild(companion, companionTmpl.body ++ staticDefs, Nil) :: rebuild(module, staticTiedDefs.toList ++ remainingDefs.toList, localStaticDefs.toList) :: Nil } } val newPairs = @@ -78,7 +116,11 @@ class MoveStatics extends MiniPhase with SymTransformer { if (classes.tail.isEmpty) { val classDef = classes.head val tmpl = classDef.rhs.asInstanceOf[Template] - rebuild(classDef, tmpl.body) :: Nil + val (localStaticDefs, remainingDefs) = tmpl.body.partition { + case valDef: ValDef => valDef.symbol.isMethodLookupForLazy && valDef.symbol.isScalaStatic + case _ => false + } + rebuild(classDef, remainingDefs, localStaticDefs.asInstanceOf[List[ValDef]]) :: Nil } else move(classes.head, classes.tail.head) Trees.flatten(newPairs.toList.flatten ++ others) diff --git a/presentation-compiler/test/dotty/tools/pc/tests/completion/CompletionRelease8Suite.scala b/presentation-compiler/test/dotty/tools/pc/tests/completion/CompletionRelease8Suite.scala index 587cd5a53073..d77f17bc9a36 100644 --- a/presentation-compiler/test/dotty/tools/pc/tests/completion/CompletionRelease8Suite.scala +++ b/presentation-compiler/test/dotty/tools/pc/tests/completion/CompletionRelease8Suite.scala @@ -4,10 +4,11 @@ import dotty.tools.pc.base.BaseCompletionSuite import org.junit.Test import org.junit.Before +import org.junit.Ignore import java.nio.file.Path import dotty.tools.pc.utils.JRE -class CompletionRelease8Suite extends BaseCompletionSuite: +@Ignore class CompletionRelease8Suite extends BaseCompletionSuite: override protected def scalacOptions(classpath: Seq[Path]): Seq[String] = "-release:8" +: super.scalacOptions(classpath) diff --git a/project/MiMaFilters.scala b/project/MiMaFilters.scala index e2ca983081d2..5d28d248c3eb 100644 --- a/project/MiMaFilters.scala +++ b/project/MiMaFilters.scala @@ -138,6 +138,16 @@ object MiMaFilters { ProblemFilters.exclude[ReversedMissingMethodProblem]("scala.quoted.Quotes#reflectModule#MethodTypeMethods.isContextual"), ProblemFilters.exclude[ReversedMissingMethodProblem]("scala.quoted.Quotes#reflectModule#ImplicitsModule.searchIgnoring"), ProblemFilters.exclude[ReversedMissingMethodProblem]("scala.quoted.Quotes#reflectModule#ValDefModule.let"), + + // Changes to lazy vals (added static constructors) + ProblemFilters.exclude[DirectMissingMethodProblem]("scala.Tuple."), + ProblemFilters.exclude[DirectMissingMethodProblem]("scala.collection.immutable.ArraySeq."), + ProblemFilters.exclude[DirectMissingMethodProblem]("scala.concurrent.ExecutionContext."), + ProblemFilters.exclude[DirectMissingMethodProblem]("scala.io.Codec."), + ProblemFilters.exclude[DirectMissingMethodProblem]("scala.math.BigDecimal."), + ProblemFilters.exclude[DirectMissingMethodProblem]("scala.sys.SystemProperties."), + ProblemFilters.exclude[DirectMissingMethodProblem]("scala.sys.process.Process."), + // Change `experimental` annotation to a final class ProblemFilters.exclude[FinalClassProblem]("scala.annotation.experimental"), diff --git a/sbt-test/sbt-dotty/scaladoc-regressions/build.sbt b/sbt-test/sbt-dotty/scaladoc-regressions/build.sbt index bfdadb5ee038..513b7ef24a73 100644 --- a/sbt-test/sbt-dotty/scaladoc-regressions/build.sbt +++ b/sbt-test/sbt-dotty/scaladoc-regressions/build.sbt @@ -6,4 +6,4 @@ lazy val i20476 = project lazy val i18231 = project .in(file("i18231")) - .settings(scalacOptions += "-release:8") + // .settings(scalacOptions += "-release:8") diff --git a/tests/neg/i15144.scala b/tests/disabled/neg/i15144.scala similarity index 100% rename from tests/neg/i15144.scala rename to tests/disabled/neg/i15144.scala diff --git a/tests/neg-custom-args/jdk-9-app.check b/tests/disabled/neg/jdk-9-app.check similarity index 100% rename from tests/neg-custom-args/jdk-9-app.check rename to tests/disabled/neg/jdk-9-app.check diff --git a/tests/neg/jdk-9-app.scala b/tests/disabled/neg/jdk-9-app.scala similarity index 100% rename from tests/neg/jdk-9-app.scala rename to tests/disabled/neg/jdk-9-app.scala diff --git a/tests/pos/jdk-8-app.scala b/tests/disabled/pos/jdk-8-app.scala similarity index 100% rename from tests/pos/jdk-8-app.scala rename to tests/disabled/pos/jdk-8-app.scala diff --git a/tests/printing/transformed/lazy-vals-new.check b/tests/printing/transformed/lazy-vals-new.check index 75ae8885f68c..99de2f2fa734 100644 --- a/tests/printing/transformed/lazy-vals-new.check +++ b/tests/printing/transformed/lazy-vals-new.check @@ -9,14 +9,17 @@ package { } @static private def (): Unit = { - A.OFFSET$_m_0 = - scala.runtime.LazyVals.getOffsetStatic( - classOf[Object {...}].getDeclaredField("x$lzy1")) + val A$$$methodHandleLookup: + java.lang.invoke.MethodHandles.MethodHandles$Lookup = + java.lang.invoke.MethodHandles.lookup() + A.x$lzy1$$lzyHandle = + A$$$methodHandleLookup.findVarHandle(classOf[Object {...}], "x$lzy1", + classOf[Object]) () } - @static @static val OFFSET$_m_0: Long = - scala.runtime.LazyVals.getOffsetStatic( - classOf[Object {...}].getDeclaredField("x$lzy1")) + @static val x$lzy1$$lzyHandle: java.lang.invoke.VarHandle = + A.A$$$methodHandleLookup.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 @@ -33,7 +36,7 @@ package { 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 { @@ -49,15 +52,14 @@ package { } 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 () @@ -71,7 +73,7 @@ package { 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()) () }