Skip to content

Commit 5b22ab7

Browse files
authored
Merge pull request #1994 from dotty-staging/fix-#1991
Fix #1991: Use classtag where available in unapply
2 parents 63083f1 + 75dcc47 commit 5b22ab7

File tree

3 files changed

+54
-20
lines changed

3 files changed

+54
-20
lines changed

compiler/src/dotty/tools/dotc/typer/Applications.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -916,7 +916,7 @@ trait Applications extends Compatibility { self: Typer with Dynamic =>
916916
val result = assignType(cpy.UnApply(tree)(unapplyFn, unapplyImplicits, unapplyPatterns), ownType)
917917
unapp.println(s"unapply patterns = $unapplyPatterns")
918918
if ((ownType eq selType) || ownType.isError) result
919-
else Typed(result, TypeTree(ownType))
919+
else tryWithClassTag(Typed(result, TypeTree(ownType)), selType)
920920
case tp =>
921921
val unapplyErr = if (tp.isError) unapplyFn else notAnExtractor(unapplyFn)
922922
val typedArgsErr = args mapconserve (typed(_, defn.AnyType))

compiler/src/dotty/tools/dotc/typer/Typer.scala

Lines changed: 24 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -523,18 +523,8 @@ class Typer extends Namer with TypeAssigner with Applications with Implicits wit
523523
def handlePattern: Tree = {
524524
val tpt1 = typedTpt
525525
// special case for an abstract type that comes with a class tag
526-
tpt1.tpe.dealias match {
527-
case tref: TypeRef if !tref.symbol.isClass && !ctx.isAfterTyper =>
528-
inferImplicit(defn.ClassTagType.appliedTo(tref),
529-
EmptyTree, tpt1.pos)(ctx.retractMode(Mode.Pattern)) match {
530-
case SearchSuccess(arg, _, _, _) =>
531-
return typed(untpd.Apply(untpd.TypedSplice(arg), tree.expr), pt)
532-
case _ =>
533-
}
534-
case _ =>
535-
if (!ctx.isAfterTyper) tpt1.tpe.<:<(pt)(ctx.addMode(Mode.GADTflexible))
536-
}
537-
ascription(tpt1, isWildcard = true)
526+
if (!ctx.isAfterTyper) tpt1.tpe.<:<(pt)(ctx.addMode(Mode.GADTflexible))
527+
tryWithClassTag(ascription(tpt1, isWildcard = true), pt)
538528
}
539529
cases(
540530
ifPat = handlePattern,
@@ -543,6 +533,23 @@ class Typer extends Namer with TypeAssigner with Applications with Implicits wit
543533
}
544534
}
545535

536+
/** For a typed tree `e: T`, if `T` is an abstract type for which an implicit class tag `ctag`
537+
* exists, rewrite to `ctag(e)`.
538+
* @pre We are in pattern-matching mode (Mode.Pattern)
539+
*/
540+
def tryWithClassTag(tree: Typed, pt: Type)(implicit ctx: Context) = tree.tpt.tpe.dealias match {
541+
case tref: TypeRef if !tref.symbol.isClass && !ctx.isAfterTyper =>
542+
require(ctx.mode.is(Mode.Pattern))
543+
inferImplicit(defn.ClassTagType.appliedTo(tref),
544+
EmptyTree, tree.tpt.pos)(ctx.retractMode(Mode.Pattern)) match {
545+
case SearchSuccess(clsTag, _, _, _) =>
546+
typed(untpd.Apply(untpd.TypedSplice(clsTag), untpd.TypedSplice(tree.expr)), pt)
547+
case _ =>
548+
tree
549+
}
550+
case _ => tree
551+
}
552+
546553
def typedNamedArg(tree: untpd.NamedArg, pt: Type)(implicit ctx: Context) = track("typedNamedArg") {
547554
val arg1 = typed(tree.arg, pt)
548555
assignType(cpy.NamedArg(tree)(tree.name, arg1), arg1)
@@ -1121,15 +1128,13 @@ class Typer extends Namer with TypeAssigner with Applications with Implicits wit
11211128
def typedBind(tree: untpd.Bind, pt: Type)(implicit ctx: Context): Tree = track("typedBind") {
11221129
val pt1 = fullyDefinedType(pt, "pattern variable", tree.pos)
11231130
val body1 = typed(tree.body, pt1)
1124-
typr.println(i"typed bind $tree pt = $pt1 bodytpe = ${body1.tpe}")
11251131
body1 match {
1126-
case UnApply(fn, Nil, arg :: Nil) if tree.body.isInstanceOf[untpd.Typed] =>
1127-
// A typed pattern `x @ (_: T)` with an implicit `ctag: ClassTag[T]`
1128-
// was rewritten to `x @ ctag(_)`.
1129-
// Rewrite further to `ctag(x @ _)`
1130-
assert(fn.symbol.owner == defn.ClassTagClass)
1132+
case UnApply(fn, Nil, arg :: Nil) if fn.symbol.owner == defn.ClassTagClass && !body1.tpe.isError =>
1133+
// A typed pattern `x @ (e: T)` with an implicit `ctag: ClassTag[T]`
1134+
// was rewritten to `x @ ctag(e)` by `tryWithClassTag`.
1135+
// Rewrite further to `ctag(x @ e)`
11311136
tpd.cpy.UnApply(body1)(fn, Nil,
1132-
typed(untpd.Bind(tree.name, arg).withPos(tree.pos), arg.tpe) :: Nil)
1137+
typed(untpd.Bind(tree.name, untpd.TypedSplice(arg)).withPos(tree.pos), arg.tpe) :: Nil)
11331138
case _ =>
11341139
val sym = newPatternBoundSym(tree.name, body1.tpe, tree.pos)
11351140
assignType(cpy.Bind(tree)(tree.name, body1), sym)

tests/run/i1991.scala

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
import scala.reflect.ClassTag
2+
3+
class A[Foo](implicit tag: ClassTag[Foo]) {
4+
object ExtractFoo {
5+
def unapply(foo: Foo): Boolean = true
6+
}
7+
8+
def isFoo(x: Any) = x match {
9+
case ExtractFoo() => true
10+
//case foo: Foo => true
11+
case _ => false
12+
}
13+
14+
def testBind(x: Any) = x match {
15+
case foo0: Foo =>
16+
(foo0: Foo)
17+
case foo1 @ (_: Foo) =>
18+
(foo1: Foo)
19+
case foo2 @ ExtractFoo() =>
20+
(foo2: Foo)
21+
}
22+
}
23+
24+
object Test {
25+
def main(args: Array[String]): Unit = {
26+
assert((new A[String]).isFoo("foo")) // OK
27+
assert(!(new A[String]).isFoo(42)) // OK in scalac, fails in Dotty
28+
}
29+
}

0 commit comments

Comments
 (0)