diff --git a/src/dotty/DottyPredef.scala b/src/dotty/DottyPredef.scala index 0c2492c94725..5fa3b0bf4115 100644 --- a/src/dotty/DottyPredef.scala +++ b/src/dotty/DottyPredef.scala @@ -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]] } diff --git a/src/dotty/tools/backend/jvm/DottyBackendInterface.scala b/src/dotty/tools/backend/jvm/DottyBackendInterface.scala index 5776cc8e234f..2e57b499650a 100644 --- a/src/dotty/tools/backend/jvm/DottyBackendInterface.scala +++ b/src/dotty/tools/backend/jvm/DottyBackendInterface.scala @@ -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]) @@ -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]) diff --git a/src/dotty/tools/dotc/Compiler.scala b/src/dotty/tools/dotc/Compiler.scala index 99b7bd880fff..7c7648657d03 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/Desugar.scala b/src/dotty/tools/dotc/ast/Desugar.scala index 87694843ad6a..b59121462f82 100644 --- a/src/dotty/tools/dotc/ast/Desugar.scala +++ b/src/dotty/tools/dotc/ast/Desugar.scala @@ -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 { @@ -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] @@ -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 } diff --git a/src/dotty/tools/dotc/ast/tpd.scala b/src/dotty/tools/dotc/ast/tpd.scala index b78e4c79f3b2..a0400c1e84f8 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 c5d42a4728e9..188a00fc2683 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 81d19f7e48eb..16caac02e20b 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 51a68f9033e5..93aca1b74ca9 100644 --- a/src/dotty/tools/dotc/transform/ClassOf.scala +++ b/src/dotty/tools/dotc/transform/ClassOf.scala @@ -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._ @@ -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 } diff --git a/src/dotty/tools/dotc/transform/GetClass.scala b/src/dotty/tools/dotc/transform/GetClass.scala index 9d182382d211..f25fd6f64128 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 854bc2094dea..4d0efe875da8 100644 --- a/src/dotty/tools/dotc/typer/Typer.scala +++ b/src/dotty/tools/dotc/typer/Typer.scala @@ -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) { @@ -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) diff --git a/test/dotc/tests.scala b/test/dotc/tests.scala index 70af32bd3d53..9d9c0e8e9b1a 100644 --- a/test/dotc/tests.scala +++ b/test/dotc/tests.scala @@ -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) @@ -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) diff --git a/tests/neg/arrayclone-new.scala b/tests/neg/arrayclone-new.scala deleted file mode 100644 index 4e33a7d8cde6..000000000000 --- a/tests/neg/arrayclone-new.scala +++ /dev/null @@ -1,38 +0,0 @@ -// Run with -explaintypes to see information about shadowing failures -import scala.reflect.{ClassTag, classTag} - -object Test extends dotty.runtime.LegacyApp{ - ObjectArrayClone; - PolymorphicArrayClone; -} - -object ObjectArrayClone{ - val it : Array[String] = Array("1", "0"); - val cloned = it.clone(); - assert(cloned.sameElements(it)); - cloned(0) = "0"; - assert(it(0) == "1") -} - -object PolymorphicArrayClone{ - def testIt[T](it : Array[T], one : T, zero : T) = { - val cloned = it.clone(); - assert(cloned.sameElements(it)); - cloned(0) = zero; - assert(it(0) == one) - } - - testIt(Array("one", "two"), "one", "two"); - - class Mangler[T: ClassTag](ts : T*){ - // this will always be a BoxedAnyArray even after we've unboxed its contents. - val it = ts.toArray[T]; - } - - val mangled = new Mangler[Int](0, 1); - - val y : Array[Int] = mangled.it; // make sure it's unboxed - - testIt(mangled.it, 0, 1); -} - diff --git a/tests/neg/classOf.scala b/tests/neg/classOf.scala new file mode 100644 index 000000000000..e13cf71c43a2 --- /dev/null +++ b/tests/neg/classOf.scala @@ -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 +}