From e4989b3cc13f70d8316790e309b5d3b27317d80e Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Thu, 18 Feb 2016 14:07:03 +0100 Subject: Synthesize classTags in Typer. Now diagnoses missing ClassTags of abstract types as implicit failures. Also: Simpler API of tpd.clsOf. --- src/dotty/DottyPredef.scala | 34 +++---------------- src/dotty/tools/dotc/Compiler.scala | 1 - src/dotty/tools/dotc/ast/tpd.scala | 38 ++++++++++------------ src/dotty/tools/dotc/core/Definitions.scala | 3 ++ .../tools/dotc/core/tasty/TreeUnpickler.scala | 2 ++ src/dotty/tools/dotc/transform/ClassOf.scala | 2 +- src/dotty/tools/dotc/transform/GetClass.scala | 4 ++- src/dotty/tools/dotc/typer/Typer.scala | 25 +++++++++++++- 8 files changed, 55 insertions(+), 54 deletions(-) diff --git a/src/dotty/DottyPredef.scala b/src/dotty/DottyPredef.scala index 0c2492c94..007493fcb 100644 --- a/src/dotty/DottyPredef.scala +++ b/src/dotty/DottyPredef.scala @@ -1,37 +1,13 @@ package dotty -import scala.reflect.ClassTag import scala.reflect.runtime.universe.TypeTag 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 +// not yet: +// def classOf[T](implicit ctag: ClassTag[T]): Class[T] = +// ctag.runtimeClass } diff --git a/src/dotty/tools/dotc/Compiler.scala b/src/dotty/tools/dotc/Compiler.scala index 99b7bd880..7c7648657 100644 --- a/src/dotty/tools/dotc/Compiler.scala +++ b/src/dotty/tools/dotc/Compiler.scala @@ -59,7 +59,6 @@ class Compiler { new SeqLiterals, new InterceptedMethods, new Getters, - new ClassTags, new ElimByName, new AugmentScala2Traits, new ResolveSuper), diff --git a/src/dotty/tools/dotc/ast/tpd.scala b/src/dotty/tools/dotc/ast/tpd.scala index 8e52d695b..7cd469d7a 100644 --- a/src/dotty/tools/dotc/ast/tpd.scala +++ b/src/dotty/tools/dotc/ast/tpd.scala @@ -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 */ @@ -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) diff --git a/src/dotty/tools/dotc/core/Definitions.scala b/src/dotty/tools/dotc/core/Definitions.scala index c5d42a472..188a00fc2 100644 --- a/src/dotty/tools/dotc/core/Definitions.scala +++ b/src/dotty/tools/dotc/core/Definitions.scala @@ -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") diff --git a/src/dotty/tools/dotc/core/tasty/TreeUnpickler.scala b/src/dotty/tools/dotc/core/tasty/TreeUnpickler.scala index 81d19f7e4..16caac02e 100644 --- a/src/dotty/tools/dotc/core/tasty/TreeUnpickler.scala +++ b/src/dotty/tools/dotc/core/tasty/TreeUnpickler.scala @@ -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()) } diff --git a/src/dotty/tools/dotc/transform/ClassOf.scala b/src/dotty/tools/dotc/transform/ClassOf.scala index 51a68f903..f8f4991c8 100644 --- a/src/dotty/tools/dotc/transform/ClassOf.scala +++ b/src/dotty/tools/dotc/transform/ClassOf.scala @@ -31,7 +31,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 } diff --git a/src/dotty/tools/dotc/transform/GetClass.scala b/src/dotty/tools/dotc/transform/GetClass.scala index 9d182382d..f25fd6f64 100644 --- a/src/dotty/tools/dotc/transform/GetClass.scala +++ b/src/dotty/tools/dotc/transform/GetClass.scala @@ -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: @@ -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 } } diff --git a/src/dotty/tools/dotc/typer/Typer.scala b/src/dotty/tools/dotc/typer/Typer.scala index fc2bf2381..445037228 100644 --- a/src/dotty/tools/dotc/typer/Typer.scala +++ b/src/dotty/tools/dotc/typer/Typer.scala @@ -1477,7 +1477,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) { @@ -1538,6 +1540,27 @@ 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 => + arg.underlyingClassRef(refinementOK = false) match { + case tref: TypeRef => + return ref(defn.ClassTagModule) + .select(nme.apply) + .appliedToType(arg) + .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) -- cgit v1.2.3