diff --git a/compiler/src/dotty/tools/dotc/transform/init/Objects.scala b/compiler/src/dotty/tools/dotc/transform/init/Objects.scala index 474ec4de7962..b8a213718a19 100644 --- a/compiler/src/dotty/tools/dotc/transform/init/Objects.scala +++ b/compiler/src/dotty/tools/dotc/transform/init/Objects.scala @@ -144,6 +144,8 @@ class Objects(using Context @constructorOnly): def isObjectRef: Boolean = this.isInstanceOf[ObjectRef] + def asObjectRef: ObjectRef = this.asInstanceOf[ObjectRef] + def valValue(sym: Symbol)(using Heap.MutableData): Value = Heap.readVal(this, sym) def varValue(sym: Symbol)(using Heap.MutableData): Value = Heap.readVal(this, sym) @@ -178,6 +180,12 @@ class Objects(using Context @constructorOnly): /** A reference to a static object */ case class ObjectRef private (klass: ClassSymbol)(using Trace) extends Ref: + var afterSuperCall = false + + def isAfterSuperCall = afterSuperCall + + def setAfterSuperCall(): Unit = afterSuperCall = true + def owner = klass def show(using Context) = "ObjectRef(" + klass.show + ")" @@ -1447,9 +1455,15 @@ class Objects(using Context @constructorOnly): /** Check an individual object */ private def accessObject(classSym: ClassSymbol)(using Context, State.Data, Trace, Heap.MutableData, EnvMap.EnvMapMutableData): ObjectRef = log("accessing " + classSym.show, printer, (_: Value).show) { if classSym.hasSource then - State.checkObjectAccess(classSym) + val obj = State.checkObjectAccess(classSym) + if !obj.isAfterSuperCall then + report.warning("Accessing " + obj.klass + " before the super constructor of the object finishes! " + Trace.show, Trace.position) + end if + obj else - ObjectRef(classSym) + val obj = ObjectRef(classSym) + obj.setAfterSuperCall() + obj } @@ -2112,6 +2126,10 @@ class Objects(using Context @constructorOnly): tasks.foreach(task => task()) end if + if thisV.isInstanceOf[ObjectRef] && klass == thisV.klass then + thisV.asObjectRef.setAfterSuperCall() + end if + // class body tpl.body.foreach { case vdef : ValDef if !vdef.symbol.is(Flags.Lazy) && !vdef.rhs.isEmpty => diff --git a/tests/init-global/pos/multiple-by-name.scala b/tests/init-global/pos/multiple-by-name.scala index 5697c7c1bc52..1cc0df80e670 100644 --- a/tests/init-global/pos/multiple-by-name.scala +++ b/tests/init-global/pos/multiple-by-name.scala @@ -18,10 +18,3 @@ object O { val c = foo2(new Y) val d = foo3(new Y) } - -/** - * Pass arg to by-name parameter: create a Fun where body is the argument expression - * Read value of by-name parameter: call 'apply' on every possible Fun value of the by-name parameter - * Solution: Add special EnvRefs for by-name params; - * differentiate these EnvRefs by the arg tree passed to the by-name param - */ \ No newline at end of file diff --git a/tests/init-global/warn/call-before-super.scala b/tests/init-global/warn/call-before-super.scala new file mode 100644 index 000000000000..89a24099fe5b --- /dev/null +++ b/tests/init-global/warn/call-before-super.scala @@ -0,0 +1,7 @@ +class C(i: Int = 42, j: Int = 27) + +object X extends C(j = X.foo()): // warn + def foo() = 5 + +@main def test = println: + X diff --git a/tests/init-global/warn/call-before-super2.scala b/tests/init-global/warn/call-before-super2.scala new file mode 100644 index 000000000000..1fe6c95aafea --- /dev/null +++ b/tests/init-global/warn/call-before-super2.scala @@ -0,0 +1,10 @@ +class C(j: Int) + +object A: + def foo = X.k // warn + +object X extends C(A.foo): + def k = 5 + +@main def test = println: + X \ No newline at end of file diff --git a/tests/init-global/warn/call-before-super3.scala b/tests/init-global/warn/call-before-super3.scala new file mode 100644 index 000000000000..dab1cb9248ff --- /dev/null +++ b/tests/init-global/warn/call-before-super3.scala @@ -0,0 +1,9 @@ +class C(i: Int = 42, j: Int = 27) { + val f = X.foo() // warn +} + +object X extends C(j = 5): + def foo() = 5 + +@main def test = println: + X \ No newline at end of file diff --git a/tests/init-global/warn/global-cycle10.check b/tests/init-global/warn/global-cycle10.check new file mode 100644 index 000000000000..9d3d7bd2875c --- /dev/null +++ b/tests/init-global/warn/global-cycle10.check @@ -0,0 +1,28 @@ +-- Warning: tests/init-global/warn/global-cycle10.scala:13:14 ---------------------------------------------------------- +13 | def foo() = new Inner // warn + | ^^^^^^^^^ + | Accessing module class O$ before the super constructor of the object finishes! Calling trace: + | ├── object O extends Base { // error [ global-cycle10.scala:7 ] + | │ ^ + | ├── abstract class Base { [ global-cycle10.scala:1 ] + | │ ^ + | ├── foo() [ global-cycle10.scala:4 ] + | │ ^^^^^ + | └── def foo() = new Inner // warn [ global-cycle10.scala:13 ] + | ^^^^^^^^^ +-- Warning: tests/init-global/warn/global-cycle10.scala:10:12 ---------------------------------------------------------- +10 | println(msg) // warn + | ^^^ + | Accessing module class O$ before the super constructor of the object finishes! Calling trace: + | ├── object O extends Base { // error [ global-cycle10.scala:7 ] + | │ ^ + | ├── abstract class Base { [ global-cycle10.scala:1 ] + | │ ^ + | ├── foo() [ global-cycle10.scala:4 ] + | │ ^^^^^ + | ├── def foo() = new Inner // warn [ global-cycle10.scala:13 ] + | │ ^^^^^^^^^ + | ├── class Inner { [ global-cycle10.scala:9 ] + | │ ^ + | └── println(msg) // warn [ global-cycle10.scala:10 ] + | ^^^ diff --git a/tests/init-global/pos/global-cycle10.scala b/tests/init-global/warn/global-cycle10.scala similarity index 74% rename from tests/init-global/pos/global-cycle10.scala rename to tests/init-global/warn/global-cycle10.scala index 9d6200cd884d..0d4eaf97d0c2 100644 --- a/tests/init-global/pos/global-cycle10.scala +++ b/tests/init-global/warn/global-cycle10.scala @@ -7,10 +7,10 @@ abstract class Base { object O extends Base { // error class Inner { - println(msg) + println(msg) // warn } - def foo() = new Inner + def foo() = new Inner // warn } @main diff --git a/tests/init-global/warn/i9176.check b/tests/init-global/warn/i9176.check index 05a208d1f8a1..72aafebacf64 100644 --- a/tests/init-global/warn/i9176.check +++ b/tests/init-global/warn/i9176.check @@ -4,5 +4,11 @@ | Cyclic initialization: object A -> object B -> object A. Calling trace: | ├── case object A extends Foo(B) // warn [ i9176.scala:2 ] | │ ^ - | └── case object B extends Foo(A) [ i9176.scala:3 ] + | └── case object B extends Foo(A) // warn [ i9176.scala:3 ] | ^ +-- Warning: tests/init-global/warn/i9176.scala:3:26 -------------------------------------------------------------------- +3 |case object B extends Foo(A) // warn + | ^ + | Accessing module class A$ before the super constructor of the object finishes! Calling trace: + | └── case object B extends Foo(A) // warn [ i9176.scala:3 ] + | ^ diff --git a/tests/init-global/warn/i9176.scala b/tests/init-global/warn/i9176.scala index 86b65f6d8c0f..106fc5c4bcf3 100644 --- a/tests/init-global/warn/i9176.scala +++ b/tests/init-global/warn/i9176.scala @@ -1,6 +1,6 @@ class Foo(val opposite: Foo) case object A extends Foo(B) // warn -case object B extends Foo(A) +case object B extends Foo(A) // warn object Test { def main(args: Array[String]): Unit = { println(A.opposite) diff --git a/tests/init-global/warn/resolve-parent-this.check b/tests/init-global/warn/resolve-parent-this.check index 58299cca167e..abb0cf715f9d 100644 --- a/tests/init-global/warn/resolve-parent-this.check +++ b/tests/init-global/warn/resolve-parent-this.check @@ -1,3 +1,13 @@ +-- Warning: tests/init-global/warn/resolve-parent-this.scala:3:18 ------------------------------------------------------ +3 | val f: O.type = O // warn + | ^ + | Accessing module class O$ before the super constructor of the object finishes! Calling trace: + | ├── object O extends Delegate { [ resolve-parent-this.scala:6 ] + | │ ^ + | ├── class Delegate { [ resolve-parent-this.scala:1 ] + | │ ^ + | └── val f: O.type = O // warn [ resolve-parent-this.scala:3 ] + | ^ -- Warning: tests/init-global/warn/resolve-parent-this.scala:7:21 ------------------------------------------------------ 7 | val a: Int = foo().a // warn | ^^^^^^^ diff --git a/tests/init-global/warn/resolve-parent-this.scala b/tests/init-global/warn/resolve-parent-this.scala index 02f7a6266437..84ac48fc296e 100644 --- a/tests/init-global/warn/resolve-parent-this.scala +++ b/tests/init-global/warn/resolve-parent-this.scala @@ -1,6 +1,6 @@ class Delegate { def foo() = f - val f: O.type = O + val f: O.type = O // warn } object O extends Delegate { diff --git a/tests/init-global/warn/t9261.check b/tests/init-global/warn/t9261.check index 1e8b05d1ffa2..43fe1db015cb 100644 --- a/tests/init-global/warn/t9261.check +++ b/tests/init-global/warn/t9261.check @@ -4,5 +4,11 @@ | Cyclic initialization: object Buy -> object Sell -> object Buy. Calling trace: | ├── case object Buy extends OrderType(Sell) // warn [ t9261.scala:2 ] | │ ^^^^ - | └── case object Sell extends OrderType(Buy) [ t9261.scala:3 ] + | └── case object Sell extends OrderType(Buy) // warn [ t9261.scala:3 ] | ^^^ +-- Warning: tests/init-global/warn/t9261.scala:3:35 -------------------------------------------------------------------- +3 |case object Sell extends OrderType(Buy) // warn + | ^^^ + | Accessing module class Buy$ before the super constructor of the object finishes! Calling trace: + | └── case object Sell extends OrderType(Buy) // warn [ t9261.scala:3 ] + | ^^^ diff --git a/tests/init-global/warn/t9261.scala b/tests/init-global/warn/t9261.scala index fcb2f3c62901..c8514891ff6c 100644 --- a/tests/init-global/warn/t9261.scala +++ b/tests/init-global/warn/t9261.scala @@ -1,3 +1,3 @@ sealed abstract class OrderType(val reverse: OrderType) case object Buy extends OrderType(Sell) // warn -case object Sell extends OrderType(Buy) +case object Sell extends OrderType(Buy) // warn