(a) A late-closure-class is specialized when -no-specialization is false, and not specialized otherwise.
Exactly like traditional anon-closure-classes.
Compare for the anon-function in:
class C {
def m() {
(1 to 10) foreach { i => println(i) }
}
}
Bytecode for the anon-closure-class above:
- traditional https://gist.github.com/magarciaEPFL/5622649
- late-closure https://gist.github.com/magarciaEPFL/5622675
The example also shows where the "closure body" now lives: not in the anon-closure-class but in the original enclosing class. Why would one do that? How about this: laying the groundwork for lambdas-as-MethodHandles. In case a MethodHandle replaces an anon-closure-class, well, then there's no way anymore for a closure-body to remain in a non-existent class.
--
You received this message because you are subscribed to the Google Groups "scala-internals" group.
To unsubscribe from this group and stop receiving emails from it, send an email to scala-interna...@googlegroups.com.
For more options, visit https://groups.google.com/groups/opt_out.
Yes, my previous post covered only the "what" and not the "how" of Late-Closure-Classes.
All things code-gen of late-closures is part of the new bytecode emitter, specifically `genLateClosure()`
https://github.com/magarciaEPFL/scala/blob/GenRefactored56/src/compiler/scala/tools/nsc/backend/jvm/BCodeLateClosuBuilder.scala#L106
Initially there's only a callsite to the method holding the closure-body, the signature of that method reflects non-erased type information (unless, well, the anon-function's args were generic to start with and not specialized). Based on that signature, the "right" (preferably specialized) subclass of FunctionX (respectively AbstractFunctionX) can be built, that's the responsibility of `takingIntoAccountSpecialization()`
https://github.com/magarciaEPFL/scala/blob/GenRefactored56/src/compiler/scala/tools/nsc/backend/jvm/BCodeLateClosuBuilder.scala#L273
The advantage of representing the lambda as a (wrapped) Apply node is
that later compiler phases (e.g. Explicitouter) need not be modified.
The risk is that something transforms away the special structure. When
I reviewed an earlier version of this work, I noted that erasure might
eliminate a redundant cast, which would destroy the lambda. I think
that's what the 'lccDisguiser' apply prevents.
In any case, it probably isn't a massive job to introduce a purpose
built tree node to encode this if we decide that the hack is too
fragile.
--
You received this message because you are subscribed to the Google Groups "scala-internals" group.
To unsubscribe from this group and stop receiving emails from it, send an email to scala-interna...@googlegroups.com.
For more options, visit https://groups.google.com/groups/opt_out.
--
You received this message because you are subscribed to the Google Groups "scala-internals" group.
To unsubscribe from this group and stop receiving emails from it, send an email to scala-interna...@googlegroups.com.
For more options, visit https://groups.google.com/groups/opt_out.
I am not sure about comprehensibility of lccDisguiserMethod either, even though it's true that it does make good use of the existing LambdaLift logic. But I believe Functions are not what we want either. Abstractly, what you are creating is not a Function node, but a Closure node, which would look something like that:case class Closure(env: List[Tree], methodRef: Select) extends TreeWhere the `env` is a list of closed over values and the `methodRef` node would be a Select with the receiver and method symbol. One potentially risky aspect here is that it would require an adaptation of the environment lifting logic to this new kind of node. So I understand the attractiveness of using a closure class like this one instead:case class Closure(apply: Apply) extends Treewhere the Apply would receive dummy arguments for missing arguments, as in Miguel's version. That second version is very close to what Miguel has; essentially we exchange the lccDisuiserMethod call followed by the cast with the Closure node. I believe it would not be very risky.For me, either of the two Closure representations would be fine.
The difference wrt to Late-Closure-Classes boils down to:
> How about we keep after uncurry very simple Function node that just forwards to local def?
> Since the body of a Function will be very simple, the risk that it won't be transformed by later phases is rather low.The above assumes:
(a) the proposed Function nodes won't demand more processing than a plain method call
(that Function node plays the same role as lccDisguiserMethod in the new backend)
(b) regarding risk of being transformed when it shouldn't,
perhaps we should start by settling the issue whether an Apply with lccDisguiserMethod symbol can be transformed "when it shouldn't"
(ie transformed in a way that prevents the correct Late-Closure-Class from being emitted).
I say that risk is zero. Counter-example, anyone?
Whether a Function node (handled in a special way) would also arrive in pristine state to the bytecode emitter, is open to debate.
For starters, because we don't have *evidence* in the form of a working implementation about that.
In fact, "one can almost see" that at any point in the pipeline after uncurry, a bidirectional conversion is possible, as many times as desired between:
and
case class Closure(env: List[Tree], methodRef: Select) extends Tree
lccDisguiserMethod( delegateMethod ( <apply-args> <captured-values ) )
where the latter is precisely the AST shape used in the new backend. If each can be exchanged for the other without information loss, then other reasons should inform which one to pick. We're back to the topic that an alternative should at least match the capabilities of the new backend / optimizer.
On 23 May 2013 13:37, martin odersky <martin....@epfl.ch> wrote:
Hi Martin,I am not sure about comprehensibility of lccDisguiserMethod either, even though it's true that it does make good use of the existing LambdaLift logic. But I believe Functions are not what we want either. Abstractly, what you are creating is not a Function node, but a Closure node, which would look something like that:case class Closure(env: List[Tree], methodRef: Select) extends TreeWhere the `env` is a list of closed over values and the `methodRef` node would be a Select with the receiver and method symbol. One potentially risky aspect here is that it would require an adaptation of the environment lifting logic to this new kind of node. So I understand the attractiveness of using a closure class like this one instead:case class Closure(apply: Apply) extends Treewhere the Apply would receive dummy arguments for missing arguments, as in Miguel's version. That second version is very close to what Miguel has; essentially we exchange the lccDisuiserMethod call followed by the cast with the Closure node. I believe it would not be very risky.For me, either of the two Closure representations would be fine.I agree that if we were able to introduce a new tree node thencase class Closure(env: List[Tree], methodRef: Select) extends Treeis the best and most straightforward node we could use in this case. However, I always heard that introducing a new tree node is essentially impossible at this stage of Scala development.
Therefore I'm trying to find the closest approximation we can get using existing nodes. I believe that Apply node with dummy arguments wrapped in lccDisguiserMethod method and followed by cast is modelled more nicely by Function node which has just Apply node with function arguments provided.The advantages are:
- there are no magic symbols and tree shapes
- after lambdalift you get a tree shape representing a closure that is a valid Scala program that user could write
The main disadvantage is:
- You need to make sure that Apply node inside of Function node is properly processed by lambdalift logic so additional arguments to Apply are supplied if needed but this seems to be fairly easy to achieve
--
You received this message because you are subscribed to the Google Groups "scala-internals" group.
To unsubscribe from this group and stop receiving emails from it, send an email to scala-interna...@googlegroups.com.
For more options, visit https://groups.google.com/groups/opt_out.
and in fact it should be better to make things explicit via a new node rather than hiding them with special symbols and implicit assumptions.
That's why an lccDisguiserMethod (just a name) has the same contract as any other method.
> Adding more semantics to Apply should be the last of all last resorts.
More semantics? You've been watching too much TypeRef lately. Go and get some fresh air: https://github.com/magarciaEPFL/scala/compare/master...GenRefactored58
I agree with the following two facts you state, but not with the conclusion the new backend is an impressive "experiment" based on a "code smell".
It's great the discussion covered the main points from our respective perspectives.
I don't have much to add.