aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDmitry Petrashko <dark@d-d.me>2014-10-22 21:09:14 +0200
committerDmitry Petrashko <dark@d-d.me>2014-10-22 21:09:14 +0200
commit7eaee332674c16179221d12cba202d430bc3d5a3 (patch)
tree8003cf9372da029809ff4a1650570a0793ed6bab
parent5f05c4be3e986b7a370f566fd4409b29463fb781 (diff)
parentf27010fbfb3b56d1b62e3549b8bb64d6f4dce2cc (diff)
downloaddotty-7eaee332674c16179221d12cba202d430bc3d5a3.tar.gz
dotty-7eaee332674c16179221d12cba202d430bc3d5a3.tar.bz2
dotty-7eaee332674c16179221d12cba202d430bc3d5a3.zip
Merge pull request #189 from dotty-staging/merge
constructors & getters-setters
-rw-r--r--src/dotty/tools/dotc/Compiler.scala19
-rw-r--r--src/dotty/tools/dotc/ElimLocals.scala2
-rw-r--r--src/dotty/tools/dotc/Flatten.scala2
-rw-r--r--src/dotty/tools/dotc/TypeErasure.scala51
-rw-r--r--src/dotty/tools/dotc/ast/Desugar.scala2
-rw-r--r--src/dotty/tools/dotc/ast/TreeTypeMap.scala9
-rw-r--r--src/dotty/tools/dotc/ast/Trees.scala24
-rw-r--r--src/dotty/tools/dotc/ast/tpd.scala50
-rw-r--r--src/dotty/tools/dotc/ast/untpd.scala12
-rw-r--r--src/dotty/tools/dotc/config/ScalaSettings.scala5
-rw-r--r--src/dotty/tools/dotc/core/Contexts.scala2
-rw-r--r--src/dotty/tools/dotc/core/Definitions.scala2
-rw-r--r--src/dotty/tools/dotc/core/Denotations.scala41
-rw-r--r--src/dotty/tools/dotc/core/Flags.scala14
-rw-r--r--src/dotty/tools/dotc/core/NameOps.scala32
-rw-r--r--src/dotty/tools/dotc/core/Phases.scala11
-rw-r--r--src/dotty/tools/dotc/core/Scopes.scala19
-rw-r--r--src/dotty/tools/dotc/core/StdNames.scala13
-rw-r--r--src/dotty/tools/dotc/core/SymDenotations.scala89
-rw-r--r--src/dotty/tools/dotc/core/TypeComparer.scala13
-rw-r--r--src/dotty/tools/dotc/core/Types.scala15
-rw-r--r--src/dotty/tools/dotc/core/pickling/ClassfileParser.scala6
-rw-r--r--src/dotty/tools/dotc/printing/RefinedPrinter.scala33
-rw-r--r--src/dotty/tools/dotc/transform/CapturedVars.scala35
-rw-r--r--src/dotty/tools/dotc/transform/Constructors.scala240
-rw-r--r--src/dotty/tools/dotc/transform/ElimByName.scala25
-rw-r--r--src/dotty/tools/dotc/transform/Erasure.scala16
-rw-r--r--src/dotty/tools/dotc/transform/ExplicitOuter.scala4
-rw-r--r--src/dotty/tools/dotc/transform/GettersSetters.scala118
-rw-r--r--src/dotty/tools/dotc/transform/LambdaLift.scala404
-rw-r--r--src/dotty/tools/dotc/transform/PatternMatcher.scala270
-rw-r--r--src/dotty/tools/dotc/transform/SymUtils.scala42
-rw-r--r--src/dotty/tools/dotc/transform/SyntheticMethods.scala4
-rw-r--r--src/dotty/tools/dotc/transform/TreeChecker.scala8
-rw-r--r--src/dotty/tools/dotc/transform/TypeTestsCasts.scala9
-rw-r--r--src/dotty/tools/dotc/transform/TypeUtils.scala4
-rw-r--r--src/dotty/tools/dotc/typer/Applications.scala98
-rw-r--r--src/dotty/tools/dotc/typer/ErrorReporting.scala6
-rw-r--r--src/dotty/tools/dotc/typer/EtaExpansion.scala14
-rw-r--r--src/dotty/tools/dotc/typer/Namer.scala5
-rw-r--r--src/dotty/tools/dotc/typer/ReTyper.scala3
-rw-r--r--src/dotty/tools/dotc/typer/TypeAssigner.scala15
-rw-r--r--src/dotty/tools/dotc/typer/Typer.scala38
-rw-r--r--src/dotty/tools/dotc/typer/VarianceChecker.scala4
-rw-r--r--src/dotty/tools/dotc/typer/Variances.scala7
-rw-r--r--test/dotc/tests.scala21
-rw-r--r--test/test/CompilerTest.scala8
-rw-r--r--tests/neg/t2660.scala47
-rw-r--r--tests/pending/pos/annot.scala5
-rw-r--r--tests/pending/pos/t1672.scala36
-rw-r--r--tests/pending/pos/vararg-pattern.scala12
-rw-r--r--tests/pos/Fileish.scala53
-rw-r--r--tests/pos/Patterns.scala3
-rw-r--r--tests/pos/array-clone.scala7
-rw-r--r--tests/pos/constrs.scala33
-rw-r--r--tests/pos/functions1.scala6
-rw-r--r--tests/pos/getset.scala23
-rw-r--r--tests/pos/lambdalift.scala46
-rw-r--r--tests/pos/points.scala8
-rw-r--r--tests/pos/synthetics.scala4
-rw-r--r--tests/pos/t2660.scala25
-rw-r--r--tests/pos/t7093.scala27
-rw-r--r--tests/pos/unapply.scala11
63 files changed, 1717 insertions, 493 deletions
diff --git a/src/dotty/tools/dotc/Compiler.scala b/src/dotty/tools/dotc/Compiler.scala
index 258cab368..1aa1cce10 100644
--- a/src/dotty/tools/dotc/Compiler.scala
+++ b/src/dotty/tools/dotc/Compiler.scala
@@ -24,17 +24,6 @@ class Compiler {
* all refs to it would become outdated - they could not be dereferenced in the
* new phase.
*
- * As an example, addGetters would change a field
- *
- * val x: T
- *
- * to a method
- *
- * def x: T
- *
- * but this would affect the signature of `x` (goes from NotAMethod to a method
- * signature). So we can't do this before erasure.
- *
* After erasure, signature changing denot-transformers are OK because erasure
* will make sure that only term refs with fixed SymDenotations survive beyond it. This
* is possible because:
@@ -57,13 +46,15 @@ class Compiler {
new TailRec),
List(new PatternMatcher,
new ExplicitOuter,
- new LazyValsTransform,
+ // new LazyValTranformContext().transformer, // disabled, awaiting fixes
new Splitter),
List(new ElimByName,
new InterceptedMethods,
- new Literalize),
+ new Literalize,
+ new GettersSetters),
List(new Erasure),
- List(new CapturedVars)
+ List(new CapturedVars, new Constructors)/*,
+ List(new LambdaLift)*/
)
var runId = 1
diff --git a/src/dotty/tools/dotc/ElimLocals.scala b/src/dotty/tools/dotc/ElimLocals.scala
index 20afc8eb4..d18ad0288 100644
--- a/src/dotty/tools/dotc/ElimLocals.scala
+++ b/src/dotty/tools/dotc/ElimLocals.scala
@@ -19,4 +19,4 @@ class ElimLocals extends MiniPhaseTransform with SymTransformer { thisTransforme
private def dropLocal(ref: SymDenotation)(implicit ctx: Context) =
if (ref.flags is Local) ref.copySymDenotation(initFlags = ref.flags &~ Local)
else ref
-} \ No newline at end of file
+}
diff --git a/src/dotty/tools/dotc/Flatten.scala b/src/dotty/tools/dotc/Flatten.scala
index 71f669e26..d7ccc1ae4 100644
--- a/src/dotty/tools/dotc/Flatten.scala
+++ b/src/dotty/tools/dotc/Flatten.scala
@@ -12,4 +12,4 @@ class Flatten extends MiniPhaseTransform with SymTransformer { thisTransformer =
override def phaseName = "flatten"
def transformSym(ref: SymDenotation)(implicit ctx: Context) = ???
-} \ No newline at end of file
+}
diff --git a/src/dotty/tools/dotc/TypeErasure.scala b/src/dotty/tools/dotc/TypeErasure.scala
index 1212c9e4d..50aaafc82 100644
--- a/src/dotty/tools/dotc/TypeErasure.scala
+++ b/src/dotty/tools/dotc/TypeErasure.scala
@@ -31,7 +31,7 @@ object TypeErasure {
*/
def isErasedType(tp: Type)(implicit ctx: Context): Boolean = tp match {
case tp: TypeRef =>
- tp.symbol.isClass
+ tp.symbol.isClass && tp.symbol != defn.AnyClass
case _: TermRef =>
true
case JavaArrayType(elem) =>
@@ -87,15 +87,36 @@ object TypeErasure {
private val javaSigFn = erasureFn(isJava = true, isSemi = false, isConstructor = false, wildcardOK = true)
private val semiErasureFn = erasureFn(isJava = false, isSemi = true, isConstructor = false, wildcardOK = false)
- def erasure(tp: Type)(implicit ctx: Context): Type = scalaErasureFn(tp)
- def semiErasure(tp: Type)(implicit ctx: Context): Type = semiErasureFn(tp)
+ /** The current context with a phase no later than erasure */
+ private def erasureCtx(implicit ctx: Context) =
+ if (ctx.erasedTypes) ctx.withPhase(ctx.erasurePhase) 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 sigName(tp: Type, isJava: Boolean)(implicit ctx: Context): TypeName = {
val normTp =
if (tp.isRepeatedParam) tp.translateParameterized(defn.RepeatedParamClass, defn.SeqClass)
else tp
- (if (isJava) javaSigFn else scalaSigFn).sigName(normTp)
+ (if (isJava) javaSigFn else scalaSigFn).sigName(normTp)(erasureCtx)
+ }
+
+ /** The erasure of a top-level reference. Differs from normal erasure in that
+ * TermRefs are kept instead of being widened away.
+ */
+ def erasedRef(tp: Type)(implicit ctx: Context): Type = tp match {
+ case tp: TermRef =>
+ assert(tp.symbol.exists, tp)
+ TermRef(erasedRef(tp.prefix), tp.symbol.asTerm)
+ case tp =>
+ erasure(tp)
}
+ /** 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
@@ -113,8 +134,8 @@ object TypeErasure {
if ((sym eq defn.Any_asInstanceOf) || (sym eq defn.Any_isInstanceOf)) eraseParamBounds(sym.info.asInstanceOf[PolyType])
else if (sym.isAbstractType) TypeAlias(WildcardType)
- else if (sym.isConstructor) outer.addParam(sym.owner.asClass, erase(tp))
- else erase(tp)
+ else if (sym.isConstructor) outer.addParam(sym.owner.asClass, erase(tp)(erasureCtx))
+ else erase(tp)(erasureCtx)
}
def isUnboundedGeneric(tp: Type)(implicit ctx: Context) = !(
@@ -147,7 +168,7 @@ object TypeErasure {
def loop(bcs: List[ClassSymbol], bestSoFar: ClassSymbol): ClassSymbol = bcs match {
case bc :: bcs1 =>
if (cls2.derivesFrom(bc))
- if (!bc.is(Trait)) bc
+ if (!bc.is(Trait) && bc != defn.AnyClass) bc
else loop(bcs1, if (bestSoFar.derivesFrom(bc)) bestSoFar else bc)
else
loop(bcs1, bestSoFar)
@@ -225,7 +246,7 @@ class TypeErasure(isJava: Boolean, isSemi: Boolean, isConstructor: Boolean, wild
* - For NoType or NoPrefix, the type itself.
* - For any other type, exception.
*/
- def apply(tp: Type)(implicit ctx: Context): Type = tp match {
+ private def apply(tp: Type)(implicit ctx: Context): Type = tp match {
case tp: TypeRef =>
val sym = tp.symbol
if (!sym.isClass) this(tp.info)
@@ -236,8 +257,7 @@ class TypeErasure(isJava: Boolean, isSemi: Boolean, isConstructor: Boolean, wild
if (parent isRef defn.ArrayClass) eraseArray(tp)
else this(parent)
case tp: TermRef =>
- assert(tp.symbol.exists, tp)
- TermRef(NoPrefix, tp.symbol.asTerm)
+ this(tp.widen)
case ThisType(_) | SuperType(_, _) =>
tp
case ExprType(rt) =>
@@ -269,7 +289,8 @@ class TypeErasure(isJava: Boolean, isSemi: Boolean, isConstructor: Boolean, wild
val parents: List[TypeRef] =
if ((cls eq defn.ObjectClass) || cls.isPrimitiveValueClass) Nil
else removeLaterObjects(classParents.mapConserve(eraseTypeRef))
- tp.derivedClassInfo(NoPrefix, parents, decls, this(tp.selfType))
+ val erasedDecls = decls.filteredScope(d => !d.isType || d.isClass)
+ tp.derivedClassInfo(NoPrefix, parents, erasedDecls, erasedRef(tp.selfType))
// can't replace selftype by NoType because this would lose the sourceModule link
}
case NoType | NoPrefix | ErrorType | JavaArrayType(_) =>
@@ -278,7 +299,7 @@ class TypeErasure(isJava: Boolean, isSemi: Boolean, isConstructor: Boolean, wild
tp
}
- def eraseArray(tp: RefinedType)(implicit ctx: Context) = {
+ private def eraseArray(tp: RefinedType)(implicit ctx: Context) = {
val defn.ArrayType(elemtp) = tp
if (elemtp derivesFrom defn.NullClass) JavaArrayType(defn.ObjectType)
else if (isUnboundedGeneric(elemtp))
@@ -327,7 +348,7 @@ class TypeErasure(isJava: Boolean, isSemi: Boolean, isConstructor: Boolean, wild
/** The name of the type as it is used in `Signature`s.
* Need to ensure correspondence with erasure!
*/
- def sigName(tp: Type)(implicit ctx: Context): TypeName = tp match {
+ private def sigName(tp: Type)(implicit ctx: Context): TypeName = tp match {
case tp: TypeRef =>
val sym = tp.symbol
if (!sym.isClass) sigName(tp.info)
@@ -337,8 +358,8 @@ class TypeErasure(isJava: Boolean, isSemi: Boolean, isConstructor: Boolean, wild
sigName(this(tp))
case JavaArrayType(elem) =>
sigName(elem) ++ "[]"
- case tp: TypeBounds =>
- sigName(tp.hi)
+ case tp: TermRef =>
+ sigName(tp.widen)
case ExprType(rt) =>
sigName(defn.FunctionType(Nil, rt))
case tp: TypeProxy =>
diff --git a/src/dotty/tools/dotc/ast/Desugar.scala b/src/dotty/tools/dotc/ast/Desugar.scala
index d7bbdf2bb..dba3872cb 100644
--- a/src/dotty/tools/dotc/ast/Desugar.scala
+++ b/src/dotty/tools/dotc/ast/Desugar.scala
@@ -90,7 +90,7 @@ object desugar {
def valDef(vdef: ValDef)(implicit ctx: Context): Tree = {
val ValDef(mods, name, tpt, rhs) = vdef
def setterNeeded =
- (mods is Mutable) && ctx.owner.isClass && (!(mods is Private) || (ctx.owner is Trait))
+ (mods is Mutable) && ctx.owner.isClass && (!(mods is PrivateLocal) || (ctx.owner is Trait))
if (setterNeeded) {
// todo: copy of vdef as getter needed?
// val getter = ValDef(mods, name, tpt, rhs) withPos vdef.pos ?
diff --git a/src/dotty/tools/dotc/ast/TreeTypeMap.scala b/src/dotty/tools/dotc/ast/TreeTypeMap.scala
index 56602cca9..33be848c1 100644
--- a/src/dotty/tools/dotc/ast/TreeTypeMap.scala
+++ b/src/dotty/tools/dotc/ast/TreeTypeMap.scala
@@ -6,6 +6,7 @@ import core._
import Types._, Contexts._, Constants._, Names._, Flags._
import SymDenotations._, Symbols._, Annotations._, Trees._, Symbols._
import Denotations._, Decorators._
+import dotty.tools.dotc.transform.SymUtils._
/** A map that applies three functions and a substitution together to a tree and
* makes sure they are coordinated so that the result is well-typed. The functions are
@@ -42,13 +43,7 @@ final class TreeTypeMap(
import tpd._
/** If `sym` is one of `oldOwners`, replace by corresponding symbol in `newOwners` */
- def mapOwner(sym: Symbol) = {
- def loop(from: List[Symbol], to: List[Symbol]): Symbol =
- if (from.isEmpty) sym
- else if (sym eq from.head) to.head
- else loop(from.tail, to.tail)
- loop(oldOwners, newOwners)
- }
+ def mapOwner(sym: Symbol) = sym.subst(oldOwners, newOwners)
/** Replace occurrences of `This(oldOwner)` in some prefix of a type
* by the corresponding `This(newOwner)`.
diff --git a/src/dotty/tools/dotc/ast/Trees.scala b/src/dotty/tools/dotc/ast/Trees.scala
index b63f0ad8c..f1ccfdb75 100644
--- a/src/dotty/tools/dotc/ast/Trees.scala
+++ b/src/dotty/tools/dotc/ast/Trees.scala
@@ -408,7 +408,6 @@ object Trees {
abstract class NameTree[-T >: Untyped] extends DenotingTree[T] {
type ThisTree[-T >: Untyped] <: NameTree[T]
def name: Name
- def withName(name1: Name)(implicit ctx: Context): untpd.NameTree
}
/** Tree refers by name to a denotation */
@@ -449,7 +448,6 @@ object Trees {
case class Ident[-T >: Untyped] private[ast] (name: Name)
extends RefTree[T] {
type ThisTree[-T >: Untyped] = Ident[T]
- def withName(name: Name)(implicit ctx: Context): untpd.Ident = untpd.cpy.Ident(this)(name)
def qualifier: Tree[T] = genericEmptyTree
}
@@ -460,7 +458,6 @@ object Trees {
case class Select[-T >: Untyped] private[ast] (qualifier: Tree[T], name: Name)
extends RefTree[T] {
type ThisTree[-T >: Untyped] = Select[T]
- def withName(name: Name)(implicit ctx: Context): untpd.Select = untpd.cpy.Select(this)(qualifier, name)
}
class SelectWithSig[-T >: Untyped] private[ast] (qualifier: Tree[T], name: Name, val sig: Signature)
@@ -656,7 +653,6 @@ object Trees {
case class SelectFromTypeTree[-T >: Untyped] private[ast] (qualifier: Tree[T], name: Name)
extends RefTree[T] {
type ThisTree[-T >: Untyped] = SelectFromTypeTree[T]
- def withName(name: Name)(implicit ctx: Context): untpd.SelectFromTypeTree = untpd.cpy.SelectFromTypeTree(this)(qualifier, name)
}
/** left & right */
@@ -702,7 +698,6 @@ object Trees {
extends NameTree[T] with DefTree[T] with PatternTree[T] {
type ThisTree[-T >: Untyped] = Bind[T]
override def envelope: Position = pos union initialPos
- def withName(name: Name)(implicit ctx: Context): untpd.Bind = untpd.cpy.Bind(this)(name, body)
}
/** tree_1 | ... | tree_n */
@@ -734,7 +729,6 @@ object Trees {
case class ValDef[-T >: Untyped] private[ast] (mods: Modifiers[T], name: TermName, tpt: Tree[T], rhs: Tree[T])
extends ValOrDefDef[T] {
type ThisTree[-T >: Untyped] = ValDef[T]
- def withName(name: Name)(implicit ctx: Context): untpd.ValDef = untpd.cpy.ValDef(this)(name = name.toTermName)
assert(isEmpty || tpt != genericEmptyTree)
}
@@ -742,7 +736,6 @@ object Trees {
case class DefDef[-T >: Untyped] private[ast] (mods: Modifiers[T], name: TermName, tparams: List[TypeDef[T]], vparamss: List[List[ValDef[T]]], tpt: Tree[T], rhs: Tree[T])
extends ValOrDefDef[T] {
type ThisTree[-T >: Untyped] = DefDef[T]
- def withName(name: Name)(implicit ctx: Context): untpd.DefDef = untpd.cpy.DefDef(this)(name = name.toTermName)
assert(tpt != genericEmptyTree)
}
@@ -754,7 +747,6 @@ object Trees {
case class TypeDef[-T >: Untyped] private[ast] (mods: Modifiers[T], name: TypeName, rhs: Tree[T])
extends MemberDef[T] {
type ThisTree[-T >: Untyped] = TypeDef[T]
- def withName(name: Name)(implicit ctx: Context): untpd.TypeDef = untpd.cpy.TypeDef(this)(name = name.toTypeName)
/** Is this a definition of a class? */
def isClassDef = rhs.isInstanceOf[Template[_]]
@@ -820,7 +812,7 @@ object Trees {
}
class EmptyValDef[T >: Untyped] extends ValDef[T](
- Modifiers[T](Private), nme.WILDCARD, genericEmptyTree[T], genericEmptyTree[T]) with WithoutTypeOrPos[T] {
+ Modifiers[T](PrivateLocal), nme.WILDCARD, genericEmptyTree[T], genericEmptyTree[T]) with WithoutTypeOrPos[T] {
override def isEmpty: Boolean = true
}
@@ -1344,6 +1336,7 @@ object Trees {
abstract class TreeTraverser extends TreeAccumulator[Unit] {
def traverse(tree: Tree): Unit
def apply(x: Unit, tree: Tree) = traverse(tree)
+ protected def traverseChildren(tree: Tree) = foldOver((), tree)
}
/** Fold `f` over all tree nodes, in depth-first, prefix order */
@@ -1361,6 +1354,19 @@ object Trees {
else foldOver(x1, tree)
}
}
+
+ def rename(tree: NameTree, newName: Name)(implicit ctx: Context): tree.ThisTree[T] = {
+ tree match {
+ case tree: Ident => cpy.Ident(tree)(newName)
+ case tree: Select => cpy.Select(tree)(tree.qualifier, newName)
+ case tree: Bind => cpy.Bind(tree)(newName, tree.body)
+ case tree: ValDef => cpy.ValDef(tree)(name = newName.asTermName)
+ case tree: DefDef => cpy.DefDef(tree)(name = newName.asTermName)
+ case tree: untpd.PolyTypeDef => untpd.cpy.PolyTypeDef(tree)(tree.mods, newName.asTypeName, tree.tparams, tree.rhs)
+ case tree: TypeDef => cpy.TypeDef(tree)(name = newName.asTypeName)
+ case tree: SelectFromTypeTree => cpy.SelectFromTypeTree(tree)(tree.qualifier, newName)
+ }
+ }.asInstanceOf[tree.ThisTree[T]]
}
}
// ----- Helper functions and classes ---------------------------------------
diff --git a/src/dotty/tools/dotc/ast/tpd.scala b/src/dotty/tools/dotc/ast/tpd.scala
index 3fa08b9c6..9b7c9cbae 100644
--- a/src/dotty/tools/dotc/ast/tpd.scala
+++ b/src/dotty/tools/dotc/ast/tpd.scala
@@ -2,8 +2,8 @@ package dotty.tools
package dotc
package ast
+import transform.SymUtils._
import core._
-import dotty.tools.dotc.transform.TypeUtils
import util.Positions._, Types._, Contexts._, Constants._, Names._, Flags._
import SymDenotations._, Symbols._, StdNames._, Annotations._, Trees._, Symbols._
import Denotations._, Decorators._
@@ -44,9 +44,6 @@ object tpd extends Trees.Instance[Type] with TypedTreeInfo {
def Apply(fn: Tree, args: List[Tree])(implicit ctx: Context): Apply =
ta.assignType(untpd.Apply(fn, args), fn, args)
- def ensureApplied(fn: Tree)(implicit ctx: Context): Tree =
- if (fn.tpe.widen.isParameterless) fn else Apply(fn, Nil)
-
def TypeApply(fn: Tree, args: List[Tree])(implicit ctx: Context): TypeApply =
ta.assignType(untpd.TypeApply(fn, args), fn, args)
@@ -358,7 +355,6 @@ object tpd extends Trees.Instance[Type] with TypedTreeInfo {
else if (tpw isRef defn.ShortClass) Literal(Constant(0.toShort))
else Literal(Constant(null)).select(defn.Any_asInstanceOf).appliedToType(tpe)
}
-
private class FindLocalDummyAccumulator(cls: ClassSymbol)(implicit ctx: Context) extends TreeAccumulator[Symbol] {
def apply(sym: Symbol, tree: Tree) =
if (sym.exists) sym
@@ -565,15 +561,7 @@ object tpd extends Trees.Instance[Type] with TypedTreeInfo {
def appliedToArgss(argss: List[List[Tree]])(implicit ctx: Context): Tree =
((tree: Tree) /: argss)(Apply(_, _))
- def appliedToNone(implicit ctx: Context): Tree = appliedToArgs(Nil)
-
- def appliedIfMethod(implicit ctx: Context): Tree = {
- tree.tpe.widen match {
- case fntpe: MethodType => appliedToArgs(Nil)
- case _ => tree
- }
- }
-
+ def appliedToNone(implicit ctx: Context): Apply = appliedToArgs(Nil)
def appliedToType(targ: Type)(implicit ctx: Context): Tree =
appliedToTypes(targ :: Nil)
@@ -584,6 +572,9 @@ object tpd extends Trees.Instance[Type] with TypedTreeInfo {
def appliedToTypeTrees(targs: List[Tree])(implicit ctx: Context): Tree =
if (targs.isEmpty) tree else TypeApply(tree, targs)
+ def ensureApplied(implicit ctx: Context): Tree =
+ if (tree.tpe.widen.isParameterless) tree else tree.appliedToNone
+
def isInstance(tp: Type)(implicit ctx: Context): Tree =
tree.select(defn.Any_isInstanceOf).appliedToType(tp)
@@ -601,6 +592,19 @@ object tpd extends Trees.Instance[Type] with TypedTreeInfo {
def or(that: Tree)(implicit ctx: Context): Tree =
tree.select(defn.Boolean_||).appliedTo(that)
+ def becomes(rhs: Tree)(implicit ctx: Context): Tree =
+ if (tree.symbol is Method) {
+ val setr = tree match {
+ case Ident(_) =>
+ val setter = tree.symbol.setter
+ assert(setter.exists, tree.symbol.showLocated)
+ ref(tree.symbol.setter)
+ case Select(qual, _) => qual.select(tree.symbol.setter)
+ }
+ setr.appliedTo(rhs)
+ }
+ else Assign(tree, rhs)
+
// --- Higher order traversal methods -------------------------------
def foreachSubTree(f: Tree => Unit): Unit = { //TODO should go in tpd.
@@ -627,7 +631,7 @@ object tpd extends Trees.Instance[Type] with TypedTreeInfo {
val mname = ("to" + numericCls.name).toTermName
val conversion = tree.tpe member mname
if (conversion.symbol.exists)
- ensureApplied(tree.select(conversion.symbol.termRef))
+ tree.select(conversion.symbol.termRef).ensureApplied
else if (tree.tpe.widen isRef numericCls)
tree
else {
@@ -655,6 +659,22 @@ object tpd extends Trees.Instance[Type] with TypedTreeInfo {
Ident(defn.ScalaRuntimeModule.requiredMethod(name).termRef).appliedToArgs(args)
}
+ /** A traverser that passes the enlcosing class or method as an argumenr
+ * to the traverse method.
+ */
+ abstract class EnclosingMethodTraverser(implicit ctx: Context) extends TreeAccumulator[Symbol] {
+ def traverse(enclMeth: Symbol, tree: Tree): Unit
+ def apply(enclMeth: Symbol, tree: Tree) = {
+ tree match {
+ case _: DefTree if tree.symbol.exists =>
+ traverse(tree.symbol.enclosingMethod, tree)
+ case _ =>
+ traverse(enclMeth, tree)
+ }
+ enclMeth
+ }
+ }
+
// ensure that constructors are fully applied?
// ensure that normal methods are fully applied?
diff --git a/src/dotty/tools/dotc/ast/untpd.scala b/src/dotty/tools/dotc/ast/untpd.scala
index 9ac01df9c..5a6c9fa89 100644
--- a/src/dotty/tools/dotc/ast/untpd.scala
+++ b/src/dotty/tools/dotc/ast/untpd.scala
@@ -58,9 +58,7 @@ object untpd extends Trees.Instance[Untyped] with UntypedTreeInfo {
case class PatDef(mods: Modifiers, pats: List[Tree], tpt: Tree, rhs: Tree) extends DefTree
class PolyTypeDef(mods: Modifiers, name: TypeName, override val tparams: List[TypeDef], rhs: Tree)
- extends TypeDef(mods, name, rhs) {
- override def withName(name: Name)(implicit ctx: Context) = cpy.PolyTypeDef(this)(mods, name.toTypeName, tparams, rhs)
- }
+ extends TypeDef(mods, name, rhs)
// ----- TypeTrees that refer to other tree's symbols -------------------
@@ -210,7 +208,7 @@ object untpd extends Trees.Instance[Untyped] with UntypedTreeInfo {
makeConstructor(Modifiers(), Nil, Nil)
def makeSelfDef(name: TermName, tpt: Tree)(implicit ctx: Context) =
- ValDef(Modifiers(Private), name, tpt, EmptyTree)
+ ValDef(Modifiers(PrivateLocal), name, tpt, EmptyTree)
def makeTupleOrParens(ts: List[Tree])(implicit ctx: Context) = ts match {
case t :: Nil => Parens(t)
@@ -414,4 +412,10 @@ object untpd extends Trees.Instance[Untyped] with UntypedTreeInfo {
super.foldOver(x, tree)
}
}
+
+ override def rename(tree: NameTree, newName: Name)(implicit ctx: Context): tree.ThisTree[Untyped] = tree match {
+ case t: PolyTypeDef =>
+ cpy.PolyTypeDef(t)(t.mods, newName.asTypeName, t.tparams, t.rhs).asInstanceOf[tree.ThisTree[Untyped]]
+ case _ => super.rename(tree, newName)
+ }
}
diff --git a/src/dotty/tools/dotc/config/ScalaSettings.scala b/src/dotty/tools/dotc/config/ScalaSettings.scala
index f23fb6201..743925e40 100644
--- a/src/dotty/tools/dotc/config/ScalaSettings.scala
+++ b/src/dotty/tools/dotc/config/ScalaSettings.scala
@@ -145,8 +145,9 @@ class ScalaSettings extends Settings.SettingGroup {
val noSelfCheck = BooleanSetting("-Yno-self-type-checks", "Suppress check for self-type conformance among inherited members.")
val YshowSuppressedErrors = BooleanSetting("-Yshow-suppressed-errors", "Also show follow-on errors and warnings that are normally supressed.")
val Yheartbeat = BooleanSetting("-Yheartbeat", "show heartbeat stack trace of compiler operations.")
- val Yprintpos = BooleanSetting("-Yprintpos", "show tree positions")
- val YnoDeepSubtypes = BooleanSetting("-YnoDeepSubtypes", "throw an exception on deep subtyping call stacks")
+ val Yprintpos = BooleanSetting("-Yprintpos", "show tree positions.")
+ val YnoDeepSubtypes = BooleanSetting("-Yno-deep-subtypes", "throw an exception on deep subtyping call stacks.")
+ val YprintSyms = BooleanSetting("-Yprint-syms", "when printing trees print info in symbols instead of corresponding info in trees.")
def stop = YstopAfter
/** Area-specific debug output.
diff --git a/src/dotty/tools/dotc/core/Contexts.scala b/src/dotty/tools/dotc/core/Contexts.scala
index 6b9b1dec7..3b14872b7 100644
--- a/src/dotty/tools/dotc/core/Contexts.scala
+++ b/src/dotty/tools/dotc/core/Contexts.scala
@@ -282,7 +282,7 @@ object Contexts {
* from constructor parameters to class paramater accessors.
*/
def superCallContext: Context = {
- val locals = newScopeWith(owner.decls.filter(_ is ParamAccessor).toList: _*)
+ val locals = newScopeWith(owner.asClass.paramAccessors: _*)
superOrThisCallContext(owner.primaryConstructor, locals)
}
diff --git a/src/dotty/tools/dotc/core/Definitions.scala b/src/dotty/tools/dotc/core/Definitions.scala
index b74506caf..b1c2baff6 100644
--- a/src/dotty/tools/dotc/core/Definitions.scala
+++ b/src/dotty/tools/dotc/core/Definitions.scala
@@ -4,7 +4,6 @@ package core
import Types._, Contexts._, Symbols._, Denotations._, SymDenotations._, StdNames._, Names._
import Flags._, Scopes._, Decorators._, NameOps._, util.Positions._
-import TypeApplications._
import pickling.UnPickler.ensureConstructor
import scala.annotation.{ switch, meta }
import scala.collection.{ mutable, immutable }
@@ -192,6 +191,7 @@ class Definitions {
lazy val ScalaStaticsClass = ScalaStaticsModule.moduleClass.asClass
def staticsMethod(name: PreName) = ctx.requiredMethod(ScalaStaticsClass, name)
+
lazy val DottyPredefModule = ctx.requiredModule("dotty.DottyPredef")
lazy val NilModule = ctx.requiredModule("scala.collection.immutable.Nil")
lazy val PredefConformsClass = ctx.requiredClass("scala.Predef." + tpnme.Conforms)
diff --git a/src/dotty/tools/dotc/core/Denotations.scala b/src/dotty/tools/dotc/core/Denotations.scala
index 9970c5948..82fd60fa0 100644
--- a/src/dotty/tools/dotc/core/Denotations.scala
+++ b/src/dotty/tools/dotc/core/Denotations.scala
@@ -603,26 +603,26 @@ object Denotations {
*/
protected def installAfter(phase: DenotTransformer)(implicit ctx: Context): Unit = {
val targetId = phase.next.id
- assert(ctx.phaseId == targetId,
- s"denotation update for $this called in phase ${ctx.phase}, expected was ${phase.next}")
- val current = symbol.current
- // println(s"installing $this after $phase/${phase.id}, valid = ${current.validFor}")
- // printPeriods(current)
- this.nextInRun = current.nextInRun
- 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
- current.validFor = Nowhere
- }
+ if (ctx.phaseId != targetId) installAfter(phase)(ctx.withPhase(phase.next))
else {
- // insert this denotation after current
- current.validFor = Period(ctx.runId, current.validFor.firstPhaseId, targetId - 1)
- current.nextInRun = this
- }
+ val current = symbol.current
+ // println(s"installing $this after $phase/${phase.id}, valid = ${current.validFor}")
+ // printPeriods(current)
+ this.nextInRun = current.nextInRun
+ 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
+ current.validFor = Nowhere
+ } else {
+ // insert this denotation after current
+ current.validFor = Period(ctx.runId, current.validFor.firstPhaseId, targetId - 1)
+ current.nextInRun = this
+ }
// printPeriods(this)
+ }
}
def staleSymbolError(implicit ctx: Context) = {
@@ -681,6 +681,7 @@ object Denotations {
// ------ PreDenotation ops ----------------------------------------------
final def first = this
+ final def last = this
final def toDenot(pre: Type)(implicit ctx: Context): Denotation = this
final def containsSym(sym: Symbol): Boolean = hasUniqueSym && (symbol eq sym)
final def containsSig(sig: Signature)(implicit ctx: Context) =
@@ -766,8 +767,9 @@ object Denotations {
/** A denotation in the group exists */
def exists: Boolean
- /** First denotation in the group */
+ /** First/last denotation in the group */
def first: Denotation
+ def last: Denotation
/** Convert to full denotation by &-ing all elements */
def toDenot(pre: Type)(implicit ctx: Context): Denotation
@@ -832,6 +834,7 @@ object Denotations {
assert(denots1.exists && denots2.exists, s"Union of non-existing denotations ($denots1) and ($denots2)")
def exists = true
def first = denots1.first
+ def last = denots2.last
def toDenot(pre: Type)(implicit ctx: Context) =
(denots1 toDenot pre) & (denots2 toDenot pre, pre)
def containsSym(sym: Symbol) =
diff --git a/src/dotty/tools/dotc/core/Flags.scala b/src/dotty/tools/dotc/core/Flags.scala
index e34483f8f..a27dd6614 100644
--- a/src/dotty/tools/dotc/core/Flags.scala
+++ b/src/dotty/tools/dotc/core/Flags.scala
@@ -421,7 +421,7 @@ object Flags {
/** Flags representing source modifiers */
final val SourceModifierFlags =
commonFlags(Private, Protected, Abstract, Final,
- Sealed, Case, Implicit, Override, AbsOverride, Lazy)
+ Sealed, Case, Implicit, Override, AbsOverride, Lazy, Static)
/** Flags representing modifiers that can appear in trees */
final val ModifierFlags =
@@ -459,6 +459,9 @@ object Flags {
/** Module classes always have these flags set */
final val ModuleClassCreationFlags = ModuleClass | Final
+ /** Accessors always have these flags set */
+ final val AccessorCreationFlags = Method | Accessor
+
/** The flags of the self symbol */
final val SelfSymFlags = Private | Local | Deferred
@@ -526,7 +529,7 @@ object Flags {
final val HasDefaultParams = DefaultParameterized | InheritedDefaultParams
/** Is valid forever */
- final val ValidForever = Package | Permanent
+ final val ValidForever = Package | Permanent | Scala2ExistentialCommon
/** Is a default parameter in Scala 2*/
final val DefaultParameter = allOf(Param, DefaultParameterized)
@@ -543,12 +546,15 @@ object Flags {
/** Labeled private[this] */
final val PrivateLocal = allOf(Private, Local)
- /** A private parameter accessor */
+ /** A private[this] parameter accessor */
final val PrivateLocalParamAccessor = allOf(Private, Local, ParamAccessor)
- /** A private parameter */
+ /** A private[this] parameter */
final val PrivateLocalParam = allOf(Private, Local, Param)
+ /** A private parameter accessor */
+ final val PrivateParamAccessor = allOf(Private, ParamAccessor)
+
/** A local parameter */
final val ParamAndLocal = allOf(Param, Local)
diff --git a/src/dotty/tools/dotc/core/NameOps.scala b/src/dotty/tools/dotc/core/NameOps.scala
index b18f708ed..beb3142d3 100644
--- a/src/dotty/tools/dotc/core/NameOps.scala
+++ b/src/dotty/tools/dotc/core/NameOps.scala
@@ -68,10 +68,11 @@ object NameOps {
def isProtectedAccessorName = name startsWith PROTECTED_PREFIX
def isReplWrapperName = name containsSlice INTERPRETER_IMPORT_WRAPPER
def isSetterName = name endsWith SETTER_SUFFIX
- def isTraitSetterName = isSetterName && (name containsSlice TRAIT_SETTER_SEPARATOR)
+ def isTraitSetterName = isSetterName && (name containsSlice TRAIT_SETTER_PREFIX)
def isSingletonName = name endsWith SINGLETON_SUFFIX
def isModuleClassName = name endsWith MODULE_SUFFIX
def isImportName = name startsWith IMPORT
+ def isFieldName = name endsWith LOCAL_SUFFIX
def isInheritedName = name.length > 0 && name.head == '(' && name.startsWith(nme.INHERITED)
def isDefaultGetterName = name.isTermName && name.asTermName.defaultGetterIndex >= 0
@@ -223,23 +224,36 @@ object NameOps {
implicit class TermNameDecorator(val name: TermName) extends AnyVal {
import nme._
- /** The expanded setter name of `name` relative to this class `base`
- */
- def expandedSetterName(base: Symbol)(implicit ctx: Context): TermName =
- name.expandedName(base, separator = TRAIT_SETTER_SEPARATOR)
+ def traitSetterName: TermName =
+ nme.TRAIT_SETTER_PREFIX ++ setterName
+
+ def setterName: TermName =
+ if (name.isFieldName) name.fieldToGetter.setterName
+ else name ++ SETTER_SUFFIX
+
+ def getterName: TermName =
+ if (name.isFieldName) fieldToGetter
+ else setterToGetter
- def setterName: TermName = name ++ SETTER_SUFFIX
+ def fieldName: TermName =
+ if (name.isSetterName) getterName.fieldName
+ else name ++ LOCAL_SUFFIX
- def setterToGetter: TermName = {
- val p = name.indexOfSlice(TRAIT_SETTER_SEPARATOR)
+ private def setterToGetter: TermName = {
+ val p = name.indexOfSlice(TRAIT_SETTER_PREFIX)
if (p >= 0)
- (name drop (p + TRAIT_SETTER_SEPARATOR.length)).asTermName.setterToGetter
+ (name drop (p + TRAIT_SETTER_PREFIX.length)).asTermName.getterName
else {
assert(name.endsWith(SETTER_SUFFIX), name + " is referenced as a setter but has wrong name format")
name.take(name.length - SETTER_SUFFIX.length).asTermName
}
}
+ def fieldToGetter: TermName = {
+ assert(name.isFieldName)
+ name.take(name.length - LOCAL_SUFFIX.length).asTermName
+ }
+
/** Nominally, name$default$N, encoded for <init>
* @param Post the parameters position.
* @note Default getter name suffixes start at 1, so `pos` has to be adjusted by +1
diff --git a/src/dotty/tools/dotc/core/Phases.scala b/src/dotty/tools/dotc/core/Phases.scala
index 5eb8cd920..7b589fec1 100644
--- a/src/dotty/tools/dotc/core/Phases.scala
+++ b/src/dotty/tools/dotc/core/Phases.scala
@@ -9,7 +9,7 @@ import Denotations._
import config.Printers._
import scala.collection.mutable.{ListBuffer, ArrayBuffer}
import dotty.tools.dotc.transform.TreeTransforms.{TreeTransformer, MiniPhase, TreeTransform}
-import dotty.tools.dotc.transform.{ExplicitOuter, TreeTransforms, Erasure, Flatten}
+import dotty.tools.dotc.transform.{TreeTransforms, ExplicitOuter, Erasure, Flatten, GettersSetters}
import Periods._
import typer.{FrontEnd, RefChecks}
import ast.tpd
@@ -169,12 +169,14 @@ object Phases {
private val erasureCache = new PhaseCache(classOf[Erasure])
private val flattenCache = new PhaseCache(classOf[Flatten])
private val explicitOuterCache = new PhaseCache(classOf[ExplicitOuter])
+ private val gettersSettersCache = new PhaseCache(classOf[GettersSetters])
def typerPhase = typerCache.phase
def refchecksPhase = refChecksCache.phase
def erasurePhase = erasureCache.phase
def flattenPhase = flattenCache.phase
- def explicitOuter = explicitOuterCache.phase
+ def explicitOuterPhase = explicitOuterCache.phase
+ def gettersSettersPhase = gettersSettersCache.phase
def isAfterTyper(phase: Phase): Boolean = phase.id > typerPhase.id
}
@@ -200,6 +202,11 @@ object Phases {
*/
def checkPostCondition(tree: tpd.Tree)(implicit ctx: Context): Unit = ()
+ /** If set, allow missing or superfluous arguments in applications
+ * and type applications.
+ */
+ def relaxedTyping: Boolean = false
+
def exists: Boolean = true
private var myPeriod: Period = Periods.InvalidPeriod
diff --git a/src/dotty/tools/dotc/core/Scopes.scala b/src/dotty/tools/dotc/core/Scopes.scala
index 54317a496..3f7a4cb2c 100644
--- a/src/dotty/tools/dotc/core/Scopes.scala
+++ b/src/dotty/tools/dotc/core/Scopes.scala
@@ -106,7 +106,10 @@ object Scopes {
def next(): Symbol = { val r = e.sym; e = lookupNextEntry(e); r }
}
- /** The denotation set of all the symbols with given name in this scope */
+ /** The denotation set of all the symbols with given name in this scope
+ * Symbols occur in the result in reverse order relative to their occurrence
+ * in `this.toList`.
+ */
final def denotsNamed(name: Name, select: SymDenotation => Boolean = selectAll)(implicit ctx: Context): PreDenotation = {
var syms: PreDenotation = NoDenotation
var e = lookupEntry(name)
@@ -118,6 +121,20 @@ object Scopes {
syms
}
+ /** The scope that keeps only those symbols from this scope that match the
+ * given predicates. If all symbols match, returns the scope itself, otherwise
+ * a copy with the matching symbols.
+ */
+ final def filteredScope(p: Symbol => Boolean)(implicit ctx: Context): Scope = {
+ var result: MutableScope = null
+ for (sym <- iterator)
+ if (!p(sym)) {
+ if (result == null) result = cloneScope
+ result.unlink(sym)
+ }
+ if (result == null) this else result
+ }
+
def implicitDecls(implicit ctx: Context): List[TermRef] = Nil
final def toText(printer: Printer): Text = printer.toText(this)
diff --git a/src/dotty/tools/dotc/core/StdNames.scala b/src/dotty/tools/dotc/core/StdNames.scala
index 4f2fe9dba..f7354a8b4 100644
--- a/src/dotty/tools/dotc/core/StdNames.scala
+++ b/src/dotty/tools/dotc/core/StdNames.scala
@@ -119,7 +119,7 @@ object StdNames {
val SINGLETON_SUFFIX: N = ".type"
val SPECIALIZED_SUFFIX: N = "$sp"
val SUPER_PREFIX: N = "super$"
- val TRAIT_SETTER_SEPARATOR: N = "$_setter_$"
+ val TRAIT_SETTER_PREFIX: N = "_setter_$"
val WHILE_PREFIX: N = "while$"
// value types (and AnyRef) are all used as terms as well
@@ -226,7 +226,7 @@ object StdNames {
val LAZY_LOCAL: N = "$lzy"
val LAZY_FIELD_OFFSET: N = "OFFSET$"
val LAZY_SLOW_SUFFIX: N = "$lzycompute"
- val LOCAL_SUFFIX: N = " "
+ val LOCAL_SUFFIX: N = "$$local"
val UNIVERSE_BUILD_PREFIX: N = "$u.build."
val UNIVERSE_BUILD: N = "$u.build"
val UNIVERSE_PREFIX: N = "$u."
@@ -685,15 +685,6 @@ object StdNames {
def newBitmapName(bitmapPrefix: TermName, n: Int): TermName = bitmapPrefix ++ n.toString
def selectorName(n: Int): TermName = "_" + (n + 1)
- /** Is name a variable name? */
- def isVariableName(name: Name): Boolean = {
- val first = name.firstChar
- ( ((first.isLower && first.isLetter) || first == '_')
- && (name != nme.false_)
- && (name != nme.true_)
- && (name != nme.null_)
- )
- }
object primitive {
val arrayApply: TermName = "[]apply"
diff --git a/src/dotty/tools/dotc/core/SymDenotations.scala b/src/dotty/tools/dotc/core/SymDenotations.scala
index 073c9112d..ae37ab87c 100644
--- a/src/dotty/tools/dotc/core/SymDenotations.scala
+++ b/src/dotty/tools/dotc/core/SymDenotations.scala
@@ -43,14 +43,10 @@ trait SymDenotations { this: Context =>
if (denot is ValidForever) true
else try {
val owner = denot.owner.denot
- def isSelfSym = owner.infoOrCompleter match {
- case ClassInfo(_, _, _, _, selfInfo) => selfInfo == denot.symbol
- case _ => false
- }
stillValid(owner) && (
!owner.isClass
|| (owner.decls.lookupAll(denot.name) contains denot.symbol)
- || isSelfSym
+ || denot.isSelfSym
)
} catch {
case ex: StaleSymbol => false
@@ -200,7 +196,7 @@ object SymDenotations {
final def hasAnnotation(cls: Symbol)(implicit ctx: Context) =
dropOtherAnnotations(annotations, cls).nonEmpty
- /** Optionally, get annotation matching the given class symbol */
+ /** Optionally, the annotation matching the given class symbol */
final def getAnnotation(cls: Symbol)(implicit ctx: Context): Option[Annotation] =
dropOtherAnnotations(annotations, cls) match {
case annot :: _ => Some(annot)
@@ -360,6 +356,23 @@ object SymDenotations {
/** Is this symbol an abstract or alias type? */
final def isAbstractOrAliasType = isType & !isClass
+ /** Is this the denotation of a self symbol of some class?
+ * This is the case if one of two conditions holds:
+ * 1. It is the symbol referred to in the selfInfo part of the ClassInfo
+ * which is the type of this symbol's owner.
+ * 2. This symbol is owned by a class, it's selfInfo field refers to a type
+ * (indicating the self definition does not introduce a name), and the
+ * symbol's name is "_".
+ * TODO: Find a more robust way to characterize self symbols, maybe by
+ * spending a Flag on them?
+ */
+ final def isSelfSym(implicit ctx: Context) = owner.infoOrCompleter match {
+ case ClassInfo(_, _, _, _, selfInfo) =>
+ selfInfo == symbol ||
+ selfInfo.isInstanceOf[Type] && name == nme.WILDCARD
+ case _ => false
+ }
+
/** Is this definition contained in `boundary`?
* Same as `ownersIterator contains boundary` but more efficient.
*/
@@ -372,6 +385,9 @@ object SymDenotations {
recur(symbol)
}
+ final def isProperlyContainedIn(boundary: Symbol)(implicit ctx: Context): Boolean =
+ symbol != boundary && isContainedIn(boundary)
+
/** Is this denotation static (i.e. with no outer instance)? */
final def isStatic(implicit ctx: Context) =
(this is Static) || this.exists && owner.isStaticOwner
@@ -428,7 +444,7 @@ object SymDenotations {
/** Does this symbol denote the primary constructor of its enclosing class? */
final def isPrimaryConstructor(implicit ctx: Context) =
- isConstructor && owner.primaryConstructor.denot == this
+ isConstructor && owner.primaryConstructor == symbol
/** Is this a subclass of the given class `base`? */
def isSubClass(base: Symbol)(implicit ctx: Context) = false
@@ -609,7 +625,7 @@ object SymDenotations {
/** The field accessed by this getter or setter, or if it does not exist, the getter */
def accessedFieldOrGetter(implicit ctx: Context): Symbol = {
- val fieldName = if (isSetter) name.asTermName.setterToGetter else name
+ val fieldName = if (isSetter) name.asTermName.getterName else name
val d = owner.info.decl(fieldName)
val field = d.suchThat(!_.is(Method)).symbol
def getter = d.suchThat(_.info.isParameterless).symbol
@@ -916,7 +932,7 @@ object SymDenotations {
privateWithin: Symbol = null,
annotations: List[Annotation] = null)(implicit ctx: Context) =
{ // simulate default parameters, while also passing implicit context ctx to the default values
- val initFlags1 = if (initFlags != UndefinedFlags) initFlags else this.flags &~ Frozen
+ val initFlags1 = (if (initFlags != UndefinedFlags) initFlags else this.flags) &~ Frozen
val info1 = if (info != null) info else this.info
val privateWithin1 = if (privateWithin != null) privateWithin else this.privateWithin
val annotations1 = if (annotations != null) annotations else this.annotations
@@ -1314,6 +1330,18 @@ object SymDenotations {
case _ => bt
}
+ 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.
+ */
+ def isCachable(tp: Type): Boolean = tp match {
+ case tp: TypeVar => tp.inst.exists && inCache(tp.inst)
+ case tp: TypeProxy => inCache(tp.underlying)
+ case tp: AndOrType => inCache(tp.tp1) && inCache(tp.tp2)
+ case _ => true
+ }
+
def computeBaseTypeRefOf(tp: Type): Type = {
Stats.record("computeBaseTypeOf")
if (symbol.isStatic && tp.derivesFrom(symbol))
@@ -1330,9 +1358,6 @@ object SymDenotations {
case _ =>
baseTypeRefOf(tp.underlying)
}
- case tp: TypeVar =>
- if (tp.inst.exists) computeBaseTypeRefOf(tp.inst)
- else Uncachable(computeBaseTypeRefOf(tp.underlying))
case tp: TypeProxy =>
baseTypeRefOf(tp.underlying)
case AndType(tp1, tp2) =>
@@ -1353,15 +1378,9 @@ object SymDenotations {
var basetp = baseTypeRefCache get tp
if (basetp == null) {
baseTypeRefCache.put(tp, NoPrefix)
- val computedBT = computeBaseTypeRefOf(tp)
- basetp = computedBT match {
- case Uncachable(basetp) =>
- baseTypeRefCache.remove(tp)
- computedBT
- case basetp =>
- baseTypeRefCache.put(tp, basetp)
- basetp
- }
+ basetp = computeBaseTypeRefOf(tp)
+ if (isCachable(tp)) baseTypeRefCache.put(tp, basetp)
+ else baseTypeRefCache.remove(tp)
} else if (basetp == NoPrefix) {
throw CyclicReference(this)
}
@@ -1420,27 +1439,29 @@ object SymDenotations {
override def primaryConstructor(implicit ctx: Context): Symbol = {
val cname = if (this is ImplClass) nme.IMPLCLASS_CONSTRUCTOR else nme.CONSTRUCTOR
- decls.denotsNamed(cname).first.symbol
+ decls.denotsNamed(cname).last.symbol // denotsNamed returns Symbols in reverse order of occurrence
}
+ /** The parameter accessors of this class. Term and type accessors,
+ * getters and setters are all returned int his list
+ */
+ def paramAccessors(implicit ctx: Context): List[Symbol] =
+ decls.filter(_ is ParamAccessor).toList
+
/** If this class has the same `decls` scope reference in `phase` and
* `phase.next`, install a new denotation with a cloned scope in `phase.next`.
- * @pre Can only be called in `phase.next`.
*/
- def ensureFreshScopeAfter(phase: DenotTransformer)(implicit ctx: Context): Unit = {
- assert(ctx.phaseId == phase.next.id)
- val prevCtx = ctx.withPhase(phase)
- val ClassInfo(pre, _, ps, decls, selfInfo) = classInfo
- if (classInfo(prevCtx).decls eq decls) {
- copySymDenotation(
- info = ClassInfo(pre, classSymbol, ps, decls.cloneScope, selfInfo),
- initFlags = this.flags &~ Frozen).installAfter(phase)
+ def ensureFreshScopeAfter(phase: DenotTransformer)(implicit ctx: Context): Unit =
+ if (ctx.phaseId != phase.next.id) ensureFreshScopeAfter(phase)(ctx.withPhase(phase.next))
+ else {
+ val prevCtx = ctx.withPhase(phase)
+ val ClassInfo(pre, _, ps, decls, selfInfo) = classInfo
+ if (classInfo(prevCtx).decls eq decls)
+ copySymDenotation(info = ClassInfo(pre, classSymbol, ps, decls.cloneScope, selfInfo))
+ .installAfter(phase)
}
- }
}
- private case class Uncachable(tp: Type) extends UncachedGroundType
-
/** The denotation of a package class.
* It overrides ClassDenotation to take account of package objects when looking for members
*/
diff --git a/src/dotty/tools/dotc/core/TypeComparer.scala b/src/dotty/tools/dotc/core/TypeComparer.scala
index 6d0f81c72..f36572755 100644
--- a/src/dotty/tools/dotc/core/TypeComparer.scala
+++ b/src/dotty/tools/dotc/core/TypeComparer.scala
@@ -587,11 +587,22 @@ class TypeComparer(initctx: Context) extends DotClass {
else thirdTry(tp1, tp2)
}
tp1.info match {
+ // There was the following code, which was meant to implement this logic:
+ // If x has type A | B, then x.type <: C if
+ // x.type <: C assuming x has type A, and
+ // x.type <: C assuming x has type B.
+ // But it did not work, because derivedRef would always give back the same
+ // type and cache the denotation. So it ended up copmparing just one branch.
+ // The code seems to be unncessary for the tests and does not seems to help performance.
+ // So it is commented out. If we ever need to come back to this, we would have
+ // to create unchached TermRefs in order to avoid cross talk between the branches.
+ /*
case OrType(tp11, tp12) =>
val sd = tp1.denot.asSingleDenotation
def derivedRef(tp: Type) =
NamedType(tp1.prefix, tp1.name, sd.derivedSingleDenotation(sd.symbol, tp))
secondTry(OrType.make(derivedRef(tp11), derivedRef(tp12)), tp2)
+ */
case TypeBounds(lo1, hi1) =>
if ((ctx.mode is Mode.GADTflexible) && (tp1.symbol is GADTFlexType) &&
!isSubTypeWhenFrozen(hi1, tp2))
@@ -762,6 +773,8 @@ class TypeComparer(initctx: Context) extends DotClass {
tp2.info match {
case tp2i: TermRef =>
isSubType(tp1, tp2i)
+ case ExprType(tp2i: TermRef) if (ctx.phase.id > ctx.gettersSettersPhase.id) =>
+ isSubType(tp1, tp2i)
case _ =>
false
}
diff --git a/src/dotty/tools/dotc/core/Types.scala b/src/dotty/tools/dotc/core/Types.scala
index 529452c73..d93e4eb09 100644
--- a/src/dotty/tools/dotc/core/Types.scala
+++ b/src/dotty/tools/dotc/core/Types.scala
@@ -906,10 +906,14 @@ object Types {
/** Turn type into a function type.
* @pre this is a non-dependent method type.
+ * @param drop The number of trailing parameters that should be dropped
+ * when forming the function type.
*/
- def toFunctionType(implicit ctx: Context): Type = this match {
+ def toFunctionType(dropLast: Int = 0)(implicit ctx: Context): Type = this match {
case mt @ MethodType(_, formals) if !mt.isDependent =>
- defn.FunctionType(formals mapConserve (_.underlyingIfRepeated(mt.isJava)), mt.resultType)
+ val formals1 = if (dropLast == 0) formals else formals dropRight dropLast
+ defn.FunctionType(
+ formals1 mapConserve (_.underlyingIfRepeated(mt.isJava)), mt.resultType)
}
/** The signature of this type. This is by default NotAMethod,
@@ -1169,7 +1173,12 @@ object Types {
else loadDenot
}
if (ctx.typerState.ephemeral) record("ephemeral cache miss: loadDenot")
- else {
+ else if (d.exists) {
+ // Avoid storing NoDenotations in the cache - we will not be able to recover from
+ // them. The situation might arise that a type has NoDenotation in some later
+ // phase but a defined denotation earlier (e.g. a TypeRef to an abstract type
+ // is undefined after erasure.) We need to be able to do time travel back and
+ // forth also in these cases.
lastDenotation = d
lastSymbol = d.symbol
checkedPeriod = ctx.period
diff --git a/src/dotty/tools/dotc/core/pickling/ClassfileParser.scala b/src/dotty/tools/dotc/core/pickling/ClassfileParser.scala
index 193c872f1..67f825502 100644
--- a/src/dotty/tools/dotc/core/pickling/ClassfileParser.scala
+++ b/src/dotty/tools/dotc/core/pickling/ClassfileParser.scala
@@ -226,7 +226,9 @@ class ClassfileParser(
while (!isDelimiter(sig(index))) { index += 1 }
sig.slice(start, index)
}
- def sig2type(tparams: immutable.Map[Name,Symbol], skiptvs: Boolean): Type = {
+ // Warning: sigToType contains nested completers which might be forced in a later run!
+ // So local methods need their own ctx parameters.
+ def sig2type(tparams: immutable.Map[Name,Symbol], skiptvs: Boolean)(implicit ctx: Context): Type = {
val tag = sig(index); index += 1
(tag: @switch) match {
case BYTE_TAG => defn.ByteType
@@ -321,7 +323,7 @@ class ClassfileParser(
}
} // sig2type(tparams, skiptvs)
- def sig2typeBounds(tparams: immutable.Map[Name, Symbol], skiptvs: Boolean): Type = {
+ def sig2typeBounds(tparams: immutable.Map[Name, Symbol], skiptvs: Boolean)(implicit ctx: Context): Type = {
val ts = new ListBuffer[Type]
while (sig(index) == ':') {
index += 1
diff --git a/src/dotty/tools/dotc/printing/RefinedPrinter.scala b/src/dotty/tools/dotc/printing/RefinedPrinter.scala
index d66d93a16..d9e248e40 100644
--- a/src/dotty/tools/dotc/printing/RefinedPrinter.scala
+++ b/src/dotty/tools/dotc/printing/RefinedPrinter.scala
@@ -180,7 +180,8 @@ class RefinedPrinter(_ctx: Context) extends PlainPrinter(_ctx) {
def annotText(tree: untpd.Tree): Text = "@" ~ constrText(tree) // DD
- def useSymbol = ctx.isAfterTyper(ctx.phase) && tree.symbol != null && tree.symbol.exists
+ def useSymbol =
+ tree.hasType && tree.symbol.exists && ctx.settings.YprintSyms.value
def modText(mods: untpd.Modifiers, kw: String): Text = { // DD
val suppressKw = if (ownerIsClass) mods is ParamAndLocal else mods is Param
@@ -209,6 +210,9 @@ class RefinedPrinter(_ctx: Context) extends PlainPrinter(_ctx) {
if (ctx.settings.uniqid.value && tree.hasType && tree.symbol.exists) s"#${tree.symbol.id}" else ""
}
+ def nameIdText(tree: untpd.NameTree): Text =
+ toText(tree.name) ~ idText(tree)
+
import untpd._
var txt: Text = tree match {
@@ -219,8 +223,8 @@ class RefinedPrinter(_ctx: Context) extends PlainPrinter(_ctx) {
case tp: NamedType if name != nme.WILDCARD => toTextPrefix(tp.prefix) ~ selectionString(tp)
case _ => toText(name)
}
- case Select(qual, name) =>
- toTextLocal(qual) ~ ("." ~ toText(name) provided name != nme.CONSTRUCTOR)
+ case tree @ Select(qual, name) =>
+ toTextLocal(qual) ~ ("." ~ nameIdText(tree) provided name != nme.CONSTRUCTOR)
case This(name) =>
optDotPrefix(name) ~ "this" ~ idText(tree)
case Super(This(name), mix) =>
@@ -297,15 +301,15 @@ class RefinedPrinter(_ctx: Context) extends PlainPrinter(_ctx) {
toTextLocal(extractor) ~
"(" ~ toTextGlobal(patterns, ", ") ~ ")" ~
("(" ~ toTextGlobal(implicits, ", ") ~ ")" provided implicits.nonEmpty)
- case ValDef(mods, name, tpt, rhs) =>
+ case tree @ ValDef(mods, name, tpt, rhs) =>
dclTextOr {
- modText(mods, if (mods is Mutable) "var" else "val") ~~ toText(name) ~
+ modText(mods, if (mods is Mutable) "var" else "val") ~~ nameIdText(tree) ~
optAscription(tpt)
} ~ optText(rhs)(" = " ~ _)
- case DefDef(mods, name, tparams, vparamss, tpt, rhs) =>
+ case tree @ DefDef(mods, name, tparams, vparamss, tpt, rhs) =>
atOwner(tree) {
dclTextOr {
- val first = modText(mods, "def") ~~ toText(name) ~ tparamsText(tparams)
+ val first = modText(mods, "def") ~~ nameIdText(tree) ~ tparamsText(tparams)
addVparamssText(first, vparamss) ~ optAscription(tpt)
} ~ optText(rhs)(" = " ~ _)
}
@@ -314,11 +318,11 @@ class RefinedPrinter(_ctx: Context) extends PlainPrinter(_ctx) {
def typeDefText(rhsText: Text) =
dclTextOr {
val rhsText1 = if (tree.hasType) toText(tree.symbol.info) else rhsText
- modText(mods, "type") ~~ toText(name) ~ tparamsText(tree.tparams) ~ rhsText1
+ modText(mods, "type") ~~ nameIdText(tree) ~ tparamsText(tree.tparams) ~ rhsText1
}
rhs match {
case impl: Template =>
- modText(mods, if (mods is Trait) "trait" else "class") ~~ toText(name) ~~ idText(tree) ~ toText(impl) ~
+ modText(mods, if (mods is Trait) "trait" else "class") ~~ nameIdText(tree) ~ toText(impl) ~
(if (tree.hasType && ctx.settings.verbose.value) s"[decls = ${tree.symbol.info.decls}]" else "")
case rhs: TypeBoundsTree =>
typeDefText(toText(rhs))
@@ -326,10 +330,11 @@ class RefinedPrinter(_ctx: Context) extends PlainPrinter(_ctx) {
typeDefText(optText(rhs)(" = " ~ _))
}
}
- case Template(DefDef(mods, _, tparams, vparamss, _, _), parents, self, stats) =>
+ case Template(constr @ DefDef(mods, _, tparams, vparamss, _, rhs), parents, self, stats) =>
val tparamsTxt = tparamsText(tparams)
+ val primaryConstrs = if (rhs.isEmpty) Nil else constr :: Nil
val prefix: Text =
- if (vparamss.isEmpty) tparamsTxt
+ if (vparamss.isEmpty || primaryConstrs.nonEmpty) tparamsTxt
else {
var modsText = modText(mods, "")
if (mods.hasAnnotations && !mods.hasFlags) modsText = modsText ~~ " this"
@@ -340,7 +345,7 @@ class RefinedPrinter(_ctx: Context) extends PlainPrinter(_ctx) {
val selfName = if (self.name == nme.WILDCARD) "this" else self.name.toString
(selfName ~ optText(self.tpt)(": " ~ _) ~ " =>").close
} provided !self.isEmpty
- val bodyText = "{" ~~ selfText ~~ toTextGlobal(stats, "\n") ~ "}"
+ val bodyText = "{" ~~ selfText ~~ toTextGlobal(primaryConstrs ::: stats, "\n") ~ "}"
prefix ~~ (" extends" provided ownerIsClass) ~~ parentsText ~~ bodyText
case Import(expr, selectors) =>
def selectorText(sel: Tree): Text = sel match {
@@ -366,9 +371,9 @@ class RefinedPrinter(_ctx: Context) extends PlainPrinter(_ctx) {
"<empty>"
case TypedSplice(t) =>
toText(t)
- case ModuleDef(mods, name, impl) =>
+ case tree @ ModuleDef(mods, name, impl) =>
atOwner(tree) {
- modText(mods, "object") ~~ toText(name) ~ toText(impl)
+ modText(mods, "object") ~~ nameIdText(tree) ~ toText(impl)
}
case SymbolLit(str) =>
"'" + str
diff --git a/src/dotty/tools/dotc/transform/CapturedVars.scala b/src/dotty/tools/dotc/transform/CapturedVars.scala
index d5bd56bc3..91b81fea5 100644
--- a/src/dotty/tools/dotc/transform/CapturedVars.scala
+++ b/src/dotty/tools/dotc/transform/CapturedVars.scala
@@ -17,31 +17,26 @@ import SymUtils._
import collection.{ mutable, immutable }
import collection.mutable.{ LinkedHashMap, LinkedHashSet, TreeSet }
-class CapturedVars extends MiniPhaseTransform with SymTransformer { thisTransformer =>
+class CapturedVars extends MiniPhaseTransform with SymTransformer { thisTransform =>
import ast.tpd._
/** the following two members override abstract members in Transform */
val phaseName: String = "capturedVars"
- override def treeTransformPhase = thisTransformer.next
+ override def treeTransformPhase = thisTransform.next
private var captured: mutable.HashSet[Symbol] = _
- private class CollectCaptured(implicit ctx: Context) extends TreeAccumulator[Symbol] {
- def apply(enclMeth: Symbol, tree: Tree) = {
- tree match {
- case id: Ident =>
- val sym = id.symbol
- if (sym.is(Mutable, butNot = Method) && sym.owner.isTerm && sym.enclosingMethod != enclMeth) {
- ctx.log(i"capturing $sym in ${sym.enclosingMethod}, referenced from $enclMeth")
- captured += sym
- }
- case tree: DefTree if tree.symbol.exists =>
- foldOver(tree.symbol.enclosingMethod, tree)
- case _ =>
- foldOver(enclMeth, tree)
- }
- enclMeth
+ private class CollectCaptured(implicit ctx: Context) extends EnclosingMethodTraverser {
+ def traverse(enclMeth: Symbol, tree: Tree) = tree match {
+ case id: Ident =>
+ val sym = id.symbol
+ if (sym.is(Mutable, butNot = Method) && sym.owner.isTerm && sym.enclosingMethod != enclMeth) {
+ ctx.log(i"capturing $sym in ${sym.enclosingMethod}, referenced from $enclMeth")
+ captured += sym
+ }
+ case _ =>
+ foldOver(enclMeth, tree)
}
def runOver(tree: Tree) = {
captured = mutable.HashSet()
@@ -50,7 +45,7 @@ class CapturedVars extends MiniPhaseTransform with SymTransformer { thisTransfor
}
override def init(implicit ctx: Context, info: TransformerInfo): Unit =
- (new CollectCaptured).runOver(ctx.compilationUnit.tpdTree)
+ (new CollectCaptured)(ctx.withPhase(thisTransform)).runOver(ctx.compilationUnit.tpdTree)
override def transformSym(sd: SymDenotation)(implicit ctx: Context): SymDenotation =
if (captured(sd.symbol)) {
@@ -69,7 +64,7 @@ class CapturedVars extends MiniPhaseTransform with SymTransformer { thisTransfor
}
def capturedType(vble: Symbol)(implicit ctx: Context): Type = {
- val oldInfo = vble.denot(ctx.withPhase(thisTransformer)).info
+ val oldInfo = vble.denot(ctx.withPhase(thisTransform)).info
refCls(oldInfo.classSymbol, vble.isVolatile).typeRef
}
@@ -91,7 +86,7 @@ class CapturedVars extends MiniPhaseTransform with SymTransformer { thisTransfor
override def transformIdent(id: Ident)(implicit ctx: Context, info: TransformerInfo): Tree = {
val vble = id.symbol
if (captured(vble))
- (id select nme.elem).ensureConforms(vble.denot(ctx.withPhase(thisTransformer)).info)
+ (id select nme.elem).ensureConforms(vble.denot(ctx.withPhase(thisTransform)).info)
else id
}
diff --git a/src/dotty/tools/dotc/transform/Constructors.scala b/src/dotty/tools/dotc/transform/Constructors.scala
index 34c90fdc2..7bde1ba4f 100644
--- a/src/dotty/tools/dotc/transform/Constructors.scala
+++ b/src/dotty/tools/dotc/transform/Constructors.scala
@@ -1,27 +1,235 @@
-package dotty.tools.dotc.transform
+package dotty.tools.dotc
+package transform
+import core._
import TreeTransforms._
import dotty.tools.dotc.ast.tpd._
import dotty.tools.dotc.core.Contexts.Context
import dotty.tools.dotc.core.StdNames._
+import Phases._
+import ast._
+import Trees._
+import Flags._
+import SymUtils._
+import Symbols._
+import SymDenotations._
+import Types._
+import Decorators._
+import DenotTransformers._
+import util.Positions._
+import collection.mutable
-/** This transform moves initializers from body to constructor.
- * Right now it's a dummy.
- * Awaiting for real implemetation
+/** This transform
+ * - moves initializers from body to constructor.
+ * - makes all supercalls explicit
+ * - also moves private fields that are accessed only from constructor
+ * into the constructor if possible.
*/
-class Constructors extends MiniPhaseTransform {
+class Constructors extends MiniPhaseTransform with SymTransformer { thisTransform =>
+ import tpd._
override def phaseName: String = "constructors"
- override def transformDefDef(tree: DefDef)(implicit ctx: Context, info: TransformerInfo): Tree = {
- if(tree.symbol.isClassConstructor) {
- val claz = tree.symbol.enclosingClass.asClass
- val zuper = claz.info.parents.head.typeSymbol
- cpy.DefDef(tree)(rhs = {
- val parentCall =
- Super(This(claz), tpnme.EMPTY, true).select(zuper.primaryConstructor).appliedToNone
- if(tree.rhs.isEmpty) parentCall
- else Block(List(parentCall), tree.rhs)
- })
- } else tree
+ override def runsAfter: Set[Class[_ <: Phase]] = Set(classOf[Erasure])
+
+ override def treeTransformPhase = thisTransform.next
+
+ /** Symbols that are owned by either <local dummy> or a class field move into the
+ * primary constructor.
+ */
+ override def transformSym(sym: SymDenotation)(implicit ctx: Context): SymDenotation = {
+ def ownerBecomesConstructor(owner: Symbol): Boolean =
+ (owner.isLocalDummy || owner.isTerm && !owner.is(Method | Lazy)) &&
+ owner.owner.isClass
+ if (ownerBecomesConstructor(sym.owner))
+ sym.copySymDenotation(owner = sym.owner.enclosingClass.primaryConstructor)
+ else sym
+ }
+
+ /** @return true if after ExplicitOuter, all references from this tree go via an
+ * outer link, so no parameter accessors need to be rewired to parameters
+ */
+ private def noDirectRefsFrom(tree: Tree)(implicit ctx: Context) =
+ tree.isDef && tree.symbol.isClass && !tree.symbol.is(InSuperCall)
+
+ /** Class members that can be eliminated if referenced only from their own
+ * constructor.
+ */
+ private def mightBeDropped(sym: Symbol)(implicit ctx: Context) =
+ sym.is(Private, butNot = KeeperFlags) && !sym.is(MutableParamAccessor)
+
+ private final val KeeperFlags = Method | Lazy | NotJavaPrivate
+ private final val MutableParamAccessor = allOf(Mutable, ParamAccessor)
+
+ override def transformTemplate(tree: Template)(implicit ctx: Context, info: TransformerInfo): Tree = {
+ val cls = ctx.owner.asClass
+ val constr @ DefDef(_, nme.CONSTRUCTOR, Nil, vparams :: Nil, _, EmptyTree) = tree.constr
+
+ // Produce aligned accessors and constructor parameters. We have to adjust
+ // for any outer parameters, which are last in the sequence of original
+ // parameter accessors but should come first in the constructor parameter list.
+ var accessors = cls.paramAccessors.filterNot(_.isSetter)
+ var vparamsWithOuter = vparams
+ if (!accessors.hasSameLengthAs(vparams)) {
+ accessors.reverse match {
+ case last :: _ if (last.name == nme.OUTER) =>
+ accessors = last :: accessors.init // align wth calling convention
+ vparamsWithOuter = ValDef(last.asTerm) :: vparams
+ case _ =>
+ }
+ assert(accessors.hasSameLengthAs(vparamsWithOuter),
+ i"lengths differ for $cls, param accs = $accessors, params = ($vparamsWithOuter%, %)")
+ }
+ val paramSyms = vparamsWithOuter map (_.symbol)
+
+ // Adjustments performed when moving code into the constructor:
+ // (1) Replace references to param accessors by constructor parameters
+ // except possibly references to mutable variables, if `excluded = Mutable`.
+ // (Mutable parameters should be replaced only during the super call)
+ // (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.owner.isConstructor) ref(sym).withPos(tree.pos) else tree
+ case Apply(fn, Nil) =>
+ val fn1 = transform(fn)
+ if ((fn1 ne fn) && fn1.symbol.is(Param) && fn1.symbol.owner.isPrimaryConstructor)
+ fn1 // in this case, fn1.symbol was an alias for a parameter in a superclass
+ else cpy.Apply(tree)(fn1, Nil)
+ case _ =>
+ 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)
+ }
+ }
+
+ val superCalls = new mutable.ListBuffer[Tree]
+
+ // If parent is a constructor call, pull out the call into a separate
+ // supercall constructor, which gets appended to `superCalls`, and keep
+ // only the type.
+ def normalizeParent(tree: Tree) = tree match {
+ case superApp @ Apply(
+ superSel @ Select(
+ superNew @ New(superType),
+ nme.CONSTRUCTOR),
+ superArgs) =>
+ val toClass = !superType.symbol.is(Trait)
+ val mappedArgs = superArgs.map(intoConstr(_, inSuperCall = toClass))
+ val receiver =
+ if (toClass) Super(This(cls), tpnme.EMPTY, inConstrCall = true)
+ else This(cls)
+ superCalls +=
+ cpy.Apply(superApp)(
+ receiver.withPos(superNew.pos)
+ .select(superSel.symbol).withPos(superSel.pos),
+ mappedArgs)
+ superType
+ case tree: TypeTree => tree
+ }
+ val parentTypeTrees = tree.parents.map(normalizeParent)
+
+ // Collect all private parameter accessors and value definitions that need
+ // to be retained. There are several reasons why a parameter accessor or
+ // definition might need to be retained:
+ // 1. It is accessed after the constructor has finished
+ // 2. It is accessed before it is defined
+ // 3. It is accessed on an object other than `this`
+ // 4. It is a mutable parameter accessor
+ // 5. It is has a wildcard initializer `_`
+ object usage extends TreeTraverser {
+ private var inConstr: Boolean = true
+ private val seen = mutable.Set[Symbol](accessors: _*)
+ val retained = mutable.Set[Symbol]()
+ def dropped: collection.Set[Symbol] = seen -- retained
+ override def traverse(tree: Tree) = {
+ val sym = tree.symbol
+ tree match {
+ case Ident(_) | Select(This(_), _) if inConstr && seen(tree.symbol) =>
+ // could refer to definition in constructors, so no retention necessary
+ case tree: RefTree =>
+ if (mightBeDropped(sym)) retained += sym
+ case _ =>
+ }
+ if (!noDirectRefsFrom(tree)) traverseChildren(tree)
+ }
+ def collect(stats: List[Tree]): Unit = stats foreach {
+ case stat: ValDef if !stat.symbol.is(Lazy) =>
+ traverse(stat)
+ if (mightBeDropped(stat.symbol))
+ (if (isWildcardStarArg(stat.rhs)) retained else seen) += stat.symbol
+ case stat: DefTree =>
+ inConstr = false
+ traverse(stat)
+ inConstr = true
+ case stat =>
+ traverse(stat)
+ }
+ }
+ usage.collect(superCalls.toList ++ tree.body)
+
+ def isRetained(acc: Symbol) = !mightBeDropped(acc) || usage.retained(acc)
+
+ val constrStats, clsStats = new mutable.ListBuffer[Tree]
+
+ def assign(vble: Symbol, rhs: Tree): Tree =
+ if (cls is Trait) ref(vble.setter).appliedTo(rhs)
+ else Assign(ref(vble), rhs)
+
+ // Split class body into statements that go into constructor and
+ // definitions that are kept as members of the class.
+ def splitStats(stats: List[Tree]): Unit = stats match {
+ case stat :: stats1 =>
+ stat match {
+ case stat @ ValDef(mods, name, tpt, rhs) if !stat.symbol.is(Lazy) =>
+ val sym = stat.symbol
+ if (isRetained(sym)) {
+ if (!rhs.isEmpty && !isWildcardArg(rhs))
+ constrStats += assign(sym, intoConstr(rhs)).withPos(stat.pos)
+ clsStats += cpy.ValDef(stat)(rhs = EmptyTree)
+ }
+ else if (!rhs.isEmpty) {
+ sym.copySymDenotation(
+ initFlags = sym.flags &~ Private,
+ owner = constr.symbol).installAfter(thisTransform)
+ constrStats += intoConstr(stat)
+ }
+ case _: DefTree =>
+ clsStats += stat
+ case _ =>
+ constrStats += intoConstr(stat)
+ }
+ splitStats(stats1)
+ case Nil =>
+ (Nil, Nil)
+ }
+ splitStats(tree.body)
+
+ val accessorFields = accessors.filterNot(_ is Method)
+
+ // The initializers for the retained accessors */
+ val copyParams = accessorFields.filter(isRetained).map(acc =>
+ assign(acc, ref(acc.subst(accessors, paramSyms))).withPos(tree.pos))
+
+ // Drop accessors that are not retained from class scope
+ val dropped = usage.dropped
+ if (dropped.nonEmpty) {
+ val clsInfo = cls.classInfo // TODO investigate: expand clsInfo to cls.info => dotty type error
+ cls.copy(
+ info = clsInfo.derivedClassInfo(
+ decls = clsInfo.decls.filteredScope(!dropped.contains(_))))
+ }
+
+ cpy.Template(tree)(
+ constr = cpy.DefDef(constr)(
+ rhs = Block(superCalls.toList ::: copyParams ::: constrStats.toList, unitLiteral)),
+ parents = parentTypeTrees,
+ body = clsStats.toList)
}
} \ No newline at end of file
diff --git a/src/dotty/tools/dotc/transform/ElimByName.scala b/src/dotty/tools/dotc/transform/ElimByName.scala
index 5925ffa72..1d0307398 100644
--- a/src/dotty/tools/dotc/transform/ElimByName.scala
+++ b/src/dotty/tools/dotc/transform/ElimByName.scala
@@ -4,6 +4,7 @@ package transform
import TreeTransforms._
import core.DenotTransformers._
import core.Symbols._
+import core.SymDenotations._
import core.Contexts._
import core.Types._
import core.Flags._
@@ -75,13 +76,29 @@ class ElimByName extends MiniPhaseTransform with InfoTransformer { thisTransform
cpy.Apply(tree)(tree.fun, args1)
}
- override def transformIdent(tree: Ident)(implicit ctx: Context, info: TransformerInfo): Tree = {
- val origDenot = originalDenotation(tree)
- if ((origDenot is Param) && (origDenot.info.isInstanceOf[ExprType]))
+ /** If denotation had an ExprType before, it now gets a function type */
+ private def exprBecomesFunction(symd: SymDenotation)(implicit ctx: Context) =
+ (symd is Param) || (symd is (ParamAccessor, butNot = Method))
+
+ /** Map `tree` to `tree.apply()` is `ftree` was of ExprType and becomes now a function */
+ private def applyIfFunction(tree: Tree, ftree: Tree)(implicit ctx: Context) = {
+ val origDenot = originalDenotation(ftree)
+ if (exprBecomesFunction(origDenot) && (origDenot.info.isInstanceOf[ExprType]))
tree.select(defn.Function0_apply).appliedToNone
else tree
}
+ override def transformIdent(tree: Ident)(implicit ctx: Context, info: TransformerInfo): Tree =
+ applyIfFunction(tree, tree)
+
+ override def transformTypeApply(tree: TypeApply)(implicit ctx: Context, info: TransformerInfo): Tree = tree match {
+ case TypeApply(Select(_, nme.asInstanceOf_), arg :: Nil) =>
+ // tree might be of form e.asInstanceOf[x.type] where x becomes a function.
+ // See pos/t296.scala
+ applyIfFunction(tree, arg)
+ case _ => tree
+ }
+
def elimByNameParams(tp: Type)(implicit ctx: Context): Type = tp match {
case tp: PolyType =>
tp.derivedPolyType(tp.paramNames, tp.paramBounds, elimByNameParams(tp.resultType))
@@ -98,6 +115,6 @@ class ElimByName extends MiniPhaseTransform with InfoTransformer { thisTransform
}
def transformInfo(tp: Type, sym: Symbol)(implicit ctx: Context): Type =
- if (sym is Param) transformParamInfo(tp)
+ if (exprBecomesFunction(sym)) transformParamInfo(tp)
else elimByNameParams(tp)
}
diff --git a/src/dotty/tools/dotc/transform/Erasure.scala b/src/dotty/tools/dotc/transform/Erasure.scala
index aae1ff1ba..ed853f8f1 100644
--- a/src/dotty/tools/dotc/transform/Erasure.scala
+++ b/src/dotty/tools/dotc/transform/Erasure.scala
@@ -195,8 +195,8 @@ object Erasure extends TypeTestsCasts{
// See SI-2386 for one example of when this might be necessary.
cast(ref(defn.runtimeMethod(nme.toObjectArray)).appliedTo(tree), pt)
case _ =>
- ctx.log(s"casting from ${tree.showSummary}: ${tree.tpe.show} to ${pt.show}")
- tree.asInstance(pt)
+ if (pt.isPrimitiveValueType) primitiveConversion(tree, pt.classSymbol)
+ else tree.asInstance(pt)
}
}
@@ -234,11 +234,14 @@ object Erasure extends TypeTestsCasts{
class Typer extends typer.ReTyper with NoChecking {
import Boxing._
- def erasedType(tree: untpd.Tree)(implicit ctx: Context): Type = erasure(tree.typeOpt)
+ def erasedType(tree: untpd.Tree)(implicit ctx: Context): Type = tree.typeOpt match {
+ case tp: TermRef if tree.isTerm => erasedRef(tp)
+ case tp => erasure(tp)
+ }
override def promote(tree: untpd.Tree)(implicit ctx: Context): tree.ThisTree[Type] = {
assert(tree.hasType)
- val erased = erasedType(tree)(ctx.withPhase(ctx.erasurePhase))
+ val erased = erasedType(tree)
ctx.log(s"promoting ${tree.show}: ${erased.showWithUnderlying()}")
tree.withType(erased)
}
@@ -364,10 +367,9 @@ object Erasure extends TypeTestsCasts{
override def typedDefDef(ddef: untpd.DefDef, sym: Symbol)(implicit ctx: Context) = {
val ddef1 = untpd.cpy.DefDef(ddef)(
tparams = Nil,
- vparamss = if (ddef.vparamss.isEmpty) Nil :: Nil else ddef.vparamss,
+ vparamss = ddef.vparamss.flatten :: Nil,
tpt = // keep UnitTypes intact in result position
- if (ddef.tpt.typeOpt isRef defn.UnitClass) untpd.TypeTree(defn.UnitType) withPos ddef.tpt.pos
- else ddef.tpt)
+ untpd.TypedSplice(TypeTree(eraseResult(ddef.tpt.typeOpt)).withPos(ddef.tpt.pos)))
super.typedDefDef(ddef1, sym)
}
diff --git a/src/dotty/tools/dotc/transform/ExplicitOuter.scala b/src/dotty/tools/dotc/transform/ExplicitOuter.scala
index aeda6a1d3..179f8d712 100644
--- a/src/dotty/tools/dotc/transform/ExplicitOuter.scala
+++ b/src/dotty/tools/dotc/transform/ExplicitOuter.scala
@@ -112,7 +112,7 @@ object ExplicitOuter {
def ensureOuterAccessors(cls: ClassSymbol)(implicit ctx: Context): Unit = {
//todo: implementing #165 would simplify this logic
val prevPhase = ctx.phase.prev
- assert(prevPhase.id <= ctx.explicitOuter.id, "can add $outer symbols only before ExplicitOuter")
+ assert(prevPhase.id <= ctx.explicitOuterPhase.id, "can add $outer symbols only before ExplicitOuter")
assert(prevPhase.isInstanceOf[DenotTransformer], "adding outerAccessors requires being DenotTransformer")
if (!hasOuter(cls)) {
newOuterAccessors(cls).foreach(_.enteredAfter(prevPhase.asInstanceOf[DenotTransformer]))
@@ -190,7 +190,7 @@ object ExplicitOuter {
*/
def referencesOuter(cls: Symbol, tree: Tree)(implicit ctx: Context): Boolean = {
def isOuter(sym: Symbol) =
- sym != cls && !sym.isStaticOwner && cls.isContainedIn(sym)
+ !sym.isStaticOwner && cls.isProperlyContainedIn(sym)
tree match {
case thisTree @ This(_) =>
isOuter(thisTree.symbol)
diff --git a/src/dotty/tools/dotc/transform/GettersSetters.scala b/src/dotty/tools/dotc/transform/GettersSetters.scala
new file mode 100644
index 000000000..772a63e52
--- /dev/null
+++ b/src/dotty/tools/dotc/transform/GettersSetters.scala
@@ -0,0 +1,118 @@
+package dotty.tools.dotc
+package transform
+
+import core._
+import DenotTransformers.SymTransformer
+import Phases.Phase
+import Contexts.Context
+import SymDenotations.SymDenotation
+import Types._
+import Symbols._
+import SymUtils._
+import Constants._
+import ast.Trees._
+import TreeTransforms._
+import NameOps._
+import Flags._
+import Decorators._
+
+/** Performs the following rewritings:
+ *
+ * val x: T = e
+ * --> private val x_L: T = e
+ * <stable> def x: T = x_L (if in class)
+ * --> private notJavaPrivate var x_L: T = e
+ * <stable> def x: T = x_L
+ * private notJavaPrivate def x_=(x: T): Unit = x_L = x (if in trait)
+ * var x: T = e
+ * --> private var x_L: T = e
+ * def x: T = x_L
+ * def x_=(x: T): Unit = x_L = x (if in class or trait)
+ * lazy val x: T = e
+ * --> lazy def x = e
+ *
+ * Furthermore, assignements to mutable vars are replaced by setter calls
+ *
+ * p.x = e
+ * --> p.x_=(e)
+ */
+class GettersSetters extends MiniPhaseTransform with SymTransformer { thisTransform =>
+ import ast.tpd._
+
+ override def phaseName = "gettersSetters"
+
+ override def treeTransformPhase = thisTransform.next
+
+ override def transformSym(d: SymDenotation)(implicit ctx: Context): SymDenotation = {
+ def noGetterNeeded =
+ d.is(Method | Param | JavaDefined) ||
+ d.initial.asInstanceOf[SymDenotation].is(PrivateLocal) && !d.owner.is(Trait) ||
+ d.is(Module) && d.isStatic ||
+ d.isSelfSym
+ if (d.isTerm && (d.owner.isClass || d.is(Lazy)) && d.info.isValueType && !noGetterNeeded) {
+ val maybeStable = if (d.isStable) Stable else EmptyFlags
+ if (d.name.toString == "_") println(i"make accessor $d in ${d.owner} ${d.symbol.id}")
+ d.copySymDenotation(
+ initFlags = d.flags | maybeStable | AccessorCreationFlags,
+ info = ExprType(d.info))
+ }
+ else d
+ }
+
+ override def transformValDef(tree: ValDef)(implicit ctx: Context, info: TransformerInfo): Tree = {
+ if (tree.symbol is Method) {
+ val getter = tree.symbol.asTerm
+ assert(getter is Accessor)
+ if (getter.is(Lazy | Deferred | ParamAccessor)) DefDef(getter, tree.rhs)
+ else {
+ val inTrait = getter.owner.is(Trait)
+ val maybePrivate =
+ if (inTrait) Private | NotJavaPrivate
+ else if (getter.owner.isClass) Private
+ else EmptyFlags
+ val maybeMutable =
+ if (inTrait || getter.is(Mutable)) Mutable
+ else EmptyFlags
+ val field = ctx.newSymbol(
+ owner = ctx.owner,
+ name = getter.name.fieldName,
+ flags = maybePrivate | maybeMutable,
+ info = getter.info.resultType).enteredAfter(thisTransform)
+ assert(tree.rhs.tpe.exists, tree.show)
+ val fieldDef =
+ cpy.ValDef(tree)(
+ mods = tree.mods & EmptyFlags | field.flags,
+ name = field.name,
+ rhs = tree.rhs.changeOwner(getter, field).ensureConforms(field.info.widen)
+ ).withType(field.valRef)
+ val rhs = ref(field)
+ assert(rhs.hasType)
+ val getterDef = DefDef(getter, rhs.ensureConforms(getter.info.widen))
+ if (!getter.is(Mutable) && inTrait) { // add a setter anyway, will be needed for mixin
+ val setter = ctx.newSymbol(
+ owner = ctx.owner,
+ name = getter.name.traitSetterName,
+ flags = (getter.flags & AccessFlags) | Accessor | maybePrivate,
+ info = MethodType(field.info :: Nil, defn.UnitType)).enteredAfter(thisTransform)
+ val setterDef = DefDef(setter.asTerm, vrefss => Assign(ref(field), vrefss.head.head))
+ Thicket(fieldDef, getterDef, setterDef)
+ }
+ else Thicket(fieldDef, getterDef)
+ }
+ }
+ else tree
+ }
+
+ override def transformDefDef(tree: DefDef)(implicit ctx: Context, info: TransformerInfo): Tree =
+ if (tree.symbol.isSetter && !tree.symbol.is(Deferred | ParamAccessor)) {
+ val Literal(Constant(())) = tree.rhs
+ val initializer = Assign(ref(tree.symbol.field), ref(tree.vparamss.head.head.symbol))
+ assert(initializer.hasType)
+ cpy.DefDef(tree)(rhs = initializer)
+ }
+ else tree
+
+ override def transformAssign(tree: Assign)(implicit ctx: Context, info: TransformerInfo): Tree =
+ if (tree.lhs.symbol is Method) tree.lhs.becomes(tree.rhs)
+ else tree
+} \ No newline at end of file
diff --git a/src/dotty/tools/dotc/transform/LambdaLift.scala b/src/dotty/tools/dotc/transform/LambdaLift.scala
new file mode 100644
index 000000000..a08df1c33
--- /dev/null
+++ b/src/dotty/tools/dotc/transform/LambdaLift.scala
@@ -0,0 +1,404 @@
+package dotty.tools.dotc
+package transform
+
+import TreeTransforms._
+import core.DenotTransformers._
+import core.Symbols._
+import core.Contexts._
+import core.Types._
+import core.Flags._
+import core.Decorators._
+import core.StdNames.nme
+import core.Names._
+import core.NameOps._
+import core.Phases._
+import ast.Trees._
+import SymUtils._
+import ExplicitOuter.outer
+import util.Attachment
+import util.NameTransformer
+import util.Positions._
+import collection.{ mutable, immutable }
+import collection.mutable.{ HashMap, HashSet, LinkedHashMap, LinkedHashSet, TreeSet }
+
+object LambdaLift {
+ private val NJ = NameTransformer.NAME_JOIN_STRING
+ private class NoPath extends Exception
+}
+
+class LambdaLift extends MiniPhaseTransform with IdentityDenotTransformer { thisTransform =>
+ import LambdaLift._
+ import ast.tpd._
+
+ /** the following two members override abstract members in Transform */
+ val phaseName: String = "lambdalift"
+
+ override def runsAfter: Set[Class[_ <: Phase]] = Set(classOf[Constructors])
+ // Constructors has to happen before LambdaLift because the lambda lift logic
+ // becomes simpler if it can assume that parameter accessors have already been
+ // converted to parameters in super calls. Without this it is very hard to get
+ // lambda lift for super calls right. Witness the implementation restrictions to
+ // this effect in scalac.
+
+ override def treeTransformPhase = thisTransform.next
+ override def relaxedTyping = true
+
+ private type SymSet = TreeSet[Symbol]
+
+ /** A map storing free variables of functions and classes */
+ private val free = new LinkedHashMap[Symbol, SymSet]
+
+ /** A map storing the free variable proxies of functions and classes.
+ * For every function and class, this is a map from the free variables
+ * of that function or class to the proxy symbols accessing them.
+ */
+ private val proxyMap = new LinkedHashMap[Symbol, Map[Symbol, Symbol]]
+
+ /** A hashtable storing calls between functions */
+ private val called = new LinkedHashMap[Symbol, SymSet]
+
+ /** Symbols that are called from an inner class. */
+ private val calledFromInner = new HashSet[Symbol]
+
+ /** A map from local methods and classes to the owners to which they will be lifted as members.
+ * For methods and classes that do not have any dependencies this will be the enclosing package.
+ * symbols with packages as lifted owners will subsequently represented as static
+ * members of their toplevel class.
+ */
+ private val liftedOwner = new HashMap[Symbol, Symbol]
+
+ /** Buffers for lifted out classes and methods, indexed by owner */
+ private val liftedDefs = new HashMap[Symbol, mutable.ListBuffer[Tree]]
+
+ /** A flag to indicate whether new free variables have been found */
+ private var changedFreeVars: Boolean = _
+
+ /** A flag to indicate whether lifted owners have changed */
+ private var changedLiftedOwner: Boolean = _
+
+ private val ord: Ordering[Symbol] = Ordering.by((_: Symbol).id) // Dotty deviation: Type annotation needed. TODO: figure out why
+ private def newSymSet = TreeSet.empty[Symbol](ord)
+
+ private def symSet(f: LinkedHashMap[Symbol, SymSet], sym: Symbol): SymSet =
+ f.getOrElseUpdate(sym, newSymSet)
+
+ def proxies(sym: Symbol): List[Symbol] = {
+ val pm: Map[Symbol, Symbol] = proxyMap.getOrElse(sym, Map.empty) // Dotty deviation: Type annotation needed. TODO: figure out why
+ free.getOrElse(sym, Nil).toList.map(pm)
+ }
+
+ def narrowLiftedOwner(sym: Symbol, owner: Symbol)(implicit ctx: Context) = {
+ println(i"narrow lifted $sym")
+ if (sym.owner.skipConstructor.isTerm &&
+ owner.isProperlyContainedIn(liftedOwner(sym))) {
+ changedLiftedOwner = true
+ liftedOwner(sym) = owner
+ }
+ }
+
+ /** Mark symbol `sym` as being free in `enclosure`, unless `sym`
+ * is defined in `enclosure` or there is a class between `enclosure`s owner
+ * and the owner of `sym`.
+ * Return `true` if there is no class between `enclosure` and
+ * the owner of sym.
+ * pre: sym.owner.isTerm, (enclosure.isMethod || enclosure.isClass)
+ *
+ * The idea of `markFree` is illustrated with an example:
+ *
+ * def f(x: int) = {
+ * class C {
+ * class D {
+ * val y = x
+ * }
+ * }
+ * }
+ *
+ * In this case `x` is free in the primary constructor of class `C`.
+ * but it is not free in `D`, because after lambda lift the code would be transformed
+ * as follows:
+ *
+ * def f(x$0: int) {
+ * class C(x$0: int) {
+ * val x$1 = x$0
+ * class D {
+ * val y = outer.x$1
+ * }
+ * }
+ * }
+ */
+ private def markFree(sym: Symbol, enclosure: Symbol)(implicit ctx: Context): Boolean = try {
+ if (!enclosure.exists) throw new NoPath
+ println(i"mark free: ${sym.showLocated} with owner ${sym.maybeOwner} marked free in $enclosure")
+ (enclosure == sym.enclosure) || {
+ ctx.debuglog(i"$enclosure != ${sym.enclosure}")
+ narrowLiftedOwner(enclosure, sym.enclosingClass)
+ if (enclosure.is(PackageClass) ||
+ !markFree(sym, enclosure.skipConstructor.enclosure)) false
+ else {
+ val ss = symSet(free, enclosure)
+ if (!ss(sym)) {
+ ss += sym
+ changedFreeVars = true
+ ctx.debuglog(i"$sym is free in $enclosure")
+ }
+ !enclosure.isClass
+ }
+ }
+ }
+ catch {
+ case ex: NoPath =>
+ println(i"error lambda lifting ${ctx.compilationUnit}: $sym is not visible from $enclosure")
+ throw ex
+ }
+
+ private def markCalled(callee: Symbol, caller: Symbol)(implicit ctx: Context): Unit = {
+ ctx.debuglog(i"mark called: $callee of ${callee.owner} is called by $caller")
+ assert(callee.skipConstructor.owner.isTerm)
+ symSet(called, caller) += callee
+ if (callee.enclosingClass != caller.enclosingClass) calledFromInner += callee
+ }
+
+ private class CollectDependencies(implicit ctx: Context) extends EnclosingMethodTraverser {
+ def traverse(enclMeth: Symbol, tree: Tree) = try { //debug
+ val enclosure = enclMeth.skipConstructor
+ val sym = tree.symbol
+ tree match {
+ case tree: Ident =>
+ if (sym.maybeOwner.isTerm)
+ if (sym is (Method, butNot = Label)) markCalled(sym, enclosure)
+ else if (sym.isTerm) markFree(sym, enclosure)
+ case tree: Select =>
+ if (sym.isConstructor && sym.owner.owner.isTerm)
+ markCalled(sym, enclosure)
+ case tree: This =>
+ val thisClass = tree.symbol.asClass
+ val enclClass = enclosure.enclosingClass
+ if (!thisClass.isStaticOwner && thisClass != enclClass)
+ narrowLiftedOwner(enclosure,
+ if (enclClass.isContainedIn(thisClass)) thisClass
+ else enclClass) // unknown this reference, play it safe and assume the narrowest possible owner
+ case tree: DefDef =>
+ if (sym.owner.isTerm && !sym.is(Label)) liftedOwner(sym) = sym.topLevelClass.owner
+ else if (sym.isPrimaryConstructor && sym.owner.owner.isTerm) symSet(called, sym) += sym.owner
+ case tree: TypeDef =>
+ if (sym.owner.isTerm) liftedOwner(sym) = sym.topLevelClass.owner
+ case tree: Template =>
+ liftedDefs(enclosure) = new mutable.ListBuffer
+ case _ =>
+ }
+ foldOver(enclosure, tree)
+ } catch { //debug
+ case ex: Exception =>
+ println(i"$ex while traversing $tree")
+ throw ex
+ }
+ }
+
+ /** Compute final free variables map `fvs by closing over caller dependencies. */
+ private def computeFreeVars()(implicit ctx: Context): Unit =
+ do {
+ changedFreeVars = false
+ // println(s"called = ${called.toList map { case (from, to) => from.showLocated + " -> " + to.toList.map(_.showLocated) }}")
+ for {
+ caller <- called.keys
+ callee <- called(caller)
+ fvs <- free get callee
+ fv <- fvs
+ } markFree(fv, caller)
+ } while (changedFreeVars)
+
+ /** Compute final liftedOwner map by closing over caller dependencies */
+ private def computeLiftedOwners()(implicit ctx: Context): Unit =
+ do {
+ changedLiftedOwner = false
+ for {
+ caller <- called.keys
+ callee <- called(caller)
+ } narrowLiftedOwner(caller, liftedOwner(callee.skipConstructor))
+ } while (changedLiftedOwner)
+
+ private def newName(sym: Symbol)(implicit ctx: Context): Name = {
+ def freshen(prefix: String): Name = {
+ val fname = ctx.freshName(prefix)
+ if (sym.isType) fname.toTypeName else fname.toTermName
+ }
+ if (sym.isAnonymousFunction && sym.owner.is(Method, butNot = Label))
+ freshen(sym.name.toString ++ NJ ++ sym.owner.name ++ NJ)
+ else if (sym is ModuleClass)
+ freshen(sym.sourceModule.name.toString ++ NJ).moduleClassName
+ else
+ freshen(sym.name.toString ++ NJ)
+ }
+
+ private def generateProxies()(implicit ctx: Context): Unit =
+ for ((owner, freeValues) <- free.toIterator) {
+ val newFlags = Synthetic | (if (owner.isClass) ParamAccessor | Private else Param)
+ ctx.debuglog(i"free var proxy: ${owner.showLocated}, ${freeValues.toList}%, %")
+ proxyMap(owner) = {
+ for (fv <- freeValues.toList) yield {
+ val proxyName = newName(fv)
+ val proxy = ctx.newSymbol(owner, proxyName.asTermName, newFlags, fv.info, coord = fv.coord)
+ if (owner.isClass) proxy.enteredAfter(thisTransform)
+ (fv, proxy)
+ }
+ }.toMap
+ }
+
+ private def liftedInfo(local: Symbol)(implicit ctx: Context): Type = local.info match {
+ case mt @ MethodType(pnames, ptypes) =>
+ val ps = proxies(local.skipConstructor)
+ MethodType(
+ pnames ++ ps.map(_.name.asTermName),
+ ptypes ++ ps.map(_.info),
+ mt.resultType)
+ case info => info
+ }
+
+ private def liftLocals()(implicit ctx: Context): Unit = {
+ for ((local, lOwner) <- liftedOwner) {
+ val (newOwner, maybeStatic) =
+ if (lOwner is Package) (local.topLevelClass, Static)
+ else (lOwner, EmptyFlags)
+ val maybeNotJavaPrivate = if (calledFromInner(local)) NotJavaPrivate else EmptyFlags
+ local.copySymDenotation(
+ owner = newOwner,
+ name = newName(local),
+ initFlags = local.flags | Private | maybeStatic | maybeNotJavaPrivate,
+ info = liftedInfo(local)).installAfter(thisTransform)
+ if (local.isClass)
+ for (member <- local.asClass.decls)
+ if (member.isConstructor) {
+ val linfo = liftedInfo(member)
+ if (linfo ne member.info)
+ member.copySymDenotation(info = linfo).installAfter(thisTransform)
+ }
+ }
+ }
+
+ override def init(implicit ctx: Context, info: TransformerInfo) = {
+ assert(ctx.phase == thisTransform)
+ (new CollectDependencies).traverse(NoSymbol, ctx.compilationUnit.tpdTree)
+ computeFreeVars()
+ computeLiftedOwners()
+ generateProxies()(ctx.withPhase(thisTransform.next))
+ liftLocals()(ctx.withPhase(thisTransform.next))
+ }
+
+ private def currentEnclosure(implicit ctx: Context) =
+ ctx.owner.enclosingMethod.skipConstructor
+
+ private def inCurrentOwner(sym: Symbol)(implicit ctx: Context) =
+ sym.enclosure == currentEnclosure
+
+ private def proxy(sym: Symbol)(implicit ctx: Context): Symbol = {
+ def searchIn(enclosure: Symbol): Symbol = {
+ if (!enclosure.exists)
+ throw new IllegalArgumentException(i"Could not find proxy for ${sym.showDcl} in ${sym.ownersIterator.toList}, currentOwner= $currentEnclosure")
+ ctx.debuglog(i"searching for $sym(${sym.owner}) in $enclosure")
+ proxyMap get enclosure match {
+ case Some(pmap) =>
+ pmap get sym match {
+ case Some(proxy) => return proxy
+ case none =>
+ }
+ case none =>
+ }
+ searchIn(enclosure.enclosure)
+ }
+ if (inCurrentOwner(sym)) sym else searchIn(currentEnclosure)
+ }
+
+ private def memberRef(sym: Symbol)(implicit ctx: Context, info: TransformerInfo): Tree = {
+ val clazz = sym.owner
+ val qual = if (clazz.isStaticOwner) singleton(clazz.thisType) else outer.path(clazz)
+ transformFollowingDeep(qual.select(sym))
+ }
+
+ private def proxyRef(sym: Symbol)(implicit ctx: Context, info: TransformerInfo): Tree = {
+ val psym = proxy(sym)
+ transformFollowingDeep(if (psym.owner.isTerm) ref(psym) else memberRef(psym))
+ }
+
+ private def addFreeArgs(sym: Symbol, args: List[Tree])(implicit ctx: Context, info: TransformerInfo) =
+ free get sym match {
+ case Some(fvs) => args ++ fvs.toList.map(proxyRef(_))
+ case _ => args
+ }
+
+ private def addFreeParams(tree: Tree, proxies: List[Symbol])(implicit ctx: Context, info: TransformerInfo): Tree = proxies match {
+ case Nil => tree
+ case proxies =>
+ val ownProxies =
+ if (!tree.symbol.isConstructor) proxies
+ else proxies.map(_.copy(owner = tree.symbol, flags = Synthetic | Param))
+ val freeParamDefs = ownProxies.map(proxy =>
+ transformFollowingDeep(ValDef(proxy.asTerm).withPos(tree.pos)).asInstanceOf[ValDef])
+ tree match {
+ case tree: DefDef =>
+ cpy.DefDef(tree)(vparamss = tree.vparamss.map(_ ++ freeParamDefs))
+ case tree: Template =>
+ cpy.Template(tree)(body = tree.body ++ freeParamDefs)
+ }
+ }
+
+ private def liftDef(tree: MemberDef)(implicit ctx: Context): Tree = {
+ liftedDefs(tree.symbol.owner) += rename(tree, tree.symbol.name)
+ EmptyTree
+ }
+
+ private def needsLifting(sym: Symbol) = liftedOwner contains sym
+
+ override def transformIdent(tree: Ident)(implicit ctx: Context, info: TransformerInfo) = {
+ val sym = tree.symbol
+ tree.tpe match {
+ case TermRef(NoPrefix, _) if sym.enclosure != currentEnclosure && !sym.isStatic =>
+ (if (sym is Method) memberRef(sym) else proxyRef(sym)).withPos(tree.pos)
+ case _ =>
+ tree
+ }
+ }
+
+ override def transformApply(tree: Apply)(implicit ctx: Context, info: TransformerInfo) =
+ cpy.Apply(tree)(tree.fun, addFreeArgs(tree.symbol.skipConstructor, tree.args)).withPos(tree.pos)
+
+ override def transformClosure(tree: Closure)(implicit ctx: Context, info: TransformerInfo) =
+ cpy.Closure(tree)(env = addFreeArgs(tree.meth.symbol, tree.env))
+
+ override def transformDefDef(tree: DefDef)(implicit ctx: Context, info: TransformerInfo) = {
+ val sym = tree.symbol
+ val proxyHolder = sym.skipConstructor
+ if (needsLifting(proxyHolder)) {
+ val paramsAdded = addFreeParams(tree, proxies(proxyHolder)).asInstanceOf[DefDef]
+ if (sym.isConstructor) paramsAdded else liftDef(paramsAdded)
+ }
+ else tree
+ }
+
+ override def transformReturn(tree: Return)(implicit ctx: Context, info: TransformerInfo) = tree.expr match {
+ case Block(stats, value) =>
+ Block(stats, Return(value, tree.from)).withPos(tree.pos)
+ case _ =>
+ tree
+ }
+
+ override def transformTemplate(tree: Template)(implicit ctx: Context, info: TransformerInfo) = {
+ val cls = ctx.owner
+ val impl = addFreeParams(tree, proxies(cls)).asInstanceOf[Template]
+ cpy.Template(impl)(body = impl.body ++ liftedDefs.remove(cls).get)
+ }
+
+ override def transformTypeDef(tree: TypeDef)(implicit ctx: Context, info: TransformerInfo) =
+ if (needsLifting(tree.symbol)) liftDef(tree) else tree
+ }
+
+
+/* done in lazyvals?
+ case Block(stats, expr0) =>
+ val (lzyVals, rest) = stats partition {
+ case stat: ValDef => stat.symbol.isLazy || stat.symbol.isModuleVar
+ case _ => false
+ }
+ if (lzyVals.isEmpty) tree
+ else treeCopy.Block(tree, lzyVals ::: rest, expr0)
+
+*/
diff --git a/src/dotty/tools/dotc/transform/PatternMatcher.scala b/src/dotty/tools/dotc/transform/PatternMatcher.scala
index 97525e289..ac92bb80c 100644
--- a/src/dotty/tools/dotc/transform/PatternMatcher.scala
+++ b/src/dotty/tools/dotc/transform/PatternMatcher.scala
@@ -20,7 +20,7 @@ import typer.ErrorReporting._
import ast.Trees._
import Applications._
import TypeApplications._
-import TypeUtils._
+import SymUtils._, core.NameOps._
import dotty.tools.dotc.util.Positions.Position
import dotty.tools.dotc.core.Decorators._
@@ -112,7 +112,7 @@ class PatternMatcher extends MiniPhaseTransform with DenotTransformer {thisTrans
def tupleSel(binder: Symbol)(i: Int): Tree = ref(binder).select(nme.productAccessorName(i))
def index(tgt: Tree)(i: Int): Tree = {
if (i > 0) tgt.select(defn.Seq_apply).appliedTo(Literal(Constant(i)))
- else tgt.select(defn.Seq_head).appliedIfMethod
+ else tgt.select(defn.Seq_head).ensureApplied
}
// Right now this blindly calls drop on the result of the unapplySeq
@@ -138,73 +138,28 @@ class PatternMatcher extends MiniPhaseTransform with DenotTransformer {thisTrans
}
- object Substitution {
- def apply(from: Symbol, to: Tree) = new Substitution(List(from), List(to))
+ object Rebindings {
+ def apply(from: Symbol, to: Symbol) = new Rebindings(List(from), List(ref(to)))
// requires sameLength(from, to)
def apply(from: List[Symbol], to: List[Tree]) =
- if (from nonEmpty) new Substitution(from, to) else EmptySubstitution
+ if (from nonEmpty) new Rebindings(from, to) else NoRebindings
}
- class Substitution(val from: List[Symbol], val to: List[Tree]) {
-
- // We must explicitly type the trees that we replace inside some other tree, since the latter may already have been typed,
- // and will thus not be retyped. This means we might end up with untyped subtrees inside bigger, typed trees.
- def apply(tree: Tree): Tree = {
- // according to -Ystatistics 10% of translateMatch's time is spent in this method...
- // since about half of the typedSubst's end up being no-ops, the check below shaves off 5% of the time spent in typedSubst
- /*if (!tree.exists { case i@Ident(_) => from contains i.symbol case _ => false}) tree
- else*/
-
- var replaced = 0
- val toAdapted = (from zip to) map {
- case (orig, nw) =>
- if (nw.tpe <:< orig.info) nw else nw.ensureConforms(orig.info & nw.tpe)
- }
-
- val identReplace: Ident => Tree = { ident =>
- def subst(from: List[Symbol], to: List[Tree]): Tree =
- if (from.isEmpty) ident
- else if (ident.symbol == from.head) {
- replaced += 1
- to.head //typedIfOrigTyped(to.head.shallowDuplicate.setPos(tree.pos), tree.tpe)
- }
- else subst(from.tail, to.tail)
- subst(from, toAdapted)
- }
- val start = System.currentTimeMillis()
- val res = new tpd.TreeMap() {
- override def transform(tree: tpd.Tree)(implicit ctx: Context): tpd.Tree = tree match{
- case s: Ident => identReplace(s)
- case _ => super.transform(tree)
- }
- }.transform(tree)
- //new TreeTypeMap(treeMap = identReplace/*, substFrom = from, substTo = to.map(_.symbol)*/).transform(tree)
-
- ctx.debuglog(s"${id} TreeTM replacing ${from.size} elems took ${(System.currentTimeMillis() - start)/1000.0} replaced:$replaced")
- def treeSize = new ShallowFolder[Int]((a,b) => a + 1).apply(0, tree)
-
- ctx.debuglog(s"location: ${ctx.owner.showFullName}, size: ${treeSize}")
- res
- }
-
-
- // the substitution that chains `other` before `this` substitution
- // forall t: Tree. this(other(t)) == (this >> other)(t)
- def >>(other: Substitution): Substitution = {
- if (other == EmptySubstitution) this
- else if (this == EmptySubstitution) other
+ class Rebindings(val lhs: List[Symbol], val rhs: List[Tree]) {
+ def >>(other: Rebindings) = {
+ if (other eq NoRebindings) this
+ else if (this eq NoRebindings) other
else {
- val (fromFiltered, toFiltered) = (from, to).zipped filter { (f, t) => !other.from.contains(f)}
- new Substitution(other.from ++ fromFiltered, other.to.map(apply) ++ toFiltered) // a quick benchmarking run indicates the `.map(apply)` is not too costly
+ assert((lhs.toSet ++ other.lhs.toSet).size == lhs.length + other.lhs.length, "no double assignments")
+ new Rebindings(this.lhs ++ other.lhs, this.rhs ++ other.rhs)
}
}
- override def toString = (from.map(_.name) zip to) mkString("Substitution(", ", ", ")")
- }
- object EmptySubstitution extends Substitution(Nil, Nil) {
- override def apply(tree: Tree): Tree = tree
- override def >>(other: Substitution): Substitution = other
+ def emitValDefs: List[ValDef] = {
+ Collections.map2(lhs, rhs)((symbol, tree) => ValDef(symbol.asTerm, tree.ensureConforms(symbol.info)))
+ }
}
+ object NoRebindings extends Rebindings(Nil, Nil)
trait OptimizedCodegen extends CodegenCore {
@@ -237,7 +192,7 @@ class PatternMatcher extends MiniPhaseTransform with DenotTransformer {thisTrans
val matchFail = newSynthCaseLabel(ctx.freshName("matchFail"), MethodType(Nil, restpe))
val catchAllDefBody = DefDef(matchFail, catchAllDef)
- val nextCases = (caseSyms.tail ::: List(matchFail)).map(ref(_).appliedIfMethod)
+ val nextCases = (caseSyms.tail ::: List(matchFail)).map(ref(_).ensureApplied)
val caseDefs = (cases zip caseSyms zip nextCases).foldRight[Tree](catchAllDefBody) {
// dotty deviation
//case (((mkCase, sym), nextCase), acc) =>
@@ -246,16 +201,14 @@ class PatternMatcher extends MiniPhaseTransform with DenotTransformer {thisTrans
case ((mkCase, sym), nextCase) =>
val body = mkCase(new OptimizedCasegen(nextCase)).ensureConforms(restpe)
- val caseBody = DefDef(sym, _ => Block(List(acc), body))
-
- Block(List(caseBody),ref(sym).appliedIfMethod)
+ DefDef(sym, _ => Block(List(acc), body))
}}
// scrutSym == NoSymbol when generating an alternatives matcher
// val scrutDef = scrutSym.fold(List[Tree]())(ValDef(_, scrut) :: Nil) // for alternatives
- caseDefs
+ Block(List(caseDefs), ref(caseSyms.head).ensureApplied)
}
class OptimizedCasegen(nextCase: Tree) extends CommonCodegen with Casegen {
@@ -273,23 +226,28 @@ class PatternMatcher extends MiniPhaseTransform with DenotTransformer {thisTrans
// next: MatchMonad[U]
// returns MatchMonad[U]
def flatMap(prev: Tree, b: Symbol, next: Tree): Tree = {
- val prevSym = freshSym(prev.pos, prev.tpe, "o")
+
val getTp = extractorMemberType(prev.tpe, nme.get)
val isDefined = extractorMemberType(prev.tpe, nme.isDefined)
if ((isDefined isRef defn.BooleanClass) && getTp.exists) {
- val prevValue = ref(prevSym).select("get".toTermName).appliedIfMethod
+ val tmpSym = freshSym(prev.pos, prev.tpe, "o")
+ val prevValue = ref(tmpSym).select("get".toTermName).ensureApplied
+
Block(
- List(ValDef(prevSym, prev)),
+ List(ValDef(tmpSym, prev)),
// must be isEmpty and get as we don't control the target of the call (prev is an extractor call)
ifThenElseZero(
- ref(prevSym).select(nme.isDefined).select(defn.Boolean_!),
- Substitution(b, prevValue)(next)
+ ref(tmpSym).select(nme.isDefined),
+ Block(List(ValDef(b.asTerm, prevValue)), next)
)
)
} else {
assert(defn.isProductSubType(prev.tpe))
- Substitution(b, ref(prevSym))(next)
+ Block(
+ List(ValDef(b.asTerm, prev)),
+ next //Substitution(b, ref(prevSym))(next)
+ )
}
}
@@ -341,23 +299,23 @@ class PatternMatcher extends MiniPhaseTransform with DenotTransformer {thisTrans
abstract class TreeMaker {
def pos: Position
- private[this] var currSub: Substitution = null
+ private[this] var currSub: Rebindings = null
/** captures the scope and the value of the bindings in patterns
* important *when* the substitution happens (can't accumulate and do at once after the full matcher has been constructed)
*/
- def substitution: Substitution =
- if (currSub eq null) localSubstitution
+ def rebindings: Rebindings =
+ if (currSub eq null) introducedRebindings
else currSub
- protected def localSubstitution: Substitution
+ protected def introducedRebindings: Rebindings
- private[TreeMakers] def incorporateOuterSubstitution(outerSubst: Substitution): Unit = {
+ private[TreeMakers] def incorporateOuterRebinding(outerSubst: Rebindings): Unit = {
if (currSub ne null) {
- ctx.debuglog("BUG: incorporateOuterSubstitution called more than once for "+ ((this, currSub, outerSubst)))
+ ctx.debuglog("BUG: incorporateOuterRebinding called more than once for "+ ((this, currSub, outerSubst)))
Thread.dumpStack()
}
- else currSub = outerSubst >> substitution
+ else currSub = outerSubst >> rebindings
}
/** The substitution that specifies the trees that compute the values of the subpattern binders.
@@ -372,14 +330,14 @@ class PatternMatcher extends MiniPhaseTransform with DenotTransformer {thisTrans
* TODO: clean this up, would be nicer to have some higher-level way to compute
* the binders bound by this tree maker and the symbolic values that correspond to them
*/
- def subPatternsAsSubstitution: Substitution = substitution
+ def subPatternsAsRebindings: Rebindings = rebindings
// build Tree that chains `next` after the current extractor
def chainBefore(next: Tree)(casegen: Casegen): Tree
}
sealed trait NoNewBinders extends TreeMaker {
- protected val localSubstitution: Substitution = EmptySubstitution
+ protected val introducedRebindings: Rebindings = NoRebindings
}
case class TrivialTreeMaker(tree: Tree) extends TreeMaker with NoNewBinders {
@@ -392,15 +350,34 @@ class PatternMatcher extends MiniPhaseTransform with DenotTransformer {thisTrans
def pos = body.pos
def chainBefore(next: Tree)(casegen: Casegen): Tree = // assert(next eq EmptyTree)
- /*atPos(body.pos)*/(casegen.one(substitution(body))) // since SubstOnly treemakers are dropped, need to do it here
+ /*atPos(body.pos)*/(casegen.one(body)) // since SubstOnly treemakers are dropped, need to do it here
override def toString = "B"+((body, matchPt))
}
+ /**
+ * In scalac for such block
+ * x match {
+ * case d => <body>
+ * }
+ *
+ * d inside <body> was to be substitued by x.
+ *
+ * In dotty, SubstOnlyTreeMakers instead generate normal ValDef,
+ * and does not create a new substitution.
+ *
+ * This was done for several reasons:
+ * 1) it is a lot easyer to Y-check,
+ * as d type could be used in <body>.
+ * 2) it would simplify debugging of the generated code as
+ * this works also for nested patterns, and previously they used unreadable names
+ * 3) It showed better(~30%), performance,
+ * Rebuilding tree and propagating types was taking substantial time.
+ */
case class SubstOnlyTreeMaker(prevBinder: Symbol, nextBinder: Symbol) extends TreeMaker {
val pos = Positions.NoPosition
- val localSubstitution = EmptySubstitution
- def chainBefore(next: Tree)(casegen: Casegen): Tree = Block(List(ValDef(prevBinder.asTerm, ref(nextBinder))), next)
+ val introducedRebindings = Rebindings(prevBinder, nextBinder)
+ def chainBefore(next: Tree)(casegen: Casegen): Tree = next
//override def toString = "S" + localSubstitution
}
@@ -415,11 +392,13 @@ class PatternMatcher extends MiniPhaseTransform with DenotTransformer {thisTrans
val cond: Tree
val res: Tree
- lazy val nextBinder = freshSym(pos, nextBinderTp)
- lazy val localSubstitution = Substitution(List(prevBinder), List(ref(nextBinder)))
+ val nextBinder: Symbol
+ lazy val introducedRebindings =
+ if(nextBinder ne prevBinder) Rebindings(prevBinder, nextBinder)
+ else NoRebindings
def chainBefore(next: Tree)(casegen: Casegen): Tree =
- /*atPos(pos)(*/casegen.flatMapCond(cond, res, nextBinder, substitution(next))//)
+ /*atPos(pos)(*/casegen.flatMapCond(cond, res, nextBinder, next)//)
}
// unless we're optimizing, emit local variable bindings for all subpatterns of extractor/case class patterns
@@ -447,10 +426,10 @@ class PatternMatcher extends MiniPhaseTransform with DenotTransformer {thisTrans
private lazy val (stored, substed) = (subPatBinders, subPatRefs).zipped.partition{ case (sym, _) => storedBinders(sym) }
- protected lazy val localSubstitution: Substitution = if (!emitVars) Substitution(subPatBinders, subPatRefs)
+ protected lazy val introducedRebindings: Rebindings = if (!emitVars) Rebindings(subPatBinders, subPatRefs)
else {
val (subPatBindersSubstituted, subPatRefsSubstituted) = substed.unzip
- Substitution(subPatBindersSubstituted.toList, subPatRefsSubstituted.toList)
+ Rebindings(subPatBindersSubstituted.toList, subPatRefsSubstituted.toList)
}
/** The substitution that specifies the trees that compute the values of the subpattern binders.
@@ -458,8 +437,8 @@ class PatternMatcher extends MiniPhaseTransform with DenotTransformer {thisTrans
* We pretend to replace the subpattern binders by subpattern refs
* (Even though we don't do so anymore -- see SI-5158, SI-5739 and SI-6070.)
*/
- override def subPatternsAsSubstitution =
- Substitution(subPatBinders, subPatRefs) >> super.subPatternsAsSubstitution
+ override def subPatternsAsRebindings =
+ Rebindings(subPatBinders, subPatRefs) >> super.subPatternsAsRebindings
def bindSubPats(in: Tree): Tree =
if (!emitVars) in
@@ -527,13 +506,13 @@ class PatternMatcher extends MiniPhaseTransform with DenotTransformer {thisTrans
def chainBefore(next: Tree)(casegen: Casegen): Tree = {
val condAndNext = extraCond match {
case Some(cond: Tree) =>
- casegen.ifThenElseZero(substitution(cond), bindSubPats(substitution(next)))
+ casegen.ifThenElseZero(cond, bindSubPats(next))
case _ =>
- bindSubPats(substitution(next))
+ bindSubPats(next)
}
if (extractorReturnsBoolean) casegen.flatMapCond(extractor, unitLiteral, nextBinder, condAndNext)
- else casegen.flatMap(extractor, nextBinder, condAndNext)
+ else casegen.flatMap(extractor, nextBinder, condAndNext) // getType?
}
@@ -583,13 +562,13 @@ class PatternMatcher extends MiniPhaseTransform with DenotTransformer {thisTrans
cond match {
case Some(cond: Tree) =>
- casegen.ifThenElseZero(cond, bindSubPats(substitution(next)))
+ casegen.ifThenElseZero(cond, bindSubPats(next))
case _ =>
- bindSubPats(substitution(next))
+ bindSubPats(next)
}
}
- override def toString = "P"+((prevBinder.name, extraCond getOrElse "", localSubstitution))
+ override def toString = "P"+((prevBinder.name, extraCond getOrElse "", introducedRebindings))
}
object IrrefutableExtractorTreeMaker {
@@ -711,7 +690,7 @@ class PatternMatcher extends MiniPhaseTransform with DenotTransformer {thisTrans
val prevBinder = testedBinder
- override lazy val nextBinder = afterTest.asTerm
+ val nextBinder = afterTest.asTerm
def needsOuterTest(patType: Type, selType: Type, currentOwner: Symbol) = {
// See the test for SI-7214 for motivation for dealias. Later `treeCondStrategy#outerTest`
@@ -725,7 +704,7 @@ class PatternMatcher extends MiniPhaseTransform with DenotTransformer {thisTrans
}
}
- override lazy val localSubstitution: Substitution = EmptySubstitution
+ override lazy val introducedRebindings = NoRebindings
def outerTestNeeded = {
val np = expectedTp.normalizedPrefix
@@ -806,22 +785,23 @@ class PatternMatcher extends MiniPhaseTransform with DenotTransformer {thisTrans
}
// need to substitute to deal with existential types -- TODO: deal with existentials better, don't substitute (see RichClass during quick.comp)
- case class EqualityTestTreeMaker(prevBinder: Symbol, patTree: Tree, override val pos: Position) extends CondTreeMaker {
- val nextBinderTp = prevBinder.info.widen
+ case class EqualityTestTreeMaker(prevBinder: Symbol, subpatBinder: Symbol, patTree: Tree, override val pos: Position) extends CondTreeMaker {
+ val nextBinderTp = patTree.tpe & prevBinder.info
+ val nextBinder = if (prevBinder eq subpatBinder) freshSym(pos, nextBinderTp) else subpatBinder
// NOTE: generate `patTree == patBinder`, since the extractor must be in control of the equals method (also, patBinder may be null)
// equals need not be well-behaved, so don't intersect with pattern's (stabilized) type (unlike MaybeBoundTyped's accumType, where it's required)
val cond = codegen._equals(patTree, prevBinder)
- val res = ref(prevBinder)
+ val res = ref(prevBinder).ensureConforms(nextBinderTp)
override def toString = "ET"+((prevBinder.name, patTree))
}
case class AlternativesTreeMaker(prevBinder: Symbol, var altss: List[List[TreeMaker]], pos: Position) extends TreeMaker with NoNewBinders {
// don't substitute prevBinder to nextBinder, a set of alternatives does not need to introduce a new binder, simply reuse the previous one
- override private[TreeMakers] def incorporateOuterSubstitution(outerSubst: Substitution): Unit = {
- super.incorporateOuterSubstitution(outerSubst)
- altss = altss map (alts => propagateSubstitution(alts, substitution))
+ override private[TreeMakers] def incorporateOuterRebinding(outerSubst: Rebindings): Unit = {
+ super.incorporateOuterRebinding(outerSubst)
+ altss = altss map (alts => propagateRebindings(alts, rebindings))
}
def chainBefore(next: Tree)(codegenAlt: Casegen): Tree = {
@@ -833,7 +813,7 @@ class PatternMatcher extends MiniPhaseTransform with DenotTransformer {thisTrans
)
val findAltMatcher = codegenAlt.matcher(EmptyTree, NoSymbol, defn.BooleanType)(combinedAlts, Some((x: Symbol) => Literal(Constant(false))))
- codegenAlt.ifThenElseZero(findAltMatcher, substitution(next))
+ codegenAlt.ifThenElseZero(findAltMatcher, next)
}
}
}
@@ -841,41 +821,38 @@ class PatternMatcher extends MiniPhaseTransform with DenotTransformer {thisTrans
case class GuardTreeMaker(guardTree: Tree) extends TreeMaker with NoNewBinders {
val pos = guardTree.pos
- def chainBefore(next: Tree)(casegen: Casegen): Tree = casegen.flatMapGuard(substitution(guardTree), next)
+ def chainBefore(next: Tree)(casegen: Casegen): Tree = casegen.flatMapGuard(guardTree, next)
override def toString = "G("+ guardTree +")"
}
// combineExtractors changes the current substitution's of the tree makers in `treeMakers`
// requires propagateSubstitution(treeMakers) has been called
- def combineExtractors(treeMakers: List[TreeMaker])(casegen: Casegen): Tree =
- treeMakers.foldRight(EmptyTree: Tree)((a, b) => a.chainBefore(b)(casegen))
-
- def removeSubstOnly(makers: List[TreeMaker]) = makers filterNot (_.isInstanceOf[SubstOnlyTreeMaker])
-
+ def combineExtractors(treeMakers: List[TreeMaker])(casegen: Casegen): Tree = {
+ val (testsMakers, guardAndBodyMakers) = treeMakers.span(t => !(t.isInstanceOf[NoNewBinders]))
+ val body = guardAndBodyMakers.foldRight(EmptyTree: Tree)((a, b) => a.chainBefore(b)(casegen))
+ val rebindings = guardAndBodyMakers.last.rebindings.emitValDefs
+ testsMakers.foldRight(Block(rebindings, body): Tree)((a, b) => a.chainBefore(b)(casegen))
+ }
// a foldLeft to accumulate the localSubstitution left-to-right
- // it drops SubstOnly tree makers, since their only goal in life is to propagate substitutions to the next tree maker, which is fullfilled by propagateSubstitution
- def propagateSubstitution(treeMakers: List[TreeMaker], initial: Substitution): List[TreeMaker] = {
- var accumSubst: Substitution = initial
+ // unlike in scalace it does not drop SubstOnly tree makers,
+ // as there could be types having them as prefix
+ def propagateRebindings(treeMakers: List[TreeMaker], initial: Rebindings): List[TreeMaker] = {
+ var accumSubst: Rebindings = initial
treeMakers foreach { maker =>
- maker incorporateOuterSubstitution accumSubst
- accumSubst = maker.substitution
+ maker incorporateOuterRebinding accumSubst
+ accumSubst = maker.rebindings
}
- removeSubstOnly(treeMakers)
+ treeMakers
}
// calls propagateSubstitution on the treemakers
def combineCases(scrut: Tree, scrutSym: Symbol, casesRaw: List[List[TreeMaker]], pt: Type, owner: Symbol, matchFailGenOverride: Option[Symbol => Tree]): Tree = {
- // drops SubstOnlyTreeMakers, since their effect is now contained in the TreeMakers that follow them
- val casesNoSubstOnly = casesRaw map (propagateSubstitution(_, EmptySubstitution))
- combineCasesNoSubstOnly(scrut, scrutSym, casesNoSubstOnly, pt, owner, matchFailGenOverride)
- }
+ // unlike in scalac SubstOnlyTreeMakers are maintained.
+ val casesRebindingPropagated = casesRaw map (propagateRebindings(_, NoRebindings))
- // pt is the fully defined type of the cases (either pt or the lub of the types of the cases)
- def combineCasesNoSubstOnly(scrut: Tree, scrutSym: Symbol, casesNoSubstOnly: List[List[TreeMaker]], pt: Type, owner: Symbol, matchFailGenOverride: Option[Symbol => Tree]): Tree =
- /*fixerUpper(owner, scrut.pos)*/ {
- def matchFailGen = matchFailGenOverride orElse Some((arg: Symbol) => Throw(New(defn.MatchErrorType, List(ref(arg)))))
+ def matchFailGen = matchFailGenOverride orElse Some((arg: Symbol) => Throw(New(defn.MatchErrorType, List(ref(arg)))))
- ctx.debuglog("combining cases: "+ (casesNoSubstOnly.map(_.mkString(" >> ")).mkString("{", "\n", "}")))
+ ctx.debuglog("combining cases: "+ (casesRebindingPropagated.map(_.mkString(" >> ")).mkString("{", "\n", "}")))
val (suppression, requireSwitch): (Suppression, Boolean) =
/*if (settings.XnoPatmatAnalysis)*/ (Suppression.NoSuppression, false)
@@ -894,10 +871,10 @@ class PatternMatcher extends MiniPhaseTransform with DenotTransformer {thisTrans
(Suppression.NoSuppression, false)
}*/
- emitSwitch(scrut, scrutSym, casesNoSubstOnly, pt, matchFailGenOverride, suppression.exhaustive).getOrElse{
+ emitSwitch(scrut, scrutSym, casesRebindingPropagated, pt, matchFailGenOverride, suppression.exhaustive).getOrElse{
if (requireSwitch) ctx.warning("could not emit switch for @switch annotated match", scrut.pos)
- if (casesNoSubstOnly nonEmpty) {
+ if (casesRebindingPropagated nonEmpty) {
// before optimizing, check casesNoSubstOnly for presence of a default case,
// since DCE will eliminate trivial cases like `case _ =>`, even if they're the last one
// exhaustivity and reachability must be checked before optimization as well
@@ -905,15 +882,16 @@ class PatternMatcher extends MiniPhaseTransform with DenotTransformer {thisTrans
// ("trivial" depends on whether we're emitting a straight match or an exception, or more generally, any supertype of scrutSym.tpe is a no-op)
// irrefutability checking should use the approximation framework also used for CSE, unreachability and exhaustivity checking
val synthCatchAll: Option[Symbol => Tree] =
- if (casesNoSubstOnly.nonEmpty && {
- val nonTrivLast = casesNoSubstOnly.last
+ if (casesRebindingPropagated.nonEmpty && {
+ val nonTrivLast = casesRebindingPropagated.last
nonTrivLast.nonEmpty && nonTrivLast.head.isInstanceOf[BodyTreeMaker]
}) None
else matchFailGen
- analyzeCases(scrutSym, casesNoSubstOnly, pt, suppression)
+ analyzeCases(scrutSym, casesRebindingPropagated, pt, suppression)
+
+ val (cases, toHoist) = optimizeCases(scrutSym, casesRebindingPropagated, pt)
- val (cases, toHoist) = optimizeCases(scrutSym, casesNoSubstOnly, pt)
val matchRes = codegen.matcher(scrut, scrutSym, pt)(cases.map(x => combineExtractors(x) _), synthCatchAll)
@@ -945,7 +923,7 @@ class PatternMatcher extends MiniPhaseTransform with DenotTransformer {thisTrans
def isVarPattern(pat: Tree): Boolean = pat match {
case x: BackquotedIdent => false
- case x: Ident => nme.isVariableName(x.name)
+ case x: Ident => x.name.isVariableName
case _ => false
}
@@ -1001,12 +979,20 @@ class PatternMatcher extends MiniPhaseTransform with DenotTransformer {thisTrans
object SymbolAndTypeBound {
def unapply(tree: Tree): Option[(Symbol, Type)] = tree match {
+ case SymbolBound(sym, Typed(_: UnApply, _)) => None // see comment in #189
case SymbolBound(sym, TypeBound(tpe)) => Some(sym -> tpe)
case TypeBound(tpe) => Some(binder -> tpe)
case _ => None
}
}
+ object SymbolAndValueBound {
+ def unapply(tree: Tree): Option[(Symbol, Tree)] = tree match {
+ case SymbolBound(sym, ConstantPattern(const)) => Some(sym -> const)
+ case _ => None
+ }
+ }
+
object TypeBound {
def unapply(tree: Tree): Option[Type] = tree match {
case Typed(_, _) => Some(tree.typeOpt)
@@ -1014,11 +1000,19 @@ class PatternMatcher extends MiniPhaseTransform with DenotTransformer {thisTrans
}
}
+ object ConstantPattern {
+ def unapply(tree: Tree): Option[Tree] = tree match {
+ case Literal(Constant(_)) | Ident(_) | Select(_, _) | This(_) => Some(tree)
+ case _ => None
+ }
+ }
+
private def rebindTo(pattern: Tree) = BoundTree(binder, pattern)
private def step(treeMakers: TreeMaker*)(subpatterns: BoundTree*): TranslationStep = TranslationStep(treeMakers.toList, subpatterns.toList)
private def bindingStep(sub: Symbol, subpattern: Tree) = step(SubstOnlyTreeMaker(sub, binder))(rebindTo(subpattern))
- private def equalityTestStep() = step(EqualityTestTreeMaker(binder, tree, pos))()
+ private def equalityTestStep(testedSymbol: Symbol, constantSymbol: Symbol, constant: Tree)
+ = step(EqualityTestTreeMaker(testedSymbol, constantSymbol, constant, pos))()
private def typeTestStep(sub: Symbol, subPt: Type) = step(TypeTestTreeMaker(sub, binder, subPt, sub.termRef)(pos))()
private def alternativesStep(alts: List[Tree]) = step(AlternativesTreeMaker(binder, translatedAlts(alts), alts.head.pos))()
private def translatedAlts(alts: List[Tree]) = alts map (alt => rebindTo(alt).translate())
@@ -1072,7 +1066,7 @@ class PatternMatcher extends MiniPhaseTransform with DenotTransformer {thisTrans
case TypeBound(tpe) => typeTestStep(binder, tpe)
case SymbolBound(sym, expr) => bindingStep(sym, expr)
case WildcardPattern() => noStep()
- case Literal(Constant(_)) | Ident(_) | Select(_, _) | This(_) => equalityTestStep()
+ case ConstantPattern(const) => equalityTestStep(binder, binder, const)
case Alternative(alts) => alternativesStep(alts)
case _ => ctx.error(unsupportedPatternMsg, pos) ; noStep()
}
@@ -1391,7 +1385,7 @@ class PatternMatcher extends MiniPhaseTransform with DenotTransformer {thisTrans
val accessors =
if (defn.isProductSubType(binder.info))
productSelectors(binder.info)
- else binder.info.caseAccessors
+ else binder.caseAccessors
val res =
if (accessors.isDefinedAt(i - 1)) ref(binder).select(accessors(i - 1).name)
else codegen.tupleSel(binder)(i) // this won't type check for case classes, as they do not inherit ProductN
@@ -1480,7 +1474,7 @@ class PatternMatcher extends MiniPhaseTransform with DenotTransformer {thisTrans
* when `binderKnownNonNull` is `true`, `ProductExtractorTreeMaker` does not do a (redundant) null check on binder
*/
def treeMaker(binder: Symbol, binderKnownNonNull: Boolean, pos: Position, binderTypeTested: Type): TreeMaker = {
- val paramAccessors = binder.info.caseAccessors
+ val paramAccessors = binder.caseAccessors
// binders corresponding to mutable fields should be stored (SI-5158, SI-6070)
// make an exception for classes under the scala package as they should be well-behaved,
// to optimize matching on List
@@ -1800,7 +1794,7 @@ class PatternMatcher extends MiniPhaseTransform with DenotTransformer {thisTrans
if ((extractorMemberType(resultType, nme.isDefined) isRef defn.BooleanClass) && resultOfGet.exists)
getUnapplySelectors(resultOfGet, args)
else if (defn.isProductSubType(resultType)) productSelectorTypes(resultType)
- else if (resultType =:= defn.BooleanType) Nil
+ else if (resultType isRef defn.BooleanClass) Nil
else {
ctx.error(i"invalid return type in Unapply node: $resultType")
Nil
diff --git a/src/dotty/tools/dotc/transform/SymUtils.scala b/src/dotty/tools/dotc/transform/SymUtils.scala
index df0ac59ae..2875327c4 100644
--- a/src/dotty/tools/dotc/transform/SymUtils.scala
+++ b/src/dotty/tools/dotc/transform/SymUtils.scala
@@ -7,6 +7,9 @@ import Contexts._
import Symbols._
import Decorators._
import Names._
+import StdNames._
+import NameOps._
+import Flags._
import language.implicitConversions
object SymUtils {
@@ -17,9 +20,48 @@ object SymUtils {
* that are needed in the transformer pipeline.
*/
class SymUtils(val self: Symbol) extends AnyVal {
+ import SymUtils._
def isTypeTestOrCast(implicit ctx: Context): Boolean =
self == defn.Any_asInstanceOf || self == defn.Any_isInstanceOf
def isVolatile(implicit ctx: Context) = self.hasAnnotation(defn.VolatileAnnot)
+
+ /** If this is a constructor, its owner: otherwise this. */
+ final def skipConstructor(implicit ctx: Context): Symbol =
+ if (self.isConstructor) self.owner else self
+
+ final def isAnonymousFunction(implicit ctx: Context): Boolean =
+ self.is(Method) && (self.denot.initial.asSymDenotation.name startsWith nme.ANON_FUN)
+
+ /** The logically enclosing method or class for this symbol.
+ * Instead of constructors one always picks the enclosing class.
+ */
+ final def enclosure(implicit ctx: Context) = self.owner.enclosingMethod.skipConstructor
+
+ /** Apply symbol/symbol substitution to this symbol */
+ def subst(from: List[Symbol], to: List[Symbol]): Symbol = {
+ def loop(from: List[Symbol], to: List[Symbol]): Symbol =
+ if (from.isEmpty) self
+ else if (self eq from.head) to.head
+ else loop(from.tail, to.tail)
+ loop(from, to)
+ }
+
+ def accessorNamed(name: TermName)(implicit ctx: Context): Symbol =
+ self.owner.info.decl(name).suchThat(_ is Accessor).symbol
+
+ def caseAccessors(implicit ctx:Context) =
+ self.decls.filter(_ is CaseAccessor).toList
+
+ def getter(implicit ctx: Context): Symbol =
+ if (self.isGetter) self else accessorNamed(self.asTerm.name.getterName)
+
+ def setter(implicit ctx: Context): Symbol =
+ if (self.isSetter) self
+ else accessorNamed(self.asTerm.name.setterName) orElse
+ accessorNamed(self.asTerm.name.traitSetterName)
+
+ def field(implicit ctx: Context): Symbol =
+ self.owner.info.decl(self.asTerm.name.fieldName).suchThat(!_.is(Method)).symbol
}
diff --git a/src/dotty/tools/dotc/transform/SyntheticMethods.scala b/src/dotty/tools/dotc/transform/SyntheticMethods.scala
index 34cca2872..128449efa 100644
--- a/src/dotty/tools/dotc/transform/SyntheticMethods.scala
+++ b/src/dotty/tools/dotc/transform/SyntheticMethods.scala
@@ -2,7 +2,7 @@ package dotty.tools.dotc
package transform
import core._
-import Symbols._, Types._, Contexts._, Names._, StdNames._, Constants._
+import Symbols._, Types._, Contexts._, Names._, StdNames._, Constants._, SymUtils._
import scala.collection.{ mutable, immutable }
import Flags._
import TreeTransforms._
@@ -48,7 +48,7 @@ class SyntheticMethods extends MiniPhaseTransform with IdentityDenotTransformer
*/
def syntheticMethods(clazz: ClassSymbol)(implicit ctx: Context): List[Tree] = {
val clazzType = clazz.typeRef
- def accessors = clazz.decls.filter(_ is CaseAccessor).toList
+ lazy val accessors = clazz.caseAccessors
val symbolsToSynthesize: List[Symbol] =
if (clazz.is(Case)) caseSymbols
diff --git a/src/dotty/tools/dotc/transform/TreeChecker.scala b/src/dotty/tools/dotc/transform/TreeChecker.scala
index ef5baca07..1b5cc7c07 100644
--- a/src/dotty/tools/dotc/transform/TreeChecker.scala
+++ b/src/dotty/tools/dotc/transform/TreeChecker.scala
@@ -20,6 +20,7 @@ import reporting.ThrowingReporter
import ast.Trees._
import ast.{tpd, untpd}
import util.SourcePosition
+import ProtoTypes._
import java.lang.AssertionError
/** Run by -Ycheck option after a given phase, this class retypes all syntax trees
@@ -130,10 +131,13 @@ class TreeChecker {
super.typedStats(trees, exprOwner)
}
+ override def ensureNoLocalRefs(block: Block, pt: Type, forcedDefined: Boolean = false)(implicit ctx: Context): Tree =
+ block
override def adapt(tree: Tree, pt: Type, original: untpd.Tree = untpd.EmptyTree)(implicit ctx: Context) = {
- if (ctx.mode.isExpr)
- if(!(tree.tpe <:< pt))
+ def isPrimaryConstructorReturn =
+ ctx.owner.isPrimaryConstructor && pt.isRef(ctx.owner.owner) && tree.tpe.isRef(defn.UnitClass)
+ if (ctx.mode.isExpr && !isPrimaryConstructorReturn && !pt.isInstanceOf[FunProto])
assert(tree.tpe <:< pt,
s"error at ${sourcePos(tree.pos)}\n" +
err.typeMismatchStr(tree.tpe, pt))
diff --git a/src/dotty/tools/dotc/transform/TypeTestsCasts.scala b/src/dotty/tools/dotc/transform/TypeTestsCasts.scala
index 7aaf7d86d..c25e81af9 100644
--- a/src/dotty/tools/dotc/transform/TypeTestsCasts.scala
+++ b/src/dotty/tools/dotc/transform/TypeTestsCasts.scala
@@ -13,6 +13,7 @@ import core.TypeErasure.isUnboundedGeneric
import typer.ErrorReporting._
import ast.Trees._
import Erasure.Boxing._
+import core.TypeErasure._
/** This transform normalizes type tests and type casts,
* also replacing type tests with singleton argument type with reference equality check
@@ -69,7 +70,7 @@ trait TypeTestsCasts {
}
case defn.MultiArrayType(elem, ndims) if isUnboundedGeneric(elem) =>
def isArrayTest(arg: Tree) =
- runtimeCall(nme.isArray, arg :: Literal(Constant(ndims)) :: Nil)
+ ref(defn.runtimeMethod(nme.isArray)).appliedTo(arg, Literal(Constant(ndims)))
if (ndims == 1) isArrayTest(qual)
else evalOnce(qual) { qual1 =>
derivedTree(qual1, defn.Any_isInstanceOf, qual1.tpe) and isArrayTest(qual1)
@@ -92,11 +93,11 @@ trait TypeTestsCasts {
else
derivedTree(qual, defn.Any_asInstanceOf, argType)
}
-
+ def erasedArg = erasure(tree.args.head.tpe)
if (sym eq defn.Any_isInstanceOf)
- transformIsInstanceOf(qual, tree.args.head.tpe)
+ transformIsInstanceOf(qual, erasedArg)
else if (sym eq defn.Any_asInstanceOf)
- transformAsInstanceOf(tree.args.head.tpe)
+ transformAsInstanceOf(erasedArg)
else tree
case _ =>
diff --git a/src/dotty/tools/dotc/transform/TypeUtils.scala b/src/dotty/tools/dotc/transform/TypeUtils.scala
index ceb4048a7..e510fcc88 100644
--- a/src/dotty/tools/dotc/transform/TypeUtils.scala
+++ b/src/dotty/tools/dotc/transform/TypeUtils.scala
@@ -26,6 +26,4 @@ class TypeUtils(val self: Type) extends AnyVal {
def isPrimitiveValueType(implicit ctx: Context): Boolean =
self.classSymbol.isPrimitiveValueClass
-
- def caseAccessors(implicit ctx:Context) = self.decls.filter(x => x.is(Flags.CaseAccessor) && x.is(Flags.Method)).toList
-} \ No newline at end of file
+}
diff --git a/src/dotty/tools/dotc/typer/Applications.scala b/src/dotty/tools/dotc/typer/Applications.scala
index 2ff116f46..035f19028 100644
--- a/src/dotty/tools/dotc/typer/Applications.scala
+++ b/src/dotty/tools/dotc/typer/Applications.scala
@@ -132,8 +132,7 @@ trait Applications extends Compatibility { self: Typer =>
protected def appPos: Position
- /** If constructing trees, the current function part, which might be
- * affected by lifting. EmptyTree otherwise.
+ /** The current function part, which might be affected by lifting.
*/
protected def normalizedFun: Tree
@@ -251,40 +250,52 @@ trait Applications extends Compatibility { self: Typer =>
/** Find reference to default parameter getter for parameter #n in current
* parameter list, or NoType if none was found
*/
- def findDefaultGetter(n: Int)(implicit ctx: Context): Type = {
- val meth = methRef.symbol
- val prefix =
- if ((meth is Synthetic) && meth.name == nme.apply) nme.CONSTRUCTOR else methRef.name
- def getterName = prefix.defaultGetterName(n)
- def ref(pre: Type, sym: Symbol): Type =
- if (pre.exists && sym.isTerm) pre select sym else NoType
- if (meth.hasDefaultParams)
- methRef.prefix match {
- case NoPrefix =>
- def findDefault(cx: Context): Type = {
- if (cx eq NoContext) NoType
- else if (cx.scope != cx.outer.scope &&
- cx.denotNamed(methRef.name).hasAltWith(_.symbol == meth)) {
- val denot = cx.denotNamed(getterName)
- assert(denot.exists, s"non-existent getter denotation ($denot) for getter($getterName)")
- cx.owner.thisType.select(getterName, denot)
- } else findDefault(cx.outer)
- }
- findDefault(ctx)
- case mpre =>
- val cls = meth.owner
- val pre =
- if (meth.isClassConstructor) {
- // default getters for class constructors are found in the companion object
- mpre.baseTypeRef(cls) match {
- case tp: TypeRef => ref(tp.prefix, cls.companionModule)
- case _ => NoType
- }
- } else mpre
- val getter = pre.member(getterName)
- ref(pre, getter.symbol)
+ def findDefaultGetter(n: Int)(implicit ctx: Context): Tree = {
+ val meth = methRef.symbol.asTerm
+ val receiver: Tree = methPart(normalizedFun) match {
+ case Select(receiver, _) => receiver
+ case mr => mr.tpe.normalizedPrefix match {
+ case mr: TermRef => ref(mr)
+ case _ => EmptyTree
+ }
+ }
+ val getterPrefix =
+ if ((meth is Synthetic) && meth.name == nme.apply) nme.CONSTRUCTOR else meth.name
+ def getterName = getterPrefix.defaultGetterName(n)
+ if (!meth.hasDefaultParams)
+ EmptyTree
+ else if (receiver.isEmpty) {
+ def findGetter(cx: Context): Tree = {
+ if (cx eq NoContext) EmptyTree
+ else if (cx.scope != cx.outer.scope &&
+ cx.denotNamed(meth.name).hasAltWith(_.symbol == meth)) {
+ val denot = cx.denotNamed(getterName)
+ assert(denot.exists, s"non-existent getter denotation ($denot) for getter($getterName)")
+ ref(TermRef(cx.owner.thisType, getterName, denot))
+ } else findGetter(cx.outer)
+ }
+ findGetter(ctx)
+ }
+ else {
+ def selectGetter(qual: Tree): Tree = {
+ val getterDenot = qual.tpe.member(getterName)
+ if (getterDenot.exists) qual.select(TermRef(qual.tpe, getterName, getterDenot))
+ else EmptyTree
+ }
+ if (!meth.isClassConstructor)
+ selectGetter(receiver)
+ else {
+ // default getters for class constructors are found in the companion object
+ val cls = meth.owner
+ val companion = cls.companionModule
+ receiver.tpe.baseTypeRef(cls) match {
+ case tp: TypeRef if companion.isTerm =>
+ selectGetter(ref(TermRef(tp.prefix, companion.asTerm)))
+ case _ =>
+ EmptyTree
+ }
}
- else NoType
+ }
}
/** Match re-ordered arguments against formal parameters
@@ -305,13 +316,12 @@ trait Applications extends Compatibility { self: Typer =>
}
def tryDefault(n: Int, args1: List[Arg]): Unit = {
- findDefaultGetter(n + numArgs(normalizedFun)) match {
- case dref: NamedType =>
- liftFun()
- addTyped(treeToArg(spliceMeth(ref(dref) withPos appPos, normalizedFun)), formal)
- matchArgs(args1, formals1, n + 1)
- case _ =>
- missingArg(n)
+ liftFun()
+ val getter = findDefaultGetter(n + numArgs(normalizedFun))
+ if (getter.isEmpty) missingArg(n)
+ else {
+ addTyped(treeToArg(spliceMeth(getter withPos appPos, normalizedFun)), formal)
+ matchArgs(args1, formals1, n + 1)
}
}
@@ -364,7 +374,7 @@ trait Applications extends Compatibility { self: Typer =>
def fail(msg: => String) =
ok = false
def appPos = NoPosition
- def normalizedFun = EmptyTree
+ lazy val normalizedFun = ref(methRef)
init()
}
@@ -594,7 +604,7 @@ trait Applications extends Compatibility { self: Typer =>
def followTypeAlias(tree: untpd.Tree): untpd.Tree = {
tree match {
case tree: untpd.RefTree =>
- val ttree = typedType(tree.withName(tree.name.toTypeName))
+ val ttree = typedType(untpd.rename(tree, tree.name.toTypeName))
ttree.tpe match {
case alias: TypeRef if alias.info.isAlias =>
companionRef(alias) match {
diff --git a/src/dotty/tools/dotc/typer/ErrorReporting.scala b/src/dotty/tools/dotc/typer/ErrorReporting.scala
index 1f55df2bc..e96e04b1a 100644
--- a/src/dotty/tools/dotc/typer/ErrorReporting.scala
+++ b/src/dotty/tools/dotc/typer/ErrorReporting.scala
@@ -56,10 +56,12 @@ object ErrorReporting {
else ""
def expectedTypeStr(tp: Type): String = tp match {
+ case tp: PolyProto =>
+ d"type arguments [${tp.targs}%, %] and ${expectedTypeStr(tp.resultType)}"
case tp: FunProto =>
val result = tp.resultType match {
- case tp: WildcardType => ""
- case tp => d"and expected result type $tp"
+ case _: WildcardType | _: IgnoredProto => ""
+ case tp => d" and expected result type $tp"
}
d"arguments (${tp.typedArgs.tpes}%, %)$result"
case _ =>
diff --git a/src/dotty/tools/dotc/typer/EtaExpansion.scala b/src/dotty/tools/dotc/typer/EtaExpansion.scala
index 7c1130b83..394accd03 100644
--- a/src/dotty/tools/dotc/typer/EtaExpansion.scala
+++ b/src/dotty/tools/dotc/typer/EtaExpansion.scala
@@ -84,7 +84,7 @@ object EtaExpansion {
case TypeApply(fn, targs) =>
cpy.TypeApply(tree)(liftApp(defs, fn), targs)
case Select(pre, name) if isPureRef(tree) =>
- cpy.Select(tree)(liftApp(defs, pre), name)
+ cpy.Select(tree)(liftPrefix(defs, pre), name)
case Block(stats, expr) =>
liftApp(defs ++= stats, expr)
case New(tpt) =>
@@ -93,6 +93,18 @@ object EtaExpansion {
lift(defs, tree)
}
+ /** Lift prefix `pre` of an application `pre.f(...)` to
+ *
+ * val x0 = pre
+ * x0.f(...)
+ *
+ * unless `pre` is a `New` or `pre` is idempotent.
+ */
+ def liftPrefix(defs: mutable.ListBuffer[Tree], tree: Tree)(implicit ctx: Context): Tree = tree match {
+ case New(_) => tree
+ case _ => if (isIdempotentExpr(tree)) tree else lift(defs, tree)
+ }
+
/** Eta-expanding a tree means converting a method reference to a function value.
* @param tree The tree to expand
* @param mt The type of the method reference
diff --git a/src/dotty/tools/dotc/typer/Namer.scala b/src/dotty/tools/dotc/typer/Namer.scala
index 5ceab475e..3a1f0a98b 100644
--- a/src/dotty/tools/dotc/typer/Namer.scala
+++ b/src/dotty/tools/dotc/typer/Namer.scala
@@ -632,10 +632,7 @@ class Namer { typer: Typer =>
completeParams(tparams)
vparamss foreach completeParams
val isConstructor = name == nme.CONSTRUCTOR
- val isSecondaryConstructor = isConstructor && sym != sym.owner.primaryConstructor
- def typeParams =
- if (isSecondaryConstructor) sym.owner.primaryConstructor.typeParams
- else tparams map symbolOfTree
+ def typeParams = tparams map symbolOfTree
def wrapMethType(restpe: Type): Type = {
var paramSymss = vparamss.nestedMap(symbolOfTree)
// Make sure constructor has one non-implicit parameter list
diff --git a/src/dotty/tools/dotc/typer/ReTyper.scala b/src/dotty/tools/dotc/typer/ReTyper.scala
index 31a776fc9..6895f9978 100644
--- a/src/dotty/tools/dotc/typer/ReTyper.scala
+++ b/src/dotty/tools/dotc/typer/ReTyper.scala
@@ -71,6 +71,9 @@ class ReTyper extends Typer {
override def addTypedModifiersAnnotations(mods: untpd.Modifiers, sym: Symbol)(implicit ctx: Context): Modifiers =
typedModifiers(mods, sym)
+ override def ensureConstrCall(cls: ClassSymbol, parents: List[Tree])(implicit ctx: Context): List[Tree] =
+ parents
+
override def encodeName(tree: untpd.NameTree)(implicit ctx: Context) = tree
override def handleUnexpectedFunType(tree: untpd.Apply, fun: Tree)(implicit ctx: Context): Tree = fun.tpe match {
diff --git a/src/dotty/tools/dotc/typer/TypeAssigner.scala b/src/dotty/tools/dotc/typer/TypeAssigner.scala
index c23b820e4..ccf67b55b 100644
--- a/src/dotty/tools/dotc/typer/TypeAssigner.scala
+++ b/src/dotty/tools/dotc/typer/TypeAssigner.scala
@@ -209,10 +209,11 @@ trait TypeAssigner {
def assignType(tree: untpd.Literal)(implicit ctx: Context) =
tree.withType {
- tree.const.tag match {
+ val value = tree.const
+ value.tag match {
case UnitTag => defn.UnitType
case NullTag => defn.NullType
- case _ => ConstantType(tree.const)
+ case _ => if (ctx.erasedTypes) value.tpe else ConstantType(value)
}
}
@@ -235,7 +236,7 @@ trait TypeAssigner {
}
val owntype =
if (!mix.isEmpty) findMixinSuper(cls.info)
- else if (inConstrCall) cls.info.firstParent
+ else if (inConstrCall || ctx.erasedTypes) cls.info.firstParent
else {
val ps = cls.info.parents
if (ps.isEmpty) defn.AnyType else ps.reduceLeft((x: Type, y: Type) => x & y)
@@ -246,7 +247,7 @@ trait TypeAssigner {
def assignType(tree: untpd.Apply, fn: Tree, args: List[Tree])(implicit ctx: Context) = {
val ownType = fn.tpe.widen match {
case fntpe @ MethodType(_, ptypes) =>
- if (sameLength(ptypes, args)) fntpe.instantiate(args.tpes)
+ if (sameLength(ptypes, args) || ctx.phase.prev.relaxedTyping) fntpe.instantiate(args.tpes)
else errorType(i"wrong number of parameters for ${fn.tpe}; expected: ${ptypes.length}", tree.pos)
case t =>
errorType(i"${err.exprStr(fn)} does not take parameters", tree.pos)
@@ -258,7 +259,7 @@ trait TypeAssigner {
val ownType = fn.tpe.widen match {
case pt: PolyType =>
val argTypes = args.tpes
- if (sameLength(argTypes, pt.paramNames)) pt.instantiate(argTypes)
+ if (sameLength(argTypes, pt.paramNames)|| ctx.phase.prev.relaxedTyping) pt.instantiate(argTypes)
else errorType(d"wrong number of type parameters for ${fn.tpe}; expected: ${pt.paramNames.length}", tree.pos)
case _ =>
errorType(i"${err.exprStr(fn)} does not take type parameters", tree.pos)
@@ -285,7 +286,9 @@ trait TypeAssigner {
tree.withType(thenp.tpe | elsep.tpe)
def assignType(tree: untpd.Closure, meth: Tree, target: Tree)(implicit ctx: Context) =
- tree.withType(if (target.isEmpty) meth.tpe.widen.toFunctionType else target.tpe)
+ tree.withType(
+ if (target.isEmpty) meth.tpe.widen.toFunctionType(tree.env.length)
+ else target.tpe)
def assignType(tree: untpd.CaseDef, body: Tree)(implicit ctx: Context) =
tree.withType(body.tpe)
diff --git a/src/dotty/tools/dotc/typer/Typer.scala b/src/dotty/tools/dotc/typer/Typer.scala
index 9066012f0..80eb5965c 100644
--- a/src/dotty/tools/dotc/typer/Typer.scala
+++ b/src/dotty/tools/dotc/typer/Typer.scala
@@ -374,8 +374,11 @@ class Typer extends Namer with TypeAssigner with Applications with Implicits wit
case lhs =>
val lhsCore = typedUnadapted(lhs)
def lhs1 = typed(untpd.TypedSplice(lhsCore))
+ def canAssign(sym: Symbol) = // allow assignments from the primary constructor to class fields
+ sym.is(Mutable, butNot = Accessor) ||
+ ctx.owner.isPrimaryConstructor && !sym.is(Method) && sym.owner == ctx.owner.owner
lhsCore.tpe match {
- case ref: TermRef if ref.symbol is (Mutable, butNot = Accessor) =>
+ case ref: TermRef if canAssign(ref.symbol) =>
assignType(cpy.Assign(tree)(lhs1, typed(tree.rhs, ref.info)))
case _ =>
def reassignmentToVal =
@@ -390,8 +393,8 @@ class Typer extends Namer with TypeAssigner with Applications with Implicits wit
case lhsCore: RefTree if setter.exists =>
val setterTypeRaw = pre select (setterName, setter)
val setterType = ensureAccessible(setterTypeRaw, isSuperSelection(lhsCore), tree.pos)
- val lhs2 = lhsCore.withName(setterName).withType(setterType)
- typed(cpy.Apply(tree)(untpd.TypedSplice(lhs2), tree.rhs :: Nil))
+ val lhs2 = untpd.rename(lhsCore, setterName).withType(setterType)
+ typedUnadapted(cpy.Apply(tree)(untpd.TypedSplice(lhs2), tree.rhs :: Nil))
case _ =>
reassignmentToVal
}
@@ -843,20 +846,10 @@ class Typer extends Namer with TypeAssigner with Applications with Implicits wit
result
}
- /** If this is a real class, make sure its first parent is a
- * constructor call. Cannot simply use a type.
- */
- def ensureConstrCall(parents: List[Tree]): List[Tree] = {
- val firstParent :: otherParents = parents
- if (firstParent.isType && !(cls is Trait))
- typed(untpd.New(untpd.TypedSplice(firstParent), Nil))(superCtx) :: otherParents
- else parents
- }
-
val mods1 = addTypedModifiersAnnotations(mods, cls)
val constr1 = typed(constr).asInstanceOf[DefDef]
- val parents1 = ensureConstrCall(ensureFirstIsClass(
- parents mapconserve typedParent, cdef.pos.toSynthetic))
+ val parentsWithClass = ensureFirstIsClass(parents mapconserve typedParent, cdef.pos.toSynthetic)
+ val parents1 = ensureConstrCall(cls, parentsWithClass)(superCtx)
val self1 = typed(self)(ctx.outer).asInstanceOf[ValDef] // outer context where class members are not visible
val dummy = localDummy(cls, impl)
val body1 = typedStats(body, dummy)(inClassContext(self1.symbol))
@@ -875,6 +868,16 @@ class Typer extends Namer with TypeAssigner with Applications with Implicits wit
// 4. Polymorphic type defs override nothing.
}
+ /** If this is a real class, make sure its first parent is a
+ * constructor call. Cannot simply use a type. Overridden in ReTyper.
+ */
+ def ensureConstrCall(cls: ClassSymbol, parents: List[Tree])(implicit ctx: Context): List[Tree] = {
+ val firstParent :: otherParents = parents
+ if (firstParent.isType && !(cls is Trait))
+ typed(untpd.New(untpd.TypedSplice(firstParent), Nil)) :: otherParents
+ else parents
+ }
+
/** Overridden in retyper */
def checkVariance(tree: Tree)(implicit ctx: Context) = VarianceChecker.check(tree)
@@ -1009,7 +1012,8 @@ class Typer extends Namer with TypeAssigner with Applications with Implicits wit
}
}
- protected def encodeName(tree: untpd.NameTree)(implicit ctx: Context) = tree withName tree.name.encode
+ protected def encodeName(tree: untpd.NameTree)(implicit ctx: Context): untpd.NameTree =
+ untpd.rename(tree, tree.name.encode)
def typed(tree: untpd.Tree, pt: Type = WildcardType)(implicit ctx: Context): Tree = /*>|>*/ ctx.traceIndented (i"typing $tree", typr, show = true) /*<|<*/ {
assertPositioned(tree)
@@ -1272,7 +1276,7 @@ class Typer extends Namer with TypeAssigner with Applications with Implicits wit
if defn.isFunctionType(wtp) && !defn.isFunctionType(pt) =>
pt match {
case SAMType(meth)
- if wtp <:< meth.info.toFunctionType =>
+ if wtp <:< meth.info.toFunctionType() =>
// was ... && isFullyDefined(pt, ForceDegree.noBottom)
// but this prevents case blocks from implementing polymorphic partial functions,
// since we do not know the result parameter a priori. Have to wait until the
diff --git a/src/dotty/tools/dotc/typer/VarianceChecker.scala b/src/dotty/tools/dotc/typer/VarianceChecker.scala
index 5865f0133..e1f50ded9 100644
--- a/src/dotty/tools/dotc/typer/VarianceChecker.scala
+++ b/src/dotty/tools/dotc/typer/VarianceChecker.scala
@@ -125,7 +125,7 @@ class VarianceChecker()(implicit ctx: Context) {
ctx.debuglog(s"Skipping variance check of ${sym.showDcl}")
case tree: TypeDef =>
checkVariance(sym)
- foldOver((), tree)
+ traverseChildren(tree)
case tree: ValDef =>
checkVariance(sym)
case DefDef(_, _, tparams, vparamss, _, _) =>
@@ -133,7 +133,7 @@ class VarianceChecker()(implicit ctx: Context) {
tparams foreach traverse
vparamss foreach (_ foreach traverse)
case Template(_, _, _, body) =>
- foldOver((), tree)
+ traverseChildren(tree)
case _ =>
}
}
diff --git a/src/dotty/tools/dotc/typer/Variances.scala b/src/dotty/tools/dotc/typer/Variances.scala
index 44b64553b..0fec1e5a7 100644
--- a/src/dotty/tools/dotc/typer/Variances.scala
+++ b/src/dotty/tools/dotc/typer/Variances.scala
@@ -68,11 +68,10 @@ object Variances {
/** Compute variance of type parameter <code>tparam</code> in type <code>tp</code>. */
def varianceInType(tp: Type)(tparam: Symbol)(implicit ctx: Context): Variance = tp match {
- case TermRef(pre, sym) =>
+ case TermRef(pre, _) =>
varianceInType(pre)(tparam)
- case TypeRef(pre, sym) =>
- /* @odersky sym is a typeName here, comparison is always false */
- if (sym == tparam) Covariant else varianceInType(pre)(tparam)
+ case tp @ TypeRef(pre, _) =>
+ if (tp.symbol == tparam) Covariant else varianceInType(pre)(tparam)
case tp @ TypeBounds(lo, hi) =>
if (lo eq hi) compose(varianceInType(hi)(tparam), tp.variance)
else flip(varianceInType(lo)(tparam)) & varianceInType(hi)(tparam)
diff --git a/test/dotc/tests.scala b/test/dotc/tests.scala
index 021845997..614dc9527 100644
--- a/test/dotc/tests.scala
+++ b/test/dotc/tests.scala
@@ -14,16 +14,13 @@ class tests extends CompilerTest {
"-pagewidth", "160")
implicit val defaultOptions = noCheckOptions ++ List(
- "-YnoDeepSubtypes",
- "-Ycheck:patternMatcher,literalize,capturedVars",
- "-Ystop-before:erasure"
- /*,"-uniqid", "-explaintypes", "-verbose", "-Ylog:splitter", "-Xprompt", "-YnoDoubleBindings"*/
+ "-Yno-deep-subtypes",
+ "-Ycheck:patternMatcher,gettersSetters,constructors"
)
- val allowDeepSubtypes = defaultOptions diff List("-YnoDeepSubtypes")
-
- val twice = List("#runs", "2", "-YnoDoubleBindings", "-Ystop-before:terminal")
+ val twice = List("#runs", "2", "-YnoDoubleBindings")
val doErase = List("-Ystop-before:terminal")
+ val allowDeepSubtypes = defaultOptions diff List("-Yno-deep-subtypes")
val posDir = "./tests/pos/"
val negDir = "./tests/neg/"
@@ -49,7 +46,6 @@ class tests extends CompilerTest {
@Test def pos_desugar() = compileFile(posDir, "desugar", doErase)
@Test def pos_sigs() = compileFile(posDir, "sigs", doErase)
@Test def pos_typers() = compileFile(posDir, "typers", doErase)
-
@Test def pos_typedidents() = compileFile(posDir, "typedIdents", doErase)
@Test def pos_assignments() = compileFile(posDir, "assignments", doErase)
@Test def pos_packageobject() = compileFile(posDir, "packageobject", doErase)
@@ -97,29 +93,25 @@ class tests extends CompilerTest {
@Test def nef_t1279a = compileFile(negDir, "t1279a", xerrors = 1)
@Test def neg_t1843 = compileFile(negDir, "t1843", xerrors = 1)
@Test def neg_t1843_variances = compileFile(negDir, "t1843-variances", xerrors = 1)
+ @Test def neg_t2660_ambi = compileFile(negDir, "t2660", xerrors = 2)
@Test def neg_t2994 = compileFile(negDir, "t2994", xerrors = 2)
@Test def neg_subtyping = compileFile(negDir, "subtyping", xerrors = 1)
@Test def neg_variances = compileFile(negDir, "variances", xerrors = 2)
@Test def neg_badAuxConstr = compileFile(negDir, "badAuxConstr", xerrors = 2)
@Test def neg_typetest = compileFile(negDir, "typetest", xerrors = 1)
-
@Test def dotc = compileDir(dotcDir + "tools/dotc", twice)(allowDeepSubtypes)
@Test def dotc_ast = compileDir(dotcDir + "tools/dotc/ast", twice)
@Test def dotc_config = compileDir(dotcDir + "tools/dotc/config", twice)
@Test def dotc_core = compileDir(dotcDir + "tools/dotc/core", twice)(allowDeepSubtypes)
@Test def dotc_core_pickling = compileDir(dotcDir + "tools/dotc/core/pickling", twice)(allowDeepSubtypes)
-
@Test def dotc_transform = compileDir(dotcDir + "tools/dotc/transform", twice)
-
@Test def dotc_parsing = compileDir(dotcDir + "tools/dotc/parsing", twice)
@Test def dotc_printing = compileDir(dotcDir + "tools/dotc/printing", twice)
-
@Test def dotc_reporting = compileDir(dotcDir + "tools/dotc/reporting", twice)
@Test def dotc_typer = compileDir(dotcDir + "tools/dotc/typer", twice)
-
@Test def dotc_util = compileDir(dotcDir + "tools/dotc/util", twice)
@Test def tools_io = compileDir(dotcDir + "tools/io", twice)
- @Test def tools = compileDir(dotcDir + "tools", twice)(allowDeepSubtypes)
+ //@Test def tools = compileDir(dotcDir + "tools", "-deep" :: Nil)(allowDeepSubtypes)
@Test def testNonCyclic = compileArgs(Array(
dotcDir + "tools/dotc/CompilationUnit.scala",
@@ -129,7 +121,6 @@ class tests extends CompilerTest {
"-Xprompt",
"#runs", "2"))
-
@Test def testIssue_34 = compileArgs(Array(
dotcDir + "tools/dotc/config/Properties.scala",
dotcDir + "tools/dotc/config/PathResolver.scala",
diff --git a/test/test/CompilerTest.scala b/test/test/CompilerTest.scala
index f5b138454..c9c7c602b 100644
--- a/test/test/CompilerTest.scala
+++ b/test/test/CompilerTest.scala
@@ -24,8 +24,12 @@ class CompilerTest extends DottyTest {
compileDir(Directory(path), args, xerrors)
def compileDir(dir: Directory, args: List[String], xerrors: Int)(implicit defaultOptions: List[String]): Unit = {
- val fileNames = dir.deepFiles.toArray.map(_.toString).filter(_ endsWith ".scala")
- compileArgs(fileNames ++ args, xerrors)
+ val (files, normArgs) = args match {
+ case "-deep" :: args1 => (dir.deepFiles, args1)
+ case _ => (dir.files, args)
+ }
+ val fileNames = files.toArray.map(_.toString).filter(_ endsWith ".scala")
+ compileArgs(fileNames ++ normArgs, xerrors)
}
def compileFiles(path: String, args: List[String] = Nil)(implicit defaultOptions: List[String]): Unit = {
diff --git a/tests/neg/t2660.scala b/tests/neg/t2660.scala
new file mode 100644
index 000000000..85e318915
--- /dev/null
+++ b/tests/neg/t2660.scala
@@ -0,0 +1,47 @@
+// Dotty deviation. The calls here now are classified as ambiguous.
+
+package hoho
+
+class G
+
+class H extends G
+
+class A[T](x: T) {
+
+ def this(y: G, z: T) = {
+ this(z)
+ print(1)
+ }
+
+ def this(z: H, h: T) = {
+ this(h)
+ print(2)
+ }
+}
+
+object T {
+ def main(args: Array[String]): Unit = {
+ implicit def g2h(g: G): H = new H
+ new A[Int](new H, 23)
+ // in the context here, either secondary constructor is applicable
+ // to the other, due to the implicit in scope. So the call is ambiguous.
+ }
+}
+
+
+// A version of t2660 which does not use constructors
+
+object X {
+ def f[T](x: T) = ???
+ def f[T](y: G, z: T) = ???
+ def f[T](z: H, h: T) = ???
+}
+
+object T2 {
+ def main(args: Array[String]): Unit = {
+ implicit def g2h(g: G): H = new H
+ X.f(new H, 23)
+ }
+}
+
+
diff --git a/tests/pending/pos/annot.scala b/tests/pending/pos/annot.scala
new file mode 100644
index 000000000..8e21803b4
--- /dev/null
+++ b/tests/pending/pos/annot.scala
@@ -0,0 +1,5 @@
+class Test {
+
+ @SuppressWarnings("hi") def foo() = ???
+
+}
diff --git a/tests/pending/pos/t1672.scala b/tests/pending/pos/t1672.scala
new file mode 100644
index 000000000..77a86db22
--- /dev/null
+++ b/tests/pending/pos/t1672.scala
@@ -0,0 +1,36 @@
+// moved to pending.
+/* Tail calls translates this program to:
+
+final lazy object Test1672: Test1672$ = new Test1672$()
+ final class Test1672$() extends Object() { this: Test1672$.type =>
+ @tailrec def bar: (x: Int)(y: Int)Nothing = {
+ def tailLabel2: ($this: Test1672$.type)(x: Int)(y: Int)Nothing = {
+ try {
+ throw new scala.package.RuntimeException()
+ } catch {
+ def $anonfun: (x$1: Throwable)Nothing =
+ x$1 match {
+ case _: scala.package.Throwable =>
+ tailLabel2($this)(x)(y)
+ }
+ closure($anonfun)
+ }
+ }
+ tailLabel2(Test1672$.this)(x)(y)
+ }
+ }
+
+Note the tail call to taillabel2 from the local method $anonfun.
+LambdaLift doe snot know how to deal wioth this.
+*/
+
+object Test1672 {
+ @annotation.tailrec
+ def bar(x: Int)(y: Int) : Nothing = {
+ try {
+ throw new RuntimeException
+ } catch {
+ case _: Throwable => bar(x)(y)
+ }
+ }
+}
diff --git a/tests/pending/pos/vararg-pattern.scala b/tests/pending/pos/vararg-pattern.scala
new file mode 100644
index 000000000..314d6460f
--- /dev/null
+++ b/tests/pending/pos/vararg-pattern.scala
@@ -0,0 +1,12 @@
+object Test {
+
+ List(1, 2, 3, 4) match {
+ case List(1, 2, xs: _*) =>
+ val ys: Seq[Int] = xs
+ println(ys)
+ }
+ val List(1, 2, x: _*) = List(1, 2, 3, 4)
+
+}
+
+
diff --git a/tests/pos/Fileish.scala b/tests/pos/Fileish.scala
new file mode 100644
index 000000000..d226bb0bd
--- /dev/null
+++ b/tests/pos/Fileish.scala
@@ -0,0 +1,53 @@
+// Inspired by the original Fileish,
+// testing combinations of lazy and non-lazy vals for their treatment in constructors
+package dotty.tools
+package io
+
+import java.io.{ InputStream }
+import java.util.jar.JarEntry
+import language.postfixOps
+
+/** A common interface for File-based things and Stream-based things.
+ * (In particular, io.File and JarEntry.)
+ */
+class Fileish(val path: Path, val input: () => InputStream) extends Streamable.Chars {
+ def inputStream() = input()
+
+ def parent = path.parent
+ def name = path.name
+ def isSourceFile = path.hasExtension("java", "scala")
+
+ private lazy val pkgLines = lines() collect { case x if x startsWith "package " => x stripPrefix "package" trim }
+ lazy val pkgFromPath = parent.path.replaceAll("""[/\\]""", ".")
+ lazy val pkgFromSource = pkgLines map (_ stripSuffix ";") mkString "."
+
+ override def toString = path.path
+}
+class Fileish2(val path: Path, val input: () => InputStream) extends Streamable.Chars {
+ def inputStream() = input()
+
+ def parent = path.parent
+ def name = path.name
+ def isSourceFile = path.hasExtension("java", "scala")
+
+ private val pkgLines = lines() collect { case x if x startsWith "package " => x stripPrefix "package" trim }
+ lazy val pkgFromPath = parent.path.replaceAll("""[/\\]""", ".")
+ lazy val pkgFromSource = pkgLines map (_ stripSuffix ";") mkString "."
+
+ override def toString = path.path
+}
+
+class Fileish3(val path: Path, val input: () => InputStream) extends Streamable.Chars {
+ def inputStream() = input()
+
+ def parent = path.parent
+ def name = path.name
+ def isSourceFile = path.hasExtension("java", "scala")
+
+ private val pkgLines = lines() collect { case x if x startsWith "package " => x stripPrefix "package" trim }
+ private val pkgFromPath = parent.path.replaceAll("""[/\\]""", ".")
+ private val pkgFromSource = pkgLines map (_ stripSuffix ";") mkString "."
+
+ override def toString = path.path
+}
+
diff --git a/tests/pos/Patterns.scala b/tests/pos/Patterns.scala
index 228345b98..54c4d8ab2 100644
--- a/tests/pos/Patterns.scala
+++ b/tests/pos/Patterns.scala
@@ -1,3 +1,4 @@
+import dotty.tools.dotc.ast.Trees._
import dotty.tools.dotc.core.Types._
object Patterns {
@@ -13,6 +14,8 @@ object Patterns {
d match {
case WildcardType(bounds: TypeBounds) =>
bounds.variance
+ case a @ Assign(Ident(id), rhs) => id
+ case a: Object => a
}
('1', "1") match {
diff --git a/tests/pos/array-clone.scala b/tests/pos/array-clone.scala
new file mode 100644
index 000000000..ef5ac5c85
--- /dev/null
+++ b/tests/pos/array-clone.scala
@@ -0,0 +1,7 @@
+object test {
+
+ val xs = Array(1, 2, 3)
+
+ val ys: Array[Int] = xs.clone()
+
+}
diff --git a/tests/pos/constrs.scala b/tests/pos/constrs.scala
new file mode 100644
index 000000000..dc0e1a369
--- /dev/null
+++ b/tests/pos/constrs.scala
@@ -0,0 +1,33 @@
+class Foo(x: Int, var y: Int) {
+
+ val z: Int = 0
+
+ var u: Int = _
+
+ def f = x
+
+}
+
+class Baz(val base: Int) {
+
+}
+
+
+class Bar(base: Int, byName: => String, local: Int) extends Baz(base + local) {
+
+ def f() = println(base.toString + byName)
+
+}
+
+class Rational(n: Int, d: Int) {
+ def gcd(x: Int, y: Int): Int = ???
+ private val x = gcd(n, d)
+ def numer = n / x
+ def denom = d / x
+}
+class Rational2(n: Int, d: Int) {
+ def gcd(x: Int, y: Int): Int = ???
+ private val x = gcd(n, d)
+ val numer = n / x
+ val denom = d / x
+}
diff --git a/tests/pos/functions1.scala b/tests/pos/functions1.scala
index 93f4fc7b6..04b90d80e 100644
--- a/tests/pos/functions1.scala
+++ b/tests/pos/functions1.scala
@@ -12,7 +12,7 @@ object Functions {
val x2: String => Int = x.foo
val x3 = x.foo _
}
-
+
object sams {
abstract class Spore[T, U] {
def run(x: T): U
@@ -22,9 +22,9 @@ object Functions {
def run(x: T): U
}
- val x: String => String = {
+ val x33: String => String = x22 => x22 match {
case "abc" => ""
- case x => x
+ case x34 => x34
}
val y: PartialFunction[String, String] = x => x match {
case "abc" => ""
diff --git a/tests/pos/getset.scala b/tests/pos/getset.scala
new file mode 100644
index 000000000..7b6207e94
--- /dev/null
+++ b/tests/pos/getset.scala
@@ -0,0 +1,23 @@
+package test
+
+trait T {
+
+ val x = 2
+
+ var y = 2
+
+ private[this] var z = 3
+
+ private var a = 3
+
+}
+class C {
+
+ val x = 2
+
+ var y = 2
+
+ private[this] var z = 3
+
+ private var a = 3
+}
diff --git a/tests/pos/lambdalift.scala b/tests/pos/lambdalift.scala
new file mode 100644
index 000000000..febae6828
--- /dev/null
+++ b/tests/pos/lambdalift.scala
@@ -0,0 +1,46 @@
+object test {
+
+ def foo(x: Int) = {
+
+ def bar(y: Int) = x + y
+ def baz(z: Int) = bar(z)
+
+ baz(1)
+
+ }
+
+ def foo2(x: Int) = {
+
+ class C {
+ def bam(y: Int): String => Int = {
+ def baz = x + y
+ z => baz * z.length
+ }
+ }
+
+ val fun = new C().bam(1)
+ fun("abc")
+
+ }
+}
+
+class Super(x: Int)
+
+class Sub extends Super({
+ def foo3(x: Int) = {
+
+ class C {
+ def this(name: String) = this()
+
+ def bam(y: Int): String => Int = {
+ def baz = x + y
+ z => baz * z.length
+ }
+ }
+
+ val fun = new C("dummy").bam(1)
+ fun("abc")
+
+ }
+ foo3(22)
+})
diff --git a/tests/pos/points.scala b/tests/pos/points.scala
new file mode 100644
index 000000000..db6104c88
--- /dev/null
+++ b/tests/pos/points.scala
@@ -0,0 +1,8 @@
+class Point extends Comparable[Point] {
+ override def compareTo(other: Point): Int = ???
+}
+
+class ColoredPoint extends Point with Comparable[ColoredPoint] {
+ override def compareTo(other: ColoredPoint): Int = ???
+}
+
diff --git a/tests/pos/synthetics.scala b/tests/pos/synthetics.scala
new file mode 100644
index 000000000..c7d49df70
--- /dev/null
+++ b/tests/pos/synthetics.scala
@@ -0,0 +1,4 @@
+case class C(x: Int, var y: String) {
+
+}
+
diff --git a/tests/pos/t2660.scala b/tests/pos/t2660.scala
deleted file mode 100644
index 94a40f740..000000000
--- a/tests/pos/t2660.scala
+++ /dev/null
@@ -1,25 +0,0 @@
-package hoho
-
-class G
-
-class H extends G
-
-class A[T](x: T) {
-
- def this(y: G, z: T) = {
- this(z)
- print(1)
- }
-
- def this(z: H, h: T) = {
- this(h)
- print(2)
- }
-}
-
-object T {
- def main(args: Array[String]): Unit = {
- implicit def g2h(g: G): H = new H
- new A[Int](new H, 23)
- }
-}
diff --git a/tests/pos/t7093.scala b/tests/pos/t7093.scala
new file mode 100644
index 000000000..287b7a78c
--- /dev/null
+++ b/tests/pos/t7093.scala
@@ -0,0 +1,27 @@
+object Test {
+
+ trait A[+X] {
+ protected[this] def f(x: X): X = x
+ }
+
+ trait B extends A[B] {
+ def kaboom = f(new B {})
+ }
+
+// protected[this] disables variance checking
+// of the signature of `f`.
+//
+// C's parent list unifies A[B] with A[C]
+//
+// The protected[this] loophole is widely used
+// in the collections, every newBuilder method
+// would fail variance checking otherwise.
+ class C extends B with A[C] {
+ override protected[this] def f(c: C) = c
+ }
+
+// java.lang.ClassCastException: B$$anon$1 cannot be cast to C
+// at C.f(<console>:15)
+ new C().kaboom
+}
+
diff --git a/tests/pos/unapply.scala b/tests/pos/unapply.scala
new file mode 100644
index 000000000..ba885be73
--- /dev/null
+++ b/tests/pos/unapply.scala
@@ -0,0 +1,11 @@
+object test {
+ class Foo[T](val arg : T)
+
+ object Foo {
+ def unapply [a](m : Foo[a]) = Some (m.arg)
+ }
+ def matchAndGetArgFromFoo[b]( e:Foo[b]):b = {e match { case Foo(x) => x }}
+// Unapply node here will have type argument [a] instantiated to scala.Nothing:
+// UnApply(TypeApply(Select(Ident(Foo),unapply),List(TypeTree[TypeVar(PolyParam(a) -> TypeRef(ThisType(TypeRef(NoPrefix,scala)),Nothing))])),List(),List(Bind(x,Ident(_))))
+// but the type of the UnApply node itself is correct: RefinedType(TypeRef(ThisType(TypeRef(ThisType(TypeRef(NoPrefix,<empty>)),test$)),Foo), test$$Foo$$a, TypeAlias(TypeRef(NoPrefix,a)))
+}