Skip to content

Change classof #1108

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

Closed
wants to merge 10 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
41 changes: 12 additions & 29 deletions src/dotty/DottyPredef.scala
Original file line number Diff line number Diff line change
@@ -1,37 +1,20 @@
package dotty

import scala.reflect.ClassTag
import scala.reflect.runtime.universe.TypeTag
import scala.Predef.???
import scala.reflect.ClassTag
import scala.Predef.{???, implicitly}

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 {
implicit def arrayTag[T](implicit ctag: ClassTag[T]): ClassTag[Array[T]] = {
val ctag1 =
if (ctag == ClassTag.Unit) implicitly[ClassTag[scala.runtime.BoxedUnit]]
else ctag
ctag1.wrap.asInstanceOf[ClassTag[Array[T]]]
}

/** ClassTags for final classes */
implicit val NothingClassTag: ClassTag[Nothing] = ClassTag.Nothing
def classOf[T](implicit ctag: ClassTag[T]): Class[T] =
ctag.runtimeClass.asInstanceOf[Class[T]]
}
10 changes: 5 additions & 5 deletions src/dotty/tools/backend/jvm/DottyBackendInterface.scala
Original file line number Diff line number Diff line change
Expand Up @@ -179,14 +179,14 @@ class DottyBackendInterface(outputDirectory: AbstractFile)(implicit ctx: Context
implicit val ApplyTag: ClassTag[Apply] = ClassTag[Apply](classOf[Apply])
implicit val SelectTag: ClassTag[Select] = ClassTag[Select](classOf[Select])
implicit val TypeApplyTag: ClassTag[TypeApply] = ClassTag[TypeApply](classOf[TypeApply])
implicit val ClassDefTag: ClassTag[ClassDef] = ClassTag[TypeDef](classOf[TypeDef])
val ClassDefTag: ClassTag[ClassDef] = ClassTag[TypeDef](classOf[TypeDef]) // no implicit, it's an alias of TypeDefTag
implicit val TryTag: ClassTag[Try] = ClassTag[Try](classOf[Try])
implicit val AssignTag: ClassTag[Assign] = ClassTag[Assign](classOf[Assign])
implicit val IdentTag: ClassTag[Ident] = ClassTag[Ident](classOf[Ident])
implicit val IfTag: ClassTag[If] = ClassTag[If](classOf[If])
implicit val LabelDefTag: ClassTag[LabelDef] = ClassTag[LabelDef](classOf[LabelDef])
val LabelDefTag: ClassTag[LabelDef] = ClassTag[LabelDef](classOf[LabelDef]) // no implicit, it's an alias of DefDefTag
implicit val ValDefTag: ClassTag[ValDef] = ClassTag[ValDef](classOf[ValDef])
implicit val ThrowTag: ClassTag[Throw] = ClassTag[Throw](classOf[Throw])
val ThrowTag: ClassTag[Throw] = ClassTag[Throw](classOf[Throw]) // no implicit, it's an alias of ApplyTag
implicit val ReturnTag: ClassTag[Return] = ClassTag[Return](classOf[Return])
implicit val LiteralTag: ClassTag[Literal] = ClassTag[Literal](classOf[Literal])
implicit val BlockTag: ClassTag[Block] = ClassTag[Block](classOf[Block])
Expand All @@ -197,12 +197,12 @@ class DottyBackendInterface(outputDirectory: AbstractFile)(implicit ctx: Context
implicit val ThisTag: ClassTag[This] = ClassTag[This](classOf[This])
implicit val AlternativeTag: ClassTag[Alternative] = ClassTag[Alternative](classOf[Alternative])
implicit val DefDefTag: ClassTag[DefDef] = ClassTag[DefDef](classOf[DefDef])
implicit val ModuleDefTag: ClassTag[ModuleDef] = ClassTag[ModuleDef](classOf[ModuleDef])
val ModuleDefTag: ClassTag[ModuleDef] = ClassTag[ModuleDef](classOf[ModuleDef]) // no implicit, it's an alias of NullTag
implicit val NameTag: ClassTag[Name] = ClassTag[Name](classOf[Name])
implicit val TemplateTag: ClassTag[Template] = ClassTag[Template](classOf[Template])
implicit val BindTag: ClassTag[Bind] = ClassTag[Bind](classOf[Bind])
implicit val NewTag: ClassTag[New] = ClassTag[New](classOf[New])
implicit val ApplyDynamicTag: ClassTag[ApplyDynamic] = ClassTag[ApplyDynamic](classOf[ApplyDynamic])
val ApplyDynamicTag: ClassTag[ApplyDynamic] = ClassTag[ApplyDynamic](classOf[ApplyDynamic]) // no implicit, it's an alias of NullTag
implicit val SuperTag: ClassTag[Super] = ClassTag[Super](classOf[Super])
implicit val ConstantClassTag: ClassTag[Constant] = ClassTag[Constant](classOf[Constant])
implicit val ClosureTag: ClassTag[Closure] = ClassTag[Closure](classOf[Closure])
Expand Down
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 @@ -255,10 +268,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
3 changes: 3 additions & 0 deletions src/dotty/tools/dotc/core/Definitions.scala
Original file line number Diff line number Diff line change
Expand Up @@ -419,6 +419,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
7 changes: 6 additions & 1 deletion src/dotty/tools/dotc/transform/ClassOf.scala
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,11 @@ import TreeTransforms.{MiniPhaseTransform, TransformerInfo, TreeTransform}
* classOf[C] -> B.TYPE
* For every non-primitive class D:
* classOf[D] -> Literal(Constant(erasure(D)))
*
* NOTE: This is almost redundant since classOf now resolves
* to DottyPredef.classOf, which does not need the transform. The phase
* is kept in in order not to crash if someone calls Predef.classOf.
* Once we have merged Predef and DottyPredef, the phase can be dropped.
*/
class ClassOf extends MiniPhaseTransform {
import tpd._
Expand All @@ -31,7 +36,7 @@ class ClassOf extends MiniPhaseTransform {
override def transformTypeApply(tree: TypeApply)(implicit ctx: Context, info: TransformerInfo): Tree =
if (tree.symbol eq classOfMethod) {
val targ = tree.args.head.tpe
tree.clsOf(targ, Literal(Constant(TypeErasure.erasure(targ))))
clsOf(targ).ensureConforms(tree.tpe).withPos(tree.pos)
}
else tree
}
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
26 changes: 25 additions & 1 deletion src/dotty/tools/dotc/typer/Typer.scala
Original file line number Diff line number Diff line change
Expand Up @@ -1454,7 +1454,9 @@ class Typer extends Namer with TypeAssigner with Applications with Implicits wit
case ambi: AmbiguousImplicits =>
implicitArgError(s"ambiguous implicits: ${ambi.explanation} of $where")
case failure: SearchFailure =>
implicitArgError(d"no implicit argument of type $formal found for $where" + failure.postscript)
val arg = synthesizedClassTag(formal)
if (!arg.isEmpty) arg
else implicitArgError(d"no implicit argument of type $formal found for $where" + failure.postscript)
}
}
if (errors.nonEmpty) {
Expand Down Expand Up @@ -1515,6 +1517,28 @@ class Typer extends Namer with TypeAssigner with Applications with Implicits wit
}
}

/** If `formal` is of the form ClassTag[T], where `T` is a class type,
* synthesize a class tag for `T`.
*/
def synthesizedClassTag(formal: Type): Tree = {
if (formal.isRef(defn.ClassTagClass))
formal.argTypes match {
case arg :: Nil =>
val tp = fullyDefinedType(arg, "ClassTag argument", tree.pos)
tp.underlyingClassRef(refinementOK = false) match {
case tref: TypeRef =>
return ref(defn.ClassTagModule)
.select(nme.apply)
.appliedToType(tp)
.appliedTo(clsOf(tref))
.withPos(tree.pos.endPos)
case _ =>
}
case _ =>
}
EmptyTree
}

/** Adapt an expression of constant type to a different constant type `tpe`. */
def adaptConstant(tree: Tree, tpe: ConstantType): Tree = {
def lit = Literal(tpe.value).withPos(tree.pos)
Expand Down
2 changes: 1 addition & 1 deletion test/dotc/tests.scala
Original file line number Diff line number Diff line change
Expand Up @@ -144,6 +144,7 @@ class tests extends CompilerTest {
@Test def neg_variancesConstr = compileFile(negDir, "variances-constr", xerrors = 2)
@Test def neg_i871_missingReturnType = compileFile(negDir, "i871", xerrors = 2)
@Test def neg_badAuxConstr = compileFile(negDir, "badAuxConstr", xerrors = 2)
@Test def neg_classOf = compileFile(negDir, "classOf", xerrors = 4)
@Test def neg_typetest = compileFile(negDir, "typetest", xerrors = 1)
@Test def neg_t1569_failedAvoid = compileFile(negDir, "t1569-failedAvoid", xerrors = 1)
@Test def neg_clashes = compileFile(negDir, "clashes", xerrors = 2)
Expand Down Expand Up @@ -174,7 +175,6 @@ class tests extends CompilerTest {
@Test def neg_selfInheritance = compileFile(negDir, "selfInheritance", xerrors = 6)
@Test def neg_selfreq = compileFile(negDir, "selfreq", xerrors = 2)
@Test def neg_singletons = compileFile(negDir, "singletons", xerrors = 8)
@Test def neg_shadowedImplicits = compileFile(negDir, "arrayclone-new", xerrors = 2)
@Test def neg_ski = compileFile(negDir, "ski", xerrors = 12)
@Test def neg_traitParamsTyper = compileFile(negDir, "traitParamsTyper", xerrors = 5)
@Test def neg_traitParamsMixin = compileFile(negDir, "traitParamsMixin", xerrors = 2)
Expand Down
38 changes: 0 additions & 38 deletions tests/neg/arrayclone-new.scala

This file was deleted.

11 changes: 11 additions & 0 deletions tests/neg/classOf.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
object Test {

class C { type I }
type A = C

def f1[T] = classOf[T] // error
def f2[T <: String] = classOf[T] // error
val x = classOf[Test.type] // error
val y = classOf[C { type I = String }] // error
val z = classOf[A] // ok
}