Skip to content

Commit 60ddc12

Browse files
committed
Implement parsing for multiple parameter lambdas with erased parameters
Contextual lambdas with erased parameters are also automatically supported. Also reject normal tuples with ValDefs in them. This comes up when trying to parse parameters out of tuples.
1 parent 17d954c commit 60ddc12

File tree

4 files changed

+96
-10
lines changed

4 files changed

+96
-10
lines changed

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

Lines changed: 19 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -463,6 +463,15 @@ object Parsers {
463463
case _ =>
464464
fail()
465465

466+
/** Checks that tuples don't contain a parameter. */
467+
def checkNonParamTuple(t: Tree) = t match
468+
case Tuple(ts) => ts.collectFirst {
469+
case param: ValDef =>
470+
syntaxError(em"invalid parameter definition syntax in tuple value", param.span)
471+
}
472+
case _ =>
473+
474+
466475
/** Convert (qual)ident to type identifier
467476
*/
468477
def convertToTypeId(tree: Tree): Tree = tree match {
@@ -2095,12 +2104,9 @@ object Parsers {
20952104

20962105
def expr(location: Location): Tree = {
20972106
val start = in.offset
2098-
def isSpecialClosureStart = in.lookahead.isIdent(nme.erased) && in.erasedEnabled
20992107
in.token match
21002108
case IMPLICIT =>
21012109
closure(start, location, modifiers(BitSet(IMPLICIT)))
2102-
case LPAREN if isSpecialClosureStart =>
2103-
closure(start, location, Modifiers())
21042110
case LBRACKET =>
21052111
val start = in.offset
21062112
val tparams = typeParamClause(ParamOwner.TypeParam)
@@ -2130,7 +2136,9 @@ object Parsers {
21302136
else if isWildcard(t) then
21312137
placeholderParams = placeholderParams ::: saved
21322138
t
2133-
else wrapPlaceholders(t)
2139+
else
2140+
checkNonParamTuple(t)
2141+
wrapPlaceholders(t)
21342142
}
21352143

21362144
def expr1(location: Location = Location.ElseWhere): Tree = in.token match
@@ -2323,10 +2331,7 @@ object Parsers {
23232331
Nil
23242332
else
23252333
try
2326-
commaSeparated(() =>
2327-
val mods1 = if isErased then addModifier(mods) else mods
2328-
binding(mods1)
2329-
)
2334+
commaSeparated(() => binding(mods))
23302335
finally
23312336
accept(RPAREN)
23322337
else {
@@ -2350,10 +2355,13 @@ object Parsers {
23502355
(atSpan(start) { makeParameter(name, t, mods) }) :: Nil
23512356
}
23522357

2353-
/** Binding ::= (id | `_') [`:' Type]
2358+
/** Binding ::= [`erased`] (id | `_') [`:' Type]
23542359
*/
23552360
def binding(mods: Modifiers): Tree =
2356-
atSpan(in.offset) { makeParameter(bindingName(), typedOpt(), mods) }
2361+
atSpan(in.offset) {
2362+
val mods1 = if isErased then addModifier(mods) else mods
2363+
makeParameter(bindingName(), typedOpt(), mods1)
2364+
}
23572365

23582366
def bindingName(): TermName =
23592367
if (in.token == USCORE) {
@@ -2550,6 +2558,7 @@ object Parsers {
25502558
else in.currentRegion.withCommasExpected {
25512559
var isFormalParams = false
25522560
def exprOrBinding() =
2561+
if isErased then isFormalParams = true
25532562
if isFormalParams then binding(Modifiers())
25542563
else
25552564
val t = exprInParens()
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
@main def Test() =
2+
val x = 5
3+
val y = 7
4+
5+
val t1 = (x, erased y) // error
6+
val t2 = (erased x, y) // error
7+
val t1a = (x: Int, erased y: Int) // error
8+
val t2a = (erased x: Int, y: Int) // error
9+
10+
val nest = (x, (x, erased y)) // error
11+
12+
def use(f: (Int, Int) => Any) = f(5, 6)
13+
14+
use((_, erased _)) // error
15+
16+
(x, erased y) // error
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
type F = (Int, erased Int) => Int
2+
3+
erased class A
4+
5+
@main def Test() =
6+
val a: F = (x, y) => x + 1 // error: Expected F got (Int, Int) => Int
7+
val b: F = (x, erased y) => x + 1 // ok
8+
val c: F = (_, _) => 5 // error: Expected F got (Int, Int) => Int
9+
val d: F = (_, erased _) => 5 // ok
10+
11+
def use(f: F) = f(5, 6)
12+
13+
use { (x, y) => x } // error: Expected F got (Int, Int) => Int
14+
15+
def singleParam(f: (erased Int) => Int) = f(5)
16+
17+
singleParam(x => 5) // error: Expected (erased Int) => Int got Int => Int
18+
singleParam((erased x) => 5) // ok
19+
20+
def erasedClass(f: A => Int) = f(new A)
21+
22+
erasedClass(_ => 5) // ok since A is implicitly erased
23+
Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
// lambdas should parse and work
2+
3+
type F = (erased Int, String) => String
4+
type S = (Int, erased String) => Int
5+
6+
def useF(f: F) = f(5, "a")
7+
def useS(f: S) = f(5, "a")
8+
9+
val ff: F = (erased x, y) => y
10+
11+
val fs: S = (x, erased y) => x
12+
val fsExpl = (x: Int, erased y: String) => x
13+
14+
// contextual lambdas should work
15+
16+
type FC = (Int, erased String) ?=> Int
17+
18+
def useCtx(f: FC) = f(using 5, "a")
19+
20+
val fCv: FC = (x, erased y) ?=> x
21+
val fCvExpl = (x: Int, erased y: String) ?=> x
22+
23+
// nested lambdas should work
24+
25+
val nested: Int => (String, erased Int) => FC = a => (_, erased _) => (c, erased d) ?=> a + c
26+
27+
@main def Test() =
28+
assert("a" == useF(ff))
29+
30+
assert(5 == useS(fs))
31+
assert(5 == useS(fsExpl))
32+
assert(5 == useS { (x, erased y) => x })
33+
34+
assert(5 == useCtx(fCv))
35+
assert(5 == useCtx(fCvExpl))
36+
assert(5 == useCtx { (x, erased y) ?=> x })
37+
38+
assert(6 == useCtx(nested(1)("b", 2)))

0 commit comments

Comments
 (0)