diff --git a/src/dotty/tools/dotc/Compiler.scala b/src/dotty/tools/dotc/Compiler.scala index 7b657b47be21..709f4b2db299 100644 --- a/src/dotty/tools/dotc/Compiler.scala +++ b/src/dotty/tools/dotc/Compiler.scala @@ -9,20 +9,22 @@ import Scopes._ import typer.{FrontEnd, Typer, Mode, ImportInfo} import reporting.ConsoleReporter import dotty.tools.dotc.core.Phases.Phase -import dotty.tools.dotc.transform.{UncurryTreeTransform, LazyValsCreateCompanionObjects, LazyValTranformContext} +import dotty.tools.dotc.transform._ import dotty.tools.dotc.transform.TreeTransforms.{TreeTransform, TreeTransformer} import dotty.tools.dotc.transform.PostTyperTransformers.PostTyperTransformer import dotty.tools.dotc.core.DenotTransformers.DenotTransformer import dotty.tools.dotc.core.Denotations.SingleDenotation -import dotty.tools.dotc.transform.TreeTransforms.Separator class Compiler { - def phases: List[List[Phase]] = List( - List(new FrontEnd), List(new LazyValsCreateCompanionObjects), - //force separataion between lazyVals and LVCreateCO - List(new LazyValTranformContext().transformer, new UncurryTreeTransform) - ) + def phases: List[List[Phase]] = + List( + List(new FrontEnd), + List(new LazyValsCreateCompanionObjects), //force separataion between lazyVals and LVCreateCO + List(new LazyValTranformContext().transformer, new TypeTestsCasts), + List(new Erasure), + List(new UncurryTreeTransform) + ) var runId = 1 def nextRunId = { diff --git a/src/dotty/tools/dotc/Run.scala b/src/dotty/tools/dotc/Run.scala index 89ca45071b05..3f15bd4c3597 100644 --- a/src/dotty/tools/dotc/Run.scala +++ b/src/dotty/tools/dotc/Run.scala @@ -2,7 +2,7 @@ package dotty.tools package dotc import core._ -import Contexts._, Periods._, Symbols._ +import Contexts._, Periods._, Symbols._, Phases._, Decorators._ import io.PlainFile import util.{SourceFile, NoSource, Stats, SimpleMap} import reporting.Reporter @@ -30,13 +30,29 @@ class Run(comp: Compiler)(implicit ctx: Context) { def compileSources(sources: List[SourceFile]) = Stats.monitorHeartBeat { if (sources forall (_.exists)) { units = sources map (new CompilationUnit(_)) - for (phase <- ctx.allPhases.init) { - if (!ctx.reporter.hasErrors) + def stoppedBefore(phase: Phase) = + ctx.settings.YstopBefore.value.containsPhase(phase) || + ctx.settings.YstopAfter.value.containsPhase(phase.prev) + val phasesToRun = ctx.allPhases.init + .takeWhile(!stoppedBefore(_)) + .filterNot(ctx.settings.Yskip.value.containsPhase(_)) + for (phase <- phasesToRun) { + if (!ctx.reporter.hasErrors) { phase.runOn(units) + if (ctx.settings.Xprint.value.containsPhase(phase)) + for (unit <- units) + printTree(ctx.fresh.setPhase(phase).setCompilationUnit(unit)) + } } } } + private def printTree(implicit ctx: Context) = { + val unit = ctx.compilationUnit + println(s"result of $unit after ${ctx.phase}:") + println(unit.tpdTree.show) + } + def compile(sourceCode: String): Unit = { val virtualFile = new VirtualFile(sourceCode) // use source code as name as it's used for equals val writer = new BufferedWriter(new OutputStreamWriter(virtualFile.output, "UTF-8")) // buffering is still advised by javadoc diff --git a/src/dotty/tools/dotc/ast/Trees.scala b/src/dotty/tools/dotc/ast/Trees.scala index 5eb9a6409bc5..f69ee265e024 100644 --- a/src/dotty/tools/dotc/ast/Trees.scala +++ b/src/dotty/tools/dotc/ast/Trees.scala @@ -384,6 +384,7 @@ object Trees { trait DefTree[-T >: Untyped] extends DenotingTree[T] { type ThisTree[-T >: Untyped] <: DefTree[T] override def isDef = true + def namedType = tpe.asInstanceOf[NamedType] } /** Tree defines a new symbol and carries modifiers. diff --git a/src/dotty/tools/dotc/ast/tpd.scala b/src/dotty/tools/dotc/ast/tpd.scala index 9d4ec21dec5e..173f8189401b 100644 --- a/src/dotty/tools/dotc/ast/tpd.scala +++ b/src/dotty/tools/dotc/ast/tpd.scala @@ -5,7 +5,7 @@ package ast import core._ import util.Positions._, Types._, Contexts._, Constants._, Names._, Flags._ import SymDenotations._, Symbols._, StdNames._, Annotations._, Trees._ -import CheckTrees._, Denotations._ +import CheckTrees._, Denotations._, Decorators._ import config.Printers._ /** Some creators for typed trees */ @@ -27,6 +27,9 @@ object tpd extends Trees.Instance[Type] with TypedTreeInfo { def Select(qualifier: Tree, tp: NamedType)(implicit ctx: Context): Select = untpd.Select(qualifier, tp.name).withType(tp) + def Select(qualifier: Tree, sym: Symbol)(implicit ctx: Context): Select = + untpd.Select(qualifier, sym.name).withType(qualifier.tpe select sym) + def SelectWithSig(qualifier: Tree, name: Name, sig: Signature)(implicit ctx: Context) = untpd.SelectWithSig(qualifier, name, sig) .withType(TermRef.withSig(qualifier.tpe, name.asTermName, sig)) @@ -46,6 +49,9 @@ 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) @@ -165,6 +171,9 @@ object tpd extends Trees.Instance[Type] with TypedTreeInfo { def ValDef(sym: TermSymbol, rhs: Tree = EmptyTree)(implicit ctx: Context): ValDef = ta.assignType(untpd.ValDef(Modifiers(sym), sym.name, TypeTree(sym.info), rhs), sym) + def SyntheticValDef(name: TermName, rhs: Tree)(implicit ctx: Context): ValDef = + ValDef(ctx.newSymbol(ctx.owner, name, Synthetic, rhs.tpe, coord = rhs.pos), rhs) + def DefDef(sym: TermSymbol, rhs: Tree = EmptyTree)(implicit ctx: Context): DefDef = ta.assignType(DefDef(sym, Function.const(rhs) _), sym) @@ -234,9 +243,16 @@ object tpd extends Trees.Instance[Type] with TypedTreeInfo { case pre => SelectFromTypeTree(TypeTree(pre), tp) } // no checks necessary - def ref(sym: Symbol)(implicit ctx: Context): tpd.NameTree = + def ref(sym: Symbol)(implicit ctx: Context): NameTree = ref(NamedType(sym.owner.thisType, sym.name, sym.denot)) + def singleton(tp: Type)(implicit ctx: Context): Tree = tp match { + case tp: TermRef => ref(tp) + case ThisType(cls) => This(cls) + case SuperType(qual, _) => singleton(qual) + case ConstantType(value) => Literal(value) + } + // ------ Creating typed equivalents of trees that exist only in untyped form ------- /** new C(args) */ @@ -383,6 +399,27 @@ object tpd extends Trees.Instance[Type] with TypedTreeInfo { ownerMap andThen (from zip to).toMap) } + // convert a numeric with a toXXX method + def numericConversion(tree: Tree, numericCls: Symbol)(implicit ctx: Context): Tree = { + val mname = ("to" + numericCls.name).toTermName + val conversion = tree.tpe member mname + assert(conversion.symbol.exists, s"$tree => $numericCls") + ensureApplied(Select(tree, conversion.symbol.termRef)) + } + + def evalOnce(tree: Tree)(within: Tree => Tree)(implicit ctx: Context) = { + if (isIdempotentExpr(tree)) within(tree) + else { + val vdef = SyntheticValDef(ctx.freshName("ev$").toTermName, tree) + Block(vdef :: Nil, within(Ident(vdef.namedType))) + } + } + + def runtimeCall(name: TermName, args: List[Tree])(implicit ctx: Context): Tree = ??? + + def mkAnd(tree1: Tree, tree2: Tree)(implicit ctx: Context) = + Apply(Select(tree1, defn.Boolean_and), tree2 :: Nil) + // ensure that constructors are fully applied? // ensure that normal methods are fully applied? diff --git a/src/dotty/tools/dotc/config/Config.scala b/src/dotty/tools/dotc/config/Config.scala index 9ed40cd89976..f69566733f1c 100644 --- a/src/dotty/tools/dotc/config/Config.scala +++ b/src/dotty/tools/dotc/config/Config.scala @@ -27,4 +27,7 @@ object Config { * Disadvantage: It might hide inconsistencies, so while debugging it's better to turn it off */ final val newMatch = false + + /** The recursion depth for showing a summarized string */ + final val summarizeDepth = 2 } \ No newline at end of file diff --git a/src/dotty/tools/dotc/config/ScalaSettings.scala b/src/dotty/tools/dotc/config/ScalaSettings.scala index aaf3b83858ff..8ed725e36789 100644 --- a/src/dotty/tools/dotc/config/ScalaSettings.scala +++ b/src/dotty/tools/dotc/config/ScalaSettings.scala @@ -125,12 +125,12 @@ class ScalaSettings extends Settings.SettingGroup { val XshowtreesStringified = BooleanSetting("-Yshow-trees-stringified", "(Requires -Xprint:) Print stringifications along with detailed ASTs.") val Yshowsyms = BooleanSetting("-Yshow-syms", "Print the AST symbol hierarchy after each phase.") val Yshowsymkinds = BooleanSetting("-Yshow-symkinds", "Print abbreviated symbol kinds next to symbol names.") - val skip = PhasesSetting("-Yskip", "Skip") + val Yskip = PhasesSetting("-Yskip", "Skip") val Ygenjavap = StringSetting("-Ygen-javap", "dir", "Generate a parallel output directory of .javap files.", "") val Ydumpclasses = StringSetting("-Ydump-classes", "dir", "Dump the generated bytecode to .class files (useful for reflective compilation that utilizes in-memory classloaders).", "") val Ynosqueeze = BooleanSetting("-Yno-squeeze", "Disable creation of compact code in matching.") - val stopAfter = PhasesSetting("-Ystop-after", "Stop after") withAbbreviation ("-stop") // backward compat - val stopBefore = PhasesSetting("-Ystop-before", "Stop before") + val YstopAfter = PhasesSetting("-Ystop-after", "Stop after") withAbbreviation ("-stop") // backward compat + val YstopBefore = PhasesSetting("-Ystop-before", "Stop before", "erasure") // stop before erasure as long as we have not debugged it fully val refinementMethodDispatch = ChoiceSetting("-Ystruct-dispatch", "policy", "structural method dispatch policy", List("no-cache", "mono-cache", "poly-cache", "invoke-dynamic"), "poly-cache") val Yrangepos = BooleanSetting("-Yrangepos", "Use range positions for syntax trees.") val Ybuilderdebug = ChoiceSetting("-Ybuilder-debug", "manager", "Compile using the specified build manager.", List("none", "refined", "simple"), "none") @@ -144,7 +144,7 @@ class ScalaSettings extends Settings.SettingGroup { 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") - def stop = stopAfter + def stop = YstopAfter /** Area-specific debug output. */ diff --git a/src/dotty/tools/dotc/core/Definitions.scala b/src/dotty/tools/dotc/core/Definitions.scala index 0ec770149283..3ae74b467419 100644 --- a/src/dotty/tools/dotc/core/Definitions.scala +++ b/src/dotty/tools/dotc/core/Definitions.scala @@ -170,6 +170,9 @@ class Definitions { lazy val UnitClass = valueClassSymbol("scala.Unit", BoxedUnitClass, java.lang.Void.TYPE, UnitEnc) lazy val BooleanClass = valueClassSymbol("scala.Boolean", BoxedBooleanClass, java.lang.Boolean.TYPE, BooleanEnc) + + lazy val Boolean_and = BooleanClass.requiredMethod(nme.ZAND) + lazy val ByteClass = valueClassSymbol("scala.Byte", BoxedByteClass, java.lang.Byte.TYPE, ByteEnc) lazy val ShortClass = valueClassSymbol("scala.Short", BoxedShortClass, java.lang.Short.TYPE, ShortEnc) lazy val CharClass = valueClassSymbol("scala.Char", BoxedCharClass, java.lang.Character.TYPE, CharEnc) @@ -179,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") @@ -192,7 +198,7 @@ class Definitions { lazy val EqualsPatternClass = specialPolyClass(tpnme.EQUALS_PATTERN, EmptyFlags, AnyType) lazy val RepeatedParamClass = specialPolyClass(tpnme.REPEATED_PARAM_CLASS, Covariant, SeqType) - lazy val JavaRepeatedParamClass = specialPolyClass(tpnme.JAVA_REPEATED_PARAM_CLASS, Covariant, ArrayType) + lazy val JavaRepeatedParamClass = specialPolyClass(tpnme.JAVA_REPEATED_PARAM_CLASS, Covariant, ArrayClass.typeRef) // fundamental classes lazy val StringClass = ctx.requiredClass("java.lang.String") @@ -250,8 +256,7 @@ class Definitions { def NothingType: Type = NothingClass.typeRef def NullType: Type = NullClass.typeRef def SeqType: Type = SeqClass.typeRef - def ArrayType: Type = ArrayClass.typeRef - def ObjectArrayType = ArrayType.appliedTo(ObjectType) + def ObjectArrayType = ArrayType(ObjectType) def UnitType: Type = UnitClass.typeRef def BooleanType: Type = BooleanClass.typeRef @@ -298,6 +303,29 @@ class Definitions { } } + object ArrayType { + def apply(elem: Type) = + ArrayClass.typeRef.appliedTo(elem :: Nil) + def unapply(tp: Type) = tp.dealias match { + case at: RefinedType if (at isRef ArrayClass) && at.argInfos.length == 1 => Some(at.argInfos.head) + case _ => None + } + } + + object MultiArrayType { + def apply(elem: Type, ndims: Int): Type = + if (ndims == 0) elem else ArrayType(apply(elem, ndims - 1)) + def unapply(tp: Type): Option[(Type, Int)] = tp match { + case ArrayType(elemtp) => + elemtp match { + case MultiArrayType(finalElemTp, n) => Some(finalElemTp, n + 1) + case _ => Some(elemtp, 1) + } + case _ => + None + } + } + // ----- Symbol sets --------------------------------------------------- lazy val FunctionClass = mkArityArray("Function", MaxFunctionArity, 0) @@ -390,7 +418,7 @@ class Definitions { hkTraitOfArity.getOrElseUpdate(vcs, createTrait) } - // ----- Value class machinery ------------------------------------------ + // ----- primitive value class machinery ------------------------------------------ lazy val ScalaValueClasses: collection.Set[Symbol] = Set( UnitClass, @@ -403,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 b9df8f6ed162..35f94aac7ddf 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/Phases.scala b/src/dotty/tools/dotc/core/Phases.scala index 12e700521630..87242c9b5b37 100644 --- a/src/dotty/tools/dotc/core/Phases.scala +++ b/src/dotty/tools/dotc/core/Phases.scala @@ -72,7 +72,7 @@ object Phases { * The list should never contain NoPhase. * if squashing is enabled, phases in same subgroup will be squashed to single phase. */ - def usePhases(phasess: List[List[Phase]], squash: Boolean = false) = { + def usePhases(phasess: List[List[Phase]], squash: Boolean = true) = { phases = (NoPhase :: phasess.flatten ::: new TerminalPhase :: Nil).toArray nextDenotTransformerId = new Array[Int](phases.length) denotTransformers = new Array[DenotTransformer](phases.length) diff --git a/src/dotty/tools/dotc/core/Signature.scala b/src/dotty/tools/dotc/core/Signature.scala index 18e4e5f6c617..02b91b18e42c 100644 --- a/src/dotty/tools/dotc/core/Signature.scala +++ b/src/dotty/tools/dotc/core/Signature.scala @@ -38,8 +38,8 @@ case class Signature private (paramsSig: List[TypeName], resSig: TypeName) { /** Construct a signature by prepending the signature names of the given `params` * to the parameter part of this signature. */ - def ++:(params: List[Type])(implicit ctx: Context) = - Signature((params map sigName) ++ paramsSig, resSig) + def prepend(params: List[Type], isJava: Boolean)(implicit ctx: Context) = + Signature((params.map(sigName(_, isJava))) ++ paramsSig, resSig) } @@ -51,6 +51,6 @@ object Signature { val NotAMethod = Signature(List(), EmptyTypeName) /** The signature of a method with no parameters and result type `resultType`. */ - def apply(resultType: Type)(implicit ctx: Context): Signature = - apply(Nil, sigName(resultType)) + def apply(resultType: Type, isJava: Boolean)(implicit ctx: Context): Signature = + apply(Nil, sigName(resultType, isJava)) } \ No newline at end of file diff --git a/src/dotty/tools/dotc/core/SymDenotations.scala b/src/dotty/tools/dotc/core/SymDenotations.scala index b5c7ea5396c0..f07eaa2bebdc 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/Symbols.scala b/src/dotty/tools/dotc/core/Symbols.scala index ff70679b847a..b4db26ae6cf8 100644 --- a/src/dotty/tools/dotc/core/Symbols.scala +++ b/src/dotty/tools/dotc/core/Symbols.scala @@ -379,6 +379,11 @@ object Symbols { /** If this symbol satisfies predicate `p` this symbol, otherwise `NoSymbol` */ def filter(p: Symbol => Boolean): Symbol = if (p(this)) this else NoSymbol + /** Is this symbol a user-defined value class? */ + final def isDerivedValueClass(implicit ctx: Context): Boolean = + false && // value classes are not supported yet + isClass && denot.derivesFrom(defn.AnyValClass) && !isPrimitiveValueClass + /** Is symbol a primitive value class? */ def isPrimitiveValueClass(implicit ctx: Context) = defn.ScalaValueClasses contains this diff --git a/src/dotty/tools/dotc/core/TypeApplications.scala b/src/dotty/tools/dotc/core/TypeApplications.scala index b61f39ed7f0e..9cd635cd9f38 100644 --- a/src/dotty/tools/dotc/core/TypeApplications.scala +++ b/src/dotty/tools/dotc/core/TypeApplications.scala @@ -298,23 +298,6 @@ class TypeApplications(val self: Type) extends AnyVal { def elemType(implicit ctx: Context): Type = firstBaseArgInfo(defn.SeqClass) orElse firstBaseArgInfo(defn.ArrayClass) - /** If this type is of the normalized form Array[...[Array[T]...] - * return the number of Array wrappers and T. - * Otherwise return 0 and the type itself - */ - final def splitArray(implicit ctx: Context): (Int, Type) = { - def recur(n: Int, tp: Type): (Int, Type) = tp.stripTypeVar match { - case RefinedType(tycon, _) if tycon isRef defn.ArrayClass => - tp.argInfos match { - case arg :: Nil => recur(n + 1, arg) - case _ => (n, tp) - } - case _ => - (n, tp) - } - recur(0, self) - } - /** Given a type alias * * type T[boundSyms] = p.C[targs] diff --git a/src/dotty/tools/dotc/core/Types.scala b/src/dotty/tools/dotc/core/Types.scala index ce481759bd4d..a7a33ce8e20e 100644 --- a/src/dotty/tools/dotc/core/Types.scala +++ b/src/dotty/tools/dotc/core/Types.scala @@ -1072,9 +1072,6 @@ object Types { } } - def isType = name.isTypeName - def isTerm = name.isTermName - def symbol(implicit ctx: Context): Symbol = { val now = ctx.period if (checkedPeriod == now || @@ -1084,6 +1081,9 @@ object Types { def info(implicit ctx: Context): Type = denot.info + def isType = isInstanceOf[TypeRef] + def isTerm = isInstanceOf[TermRef] + /** Guard against cycles that can arise if given `op` * follows info. The prblematic cases are a type alias to itself or * bounded by itself or a val typed as itself: @@ -1445,7 +1445,7 @@ object Types { protected def resultSignature(implicit ctx: Context) = resultType match { case rtp: SignedType => rtp.signature - case tp => Signature(tp) + case tp => Signature(tp, isJava = false) } final override def signature(implicit ctx: Context): Signature = { @@ -1496,7 +1496,7 @@ object Types { } protected def computeSignature(implicit ctx: Context): Signature = - paramTypes ++: resultSignature + resultSignature.prepend(paramTypes, isJava) def derivedMethodType(paramNames: List[TermName], paramTypes: List[Type], restpe: Type)(implicit ctx: Context) = if ((paramNames eq this.paramNames) && (paramTypes eq this.paramTypes) && (restpe eq this.resultType)) this diff --git a/src/dotty/tools/dotc/core/pickling/ClassfileParser.scala b/src/dotty/tools/dotc/core/pickling/ClassfileParser.scala index c33d5c18f688..f5942dac24b8 100644 --- a/src/dotty/tools/dotc/core/pickling/ClassfileParser.scala +++ b/src/dotty/tools/dotc/core/pickling/ClassfileParser.scala @@ -300,8 +300,7 @@ class ClassfileParser( if (elemtp.typeSymbol.isAbstractType && !(elemtp.derivesFrom(defn.ObjectClass))) { elemtp = AndType(elemtp, defn.ObjectType) } - - defn.ArrayType.appliedTo(elemtp) + defn.ArrayType(elemtp) case '(' => // we need a method symbol. given in line 486 by calling getType(methodSym, ..) val paramtypes = new ListBuffer[Type]() diff --git a/src/dotty/tools/dotc/core/transform/Erasure.scala b/src/dotty/tools/dotc/core/transform/Erasure.scala index 093b59ae8112..d59bdf1a917b 100644 --- a/src/dotty/tools/dotc/core/transform/Erasure.scala +++ b/src/dotty/tools/dotc/core/transform/Erasure.scala @@ -2,10 +2,93 @@ package dotty.tools.dotc package core package transform -import Symbols._, Types._, Contexts._, Flags._, Names._, StdNames._ +import Symbols._, Types._, Contexts._, Flags._, Names._, StdNames._, Flags.JavaDefined +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) + + (if (isConstructor) 4 else 0) + + (if (wildcardOK) 8 else 0) + + private var erasures = new Array[Erasure](16) + + for { + isJava <- List(false, true) + isSemi <- List(false, true) + isConstructor <- List(false, true) + wildcardOK <- List(false, true) + } erasures(erasureIdx(isJava, isSemi, isConstructor, wildcardOK)) = + new Erasure(isJava, isSemi, isConstructor, wildcardOK) + + /** Produces an erasure function. + * @param isJava Arguments should be treated the way Java does it + * @param isSemi Value classes are mapped in an intermediate step to + * ErasedValueClass types, instead of going directly to + * the erasure of the underlying type. + * @param isConstructor Argument forms part of the type of a constructor + * @param wildcardOK Wildcards are acceptable (true when using the erasure + * for computing a signature name). + */ + private def erasureFn(isJava: Boolean, isSemi: Boolean, isConstructor: Boolean, wildcardOK: Boolean): Erasure = + erasures(erasureIdx(isJava, isSemi, isConstructor, wildcardOK)) + + private val scalaErasureFn = erasureFn(isJava = false, isSemi = false, isConstructor = false, wildcardOK = false) + private val scalaSigFn = erasureFn(isJava = false, isSemi = false, isConstructor = false, wildcardOK = true) + private val javaSigFn = erasureFn(isJava = true, isSemi = false, isConstructor = false, wildcardOK = true) + private val semiErasureFn = erasureFn(isJava = false, isSemi = true, isConstructor = false, wildcardOK = false) + + def erasure(tp: Type)(implicit ctx: Context): Type = scalaErasureFn(tp) + def semiErasure(tp: Type)(implicit ctx: Context): Type = semiErasureFn(tp) + def sigName(tp: Type, isJava: Boolean)(implicit ctx: Context): TypeName = + (if (isJava) javaSigFn else scalaSigFn).sigName(tp) + + /** The symbol's erased info. This is the type's erasure, except for the following symbols: + * + * - For $asInstanceOf : [T]T + * - For $isInstanceOf : [T]scala#Boolean + * - For Array[T]. : [T]{scala#Int)Array[T] + * - For type members of Array : The original info + * - For all other abstract types: = ? + * - For all other symbols : the semi-erasure of their types, with + * isJava, isConstructor set according to symbol. + */ + def transformInfo(sym: Symbol, tp: Type)(implicit ctx: Context): Type = { + val erase = erasureFn(sym is JavaDefined, isSemi = true, sym.isConstructor, wildcardOK = false) + if ((sym eq defn.Object_asInstanceOf) || sym.isType && (sym.owner eq defn.ArrayClass)) + sym.info + else if ((sym eq defn.Object_isInstanceOf) || (sym eq defn.ArrayClass.primaryConstructor)) { + val tp @ PolyType(pnames) = sym.info + tp.derivedPolyType(pnames, TypeBounds.empty :: Nil, erase(tp.resultType)) + } + else if (sym.isAbstractType) + TypeAlias(WildcardType) + else + erase(tp) + } + + def isUnboundedGeneric(tp: Type)(implicit ctx: Context) = !( + (tp derivesFrom defn.ObjectClass) || + tp.classSymbol.isPrimitiveValueClass || + (tp.typeSymbol is JavaDefined)) + +} +import Erasure._ + +/** + * This is used as the Scala erasure during the erasure phase itself + * It differs from normal erasure in that value classes are erased to ErasedValueTypes which + * are then later converted to the underlying parameter type in phase posterasure. + * + */ +class Erasure(isJava: Boolean, isSemi: Boolean, isConstructor: Boolean, wildcardOK: Boolean) extends DotClass { + /** The erasure |T| of a type T. This is: * * - For a refined type scala.Array+[T]: @@ -15,8 +98,9 @@ object Erasure { * - otherwise, Object * - For a constant type, NoType or NoPrefix, the type itself. * - For all other type proxies: The erasure of the underlying type. - * - For a typeref scala.Any, scala.AnyVal, scala.Singleon or scala.NotNull, java.lang.Object. + * - For a typeref scala.Any, scala.AnyVal, scala.Singleon or scala.NotNull: java.lang.Object. * - For a typeref scala.Unit, scala.runtime.BoxedUnit. + * - For a typeref whose symbol is owned by Array: The typeref itself * - For a typeref P.C where C refers to a toplevel class, P.C. * - For a typeref P.C where C refers to a nested class, |P|.C. * - For a typeref P.C where C refers to an alias type, the erasure of C's alias. @@ -32,56 +116,77 @@ object Erasure { * parents |Ps|, but with duplicate references of Object removed. * - For any other type, exception. */ - def erasure(tp: Type)(implicit ctx: Context): Type = tp match { + def apply(tp: Type)(implicit ctx: Context): Type = tp match { case tp: TypeRef => val sym = tp.symbol - if (sym.isClass) - /*if (sym.isDerivedValueClass) eraseDerivedValueClassRef(tref) - else */if (sym.owner is Package) normalizeClass(sym.asClass).typeRef - else tp.derivedSelect(erasure(tp.prefix)) - else erasure(tp.info) + if (!sym.isClass) + if (sym.owner eq defn.ArrayClass) tp else this(tp.info) + else if (sym.isDerivedValueClass) eraseDerivedValueClassRef(tp) + else eraseNormalClassRef(tp) case tp: RefinedType => val parent = tp.parent if (parent isRef defn.ArrayClass) eraseArray(tp) - else erasure(parent) - case ConstantType(_) | NoType | NoPrefix => + else this(parent) + 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 => - erasure(tp.underlying) + this(tp.underlying) case AndType(tp1, tp2) => - erasure(tp1) + mergeAnd(this(tp1), this(tp2)) case OrType(tp1, tp2) => - erasure(tp.baseTypeRef(lubClass(tp1, tp2))) + this(tp.baseTypeRef(lubClass(tp1, tp2))) case tp: MethodType => + val paramErasure = erasureFn(tp.isJava, isSemi, isConstructor, wildcardOK)(_) tp.derivedMethodType( - tp.paramNames, tp.paramTypes.mapConserve(erasure), resultErasure(tp.resultType)) + tp.paramNames, tp.paramTypes.mapConserve(paramErasure), eraseResult(tp.resultType)) case tp: PolyType => - erasure(tp.resultType) + this(tp.resultType) case tp @ ClassInfo(pre, cls, classParents, decls, _) => + def eraseTypeRef(p: TypeRef) = this(p).asInstanceOf[TypeRef] val parents: List[TypeRef] = - if (cls == defn.ObjectClass || cls.isPrimitiveValueClass) Nil - else if (cls == defn.ArrayClass) defn.ObjectClass.typeRef :: Nil - else removeLaterObjects(classParents mapConserve (erasure(_).asInstanceOf[TypeRef])) - tp.derivedClassInfo(erasure(pre), parents, NoType) - case ErrorType => + if ((cls eq defn.ObjectClass) || cls.isPrimitiveValueClass) Nil + else if (cls eq defn.ArrayClass) defn.ObjectClass.typeRef :: Nil + else removeLaterObjects(classParents.mapConserve(eraseTypeRef)) + tp.derivedClassInfo(this(pre), parents, this(tp.selfType)) + case NoType | NoPrefix | ErrorType => + tp + case tp: WildcardType if wildcardOK => tp } - def eraseArray(tp: RefinedType)(implicit ctx: Context) = { - val (n, elemtp) = tp.splitArray - val elemCls = elemtp.classSymbol - if (elemCls.isSubClass(defn.NullClass)) - defn.ObjectArrayType - else if (elemCls.isSubClass(defn.ObjectClass) || elemCls.isPrimitiveValueClass) - (erasure(elemtp) /: (0 until n))((erased, _) => - defn.ArrayType.appliedTo(erased)) - else if (elemtp.typeSymbol is JavaDefined) - defn.ObjectArrayType - else - defn.ObjectType + private def eraseArray(tp: RefinedType)(implicit ctx: Context) = { + val defn.ArrayType(elemtp) = tp + if (elemtp derivesFrom defn.NullClass) defn.ObjectArrayType + else if (isUnboundedGeneric(elemtp)) defn.ObjectType + else defn.ArrayType(this(elemtp)) + } + + private def eraseDerivedValueClassRef(tref: TypeRef)(implicit ctx: Context): Type = + unsupported("eraseDerivedValueClass") + + private def eraseNormalClassRef(tref: TypeRef)(implicit ctx: Context): Type = { + val sym = tref.symbol + if (sym.owner is Package) normalizeClass(sym.asClass).typeRef + else tref.derivedSelect(this(tref.prefix)) + } + + private def eraseResult(tp: Type)(implicit ctx: Context): Type = tp match { + case tp: TypeRef => + val sym = tp.typeSymbol + if (sym eq defn.UnitClass) sym.typeRef + else if (sym.isDerivedValueClass) eraseNormalClassRef(tp) + else this(tp) + case RefinedType(parent, _) if !(parent isRef defn.ArrayClass) => + eraseResult(parent) + case _ => + this(tp) } - def normalizeClass(cls: ClassSymbol)(implicit ctx: Context): ClassSymbol = { + private def normalizeClass(cls: ClassSymbol)(implicit ctx: Context): ClassSymbol = { if (cls.owner == defn.ScalaPackageClass) { if (cls == defn.AnyClass || cls == defn.AnyValClass || cls == defn.SingletonClass || cls == defn.NotNullClass) return defn.ObjectClass @@ -91,7 +196,7 @@ object Erasure { cls } - def lubClass(tp1: Type, tp2: Type)(implicit ctx: Context): ClassSymbol = { + private def lubClass(tp1: Type, tp2: Type)(implicit ctx: Context): ClassSymbol = { var bcs1 = tp1.baseClasses val bc2 = tp2.baseClasses.head while (bcs1.nonEmpty && !bc2.derivesFrom(bcs1.head)) @@ -99,44 +204,55 @@ object Erasure { if (bcs1.isEmpty) defn.ObjectClass else bcs1.head } + private def removeLaterObjects(trs: List[TypeRef])(implicit ctx: Context): List[TypeRef] = trs match { + case tr :: trs1 => tr :: trs1.filterNot(_ isRef defn.ObjectClass) + case nil => nil + } + + private def mergeAnd(tp1: Type, tp2: Type)(implicit ctx: Context): Type = tp1 match { + case defn.ArrayType(elem1) => + tp2 match { + case defn.ArrayType(elem2) => defn.ArrayType(mergeAnd(elem1, elem2)) + case _ => defn.ObjectType + } + case _ => + tp2 match { + case defn.ArrayType(_) => defn.ObjectType + case _ => + val tsym1 = tp1.typeSymbol + val tsym2 = tp2.typeSymbol + if (!tsym2.exists) tp1 + else if (!tsym1.exists) tp2 + else if (!isJava && tsym1.derivesFrom(tsym2)) tp1 + else if (!isJava && tsym2.derivesFrom(tsym1)) tp2 + else if (tp1.typeSymbol.isRealClass) tp1 + else if (tp2.typeSymbol.isRealClass) tp2 + else tp1 + } + } + /** 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 { case tp: TypeRef => val sym = tp.symbol - if (sym.isClass) - /*if (sym.isDerivedValueClass) eraseDerivedValueClassRef(tref) - else */if (sym.owner is Package) normalizeClass(sym.asClass).fullName.asTypeName - else sym.asClass.fullName.asTypeName - else sigName(tp.info) - case tp: RefinedType => - val parent = tp.parent - if (parent isRef defn.ArrayClass) - eraseArray(tp) match { - case tp1: RefinedType if tp1.parent isRef defn.ArrayClass => - sigName(tp1.refinedInfo) ++ "[]" - case tp1 => - sigName(tp1) - } - else sigName(parent) + if (!sym.isClass) sigName(tp.info) + else if (sym.isDerivedValueClass) sigName(eraseDerivedValueClassRef(tp)) + else normalizeClass(sym.asClass).fullName.asTypeName + case defn.ArrayType(elem) => + sigName(elem) ++ "[]" + case tp: TypeBounds => + sigName(tp.hi) case tp: TypeProxy => sigName(tp.underlying) - case AndType(tp1, tp2) => - sigName(tp1) - case OrType(tp1, tp2) => - lubClass(tp1, tp2).name - case tp: WildcardType => - tpnme.WILDCARD - case ErrorType => + case ErrorType | WildcardType => tpnme.WILDCARD - } - - def resultErasure(tp: Type)(implicit ctx: Context) = - if (tp isRef defn.UnitClass) tp else erasure(tp) - - def removeLaterObjects(trs: List[TypeRef])(implicit ctx: Context): List[TypeRef] = trs match { - case tr :: trs1 => tr :: trs1.filterNot(_ isRef defn.ObjectClass) - case nil => nil + case tp: WildcardType => + sigName(tp.optBounds) + case _ => + val erased = this(tp) + assert(erased ne tp) + sigName(erased) } } diff --git a/src/dotty/tools/dotc/printing/PlainPrinter.scala b/src/dotty/tools/dotc/printing/PlainPrinter.scala index 332b7bd21006..f10aa7225fcf 100644 --- a/src/dotty/tools/dotc/printing/PlainPrinter.scala +++ b/src/dotty/tools/dotc/printing/PlainPrinter.scala @@ -7,6 +7,7 @@ import Contexts.Context, Scopes.Scope, Denotations.Denotation, Annotations.Annot import StdNames.nme import ast.Trees._, ast.untpd import java.lang.Integer.toOctalString +import config.Config.summarizeDepth import scala.annotation.switch class PlainPrinter(_ctx: Context) extends Printer { @@ -15,7 +16,7 @@ class PlainPrinter(_ctx: Context) extends Printer { protected def maxToTextRecursions = 100 protected final def controlled(op: => Text): Text = - if (ctx.toTextRecursions < maxToTextRecursions) + if (ctx.toTextRecursions < maxToTextRecursions && ctx.toTextRecursions < maxSummarized) try { ctx.toTextRecursions += 1 op @@ -23,12 +24,13 @@ class PlainPrinter(_ctx: Context) extends Printer { ctx.toTextRecursions -= 1 } else { - recursionLimitExceeded() + if (ctx.toTextRecursions >= maxToTextRecursions) + recursionLimitExceeded() "..." } protected def recursionLimitExceeded() = { - ctx.warning("Exceeded recursion depth attempting to print type.") + ctx.warning("Exceeded recursion depth attempting to print.") (new Throwable).printStackTrace } @@ -393,6 +395,17 @@ class PlainPrinter(_ctx: Context) extends Printer { } }.close // todo: override in refined printer + private var maxSummarized = Int.MaxValue + + def summarized[T](depth: Int)(op: => T): T = { + val saved = maxSummarized + maxSummarized = ctx.toTextRecursions + depth + try op + finally maxSummarized = depth + } + + def summarized[T](op: => T): T = summarized(summarizeDepth)(op) + def plain = this } diff --git a/src/dotty/tools/dotc/printing/Printer.scala b/src/dotty/tools/dotc/printing/Printer.scala index af87302e4a7b..65162a10ff21 100644 --- a/src/dotty/tools/dotc/printing/Printer.scala +++ b/src/dotty/tools/dotc/printing/Printer.scala @@ -88,6 +88,11 @@ abstract class Printer { /** Textual representation of tree */ def toText[T >: Untyped](tree: Tree[T]): Text + /** Perform string or text-producing operation `op` so that only a + * summarized text with given recursion depth is shown + */ + def summarized[T](depth: Int)(op: => T): T + /** A plain printer without any embellishments */ def plain: Printer } diff --git a/src/dotty/tools/dotc/printing/RefinedPrinter.scala b/src/dotty/tools/dotc/printing/RefinedPrinter.scala index 1a65b29789e5..557a2b5db6f8 100644 --- a/src/dotty/tools/dotc/printing/RefinedPrinter.scala +++ b/src/dotty/tools/dotc/printing/RefinedPrinter.scala @@ -112,7 +112,7 @@ class RefinedPrinter(_ctx: Context) extends PlainPrinter(_ctx) { case tp: ViewProto => return toText(tp.argType) ~ " ?=>? " ~ toText(tp.resultType) case tp: TypeRef => - if (tp.symbol is TypeParam | TypeArgument) { + if ((tp.symbol is TypeParam | TypeArgument) && !ctx.phase.erasedTypes) { return tp.info match { case TypeAlias(hi) => toText(hi) case _ => nameString(tp.symbol) @@ -127,7 +127,7 @@ class RefinedPrinter(_ctx: Context) extends PlainPrinter(_ctx) { super.toText(tp) } - override def toText[T >: Untyped](tree: Tree[T]): Text = { + override def toText[T >: Untyped](tree: Tree[T]): Text = controlled { def optDotPrefix(name: Name) = optText(name)(_ ~ ".") @@ -242,7 +242,7 @@ class RefinedPrinter(_ctx: Context) extends PlainPrinter(_ctx) { case SeqLiteral(elems) => "[" ~ toTextGlobal(elems, ",") ~ "]" case tpt: untpd.DerivedTypeTree => - "" + "" case TypeTree(orig) => if (tree.hasType) toText(tree.typeOpt) else toText(orig) case SingletonTypeTree(ref) => diff --git a/src/dotty/tools/dotc/printing/Showable.scala b/src/dotty/tools/dotc/printing/Showable.scala index cb65193e6fb9..550d80060a15 100644 --- a/src/dotty/tools/dotc/printing/Showable.scala +++ b/src/dotty/tools/dotc/printing/Showable.scala @@ -4,6 +4,7 @@ package printing import core._ import Contexts._, Texts._, Decorators._ +import config.Config.summarizeDepth trait Showable extends Any { @@ -20,4 +21,13 @@ trait Showable extends Any { /** The string representation of this showable element. */ def show(implicit ctx: Context): String = toText(ctx.printer).show -} \ No newline at end of file + + /** The summarized string representation of this showable element. + * Recursion depth is limited to some smallish value. Default is + * Config.summarizeDepth. + */ + def showSummary(depth: Int)(implicit ctx: Context): String = + ctx.printer.summarized(depth)(show) + + def showSummary(implicit ctx: Context): String = showSummary(summarizeDepth) +} diff --git a/src/dotty/tools/dotc/reporting/Reporter.scala b/src/dotty/tools/dotc/reporting/Reporter.scala index e2b32058820f..933262861fcb 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 000000000000..f9b602f54cc9 --- /dev/null +++ b/src/dotty/tools/dotc/transform/Erasure.scala @@ -0,0 +1,292 @@ +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.setPhase(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.linkedClass.info.member(nme.box).symbol + def unboxMethod(cls: ClassSymbol)(implicit ctx: Context) = + cls.linkedClass.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.widen.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.widen match { + case ErasedValueType(clazz, _) => + New(clazz.typeRef, cast(tree, clazz.underlyingOfValueClass) :: Nil) // todo: use adaptToType? + case tp => + val cls = tp.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.widen) && !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.widen)) + adaptToType(box(tree), pt) + else if (isErasedValueType(pt)) + adaptToType(unbox(tree, pt), pt) + else if (isPrimitiveValueType(tree.tpe.widen) && !isPrimitiveValueType(pt)) + adaptToType(box(tree), pt) + else if (isPrimitiveValueType(pt) && !isPrimitiveValueType(tree.tpe.widen)) + adaptToType(unbox(tree, pt), pt) + else + cast(tree, pt) + } + + class Typer extends typer.Typer with NoChecking { + import Boxing._ + + def erasedType(tree: untpd.Tree)(implicit ctx: Context): Type = + erasure(tree.tpe.asInstanceOf[Type]) + + private def promote(tree: untpd.Tree)(implicit ctx: Context): tree.ThisTree[Type] = { + assert(tree.hasType) + val erased = erasedType(tree)(ctx.withPhase(ctx.erasurePhase)) + ctx.log(s"promoting ${tree.show}: ${erased.showWithUnderlying()}") + tree.withType(erased) + } + + 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 tpt1 = // keep UnitTypes intact in result position + if (ddef.tpt.typeOpt isRef defn.UnitClass) untpd.TypeTree(defn.UnitType) withPos ddef.tpt.pos + else ddef.tpt + val ddef1 = untpd.cpy.DefDef(ddef, ddef.mods, ddef.name, Nil, ddef.vparamss, tpt1, 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, ctx.phase) + if (tree.isEmpty) tree else adaptToType(tree, pt) + } + } +} \ No newline at end of file diff --git a/src/dotty/tools/dotc/transform/LazyVals.scala b/src/dotty/tools/dotc/transform/LazyVals.scala index 28a8d12d99f1..650a115a8aad 100644 --- a/src/dotty/tools/dotc/transform/LazyVals.scala +++ b/src/dotty/tools/dotc/transform/LazyVals.scala @@ -1,7 +1,7 @@ -package dotty.tools.dotc.transform +package dotty.tools.dotc +package transform import scala.collection.mutable -import dotty.tools.dotc._ import core._ import Contexts._ import Symbols._ @@ -107,7 +107,7 @@ class LazyValTranformContext { } } } - /** Replace a local lazy val inside a method, + /** Replace a local lazy val inside a method, * with a LazyHolder from * dotty.runtime(eg dotty.runtime.LazyInt) */ diff --git a/src/dotty/tools/dotc/transform/TypeTestsCasts.scala b/src/dotty/tools/dotc/transform/TypeTestsCasts.scala new file mode 100644 index 000000000000..1c453750ea56 --- /dev/null +++ b/src/dotty/tools/dotc/transform/TypeTestsCasts.scala @@ -0,0 +1,101 @@ +package dotty.tools.dotc +package transform + +import TreeTransforms._ +import core.DenotTransformers._ +import core.Denotations._ +import core.SymDenotations._ +import core.Contexts._ +import core.Symbols._ +import core.Types._ +import core.Constants._ +import core.StdNames._ +import core.transform.Erasure.isUnboundedGeneric +import typer.ErrorReporting._ +import ast.Trees._ + +/** This transform normalizes type tests and type casts. + * Any remaining type tests + * - use the object methods $isInstanceOf and $asInstanceOf + * - have a reference type as receiver + * - can be translated directly to machine instructions + */ +class TypeTestsCasts extends TreeTransform { + import ast.tpd._ + + def box(tree: Tree): Tree = ??? + + override def name: String = "typeTestsCasts" + + override def transformTypeApply(tree: TypeApply)(implicit ctx: Context, info: TransformerInfo): Tree = ctx.traceIndented(s"transforming ${tree.show}", show = true) { + tree.fun match { + case fun @ Select(qual, selector) => + val sym = tree.symbol + + def isPrimitive(tp: Type) = tp.classSymbol.isPrimitiveValueClass + + def derivedTree(qual1: Tree, sym: Symbol) = + cpy.TypeApply(tree, Select(qual1, sym) withPos qual.pos, tree.args) + + def qualCls = qual.tpe.classSymbol + + def transformIsInstanceOf(argType: Type): Tree = { + if (qual.tpe <:< argType) + Literal(Constant(true)) withPos tree.pos + else if (qualCls.isPrimitiveValueClass) { + val argCls = argType.classSymbol + if (argCls.isPrimitiveValueClass) Literal(Constant(qualCls == argCls)) + else errorTree(tree, "isInstanceOf cannot test if value types are references") + } + else argType.dealias match { + case _: SingletonType => + val cmpOp = if (argType derivesFrom defn.AnyValClass) defn.Any_equals else defn.Object_eq + Apply(Select(qual, cmpOp), singleton(argType) :: Nil) + case AndType(tp1, tp2) => + evalOnce(fun) { fun => + val erased1 = transformIsInstanceOf(tp1) + val erased2 = transformIsInstanceOf(tp2) + erased1 match { + case Literal(Constant(true)) => erased2 + case _ => + erased2 match { + case Literal(Constant(true)) => erased1 + case _ => mkAnd(erased1, erased2) + } + } + } + case defn.MultiArrayType(elem, ndims) if isUnboundedGeneric(elem) => + def isArrayTest(arg: Tree) = + runtimeCall(nme.isArray, arg :: Literal(Constant(ndims)) :: Nil) + if (ndims == 1) isArrayTest(qual) + else evalOnce(qual) { qual1 => + mkAnd(derivedTree(qual1, defn.Object_isInstanceOf), isArrayTest(qual1)) + } + case _ => + derivedTree(qual, defn.Object_isInstanceOf) + } + } + + def transformAsInstanceOf(argType: Type): Tree = { + if (qual.tpe <:< argType) + Typed(qual, tree.args.head) + else if (qualCls.isPrimitiveValueClass) { + val argCls = argType.classSymbol + if (argCls.isPrimitiveValueClass) numericConversion(qual, argCls) + else derivedTree(box(qual), defn.Object_asInstanceOf) + } + else + derivedTree(qual, defn.Object_asInstanceOf) + } + + if (sym eq defn.Any_isInstanceOf) + transformIsInstanceOf(tree.args.head.tpe) + else if (defn.asInstanceOfMethods contains sym) + transformAsInstanceOf(tree.args.head.tpe) + else tree + + case _ => + tree + } + } +} \ 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 36822cb851b5..5818da180e69 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/TypeAssigner.scala b/src/dotty/tools/dotc/typer/TypeAssigner.scala index 6eeec56e1232..8be73ac82d01 100644 --- a/src/dotty/tools/dotc/typer/TypeAssigner.scala +++ b/src/dotty/tools/dotc/typer/TypeAssigner.scala @@ -217,9 +217,9 @@ trait TypeAssigner { val ownType = fn.tpe.widen match { case fntpe @ MethodType(_, ptypes) => if (sameLength(ptypes, args)) fntpe.instantiate(args.tpes) - else errorType(s"wrong number of type parameters for ${fn.tpe}; expected: ${ptypes.length}", tree.pos) + else errorType(s"wrong number of parameters for ${fn.tpe}; expected: ${ptypes.length}", tree.pos) case t => - errorType(s"${err.exprStr(fn)} does not take type parameters", tree.pos) + errorType(s"${err.exprStr(fn)} does not take parameters", tree.pos) } tree.withType(ownType) } diff --git a/src/dotty/tools/dotc/typer/Typer.scala b/src/dotty/tools/dotc/typer/Typer.scala index 0ba53f8c00ea..87bc643a3c31 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) } } diff --git a/tests/pos/erasure.scala b/tests/pos/erasure.scala new file mode 100644 index 000000000000..d794e9186d7a --- /dev/null +++ b/tests/pos/erasure.scala @@ -0,0 +1,15 @@ +object erasure { + + class C(x: Int) { + + def this() = this(0) + } + + import java.lang._ + + def const[T](x: T, y: T) = x + + val x = 2 + val y = const(x, 3) + +}