From d50b823d67e25f54d7461528f04509c9b1aa23d4 Mon Sep 17 00:00:00 2001 From: Som Snytt Date: Sun, 26 Feb 2023 14:36:11 -0800 Subject: [PATCH] Accept implicit _, implicit (x: Int) in lambda --- spec/06-expressions.md | 4 +- spec/13-syntax-summary.md | 4 +- .../scala/tools/nsc/ast/parser/Parsers.scala | 96 +++++++++---------- test/files/pos/t3672.scala | 22 ++++- 4 files changed, 71 insertions(+), 55 deletions(-) diff --git a/spec/06-expressions.md b/spec/06-expressions.md index b0eb6ac2d8ad..cb6614baef4c 100644 --- a/spec/06-expressions.md +++ b/spec/06-expressions.md @@ -1182,8 +1182,8 @@ for `try { try { ´b´ } catch ´e_1´ } finally ´e_2´`. ## Anonymous Functions ```ebnf -Expr ::= (Bindings | [‘implicit’] id | ‘_’) ‘=>’ Expr -ResultExpr ::= (Bindings | ([‘implicit’] id | ‘_’) ‘:’ CompoundType) ‘=>’ Block +Expr ::= (Bindings | [‘implicit’] (id | ‘_’)) ‘=>’ Expr +ResultExpr ::= (Bindings | [‘implicit’] (id | ‘_’) [‘:’ CompoundType]) ‘=>’ Block Bindings ::= ‘(’ Binding {‘,’ Binding} ‘)’ Binding ::= (id | ‘_’) [‘:’ Type] ``` diff --git a/spec/13-syntax-summary.md b/spec/13-syntax-summary.md index 65eb47049519..6e225e177729 100644 --- a/spec/13-syntax-summary.md +++ b/spec/13-syntax-summary.md @@ -144,7 +144,7 @@ grammar: | ‘:’ Annotation {Annotation} | ‘:’ ‘_’ ‘*’ - Expr ::= (Bindings | [‘implicit’] id | ‘_’) ‘=>’ Expr + Expr ::= (Bindings | [‘implicit’] (id | ‘_’)) ‘=>’ Expr | Expr1 Expr1 ::= ‘if’ ‘(’ Expr ‘)’ {nl} Expr [[semi] ‘else’ Expr] | ‘while’ ‘(’ Expr ‘)’ {nl} Expr @@ -188,7 +188,7 @@ grammar: | Expr1 | ResultExpr ::= Expr1 - | (Bindings | ([‘implicit’] id | ‘_’) ‘:’ CompoundType) ‘=>’ Block + | (Bindings | [‘implicit’] (id | ‘_’) [‘:’ CompoundType]) ‘=>’ Block Enumerators ::= Generator {semi Generator} Generator ::= [‘case’] Pattern1 ‘<-’ Expr {[semi] Guard | semi Pattern1 ‘=’ Expr} diff --git a/src/compiler/scala/tools/nsc/ast/parser/Parsers.scala b/src/compiler/scala/tools/nsc/ast/parser/Parsers.scala index ab125e793582..cf8b278818da 100644 --- a/src/compiler/scala/tools/nsc/ast/parser/Parsers.scala +++ b/src/compiler/scala/tools/nsc/ast/parser/Parsers.scala @@ -477,7 +477,7 @@ self => /* --------------- PLACEHOLDERS ------------------------------------------- */ - /** The implicit parameters introduced by `_` in the current expression. + /** The parameters introduced by `_` "placeholder syntax" in the current expression. * Parameters appear in reverse order. */ var placeholderParams: List[ValDef] = Nil @@ -529,8 +529,8 @@ self => @tailrec final def isWildcard(t: Tree): Boolean = t match { - case Ident(name1) => !placeholderParams.isEmpty && name1 == placeholderParams.head.name - case Typed(t1, _) => isWildcard(t1) + case Ident(name1) => !placeholderParams.isEmpty && name1 == placeholderParams.head.name + case Typed(t1, _) => isWildcard(t1) case Annotated(t1, _) => isWildcard(t1) case _ => false } @@ -784,7 +784,7 @@ self => /** Convert tree to formal parameter. */ def convertToParam(tree: Tree): ValDef = atPos(tree.pos) { def removeAsPlaceholder(name: Name): Unit = { - placeholderParams = placeholderParams filter (_.name != name) + placeholderParams = placeholderParams.filter(_.name != name) } def errorParam = makeParam(nme.ERROR, errorTypeTree setPos o2p(tree.pos.end)) def propagateNoWarnAttachment(from: Tree, to: ValDef): to.type = @@ -1711,54 +1711,52 @@ self => case IMPLICIT => implicitClosure(in.skipToken(), location) case _ => - def parseOther = { + def parseOther: Tree = { var t = postfixExpr() - if (in.token == EQUALS) { - t match { - case Ident(_) | Select(_, _) | Apply(_, _) => - t = atPos(t.pos.start, in.skipToken()) { gen.mkAssign(t, expr()) } - case _ => - } - } else if (in.token == COLON) { - t = stripParens(t) - val colonPos = in.skipToken() - if (in.token == USCORE) { - //todo: need to handle case where USCORE is a wildcard in a type - val uscorePos = in.skipToken() - if (isIdent && in.name == nme.STAR) { - in.nextToken() - t = atPos(t.pos.start, colonPos) { - Typed(t, atPos(uscorePos) { Ident(tpnme.WILDCARD_STAR) }) - } - } else { - syntaxErrorOrIncomplete("`*` expected", skipIt = true) + in.token match { + case EQUALS => + t match { + case Ident(_) | Select(_, _) | Apply(_, _) => + t = atPos(t.pos.start, in.skipToken()) { gen.mkAssign(t, expr()) } + case _ => } - } else if (isAnnotation) { - t = annotations(skipNewLines = false).foldLeft(t)(makeAnnotated) - } else { - t = atPos(t.pos.start, colonPos) { - val tpt = typeOrInfixType(location) - if (isWildcard(t)) - (placeholderParams: @unchecked) match { - case (vd @ ValDef(mods, name, _, _)) :: rest => - placeholderParams = treeCopy.ValDef(vd, mods, name, tpt.duplicate, EmptyTree) :: rest + case COLON => + t = stripParens(t) + val colonPos = in.skipToken() + if (in.token == USCORE) { + //todo: need to handle case where USCORE is a wildcard in a type + val uscorePos = in.skipToken() + if (isIdent && in.name == nme.STAR) { + in.nextToken() + t = atPos(t.pos.start, colonPos) { + Typed(t, atPos(uscorePos) { Ident(tpnme.WILDCARD_STAR) }) } - // this does not correspond to syntax, but is necessary to - // accept closures. We might restrict closures to be between {...} only. - Typed(t, tpt) + } + else syntaxErrorOrIncomplete("`*` expected", skipIt = true) } - } - } else if (in.token == MATCH) { - t = atPos(t.pos.start, in.skipToken())(Match(stripParens(t), inBracesOrNil(caseClauses()))) + else if (isAnnotation) + t = annotations(skipNewLines = false).foldLeft(t)(makeAnnotated) + else + t = atPos(t.pos.start, colonPos) { + val tpt = typeOrInfixType(location) + // for placeholder syntax `(_: Int) + 1`; function literal `(_: Int) => 42` uses `t` below + if (isWildcard(t)) + (placeholderParams: @unchecked) match { + case (vd @ ValDef(mods, name, _, _)) :: rest => + placeholderParams = treeCopy.ValDef(vd, mods, name, tpt.duplicate, EmptyTree) :: rest + } + // this does not correspond to syntax, but is necessary to accept closures. See below & convertToParam. + Typed(t, tpt) + } + case MATCH => + t = atPos(t.pos.start, in.skipToken())(Match(stripParens(t), inBracesOrNil(caseClauses()))) + case _ => } // disambiguate between self types "x: Int =>" and orphan function literals "(x: Int) => ???" // "(this: Int) =>" is parsed as an erroneous function literal but emits special guidance on // what's probably intended. def lhsIsTypedParamList() = t match { - case Parens(List(Typed(This(_), _))) => { - reporter.error(t.pos, "self-type annotation may not be in parentheses") - false - } + case Parens(List(Typed(This(_), _))) => reporter.error(t.pos, "self-type annotation may not be in parentheses"); false case Parens(xs) => xs.forall(isTypedParam) case _ => false } @@ -1779,15 +1777,15 @@ self => * Expr ::= implicit Id `=>` Expr * }}} */ - def implicitClosure(start: Offset, location: Location): Tree = { val param0 = convertToParam { atPos(in.offset) { - Ident(ident()) match { - case expr if in.token == COLON => - in.nextToken() ; Typed(expr, typeOrInfixType(location)) - case expr => expr + val p = stripParens(postfixExpr()) //if (in.token == USCORE) freshPlaceholder() else Ident(ident()) + if (in.token == COLON) { + in.nextToken() + Typed(p, typeOrInfixType(location)) } + else p } } val param = copyValDef(param0)(mods = param0.mods | Flags.IMPLICIT) @@ -3507,7 +3505,7 @@ self => else if (isDefIntro || isLocalModifier || isAnnotation) { if (in.token == IMPLICIT) { val start = in.skipToken() - if (isIdent) stats += implicitClosure(start, InBlock) + if (isIdent || in.token == USCORE) stats += implicitClosure(start, InBlock) else stats ++= localDef(Flags.IMPLICIT) } else { stats ++= localDef(0) diff --git a/test/files/pos/t3672.scala b/test/files/pos/t3672.scala index b2752ce21ff7..2c17a17ff8b1 100644 --- a/test/files/pos/t3672.scala +++ b/test/files/pos/t3672.scala @@ -1,4 +1,22 @@ object Test { - def foo(f: Int => Int) = () ; foo { implicit x : Int => x + 1 } - def bar(f: Int => Int) = () ; foo { x : Int => x + 1 } + def foo(f: Int => Int) = () + def test(): Unit = { + foo { x => x + 1 } + foo { implicit x => x + 1 } + foo { x: Int => x + 1 } + foo { implicit x: Int => x + 1 } + foo { _ => 42 } + foo { implicit _ => implicitly[Int] + 1 } // scala 2 deficit + foo { _: Int => 42 } + foo { implicit _: Int => implicitly[Int] + 1 } // scala 2 deficit + + foo(x => x + 1) + foo(implicit x => x + 1) + foo((x: Int) => x + 1) + foo(implicit (x: Int) => x + 1) // scala 3 + foo(_ => 42) + foo(implicit _ => implicitly[Int] + 1) // scala 2 deficit + foo((_: Int) => 42) + foo(implicit (_: Int) => implicitly[Int] + 1) // scala 3 + } }