Skip to content

Commit 07611bf

Browse files
committed
Put extensions under language.experimental.relaxedLambdaSyntax
1 parent 8665a3c commit 07611bf

File tree

10 files changed

+41
-20
lines changed

10 files changed

+41
-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
@@ -38,6 +38,7 @@ object Feature:
3838
val quotedPatternsWithPolymorphicFunctions = experimental("quotedPatternsWithPolymorphicFunctions")
3939
val packageObjectValues = experimental("packageObjectValues")
4040
val subCases = experimental("subCases")
41+
val relaxedLambdaSyntax = experimental("relaxedLambdaSyntax")
4142

4243
def experimentalAutoEnableFeatures(using Context): List[TermName] =
4344
defn.languageExperimentalFeatures

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

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

10871087
/** Is the token sequence following the current `:` token classified as a lambda?
10881088
* If yes return a defined parsing function to parse the lambda body, if not
1089-
* return None. The case is triggered in two :if the input starts with an identifier,
1090-
* a wildcard, or something enclosed in (...) or [...], this is followed by a
1091-
* `=>` or `?=>`, one one of the following two cases applies:
1092-
* 1. The next token is an indent. In this case the return parsing function parses
1093-
* an Expr in location Location.InColonArg.
1094-
* 2. The next token is on the same line and the enclosing region is not `(...)`.
1095-
* In this case the parsing function parses an Expr in location Location.InColonArg
1096-
* enclosed in a SingleLineLambda region, and then eats the ENDlambda token
1097-
* generated by the Scanner at the end of that region.
1098-
* The reason for excluding (2) in regions enclosed in parentheses is to avoid
1099-
* an ambiguity with type ascription `(x: A => B)`, where function types are only
1100-
* allowed inside parentheses.
1089+
* return None. The case is triggered in two situations:
1090+
* 1. If the input starts with an identifier, a wildcard, or something
1091+
* enclosed in (...) or [...], this is followed by a `=>` or `?=>`,
1092+
* and one of the following two subcases applies:
1093+
* 1a. The next token is an indent. In this case the return parsing function parses
1094+
* an Expr in location Location.InColonArg.
1095+
* 1b. Under relaxedLambdaSyntax: the next token is on the same line and the enclosing region is not `(...)`.
1096+
* In this case the parsing function parses an Expr in location Location.InColonArg
1097+
* enclosed in a SingleLineLambda region, and then eats the ENDlambda token
1098+
* generated by the Scanner at the end of that region.
1099+
* The reason for excluding (1b) in regions enclosed in parentheses is to avoid
1100+
* an ambiguity with type ascription `(x: A => B)`, where function types are only
1101+
* allowed inside parentheses.
1102+
* 2. Under relaxedLambdaSyntax: the input starts with a `case`.
11011103
*/
11021104
def followingIsLambdaAfterColon(): Option[() => Tree] =
11031105
val lookahead = in.LookaheadScanner(allowIndent = true)
@@ -1107,7 +1109,9 @@ object Parsers {
11071109
lookahead.observeArrowIndented()
11081110
if lookahead.token == INDENT || lookahead.token == EOF then
11091111
Some(() => expr(Location.InColonArg))
1110-
else if !in.currentRegion.isInstanceOf[InParens] then
1112+
else if in.featureEnabled(Feature.relaxedLambdaSyntax)
1113+
&& !in.currentRegion.isInstanceOf[InParens]
1114+
then
11111115
Some: () =>
11121116
val t = inSepRegion(SingleLineLambda(_)):
11131117
expr(Location.InColonArg)
@@ -1122,7 +1126,7 @@ object Parsers {
11221126
else if lookahead.token == LPAREN || lookahead.token == LBRACKET then
11231127
lookahead.skipParens()
11241128
isArrowIndent()
1125-
else if lookahead.token == CASE then
1129+
else if lookahead.token == CASE && in.featureEnabled(Feature.relaxedLambdaSyntax) then
11261130
Some(() => singleCaseMatch())
11271131
else
11281132
None
@@ -1193,9 +1197,11 @@ object Parsers {
11931197
/** Optionally, if we are seeing a lambda argument after a colon of the form
11941198
* : (params) =>
11951199
* body
1196-
* or a single-line lambda
1200+
* or a single-line lambda (under relaxedLambdaSyntax)
11971201
* : (params) => body
1198-
* then return the function used to parse `body`.
1202+
* or a case clause (under relaxedLambdaSyntax)
1203+
* : case pat guard => rhs
1204+
* then return the function used to parse `body` or the case clause.
11991205
*/
12001206
def detectColonLambda: Option[() => Tree] =
12011207
if sourceVersion.enablesFewerBraces && in.token == COLONfollow
@@ -2432,7 +2438,7 @@ object Parsers {
24322438
val arrowOffset = accept(ARROW)
24332439
val body = expr(location)
24342440
makePolyFunction(tparams, body, "literal", errorTermTree(arrowOffset), start, arrowOffset)
2435-
case CASE =>
2441+
case CASE if in.featureEnabled(Feature.relaxedLambdaSyntax) =>
24362442
singleCaseMatch()
24372443
case _ =>
24382444
val saved = placeholderParams

library/src/scala/language.scala

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -355,6 +355,10 @@ object language {
355355
@compileTimeOnly("`subCases` can only be used at compile time in import statements")
356356
object subCases
357357

358+
/** Experimental support for single-line lambdas and case clause expressions after `:`
359+
*/
360+
@compileTimeOnly("`relaxedLambdaSyntax` can only be used at compile time in import statements")
361+
object relaxedLambdaSyntax
358362
}
359363

360364
/** 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
@@ -161,6 +161,11 @@ object language:
161161
*/
162162
@compileTimeOnly("`subCases` can only be used at compile time in import statements")
163163
object subCases
164+
165+
/** Experimental support for single-line lambdas and case clause expressions after `:`
166+
*/
167+
@compileTimeOnly("`relaxedLambdaSyntax` can only be used at compile time in import statements")
168+
object relaxedLambdaSyntax
164169
end experimental
165170

166171
/** 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: 2 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

0 commit comments

Comments
 (0)