Skip to content

Commit 6f37a29

Browse files
Backport "Consider extension methods in Space isSameUnapply" to LTS (#20716)
Backports #18642 to the LTS branch. PR submitted by the release tooling. [skip ci]
2 parents d93b8cb + 2466f16 commit 6f37a29

File tree

3 files changed

+51
-2
lines changed

3 files changed

+51
-2
lines changed

compiler/src/dotty/tools/dotc/transform/patmat/Space.scala

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -525,10 +525,14 @@ object SpaceEngine {
525525
* We assume that unapply methods are pure, but the same method may
526526
* be called with different prefixes, thus behaving differently.
527527
*/
528-
def isSameUnapply(tp1: TermRef, tp2: TermRef)(using Context): Boolean =
528+
def isSameUnapply(tp1: TermRef, tp2: TermRef)(using Context): Boolean = trace(i"isSameUnapply($tp1, $tp2)") {
529+
def isStable(tp: TermRef) =
530+
!tp.symbol.is(ExtensionMethod) // The "prefix" of an extension method may be, but the receiver isn't, so exclude
531+
&& tp.prefix.isStable
529532
// always assume two TypeTest[S, T].unapply are the same if they are equal in types
530-
(tp1.prefix.isStable && tp2.prefix.isStable || tp1.symbol == defn.TypeTest_unapply)
533+
(isStable(tp1) && isStable(tp2) || tp1.symbol == defn.TypeTest_unapply)
531534
&& tp1 =:= tp2
535+
}
532536

533537
/** Return term parameter types of the extractor `unapp`.
534538
* Parameter types of the case class type `tp`. Adapted from `unapplyPlan` in patternMatcher */

tests/pos/i18601.scala

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
//> using options -Werror
2+
extension (sc: StringContext)
3+
def m: StringContext = sc
4+
def unapply(string: String): Option[String] =
5+
val pattern = sc.parts.head
6+
if string.length == pattern.length then Some(string) else None
7+
8+
class Test:
9+
def parse(x: PartialFunction[String, String]) = x
10+
11+
val pf = parse {
12+
case m"x$s" => s
13+
case m"xx$s" => s // was: unreachable
14+
}
15+
16+
// proof that the second case isn't unreachable (matches "ab")
17+
def t1 = pf.applyOrElse("a", _ => ".") // "a"
18+
def t2 = pf.applyOrElse("ab", _ => ".") // "ab"
19+
def t3 = pf.applyOrElse("abc", _ => ".") // "."

tests/pos/i18601b.scala

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
//> using options -Werror
2+
3+
// like pos/i18601
4+
// but with a dedicated SC class
5+
// that made the false positive redundancy warning go away
6+
7+
extension (sc: StringContext)
8+
def m: SC = SC(sc)
9+
10+
class SC(sc: StringContext):
11+
def unapply(string: String): Option[String] =
12+
val pattern = sc.parts.head
13+
if string.length == pattern.length then Some(string) else None
14+
15+
class Test:
16+
def parse(x: PartialFunction[String, String]) = x
17+
18+
val pf = parse {
19+
case m"x$s" => s
20+
case m"xx$s" => s // was: not unreachable (as a counter-example)
21+
}
22+
23+
// proof that the second case isn't unreachable (matches "ab")
24+
def t1 = pf.applyOrElse("a", _ => ".") // "a"
25+
def t2 = pf.applyOrElse("ab", _ => ".") // "ab"
26+
def t3 = pf.applyOrElse("abc", _ => ".") // "."

0 commit comments

Comments
 (0)