Skip to content

Commit af62be6

Browse files
author
EnzeXing
committed
Warning when calling object methods before super constructor finishes
1 parent 3bea2f3 commit af62be6

File tree

6 files changed

+50
-7
lines changed

6 files changed

+50
-7
lines changed

compiler/src/dotty/tools/dotc/transform/init/Objects.scala

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -144,6 +144,8 @@ class Objects(using Context @constructorOnly):
144144

145145
def isObjectRef: Boolean = this.isInstanceOf[ObjectRef]
146146

147+
def asObjectRef: ObjectRef = this.asInstanceOf[ObjectRef]
148+
147149
def valValue(sym: Symbol)(using Heap.MutableData): Value = Heap.readVal(this, sym)
148150

149151
def varValue(sym: Symbol)(using Heap.MutableData): Value = Heap.readVal(this, sym)
@@ -178,6 +180,12 @@ class Objects(using Context @constructorOnly):
178180

179181
/** A reference to a static object */
180182
case class ObjectRef private (klass: ClassSymbol)(using Trace) extends Ref:
183+
var afterSuperCall = false
184+
185+
def isAfterSuperCall = afterSuperCall
186+
187+
def setAfterSuperCall(): Unit = afterSuperCall = true
188+
181189
def owner = klass
182190

183191
def show(using Context) = "ObjectRef(" + klass.show + ")"
@@ -1058,6 +1066,9 @@ class Objects(using Context @constructorOnly):
10581066
else if target.equals(defn.Predef_classOf) then
10591067
// Predef.classOf is a stub method in tasty and is replaced in backend
10601068
UnknownValue
1069+
else if ref.isInstanceOf[ObjectRef] && !ref.asObjectRef.isAfterSuperCall then
1070+
report.warning("Calling " + target + " of object " + ref.klass + " before the super constructor of the object finishes! " + Trace.show, Trace.position)
1071+
Bottom
10611072
else if target.hasSource then
10621073
val cls = target.owner.enclosingClass.asClass
10631074
val ddef = target.defTree.asInstanceOf[DefDef]
@@ -2112,6 +2123,10 @@ class Objects(using Context @constructorOnly):
21122123
tasks.foreach(task => task())
21132124
end if
21142125

2126+
if thisV.isInstanceOf[ObjectRef] && klass == thisV.klass then
2127+
thisV.asObjectRef.setAfterSuperCall()
2128+
end if
2129+
21152130
// class body
21162131
tpl.body.foreach {
21172132
case vdef : ValDef if !vdef.symbol.is(Flags.Lazy) && !vdef.rhs.isEmpty =>

tests/init-global/pos/multiple-by-name.scala

Lines changed: 0 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -18,10 +18,3 @@ object O {
1818
val c = foo2(new Y)
1919
val d = foo3(new Y)
2020
}
21-
22-
/**
23-
* Pass arg to by-name parameter: create a Fun where body is the argument expression
24-
* Read value of by-name parameter: call 'apply' on every possible Fun value of the by-name parameter
25-
* Solution: Add special EnvRefs for by-name params;
26-
* differentiate these EnvRefs by the arg tree passed to the by-name param
27-
*/
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
class C(i: Int = 42, j: Int = 27)
2+
3+
object X extends C(j = X.foo()): // warn
4+
def foo() = 5
5+
6+
@main def test = println:
7+
X
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
class C(j: Int)
2+
3+
object A:
4+
def foo = X.k // warn
5+
6+
object X extends C(A.foo):
7+
def k = 5
8+
9+
@main def test = println:
10+
X
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
abstract class Foo[T](defaultValue: => T, arg1: Int = 1, arg2: Int = 2):
2+
def getValue: T = defaultValue
3+
4+
enum Baz:
5+
case E1, E2 // warn
6+
7+
object Baz extends Foo[Baz](Baz.E1, arg2 = 2) // warn
8+
9+
@main def test = println(Baz.getValue)
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
class C(i: Int = 42, j: Int = 27) {
2+
val f = X.foo() // warn
3+
}
4+
5+
object X extends C(j = 5):
6+
def foo() = 5
7+
8+
@main def test = println:
9+
X

0 commit comments

Comments
 (0)