Skip to content

Commit e9fbfd1

Browse files
authored
Merge pull request #15356 from dotty-staging/add-unapply-tparams
Better handling of type parameters in constructor pattern
2 parents 6635929 + c01cc27 commit e9fbfd1

File tree

2 files changed

+64
-15
lines changed

2 files changed

+64
-15
lines changed

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

Lines changed: 28 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1260,28 +1260,41 @@ trait Applications extends Compatibility {
12601260
*/
12611261
def trySelectUnapply(qual: untpd.Tree)(fallBack: (Tree, TyperState) => Tree): Tree = {
12621262
// try first for non-overloaded, then for overloaded ocurrences
1263-
def tryWithName(name: TermName)(fallBack: (Tree, TyperState) => Tree)(using Context): Tree = {
1264-
def tryWithProto(pt: Type)(using Context) = {
1265-
val result = typedExpr(untpd.Select(qual, name), new UnapplyFunProto(pt, this))
1263+
def tryWithName(name: TermName)(fallBack: (Tree, TyperState) => Tree)(using Context): Tree =
1264+
1265+
def tryWithProto(qual: untpd.Tree, targs: List[Tree], pt: Type)(using Context) =
1266+
val proto = UnapplyFunProto(pt, this)
1267+
val unapp = untpd.Select(qual, name)
1268+
val result =
1269+
if targs.isEmpty then typedExpr(unapp, proto)
1270+
else typedExpr(unapp, PolyProto(targs, proto)).appliedToTypeTrees(targs)
12661271
if !result.symbol.exists
12671272
|| result.symbol.name == name
12681273
|| ctx.reporter.hasErrors
12691274
then result
12701275
else notAnExtractor(result)
12711276
// It might be that the result of typedExpr is an `apply` selection or implicit conversion.
12721277
// Reject in this case.
1273-
}
1274-
tryEither {
1275-
tryWithProto(selType)
1276-
} {
1277-
(sel, state) =>
1278-
tryEither {
1279-
tryWithProto(WildcardType)
1280-
} {
1281-
(_, _) => fallBack(sel, state)
1282-
}
1283-
}
1284-
}
1278+
1279+
def tryWithTypeArgs(qual: untpd.Tree, targs: List[Tree])(fallBack: (Tree, TyperState) => Tree): Tree =
1280+
tryEither {
1281+
tryWithProto(qual, targs, selType)
1282+
} {
1283+
(sel, state) =>
1284+
tryEither {
1285+
tryWithProto(qual, targs, WildcardType)
1286+
} {
1287+
(_, _) => fallBack(sel, state)
1288+
}
1289+
}
1290+
1291+
qual match
1292+
case TypeApply(qual1, targs) =>
1293+
tryWithTypeArgs(qual1, targs.mapconserve(typedType(_)))((t, ts) =>
1294+
tryWithTypeArgs(qual, Nil)(fallBack))
1295+
case _ =>
1296+
tryWithTypeArgs(qual, Nil)(fallBack)
1297+
end tryWithName
12851298

12861299
// try first for unapply, then for unapplySeq
12871300
tryWithName(nme.unapply) {

tests/run/unapply-tparam.scala

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
class Foo[T] {
2+
def unapply(x: Int): Option[Int] = Some(4)
3+
}
4+
5+
object Foo {
6+
def unapply[T](x: T): Option[Int] = Some(5)
7+
}
8+
9+
object Bar {
10+
def unapply[T](x: T): Option[Int] = Some(5)
11+
}
12+
13+
class Baz[T] {
14+
def unapply(x: Int): Option[Int] = Some(4)
15+
}
16+
17+
object Baz
18+
19+
20+
object Test extends App {
21+
1 match {
22+
case Foo(x) => assert(x == 5)
23+
}
24+
25+
1 match {
26+
case Foo[Int](x) => assert(x == 5) // Type params on object takes precedence
27+
}
28+
29+
1 match {
30+
case Bar[Int](x) => assert(x == 5)
31+
}
32+
33+
1 match {
34+
case Baz[Int](x) => assert(x == 4) // Otherwise type params are for the class
35+
}
36+
}

0 commit comments

Comments
 (0)