package transform
Type Members
- trait AccessorSynthesis extends SubComponent with Transform with TreeDSL
- abstract class CleanUp extends Statics with Transform with TreeDSL
- abstract class Constructors extends Statics with Transform with TypingTransformers with TreeDSL
This phase converts classes with parameters into Java-like classes with fields, which are assigned to from constructors.
- abstract class Delambdafy extends SubComponent with Transform with TypingTransformers with TreeDSL with TypeAdaptingTransformer
This transformer is responsible for preparing Function nodes for runtime, by translating to a tree that will be converted to an invokedynamic by the backend.
This transformer is responsible for preparing Function nodes for runtime, by translating to a tree that will be converted to an invokedynamic by the backend.
The main assumption it makes is that a Function {args => body} has been turned into {args => liftedBody()} where lifted body is a top level method that implements the body of the function. Currently Uncurry is responsible for that transformation.
From this shape of Function, Delambdafy will create:
An application of the captured arguments to a fictional symbol representing the lambda factory. This will be translated by the backed into an invokedynamic using a bootstrap method in JDK8's
LambdaMetaFactory
. The captured arguments includethis
ifliftedBody
is unable to be made STATIC. - abstract class Erasure extends SubComponent with InfoTransform with reflect.internal.transform.Erasure with Analyzer with TypingTransformers with TreeDSL with TypeAdaptingTransformer
- abstract class ExplicitOuter extends SubComponent with InfoTransform with TypingTransformers with TreeDSL
This class ...
- abstract class ExtensionMethods extends SubComponent with Transform with TypingTransformers
Perform Step 1 in the inline classes SIP: Creates extension methods for all methods in a value class, except parameter or super accessors, or constructors.
- abstract class Fields extends SubComponent with InfoTransform with TreeDSL with TypingTransformers with AccessorSynthesis
Synthesize accessors, fields (and bitmaps) for (lazy) vals and modules.
Synthesize accessors, fields (and bitmaps) for (lazy) vals and modules.
During Namers, a
ValDef
that islazy
, deferred and/or defined in a trait carries its getter's symbol. The underlying field symbol does not exist until this phase.For
val
s defined in classes, we still emit a field immediately. TODO: uniformly assign getter symbol to allValDef
s, stop usingaccessed
.This phase synthesizes accessors, fields and bitmaps (for lazy or init-checked vals under -Xcheckinit) in the first (closest in the subclassing lattice) subclass (not a trait) of a trait.
For lazy vals and modules, we emit accessors that using double-checked locking (DCL) to balance thread safety and performance. For both lazy vals and modules, the a compute method contains the DCL's slow path.
Local lazy vals do not receive bitmaps, but use a Lazy*Holder that has the volatile init bit and the computed value. See
mkLazyLocalDef
.Constructors will move the rhs to an assignment in the template body. Those statements then move to the template into the constructor, which means it will initialize the fields defined in this template (and execute the corresponding side effects). We need to maintain the connection between getter and rhs until after specialization so that it can duplicate vals.
A ModuleDef is desugared to a ClassDef, an accessor (which reuses the module's term symbol) and a module var (unless the module is static and does not implement a member of a supertype, or we're in a trait).
For subclasses of traits that define modules, a module var is mixed in, as well as the required module accessors.
Phase ordering:
- Runs after uncurry to deal with classes that implement SAM traits with ValDefs.
- Runs before erasure (to get bridges), and thus before lambdalift/flatten, so that nested functions/definitions must be considered.
- Lambdalift introduces new paramaccessors for captured vals, but runs too late in the pipeline, so mixins still synthesizes implementations for these accessors when a local trait that captures is subclassed.
In the future, would like to get closer to dotty, which lifts a val's RHS (a similar thing is done for template-level statements) to a method
$_initialize_$1$x
instead of a block, which is used in the constructor to initialize the val. This makes for a nice unification of strict and lazy vals, in that the RHS is lifted to a method for both, with the corresponding compute method called at the appropriate time.)This only reduces the required number of methods per field declaration in traits, if we encode the name (and place in initialisation order) of the field in the name of its initializing method, to allow separate compilation. (The name mangling must include ordering, and thus complicate incremental compilation: ideally, we'd avoid renumbering unchanged methods, but that would result in different bytecode between clean recompiles and incremental ones).
In the even longer term (Scala 3?), I agree with @DarkDimius that it would make sense to hide the difference between strict and lazy vals. All vals are lazy, but the memoization overhead is removed when we statically know they are forced during initialization. We could still expose the low-level field semantics through
private[this] val
s.In any case, the current behavior of overriding vals is pretty surprising. An overridden val's side-effect is still performed. The only change due to overriding is that its value is never written to the field (the overridden val's value is, of course, stored in the field in addition to its side-effect being performed).
TODO: Java 9 support for vals defined in traits. They are currently emitted as final, but the write (putfield) to the val does not occur syntactically within the <init> method (it's done by the trait setter, which is called from the trait's mixin constructor, which is called from the subclass's constructor...)
- abstract class Flatten extends SubComponent with InfoTransform
- trait InfoTransform extends SubComponent with Transform
An InfoTransform contains a compiler phase that transforms trees and symbol infos -- making sure they stay consistent.
An InfoTransform contains a compiler phase that transforms trees and symbol infos -- making sure they stay consistent. The symbol info is transformed assuming it is consistent right before this phase. The info transformation is triggered by Symbol::rawInfo, which caches the results in the symbol's type history. This way sym.info (during an enteringPhase(p)) can look up what the symbol's info should look like at the beginning of phase p. (If the transformed info had not been stored yet, rawInfo will compute the info by composing the info-transformers of the most recent phase before p, up to the transformer of the phase right before p.)
Concretely, enteringPhase(p) { sym.info } yields the info *before* phase p has transformed it. Imagine you're a phase and it all makes sense.
- abstract class LambdaLift extends SubComponent with InfoTransform
- abstract class Mixin extends SubComponent with Transform with TreeDSL with AccessorSynthesis
- abstract class OverridingPairs extends SymbolPairs
A class that yields a kind of iterator (
Cursor
), which yields pairs of corresponding symbols visible in some base class, unless there's a parent class that already contains the same pairs.A class that yields a kind of iterator (
Cursor
), which yields pairs of corresponding symbols visible in some base class, unless there's a parent class that already contains the same pairs. Most of the logic is in SymbolPairs, which contains generic pair-oriented traversal logic. - trait PostErasure extends SubComponent with InfoTransform with TypingTransformers with reflect.internal.transform.PostErasure
This phase maps ErasedValueTypes to the underlying unboxed representation and performs peephole optimizations.
- abstract class SampleTransform extends SubComponent with Transform
A sample transform.
- abstract class SpecializeTypes extends SubComponent with InfoTransform with TypingTransformers
Specialize code on types.
Specialize code on types.
Make sure you've read the thesis:
Iulian Dragos: Compiling Scala for Performance (chapter 4)
There are some things worth noting, (possibly) not mentioned there: 0) Make sure you understand the meaning of various
SpecializedInfo
descriptors defined below.1) Specializing traits by introducing bridges in specialized methods of the specialized trait may introduce problems during mixin composition. Concretely, it may cause cyclic calls and result in a stack overflow. See ticket #4351. This was solved by introducing an
Abstract
specialized info descriptor. Instead of generating a bridge in the trait, an abstract method is generated.2) Specialized private members sometimes have to be switched to protected. In some cases, even this is not enough. Example:
class A[@specialized T](protected val d: T) { def foo(that: A[T]) = that.d }
Specialization will generate a specialized class and a specialized method:
class A$mcI$sp(protected val d: Int) extends A[Int] { def foo(that: A[Int]) = foo$mcI$sp(that) def foo(that: A[Int]) = that.d }
Above,
A$mcI$sp
cannot accessd
, so the method cannot be typechecked. - abstract class Statics extends SubComponent with Transform with TreeDSL
- abstract class TailCalls extends SubComponent with Transform
Perform tail recursive call elimination.
- trait Transform extends SubComponent
A base class for transforms.
A base class for transforms.
A transform contains a compiler phase which applies a tree transformer.
- trait TypeAdaptingTransformer extends AnyRef
A trait usable by transforms that need to adapt trees of one type to another type
- trait TypingTransformers extends AnyRef
A base class for transforms.
A base class for transforms. A transform contains a compiler phase which applies a tree transformer.
- abstract class UnCurry extends SubComponent with InfoTransform with reflect.internal.transform.UnCurry with TypingTransformers with TreeDSL
- uncurry all symbol and tree types (@see UnCurryPhase) -- this includes normalizing all proper types.
- uncurry all symbol and tree types (@see UnCurryPhase) -- this includes normalizing all proper types.
- for every curried parameter list: (ps_1) ... (ps_n) ==> (ps_1, ..., ps_n)
- for every curried application: f(args_1)...(args_n) ==> f(args_1, ..., args_n)
- for every type application: f[Ts] ==> f[Ts]() unless followed by parameters
- for every use of a parameterless function: f ==> f() and q.f ==> q.f()
- for every def-parameter: x: => T ==> x: () => T
- for every use of a def-parameter: x ==> x.apply()
- for every argument to a def parameter
x: => T': if argument is not a reference to a def parameter: convert argument
eto (expansion of)
() => e' - for every repeated Scala parameter
x: T*' --> x: Seq[T].
- for every repeated Java parameter
x: T...' --> x: Array[T], except: if T is an unbounded abstract type, replace --> x: Array[Object]
- for every method defining repeated parameters annotated with @varargs, generate a synthetic Java-style vararg method
- for every argument list that corresponds to a repeated Scala parameter (a_1, ..., a_n) => (Seq(a_1, ..., a_n))
- for every argument list that corresponds to a repeated Java parameter (a_1, ..., a_n) => (Array(a_1, ..., a_n))
- for every argument list that is an escaped sequence (a_1:_*) => (a_1) (possibly converted to sequence or array, as needed)
- convert implicit method types to method types
- convert non-trivial catches in try statements to matches
- convert non-local returns to throws with enclosing try statements.
- convert try-catch expressions in contexts where there might be values on the stack to a local method and a call to it (since an exception empties the evaluation stack):
meth(x_1,..., try { x_i } catch { ..}, .. x_b0) ==> { def liftedTry$1 = try { x_i } catch { .. } meth(x_1, .., liftedTry$1(), .. ) }
- remove calls to elidable methods and replace their bodies with NOPs when elide-below requires it
The Scala compiler and reflection APIs.