Skip to content

Commit

Permalink
Update tests and docs to new extension syntax
Browse files Browse the repository at this point in the history
  • Loading branch information
odersky committed Jun 30, 2020
1 parent ebd16a1 commit 6ff952d
Show file tree
Hide file tree
Showing 110 changed files with 312 additions and 302 deletions.
2 changes: 1 addition & 1 deletion compiler/src/dotty/tools/dotc/typer/Inferencing.scala
Original file line number Diff line number Diff line change
Expand Up @@ -530,7 +530,7 @@ trait Inferencing { this: Typer =>
* is in i7558.scala:
*
* type Tr[+V1, +O1 <: V1]
* def [V2, O2 <: V2](tr: Tr[V2, O2]) sl: Tr[V2, O2] = ???
* extension [V2, O2 <: V2](tr: Tr[V2, O2]) def sl: Tr[V2, O2] = ???
* def as[V3, O3 <: V3](tr: Tr[V3, O3]) : Tr[V3, O3] = tr.sl
*
* Here we interpolate at some point V2 and O2 given the constraint
Expand Down
2 changes: 1 addition & 1 deletion docs/docs/contributing/debugging.md
Original file line number Diff line number Diff line change
Expand Up @@ -88,7 +88,7 @@ But you can also do:
assertPositioned(tree.reporting(s"Tree is: $result"))
```

`def (a: A).reporting(f: WrappedResult[T] ?=> String, p: Printer = Printers.default): A` is defined on all types. The function `f` can be written without the argument since it is a context function`. The `result` variable is a part of the `WrapperResult` – a tiny framework powering the `reporting` function. Basically, whenever you are using `reporting` on an object `A`, you can use the `result: A` variable from this function and it will be equal to the object you are calling `reporting` on.
`extension (a: A) def reporting(f: WrappedResult[T] ?=> String, p: Printer = Printers.default): A` is defined on all types. The function `f` can be written without the argument since it is a context function`. The `result` variable is a part of the `WrapperResult` – a tiny framework powering the `reporting` function. Basically, whenever you are using `reporting` on an object `A`, you can use the `result: A` variable from this function and it will be equal to the object you are calling `reporting` on.

## Printing out trees after phases
To print out the trees you are compiling after the FrontEnd (scanner, parser, namer, typer) phases:
Expand Down
4 changes: 2 additions & 2 deletions docs/docs/reference/contextual/context-functions.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ the same way methods with context parameters are applied. For instance:
given ec as ExecutionContext = ...

def f(x: Int): ExecutionContext ?=> Int = ...

// could be written as follows with the type alias from above
// def f(x: Int): Executable[Int] = ...

Expand Down Expand Up @@ -125,7 +125,7 @@ object PostConditions {

def result[T](using r: WrappedResult[T]): T = r

def [T] (x: T).ensuring(condition: WrappedResult[T] ?=> Boolean): T = {
extension [T](x: T) def ensuring(condition: WrappedResult[T] ?=> Boolean): T = {
assert(condition(using x))
x
}
Expand Down
6 changes: 4 additions & 2 deletions docs/docs/reference/contextual/derivation-macro.md
Original file line number Diff line number Diff line change
Expand Up @@ -120,7 +120,8 @@ Alternatively and what is shown below is that we can call the `eqv` method
directly. The `eqGen` can trigger the derivation.

```scala
inline def [T](x: =>T) === (y: =>T)(using eq: Eq[T]): Boolean = eq.eqv(x, y)
extension [T](x: =>T)
inline def === (y: =>T)(using eq: Eq[T]): Boolean = eq.eqv(x, y)

implicit inline def eqGen[T]: Eq[T] = ${ Eq.derived[T] }
```
Expand Down Expand Up @@ -216,7 +217,8 @@ object Eq {
}

object Macro3 {
inline def [T](x: =>T) === (y: =>T)(using eq: Eq[T]): Boolean = eq.eqv(x, y)
extension [T](x: =>T)
inline def === (y: =>T)(using eq: Eq[T]): Boolean = eq.eqv(x, y)

implicit inline def eqGen[T]: Eq[T] = ${ Eq.derived[T] }
}
Expand Down
4 changes: 2 additions & 2 deletions docs/docs/reference/contextual/givens.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,8 @@ that serve for synthesizing arguments to [context parameters](./using-clauses.ht
```scala
trait Ord[T] {
def compare(x: T, y: T): Int
def (x: T) < (y: T) = compare(x, y) < 0
def (x: T) > (y: T) = compare(x, y) > 0
extension (x: T) def < (y: T) = compare(x, y) < 0
extension (x: T) def > (y: T) = compare(x, y) > 0
}

given intOrd as Ord[Int] {
Expand Down
2 changes: 1 addition & 1 deletion docs/docs/reference/contextual/relationship-implicits.md
Original file line number Diff line number Diff line change
Expand Up @@ -111,7 +111,7 @@ will map to with clauses instead.

Extension methods have no direct counterpart in Scala 2, but they can be simulated with implicit classes. For instance, the extension method
```scala
def (c: Circle).circumference: Double = c.radius * math.Pi * 2
extension (c: Circle) def circumference: Double = c.radius * math.Pi * 2
```
could be simulated to some degree by
```scala
Expand Down
2 changes: 1 addition & 1 deletion docs/docs/reference/contextual/type-classes.md
Original file line number Diff line number Diff line change
Expand Up @@ -156,7 +156,7 @@ A `List` can be turned into a monad via this `given` instance:
given listMonad as Monad[List]:
def pure[A](x: A): List[A] =
List(x)
extension def [A, B](xs: List[A])
extension [A, B](xs: List[A])
def flatMap(f: A => List[B]): List[B] =
xs.flatMap(f) // rely on the existing `flatMap` method of `List`
```
Expand Down
2 changes: 1 addition & 1 deletion docs/docs/reference/dropped-features/package-objects.md
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ def b = a._2
case class C()

implicit object Cops {
def (x: C).pair(y: C) = (x, y)
extension (x: C) def pair(y: C) = (x, y)
}
```
There may be several source files in a package containing such toplevel definitions, and source files can freely mix toplevel value, method, and type definitions with classes and objects.
Expand Down
2 changes: 1 addition & 1 deletion docs/docs/reference/metaprogramming/macros.md
Original file line number Diff line number Diff line change
Expand Up @@ -717,7 +717,7 @@ This might be used to then perform an implicit search as in:


```scala
inline def (inline sc: StringContext).showMe(inline args: Any*): String = ${ showMeExpr('sc, 'args) }
extension (inline sc: StringContext) inline def showMe(inline args: Any*): String = ${ showMeExpr('sc, 'args) }

private def showMeExpr(sc: Expr[StringContext], argsExpr: Expr[Seq[Any]])(using QuoteContext): Expr[String] = {
argsExpr match {
Expand Down
16 changes: 8 additions & 8 deletions docs/docs/reference/other-new-features/opaques.md
Original file line number Diff line number Diff line change
Expand Up @@ -29,15 +29,15 @@ object Logarithms {
}
```

This introduces `Logarithm` as a new abstract type, which is implemented as `Double`.
This introduces `Logarithm` as a new abstract type, which is implemented as `Double`.
The fact that `Logarithm` is the same as `Double` is only known in the scope where
`Logarithm` is defined which in the above example corresponds to the object `Logarithms`.
Or in other words, within the scope it is treated as type alias, but this is opaque to the outside world
Or in other words, within the scope it is treated as type alias, but this is opaque to the outside world
where in consequence `Logarithm` is seen as an abstract type and has nothing to do with `Double`.

The public API of `Logarithm` consists of the `apply` and `safe` methods defined in the companion object.
The public API of `Logarithm` consists of the `apply` and `safe` methods defined in the companion object.
They convert from `Double`s to `Logarithm` values. Moreover, a collective extension `logarithmOps` provides the extension methods `toDouble` that converts the other way,
and operations `+` and `*` on `Logarithm` values.
and operations `+` and `*` on `Logarithm` values.
The following operations would be valid because they use functionality implemented in the `Logarithms` object.

```scala
Expand Down Expand Up @@ -68,10 +68,10 @@ object Access {
opaque type PermissionChoice = Int
opaque type Permission <: Permissions & PermissionChoice = Int

def (x: Permissions) & (y: Permissions): Permissions = x | y
def (x: PermissionChoice) | (y: PermissionChoice): PermissionChoice = x | y
def (granted: Permissions).is(required: Permissions) = (granted & required) == required
def (granted: Permissions).isOneOf(required: PermissionChoice) = (granted & required) != 0
extension (x: Permissions) def & (y: Permissions): Permissions = x | y
extension (x: PermissionChoice) def | (y: PermissionChoice): PermissionChoice = x | y
extension (granted: Permissions) def is(required: Permissions) = (granted & required) == required
extension (granted: Permissions) def isOneOf(required: PermissionChoice) = (granted & required) != 0

val NoPermission: Permission = 0
val Read: Permission = 1
Expand Down
4 changes: 2 additions & 2 deletions docs/docs/reference/other-new-features/quoted-pattern-spec.md
Original file line number Diff line number Diff line change
Expand Up @@ -46,11 +46,11 @@ type Env
def notMatched = None
def matched = Some(()) // aka Some(Tuple0())
def matched[T](x: T) = Some(Tuple1(x))
def (x: Matching) && (y: Matching) = if (x == None || y == None) None else Some(x.get ++ y.get)
extension (x: Matching) def && (y: Matching) = if (x == None || y == None) None else Some(x.get ++ y.get)
def fold[T](m: Mattching*)(using Env): Matching = m.fold(matched)(_ && _)

// `a =#= b` stands for `a` matches `b`
def (scrutinee: Tree) =#= pattern: Tree)(using Env): Matching // described by cases in the tables below
extension (scrutinee: Tree) def =#= pattern: Tree)(using Env): Matching // described by cases in the tables below

def envWith(equiv: (Symbol, Symbol)*)(using Env): Env // Adds to the current environment the fact that s1 from the scrutinee is equivalent to s2 in the pattern

Expand Down
9 changes: 6 additions & 3 deletions docs/docs/reference/other-new-features/tupled-function.md
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,8 @@ The following defines `tupled` as [extension method](../contextual/extension-met
* @tparam Args the tuple type with the same types as the function arguments of F
* @tparam R the return type of F
*/
def [F, Args <: Tuple, R](f: F).tupled(using tf: TupledFunction[F, Args => R]): Args => R = tf.tupled(f)
extension [F, Args <: Tuple, R](f: F)
def tupled(using tf: TupledFunction[F, Args => R]): Args => R = tf.tupled(f)
```

`TupledFunction` can be used to generalize the `Function.untupled` to a function of any arities ([full example](https://github.com/lampepfl/dotty/blob/master/tests/run/tupled-function-untupled.scala))
Expand All @@ -59,7 +60,8 @@ def [F, Args <: Tuple, R](f: F).tupled(using tf: TupledFunction[F, Args => R]):
* @tparam Args the tuple type with the same types as the function arguments of F
* @tparam R the return type of F
*/
def [F, Args <: Tuple, R](f: Args => R).untupled(using tf: TupledFunction[F, Args => R]): F = tf.untupled(f)
extension [F, Args <: Tuple, R](f: Args => R)
def untupled(using tf: TupledFunction[F, Args => R]): F = tf.untupled(f)
```

`TupledFunction` can also be used to generalize the [`Tuple1.compose`](https://github.com/lampepfl/dotty/blob/master/tests/run/tupled-function-compose.scala) and [`Tuple1.andThen`](https://github.com/lampepfl/dotty/blob/master/tests/run/tupled-function-andThen.scala) methods to compose functions of larger arities and with functions that return tuples.
Expand All @@ -73,7 +75,8 @@ def [F, Args <: Tuple, R](f: Args => R).untupled(using tf: TupledFunction[F, Arg
* @tparam GArgs the tuple type with the same types as the function arguments of G
* @tparam R the return type of F
*/
def [F, G, FArgs <: Tuple, GArgs <: Tuple, R](f: F).compose(g: G)(using tg: TupledFunction[G, GArgs => FArgs], tf: TupledFunction[F, FArgs => R]): GArgs => R = {
extension [F, G, FArgs <: Tuple, GArgs <: Tuple, R](f: F)
def compose(g: G)(using tg: TupledFunction[G, GArgs => FArgs], tf: TupledFunction[F, FArgs => R]): GArgs => R = {
(x: GArgs) => tf.tupled(f)(tg.tupled(g)(x))
}
```
2 changes: 1 addition & 1 deletion tests/init/crash/i5606.scala
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
object Test extends App {

def [A, B](f: A => B) `$` (a: A): B = f(a)
extension [A, B](f: A => B) def `$` (a: A): B = f(a)

assert((((a: Int) => a.toString()) `$` 10) == "10")

Expand Down
4 changes: 2 additions & 2 deletions tests/init/crash/i7821.scala
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ object XObject {
def anX: X = 5

given ops as Object {
def (x: X) + (y: X): X = x + y
extension (x: X) def + (y: X): X = x + y
}
}

Expand All @@ -14,7 +14,7 @@ object MyXObject {
def anX: MyX = XObject.anX

given ops as Object {
def (x: MyX) + (y: MyX): MyX = x + y // error: warring: Infinite recursive call
extension (x: MyX) def + (y: MyX): MyX = x + y // error: warring: Infinite recursive call
}
}

Expand Down
4 changes: 2 additions & 2 deletions tests/neg-custom-args/fatal-warnings/i7821.scala
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ object XObject {
def anX: X = 5

given ops as Object {
def (x: X) + (y: X): X = x + y
extension (x: X) def + (y: X): X = x + y
}
}

Expand All @@ -14,7 +14,7 @@ object MyXObject {
def anX: MyX = XObject.anX

given ops as Object {
def (x: MyX) + (y: MyX): MyX = x + y // error: warring: Infinite recursive call
extension (x: MyX) def + (y: MyX): MyX = x + y // error: warring: Infinite recursive call
}
}

Expand Down
6 changes: 3 additions & 3 deletions tests/neg-custom-args/infix.scala
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,9 @@ class C:

object C:
given AnyRef:
def (x: C) iop (y: Int) = ???
def (x: C).mop (y: Int) = ???
def (x: C) ++ (y: Int) = ???
extension (x: C) @infix def iop (y: Int) = ???
extension (x: C) def mop (y: Int) = ???
extension (x: C) def ++ (y: Int) = ???

val c = C()
def test() = {
Expand Down
2 changes: 1 addition & 1 deletion tests/neg-macros/i6432/Macro_1.scala
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import scala.quoted.autolift


object Macro {
inline def (inline sc: StringContext).foo(args: String*): Unit = ${ impl('sc) }
extension (inline sc: StringContext) inline def foo(args: String*): Unit = ${ impl('sc) }

def impl(sc: Expr[StringContext])(using qctx: QuoteContext) : Expr[Unit] = {
import qctx.tasty._
Expand Down
2 changes: 1 addition & 1 deletion tests/neg-macros/i6432b/Macro_1.scala
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import scala.quoted.autolift


object Macro {
inline def (inline sc: StringContext).foo(args: String*): Unit = ${ impl('sc) }
extension (inline sc: StringContext) inline def foo(args: String*): Unit = ${ impl('sc) }

def impl(sc: Expr[StringContext])(using qctx: QuoteContext) : Expr[Unit] = {
import qctx.tasty._
Expand Down
2 changes: 1 addition & 1 deletion tests/neg-macros/i7698.scala
Original file line number Diff line number Diff line change
Expand Up @@ -10,4 +10,4 @@ def showInterpolatorImpl(sc: Expr[StringContext], argsExpr: Expr[Seq[Any]])(usin
case '[ Int ] => // error
???

inline def (inline sc: StringContext) show (args: Any*): String = ${ showInterpolatorImpl('sc, 'args) }
extension (inline sc: StringContext) inline def show (args: Any*): String = ${ showInterpolatorImpl('sc, 'args) }
2 changes: 1 addition & 1 deletion tests/neg-macros/reflect-inline/assert_1.scala
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import scala.quoted._

object api {
inline def (inline x: String).stripMargin2: String =
extension (inline x: String) inline def stripMargin2: String =
${ stripImpl('x) }

private def stripImpl(x: Expr[String])(using qctx: QuoteContext): Expr[String] =
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import scala.language.implicitConversions

object Macro {

implicit inline def (strCtx: => StringContext).f2(args: =>Any*): String = ${FIntepolator.apply('strCtx, 'args)}
extension (strCtx: => StringContext) implicit inline def f2(args: =>Any*): String = ${FIntepolator.apply('strCtx, 'args)}

}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import scala.language.implicitConversions

object Macro {

implicit inline def (strCtx: => StringContext).f3(args: =>Any*): String = ${FIntepolator.apply('strCtx, 'args)}
extension (strCtx: => StringContext) implicit inline def f3(args: =>Any*): String = ${FIntepolator.apply('strCtx, 'args)}

}

Expand Down
6 changes: 3 additions & 3 deletions tests/neg/capture1.scala
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,11 @@ object Test extends App {

val l: mutable.Seq[String] = mutable.ArrayBuffer()

def [T, U](xs: List[T]) emap (f: T => U): List[U] = xs.map(f)
extension [T, U](xs: List[T]) def emap (f: T => U): List[U] = xs.map(f)

def [T](xs: List[T]) ereduce (f: (T, T) => T): T = xs.reduceLeft(f)
extension [T](xs: List[T]) def ereduce (f: (T, T) => T): T = xs.reduceLeft(f)

def [T](xs: mutable.Seq[T]) append (ys: mutable.Seq[T]): mutable.Seq[T] = xs ++ ys
extension [T](xs: mutable.Seq[T]) def append (ys: mutable.Seq[T]): mutable.Seq[T] = xs ++ ys

List(l, mutable.ArrayBuffer(1))
.emap(list => list)
Expand Down
8 changes: 4 additions & 4 deletions tests/neg/extension-method-not-allowed.check
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
-- Error: tests/neg/extension-method-not-allowed.scala:3:8 -------------------------------------------------------------
3 | def (c: T).f: T = ??? // error : No extension method allowed here
| ^^^^^^^^^^^^^^^^^^^^^
| no extension method allowed here since leading parameter was already given
-- Error: tests/neg/extension-method-not-allowed.scala:3:15 ------------------------------------------------------------
3 | extension (c: T) def f: T = ??? // error : No extension method allowed here
| ^^^^^^^^^^^^^^^^^^^^
| extension clause can only define methods
2 changes: 1 addition & 1 deletion tests/neg/extension-method-not-allowed.scala
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
object Test {
extension on[T] (t: T) {
def (c: T).f: T = ??? // error : No extension method allowed here
extension (c: T) def f: T = ??? // error : No extension method allowed here
}
}
4 changes: 2 additions & 2 deletions tests/neg/extension-methods.scala
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
object Test {

implicit object O {
def (x: String).l1 = x.length
extension (x: String) def l1 = x.length
def l1(x: Int) = x * x
def l2(x: String) = x.length
}
Expand All @@ -11,7 +11,7 @@ object Test {
1.l1 // error

extension on [T](xs: List[T]) {
def (x: Int).f1: T = ??? // error: No extension method allowed here, since collective parameters are given
extension (x: Int) def f1: T = ??? // error: No extension method allowed here, since collective parameters are given
def f2[T]: T = ??? // error: T is already defined as type T
def f3(xs: List[T]) = ??? // error: xs is already defined as value xs
}
Expand Down
4 changes: 2 additions & 2 deletions tests/neg/extmethod-override.scala
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
class A {
def f(x: Int)(y: Int): Int = 0
def (x: Int).g(y: Int): Int = 1
extension (x: Int) def g(y: Int): Int = 1
}
class B extends A {
override def (x: Int).f(y: Int): Int = 1 // error
extension (x: Int) override def f(y: Int): Int = 1 // error
override def g(x: Int)(y: Int): Int = 0 // error
}
10 changes: 5 additions & 5 deletions tests/neg/i5773.scala
Original file line number Diff line number Diff line change
@@ -1,16 +1,16 @@
trait Semigroup[T] {
def (lhs: T).append(rhs: T): T
def (lhs: Int).appendS(rhs: T): T = ???
extension (lhs: T) def append(rhs: T): T
extension (lhs: Int) def appendS(rhs: T): T = ???
}

object Semigroup {
implicit object stringAppend extends Semigroup[String] {
override def (lhs: String).append(rhs: String): String = lhs + rhs
extension (lhs: String) override def append(rhs: String): String = lhs + rhs
}

implicit def sumSemigroup[N](implicit N: Numeric[N]): Semigroup[N] = new {
override def (lhs: N).append(rhs: N): N = N.plus(lhs, rhs)
def (lhs: Int).appendS(rhs: N): N = ??? // N.plus(lhs, rhs)
extension (lhs: N) override def append(rhs: N): N = N.plus(lhs, rhs)
extension (lhs: Int) def appendS(rhs: N): N = ??? // N.plus(lhs, rhs)
}
}

Expand Down
8 changes: 4 additions & 4 deletions tests/neg/i6662.scala
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
opaque type Opt[A >: Null] = A

inline def [A >: Null](x: Opt[A]) nonEmpty: Boolean = x.get != null // error: Implementation restriction
inline def [A >: Null](x: Opt[A]) isEmpty: Boolean = x.get == null // error: Implementation restriction
inline def [A >: Null](x: Opt[A]) isDefined: Boolean = x.nonEmpty // error: Implementation restriction
inline def [A >: Null](x: Opt[A]) get: A = Opt.unOpt(x) // error: Implementation restriction
extension [A >: Null](x: Opt[A]) inline def nonEmpty: Boolean = x.get != null // error: Implementation restriction
extension [A >: Null](x: Opt[A]) inline def isEmpty: Boolean = x.get == null // error: Implementation restriction
extension [A >: Null](x: Opt[A]) inline def isDefined: Boolean = x.nonEmpty // error: Implementation restriction
extension [A >: Null](x: Opt[A]) inline def get: A = Opt.unOpt(x) // error: Implementation restriction

object Opt
{
Expand Down
2 changes: 1 addition & 1 deletion tests/neg/i6762b.scala
Original file line number Diff line number Diff line change
Expand Up @@ -9,5 +9,5 @@ type Liftable
given Liftable = ???

implicit object ExprOps {
def [T](x: T).toExpr(using Liftable): Expr[T] = ???
extension [T](x: T) def toExpr(using Liftable): Expr[T] = ???
}
Loading

0 comments on commit 6ff952d

Please sign in to comment.