-
Notifications
You must be signed in to change notification settings - Fork 1.1k
Global object init checker gives warning when calling object methods before super constructor finishes #24349
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -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 + ")" | ||
|
|
@@ -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 | ||
| else if target.hasSource then | ||
| val cls = target.owner.enclosingClass.asClass | ||
| val ddef = target.defTree.asInstanceOf[DefDef] | ||
|
|
@@ -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 | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I'm not sure about
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe 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 |
||
| } | ||
|
|
||
|
|
||
|
|
@@ -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 => | ||
|
|
||
| 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 |
| 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 |
| 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 |
There was a problem hiding this comment.
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?There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think so too.