Skip to content
Open
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
19 changes: 18 additions & 1 deletion 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 @@ -1058,6 +1066,9 @@ class Objects(using Context @constructorOnly):
else if target.equals(defn.Predef_classOf) then
// Predef.classOf is a stub method in tasty and is replaced in backend
UnknownValue
else if ref.isInstanceOf[ObjectRef] && !ref.asObjectRef.isAfterSuperCall then
report.warning("Calling " + target + " of object " + ref.klass + " before the super constructor of the object finishes! " + Trace.show, Trace.position)
Bottom
Copy link
Contributor

Choose a reason for hiding this comment

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

I was thinking whether the check should be moved to accessObject?

Copy link
Contributor

Choose a reason for hiding this comment

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

I think so too.

else if target.hasSource then
val cls = target.owner.enclosingClass.asClass
val ddef = target.defTree.asInstanceOf[DefDef]
Expand Down Expand Up @@ -1449,7 +1460,9 @@ class Objects(using Context @constructorOnly):
if classSym.hasSource then
State.checkObjectAccess(classSym)
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 +2125,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
10 changes: 10 additions & 0 deletions tests/init-global/warn/global-cycle7.check
Original file line number Diff line number Diff line change
Expand Up @@ -18,3 +18,13 @@
| │ ^
| └── val m: Int = A.n // warn [ global-cycle7.scala:6 ]
| ^^^
-- Warning: /Users/enzexing/IdeaProjects/dotty-staging/dotty/tests/init-global/warn/global-cycle7.scala:12:66
12 | val tokenString, debugString: Array[String] = new Array[String](maxToken + 1)
| ^^^^^^^^
|Calling method maxToken of object module class JavaTokens$ before the super constructor of the object finishes! Calling trace:
|├── object JavaTokens extends TokensCommon { [ global-cycle7.scala:15 ]
|│ ^
|├── abstract class TokensCommon { [ global-cycle7.scala:9 ]
|│ ^
|└── val tokenString, debugString: Array[String] = new Array[String](maxToken + 1) [ global-cycle7.scala:12 ]
| ^^^^^^^^
2 changes: 1 addition & 1 deletion tests/init-global/warn/global-cycle7.scala
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ object B {
abstract class TokensCommon {
def maxToken: Int

val tokenString, debugString: Array[String] = new Array[String](maxToken + 1)
val tokenString, debugString: Array[String] = new Array[String](maxToken + 1) // warn
}

object JavaTokens extends TokensCommon {
Expand Down
Loading