Skip to content

Commit f35df41

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 5851c62 commit f35df41

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)
@@ -457,7 +460,6 @@ object Parsers {
457460
case _ =>
458461
syntaxError(s"not a legal $expected", tree.span)
459462
makeParameter(nme.ERROR, tree, mods)
460-
}
461463

462464
/** Convert (qual)ident to type identifier
463465
*/
@@ -915,6 +917,21 @@ object Parsers {
915917
}
916918
}
917919

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

920937
var opStack: List[OpInfo] = Nil
@@ -2283,7 +2300,7 @@ object Parsers {
22832300
placeholderParams = param :: placeholderParams
22842301
atSpan(start) { Ident(pname) }
22852302
case LPAREN =>
2286-
atSpan(in.offset) { makeTupleOrParens(inParens(exprsInParensOpt())) }
2303+
atSpan(in.offset) { makeTupleOrParens(inParens(exprsInParensOrBindings())) }
22872304
case LBRACE | INDENT =>
22882305
canApply = false
22892306
blockExpr()
@@ -2353,7 +2370,17 @@ object Parsers {
23532370
val app = applyToClosure(t, in.offset, convertToParams(termIdent()))
23542371
simpleExprRest(app, location, canApply = true)
23552372
case _ =>
2356-
t
2373+
t match
2374+
case id @ Ident(name)
2375+
if in.isColon() && location == Location.InParens && followingisLambdaParams() =>
2376+
if name.is(WildcardParamName) then
2377+
assert(name == placeholderParams.head.name)
2378+
placeholderParams = placeholderParams.tail
2379+
atSpan(startOffset(id)) {
2380+
makeParameter(name.asTermName, typedOpt(), Modifiers(), isBackquoted = isBackquoted(id))
2381+
}
2382+
case _ =>
2383+
t
23572384
}
23582385
}
23592386

@@ -2387,9 +2414,20 @@ object Parsers {
23872414
}
23882415

23892416
/** ExprsInParens ::= ExprInParens {`,' ExprInParens}
2417+
* Bindings ::= Binding {`,' Binding}
23902418
*/
2391-
def exprsInParensOpt(): List[Tree] =
2392-
if (in.token == RPAREN) Nil else commaSeparated(exprInParens)
2419+
def exprsInParensOrBindings(): List[Tree] =
2420+
if in.token == RPAREN then Nil
2421+
else in.currentRegion.withCommasExpected {
2422+
var isFormalParams = false
2423+
def exprOrBinding() =
2424+
if isFormalParams then binding(Modifiers())
2425+
else
2426+
val t = exprInParens()
2427+
if t.isInstanceOf[ValDef] then isFormalParams = true
2428+
t
2429+
commaSeparatedRest(exprOrBinding(), exprOrBinding)
2430+
}
23932431

23942432
/** ParArgumentExprs ::= `(' [‘using’] [ExprsInParens] `)'
23952433
* | `(' [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)