Skip to content

Commit ae62466

Browse files
committed
Merge pull request #1111 from dotty-staging/fix-#1099
Special case pattern matching against abstract types with class tags
2 parents 0ae3ef2 + 1d2fe48 commit ae62466

16 files changed

+171
-211
lines changed

src/dotty/DottyPredef.scala

Lines changed: 5 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -1,37 +1,13 @@
11
package dotty
22

3-
import scala.reflect.ClassTag
43
import scala.reflect.runtime.universe.TypeTag
4+
import scala.reflect.ClassTag
55
import scala.Predef.???
66

7-
abstract class I1 {
8-
implicit def classTag[T]: ClassTag[T] = ???
7+
/** unimplemented implicit for TypeTag */
8+
object DottyPredef {
99
implicit def typeTag[T]: TypeTag[T] = ???
10-
implicit val DoubleClassTag: ClassTag[Double] = ClassTag.Double
11-
}
12-
abstract class I2 extends I1 {
13-
implicit val FloatClassTag: ClassTag[Double] = ClassTag.Double
14-
}
15-
abstract class I3 extends I2 {
16-
implicit val LongClassTag: ClassTag[Long] = ClassTag.Long
17-
}
18-
abstract class I4 extends I3 {
19-
implicit val IntClassTag: ClassTag[Int] = ClassTag.Int
20-
}
21-
abstract class I5 extends I4 {
22-
implicit val ShortClassTag: ClassTag[Short] = ClassTag.Short
23-
}
24-
abstract class I6 extends I5 {
25-
implicit val ByteClassTag: ClassTag[Byte] = ClassTag.Byte
26-
implicit val CharClassTag: ClassTag[Char] = ClassTag.Char
27-
implicit val BooleanClassTag: ClassTag[Boolean] = ClassTag.Boolean
28-
implicit val UnitClassTag: ClassTag[Unit] = ClassTag.Unit
29-
implicit val NullClassTag: ClassTag[Null] = ClassTag.Null
30-
}
31-
32-
/** implicits for ClassTag and TypeTag. Should be implemented with macros */
33-
object DottyPredef extends I6 {
3410

35-
/** ClassTags for final classes */
36-
implicit val NothingClassTag: ClassTag[Nothing] = ClassTag.Nothing
11+
implicit def arrayTag[T](implicit ctag: ClassTag[T]): ClassTag[Array[T]] =
12+
ctag.wrap
3713
}

src/dotty/tools/dotc/Compiler.scala

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -59,7 +59,6 @@ class Compiler {
5959
new SeqLiterals,
6060
new InterceptedMethods,
6161
new Getters,
62-
new ClassTags,
6362
new ElimByName,
6463
new AugmentScala2Traits,
6564
new ResolveSuper),

src/dotty/tools/dotc/ast/Desugar.scala

Lines changed: 30 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -147,18 +147,7 @@ object desugar {
147147
tparam
148148
}
149149

150-
val meth1 = epbuf.toList match {
151-
case Nil =>
152-
meth
153-
case evidenceParams =>
154-
val vparamss1 = vparamss.reverse match {
155-
case (vparams @ (vparam :: _)) :: rvparamss if vparam.mods is Implicit =>
156-
((vparams ++ evidenceParams) :: rvparamss).reverse
157-
case _ =>
158-
vparamss :+ evidenceParams
159-
}
160-
cpy.DefDef(meth)(tparams = tparams1, vparamss = vparamss1)
161-
}
150+
val meth1 = addEvidenceParams(cpy.DefDef(meth)(tparams = tparams1), epbuf.toList)
162151

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

196+
// Add all evidence parameters in `params` as implicit parameters to `meth` */
197+
private def addEvidenceParams(meth: DefDef, params: List[ValDef])(implicit ctx: Context): DefDef =
198+
params match {
199+
case Nil =>
200+
meth
201+
case evidenceParams =>
202+
val vparamss1 = meth.vparamss.reverse match {
203+
case (vparams @ (vparam :: _)) :: rvparamss if vparam.mods is Implicit =>
204+
((vparams ++ evidenceParams) :: rvparamss).reverse
205+
case _ =>
206+
meth.vparamss :+ evidenceParams
207+
}
208+
cpy.DefDef(meth)(vparamss = vparamss1)
209+
}
210+
211+
/** The implicit evidence parameters of `meth`, as generated by `desugar.defDef` */
212+
private def evidenceParams(meth: DefDef)(implicit ctx: Context): List[ValDef] =
213+
meth.vparamss.reverse match {
214+
case (vparams @ (vparam :: _)) :: _ if vparam.mods is Implicit =>
215+
vparams.dropWhile(!_.name.startsWith(nme.EVIDENCE_PARAM_PREFIX))
216+
case _ =>
217+
Nil
218+
}
219+
207220
/** Fill in empty type bounds with Nothing/Any. Expand private local type parameters as follows:
208221
*
209222
* class C[v T]
@@ -256,10 +269,13 @@ object desugar {
256269
else constr1.vparamss.nestedMap(toDefParam)
257270
val constr = cpy.DefDef(constr1)(tparams = constrTparams, vparamss = constrVparamss)
258271

259-
// Add constructor type parameters to auxiliary constructors
272+
// Add constructor type parameters and evidence implicit parameters
273+
// to auxiliary constructors
260274
val normalizedBody = impl.body map {
261275
case ddef: DefDef if ddef.name.isConstructorName =>
262-
cpy.DefDef(ddef)(tparams = constrTparams)
276+
addEvidenceParams(
277+
cpy.DefDef(ddef)(tparams = constrTparams),
278+
evidenceParams(constr1).map(toDefParam))
263279
case stat =>
264280
stat
265281
}

src/dotty/tools/dotc/ast/tpd.scala

Lines changed: 17 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -777,27 +777,6 @@ object tpd extends Trees.Instance[Type] with TypedTreeInfo {
777777
}
778778
else Assign(tree, rhs)
779779

780-
/** A tree in place of this tree that represents the class of type `tp`.
781-
* Contains special handling if the class is a primitive value class
782-
* and invokes a `default` method otherwise.
783-
*/
784-
def clsOf(tp: Type, default: => Tree)(implicit ctx: Context): Tree = {
785-
def TYPE(module: TermSymbol) =
786-
ref(module).select(nme.TYPE_).ensureConforms(tree.tpe).withPos(tree.pos)
787-
defn.scalaClassName(tp) match {
788-
case tpnme.Boolean => TYPE(defn.BoxedBooleanModule)
789-
case tpnme.Byte => TYPE(defn.BoxedByteModule)
790-
case tpnme.Short => TYPE(defn.BoxedShortModule)
791-
case tpnme.Char => TYPE(defn.BoxedCharModule)
792-
case tpnme.Int => TYPE(defn.BoxedIntModule)
793-
case tpnme.Long => TYPE(defn.BoxedLongModule)
794-
case tpnme.Float => TYPE(defn.BoxedFloatModule)
795-
case tpnme.Double => TYPE(defn.BoxedDoubleModule)
796-
case tpnme.Unit => TYPE(defn.BoxedUnitModule)
797-
case _ => default
798-
}
799-
}
800-
801780
// --- Higher order traversal methods -------------------------------
802781

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

824+
/** A tree that represents the class of the erasure of type `tp`. */
825+
def clsOf(tp: Type)(implicit ctx: Context): Tree = {
826+
def TYPE(module: TermSymbol) = ref(module).select(nme.TYPE_)
827+
defn.scalaClassName(tp) match {
828+
case tpnme.Boolean => TYPE(defn.BoxedBooleanModule)
829+
case tpnme.Byte => TYPE(defn.BoxedByteModule)
830+
case tpnme.Short => TYPE(defn.BoxedShortModule)
831+
case tpnme.Char => TYPE(defn.BoxedCharModule)
832+
case tpnme.Int => TYPE(defn.BoxedIntModule)
833+
case tpnme.Long => TYPE(defn.BoxedLongModule)
834+
case tpnme.Float => TYPE(defn.BoxedFloatModule)
835+
case tpnme.Double => TYPE(defn.BoxedDoubleModule)
836+
case tpnme.Unit => TYPE(defn.BoxedUnitModule)
837+
case _ => Literal(Constant(TypeErasure.erasure(tp)))
838+
}
839+
}
840+
845841
def applyOverloaded(receiver: Tree, method: TermName, args: List[Tree], targs: List[Type], expectedType: Type, isAnnotConstructor: Boolean = false)(implicit ctx: Context): Tree = {
846842
val typer = ctx.typer
847843
val proto = new FunProtoTyped(args, expectedType, typer)

src/dotty/tools/dotc/core/Definitions.scala

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -221,6 +221,8 @@ class Definitions {
221221

222222
lazy val Predef_conformsR = ScalaPredefModule.requiredMethodRef("$conforms")
223223
def Predef_conforms(implicit ctx: Context) = Predef_conformsR.symbol
224+
lazy val Predef_classOfR = ScalaPredefModule.requiredMethodRef("classOf")
225+
def Predef_classOf(implicit ctx: Context) = Predef_classOfR.symbol
224226

225227
lazy val ScalaRuntimeModuleRef = ctx.requiredModuleRef("scala.runtime.ScalaRunTime")
226228
def ScalaRuntimeModule(implicit ctx: Context) = ScalaRuntimeModuleRef.symbol
@@ -419,6 +421,9 @@ class Definitions {
419421
lazy val LanguageModuleRef = ctx.requiredModule("dotty.language")
420422
def LanguageModuleClass(implicit ctx: Context) = LanguageModuleRef.symbol.moduleClass.asClass
421423
lazy val NonLocalReturnControlType: TypeRef = ctx.requiredClassRef("scala.runtime.NonLocalReturnControl")
424+
lazy val ClassTagType = ctx.requiredClassRef("scala.reflect.ClassTag")
425+
def ClassTagClass(implicit ctx: Context) = ClassTagType.symbol.asClass
426+
def ClassTagModule(implicit ctx: Context) = ClassTagClass.companionModule
422427

423428
// Annotation base classes
424429
lazy val AnnotationType = ctx.requiredClassRef("scala.annotation.Annotation")

src/dotty/tools/dotc/core/tasty/TreeUnpickler.scala

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -290,6 +290,8 @@ class TreeUnpickler(reader: TastyReader, tastyName: TastyName.Table) {
290290
ConstantType(Constant(readName().toString))
291291
case NULLconst =>
292292
ConstantType(Constant(null))
293+
case CLASSconst =>
294+
ConstantType(Constant(readType()))
293295
case BYNAMEtype =>
294296
ExprType(readType())
295297
}

src/dotty/tools/dotc/transform/ClassOf.scala

Lines changed: 2 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -21,17 +21,10 @@ class ClassOf extends MiniPhaseTransform {
2121

2222
override def phaseName: String = "classOf"
2323

24-
private var classOfMethod: TermSymbol = _
25-
26-
override def prepareForUnit(tree: tpd.Tree)(implicit ctx: Context): TreeTransform = {
27-
classOfMethod = defn.ScalaPredefModule.requiredMethod(nme.classOf)
28-
this
29-
}
30-
3124
override def transformTypeApply(tree: TypeApply)(implicit ctx: Context, info: TransformerInfo): Tree =
32-
if (tree.symbol eq classOfMethod) {
25+
if (tree.symbol eq defn.Predef_classOf) {
3326
val targ = tree.args.head.tpe
34-
tree.clsOf(targ, Literal(Constant(TypeErasure.erasure(targ))))
27+
clsOf(targ).ensureConforms(tree.tpe).withPos(tree.pos)
3528
}
3629
else tree
3730
}

src/dotty/tools/dotc/transform/ClassTags.scala

Lines changed: 0 additions & 68 deletions
This file was deleted.

src/dotty/tools/dotc/transform/GetClass.scala

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import ast.tpd
55
import core.Contexts.Context
66
import core.StdNames.nme
77
import core.Phases.Phase
8+
import TypeUtils._
89
import TreeTransforms.{MiniPhaseTransform, TransformerInfo}
910

1011
/** Rewrite `getClass` calls as follow:
@@ -24,7 +25,8 @@ class GetClass extends MiniPhaseTransform {
2425
override def transformApply(tree: Apply)(implicit ctx: Context, info: TransformerInfo): Tree = {
2526
import ast.Trees._
2627
tree match {
27-
case Apply(Select(qual, nme.getClass_), Nil) => tree.clsOf(qual.tpe.widen, tree)
28+
case Apply(Select(qual, nme.getClass_), Nil) if qual.tpe.widen.isPrimitiveValueType =>
29+
clsOf(qual.tpe.widen).withPos(tree.pos)
2830
case _ => tree
2931
}
3032
}

src/dotty/tools/dotc/typer/Applications.scala

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -617,6 +617,10 @@ trait Applications extends Compatibility { self: Typer =>
617617
case pt: PolyType =>
618618
if (typedArgs.length <= pt.paramBounds.length && !isNamed)
619619
typedArgs = typedArgs.zipWithConserve(pt.paramBounds)(adaptTypeArg)
620+
if (typedFn.symbol == defn.Predef_classOf && typedArgs.nonEmpty) {
621+
val arg = typedArgs.head
622+
checkClassType(arg.tpe, arg.pos, traitReq = false, stablePrefixReq = false)
623+
}
620624
case _ =>
621625
}
622626
assignType(cpy.TypeApply(tree)(typedFn, typedArgs), typedFn, typedArgs)

src/dotty/tools/dotc/typer/Checking.scala

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -394,16 +394,17 @@ trait Checking {
394394
ctx.error(i"$tp cannot be instantiated since it${rstatus.msg}", pos)
395395
}
396396

397-
/** Check that `tp` is a class type with a stable prefix. Also, if `traitReq` is
398-
* true check that `tp` is a trait.
399-
* Stability checking is disabled in phases after RefChecks.
397+
/** Check that `tp` is a class type.
398+
* Also, if `traitReq` is true, check that `tp` is a trait.
399+
* Also, if `stablePrefixReq` is true and phase is not after RefChecks,
400+
* check that class prefix is stable.
400401
* @return `tp` itself if it is a class or trait ref, ObjectType if not.
401402
*/
402-
def checkClassTypeWithStablePrefix(tp: Type, pos: Position, traitReq: Boolean)(implicit ctx: Context): Type =
403+
def checkClassType(tp: Type, pos: Position, traitReq: Boolean, stablePrefixReq: Boolean)(implicit ctx: Context): Type =
403404
tp.underlyingClassRef(refinementOK = false) match {
404405
case tref: TypeRef =>
405-
if (ctx.phase <= ctx.refchecksPhase) checkStable(tref.prefix, pos)
406406
if (traitReq && !(tref.symbol is Trait)) ctx.error(d"$tref is not a trait", pos)
407+
if (stablePrefixReq && ctx.phase <= ctx.refchecksPhase) checkStable(tref.prefix, pos)
407408
tp
408409
case _ =>
409410
ctx.error(d"$tp is not a class type", pos)
@@ -506,7 +507,7 @@ trait NoChecking extends Checking {
506507
override def checkNonCyclic(sym: Symbol, info: TypeBounds, reportErrors: Boolean)(implicit ctx: Context): Type = info
507508
override def checkValue(tree: Tree, proto: Type)(implicit ctx: Context): tree.type = tree
508509
override def checkStable(tp: Type, pos: Position)(implicit ctx: Context): Unit = ()
509-
override def checkClassTypeWithStablePrefix(tp: Type, pos: Position, traitReq: Boolean)(implicit ctx: Context): Type = tp
510+
override def checkClassType(tp: Type, pos: Position, traitReq: Boolean, stablePrefixReq: Boolean)(implicit ctx: Context): Type = tp
510511
override def checkImplicitParamsNotSingletons(vparamss: List[List[ValDef]])(implicit ctx: Context): Unit = ()
511512
override def checkFeasible(tp: Type, pos: Position, where: => String = "")(implicit ctx: Context): Type = tp
512513
override def checkNoDoubleDefs(cls: Symbol)(implicit ctx: Context): Unit = ()

src/dotty/tools/dotc/typer/Namer.scala

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -603,7 +603,8 @@ class Namer { typer: Typer =>
603603
val ptype = parentType(parent)(ctx.superCallContext)
604604
if (cls.isRefinementClass) ptype
605605
else {
606-
val pt = checkClassTypeWithStablePrefix(ptype, parent.pos, traitReq = parent ne parents.head)
606+
val pt = checkClassType(ptype, parent.pos,
607+
traitReq = parent ne parents.head, stablePrefixReq = true)
607608
if (pt.derivesFrom(cls)) {
608609
val addendum = parent match {
609610
case Select(qual: Super, _) if ctx.scala2Mode =>

0 commit comments

Comments
 (0)