Skip to content

Fix #10484: Switch back to old context function closure syntax #10487

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 5 commits into from
Nov 27, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
56 changes: 25 additions & 31 deletions compiler/src/dotty/tools/dotc/parsing/Parsers.scala
Original file line number Diff line number Diff line change
Expand Up @@ -443,35 +443,35 @@ object Parsers {

/** Convert tree to formal parameter list
*/
def convertToParams(tree: Tree): List[ValDef] = tree match {
def convertToParams(tree: Tree, mods: Modifiers): List[ValDef] = tree match {
case Parens(t) =>
convertToParam(t) :: Nil
convertToParam(t, mods) :: Nil
case Tuple(ts) =>
ts.map(convertToParam(_))
ts.map(convertToParam(_, mods))
case t: Typed =>
report.errorOrMigrationWarning(
em"parentheses are required around the parameter of a lambda${rewriteNotice()}",
in.sourcePos())
if migrateTo3 then
patch(source, t.span.startPos, "(")
patch(source, t.span.endPos, ")")
convertToParam(t) :: Nil
convertToParam(t, mods) :: Nil
case t =>
convertToParam(t) :: Nil
convertToParam(t, mods) :: Nil
}

/** Convert tree to formal parameter
*/
def convertToParam(tree: Tree, expected: String = "formal parameter"): ValDef = tree match {
def convertToParam(tree: Tree, mods: Modifiers, expected: String = "formal parameter"): ValDef = tree match {
case id @ Ident(name) =>
makeParameter(name.asTermName, TypeTree(), EmptyModifiers, isBackquoted = isBackquoted(id)).withSpan(tree.span)
makeParameter(name.asTermName, TypeTree(), mods, isBackquoted = isBackquoted(id)).withSpan(tree.span)
case Typed(id @ Ident(name), tpt) =>
makeParameter(name.asTermName, tpt, EmptyModifiers, isBackquoted = isBackquoted(id)).withSpan(tree.span)
makeParameter(name.asTermName, tpt, mods, isBackquoted = isBackquoted(id)).withSpan(tree.span)
case Typed(Splice(Ident(name)), tpt) =>
makeParameter(("$" + name).toTermName, tpt, EmptyModifiers).withSpan(tree.span)
makeParameter(("$" + name).toTermName, tpt, mods).withSpan(tree.span)
case _ =>
syntaxError(s"not a legal $expected", tree.span)
makeParameter(nme.ERROR, tree, EmptyModifiers)
makeParameter(nme.ERROR, tree, mods)
}

/** Convert (qual)ident to type identifier
Expand Down Expand Up @@ -1864,14 +1864,14 @@ object Parsers {
accept(altToken)
t

/** Expr ::= [`implicit'] FunParams ‘=>’ Expr
/** Expr ::= [`implicit'] FunParams (‘=>’ | ‘?=>’) Expr
* | Expr1
* FunParams ::= Bindings
* | id
* | `_'
* ExprInParens ::= PostfixExpr `:' Type
* | Expr
* BlockResult ::= [‘implicit’] FunParams ‘=>’ Block
* BlockResult ::= [‘implicit’] FunParams (‘=>’ | ‘?=>’) Block
* | Expr1
* Expr1 ::= [‘inline’] `if' `(' Expr `)' {nl} Expr [[semi] else Expr]
* | [‘inline’] `if' Expr `then' Expr [[semi] else Expr]
Expand Down Expand Up @@ -1920,9 +1920,10 @@ object Parsers {
finally placeholderParams = saved

val t = expr1(location)
if (in.token == ARROW) {
if (in.token == ARROW || in.token == CTXARROW) {
placeholderParams = Nil // don't interpret `_' to the left of `=>` as placeholder
wrapPlaceholders(closureRest(start, location, convertToParams(t)))
val paramMods = if in.token == CTXARROW then Modifiers(Given) else EmptyModifiers
wrapPlaceholders(closureRest(start, location, convertToParams(t, paramMods)))
}
else if (isWildcard(t)) {
placeholderParams = placeholderParams ::: saved
Expand Down Expand Up @@ -2124,7 +2125,7 @@ object Parsers {
/** FunParams ::= Bindings
* | id
* | `_'
* Bindings ::= `(' [[‘using’] [‘erased’] Binding {`,' Binding}] `)'
* Bindings ::= `(' [[‘erased’] Binding {`,' Binding}] `)'
*/
def funParams(mods: Modifiers, location: Location): List[Tree] =
if in.token == LPAREN then
Expand Down Expand Up @@ -2185,7 +2186,7 @@ object Parsers {

def closureRest(start: Int, location: Location, params: List[Tree]): Tree =
atSpan(start, in.offset) {
accept(ARROW)
if in.token == CTXARROW then in.nextToken() else accept(ARROW)
Function(params, if (location == Location.InBlock) block() else expr())
}

Expand Down Expand Up @@ -2309,7 +2310,7 @@ object Parsers {
possibleTemplateStart()
val parents =
if in.isNestedStart then Nil
else constrApps(commaOK = false, templateCanFollow = true)
else constrApps(commaOK = false)
colonAtEOLOpt()
possibleTemplateStart(isNew = true)
parents match {
Expand Down Expand Up @@ -3492,7 +3493,7 @@ object Parsers {
val parents =
if (in.token == EXTENDS) {
in.nextToken()
constrApps(commaOK = true, templateCanFollow = false)
constrApps(commaOK = true)
}
else Nil
Template(constr, parents, Nil, EmptyValDef, Nil)
Expand Down Expand Up @@ -3536,7 +3537,7 @@ object Parsers {
val noParams = tparams.isEmpty && vparamss.isEmpty
if !(name.isEmpty && noParams) then
accept(nme.as)
val parents = constrApps(commaOK = true, templateCanFollow = true)
val parents = constrApps(commaOK = true)
if in.token == EQUALS && parents.length == 1 && parents.head.isType then
accept(EQUALS)
mods1 |= Final
Expand Down Expand Up @@ -3616,19 +3617,12 @@ object Parsers {

/** ConstrApps ::= ConstrApp {(‘,’ | ‘with’) ConstrApp}
*/
def constrApps(commaOK: Boolean, templateCanFollow: Boolean): List[Tree] =
def constrApps(commaOK: Boolean): List[Tree] =
val t = constrApp()
val ts =
if in.token == WITH then
in.nextToken()
newLineOptWhenFollowedBy(LBRACE)
if templateCanFollow && (in.token == LBRACE || in.token == INDENT) then
Nil
else
constrApps(commaOK, templateCanFollow)
else if commaOK && in.token == COMMA then
if in.token == WITH || commaOK && in.token == COMMA then
in.nextToken()
constrApps(commaOK, templateCanFollow)
constrApps(commaOK)
else Nil
t :: ts

Expand All @@ -3645,7 +3639,7 @@ object Parsers {
in.sourcePos())
Nil
}
else constrApps(commaOK = true, templateCanFollow = true)
else constrApps(commaOK = true)
}
else Nil
newLinesOptWhenFollowedBy(nme.derives)
Expand Down Expand Up @@ -3772,7 +3766,7 @@ object Parsers {
case Typed(tree @ This(EmptyTypeIdent), tpt) =>
self = makeSelfDef(nme.WILDCARD, tpt).withSpan(first.span)
case _ =>
val ValDef(name, tpt, _) = convertToParam(first, "self type clause")
val ValDef(name, tpt, _) = convertToParam(first, EmptyModifiers, "self type clause")
if (name != nme.ERROR)
self = makeSelfDef(name, tpt).withSpan(first.span)
}
Expand Down
6 changes: 3 additions & 3 deletions docs/docs/internals/syntax.md
Original file line number Diff line number Diff line change
Expand Up @@ -185,9 +185,9 @@ Types ::= Type {‘,’ Type}

### Expressions
```ebnf
Expr ::= FunParams ‘=>’ Expr Function(args, expr), Function(ValDef([implicit], id, TypeTree(), EmptyTree), expr)
Expr ::= FunParams (‘=>’ | ‘?=>’) Expr Function(args, expr), Function(ValDef([implicit], id, TypeTree(), EmptyTree), expr)
| Expr1
BlockResult ::= FunParams ‘=>’ Block
BlockResult ::= FunParams (‘=>’ | ‘?=>’) Block
| Expr1
FunParams ::= Bindings
| id
Expand Down Expand Up @@ -322,7 +322,7 @@ ClosureMods ::= { ‘implicit’ | ‘given’}

### Bindings and Imports
```ebnf
Bindings ::= ‘(’ [[‘using’] Binding {‘,’ Binding}] ‘)’
Bindings ::= ‘(’ [Binding {‘,’ Binding}] ‘)’
Binding ::= (id | ‘_’) [‘:’ Type] ValDef(_, id, tpe, EmptyTree)

Modifier ::= LocalModifier
Expand Down
4 changes: 2 additions & 2 deletions docs/docs/reference/contextual/context-functions-spec.md
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ trait ContextFunctionN[-T1 , ... , -TN, +R] {
Context function types erase to normal function types, so these classes are
generated on the fly for typechecking, but not realized in actual code.

Context function literals `(using x1: T1, ..., xn: Tn) => e` map
Context function literals `(x1: T1, ..., xn: Tn) ?=> e` map
context parameters `xi` of types `Ti` to the result of evaluating the expression `e`.
The scope of each context parameter `xi` is `e`. The parameters must have pairwise distinct names.

Expand All @@ -54,7 +54,7 @@ Note: The closing paragraph of the
[Anonymous Functions section](https://www.scala-lang.org/files/archive/spec/2.12/06-expressions.html#anonymous-functions)
of Scala 2.12 is subsumed by context function types and should be removed.

Context function literals `(using x1: T1, ..., xn: Tn) => e` are
Context function literals `(x1: T1, ..., xn: Tn) ?=> e` are
automatically created for any expression `e` whose expected type is
`scala.ContextFunctionN[T1, ..., Tn, R]`, unless `e` is
itself a context function literal. This is analogous to the automatic
Expand Down
16 changes: 8 additions & 8 deletions docs/docs/reference/contextual/context-functions.md
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ Conversely, if the expected type of an expression `E` is a context function type
`(T_1, ..., T_n) ?=> U` and `E` is not already an
context function literal, `E` is converted to a context function literal by rewriting it to
```scala
(using x_1: T1, ..., x_n: Tn) => E
(x_1: T1, ..., x_n: Tn) ?=> E
```
where the names `x_1`, ..., `x_n` are arbitrary. This expansion is performed
before the expression `E` is typechecked, which means that `x_1`, ..., `x_n`
Expand All @@ -39,12 +39,12 @@ For example, continuing with the previous definitions,
```scala
def g(arg: Executable[Int]) = ...

g(22) // is expanded to g((using ev: ExecutionContext) => 22)
g(22) // is expanded to g((ev: ExecutionContext) ?=> 22)

g(f(2)) // is expanded to g((using ev: ExecutionContext) => f(2)(using ev))
g(f(2)) // is expanded to g((ev: ExecutionContext) ?=> f(2)(using ev))

g(ExecutionContext ?=> f(3)) // is expanded to g((using ev: ExecutionContext) => f(3)(using ev))
g((using ctx: ExecutionContext) => f(22)(using ctx)) // is left as it is
g((ctx: ExecutionContext) ?=> f(3)) // is expanded to g((ctx: ExecutionContext) ?=> f(3)(using ctx))
g((ctx: ExecutionContext) ?=> f(3)(using ctx)) // is left as it is
```

### Example: Builder Pattern
Expand Down Expand Up @@ -102,14 +102,14 @@ that would otherwise be necessary.
```
With that setup, the table construction code above compiles and expands to:
```scala
table { (using $t: Table) =>
table { ($t: Table) ?=>

row { (using $r: Row) =>
row { ($r: Row) ?=>
cell("top left")(using $r)
cell("top right")(using $r)
}(using $t)

row { (using $r: Row) =>
row { ($r: Row) ?=>
cell("bottom left")(using $r)
cell("bottom right")(using $r)
}(using $t)
Expand Down
4 changes: 2 additions & 2 deletions staging/src/scala/quoted/staging/staging.scala
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ package object staging:
*
* Usage:
* ```
* val e: T = run { // (using Quotes) =>
* val e: T = run { // (quotes: Quotes) ?=>
* expr
* }
* ```
Expand All @@ -23,7 +23,7 @@ package object staging:
*
* Usage:
* ```
* val e: T = withQuotes { // (using Quotes) =>
* val e: T = withQuotes { // (quotes: Quotes) ?=>
* thunk
* }
* ```
Expand Down
8 changes: 4 additions & 4 deletions tests/disabled/run/tupled-function-extension-method.scala
Original file line number Diff line number Diff line change
Expand Up @@ -17,11 +17,11 @@ object Test {
println(f3(1, 2, 3))
println(f25(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25))

val if1 = new Expr((using i: Int) => Tuple1(i))
val if2 = new Expr((using i: Int, j: Int) => (i, i + j))
val if3 = new Expr((using i: Int, j: Int, k: Int) => (i, i + j, i + j + k))
val if1 = new Expr((i: Int) ?=> Tuple1(i))
val if2 = new Expr((i: Int, j: Int) ?=> (i, i + j))
val if3 = new Expr((i: Int, j: Int, k: Int) ?=> (i, i + j, i + j + k))
val if25 = new Expr(
(using x1: Int, x2: Int, x3: Int, x4: Int, x5: Int, x6: Int, x7: Int, x8: Int, x9: Int, x10: Int, x11: Int, x12: Int, x13: Int, x14: Int, x15: Int, x16: Int, x17: Int, x18: Int, x19: Int, x20: Int, x21: Int, x22: Int, x23: Int, x24: Int, x25: Int) =>
(x1: Int, x2: Int, x3: Int, x4: Int, x5: Int, x6: Int, x7: Int, x8: Int, x9: Int, x10: Int, x11: Int, x12: Int, x13: Int, x14: Int, x15: Int, x16: Int, x17: Int, x18: Int, x19: Int, x20: Int, x21: Int, x22: Int, x23: Int, x24: Int, x25: Int) ?=>
(x1, x2, x3, x4, x5, x6, x7, x8, x9, x10, x11, x12, x13, x14, x15, x16, x17, x18, x19, x20, x21, x22, x23, x24, x25)
)

Expand Down
4 changes: 2 additions & 2 deletions tests/neg/i2006.scala
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ object Test {
inline def bar(f: Int ?=> Int) = f // error

def main(args: Array[String]) = {
foo((using thisTransaction) => 43)
bar((using thisTransaction) => 44)
foo(thisTransaction ?=> 43)
bar(thisTransaction ?=> 44)
}
}
2 changes: 1 addition & 1 deletion tests/neg/i2146.scala
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
class Test {
def foo[A, B]: A ?=> B ?=> Int = { (using b: B) => // error: found Int, required: A ?=> B ?=> Int
def foo[A, B]: A ?=> B ?=> Int = { (b: B) ?=> // error: found Int, required: A ?=> B ?=> Int
42
}
}
5 changes: 2 additions & 3 deletions tests/neg/i2514a.scala
Original file line number Diff line number Diff line change
@@ -1,11 +1,10 @@
object Foo {
def foo(): Int = {
val f: Int ?=> Int = (using x: Int) => 2 * x
val f: Int ?=> Int = (x: Int) ?=> 2 * x
f(using 2)
}

val f = implicit (x: Int) => x

((using x: Int) => x): (Int ?=> Int) // error: no implicit argument found
((x: Int) ?=> x): (Int ?=> Int) // error: no implicit argument found
}

2 changes: 1 addition & 1 deletion tests/neg/i4668.scala
Original file line number Diff line number Diff line change
Expand Up @@ -8,4 +8,4 @@ trait Functor[F[_]] { def map[A,B](x: F[A])(f: A => B): F[B] }
object Functor { implicit object listFun extends Functor[List] { def map[A,B](ls: List[A])(f: A => B) = ls.map(f) } }

val map: (A:Type,B:Type,F:Type1) ?=> (Functor[F.T]) ?=> (F.T[A.T]) => (A.T => B.T) => F.T[B.T] =
(using fun) => (using x) => f => fun.map(x)(f) // error
fun ?=> x => f => fun.map(x)(f) // error
4 changes: 2 additions & 2 deletions tests/neg/scoped-quoted-expr-proto.scala
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,7 @@ package b {
}
}

r { (using qctx) =>
r { qctx ?=>
var escaped: qctx.Expr[Double] = ???
q{ (x: Double) =>
s{
Expand Down Expand Up @@ -127,7 +127,7 @@ package c {
}
}

r { (using qctx) =>
r { qctx ?=>
var escaped: qctx.Expr[Double] = ???
q{ (x: Double) =>
s{
Expand Down
2 changes: 1 addition & 1 deletion tests/pos/case-getters.scala
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
case class Foo(x: 1, y: Int ?=> Int)
object Test {
val f = Foo(1, (using i: Int) => i)
val f = Foo(1, (i: Int) ?=> i)
val fx1: 1 = f.x
val fx2: 1 = f._1
val fy1: Int = f.y(using 1)
Expand Down
2 changes: 1 addition & 1 deletion tests/pos/i5966.scala
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
object Test {
def foo = (using v: Int) => (x: Int) => v + x
def foo = (v: Int) ?=> (x: Int) => v + x
given myInt as Int = 4

foo.apply(1)
Expand Down
2 changes: 1 addition & 1 deletion tests/pos/i6862/lib_1.scala
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
trait Ctx
inline def foo(): Unit = (using x: Ctx) => ()
inline def foo(): Unit = (x: Ctx) ?=> ()
def bar[T](b: Ctx ?=> Unit): Unit = ???
2 changes: 1 addition & 1 deletion tests/pos/i6863/lib_1.scala
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
trait Ctx
inline def foo(): Unit = (using x: Ctx) => ()
inline def foo(): Unit = (x: Ctx) ?=> ()
2 changes: 1 addition & 1 deletion tests/pos/ift-assign.scala
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
class Context

object Test {
var f: Context ?=> String = ((using _) => "")
var f: Context ?=> String = (_ ?=> "")

f = f

Expand Down
2 changes: 1 addition & 1 deletion tests/pos/inline-apply.scala
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ object Test {
def transform()(implicit ctx: Context) = {
inline def withLocalOwner[T](op: Context ?=> T) = op(using ctx)

withLocalOwner { (using ctx) => }
withLocalOwner { ctx ?=> }

}
}
6 changes: 3 additions & 3 deletions tests/pos/reference/delegates.scala
Original file line number Diff line number Diff line change
Expand Up @@ -78,12 +78,12 @@ object Instances extends Common:
println(minimum(xs))

case class Context(value: String)
val c0: Context ?=> String = (using ctx) => ctx.value
val c1: Context ?=> String = (using ctx: Context) => ctx.value
val c0: Context ?=> String = ctx ?=> ctx.value
val c1: Context ?=> String = (ctx: Context) ?=> ctx.value

class A
class B
val ab: (x: A, y: B) ?=> Int = (using a: A, b: B) => 22
val ab: (x: A, y: B) ?=> Int = (a: A, b: B) ?=> 22

trait TastyAPI:
type Symbol
Expand Down
Loading