diff --git a/community-build/src/scala/dotty/communitybuild/projects.scala b/community-build/src/scala/dotty/communitybuild/projects.scala index c5e7997d0b21..68e95d6d7310 100644 --- a/community-build/src/scala/dotty/communitybuild/projects.scala +++ b/community-build/src/scala/dotty/communitybuild/projects.scala @@ -791,14 +791,19 @@ object projects: dependencies = () => List(cats, disciplineMunit) ) - lazy val http4s = SbtCommunityProject( - project = "http4s", - sbtTestCommand = "tests/test; server/test; client/test; ember-core/test; ember-server/test; ember-client/test; circe/test", - sbtPublishCommand = "publishLocal", - scalacOptions = SbtCommunityProject.scalacOptions.filter(_ != "-Ysafe-init"), - dependencies = () => List(cats, catsEffect3, fs2, disciplineMunit, scalacheckEffect) + lazy val specs2 = SbtCommunityProject( + project = "specs2", + sbtTestCommand = "core/testOnly -- exclude ci", + sbtPublishCommand = "core/publishLocal", + dependencies = () => List() ) + lazy val spire = SbtCommunityProject( + project = "spire", + sbtTestCommand = "test", + sbtPublishCommand = "publishLocal", + dependencies = () => List(cats, disciplineMunit) + ) end projects lazy val forwardCompatMapping = Map[CommunityProject, CommunityProject]( diff --git a/compiler/src/dotty/tools/dotc/transform/LazyVals.scala b/compiler/src/dotty/tools/dotc/transform/LazyVals.scala index e72874dfaadd..fc9e6c3977d5 100644 --- a/compiler/src/dotty/tools/dotc/transform/LazyVals.scala +++ b/compiler/src/dotty/tools/dotc/transform/LazyVals.scala @@ -23,10 +23,17 @@ class LazyVals extends MiniPhase with IdentityDenotTransformer { import LazyVals._ import tpd._ - /** this map contains mutable state of transformation: OffsetDefs to be appended to companion object definitions, - * and number of bits currently used */ - class OffsetInfo(var defs: List[Tree], var ord:Int) + /** + * The map contains the list of the offset trees. + */ + class OffsetInfo(var defs: List[Tree]) + /** + * This map contains mutable state of transformation: OffsetDefs to be appended + * to companion object definitions, and number of bits currently used. + */ + class OldOffsetInfo(defs: List[Tree], var ord: Int) extends OffsetInfo(defs) private val appendOffsetDefs = mutable.Map.empty[Symbol, OffsetInfo] + private val oldAppendOffsetDefs = mutable.Map.empty[Symbol, OldOffsetInfo] override def phaseName: String = LazyVals.name @@ -52,6 +59,20 @@ class LazyVals extends MiniPhase with IdentityDenotTransformer { else nullables.toList } + private inline def isOldLazyVals(using ctx: Context): Boolean = + import dotty.tools.dotc.config.ScalaRelease._ + ctx.scalaRelease <= Release3_1 + + private def initBlock(stats: List[Tree])(using Context): Block = stats match + case Nil => throw new IllegalArgumentException("trying to create an empty Block") + case x :: Nil => Block(List(x), EmptyTree) + case x :: xs => Block(stats.init, stats.last) + + private def needsBoxing(tp: Type)(using Context): Boolean = tp != NoType && tp != defn.UnitType && tp.classSymbol.isPrimitiveValueClass + + private def boxIfCan(tp: Type)(using Context): Type = + assert(needsBoxing(tp)) + defn.boxedType(tp) override def prepareForUnit(tree: Tree)(using Context): Context = { if (lazyValNullables == null) @@ -62,7 +83,6 @@ class LazyVals extends MiniPhase with IdentityDenotTransformer { override def transformDefDef(tree: DefDef)(using Context): Tree = transformLazyVal(tree) - override def transformValDef(tree: ValDef)(using Context): Tree = transformLazyVal(tree) @@ -101,13 +121,13 @@ class LazyVals extends MiniPhase with IdentityDenotTransformer { } } - - /** Append offset fields to companion objects - */ + /** + * Append offset fields to companion objects. + */ override def transformTemplate(template: Template)(using Context): Tree = { val cls = ctx.owner.asClass - - appendOffsetDefs.get(cls) match { + + (if isOldLazyVals then oldAppendOffsetDefs else appendOffsetDefs).get(cls) match { case None => template case Some(data) => data.defs.foreach(_.symbol.addAnnotation(Annotation(defn.ScalaStaticAnnot))) @@ -115,16 +135,15 @@ class LazyVals extends MiniPhase with IdentityDenotTransformer { } } - private def addInFront(prefix: List[Tree], stats: List[Tree]) = stats match { case first :: rest if isSuperConstrCall(first) => first :: prefix ::: rest case _ => prefix ::: stats } - /** Make an eager val that would implement synthetic module. - * Eager val ensures thread safety and has less code generated. - * - */ + /** + * Make an eager val that would implement synthetic module. + * Eager val ensures thread safety and has less code generated. + */ def transformSyntheticModule(tree: ValOrDefDef)(using Context): Thicket = { val sym = tree.symbol val holderSymbol = newSymbol(sym.owner, LazyLocalName.fresh(sym.asTerm.name), @@ -134,7 +153,8 @@ class LazyVals extends MiniPhase with IdentityDenotTransformer { Thicket(field, getter) } - /** Desugar a local `lazy val x: Int = ` into: + /** + * Desugar a local `lazy val x: Int = ` into: * * ``` * val x$lzy = new scala.runtime.LazyInt() @@ -186,7 +206,6 @@ class LazyVals extends MiniPhase with IdentityDenotTransformer { Thicket(holderTree, initTree, accessor) } - override def transformStats(trees: List[tpd.Tree])(using Context): List[Tree] = { // backend requires field usage to be after field definition // need to bring containers to start of method @@ -274,6 +293,227 @@ class LazyVals extends MiniPhase with IdentityDenotTransformer { } } + def transformMemberDefThreadSafe(x: ValOrDefDef)(using Context): Thicket = { + assert(!(x.symbol is Mutable)) + // generate old code for compatibility + // TODO find more meaningful names than old/new + if isOldLazyVals then + transformMemberDefThreadSafeOld(x) + else + transformMemberDefThreadSafeNew(x) + } + + /** + * Create a threadsafe lazy accessor equivalent to the following code: + * ``` + * private @volatile var _x: AnyRef = null + * @tailrec def x: A = + * _x match + * case current: A => + * current + * case null => + * if CAS(_x, null, Evaluating) then + * var result: AnyRef = null // here, we need `AnyRef` to possibly assign `NULL` + * try + * result = rhs + * nullable = null // if the field is nullable; see `CollectNullableFields` + * if result == null then result = NULL // drop if A is non-nullable + * finally + * if !CAS(_x, Evaluating, result) then + * val lock = _x.asInstanceOf[Waiting] + * CAS(_x, lock, result) + * lock.release() + * x + * case Evaluating => + * CAS(_x, Evaluating, new Waiting) + * x + * case current: Waiting => + * current.awaitRelease() + * x + * case NULL => null + * ``` + * Where `Evaluating` and `NULL` are represented by `object`s and `Waiting` by a class that + * allows awaiting the completion of the evaluation. Note that since tail-recursive + * functions are transformed *before* lazy-vals, this implementation directly implements + * the resulting loop. `PatternMatcher` coming before `LazyVals`, the pattern matching block + * is implemented using if-s. That is: + * + * ``` + * private @volatile var _x: AnyRef = null + * def x: A = + * while true do + * val current: AnyRef = _x + * if current == null then + * if CAS(_x, null, Evaluating) then + * var result: AnyRef = null + * try + * result = rhs + * nullable = null + * if result == null then result = NULL + * finally + * if !CAS(_x, Evaluating, result) then + * val lock = _x.asInstanceOf[Waiting] + * CAS(_x, lock, result) + * lock.release() + * else + * if current.isInstanceOf[Evaluating] then + * CAS(current, Evaluating, new Waiting) + * else if current.isInstanceOf[NULL] then + * null + * else if current.isInstanceOf[Waiting] then + * current.asInstanceOf[Waiting].awaitRelease() + * else + * current.asInstanceOf[A] + * end while + * ``` + * + * @param methodSymbol the symbol of the new method + * @param claz the class containing this lazy val field + * @param target the target synthetic field + * @param rhs the right-hand side expression of the lazy val + * @param tp the type of the lazy val + * @param offset the offset of the field in the bitmap + * @param getFlag a flag for the volatile get function + * @param objCasFlag a flag for the CAS function operating on objects + * @param waiting a reference to the `Waiting` runtime class + * @param evaluating a reference to the `Evaluating` runtime object + * @param nullValued a reference to the `NULL` runtime object + */ + def mkThreadSafeDefNew(methodSymbol: TermSymbol, + claz: ClassSymbol, + target: Symbol, + rhs: Tree, + tp: Type, + offset: Tree, + objCasFlag: Tree, + waiting: Tree, + evaluating: Tree, + nullValued: Tree, + thiz: Tree)(using Context): DefDef = { + val discardSymb = newSymbol(methodSymbol, lazyNme.discard, Method | Synthetic, MethodType(Nil)(_ => Nil, _ => defn.UnitType)) + val discardDef = DefDef(discardSymb, initBlock( + objCasFlag.appliedTo(thiz, offset, evaluating, Select(New(waiting), StdNames.nme.CONSTRUCTOR).ensureApplied) + :: Return(unitLiteral, discardSymb) :: Nil)) + // if observed a null value + val unevaluated = { + // var res: AnyRef + val resSymb = newSymbol(methodSymbol, lazyNme.result, Synthetic | Mutable, defn.ObjectType) + // releasing block in finally + val lockRel = { + val lockSymb = newSymbol(methodSymbol, lazyNme.lock, Synthetic, waiting.typeOpt) + initBlock(ValDef(lockSymb, ref(target).cast(waiting.typeOpt)) + :: objCasFlag.appliedTo(thiz, offset, ref(lockSymb), ref(resSymb)) + :: ref(lockSymb).select(lazyNme.RLazyVals.waitingRelease).ensureApplied :: Nil) + } + // finally block + val fin = If( + objCasFlag.appliedTo(thiz, offset, evaluating, ref(resSymb)).equal(Literal(Constant(false))), + lockRel, + EmptyTree + ).withType(defn.UnitType) + // entire try block + val evaluate = Try( + initBlock( + Assign(ref(resSymb), if needsBoxing(tp) && rhs != EmptyTree then rhs.ensureConforms(boxIfCan(tp)) else rhs) // try result = rhs + :: nullOut(nullableFor(methodSymbol)) + ::: If(ref(resSymb).equal(nullLiteral), Assign(ref(resSymb), nullValued), EmptyTree).withType(defn.UnitType) // if result == null then result = NULL + :: Nil + ), + Nil, + fin + ) + // if CAS(...) + If( + objCasFlag.appliedTo(thiz, offset, nullLiteral, evaluating), + initBlock(ValDef(resSymb, nullLiteral) // var result: AnyRef = null + :: evaluate // try ... finally ... + :: Nil), + EmptyTree + ).withType(defn.UnitType) + } + val current = newSymbol(methodSymbol, lazyNme.current, Synthetic, defn.ObjectType) + val ifNotNull = + If( + ref(current).select(defn.Any_isInstanceOf).appliedToTypeTree(evaluating), + ref(discardSymb).ensureApplied, + // not an Evaluating + If( + ref(current).select(defn.Any_isInstanceOf).appliedToTypeTree(nullValued), + Return(defaultValue(tp), methodSymbol), + // not a NULL + If( + ref(current).select(defn.Any_isInstanceOf).appliedToTypeTree(waiting), + ref(current).select(defn.Any_asInstanceOf).appliedToTypeTree(waiting).select(lazyNme.RLazyVals.waitingAwaitRelease).ensureApplied, + // not a Waiting, then is an A + Return(ref(current).ensureConforms(tp), methodSymbol) + ) + ) + ) + val body = initBlock(ValDef(current, ref(target)) :: If(ref(current).equal(nullLiteral), unevaluated, ifNotNull) :: Nil) + val mainLoop = WhileDo(EmptyTree, body) // becomes: while (true) do { body } + val ret = DefDef(methodSymbol, initBlock(discardDef :: mainLoop :: Nil)) + ret + } + + def transformMemberDefThreadSafeNew(x: ValOrDefDef)(using Context): Thicket = { + import dotty.tools.dotc.core.Types._ + import dotty.tools.dotc.core.Flags._ + + val runtimeModule = "scala.runtime.LazyVals" + val tpe = x.tpe.widen.resultType.widen + val claz = x.symbol.owner.asClass + val thizClass = Literal(Constant(claz.info)) + val helperModule = requiredModule(runtimeModule) + var offsetSymbol: TermSymbol | Null = null + + 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, containerFlags, defn.ObjectType).enteredAfter(this) + containerSymbol.addAnnotation(Annotation(defn.VolatileAnnot)) // private @volatile var _x: AnyRef + val stat = x.symbol.isStatic + if stat then + containerSymbol.setFlag(JavaStatic) + val getOffset = + if stat then + Select(ref(helperModule), lazyNme.RLazyVals.getStaticOffset) + else + Select(ref(helperModule), lazyNme.RLazyVals.getOffset) + val containerTree = ValDef(containerSymbol, nullLiteral) + def staticOrFieldOff: Tree = getOffset.appliedTo(thizClass, Literal(Constant(containerName.toString))) + + // create an offset for this lazy val + appendOffsetDefs.get(claz) match + case Some(info) => + offsetSymbol = newSymbol(claz, offsetName(info.defs.size), Synthetic, defn.LongType).enteredAfter(this) + offsetSymbol.nn.addAnnotation(Annotation(defn.ScalaStaticAnnot)) + val offsetTree = ValDef(offsetSymbol.nn, staticOrFieldOff) + info.defs = offsetTree :: info.defs + case None => + offsetSymbol = newSymbol(claz, offsetName(0), Synthetic, defn.LongType).enteredAfter(this) + offsetSymbol.nn.addAnnotation(Annotation(defn.ScalaStaticAnnot)) + val offsetTree = ValDef(offsetSymbol.nn, staticOrFieldOff) + appendOffsetDefs += (claz -> new OffsetInfo(List(offsetTree))) + + val waiting = requiredClass(s"$runtimeModule.${lazyNme.RLazyVals.waiting}") + val evaluating = Select(ref(helperModule), lazyNme.RLazyVals.evaluating) + val nullValued = Select(ref(helperModule), lazyNme.RLazyVals.nullValued) + val objCas = Select(ref(helperModule), lazyNme.RLazyVals.objCas) + + val offset = ref(offsetSymbol.nn) + + val swapOver = + if stat then + tpd.clsOf(x.symbol.owner.typeRef) + else + This(claz) + + val methodSymbol = x.symbol.asTerm + val accessor = mkThreadSafeDefNew(methodSymbol, claz, containerSymbol, x.rhs, tpe, offset, objCas, + ref(waiting), evaluating, nullValued, swapOver) + Thicket(containerTree, accessor) + } + /** Create a threadsafe lazy accessor equivalent to such code * ``` * def methodSymbol(): Int = { @@ -305,7 +545,7 @@ class LazyVals extends MiniPhase with IdentityDenotTransformer { * } * ``` */ - def mkThreadSafeDef(methodSymbol: TermSymbol, + def mkThreadSafeDefOld(methodSymbol: TermSymbol, claz: ClassSymbol, ord: Int, target: Symbol, @@ -374,9 +614,7 @@ class LazyVals extends MiniPhase with IdentityDenotTransformer { DefDef(methodSymbol, loop) } - def transformMemberDefThreadSafe(x: ValOrDefDef)(using Context): Thicket = { - assert(!(x.symbol is Mutable)) - + def transformMemberDefThreadSafeOld(x: ValOrDefDef)(using Context): Thicket = { val tpe = x.tpe.widen.resultType.widen val claz = x.symbol.owner.asClass val thizClass = Literal(Constant(claz.info)) @@ -389,7 +627,7 @@ class LazyVals extends MiniPhase with IdentityDenotTransformer { def offsetName(id: Int) = s"${StdNames.nme.LAZY_FIELD_OFFSET}${if (x.symbol.owner.is(Module)) "_m_" else ""}$id".toTermName // compute or create appropriate offsetSymbol, bitmap and bits used by current ValDef - appendOffsetDefs.get(claz) match { + oldAppendOffsetDefs.get(claz) match { case Some(info) => val flagsPerLong = (64 / scala.runtime.LazyVals.BITS_PER_LAZY_VAL).toInt info.ord += 1 @@ -417,7 +655,7 @@ class LazyVals extends MiniPhase with IdentityDenotTransformer { val flagSymbol = newSymbol(claz, flagName, containerFlags, defn.LongType).enteredAfter(this) flag = ValDef(flagSymbol, Literal(Constant(0L))) val offsetTree = ValDef(offsetSymbol.nn, getOffset.appliedTo(thizClass, Literal(Constant(flagName.toString)))) - appendOffsetDefs += (claz -> new OffsetInfo(List(offsetTree), ord)) + oldAppendOffsetDefs += (claz -> new OldOffsetInfo(List(offsetTree), ord)) } val containerName = LazyLocalName.fresh(x.name.asTermName) @@ -432,7 +670,7 @@ class LazyVals extends MiniPhase with IdentityDenotTransformer { val state = Select(ref(helperModule), lazyNme.RLazyVals.state) val cas = Select(ref(helperModule), lazyNme.RLazyVals.cas) - val accessor = mkThreadSafeDef(x.symbol.asTerm, claz, ord, containerSymbol, x.rhs, tpe, offset, getFlag, state, cas, setFlag, wait) + val accessor = mkThreadSafeDefOld(x.symbol.asTerm, claz, ord, containerSymbol, x.rhs, tpe, offset, getFlag, state, cas, setFlag, wait) if (flag eq EmptyTree) Thicket(containerTree, accessor) else Thicket(containerTree, flag, accessor) @@ -447,12 +685,19 @@ object LazyVals { import Names.TermName object RLazyVals { import scala.runtime.LazyVals.{Names => N} + val waiting: TermName = N.waiting.toTermName + val waitingAwaitRelease: TermName = N.waitingAwaitRelease.toTermName + val waitingRelease: TermName = N.waitingRelease.toTermName + val evaluating: TermName = N.evaluating.toTermName + val nullValued: TermName = N.nullValued.toTermName + val objCas: TermName = N.objCas.toTermName + val getOffset: TermName = N.getOffset.toTermName + val getStaticOffset: TermName = N.getStaticOffset.toTermName val get: TermName = N.get.toTermName val setFlag: TermName = N.setFlag.toTermName val wait4Notification: TermName = N.wait4Notification.toTermName val state: TermName = N.state.toTermName val cas: TermName = N.cas.toTermName - val getOffset: TermName = N.getOffset.toTermName } val flag: TermName = "flag".toTermName val state: TermName = "state".toTermName @@ -461,5 +706,8 @@ object LazyVals { val initialized: TermName = "initialized".toTermName val initialize: TermName = "initialize".toTermName val retry: TermName = "retry".toTermName + val current: TermName = "current".toTermName + val lock: TermName = "lock".toTermName + val discard: TermName = "discard".toTermName } } diff --git a/compiler/test-resources/scripting/scriptPath.sc b/compiler/test-resources/scripting/scriptPath.sc index 46cd5e8a7385..837bd674aad8 100755 --- a/compiler/test-resources/scripting/scriptPath.sc +++ b/compiler/test-resources/scripting/scriptPath.sc @@ -16,6 +16,8 @@ val pathEntries = System.getenv("PATH").split(psep).toList System.err.printf("sun.java.command: %s\n", sys.props("sun.java.command")) System.err.printf("first 5 PATH entries:\n%s\n",pathEntries.take(5).mkString("\n")) + printf("script.path: %s\n",path.norm) + assert(path.endsWith("scriptPath.sc"),s"actual path [$path]") } extension(s: String) diff --git a/compiler/test/dotty/tools/backend/jvm/DottyBytecodeTests.scala b/compiler/test/dotty/tools/backend/jvm/DottyBytecodeTests.scala index a85c28a9f878..a9b70faab729 100644 --- a/compiler/test/dotty/tools/backend/jvm/DottyBytecodeTests.scala +++ b/compiler/test/dotty/tools/backend/jvm/DottyBytecodeTests.scala @@ -597,7 +597,7 @@ class TestBCode extends DottyBytecodeTest { val clsIn = dir.lookupName("Test.class", directory = false).input val clsNode = loadClassNode(clsIn) val method = getMethod(clsNode, "test") - assertEquals(93, instructionsFromMethod(method).size) + assertEquals(122, instructionsFromMethod(method).size) } } diff --git a/library/src/scala/runtime/LazyVals.scala b/library/src/scala/runtime/LazyVals.scala index 85ca4f5cb3a0..1713fbe141ff 100644 --- a/library/src/scala/runtime/LazyVals.scala +++ b/library/src/scala/runtime/LazyVals.scala @@ -1,5 +1,7 @@ package scala.runtime +import java.lang.reflect.Modifier + /** * Helper methods used in thread-safe lazy vals. */ @@ -22,6 +24,7 @@ object LazyVals { val processors = java.lang.Runtime.getRuntime.nn.availableProcessors() 8 * processors * processors } + private[this] val monitors: Array[Object] = Array.tabulate(base)(_ => new Object) @@ -37,6 +40,41 @@ object LazyVals { /* ------------- Start of public API ------------- */ + /** + * Used to indicate the state of a lazy val that is being + * evaluated and of which other threads await the result. + */ + final class Waiting: + private var done = false + + /** + * Wakes up waiting threads. Called on completion of the evaluation + * of lazy val's right-hand side. + */ + def release(): Unit = synchronized { + done = true + notifyAll() + } + + /** + * Awaits the completion of the evaluation of lazy val's right-hand side. + */ + def awaitRelease(): Unit = synchronized { + while !done do wait() + } + + /** + * Used to indicate the state of a lazy val that is currently being + * evaluated with no other thread awaiting its result. + */ + object Evaluating + + /** + * Used to indicate the state of a lazy val that has been evaluated to + * `null`. + */ + object NULL + final val BITS_PER_LAZY_VAL = 2L def STATE(cur: Long, ord: Int): Long = { @@ -54,6 +92,12 @@ object LazyVals { unsafe.compareAndSwapLong(t, offset, e, n) } + def objCAS(t: Object, offset: Long, exp: Object, n: Object): Boolean = { + if (debug) + println(s"objCAS($t, $exp, $n)") + unsafe.compareAndSwapObject(t, offset, exp, n) + } + def setFlag(t: Object, offset: Long, v: Int, ord: Int): Unit = { if (debug) println(s"setFlag($t, $offset, $v, $ord)") @@ -106,12 +150,26 @@ object LazyVals { r } + def getStaticOffset(clz: Class[_], name: String): Long = { + val r = unsafe.staticFieldOffset(clz.getDeclaredField(name)) + if (debug) + println(s"getStaticOffset($clz, $name) = $r") + r + } + object Names { + final val waiting = "Waiting" + final val evaluating = "Evaluating" + final val nullValued = "NULL" + final val waitingAwaitRelease = "awaitRelease" + final val waitingRelease = "release" final val state = "STATE" final val cas = "CAS" + final val objCas = "objCAS" final val setFlag = "setFlag" final val wait4Notification = "wait4Notification" final val get = "get" final val getOffset = "getOffset" + final val getStaticOffset = "getStaticOffset" } } diff --git a/project/MiMaFilters.scala b/project/MiMaFilters.scala index 8bd16f134f57..44276fa8d6fe 100644 --- a/project/MiMaFilters.scala +++ b/project/MiMaFilters.scala @@ -12,12 +12,24 @@ object MiMaFilters { ProblemFilters.exclude[ReversedMissingMethodProblem]("scala.quoted.Quotes#reflectModule#SymbolMethods.termRef"), ProblemFilters.exclude[ReversedMissingMethodProblem]("scala.quoted.Quotes#reflectModule#TypeTreeModule.ref"), - // Experimental `MainAnnotation` APIs. Can be added in 3.3.0 or later. - ProblemFilters.exclude[MissingClassProblem]("scala.annotation.MainAnnotation"), - ProblemFilters.exclude[MissingClassProblem]("scala.annotation.MainAnnotation$"), - ProblemFilters.exclude[MissingClassProblem]("scala.annotation.MainAnnotation$Command"), - ProblemFilters.exclude[MissingClassProblem]("scala.annotation.MainAnnotation$CommandInfo"), - ProblemFilters.exclude[MissingClassProblem]("scala.annotation.MainAnnotation$ParameterInfo"), - ProblemFilters.exclude[MissingClassProblem]("scala.annotation.MainAnnotation$ParameterAnnotation"), + // Private to the compiler - needed for forward binary compatibility + ProblemFilters.exclude[MissingClassProblem]("scala.annotation.since"), + + ProblemFilters.exclude[DirectMissingMethodProblem]("scala.runtime.LazyVals.getStaticOffset"), + ProblemFilters.exclude[DirectMissingMethodProblem]("scala.runtime.LazyVals.getStaticOffset"), + ProblemFilters.exclude[DirectMissingMethodProblem]("scala.runtime.LazyVals.objCAS"), + ProblemFilters.exclude[DirectMissingMethodProblem]("scala.runtime.LazyVals.objCAS"), + ProblemFilters.exclude[DirectMissingMethodProblem]("scala.runtime.LazyVals#Names.evaluating"), + ProblemFilters.exclude[DirectMissingMethodProblem]("scala.runtime.LazyVals#Names.getStaticOffset"), + ProblemFilters.exclude[DirectMissingMethodProblem]("scala.runtime.LazyVals#Names.nullValued"), + ProblemFilters.exclude[DirectMissingMethodProblem]("scala.runtime.LazyVals#Names.objCas"), + ProblemFilters.exclude[DirectMissingMethodProblem]("scala.runtime.LazyVals#Names.waiting"), + ProblemFilters.exclude[DirectMissingMethodProblem]("scala.runtime.LazyVals#Names.waitingAwaitRelease"), + ProblemFilters.exclude[DirectMissingMethodProblem]("scala.runtime.LazyVals#Names.waitingRelease"), + ProblemFilters.exclude[MissingClassProblem]("scala.runtime.LazyVals$Evaluating$"), + ProblemFilters.exclude[MissingClassProblem]("scala.runtime.LazyVals$NULL$"), + ProblemFilters.exclude[MissingClassProblem]("scala.runtime.LazyVals$Waiting"), + ProblemFilters.exclude[MissingFieldProblem]("scala.runtime.LazyVals.Evaluating"), + ProblemFilters.exclude[MissingFieldProblem]("scala.runtime.LazyVals.NULL") ) } diff --git a/tests/init/neg/promotion-loop.check b/tests/init/neg/promotion-loop.check index b53dd676081f..24f63e7dcedd 100644 --- a/tests/init/neg/promotion-loop.check +++ b/tests/init/neg/promotion-loop.check @@ -1,7 +1,11 @@ -- Error: tests/init/neg/promotion-loop.scala:16:10 -------------------------------------------------------------------- 16 | println(b) // error | ^ +<<<<<<< HEAD + | Cannot prove that the value is fully-initialized. Only initialized values may be used as arguments. +======= | Cannot prove that the value is fully initialized. Only initialized values may be used as arguments. +>>>>>>> 91c03b1f7835d574290eba1ecf236e0f59fc2bf0 | | The unsafe promotion may cause the following problem: | Cannot prove that the value is fully initialized. Only initialized values may be used as arguments. diff --git a/tests/init/neg/t3273.check b/tests/init/neg/t3273.check index 8c7f0f32e2d8..04704b1d92ba 100644 --- a/tests/init/neg/t3273.check +++ b/tests/init/neg/t3273.check @@ -1,7 +1,7 @@ -- Error: tests/init/neg/t3273.scala:4:42 ------------------------------------------------------------------------------ 4 | val num1: LazyList[Int] = 1 #:: num1.map(_ + 1) // error | ^^^^^^^^^^^^^^^ - | Cannot prove that the value is fully initialized. Only initialized values may be used as arguments. + | Cannot prove that the value is fully-initialized. Only initialized values may be used as arguments. | | The unsafe promotion may cause the following problem: | Access non-initialized value num1. Calling trace: @@ -10,7 +10,7 @@ -- Error: tests/init/neg/t3273.scala:5:61 ------------------------------------------------------------------------------ 5 | val num2: LazyList[Int] = 1 #:: num2.iterator.map(_ + 1).to(LazyList) // error | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | Cannot prove that the value is fully initialized. Only initialized values may be used as arguments. + | Cannot prove that the value is fully-initialized. Only initialized values may be used as arguments. | | The unsafe promotion may cause the following problem: | Access non-initialized value num2. Calling trace: diff --git a/tests/run/lazyVals_c3.0.0.check b/tests/run/lazyVals_c3.0.0.check new file mode 100644 index 000000000000..a0aad90603ed --- /dev/null +++ b/tests/run/lazyVals_c3.0.0.check @@ -0,0 +1,4 @@ +computing x +x +computing y +y \ No newline at end of file diff --git a/tests/run/lazyVals_c3.0.0.scala b/tests/run/lazyVals_c3.0.0.scala new file mode 100644 index 000000000000..5df4069bc94c --- /dev/null +++ b/tests/run/lazyVals_c3.0.0.scala @@ -0,0 +1,13 @@ +// Compiled with 3.0.0 and run with current compiler +class Foo: + lazy val x = + println("computing x") + "x" + lazy val y = + println("computing y") + "y" + +@main def Test = + val foo = new Foo + println(foo.x) + println(foo.y) diff --git a/tests/run/lazyVals_c3.1.0.check b/tests/run/lazyVals_c3.1.0.check new file mode 100644 index 000000000000..a0aad90603ed --- /dev/null +++ b/tests/run/lazyVals_c3.1.0.check @@ -0,0 +1,4 @@ +computing x +x +computing y +y \ No newline at end of file diff --git a/tests/run/lazyVals_c3.1.0.scala b/tests/run/lazyVals_c3.1.0.scala new file mode 100644 index 000000000000..45e796ab46d3 --- /dev/null +++ b/tests/run/lazyVals_c3.1.0.scala @@ -0,0 +1,13 @@ +// Compiled with 3.1.0 and run with current compiler +class Foo: + lazy val x = + println("computing x") + "x" + lazy val y = + println("computing y") + "y" + +@main def Test = + val foo = new Foo + println(foo.x) + println(foo.y) \ No newline at end of file