-
Notifications
You must be signed in to change notification settings - Fork 1.1k
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
Constructor not generated on 3.3.1 #19569
Comments
reproduce scriptA simple reproduce scala-cli script like this: //> using scala 2.13.8
//> using dep "org.apache.pekko::pekko-actor:1.0.2"
import org.apache.pekko.actor.ActorSystem
val system = ActorSystem("Main")
println("test")
val ref = new AnyRef {
import org.apache.pekko.actor.{ Actor, Props, Terminated }
class WatchActor extends Actor {
val child = context.actorOf(Props.empty, "child")
context.watch(child)
var lastSender = context.system.deadLetters
def receive = {
case "kill" =>
context.stop(child)
lastSender = sender()
case Terminated(`child`) =>
lastSender ! "finished"
}
}
val victim = system.actorOf(Props(classOf[WatchActor], this))
print(victim.path.toSerializationFormat)
}
Thread.sleep(1000) Scala 3.3.1 result
Scala 2.13.8 result
|
It probably has a constructor that takes no argument, because it is effectively a local class, and it does not need the reference to the enclosing |
No, we need a reproducer with no dependencies, so we can have it as a test case. |
The bug is: The actor
public Constructor<T> getDeclaredConstructor(Class<?>... parameterTypes)
throws NoSuchMethodException, SecurityException I'm on windows and the bug of scala-cli prevents me to test it :(
|
@dwijnand I think you know Akka/Pekko better than me, would you like to help out, thanks. |
object helper {
def test(cls: Class[?]) = println(cls.getDeclaredConstructors.toList)
}
import helper.test
object T1 { class C1; test(classOf[C1]) }
object T2 { new AnyRef { class C2; test(classOf[C2]) } }
object T3 { def t3(): Unit = { class C3; test(classOf[C3]) } }
object T4 { def t4(): Unit = new AnyRef { class C4; test(classOf[C4]) } }
class T5 { class C5; test(classOf[C5]) }
class T6 { new AnyRef { class C6; test(classOf[C6]) } }
class T7 { def t7(): Unit = { class C7; test(classOf[C7]) } }
class T8 { def t8(): Unit = new AnyRef { class C8; test(classOf[C8]) } }
object Test {
def main(args: Array[String]): Unit = {
T1.toString
T2.toString
T3.t3()
T4.t4()
new T5().toString
new T6().toString
new T7().t7()
new T8().t8()
}
}
The So it looks like Scala 3 loses the enclosing class as soon as the class is within an anonymous class or a method (because both are unnameable?), provided we're within a class. When we're in an object, we keep the enclosing anonymous class, when we have one, however we also keep the enclosing module class, when we're in an object method, which I think is also wrong. |
So is this really a bug? Must the constructor of the inner class always take an outer class instance even if it doesn't use it? |
Let class T:
def t() =
new AnyRef:
val x = 0
class C:
def g = x Then class T$$anon$1$C extends Object {
def <init>($outer: Object {...}): Unit =
{
if $outer.eq(null) then throw new NullPointerException() else ()
T$$anon$1$C.this.$outer = $outer
super()
()
}
def g(): Int = this.$outer.x()
private val $outer: Object {...}
final def T$_$$anon$C$$$outer(): Object {...} = T$$anon$1$C.this.$outer
} Now let class T:
def t() =
new AnyRef:
class C:
val x = 0
def g = x Then class T$$anon$1$C extends Object {
def <init>(): Unit =
{
super()
this.x = 0
()
}
private val x: Int
def x(): Int = this.x
def g(): Int = this.x()
} |
That's very much of a feature in Scala 3. Unnecessary outer pointers are a source of memory leaks. Scala 3 does a much better job at eliminating outer pointers that don't affect semantics (ignoring reflection). And we don't want to go back. |
I agree. There's no point in generating useless outer pointers, even as constructor parameters, for local classes. Even the JavaDoc reflection spec does not apply here, because this is a local class, not an inner class. |
We can fix the T3 example above. And we can describe how Akka/Pekko can handle these classes in their reflection utilities. |
I don't think that's right, because a local class is an inner class, from jls-8.1.3:
|
Would anyone have insights on what In particular, // static?
class T3 {
// non-static
def t3(): Unit = {
// non-static
class C3
}
}
// static
object T7 {
// static
def t7(): Unit = {
// non-static
class C7
}
} Shouldn't |
Tentative: diff --git a/compiler/src/dotty/tools/dotc/transform/ExplicitOuter.scala b/compiler/src/dotty/tools/dotc/transform/ExplicitOuter.scala
index f57595293a..5fd506c91d 100644
--- a/compiler/src/dotty/tools/dotc/transform/ExplicitOuter.scala
+++ b/compiler/src/dotty/tools/dotc/transform/ExplicitOuter.scala
@@ -227,7 +227,7 @@ object ExplicitOuter {
private def hasLocalInstantiation(cls: ClassSymbol)(using Context): Boolean =
// Modules are normally locally instantiated, except if they are declared in a trait,
// in which case they will be instantiated in the classes that mix in the trait.
- cls.owner.ownersIterator.takeWhile(!_.isStatic).exists(_.isTerm)
+ cls.owner.ownersIterator.exists(o => o.is(Method) || o.isAnonymousClass)
|| cls.is(Private, butNot = Module)
|| cls.is(Module) && !cls.owner.is(Trait) |
I think it should be cls.owner.ownersIterator.takeWhile(!_.isStaticOwner).exists(_.isTerm) |
Ah cool, seems to work! It allows removing outer pointers for all Dale's examples (#19569 (comment)) except T5. |
Indeed, isTerm works for the anonymous class too because |
By the way, we were wondering, are there symbols for which |
#19803 opened. |
Going back to the Akka/Pekko use-case: "xxxxx test" in {
new AnyRef {
class WatchActor extends Actor {
...
}
val ref = system.actorOf(Props(classOf[WatchActor], this))
}
} The second argument to Props defines the constructor arguments. In order to make this code cross-compatible between Scala 2 and Scala 3, |
Compiler version
3.3.1
In pekko /Akka project
Minimized code
Sbt docs
+3.3.1
TestOnly ※ActorDocSpec※
I edited this on phone , sorry
Output
apache/pekko#1082
Expectation
Works as scala 2.12 and 2.13
The WatchActor is in a none static scope, so the first argument of it's constructor should be the type of the outter scope, but there is no matched constructors, seems just ignored the outter scope.
See Class.getDeclaredConstructors for more details.
The text was updated successfully, but these errors were encountered: