Skip to content

Tweak unapply check to fail if type param follows explicit term param #23502

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

Open
wants to merge 2 commits into
base: main
Choose a base branch
from
Open
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
28 changes: 13 additions & 15 deletions compiler/src/dotty/tools/dotc/typer/Applications.scala
Original file line number Diff line number Diff line change
Expand Up @@ -1570,28 +1570,26 @@ trait Applications extends Compatibility {
def trySelectUnapply(qual: untpd.Tree)(fallBack: (Tree, TyperState) => Tree): Tree = {
// try first for non-overloaded, then for overloaded occurrences
def tryWithName(name: TermName)(fallBack: (Tree, TyperState) => Tree)(using Context): Tree =
/** Returns `true` if there are type parameters after the last explicit
* (non-implicit) term parameters list.
*/
@tailrec
def hasTrailingTypeParams(paramss: List[List[Symbol]], acc: Boolean = false): Boolean =
paramss match
case Nil => acc
case params :: rest =>
val newAcc =
params match
case param :: _ if param.isType => true
case param :: _ if param.isTerm && !param.isOneOf(GivenOrImplicit) => false
case _ => acc
hasTrailingTypeParams(paramss.tail, newAcc)
// `true` if there are no type parameters following the first explicit term parameter.
def hasOnlyLeadingTypeParams(fun: Symbol): Boolean =
def checkForIt(paramss: List[List[Symbol]], leading: Int): Boolean =
inline def isLeading = leading > 0
paramss match
case params :: paramss =>
params match
case param :: _ if param.isType && !isLeading => false
case param :: _ if param.isTerm && !param.isOneOf(GivenOrImplicit) => checkForIt(paramss, leading - 1)
case _ => checkForIt(paramss, leading)
case nil => true
checkForIt(fun.paramSymss, leading = if fun.is(Extension) then 2 else 1)

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 && hasTrailingTypeParams(result.symbol.paramSymss) then
if result.symbol.exists && !hasOnlyLeadingTypeParams(result.symbol) then
// We don't accept `unapply` or `unapplySeq` methods with type
// parameters after the last explicit term parameter because we
// can't encode them: `UnApply` nodes cannot take type paremeters.
Expand Down
28 changes: 28 additions & 0 deletions tests/pos/i23499.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@


def summonsTest =
given Type[String] = ???
val opt1: Option[Wrapper[String]] = Wrapper.unapply[String] // ok
val opt2 = Wrapper.unapply[String]
opt2.map(_.getValue) // only error when using workaround

def patternMatchTest =
Type[String] match
case Wrapper(v) => v.getValue // was error // was error

type Type[A] = Class[A] // any rhs would work here
object Type:
def apply[A]: Type[A] = ???

trait Wrapper[T]:
def getValue: T = ???
object Wrapper:
def unapply[T](implicit ev: Type[T]): Option[Wrapper[T]] = None

/* Workaround:
@annotation.targetName("unapplyValue")
def unapply[T](ev: Type[T]): Option[Wrapper[T]] = unapply(using ev)

@annotation.targetName("unapplySummon")
def unapply[T](using Type[T]): Option[Wrapper[T]] = ???
*/
Loading