From ef6aa5258f177ba3d9ca7a6449a9aa86d8845f81 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20Doeraene?= Date: Wed, 25 Nov 2020 16:43:30 +0100 Subject: [PATCH] Fix #9424: Add `releaseFence()` call when mixing in a trait `val`. This is a forward port of relevant parts of the upstream PR https://github.com/scala/scala/pull/7028 --- .../src/dotty/tools/dotc/core/StdNames.scala | 1 + .../dotty/tools/dotc/transform/Memoize.scala | 31 ++++++++++++++++++- 2 files changed, 31 insertions(+), 1 deletion(-) diff --git a/compiler/src/dotty/tools/dotc/core/StdNames.scala b/compiler/src/dotty/tools/dotc/core/StdNames.scala index 0bb2b9bc898b..dddf9c0e41eb 100644 --- a/compiler/src/dotty/tools/dotc/core/StdNames.scala +++ b/compiler/src/dotty/tools/dotc/core/StdNames.scala @@ -561,6 +561,7 @@ object StdNames { val reflect: N = "reflect" val reflectiveSelectable: N = "reflectiveSelectable" val reify : N = "reify" + val releaseFence : N = "releaseFence" val rootMirror : N = "rootMirror" val run: N = "run" val runOrElse: N = "runOrElse" diff --git a/compiler/src/dotty/tools/dotc/transform/Memoize.scala b/compiler/src/dotty/tools/dotc/transform/Memoize.scala index f46c3d14fbf9..b833d0cf1f6c 100644 --- a/compiler/src/dotty/tools/dotc/transform/Memoize.scala +++ b/compiler/src/dotty/tools/dotc/transform/Memoize.scala @@ -16,9 +16,16 @@ import NameKinds.TraitSetterName import NameOps._ import Flags._ import Decorators._ +import StdNames.nme + +import util.Store object Memoize { val name: String = "memoize" + + private final class MyState { + val classesThatNeedReleaseFence = new util.HashSet[Symbol] + } } /** Provides the implementations of all getters and setters, introducing @@ -37,10 +44,17 @@ object Memoize { * --> def x_=(y: T): Unit = x = y */ class Memoize extends MiniPhase with IdentityDenotTransformer { thisPhase => + import Memoize.MyState import ast.tpd._ override def phaseName: String = Memoize.name + private var MyState: Store.Location[MyState] = _ + private def myState(using Context): MyState = ctx.store(MyState) + + override def initContext(ctx: FreshContext): Unit = + MyState = ctx.addLocation[MyState]() + /* Makes sure that, after getters and constructors gen, there doesn't * exist non-deferred definitions that are not implemented. */ override def checkPostCondition(tree: Tree)(using Context): Unit = { @@ -69,6 +83,17 @@ class Memoize extends MiniPhase with IdentityDenotTransformer { thisPhase => */ override def runsAfter: Set[String] = Set(Mixin.name) + override def prepareForUnit(tree: Tree)(using Context): Context = + ctx.fresh.updateStore(MyState, new MyState()) + + override def transformTemplate(tree: Template)(using Context): Tree = + val cls = ctx.owner.asClass + if myState.classesThatNeedReleaseFence.contains(cls) then + val releaseFenceCall = ref(defn.staticsMethodRef(nme.releaseFence)).appliedToNone + cpy.Template(tree)(tree.constr, tree.parents, Nil, tree.self, tree.body :+ releaseFenceCall) + else + tree + override def transformDefDef(tree: DefDef)(using Context): Tree = { val sym = tree.symbol @@ -154,7 +179,11 @@ class Memoize extends MiniPhase with IdentityDenotTransformer { thisPhase => // See tests/run/traitValOverriddenByParamAccessor.scala tree else - field.setFlag(Mutable) // Necessary for vals mixed in from traits + if !field.is(Mutable) then + // This is a val mixed in from a trait. + // We make it mutable, and mark the class as needing a releaseFence() in the constructor + field.setFlag(Mutable) + myState.classesThatNeedReleaseFence += sym.owner val initializer = if (isErasableBottomField(field, tree.vparamss.head.head.tpt.tpe.classSymbol)) Literal(Constant(())) else Assign(ref(field), adaptToField(field, ref(tree.vparamss.head.head.symbol)))