Skip to content

Commit

Permalink
Backport "Consider extension methods in Space isSameUnapply" to LTS (#…
Browse files Browse the repository at this point in the history
…20716)

Backports #18642 to the LTS branch.

PR submitted by the release tooling.
[skip ci]
  • Loading branch information
WojciechMazur committed Jun 22, 2024
2 parents d93b8cb + 2466f16 commit 6f37a29
Show file tree
Hide file tree
Showing 3 changed files with 51 additions and 2 deletions.
8 changes: 6 additions & 2 deletions compiler/src/dotty/tools/dotc/transform/patmat/Space.scala
Original file line number Diff line number Diff line change
Expand Up @@ -525,10 +525,14 @@ object SpaceEngine {
* We assume that unapply methods are pure, but the same method may
* be called with different prefixes, thus behaving differently.
*/
def isSameUnapply(tp1: TermRef, tp2: TermRef)(using Context): Boolean =
def isSameUnapply(tp1: TermRef, tp2: TermRef)(using Context): Boolean = trace(i"isSameUnapply($tp1, $tp2)") {
def isStable(tp: TermRef) =
!tp.symbol.is(ExtensionMethod) // The "prefix" of an extension method may be, but the receiver isn't, so exclude
&& tp.prefix.isStable
// always assume two TypeTest[S, T].unapply are the same if they are equal in types
(tp1.prefix.isStable && tp2.prefix.isStable || tp1.symbol == defn.TypeTest_unapply)
(isStable(tp1) && isStable(tp2) || tp1.symbol == defn.TypeTest_unapply)
&& tp1 =:= tp2
}

/** Return term parameter types of the extractor `unapp`.
* Parameter types of the case class type `tp`. Adapted from `unapplyPlan` in patternMatcher */
Expand Down
19 changes: 19 additions & 0 deletions tests/pos/i18601.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
//> using options -Werror
extension (sc: StringContext)
def m: StringContext = sc
def unapply(string: String): Option[String] =
val pattern = sc.parts.head
if string.length == pattern.length then Some(string) else None

class Test:
def parse(x: PartialFunction[String, String]) = x

val pf = parse {
case m"x$s" => s
case m"xx$s" => s // was: unreachable
}

// proof that the second case isn't unreachable (matches "ab")
def t1 = pf.applyOrElse("a", _ => ".") // "a"
def t2 = pf.applyOrElse("ab", _ => ".") // "ab"
def t3 = pf.applyOrElse("abc", _ => ".") // "."
26 changes: 26 additions & 0 deletions tests/pos/i18601b.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
//> using options -Werror

// like pos/i18601
// but with a dedicated SC class
// that made the false positive redundancy warning go away

extension (sc: StringContext)
def m: SC = SC(sc)

class SC(sc: StringContext):
def unapply(string: String): Option[String] =
val pattern = sc.parts.head
if string.length == pattern.length then Some(string) else None

class Test:
def parse(x: PartialFunction[String, String]) = x

val pf = parse {
case m"x$s" => s
case m"xx$s" => s // was: not unreachable (as a counter-example)
}

// proof that the second case isn't unreachable (matches "ab")
def t1 = pf.applyOrElse("a", _ => ".") // "a"
def t2 = pf.applyOrElse("ab", _ => ".") // "ab"
def t3 = pf.applyOrElse("abc", _ => ".") // "."

0 comments on commit 6f37a29

Please sign in to comment.