Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
22 changes: 20 additions & 2 deletions compiler/src/dotty/tools/dotc/transform/init/Objects.scala
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down Expand Up @@ -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 + ")"
Expand Down Expand Up @@ -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
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not sure about obj.setAfterSuperCall here.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is when we don't have source, so we're not checking this object at all. Presumably in that case it should always be OK to access the object, so setAfterSuperCall makes sense to me.

}


Expand Down Expand Up @@ -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 =>
Expand Down
7 changes: 0 additions & 7 deletions tests/init-global/pos/multiple-by-name.scala
Original file line number Diff line number Diff line change
Expand Up @@ -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
*/
7 changes: 7 additions & 0 deletions tests/init-global/warn/call-before-super.scala
Original file line number Diff line number Diff line change
@@ -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
10 changes: 10 additions & 0 deletions tests/init-global/warn/call-before-super2.scala
Original file line number Diff line number Diff line change
@@ -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
9 changes: 9 additions & 0 deletions tests/init-global/warn/call-before-super3.scala
Original file line number Diff line number Diff line change
@@ -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
28 changes: 28 additions & 0 deletions tests/init-global/warn/global-cycle10.check
Original file line number Diff line number Diff line change
@@ -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 ]
| ^^^
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
8 changes: 7 additions & 1 deletion tests/init-global/warn/i9176.check
Original file line number Diff line number Diff line change
Expand Up @@ -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 ]
| ^
2 changes: 1 addition & 1 deletion tests/init-global/warn/i9176.scala
Original file line number Diff line number Diff line change
@@ -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)
Expand Down
10 changes: 10 additions & 0 deletions tests/init-global/warn/resolve-parent-this.check
Original file line number Diff line number Diff line change
@@ -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
| ^^^^^^^
Expand Down
2 changes: 1 addition & 1 deletion tests/init-global/warn/resolve-parent-this.scala
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
class Delegate {
def foo() = f
val f: O.type = O
val f: O.type = O // warn
}

object O extends Delegate {
Expand Down
8 changes: 7 additions & 1 deletion tests/init-global/warn/t9261.check
Original file line number Diff line number Diff line change
Expand Up @@ -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 ]
| ^^^
2 changes: 1 addition & 1 deletion tests/init-global/warn/t9261.scala
Original file line number Diff line number Diff line change
@@ -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
Loading