From b35b3487022a7534f6415080a1dbc749d4baa5d1 Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Thu, 29 Apr 2021 16:08:07 +0200 Subject: [PATCH] Allow braces just on the outside of polymorphic lambdas Also refactor grammar so that polymorphic lambdas and regular lambdas have the same level of precedence. Fixes #12277 --- .../dotty/tools/dotc/parsing/Parsers.scala | 73 ++++++++++--------- docs/docs/internals/syntax.md | 3 +- docs/docs/reference/syntax.md | 3 +- tests/neg/tuple-ops.scala | 4 +- tests/pos/i12277.scala | 18 +++++ 5 files changed, 61 insertions(+), 40 deletions(-) create mode 100644 tests/pos/i12277.scala diff --git a/compiler/src/dotty/tools/dotc/parsing/Parsers.scala b/compiler/src/dotty/tools/dotc/parsing/Parsers.scala index 27ac53f5af34..f3c55d110e85 100644 --- a/compiler/src/dotty/tools/dotc/parsing/Parsers.scala +++ b/compiler/src/dotty/tools/dotc/parsing/Parsers.scala @@ -1838,6 +1838,7 @@ object Parsers { t /** Expr ::= [`implicit'] FunParams (‘=>’ | ‘?=>’) Expr + * | HkTypeParamClause ‘=>’ Expr * | Expr1 * FunParams ::= Bindings * | id @@ -1845,6 +1846,7 @@ object Parsers { * ExprInParens ::= PostfixExpr `:' Type * | Expr * BlockResult ::= [‘implicit’] FunParams (‘=>’ | ‘?=>’) Block + * | HkTypeParamClause ‘=>’ Block * | Expr1 * Expr1 ::= [‘inline’] `if' `(' Expr `)' {nl} Expr [[semi] else Expr] * | [‘inline’] `if' Expr `then' Expr [[semi] else Expr] @@ -1855,7 +1857,6 @@ object Parsers { * | `throw' Expr * | `return' [Expr] * | ForExpr - * | HkTypeParamClause ‘=>’ Expr * | [SimpleExpr `.'] id `=' Expr * | SimpleExpr1 ArgumentExprs `=' Expr * | PostfixExpr [Ascription] @@ -1876,28 +1877,41 @@ object Parsers { def expr(location: Location): Tree = { val start = in.offset def isSpecialClosureStart = in.lookahead.isIdent(nme.erased) && in.erasedEnabled - if in.token == IMPLICIT then - closure(start, location, modifiers(BitSet(IMPLICIT))) - else if in.token == LPAREN && isSpecialClosureStart then - closure(start, location, Modifiers()) - else { - val saved = placeholderParams - placeholderParams = Nil - - def wrapPlaceholders(t: Tree) = try - if (placeholderParams.isEmpty) t - else new WildcardFunction(placeholderParams.reverse, t) - finally placeholderParams = saved - - val t = expr1(location) - if in.isArrow then - placeholderParams = Nil // don't interpret `_' to the left of `=>` as placeholder - wrapPlaceholders(closureRest(start, location, convertToParams(t))) - else if isWildcard(t) then - placeholderParams = placeholderParams ::: saved - t - else wrapPlaceholders(t) - } + in.token match + case IMPLICIT => + closure(start, location, modifiers(BitSet(IMPLICIT))) + case LPAREN if isSpecialClosureStart => + closure(start, location, Modifiers()) + case LBRACKET => + val start = in.offset + val tparams = typeParamClause(ParamOwner.TypeParam) + val arrowOffset = accept(ARROW) + val body = expr(location) + atSpan(start, arrowOffset) { + if (isFunction(body)) + PolyFunction(tparams, body) + else { + syntaxError("Implementation restriction: polymorphic function literals must have a value parameter", arrowOffset) + errorTermTree + } + } + case _ => + val saved = placeholderParams + placeholderParams = Nil + + def wrapPlaceholders(t: Tree) = try + if (placeholderParams.isEmpty) t + else new WildcardFunction(placeholderParams.reverse, t) + finally placeholderParams = saved + + val t = expr1(location) + if in.isArrow then + placeholderParams = Nil // don't interpret `_' to the left of `=>` as placeholder + wrapPlaceholders(closureRest(start, location, convertToParams(t))) + else if isWildcard(t) then + placeholderParams = placeholderParams ::: saved + t + else wrapPlaceholders(t) } def expr1(location: Location = Location.ElseWhere): Tree = in.token match @@ -1981,19 +1995,6 @@ object Parsers { } case FOR => forExpr() - case LBRACKET => - val start = in.offset - val tparams = typeParamClause(ParamOwner.TypeParam) - val arrowOffset = accept(ARROW) - val body = expr() - atSpan(start, arrowOffset) { - if (isFunction(body)) - PolyFunction(tparams, body) - else { - syntaxError("Implementation restriction: polymorphic function literals must have a value parameter", arrowOffset) - errorTermTree - } - } case _ => if isIdent(nme.inline) && !in.inModifierPosition() diff --git a/docs/docs/internals/syntax.md b/docs/docs/internals/syntax.md index e5aa1482fab6..5eb0b8f63289 100644 --- a/docs/docs/internals/syntax.md +++ b/docs/docs/internals/syntax.md @@ -205,8 +205,10 @@ Types ::= Type {‘,’ Type} ### Expressions ```ebnf Expr ::= FunParams (‘=>’ | ‘?=>’) Expr Function(args, expr), Function(ValDef([implicit], id, TypeTree(), EmptyTree), expr) + | HkTypeParamClause ‘=>’ Expr PolyFunction(ts, expr) | Expr1 BlockResult ::= FunParams (‘=>’ | ‘?=>’) Block + | HkTypeParamClause ‘=>’ Block | Expr1 FunParams ::= Bindings | id @@ -220,7 +222,6 @@ Expr1 ::= [‘inline’] ‘if’ ‘(’ Expr ‘)’ {nl} Expr [[ | ‘throw’ Expr Throw(expr) | ‘return’ [Expr] Return(expr?) | ForExpr - | HkTypeParamClause ‘=>’ Expr PolyFunction(ts, expr) | [SimpleExpr ‘.’] id ‘=’ Expr Assign(expr, expr) | SimpleExpr1 ArgumentExprs ‘=’ Expr Assign(expr, expr) | PostfixExpr [Ascription] diff --git a/docs/docs/reference/syntax.md b/docs/docs/reference/syntax.md index 263c1abc0c22..440676906f63 100644 --- a/docs/docs/reference/syntax.md +++ b/docs/docs/reference/syntax.md @@ -202,8 +202,10 @@ Types ::= Type {‘,’ Type} ### Expressions ```ebnf Expr ::= FunParams (‘=>’ | ‘?=>’) Expr + | HkTypeParamClause ‘=>’ Expr | Expr1 BlockResult ::= FunParams (‘=>’ | ‘?=>’) Block + | HkTypeParamClause ‘=>’ Block | Expr1 FunParams ::= Bindings | id @@ -217,7 +219,6 @@ Expr1 ::= [‘inline’] ‘if’ ‘(’ Expr ‘)’ {nl} Expr [[ | ‘throw’ Expr | ‘return’ [Expr] | ForExpr - | HkTypeParamClause ‘=>’ Expr | [SimpleExpr ‘.’] id ‘=’ Expr | SimpleExpr1 ArgumentExprs ‘=’ Expr | PostfixExpr [Ascription] diff --git a/tests/neg/tuple-ops.scala b/tests/neg/tuple-ops.scala index 4e787c1932c8..47a8c00cc6ef 100644 --- a/tests/neg/tuple-ops.scala +++ b/tests/neg/tuple-ops.scala @@ -12,13 +12,13 @@ val r3: ((2, 1), (8, 2)) = c.zip(a) // error // Map case class Foo[X](x: X) -val r6: (Int, Int, String) = a.map[[t] =>> Int]([t] => x: t => x match { // error +val r6: (Int, Int, String) = a.map[[t] =>> Int]([t] => (x: t) => x match { // error case x: Int => x * x case _ => ??? }) val r7: ((1, Foo[1]), (2), (3, Foo[3])) = - a.map[[t] =>> (t, Foo[t])]( [t] => x: t => (x, Foo(x)) ) // error + a.map[[t] =>> (t, Foo[t])]( [t] => (x: t) => (x, Foo(x)) ) // error // More Zip val t1: Int *: Long *: Tuple = (1, 2l, 100, 200) diff --git a/tests/pos/i12277.scala b/tests/pos/i12277.scala new file mode 100644 index 000000000000..99eb7238da00 --- /dev/null +++ b/tests/pos/i12277.scala @@ -0,0 +1,18 @@ +def foo(f: => () => Unit): Unit = ??? +def boo(f: [A] => () => Unit): Unit = ??? + +object test: + foo { () => // okay + println(1) + println(2) + } + + boo { [A] => () => // error + println(1) + println(2) + } + + boo { [A] => () => { // okay + println(1) + println(2) + }} \ No newline at end of file