Skip to content

Commit d64c6dc

Browse files
committed
Better prediction of formal parameters of lambdas
Instead of parsing all formal parameters of a lambda as expressions and converting to parameters at the end, we now scan ahead the first time we see a `:` to determine whether the list is followed by `=>` or `?=>`. If that's the case we parse this parameter and all following ones as bindings instead of expressions.
1 parent f1d57c4 commit d64c6dc

File tree

5 files changed

+51
-17
lines changed

5 files changed

+51
-17
lines changed

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

Lines changed: 44 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -444,9 +444,12 @@ object Parsers {
444444

445445
/** Convert tree to formal parameter
446446
*/
447-
def convertToParam(tree: Tree, mods: Modifiers, expected: String = "formal parameter"): ValDef = tree match {
447+
def convertToParam(tree: Tree, mods: Modifiers, expected: String = "formal parameter"): ValDef = tree match
448+
case param: ValDef =>
449+
param.withMods(param.mods | mods.flags)
448450
case id @ Ident(name) =>
449451
makeParameter(name.asTermName, TypeTree(), mods, isBackquoted = isBackquoted(id)).withSpan(tree.span)
452+
// the following three cases are needed only for 2.x parameters without enclosing parentheses
450453
case Typed(_, tpt: TypeBoundsTree) =>
451454
syntaxError(s"not a legal $expected", tree.span)
452455
makeParameter(nme.ERROR, tree, mods)
@@ -455,7 +458,6 @@ object Parsers {
455458
case _ =>
456459
syntaxError(s"not a legal $expected", tree.span)
457460
makeParameter(nme.ERROR, tree, mods)
458-
}
459461

460462
/** Convert (qual)ident to type identifier
461463
*/
@@ -913,6 +915,21 @@ object Parsers {
913915
}
914916
}
915917

918+
/** When encountering a `:`, is that in the first binding of a lambda?
919+
* @pre location of the enclosing expression is `InParens`, so there is am open `(`.
920+
*/
921+
def followingisLambdaParams() =
922+
val lookahead = in.LookaheadScanner()
923+
lookahead.nextToken()
924+
while lookahead.token != RPAREN && lookahead.token != EOF do
925+
if lookahead.token == LPAREN then lookahead.skipParens()
926+
else lookahead.nextToken()
927+
lookahead.token == RPAREN
928+
&& {
929+
lookahead.nextToken()
930+
lookahead.isArrow
931+
}
932+
916933
/* --------- OPERAND/OPERATOR STACK --------------------------------------- */
917934

918935
var opStack: List[OpInfo] = Nil
@@ -2292,7 +2309,7 @@ object Parsers {
22922309
placeholderParams = param :: placeholderParams
22932310
atSpan(start) { Ident(pname) }
22942311
case LPAREN =>
2295-
atSpan(in.offset) { makeTupleOrParens(inParens(exprsInParensOpt())) }
2312+
atSpan(in.offset) { makeTupleOrParens(inParens(exprsInParensOrBindings())) }
22962313
case LBRACE | INDENT =>
22972314
canApply = false
22982315
blockExpr()
@@ -2362,7 +2379,17 @@ object Parsers {
23622379
val app = applyToClosure(t, in.offset, convertToParams(termIdent()))
23632380
simpleExprRest(app, location, canApply = true)
23642381
case _ =>
2365-
t
2382+
t match
2383+
case id @ Ident(name)
2384+
if in.isColon() && location == Location.InParens && followingisLambdaParams() =>
2385+
if name.is(WildcardParamName) then
2386+
assert(name == placeholderParams.head.name)
2387+
placeholderParams = placeholderParams.tail
2388+
atSpan(startOffset(id)) {
2389+
makeParameter(name.asTermName, typedOpt(), Modifiers(), isBackquoted = isBackquoted(id))
2390+
}
2391+
case _ =>
2392+
t
23662393
}
23672394
}
23682395

@@ -2396,9 +2423,20 @@ object Parsers {
23962423
}
23972424

23982425
/** ExprsInParens ::= ExprInParens {`,' ExprInParens}
2426+
* Bindings ::= Binding {`,' Binding}
23992427
*/
2400-
def exprsInParensOpt(): List[Tree] =
2401-
if (in.token == RPAREN) Nil else commaSeparated(exprInParens)
2428+
def exprsInParensOrBindings(): List[Tree] =
2429+
if in.token == RPAREN then Nil
2430+
else in.currentRegion.withCommasExpected {
2431+
var isFormalParams = false
2432+
def exprOrBinding() =
2433+
if isFormalParams then binding(Modifiers())
2434+
else
2435+
val t = exprInParens()
2436+
if t.isInstanceOf[ValDef] then isFormalParams = true
2437+
t
2438+
commaSeparatedRest(exprOrBinding(), exprOrBinding)
2439+
}
24022440

24032441
/** ParArgumentExprs ::= `(' [‘using’] [ExprsInParens] `)'
24042442
* | `(' [ExprsInParens `,'] PostfixExpr `*' ')'

tests/neg/i13769.check

Lines changed: 4 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,6 @@
1-
-- Error: tests/neg/i13769.scala:2:18 ----------------------------------------------------------------------------------
2-
2 |val te = tup.map((x: _ <: Int) => List(x)) // error // error
3-
| ^^^^^^^^^^^
4-
| not a legal formal parameter
5-
-- [E006] Not Found Error: tests/neg/i13769.scala:2:39 -----------------------------------------------------------------
6-
2 |val te = tup.map((x: _ <: Int) => List(x)) // error // error
7-
| ^
8-
| Not found: x
1+
-- [E035] Syntax Error: tests/neg/i13769.scala:2:21 --------------------------------------------------------------------
2+
2 |val te = tup.map((x: _ <: Int) => List(x)) // error
3+
| ^^^^^^^^
4+
| Unbound wildcard type
95
|
106
| longer explanation available when compiling with `-explain`

tests/neg/i13769.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,2 @@
11
val tup = (1, "s")
2-
val te = tup.map((x: _ <: Int) => List(x)) // error // error
2+
val te = tup.map((x: _ <: Int) => List(x)) // error

tests/neg/i1424.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,3 @@
11
class Test {
2-
(x: Int) => x // error: not a legal self type clause // error: not found x
2+
(x: Int) => x // error: not a legal self type clause
33
}

tests/neg/i7818.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
def foo = (x: @) => () // error // error
1+
def foo = (x: @) => () // error

0 commit comments

Comments
 (0)