Skip to content

Commit

Permalink
Merge pull request #10487 from dotty-staging/change-ift-params
Browse files Browse the repository at this point in the history
Fix #10484: Switch back to old context function closure syntax
  • Loading branch information
nicolasstucki authored Nov 27, 2020
2 parents 8059fce + 1ec425a commit 9c04baa
Show file tree
Hide file tree
Showing 30 changed files with 101 additions and 108 deletions.
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

0 comments on commit 9c04baa

Please sign in to comment.