Skip to content

Commit 8064536

Browse files
Fix exception on sequence matching with drop (#21281)
When using scalac with the flag Ysafe-init-global to compile the following example, I get the following exception. This PR fixes it and adds a test case to catch things like this in the future. Note, I made this pull request in collaboration with Ondrej Lhotak, Enze Xing, Fengyun Liu and David Hua) The example ```Scala object Matcher { val vararg_arr = Array(0, 1, 2, 3) val vararg_lst = List(vararg_arr*) val vararg_splice = vararg_lst match case List(0, 1, xs*) => 1 // binds xs to Seq(2, 3) case List(1, _*) => 0 // wildcard pattern case _ => 2 println(vararg_splice) } ``` The exception ``` Exception in thread "main" java.lang.AssertionError: NoDenotation.owner at dotty.tools.dotc.core.SymDenotations$NoDenotation$.owner(SymDenotations.scala:2623) at dotty.tools.dotc.transform.init.Objects.call(Objects.scala:660) at dotty.tools.dotc.transform.init.Objects.evalSeqPatterns$1(Objects.scala:1494) at dotty.tools.dotc.transform.init.Objects.evalPattern$1(Objects.scala:1406) at dotty.tools.dotc.transform.init.Objects.evalCase$1(Objects.scala:1336) at dotty.tools.dotc.transform.init.Objects.patternMatch$$anonfun$1(Objects.scala:1505) at scala.collection.immutable.List.map(List.scala:247) at dotty.tools.dotc.transform.init.Objects.patternMatch(Objects.scala:1505) at dotty.tools.dotc.transform.init.Objects.cases(Objects.scala:1255) at dotty.tools.dotc.transform.init.Objects.eval$$anonfun$1(Objects.scala:1095) at dotty.tools.dotc.transform.init.Objects$Cache$Data.$anonfun$5(Objects.scala:539) at dotty.tools.dotc.transform.init.Cache.cachedEval(Cache.scala:112) at dotty.tools.dotc.transform.init.Objects$Cache$Data.cachedEval(Objects.scala:538) at dotty.tools.dotc.transform.init.Objects.eval(Objects.scala:1095) at dotty.tools.dotc.transform.init.Objects.init$$anonfun$4(Objects.scala:1708) at scala.collection.immutable.List.foreach(List.scala:334) at dotty.tools.dotc.transform.init.Objects.init(Objects.scala:1705) at dotty.tools.dotc.transform.init.Objects$State$.iterate$1(Objects.scala:278) at dotty.tools.dotc.transform.init.Objects$State$.doCheckObject(Objects.scala:293) at dotty.tools.dotc.transform.init.Objects$State$.checkObjectAccess(Objects.scala:320) at dotty.tools.dotc.transform.init.Objects.accessObject(Objects.scala:1059) at dotty.tools.dotc.transform.init.Objects.checkClasses$$anonfun$2(Objects.scala:1072) at scala.collection.IterableOnceOps.foreach(IterableOnce.scala:619) at scala.collection.IterableOnceOps.foreach$(IterableOnce.scala:617) at scala.collection.AbstractIterable.foreach(Iterable.scala:935) at scala.collection.IterableOps$WithFilter.foreach(Iterable.scala:905) at dotty.tools.dotc.transform.init.Objects.checkClasses(Objects.scala:1070) at dotty.tools.dotc.transform.init.Checker.runOn$$anonfun$1(Checker.scala:58) at scala.runtime.function.JProcedure1.apply(JProcedure1.java:15) at scala.runtime.function.JProcedure1.apply(JProcedure1.java:10) at dotty.tools.dotc.core.Phases$Phase.cancellable(Phases.scala:521) at dotty.tools.dotc.transform.init.Checker.runOn(Checker.scala:59) at dotty.tools.dotc.Run.runPhases$1$$anonfun$1(Run.scala:343) at scala.runtime.function.JProcedure1.apply(JProcedure1.java:15) at scala.runtime.function.JProcedure1.apply(JProcedure1.java:10) at scala.collection.ArrayOps$.foreach$extension(ArrayOps.scala:1323) at dotty.tools.dotc.Run.runPhases$1(Run.scala:336) at dotty.tools.dotc.Run.compileUnits$$anonfun$1(Run.scala:384) at dotty.tools.dotc.Run.compileUnits$$anonfun$adapted$1(Run.scala:396) at dotty.tools.dotc.util.Stats$.maybeMonitored(Stats.scala:69) at dotty.tools.dotc.Run.compileUnits(Run.scala:396) at dotty.tools.dotc.Run.compileSources(Run.scala:282) at dotty.tools.dotc.Run.compile(Run.scala:267) at dotty.tools.dotc.Driver.doCompile(Driver.scala:37) at dotty.tools.dotc.Driver.process(Driver.scala:201) at dotty.tools.dotc.Driver.process(Driver.scala:169) at dotty.tools.dotc.Driver.process(Driver.scala:181) at dotty.tools.dotc.Driver.main(Driver.scala:211) at dotty.tools.dotc.Main.main(Main.scala) exception occurred while compiling List(../test-case.scala) An unhandled exception was thrown in the compiler. Please file a crash report here: https://github.com/scala/scala3/issues/new/choose For non-enriched exceptions, compile with -Xno-enrich-error-messages. while compiling: <no file> during phase: parser mode: Mode(ImplicitsEnabled,ReadPositions) library version: version 2.13.14 compiler version: version 3.6.0-RC1-bin-SNAPSHOT-nonbootstrapped-git-3dfd762 settings: -Ysafe-init-global true -classpath /home/kavin/.cache/coursier/v1/https/repo1.maven.org/maven2/org/scala-lang/scala-library/2.13.14/scala-library-2.13.14.jar:/home/kavin/Documents/4A/URA2/scala3/library/../out/bootstrap/scala3-library-bootstrapped/scala-3.6.0-RC1-bin-SNAPSHOT-nonbootstrapped/scala3-library_3-3.6.0-RC1-bin-SNAPSHOT.jar -d /home/kavin/Documents/4A/URA2/scala3/compiler/../out/default-last-scalac-out.jar [error] Nonzero exit code returned from runner: 1 [error] (scala3-compiler / Compile / runMain) Nonzero exit code returned from runner: 1 [error] Total time: 17 s, completed Jul 26, 2024, 6:35:43 PM ```
1 parent 8e1e229 commit 8064536

File tree

2 files changed

+120
-2
lines changed

2 files changed

+120
-2
lines changed

Diff for: compiler/src/dotty/tools/dotc/transform/init/Objects.scala

+2-2
Original file line numberDiff line numberDiff line change
@@ -1485,12 +1485,12 @@ class Objects(using Context @constructorOnly):
14851485
if isWildcardStarArgList(pats) then
14861486
if pats.size == 1 then
14871487
// call .toSeq
1488-
val toSeqDenot = scrutineeType.member(nme.toSeq).suchThat(_.info.isParameterless)
1488+
val toSeqDenot = getMemberMethod(scrutineeType, nme.toSeq, toSeqType(elemType))
14891489
val toSeqRes = call(scrutinee, toSeqDenot.symbol, Nil, scrutineeType, superType = NoType, needResolve = true)
14901490
evalPattern(toSeqRes, pats.head)
14911491
else
14921492
// call .drop
1493-
val dropDenot = getMemberMethod(scrutineeType, nme.drop, applyType(elemType))
1493+
val dropDenot = getMemberMethod(scrutineeType, nme.drop, dropType(elemType))
14941494
val dropRes = call(scrutinee, dropDenot.symbol, ArgInfo(Bottom, summon[Trace], EmptyTree) :: Nil, scrutineeType, superType = NoType, needResolve = true)
14951495
for pat <- pats.init do evalPattern(applyRes, pat)
14961496
evalPattern(dropRes, pats.last)

Diff for: tests/init-global/pos/match-complete.scala

+118
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,118 @@
1+
object Matcher {
2+
// Chained Match
3+
val chained_match_xs: List[Any] = List(1, 2, 3)
4+
val chained_match_x = chained_match_xs match {
5+
case Nil => "empty"
6+
case _ => "nonempty"
7+
} match {
8+
case "empty" => 0
9+
case "nonempty" => 1
10+
}
11+
println(chained_match_x)
12+
13+
// Vararg Splices
14+
val vararg_arr = Array(0, 1, 2, 3)
15+
val vararg_lst = List(vararg_arr*) // vararg splice argument
16+
// Throws an exception?
17+
val vararg_splice = vararg_lst match
18+
case List(0, 1, xs*) => 1 // binds xs to Seq(2, 3)
19+
case List(1, _*) => 0 // wildcard pattern
20+
case _ => 2
21+
println(vararg_splice)
22+
println(vararg_lst)
23+
24+
// Pattern Definitions
25+
val patter_def_xs: List[Any] = List(1, 2, 3)
26+
val (patter_def_x: Any) :: _ = patter_def_xs : @unchecked
27+
println(patter_def_x)
28+
29+
val patter_def_pair = (1, true)
30+
val (patter_def_a, patter_def_b) = patter_def_pair
31+
println(patter_def_a)
32+
33+
val elems: List[(Int, Int)] = List((1, 2), (3, 4), (5, 6))
34+
35+
for ((x,y) <- elems) do println(x)
36+
37+
def main(args: Array[String]) = {
38+
// println(chained_match_x)
39+
println(vararg_splice)
40+
// println(patter_def_x)
41+
// println(
42+
}
43+
}
44+
45+
46+
// Patter Matching Using Extractors
47+
48+
// Option Extractors
49+
case class Person(name: String, age: Int)
50+
object Person {
51+
def unapply(person: Person): Option[(String, Int)] = Some((person.name, person.age))
52+
}
53+
54+
object OptionMatcher {
55+
val person = Person("Alice", 25)
56+
57+
val result = person match {
58+
case Person(name, age) => s"Name: $name, Age: $age"
59+
case _ => "Not a person"
60+
}
61+
println(result)
62+
}
63+
64+
65+
66+
// Boolean Extractors
67+
object Adult {
68+
def unapply(person: Person): Boolean = person.age >= 18
69+
}
70+
71+
object BooleanMatcher {
72+
val person = Person("Charlie", 17)
73+
74+
val adultResult = person match {
75+
case Adult() => s"${person.name} is an adult"
76+
case _ => s"${person.name} is not an adult"
77+
}
78+
79+
println(adultResult)
80+
}
81+
82+
83+
84+
// Variadic Extractors
85+
// Add cases for exceptions
86+
//
87+
// Adding some warning test cases
88+
// -
89+
90+
object VariadicExtractor {
91+
// Define an unapply method that takes a List and returns an Option of Seq
92+
def unapplySeq[A](list: List[A]): Option[Seq[A]] = Some(list)
93+
}
94+
95+
object PatternMatchExample extends App {
96+
def describeList(list: List[Int]): String = list match {
97+
case VariadicExtractor(1, 2, rest @ _*) =>
98+
s"Starts with 1, 2 followed by: ${rest.mkString(", ")}"
99+
case VariadicExtractor(1, rest @ _*) =>
100+
s"Starts with 1 followed by: ${rest.mkString(", ")}"
101+
case VariadicExtractor(first, second, rest @ _*) =>
102+
s"Starts with $first, $second followed by: ${rest.mkString(", ")}"
103+
case VariadicExtractor(single) =>
104+
s"Only one element: $single"
105+
case VariadicExtractor() =>
106+
"Empty list"
107+
case _ =>
108+
"Unknown pattern"
109+
}
110+
111+
// Test cases
112+
println(describeList(List(1, 2, 3, 4, 5))) // Output: Starts with 1, 2 followed by: 3, 4, 5
113+
println(describeList(List(1, 3, 4, 5))) // Output: Starts with 1 followed by: 3, 4, 5
114+
println(describeList(List(2, 3, 4, 5))) // Output: Starts with 2, 3 followed by: 4, 5
115+
println(describeList(List(1))) // Output: Only one element: 1
116+
println(describeList(List())) // Output: Empty list
117+
}
118+

0 commit comments

Comments
 (0)