diff --git a/compiler/src/dotty/tools/dotc/transform/init/Checker.scala b/compiler/src/dotty/tools/dotc/transform/init/Checker.scala index d7f0d074cb9d..7172db190ce0 100644 --- a/compiler/src/dotty/tools/dotc/transform/init/Checker.scala +++ b/compiler/src/dotty/tools/dotc/transform/init/Checker.scala @@ -47,7 +47,7 @@ class Checker extends MiniPhase { // A concrete class may not be instantiated if the self type is not satisfied if (instantiable) { implicit val state: Checking.State = Checking.State( - visited = mutable.Set.empty, + visited = Set.empty, path = Vector.empty, thisClass = cls, fieldsInited = mutable.Set.empty, diff --git a/compiler/src/dotty/tools/dotc/transform/init/Checking.scala b/compiler/src/dotty/tools/dotc/transform/init/Checking.scala index 09742af9d0ae..90741d86124f 100644 --- a/compiler/src/dotty/tools/dotc/transform/init/Checking.scala +++ b/compiler/src/dotty/tools/dotc/transform/init/Checking.scala @@ -30,19 +30,30 @@ object Checking { * */ case class State( - visited: mutable.Set[Effect], // effects that have been checked + private var visited: Set[Effect], // effects that have been checked path: Vector[Tree], // the path that leads to the current effect thisClass: ClassSymbol, // the concrete class of `this` fieldsInited: mutable.Set[Symbol], parentsInited: mutable.Set[ClassSymbol], env: Env ) { + def withVisited(eff: Effect): State = { - visited += eff + visited = visited + eff copy(path = this.path :+ eff.source) } + def hasVisited(eff: Effect): Boolean = + visited.contains(eff) + def withOwner(sym: Symbol): State = copy(env = env.withOwner(sym)) + + def test(op: State ?=> Errors): Errors = { + val saved = visited + val errors = op(using this) + visited = saved + errors + } } private implicit def theEnv(implicit state: State): Env = state.env @@ -148,7 +159,7 @@ object Checking { } private def check(eff: Effect)(implicit state: State): Errors = - if (state.visited.contains(eff)) Errors.empty + if (state.hasVisited(eff)) Errors.empty else trace("checking effect " + eff.show, init, errs => Errors.show(errs.asInstanceOf[Errors])) { implicit val state2: State = state.withVisited(eff) @@ -162,11 +173,13 @@ object Checking { PromoteCold(eff.source, state2.path).toErrors case pot @ Warm(cls, outer) => - PromoteWarm(pot, eff.source, state2.path).toErrors + val errors = state.test { check(Promote(outer)(eff.source)) } + if (errors.isEmpty) Errors.empty + else PromoteWarm(pot, eff.source, state2.path).toErrors case Fun(pots, effs) => - val errs1 = effs.flatMap { check(_) } - val errs2 = pots.flatMap { pot => check(Promote(pot)(eff.source))(state.copy(path = Vector.empty)) } + val errs1 = state.test { effs.flatMap { check(_) } } + val errs2 = state.test { pots.flatMap { pot => check(Promote(pot)(eff.source))(state.copy(path = Vector.empty)) } } if (errs1.nonEmpty || errs2.nonEmpty) UnsafePromotion(pot, eff.source, state2.path, errs1 ++ errs2).toErrors else diff --git a/tests/init/neg/function1.scala b/tests/init/neg/function1.scala index e01864ae1f47..15427f3de750 100644 --- a/tests/init/neg/function1.scala +++ b/tests/init/neg/function1.scala @@ -4,7 +4,7 @@ class Foo { val fun2: Int => Int = n => 1 + n + list.size fun2(5) - List(5, 9).map(n => 2 + n + list.size) + List(5, 9).map(n => 2 + n + list.size) // error final val list = List(1, 2, 3) // error diff --git a/tests/init/pos/i9664.scala b/tests/init/pos/i9664.scala new file mode 100644 index 000000000000..34bcd84e6ee9 --- /dev/null +++ b/tests/init/pos/i9664.scala @@ -0,0 +1,14 @@ +object Wrap1 { + class E + object E { + final val A = E() + val $values = Array(A) + } +} +object Wrap2 { + class E + object E { + final val A = E() + val ref = A + } +}