Skip to content

Special case pattern matching against abstract types with class tags #1111

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 12 commits into from
Mar 2, 2016
Merged
34 changes: 5 additions & 29 deletions src/dotty/DottyPredef.scala
Original file line number Diff line number Diff line change
@@ -1,37 +1,13 @@
package dotty

import scala.reflect.ClassTag
import scala.reflect.runtime.universe.TypeTag
import scala.reflect.ClassTag
import scala.Predef.???

abstract class I1 {
implicit def classTag[T]: ClassTag[T] = ???
/** unimplemented implicit for TypeTag */
object DottyPredef {
implicit def typeTag[T]: TypeTag[T] = ???
implicit val DoubleClassTag: ClassTag[Double] = ClassTag.Double
}
abstract class I2 extends I1 {
implicit val FloatClassTag: ClassTag[Double] = ClassTag.Double
}
abstract class I3 extends I2 {
implicit val LongClassTag: ClassTag[Long] = ClassTag.Long
}
abstract class I4 extends I3 {
implicit val IntClassTag: ClassTag[Int] = ClassTag.Int
}
abstract class I5 extends I4 {
implicit val ShortClassTag: ClassTag[Short] = ClassTag.Short
}
abstract class I6 extends I5 {
implicit val ByteClassTag: ClassTag[Byte] = ClassTag.Byte
implicit val CharClassTag: ClassTag[Char] = ClassTag.Char
implicit val BooleanClassTag: ClassTag[Boolean] = ClassTag.Boolean
implicit val UnitClassTag: ClassTag[Unit] = ClassTag.Unit
implicit val NullClassTag: ClassTag[Null] = ClassTag.Null
}

/** implicits for ClassTag and TypeTag. Should be implemented with macros */
object DottyPredef extends I6 {

/** ClassTags for final classes */
implicit val NothingClassTag: ClassTag[Nothing] = ClassTag.Nothing
implicit def arrayTag[T](implicit ctag: ClassTag[T]): ClassTag[Array[T]] =
ctag.wrap
}
1 change: 0 additions & 1 deletion src/dotty/tools/dotc/Compiler.scala
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,6 @@ class Compiler {
new SeqLiterals,
new InterceptedMethods,
new Getters,
new ClassTags,
new ElimByName,
new AugmentScala2Traits,
new ResolveSuper),
Expand Down
44 changes: 30 additions & 14 deletions src/dotty/tools/dotc/ast/Desugar.scala
Original file line number Diff line number Diff line change
Expand Up @@ -147,18 +147,7 @@ object desugar {
tparam
}

val meth1 = epbuf.toList match {
case Nil =>
meth
case evidenceParams =>
val vparamss1 = vparamss.reverse match {
case (vparams @ (vparam :: _)) :: rvparamss if vparam.mods is Implicit =>
((vparams ++ evidenceParams) :: rvparamss).reverse
case _ =>
vparamss :+ evidenceParams
}
cpy.DefDef(meth)(tparams = tparams1, vparamss = vparamss1)
}
val meth1 = addEvidenceParams(cpy.DefDef(meth)(tparams = tparams1), epbuf.toList)

/** The longest prefix of parameter lists in vparamss whose total length does not exceed `n` */
def takeUpTo(vparamss: List[List[ValDef]], n: Int): List[List[ValDef]] = vparamss match {
Expand Down Expand Up @@ -204,6 +193,30 @@ object desugar {
}
}

// Add all evidence parameters in `params` as implicit parameters to `meth` */
private def addEvidenceParams(meth: DefDef, params: List[ValDef])(implicit ctx: Context): DefDef =
params match {
case Nil =>
meth
case evidenceParams =>
val vparamss1 = meth.vparamss.reverse match {
case (vparams @ (vparam :: _)) :: rvparamss if vparam.mods is Implicit =>
((vparams ++ evidenceParams) :: rvparamss).reverse
case _ =>
meth.vparamss :+ evidenceParams
}
cpy.DefDef(meth)(vparamss = vparamss1)
}

/** The implicit evidence parameters of `meth`, as generated by `desugar.defDef` */
private def evidenceParams(meth: DefDef)(implicit ctx: Context): List[ValDef] =
meth.vparamss.reverse match {
case (vparams @ (vparam :: _)) :: _ if vparam.mods is Implicit =>
vparams.dropWhile(!_.name.startsWith(nme.EVIDENCE_PARAM_PREFIX))
case _ =>
Nil
}

/** Fill in empty type bounds with Nothing/Any. Expand private local type parameters as follows:
*
* class C[v T]
Expand Down Expand Up @@ -256,10 +269,13 @@ object desugar {
else constr1.vparamss.nestedMap(toDefParam)
val constr = cpy.DefDef(constr1)(tparams = constrTparams, vparamss = constrVparamss)

// Add constructor type parameters to auxiliary constructors
// Add constructor type parameters and evidence implicit parameters
// to auxiliary constructors
val normalizedBody = impl.body map {
case ddef: DefDef if ddef.name.isConstructorName =>
cpy.DefDef(ddef)(tparams = constrTparams)
addEvidenceParams(
cpy.DefDef(ddef)(tparams = constrTparams),
evidenceParams(constr1).map(toDefParam))
case stat =>
stat
}
Expand Down
38 changes: 17 additions & 21 deletions src/dotty/tools/dotc/ast/tpd.scala
Original file line number Diff line number Diff line change
Expand Up @@ -777,27 +777,6 @@ object tpd extends Trees.Instance[Type] with TypedTreeInfo {
}
else Assign(tree, rhs)

/** A tree in place of this tree that represents the class of type `tp`.
* Contains special handling if the class is a primitive value class
* and invokes a `default` method otherwise.
*/
def clsOf(tp: Type, default: => Tree)(implicit ctx: Context): Tree = {
def TYPE(module: TermSymbol) =
ref(module).select(nme.TYPE_).ensureConforms(tree.tpe).withPos(tree.pos)
defn.scalaClassName(tp) match {
case tpnme.Boolean => TYPE(defn.BoxedBooleanModule)
case tpnme.Byte => TYPE(defn.BoxedByteModule)
case tpnme.Short => TYPE(defn.BoxedShortModule)
case tpnme.Char => TYPE(defn.BoxedCharModule)
case tpnme.Int => TYPE(defn.BoxedIntModule)
case tpnme.Long => TYPE(defn.BoxedLongModule)
case tpnme.Float => TYPE(defn.BoxedFloatModule)
case tpnme.Double => TYPE(defn.BoxedDoubleModule)
case tpnme.Unit => TYPE(defn.BoxedUnitModule)
case _ => default
}
}

// --- Higher order traversal methods -------------------------------

/** Apply `f` to each subtree of this tree */
Expand Down Expand Up @@ -842,6 +821,23 @@ object tpd extends Trees.Instance[Type] with TypedTreeInfo {
}
}

/** A tree that represents the class of the erasure of type `tp`. */
def clsOf(tp: Type)(implicit ctx: Context): Tree = {
def TYPE(module: TermSymbol) = ref(module).select(nme.TYPE_)
defn.scalaClassName(tp) match {
case tpnme.Boolean => TYPE(defn.BoxedBooleanModule)
case tpnme.Byte => TYPE(defn.BoxedByteModule)
case tpnme.Short => TYPE(defn.BoxedShortModule)
case tpnme.Char => TYPE(defn.BoxedCharModule)
case tpnme.Int => TYPE(defn.BoxedIntModule)
case tpnme.Long => TYPE(defn.BoxedLongModule)
case tpnme.Float => TYPE(defn.BoxedFloatModule)
case tpnme.Double => TYPE(defn.BoxedDoubleModule)
case tpnme.Unit => TYPE(defn.BoxedUnitModule)
case _ => Literal(Constant(TypeErasure.erasure(tp)))
}
}

def applyOverloaded(receiver: Tree, method: TermName, args: List[Tree], targs: List[Type], expectedType: Type, isAnnotConstructor: Boolean = false)(implicit ctx: Context): Tree = {
val typer = ctx.typer
val proto = new FunProtoTyped(args, expectedType, typer)
Expand Down
5 changes: 5 additions & 0 deletions src/dotty/tools/dotc/core/Definitions.scala
Original file line number Diff line number Diff line change
Expand Up @@ -221,6 +221,8 @@ class Definitions {

lazy val Predef_conformsR = ScalaPredefModule.requiredMethodRef("$conforms")
def Predef_conforms(implicit ctx: Context) = Predef_conformsR.symbol
lazy val Predef_classOfR = ScalaPredefModule.requiredMethodRef("classOf")
def Predef_classOf(implicit ctx: Context) = Predef_classOfR.symbol

lazy val ScalaRuntimeModuleRef = ctx.requiredModuleRef("scala.runtime.ScalaRunTime")
def ScalaRuntimeModule(implicit ctx: Context) = ScalaRuntimeModuleRef.symbol
Expand Down Expand Up @@ -419,6 +421,9 @@ class Definitions {
lazy val LanguageModuleRef = ctx.requiredModule("dotty.language")
def LanguageModuleClass(implicit ctx: Context) = LanguageModuleRef.symbol.moduleClass.asClass
lazy val NonLocalReturnControlType: TypeRef = ctx.requiredClassRef("scala.runtime.NonLocalReturnControl")
lazy val ClassTagType = ctx.requiredClassRef("scala.reflect.ClassTag")
def ClassTagClass(implicit ctx: Context) = ClassTagType.symbol.asClass
def ClassTagModule(implicit ctx: Context) = ClassTagClass.companionModule

// Annotation base classes
lazy val AnnotationType = ctx.requiredClassRef("scala.annotation.Annotation")
Expand Down
2 changes: 2 additions & 0 deletions src/dotty/tools/dotc/core/tasty/TreeUnpickler.scala
Original file line number Diff line number Diff line change
Expand Up @@ -290,6 +290,8 @@ class TreeUnpickler(reader: TastyReader, tastyName: TastyName.Table) {
ConstantType(Constant(readName().toString))
case NULLconst =>
ConstantType(Constant(null))
case CLASSconst =>
ConstantType(Constant(readType()))
case BYNAMEtype =>
ExprType(readType())
}
Expand Down
11 changes: 2 additions & 9 deletions src/dotty/tools/dotc/transform/ClassOf.scala
Original file line number Diff line number Diff line change
Expand Up @@ -21,17 +21,10 @@ class ClassOf extends MiniPhaseTransform {

override def phaseName: String = "classOf"

private var classOfMethod: TermSymbol = _

override def prepareForUnit(tree: tpd.Tree)(implicit ctx: Context): TreeTransform = {
classOfMethod = defn.ScalaPredefModule.requiredMethod(nme.classOf)
this
}

override def transformTypeApply(tree: TypeApply)(implicit ctx: Context, info: TransformerInfo): Tree =
if (tree.symbol eq classOfMethod) {
if (tree.symbol eq defn.Predef_classOf) {
val targ = tree.args.head.tpe
tree.clsOf(targ, Literal(Constant(TypeErasure.erasure(targ))))
clsOf(targ).ensureConforms(tree.tpe).withPos(tree.pos)
}
else tree
}
68 changes: 0 additions & 68 deletions src/dotty/tools/dotc/transform/ClassTags.scala

This file was deleted.

4 changes: 3 additions & 1 deletion src/dotty/tools/dotc/transform/GetClass.scala
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import ast.tpd
import core.Contexts.Context
import core.StdNames.nme
import core.Phases.Phase
import TypeUtils._
import TreeTransforms.{MiniPhaseTransform, TransformerInfo}

/** Rewrite `getClass` calls as follow:
Expand All @@ -24,7 +25,8 @@ class GetClass extends MiniPhaseTransform {
override def transformApply(tree: Apply)(implicit ctx: Context, info: TransformerInfo): Tree = {
import ast.Trees._
tree match {
case Apply(Select(qual, nme.getClass_), Nil) => tree.clsOf(qual.tpe.widen, tree)
case Apply(Select(qual, nme.getClass_), Nil) if qual.tpe.widen.isPrimitiveValueType =>
clsOf(qual.tpe.widen).withPos(tree.pos)
case _ => tree
}
}
Expand Down
4 changes: 4 additions & 0 deletions src/dotty/tools/dotc/typer/Applications.scala
Original file line number Diff line number Diff line change
Expand Up @@ -617,6 +617,10 @@ trait Applications extends Compatibility { self: Typer =>
case pt: PolyType =>
if (typedArgs.length <= pt.paramBounds.length && !isNamed)
typedArgs = typedArgs.zipWithConserve(pt.paramBounds)(adaptTypeArg)
if (typedFn.symbol == defn.Predef_classOf && typedArgs.nonEmpty) {
val arg = typedArgs.head
checkClassType(arg.tpe, arg.pos, traitReq = false, stablePrefixReq = false)
}
case _ =>
}
assignType(cpy.TypeApply(tree)(typedFn, typedArgs), typedFn, typedArgs)
Expand Down
13 changes: 7 additions & 6 deletions src/dotty/tools/dotc/typer/Checking.scala
Original file line number Diff line number Diff line change
Expand Up @@ -394,16 +394,17 @@ trait Checking {
ctx.error(i"$tp cannot be instantiated since it${rstatus.msg}", pos)
}

/** Check that `tp` is a class type with a stable prefix. Also, if `traitReq` is
* true check that `tp` is a trait.
* Stability checking is disabled in phases after RefChecks.
/** Check that `tp` is a class type.
* Also, if `traitReq` is true, check that `tp` is a trait.
* Also, if `stablePrefixReq` is true and phase is not after RefChecks,
* check that class prefix is stable.
* @return `tp` itself if it is a class or trait ref, ObjectType if not.
*/
def checkClassTypeWithStablePrefix(tp: Type, pos: Position, traitReq: Boolean)(implicit ctx: Context): Type =
def checkClassType(tp: Type, pos: Position, traitReq: Boolean, stablePrefixReq: Boolean)(implicit ctx: Context): Type =
tp.underlyingClassRef(refinementOK = false) match {
case tref: TypeRef =>
if (ctx.phase <= ctx.refchecksPhase) checkStable(tref.prefix, pos)
if (traitReq && !(tref.symbol is Trait)) ctx.error(d"$tref is not a trait", pos)
if (stablePrefixReq && ctx.phase <= ctx.refchecksPhase) checkStable(tref.prefix, pos)
tp
case _ =>
ctx.error(d"$tp is not a class type", pos)
Expand Down Expand Up @@ -506,7 +507,7 @@ trait NoChecking extends Checking {
override def checkNonCyclic(sym: Symbol, info: TypeBounds, reportErrors: Boolean)(implicit ctx: Context): Type = info
override def checkValue(tree: Tree, proto: Type)(implicit ctx: Context): tree.type = tree
override def checkStable(tp: Type, pos: Position)(implicit ctx: Context): Unit = ()
override def checkClassTypeWithStablePrefix(tp: Type, pos: Position, traitReq: Boolean)(implicit ctx: Context): Type = tp
override def checkClassType(tp: Type, pos: Position, traitReq: Boolean, stablePrefixReq: Boolean)(implicit ctx: Context): Type = tp
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 = ()
Expand Down
3 changes: 2 additions & 1 deletion src/dotty/tools/dotc/typer/Namer.scala
Original file line number Diff line number Diff line change
Expand Up @@ -603,7 +603,8 @@ class Namer { typer: Typer =>
val ptype = parentType(parent)(ctx.superCallContext)
if (cls.isRefinementClass) ptype
else {
val pt = checkClassTypeWithStablePrefix(ptype, parent.pos, traitReq = parent ne parents.head)
val pt = checkClassType(ptype, parent.pos,
traitReq = parent ne parents.head, stablePrefixReq = true)
if (pt.derivesFrom(cls)) {
val addendum = parent match {
case Select(qual: Super, _) if ctx.scala2Mode =>
Expand Down
Loading