diff options
author | Martin Odersky <odersky@gmail.com> | 2014-03-26 19:15:40 +0100 |
---|---|---|
committer | Dmitry Petrashko <dmitry.petrashko@gmail.com> | 2014-03-29 09:29:05 +0100 |
commit | 37cf9efe509b0bac4fb6bda01b7126e7511e43f0 (patch) | |
tree | ee5fa94171acf87de4e5da5bf7ee704a18596163 | |
parent | 0a1e969cb09e953d6b3f3b64b63a050588aa3360 (diff) | |
download | dotty-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.scala | 3 | ||||
-rw-r--r-- | src/dotty/tools/dotc/core/Definitions.scala | 5 | ||||
-rw-r--r-- | src/dotty/tools/dotc/core/NameOps.scala | 8 | ||||
-rw-r--r-- | src/dotty/tools/dotc/core/SymDenotations.scala | 4 | ||||
-rw-r--r-- | src/dotty/tools/dotc/core/transform/Erasure.scala | 14 | ||||
-rw-r--r-- | src/dotty/tools/dotc/reporting/Reporter.scala | 10 | ||||
-rw-r--r-- | src/dotty/tools/dotc/transform/Erasure.scala | 287 | ||||
-rw-r--r-- | src/dotty/tools/dotc/typer/Checking.scala | 46 | ||||
-rw-r--r-- | src/dotty/tools/dotc/typer/Typer.scala | 69 |
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) } } |