Skip to content

Commit

Permalink
Accept implicit _, implicit (x: Int) in lambda
Browse files Browse the repository at this point in the history
  • Loading branch information
som-snytt committed Mar 15, 2023
1 parent 15538c7 commit d50b823
Show file tree
Hide file tree
Showing 4 changed files with 71 additions and 55 deletions.
4 changes: 2 additions & 2 deletions spec/06-expressions.md
Expand Up @@ -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]
```
Expand Down
4 changes: 2 additions & 2 deletions spec/13-syntax-summary.md
Expand Up @@ -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
Expand Down Expand Up @@ -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}
Expand Down
96 changes: 47 additions & 49 deletions src/compiler/scala/tools/nsc/ast/parser/Parsers.scala
Expand Up @@ -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
Expand Down Expand Up @@ -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
}
Expand Down Expand Up @@ -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 =
Expand Down Expand Up @@ -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
}
Expand All @@ -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)
Expand Down Expand Up @@ -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)
Expand Down
22 changes: 20 additions & 2 deletions 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
}
}

0 comments on commit d50b823

Please sign in to comment.