aboutsummaryrefslogtreecommitdiff
path: root/src/dotty
diff options
context:
space:
mode:
Diffstat (limited to 'src/dotty')
-rw-r--r--src/dotty/runtime/LazyVals.scala6
-rw-r--r--src/dotty/tools/backend/jvm/DottyBackendInterface.scala2
-rw-r--r--src/dotty/tools/dotc/Compiler.scala12
-rw-r--r--src/dotty/tools/dotc/Run.scala7
-rw-r--r--src/dotty/tools/dotc/ast/Desugar.scala7
-rw-r--r--src/dotty/tools/dotc/ast/TreeInfo.scala9
-rw-r--r--src/dotty/tools/dotc/ast/tpd.scala32
-rw-r--r--src/dotty/tools/dotc/core/Denotations.scala38
-rw-r--r--src/dotty/tools/dotc/core/Phases.scala2
-rw-r--r--src/dotty/tools/dotc/core/StdNames.scala2
-rw-r--r--src/dotty/tools/dotc/core/SymDenotations.scala16
-rw-r--r--src/dotty/tools/dotc/core/Symbols.scala8
-rw-r--r--src/dotty/tools/dotc/core/TypeApplications.scala15
-rw-r--r--src/dotty/tools/dotc/core/TypeComparer.scala8
-rw-r--r--src/dotty/tools/dotc/core/TypeErasure.scala143
-rw-r--r--src/dotty/tools/dotc/core/Types.scala6
-rw-r--r--src/dotty/tools/dotc/core/pickling/NameBuffer.scala2
-rw-r--r--src/dotty/tools/dotc/core/pickling/PositionPickler.scala2
-rw-r--r--src/dotty/tools/dotc/core/pickling/TastyBuffer.scala5
-rw-r--r--src/dotty/tools/dotc/core/pickling/TreeBuffer.scala3
-rw-r--r--src/dotty/tools/dotc/parsing/Parsers.scala22
-rw-r--r--src/dotty/tools/dotc/printing/RefinedPrinter.scala7
-rw-r--r--src/dotty/tools/dotc/transform/Constructors.scala14
-rw-r--r--src/dotty/tools/dotc/transform/ElimErasedValueType.scala82
-rw-r--r--src/dotty/tools/dotc/transform/Erasure.scala140
-rw-r--r--src/dotty/tools/dotc/transform/ExplicitOuter.scala2
-rw-r--r--src/dotty/tools/dotc/transform/ExtensionMethods.scala154
-rw-r--r--src/dotty/tools/dotc/transform/FullParameterization.scala21
-rw-r--r--src/dotty/tools/dotc/transform/Getters.scala20
-rw-r--r--src/dotty/tools/dotc/transform/InterceptedMethods.scala1
-rw-r--r--src/dotty/tools/dotc/transform/LazyVals.scala196
-rw-r--r--src/dotty/tools/dotc/transform/Memoize.scala34
-rw-r--r--src/dotty/tools/dotc/transform/Mixin.scala9
-rw-r--r--src/dotty/tools/dotc/transform/MixinOps.scala2
-rw-r--r--src/dotty/tools/dotc/transform/ParamForwarding.scala2
-rw-r--r--src/dotty/tools/dotc/transform/SuperAccessors.scala2
-rw-r--r--src/dotty/tools/dotc/transform/TreeChecker.scala3
-rw-r--r--src/dotty/tools/dotc/transform/TreeExtractors.scala48
-rw-r--r--src/dotty/tools/dotc/transform/TypeTestsCasts.scala7
-rw-r--r--src/dotty/tools/dotc/transform/VCInline.scala59
-rw-r--r--src/dotty/tools/dotc/transform/ValueClasses.scala15
-rw-r--r--src/dotty/tools/dotc/typer/Applications.scala7
-rw-r--r--src/dotty/tools/dotc/typer/Typer.scala2
43 files changed, 832 insertions, 342 deletions
diff --git a/src/dotty/runtime/LazyVals.scala b/src/dotty/runtime/LazyVals.scala
index 4130d4d60..2aa45e6fd 100644
--- a/src/dotty/runtime/LazyVals.scala
+++ b/src/dotty/runtime/LazyVals.scala
@@ -11,8 +11,8 @@ object LazyVals {
final val BITS_PER_LAZY_VAL = 2
final val LAZY_VAL_MASK = 3
- @inline def STATE(cur: Long, ord: Long) = (cur >> (ord * BITS_PER_LAZY_VAL)) & LAZY_VAL_MASK
- @inline def CAS(t: Object, offset: Long, e: Long, v: Long, ord: Int) = {
+ @inline def STATE(cur: Long, ord: Int) = (cur >> (ord * BITS_PER_LAZY_VAL)) & LAZY_VAL_MASK
+ @inline def CAS(t: Object, offset: Long, e: Long, v: Int, ord: Int) = {
val mask = ~(LAZY_VAL_MASK << ord * BITS_PER_LAZY_VAL)
val n = (e & mask) | (v << (ord * BITS_PER_LAZY_VAL))
compareAndSet(t, offset, e, n)
@@ -65,7 +65,7 @@ object LazyVals {
monitors(id)
}
- @inline def getOffset(obj: Object, name: String) = unsafe.objectFieldOffset(obj.getClass.getDeclaredField(name))
+ @inline def getOffset(clz: Class[_], name: String) = unsafe.objectFieldOffset(clz.getDeclaredField(name))
object Names {
final val state = "STATE"
diff --git a/src/dotty/tools/backend/jvm/DottyBackendInterface.scala b/src/dotty/tools/backend/jvm/DottyBackendInterface.scala
index 009b7fb2b..2e904cc23 100644
--- a/src/dotty/tools/backend/jvm/DottyBackendInterface.scala
+++ b/src/dotty/tools/backend/jvm/DottyBackendInterface.scala
@@ -589,7 +589,7 @@ class DottyBackendInterface()(implicit ctx: Context) extends BackendInterface{
def isDeferred: Boolean = sym is Flags.Deferred
def isPrivate: Boolean = sym is Flags.Private
def getsJavaFinalFlag: Boolean =
- isFinal && !toDenot(sym).isClassConstructor && !(sym is Flags.Mutable) && !(sym.enclosingClass is Flags.JavaInterface)
+ isFinal && !toDenot(sym).isClassConstructor && !(sym is Flags.Mutable) && !(sym.enclosingClass is Flags.Trait)
def getsJavaPrivateFlag: Boolean =
isPrivate //|| (sym.isPrimaryConstructor && sym.owner.isTopLevelModuleClass)
diff --git a/src/dotty/tools/dotc/Compiler.scala b/src/dotty/tools/dotc/Compiler.scala
index 6787e3a2d..44e7ac450 100644
--- a/src/dotty/tools/dotc/Compiler.scala
+++ b/src/dotty/tools/dotc/Compiler.scala
@@ -49,20 +49,22 @@ class Compiler {
List(new PatternMatcher,
new ExplicitOuter,
new Splitter),
- List(new LazyVals,
- new SeqLiterals,
+ List(new SeqLiterals,
new InterceptedMethods,
new Literalize,
new Getters,
new ElimByName,
new ResolveSuper),
List(new Erasure),
- List(new Mixin,
+ List(new ElimErasedValueType,
+ new VCInline,
+ new Mixin,
+ new LazyVals,
new Memoize,
- new CapturedVars,
+ new CapturedVars, // capturedVars has a transformUnit: no phases should introduce local mutable vars here
new Constructors,
new FunctionalInterfaces),
- List(new LambdaLift,
+ List(new LambdaLift, // in this mini-phase block scopes are incorrect. No phases that rely on scopes should be here
new Flatten,
new RestoreScopes),
List(/*new PrivateToStatic,*/ new CollectEntryPoints, new LabelDefs, new ElimWildcardIdents, new TraitConstructors),
diff --git a/src/dotty/tools/dotc/Run.scala b/src/dotty/tools/dotc/Run.scala
index a008214c9..c99f5efb9 100644
--- a/src/dotty/tools/dotc/Run.scala
+++ b/src/dotty/tools/dotc/Run.scala
@@ -10,6 +10,7 @@ import reporting.Reporter
import transform.TreeChecker
import java.io.{BufferedWriter, OutputStreamWriter}
import scala.reflect.io.VirtualFile
+import scala.util.control.NonFatal
class Run(comp: Compiler)(implicit ctx: Context) {
@@ -27,9 +28,13 @@ class Run(comp: Compiler)(implicit ctx: Context) {
}
}
- def compile(fileNames: List[String]): Unit = {
+ def compile(fileNames: List[String]): Unit = try {
val sources = fileNames map getSource
compileSources(sources)
+ } catch {
+ case NonFatal(ex) =>
+ println(s"exception occurred while compiling $units%, %")
+ throw ex
}
/** TODO: There's a fundamental design problem here: We assmble phases using `squash`
diff --git a/src/dotty/tools/dotc/ast/Desugar.scala b/src/dotty/tools/dotc/ast/Desugar.scala
index 0c13d1ecc..f7904abc0 100644
--- a/src/dotty/tools/dotc/ast/Desugar.scala
+++ b/src/dotty/tools/dotc/ast/Desugar.scala
@@ -289,8 +289,13 @@ object desugar {
val caseParams = constrVparamss.head.toArray
val productElemMeths = for (i <- 0 until arity) yield
syntheticProperty(nme.selectorName(i), Select(This(EmptyTypeName), caseParams(i).name))
+ def isRepeated(tree: Tree): Boolean = tree match {
+ case PostfixOp(_, nme.raw.STAR) => true
+ case ByNameTypeTree(tree1) => isRepeated(tree1)
+ case _ => false
+ }
val hasRepeatedParam = constrVparamss.exists(_.exists {
- case ValDef(_, PostfixOp(_, nme.raw.STAR), _) => true
+ case ValDef(_, tpt, _) => isRepeated(tpt)
case _ => false
})
val copyMeths =
diff --git a/src/dotty/tools/dotc/ast/TreeInfo.scala b/src/dotty/tools/dotc/ast/TreeInfo.scala
index c6d7e10f7..6d1c04978 100644
--- a/src/dotty/tools/dotc/ast/TreeInfo.scala
+++ b/src/dotty/tools/dotc/ast/TreeInfo.scala
@@ -160,11 +160,12 @@ trait TreeInfo[T >: Untyped <: Type] { self: Trees.Instance[T] =>
case _ => Nil
}
- /** Is tpt a vararg type of the form T* ? */
- def isRepeatedParamType(tpt: Tree)(implicit ctx: Context) = tpt match {
+ /** Is tpt a vararg type of the form T* or => T*? */
+ def isRepeatedParamType(tpt: Tree)(implicit ctx: Context): Boolean = tpt match {
+ case ByNameTypeTree(tpt1) => isRepeatedParamType(tpt1)
case tpt: TypeTree => tpt.typeOpt.isRepeatedParam
- case AppliedTypeTree(Select(_, tpnme.REPEATED_PARAM_CLASS), _) => true
- case _ => false
+ case AppliedTypeTree(Select(_, tpnme.REPEATED_PARAM_CLASS), _) => true
+ case _ => false
}
/** Is name a left-associative operator? */
diff --git a/src/dotty/tools/dotc/ast/tpd.scala b/src/dotty/tools/dotc/ast/tpd.scala
index 0a1611b61..de0ef3344 100644
--- a/src/dotty/tools/dotc/ast/tpd.scala
+++ b/src/dotty/tools/dotc/ast/tpd.scala
@@ -2,6 +2,7 @@ package dotty.tools
package dotc
package ast
+import dotty.tools.dotc.transform.ExplicitOuter
import dotty.tools.dotc.typer.ProtoTypes.FunProtoTyped
import transform.SymUtils._
import core._
@@ -243,6 +244,17 @@ object tpd extends Trees.Instance[Type] with TypedTreeInfo {
ta.assignType(untpd.TypeDef(cls.name, impl), cls)
}
+ // { <label> def while$(): Unit = if (cond) { body; while$() } ; while$() }
+ def WhileDo(owner: Symbol, cond: Tree, body: List[Tree])(implicit ctx: Context): Tree = {
+ val sym = ctx.newSymbol(owner, nme.WHILE_PREFIX, Flags.Label | Flags.Synthetic,
+ MethodType(Nil, defn.UnitType), coord = cond.pos)
+
+ val call = Apply(ref(sym), Nil)
+ val rhs = If(cond, Block(body, call), unitLiteral)
+ Block(List(DefDef(sym, rhs)), call)
+ }
+
+
def Import(expr: Tree, selectors: List[untpd.Tree])(implicit ctx: Context): Import =
ta.assignType(untpd.Import(expr, selectors), ctx.newImportSymbol(ctx.owner, expr))
@@ -288,7 +300,16 @@ object tpd extends Trees.Instance[Type] with TypedTreeInfo {
if (tp.isType) TypeTree(tp)
else if (prefixIsElidable(tp)) Ident(tp)
else tp.prefix match {
- case pre: SingletonType => singleton(pre).select(tp)
+ case pre: SingletonType =>
+ val prefix =
+ singleton(pre) match {
+ case t: This if ctx.erasedTypes && !(t.symbol == ctx.owner.enclosingClass || t.symbol.isStaticOwner) =>
+ // after erasure outer paths should be respected
+ new ExplicitOuter.OuterOps(ctx).path(t.tpe.widen.classSymbol)
+ case t =>
+ t
+ }
+ prefix.select(tp)
case pre => SelectFromTypeTree(TypeTree(pre), tp)
} // no checks necessary
@@ -563,7 +584,7 @@ object tpd extends Trees.Instance[Type] with TypedTreeInfo {
loop(from.owner, from :: froms, to :: tos)
else {
//println(i"change owner ${from :: froms}%, % ==> $tos of $tree")
- new TreeTypeMap(oldOwners = from :: froms, newOwners = tos).apply(tree)
+ new TreeTypeMap(oldOwners = from :: froms, newOwners = tos)(ctx.withMode(Mode.FutureDefsOK)).apply(tree)
}
}
loop(from, Nil, to :: Nil)
@@ -578,8 +599,11 @@ object tpd extends Trees.Instance[Type] with TypedTreeInfo {
def traverse(tree: Tree)(implicit ctx: Context) = tree match {
case tree: DefTree =>
val sym = tree.symbol
- if (sym.denot(ctx.withPhase(trans)).owner == from)
- sym.copySymDenotation(owner = to).installAfter(trans)
+ if (sym.denot(ctx.withPhase(trans)).owner == from) {
+ val d = sym.copySymDenotation(owner = to)
+ d.installAfter(trans)
+ d.transformAfter(trans, d => if (d.owner eq from) d.copySymDenotation(owner = to) else d)
+ }
if (sym.isWeakOwner) traverseChildren(tree)
case _ =>
traverseChildren(tree)
diff --git a/src/dotty/tools/dotc/core/Denotations.scala b/src/dotty/tools/dotc/core/Denotations.scala
index a30cff714..6502c4a40 100644
--- a/src/dotty/tools/dotc/core/Denotations.scala
+++ b/src/dotty/tools/dotc/core/Denotations.scala
@@ -620,14 +620,9 @@ object Denotations {
// println(s"installing $this after $phase/${phase.id}, valid = ${current.validFor}")
// printPeriods(current)
this.validFor = Period(ctx.runId, targetId, current.validFor.lastPhaseId)
- if (current.validFor.firstPhaseId == targetId) {
- // replace current with this denotation
- var prev = current
- while (prev.nextInRun ne current) prev = prev.nextInRun
- prev.nextInRun = this
- this.nextInRun = current.nextInRun
- current.validFor = Nowhere
- } else {
+ if (current.validFor.firstPhaseId == targetId)
+ replaceDenotation(current)
+ else {
// insert this denotation after current
current.validFor = Period(ctx.runId, current.validFor.firstPhaseId, targetId - 1)
this.nextInRun = current.nextInRun
@@ -637,6 +632,33 @@ object Denotations {
}
}
+ /** Apply a transformation `f` to all denotations in this group that start at or after
+ * given phase. Denotations are replaced while keeping the same validity periods.
+ */
+ protected def transformAfter(phase: DenotTransformer, f: SymDenotation => SymDenotation)(implicit ctx: Context): Unit = {
+ var current = symbol.current
+ while (current.validFor.firstPhaseId < phase.id && (current.nextInRun.validFor.code > current.validFor.code))
+ current = current.nextInRun
+ var hasNext = true
+ while ((current.validFor.firstPhaseId >= phase.id) && hasNext) {
+ val current1: SingleDenotation = f(current.asSymDenotation)
+ if (current1 ne current) {
+ current1.validFor = current.validFor
+ current1.replaceDenotation(current)
+ }
+ hasNext = current1.nextInRun.validFor.code > current1.validFor.code
+ current = current1.nextInRun
+ }
+ }
+
+ private def replaceDenotation(current: SingleDenotation): Unit = {
+ var prev = current
+ while (prev.nextInRun ne current) prev = prev.nextInRun
+ prev.nextInRun = this
+ this.nextInRun = current.nextInRun
+ current.validFor = Nowhere
+ }
+
def staleSymbolError(implicit ctx: Context) = {
def ownerMsg = this match {
case denot: SymDenotation => s"in ${denot.owner}"
diff --git a/src/dotty/tools/dotc/core/Phases.scala b/src/dotty/tools/dotc/core/Phases.scala
index 96066db5e..406a3457a 100644
--- a/src/dotty/tools/dotc/core/Phases.scala
+++ b/src/dotty/tools/dotc/core/Phases.scala
@@ -232,6 +232,7 @@ object Phases {
private val typerCache = new PhaseCache(classOf[FrontEnd])
private val refChecksCache = new PhaseCache(classOf[RefChecks])
+ private val extensionMethodsCache = new PhaseCache(classOf[ExtensionMethods])
private val erasureCache = new PhaseCache(classOf[Erasure])
private val patmatCache = new PhaseCache(classOf[PatternMatcher])
private val flattenCache = new PhaseCache(classOf[Flatten])
@@ -241,6 +242,7 @@ object Phases {
def typerPhase = typerCache.phase
def refchecksPhase = refChecksCache.phase
+ def extensionMethodsPhase = extensionMethodsCache.phase
def erasurePhase = erasureCache.phase
def patmatPhase = patmatCache.phase
def flattenPhase = flattenCache.phase
diff --git a/src/dotty/tools/dotc/core/StdNames.scala b/src/dotty/tools/dotc/core/StdNames.scala
index 829ff8b8f..74a121b47 100644
--- a/src/dotty/tools/dotc/core/StdNames.scala
+++ b/src/dotty/tools/dotc/core/StdNames.scala
@@ -226,6 +226,7 @@ object StdNames {
val ANYname: N = "<anyname>"
val CONSTRUCTOR: N = Names.CONSTRUCTOR.toString
val DEFAULT_CASE: N = "defaultCase$"
+ val EVT2U: N = "evt2u$"
val EQEQ_LOCAL_VAR: N = "eqEqTemp$"
val FAKE_LOCAL_THIS: N = "this$"
val IMPLCLASS_CONSTRUCTOR: N = "$init$"
@@ -257,6 +258,7 @@ object StdNames {
val SKOLEM: N = "<skolem>"
val SPECIALIZED_INSTANCE: N = "specInstance$"
val THIS: N = "_$this"
+ val U2EVT: N = "u2evt$"
final val Nil: N = "Nil"
final val Predef: N = "Predef"
diff --git a/src/dotty/tools/dotc/core/SymDenotations.scala b/src/dotty/tools/dotc/core/SymDenotations.scala
index 83499ca7b..14be606a1 100644
--- a/src/dotty/tools/dotc/core/SymDenotations.scala
+++ b/src/dotty/tools/dotc/core/SymDenotations.scala
@@ -1043,6 +1043,12 @@ object SymDenotations {
/** Install this denotation as the result of the given denotation transformer. */
override def installAfter(phase: DenotTransformer)(implicit ctx: Context): Unit =
super.installAfter(phase)
+
+ /** Apply a transformation `f` to all denotations in this group that start at or after
+ * given phase. Denotations are replaced while keeping the same validity periods.
+ */
+ override def transformAfter(phase: DenotTransformer, f: SymDenotation => SymDenotation)(implicit ctx: Context): Unit =
+ super.transformAfter(phase, f)
}
/** The contents of a class definition during a period
@@ -1441,10 +1447,16 @@ object SymDenotations {
def inCache(tp: Type) = baseTypeRefCache.containsKey(tp)
- /** Can't cache types containing type variables which are uninstantiated
- * or whose instances can change, depending on typerstate.
+ /** We cannot cache:
+ * - type variables which are uninstantiated or whose instances can
+ * change, depending on typerstate.
+ * - types where the underlying type is an ErasedValueType, because
+ * this underlying type will change after ElimErasedValueType,
+ * and this changes subtyping relations. As a shortcut, we do not
+ * cache ErasedValueType at all.
*/
def isCachable(tp: Type): Boolean = tp match {
+ case _: TypeErasure.ErasedValueType => false
case tp: TypeVar => tp.inst.exists && inCache(tp.inst)
case tp: TypeProxy => inCache(tp.underlying)
case tp: AndOrType => inCache(tp.tp1) && inCache(tp.tp2)
diff --git a/src/dotty/tools/dotc/core/Symbols.scala b/src/dotty/tools/dotc/core/Symbols.scala
index 9f18e723c..2b91efbcd 100644
--- a/src/dotty/tools/dotc/core/Symbols.scala
+++ b/src/dotty/tools/dotc/core/Symbols.scala
@@ -434,14 +434,6 @@ object Symbols {
/** If this symbol satisfies predicate `p` this symbol, otherwise `NoSymbol` */
def filter(p: Symbol => Boolean): Symbol = if (p(this)) this else NoSymbol
- /** Is this symbol a user-defined value class? */
- final def isDerivedValueClass(implicit ctx: Context): Boolean = {
- this.derivesFrom(defn.AnyValClass)(ctx.withPhase(denot.validFor.firstPhaseId))
- // Simulate ValueClasses.isDerivedValueClass
- false // will migrate to ValueClasses.isDerivedValueClass;
- // unsupported value class code will continue to use this stub while it exists
- }
-
/** The current name of this symbol */
final def name(implicit ctx: Context): ThisName = denot.name.asInstanceOf[ThisName]
diff --git a/src/dotty/tools/dotc/core/TypeApplications.scala b/src/dotty/tools/dotc/core/TypeApplications.scala
index 5325189e1..a5e24c5ff 100644
--- a/src/dotty/tools/dotc/core/TypeApplications.scala
+++ b/src/dotty/tools/dotc/core/TypeApplications.scala
@@ -282,12 +282,17 @@ class TypeApplications(val self: Type) extends AnyVal {
/** Translate a type of the form From[T] to To[T], keep other types as they are.
* `from` and `to` must be static classes, both with one type parameter, and the same variance.
+ * Do the same for by name types => From[T] and => To[T]
*/
- def translateParameterized(from: ClassSymbol, to: ClassSymbol)(implicit ctx: Context): Type =
- if (self.derivesFrom(from))
- if (ctx.erasedTypes) to.typeRef
- else RefinedType(to.typeRef, to.typeParams.head.name, self.member(from.typeParams.head.name).info)
- else self
+ def translateParameterized(from: ClassSymbol, to: ClassSymbol)(implicit ctx: Context): Type = self match {
+ case self @ ExprType(tp) =>
+ self.derivedExprType(tp.translateParameterized(from, to))
+ case _ =>
+ if (self.derivesFrom(from))
+ if (ctx.erasedTypes) to.typeRef
+ else RefinedType(to.typeRef, to.typeParams.head.name, self.member(from.typeParams.head.name).info)
+ else self
+ }
/** If this is repeated parameter type, its underlying Seq type,
* or, if isJava is true, Array type, else the type itself.
diff --git a/src/dotty/tools/dotc/core/TypeComparer.scala b/src/dotty/tools/dotc/core/TypeComparer.scala
index a59a64a91..18f9f08bb 100644
--- a/src/dotty/tools/dotc/core/TypeComparer.scala
+++ b/src/dotty/tools/dotc/core/TypeComparer.scala
@@ -229,6 +229,14 @@ class TypeComparer(initctx: Context) extends DotClass with ConstraintHandling wi
compareSuper
case AndType(tp21, tp22) =>
isSubType(tp1, tp21) && isSubType(tp1, tp22)
+ case TypeErasure.ErasedValueType(cls2, underlying2) =>
+ def compareErasedValueType = tp1 match {
+ case TypeErasure.ErasedValueType(cls1, underlying1) =>
+ (cls1 eq cls2) && isSameType(underlying1, underlying2)
+ case _ =>
+ secondTry(tp1, tp2)
+ }
+ compareErasedValueType
case ErrorType =>
true
case _ =>
diff --git a/src/dotty/tools/dotc/core/TypeErasure.scala b/src/dotty/tools/dotc/core/TypeErasure.scala
index 20cf816c2..e695e6721 100644
--- a/src/dotty/tools/dotc/core/TypeErasure.scala
+++ b/src/dotty/tools/dotc/core/TypeErasure.scala
@@ -3,12 +3,15 @@ package dotc
package core
import Symbols._, Types._, Contexts._, Flags._, Names._, StdNames._, Decorators._, Flags.JavaDefined
+import Uniques.unique
import dotc.transform.ExplicitOuter._
+import dotc.transform.ValueClasses._
import typer.Mode
import util.DotClass
/** Erased types are:
*
+ * ErasedValueType
* TypeRef(prefix is ignored, denot is ClassDenotation)
* TermRef(prefix is ignored, denot is SymDenotation)
* JavaArrayType
@@ -29,8 +32,12 @@ object TypeErasure {
/** A predicate that tests whether a type is a legal erased type. Only asInstanceOf and
* isInstanceOf may have types that do not satisfy the predicate.
+ * ErasedValueType is considered an erased type because it is valid after Erasure (it is
+ * eliminated by ElimErasedValueType).
*/
def isErasedType(tp: Type)(implicit ctx: Context): Boolean = tp match {
+ case _: ErasedValueType =>
+ true
case tp: TypeRef =>
tp.symbol.isClass && tp.symbol != defn.AnyClass
case _: TermRef =>
@@ -51,13 +58,35 @@ object TypeErasure {
false
}
- case class ErasedValueType(cls: ClassSymbol, underlying: Type) extends CachedGroundType {
- override def computeHash = doHash(cls, underlying)
+ /** A type representing the semi-erasure of a derived value class, see SIP-15
+ * where it's called "C$unboxed" for a class C.
+ * Derived value classes are erased to this type during Erasure (when
+ * semiEraseVCs = true) and subsequently erased to their underlying type
+ * during ElimErasedValueType. This type is outside the normal Scala class
+ * hierarchy: it is a subtype of no other type and is a supertype only of
+ * Nothing. This is because this type is only useful for type adaptation (see
+ * [[Erasure.Boxing#adaptToType]]).
+ *
+ * @param cls The value class symbol
+ * @param erasedUnderlying The erased type of the single field of the value class
+ */
+ abstract case class ErasedValueType(cls: ClassSymbol, erasedUnderlying: Type)
+ extends CachedGroundType with ValueType {
+ override def computeHash = doHash(cls, erasedUnderlying)
}
- private def erasureIdx(isJava: Boolean, isSemi: Boolean, isConstructor: Boolean, wildcardOK: Boolean) =
+ final class CachedErasedValueType(cls: ClassSymbol, erasedUnderlying: Type)
+ extends ErasedValueType(cls, erasedUnderlying)
+
+ object ErasedValueType {
+ def apply(cls: ClassSymbol, erasedUnderlying: Type)(implicit ctx: Context) = {
+ unique(new CachedErasedValueType(cls, erasedUnderlying))
+ }
+ }
+
+ private def erasureIdx(isJava: Boolean, semiEraseVCs: Boolean, isConstructor: Boolean, wildcardOK: Boolean) =
(if (isJava) 1 else 0) +
- (if (isSemi) 2 else 0) +
+ (if (semiEraseVCs) 2 else 0) +
(if (isConstructor) 4 else 0) +
(if (wildcardOK) 8 else 0)
@@ -65,41 +94,32 @@ object TypeErasure {
for {
isJava <- List(false, true)
- isSemi <- List(false, true)
+ semiEraseVCs <- List(false, true)
isConstructor <- List(false, true)
wildcardOK <- List(false, true)
- } erasures(erasureIdx(isJava, isSemi, isConstructor, wildcardOK)) =
- new TypeErasure(isJava, isSemi, isConstructor, wildcardOK)
+ } erasures(erasureIdx(isJava, semiEraseVCs, isConstructor, wildcardOK)) =
+ new TypeErasure(isJava, semiEraseVCs, isConstructor, wildcardOK)
- /** Produces an erasure function.
- * @param isJava Arguments should be treated the way Java does it
- * @param isSemi Value classes are mapped in an intermediate step to
- * ErasedValueClass types, instead of going directly to
- * the erasure of the underlying type.
- * @param isConstructor Argument forms part of the type of a constructor
- * @param wildcardOK Wildcards are acceptable (true when using the erasure
- * for computing a signature name).
+ /** Produces an erasure function. See the documentation of the class [[TypeErasure]]
+ * for a description of each parameter.
*/
- private def erasureFn(isJava: Boolean, isSemi: Boolean, isConstructor: Boolean, wildcardOK: Boolean): TypeErasure =
- erasures(erasureIdx(isJava, isSemi, isConstructor, wildcardOK))
-
- private val scalaErasureFn = erasureFn(isJava = false, isSemi = false, isConstructor = false, wildcardOK = false)
- private val scalaSigFn = erasureFn(isJava = false, isSemi = false, isConstructor = false, wildcardOK = true)
- private val javaSigFn = erasureFn(isJava = true, isSemi = false, isConstructor = false, wildcardOK = true)
- private val semiErasureFn = erasureFn(isJava = false, isSemi = true, isConstructor = false, wildcardOK = false)
+ private def erasureFn(isJava: Boolean, semiEraseVCs: Boolean, isConstructor: Boolean, wildcardOK: Boolean): TypeErasure =
+ erasures(erasureIdx(isJava, semiEraseVCs, isConstructor, wildcardOK))
/** The current context with a phase no later than erasure */
private def erasureCtx(implicit ctx: Context) =
if (ctx.erasedTypes) ctx.withPhase(ctx.erasurePhase).addMode(Mode.FutureDefsOK) else ctx
- def erasure(tp: Type)(implicit ctx: Context): Type = scalaErasureFn(tp)(erasureCtx)
- def semiErasure(tp: Type)(implicit ctx: Context): Type = semiErasureFn(tp)(erasureCtx)
+ def erasure(tp: Type, semiEraseVCs: Boolean = true)(implicit ctx: Context): Type =
+ erasureFn(isJava = false, semiEraseVCs, isConstructor = false, wildcardOK = false)(tp)(erasureCtx)
+
def sigName(tp: Type, isJava: Boolean)(implicit ctx: Context): TypeName = {
val seqClass = if (isJava) defn.ArrayClass else defn.SeqClass
val normTp =
if (tp.isRepeatedParam) tp.translateParameterized(defn.RepeatedParamClass, seqClass)
else tp
- (if (isJava) javaSigFn else scalaSigFn).sigName(normTp)(erasureCtx)
+ val erase = erasureFn(isJava, semiEraseVCs = false, isConstructor = false, wildcardOK = true)
+ erase.sigName(normTp)(erasureCtx)
}
/** The erasure of a top-level reference. Differs from normal erasure in that
@@ -117,29 +137,20 @@ object TypeErasure {
erasure(tp)
}
- /** The erasure of a symbol's info. This is different of `erasure` in the way `ExprType`s are
- * treated. `eraseInfo` maps them them to nullary method types, whereas `erasure` maps them
- * to `Function0`.
- */
- def eraseInfo(tp: Type, sym: Symbol)(implicit ctx: Context): Type =
- scalaErasureFn.eraseInfo(tp, sym)(erasureCtx)
-
- /** The erasure of a function result type. Differs from normal erasure in that
- * Unit is kept instead of being mapped to BoxedUnit.
- */
- def eraseResult(tp: Type)(implicit ctx: Context): Type =
- scalaErasureFn.eraseResult(tp)(erasureCtx)
-
/** The symbol's erased info. This is the type's erasure, except for the following symbols:
*
* - For $asInstanceOf : [T]T
* - For $isInstanceOf : [T]Boolean
* - For all abstract types : = ?
+ * - For COMPANION_CLASS_METHOD : the erasure of their type with semiEraseVCs = false,
+ * this is needed to keep [[SymDenotation#companionClass]]
+ * working after erasure for value classes.
* - For all other symbols : the semi-erasure of their types, with
* isJava, isConstructor set according to symbol.
*/
def transformInfo(sym: Symbol, tp: Type)(implicit ctx: Context): Type = {
- val erase = erasureFn(sym is JavaDefined, isSemi = true, sym.isConstructor, wildcardOK = false)
+ val semiEraseVCs = sym.name ne nme.COMPANION_CLASS_METHOD
+ val erase = erasureFn(sym is JavaDefined, semiEraseVCs, sym.isConstructor, wildcardOK = false)
def eraseParamBounds(tp: PolyType): Type =
tp.derivedPolyType(
@@ -148,7 +159,7 @@ object TypeErasure {
if (defn.isPolymorphicAfterErasure(sym)) eraseParamBounds(sym.info.asInstanceOf[PolyType])
else if (sym.isAbstractType) TypeAlias(WildcardType)
else if (sym.isConstructor) outer.addParam(sym.owner.asClass, erase(tp)(erasureCtx))
- else eraseInfo(tp, sym)(erasureCtx) match {
+ else erase.eraseInfo(tp, sym)(erasureCtx) match {
case einfo: MethodType if sym.isGetter && einfo.resultType.isRef(defn.UnitClass) =>
defn.BoxedUnitClass.typeRef
case einfo =>
@@ -241,12 +252,15 @@ object TypeErasure {
import TypeErasure._
/**
- * This is used as the Scala erasure during the erasure phase itself
- * It differs from normal erasure in that value classes are erased to ErasedValueTypes which
- * are then later converted to the underlying parameter type in phase posterasure.
- *
+ * @param isJava Arguments should be treated the way Java does it
+ * @param semiEraseVCs If true, value classes are semi-erased to ErasedValueType
+ * (they will be fully erased in [[ElimErasedValueType]]).
+ * If false, they are erased like normal classes.
+ * @param isConstructor Argument forms part of the type of a constructor
+ * @param wildcardOK Wildcards are acceptable (true when using the erasure
+ * for computing a signature name).
*/
-class TypeErasure(isJava: Boolean, isSemi: Boolean, isConstructor: Boolean, wildcardOK: Boolean) extends DotClass {
+class TypeErasure(isJava: Boolean, semiEraseVCs: Boolean, isConstructor: Boolean, wildcardOK: Boolean) extends DotClass {
/** The erasure |T| of a type T. This is:
*
@@ -279,10 +293,12 @@ class TypeErasure(isJava: Boolean, isSemi: Boolean, isConstructor: Boolean, wild
* - For any other type, exception.
*/
private def apply(tp: Type)(implicit ctx: Context): Type = tp match {
+ case _: ErasedValueType =>
+ tp
case tp: TypeRef =>
val sym = tp.symbol
if (!sym.isClass) this(tp.info)
- else if (sym.isDerivedValueClass) eraseDerivedValueClassRef(tp)
+ else if (semiEraseVCs && isDerivedValueClass(sym)) eraseDerivedValueClassRef(tp)
else eraseNormalClassRef(tp)
case tp: RefinedType =>
val parent = tp.parent
@@ -291,7 +307,9 @@ class TypeErasure(isJava: Boolean, isSemi: Boolean, isConstructor: Boolean, wild
case tp: TermRef =>
this(tp.widen)
case tp: ThisType =>
- this(tp.cls.typeRef)
+ def thisTypeErasure(tpToErase: Type) =
+ erasureFn(isJava, semiEraseVCs = false, isConstructor, wildcardOK)(tpToErase)
+ thisTypeErasure(tp.cls.typeRef)
case SuperType(thistpe, supertpe) =>
SuperType(this(thistpe), this(supertpe))
case ExprType(rt) =>
@@ -303,7 +321,8 @@ class TypeErasure(isJava: Boolean, isSemi: Boolean, isConstructor: Boolean, wild
case OrType(tp1, tp2) =>
ctx.typeComparer.orType(this(tp1), this(tp2), erased = true)
case tp: MethodType =>
- val paramErasure = erasureFn(tp.isJava, isSemi, isConstructor, wildcardOK)(_)
+ def paramErasure(tpToErase: Type) =
+ erasureFn(tp.isJava, semiEraseVCs, isConstructor, wildcardOK)(tpToErase)
val formals = tp.paramTypes.mapConserve(paramErasure)
eraseResult(tp.resultType) match {
case rt: MethodType =>
@@ -341,11 +360,17 @@ class TypeErasure(isJava: Boolean, isSemi: Boolean, isConstructor: Boolean, wild
private def eraseArray(tp: RefinedType)(implicit ctx: Context) = {
val defn.ArrayType(elemtp) = tp
+ def arrayErasure(tpToErase: Type) =
+ erasureFn(isJava, semiEraseVCs = false, isConstructor, wildcardOK)(tpToErase)
if (elemtp derivesFrom defn.NullClass) JavaArrayType(defn.ObjectType)
else if (isUnboundedGeneric(elemtp)) defn.ObjectType
- else JavaArrayType(this(elemtp))
+ else JavaArrayType(arrayErasure(elemtp))
}
+ /** The erasure of a symbol's info. This is different from `apply` in the way `ExprType`s are
+ * treated. `eraseInfo` maps them them to nullary method types, whereas `apply` maps them
+ * to `Function0`.
+ */
def eraseInfo(tp: Type, sym: Symbol)(implicit ctx: Context) = tp match {
case ExprType(rt) =>
if (sym is Param) apply(tp)
@@ -354,22 +379,30 @@ class TypeErasure(isJava: Boolean, isSemi: Boolean, isConstructor: Boolean, wild
// forwarders to mixin methods.
// See doc comment for ElimByName for speculation how we could improve this.
else MethodType(Nil, Nil, eraseResult(rt))
- case tp => erasure(tp)
+ case tp => this(tp)
+ }
+
+ private def eraseDerivedValueClassRef(tref: TypeRef)(implicit ctx: Context): Type = {
+ val cls = tref.symbol.asClass
+ val underlying = underlyingOfValueClass(cls)
+ ErasedValueType(cls, erasure(underlying))
}
- private def eraseDerivedValueClassRef(tref: TypeRef)(implicit ctx: Context): Type =
- unsupported("eraseDerivedValueClass")
private def eraseNormalClassRef(tref: TypeRef)(implicit ctx: Context): Type = {
val cls = tref.symbol.asClass
(if (cls.owner is Package) normalizeClass(cls) else cls).typeRef
}
+ /** The erasure of a function result type. */
private def eraseResult(tp: Type)(implicit ctx: Context): Type = tp match {
case tp: TypeRef =>
val sym = tp.typeSymbol
if (sym eq defn.UnitClass) sym.typeRef
- else if (sym.isDerivedValueClass) eraseNormalClassRef(tp)
+ // For a value class V, "new V(x)" should have type V for type adaptation to work
+ // correctly (see SIP-15 and [[Erasure.Boxing.adaptToType]]), so the return type of a
+ // constructor method should not be semi-erased.
+ else if (isConstructor && isDerivedValueClass(sym)) eraseNormalClassRef(tp)
else this(tp)
case RefinedType(parent, _) if !(parent isRef defn.ArrayClass) =>
eraseResult(parent)
@@ -391,10 +424,12 @@ class TypeErasure(isJava: Boolean, isSemi: Boolean, isConstructor: Boolean, wild
* Need to ensure correspondence with erasure!
*/
private def sigName(tp: Type)(implicit ctx: Context): TypeName = tp match {
+ case ErasedValueType(_, underlying) =>
+ sigName(underlying)
case tp: TypeRef =>
val sym = tp.symbol
if (!sym.isClass) sigName(tp.info)
- else if (sym.isDerivedValueClass) sigName(eraseDerivedValueClassRef(tp))
+ else if (isDerivedValueClass(sym)) sigName(eraseDerivedValueClassRef(tp))
else normalizeClass(sym.asClass).fullName.asTypeName
case defn.ArrayType(elem) =>
sigName(this(tp))
diff --git a/src/dotty/tools/dotc/core/Types.scala b/src/dotty/tools/dotc/core/Types.scala
index e290e8868..595732b37 100644
--- a/src/dotty/tools/dotc/core/Types.scala
+++ b/src/dotty/tools/dotc/core/Types.scala
@@ -2066,14 +2066,16 @@ object Types {
def apply(paramTypes: List[Type], resultType: Type)(implicit ctx: Context): MethodType =
apply(nme.syntheticParamNames(paramTypes.length), paramTypes, resultType)
def fromSymbols(params: List[Symbol], resultType: Type)(implicit ctx: Context) = {
- def paramInfo(param: Symbol): Type = param.info match {
+ def translateRepeated(tp: Type): Type = tp match {
+ case tp @ ExprType(tp1) => tp.derivedExprType(translateRepeated(tp1))
case AnnotatedType(annot, tp) if annot matches defn.RepeatedAnnot =>
- val typeSym = param.info.typeSymbol.asClass
+ val typeSym = tp.typeSymbol.asClass
assert(typeSym == defn.SeqClass || typeSym == defn.ArrayClass)
tp.translateParameterized(typeSym, defn.RepeatedParamClass)
case tp =>
tp
}
+ def paramInfo(param: Symbol): Type = translateRepeated(param.info)
def transformResult(mt: MethodType) =
resultType.subst(params, (0 until params.length).toList map (MethodParam(mt, _)))
apply(params map (_.name.asTermName), params map paramInfo)(transformResult _)
diff --git a/src/dotty/tools/dotc/core/pickling/NameBuffer.scala b/src/dotty/tools/dotc/core/pickling/NameBuffer.scala
index 2a6239c5a..7ea94089f 100644
--- a/src/dotty/tools/dotc/core/pickling/NameBuffer.scala
+++ b/src/dotty/tools/dotc/core/pickling/NameBuffer.scala
@@ -11,7 +11,7 @@ import scala.io.Codec
import TastyName._
import PickleFormat._
-class NameBuffer extends TastyBuffer(100000) {
+class NameBuffer extends TastyBuffer(10000) {
private val nameRefs = new mutable.LinkedHashMap[TastyName, NameRef]
diff --git a/src/dotty/tools/dotc/core/pickling/PositionPickler.scala b/src/dotty/tools/dotc/core/pickling/PositionPickler.scala
index e8a0b3d01..1e36105cb 100644
--- a/src/dotty/tools/dotc/core/pickling/PositionPickler.scala
+++ b/src/dotty/tools/dotc/core/pickling/PositionPickler.scala
@@ -38,7 +38,7 @@ object PositionPickler {
import PositionPickler._
class PositionPickler(pickler: TastyPickler, addrOfTree: Tree => Option[Addr]) {
- val buf = new TastyBuffer(100000)
+ val buf = new TastyBuffer(5000)
pickler.newSection("Positions", buf)
import buf._
diff --git a/src/dotty/tools/dotc/core/pickling/TastyBuffer.scala b/src/dotty/tools/dotc/core/pickling/TastyBuffer.scala
index a67722227..f57c15a3d 100644
--- a/src/dotty/tools/dotc/core/pickling/TastyBuffer.scala
+++ b/src/dotty/tools/dotc/core/pickling/TastyBuffer.scala
@@ -44,7 +44,8 @@ class TastyBuffer(initialSize: Int) {
/** Write a byte of data. */
def writeByte(b: Int): Unit = {
- if (length == bytes.length) bytes = dble(bytes)
+ if (length >= bytes.length)
+ bytes = dble(bytes)
bytes(length) = b.toByte
length += 1
}
@@ -116,6 +117,8 @@ class TastyBuffer(initialSize: Int) {
def putNat(at: Addr, x: Int, width: Int): Unit = {
var y = x
var w = width
+ if(at.index + w >= bytes.length)
+ bytes = dble(bytes)
var digit = y & 0x7f | 0x80
while (w > 0) {
w -= 1
diff --git a/src/dotty/tools/dotc/core/pickling/TreeBuffer.scala b/src/dotty/tools/dotc/core/pickling/TreeBuffer.scala
index c224fc30b..393ffd278 100644
--- a/src/dotty/tools/dotc/core/pickling/TreeBuffer.scala
+++ b/src/dotty/tools/dotc/core/pickling/TreeBuffer.scala
@@ -8,10 +8,9 @@ import TastyBuffer.{Addr, AddrWidth}
import config.Printers.pickling
import ast.tpd.Tree
-class TreeBuffer extends TastyBuffer(1000000) {
+class TreeBuffer extends TastyBuffer(50000) {
private final val ItemsOverOffsets = 2
-
private val initialOffsetSize = bytes.length / (AddrWidth * ItemsOverOffsets)
private var offsets = new Array[Int](initialOffsetSize)
private var isRelative = new Array[Boolean](initialOffsetSize)
diff --git a/src/dotty/tools/dotc/parsing/Parsers.scala b/src/dotty/tools/dotc/parsing/Parsers.scala
index cbefb81fe..dd2c9bcaa 100644
--- a/src/dotty/tools/dotc/parsing/Parsers.scala
+++ b/src/dotty/tools/dotc/parsing/Parsers.scala
@@ -754,17 +754,21 @@ object Parsers {
if (in.token == ARROW) atPos(in.skipToken()) { ByNameTypeTree(argType()) }
else argType()
- /** ParamType ::= FunArgType | ArgType `*'
+ /** ParamType ::= [`=>'] ParamValueType
*/
def paramType(): Tree =
- if (in.token == ARROW) funArgType()
- else {
- val t = argType()
- if (isIdent(nme.raw.STAR)) {
- in.nextToken()
- atPos(t.pos.start) { PostfixOp(t, nme.raw.STAR) }
- } else t
- }
+ if (in.token == ARROW) atPos(in.skipToken()) { ByNameTypeTree(paramValueType()) }
+ else paramValueType()
+
+ /** ParamValueType ::= Type [`*']
+ */
+ def paramValueType(): Tree = {
+ val t = typ()
+ if (isIdent(nme.raw.STAR)) {
+ in.nextToken()
+ atPos(t.pos.start) { PostfixOp(t, nme.raw.STAR) }
+ } else t
+ }
/** TypeArgs ::= `[' ArgType {`,' ArgType} `]'
*/
diff --git a/src/dotty/tools/dotc/printing/RefinedPrinter.scala b/src/dotty/tools/dotc/printing/RefinedPrinter.scala
index 67bd65bc7..11d451255 100644
--- a/src/dotty/tools/dotc/printing/RefinedPrinter.scala
+++ b/src/dotty/tools/dotc/printing/RefinedPrinter.scala
@@ -3,6 +3,7 @@ package printing
import core._
import Texts._, Types._, Flags._, Names._, Symbols._, NameOps._, Constants._
+import TypeErasure.ErasedValueType
import Contexts.Context, Scopes.Scope, Denotations._, SymDenotations._, Annotations.Annotation
import StdNames.nme
import ast.{Trees, untpd, tpd}
@@ -63,7 +64,7 @@ class RefinedPrinter(_ctx: Context) extends PlainPrinter(_ctx) {
}
override def toTextPrefix(tp: Type): Text = controlled {
- def isOmittable(sym: Symbol) =
+ def isOmittable(sym: Symbol) =
if (ctx.settings.verbose.value) false
else if (homogenizedView) isEmptyPrefix(sym) // drop <root> and anonymous classes, but not scala, Predef.
else isOmittablePrefix(sym)
@@ -132,6 +133,8 @@ class RefinedPrinter(_ctx: Context) extends PlainPrinter(_ctx) {
return toText(tp.info)
case ExprType(result) =>
return "=> " ~ toText(result)
+ case ErasedValueType(clazz, underlying) =>
+ return "ErasedValueType(" ~ toText(clazz.typeRef) ~ ", " ~ toText(underlying) ~ ")"
case tp: ClassInfo =>
return toTextParents(tp.instantiatedParents) ~ "{...}"
case JavaArrayType(elemtp) =>
@@ -351,6 +354,8 @@ class RefinedPrinter(_ctx: Context) extends PlainPrinter(_ctx) {
toTextLocal(tpt) ~ " " ~ blockText(refines)
case AppliedTypeTree(tpt, args) =>
toTextLocal(tpt) ~ "[" ~ Text(args map argText, ", ") ~ "]"
+ case ByNameTypeTree(tpt) =>
+ "=> " ~ toTextLocal(tpt)
case TypeBoundsTree(lo, hi) =>
optText(lo)(" >: " ~ _) ~ optText(hi)(" <: " ~ _)
case Bind(name, body) =>
diff --git a/src/dotty/tools/dotc/transform/Constructors.scala b/src/dotty/tools/dotc/transform/Constructors.scala
index cd64497e9..ddd64d500 100644
--- a/src/dotty/tools/dotc/transform/Constructors.scala
+++ b/src/dotty/tools/dotc/transform/Constructors.scala
@@ -80,11 +80,10 @@ class Constructors extends MiniPhaseTransform with SymTransformer { thisTransfor
// (2) If the parameter accessor reference was to an alias getter,
// drop the () when replacing by the parameter.
object intoConstr extends TreeMap {
- private var excluded: FlagSet = _
override def transform(tree: Tree)(implicit ctx: Context): Tree = tree match {
case Ident(_) | Select(This(_), _) =>
var sym = tree.symbol
- if (sym is (ParamAccessor, butNot = excluded)) sym = sym.subst(accessors, paramSyms)
+ if (sym is (ParamAccessor, butNot = Mutable)) sym = sym.subst(accessors, paramSyms)
if (sym.owner.isConstructor) ref(sym).withPos(tree.pos) else tree
case Apply(fn, Nil) =>
val fn1 = transform(fn)
@@ -95,9 +94,8 @@ class Constructors extends MiniPhaseTransform with SymTransformer { thisTransfor
if (noDirectRefsFrom(tree)) tree else super.transform(tree)
}
- def apply(tree: Tree, inSuperCall: Boolean = false)(implicit ctx: Context): Tree = {
- this.excluded = if (inSuperCall) EmptyFlags else Mutable
- transform(tree)
+ def apply(tree: Tree, prevOwner: Symbol)(implicit ctx: Context): Tree = {
+ transform(tree).changeOwnerAfter(prevOwner, constr.symbol, thisTransform)
}
}
@@ -153,19 +151,19 @@ class Constructors extends MiniPhaseTransform with SymTransformer { thisTransfor
val sym = stat.symbol
if (isRetained(sym)) {
if (!stat.rhs.isEmpty && !isWildcardArg(stat.rhs))
- constrStats += Assign(ref(sym), intoConstr(stat.rhs)).withPos(stat.pos)
+ constrStats += Assign(ref(sym), intoConstr(stat.rhs, sym)).withPos(stat.pos)
clsStats += cpy.ValDef(stat)(rhs = EmptyTree)
}
else if (!stat.rhs.isEmpty) {
sym.copySymDenotation(
initFlags = sym.flags &~ Private,
owner = constr.symbol).installAfter(thisTransform)
- constrStats += intoConstr(stat)
+ constrStats += intoConstr(stat, sym)
}
case _: DefTree =>
clsStats += stat
case _ =>
- constrStats += intoConstr(stat)
+ constrStats += intoConstr(stat, tree.symbol)
}
splitStats(stats1)
case Nil =>
diff --git a/src/dotty/tools/dotc/transform/ElimErasedValueType.scala b/src/dotty/tools/dotc/transform/ElimErasedValueType.scala
new file mode 100644
index 000000000..8a18c9c17
--- /dev/null
+++ b/src/dotty/tools/dotc/transform/ElimErasedValueType.scala
@@ -0,0 +1,82 @@
+package dotty.tools.dotc
+package transform
+
+import ast.{Trees, tpd}
+import core._, core.Decorators._
+import TreeTransforms._, Phases.Phase
+import Types._, Contexts._, Constants._, Names._, NameOps._, Flags._, DenotTransformers._
+import SymDenotations._, Symbols._, StdNames._, Annotations._, Trees._, Scopes._, Denotations._
+import TypeErasure.ErasedValueType, ValueClasses._
+
+/** This phase erases ErasedValueType to their underlying type.
+ * It also removes the synthetic cast methods u2evt$ and evt2u$ which are
+ * no longer needed afterwards.
+ */
+class ElimErasedValueType extends MiniPhaseTransform with InfoTransformer {
+
+ import tpd._
+
+ override def phaseName: String = "elimErasedValueType"
+
+ override def runsAfter: Set[Class[_ <: Phase]] = Set(classOf[Erasure])
+
+ def transformInfo(tp: Type, sym: Symbol)(implicit ctx: Context): Type = sym match {
+ case sym: ClassSymbol if sym is ModuleClass =>
+ sym.companionClass match {
+ case origClass: ClassSymbol if isDerivedValueClass(origClass) =>
+ val cinfo = tp.asInstanceOf[ClassInfo]
+ val decls1 = cinfo.decls.cloneScope
+ ctx.atPhase(this.next) { implicit ctx =>
+ // Remove synthetic cast methods introduced by ExtensionMethods,
+ // they are no longer needed after this phase.
+ decls1.unlink(cinfo.decl(nme.U2EVT).symbol)
+ decls1.unlink(cinfo.decl(nme.EVT2U).symbol)
+ }
+ cinfo.derivedClassInfo(decls = decls1)
+ case _ =>
+ tp
+ }
+ case _ =>
+ elimEVT(tp)
+ }
+
+ def elimEVT(tp: Type)(implicit ctx: Context): Type = tp match {
+ case ErasedValueType(_, underlying) =>
+ elimEVT(underlying)
+ case tp: MethodType =>
+ val paramTypes = tp.paramTypes.mapConserve(elimEVT)
+ val retType = elimEVT(tp.resultType)
+ tp.derivedMethodType(tp.paramNames, paramTypes, retType)
+ case _ =>
+ tp
+ }
+
+ def transformTypeOfTree(tree: Tree)(implicit ctx: Context): Tree =
+ tree.withType(elimEVT(tree.tpe))
+
+ override def transformApply(tree: Apply)(implicit ctx: Context, info: TransformerInfo): Tree = {
+ val Apply(fun, args) = tree
+ val name = fun.symbol.name
+
+ // The casts to and from ErasedValueType are no longer needed once ErasedValueType
+ // has been eliminated.
+ val t =
+ if ((name eq nme.U2EVT) || (name eq nme.EVT2U))
+ args.head
+ else
+ tree
+ transformTypeOfTree(t)
+ }
+
+ // FIXME: transformIf and transformBlock won't be required anymore once #444 is fixed.
+ override def transformIdent(tree: Ident)(implicit ctx: Context, info: TransformerInfo): Tree =
+ transformTypeOfTree(tree)
+ override def transformSelect(tree: Select)(implicit ctx: Context, info: TransformerInfo): Tree =
+ transformTypeOfTree(tree)
+ override def transformBlock(tree: Block)(implicit ctx: Context, info: TransformerInfo): Tree =
+ transformTypeOfTree(tree)
+ override def transformIf(tree: If)(implicit ctx: Context, info: TransformerInfo): Tree =
+ transformTypeOfTree(tree)
+ override def transformTypeTree(tree: TypeTree)(implicit ctx: Context, info: TransformerInfo): Tree =
+ transformTypeOfTree(tree)
+}
diff --git a/src/dotty/tools/dotc/transform/Erasure.scala b/src/dotty/tools/dotc/transform/Erasure.scala
index 79db568b8..996c480ce 100644
--- a/src/dotty/tools/dotc/transform/Erasure.scala
+++ b/src/dotty/tools/dotc/transform/Erasure.scala
@@ -62,7 +62,7 @@ class Erasure extends Phase with DenotTransformer { thisTransformer =>
}
}
case ref =>
- ref.derivedSingleDenotation(ref.symbol, eraseInfo(ref.info, ref.symbol))
+ ref.derivedSingleDenotation(ref.symbol, transformInfo(ref.symbol, ref.info))
}
val eraser = new Erasure.Typer
@@ -170,15 +170,29 @@ object Erasure extends TypeTestsCasts{
def unbox(tree: Tree, pt: Type)(implicit ctx: Context): Tree = ctx.traceIndented(i"unboxing ${tree.showSummary}: ${tree.tpe} as a $pt") {
pt match {
case ErasedValueType(clazz, underlying) =>
+ def unboxedTree(t: Tree) =
+ adaptToType(t, clazz.typeRef)
+ .select(valueClassUnbox(clazz))
+ .appliedToNone
+
+ // Null unboxing needs to be treated separately since we cannot call a method on null.
+ // "Unboxing" null to underlying is equivalent to doing null.asInstanceOf[underlying]
+ // See tests/pos/valueclasses/nullAsInstanceOfVC.scala for cases where this might happen.
val tree1 =
- if ((tree.tpe isRef defn.NullClass) && underlying.isPrimitiveValueType)
- // convert `null` directly to underlying type, as going
- // via the unboxed type would yield a NPE (see SI-5866)
- unbox(tree, underlying)
- else
- adaptToType(tree, clazz.typeRef)
- .select(valueClassUnbox(clazz))
- .appliedToNone
+ if (tree.tpe isRef defn.NullClass)
+ adaptToType(tree, underlying)
+ else if (!(tree.tpe <:< clazz.typeRef)) {
+ assert(!(tree.tpe.typeSymbol.isPrimitiveValueClass))
+ val nullTree = Literal(Constant(null))
+ val unboxedNull = adaptToType(nullTree, underlying)
+
+ evalOnce(tree) { t =>
+ If(t.select(defn.Object_eq).appliedTo(nullTree),
+ unboxedNull,
+ unboxedTree(t))
+ }
+ } else unboxedTree(tree)
+
cast(tree1, pt)
case _ =>
val cls = pt.widen.classSymbol
@@ -192,6 +206,8 @@ object Erasure extends TypeTestsCasts{
/** Generate a synthetic cast operation from tree.tpe to pt.
* Does not do any boxing/unboxing (this is handled upstream).
+ * Casts from and to ErasedValueType are special, see the explanation
+ * in ExtensionMethods#transform.
*/
def cast(tree: Tree, pt: Type)(implicit ctx: Context): Tree = {
// TODO: The commented out assertion fails for tailcall/t6574.scala
@@ -203,9 +219,18 @@ object Erasure extends TypeTestsCasts{
if treeElem.widen.isPrimitiveValueType && !ptElem.isPrimitiveValueType =>
// See SI-2386 for one example of when this might be necessary.
cast(ref(defn.runtimeMethod(nme.toObjectArray)).appliedTo(tree), pt)
+ case (_, ErasedValueType(cls, _)) =>
+ ref(u2evt(cls)).appliedTo(tree)
case _ =>
- if (pt.isPrimitiveValueType) primitiveConversion(tree, pt.classSymbol)
- else tree.asInstance(pt)
+ tree.tpe.widen match {
+ case ErasedValueType(cls, _) =>
+ ref(evt2u(cls)).appliedTo(tree)
+ case _ =>
+ if (pt.isPrimitiveValueType)
+ primitiveConversion(tree, pt.classSymbol)
+ else
+ tree.asInstance(pt)
+ }
}
}
@@ -243,18 +268,39 @@ object Erasure extends TypeTestsCasts{
class Typer extends typer.ReTyper with NoChecking {
import Boxing._
- def erasedType(tree: untpd.Tree)(implicit ctx: Context): Type = tree.typeOpt match {
- case tp: TermRef if tree.isTerm => erasedRef(tp)
- case tp => erasure(tp)
- }
+ def erasedType(tree: untpd.Tree, semiEraseVCs: Boolean = true)(implicit ctx: Context): Type =
+ tree.typeOpt match {
+ case tp: TermRef if tree.isTerm => erasedRef(tp)
+ case tp => erasure(tp, semiEraseVCs)
+ }
- override def promote(tree: untpd.Tree)(implicit ctx: Context): tree.ThisTree[Type] = {
+ def promote(tree: untpd.Tree, semiEraseVCs: Boolean)(implicit ctx: Context): tree.ThisTree[Type] = {
assert(tree.hasType)
- val erased = erasedType(tree)
+ val erased = erasedType(tree, semiEraseVCs)
ctx.log(s"promoting ${tree.show}: ${erased.showWithUnderlying()}")
tree.withType(erased)
}
+ override def promote(tree: untpd.Tree)(implicit ctx: Context): tree.ThisTree[Type] = {
+ promote(tree, true)
+ }
+
+ /** When erasing most TypeTrees we should not semi-erase value types.
+ * This is not the case for [[DefDef#tpt]], [[ValDef#tpt]] and [[Typed#tpt]], they
+ * are handled separately by [[typedDefDef]], [[typedValDef]] and [[typedTyped]].
+ */
+ override def typedTypeTree(tree: untpd.TypeTree, pt: Type)(implicit ctx: Context): TypeTree = {
+ promote(tree, semiEraseVCs = false)
+ }
+
+ /** This override is only needed to semi-erase type ascriptions */
+ override def typedTyped(tree: untpd.Typed, pt: Type)(implicit ctx: Context): Tree = {
+ val Typed(expr, tpt) = tree
+ val tpt1 = promote(tpt)
+ val expr1 = typed(expr, tpt1.tpe)
+ assignType(untpd.cpy.Typed(tree)(expr1, tpt1), tpt1)
+ }
+
override def typedLiteral(tree: untpd.Literal)(implicit ctc: Context): Literal =
if (tree.typeOpt.isRef(defn.UnitClass)) tree.withType(tree.typeOpt)
else super.typedLiteral(tree)
@@ -319,7 +365,7 @@ object Erasure extends TypeTestsCasts{
assert(sym.isConstructor, s"${sym.showLocated}")
select(qual, defn.ObjectClass.info.decl(sym.name).symbol)
}
- else if (qualIsPrimitive && !symIsPrimitive || qual.tpe.isErasedValueType)
+ else if (qualIsPrimitive && !symIsPrimitive || qual.tpe.widenDealias.isErasedValueType)
recur(box(qual))
else if (!qualIsPrimitive && symIsPrimitive)
recur(unbox(qual, sym.owner.typeRef))
@@ -338,7 +384,7 @@ object Erasure extends TypeTestsCasts{
}
override def typedSelectFromTypeTree(tree: untpd.SelectFromTypeTree, pt: Type)(implicit ctx: Context) =
- untpd.Ident(tree.name).withPos(tree.pos).withType(erasedType(tree))
+ untpd.Ident(tree.name).withPos(tree.pos).withType(erasedType(tree, semiEraseVCs = false))
override def typedThis(tree: untpd.This)(implicit ctx: Context): Tree =
if (tree.symbol == ctx.owner.enclosingClass || tree.symbol.isStaticOwner) promote(tree)
@@ -435,6 +481,58 @@ object Erasure extends TypeTestsCasts{
super.typedDefDef(ddef1, sym)
}
+ /** After erasure, we may have to replace the closure method by a bridge.
+ * LambdaMetaFactory handles this automatically for most types, but we have
+ * to deal with boxing and unboxing of value classes ourselves.
+ */
+ override def typedClosure(tree: untpd.Closure, pt: Type)(implicit ctx: Context) = {
+ val implClosure @ Closure(_, meth, _) = super.typedClosure(tree, pt)
+ implClosure.tpe match {
+ case SAMType(sam) =>
+ val implType = meth.tpe.widen
+
+ val List(implParamTypes) = implType.paramTypess
+ val List(samParamTypes) = sam.info.paramTypess
+ val implResultType = implType.resultType
+ val samResultType = sam.info.resultType
+
+ // Given a value class V with an underlying type U, the following code:
+ // val f: Function1[V, V] = x => ...
+ // results in the creation of a closure and a method:
+ // def $anonfun(v1: V): V = ...
+ // val f: Function1[V, V] = closure($anonfun)
+ // After [[Erasure]] this method will look like:
+ // def $anonfun(v1: ErasedValueType(V, U)): ErasedValueType(V, U) = ...
+ // And after [[ElimErasedValueType]] it will look like:
+ // def $anonfun(v1: U): U = ...
+ // This method does not implement the SAM of Function1[V, V] anymore and
+ // needs to be replaced by a bridge:
+ // def $anonfun$2(v1: V): V = new V($anonfun(v1.underlying))
+ // val f: Function1 = closure($anonfun$2)
+ // In general, a bridge is needed when the signature of the closure method after
+ // Erasure contains an ErasedValueType but the corresponding type in the functional
+ // interface is not an ErasedValueType.
+ val bridgeNeeded =
+ (implResultType :: implParamTypes, samResultType :: samParamTypes).zipped.forall(
+ (implType, samType) => implType.isErasedValueType && !samType.isErasedValueType
+ )
+
+ if (bridgeNeeded) {
+ val bridge = ctx.newSymbol(ctx.owner, nme.ANON_FUN, Flags.Synthetic | Flags.Method, sam.info)
+ val bridgeCtx = ctx.withOwner(bridge)
+ Closure(bridge, bridgeParamss => {
+ implicit val ctx: Context = bridgeCtx
+
+ val List(bridgeParams) = bridgeParamss
+ val rhs = Apply(meth, (bridgeParams, implParamTypes).zipped.map(adapt(_, _)))
+ adapt(rhs, sam.info.resultType)
+ })
+ } else implClosure
+ case _ =>
+ implClosure
+ }
+ }
+
override def typedTypeDef(tdef: untpd.TypeDef, sym: Symbol)(implicit ctx: Context) =
EmptyTree
@@ -502,6 +600,8 @@ object Erasure extends TypeTestsCasts{
traverse(newStats, oldStats)
}
+
+ private final val NoBridgeFlags = Flags.Accessor | Flags.Deferred | Flags.Lazy
/** Create a bridge DefDef which overrides a parent method.
*
@@ -520,7 +620,7 @@ object Erasure extends TypeTestsCasts{
???
}
val bridge = ctx.newSymbol(currentClass,
- parentSym.name, parentSym.flags | Flags.Bridge, parentSym.info, coord = newDefSym.owner.coord).asTerm
+ parentSym.name, parentSym.flags &~ NoBridgeFlags | Flags.Bridge, parentSym.info, coord = newDefSym.owner.coord).asTerm
bridge.enteredAfter(ctx.phase.prev.asInstanceOf[DenotTransformer]) // this should be safe, as we're executing in context of next phase
ctx.debuglog(s"generating bridge from ${newDefSym} to $bridge")
diff --git a/src/dotty/tools/dotc/transform/ExplicitOuter.scala b/src/dotty/tools/dotc/transform/ExplicitOuter.scala
index 9fc164d3b..20e367e1f 100644
--- a/src/dotty/tools/dotc/transform/ExplicitOuter.scala
+++ b/src/dotty/tools/dotc/transform/ExplicitOuter.scala
@@ -28,8 +28,8 @@ import collection.mutable
*
* - add outer parameters to constructors
* - pass outer arguments in constructor calls
- * - replace outer this by outer paths.
*
+ * replacement of outer this by outer paths is done in Erasure.
* needs to run after pattern matcher as it can add outer checks and force creation of $outer
*/
class ExplicitOuter extends MiniPhaseTransform with InfoTransformer { thisTransformer =>
diff --git a/src/dotty/tools/dotc/transform/ExtensionMethods.scala b/src/dotty/tools/dotc/transform/ExtensionMethods.scala
index ae22adc39..b2f402bc5 100644
--- a/src/dotty/tools/dotc/transform/ExtensionMethods.scala
+++ b/src/dotty/tools/dotc/transform/ExtensionMethods.scala
@@ -14,17 +14,29 @@ import core._
import Phases.Phase
import Types._, Contexts._, Constants._, Names._, NameOps._, Flags._, DenotTransformers._
import SymDenotations._, Symbols._, StdNames._, Annotations._, Trees._, Scopes._, Denotations._
+import TypeErasure.{ erasure, ErasedValueType }
import TypeUtils._
import util.Positions._
import Decorators._
+import SymUtils._
/**
* 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.
+ *
+ * Additionally, for a value class V, let U be the underlying type after erasure. We add
+ * to the companion module of V two cast methods:
+ * def u2evt$(x0: U): ErasedValueType(V, U)
+ * def evt2u$(x0: ErasedValueType(V, U)): U
+ * The casts are used in [[Erasure]] to make it typecheck, they are then removed
+ * in [[ElimErasedValueType]].
+ * This is different from the implementation of value classes in Scala 2
+ * (see SIP-15) which uses `asInstanceOf` which does not typecheck.
*/
class ExtensionMethods extends MiniPhaseTransform with DenotTransformer with FullParameterization { thisTransformer =>
import tpd._
+ import ExtensionMethods._
/** the following two members override abstract members in Transform */
override def phaseName: String = "extmethods"
@@ -37,13 +49,27 @@ class ExtensionMethods extends MiniPhaseTransform with DenotTransformer with Ful
case ref: ClassDenotation if ref is ModuleClass =>
ref.linkedClass match {
case origClass: ClassSymbol if isDerivedValueClass(origClass) =>
- val cinfo = ref.classInfo // ./tests/pos/t2667.scala dies here for module class AnyVal$
+ val cinfo = ref.classInfo
val decls1 = cinfo.decls.cloneScope
ctx.atPhase(thisTransformer.next) { implicit ctx =>
- for (decl <- origClass.classInfo.decls) {
- if (isMethodWithExtension(decl))
- decls1.enter(createExtensionMethod(decl, ref.symbol))
+ // In Scala 2, extension methods are added before pickling so we should
+ // not generate them again.
+ if (!(origClass is Scala2x)) {
+ for (decl <- origClass.classInfo.decls) {
+ if (isMethodWithExtension(decl))
+ decls1.enter(createExtensionMethod(decl, ref.symbol))
+ }
}
+
+ val sym = ref.symbol
+ val underlying = erasure(underlyingOfValueClass(origClass))
+ val evt = ErasedValueType(origClass, underlying)
+ val u2evtSym = ctx.newSymbol(sym, nme.U2EVT, Synthetic | Method,
+ MethodType(List(nme.x_0), List(underlying), evt))
+ val evt2uSym = ctx.newSymbol(sym, nme.EVT2U, Synthetic | Method,
+ MethodType(List(nme.x_0), List(evt), underlying))
+ decls1.enter(u2evtSym)
+ decls1.enter(evt2uSym)
}
if (decls1.isEmpty) ref
else ref.copySymDenotation(info = cinfo.derivedClassInfo(decls = decls1))
@@ -64,65 +90,6 @@ class ExtensionMethods extends MiniPhaseTransform with DenotTransformer with Ful
target.owner.linkedClass == derived.owner) extensionMethod(target)
else NoSymbol
- /** Generate stream of possible names for the extension version of given instance method `imeth`.
- * If the method is not overloaded, this stream consists of just "imeth$extension".
- * If the method is overloaded, the stream has as first element "imeth$extenionX", where X is the
- * index of imeth in the sequence of overloaded alternatives with the same name. This choice will
- * always be picked as the name of the generated extension method.
- * After this first choice, all other possible indices in the range of 0 until the number
- * of overloaded alternatives are returned. The secondary choices are used to find a matching method
- * in `extensionMethod` if the first name has the wrong type. We thereby gain a level of insensitivity
- * of how overloaded types are ordered between phases and picklings.
- */
- private def extensionNames(imeth: Symbol)(implicit ctx: Context): Stream[Name] = {
- val decl = imeth.owner.info.decl(imeth.name)
-
- /** No longer needed for Dotty, as we are more disciplined with scopes now.
- // Bridge generation is done at phase `erasure`, but new scopes are only generated
- // for the phase after that. So bridges are visible in earlier phases.
- //
- // `info.member(imeth.name)` filters these out, but we need to use `decl`
- // to restrict ourselves to members defined in the current class, so we
- // must do the filtering here.
- val declTypeNoBridge = decl.filter(sym => !sym.isBridge).tpe
- */
- decl match {
- case decl: MultiDenotation =>
- val alts = decl.alternatives
- val index = alts indexOf imeth.denot
- assert(index >= 0, alts + " does not contain " + imeth)
- def altName(index: Int) = (imeth.name + "$extension" + index).toTermName
- altName(index) #:: ((0 until alts.length).toStream filter (index != _) map altName)
- case decl =>
- assert(decl.exists, imeth.name + " not found in " + imeth.owner + "'s decls: " + imeth.owner.info.decls)
- Stream((imeth.name + "$extension").toTermName)
- }
- }
-
- /** Return the extension method that corresponds to given instance method `meth`. */
- def extensionMethod(imeth: Symbol)(implicit ctx: Context): TermSymbol =
- ctx.atPhase(thisTransformer.next) { implicit ctx =>
- // FIXME use toStatic instead?
- val companionInfo = imeth.owner.companionModule.info
- val candidates = extensionNames(imeth) map (companionInfo.decl(_).symbol) filter (_.exists)
- val matching = candidates filter (c => memberSignature(c.info) == imeth.signature)
- assert(matching.nonEmpty,
- sm"""|no extension method found for:
- |
- | $imeth:${imeth.info.show} with signature ${imeth.signature}
- |
- | Candidates:
- |
- | ${candidates.map(c => c.name + ":" + c.info.show).mkString("\n")}
- |
- | Candidates (signatures normalized):
- |
- | ${candidates.map(c => c.name + ":" + c.info.signature + ":" + memberSignature(c.info)).mkString("\n")}
- |
- | Eligible Names: ${extensionNames(imeth).mkString(",")}""")
- matching.head.asTerm
- }
-
private def createExtensionMethod(imeth: Symbol, staticClass: Symbol)(implicit ctx: Context): TermSymbol = {
assert(ctx.phase == thisTransformer.next)
val extensionName = extensionNames(imeth).head.toTermName
@@ -184,3 +151,64 @@ class ExtensionMethods extends MiniPhaseTransform with DenotTransformer with Ful
} else tree
}
}
+
+object ExtensionMethods {
+ /** Generate stream of possible names for the extension version of given instance method `imeth`.
+ * If the method is not overloaded, this stream consists of just "imeth$extension".
+ * If the method is overloaded, the stream has as first element "imeth$extenionX", where X is the
+ * index of imeth in the sequence of overloaded alternatives with the same name. This choice will
+ * always be picked as the name of the generated extension method.
+ * After this first choice, all other possible indices in the range of 0 until the number
+ * of overloaded alternatives are returned. The secondary choices are used to find a matching method
+ * in `extensionMethod` if the first name has the wrong type. We thereby gain a level of insensitivity
+ * of how overloaded types are ordered between phases and picklings.
+ */
+ private def extensionNames(imeth: Symbol)(implicit ctx: Context): Stream[Name] = {
+ val decl = imeth.owner.info.decl(imeth.name)
+
+ /** No longer needed for Dotty, as we are more disciplined with scopes now.
+ // Bridge generation is done at phase `erasure`, but new scopes are only generated
+ // for the phase after that. So bridges are visible in earlier phases.
+ //
+ // `info.member(imeth.name)` filters these out, but we need to use `decl`
+ // to restrict ourselves to members defined in the current class, so we
+ // must do the filtering here.
+ val declTypeNoBridge = decl.filter(sym => !sym.isBridge).tpe
+ */
+ decl match {
+ case decl: MultiDenotation =>
+ val alts = decl.alternatives
+ val index = alts indexOf imeth.denot
+ assert(index >= 0, alts + " does not contain " + imeth)
+ def altName(index: Int) = (imeth.name + "$extension" + index).toTermName
+ altName(index) #:: ((0 until alts.length).toStream filter (index != _) map altName)
+ case decl =>
+ assert(decl.exists, imeth.name + " not found in " + imeth.owner + "'s decls: " + imeth.owner.info.decls)
+ Stream((imeth.name + "$extension").toTermName)
+ }
+ }
+
+ /** Return the extension method that corresponds to given instance method `meth`. */
+ def extensionMethod(imeth: Symbol)(implicit ctx: Context): TermSymbol =
+ ctx.atPhase(ctx.extensionMethodsPhase.next) { implicit ctx =>
+ // FIXME use toStatic instead?
+ val companionInfo = imeth.owner.companionModule.info
+ val candidates = extensionNames(imeth) map (companionInfo.decl(_).symbol) filter (_.exists)
+ val matching = candidates filter (c => FullParameterization.memberSignature(c.info) == imeth.signature)
+ assert(matching.nonEmpty,
+ sm"""|no extension method found for:
+ |
+ | $imeth:${imeth.info.show} with signature ${imeth.signature}
+ |
+ | Candidates:
+ |
+ | ${candidates.map(c => c.name + ":" + c.info.show).mkString("\n")}
+ |
+ | Candidates (signatures normalized):
+ |
+ | ${candidates.map(c => c.name + ":" + c.info.signature + ":" + FullParameterization.memberSignature(c.info)).mkString("\n")}
+ |
+ | Eligible Names: ${extensionNames(imeth).mkString(",")}""")
+ matching.head.asTerm
+ }
+}
diff --git a/src/dotty/tools/dotc/transform/FullParameterization.scala b/src/dotty/tools/dotc/transform/FullParameterization.scala
index f46942fb3..d402c2e7f 100644
--- a/src/dotty/tools/dotc/transform/FullParameterization.scala
+++ b/src/dotty/tools/dotc/transform/FullParameterization.scala
@@ -52,6 +52,7 @@ import ast.Trees._
trait FullParameterization {
import tpd._
+ import FullParameterization._
/** If references to original symbol `referenced` from within fully parameterized method
* `derived` should be rewired to some fully parameterized method, the rewiring target symbol,
@@ -124,15 +125,6 @@ trait FullParameterization {
}
}
- /** Assuming `info` is a result of a `fullyParameterizedType` call, the signature of the
- * original method type `X` such that `info = fullyParameterizedType(X, ...)`.
- */
- def memberSignature(info: Type)(implicit ctx: Context): Signature = info match {
- case info: PolyType => memberSignature(info.resultType)
- case info @ MethodType(nme.SELF :: Nil, _) => info.resultType.ensureMethodic.signature
- case _ => Signature.NotAMethod
- }
-
/** The type parameters (skolems) of the method definition `originalDef`,
* followed by the class parameters of its enclosing class.
*/
@@ -230,3 +222,14 @@ trait FullParameterization {
.appliedToArgss(originalDef.vparamss.nestedMap(vparam => ref(vparam.symbol)))
.withPos(originalDef.rhs.pos)
}
+
+object FullParameterization {
+ /** Assuming `info` is a result of a `fullyParameterizedType` call, the signature of the
+ * original method type `X` such that `info = fullyParameterizedType(X, ...)`.
+ */
+ def memberSignature(info: Type)(implicit ctx: Context): Signature = info match {
+ case info: PolyType => memberSignature(info.resultType)
+ case info @ MethodType(nme.SELF :: Nil, _) => info.resultType.ensureMethodic.signature
+ case _ => Signature.NotAMethod
+ }
+}
diff --git a/src/dotty/tools/dotc/transform/Getters.scala b/src/dotty/tools/dotc/transform/Getters.scala
index 918a92a04..e1c35feba 100644
--- a/src/dotty/tools/dotc/transform/Getters.scala
+++ b/src/dotty/tools/dotc/transform/Getters.scala
@@ -16,15 +16,21 @@ import Decorators._
/** Performs the following rewritings for fields of a class:
*
* <mods> val x: T = e
- * --> <mods> <stable> def x: T = e
+ * --> <mods> <stable> <accessor> def x: T = e
* <mods> var x: T = e
- * --> <mods> def x: T = e
+ * --> <mods> <accessor> def x: T = e
*
* <mods> val x: T
- * --> <mods> <stable> def x: T
+ * --> <mods> <stable> <accessor> def x: T
+ *
+ * <mods> lazy val x: T = e
+ * --> <mods> <accessor> lazy def x: T =e
*
* <mods> var x: T
- * --> <mods> def x: T
+ * --> <mods> <accessor> def x: T
+ *
+ * <mods> non-static <module> val x$ = e
+ * --> <mods> <module> <accessor> def x$ = e
*
* Omitted from the rewritings are
*
@@ -47,10 +53,10 @@ class Getters extends MiniPhaseTransform with SymTransformer { thisTransform =>
override def transformSym(d: SymDenotation)(implicit ctx: Context): SymDenotation = {
def noGetterNeeded =
d.is(NoGetterNeeded) ||
- d.initial.asInstanceOf[SymDenotation].is(PrivateLocal) && !d.owner.is(Trait) ||
+ d.initial.asInstanceOf[SymDenotation].is(PrivateLocal) && !d.owner.is(Trait) && !d.is(Flags.Lazy) ||
d.is(Module) && d.isStatic ||
d.isSelfSym
- if (d.isTerm && d.owner.isClass && d.info.isValueType && !noGetterNeeded) {
+ if (d.isTerm && (d.is(Lazy) || d.owner.isClass) && d.info.isValueType && !noGetterNeeded) {
val maybeStable = if (d.isStable) Stable else EmptyFlags
d.copySymDenotation(
initFlags = d.flags | maybeStable | AccessorCreationFlags,
@@ -58,7 +64,7 @@ class Getters extends MiniPhaseTransform with SymTransformer { thisTransform =>
}
else d
}
- private val NoGetterNeeded = Method | Param | JavaDefined | JavaStatic | Lazy
+ private val NoGetterNeeded = Method | Param | JavaDefined | JavaStatic
override def transformValDef(tree: ValDef)(implicit ctx: Context, info: TransformerInfo): Tree =
if (tree.symbol is Method) DefDef(tree.symbol.asTerm, tree.rhs) else tree
diff --git a/src/dotty/tools/dotc/transform/InterceptedMethods.scala b/src/dotty/tools/dotc/transform/InterceptedMethods.scala
index 725910949..ff354a54c 100644
--- a/src/dotty/tools/dotc/transform/InterceptedMethods.scala
+++ b/src/dotty/tools/dotc/transform/InterceptedMethods.scala
@@ -27,6 +27,7 @@ import scala.collection.mutable.ListBuffer
import dotty.tools.dotc.core.Denotations.SingleDenotation
import dotty.tools.dotc.core.SymDenotations.SymDenotation
import StdNames._
+import Phases.Phase
/** Replace member references as follows:
*
diff --git a/src/dotty/tools/dotc/transform/LazyVals.scala b/src/dotty/tools/dotc/transform/LazyVals.scala
index a28102d7b..62dc2f085 100644
--- a/src/dotty/tools/dotc/transform/LazyVals.scala
+++ b/src/dotty/tools/dotc/transform/LazyVals.scala
@@ -1,6 +1,8 @@
package dotty.tools.dotc
package transform
+import dotty.tools.dotc.typer.Mode
+
import scala.collection.mutable
import core._
import Contexts._
@@ -14,54 +16,46 @@ import dotty.tools.dotc.ast.{untpd, tpd}
import dotty.tools.dotc.core.Constants.Constant
import dotty.tools.dotc.core.Types.{ExprType, NoType, MethodType}
import dotty.tools.dotc.core.Names.Name
-import dotty.runtime.{LazyVals => RLazyVals} // dotty deviation
import SymUtils._
import scala.collection.mutable.ListBuffer
import dotty.tools.dotc.core.Denotations.SingleDenotation
import dotty.tools.dotc.core.SymDenotations.SymDenotation
import dotty.tools.dotc.core.DenotTransformers.{SymTransformer, IdentityDenotTransformer, DenotTransformer}
+import Erasure.Boxing.adaptToType
-class LazyVals extends MiniPhaseTransform with SymTransformer {
+class LazyVals extends MiniPhaseTransform with IdentityDenotTransformer {
+ import LazyVals._
import tpd._
-
- def transformSym(d: SymDenotation)(implicit ctx: Context): SymDenotation = {
- if (d is(Flags.Lazy, butNot = Flags.ModuleVal | Flags.Method)) {
- // Method flag is set on lazy vals coming from Unpickler. They are already methods and shouldn't be transformed twice
- d.copySymDenotation(
- initFlags = d.flags | Flags.Method,
- info = ExprType(d.info))
- }
- else d
- }
-
def transformer = new LazyVals
val containerFlags = Flags.Synthetic | Flags.Mutable | Flags.Lazy
- val initFlags = Flags.Synthetic | Flags.Method
+ val initFlags = Flags.Synthetic | Flags.Method
+
+ val containerFlagsMask = Flags.Method | Flags.Lazy | Flags.Accessor | Flags.Module
/** this map contains mutable state of transformation: OffsetDefs to be appended to companion object definitions,
* and number of bits currently used */
class OffsetInfo(var defs: List[Tree], var ord:Int)
- val appendOffsetDefs = mutable.Map.empty[Name, OffsetInfo]
+ val appendOffsetDefs = mutable.Map.empty[Symbol, OffsetInfo]
override def phaseName: String = "LazyVals"
- /** List of names of phases that should have finished processing of tree
- * before this phase starts processing same tree */
- // override def ensureAfter: Set[String] = Set("mixin")
+ /** List of names of phases that should have finished processing of tree
+ * before this phase starts processing same tree */
+ override def runsAfter = Set(classOf[Mixin])
- override def transformValDef(tree: ValDef)(implicit ctx: Context, info: TransformerInfo): Tree = {
- if (!(tree.mods is Flags.Lazy) || (tree.mods is Flags.ModuleVal)) tree
+ override def transformDefDef(tree: DefDef)(implicit ctx: Context, info: TransformerInfo): Tree = {
+ if (!(tree.symbol is Flags.Lazy) || tree.symbol.owner.is(Flags.Trait)) tree
else {
- val isField = tree.symbol.owner.isClass
+ val isField = tree.symbol.owner.isClass
- if (isField) {
- if (tree.symbol.isVolatile) transformFieldValDefVolatile(tree)
- else transformFieldValDefNonVolatile(tree)
- }
- else transformLocalValDef(tree)
+ if (isField) {
+ if (tree.symbol.isVolatile || tree.symbol.is(Flags.Module)) transformMemberDefVolatile(tree)
+ else transformMemberDefNonVolatile(tree)
+ }
+ else transformLocalDef(tree)
}
}
@@ -70,11 +64,12 @@ class LazyVals extends MiniPhaseTransform with SymTransformer {
override def transformTypeDef(tree: TypeDef)(implicit ctx: Context, info: TransformerInfo): Tree = {
if (!tree.symbol.isClass) tree
else {
- appendOffsetDefs.get(tree.symbol.name) match {
+ appendOffsetDefs.get(tree.symbol) match {
case None => tree
case Some(data) =>
val template = tree.rhs.asInstanceOf[Template]
- ClassDef(tree.symbol.asClass, template.constr, data.defs.mapConserve(transformFollowingDeep) ::: template.body)
+ val newTemplate = cpy.Template(template)(body = data.defs ::: template.body)
+ cpy.TypeDef(tree)(rhs = newTemplate) //(ctx.withMode(Mode.FutureDefsOK))
}
}
}
@@ -82,12 +77,11 @@ class LazyVals extends MiniPhaseTransform with SymTransformer {
* with a LazyHolder from
* dotty.runtime(eg dotty.runtime.LazyInt)
*/
- def transformLocalValDef(x: ValDef)(implicit ctx: Context) = x match {
- case ValDef(name, tpt, _) =>
+ def transformLocalDef(x: DefDef)(implicit ctx: Context) = {
val valueInitter = x.rhs
- val holderName = ctx.freshName(name.toString + StdNames.nme.LAZY_LOCAL).toTermName
- val initName = ctx.freshName(name.toString + StdNames.nme.LAZY_LOCAL_INIT).toTermName
- val tpe = x.tpe.widen
+ val holderName = ctx.freshName(x.name ++ StdNames.nme.LAZY_LOCAL).toTermName
+ val initName = ctx.freshName(x.name ++ StdNames.nme.LAZY_LOCAL_INIT).toTermName
+ val tpe = x.tpe.widen.resultType.widen
val holderType =
if (tpe isRef defn.IntClass) "LazyInt"
@@ -105,17 +99,19 @@ class LazyVals extends MiniPhaseTransform with SymTransformer {
val holderSymbol = ctx.newSymbol(x.symbol.owner, holderName, containerFlags, holderImpl.typeRef, coord = x.pos)
val initSymbol = ctx.newSymbol(x.symbol.owner, initName, initFlags, MethodType(Nil, tpe), coord = x.pos)
- val result = ref(holderSymbol).select("value".toTermName)
- val flag = ref(holderSymbol).select("initialized".toTermName)
+ val result = ref(holderSymbol).select(lazyNme.value)
+ val flag = ref(holderSymbol).select(lazyNme.initialized)
val initer = valueInitter.changeOwner(x.symbol, initSymbol)
val initBody =
- ref(holderSymbol).select(defn.Object_synchronized).appliedToType(tpe).appliedTo(
- mkNonThreadSafeDef(result, flag, initer).ensureConforms(tpe))
+ adaptToType(
+ ref(holderSymbol).select(defn.Object_synchronized).appliedTo(
+ adaptToType(mkNonThreadSafeDef(result, flag, initer), defn.ObjectType)),
+ tpe)
val initTree = DefDef(initSymbol, initBody)
val holderTree = ValDef(holderSymbol, New(holderImpl.typeRef, List()))
val methodBody = {
tpd.If(flag, EmptyTree, ref(initSymbol))
- result.ensureConforms(tpe)
+ result.ensureApplied.ensureConforms(tpe)
}
val methodTree = DefDef(x.symbol.asTerm, methodBody)
ctx.debuglog(s"found a lazy val ${x.show},\n rewrote with ${holderTree.show}")
@@ -124,7 +120,16 @@ class LazyVals extends MiniPhaseTransform with SymTransformer {
override def transformStats(trees: List[tpd.Tree])(implicit ctx: Context, info: TransformerInfo): List[tpd.Tree] = {
- val (holders, stats) = trees.partition { _.symbol.flags == containerFlags}
+ // backend requires field usage to be after field definition
+ // need to bring containers to start of method
+ val (holders, stats) =
+ atGroupEnd { implicit ctx: Context =>
+ trees.partition {
+ _.symbol.flags.&~(Flags.Touched) == containerFlags
+ // Filtering out Flags.Touched is not required currently, as there are no LazyTypes involved here
+ // but just to be more safe
+ }
+ }
holders:::stats
}
@@ -140,10 +145,10 @@ class LazyVals extends MiniPhaseTransform with SymTransformer {
*/
def mkNonThreadSafeDef(target: Tree, flag: Tree, rhs: Tree)(implicit ctx: Context) = {
- val setFlag = Assign(flag, Literal(Constants.Constant(true)))
- val setTarget = Assign(target, rhs)
- val init = Block(List(setFlag, setTarget), target)
- If(flag, target, init)
+ val setFlag = flag.becomes(Literal(Constants.Constant(true)))
+ val setTarget = target.becomes(rhs)
+ val init = Block(List(setFlag, setTarget), target.ensureApplied)
+ If(flag.ensureApplied, target.ensureApplied, init)
}
/** Create non-threadsafe lazy accessor for not-nullable types equivalent to such code
@@ -157,34 +162,36 @@ class LazyVals extends MiniPhaseTransform with SymTransformer {
def mkDefNonThreadSafeNonNullable(target: Symbol, rhs: Tree)(implicit ctx: Context) = {
val cond = ref(target).select(nme.eq).appliedTo(Literal(Constant(null)))
val exp = ref(target)
- val setTarget = Assign(exp, rhs)
+ val setTarget = exp.becomes(rhs)
val init = Block(List(setTarget), exp)
If(cond, init, exp)
}
- def transformFieldValDefNonVolatile(x: ValDef)(implicit ctx: Context) = x match {
- case ValDef(name, tpt, _) if (x.mods is Flags.Lazy) =>
+ def transformMemberDefNonVolatile(x: DefDef)(implicit ctx: Context) = {
val claz = x.symbol.owner.asClass
- val tpe = x.tpe.widen
+ val tpe = x.tpe.widen.resultType.widen
assert(!(x.mods is Flags.Mutable))
- val containerName = ctx.freshName(name.toString + StdNames.nme.LAZY_LOCAL).toTermName
- val containerSymbol = ctx.newSymbol(claz, containerName, (x.mods &~ Flags.Lazy | containerFlags).flags, tpe, coord = x.symbol.coord).enteredAfter(this)
+ val containerName = ctx.freshName(x.name ++ StdNames.nme.LAZY_LOCAL).toTermName
+ val containerSymbol = ctx.newSymbol(claz, containerName,
+ x.symbol.flags &~ containerFlagsMask | containerFlags | Flags.Private,
+ tpe, coord = x.symbol.coord
+ ).entered
val containerTree = ValDef(containerSymbol, initValue(tpe))
- if (x.tpe.isNotNull && tpe <:< defn.AnyRefType) { // can use 'null' value instead of flag
+ if (x.tpe.isNotNull && tpe <:< defn.ObjectType) { // can use 'null' value instead of flag
val slowPath = DefDef(x.symbol.asTerm, mkDefNonThreadSafeNonNullable(containerSymbol, x.rhs))
Thicket(List(containerTree, slowPath))
}
else {
- val flagName = ctx.freshName(name.toString + StdNames.nme.BITMAP_PREFIX).toTermName
- val flagSymbol = ctx.newSymbol(x.symbol.owner, flagName, containerFlags, defn.BooleanType)
+ val flagName = ctx.freshName(x.name ++ StdNames.nme.BITMAP_PREFIX).toTermName
+ val flagSymbol = ctx.newSymbol(x.symbol.owner, flagName, containerFlags | Flags.Private, defn.BooleanType).entered
val flag = ValDef(flagSymbol, Literal(Constants.Constant(false)))
val slowPath = DefDef(x.symbol.asTerm, mkNonThreadSafeDef(ref(containerSymbol), ref(flagSymbol), x.rhs))
Thicket(List(containerTree, flag, slowPath))
}
}
- /** Create non-threadsafe lazy accessor equivalent to such code
+ /** Create a threadsafe lazy accessor equivalent to such code
*
* def methodSymbol(): Int = {
* val result: Int = 0
@@ -215,39 +222,38 @@ class LazyVals extends MiniPhaseTransform with SymTransformer {
* }
* result
* }
- * FIXME: Don't use strings with toTermName, use predefined names instead.
*/
def mkThreadSafeDef(methodSymbol: TermSymbol, claz: ClassSymbol, ord: Int, target: Symbol, rhs: Tree, tp: Types.Type, offset: Tree, getFlag: Tree, stateMask: Tree, casFlag: Tree, setFlagState: Tree, waitOnLock: Tree)(implicit ctx: Context) = {
val initState = Literal(Constants.Constant(0))
val computeState = Literal(Constants.Constant(1))
val notifyState = Literal(Constants.Constant(2))
val computedState = Literal(Constants.Constant(3))
- val flagSymbol = ctx.newSymbol(methodSymbol, "flag".toTermName, containerFlags, defn.LongType)
+ val flagSymbol = ctx.newSymbol(methodSymbol, lazyNme.flag, containerFlags, defn.LongType)
val flagDef = ValDef(flagSymbol, Literal(Constant(0L)))
val thiz = This(claz)(ctx.fresh.setOwner(claz))
- val resultSymbol = ctx.newSymbol(methodSymbol, "result".toTermName, containerFlags, tp)
+ val resultSymbol = ctx.newSymbol(methodSymbol, lazyNme.result, containerFlags, tp)
val resultDef = ValDef(resultSymbol, initValue(tp))
- val retrySymbol = ctx.newSymbol(methodSymbol, "retry".toTermName, containerFlags, defn.BooleanType)
+ val retrySymbol = ctx.newSymbol(methodSymbol, lazyNme.retry, containerFlags, defn.BooleanType)
val retryDef = ValDef(retrySymbol, Literal(Constants.Constant(true)))
val whileCond = ref(retrySymbol)
val compute = {
- val handlerSymbol = ctx.newSymbol(methodSymbol, "$anonfun".toTermName, Flags.Synthetic,
- MethodType(List("x$1".toTermName), List(defn.ThrowableType), defn.IntType))
+ val handlerSymbol = ctx.newSymbol(methodSymbol, nme.ANON_FUN, Flags.Synthetic,
+ MethodType(List(nme.x_1), List(defn.ThrowableType), defn.IntType))
val caseSymbol = ctx.newSymbol(methodSymbol, nme.DEFAULT_EXCEPTION_NAME, Flags.Synthetic, defn.ThrowableType)
val complete = setFlagState.appliedTo(thiz, offset, initState, Literal(Constant(ord)))
val handler = CaseDef(Bind(caseSymbol, ref(caseSymbol)), EmptyTree,
Block(List(complete), Throw(ref(caseSymbol))
))
- val compute = Assign(ref(resultSymbol), rhs)
+ val compute = ref(resultSymbol).becomes(rhs)
val tr = Try(compute, List(handler), EmptyTree)
- val assign = Assign(ref(target), ref(resultSymbol))
- val noRetry = Assign(ref(retrySymbol), Literal(Constants.Constant(false)))
+ val assign = ref(target).becomes(ref(resultSymbol))
+ val noRetry = ref(retrySymbol).becomes(Literal(Constants.Constant(false)))
val body = If(casFlag.appliedTo(thiz, offset, ref(flagSymbol), computeState, Literal(Constant(ord))),
Block(tr :: assign :: complete :: noRetry :: Nil, Literal(Constant(()))),
Literal(Constant(())))
@@ -266,38 +272,39 @@ class LazyVals extends MiniPhaseTransform with SymTransformer {
}
val computed = {
- val noRetry = Assign(ref(retrySymbol), Literal(Constants.Constant(false)))
- val result = Assign(ref(resultSymbol), ref(target))
+ val noRetry = ref(retrySymbol).becomes(Literal(Constants.Constant(false)))
+ val result = ref(resultSymbol).becomes(ref(target))
val body = Block(noRetry :: result :: Nil, Literal(Constant(())))
CaseDef(computedState, EmptyTree, body)
}
+ val default = CaseDef(untpd.Ident(nme.WILDCARD).withType(defn.LongType), EmptyTree, Literal(Constant(())))
+
val cases = Match(stateMask.appliedTo(ref(flagSymbol), Literal(Constant(ord))),
- List(compute, waitFirst, waitSecond, computed)) //todo: annotate with @switch
+ List(compute, waitFirst, waitSecond, computed, default)) //todo: annotate with @switch
- val whileBody = Block(List(Assign(ref(flagSymbol), getFlag.appliedTo(thiz, offset))), cases)
- val cycle = untpd.WhileDo(whileCond, whileBody).withTypeUnchecked(defn.UnitType)
+ val whileBody = List(ref(flagSymbol).becomes(getFlag.appliedTo(thiz, offset)), cases)
+ val cycle = WhileDo(methodSymbol, whileCond, whileBody)
DefDef(methodSymbol, Block(resultDef :: retryDef :: flagDef :: cycle :: Nil, ref(resultSymbol)))
}
- def transformFieldValDefVolatile(x: ValDef)(implicit ctx: Context) = x match {
- case ValDef(name, tpt, _) if (x.mods is Flags.Lazy) =>
+ def transformMemberDefVolatile(x: DefDef)(implicit ctx: Context) = {
assert(!(x.mods is Flags.Mutable))
- val tpe = x.tpe.widen
+ val tpe = x.tpe.widen.resultType.widen
val claz = x.symbol.owner.asClass
- val thiz = This(claz)(ctx.fresh.setOwner(claz))
+ val thizClass = Literal(Constant(claz.info))
val companion = claz.companionModule
val helperModule = ctx.requiredModule("dotty.runtime.LazyVals")
- val getOffset = Select(ref(helperModule), RLazyVals.Names.getOffset.toTermName)
+ val getOffset = Select(ref(helperModule), lazyNme.RLazyVals.getOffset)
var offsetSymbol: TermSymbol = null
var flag: Tree = EmptyTree
var ord = 0
// compute or create appropriate offsetSymol, bitmap and bits used by current ValDef
- appendOffsetDefs.get(companion.name.moduleClassName) match {
+ appendOffsetDefs.get(companion.moduleClass) match {
case Some(info) =>
- val flagsPerLong = 64 / RLazyVals.BITS_PER_LAZY_VAL
+ val flagsPerLong = 64 / dotty.runtime.LazyVals.BITS_PER_LAZY_VAL
info.ord += 1
ord = info.ord % flagsPerLong
val id = info.ord / flagsPerLong
@@ -310,7 +317,7 @@ class LazyVals extends MiniPhaseTransform with SymTransformer {
val flagName = (StdNames.nme.BITMAP_PREFIX + id.toString).toTermName
val flagSymbol = ctx.newSymbol(claz, flagName, containerFlags, defn.LongType).enteredAfter(this)
flag = ValDef(flagSymbol, Literal(Constants.Constant(0L)))
- val offsetTree = ValDef(offsetSymbol, getOffset.appliedTo(thiz, Literal(Constant(flagName.toString))))
+ val offsetTree = ValDef(offsetSymbol, getOffset.appliedTo(thizClass, Literal(Constant(flagName.toString))))
info.defs = offsetTree :: info.defs
}
@@ -319,20 +326,20 @@ class LazyVals extends MiniPhaseTransform with SymTransformer {
val flagName = (StdNames.nme.BITMAP_PREFIX + "0").toTermName
val flagSymbol = ctx.newSymbol(claz, flagName, containerFlags, defn.LongType).enteredAfter(this)
flag = ValDef(flagSymbol, Literal(Constants.Constant(0L)))
- val offsetTree = ValDef(offsetSymbol, getOffset.appliedTo(thiz, Literal(Constant(flagName.toString))))
- appendOffsetDefs += (companion.name.moduleClassName -> new OffsetInfo(List(offsetTree), ord))
+ val offsetTree = ValDef(offsetSymbol, getOffset.appliedTo(thizClass, Literal(Constant(flagName.toString))))
+ appendOffsetDefs += (companion.moduleClass -> new OffsetInfo(List(offsetTree), ord))
}
- val containerName = ctx.freshName(name.toString + StdNames.nme.LAZY_LOCAL).toTermName
- val containerSymbol = ctx.newSymbol(claz, containerName, (x.mods &~ Flags.Lazy | containerFlags).flags, tpe, coord = x.symbol.coord).enteredAfter(this)
+ val containerName = ctx.freshName(x.name ++ StdNames.nme.LAZY_LOCAL).toTermName
+ val containerSymbol = ctx.newSymbol(claz, containerName, (x.mods &~ containerFlagsMask | containerFlags).flags, tpe, coord = x.symbol.coord).entered
val containerTree = ValDef(containerSymbol, initValue(tpe))
- val offset = Select(ref(companion), offsetSymbol.name)
- val getFlag = Select(ref(helperModule), RLazyVals.Names.get.toTermName)
- val setFlag = Select(ref(helperModule), RLazyVals.Names.setFlag.toTermName)
- val wait = Select(ref(helperModule), RLazyVals.Names.wait4Notification.toTermName)
- val state = Select(ref(helperModule), RLazyVals.Names.state.toTermName)
- val cas = Select(ref(helperModule), RLazyVals.Names.cas.toTermName)
+ val offset = ref(companion).ensureApplied.select(offsetSymbol)
+ val getFlag = Select(ref(helperModule), lazyNme.RLazyVals.get)
+ val setFlag = Select(ref(helperModule), lazyNme.RLazyVals.setFlag)
+ val wait = Select(ref(helperModule), lazyNme.RLazyVals.wait4Notification)
+ val state = Select(ref(helperModule), lazyNme.RLazyVals.state)
+ val cas = Select(ref(helperModule), lazyNme.RLazyVals.cas)
val accessor = mkThreadSafeDef(x.symbol.asTerm, claz, ord, containerSymbol, x.rhs, tpe, offset, getFlag, state, cas, setFlag, wait)
if (flag eq EmptyTree)
@@ -341,5 +348,24 @@ class LazyVals extends MiniPhaseTransform with SymTransformer {
}
}
+object LazyVals {
+ object lazyNme {
+ object RLazyVals {
+ import dotty.runtime.LazyVals._
+ val get = Names.get.toTermName
+ val setFlag = Names.setFlag.toTermName
+ val wait4Notification = Names.wait4Notification.toTermName
+ val state = Names.state.toTermName
+ val cas = Names.cas.toTermName
+ val getOffset = Names.getOffset.toTermName
+ }
+ val flag = "flag".toTermName
+ val result = "result".toTermName
+ val value = "value".toTermName
+ val initialized = "initialized".toTermName
+ val retry = "retry".toTermName
+ }
+}
+
diff --git a/src/dotty/tools/dotc/transform/Memoize.scala b/src/dotty/tools/dotc/transform/Memoize.scala
index 75a195032..d96a52868 100644
--- a/src/dotty/tools/dotc/transform/Memoize.scala
+++ b/src/dotty/tools/dotc/transform/Memoize.scala
@@ -48,38 +48,26 @@ import Decorators._
case _ =>
}
- override def prepareForDefDef(tree: DefDef)(implicit ctx: Context) = {
- val sym = tree.symbol
- if (sym.isGetter && !sym.is(NoFieldNeeded)) {
- // allocate field early so that initializer has the right owner for subsequeny phases in
- // the group.
- val maybeMutable = if (sym is Stable) EmptyFlags else Mutable
- val field = ctx.newSymbol(
- owner = ctx.owner,
- name = sym.name.asTermName.fieldName,
- flags = Private | maybeMutable,
- info = sym.info.resultType,
- coord = tree.pos).enteredAfter(thisTransform)
- tree.rhs.changeOwnerAfter(sym, field, thisTransform)
- }
- this
- }
-
override def transformDefDef(tree: DefDef)(implicit ctx: Context, info: TransformerInfo): Tree = {
val sym = tree.symbol
- def field = {
- val field = sym.field.asTerm
- assert(field.exists, i"no field for ${sym.showLocated} in ${sym.owner.info.decls.toList.map{_.showDcl}}%; %")
- field
- }
+
+ def newField = ctx.newSymbol(
+ owner = ctx.owner,
+ name = sym.name.asTermName.fieldName,
+ flags = Private | (if (sym is Stable) EmptyFlags else Mutable),
+ info = sym.info.resultType,
+ coord = tree.pos).enteredAfter(thisTransform)
+
+ lazy val field = sym.field.orElse(newField).asTerm
if (sym.is(Accessor, butNot = NoFieldNeeded))
if (sym.isGetter) {
+ tree.rhs.changeOwnerAfter(sym, field, thisTransform)
val fieldDef = transformFollowing(ValDef(field, tree.rhs))
val getterDef = cpy.DefDef(tree)(rhs = transformFollowingDeep(ref(field)))
Thicket(fieldDef, getterDef)
}
else if (sym.isSetter) {
- if (!sym.is(ParamAccessor)) { val Literal(Constant(())) = tree.rhs }
+ if (!sym.is(ParamAccessor)) { val Literal(Constant(())) = tree.rhs } // this is intended as an assertion
val initializer = Assign(ref(field), ref(tree.vparamss.head.head.symbol))
cpy.DefDef(tree)(rhs = transformFollowingDeep(initializer))
}
diff --git a/src/dotty/tools/dotc/transform/Mixin.scala b/src/dotty/tools/dotc/transform/Mixin.scala
index 7e307c736..e20468899 100644
--- a/src/dotty/tools/dotc/transform/Mixin.scala
+++ b/src/dotty/tools/dotc/transform/Mixin.scala
@@ -97,7 +97,9 @@ class Mixin extends MiniPhaseTransform with SymTransformer { thisTransform =>
def traitDefs(stats: List[Tree]): List[Tree] = {
val initBuf = new mutable.ListBuffer[Tree]
stats flatMap {
- case stat: DefDef if stat.symbol.isGetter && !stat.rhs.isEmpty =>
+ case stat: DefDef if stat.symbol.isGetter && !stat.rhs.isEmpty && !stat.symbol.is(Flags.Lazy) =>
+ // make initializer that has all effects of previous getter,
+ // replace getter rhs with empty tree.
val vsym = stat.symbol
val isym = initializer(vsym)
val rhs = Block(
@@ -150,7 +152,8 @@ class Mixin extends MiniPhaseTransform with SymTransformer { thisTransform =>
def traitInits(mixin: ClassSymbol): List[Tree] =
for (getter <- mixin.info.decls.filter(getr => getr.isGetter && !wasDeferred(getr)).toList)
yield {
- DefDef(implementation(getter.asTerm), superRef(initializer(getter)).appliedToNone)
+ // transformFollowing call is needed to make memoize & lazy vals run
+ transformFollowing(DefDef(implementation(getter.asTerm), superRef(initializer(getter)).appliedToNone))
}
def setters(mixin: ClassSymbol): List[Tree] =
@@ -163,7 +166,7 @@ class Mixin extends MiniPhaseTransform with SymTransformer { thisTransform =>
if (cls is Trait) traitDefs(impl.body)
else {
val mixInits = mixins.flatMap { mixin =>
- traitInits(mixin) ::: superCallOpt(mixin) ::: setters(mixin)
+ flatten(traitInits(mixin)) ::: superCallOpt(mixin) ::: setters(mixin)
}
superCallOpt(superCls) ::: mixInits ::: impl.body
})
diff --git a/src/dotty/tools/dotc/transform/MixinOps.scala b/src/dotty/tools/dotc/transform/MixinOps.scala
index e6074323a..1dce85eaa 100644
--- a/src/dotty/tools/dotc/transform/MixinOps.scala
+++ b/src/dotty/tools/dotc/transform/MixinOps.scala
@@ -17,7 +17,7 @@ class MixinOps(cls: ClassSymbol, thisTransform: DenotTransformer)(implicit ctx:
member.copy(
owner = cls,
name = member.name.stripScala2LocalSuffix,
- flags = member.flags &~ Deferred &~ Module,
+ flags = member.flags &~ Deferred,
info = cls.thisType.memberInfo(member)).enteredAfter(thisTransform).asTerm
def superRef(target: Symbol, pos: Position = cls.pos): Tree = {
diff --git a/src/dotty/tools/dotc/transform/ParamForwarding.scala b/src/dotty/tools/dotc/transform/ParamForwarding.scala
index 87ecaba07..d017e75a2 100644
--- a/src/dotty/tools/dotc/transform/ParamForwarding.scala
+++ b/src/dotty/tools/dotc/transform/ParamForwarding.scala
@@ -11,7 +11,7 @@ import Contexts._, Types._, Symbols._, Flags._, TypeUtils._, DenotTransformers._
*
* if
* (1) x is forwarded in the supercall to a parameter that's also named `x`
- * (2) the superclass parameter accessor for `x` is accessible from the current class to
+ * (2) the superclass parameter accessor for `x` is accessible from the current class
* change the accessor to
*
* def x: T = super.x.asInstanceOf[T]
diff --git a/src/dotty/tools/dotc/transform/SuperAccessors.scala b/src/dotty/tools/dotc/transform/SuperAccessors.scala
index b111fdb92..a37b8df1f 100644
--- a/src/dotty/tools/dotc/transform/SuperAccessors.scala
+++ b/src/dotty/tools/dotc/transform/SuperAccessors.scala
@@ -48,7 +48,7 @@ class SuperAccessors(thisTransformer: DenotTransformer) {
* of adding accessors. For instance, super calls from these regions
* always have to go through an accessor.
*
- * The `invalidOwner` field, if different from NoSymbol,
+ * The `invalidEnclClass` field, if different from NoSymbol,
* contains the symbol that is not a valid owner.
*/
private var invalidEnclClass: Symbol = NoSymbol
diff --git a/src/dotty/tools/dotc/transform/TreeChecker.scala b/src/dotty/tools/dotc/transform/TreeChecker.scala
index 1661f7576..7c8ba8432 100644
--- a/src/dotty/tools/dotc/transform/TreeChecker.scala
+++ b/src/dotty/tools/dotc/transform/TreeChecker.scala
@@ -50,8 +50,7 @@ class TreeChecker extends Phase with SymTransformer {
def testDuplicate(sym: Symbol, registry: mutable.Map[String, Symbol], typ: String)(implicit ctx: Context) = {
val name = sym.fullName.toString
- if (registry.contains(name))
- if (this.flatClasses || !(sym.isAnonymousFunction || sym.isAnonymousClass || sym.isAnonymousModuleVal))
+ if (this.flatClasses && registry.contains(name))
printError(s"$typ defined twice $sym ${sym.id} ${registry(name).id}")
registry(name) = sym
}
diff --git a/src/dotty/tools/dotc/transform/TreeExtractors.scala b/src/dotty/tools/dotc/transform/TreeExtractors.scala
new file mode 100644
index 000000000..7a5c5df9d
--- /dev/null
+++ b/src/dotty/tools/dotc/transform/TreeExtractors.scala
@@ -0,0 +1,48 @@
+package dotty.tools.dotc
+package transform
+
+import ast.{Trees, tpd}
+import core._, core.Decorators._
+import Contexts._, Flags._, Trees._, Types._, StdNames._, Symbols._
+import ValueClasses._
+
+object TreeExtractors {
+ import tpd._
+
+ /** Match arg1.op(arg2) and extract (arg1, op.symbol, arg2) */
+ object BinaryOp {
+ def unapply(t: Tree)(implicit ctx: Context): Option[(Tree, Symbol, Tree)] = t match {
+ case Apply(sel @ Select(arg1, _), List(arg2)) =>
+ Some((arg1, sel.symbol, arg2))
+ case _ =>
+ None
+ }
+ }
+
+ /** Match new C(args) and extract (C, args) */
+ object NewWithArgs {
+ def unapply(t: Tree)(implicit ctx: Context): Option[(Type, List[Tree])] = t match {
+ case Apply(Select(New(_), nme.CONSTRUCTOR), args) =>
+ Some((t.tpe, args))
+ case _ =>
+ None
+ }
+ }
+
+ /** For an instance v of a value class like:
+ * class V(val underlying: X) extends AnyVal
+ * Match v.underlying() and extract v
+ */
+ object ValueClassUnbox {
+ def unapply(t: Tree)(implicit ctx: Context): Option[Tree] = t match {
+ case Apply(sel @ Select(ref, _), Nil) =>
+ val d = ref.tpe.widenDealias.typeSymbol.denot
+ if (isDerivedValueClass(d) && (sel.symbol eq valueClassUnbox(d.asClass))) {
+ Some(ref)
+ } else
+ None
+ case _ =>
+ None
+ }
+ }
+}
diff --git a/src/dotty/tools/dotc/transform/TypeTestsCasts.scala b/src/dotty/tools/dotc/transform/TypeTestsCasts.scala
index 9d827d3e0..d7fa9feaf 100644
--- a/src/dotty/tools/dotc/transform/TypeTestsCasts.scala
+++ b/src/dotty/tools/dotc/transform/TypeTestsCasts.scala
@@ -14,6 +14,7 @@ import typer.ErrorReporting._
import ast.Trees._
import Erasure.Boxing._
import core.TypeErasure._
+import ValueClasses._
/** This transform normalizes type tests and type casts,
* also replacing type tests with singleton argument type with reference equality check
@@ -90,10 +91,12 @@ trait TypeTestsCasts {
}
else if (argCls.isPrimitiveValueClass)
unbox(qual.ensureConforms(defn.ObjectType), argType)
- else
+ else if (isDerivedValueClass(argCls)) {
+ qual // adaptToType in Erasure will do the necessary type adaptation
+ } else
derivedTree(qual, defn.Any_asInstanceOf, argType)
}
- def erasedArg = erasure(tree.args.head.tpe)
+ def erasedArg = erasure(tree.args.head.tpe, semiEraseVCs = false)
if (sym eq defn.Any_isInstanceOf)
transformIsInstanceOf(qual, erasedArg)
else if (sym eq defn.Any_asInstanceOf)
diff --git a/src/dotty/tools/dotc/transform/VCInline.scala b/src/dotty/tools/dotc/transform/VCInline.scala
new file mode 100644
index 000000000..e7b16f59e
--- /dev/null
+++ b/src/dotty/tools/dotc/transform/VCInline.scala
@@ -0,0 +1,59 @@
+package dotty.tools.dotc
+package transform
+
+import ast.{Trees, tpd}
+import core._, core.Decorators._
+import Contexts._, Trees._, StdNames._, Symbols._
+import DenotTransformers._, TreeTransforms._, Phases.Phase
+import ExtensionMethods._, TreeExtractors._, ValueClasses._
+
+/** This phase inlines calls to methods and fields of value classes.
+ *
+ * For a value class V defined as:
+ * case class V(val underlying: U) extends AnyVal
+ * We replace method calls by calls to the corresponding extension method:
+ * v.foo(args) => V.foo$extension(v.underlying(), args)
+ * And we avoid unnecessary allocations:
+ * new V(u1) == new V(u2) => u1 == u2
+ * (new V(u)).underlying() => u
+ */
+class VCInline extends MiniPhaseTransform with IdentityDenotTransformer {
+ import tpd._
+
+ override def phaseName: String = "vcInline"
+
+ override def runsAfter: Set[Class[_ <: Phase]] = Set(classOf[ElimErasedValueType])
+
+ override def transformApply(tree: Apply)(implicit ctx: Context, info: TransformerInfo): Tree =
+ tree match {
+ // new V(u1) == new V(u2) => u1 == u2
+ // (We don't handle != because it has been eliminated by InterceptedMethods)
+ case BinaryOp(NewWithArgs(tp1, List(u1)), op, NewWithArgs(tp2, List(u2)))
+ if (tp1 eq tp2) && (op eq defn.Any_==) && isDerivedValueClass(tp1.typeSymbol) =>
+ // == is overloaded in primitive classes
+ applyOverloaded(u1, nme.EQ, List(u2), Nil, defn.BooleanType)
+
+ // (new V(u)).underlying() => u
+ case ValueClassUnbox(NewWithArgs(_, List(u))) =>
+ u
+
+ // (new V(u)).foo(args) => V.foo$extension(u, args)
+ // v.foo(args) => V.foo$extension(v.underlying(), args)
+ case Apply(sel @ Select(receiver, _), args) =>
+ val classMeth = sel.symbol
+ if (isMethodWithExtension(classMeth)) {
+ val classSym = receiver.tpe.widenDealias.typeSymbol.asClass
+ val unboxedReceiver = receiver match {
+ case NewWithArgs(_, List(u)) =>
+ u
+ case _ =>
+ receiver.select(valueClassUnbox(classSym)).appliedToNone
+ }
+ val extensionMeth = extensionMethod(classMeth)
+ ref(extensionMeth).appliedToArgs(unboxedReceiver :: args)
+ } else tree
+
+ case _ =>
+ tree
+ }
+}
diff --git a/src/dotty/tools/dotc/transform/ValueClasses.scala b/src/dotty/tools/dotc/transform/ValueClasses.scala
index a7a0db97c..8969b9321 100644
--- a/src/dotty/tools/dotc/transform/ValueClasses.scala
+++ b/src/dotty/tools/dotc/transform/ValueClasses.scala
@@ -13,6 +13,7 @@ import StdNames._
object ValueClasses {
def isDerivedValueClass(d: SymDenotation)(implicit ctx: Context) = {
+ !d.isRefinementClass &&
d.isValueClass &&
(d.initial.symbol ne defn.AnyValClass) && // Compare the initial symbol because AnyVal does not exist after erasure
!d.isPrimitiveValueClass
@@ -34,6 +35,20 @@ object ValueClasses {
.map(_.symbol)
.getOrElse(NoSymbol)
+ /** For a value class `d`, this returns the synthetic cast from the underlying type to
+ * ErasedValueType defined in the companion module. This method is added to the module
+ * and further described in [[ExtensionMethods]].
+ */
+ def u2evt(d: ClassDenotation)(implicit ctx: Context): Symbol =
+ d.linkedClass.info.decl(nme.U2EVT).symbol
+
+ /** For a value class `d`, this returns the synthetic cast from ErasedValueType to the
+ * underlying type defined in the companion module. This method is added to the module
+ * and further described in [[ExtensionMethods]].
+ */
+ def evt2u(d: ClassDenotation)(implicit ctx: Context): Symbol =
+ d.linkedClass.info.decl(nme.EVT2U).symbol
+
/** The unboxed type that underlies a derived value class */
def underlyingOfValueClass(d: ClassDenotation)(implicit ctx: Context): Type =
valueClassUnbox(d).info.resultType
diff --git a/src/dotty/tools/dotc/typer/Applications.scala b/src/dotty/tools/dotc/typer/Applications.scala
index 2ba27d0f4..7a826fee8 100644
--- a/src/dotty/tools/dotc/typer/Applications.scala
+++ b/src/dotty/tools/dotc/typer/Applications.scala
@@ -330,7 +330,7 @@ trait Applications extends Compatibility { self: Typer =>
case arg :: Nil if isVarArg(arg) =>
addTyped(arg, formal)
case _ =>
- val elemFormal = formal.argTypesLo.head
+ val elemFormal = formal.widenExpr.argTypesLo.head
args foreach (addTyped(_, elemFormal))
makeVarArg(args.length, elemFormal)
}
@@ -842,7 +842,10 @@ trait Applications extends Compatibility { self: Typer =>
val tparams = ctx.newTypeParams(alt1.symbol, tp1.paramNames, EmptyFlags, tp1.instantiateBounds)
isAsSpecific(alt1, tp1.instantiate(tparams map (_.typeRef)), alt2, tp2)
case tp1: MethodType =>
- def repeatedToSingle(tp: Type) = if (tp.isRepeatedParam) tp.argTypesHi.head else tp
+ def repeatedToSingle(tp: Type): Type = tp match {
+ case tp @ ExprType(tp1) => tp.derivedExprType(repeatedToSingle(tp1))
+ case _ => if (tp.isRepeatedParam) tp.argTypesHi.head else tp
+ }
isApplicable(alt2, tp1.paramTypes map repeatedToSingle, WildcardType) ||
tp1.paramTypes.isEmpty && tp2.isInstanceOf[MethodOrPoly]
case _ =>
diff --git a/src/dotty/tools/dotc/typer/Typer.scala b/src/dotty/tools/dotc/typer/Typer.scala
index a2b280c6e..b58f48728 100644
--- a/src/dotty/tools/dotc/typer/Typer.scala
+++ b/src/dotty/tools/dotc/typer/Typer.scala
@@ -595,7 +595,7 @@ class Typer extends Namer with TypeAssigner with Applications with Implicits wit
}
}
- def typedClosure(tree: untpd.Closure, pt: Type)(implicit ctx: Context) = track("typedClosure") {
+ def typedClosure(tree: untpd.Closure, pt: Type)(implicit ctx: Context): Tree = track("typedClosure") {
val env1 = tree.env mapconserve (typed(_))
val meth1 = typedUnadapted(tree.meth)
val target =