diff options
author | odersky <odersky@gmail.com> | 2017-02-18 11:49:42 +0100 |
---|---|---|
committer | GitHub <noreply@github.com> | 2017-02-18 11:49:42 +0100 |
commit | 5b22ab75f682bd25380d006c4789567e848adfcd (patch) | |
tree | fd9cb77dfa8d18230692e9ef5272258cdf77311c | |
parent | 63083f1118f5d5de378811c0dd22d1d6034eb924 (diff) | |
parent | 75dcc47f99cb1bdcdd1aac99be0182ddda3b5a16 (diff) | |
download | dotty-5b22ab75f682bd25380d006c4789567e848adfcd.tar.gz dotty-5b22ab75f682bd25380d006c4789567e848adfcd.tar.bz2 dotty-5b22ab75f682bd25380d006c4789567e848adfcd.zip |
Merge pull request #1994 from dotty-staging/fix-#1991
Fix #1991: Use classtag where available in unapply
-rw-r--r-- | compiler/src/dotty/tools/dotc/typer/Applications.scala | 2 | ||||
-rw-r--r-- | compiler/src/dotty/tools/dotc/typer/Typer.scala | 43 | ||||
-rw-r--r-- | tests/run/i1991.scala | 29 |
3 files changed, 54 insertions, 20 deletions
diff --git a/compiler/src/dotty/tools/dotc/typer/Applications.scala b/compiler/src/dotty/tools/dotc/typer/Applications.scala index 42c24ffb7..0ed6ed6b4 100644 --- a/compiler/src/dotty/tools/dotc/typer/Applications.scala +++ b/compiler/src/dotty/tools/dotc/typer/Applications.scala @@ -916,7 +916,7 @@ trait Applications extends Compatibility { self: Typer with Dynamic => val result = assignType(cpy.UnApply(tree)(unapplyFn, unapplyImplicits, unapplyPatterns), ownType) unapp.println(s"unapply patterns = $unapplyPatterns") if ((ownType eq selType) || ownType.isError) result - else Typed(result, TypeTree(ownType)) + else tryWithClassTag(Typed(result, TypeTree(ownType)), selType) case tp => val unapplyErr = if (tp.isError) unapplyFn else notAnExtractor(unapplyFn) val typedArgsErr = args mapconserve (typed(_, defn.AnyType)) diff --git a/compiler/src/dotty/tools/dotc/typer/Typer.scala b/compiler/src/dotty/tools/dotc/typer/Typer.scala index 2b57cf778..498fd001b 100644 --- a/compiler/src/dotty/tools/dotc/typer/Typer.scala +++ b/compiler/src/dotty/tools/dotc/typer/Typer.scala @@ -523,18 +523,8 @@ class Typer extends Namer with TypeAssigner with Applications with Implicits wit 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 _ => - if (!ctx.isAfterTyper) tpt1.tpe.<:<(pt)(ctx.addMode(Mode.GADTflexible)) - } - ascription(tpt1, isWildcard = true) + if (!ctx.isAfterTyper) tpt1.tpe.<:<(pt)(ctx.addMode(Mode.GADTflexible)) + tryWithClassTag(ascription(tpt1, isWildcard = true), pt) } cases( ifPat = handlePattern, @@ -543,6 +533,23 @@ class Typer extends Namer with TypeAssigner with Applications with Implicits wit } } + /** For a typed tree `e: T`, if `T` is an abstract type for which an implicit class tag `ctag` + * exists, rewrite to `ctag(e)`. + * @pre We are in pattern-matching mode (Mode.Pattern) + */ + def tryWithClassTag(tree: Typed, pt: Type)(implicit ctx: Context) = tree.tpt.tpe.dealias match { + case tref: TypeRef if !tref.symbol.isClass && !ctx.isAfterTyper => + require(ctx.mode.is(Mode.Pattern)) + inferImplicit(defn.ClassTagType.appliedTo(tref), + EmptyTree, tree.tpt.pos)(ctx.retractMode(Mode.Pattern)) match { + case SearchSuccess(clsTag, _, _, _) => + typed(untpd.Apply(untpd.TypedSplice(clsTag), untpd.TypedSplice(tree.expr)), pt) + case _ => + tree + } + case _ => tree + } + def typedNamedArg(tree: untpd.NamedArg, pt: Type)(implicit ctx: Context) = track("typedNamedArg") { val arg1 = typed(tree.arg, pt) assignType(cpy.NamedArg(tree)(tree.name, arg1), arg1) @@ -1121,15 +1128,13 @@ class Typer extends Namer with TypeAssigner with Applications with Implicits wit def typedBind(tree: untpd.Bind, pt: Type)(implicit ctx: Context): Tree = track("typedBind") { val pt1 = fullyDefinedType(pt, "pattern variable", tree.pos) val body1 = typed(tree.body, pt1) - typr.println(i"typed bind $tree pt = $pt1 bodytpe = ${body1.tpe}") body1 match { - case UnApply(fn, Nil, arg :: Nil) if tree.body.isInstanceOf[untpd.Typed] => - // A typed pattern `x @ (_: T)` with an implicit `ctag: ClassTag[T]` - // was rewritten to `x @ ctag(_)`. - // Rewrite further to `ctag(x @ _)` - assert(fn.symbol.owner == defn.ClassTagClass) + case UnApply(fn, Nil, arg :: Nil) if fn.symbol.owner == defn.ClassTagClass && !body1.tpe.isError => + // A typed pattern `x @ (e: T)` with an implicit `ctag: ClassTag[T]` + // was rewritten to `x @ ctag(e)` by `tryWithClassTag`. + // Rewrite further to `ctag(x @ e)` tpd.cpy.UnApply(body1)(fn, Nil, - typed(untpd.Bind(tree.name, arg).withPos(tree.pos), arg.tpe) :: Nil) + typed(untpd.Bind(tree.name, untpd.TypedSplice(arg)).withPos(tree.pos), arg.tpe) :: Nil) case _ => val sym = newPatternBoundSym(tree.name, body1.tpe, tree.pos) assignType(cpy.Bind(tree)(tree.name, body1), sym) diff --git a/tests/run/i1991.scala b/tests/run/i1991.scala new file mode 100644 index 000000000..cec1dec89 --- /dev/null +++ b/tests/run/i1991.scala @@ -0,0 +1,29 @@ +import scala.reflect.ClassTag + +class A[Foo](implicit tag: ClassTag[Foo]) { + object ExtractFoo { + def unapply(foo: Foo): Boolean = true + } + + def isFoo(x: Any) = x match { + case ExtractFoo() => true + //case foo: Foo => true + case _ => false + } + + def testBind(x: Any) = x match { + case foo0: Foo => + (foo0: Foo) + case foo1 @ (_: Foo) => + (foo1: Foo) + case foo2 @ ExtractFoo() => + (foo2: Foo) + } +} + +object Test { + def main(args: Array[String]): Unit = { + assert((new A[String]).isFoo("foo")) // OK + assert(!(new A[String]).isFoo(42)) // OK in scalac, fails in Dotty + } +} |