Skip to content

Commit

Permalink
Merge pull request scala#8816 from dotty-staging/fix-8730-v2
Browse files Browse the repository at this point in the history
Fix scala#8730: disallow structural unapply
  • Loading branch information
smarter authored Apr 28, 2020
2 parents a3a3cf6 + 50a48dc commit 8fed152
Show file tree
Hide file tree
Showing 4 changed files with 37 additions and 2 deletions.
2 changes: 1 addition & 1 deletion compiler/src/dotty/tools/dotc/reporting/messages.scala
Original file line number Diff line number Diff line change
Expand Up @@ -1806,7 +1806,7 @@ object messages {
|""".stripMargin
}

class UnapplyInvalidReturnType(unapplyResult: Type, unapplyName: Symbol#ThisName)(implicit ctx: Context)
class UnapplyInvalidReturnType(unapplyResult: Type, unapplyName: Name)(implicit ctx: Context)
extends DeclarationMsg(UnapplyInvalidReturnTypeID) {
def msg =
val addendum =
Expand Down
8 changes: 7 additions & 1 deletion compiler/src/dotty/tools/dotc/typer/Applications.scala
Original file line number Diff line number Diff line change
Expand Up @@ -150,7 +150,10 @@ object Applications {

def unapplyArgs(unapplyResult: Type, unapplyFn: Tree, args: List[untpd.Tree], pos: SourcePosition)(using Context): List[Type] = {

val unapplyName = unapplyFn.symbol.name
val unapplyName = unapplyFn match // tolerate structural `unapply`, which does not have a symbol
case TypeApply(fn: RefTree, _) => fn.name
case fn: RefTree => fn.name

def getTp = extractorMemberType(unapplyResult, nme.get, pos)

def fail = {
Expand Down Expand Up @@ -1227,6 +1230,9 @@ trait Applications extends Compatibility {
case Apply(Apply(unapply, `dummyArg` :: Nil), args2) => assert(args2.nonEmpty); args2
case Apply(unapply, `dummyArg` :: Nil) => Nil
case Inlined(u, _, _) => unapplyImplicits(u)
case DynamicUnapply(_) =>
ctx.error("Structural unapply is not supported", unapplyFn.sourcePos)
Nil
case _ => Nil.assertingErrorsReported
}

Expand Down
11 changes: 11 additions & 0 deletions compiler/src/dotty/tools/dotc/typer/Dynamic.scala
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,17 @@ object Dynamic {
name == nme.applyDynamic || name == nme.selectDynamic || name == nme.updateDynamic || name == nme.applyDynamicNamed
}

object DynamicUnapply {
def unapply(tree: tpd.Tree): Option[List[tpd.Tree]] = tree match
case TypeApply(Select(qual, name), _) if name == nme.asInstanceOfPM =>
unapply(qual)
case Apply(Apply(Select(selectable, fname), Literal(Constant(name)) :: ctag :: Nil), _ :: implicits)
if fname == nme.applyDynamic && (name == "unapply" || name == "unapplySeq") =>
Some(selectable :: ctag :: implicits)
case _ =>
None
}

/** Handles programmable member selections of `Dynamic` instances and values
* with structural types. Two functionalities:
*
Expand Down
18 changes: 18 additions & 0 deletions tests/neg/i8730.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import reflect.Selectable.reflectiveSelectable

class Nat(val x: Int) {
def get: Int = x
def isEmpty = x < 0
}

val SomeExtractorBuilder: { def unapply(x: Int): Nat } = new {
def unapply(x: Int): Nat = new Nat(x)
}


@main
def Test = 5 match {
case SomeExtractorBuilder(n) => println(s"$n is a natural number") // error
case _ => ()
}

0 comments on commit 8fed152

Please sign in to comment.