diff options
author | Dmitry Petrashko <dark@d-d.me> | 2016-03-02 10:16:05 +0100 |
---|---|---|
committer | Dmitry Petrashko <dark@d-d.me> | 2016-03-02 10:16:05 +0100 |
commit | ae624660d3cc31e9956d7e537c7a5c7925afda68 (patch) | |
tree | 12e28b3db9125c3afbb03c6331326f6fab29ae13 | |
parent | 0ae3ef2010b90bf06d76a768b0f0c5aa56c1180a (diff) | |
parent | 1d2fe4823bc4c8a69351d49229556ac3a1532778 (diff) | |
download | dotty-ae624660d3cc31e9956d7e537c7a5c7925afda68.tar.gz dotty-ae624660d3cc31e9956d7e537c7a5c7925afda68.tar.bz2 dotty-ae624660d3cc31e9956d7e537c7a5c7925afda68.zip |
Merge pull request #1111 from dotty-staging/fix-#1099
Special case pattern matching against abstract types with class tags
-rw-r--r-- | src/dotty/DottyPredef.scala | 34 | ||||
-rw-r--r-- | src/dotty/tools/dotc/Compiler.scala | 1 | ||||
-rw-r--r-- | src/dotty/tools/dotc/ast/Desugar.scala | 44 | ||||
-rw-r--r-- | src/dotty/tools/dotc/ast/tpd.scala | 38 | ||||
-rw-r--r-- | src/dotty/tools/dotc/core/Definitions.scala | 5 | ||||
-rw-r--r-- | src/dotty/tools/dotc/core/tasty/TreeUnpickler.scala | 2 | ||||
-rw-r--r-- | src/dotty/tools/dotc/transform/ClassOf.scala | 11 | ||||
-rw-r--r-- | src/dotty/tools/dotc/transform/ClassTags.scala | 68 | ||||
-rw-r--r-- | src/dotty/tools/dotc/transform/GetClass.scala | 4 | ||||
-rw-r--r-- | src/dotty/tools/dotc/typer/Applications.scala | 4 | ||||
-rw-r--r-- | src/dotty/tools/dotc/typer/Checking.scala | 13 | ||||
-rw-r--r-- | src/dotty/tools/dotc/typer/Namer.scala | 3 | ||||
-rw-r--r-- | src/dotty/tools/dotc/typer/Typer.scala | 91 | ||||
-rw-r--r-- | test/dotc/tests.scala | 1 | ||||
-rw-r--r-- | tests/neg/arrayclone-new.scala | 38 | ||||
-rw-r--r-- | tests/run/i1099.scala | 25 |
16 files changed, 171 insertions, 211 deletions
diff --git a/src/dotty/DottyPredef.scala b/src/dotty/DottyPredef.scala index 0c2492c94..9170da476 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.reflect.ClassTag 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 + implicit def arrayTag[T](implicit ctag: ClassTag[T]): ClassTag[Array[T]] = + ctag.wrap } diff --git a/src/dotty/tools/dotc/Compiler.scala b/src/dotty/tools/dotc/Compiler.scala index d526903b8..f12ab66c5 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 e9c641282..8ba155097 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 } 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..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 @@ -419,6 +421,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..e7b6977c7 100644 --- a/src/dotty/tools/dotc/transform/ClassOf.scala +++ b/src/dotty/tools/dotc/transform/ClassOf.scala @@ -21,17 +21,10 @@ 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 - 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/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 -} 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/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 fc2bf2381..84344dbb1 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) } @@ -400,30 +400,53 @@ 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 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 && !ctx.isAfterTyper => + 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 = handlePattern, + ifExpr = ascription(typedTpt, isWildcard = false), + wildName = nme.WILDCARD) } } @@ -1477,7 +1500,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 +1563,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 f82337eac..0bb27ab14 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) 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); -} - 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)) + } +} |