aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMartin Odersky <odersky@gmail.com>2014-03-26 19:15:40 +0100
committerDmitry Petrashko <dmitry.petrashko@gmail.com>2014-03-29 09:29:05 +0100
commit37cf9efe509b0bac4fb6bda01b7126e7511e43f0 (patch)
treeee5fa94171acf87de4e5da5bf7ee704a18596163
parent0a1e969cb09e953d6b3f3b64b63a050588aa3360 (diff)
downloaddotty-37cf9efe509b0bac4fb6bda01b7126e7511e43f0.tar.gz
dotty-37cf9efe509b0bac4fb6bda01b7126e7511e43f0.tar.bz2
dotty-37cf9efe509b0bac4fb6bda01b7126e7511e43f0.zip
Erasure phase PoC
Still missing: bridge method generation, signatures. Other changes - Turned around Checking and NoChecking. Checking is the default, NoChecking disables it. - Refactored Typer#typed to expose typedNamed, so that it can be overridden in erasure. - Made logging more forgiving wrt off-buy-one phase errors. Conflicts: src/dotty/tools/dotc/typer/Typer.scala
-rw-r--r--src/dotty/tools/dotc/Compiler.scala3
-rw-r--r--src/dotty/tools/dotc/core/Definitions.scala5
-rw-r--r--src/dotty/tools/dotc/core/NameOps.scala8
-rw-r--r--src/dotty/tools/dotc/core/SymDenotations.scala4
-rw-r--r--src/dotty/tools/dotc/core/transform/Erasure.scala14
-rw-r--r--src/dotty/tools/dotc/reporting/Reporter.scala10
-rw-r--r--src/dotty/tools/dotc/transform/Erasure.scala287
-rw-r--r--src/dotty/tools/dotc/typer/Checking.scala46
-rw-r--r--src/dotty/tools/dotc/typer/Typer.scala69
9 files changed, 386 insertions, 60 deletions
diff --git a/src/dotty/tools/dotc/Compiler.scala b/src/dotty/tools/dotc/Compiler.scala
index 8ed8b6dce..ccbcf1696 100644
--- a/src/dotty/tools/dotc/Compiler.scala
+++ b/src/dotty/tools/dotc/Compiler.scala
@@ -17,7 +17,8 @@ import dotty.tools.dotc.core.Denotations.SingleDenotation
class Compiler {
- def phases: List[List[Phase]] = List(
+ def phases: List[List[Phase]] =
+ List(
List(new FrontEnd),
List(new LazyValsCreateCompanionObjects), //force separataion between lazyVals and LVCreateCO
List(new LazyValTranformContext().transformer, new TypeTestsCasts),
diff --git a/src/dotty/tools/dotc/core/Definitions.scala b/src/dotty/tools/dotc/core/Definitions.scala
index af88a04b9..3ae74b467 100644
--- a/src/dotty/tools/dotc/core/Definitions.scala
+++ b/src/dotty/tools/dotc/core/Definitions.scala
@@ -182,6 +182,9 @@ class Definitions {
lazy val DoubleClass = valueClassSymbol("scala.Double", BoxedDoubleClass, java.lang.Double.TYPE, DoubleEnc)
lazy val BoxedUnitClass = ctx.requiredClass("scala.runtime.BoxedUnit")
+
+ lazy val BoxedUnit_UNIT = BoxedUnitClass.linkedClass.requiredValue("UNIT")
+
lazy val BoxedBooleanClass = ctx.requiredClass("java.lang.Boolean")
lazy val BoxedByteClass = ctx.requiredClass("java.lang.Byte")
lazy val BoxedShortClass = ctx.requiredClass("java.lang.Short")
@@ -428,6 +431,8 @@ class Definitions {
FloatClass,
DoubleClass)
+ lazy val ScalaBoxedClasses = ScalaValueClasses map boxedClass
+
private[this] val _boxedClass = mutable.Map[Symbol, Symbol]()
private[this] val _unboxedClass = mutable.Map[Symbol, Symbol]()
diff --git a/src/dotty/tools/dotc/core/NameOps.scala b/src/dotty/tools/dotc/core/NameOps.scala
index b9df8f6ed..35f94aac7 100644
--- a/src/dotty/tools/dotc/core/NameOps.scala
+++ b/src/dotty/tools/dotc/core/NameOps.scala
@@ -181,6 +181,14 @@ object NameOps {
name.drop(tpnme.HK_TRAIT_PREFIX.length).toList.map(varianceOfSuffix)
}
+ /** The name of the generic runtime operation corresponding to an array operation */
+ def genericArrayOp: TermName = name match {
+ case nme.apply => nme.array_apply
+ case nme.length => nme.array_length
+ case nme.update => nme.array_update
+ case nme.clone_ => nme.array_clone
+ }
+
/** If name length exceeds allowable limit, replace part of it by hash */
def compactified(implicit ctx: Context): TermName = termName(compactify(name.toString))
}
diff --git a/src/dotty/tools/dotc/core/SymDenotations.scala b/src/dotty/tools/dotc/core/SymDenotations.scala
index b5c7ea539..f07eaa2be 100644
--- a/src/dotty/tools/dotc/core/SymDenotations.scala
+++ b/src/dotty/tools/dotc/core/SymDenotations.scala
@@ -1214,6 +1214,10 @@ object SymDenotations {
val cname = if (this is ImplClass) nme.IMPLCLASS_CONSTRUCTOR else nme.CONSTRUCTOR
decls.denotsNamed(cname).first.symbol
}
+
+ def underlyingOfValueClass: Type = ???
+
+ def valueClassUnbox: Symbol = ???
}
/** The denotation of a package class.
diff --git a/src/dotty/tools/dotc/core/transform/Erasure.scala b/src/dotty/tools/dotc/core/transform/Erasure.scala
index 89a504ac6..353911444 100644
--- a/src/dotty/tools/dotc/core/transform/Erasure.scala
+++ b/src/dotty/tools/dotc/core/transform/Erasure.scala
@@ -7,6 +7,10 @@ import util.DotClass
object Erasure {
+ case class ErasedValueType(cls: ClassSymbol, underlying: Type) extends CachedGroundType {
+ override def computeHash = doHash(cls, underlying)
+ }
+
private def erasureIdx(isJava: Boolean, isSemi: Boolean, isConstructor: Boolean, wildcardOK: Boolean) =
(if (isJava) 1 else 0) +
(if (isSemi) 2 else 0) +
@@ -123,10 +127,14 @@ class Erasure(isJava: Boolean, isSemi: Boolean, isConstructor: Boolean, wildcard
val parent = tp.parent
if (parent isRef defn.ArrayClass) eraseArray(tp)
else this(parent)
- case tp: ConstantType =>
+ case tp: TermRef =>
+ val sym = tp.symbol
+ if (sym.owner is Package) sym.termRef
+ else tp.derivedSelect(this(tp.prefix))
+ case _: ThisType | _: ConstantType =>
tp
case tp: TypeProxy =>
- this(tp.underlying)
+ this(tp.underlying)
case AndType(tp1, tp2) =>
mergeAnd(this(tp1), this(tp2))
case OrType(tp1, tp2) =>
@@ -138,7 +146,7 @@ class Erasure(isJava: Boolean, isSemi: Boolean, isConstructor: Boolean, wildcard
case tp: PolyType =>
this(tp.resultType)
case tp @ ClassInfo(pre, cls, classParents, decls, _) =>
- def eraseTypeRef = this.asInstanceOf[TypeRef => TypeRef]
+ def eraseTypeRef(p: TypeRef) = this(p).asInstanceOf[TypeRef]
val parents: List[TypeRef] =
if ((cls eq defn.ObjectClass) || cls.isPrimitiveValueClass) Nil
else if (cls eq defn.ArrayClass) defn.ObjectClass.typeRef :: Nil
diff --git a/src/dotty/tools/dotc/reporting/Reporter.scala b/src/dotty/tools/dotc/reporting/Reporter.scala
index e2b320588..933262861 100644
--- a/src/dotty/tools/dotc/reporting/Reporter.scala
+++ b/src/dotty/tools/dotc/reporting/Reporter.scala
@@ -106,8 +106,16 @@ trait Reporting { this: Context =>
def incompleteInputError(msg: String, pos: SourcePosition = NoSourcePosition)(implicit ctx: Context): Unit =
reporter.incomplete(Diagnostic(msg, pos, ERROR))(ctx)
+ /** Log msg if current phase or its precedessor is mentioned in
+ * settings.log.
+ * The reason we also pick the predecessor is that during the
+ * tree transform of phase X, we often are already in phase X+1.
+ * It's convenient to have logging work independently of whether
+ * we have advanced the phase or not.
+ */
def log(msg: => String): Unit =
- if (this.settings.log.value.containsPhase(phase))
+ if (this.settings.log.value.containsPhase(phase) ||
+ this.settings.log.value.containsPhase(phase.prev))
echo(s"[log ${ctx.phasesStack.reverse.mkString(" -> ")}] $msg")
def debuglog(msg: => String): Unit =
diff --git a/src/dotty/tools/dotc/transform/Erasure.scala b/src/dotty/tools/dotc/transform/Erasure.scala
new file mode 100644
index 000000000..7d9abc5ba
--- /dev/null
+++ b/src/dotty/tools/dotc/transform/Erasure.scala
@@ -0,0 +1,287 @@
+package dotty.tools.dotc
+package transform
+
+import core.Phases._
+import core.DenotTransformers._
+import core.Denotations._
+import core.SymDenotations._
+import core.Symbols._
+import core.Contexts._
+import core.Types._
+import core.Names._
+import core.StdNames._
+import core.NameOps._
+import core.Decorators._
+import core.Constants._
+import typer.NoChecking
+import typer.ProtoTypes._
+import typer.ErrorReporting._
+import core.transform.Erasure._
+import core.Decorators._
+import ast.{tpd, untpd}
+import ast.Trees._
+
+class Erasure extends Phase with DenotTransformer {
+
+ override def name: String = "erasure"
+
+ def transform(ref: SingleDenotation)(implicit ctx: Context): SingleDenotation = ref match {
+ case ref: SymDenotation =>
+ assert(ctx.phase == this, s"transforming $ref at ${ctx.phase}")
+ ref.copySymDenotation(info = transformInfo(ref.symbol, ref.info))
+ case ref =>
+ ref.derivedSingleDenotation(ref.symbol, erasure(ref.info))
+ }
+
+ val eraser = new Erasure.Typer
+
+ def run(implicit ctx: Context): Unit = {
+ val unit = ctx.compilationUnit
+ unit.tpdTree = eraser.typedExpr(unit.tpdTree)(ctx.fresh.withPhase(this.next))
+ }
+}
+
+object Erasure {
+
+ import tpd._
+
+ object Boxing {
+
+ def isUnbox(sym: Symbol)(implicit ctx: Context) =
+ sym.name == nme.unbox && (defn.ScalaBoxedClasses contains sym.owner)
+
+ def isBox(sym: Symbol)(implicit ctx: Context) =
+ sym.name == nme.box && (defn.ScalaValueClasses contains sym.owner)
+
+ def boxMethod(cls: ClassSymbol)(implicit ctx: Context) = cls.info.member(nme.box).symbol
+ def unboxMethod(cls: ClassSymbol)(implicit ctx: Context) = cls.info.member(nme.unbox).symbol
+
+ /** Isf this tree is an unbox operation which can be safely removed
+ * when enclosed in a box, the unboxed argument, otherwise EmptyTree.
+ * Note that one can't always remove a Box(Unbox(x)) combination because the
+ * process of unboxing x may lead to throwing an exception.
+ * This is important for specialization: calls to the super constructor should not box/unbox specialized
+ * fields (see TupleX). (ID)
+ */
+ private def safelyRemovableUnboxArg(tree: Tree)(implicit ctx: Context): Tree = tree match {
+ case Apply(fn, arg :: Nil)
+ if isUnbox(fn.symbol) && (defn.ScalaBoxedClasses contains arg.tpe.typeSymbol) =>
+ arg
+ case _ =>
+ EmptyTree
+ }
+
+ def isErasedValueType(tpe: Type)(implicit ctx: Context): Boolean = tpe.isInstanceOf[ErasedValueType]
+ def isPrimitiveValueType(tpe: Type)(implicit ctx: Context): Boolean = tpe.classSymbol.isPrimitiveValueClass
+
+ def constant(tree: Tree, const: Tree)(implicit ctx: Context) =
+ if (isIdempotentExpr(tree)) Block(tree :: Nil, const) else const
+
+ final def box(tree: Tree, target: => String = "")(implicit ctx: Context): Tree = ctx.traceIndented(i"boxing ${tree.showSummary}: ${tree.tpe} into $target") {
+ tree.tpe match {
+ case ErasedValueType(clazz, _) =>
+ New(clazz.typeRef, cast(tree, clazz.underlyingOfValueClass) :: Nil) // todo: use adaptToType?
+ case _ =>
+ val cls = tree.tpe.classSymbol
+ if (cls eq defn.UnitClass) constant(tree, ref(defn.BoxedUnit_UNIT))
+ else if (cls eq defn.NothingClass) tree // a non-terminating expression doesn't need boxing
+ else {
+ assert(cls ne defn.ArrayClass)
+ val arg = safelyRemovableUnboxArg(tree)
+ if (arg.isEmpty) Apply(ref(boxMethod(cls.asClass)), tree :: Nil)
+ else {
+ ctx.log(s"boxing an unbox: ${tree.symbol} -> ${arg.tpe}")
+ arg
+ }
+ }
+ }
+ }
+
+ def unbox(tree: Tree, pt: Type)(implicit ctx: Context): Tree = ctx.traceIndented(i"unboxing ${tree.showSummary}: ${tree.tpe} as a $pt") {
+ pt match {
+ case ErasedValueType(clazz, underlying) =>
+ val tree1 =
+ if ((tree.tpe isRef defn.NullClass) && isPrimitiveValueType(underlying))
+ // convert `null` directly to underlying type, as going
+ // via the unboxed type would yield a NPE (see SI-5866)
+ unbox(tree, underlying)
+ else
+ Apply(Select(adaptToType(tree, clazz.typeRef), clazz.valueClassUnbox), Nil)
+ cast(tree1, pt)
+ case _ =>
+ val cls = pt.classSymbol
+ if (cls eq defn.UnitClass) constant(tree, Literal(Constant(())))
+ else {
+ assert(cls ne defn.ArrayClass)
+ Apply(ref(unboxMethod(cls.asClass)), tree :: Nil)
+ }
+ }
+ }
+
+ /** Generate a synthetic cast operation from tree.tpe to pt.
+ */
+ def cast(tree: Tree, pt: Type)(implicit ctx: Context): Tree =
+ if (pt isRef defn.UnitClass) unbox(tree, pt)
+ else (tree.tpe, pt) match {
+ case (defn.ArrayType(treeElem), defn.ArrayType(ptElem))
+ if isPrimitiveValueType(treeElem) && !isPrimitiveValueType(ptElem) =>
+ // See SI-2386 for one example of when this might be necessary.
+ cast(runtimeCall(nme.toObjectArray, tree :: Nil), pt)
+ case _ =>
+ println(s"casting from ${tree.showSummary}: ${tree.tpe.show} to ${pt.show}")
+ TypeApply(Select(tree, defn.Object_asInstanceOf), TypeTree(pt) :: Nil)
+ }
+
+ /** Adaptation of an expression `e` to an expected type `PT`, applying the following
+ * rewritings exhaustively as long as the type of `e` is not a subtype of `PT`.
+ *
+ * e -> box(e) if `e` is of erased value type
+ * e -> unbox(e, PT) otherwise, if `PT` is an erased value type
+ * e -> box(e) if `e` is of primitive type and `PT` is not a primitive type
+ * e -> unbox(e, PT) if `PT` is a primitive type and `e` is not of primitive type
+ * e -> cast(e, PT) otherwise
+ */
+ def adaptToType(tree: Tree, pt: Type)(implicit ctx: Context): Tree =
+ if (tree.tpe <:< pt)
+ tree
+ else if (isErasedValueType(tree.tpe))
+ adaptToType(box(tree), pt)
+ else if (isErasedValueType(pt))
+ adaptToType(unbox(tree, pt), pt)
+ else if (isPrimitiveValueType(tree.tpe) && !isPrimitiveValueType(pt))
+ adaptToType(box(tree), pt)
+ else if (isPrimitiveValueType(pt) && !isPrimitiveValueType(tree.tpe))
+ adaptToType(unbox(tree, pt), pt)
+ else
+ cast(tree, pt)
+ }
+
+ class Typer extends typer.Typer with NoChecking {
+ import Boxing._
+
+ def box(tree: Tree): Tree = ???
+ def unbox(tree: Tree, target: Type): Tree = ???
+ def cast(tree: Tree, target: Type): Tree = ???
+
+ private def promote(tree: untpd.Tree)(implicit ctx: Context): tree.ThisTree[Type] = {
+ assert(tree.hasType)
+ println(s"prompting ${tree.show}: ${tree.tpe.asInstanceOf[Type].showWithUnderlying(2)}")
+ tree.withType(erasure(tree.tpe.asInstanceOf[Type]))
+ }
+
+ override def typedIdent(tree: untpd.Ident, pt: Type)(implicit ctx: Context): Tree = {
+ val tree1 = promote(tree)
+ println(i"typed ident ${tree.name}: ${tree1.tpe} at phase ${ctx.phase}, history = ${tree1.symbol.history}")
+ tree1
+ }
+
+ /** Type check select nodes, applying the following rewritings exhaustively
+ * on selections `e.m`.
+ *
+ * e.m1 -> e.m2 if `m1` is a member of Any or AnyVal and `m2` is
+ * the same-named member in Object.
+ * e.m -> box(e).m if `e` is primitive and `m` is a member or a reference class
+ * or `e` has an erased value class type.
+ * e.m -> unbox(e).m if `e` is not primitive and `m` is a member of a primtive type.
+ *
+ * Additionally, if the type of `e` does not derive from the type `OT` of the owner of `m`,
+ * the following rewritings are performed, where `ET` is the erased type of the selection's
+ * original qualifier expression.
+ *
+ * e.m -> cast(OT).m if `m` is not an array operation
+ * e.m -> cast(ET).m if `m` is an array operation and `ET` is an array type
+ * e.m -> runtime.array_m(e)
+ * if `m` is an array operation and `ET` is Object
+ */
+ override def typedSelect(tree: untpd.Select, pt: Type)(implicit ctx: Context): Tree = {
+ val sym = tree.symbol
+ assert(sym.exists)
+
+ def select(qual: Tree, sym: Symbol): Tree =
+ untpd.cpy.Select(tree, qual, sym.name) withType qual.tpe.select(sym)
+
+ def selectArrayMember(qual: Tree, erasedPre: Type) =
+ if (erasedPre isRef defn.ObjectClass) runtimeCall(tree.name.genericArrayOp, qual :: Nil)
+ else recur(cast(qual, erasedPre))
+
+ def recur(qual: Tree): Tree = {
+ val qualIsPrimitive = isPrimitiveValueType(qual.tpe)
+ val symIsPrimitive = sym.owner.isPrimitiveValueClass
+ if ((sym.owner eq defn.AnyClass) || (sym.owner eq defn.AnyValClass))
+ select(qual, defn.ObjectClass.info.decl(sym.name).symbol)
+ else if (qualIsPrimitive && !symIsPrimitive || isErasedValueType(qual.tpe))
+ recur(box(qual))
+ else if (!qualIsPrimitive && symIsPrimitive)
+ recur(unbox(qual, sym.owner.typeRef))
+ else if (qual.tpe.derivesFrom(sym.owner) || qual.isInstanceOf[Super])
+ select(qual, sym)
+ else if (sym.owner eq defn.ArrayClass)
+ selectArrayMember(qual, erasure(tree.qualifier.tpe))
+ else
+ recur(cast(qual, sym.owner.typeRef))
+ }
+
+ recur(typed(tree.qualifier, AnySelectionProto))
+ }
+
+ override def typedTypeApply(tree: untpd.TypeApply, pt: Type)(implicit ctx: Context) =
+ typedExpr(tree.fun, pt)
+
+ override def typedApply(tree: untpd.Apply, pt: Type)(implicit ctx: Context): Tree = {
+ val Apply(fun, args) = tree
+ val fun1 = typedExpr(fun, WildcardType)
+ fun1.tpe.widen match {
+ case mt: MethodType =>
+ val args1 = args.zipWithConserve(mt.paramTypes)(typedExpr)
+ untpd.cpy.Apply(tree, fun1, args1) withType mt.resultType
+ }
+ }
+
+ override def typedTypeTree(tree: untpd.TypeTree, pt: Type)(implicit ctx: Context): TypeTree =
+ promote(tree)
+
+ override def ensureNoLocalRefs(block: Block, pt: Type, forcedDefined: Boolean = false)(implicit ctx: Context): Tree =
+ block // optimization, no checking needed, as block symbols do not change.
+
+ override def typedDefDef(ddef: untpd.DefDef, sym: Symbol)(implicit ctx: Context) = {
+ val ddef1 = untpd.cpy.DefDef(ddef, ddef.mods, ddef.name, Nil, ddef.vparamss, ddef.tpt, ddef.rhs)
+ super.typedDefDef(ddef1, sym)
+ }
+
+ override def typedClassDef(cdef: untpd.TypeDef, sym: ClassSymbol)(implicit ctx: Context) = {
+ val TypeDef(mods, name, impl @ Template(constr, parents, self, body)) = cdef
+ val cdef1 = untpd.cpy.TypeDef(cdef, mods, name,
+ untpd.cpy.Template(impl, constr, parents, untpd.EmptyValDef, body))
+ super.typedClassDef(cdef1, sym)
+ }
+
+ /*
+ override def transformStats(stats: List[Tree], exprOwner: Symbol)(implicit ctx: Context) = {
+ val stats1 = super.transform(stats, exprOwner)
+ if (ctx.owner.isClass) addBridges(stats1) else stats1
+ }
+*/
+ override def typedNamed(tree: untpd.NameTree, pt: Type)(implicit ctx: Context): Tree = {
+ if (tree eq untpd.EmptyValDef) return tpd.EmptyValDef
+ assert(tree.hasType, tree)
+ val sym = tree.symbol
+ assert(sym.exists, tree)
+ def localContext = ctx.fresh.setTree(tree).setOwner(sym)
+ tree match {
+ case tree: untpd.Ident => typedIdent(tree, pt)
+ case tree: untpd.Select => typedSelect(tree, pt)
+ case tree: untpd.ValDef => typedValDef(tree, sym)(localContext)
+ case tree: untpd.DefDef => typedDefDef(tree, sym)(localContext)
+ case tree: untpd.TypeDef =>
+ if (tree.isClassDef) typedClassDef(tree, sym.asClass)(localContext)
+ else EmptyTree
+ }
+ }
+
+ override def adapt(tree: Tree, pt: Type)(implicit ctx: Context): Tree =
+ ctx.traceIndented(i"adapting ${tree.showSummary}: ${tree.tpe} to $pt", show = true) {
+ assert(ctx.phase == ctx.erasurePhase.next)
+ if (tree.isEmpty) tree else adaptToType(tree, pt)
+ }
+ }
+} \ No newline at end of file
diff --git a/src/dotty/tools/dotc/typer/Checking.scala b/src/dotty/tools/dotc/typer/Checking.scala
index 36822cb85..5818da180 100644
--- a/src/dotty/tools/dotc/typer/Checking.scala
+++ b/src/dotty/tools/dotc/typer/Checking.scala
@@ -18,26 +18,13 @@ import ErrorReporting.{errorType, InfoString}
import config.Printers._
import collection.mutable
-trait NoChecking {
- import tpd._
- def checkValue(tree: Tree, proto: Type)(implicit ctx: Context): tree.type = tree
- def checkBounds(args: List[tpd.Tree], poly: PolyType, pos: Position)(implicit ctx: Context): Unit = ()
- def checkStable(tp: Type, pos: Position)(implicit ctx: Context): Unit = ()
- def checkLegalPrefix(tp: Type, pos: Position)(implicit ctx: Context): Unit = ()
- def checkClassTypeWithStablePrefix(tp: Type, pos: Position, traitReq: Boolean)(implicit ctx: Context): Type = tp
- def checkImplicitTptNonEmpty(defTree: untpd.ValOrDefDef)(implicit ctx: Context): Unit = ()
- def checkImplicitParamsNotSingletons(vparamss: List[List[ValDef]])(implicit ctx: Context): Unit = ()
- def checkFeasible(tp: Type, pos: Position, where: => String = "")(implicit ctx: Context): Type = tp
- def checkNoDoubleDefs(cls: Symbol)(implicit ctx: Context): Unit = ()
-}
-
-trait Checking extends NoChecking {
+trait Checking {
import tpd._
/** Check that Java statics and packages can only be used in selections.
*/
- override def checkValue(tree: Tree, proto: Type)(implicit ctx: Context): tree.type = {
+ def checkValue(tree: Tree, proto: Type)(implicit ctx: Context): tree.type = {
if (!proto.isInstanceOf[SelectionProto]) {
val sym = tree.tpe.termSymbol
if ((sym is Package) || (sym is JavaModule)) ctx.error(i"$sym is not a value", tree.pos)
@@ -46,7 +33,7 @@ trait Checking extends NoChecking {
}
/** Check that type arguments `args` conform to corresponding bounds in `poly` */
- override def checkBounds(args: List[tpd.Tree], poly: PolyType, pos: Position)(implicit ctx: Context): Unit = {
+ def checkBounds(args: List[tpd.Tree], poly: PolyType, pos: Position)(implicit ctx: Context): Unit = {
val argTypes = args.tpes
def substituted(tp: Type) = tp.substParams(poly, argTypes)
for ((arg, bounds) <- args zip poly.paramBounds) {
@@ -58,20 +45,20 @@ trait Checking extends NoChecking {
}
/** Check that type `tp` is stable. */
- override def checkStable(tp: Type, pos: Position)(implicit ctx: Context): Unit =
+ def checkStable(tp: Type, pos: Position)(implicit ctx: Context): Unit =
if (!tp.isStable) ctx.error(i"$tp is not stable", pos)
/** Check that type `tp` is a legal prefix for '#'.
* @return The type itself
*/
- override def checkLegalPrefix(tp: Type, pos: Position)(implicit ctx: Context): Unit =
+ def checkLegalPrefix(tp: Type, pos: Position)(implicit ctx: Context): Unit =
if (!tp.isLegalPrefix) ctx.error(i"$tp is not a valid prefix for '#'", pos)
/** Check that `tp` is a class type with a stable prefix. Also, if `isFirst` is
* false check that `tp` is a trait.
* @return `tp` itself if it is a class or trait ref, ObjectClass.typeRef if not.
*/
- override def checkClassTypeWithStablePrefix(tp: Type, pos: Position, traitReq: Boolean)(implicit ctx: Context): Type =
+ def checkClassTypeWithStablePrefix(tp: Type, pos: Position, traitReq: Boolean)(implicit ctx: Context): Type =
tp.underlyingClassRef match {
case tref: TypeRef =>
checkStable(tref.prefix, pos)
@@ -83,7 +70,7 @@ trait Checking extends NoChecking {
}
/** Check that (return) type of implicit definition is not empty */
- override def checkImplicitTptNonEmpty(defTree: untpd.ValOrDefDef)(implicit ctx: Context): Unit = defTree.tpt match {
+ def checkImplicitTptNonEmpty(defTree: untpd.ValOrDefDef)(implicit ctx: Context): Unit = defTree.tpt match {
case tpt: untpd.DerivedTypeTree =>
case TypeTree(untpd.EmptyTree) =>
val resStr = if (defTree.isInstanceOf[untpd.DefDef]) "result " else ""
@@ -94,7 +81,7 @@ trait Checking extends NoChecking {
/** Check that a non-implicit parameter making up the first parameter section of an
* implicit conversion is not a singleton type.
*/
- override def checkImplicitParamsNotSingletons(vparamss: List[List[ValDef]])(implicit ctx: Context): Unit = vparamss match {
+ def checkImplicitParamsNotSingletons(vparamss: List[List[ValDef]])(implicit ctx: Context): Unit = vparamss match {
case (vparam :: Nil) :: _ if !(vparam.symbol is Implicit) =>
if (vparam.tpt.tpe.isInstanceOf[SingletonType])
ctx.error(s"implicit conversion may not have a parameter of singleton type", vparam.tpt.pos)
@@ -105,7 +92,7 @@ trait Checking extends NoChecking {
* their lower bound conforms to their upper cound. If a type argument is
* infeasible, issue and error and continue with upper bound.
*/
- override def checkFeasible(tp: Type, pos: Position, where: => String = "")(implicit ctx: Context): Type = tp match {
+ def checkFeasible(tp: Type, pos: Position, where: => String = "")(implicit ctx: Context): Type = tp match {
case tp: RefinedType =>
tp.derivedRefinedType(tp.parent, tp.refinedName, checkFeasible(tp.refinedInfo, pos, where))
case tp @ TypeBounds(lo, hi) if !(lo <:< hi) =>
@@ -116,7 +103,7 @@ trait Checking extends NoChecking {
}
/** Check that class does not define */
- override def checkNoDoubleDefs(cls: Symbol)(implicit ctx: Context): Unit = {
+ def checkNoDoubleDefs(cls: Symbol)(implicit ctx: Context): Unit = {
val seen = new mutable.HashMap[Name, List[Symbol]] {
override def default(key: Name) = Nil
}
@@ -147,4 +134,17 @@ trait Checking extends NoChecking {
def checkInstantiatable(cls: ClassSymbol, pos: Position): Unit = {
??? // to be done in later phase: check that class `cls` is legal in a new.
}
+}
+
+trait NoChecking extends Checking {
+ import tpd._
+ override def checkValue(tree: Tree, proto: Type)(implicit ctx: Context): tree.type = tree
+ override def checkBounds(args: List[tpd.Tree], poly: PolyType, pos: Position)(implicit ctx: Context): Unit = ()
+ override def checkStable(tp: Type, pos: Position)(implicit ctx: Context): Unit = ()
+ override def checkLegalPrefix(tp: Type, pos: Position)(implicit ctx: Context): Unit = ()
+ override def checkClassTypeWithStablePrefix(tp: Type, pos: Position, traitReq: Boolean)(implicit ctx: Context): Type = tp
+ override def checkImplicitTptNonEmpty(defTree: untpd.ValOrDefDef)(implicit ctx: Context): Unit = ()
+ override def checkImplicitParamsNotSingletons(vparamss: List[List[ValDef]])(implicit ctx: Context): Unit = ()
+ override def checkFeasible(tp: Type, pos: Position, where: => String = "")(implicit ctx: Context): Type = tp
+ override def checkNoDoubleDefs(cls: Symbol)(implicit ctx: Context): Unit = ()
} \ No newline at end of file
diff --git a/src/dotty/tools/dotc/typer/Typer.scala b/src/dotty/tools/dotc/typer/Typer.scala
index 0ba53f8c0..87bc643a3 100644
--- a/src/dotty/tools/dotc/typer/Typer.scala
+++ b/src/dotty/tools/dotc/typer/Typer.scala
@@ -398,7 +398,7 @@ class Typer extends Namer with TypeAssigner with Applications with Implicits wit
* 2. If (1) fails, force all type variables so that the block's type is
* fully defined and try again.
*/
- private def ensureNoLocalRefs(block: Block, pt: Type, forcedDefined: Boolean = false)(implicit ctx: Context): Tree = {
+ protected def ensureNoLocalRefs(block: Block, pt: Type, forcedDefined: Boolean = false)(implicit ctx: Context): Tree = {
val Block(stats, expr) = block
val leaks = CheckTrees.escapingRefs(block)
if (leaks.isEmpty) block
@@ -858,41 +858,46 @@ class Typer extends Namer with TypeAssigner with Applications with Implicits wit
def typedAsFunction(tree: untpd.Tree, pt: Type)(implicit ctx: Context): Tree =
typed(tree, if (defn.isFunctionType(pt)) pt else AnyFunctionProto)
+ def typedNamed(xtree: untpd.NameTree, pt: Type)(implicit ctx: Context): Tree = {
+ val tree = xtree withName xtree.name.encode
+ val sym = xtree.removeAttachment(SymOfTree) match {
+ case Some(sym) =>
+ sym.ensureCompleted()
+ sym
+ case none =>
+ NoSymbol
+ }
+
+ def localContext = {
+ val freshCtx = ctx.fresh.setTree(xtree)
+ if (sym.exists) freshCtx.setOwner(sym)
+ else freshCtx // can happen for self defs
+ }
+
+ tree match {
+ case tree: untpd.Ident => typedIdent(tree, pt)
+ case tree: untpd.Select => typedSelect(tree, pt)
+ case tree: untpd.SelectFromTypeTree => typedSelectFromTypeTree(tree, pt)
+ case tree: untpd.Bind => typedBind(tree, pt)
+ case tree: untpd.ValDef =>
+ if (tree.isEmpty) tpd.EmptyValDef
+ else typedValDef(tree, sym)(localContext.clearScope)
+ case tree: untpd.DefDef =>
+ val typer1 = nestedTyper.remove(sym).get
+ typer1.typedDefDef(tree, sym)(localContext.setTyper(typer1))
+ case tree: untpd.TypeDef =>
+ if (tree.isClassDef) typedClassDef(tree, sym.asClass)(localContext)
+ else typedTypeDef(tree, sym)(localContext.clearScope)
+ case _ => typedUnadapted(desugar(tree), pt)
+ }
+ }
+
def typedUnadapted(initTree: untpd.Tree, pt: Type = WildcardType)(implicit ctx: Context): Tree = {
record("typedUnadapted")
val xtree = expanded(initTree)
xtree.removeAttachment(TypedAhead) match {
case Some(ttree) => ttree
case none =>
- val sym = xtree.removeAttachment(SymOfTree) match {
- case Some(sym) =>
- sym.ensureCompleted()
- sym
- case none =>
- NoSymbol
- }
- def localContext = {
- val freshCtx = ctx.fresh.setTree(xtree)
- if (sym.exists) freshCtx.setOwner(sym)
- else freshCtx // can happen for self defs
- }
-
- def typedNamed(tree: untpd.NameTree): Tree = tree match {
- case tree: untpd.Ident => typedIdent(tree, pt)
- case tree: untpd.Select => typedSelect(tree, pt)
- case tree: untpd.SelectFromTypeTree => typedSelectFromTypeTree(tree, pt)
- case tree: untpd.Bind => typedBind(tree, pt)
- case tree: untpd.ValDef =>
- if (tree.isEmpty) tpd.EmptyValDef
- else typedValDef(tree, sym)(localContext.clearScope)
- case tree: untpd.DefDef =>
- val typer1 = nestedTyper.remove(sym).get
- typer1.typedDefDef(tree, sym)(localContext.setTyper(typer1))
- case tree: untpd.TypeDef =>
- if (tree.isClassDef) typedClassDef(tree, sym.asClass)(localContext)
- else typedTypeDef(tree, sym)(localContext.clearScope)
- case _ => typedUnadapted(desugar(tree), pt)
- }
def typedUnnamed(tree: untpd.Tree): Tree = tree match {
case tree: untpd.Apply =>
@@ -924,7 +929,6 @@ class Typer extends Namer with TypeAssigner with Applications with Implicits wit
case tree: untpd.ByNameTypeTree => typedByNameTypeTree(tree)
case tree: untpd.TypeBoundsTree => typedTypeBoundsTree(tree)
case tree: untpd.Alternative => typedAlternative(tree, pt)
- case tree: untpd.Import => typedImport(tree, sym)
case tree: untpd.PackageDef => typedPackageDef(tree)
case tree: untpd.Annotated => typedAnnotated(tree, pt)
case tree: untpd.TypedSplice => tree.tree
@@ -934,7 +938,8 @@ class Typer extends Namer with TypeAssigner with Applications with Implicits wit
}
xtree match {
- case xtree: untpd.NameTree => typedNamed(xtree withName xtree.name.encode)
+ case xtree: untpd.NameTree => typedNamed(xtree, pt)
+ case xtree: untpd.Import => typedImport(xtree, xtree.removeAttachment(SymOfTree).get)
case xtree => typedUnnamed(xtree)
}
}