Skip to content

Commit d70de9a

Browse files
committed
Put extensions under language.experimental.relaxedLambdaSyntax
1 parent ba0ec2a commit d70de9a

File tree

10 files changed

+49
-20
lines changed

10 files changed

+49
-20
lines changed

compiler/src/dotty/tools/dotc/config/Feature.scala

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@ object Feature:
4040
val packageObjectValues = experimental("packageObjectValues")
4141
val multiSpreads = experimental("multiSpreads")
4242
val subCases = experimental("subCases")
43+
val relaxedLambdaSyntax = experimental("relaxedLambdaSyntax")
4344

4445
def experimentalAutoEnableFeatures(using Context): List[TermName] =
4546
defn.languageExperimentalFeatures

compiler/src/dotty/tools/dotc/parsing/Parsers.scala

Lines changed: 23 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1091,18 +1091,20 @@ object Parsers {
10911091

10921092
/** Is the token sequence following the current `:` token classified as a lambda?
10931093
* If yes return a defined parsing function to parse the lambda body, if not
1094-
* return None. The case is triggered in two :if the input starts with an identifier,
1095-
* a wildcard, or something enclosed in (...) or [...], this is followed by a
1096-
* `=>` or `?=>`, one one of the following two cases applies:
1097-
* 1. The next token is an indent. In this case the return parsing function parses
1098-
* an Expr in location Location.InColonArg.
1099-
* 2. The next token is on the same line and the enclosing region is not `(...)`.
1100-
* In this case the parsing function parses an Expr in location Location.InColonArg
1101-
* enclosed in a SingleLineLambda region, and then eats the ENDlambda token
1102-
* generated by the Scanner at the end of that region.
1103-
* The reason for excluding (2) in regions enclosed in parentheses is to avoid
1104-
* an ambiguity with type ascription `(x: A => B)`, where function types are only
1105-
* allowed inside parentheses.
1094+
* return None. The case is triggered in two situations:
1095+
* 1. If the input starts with an identifier, a wildcard, or something
1096+
* enclosed in (...) or [...], this is followed by a `=>` or `?=>`,
1097+
* and one of the following two subcases applies:
1098+
* 1a. The next token is an indent. In this case the return parsing function parses
1099+
* an Expr in location Location.InColonArg.
1100+
* 1b. Under relaxedLambdaSyntax: the next token is on the same line and the enclosing region is not `(...)`.
1101+
* In this case the parsing function parses an Expr in location Location.InColonArg
1102+
* enclosed in a SingleLineLambda region, and then eats the ENDlambda token
1103+
* generated by the Scanner at the end of that region.
1104+
* The reason for excluding (1b) in regions enclosed in parentheses is to avoid
1105+
* an ambiguity with type ascription `(x: A => B)`, where function types are only
1106+
* allowed inside parentheses.
1107+
* 2. Under relaxedLambdaSyntax: the input starts with a `case`.
11061108
*/
11071109
def followingIsLambdaAfterColon(): Option[() => Tree] =
11081110
val lookahead = in.LookaheadScanner(allowIndent = true)
@@ -1112,7 +1114,9 @@ object Parsers {
11121114
lookahead.observeArrowIndented()
11131115
if lookahead.token == INDENT || lookahead.token == EOF then
11141116
Some(() => expr(Location.InColonArg))
1115-
else if !in.currentRegion.isInstanceOf[InParens] then
1117+
else if in.featureEnabled(Feature.relaxedLambdaSyntax)
1118+
&& !in.currentRegion.isInstanceOf[InParens]
1119+
then
11161120
Some: () =>
11171121
val t = inSepRegion(SingleLineLambda(_)):
11181122
expr(Location.InColonArg)
@@ -1127,7 +1131,7 @@ object Parsers {
11271131
else if lookahead.token == LPAREN || lookahead.token == LBRACKET then
11281132
lookahead.skipParens()
11291133
isArrowIndent()
1130-
else if lookahead.token == CASE then
1134+
else if lookahead.token == CASE && in.featureEnabled(Feature.relaxedLambdaSyntax) then
11311135
Some(() => singleCaseMatch())
11321136
else
11331137
None
@@ -1198,9 +1202,11 @@ object Parsers {
11981202
/** Optionally, if we are seeing a lambda argument after a colon of the form
11991203
* : (params) =>
12001204
* body
1201-
* or a single-line lambda
1205+
* or a single-line lambda (under relaxedLambdaSyntax)
12021206
* : (params) => body
1203-
* then return the function used to parse `body`.
1207+
* or a case clause (under relaxedLambdaSyntax)
1208+
* : case pat guard => rhs
1209+
* then return the function used to parse `body` or the case clause.
12041210
*/
12051211
def detectColonLambda: Option[() => Tree] =
12061212
if sourceVersion.enablesFewerBraces && in.token == COLONfollow
@@ -2437,7 +2443,7 @@ object Parsers {
24372443
val arrowOffset = accept(ARROW)
24382444
val body = expr(location)
24392445
makePolyFunction(tparams, body, "literal", errorTermTree(arrowOffset), start, arrowOffset)
2440-
case CASE =>
2446+
case CASE if in.featureEnabled(Feature.relaxedLambdaSyntax) =>
24412447
singleCaseMatch()
24422448
case _ =>
24432449
val saved = placeholderParams

library/src/scala/language.scala

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -367,6 +367,11 @@ object language {
367367
*/
368368
@compileTimeOnly("`subCases` can only be used at compile time in import statements")
369369
object subCases
370+
371+
/** Experimental support for single-line lambdas and case clause expressions after `:`
372+
*/
373+
@compileTimeOnly("`relaxedLambdaSyntax` can only be used at compile time in import statements")
374+
object relaxedLambdaSyntax
370375
}
371376

372377
/** The deprecated object contains features that are no longer officially suypported in Scala.

library/src/scala/runtime/stdLibPatches/language.scala

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -174,6 +174,11 @@ object language:
174174
*/
175175
@compileTimeOnly("`subCases` can only be used at compile time in import statements")
176176
object subCases
177+
178+
/** Experimental support for single-line lambdas and case clause expressions after `:`
179+
*/
180+
@compileTimeOnly("`relaxedLambdaSyntax` can only be used at compile time in import statements")
181+
object relaxedLambdaSyntax
177182
end experimental
178183

179184
/** The deprecated object contains features that are no longer officially suypported in Scala.

tests/neg/closure-args.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
1+
import language.experimental.relaxedLambdaSyntax
22
val x = List(1).map: (x: => Int) => // error
33
???
44
val z = List(1).map: + => // ok

tests/neg/i22193.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
1+
import language.experimental.relaxedLambdaSyntax
22
def fn2(arg: String, arg2: String)(f: String => Unit): Unit = f(arg)
33

44
def fn3(arg: String, arg2: String)(f: => Unit): Unit = f

tests/neg/i22906.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
//> using options -rewrite -indent
22
//> nominally using scala 3.7.0-RC1
33
// does not reproduce under "vulpix" test rig, which enforces certain flag sets?
4-
4+
import language.experimental.relaxedLambdaSyntax
55
def program: Int => Int =
66
{`1`: Int => 5} // error // error

tests/pos/change-lambda.scala

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
import language.experimental.relaxedLambdaSyntax
2+
13
def foo(x: Any) = ???
24

35
def test(xs: List[Int]) =

tests/pos/closure-args.scala

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import language.experimental.relaxedLambdaSyntax
12

23
val z = List(1).map: + => // ok
34
???

tests/run/single-case-expr.scala

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
import language.experimental.relaxedLambdaSyntax
2+
13
case class Foo(x: Int, y: Int)
24
@main def Test =
35
val f: List[Int] => Int = case y :: ys => y
@@ -25,6 +27,13 @@ case class Foo(x: Int, y: Int)
2527
val a3 = Seq((1, 2), (3, 4)).collect: case (a, b) if b > 2 => a
2628
assert(a3 == Seq(3))
2729

30+
val a4 = Seq((1, 2), (3, 4)).collect: case (a, b) if b > 2 =>
31+
a
32+
2833
val partial: PartialFunction[(Int, Int), Int] = case (a, b) if b > 2 => a
2934

35+
val mtup = (1, true).map: [T] => (x: T) => List(x)
36+
val _: (List[Int], List[Boolean]) = mtup
37+
38+
3039

0 commit comments

Comments
 (0)