From f6391c780ce7472352b60da9fdd7ec8d7496a0ea Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Thu, 18 Feb 2016 14:03:35 +0100 Subject: Remove bogus test Tests failures were caused by previous incomplete implementation of classTag. --- test/dotc/tests.scala | 1 - 1 file changed, 1 deletion(-) diff --git a/test/dotc/tests.scala b/test/dotc/tests.scala index 819526ce4..f60674ede 100644 --- a/test/dotc/tests.scala +++ b/test/dotc/tests.scala @@ -178,7 +178,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) -- cgit v1.2.3 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 From 27d91f90c8f6c295dd03dc98be61c10c77d775a7 Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Thu, 18 Feb 2016 14:48:45 +0100 Subject: Make type fully defined before searching for a ClassTag for it --- src/dotty/tools/dotc/typer/Typer.scala | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/dotty/tools/dotc/typer/Typer.scala b/src/dotty/tools/dotc/typer/Typer.scala index 445037228..a1d650d2d 100644 --- a/src/dotty/tools/dotc/typer/Typer.scala +++ b/src/dotty/tools/dotc/typer/Typer.scala @@ -1547,11 +1547,12 @@ class Typer extends Namer with TypeAssigner with Applications with Implicits wit if (formal.isRef(defn.ClassTagClass)) formal.argTypes match { case arg :: Nil => - arg.underlyingClassRef(refinementOK = false) match { + val tp = fullyDefinedType(arg, "ClassTag argument", tree.pos) + tp.underlyingClassRef(refinementOK = false) match { case tref: TypeRef => return ref(defn.ClassTagModule) .select(nme.apply) - .appliedToType(arg) + .appliedToType(tp) .appliedTo(clsOf(tref)) .withPos(tree.pos.endPos) case _ => -- cgit v1.2.3 From 02b545211b3895d18e7e9b45289897a5ae447123 Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Thu, 18 Feb 2016 17:23:50 +0100 Subject: Fix search of Array classTags. If a classtag for `T` is available, a classtag for `Array[T]` can also be generated. --- src/dotty/DottyPredef.scala | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/dotty/DottyPredef.scala b/src/dotty/DottyPredef.scala index 007493fcb..9170da476 100644 --- a/src/dotty/DottyPredef.scala +++ b/src/dotty/DottyPredef.scala @@ -1,13 +1,13 @@ package dotty import scala.reflect.runtime.universe.TypeTag +import scala.reflect.ClassTag import scala.Predef.??? /** unimplemented implicit for TypeTag */ object DottyPredef { implicit def typeTag[T]: TypeTag[T] = ??? -// not yet: -// def classOf[T](implicit ctag: ClassTag[T]): Class[T] = -// ctag.runtimeClass + implicit def arrayTag[T](implicit ctag: ClassTag[T]): ClassTag[Array[T]] = + ctag.wrap } -- cgit v1.2.3 From 1f58090849a51787a4482a8d016a31f15f3c1b9b Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Thu, 18 Feb 2016 17:25:31 +0100 Subject: Fix desugaring of classes with context bounds Context bounds did not make it before into secondary constructors. Now the evidence parameters generated by context bounds get copied into secondary constructors. Without this fix, scala.collection.immutable.PagedSeq fails to compile in new classtag scheme. --- src/dotty/tools/dotc/ast/Desugar.scala | 44 +++++++++++++++++++++++----------- 1 file changed, 30 insertions(+), 14 deletions(-) diff --git a/src/dotty/tools/dotc/ast/Desugar.scala b/src/dotty/tools/dotc/ast/Desugar.scala index 31ebc4a7d..fcc055fc7 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] @@ -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 } -- cgit v1.2.3 From ea2345d5725ca74504d5cb4b9e14e6d2e73da53c Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Thu, 18 Feb 2016 17:26:08 +0100 Subject: Simplify ClassOf phase Make use of Definitions as the repository for standard symbols in Predef. --- src/dotty/tools/dotc/core/Definitions.scala | 2 ++ src/dotty/tools/dotc/transform/ClassOf.scala | 9 +-------- 2 files changed, 3 insertions(+), 8 deletions(-) diff --git a/src/dotty/tools/dotc/core/Definitions.scala b/src/dotty/tools/dotc/core/Definitions.scala index 188a00fc2..f16f25b23 100644 --- a/src/dotty/tools/dotc/core/Definitions.scala +++ b/src/dotty/tools/dotc/core/Definitions.scala @@ -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 diff --git a/src/dotty/tools/dotc/transform/ClassOf.scala b/src/dotty/tools/dotc/transform/ClassOf.scala index f8f4991c8..e7b6977c7 100644 --- a/src/dotty/tools/dotc/transform/ClassOf.scala +++ b/src/dotty/tools/dotc/transform/ClassOf.scala @@ -21,15 +21,8 @@ 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 clsOf(targ).ensureConforms(tree.tpe).withPos(tree.pos) } -- cgit v1.2.3 From 419ee6cd11214c91e55bb74fb77b8e84760a780e Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Thu, 18 Feb 2016 17:47:49 +0100 Subject: Remove ClassTags phase. Now subsumed by functionality in Typer. --- src/dotty/tools/dotc/transform/ClassTags.scala | 68 -------------------------- 1 file changed, 68 deletions(-) delete mode 100644 src/dotty/tools/dotc/transform/ClassTags.scala diff --git a/src/dotty/tools/dotc/transform/ClassTags.scala b/src/dotty/tools/dotc/transform/ClassTags.scala deleted file mode 100644 index 8fd25f785..000000000 --- a/src/dotty/tools/dotc/transform/ClassTags.scala +++ /dev/null @@ -1,68 +0,0 @@ -package dotty.tools.dotc -package transform - -import core._ -import TreeTransforms._ -import Contexts.Context -import Flags._ -import SymUtils._ -import Symbols._ -import SymDenotations._ -import Types._ -import Decorators._ -import DenotTransformers._ -import StdNames._ -import NameOps._ -import ast.Trees._ -import dotty.tools.dotc.ast.tpd -import dotty.tools.dotc.core.Constants.Constant -import util.Positions._ -import Names._ -import collection.mutable - -/** This phase replaces calls to DottyPredef.classTag by code that synthesizes appropriate ClassTag - */ -class ClassTags extends MiniPhaseTransform with IdentityDenotTransformer { thisTransform => - import ast.tpd._ - - private var classTagCache: Symbol = null - private var typeTagCache: Symbol = null - private var scala2ClassTagModule: Symbol = null - - - override def prepareForUnit(tree: tpd.Tree)(implicit ctx: Context): TreeTransform = { - classTagCache = defn.DottyPredefModule.requiredMethod(nme.classTag) - typeTagCache = defn.DottyPredefModule.requiredMethod(nme.typeTag) - scala2ClassTagModule = ctx.requiredModule("scala.reflect.ClassTag") - this - } - - override def phaseName: String = "classTags" - - override def transformTypeApply(tree: tpd.TypeApply)(implicit ctx: Context, info: TransformerInfo): tpd.Tree = - if (tree.fun.symbol eq classTagCache) { - val tp = tree.args.head.tpe - val defn = ctx.definitions - val (elemType, ndims) = tp match { - case defn.MultiArrayOf(elem, ndims) => (elem, ndims) - case _ => (tp, 0) - } - - val claz = tp.classSymbol - val elemClaz = elemType.classSymbol - assert(!claz.isPrimitiveValueClass) // should be inserted by typer - val elemTag = - if (elemClaz.isPrimitiveValueClass || elemClaz == defn.NothingClass || elemClaz == defn.NullClass) - ref(defn.DottyPredefModule).select(s"${elemClaz.name}ClassTag".toTermName) - else if (ValueClasses.isDerivedValueClass(elemClaz)) - ref(claz.companionModule) - else if (elemClaz eq defn.AnyClass) - ref(scala2ClassTagModule).select(nme.Any) - else { - val erazedTp = TypeErasure.erasure(elemType).classSymbol.typeRef - ref(scala2ClassTagModule).select(nme.apply) - .appliedToType(erazedTp).appliedTo(Literal(Constant(erazedTp))) - } - (1 to ndims).foldLeft(elemTag)((arr, level) => Select(arr, nme.wrap).ensureApplied).ensureConforms(tree.tpe) - } else tree -} -- cgit v1.2.3 From c6064ed02c4d895b1d0df269fc018265b0d55625 Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Fri, 19 Feb 2016 13:49:15 +0100 Subject: Check that classOf gets applied to class types --- src/dotty/tools/dotc/typer/Applications.scala | 4 ++++ src/dotty/tools/dotc/typer/Checking.scala | 13 +++++++------ src/dotty/tools/dotc/typer/Namer.scala | 3 ++- src/dotty/tools/dotc/typer/Typer.scala | 2 +- 4 files changed, 14 insertions(+), 8 deletions(-) diff --git a/src/dotty/tools/dotc/typer/Applications.scala b/src/dotty/tools/dotc/typer/Applications.scala index 098385d4b..3b8c56ea6 100644 --- a/src/dotty/tools/dotc/typer/Applications.scala +++ b/src/dotty/tools/dotc/typer/Applications.scala @@ -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) diff --git a/src/dotty/tools/dotc/typer/Checking.scala b/src/dotty/tools/dotc/typer/Checking.scala index b71a3ab2a..0ca121925 100644 --- a/src/dotty/tools/dotc/typer/Checking.scala +++ b/src/dotty/tools/dotc/typer/Checking.scala @@ -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) @@ -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 = () diff --git a/src/dotty/tools/dotc/typer/Namer.scala b/src/dotty/tools/dotc/typer/Namer.scala index 13ed96249..de27333d5 100644 --- a/src/dotty/tools/dotc/typer/Namer.scala +++ b/src/dotty/tools/dotc/typer/Namer.scala @@ -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 => diff --git a/src/dotty/tools/dotc/typer/Typer.scala b/src/dotty/tools/dotc/typer/Typer.scala index a1d650d2d..1e8e3a524 100644 --- a/src/dotty/tools/dotc/typer/Typer.scala +++ b/src/dotty/tools/dotc/typer/Typer.scala @@ -383,7 +383,7 @@ class Typer extends Namer with TypeAssigner with Applications with Implicits wit case TypeApplications.EtaExpansion(tycon) => tpt1 = tpt1.withType(tycon) case _ => } - checkClassTypeWithStablePrefix(tpt1.tpe, tpt1.pos, traitReq = false) + checkClassType(tpt1.tpe, tpt1.pos, traitReq = false, stablePrefixReq = true) assignType(cpy.New(tree)(tpt1), tpt1) // todo in a later phase: checkInstantiatable(cls, tpt1.pos) } -- cgit v1.2.3 From 14096e3601e42fd33fb2446b908a5cfce3cf1fa9 Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Fri, 19 Feb 2016 15:47:44 +0100 Subject: Refactoring of typedTyped Goal: Make implementation easier to understand. Prepare the ground for special-casing of typetagged patterns. --- src/dotty/tools/dotc/typer/Typer.scala | 48 ++++++++++++++++++++-------------- 1 file changed, 28 insertions(+), 20 deletions(-) diff --git a/src/dotty/tools/dotc/typer/Typer.scala b/src/dotty/tools/dotc/typer/Typer.scala index 1e8e3a524..fabee83b7 100644 --- a/src/dotty/tools/dotc/typer/Typer.scala +++ b/src/dotty/tools/dotc/typer/Typer.scala @@ -400,30 +400,38 @@ class Typer extends Namer with TypeAssigner with Applications with Implicits wit } def typedTyped(tree: untpd.Typed, pt: Type)(implicit ctx: Context): Tree = track("typedTyped") { - def regularTyped(isWildcard: Boolean) = { - val tpt1 = - if (untpd.isWildcardStarArg(tree)) - TypeTree(defn.SeqType.appliedTo(pt :: Nil)) - else - checkSimpleKinded(typedType(tree.tpt)) - val expr1 = - if (isWildcard) tree.expr withType tpt1.tpe - else typed(tree.expr, tpt1.tpe.widenSkolem) - assignType(cpy.Typed(tree)(expr1, tpt1), tpt1) - } - tree.expr match { + /* Handles three cases: + * @param ifPat how to handle a pattern (_: T) + * @param ifExpr how to handle an expression (e: T) + * @param wildName what name `w` to use in the rewriting of + * (x: T) to (x @ (w: T)). This is either `_` or `_*`. + */ + def cases(ifPat: => Tree, ifExpr: => Tree, wildName: TermName) = tree.expr match { case id: untpd.Ident if (ctx.mode is Mode.Pattern) && isVarPattern(id) => - if (id.name == nme.WILDCARD || id.name == nme.WILDCARD_STAR) regularTyped(isWildcard = true) + if (id.name == nme.WILDCARD || id.name == nme.WILDCARD_STAR) ifPat else { import untpd._ - val name = if (untpd.isWildcardStarArg(tree)) nme.WILDCARD_STAR else nme.WILDCARD - typed(Bind(id.name, Typed(Ident(name), tree.tpt)).withPos(id.pos), pt) + typed(Bind(id.name, Typed(Ident(wildName), tree.tpt)).withPos(id.pos), pt) } - case _ => - if (untpd.isWildcardStarArg(tree)) - seqToRepeated(typedExpr(tree.expr, defn.SeqType)) - else - regularTyped(isWildcard = false) + case _ => ifExpr + } + def ascription(tpt: Tree, isWildcard: Boolean) = { + val expr1 = + if (isWildcard) tree.expr.withType(tpt.tpe) + else typed(tree.expr, tpt.tpe.widenSkolem) + assignType(cpy.Typed(tree)(expr1, tpt), tpt) + } + if (untpd.isWildcardStarArg(tree)) + cases( + ifPat = ascription(TypeTree(defn.SeqType.appliedTo(pt :: Nil)), isWildcard = true), + ifExpr = seqToRepeated(typedExpr(tree.expr, defn.SeqType)), + wildName = nme.WILDCARD_STAR) + else { + def tpt1 = checkSimpleKinded(typedType(tree.tpt)) + cases( + ifPat = ascription(tpt1, isWildcard = true), + ifExpr = ascription(tpt1, isWildcard = false), + wildName = nme.WILDCARD) } } -- cgit v1.2.3 From 050c9af51432e3a752715af78b0de577d5af7f87 Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Fri, 19 Feb 2016 16:33:43 +0100 Subject: Special case for pattern matching tagged abstract types. Add special case when pattern matching against an abstract type that comes with a class tag --- src/dotty/tools/dotc/typer/Typer.scala | 21 ++++++++++++++++++--- tests/run/i1099.scala | 25 +++++++++++++++++++++++++ 2 files changed, 43 insertions(+), 3 deletions(-) create mode 100644 tests/run/i1099.scala diff --git a/src/dotty/tools/dotc/typer/Typer.scala b/src/dotty/tools/dotc/typer/Typer.scala index fabee83b7..64c118288 100644 --- a/src/dotty/tools/dotc/typer/Typer.scala +++ b/src/dotty/tools/dotc/typer/Typer.scala @@ -427,10 +427,25 @@ class Typer extends Namer with TypeAssigner with Applications with Implicits wit ifExpr = seqToRepeated(typedExpr(tree.expr, defn.SeqType)), wildName = nme.WILDCARD_STAR) else { - def tpt1 = checkSimpleKinded(typedType(tree.tpt)) + def typedTpt = checkSimpleKinded(typedType(tree.tpt)) + def handlePattern: Tree = { + val tpt1 = typedTpt + // special case for an abstract type that comes with a class tag + tpt1.tpe.dealias match { + case tref: TypeRef if !tref.symbol.isClass => + inferImplicit(defn.ClassTagType.appliedTo(tref), + EmptyTree, tpt1.pos)(ctx.retractMode(Mode.Pattern)) match { + case SearchSuccess(arg, _, _) => + return typed(untpd.Apply(untpd.TypedSplice(arg), tree.expr), pt) + case _ => + } + case _ => + } + ascription(tpt1, isWildcard = true) + } cases( - ifPat = ascription(tpt1, isWildcard = true), - ifExpr = ascription(tpt1, isWildcard = false), + ifPat = handlePattern, + ifExpr = ascription(typedTpt, isWildcard = false), wildName = nme.WILDCARD) } } diff --git a/tests/run/i1099.scala b/tests/run/i1099.scala new file mode 100644 index 000000000..15a428cc3 --- /dev/null +++ b/tests/run/i1099.scala @@ -0,0 +1,25 @@ +import scala.reflect.ClassTag +object Test { + def foo[T: ClassTag](x: Any) = + x match { + case t: T => true + case _ => false + } + // This is what `foo` expands to + def foo2[T](x: Any)(implicit ev: ClassTag[T]) = + x match { + case t @ ev(_) => true + case _ => false + } + def main(args: Array[String]): Unit = { + assert(foo[String]("a")) + assert(!foo[String](new Integer(1))) + assert(foo[Int](1)) + assert(!foo[Int](true)) + + assert(foo2[String]("a")) + assert(!foo2[String](new Integer(1))) + assert(foo2[Int](1)) + assert(!foo2[Int](true)) + } +} -- cgit v1.2.3 From ad4ce030052d6013641eefc9481e50c2ca0b9c2e Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Fri, 19 Feb 2016 16:48:50 +0100 Subject: Search for classtag only during typer. We do not want to do implicit search during tree checking. --- src/dotty/tools/dotc/typer/Typer.scala | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/dotty/tools/dotc/typer/Typer.scala b/src/dotty/tools/dotc/typer/Typer.scala index 64c118288..84344dbb1 100644 --- a/src/dotty/tools/dotc/typer/Typer.scala +++ b/src/dotty/tools/dotc/typer/Typer.scala @@ -432,7 +432,7 @@ class Typer extends Namer with TypeAssigner with Applications with Implicits wit val tpt1 = typedTpt // special case for an abstract type that comes with a class tag tpt1.tpe.dealias match { - case tref: TypeRef if !tref.symbol.isClass => + case tref: TypeRef if !tref.symbol.isClass && !ctx.isAfterTyper => inferImplicit(defn.ClassTagType.appliedTo(tref), EmptyTree, tpt1.pos)(ctx.retractMode(Mode.Pattern)) match { case SearchSuccess(arg, _, _) => -- cgit v1.2.3 From 1d2fe4823bc4c8a69351d49229556ac3a1532778 Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Sat, 20 Feb 2016 10:20:31 +0100 Subject: Remove bogus test on rebasing --- tests/neg/arrayclone-new.scala | 38 -------------------------------------- 1 file changed, 38 deletions(-) delete mode 100644 tests/neg/arrayclone-new.scala diff --git a/tests/neg/arrayclone-new.scala b/tests/neg/arrayclone-new.scala deleted file mode 100644 index 0e42545f8..000000000 --- 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"); // error - 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"); // error - - 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); -} - -- cgit v1.2.3