Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Better handling of type parameters in constructor pattern #15356

Merged
merged 1 commit into from
Jun 14, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
43 changes: 28 additions & 15 deletions compiler/src/dotty/tools/dotc/typer/Applications.scala
Original file line number Diff line number Diff line change
Expand Up @@ -1260,28 +1260,41 @@ trait Applications extends Compatibility {
*/
def trySelectUnapply(qual: untpd.Tree)(fallBack: (Tree, TyperState) => Tree): Tree = {
// try first for non-overloaded, then for overloaded ocurrences
def tryWithName(name: TermName)(fallBack: (Tree, TyperState) => Tree)(using Context): Tree = {
def tryWithProto(pt: Type)(using Context) = {
val result = typedExpr(untpd.Select(qual, name), new UnapplyFunProto(pt, this))
def tryWithName(name: TermName)(fallBack: (Tree, TyperState) => Tree)(using Context): Tree =

def tryWithProto(qual: untpd.Tree, targs: List[Tree], pt: Type)(using Context) =
val proto = UnapplyFunProto(pt, this)
val unapp = untpd.Select(qual, name)
val result =
if targs.isEmpty then typedExpr(unapp, proto)
else typedExpr(unapp, PolyProto(targs, proto)).appliedToTypeTrees(targs)
if !result.symbol.exists
|| result.symbol.name == name
|| ctx.reporter.hasErrors
then result
else notAnExtractor(result)
// It might be that the result of typedExpr is an `apply` selection or implicit conversion.
// Reject in this case.
}
tryEither {
tryWithProto(selType)
} {
(sel, state) =>
tryEither {
tryWithProto(WildcardType)
} {
(_, _) => fallBack(sel, state)
}
}
}

def tryWithTypeArgs(qual: untpd.Tree, targs: List[Tree])(fallBack: (Tree, TyperState) => Tree): Tree =
tryEither {
tryWithProto(qual, targs, selType)
} {
(sel, state) =>
tryEither {
tryWithProto(qual, targs, WildcardType)
} {
(_, _) => fallBack(sel, state)
}
}

qual match
case TypeApply(qual1, targs) =>
tryWithTypeArgs(qual1, targs.mapconserve(typedType(_)))((t, ts) =>
tryWithTypeArgs(qual, Nil)(fallBack))
case _ =>
tryWithTypeArgs(qual, Nil)(fallBack)
end tryWithName

// try first for unapply, then for unapplySeq
tryWithName(nme.unapply) {
Expand Down
36 changes: 36 additions & 0 deletions tests/run/unapply-tparam.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
class Foo[T] {
def unapply(x: Int): Option[Int] = Some(4)
}

object Foo {
def unapply[T](x: T): Option[Int] = Some(5)
}

object Bar {
def unapply[T](x: T): Option[Int] = Some(5)
}

class Baz[T] {
def unapply(x: Int): Option[Int] = Some(4)
}

object Baz


object Test extends App {
1 match {
case Foo(x) => assert(x == 5)
}

1 match {
case Foo[Int](x) => assert(x == 5) // Type params on object takes precedence
}

1 match {
case Bar[Int](x) => assert(x == 5)
}

1 match {
case Baz[Int](x) => assert(x == 4) // Otherwise type params are for the class
}
}