From bc1656ad5a88e7567d35b1d50b65fd9f59ad3c49 Mon Sep 17 00:00:00 2001 From: Wojciech Mazur Date: Tue, 29 Jul 2025 22:48:06 +0200 Subject: [PATCH 1/2] Make SIP-62 better-fors a stable feature --- .../tools/dotc/config/SourceVersion.scala | 2 +- .../test/dotty/tools/debug/DebugTests.scala | 1 - .../better-fors.md | 2 +- docs/_docs/reference/preview/overview.md | 3 +-- docs/sidebar.yml | 3 +-- .../pc/tests/tokens/SemanticTokensSuite.scala | 3 --- .../eval-in-for-comprehension.check | 27 ------------------- .../eval-in-for-comprehension.scala | 14 ---------- tests/debug/eval-in-for-comprehension.check | 7 ----- tests/pos/better-fors-given.scala | 2 -- tests/pos/better-fors-i21804.scala | 3 --- tests/run/better-fors-map-elim.scala | 3 --- tests/run/better-fors.scala | 3 --- tests/run/fors.scala | 1 - 14 files changed, 4 insertions(+), 70 deletions(-) rename docs/_docs/reference/{preview => other-new-features}/better-fors.md (95%) delete mode 100644 tests/debug-preview/eval-in-for-comprehension.check delete mode 100644 tests/debug-preview/eval-in-for-comprehension.scala diff --git a/compiler/src/dotty/tools/dotc/config/SourceVersion.scala b/compiler/src/dotty/tools/dotc/config/SourceVersion.scala index 0183ebc7f27a..7ea0ce7aaedd 100644 --- a/compiler/src/dotty/tools/dotc/config/SourceVersion.scala +++ b/compiler/src/dotty/tools/dotc/config/SourceVersion.scala @@ -43,7 +43,7 @@ enum SourceVersion: def enablesClauseInterleaving = isAtLeast(`3.6`) def enablesNewGivens = isAtLeast(`3.6`) def enablesNamedTuples = isAtLeast(`3.7`) - def enablesBetterFors(using Context) = isAtLeast(`3.7`) && isPreviewEnabled + def enablesBetterFors = isAtLeast(`3.8`) def requiresNewSyntax = isAtLeast(future) diff --git a/compiler/test/dotty/tools/debug/DebugTests.scala b/compiler/test/dotty/tools/debug/DebugTests.scala index e8f744286ba4..faeff76adc09 100644 --- a/compiler/test/dotty/tools/debug/DebugTests.scala +++ b/compiler/test/dotty/tools/debug/DebugTests.scala @@ -19,7 +19,6 @@ class DebugTests: CompilationTest.aggregateTests( compileFile("tests/debug-custom-args/eval-explicit-nulls.scala", TestConfiguration.explicitNullsOptions), compileFilesInDir("tests/debug", TestConfiguration.defaultOptions), - compileFilesInDir("tests/debug-preview", TestConfiguration.defaultOptions.and("-preview")) ).checkDebug() object DebugTests extends ParallelTesting: diff --git a/docs/_docs/reference/preview/better-fors.md b/docs/_docs/reference/other-new-features/better-fors.md similarity index 95% rename from docs/_docs/reference/preview/better-fors.md rename to docs/_docs/reference/other-new-features/better-fors.md index d5fd32da9a1e..ae0b82e35ff6 100644 --- a/docs/_docs/reference/preview/better-fors.md +++ b/docs/_docs/reference/other-new-features/better-fors.md @@ -4,7 +4,7 @@ title: "Better fors" nightlyOf: https://docs.scala-lang.org/scala3/reference/preview/better-fors.html --- -Starting in Scala `3.7` under `-preview` mode, the usability of `for`-comprehensions is improved. +Starting in Scala `3.8` or under `-preview` mode using Scala `3.7`, the usability of `for`-comprehensions has improved. The biggest user facing change is the new ability to start `for`-comprehensions with aliases. This means that the following previously invalid code is now valid: diff --git a/docs/_docs/reference/preview/overview.md b/docs/_docs/reference/preview/overview.md index 62fc499487a1..944e47181e15 100644 --- a/docs/_docs/reference/preview/overview.md +++ b/docs/_docs/reference/preview/overview.md @@ -21,5 +21,4 @@ This flag enables the use of all preview language feature in the project. ## List of available preview features -* [`better-fors`](./better-fors.md): Enables new for-comprehension behaviour under SIP-62 under `-source:3.7` or later - +Currently there are no available preview features. diff --git a/docs/sidebar.yml b/docs/sidebar.yml index f0ca5433d649..a4856816f0df 100644 --- a/docs/sidebar.yml +++ b/docs/sidebar.yml @@ -89,6 +89,7 @@ subsection: - page: reference/other-new-features/preview-defs.md - page: reference/other-new-features/binary-literals.md - page: reference/other-new-features/toplevel-definitions.md + - page: reference/other-new-features/better-fors.md - title: Other Changed Features directory: changed-features index: reference/changed-features/changed-features.md @@ -143,8 +144,6 @@ subsection: - title: Preview Features directory: preview index: reference/preview/overview.md - subsection: - - page: reference/preview/better-fors.md - title: Experimental Features directory: experimental index: reference/experimental/overview.md diff --git a/presentation-compiler/test/dotty/tools/pc/tests/tokens/SemanticTokensSuite.scala b/presentation-compiler/test/dotty/tools/pc/tests/tokens/SemanticTokensSuite.scala index 6179f610fed3..1cfe436f1cb1 100644 --- a/presentation-compiler/test/dotty/tools/pc/tests/tokens/SemanticTokensSuite.scala +++ b/presentation-compiler/test/dotty/tools/pc/tests/tokens/SemanticTokensSuite.scala @@ -6,9 +6,6 @@ import java.nio.file.Path import org.junit.Test class SemanticTokensSuite extends BaseSemanticTokensSuite: - // -preview required for `for-comprehension` test - override protected def scalacOptions(classpath: Seq[Path]): Seq[String] = - super.scalacOptions(classpath) ++ Seq("-preview") @Test def `class, object, var, val(readonly), method, type, parameter, String(single-line)` = check( diff --git a/tests/debug-preview/eval-in-for-comprehension.check b/tests/debug-preview/eval-in-for-comprehension.check deleted file mode 100644 index 6e91c891ebdb..000000000000 --- a/tests/debug-preview/eval-in-for-comprehension.check +++ /dev/null @@ -1,27 +0,0 @@ -break Test$ 5 // in main -eval list(0) -result 1 -// TODO can we remove debug line in adapted methods? -break Test$ 5 // in main$$anonfun$adapted$1 -break Test$ 6 // in main$$anonfun$1 -eval list(0) -result 1 -eval x -result 1 -break Test$ 7 // in main$$anonfun$1$$anonfun$1 -eval x + y -result 2 - -break Test$ 11 // in main$$anonfun$2 -eval x -result 1 - -break Test$ 13 // in main -eval list(0) -result 1 -break Test$ 13 // in main$$anonfun$4 - -break Test$ 14 // in main -eval list(0) -result 1 -break Test$ 14 // in main$$anonfun$5 diff --git a/tests/debug-preview/eval-in-for-comprehension.scala b/tests/debug-preview/eval-in-for-comprehension.scala deleted file mode 100644 index 0ea86fbb0302..000000000000 --- a/tests/debug-preview/eval-in-for-comprehension.scala +++ /dev/null @@ -1,14 +0,0 @@ -object Test: - def main(args: Array[String]): Unit = - val list = List(1) - for - x <- list - y <- list - z = x + y - yield x - for - x <- list - if x == 1 - yield x - for x <- list yield x - for x <- list do println(x) \ No newline at end of file diff --git a/tests/debug/eval-in-for-comprehension.check b/tests/debug/eval-in-for-comprehension.check index fb0d62135efb..6e91c891ebdb 100644 --- a/tests/debug/eval-in-for-comprehension.check +++ b/tests/debug/eval-in-for-comprehension.check @@ -8,16 +8,9 @@ eval list(0) result 1 eval x result 1 -break Test$ 6 // in main$$anonfun$1$$anonfun$adapted$1 break Test$ 7 // in main$$anonfun$1$$anonfun$1 eval x + y result 2 -// TODO this line position does not make any sense -break Test$ 6 // in main$$anonfun$1$$anonfun$1 -break Test$ 7 // in main$$anonfun$1$$anonfun$1 -break Test$ 6 // in main$$anonfun$1$$anonfun$2 -break Test$ 6 // in main$$anonfun$1$$anonfun$2 -break Test$ 7 // in main$$anonfun$1$$anonfun$2 break Test$ 11 // in main$$anonfun$2 eval x diff --git a/tests/pos/better-fors-given.scala b/tests/pos/better-fors-given.scala index 6f70c5549469..e4d64bbb30f3 100644 --- a/tests/pos/better-fors-given.scala +++ b/tests/pos/better-fors-given.scala @@ -1,5 +1,3 @@ -//> using options -preview - @main def Test: Unit = for x <- Option(23 -> "abc") diff --git a/tests/pos/better-fors-i21804.scala b/tests/pos/better-fors-i21804.scala index 85ffb87b0296..625cd60f39b8 100644 --- a/tests/pos/better-fors-i21804.scala +++ b/tests/pos/better-fors-i21804.scala @@ -1,6 +1,3 @@ -//> using options -preview -// import scala.language.experimental.betterFors - case class Container[A](val value: A) { def map[B](f: A => B): Container[B] = Container(f(value)) } diff --git a/tests/run/better-fors-map-elim.scala b/tests/run/better-fors-map-elim.scala index 6f4db6573dec..390ad8ce5b50 100644 --- a/tests/run/better-fors-map-elim.scala +++ b/tests/run/better-fors-map-elim.scala @@ -1,6 +1,3 @@ -//> using options -preview -// import scala.language.experimental.betterFors - class myOptionModule(doOnMap: => Unit) { sealed trait MyOption[+A] { def map[B](f: A => B): MyOption[B] = this match { diff --git a/tests/run/better-fors.scala b/tests/run/better-fors.scala index b0912aacd4dc..6b7e74ad9b4f 100644 --- a/tests/run/better-fors.scala +++ b/tests/run/better-fors.scala @@ -1,6 +1,3 @@ -//> using options -preview -// import scala.language.experimental.betterFors - def for1 = for { a = 1 diff --git a/tests/run/fors.scala b/tests/run/fors.scala index 4e802af4c53d..f08b8790cf34 100644 --- a/tests/run/fors.scala +++ b/tests/run/fors.scala @@ -1,4 +1,3 @@ -//> using options -preview //############################################################################ // for-comprehensions (old and new syntax) //############################################################################ From 34e2162e30eefa4784ddb46048ad9057a9137407 Mon Sep 17 00:00:00 2001 From: Wojciech Mazur Date: Wed, 30 Jul 2025 14:56:07 +0200 Subject: [PATCH 2/2] Update expected semanticdc outputs --- .../expect/ForComprehension.expect.scala | 52 ++++++------ tests/semanticdb/metac.expect | 82 +++++++++---------- 2 files changed, 64 insertions(+), 70 deletions(-) diff --git a/tests/semanticdb/expect/ForComprehension.expect.scala b/tests/semanticdb/expect/ForComprehension.expect.scala index 815b7a93518d..864a87c6717c 100644 --- a/tests/semanticdb/expect/ForComprehension.expect.scala +++ b/tests/semanticdb/expect/ForComprehension.expect.scala @@ -3,43 +3,43 @@ package example class ForComprehension/*<-example::ForComprehension#*/ { for { a/*<-local0*/ <- List/*->scala::package.List.*/(1) - b/*<-local1*//*->local1*/ <- List/*->scala::package.List.*/(1) + b/*<-local1*/ <- List/*->scala::package.List.*/(1) if b/*->local1*/ >/*->scala::Int#`>`(+3).*/ 1 - c/*<-local2*//*->local2*/ = a/*->local0*/ +/*->scala::Int#`+`(+4).*/ b/*->local1*/ + c/*<-local2*/ = a/*->local0*/ +/*->scala::Int#`+`(+4).*/ b/*->local1*/ } yield (a/*->local0*/, b/*->local1*/, c/*->local2*/) for { - a/*<-local4*/ <- List/*->scala::package.List.*/(1) - b/*<-local5*/ <- List/*->scala::package.List.*/(a/*->local4*/) + a/*<-local3*/ <- List/*->scala::package.List.*/(1) + b/*<-local4*/ <- List/*->scala::package.List.*/(a/*->local3*/) if ( - a/*->local4*/, - b/*->local5*/ + a/*->local3*/, + b/*->local4*/ ) ==/*->scala::Any#`==`().*/ (1, 2) ( - c/*<-local7*/, - d/*<-local8*/ - ) <- List/*->scala::package.List.*/((a/*->local4*/, b/*->local5*/)) + c/*<-local6*/, + d/*<-local7*/ + ) <- List/*->scala::package.List.*/((a/*->local3*/, b/*->local4*/)) if ( - a/*->local4*/, - b/*->local5*/, - c/*->local7*/, - d/*->local8*/ + a/*->local3*/, + b/*->local4*/, + c/*->local6*/, + d/*->local7*/ ) ==/*->scala::Any#`==`().*/ (1, 2, 3, 4) - e/*<-local9*//*->local9*/ = ( - a/*->local4*/, - b/*->local5*/, - c/*->local7*/, - d/*->local8*/ + e/*<-local8*//*->local8*/ = ( + a/*->local3*/, + b/*->local4*/, + c/*->local6*/, + d/*->local7*/ ) - if e/*->local9*/ ==/*->scala::Any#`==`().*/ (1, 2, 3, 4) - f/*<-local10*/ <- List/*->scala::package.List.*/(e/*->local9*/) + if e/*->local8*/ ==/*->scala::Any#`==`().*/ (1, 2, 3, 4) + f/*<-local9*/ <- List/*->scala::package.List.*/(e/*->local8*/) } yield { ( - a/*->local4*/, - b/*->local5*/, - c/*->local7*/, - d/*->local8*/, - e/*->local9*/, - f/*->local10*/ + a/*->local3*/, + b/*->local4*/, + c/*->local6*/, + d/*->local7*/, + e/*->local8*/, + f/*->local9*/ ) } } diff --git a/tests/semanticdb/metac.expect b/tests/semanticdb/metac.expect index 13f68886dd87..a8ed97afe126 100644 --- a/tests/semanticdb/metac.expect +++ b/tests/semanticdb/metac.expect @@ -1682,9 +1682,9 @@ Schema => SemanticDB v4 Uri => ForComprehension.scala Text => empty Language => Scala -Symbols => 13 entries -Occurrences => 53 entries -Synthetics => 23 entries +Symbols => 12 entries +Occurrences => 51 entries +Synthetics => 22 entries Symbols: example/ForComprehension# => class ForComprehension extends Object { self: ForComprehension => +1 decls } @@ -1692,14 +1692,13 @@ example/ForComprehension#``(). => primary ctor (): ForComprehension local0 => param a: Int local1 => param b: Int local2 => val local c: Int -local3 => param x$1: Tuple2[Int, Int] -local4 => param a: Int -local5 => param b: Int -local6 => param x$1: Tuple2[Int, Int] -local7 => val local c: Int -local8 => val local d: Int -local9 => val local e: Tuple4[Int, Int, Int, Int] -local10 => param f: Tuple4[Int, Int, Int, Int] +local3 => param a: Int +local4 => param b: Int +local5 => param x$1: Tuple2[Int, Int] +local6 => val local c: Int +local7 => val local d: Int +local8 => val local e: Tuple4[Int, Int, Int, Int] +local9 => param f: Tuple4[Int, Int, Int, Int] Occurrences: [0:8..0:15): example <- example/ @@ -1708,60 +1707,55 @@ Occurrences: [4:4..4:5): a <- local0 [4:9..4:13): List -> scala/package.List. [5:4..5:5): b <- local1 -[5:4..5:5): b -> local1 [5:9..5:13): List -> scala/package.List. [6:7..6:8): b -> local1 [6:9..6:10): > -> scala/Int#`>`(+3). [7:4..7:5): c <- local2 -[7:4..7:5): c -> local2 [7:8..7:9): a -> local0 [7:10..7:11): + -> scala/Int#`+`(+4). [7:12..7:13): b -> local1 [8:11..8:12): a -> local0 [8:14..8:15): b -> local1 [8:17..8:18): c -> local2 -[10:4..10:5): a <- local4 +[10:4..10:5): a <- local3 [10:9..10:13): List -> scala/package.List. -[11:4..11:5): b <- local5 +[11:4..11:5): b <- local4 [11:9..11:13): List -> scala/package.List. -[11:14..11:15): a -> local4 -[13:6..13:7): a -> local4 -[14:6..14:7): b -> local5 +[11:14..11:15): a -> local3 +[13:6..13:7): a -> local3 +[14:6..14:7): b -> local4 [15:6..15:8): == -> scala/Any#`==`(). -[17:6..17:7): c <- local7 -[18:6..18:7): d <- local8 +[17:6..17:7): c <- local6 +[18:6..18:7): d <- local7 [19:9..19:13): List -> scala/package.List. -[19:15..19:16): a -> local4 -[19:18..19:19): b -> local5 -[21:6..21:7): a -> local4 -[22:6..22:7): b -> local5 -[23:6..23:7): c -> local7 -[24:6..24:7): d -> local8 +[19:15..19:16): a -> local3 +[19:18..19:19): b -> local4 +[21:6..21:7): a -> local3 +[22:6..22:7): b -> local4 +[23:6..23:7): c -> local6 +[24:6..24:7): d -> local7 [25:6..25:8): == -> scala/Any#`==`(). -[26:4..26:5): e <- local9 -[26:4..26:5): e -> local9 -[27:6..27:7): a -> local4 -[28:6..28:7): b -> local5 -[29:6..29:7): c -> local7 -[30:6..30:7): d -> local8 -[32:7..32:8): e -> local9 +[26:4..26:5): e <- local8 +[26:4..26:5): e -> local8 +[27:6..27:7): a -> local3 +[28:6..28:7): b -> local4 +[29:6..29:7): c -> local6 +[30:6..30:7): d -> local7 +[32:7..32:8): e -> local8 [32:9..32:11): == -> scala/Any#`==`(). -[33:4..33:5): f <- local10 +[33:4..33:5): f <- local9 [33:9..33:13): List -> scala/package.List. -[33:14..33:15): e -> local9 -[36:6..36:7): a -> local4 -[37:6..37:7): b -> local5 -[38:6..38:7): c -> local7 -[39:6..39:7): d -> local8 -[40:6..40:7): e -> local9 -[41:6..41:7): f -> local10 +[33:14..33:15): e -> local8 +[36:6..36:7): a -> local3 +[37:6..37:7): b -> local4 +[38:6..38:7): c -> local6 +[39:6..39:7): d -> local7 +[40:6..40:7): e -> local8 +[41:6..41:7): f -> local9 Synthetics: [4:9..4:13):List => *.apply[Int] [4:9..4:16):List(1) => List.apply[Int](*) -[5:4..7:5):b <- List(1) - if b > 1 - c => Tuple2.apply[Int, Int](*) [5:9..5:13):List => *.apply[Int] [5:9..5:16):List(1) => List.apply[Int](*) [8:10..8:19):(a, b, c) => Tuple3.apply[Int, Int, Int](*)