From 9a0ddcd7fd32f5bb88f3d8270a125975a1fa32fd Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Tue, 7 Jan 2020 14:41:08 +0100 Subject: [PATCH 01/11] Syntax Change: Allow '.' in front of extension method Allow def (c: Circle).circumference: Double alongside def (c: Circle) circumference: Double The syntax with '.' is preferred for normal methods, which have names starting with a letter and which are not declared @infix. Right now, this preference is not enforced. --- .../dotty/tools/dotc/parsing/Parsers.scala | 8 +++-- docs/docs/contributing/debugging.md | 2 +- docs/docs/internals/syntax.md | 2 +- .../reference/contextual/extension-methods.md | 20 +++++++----- .../contextual/implicit-function-types.md | 2 +- .../contextual/relationship-implicits.md | 2 +- docs/docs/reference/contextual/typeclasses.md | 6 ++-- .../dropped-features/package-objects.md | 2 +- docs/docs/reference/metaprogramming/macros.md | 2 +- .../reference/other-new-features/opaques.md | 4 +-- .../other-new-features/tupled-function.md | 6 ++-- library/src-bootstrapped/scala/IArray.scala | 30 ++++++++--------- .../neg-custom-args/extmethods-tparams.scala | 2 +- tests/neg-macros/i6432/Macro_1.scala | 2 +- tests/neg-macros/i6432b/Macro_1.scala | 2 +- .../neg-macros/reflect-inline/assert_1.scala | 2 +- .../Macro_1.scala | 2 +- .../Macro_1.scala | 2 +- tests/neg/extension-methods.scala | 4 +-- tests/neg/extmethod-override.scala | 4 +-- tests/neg/i5773.scala | 10 +++--- tests/neg/i6762b.scala | 2 +- tests/neg/i6779.scala | 2 +- tests/neg/i7438.scala | 4 +-- tests/neg/indent.scala | 2 +- tests/neg/opaque-bounds.scala | 4 +-- tests/pos/combine.scala | 2 +- tests/pos/consume.scala | 2 +- tests/pos/i5773.scala | 10 +++--- tests/pos/i5773a.scala | 6 ++-- tests/pos/i6395.scala | 4 +-- tests/pos/i7070.scala | 2 +- tests/pos/i7119.scala | 2 +- tests/pos/i7413.scala | 2 +- tests/pos/i7700.scala | 2 +- tests/pos/indent.scala | 2 +- tests/pos/opaque-propability-xm.scala | 10 +++--- tests/pos/opaque-xm.scala | 2 +- tests/pos/reference/delegates.scala | 32 +++++++++---------- tests/pos/reference/extension-methods.scala | 6 ++-- tests/pos/reference/opaque.scala | 4 +-- .../pos/toplevel-opaque-xm/Logarithm_1.scala | 2 +- .../f-interpolator-neg/Macros_1.scala | 2 +- tests/run/Pouring.scala | 2 +- tests/run/eq-xmethod.scala | 2 +- tests/run/exports.scala | 2 +- tests/run/extension-methods.scala | 14 ++++---- tests/run/extmethod-overload.scala | 8 ++--- tests/run/extmethods2.scala | 4 +-- tests/run/instances-anonymous.scala | 14 ++++---- tests/run/instances.scala | 12 +++---- ...ng-context-implicits-with-conversion.scala | 2 +- tests/run/toplevel-implicits/a.b.scala | 2 +- tests/semanticdb/expect/Enums.scala | 4 +-- tests/semanticdb/expect/Givens.scala | 4 +-- 55 files changed, 150 insertions(+), 142 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/parsing/Parsers.scala b/compiler/src/dotty/tools/dotc/parsing/Parsers.scala index be0a43fec759..386694cee662 100644 --- a/compiler/src/dotty/tools/dotc/parsing/Parsers.scala +++ b/compiler/src/dotty/tools/dotc/parsing/Parsers.scala @@ -3105,7 +3105,7 @@ object Parsers { * | this ParamClause ParamClauses `=' ConstrExpr * DefDcl ::= DefSig `:' Type * DefSig ::= id [DefTypeParamClause] DefParamClauses - * | ExtParamClause [nl] id DefParamClauses + * | ExtParamClause [nl] [‘.’] id DefParamClauses */ def defDefOrDcl(start: Offset, mods: Modifiers): Tree = atSpan(start, nameStart) { def scala2ProcedureSyntax(resultTypeStr: String) = { @@ -3134,7 +3134,11 @@ object Parsers { makeConstructor(Nil, vparamss, rhs).withMods(mods).setComment(in.getDocComment(start)) } else { - def extParamss() = try paramClause(0, prefix = true) :: Nil finally newLineOpt() + def extParamss() = + try paramClause(0, prefix = true) :: Nil + finally + if in.token == DOT then in.nextToken() + else newLineOpt() val (leadingTparams, leadingVparamss, flags) = if in.token == LBRACKET then (typeParamClause(ParamOwner.Def), extParamss(), Method | Extension) diff --git a/docs/docs/contributing/debugging.md b/docs/docs/contributing/debugging.md index 5d3f7d96a9cc..62d1244002cd 100644 --- a/docs/docs/contributing/debugging.md +++ b/docs/docs/contributing/debugging.md @@ -88,7 +88,7 @@ But you can also do: assertPositioned(tree.reporting(s"Tree is: $result")) ``` -`def (a: A) reporting(f: given WrappedResult[T] => String, p: Printer = Printers.default): A` is defined on all types. The function `f` can be written without the argument since the argument is `given`. 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. +`def (a: A).reporting(f: given WrappedResult[T] => String, p: Printer = Printers.default): A` is defined on all types. The function `f` can be written without the argument since the argument is `given`. 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: diff --git a/docs/docs/internals/syntax.md b/docs/docs/internals/syntax.md index 6bbd74a65153..dfe6fe5957a9 100644 --- a/docs/docs/internals/syntax.md +++ b/docs/docs/internals/syntax.md @@ -358,7 +358,7 @@ ValDcl ::= ids ‘:’ Type VarDcl ::= ids ‘:’ Type PatDef(_, ids, tpe, EmptyTree) DefDcl ::= DefSig ‘:’ Type DefDef(_, name, tparams, vparamss, tpe, EmptyTree) DefSig ::= id [DefTypeParamClause] DefParamClauses - | ExtParamClause [nl] id DefParamClauses + | ExtParamClause [nl] [‘.’] id DefParamClauses TypeDcl ::= id [TypeParamClause] SubtypeBounds [‘=’ Type] TypeDefTree(_, name, tparams, bound Def ::= ‘val’ PatDef diff --git a/docs/docs/reference/contextual/extension-methods.md b/docs/docs/reference/contextual/extension-methods.md index 036f669ffe55..78ef20c07a19 100644 --- a/docs/docs/reference/contextual/extension-methods.md +++ b/docs/docs/reference/contextual/extension-methods.md @@ -8,7 +8,7 @@ Extension methods allow one to add methods to a type after the type is defined. ```scala case class Circle(x: Double, y: Double, radius: Double) -def (c: Circle) circumference: Double = c.radius * math.Pi * 2 +def (c: Circle).circumference: Double = c.radius * math.Pi * 2 ``` Like regular methods, extension methods can be invoked with infix `.`: @@ -42,7 +42,7 @@ As an example, consider an extension method `longestStrings` on `Seq[String]` de ```scala trait StringSeqOps { - def (xs: Seq[String]) longestStrings = { + def (xs: Seq[String]).longestStrings = { val maxLength = xs.map(_.length).max xs.filter(_.length == maxLength) } @@ -80,22 +80,26 @@ So `circle.circumference` translates to `CircleOps.circumference(circle)`, provi ### Operators -The extension method syntax also applies to the definition of operators. -In each case the definition syntax mirrors the way the operator is applied. +The extension method syntax also applies to the definition of operators. +In this case it is allowed and preferable to omit the period between the leading parameter list +and the operator. In each case the definition syntax mirrors the way the operator is applied. Examples: ```scala def (x: String) < (y: String) = ... def (x: Elem) +: (xs: Seq[Elem]) = ... +def (x: Number) min (y: Number) = ... "ab" < "c" 1 +: List(2, 3) +x min 3 ``` -The two definitions above translate to +The three definitions above translate to ```scala def < (x: String)(y: String) = ... def +: (xs: Seq[Elem])(x: Elem) = ... +def min(x: Number)(y: Number) = ... ``` -Note that swap of the two parameters `x` and `xs` when translating +Note the swap of the two parameters `x` and `xs` when translating the right-binding operator `+:` to an extension method. This is analogous to the implementation of right binding operators as normal methods. @@ -144,7 +148,7 @@ If a given extension is anonymous (as in the last clause), its name is synthesiz The extensions above are equivalent to the following regular given instances where the implemented parent is `AnyRef` and the parameters in the `extension` clause are repeated in each extension method definition: ```scala given stringOps: AnyRef { - def (xs: Seq[String]) longestStrings: Seq[String] = { + def (xs: Seq[String]).longestStrings: Seq[String] = { val maxLength = xs.map(_.length).max xs.filter(_.length == maxLength) } @@ -165,7 +169,7 @@ Here are the syntax changes for extension methods and given extensions relative to the [current syntax](../../internals/syntax.md). `extension` is a soft keyword, recognized only after a `given`. It can be used as an identifier everywhere else. ``` DefSig ::= ... - | ExtParamClause [nl] id DefParamClauses + | ExtParamClause [nl] [‘.’] id DefParamClauses GivenDef ::= ... [id ‘:’] ‘extension’ ExtParamClause {GivenParamClause} ExtMethods ExtParamClause ::= [DefTypeParamClause] ‘(’ DefParam ‘)’ diff --git a/docs/docs/reference/contextual/implicit-function-types.md b/docs/docs/reference/contextual/implicit-function-types.md index 49f3bae729f1..6ceaa6114a35 100644 --- a/docs/docs/reference/contextual/implicit-function-types.md +++ b/docs/docs/reference/contextual/implicit-function-types.md @@ -120,7 +120,7 @@ object PostConditions { def result[T](given r: WrappedResult[T]): T = r - def (x: T) ensuring[T](condition: (given WrappedResult[T]) => Boolean): T = { + def (x: T).ensuring[T](condition: (given WrappedResult[T]) => Boolean): T = { assert(condition(given x)) x } diff --git a/docs/docs/reference/contextual/relationship-implicits.md b/docs/docs/reference/contextual/relationship-implicits.md index e30ca53f421b..f9ac61f7ddf9 100644 --- a/docs/docs/reference/contextual/relationship-implicits.md +++ b/docs/docs/reference/contextual/relationship-implicits.md @@ -104,7 +104,7 @@ will map to given 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 +def (c: Circle).circumference: Double = c.radius * math.Pi * 2 ``` could be simulated to some degree by ```scala diff --git a/docs/docs/reference/contextual/typeclasses.md b/docs/docs/reference/contextual/typeclasses.md index 01b2fd048a32..3f82ee269231 100644 --- a/docs/docs/reference/contextual/typeclasses.md +++ b/docs/docs/reference/contextual/typeclasses.md @@ -11,7 +11,7 @@ with canonical implementations defined by given instances. Here are some example ```scala trait SemiGroup[T] with - def (x: T) combine (y: T): T + def (x: T).combine(y: T): T trait Monoid[T] extends SemiGroup[T] with def unit: T @@ -20,11 +20,11 @@ object Monoid with def apply[T](given Monoid[T]) = summon[Monoid[T]] given Monoid[String] with - def (x: String) combine (y: String): String = x.concat(y) + def (x: String).combine(y: String): String = x.concat(y) def unit: String = "" given Monoid[Int] with - def (x: Int) combine (y: Int): Int = x + y + def (x: Int).combine(y: Int): Int = x + y def unit: Int = 0 def sum[T: Monoid](xs: List[T]): T = diff --git a/docs/docs/reference/dropped-features/package-objects.md b/docs/docs/reference/dropped-features/package-objects.md index 2e0f51b25fb1..22ba96eb5f86 100644 --- a/docs/docs/reference/dropped-features/package-objects.md +++ b/docs/docs/reference/dropped-features/package-objects.md @@ -22,7 +22,7 @@ def b = a._2 case class C() implicit object Cops { - def (x: C) pair (y: C) = (x, y) + def (x: C).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. diff --git a/docs/docs/reference/metaprogramming/macros.md b/docs/docs/reference/metaprogramming/macros.md index 711756d37767..6cdaa66445fb 100644 --- a/docs/docs/reference/metaprogramming/macros.md +++ b/docs/docs/reference/metaprogramming/macros.md @@ -707,7 +707,7 @@ This might be used to then perform an implicit search as in: ```scala -inline def (sc: StringContext) showMe(args: =>Any*): String = ${ showMeExpr('sc, 'args) } +inline def (sc: StringContext).showMe(args: =>Any*): String = ${ showMeExpr('sc, 'args) } private def showMeExpr(sc: Expr[StringContext], argsExpr: Expr[Seq[Any]])(given qctx: QuoteContext): Expr[String] = { argsExpr match { diff --git a/docs/docs/reference/other-new-features/opaques.md b/docs/docs/reference/other-new-features/opaques.md index c15d439a5351..1390fadd2559 100644 --- a/docs/docs/reference/other-new-features/opaques.md +++ b/docs/docs/reference/other-new-features/opaques.md @@ -71,8 +71,8 @@ object Access { def (x: Permissions) & (y: Permissions): Permissions = x | y def (x: PermissionChoice) | (y: PermissionChoice): PermissionChoice = x | y - def (x: Permissions) is (y: Permissions) = (x & y) == y - def (x: Permissions) isOneOf (y: PermissionChoice) = (x & y) != 0 + def (x: Permissions).is(y: Permissions) = (x & y) == y + def (x: Permissions).isOneOf(y: PermissionChoice) = (x & y) != 0 val NoPermission: Permission = 0 val ReadOnly: Permission = 1 diff --git a/docs/docs/reference/other-new-features/tupled-function.md b/docs/docs/reference/other-new-features/tupled-function.md index 7dff362febd7..f2e836803662 100644 --- a/docs/docs/reference/other-new-features/tupled-function.md +++ b/docs/docs/reference/other-new-features/tupled-function.md @@ -43,7 +43,7 @@ Examples * @tparam Args the tuple type with the same types as the function arguments of F * @tparam R the return type of F */ -def (f: F) tupled[F, Args <: Tuple, R](given tf: TupledFunction[F, Args => R]): Args => R = tf.tupled(f) +def [F, Args <: Tuple, R](f: F).tupled(given tf: TupledFunction[F, Args => R]): Args => R = tf.tupled(f) ``` `TupledFunction` can be used to generalize the `Function.untupled` methods to functions of any arities ([full example](https://github.com/lampepfl/dotty/blob/master/tests/run/tupled-function-untupled.scala)) @@ -58,7 +58,7 @@ def (f: F) tupled[F, Args <: Tuple, R](given 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 => R) untupled[F, Args <: Tuple, R](given tf: TupledFunction[F, Args => R]): F = tf.untupled(f) +def [F, Args <: Tuple, R](f: Args => R).untupled(given 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. @@ -72,7 +72,7 @@ def (f: Args => R) untupled[F, Args <: Tuple, R](given 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: F) compose[F, G, FArgs <: Tuple, GArgs <: Tuple, R](g: G)(given tg: TupledFunction[G, GArgs => FArgs], tf: TupledFunction[F, FArgs => R]): GArgs => R = { +def [F, G, FArgs <: Tuple, GArgs <: Tuple, R](f: F).compose(g: G)(given tg: TupledFunction[G, GArgs => FArgs], tf: TupledFunction[F, FArgs => R]): GArgs => R = { (x: GArgs) => tf.tupled(f)(tg.tupled(g)(x)) } ``` diff --git a/library/src-bootstrapped/scala/IArray.scala b/library/src-bootstrapped/scala/IArray.scala index 7a2d2f68c39b..d387229f112b 100644 --- a/library/src-bootstrapped/scala/IArray.scala +++ b/library/src-bootstrapped/scala/IArray.scala @@ -16,27 +16,27 @@ object opaques * @param n the index of the element to select * @return the element of the array at the given index */ - def (arr: IArray[Byte]) apply (n: Int): Byte = arr.asInstanceOf[Array[Byte]].apply(n) - def (arr: IArray[Short]) apply (n: Int): Short = arr.asInstanceOf[Array[Short]].apply(n) - def (arr: IArray[Char]) apply (n: Int): Char = arr.asInstanceOf[Array[Char]].apply(n) - def (arr: IArray[Int]) apply (n: Int): Int = arr.asInstanceOf[Array[Int]].apply(n) - def (arr: IArray[Long]) apply (n: Int): Long = arr.asInstanceOf[Array[Long]].apply(n) - def (arr: IArray[Float]) apply (n: Int): Float = arr.asInstanceOf[Array[Float]].apply(n) - def (arr: IArray[Double]) apply (n: Int): Double = arr.asInstanceOf[Array[Double]].apply(n) + def (arr: IArray[Byte]).apply(n: Int): Byte = arr.asInstanceOf[Array[Byte]].apply(n) + def (arr: IArray[Short]).apply(n: Int): Short = arr.asInstanceOf[Array[Short]].apply(n) + def (arr: IArray[Char]).apply(n: Int): Char = arr.asInstanceOf[Array[Char]].apply(n) + def (arr: IArray[Int]).apply(n: Int): Int = arr.asInstanceOf[Array[Int]].apply(n) + def (arr: IArray[Long]).apply(n: Int): Long = arr.asInstanceOf[Array[Long]].apply(n) + def (arr: IArray[Float]).apply(n: Int): Float = arr.asInstanceOf[Array[Float]].apply(n) + def (arr: IArray[Double]).apply(n: Int): Double = arr.asInstanceOf[Array[Double]].apply(n) def [T <: Object](arr: IArray[T]) apply (n: Int): T = arr.asInstanceOf[Array[T]].apply(n) def [T](arr: IArray[T]) apply (n: Int): T = arr.asInstanceOf[Array[T]].apply(n) /** The number of elements in an immutable array * @param arr the immutable array */ - def (arr: IArray[Byte]) length: Int = arr.asInstanceOf[Array[Byte]].length - def (arr: IArray[Short]) length: Int = arr.asInstanceOf[Array[Short]].length - def (arr: IArray[Char]) length: Int = arr.asInstanceOf[Array[Char]].length - def (arr: IArray[Int]) length: Int = arr.asInstanceOf[Array[Int]].length - def (arr: IArray[Long]) length: Int = arr.asInstanceOf[Array[Long]].length - def (arr: IArray[Float]) length: Int = arr.asInstanceOf[Array[Float]].length - def (arr: IArray[Double]) length: Int = arr.asInstanceOf[Array[Double]].length - def (arr: IArray[Object]) length: Int = arr.asInstanceOf[Array[Object]].length + def (arr: IArray[Byte]).length: Int = arr.asInstanceOf[Array[Byte]].length + def (arr: IArray[Short]).length: Int = arr.asInstanceOf[Array[Short]].length + def (arr: IArray[Char]).length: Int = arr.asInstanceOf[Array[Char]].length + def (arr: IArray[Int]).length: Int = arr.asInstanceOf[Array[Int]].length + def (arr: IArray[Long]).length: Int = arr.asInstanceOf[Array[Long]].length + def (arr: IArray[Float]).length: Int = arr.asInstanceOf[Array[Float]].length + def (arr: IArray[Double]).length: Int = arr.asInstanceOf[Array[Double]].length + def (arr: IArray[Object]).length: Int = arr.asInstanceOf[Array[Object]].length def [T](arr: IArray[T]) length: Int = arr.asInstanceOf[Array[T]].length /** Returns this array concatenated with the given array. */ diff --git a/tests/neg-custom-args/extmethods-tparams.scala b/tests/neg-custom-args/extmethods-tparams.scala index e6bd6ebe7485..59a9780a7abf 100644 --- a/tests/neg-custom-args/extmethods-tparams.scala +++ b/tests/neg-custom-args/extmethods-tparams.scala @@ -1,2 +1,2 @@ -def (self: T) foo[T] = ??? // error +def (self: T).foo[T] = ??? // error def [T1](self: T1) bar[T2] = ??? // error // error \ No newline at end of file diff --git a/tests/neg-macros/i6432/Macro_1.scala b/tests/neg-macros/i6432/Macro_1.scala index 501926e3537c..2ca399dc7a1b 100644 --- a/tests/neg-macros/i6432/Macro_1.scala +++ b/tests/neg-macros/i6432/Macro_1.scala @@ -4,7 +4,7 @@ import scala.quoted.autolift.given import scala.quoted.matching._ object Macro { - inline def (sc: => StringContext) foo (args: String*): Unit = ${ impl('sc) } + inline def (sc: => StringContext).foo(args: String*): Unit = ${ impl('sc) } def impl(sc: Expr[StringContext])(given qctx: QuoteContext): Expr[Unit] = { import qctx.tasty.{_, given} diff --git a/tests/neg-macros/i6432b/Macro_1.scala b/tests/neg-macros/i6432b/Macro_1.scala index 501926e3537c..2ca399dc7a1b 100644 --- a/tests/neg-macros/i6432b/Macro_1.scala +++ b/tests/neg-macros/i6432b/Macro_1.scala @@ -4,7 +4,7 @@ import scala.quoted.autolift.given import scala.quoted.matching._ object Macro { - inline def (sc: => StringContext) foo (args: String*): Unit = ${ impl('sc) } + inline def (sc: => StringContext).foo(args: String*): Unit = ${ impl('sc) } def impl(sc: Expr[StringContext])(given qctx: QuoteContext): Expr[Unit] = { import qctx.tasty.{_, given} diff --git a/tests/neg-macros/reflect-inline/assert_1.scala b/tests/neg-macros/reflect-inline/assert_1.scala index eec9852b53d9..cdae0f37b43b 100644 --- a/tests/neg-macros/reflect-inline/assert_1.scala +++ b/tests/neg-macros/reflect-inline/assert_1.scala @@ -1,7 +1,7 @@ import scala.quoted._ object api { - inline def (inline x: String) stripMargin2: String = + inline def (inline x: String).stripMargin2: String = ${ stripImpl(x) } private def stripImpl(x: String)(given qctx: QuoteContext): Expr[String] = diff --git a/tests/neg-macros/tasty-string-interpolator-position-a/Macro_1.scala b/tests/neg-macros/tasty-string-interpolator-position-a/Macro_1.scala index bf05adc5122d..d0865fd34833 100644 --- a/tests/neg-macros/tasty-string-interpolator-position-a/Macro_1.scala +++ b/tests/neg-macros/tasty-string-interpolator-position-a/Macro_1.scala @@ -3,7 +3,7 @@ import scala.language.implicitConversions object Macro { - implicit inline def (strCtx: => StringContext) f2 (args: =>Any*): String = ${FIntepolator.apply('strCtx, 'args)} + implicit inline def (strCtx: => StringContext).f2(args: =>Any*): String = ${FIntepolator.apply('strCtx, 'args)} } diff --git a/tests/neg-macros/tasty-string-interpolator-position-b/Macro_1.scala b/tests/neg-macros/tasty-string-interpolator-position-b/Macro_1.scala index a4de659aee3d..4cb7ad8570ac 100644 --- a/tests/neg-macros/tasty-string-interpolator-position-b/Macro_1.scala +++ b/tests/neg-macros/tasty-string-interpolator-position-b/Macro_1.scala @@ -3,7 +3,7 @@ import scala.language.implicitConversions object Macro { - implicit inline def (strCtx: => StringContext) f3 (args: =>Any*): String = ${FIntepolator.apply('strCtx, 'args)} + implicit inline def (strCtx: => StringContext).f3(args: =>Any*): String = ${FIntepolator.apply('strCtx, 'args)} } diff --git a/tests/neg/extension-methods.scala b/tests/neg/extension-methods.scala index 4eefd041f624..356a52c3ff3c 100644 --- a/tests/neg/extension-methods.scala +++ b/tests/neg/extension-methods.scala @@ -1,7 +1,7 @@ object Test { implicit object O { - def (x: String) l1 = x.length + def (x: String).l1 = x.length def l1(x: Int) = x * x def l2(x: String) = x.length } @@ -11,7 +11,7 @@ object Test { 1.l1 // error given [T](xs: List[T]) extended with { - def (x: Int) f1: T = ??? // error: No extension method allowed here, since collective parameters are given + def (x: Int).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 } diff --git a/tests/neg/extmethod-override.scala b/tests/neg/extmethod-override.scala index 3395460ecfab..9b2e809f9baa 100644 --- a/tests/neg/extmethod-override.scala +++ b/tests/neg/extmethod-override.scala @@ -1,8 +1,8 @@ class A { def f(x: Int)(y: Int): Int = 0 - def (x: Int) g (y: Int): Int = 1 + def (x: Int).g(y: Int): Int = 1 } class B extends A { - override def (x: Int) f (y: Int): Int = 1 // error + override def (x: Int).f(y: Int): Int = 1 // error override def g(x: Int)(y: Int): Int = 0 // error } \ No newline at end of file diff --git a/tests/neg/i5773.scala b/tests/neg/i5773.scala index 6e98d5e0c93e..4ae1fa220599 100644 --- a/tests/neg/i5773.scala +++ b/tests/neg/i5773.scala @@ -1,16 +1,16 @@ trait Semigroup[T] { - def (lhs: T) append (rhs: T): T - def (lhs: Int) appendS (rhs: T): T = ??? + def (lhs: T).append(rhs: T): T + def (lhs: Int).appendS(rhs: T): T = ??? } object Semigroup { implicit object stringAppend extends Semigroup[String] { - override def (lhs: String) append (rhs: String): String = lhs + rhs + override def (lhs: String).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) + override def (lhs: N).append(rhs: N): N = N.plus(lhs, rhs) + def (lhs: Int).appendS(rhs: N): N = ??? // N.plus(lhs, rhs) } } diff --git a/tests/neg/i6762b.scala b/tests/neg/i6762b.scala index 9a12fffd6dbe..2d7bf0aacef3 100644 --- a/tests/neg/i6762b.scala +++ b/tests/neg/i6762b.scala @@ -9,5 +9,5 @@ type Liftable given Liftable = ??? implicit object ExprOps { - def (x: T) toExpr[T](given Liftable): Expr[T] = ??? + def (x: T).toExpr[T](given Liftable): Expr[T] = ??? } diff --git a/tests/neg/i6779.scala b/tests/neg/i6779.scala index 5a24e0763317..54998ba378fe 100644 --- a/tests/neg/i6779.scala +++ b/tests/neg/i6779.scala @@ -3,7 +3,7 @@ type G[T] type Stuff given Stuff = ??? -def (x: T) f[T](given Stuff): F[T] = ??? +def (x: T).f[T](given Stuff): F[T] = ??? def g1[T](x: T): F[G[T]] = x.f(given summon[Stuff]) // error diff --git a/tests/neg/i7438.scala b/tests/neg/i7438.scala index c9e74ae31f5a..a263a5fe2f24 100644 --- a/tests/neg/i7438.scala +++ b/tests/neg/i7438.scala @@ -1,7 +1,7 @@ type Tr[+A] -inline def (tr: Tr[A]) map[A, B](f: A => B): Tr[B] = ??? +inline def (tr: Tr[A]).map[A, B](f: A => B): Tr[B] = ??? -def (d: Double) func: None.type => Some[Double] = ??? +def (d: Double).func: None.type => Some[Double] = ??? def run[A](query: None.type => Some[A]): Some[A] = ??? diff --git a/tests/neg/indent.scala b/tests/neg/indent.scala index b38ff81221f1..2bac87c9efb7 100644 --- a/tests/neg/indent.scala +++ b/tests/neg/indent.scala @@ -1,6 +1,6 @@ object Test { - def (x: Int) gt (y: Int) = x > y + def (x: Int).gt(y: Int) = x > y val y3 = if (1) max 10 gt 0 // error: end of statement expected but integer literal found // error // error // error 1 diff --git a/tests/neg/opaque-bounds.scala b/tests/neg/opaque-bounds.scala index 4755f58bd70b..acaebccbb6fb 100644 --- a/tests/neg/opaque-bounds.scala +++ b/tests/neg/opaque-bounds.scala @@ -21,8 +21,8 @@ object Access { def (x: Permissions) & (y: Permissions): Permissions = x & y def (x: PermissionChoice) | (y: PermissionChoice): PermissionChoice = x | y - def (x: Permissions) is (y: Permissions) = (x & y) == y - def (x: Permissions) isOneOf (y: PermissionChoice) = (x & y) != 0 + def (x: Permissions).is(y: Permissions) = (x & y) == y + def (x: Permissions).isOneOf(y: PermissionChoice) = (x & y) != 0 val NoPermission: Permission = 0 val ReadOnly: Permission = 1 diff --git a/tests/pos/combine.scala b/tests/pos/combine.scala index df69ac6bb3e9..7728b443c245 100644 --- a/tests/pos/combine.scala +++ b/tests/pos/combine.scala @@ -1,5 +1,5 @@ trait Semigroup[A] { - def (x: A) combine (y: A): A + def (x: A).combine(y: A): A } given Semigroup[Int] = ??? given [A, B]: Semigroup[A], Semigroup[B] => Semigroup[(A, B)] = ??? diff --git a/tests/pos/consume.scala b/tests/pos/consume.scala index e8d5e15be4a7..3f8b1fe36878 100644 --- a/tests/pos/consume.scala +++ b/tests/pos/consume.scala @@ -24,7 +24,7 @@ object math3 with trait Numeric[T] extends Ord[T] with def (x: T) + (y: T): T = ??? def (x: T) - (y: T): T = ??? - def (x: Int) numeric: T = ??? + def (x: Int).numeric: T = ??? end math3 object Test3 with diff --git a/tests/pos/i5773.scala b/tests/pos/i5773.scala index e799691a249d..76f1e491a63e 100644 --- a/tests/pos/i5773.scala +++ b/tests/pos/i5773.scala @@ -1,18 +1,18 @@ trait Semigroup[T] { - def (lhs: T) append (rhs: T): T + def (lhs: T).append(rhs: T): T } object Semigroup { implicit object stringAppend extends Semigroup[String] { - override def (lhs: String) append (rhs: String): String = lhs + rhs + override def (lhs: String).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) + override def (lhs: N).append(rhs: N): N = ??? // N.plus(lhs, rhs) } implicit class SumSemiGroupDeco[N](implicit N: Numeric[N]) extends Semigroup[N] { - override def (lhs: N) append (rhs: N): N = ??? // N.plus(lhs, rhs) + override def (lhs: N).append(rhs: N): N = ??? // N.plus(lhs, rhs) } } @@ -26,7 +26,7 @@ object Main { def f2 = { implicit val intSumAppend: Semigroup[Int] = sumSemigroup[Int] - println(3 append 4) + println(3 append 4) } def f3 = { diff --git a/tests/pos/i5773a.scala b/tests/pos/i5773a.scala index 2071402609c3..801fa10f3f3f 100644 --- a/tests/pos/i5773a.scala +++ b/tests/pos/i5773a.scala @@ -1,12 +1,12 @@ trait Semigroup[T] { - def (x: T) combine (y: T): T + def (x: T).combine(y: T): T } object Test { implicit val IntSemigroup: Semigroup[Int] = new { - def (x: Int) combine (y: Int): Int = x + y + def (x: Int).combine(y: Int): Int = x + y } implicit def OptionSemigroup[T: Semigroup]: Semigroup[Option[T]] = new { - def (x: Option[T]) combine (y: Option[T]): Option[T] = for { + def (x: Option[T]).combine(y: Option[T]): Option[T] = for { x0 <- x y0 <- y } yield x0.combine(y0) diff --git a/tests/pos/i6395.scala b/tests/pos/i6395.scala index 26faf3f52109..1e75d9ea42e7 100644 --- a/tests/pos/i6395.scala +++ b/tests/pos/i6395.scala @@ -1,5 +1,5 @@ object Foo { - inline def (self: Int) foo (that: Int): Int = 5 - def (self: Int) bar: Int = self + inline def (self: Int).foo(that: Int): Int = 5 + def (self: Int).bar: Int = self 1.foo(2).bar } \ No newline at end of file diff --git a/tests/pos/i7070.scala b/tests/pos/i7070.scala index 07b2fa7be9c4..0f65b8a3f418 100644 --- a/tests/pos/i7070.scala +++ b/tests/pos/i7070.scala @@ -2,7 +2,7 @@ object Test { class C - def (str: String) foo: (given C) => Int = ??? + def (str: String).foo: (given C) => Int = ??? given C = ??? diff --git a/tests/pos/i7119.scala b/tests/pos/i7119.scala index 1911068bc52f..0124dafb155d 100644 --- a/tests/pos/i7119.scala +++ b/tests/pos/i7119.scala @@ -1,6 +1,6 @@ class Impl -def (impl: Impl) prop(given Int) = ???//summon[Int] +def (impl: Impl).prop(given Int) = ???//summon[Int] def main(args: Array[String]): Unit = { diff --git a/tests/pos/i7413.scala b/tests/pos/i7413.scala index 58c95529a8c2..cf0b9bec5eee 100644 --- a/tests/pos/i7413.scala +++ b/tests/pos/i7413.scala @@ -3,7 +3,7 @@ import scala.language.implicitConversions trait Fixture[A] extends Conversion[0, A] trait TestFramework[A] { - def (testName: String) in (test: (given Fixture[A]) => Unit): Unit = ??? + def (testName: String).in(test: (given Fixture[A]) => Unit): Unit = ??? } trait Greeter { diff --git a/tests/pos/i7700.scala b/tests/pos/i7700.scala index bdca41ff87ce..2c7a1f3a1ff9 100644 --- a/tests/pos/i7700.scala +++ b/tests/pos/i7700.scala @@ -4,7 +4,7 @@ trait Show[-A] with def show(a: A): String object Macros with - inline def (sc: StringContext) show(args: =>Any*): String = ??? + inline def (sc: StringContext).show(args: =>Any*): String = ??? object Show with def[A] (a: A) show(given S: Show[A]): String = S.show(a) diff --git a/tests/pos/indent.scala b/tests/pos/indent.scala index c15a85cb4e82..1a562dab29e1 100644 --- a/tests/pos/indent.scala +++ b/tests/pos/indent.scala @@ -28,7 +28,7 @@ object Test 1 else 2 - def (x: Int) gt (y: Int) = x > y + def (x: Int).gt(y: Int) = x > y val y3 = if (1) max 10 gt 0 diff --git a/tests/pos/opaque-propability-xm.scala b/tests/pos/opaque-propability-xm.scala index 2025994eacbb..28b431e74a43 100644 --- a/tests/pos/opaque-propability-xm.scala +++ b/tests/pos/opaque-propability-xm.scala @@ -19,17 +19,17 @@ object prob { implicit val ordering: Ordering[Probability] = implicitly[Ordering[Double]] - def (p1: Probability) unary_~ : Probability = Certain - p1 + def (p1: Probability).unary_~ : Probability = Certain - p1 def (p1: Probability) & (p2: Probability): Probability = p1 * p2 def (p1: Probability) | (p2: Probability): Probability = p1 + p2 - (p1 * p2) - def (p1: Probability) isImpossible: Boolean = p1 == Never - def (p1: Probability) isCertain: Boolean = p1 == Certain + def (p1: Probability).isImpossible: Boolean = p1 == Never + def (p1: Probability).isCertain: Boolean = p1 == Certain import scala.util.Random - def (p1: Probability) sample (r: Random = Random): Boolean = r.nextDouble <= p1 - def (p1: Probability) toDouble: Double = p1 + def (p1: Probability).sample(r: Random = Random): Boolean = r.nextDouble <= p1 + def (p1: Probability).toDouble: Double = p1 } val caughtTrain = Probability.unsafe(0.3) diff --git a/tests/pos/opaque-xm.scala b/tests/pos/opaque-xm.scala index 2a936787219d..a8619c287f58 100644 --- a/tests/pos/opaque-xm.scala +++ b/tests/pos/opaque-xm.scala @@ -16,7 +16,7 @@ object opaquetypes { // Extension methods define opaque types' public APIs // This is the second way to unlift the logarithm type - def (x: Logarithm) toDouble: Double = math.exp(x) + def (x: Logarithm).toDouble: Double = math.exp(x) def (x: Logarithm) + (y: Logarithm) = Logarithm(math.exp(x) + math.exp(y)) def (x: Logarithm) * (y: Logarithm): Logarithm = Logarithm(x + y) } diff --git a/tests/pos/reference/delegates.scala b/tests/pos/reference/delegates.scala index 71303fc14513..4252868bf9aa 100644 --- a/tests/pos/reference/delegates.scala +++ b/tests/pos/reference/delegates.scala @@ -1,15 +1,15 @@ class Common with trait Ord[T] with - def (x: T) compareTo (y: T): Int + def (x: T).compareTo(y: T): Int def (x: T) < (y: T) = x.compareTo(y) < 0 def (x: T) > (y: T) = x.compareTo(y) > 0 trait Convertible[From, To] with - def (x: From) convert: To + def (x: From).convert: To trait SemiGroup[T] with - def (x: T) combine (y: T): T + def (x: T).combine(y: T): T trait Monoid[T] extends SemiGroup[T] with def unit: T @@ -26,11 +26,11 @@ class Common with object Instances extends Common with given intOrd: Ord[Int] with - def (x: Int) compareTo (y: Int) = + def (x: Int).compareTo(y: Int) = if (x < y) -1 else if (x > y) +1 else 0 given listOrd[T]: Ord[T] => Ord[List[T]] with - def (xs: List[T]) compareTo (ys: List[T]): Int = (xs, ys) match + def (xs: List[T]).compareTo(ys: List[T]): Int = (xs, ys).match case (Nil, Nil) => 0 case (Nil, _) => -1 case (_, Nil) => +1 @@ -64,7 +64,7 @@ object Instances extends Common with xs.reduceLeft((x, y) => if (x < y) y else x) def descending[T](given asc: Ord[T]): Ord[T] = new Ord[T] with - def (x: T) compareTo (y: T) = asc.compareTo(y)(x) + def (x: T).compareTo(y: T) = asc.compareTo(y)(x) def minimum[T](xs: List[T])(given Ord[T]) = maximum(xs)(given descending) @@ -87,14 +87,14 @@ object Instances extends Common with trait TastyAPI with type Symbol trait SymDeco with - def (sym: Symbol) name: String + def (sym: Symbol).name: String def symDeco: SymDeco given SymDeco = symDeco object TastyImpl extends TastyAPI with type Symbol = String val symDeco = new SymDeco with - def (sym: Symbol) name = sym + def (sym: Symbol).name = sym class D[T] @@ -141,11 +141,11 @@ end PostConditions object AnonymousInstances extends Common with given Ord[Int] with - def (x: Int) compareTo (y: Int) = + def (x: Int).compareTo(y: Int) = if (x < y) -1 else if (x > y) +1 else 0 given [T: Ord] : Ord[List[T]] with - def (xs: List[T]) compareTo (ys: List[T]): Int = (xs, ys) match + def (xs: List[T]).compareTo(ys: List[T]): Int = (xs, ys).match case (Nil, Nil) => 0 case (Nil, _) => -1 case (_, Nil) => +1 @@ -162,10 +162,10 @@ object AnonymousInstances extends Common with def second = xs.tail.head given [From, To]: (c: Convertible[From, To]) => Convertible[List[From], List[To]] with - def (x: List[From]) convert: List[To] = x.map(c.convert) + def (x: List[From]).convert: List[To] = x.map(c.convert) given Monoid[String] with - def (x: String) combine (y: String): String = x.concat(y) + def (x: String).combine(y: String): String = x.concat(y) def unit: String = "" def sum[T: Monoid](xs: List[T]): T = @@ -174,11 +174,11 @@ end AnonymousInstances object Implicits extends Common with implicit object IntOrd extends Ord[Int] with - def (x: Int) compareTo (y: Int) = + def (x: Int).compareTo(y: Int) = if (x < y) -1 else if (x > y) +1 else 0 class ListOrd[T: Ord] extends Ord[List[T]] with - def (xs: List[T]) compareTo (ys: List[T]): Int = (xs, ys) match + def (xs: List[T]).compareTo(ys: List[T]): Int = (xs, ys).match case (Nil, Nil) => 0 case (Nil, _) => -1 case (_, Nil) => +1 @@ -189,7 +189,7 @@ object Implicits extends Common with class given_Convertible_List_List[From, To](implicit c: Convertible[From, To]) extends Convertible[List[From], List[To]] with - def (x: List[From]) convert: List[To] = x.map(c.convert) + def (x: List[From]).convert: List[To] = x.map(c.convert) implicit def given_Convertible_List_List[From, To](implicit c: Convertible[From, To]) : Convertible[List[From], List[To]] = new given_Convertible_List_List[From, To] @@ -199,7 +199,7 @@ object Implicits extends Common with xs.reduceLeft((x, y) => if (x < y) y else x) def descending[T](implicit asc: Ord[T]): Ord[T] = new Ord[T] with - def (x: T) compareTo (y: T) = asc.compareTo(y)(x) + def (x: T).compareTo(y: T) = asc.compareTo(y)(x) def minimum[T](xs: List[T])(implicit cmp: Ord[T]) = maximum(xs)(descending) diff --git a/tests/pos/reference/extension-methods.scala b/tests/pos/reference/extension-methods.scala index d8e3d566a615..614efe611314 100644 --- a/tests/pos/reference/extension-methods.scala +++ b/tests/pos/reference/extension-methods.scala @@ -2,14 +2,14 @@ object ExtMethods with case class Circle(x: Double, y: Double, radius: Double) - def (c: Circle) circumference: Double = c.radius * math.Pi * 2 + def (c: Circle).circumference: Double = c.radius * math.Pi * 2 val circle = Circle(0, 0, 1) circle.circumference assert(circle.circumference == circumference(circle)) trait StringSeqOps { - def (xs: Seq[String]) longestStrings = { + def (xs: Seq[String]).longestStrings = { val maxLength = xs.map(_.length).max xs.filter(_.length == maxLength) } @@ -57,7 +57,7 @@ object ExtMethods with def largest(n: Int) = xs.sorted.takeRight(n) given stringOps1: AnyRef { - def (xs: Seq[String]) longestStrings: Seq[String] = { + def (xs: Seq[String]).longestStrings: Seq[String] = { val maxLength = xs.map(_.length).max xs.filter(_.length == maxLength) } diff --git a/tests/pos/reference/opaque.scala b/tests/pos/reference/opaque.scala index 617fbbe17b2d..5b0b24aad768 100644 --- a/tests/pos/reference/opaque.scala +++ b/tests/pos/reference/opaque.scala @@ -38,8 +38,8 @@ object Access { def (x: Permissions) & (y: Permissions): Permissions = x & y def (x: PermissionChoice) | (y: PermissionChoice): PermissionChoice = x | y - def (x: Permissions) is (y: Permissions) = (x & y) == y - def (x: Permissions) isOneOf (y: PermissionChoice) = (x & y) != 0 + def (x: Permissions).is(y: Permissions) = (x & y) == y + def (x: Permissions).isOneOf(y: PermissionChoice) = (x & y) != 0 val NoPermission: Permission = 0 val ReadOnly: Permission = 1 diff --git a/tests/pos/toplevel-opaque-xm/Logarithm_1.scala b/tests/pos/toplevel-opaque-xm/Logarithm_1.scala index 88f280a7f4a1..c9e4e439713b 100644 --- a/tests/pos/toplevel-opaque-xm/Logarithm_1.scala +++ b/tests/pos/toplevel-opaque-xm/Logarithm_1.scala @@ -16,7 +16,7 @@ implicit object Logarithm { // Extension methods define opaque types' public APIs // This is the second way to unlift the logarithm type - def (x: Logarithm) toDouble: Double = math.exp(x) + def (x: Logarithm).toDouble: Double = math.exp(x) def (x: Logarithm) + (y: Logarithm) = Logarithm(math.exp(x) + math.exp(y)) def (x: Logarithm) * (y: Logarithm): Logarithm = Logarithm(x + y) } diff --git a/tests/run-macros/f-interpolator-neg/Macros_1.scala b/tests/run-macros/f-interpolator-neg/Macros_1.scala index 8a852f3a3e4c..d21875d53c98 100644 --- a/tests/run-macros/f-interpolator-neg/Macros_1.scala +++ b/tests/run-macros/f-interpolator-neg/Macros_1.scala @@ -6,7 +6,7 @@ import scala.language.implicitConversions object TestFooErrors { // Defined in tests implicit object StringContextOps { - inline def (ctx: => StringContext) foo (args: => Any*): List[(Boolean, Int, Int, Int, String)] = ${ Macro.fooErrors('ctx, 'args) } + inline def (ctx: => StringContext).foo(args: => Any*): List[(Boolean, Int, Int, Int, String)] = ${ Macro.fooErrors('ctx, 'args) } } } diff --git a/tests/run/Pouring.scala b/tests/run/Pouring.scala index b61d54bf1a3f..add83608862a 100644 --- a/tests/run/Pouring.scala +++ b/tests/run/Pouring.scala @@ -8,7 +8,7 @@ class Pouring(capacity: Vector[Int]) with case Fill(g) => content.updated(g, capacity(g)) case Pour(from, to) => val amount = content(from) min (capacity(to) - content(to)) - def (s: Content) adjust (g: Glass, delta: Int) = s.updated(g, s(g) + delta) + def (s: Content).adjust(g: Glass, delta: Int) = s.updated(g, s(g) + delta) content.adjust(from, -amount).adjust(to, amount) case Empty(glass: Glass) diff --git a/tests/run/eq-xmethod.scala b/tests/run/eq-xmethod.scala index 6e9bfa5dd855..5231063bf62b 100644 --- a/tests/run/eq-xmethod.scala +++ b/tests/run/eq-xmethod.scala @@ -6,7 +6,7 @@ object Test extends App { class N object N extends N - def (x: N) _eq (y: R | N) = y eq N + def (x: N)._eq(y: R | N) = y eq N val r1, r2 = new R assert(r1 _eq r1) diff --git a/tests/run/exports.scala b/tests/run/exports.scala index fe978ea2f375..54ca2235fee7 100644 --- a/tests/run/exports.scala +++ b/tests/run/exports.scala @@ -12,7 +12,7 @@ object Test extends App { class Scanner { def scan() = println("scanning") - def (x: Any) scanned = scan() + def (x: Any).scanned = scan() } object Scanner extends Scanner diff --git a/tests/run/extension-methods.scala b/tests/run/extension-methods.scala index 538b7bd7dffa..82552aaf9e82 100644 --- a/tests/run/extension-methods.scala +++ b/tests/run/extension-methods.scala @@ -1,18 +1,18 @@ object Test extends App { - def (x: Int) em: Boolean = x > 0 + def (x: Int).em: Boolean = x > 0 assert(1.em == em(1)) case class Circle(x: Double, y: Double, radius: Double) - def (c: Circle) circumference: Double = c.radius * math.Pi * 2 + def (c: Circle).circumference: Double = c.radius * math.Pi * 2 val circle = new Circle(1, 1, 2.0) assert(circle.circumference == circumference(circle)) - def (xs: Seq[String]) longestStrings: Seq[String] = { + def (xs: Seq[String]).longestStrings: Seq[String] = { val maxLength = xs.map(_.length).max xs.filter(_.length == maxLength) } @@ -29,7 +29,7 @@ object Test extends App { assert(Nil.flattened == Nil) trait SemiGroup[T] { - def (x: T) combine (y: T): T + def (x: T).combine(y: T): T } trait Monoid[T] extends SemiGroup[T] { def unit: T @@ -37,7 +37,7 @@ object Test extends App { // An instance declaration: implicit object StringMonoid extends Monoid[String] { - def (x: String) combine (y: String): String = x.concat(y) + def (x: String).combine(y: String): String = x.concat(y) def unit: String = "" } @@ -48,14 +48,14 @@ object Test extends App { println(sum(names)) trait Ord[T] { - def (x: T) compareTo (y: T): Int + def (x: T).compareTo(y: T): Int def (x: T) < (y: T) = x.compareTo(y) < 0 def (x: T) > (y: T) = x.compareTo(y) > 0 val minimum: T } implicit object IntOrd extends Ord[Int] { - def (x: Int) compareTo (y: Int) = + def (x: Int).compareTo(y: Int) = if (x < y) -1 else if (x > y) +1 else 0 val minimum = Int.MinValue } diff --git a/tests/run/extmethod-overload.scala b/tests/run/extmethod-overload.scala index fe80ccebf54a..c5454944bd63 100644 --- a/tests/run/extmethod-overload.scala +++ b/tests/run/extmethod-overload.scala @@ -88,13 +88,13 @@ object Test extends App { class C { def xx (x: Any) = 2 } - def (c: C) xx (x: Int) = 1 + def (c: C).xx(x: Int) = 1 val c = new C assert(c.xx(1) == 2) // member method takes precedence object D { - def (x: Int) yy (y: Int) = x + y + def (x: Int).yy(y: Int) = x + y } given (x: Int) extended with { @@ -114,8 +114,8 @@ object Test extends App { def b: Long = a } - def (rectangle: Rectangle) area: Long = 0 - def (square: Square) area: Long = square.a * square.a + def (rectangle: Rectangle).area: Long = 0 + def (square: Square).area: Long = square.a * square.a val rectangles = List(GenericRectangle(2, 3), Square(5)) val areas = rectangles.map(_.area) assert(areas.sum == 0) diff --git a/tests/run/extmethods2.scala b/tests/run/extmethods2.scala index 9ed54f3a39b7..90441a4aa97d 100644 --- a/tests/run/extmethods2.scala +++ b/tests/run/extmethods2.scala @@ -4,8 +4,8 @@ object Test extends App { given stringListOps: TC => Object { type T = List[String] - def (x: T) foo (y: T) = (x ++ y, summon[TC]) - def (x: T) bar (y: Int) = (x(0)(y), summon[TC]) + def (x: T).foo(y: T) = (x ++ y, summon[TC]) + def (x: T).bar(y: Int) = (x(0)(y), summon[TC]) } def test(given TC) = { diff --git a/tests/run/instances-anonymous.scala b/tests/run/instances-anonymous.scala index 4d4774e98bf1..8746c623a0e0 100644 --- a/tests/run/instances-anonymous.scala +++ b/tests/run/instances-anonymous.scala @@ -1,7 +1,7 @@ object Test extends App { implicit object O { - def (x: Int) em: Boolean = x > 0 + def (x: Int).em: Boolean = x > 0 } assert(1.em == O.em(1)) @@ -17,7 +17,7 @@ object Test extends App { println(circle.circumference) given AnyRef { - def (xs: Seq[String]) longestStrings: Seq[String] = { + def (xs: Seq[String]).longestStrings: Seq[String] = { val maxLength = xs.map(_.length).max xs.filter(_.length == maxLength) } @@ -39,14 +39,14 @@ object Test extends App { assert(Nil.flattened == Nil) trait SemiGroup[T] { - def (x: T) combine (y: T): T + def (x: T).combine(y: T): T } trait Monoid[T] extends SemiGroup[T] { def unit: T } given Monoid[String] { - def (x: String) combine (y: String): String = x.concat(y) + def (x: String).combine(y: String): String = x.concat(y) def unit: String = "" } @@ -57,20 +57,20 @@ object Test extends App { println(sum(names)) trait Ord[T] { - def (x: T) compareTo (y: T): Int + def (x: T).compareTo(y: T): Int def (x: T) < (y: T) = x.compareTo(y) < 0 def (x: T) > (y: T) = x.compareTo(y) > 0 val minimum: T } given Ord[Int] { - def (x: Int) compareTo (y: Int) = + def (x: Int).compareTo(y: Int) = if (x < y) -1 else if (x > y) +1 else 0 val minimum = Int.MinValue } given [T: Ord] : Ord[List[T]] { - def (xs: List[T]) compareTo (ys: List[T]): Int = (xs, ys) match { + def (xs: List[T]).compareTo(ys: List[T]): Int = (xs, ys).match { case (Nil, Nil) => 0 case (Nil, _) => -1 case (_, Nil) => +1 diff --git a/tests/run/instances.scala b/tests/run/instances.scala index 0778d8a4fed5..033bc81c0f52 100644 --- a/tests/run/instances.scala +++ b/tests/run/instances.scala @@ -1,7 +1,7 @@ object Test extends App { implicit object O { - def (x: Int) em: Boolean = x > 0 + def (x: Int).em: Boolean = x > 0 } assert(1.em == O.em(1)) @@ -44,13 +44,13 @@ object Test extends App { assert(Nil.flattened == Nil) trait SemiGroup[T] with - def (x: T) combine (y: T): T + def (x: T).combine(y: T): T trait Monoid[T] extends SemiGroup[T] with def unit: T given StringMonoid : Monoid[String] with - def (x: String) combine (y: String): String = x.concat(y) + def (x: String).combine(y: String): String = x.concat(y) def unit: String = "" // Abstracting over a typeclass with a context bound: @@ -60,19 +60,19 @@ object Test extends App { println(sum(names)) trait Ord[T] with - def (x: T) compareTo (y: T): Int + def (x: T).compareTo(y: T): Int def (x: T) < (y: T) = x.compareTo(y) < 0 def (x: T) > (y: T) = x.compareTo(y) > 0 val minimum: T end Ord given Ord[Int] with - def (x: Int) compareTo (y: Int) = + def (x: Int).compareTo(y: Int) = if (x < y) -1 else if (x > y) +1 else 0 val minimum = Int.MinValue given listOrd[T: Ord]: Ord[List[T]] with - def (xs: List[T]) compareTo (ys: List[T]): Int = (xs, ys) match + def (xs: List[T]).compareTo(ys: List[T]): Int = (xs, ys).match case (Nil, Nil) => 0 case (Nil, _) => -1 case (_, Nil) => +1 diff --git a/tests/run/string-context-implicits-with-conversion.scala b/tests/run/string-context-implicits-with-conversion.scala index 4f3e3eb5d314..f72d96989c3a 100644 --- a/tests/run/string-context-implicits-with-conversion.scala +++ b/tests/run/string-context-implicits-with-conversion.scala @@ -1,6 +1,6 @@ object Lib { - def (sc: StringContext) showMe(args: Showed*): String = sc.s(args: _*) + def (sc: StringContext).showMe(args: Showed*): String = sc.s(args: _*) opaque type Showed = String diff --git a/tests/run/toplevel-implicits/a.b.scala b/tests/run/toplevel-implicits/a.b.scala index bfa0051d546a..a360f4d6e491 100644 --- a/tests/run/toplevel-implicits/a.b.scala +++ b/tests/run/toplevel-implicits/a.b.scala @@ -3,7 +3,7 @@ package implicits case class C() implicit object Cops { - def (x: C) pair (y: C) = (x, y) + def (x: C).pair(y: C) = (x, y) } class D { diff --git a/tests/semanticdb/expect/Enums.scala b/tests/semanticdb/expect/Enums.scala index 28f2a8a4372e..6ccd2a1fdf8d 100644 --- a/tests/semanticdb/expect/Enums.scala +++ b/tests/semanticdb/expect/Enums.scala @@ -12,10 +12,10 @@ object Enums with case Hearts, Spades, Clubs, Diamonds object Suits with - def (suit: Suits) isRed: Boolean = + def (suit: Suits).isRed: Boolean = suit == Hearts || suit == Diamonds - def (suit: Suits) isBlack: Boolean = suit match + def (suit: Suits).isBlack: Boolean = suit match case Spades | Diamonds => true case _ => false diff --git a/tests/semanticdb/expect/Givens.scala b/tests/semanticdb/expect/Givens.scala index 9460d5cba0fd..b5b5548a0b00 100644 --- a/tests/semanticdb/expect/Givens.scala +++ b/tests/semanticdb/expect/Givens.scala @@ -16,11 +16,11 @@ object Givens trait Monoid[A] def empty: A - def (x: A) combine (y: A): A + def (x: A).combine(y: A): A given Monoid[String] def empty = "" - def (x: String) combine (y: String) = x + y + def (x: String).combine(y: String) = x + y inline given int2String: Conversion[Int, String] = _.toString From f489a355aacc24cec6bac4fd761dcdaf85721655 Mon Sep 17 00:00:00 2001 From: Jamie Thompson Date: Thu, 9 Jan 2020 13:56:59 +0100 Subject: [PATCH 02/11] update semanticdb expect tests --- tests/semanticdb/expect/Enums.expect.scala | 4 ++-- tests/semanticdb/expect/Givens.expect.scala | 4 ++-- tests/semanticdb/metac.expect | 19 ++++++++++--------- 3 files changed, 14 insertions(+), 13 deletions(-) diff --git a/tests/semanticdb/expect/Enums.expect.scala b/tests/semanticdb/expect/Enums.expect.scala index 47e3d493ba28..5e4fdccc826d 100644 --- a/tests/semanticdb/expect/Enums.expect.scala +++ b/tests/semanticdb/expect/Enums.expect.scala @@ -12,10 +12,10 @@ object Enums/*<-_empty_::Enums.*/ with case Hearts/*<-_empty_::Enums.Suits.Hearts.*/, Spades/*<-_empty_::Enums.Suits.Spades.*/, Clubs/*<-_empty_::Enums.Suits.Clubs.*/, Diamonds/*<-_empty_::Enums.Suits.Diamonds.*/ object Suits/*<-_empty_::Enums.Suits.*/ with - def (suit/*<-_empty_::Enums.Suits.isRed().*//*<-_empty_::Enums.Suits.isRed().(suit)*/: Suits/*->_empty_::Enums.Suits#*/) isRed: Boolean/*->scala::Boolean#*/ = + def (suit/*<-_empty_::Enums.Suits.isRed().*//*<-_empty_::Enums.Suits.isRed().(suit)*/: Suits/*->_empty_::Enums.Suits#*/).isRed: Boolean/*->scala::Boolean#*/ = suit/*->_empty_::Enums.Suits.isRed().(suit)*/ ==/*->scala::Any#`==`().*/ Hearts/*->_empty_::Enums.Suits.Hearts.*/ ||/*->scala::Boolean#`||`().*/ suit/*->_empty_::Enums.Suits.isRed().(suit)*/ ==/*->scala::Any#`==`().*/ Diamonds/*->_empty_::Enums.Suits.Diamonds.*/ - def (suit: /*<-_empty_::Enums.Suits.isBlack().*//*<-_empty_::Enums.Suits.isBlack().(suit)*/Suits/*->_empty_::Enums.Suits#*/) isBlack: Boolean/*->scala::Boolean#*/ = suit/*->_empty_::Enums.Suits.isBlack().(suit)*/ match + def (suit: /*<-_empty_::Enums.Suits.isBlack().*//*<-_empty_::Enums.Suits.isBlack().(suit)*/Suits/*->_empty_::Enums.Suits#*/).isBlack: Boolean/*->scala::Boolean#*/ = suit/*->_empty_::Enums.Suits.isBlack().(suit)*/ match case Spades/*->_empty_::Enums.Suits.Spades.*/ | Diamonds/*->_empty_::Enums.Suits.Diamonds.*/ => true case _ => false diff --git a/tests/semanticdb/expect/Givens.expect.scala b/tests/semanticdb/expect/Givens.expect.scala index d827c41f7e86..49d99beb29eb 100644 --- a/tests/semanticdb/expect/Givens.expect.scala +++ b/tests/semanticdb/expect/Givens.expect.scala @@ -16,11 +16,11 @@ object Givens/*<-a::b::Givens.*/ trait Monoid/*<-a::b::Givens.Monoid#*/[A/*<-a::b::Givens.Monoid#[A]*/] def empty/*<-a::b::Givens.Monoid#empty().*/: A/*->a::b::Givens.Monoid#[A]*/ - def (x: A) /*<-a::b::Givens.Monoid#combine().*//*<-a::b::Givens.Monoid#combine().(x)*//*->a::b::Givens.Monoid#[A]*/combine (y/*<-a::b::Givens.Monoid#combine().(y)*/: A/*->a::b::Givens.Monoid#[A]*/): A/*->a::b::Givens.Monoid#[A]*/ + def (x: A)./*<-a::b::Givens.Monoid#combine().*//*<-a::b::Givens.Monoid#combine().(x)*//*->a::b::Givens.Monoid#[A]*/combine(y/*<-a::b::Givens.Monoid#combine().(y)*/: A/*->a::b::Givens.Monoid#[A]*/): A/*->a::b::Givens.Monoid#[A]*/ given Monoid[String] /*<-a::b::Givens.given_Monoid_String.*//*->a::b::Givens.Monoid#*//*->scala::Predef.String#*/def empty/*<-a::b::Givens.given_Monoid_String.empty().*/ = "" - def (x: Str/*<-a::b::Givens.given_Monoid_String.combine().*//*<-a::b::Givens.given_Monoid_String.combine().(x)*/ing/*->scala::Predef.String#*/) combine (y/*<-a::b::Givens.given_Monoid_String.combine().(y)*/: String/*->scala::Predef.String#*/) = x/*->a::b::Givens.given_Monoid_String.combine().(x)*/ +/*->java::lang::String#`+`().*/ y/*->a::b::Givens.given_Monoid_String.combine().(y)*/ + def (x: Str/*<-a::b::Givens.given_Monoid_String.combine().*//*<-a::b::Givens.given_Monoid_String.combine().(x)*/ing/*->scala::Predef.String#*/).combine(y/*<-a::b::Givens.given_Monoid_String.combine().(y)*/: String/*->scala::Predef.String#*/) = x/*->a::b::Givens.given_Monoid_String.combine().(x)*/ +/*->java::lang::String#`+`().*/ y/*->a::b::Givens.given_Monoid_String.combine().(y)*/ inline given int2String/*<-a::b::Givens.int2String().*/: Conversion/*->scala::Conversion#*/[Int/*->scala::Int#*/, String/*->scala::Predef.String#*/] = _.toString/*->scala::Any#toString().*/ diff --git a/tests/semanticdb/metac.expect b/tests/semanticdb/metac.expect index 23fce826a326..7ffd727153d7 100644 --- a/tests/semanticdb/metac.expect +++ b/tests/semanticdb/metac.expect @@ -1342,12 +1342,12 @@ Occurrences: [16:15..16:16): A <- a/b/Givens.Monoid#[A] [17:8..17:13): empty <- a/b/Givens.Monoid#empty(). [17:15..17:16): A -> a/b/Givens.Monoid#[A] -[18:8..18:15): (x: A) <- a/b/Givens.Monoid#combine(). +[18:8..18:15): (x: A). <- a/b/Givens.Monoid#combine(). [18:9..18:10): x <- a/b/Givens.Monoid#combine().(x) [18:12..18:13): A -> a/b/Givens.Monoid#[A] -[18:24..18:25): y <- a/b/Givens.Monoid#combine().(y) -[18:27..18:28): A -> a/b/Givens.Monoid#[A] -[18:31..18:32): A -> a/b/Givens.Monoid#[A] +[18:23..18:24): y <- a/b/Givens.Monoid#combine().(y) +[18:26..18:27): A -> a/b/Givens.Monoid#[A] +[18:30..18:31): A -> a/b/Givens.Monoid#[A] [20:8..21:4): <- a/b/Givens.given_Monoid_String. [20:8..20:14): Monoid -> a/b/Givens.Monoid# [20:15..20:21): String -> scala/Predef.String# @@ -1355,11 +1355,11 @@ Occurrences: [22:8..22:15): (x: Str <- a/b/Givens.given_Monoid_String.combine(). [22:9..22:10): x <- a/b/Givens.given_Monoid_String.combine().(x) [22:12..22:18): String -> scala/Predef.String# -[22:29..22:30): y <- a/b/Givens.given_Monoid_String.combine().(y) -[22:32..22:38): String -> scala/Predef.String# -[22:42..22:43): x -> a/b/Givens.given_Monoid_String.combine().(x) -[22:44..22:45): + -> java/lang/String#`+`(). -[22:46..22:47): y -> a/b/Givens.given_Monoid_String.combine().(y) +[22:28..22:29): y <- a/b/Givens.given_Monoid_String.combine().(y) +[22:31..22:37): String -> scala/Predef.String# +[22:41..22:42): x -> a/b/Givens.given_Monoid_String.combine().(x) +[22:43..22:44): + -> java/lang/String#`+`(). +[22:45..22:46): y -> a/b/Givens.given_Monoid_String.combine().(y) [24:15..24:25): int2String <- a/b/Givens.int2String(). [24:27..24:37): Conversion -> scala/Conversion# [24:38..24:41): Int -> scala/Int# @@ -3565,3 +3565,4 @@ Occurrences: [2:46..2:47): z -> _empty_/toplevel$package.combine(+1).(z) [3:4..3:11): combine <- _empty_/toplevel$package.combine(+2). [4:4..4:7): foo <- _empty_/toplevel$package.foo(). + From 90492d7c5fd3e2968fa111e3d7d0a9ea5b668ba9 Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Tue, 7 Jan 2020 17:29:05 +0100 Subject: [PATCH 03/11] New syntax for collective extension methods It's now: ```scala extension listOps of [T](xs: List[T]) with { def second = xs.tail.head def third: T = xs.tail.tail.head } ``` instead of ```scala given listOps: [T](xs: List[T]) extended with { ... } --- .../src/dotty/tools/dotc/core/StdNames.scala | 1 + .../dotty/tools/dotc/parsing/Parsers.scala | 49 +++++++++++++++---- .../dotty/tools/dotc/parsing/Scanners.scala | 1 + ...2019-12-20-21th-dotty-milestone-release.md | 2 +- docs/docs/internals/syntax.md | 10 ++-- .../reference/contextual/extension-methods.md | 22 +++++---- .../contextual/relationship-implicits.md | 2 +- tests/neg/extension-methods.scala | 2 +- tests/neg/extmethod-overload.scala | 4 +- tests/neg/i5455.scala | 2 +- tests/neg/i6801.scala | 2 +- tests/neg/i6900.scala | 2 +- tests/neg/i7529.scala | 2 +- tests/neg/missing-implicit1.scala | 2 +- tests/pos/i6900.scala | 2 +- tests/pos/i7084.scala | 2 +- tests/pos/i7087.scala | 2 +- tests/pos/implicit-scope.scala | 2 +- tests/pos/matrixOps.scala | 2 +- tests/pos/mirror-implicit-scope.scala | 4 +- tests/pos/reference/delegates.scala | 10 ++-- tests/pos/reference/extension-methods.scala | 6 +-- tests/pos/reference/opaque.scala | 2 +- .../pos/tasty-reflect-opaque-api-proto.scala | 2 +- tests/run/extension-specificity.scala | 4 +- tests/run/extmethod-overload.scala | 2 +- tests/run/extmethods2.scala | 6 +-- tests/run/i6902.scala | 4 +- tests/run/instances-anonymous.scala | 6 +-- tests/run/instances.scala | 8 +-- 30 files changed, 101 insertions(+), 66 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/core/StdNames.scala b/compiler/src/dotty/tools/dotc/core/StdNames.scala index f5865c7750c4..7e09701193d8 100644 --- a/compiler/src/dotty/tools/dotc/core/StdNames.scala +++ b/compiler/src/dotty/tools/dotc/core/StdNames.scala @@ -508,6 +508,7 @@ object StdNames { val notify_ : N = "notify" val null_ : N = "null" val nullExpr: N = "nullExpr" + val of: N = "of" val ofDim: N = "ofDim" val opaque: N = "opaque" val open: N = "open" diff --git a/compiler/src/dotty/tools/dotc/parsing/Parsers.scala b/compiler/src/dotty/tools/dotc/parsing/Parsers.scala index 386694cee662..cf052c65d94b 100644 --- a/compiler/src/dotty/tools/dotc/parsing/Parsers.scala +++ b/compiler/src/dotty/tools/dotc/parsing/Parsers.scala @@ -188,7 +188,7 @@ object Parsers { /* -------------- TOKEN CLASSES ------------------------------------------- */ def isIdent = in.isIdent - def isIdent(name: Name) = in.token == IDENTIFIER && in.name == name + def isIdent(name: Name) = in.isIdent(name) def isSimpleLiteral = simpleLiteralTokens contains in.token def isLiteral = literalTokens contains in.token def isNumericLit = numericLitTokens contains in.token @@ -216,10 +216,11 @@ object Parsers { in.canStartExprTokens.contains(in.token) && !in.isSoftModifierInModifierPosition def isDefIntro(allowedMods: BitSet, excludedSoftModifiers: Set[TermName] = Set.empty): Boolean = - in.token == AT || - (defIntroTokens `contains` in.token) || - (allowedMods `contains` in.token) || - in.isSoftModifierInModifierPosition && !excludedSoftModifiers.contains(in.name) + in.token == AT + || defIntroTokens.contains(in.token) + || allowedMods.contains(in.token) + || in.isSoftModifierInModifierPosition && !excludedSoftModifiers.contains(in.name) + || isIdent(nme.extension) && followingIsExtension() def isStatSep: Boolean = in.isNewLine || in.token == SEMI @@ -944,6 +945,13 @@ object Parsers { lookahead.skipParens() lookahead.token == COLON || lookahead.token == SUBTYPE + def followingIsExtension() = + val lookahead = in.LookaheadScanner() + lookahead.nextToken() + if lookahead.isIdent && !lookahead.isIdent(nme.of) then + lookahead.nextToken() + lookahead.isIdent(nme.of) + /* --------- OPERAND/OPERATOR STACK --------------------------------------- */ var opStack: List[OpInfo] = Nil @@ -3275,6 +3283,7 @@ object Parsers { * | [‘case’] ‘object’ ObjectDef * | ‘enum’ EnumDef * | ‘given’ GivenDef + * | ‘extension’ ExtensionDef */ def tmplDef(start: Int, mods: Modifiers): Tree = in.token match { @@ -3293,8 +3302,11 @@ object Parsers { case GIVEN => givenDef(start, mods, atSpan(in.skipToken()) { Mod.Given() }) case _ => - syntaxErrorOrIncomplete(ExpectedStartOfTopLevelDefinition()) - EmptyTree + if isIdent(nme.extension) && followingIsExtension() then + extensionDef(start, mods) + else + syntaxErrorOrIncomplete(ExpectedStartOfTopLevelDefinition()) + EmptyTree } /** ClassDef ::= id ClassConstr TemplateOpt @@ -3522,6 +3534,23 @@ object Parsers { finalizeDef(gdef, mods1, start) } + /** ExtensionDef ::= [id] ‘of’ ExtParamClause {GivenParamClause} ‘with’ ExtMethods + */ + def extensionDef(start: Offset, mods: Modifiers): ModuleDef = + in.nextToken() + val name = if isIdent && !isIdent(nme.of) then ident() else EmptyTermName + if !isIdent(nme.of) then syntaxErrorOrIncomplete("`of` expected") + if isIdent(nme.of) then in.nextToken() + val tparams = typeParamClauseOpt(ParamOwner.Def) + val extParams = paramClause(0, prefix = true) + val givenParamss = paramClauses(givenOnly = true) + accept(WITH) + if !in.isNestedStart then syntaxError("Extension without extension methods") + val templ = templateBodyOpt(makeConstructor(tparams, extParams :: givenParamss), Nil, Nil) + templ.body.foreach(checkExtensionMethod(tparams, _)) + val edef = ModuleDef(name, templ) + finalizeDef(edef, addFlag(mods, Given), start) + /* -------- TEMPLATES ------------------------------------------- */ /** SimpleConstrApp ::= AnnotType {ParArgumentExprs} @@ -3681,7 +3710,7 @@ object Parsers { def templateStatSeq(): (ValDef, List[Tree]) = checkNoEscapingPlaceholders { var self: ValDef = EmptyValDef val stats = new ListBuffer[Tree] - if (isExprIntro) { + if (isExprIntro && !isDefIntro(modifierTokens)) { val first = expr1() if (in.token == ARROW) { first match { @@ -3707,10 +3736,10 @@ object Parsers { stats ++= importClause(IMPORT, Import) else if (in.token == EXPORT) stats ++= importClause(EXPORT, Export.apply) - else if (isExprIntro) - stats += expr1() else if (isDefIntro(modifierTokensOrCase)) stats +++= defOrDcl(in.offset, defAnnotsMods(modifierTokens)) + else if (isExprIntro) + stats += expr1() else if (!isStatSep) { exitOnError = mustStartStat syntaxErrorOrIncomplete("illegal start of definition") diff --git a/compiler/src/dotty/tools/dotc/parsing/Scanners.scala b/compiler/src/dotty/tools/dotc/parsing/Scanners.scala index 54e1765a7df5..7225ef6c7616 100644 --- a/compiler/src/dotty/tools/dotc/parsing/Scanners.scala +++ b/compiler/src/dotty/tools/dotc/parsing/Scanners.scala @@ -1016,6 +1016,7 @@ object Scanners { def isNewLine = token == NEWLINE || token == NEWLINES def isIdent = token == IDENTIFIER || token == BACKQUOTED_IDENT + def isIdent(name: Name) = token == IDENTIFIER && this.name == name def isNestedStart = token == LBRACE || token == INDENT def isNestedEnd = token == RBRACE || token == OUTDENT diff --git a/docs/blog/_posts/2019-12-20-21th-dotty-milestone-release.md b/docs/blog/_posts/2019-12-20-21th-dotty-milestone-release.md index b409cc8c417b..e1d5cc8835a7 100644 --- a/docs/blog/_posts/2019-12-20-21th-dotty-milestone-release.md +++ b/docs/blog/_posts/2019-12-20-21th-dotty-milestone-release.md @@ -183,7 +183,7 @@ given extension (s: String) { ... } ``` or ```scala -given listOps: [T](xs: List[T]) extended with { ... } +extension listOps of [T](xs: List[T]) with { ... } given (s: String) extended with { ... } ``` diff --git a/docs/docs/internals/syntax.md b/docs/docs/internals/syntax.md index dfe6fe5957a9..08fc95e33a8e 100644 --- a/docs/docs/internals/syntax.md +++ b/docs/docs/internals/syntax.md @@ -103,7 +103,7 @@ yield ### Soft keywords ``` -as derives inline opaque open +derives extension inline opaque open ~ * | & + - ``` @@ -378,7 +378,7 @@ TmplDef ::= ([‘case’] ‘class’ | ‘trait’) ClassDef | [‘case’] ‘object’ ObjectDef | ‘enum’ EnumDef | ‘given’ GivenDef - | Export + | ‘extension’ ExtensionDef ClassDef ::= id ClassConstr [Template] ClassDef(mods, name, tparams, templ) ClassConstr ::= [ClsTypeParamClause] [ConstrMods] ClsParamClauses with DefDef(_, , Nil, vparamss, EmptyTree, EmptyTree) as first stat ConstrMods ::= {Annotation} [AccessModifier] @@ -388,11 +388,11 @@ GivenDef ::= [GivenSig (‘:’ | <:)] {FunArgTypes ‘=>’} AnnotType ‘=’ Expr | [GivenSig ‘:’] {FunArgTypes ‘=>’} ConstrApps [[‘with’] TemplateBody] - | [id ‘:’] ExtParamClause {GivenParamClause} - ‘extended’ ‘with’ ExtMethods GivenSig ::= [id] [DefTypeParamClause] {GivenParamClause} +ExtensionDef ::= [id] ‘of’ ExtParamClause {GivenParamClause} + ‘with’ ExtMethods +ExtMethods ::= ‘{’ ‘def’ DefDef {semi ‘def’ DefDef} ‘}’ ExtParamClause ::= [DefTypeParamClause] ‘(’ DefParam ‘)’ -ExtMethods ::= [nl] ‘{’ ‘def’ DefDef {semi ‘def’ DefDef} ‘}’ Template ::= InheritClauses [[‘with’] TemplateBody] Template(constr, parents, self, stats) InheritClauses ::= [‘extends’ ConstrApps] [‘derives’ QualId {‘,’ QualId}] ConstrApps ::= ConstrApp {(‘,’ | ‘with’) ConstrApp} diff --git a/docs/docs/reference/contextual/extension-methods.md b/docs/docs/reference/contextual/extension-methods.md index 78ef20c07a19..bf8140562684 100644 --- a/docs/docs/reference/contextual/extension-methods.md +++ b/docs/docs/reference/contextual/extension-methods.md @@ -80,7 +80,7 @@ So `circle.circumference` translates to `CircleOps.circumference(circle)`, provi ### Operators -The extension method syntax also applies to the definition of operators. +The extension method syntax also applies to the definition of operators. In this case it is allowed and preferable to omit the period between the leading parameter list and the operator. In each case the definition syntax mirrors the way the operator is applied. Examples: @@ -122,24 +122,25 @@ If an extension method has type parameters, they come immediately after the `def ```scala List(1, 2, 3).second[Int] ``` -### Given Instances for Extension Methods +### Collective Extensions -`given` extensions are given instances that define extension methods and nothing else. Examples: +A collective extension defines one or more concrete methods that have the same type parameters +and prefix parameter. Examples: ```scala -given stringOps: (xs: Seq[String]) extended with { +extension stringOps of (xs: Seq[String]) with { def longestStrings: Seq[String] = { val maxLength = xs.map(_.length).max xs.filter(_.length == maxLength) } } -given listOps: [T](xs: List[T]) extended with { +extension listOps of [T](xs: List[T]) with { def second = xs.tail.head def third: T = xs.tail.tail.head } -given [T](xs: List[T])(given Ordering[T]) extended with { +extension of [T](xs: List[T])(given Ordering[T]) with { def largest(n: Int) = xs.sorted.takeRight(n) } ``` @@ -163,6 +164,8 @@ given given_largest_of_List_T: AnyRef { } ``` +`extension` and `of` are soft keywords. They can also be used as a regular identifiers. + ### Syntax Here are the syntax changes for extension methods and given extensions relative @@ -170,8 +173,9 @@ to the [current syntax](../../internals/syntax.md). `extension` is a soft keywor ``` DefSig ::= ... | ExtParamClause [nl] [‘.’] id DefParamClauses -GivenDef ::= ... - [id ‘:’] ‘extension’ ExtParamClause {GivenParamClause} ExtMethods ExtParamClause ::= [DefTypeParamClause] ‘(’ DefParam ‘)’ -ExtMethods ::= [nl] ‘{’ ‘def’ DefDef {semi ‘def’ DefDef} ‘}’ +TmplDef ::= ... + | ‘extension’ ExtensionDef +ExtensionDef ::= [id] ‘of’ ExtParamClause {GivenParamClause} ‘with’ ExtMethods +ExtMethods ::= ‘{’ ‘def’ DefDef {semi ‘def’ DefDef} ‘}’ ``` diff --git a/docs/docs/reference/contextual/relationship-implicits.md b/docs/docs/reference/contextual/relationship-implicits.md index f9ac61f7ddf9..8c03b5fc1dbd 100644 --- a/docs/docs/reference/contextual/relationship-implicits.md +++ b/docs/docs/reference/contextual/relationship-implicits.md @@ -65,7 +65,7 @@ Anonymous given instances that define extension methods get their name from the name of the first extension method and the toplevel type constructor of its first parameter. For example, the given instance ```scala -given [T] (xs: List[T]) extended with { +extension of [T] (xs: List[T]) with { def second = ... } ``` diff --git a/tests/neg/extension-methods.scala b/tests/neg/extension-methods.scala index 356a52c3ff3c..ca870a223a4f 100644 --- a/tests/neg/extension-methods.scala +++ b/tests/neg/extension-methods.scala @@ -10,7 +10,7 @@ object Test { "".l2 // error 1.l1 // error - given [T](xs: List[T]) extended with { + extension of [T](xs: List[T]) with { def (x: Int).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 diff --git a/tests/neg/extmethod-overload.scala b/tests/neg/extmethod-overload.scala index ecf6afc73402..333f0c34e19d 100644 --- a/tests/neg/extmethod-overload.scala +++ b/tests/neg/extmethod-overload.scala @@ -1,8 +1,8 @@ object Test { - given a: (x: Int) extended with + extension a of (x: Int) with def |+| (y: Int) = x + y - given b: (x: Int) extended with { + extension b of (x: Int) with { def |+| (y: String) = x + y.length } assert((1 |+| 2) == 3) // error ambiguous diff --git a/tests/neg/i5455.scala b/tests/neg/i5455.scala index c73da95e2062..7c7676a74c43 100644 --- a/tests/neg/i5455.scala +++ b/tests/neg/i5455.scala @@ -11,7 +11,7 @@ object Library { def toInt(n: Nat): Int = n } - given (x: Nat) extended with + extension of (x: Nat) with def * (y: Nat): Nat = x * y def toInt: Int = x } diff --git a/tests/neg/i6801.scala b/tests/neg/i6801.scala index 94e417e7719c..ea27fd3a583c 100644 --- a/tests/neg/i6801.scala +++ b/tests/neg/i6801.scala @@ -1,4 +1,4 @@ -given myNumericOps: [T](x: T) extended with { +extension myNumericOps of [T](x: T) with { def + (y: T)(given n: Numeric[T]): T = n.plus(x,y) } def foo[T: Numeric](x: T) = 1f + x // error: no implicit argument of type Numeric[Any] diff --git a/tests/neg/i6900.scala b/tests/neg/i6900.scala index 7e696e320828..a680d074853d 100644 --- a/tests/neg/i6900.scala +++ b/tests/neg/i6900.scala @@ -1,7 +1,7 @@ object Test2 { // Works with extension method - given [A](a: A) extended with + extension of [A](a: A) with def foo[C]: C => A = _ => a // error: extension method cannot have type parameters 1.foo.foo diff --git a/tests/neg/i7529.scala b/tests/neg/i7529.scala index 974d59b268b0..64c19a42b6da 100644 --- a/tests/neg/i7529.scala +++ b/tests/neg/i7529.scala @@ -1,4 +1,4 @@ -given fooOps: [A](a: A) extended with +extension fooOps of [A](a: A) with @nonsense // error: not found: nonsense def foo = ??? \ No newline at end of file diff --git a/tests/neg/missing-implicit1.scala b/tests/neg/missing-implicit1.scala index 88d5bb23a51d..239771dd42b2 100644 --- a/tests/neg/missing-implicit1.scala +++ b/tests/neg/missing-implicit1.scala @@ -7,7 +7,7 @@ object testObjectInstance with object instances { given zipOption: Zip[Option] = ??? given traverseList: Traverse[List] = ??? - given listExtension: [T](xs: List[T]) extended with + extension listExtension of [T](xs: List[T]) with def second: T = xs.tail.head def [T](xs: List[T]) first: T = xs.head } diff --git a/tests/pos/i6900.scala b/tests/pos/i6900.scala index 3c3b23c11b9f..48cd4b4d6bad 100644 --- a/tests/pos/i6900.scala +++ b/tests/pos/i6900.scala @@ -21,7 +21,7 @@ object Test1 { object Test2 { // Works with extension method - given [A, C](a: A) extended with + extension of [A, C](a: A) with def foo: C => A = _ => a 1.foo.foo diff --git a/tests/pos/i7084.scala b/tests/pos/i7084.scala index b76042ee431c..8c353879a69b 100644 --- a/tests/pos/i7084.scala +++ b/tests/pos/i7084.scala @@ -2,7 +2,7 @@ object Test { type Foo - given (y: Any) extended with { + extension of (y: Any) with { def g(given Foo): Any = ??? } diff --git a/tests/pos/i7087.scala b/tests/pos/i7087.scala index c6340d61730f..f5d7f622d141 100644 --- a/tests/pos/i7087.scala +++ b/tests/pos/i7087.scala @@ -6,7 +6,7 @@ type F[T] = T match { case G[a] => String } -given [T](tup: T) extended with { +extension of [T](tup: T) with { def g(given Foo: F[T]) = ??? } diff --git a/tests/pos/implicit-scope.scala b/tests/pos/implicit-scope.scala index 813b6b42775f..cfe65ca934b6 100644 --- a/tests/pos/implicit-scope.scala +++ b/tests/pos/implicit-scope.scala @@ -9,7 +9,7 @@ object A { type FlagSet = opaques.FlagSet def FlagSet(bits: Long): FlagSet = opaques.FlagSet(bits) - given (xs: FlagSet) extended with { + extension of (xs: FlagSet) with { def bits: Long = opaques.toBits(xs) def | (ys: FlagSet): FlagSet = FlagSet(xs.bits | ys.bits) } diff --git a/tests/pos/matrixOps.scala b/tests/pos/matrixOps.scala index 067d23830877..baf4671ae2aa 100644 --- a/tests/pos/matrixOps.scala +++ b/tests/pos/matrixOps.scala @@ -3,7 +3,7 @@ object Test with type Matrix = Array[Array[Double]] type Vector = Array[Double] - given (m: Matrix) extended with + extension of (m: Matrix) with def nRows = m.length def nCols = m(0).length def row(i: Int): Vector = m(i) diff --git a/tests/pos/mirror-implicit-scope.scala b/tests/pos/mirror-implicit-scope.scala index a4c2dbcd7b3a..c185ab8d303c 100644 --- a/tests/pos/mirror-implicit-scope.scala +++ b/tests/pos/mirror-implicit-scope.scala @@ -3,14 +3,14 @@ import scala.deriving._ object Test { object K0 { type Generic[T] = Mirror { type Scope = K0.type ; type MirroredType = T ; type MirroredElemTypes } - given [T <: Product](gen: Generic[T]) extended with { + extension of [T <: Product](gen: Generic[T]) with { inline def toRepr (t: T): gen.MirroredElemTypes = Tuple.fromProduct(t).asInstanceOf } } object K1 { type Generic[F[_]] = Mirror { type Scope = K1.type ; type MirroredType = F ; type MirroredElemTypes[_] } - given [F[_] <: Product, T](gen: Generic[F]) extended with { + extension of [F[_] <: Product, T](gen: Generic[F]) with { inline def toRepr (t: F[T]): gen.MirroredElemTypes[T] = Tuple.fromProduct(t).asInstanceOf } } diff --git a/tests/pos/reference/delegates.scala b/tests/pos/reference/delegates.scala index 4252868bf9aa..536ac3456a2f 100644 --- a/tests/pos/reference/delegates.scala +++ b/tests/pos/reference/delegates.scala @@ -39,12 +39,12 @@ object Instances extends Common with if (fst != 0) fst else xs1.compareTo(ys1) end listOrd - given stringOps: (xs: Seq[String]) extended with + extension stringOps of (xs: Seq[String]) with def longestStrings: Seq[String] = val maxLength = xs.map(_.length).max xs.filter(_.length == maxLength) - given [T](xs: List[T]) extended with + extension of [T](xs: List[T]) with def second = xs.tail.head def third = xs.tail.tail.head @@ -133,7 +133,7 @@ object PostConditions with def result[T](given x: WrappedResult[T]): T = x - given [T](x: T) extended with + extension of [T](x: T) with def ensuring(condition: (given WrappedResult[T]) => Boolean): T = assert(condition(given x)) x @@ -153,12 +153,12 @@ object AnonymousInstances extends Common with val fst = x.compareTo(y) if (fst != 0) fst else xs1.compareTo(ys1) - given (xs: Seq[String]) extended with + extension of (xs: Seq[String]) with def longestStrings: Seq[String] = val maxLength = xs.map(_.length).max xs.filter(_.length == maxLength) - given [T](xs: List[T]) extended with + extension of [T](xs: List[T]) with def second = xs.tail.head given [From, To]: (c: Convertible[From, To]) => Convertible[List[From], List[To]] with diff --git a/tests/pos/reference/extension-methods.scala b/tests/pos/reference/extension-methods.scala index 614efe611314..3ac5e5f05934 100644 --- a/tests/pos/reference/extension-methods.scala +++ b/tests/pos/reference/extension-methods.scala @@ -41,19 +41,19 @@ object ExtMethods with List(1, 2, 3).second[Int] - given stringOps: (xs: Seq[String]) extended with { + extension stringOps of (xs: Seq[String]) with { def longestStrings: Seq[String] = { val maxLength = xs.map(_.length).max xs.filter(_.length == maxLength) } } - given listOps: [T](xs: List[T]) extended with + extension listOps of [T](xs: List[T]) with def second = xs.tail.head def third: T = xs.tail.tail.head - given [T](xs: List[T])(given Ordering[T]) extended with + extension of [T](xs: List[T])(given Ordering[T]) with def largest(n: Int) = xs.sorted.takeRight(n) given stringOps1: AnyRef { diff --git a/tests/pos/reference/opaque.scala b/tests/pos/reference/opaque.scala index 5b0b24aad768..9f2c3cc8e6c6 100644 --- a/tests/pos/reference/opaque.scala +++ b/tests/pos/reference/opaque.scala @@ -12,7 +12,7 @@ object Logarithms { } // Extension methods define opaque types' public APIs - given (x: Logarithm) extended with { + extension of (x: Logarithm) with { def toDouble: Double = math.exp(x) def + (y: Logarithm): Logarithm = Logarithm(math.exp(x) + math.exp(y)) def * (y: Logarithm): Logarithm = Logarithm(x + y) diff --git a/tests/pos/tasty-reflect-opaque-api-proto.scala b/tests/pos/tasty-reflect-opaque-api-proto.scala index 089e3c28437d..7f8e2d273e47 100644 --- a/tests/pos/tasty-reflect-opaque-api-proto.scala +++ b/tests/pos/tasty-reflect-opaque-api-proto.scala @@ -10,7 +10,7 @@ class Reflect(val internal: CompilerInterface) { opaque type Term <: Tree = internal.Term object Tree { - given ops: (tree: Tree) extended with { + extension ops of (tree: Tree) with { def show: String = ??? } } diff --git a/tests/run/extension-specificity.scala b/tests/run/extension-specificity.scala index 35ac40ff1e3a..95a57392b8af 100644 --- a/tests/run/extension-specificity.scala +++ b/tests/run/extension-specificity.scala @@ -1,10 +1,10 @@ class A class B extends A -given a: (x: A) extended with +extension a of (x: A) with def foo: Int = 1 -given b: (x: B) extended with +extension b of (x: B) with def foo: Int = 2 @main def Test = diff --git a/tests/run/extmethod-overload.scala b/tests/run/extmethod-overload.scala index c5454944bd63..c01c4f634a6f 100644 --- a/tests/run/extmethod-overload.scala +++ b/tests/run/extmethod-overload.scala @@ -97,7 +97,7 @@ object Test extends App { def (x: Int).yy(y: Int) = x + y } - given (x: Int) extended with { + extension of (x: Int) with { def yy (y: Int) = x - y } diff --git a/tests/run/extmethods2.scala b/tests/run/extmethods2.scala index 90441a4aa97d..101b5e1093d6 100644 --- a/tests/run/extmethods2.scala +++ b/tests/run/extmethods2.scala @@ -16,15 +16,15 @@ object Test extends App { test(given TC()) object A { - given listOps: [T](xs: List[T]) extended with { + extension listOps of [T](xs: List[T]) with { def second: T = xs.tail.head def third: T = xs.tail.tail.head def concat(ys: List[T]) = xs ++ ys } - given polyListOps: [T, U](xs: List[T]) extended with { + extension polyListOps of [T, U](xs: List[T]) with { def zipp(ys: List[U]): List[(T, U)] = xs.zip(ys) } - given (xs: List[Int]) extended with { + extension of (xs: List[Int]) with { def prod = (1 /: xs)(_ * _) } } diff --git a/tests/run/i6902.scala b/tests/run/i6902.scala index e3a40dd02f1e..843ade0dd54d 100644 --- a/tests/run/i6902.scala +++ b/tests/run/i6902.scala @@ -1,6 +1,6 @@ object Test { - given [A](a: A) extended with { def <<< : A = a } - given (b: Int) extended with { def <<<< : Int = b } + extension of [A](a: A) with { def <<< : A = a } + extension of (b: Int) with { def <<<< : Int = b } def main(args: Array[String]): Unit = { 1.<<< diff --git a/tests/run/instances-anonymous.scala b/tests/run/instances-anonymous.scala index 8746c623a0e0..65150f54c03c 100644 --- a/tests/run/instances-anonymous.scala +++ b/tests/run/instances-anonymous.scala @@ -8,7 +8,7 @@ object Test extends App { case class Circle(x: Double, y: Double, radius: Double) - given (c: Circle) extended with { + extension of (c: Circle) with { def circumference: Double = c.radius * math.Pi * 2 } @@ -25,13 +25,13 @@ object Test extends App { val names = List("hi", "hello", "world") assert(names.longestStrings == List("hello", "world")) - given [T](xs: Seq[T]) extended with { + extension of [T](xs: Seq[T]) with { def second = xs.tail.head } assert(names.longestStrings.second == "world") - given [T](xs: List[List[T]]) extended with { + extension of [T](xs: List[List[T]]) with { def flattened = xs.foldLeft[List[T]](Nil)(_ ++ _) } diff --git a/tests/run/instances.scala b/tests/run/instances.scala index 033bc81c0f52..e1a0e34c64e2 100644 --- a/tests/run/instances.scala +++ b/tests/run/instances.scala @@ -8,14 +8,14 @@ object Test extends App { case class Circle(x: Double, y: Double, radius: Double) - given circleOps: (c: Circle) extended with + extension circleOps of (c: Circle) with def circumference: Double = c.radius * math.Pi * 2 val circle = new Circle(1, 1, 2.0) assert(circle.circumference == circleOps.circumference(circle)) - given stringOps: (xs: Seq[String]) extended with + extension stringOps of (xs: Seq[String]) with def longestStrings: Seq[String] = val maxLength = xs.map(_.length).max xs.filter(_.length == maxLength) @@ -23,12 +23,12 @@ object Test extends App { val names = List("hi", "hello", "world") assert(names.longestStrings == List("hello", "world")) - given [T](xs: Seq[T]) extended with + extension of [T](xs: Seq[T]) with def second = xs.tail.head assert(names.longestStrings.second == "world") - given listListOps: [T](xs: List[List[T]]) extended with + extension listListOps of [T](xs: List[List[T]]) with def flattened = xs.foldLeft[List[T]](Nil)(_ ++ _) // A right associative op. Note: can't use given extension for this! From b63dd6aab87c67c929055301e274955d69539635 Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Tue, 7 Jan 2020 18:01:31 +0100 Subject: [PATCH 04/11] Change naming scheme for anonymous extensions They now start with `extension_` instead of `given_`. --- .../src/dotty/tools/dotc/ast/Desugar.scala | 47 +++++++++---------- .../src/dotty/tools/dotc/typer/Typer.scala | 2 +- .../reference/contextual/extension-methods.md | 2 +- .../contextual/relationship-implicits.md | 16 +++++-- 4 files changed, 35 insertions(+), 32 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/ast/Desugar.scala b/compiler/src/dotty/tools/dotc/ast/Desugar.scala index e5231018126a..6eedc729c226 100644 --- a/compiler/src/dotty/tools/dotc/ast/Desugar.scala +++ b/compiler/src/dotty/tools/dotc/ast/Desugar.scala @@ -954,10 +954,6 @@ object desugar { else tree } - /** Invent a name for an anonympus given of type or template `impl`. */ - def inventGivenName(impl: Tree)(implicit ctx: Context): SimpleName = - s"given_${inventName(impl)}".toTermName.asSimpleName - /** The normalized name of `mdef`. This means * 1. Check that the name does not redefine a Scala core class. * If it does redefine, issue an error and return a mangled name instead of the original one. @@ -965,7 +961,7 @@ object desugar { */ def normalizeName(mdef: MemberDef, impl: Tree)(implicit ctx: Context): Name = { var name = mdef.name - if (name.isEmpty) name = name.likeSpaced(inventGivenName(impl)) + if (name.isEmpty) name = name.likeSpaced(inventGivenOrExtensionName(impl)) if (ctx.owner == defn.ScalaPackageClass && defn.reservedScalaClassNames.contains(name.toTypeName)) { def kind = if (name.isTypeName) "class" else "object" ctx.error(em"illegal redefinition of standard $kind $name", mdef.sourcePos) @@ -974,27 +970,26 @@ object desugar { name } - /** Invent a name for an anonymous instance with template `impl`. - */ - private def inventName(impl: Tree)(implicit ctx: Context): String = impl match { - case impl: Template => - if (impl.parents.isEmpty) - impl.body.find { - case dd: DefDef if dd.mods.is(Extension) => true - case _ => false - } - match { - case Some(DefDef(name, _, (vparam :: _) :: _, _, _)) => - s"${name}_of_${inventTypeName(vparam.tpt)}" - case _ => - ctx.error(i"anonymous instance must implement a type or have at least one extension method", impl.sourcePos) - nme.ERROR.toString - } - else - impl.parents.map(inventTypeName(_)).mkString("_") - case impl: Tree => - inventTypeName(impl) - } + /** Invent a name for an anonympus given or extension of type or template `impl`. */ + def inventGivenOrExtensionName(impl: Tree)(given ctx: Context): SimpleName = + val str = impl match + case impl: Template => + if impl.parents.isEmpty then + impl.body.find { + case dd: DefDef if dd.mods.is(Extension) => true + case _ => false + } + match + case Some(DefDef(name, _, (vparam :: _) :: _, _, _)) => + s"extension_${name}_${inventTypeName(vparam.tpt)}" + case _ => + ctx.error(i"anonymous instance must implement a type or have at least one extension method", impl.sourcePos) + nme.ERROR.toString + else + impl.parents.map(inventTypeName(_)).mkString("given_", "_", "") + case impl: Tree => + "given_" ++ inventTypeName(impl) + str.toTermName.asSimpleName private class NameExtractor(followArgs: Boolean) extends UntypedTreeAccumulator[String] { private def extractArgs(args: List[Tree])(implicit ctx: Context): String = diff --git a/compiler/src/dotty/tools/dotc/typer/Typer.scala b/compiler/src/dotty/tools/dotc/typer/Typer.scala index da56757fc755..107eb7eb0f57 100644 --- a/compiler/src/dotty/tools/dotc/typer/Typer.scala +++ b/compiler/src/dotty/tools/dotc/typer/Typer.scala @@ -1541,7 +1541,7 @@ class Typer extends Namer var name = tree.name if (name == nme.WILDCARD && tree.mods.is(Given)) { val Typed(_, tpt): @unchecked = tree.body - name = desugar.inventGivenName(tpt) + name = desugar.inventGivenOrExtensionName(tpt) } if (name == nme.WILDCARD) body1 else { diff --git a/docs/docs/reference/contextual/extension-methods.md b/docs/docs/reference/contextual/extension-methods.md index bf8140562684..b95f7a0cf5ee 100644 --- a/docs/docs/reference/contextual/extension-methods.md +++ b/docs/docs/reference/contextual/extension-methods.md @@ -158,7 +158,7 @@ given listOps: AnyRef { def [T](xs: List[T]) second = xs.tail.head def [T](xs: List[T]) third: T = xs.tail.tail.head } -given given_largest_of_List_T: AnyRef { +given extension_largest_List_T: AnyRef { def [T](xs: List[T]) largest (given Ordering[T])(n: Int) = xs.sorted.takeRight(n) } diff --git a/docs/docs/reference/contextual/relationship-implicits.md b/docs/docs/reference/contextual/relationship-implicits.md index 8c03b5fc1dbd..bffd02060a2b 100644 --- a/docs/docs/reference/contextual/relationship-implicits.md +++ b/docs/docs/reference/contextual/relationship-implicits.md @@ -61,15 +61,23 @@ The synthesized type names are formed from Tuples are treated as transparent, i.e. a type `F[(X, Y)]` would get the synthesized name `F_X_Y`. Directly implemented function types `A => B` are represented as `A_to_B`. Function types used as arguments to other type constructors are represented as `Function`. -Anonymous given instances that define extension methods -get their name from the name of the first extension method and the toplevel type -constructor of its first parameter. For example, the given instance +### Anonymous Collective Extensions + +Anonymous collective extensions also get compiler synthesized names, which are formed from + + - the prefix `extension_` + - the name of the first defined extension method + - the simple name of the first parameter type of this extension method + - the simple name(s) of the toplevel argument type constructors to this type. + +For example, the extension ```scala extension of [T] (xs: List[T]) with { def second = ... } ``` -gets the synthesized name `given_second_of_List_T`. +gets the synthesized name `extension_second_List_T`. + ### Given Clauses From 74e56a0402843568d299f78bd248c810d4066c8b Mon Sep 17 00:00:00 2001 From: Jamie Thompson Date: Wed, 8 Jan 2020 11:37:25 +0100 Subject: [PATCH 05/11] update expect tests for semanticdb --- tests/semanticdb/expect/Givens.expect.scala | 16 +++--- tests/semanticdb/metac.expect | 62 ++++++++++----------- 2 files changed, 39 insertions(+), 39 deletions(-) diff --git a/tests/semanticdb/expect/Givens.expect.scala b/tests/semanticdb/expect/Givens.expect.scala index 49d99beb29eb..433e1fe1ec96 100644 --- a/tests/semanticdb/expect/Givens.expect.scala +++ b/tests/semanticdb/expect/Givens.expect.scala @@ -3,16 +3,16 @@ package b object Givens/*<-a::b::Givens.*/ - given extension [A](any: /*<-a::b::Givens.given_sayHello_of_A.*//*<-a::b::Givens.given_sayHello_of_A.sayHello().[A]*//*<-a::b::Givens.given_sayHello_of_A.sayHello().(any)*/A/*->a::b::Givens.given_sayHello_of_A.sayHello().[A]*/) - def sayHello/*<-a::b::Givens.given_sayHello_of_A.sayHello().*/ = s"/*->scala::StringContext.apply().*/Hello, I am $any/*->a::b::Givens.given_sayHello_of_A.sayHello().(any)*//*->scala::StringContext#s().*/" + given extension [A](any: A/*<-a::b::Givens.extension_sayHello_A.*//*<-a::b::Givens.extension_sayHello_A.sayHello().[A]*//*<-a::b::Givens.extension_sayHello_A.sayHello().(any)*//*->a::b::Givens.extension_sayHello_A.sayHello().[A]*/) + def sayHello/*<-a::b::Givens.extension_sayHello_A.sayHello().*/ = s"/*->scala::StringContext.apply().*/Hello, I am $any/*->a::b::Givens.extension_sayHello_A.sayHello().(any)*//*->scala::StringContext#s().*/" - given extension [B](any: B)/*<-a::b::Givens.given_sayGoodbye_of_B.*//*<-a::b::Givens.given_sayGoodbye_of_B.sayGoodbye().[B]*//*<-a::b::Givens.given_sayGoodbye_of_B.saySoLong().[B]*//*<-a::b::Givens.given_sayGoodbye_of_B.sayGoodbye().(any)*//*<-a::b::Givens.given_sayGoodbye_of_B.saySoLong().(any)*//*->a::b::Givens.given_sayGoodbye_of_B.sayGoodbye().[B]*//*->a::b::Givens.given_sayGoodbye_of_B.saySoLong().[B]*/ - def sayGoodbye/*<-a::b::Givens.given_sayGoodbye_of_B.sayGoodbye().*/ = s"/*->scala::StringContext.apply().*/Goodbye, from $any/*->a::b::Givens.given_sayGoodbye_of_B.sayGoodbye().(any)*//*->scala::StringContext#s().*/" - def saySoLong/*<-a::b::Givens.given_sayGoodbye_of_B.saySoLong().*/ = s"/*->scala::StringContext.apply().*/So Long, from $any/*->a::b::Givens.given_sayGoodbye_of_B.saySoLong().(any)*//*->scala::StringContext#s().*/" + given extension [B](any: B) +/*<-a::b::Givens.extension_sayGoodbye_B.*//*<-a::b::Givens.extension_sayGoodbye_B.sayGoodbye().[B]*//*<-a::b::Givens.extension_sayGoodbye_B.saySoLong().[B]*//*<-a::b::Givens.extension_sayGoodbye_B.sayGoodbye().(any)*//*<-a::b::Givens.extension_sayGoodbye_B.saySoLong().(any)*//*->a::b::Givens.extension_sayGoodbye_B.sayGoodbye().[B]*//*->a::b::Givens.extension_sayGoodbye_B.saySoLong().[B]*/ def sayGoodbye/*<-a::b::Givens.extension_sayGoodbye_B.sayGoodbye().*/ = s"/*->scala::StringContext.apply().*/Goodbye, from $any/*->a::b::Givens.extension_sayGoodbye_B.sayGoodbye().(any)*//*->scala::StringContext#s().*/" + def saySoLong/*<-a::b::Givens.extension_sayGoodbye_B.saySoLong().*/ = s"/*->scala::StringContext.apply().*/So Long, from $any/*->a::b::Givens.extension_sayGoodbye_B.saySoLong().(any)*//*->scala::StringContext#s().*/" - val hello1/*<-a::b::Givens.hello1.*/ = /*->a::b::Givens.given_sayHello_of_A.sayHello().*/1.sayHello - val goodbye1/*<-a::b::Givens.goodbye1.*/ = /*->a::b::Givens.given_sayGoodbye_of_B.sayGoodbye().*/1.sayGoodbye - val soLong1/*<-a::b::Givens.soLong1.*/ = /*->a::b::Givens.given_sayGoodbye_of_B.saySoLong().*/1.saySoLong + val hello1/*<-a::b::Givens.hello1.*/ = /*->a::b::Givens.extension_sayHello_A.sayHello().*/1.sayHello + val goodbye1/*<-a::b::Givens.goodbye1.*/ = /*->a::b::Givens.extension_sayGoodbye_B.sayGoodbye().*/1.sayGoodbye + val soLong1/*<-a::b::Givens.soLong1.*/ = /*->a::b::Givens.extension_sayGoodbye_B.saySoLong().*/1.saySoLong trait Monoid/*<-a::b::Givens.Monoid#*/[A/*<-a::b::Givens.Monoid#[A]*/] def empty/*<-a::b::Givens.Monoid#empty().*/: A/*->a::b::Givens.Monoid#[A]*/ diff --git a/tests/semanticdb/metac.expect b/tests/semanticdb/metac.expect index 7ffd727153d7..7b59a7ccb38b 100644 --- a/tests/semanticdb/metac.expect +++ b/tests/semanticdb/metac.expect @@ -1280,6 +1280,17 @@ a/b/Givens.Monoid#combine(). => abstract method combine a/b/Givens.Monoid#combine().(x) => param x a/b/Givens.Monoid#combine().(y) => param y a/b/Givens.Monoid#empty(). => abstract method empty +a/b/Givens.extension_sayGoodbye_B. => final implicit object extension_sayGoodbye_B +a/b/Givens.extension_sayGoodbye_B.sayGoodbye(). => method sayGoodbye +a/b/Givens.extension_sayGoodbye_B.sayGoodbye().(any) => param any +a/b/Givens.extension_sayGoodbye_B.sayGoodbye().[B] => typeparam B +a/b/Givens.extension_sayGoodbye_B.saySoLong(). => method saySoLong +a/b/Givens.extension_sayGoodbye_B.saySoLong().(any) => param any +a/b/Givens.extension_sayGoodbye_B.saySoLong().[B] => typeparam B +a/b/Givens.extension_sayHello_A. => final implicit object extension_sayHello_A +a/b/Givens.extension_sayHello_A.sayHello(). => method sayHello +a/b/Givens.extension_sayHello_A.sayHello().(any) => param any +a/b/Givens.extension_sayHello_A.sayHello().[A] => typeparam A a/b/Givens.foo(). => method foo a/b/Givens.foo().(A) => implicit param A a/b/Givens.foo().[A] => typeparam A @@ -1288,17 +1299,6 @@ a/b/Givens.given_Monoid_String.combine(). => method combine a/b/Givens.given_Monoid_String.combine().(x) => param x a/b/Givens.given_Monoid_String.combine().(y) => param y a/b/Givens.given_Monoid_String.empty(). => method empty -a/b/Givens.given_sayGoodbye_of_B. => final implicit object given_sayGoodbye_of_B -a/b/Givens.given_sayGoodbye_of_B.sayGoodbye(). => method sayGoodbye -a/b/Givens.given_sayGoodbye_of_B.sayGoodbye().(any) => param any -a/b/Givens.given_sayGoodbye_of_B.sayGoodbye().[B] => typeparam B -a/b/Givens.given_sayGoodbye_of_B.saySoLong(). => method saySoLong -a/b/Givens.given_sayGoodbye_of_B.saySoLong().(any) => param any -a/b/Givens.given_sayGoodbye_of_B.saySoLong().[B] => typeparam B -a/b/Givens.given_sayHello_of_A. => final implicit object given_sayHello_of_A -a/b/Givens.given_sayHello_of_A.sayHello(). => method sayHello -a/b/Givens.given_sayHello_of_A.sayHello().(any) => param any -a/b/Givens.given_sayHello_of_A.sayHello().[A] => typeparam A a/b/Givens.goodbye1. => val method goodbye1 a/b/Givens.hello1. => val method hello1 a/b/Givens.int2String(). => final implicit macro int2String @@ -1308,35 +1308,35 @@ Occurrences: [0:8..0:9): a <- a/ [1:8..1:9): b <- a/b/ [3:7..3:13): Givens <- a/b/Givens. -[5:8..5:27): extension [A](any: <- a/b/Givens.given_sayHello_of_A. -[5:19..5:20): A <- a/b/Givens.given_sayHello_of_A.sayHello().[A] -[5:22..5:25): any <- a/b/Givens.given_sayHello_of_A.sayHello().(any) -[5:27..5:28): A -> a/b/Givens.given_sayHello_of_A.sayHello().[A] -[6:8..6:16): sayHello <- a/b/Givens.given_sayHello_of_A.sayHello(). +[5:8..5:28): extension [A](any: A <- a/b/Givens.extension_sayHello_A. +[5:19..5:20): A <- a/b/Givens.extension_sayHello_A.sayHello().[A] +[5:22..5:25): any <- a/b/Givens.extension_sayHello_A.sayHello().(any) +[5:27..5:28): A -> a/b/Givens.extension_sayHello_A.sayHello().[A] +[6:8..6:16): sayHello <- a/b/Givens.extension_sayHello_A.sayHello(). [6:21..6:21): -> scala/StringContext.apply(). -[6:34..6:37): any -> a/b/Givens.given_sayHello_of_A.sayHello().(any) +[6:34..6:37): any -> a/b/Givens.extension_sayHello_A.sayHello().(any) [6:37..6:37): -> scala/StringContext#s(). -[8:8..8:29): extension [B](any: B) <- a/b/Givens.given_sayGoodbye_of_B. -[8:19..8:20): B <- a/b/Givens.given_sayGoodbye_of_B.sayGoodbye().[B] -[8:19..8:20): B <- a/b/Givens.given_sayGoodbye_of_B.saySoLong().[B] -[8:22..8:25): any <- a/b/Givens.given_sayGoodbye_of_B.sayGoodbye().(any) -[8:22..8:25): any <- a/b/Givens.given_sayGoodbye_of_B.saySoLong().(any) -[8:27..8:28): B -> a/b/Givens.given_sayGoodbye_of_B.sayGoodbye().[B] -[8:27..8:28): B -> a/b/Givens.given_sayGoodbye_of_B.saySoLong().[B] -[9:8..9:18): sayGoodbye <- a/b/Givens.given_sayGoodbye_of_B.sayGoodbye(). +[8:8..9:0): <- a/b/Givens.extension_sayGoodbye_B. +[8:19..8:20): B <- a/b/Givens.extension_sayGoodbye_B.sayGoodbye().[B] +[8:19..8:20): B <- a/b/Givens.extension_sayGoodbye_B.saySoLong().[B] +[8:22..8:25): any <- a/b/Givens.extension_sayGoodbye_B.sayGoodbye().(any) +[8:22..8:25): any <- a/b/Givens.extension_sayGoodbye_B.saySoLong().(any) +[8:27..8:28): B -> a/b/Givens.extension_sayGoodbye_B.sayGoodbye().[B] +[8:27..8:28): B -> a/b/Givens.extension_sayGoodbye_B.saySoLong().[B] +[9:8..9:18): sayGoodbye <- a/b/Givens.extension_sayGoodbye_B.sayGoodbye(). [9:23..9:23): -> scala/StringContext.apply(). -[9:38..9:41): any -> a/b/Givens.given_sayGoodbye_of_B.sayGoodbye().(any) +[9:38..9:41): any -> a/b/Givens.extension_sayGoodbye_B.sayGoodbye().(any) [9:41..9:41): -> scala/StringContext#s(). -[10:8..10:17): saySoLong <- a/b/Givens.given_sayGoodbye_of_B.saySoLong(). +[10:8..10:17): saySoLong <- a/b/Givens.extension_sayGoodbye_B.saySoLong(). [10:22..10:22): -> scala/StringContext.apply(). -[10:37..10:40): any -> a/b/Givens.given_sayGoodbye_of_B.saySoLong().(any) +[10:37..10:40): any -> a/b/Givens.extension_sayGoodbye_B.saySoLong().(any) [10:40..10:40): -> scala/StringContext#s(). [12:6..12:12): hello1 <- a/b/Givens.hello1. -[12:15..12:15): -> a/b/Givens.given_sayHello_of_A.sayHello(). +[12:15..12:15): -> a/b/Givens.extension_sayHello_A.sayHello(). [13:6..13:14): goodbye1 <- a/b/Givens.goodbye1. -[13:17..13:17): -> a/b/Givens.given_sayGoodbye_of_B.sayGoodbye(). +[13:17..13:17): -> a/b/Givens.extension_sayGoodbye_B.sayGoodbye(). [14:6..14:13): soLong1 <- a/b/Givens.soLong1. -[14:16..14:16): -> a/b/Givens.given_sayGoodbye_of_B.saySoLong(). +[14:16..14:16): -> a/b/Givens.extension_sayGoodbye_B.saySoLong(). [16:8..16:14): Monoid <- a/b/Givens.Monoid# [16:14..16:17): <- a/b/Givens.Monoid#``(). [16:15..16:16): A <- a/b/Givens.Monoid#[A] From 12ffa6d9f795893bff3bcab43f47daf20254e7fd Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Tue, 7 Jan 2020 14:41:08 +0100 Subject: [PATCH 06/11] Syntax Change: Allow '.' in front of extension method Allow def (c: Circle).circumference: Double alongside def (c: Circle) circumference: Double The syntax with '.' is preferred for normal methods, which have names starting with a letter and which are not declared @infix. Right now, this preference is not enforced. --- .../dotty/tools/dotc/parsing/Parsers.scala | 8 +++-- docs/docs/contributing/debugging.md | 2 +- docs/docs/internals/syntax.md | 2 +- .../reference/contextual/extension-methods.md | 20 +++++++----- .../contextual/implicit-function-types.md | 2 +- .../contextual/relationship-implicits.md | 2 +- docs/docs/reference/contextual/typeclasses.md | 6 ++-- .../dropped-features/package-objects.md | 2 +- docs/docs/reference/metaprogramming/macros.md | 2 +- .../reference/other-new-features/opaques.md | 4 +-- .../other-new-features/tupled-function.md | 6 ++-- library/src/scala/IArray.scala | 30 ++++++++--------- .../neg-custom-args/extmethods-tparams.scala | 2 +- tests/neg-macros/i6432/Macro_1.scala | 2 +- tests/neg-macros/i6432b/Macro_1.scala | 2 +- .../neg-macros/reflect-inline/assert_1.scala | 2 +- .../Macro_1.scala | 2 +- .../Macro_1.scala | 2 +- tests/neg/extension-methods.scala | 4 +-- tests/neg/extmethod-override.scala | 4 +-- tests/neg/i5773.scala | 10 +++--- tests/neg/i6762b.scala | 2 +- tests/neg/i6779.scala | 2 +- tests/neg/i7438.scala | 4 +-- tests/neg/indent.scala | 2 +- tests/neg/opaque-bounds.scala | 4 +-- tests/pos/combine.scala | 2 +- tests/pos/consume.scala | 2 +- tests/pos/i5773.scala | 10 +++--- tests/pos/i5773a.scala | 6 ++-- tests/pos/i6395.scala | 4 +-- tests/pos/i7070.scala | 2 +- tests/pos/i7119.scala | 2 +- tests/pos/i7413.scala | 2 +- tests/pos/i7700.scala | 2 +- tests/pos/indent.scala | 2 +- tests/pos/opaque-propability-xm.scala | 10 +++--- tests/pos/opaque-xm.scala | 2 +- tests/pos/reference/delegates.scala | 32 +++++++++---------- tests/pos/reference/extension-methods.scala | 6 ++-- tests/pos/reference/opaque.scala | 4 +-- .../pos/toplevel-opaque-xm/Logarithm_1.scala | 2 +- .../f-interpolator-neg/Macros_1.scala | 2 +- tests/run/Pouring.scala | 2 +- tests/run/eq-xmethod.scala | 2 +- tests/run/exports.scala | 2 +- tests/run/extension-methods.scala | 14 ++++---- tests/run/extmethod-overload.scala | 8 ++--- tests/run/extmethods2.scala | 4 +-- tests/run/instances-anonymous.scala | 14 ++++---- tests/run/instances.scala | 12 +++---- ...ng-context-implicits-with-conversion.scala | 2 +- tests/run/toplevel-implicits/a.b.scala | 2 +- tests/semanticdb/expect/Enums.scala | 4 +-- tests/semanticdb/expect/Givens.scala | 4 +-- 55 files changed, 150 insertions(+), 142 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/parsing/Parsers.scala b/compiler/src/dotty/tools/dotc/parsing/Parsers.scala index f64f627b15d9..42373809eaf1 100644 --- a/compiler/src/dotty/tools/dotc/parsing/Parsers.scala +++ b/compiler/src/dotty/tools/dotc/parsing/Parsers.scala @@ -3105,7 +3105,7 @@ object Parsers { * | this ParamClause ParamClauses `=' ConstrExpr * DefDcl ::= DefSig `:' Type * DefSig ::= id [DefTypeParamClause] DefParamClauses - * | ExtParamClause [nl] id DefParamClauses + * | ExtParamClause [nl] [‘.’] id DefParamClauses */ def defDefOrDcl(start: Offset, mods: Modifiers): Tree = atSpan(start, nameStart) { def scala2ProcedureSyntax(resultTypeStr: String) = { @@ -3134,7 +3134,11 @@ object Parsers { makeConstructor(Nil, vparamss, rhs).withMods(mods).setComment(in.getDocComment(start)) } else { - def extParamss() = try paramClause(0, prefix = true) :: Nil finally newLineOpt() + def extParamss() = + try paramClause(0, prefix = true) :: Nil + finally + if in.token == DOT then in.nextToken() + else newLineOpt() val (leadingTparams, leadingVparamss, flags) = if in.token == LBRACKET then (typeParamClause(ParamOwner.Def), extParamss(), Method | Extension) diff --git a/docs/docs/contributing/debugging.md b/docs/docs/contributing/debugging.md index 5d3f7d96a9cc..62d1244002cd 100644 --- a/docs/docs/contributing/debugging.md +++ b/docs/docs/contributing/debugging.md @@ -88,7 +88,7 @@ But you can also do: assertPositioned(tree.reporting(s"Tree is: $result")) ``` -`def (a: A) reporting(f: given WrappedResult[T] => String, p: Printer = Printers.default): A` is defined on all types. The function `f` can be written without the argument since the argument is `given`. 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. +`def (a: A).reporting(f: given WrappedResult[T] => String, p: Printer = Printers.default): A` is defined on all types. The function `f` can be written without the argument since the argument is `given`. 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: diff --git a/docs/docs/internals/syntax.md b/docs/docs/internals/syntax.md index 6bbd74a65153..dfe6fe5957a9 100644 --- a/docs/docs/internals/syntax.md +++ b/docs/docs/internals/syntax.md @@ -358,7 +358,7 @@ ValDcl ::= ids ‘:’ Type VarDcl ::= ids ‘:’ Type PatDef(_, ids, tpe, EmptyTree) DefDcl ::= DefSig ‘:’ Type DefDef(_, name, tparams, vparamss, tpe, EmptyTree) DefSig ::= id [DefTypeParamClause] DefParamClauses - | ExtParamClause [nl] id DefParamClauses + | ExtParamClause [nl] [‘.’] id DefParamClauses TypeDcl ::= id [TypeParamClause] SubtypeBounds [‘=’ Type] TypeDefTree(_, name, tparams, bound Def ::= ‘val’ PatDef diff --git a/docs/docs/reference/contextual/extension-methods.md b/docs/docs/reference/contextual/extension-methods.md index 036f669ffe55..78ef20c07a19 100644 --- a/docs/docs/reference/contextual/extension-methods.md +++ b/docs/docs/reference/contextual/extension-methods.md @@ -8,7 +8,7 @@ Extension methods allow one to add methods to a type after the type is defined. ```scala case class Circle(x: Double, y: Double, radius: Double) -def (c: Circle) circumference: Double = c.radius * math.Pi * 2 +def (c: Circle).circumference: Double = c.radius * math.Pi * 2 ``` Like regular methods, extension methods can be invoked with infix `.`: @@ -42,7 +42,7 @@ As an example, consider an extension method `longestStrings` on `Seq[String]` de ```scala trait StringSeqOps { - def (xs: Seq[String]) longestStrings = { + def (xs: Seq[String]).longestStrings = { val maxLength = xs.map(_.length).max xs.filter(_.length == maxLength) } @@ -80,22 +80,26 @@ So `circle.circumference` translates to `CircleOps.circumference(circle)`, provi ### Operators -The extension method syntax also applies to the definition of operators. -In each case the definition syntax mirrors the way the operator is applied. +The extension method syntax also applies to the definition of operators. +In this case it is allowed and preferable to omit the period between the leading parameter list +and the operator. In each case the definition syntax mirrors the way the operator is applied. Examples: ```scala def (x: String) < (y: String) = ... def (x: Elem) +: (xs: Seq[Elem]) = ... +def (x: Number) min (y: Number) = ... "ab" < "c" 1 +: List(2, 3) +x min 3 ``` -The two definitions above translate to +The three definitions above translate to ```scala def < (x: String)(y: String) = ... def +: (xs: Seq[Elem])(x: Elem) = ... +def min(x: Number)(y: Number) = ... ``` -Note that swap of the two parameters `x` and `xs` when translating +Note the swap of the two parameters `x` and `xs` when translating the right-binding operator `+:` to an extension method. This is analogous to the implementation of right binding operators as normal methods. @@ -144,7 +148,7 @@ If a given extension is anonymous (as in the last clause), its name is synthesiz The extensions above are equivalent to the following regular given instances where the implemented parent is `AnyRef` and the parameters in the `extension` clause are repeated in each extension method definition: ```scala given stringOps: AnyRef { - def (xs: Seq[String]) longestStrings: Seq[String] = { + def (xs: Seq[String]).longestStrings: Seq[String] = { val maxLength = xs.map(_.length).max xs.filter(_.length == maxLength) } @@ -165,7 +169,7 @@ Here are the syntax changes for extension methods and given extensions relative to the [current syntax](../../internals/syntax.md). `extension` is a soft keyword, recognized only after a `given`. It can be used as an identifier everywhere else. ``` DefSig ::= ... - | ExtParamClause [nl] id DefParamClauses + | ExtParamClause [nl] [‘.’] id DefParamClauses GivenDef ::= ... [id ‘:’] ‘extension’ ExtParamClause {GivenParamClause} ExtMethods ExtParamClause ::= [DefTypeParamClause] ‘(’ DefParam ‘)’ diff --git a/docs/docs/reference/contextual/implicit-function-types.md b/docs/docs/reference/contextual/implicit-function-types.md index 49f3bae729f1..6ceaa6114a35 100644 --- a/docs/docs/reference/contextual/implicit-function-types.md +++ b/docs/docs/reference/contextual/implicit-function-types.md @@ -120,7 +120,7 @@ object PostConditions { def result[T](given r: WrappedResult[T]): T = r - def (x: T) ensuring[T](condition: (given WrappedResult[T]) => Boolean): T = { + def (x: T).ensuring[T](condition: (given WrappedResult[T]) => Boolean): T = { assert(condition(given x)) x } diff --git a/docs/docs/reference/contextual/relationship-implicits.md b/docs/docs/reference/contextual/relationship-implicits.md index e30ca53f421b..f9ac61f7ddf9 100644 --- a/docs/docs/reference/contextual/relationship-implicits.md +++ b/docs/docs/reference/contextual/relationship-implicits.md @@ -104,7 +104,7 @@ will map to given 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 +def (c: Circle).circumference: Double = c.radius * math.Pi * 2 ``` could be simulated to some degree by ```scala diff --git a/docs/docs/reference/contextual/typeclasses.md b/docs/docs/reference/contextual/typeclasses.md index 01b2fd048a32..3f82ee269231 100644 --- a/docs/docs/reference/contextual/typeclasses.md +++ b/docs/docs/reference/contextual/typeclasses.md @@ -11,7 +11,7 @@ with canonical implementations defined by given instances. Here are some example ```scala trait SemiGroup[T] with - def (x: T) combine (y: T): T + def (x: T).combine(y: T): T trait Monoid[T] extends SemiGroup[T] with def unit: T @@ -20,11 +20,11 @@ object Monoid with def apply[T](given Monoid[T]) = summon[Monoid[T]] given Monoid[String] with - def (x: String) combine (y: String): String = x.concat(y) + def (x: String).combine(y: String): String = x.concat(y) def unit: String = "" given Monoid[Int] with - def (x: Int) combine (y: Int): Int = x + y + def (x: Int).combine(y: Int): Int = x + y def unit: Int = 0 def sum[T: Monoid](xs: List[T]): T = diff --git a/docs/docs/reference/dropped-features/package-objects.md b/docs/docs/reference/dropped-features/package-objects.md index 2e0f51b25fb1..22ba96eb5f86 100644 --- a/docs/docs/reference/dropped-features/package-objects.md +++ b/docs/docs/reference/dropped-features/package-objects.md @@ -22,7 +22,7 @@ def b = a._2 case class C() implicit object Cops { - def (x: C) pair (y: C) = (x, y) + def (x: C).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. diff --git a/docs/docs/reference/metaprogramming/macros.md b/docs/docs/reference/metaprogramming/macros.md index 984a0550c794..13f3cf953ee0 100644 --- a/docs/docs/reference/metaprogramming/macros.md +++ b/docs/docs/reference/metaprogramming/macros.md @@ -707,7 +707,7 @@ This might be used to then perform an implicit search as in: ```scala -inline def (sc: StringContext) showMe(args: =>Any*): String = ${ showMeExpr('sc, 'args) } +inline def (sc: StringContext).showMe(args: =>Any*): String = ${ showMeExpr('sc, 'args) } private def showMeExpr(sc: Expr[StringContext], argsExpr: Expr[Seq[Any]])(given qctx: QuoteContext): Expr[String] = { argsExpr match { diff --git a/docs/docs/reference/other-new-features/opaques.md b/docs/docs/reference/other-new-features/opaques.md index c15d439a5351..1390fadd2559 100644 --- a/docs/docs/reference/other-new-features/opaques.md +++ b/docs/docs/reference/other-new-features/opaques.md @@ -71,8 +71,8 @@ object Access { def (x: Permissions) & (y: Permissions): Permissions = x | y def (x: PermissionChoice) | (y: PermissionChoice): PermissionChoice = x | y - def (x: Permissions) is (y: Permissions) = (x & y) == y - def (x: Permissions) isOneOf (y: PermissionChoice) = (x & y) != 0 + def (x: Permissions).is(y: Permissions) = (x & y) == y + def (x: Permissions).isOneOf(y: PermissionChoice) = (x & y) != 0 val NoPermission: Permission = 0 val ReadOnly: Permission = 1 diff --git a/docs/docs/reference/other-new-features/tupled-function.md b/docs/docs/reference/other-new-features/tupled-function.md index 7dff362febd7..f2e836803662 100644 --- a/docs/docs/reference/other-new-features/tupled-function.md +++ b/docs/docs/reference/other-new-features/tupled-function.md @@ -43,7 +43,7 @@ Examples * @tparam Args the tuple type with the same types as the function arguments of F * @tparam R the return type of F */ -def (f: F) tupled[F, Args <: Tuple, R](given tf: TupledFunction[F, Args => R]): Args => R = tf.tupled(f) +def [F, Args <: Tuple, R](f: F).tupled(given tf: TupledFunction[F, Args => R]): Args => R = tf.tupled(f) ``` `TupledFunction` can be used to generalize the `Function.untupled` methods to functions of any arities ([full example](https://github.com/lampepfl/dotty/blob/master/tests/run/tupled-function-untupled.scala)) @@ -58,7 +58,7 @@ def (f: F) tupled[F, Args <: Tuple, R](given 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 => R) untupled[F, Args <: Tuple, R](given tf: TupledFunction[F, Args => R]): F = tf.untupled(f) +def [F, Args <: Tuple, R](f: Args => R).untupled(given 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. @@ -72,7 +72,7 @@ def (f: Args => R) untupled[F, Args <: Tuple, R](given 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: F) compose[F, G, FArgs <: Tuple, GArgs <: Tuple, R](g: G)(given tg: TupledFunction[G, GArgs => FArgs], tf: TupledFunction[F, FArgs => R]): GArgs => R = { +def [F, G, FArgs <: Tuple, GArgs <: Tuple, R](f: F).compose(g: G)(given tg: TupledFunction[G, GArgs => FArgs], tf: TupledFunction[F, FArgs => R]): GArgs => R = { (x: GArgs) => tf.tupled(f)(tg.tupled(g)(x)) } ``` diff --git a/library/src/scala/IArray.scala b/library/src/scala/IArray.scala index 7a2d2f68c39b..d387229f112b 100644 --- a/library/src/scala/IArray.scala +++ b/library/src/scala/IArray.scala @@ -16,27 +16,27 @@ object opaques * @param n the index of the element to select * @return the element of the array at the given index */ - def (arr: IArray[Byte]) apply (n: Int): Byte = arr.asInstanceOf[Array[Byte]].apply(n) - def (arr: IArray[Short]) apply (n: Int): Short = arr.asInstanceOf[Array[Short]].apply(n) - def (arr: IArray[Char]) apply (n: Int): Char = arr.asInstanceOf[Array[Char]].apply(n) - def (arr: IArray[Int]) apply (n: Int): Int = arr.asInstanceOf[Array[Int]].apply(n) - def (arr: IArray[Long]) apply (n: Int): Long = arr.asInstanceOf[Array[Long]].apply(n) - def (arr: IArray[Float]) apply (n: Int): Float = arr.asInstanceOf[Array[Float]].apply(n) - def (arr: IArray[Double]) apply (n: Int): Double = arr.asInstanceOf[Array[Double]].apply(n) + def (arr: IArray[Byte]).apply(n: Int): Byte = arr.asInstanceOf[Array[Byte]].apply(n) + def (arr: IArray[Short]).apply(n: Int): Short = arr.asInstanceOf[Array[Short]].apply(n) + def (arr: IArray[Char]).apply(n: Int): Char = arr.asInstanceOf[Array[Char]].apply(n) + def (arr: IArray[Int]).apply(n: Int): Int = arr.asInstanceOf[Array[Int]].apply(n) + def (arr: IArray[Long]).apply(n: Int): Long = arr.asInstanceOf[Array[Long]].apply(n) + def (arr: IArray[Float]).apply(n: Int): Float = arr.asInstanceOf[Array[Float]].apply(n) + def (arr: IArray[Double]).apply(n: Int): Double = arr.asInstanceOf[Array[Double]].apply(n) def [T <: Object](arr: IArray[T]) apply (n: Int): T = arr.asInstanceOf[Array[T]].apply(n) def [T](arr: IArray[T]) apply (n: Int): T = arr.asInstanceOf[Array[T]].apply(n) /** The number of elements in an immutable array * @param arr the immutable array */ - def (arr: IArray[Byte]) length: Int = arr.asInstanceOf[Array[Byte]].length - def (arr: IArray[Short]) length: Int = arr.asInstanceOf[Array[Short]].length - def (arr: IArray[Char]) length: Int = arr.asInstanceOf[Array[Char]].length - def (arr: IArray[Int]) length: Int = arr.asInstanceOf[Array[Int]].length - def (arr: IArray[Long]) length: Int = arr.asInstanceOf[Array[Long]].length - def (arr: IArray[Float]) length: Int = arr.asInstanceOf[Array[Float]].length - def (arr: IArray[Double]) length: Int = arr.asInstanceOf[Array[Double]].length - def (arr: IArray[Object]) length: Int = arr.asInstanceOf[Array[Object]].length + def (arr: IArray[Byte]).length: Int = arr.asInstanceOf[Array[Byte]].length + def (arr: IArray[Short]).length: Int = arr.asInstanceOf[Array[Short]].length + def (arr: IArray[Char]).length: Int = arr.asInstanceOf[Array[Char]].length + def (arr: IArray[Int]).length: Int = arr.asInstanceOf[Array[Int]].length + def (arr: IArray[Long]).length: Int = arr.asInstanceOf[Array[Long]].length + def (arr: IArray[Float]).length: Int = arr.asInstanceOf[Array[Float]].length + def (arr: IArray[Double]).length: Int = arr.asInstanceOf[Array[Double]].length + def (arr: IArray[Object]).length: Int = arr.asInstanceOf[Array[Object]].length def [T](arr: IArray[T]) length: Int = arr.asInstanceOf[Array[T]].length /** Returns this array concatenated with the given array. */ diff --git a/tests/neg-custom-args/extmethods-tparams.scala b/tests/neg-custom-args/extmethods-tparams.scala index e6bd6ebe7485..59a9780a7abf 100644 --- a/tests/neg-custom-args/extmethods-tparams.scala +++ b/tests/neg-custom-args/extmethods-tparams.scala @@ -1,2 +1,2 @@ -def (self: T) foo[T] = ??? // error +def (self: T).foo[T] = ??? // error def [T1](self: T1) bar[T2] = ??? // error // error \ No newline at end of file diff --git a/tests/neg-macros/i6432/Macro_1.scala b/tests/neg-macros/i6432/Macro_1.scala index 501926e3537c..2ca399dc7a1b 100644 --- a/tests/neg-macros/i6432/Macro_1.scala +++ b/tests/neg-macros/i6432/Macro_1.scala @@ -4,7 +4,7 @@ import scala.quoted.autolift.given import scala.quoted.matching._ object Macro { - inline def (sc: => StringContext) foo (args: String*): Unit = ${ impl('sc) } + inline def (sc: => StringContext).foo(args: String*): Unit = ${ impl('sc) } def impl(sc: Expr[StringContext])(given qctx: QuoteContext): Expr[Unit] = { import qctx.tasty.{_, given} diff --git a/tests/neg-macros/i6432b/Macro_1.scala b/tests/neg-macros/i6432b/Macro_1.scala index 501926e3537c..2ca399dc7a1b 100644 --- a/tests/neg-macros/i6432b/Macro_1.scala +++ b/tests/neg-macros/i6432b/Macro_1.scala @@ -4,7 +4,7 @@ import scala.quoted.autolift.given import scala.quoted.matching._ object Macro { - inline def (sc: => StringContext) foo (args: String*): Unit = ${ impl('sc) } + inline def (sc: => StringContext).foo(args: String*): Unit = ${ impl('sc) } def impl(sc: Expr[StringContext])(given qctx: QuoteContext): Expr[Unit] = { import qctx.tasty.{_, given} diff --git a/tests/neg-macros/reflect-inline/assert_1.scala b/tests/neg-macros/reflect-inline/assert_1.scala index eec9852b53d9..cdae0f37b43b 100644 --- a/tests/neg-macros/reflect-inline/assert_1.scala +++ b/tests/neg-macros/reflect-inline/assert_1.scala @@ -1,7 +1,7 @@ import scala.quoted._ object api { - inline def (inline x: String) stripMargin2: String = + inline def (inline x: String).stripMargin2: String = ${ stripImpl(x) } private def stripImpl(x: String)(given qctx: QuoteContext): Expr[String] = diff --git a/tests/neg-macros/tasty-string-interpolator-position-a/Macro_1.scala b/tests/neg-macros/tasty-string-interpolator-position-a/Macro_1.scala index bf05adc5122d..d0865fd34833 100644 --- a/tests/neg-macros/tasty-string-interpolator-position-a/Macro_1.scala +++ b/tests/neg-macros/tasty-string-interpolator-position-a/Macro_1.scala @@ -3,7 +3,7 @@ import scala.language.implicitConversions object Macro { - implicit inline def (strCtx: => StringContext) f2 (args: =>Any*): String = ${FIntepolator.apply('strCtx, 'args)} + implicit inline def (strCtx: => StringContext).f2(args: =>Any*): String = ${FIntepolator.apply('strCtx, 'args)} } diff --git a/tests/neg-macros/tasty-string-interpolator-position-b/Macro_1.scala b/tests/neg-macros/tasty-string-interpolator-position-b/Macro_1.scala index a4de659aee3d..4cb7ad8570ac 100644 --- a/tests/neg-macros/tasty-string-interpolator-position-b/Macro_1.scala +++ b/tests/neg-macros/tasty-string-interpolator-position-b/Macro_1.scala @@ -3,7 +3,7 @@ import scala.language.implicitConversions object Macro { - implicit inline def (strCtx: => StringContext) f3 (args: =>Any*): String = ${FIntepolator.apply('strCtx, 'args)} + implicit inline def (strCtx: => StringContext).f3(args: =>Any*): String = ${FIntepolator.apply('strCtx, 'args)} } diff --git a/tests/neg/extension-methods.scala b/tests/neg/extension-methods.scala index 4eefd041f624..356a52c3ff3c 100644 --- a/tests/neg/extension-methods.scala +++ b/tests/neg/extension-methods.scala @@ -1,7 +1,7 @@ object Test { implicit object O { - def (x: String) l1 = x.length + def (x: String).l1 = x.length def l1(x: Int) = x * x def l2(x: String) = x.length } @@ -11,7 +11,7 @@ object Test { 1.l1 // error given [T](xs: List[T]) extended with { - def (x: Int) f1: T = ??? // error: No extension method allowed here, since collective parameters are given + def (x: Int).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 } diff --git a/tests/neg/extmethod-override.scala b/tests/neg/extmethod-override.scala index 3395460ecfab..9b2e809f9baa 100644 --- a/tests/neg/extmethod-override.scala +++ b/tests/neg/extmethod-override.scala @@ -1,8 +1,8 @@ class A { def f(x: Int)(y: Int): Int = 0 - def (x: Int) g (y: Int): Int = 1 + def (x: Int).g(y: Int): Int = 1 } class B extends A { - override def (x: Int) f (y: Int): Int = 1 // error + override def (x: Int).f(y: Int): Int = 1 // error override def g(x: Int)(y: Int): Int = 0 // error } \ No newline at end of file diff --git a/tests/neg/i5773.scala b/tests/neg/i5773.scala index 6e98d5e0c93e..4ae1fa220599 100644 --- a/tests/neg/i5773.scala +++ b/tests/neg/i5773.scala @@ -1,16 +1,16 @@ trait Semigroup[T] { - def (lhs: T) append (rhs: T): T - def (lhs: Int) appendS (rhs: T): T = ??? + def (lhs: T).append(rhs: T): T + def (lhs: Int).appendS(rhs: T): T = ??? } object Semigroup { implicit object stringAppend extends Semigroup[String] { - override def (lhs: String) append (rhs: String): String = lhs + rhs + override def (lhs: String).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) + override def (lhs: N).append(rhs: N): N = N.plus(lhs, rhs) + def (lhs: Int).appendS(rhs: N): N = ??? // N.plus(lhs, rhs) } } diff --git a/tests/neg/i6762b.scala b/tests/neg/i6762b.scala index 9a12fffd6dbe..2d7bf0aacef3 100644 --- a/tests/neg/i6762b.scala +++ b/tests/neg/i6762b.scala @@ -9,5 +9,5 @@ type Liftable given Liftable = ??? implicit object ExprOps { - def (x: T) toExpr[T](given Liftable): Expr[T] = ??? + def (x: T).toExpr[T](given Liftable): Expr[T] = ??? } diff --git a/tests/neg/i6779.scala b/tests/neg/i6779.scala index 5a24e0763317..54998ba378fe 100644 --- a/tests/neg/i6779.scala +++ b/tests/neg/i6779.scala @@ -3,7 +3,7 @@ type G[T] type Stuff given Stuff = ??? -def (x: T) f[T](given Stuff): F[T] = ??? +def (x: T).f[T](given Stuff): F[T] = ??? def g1[T](x: T): F[G[T]] = x.f(given summon[Stuff]) // error diff --git a/tests/neg/i7438.scala b/tests/neg/i7438.scala index c9e74ae31f5a..a263a5fe2f24 100644 --- a/tests/neg/i7438.scala +++ b/tests/neg/i7438.scala @@ -1,7 +1,7 @@ type Tr[+A] -inline def (tr: Tr[A]) map[A, B](f: A => B): Tr[B] = ??? +inline def (tr: Tr[A]).map[A, B](f: A => B): Tr[B] = ??? -def (d: Double) func: None.type => Some[Double] = ??? +def (d: Double).func: None.type => Some[Double] = ??? def run[A](query: None.type => Some[A]): Some[A] = ??? diff --git a/tests/neg/indent.scala b/tests/neg/indent.scala index b38ff81221f1..2bac87c9efb7 100644 --- a/tests/neg/indent.scala +++ b/tests/neg/indent.scala @@ -1,6 +1,6 @@ object Test { - def (x: Int) gt (y: Int) = x > y + def (x: Int).gt(y: Int) = x > y val y3 = if (1) max 10 gt 0 // error: end of statement expected but integer literal found // error // error // error 1 diff --git a/tests/neg/opaque-bounds.scala b/tests/neg/opaque-bounds.scala index 4755f58bd70b..acaebccbb6fb 100644 --- a/tests/neg/opaque-bounds.scala +++ b/tests/neg/opaque-bounds.scala @@ -21,8 +21,8 @@ object Access { def (x: Permissions) & (y: Permissions): Permissions = x & y def (x: PermissionChoice) | (y: PermissionChoice): PermissionChoice = x | y - def (x: Permissions) is (y: Permissions) = (x & y) == y - def (x: Permissions) isOneOf (y: PermissionChoice) = (x & y) != 0 + def (x: Permissions).is(y: Permissions) = (x & y) == y + def (x: Permissions).isOneOf(y: PermissionChoice) = (x & y) != 0 val NoPermission: Permission = 0 val ReadOnly: Permission = 1 diff --git a/tests/pos/combine.scala b/tests/pos/combine.scala index df69ac6bb3e9..7728b443c245 100644 --- a/tests/pos/combine.scala +++ b/tests/pos/combine.scala @@ -1,5 +1,5 @@ trait Semigroup[A] { - def (x: A) combine (y: A): A + def (x: A).combine(y: A): A } given Semigroup[Int] = ??? given [A, B]: Semigroup[A], Semigroup[B] => Semigroup[(A, B)] = ??? diff --git a/tests/pos/consume.scala b/tests/pos/consume.scala index e8d5e15be4a7..3f8b1fe36878 100644 --- a/tests/pos/consume.scala +++ b/tests/pos/consume.scala @@ -24,7 +24,7 @@ object math3 with trait Numeric[T] extends Ord[T] with def (x: T) + (y: T): T = ??? def (x: T) - (y: T): T = ??? - def (x: Int) numeric: T = ??? + def (x: Int).numeric: T = ??? end math3 object Test3 with diff --git a/tests/pos/i5773.scala b/tests/pos/i5773.scala index e799691a249d..76f1e491a63e 100644 --- a/tests/pos/i5773.scala +++ b/tests/pos/i5773.scala @@ -1,18 +1,18 @@ trait Semigroup[T] { - def (lhs: T) append (rhs: T): T + def (lhs: T).append(rhs: T): T } object Semigroup { implicit object stringAppend extends Semigroup[String] { - override def (lhs: String) append (rhs: String): String = lhs + rhs + override def (lhs: String).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) + override def (lhs: N).append(rhs: N): N = ??? // N.plus(lhs, rhs) } implicit class SumSemiGroupDeco[N](implicit N: Numeric[N]) extends Semigroup[N] { - override def (lhs: N) append (rhs: N): N = ??? // N.plus(lhs, rhs) + override def (lhs: N).append(rhs: N): N = ??? // N.plus(lhs, rhs) } } @@ -26,7 +26,7 @@ object Main { def f2 = { implicit val intSumAppend: Semigroup[Int] = sumSemigroup[Int] - println(3 append 4) + println(3 append 4) } def f3 = { diff --git a/tests/pos/i5773a.scala b/tests/pos/i5773a.scala index 2071402609c3..801fa10f3f3f 100644 --- a/tests/pos/i5773a.scala +++ b/tests/pos/i5773a.scala @@ -1,12 +1,12 @@ trait Semigroup[T] { - def (x: T) combine (y: T): T + def (x: T).combine(y: T): T } object Test { implicit val IntSemigroup: Semigroup[Int] = new { - def (x: Int) combine (y: Int): Int = x + y + def (x: Int).combine(y: Int): Int = x + y } implicit def OptionSemigroup[T: Semigroup]: Semigroup[Option[T]] = new { - def (x: Option[T]) combine (y: Option[T]): Option[T] = for { + def (x: Option[T]).combine(y: Option[T]): Option[T] = for { x0 <- x y0 <- y } yield x0.combine(y0) diff --git a/tests/pos/i6395.scala b/tests/pos/i6395.scala index 26faf3f52109..1e75d9ea42e7 100644 --- a/tests/pos/i6395.scala +++ b/tests/pos/i6395.scala @@ -1,5 +1,5 @@ object Foo { - inline def (self: Int) foo (that: Int): Int = 5 - def (self: Int) bar: Int = self + inline def (self: Int).foo(that: Int): Int = 5 + def (self: Int).bar: Int = self 1.foo(2).bar } \ No newline at end of file diff --git a/tests/pos/i7070.scala b/tests/pos/i7070.scala index 07b2fa7be9c4..0f65b8a3f418 100644 --- a/tests/pos/i7070.scala +++ b/tests/pos/i7070.scala @@ -2,7 +2,7 @@ object Test { class C - def (str: String) foo: (given C) => Int = ??? + def (str: String).foo: (given C) => Int = ??? given C = ??? diff --git a/tests/pos/i7119.scala b/tests/pos/i7119.scala index 1911068bc52f..0124dafb155d 100644 --- a/tests/pos/i7119.scala +++ b/tests/pos/i7119.scala @@ -1,6 +1,6 @@ class Impl -def (impl: Impl) prop(given Int) = ???//summon[Int] +def (impl: Impl).prop(given Int) = ???//summon[Int] def main(args: Array[String]): Unit = { diff --git a/tests/pos/i7413.scala b/tests/pos/i7413.scala index 58c95529a8c2..cf0b9bec5eee 100644 --- a/tests/pos/i7413.scala +++ b/tests/pos/i7413.scala @@ -3,7 +3,7 @@ import scala.language.implicitConversions trait Fixture[A] extends Conversion[0, A] trait TestFramework[A] { - def (testName: String) in (test: (given Fixture[A]) => Unit): Unit = ??? + def (testName: String).in(test: (given Fixture[A]) => Unit): Unit = ??? } trait Greeter { diff --git a/tests/pos/i7700.scala b/tests/pos/i7700.scala index bdca41ff87ce..2c7a1f3a1ff9 100644 --- a/tests/pos/i7700.scala +++ b/tests/pos/i7700.scala @@ -4,7 +4,7 @@ trait Show[-A] with def show(a: A): String object Macros with - inline def (sc: StringContext) show(args: =>Any*): String = ??? + inline def (sc: StringContext).show(args: =>Any*): String = ??? object Show with def[A] (a: A) show(given S: Show[A]): String = S.show(a) diff --git a/tests/pos/indent.scala b/tests/pos/indent.scala index c15a85cb4e82..1a562dab29e1 100644 --- a/tests/pos/indent.scala +++ b/tests/pos/indent.scala @@ -28,7 +28,7 @@ object Test 1 else 2 - def (x: Int) gt (y: Int) = x > y + def (x: Int).gt(y: Int) = x > y val y3 = if (1) max 10 gt 0 diff --git a/tests/pos/opaque-propability-xm.scala b/tests/pos/opaque-propability-xm.scala index 2025994eacbb..28b431e74a43 100644 --- a/tests/pos/opaque-propability-xm.scala +++ b/tests/pos/opaque-propability-xm.scala @@ -19,17 +19,17 @@ object prob { implicit val ordering: Ordering[Probability] = implicitly[Ordering[Double]] - def (p1: Probability) unary_~ : Probability = Certain - p1 + def (p1: Probability).unary_~ : Probability = Certain - p1 def (p1: Probability) & (p2: Probability): Probability = p1 * p2 def (p1: Probability) | (p2: Probability): Probability = p1 + p2 - (p1 * p2) - def (p1: Probability) isImpossible: Boolean = p1 == Never - def (p1: Probability) isCertain: Boolean = p1 == Certain + def (p1: Probability).isImpossible: Boolean = p1 == Never + def (p1: Probability).isCertain: Boolean = p1 == Certain import scala.util.Random - def (p1: Probability) sample (r: Random = Random): Boolean = r.nextDouble <= p1 - def (p1: Probability) toDouble: Double = p1 + def (p1: Probability).sample(r: Random = Random): Boolean = r.nextDouble <= p1 + def (p1: Probability).toDouble: Double = p1 } val caughtTrain = Probability.unsafe(0.3) diff --git a/tests/pos/opaque-xm.scala b/tests/pos/opaque-xm.scala index 2a936787219d..a8619c287f58 100644 --- a/tests/pos/opaque-xm.scala +++ b/tests/pos/opaque-xm.scala @@ -16,7 +16,7 @@ object opaquetypes { // Extension methods define opaque types' public APIs // This is the second way to unlift the logarithm type - def (x: Logarithm) toDouble: Double = math.exp(x) + def (x: Logarithm).toDouble: Double = math.exp(x) def (x: Logarithm) + (y: Logarithm) = Logarithm(math.exp(x) + math.exp(y)) def (x: Logarithm) * (y: Logarithm): Logarithm = Logarithm(x + y) } diff --git a/tests/pos/reference/delegates.scala b/tests/pos/reference/delegates.scala index 71303fc14513..4252868bf9aa 100644 --- a/tests/pos/reference/delegates.scala +++ b/tests/pos/reference/delegates.scala @@ -1,15 +1,15 @@ class Common with trait Ord[T] with - def (x: T) compareTo (y: T): Int + def (x: T).compareTo(y: T): Int def (x: T) < (y: T) = x.compareTo(y) < 0 def (x: T) > (y: T) = x.compareTo(y) > 0 trait Convertible[From, To] with - def (x: From) convert: To + def (x: From).convert: To trait SemiGroup[T] with - def (x: T) combine (y: T): T + def (x: T).combine(y: T): T trait Monoid[T] extends SemiGroup[T] with def unit: T @@ -26,11 +26,11 @@ class Common with object Instances extends Common with given intOrd: Ord[Int] with - def (x: Int) compareTo (y: Int) = + def (x: Int).compareTo(y: Int) = if (x < y) -1 else if (x > y) +1 else 0 given listOrd[T]: Ord[T] => Ord[List[T]] with - def (xs: List[T]) compareTo (ys: List[T]): Int = (xs, ys) match + def (xs: List[T]).compareTo(ys: List[T]): Int = (xs, ys).match case (Nil, Nil) => 0 case (Nil, _) => -1 case (_, Nil) => +1 @@ -64,7 +64,7 @@ object Instances extends Common with xs.reduceLeft((x, y) => if (x < y) y else x) def descending[T](given asc: Ord[T]): Ord[T] = new Ord[T] with - def (x: T) compareTo (y: T) = asc.compareTo(y)(x) + def (x: T).compareTo(y: T) = asc.compareTo(y)(x) def minimum[T](xs: List[T])(given Ord[T]) = maximum(xs)(given descending) @@ -87,14 +87,14 @@ object Instances extends Common with trait TastyAPI with type Symbol trait SymDeco with - def (sym: Symbol) name: String + def (sym: Symbol).name: String def symDeco: SymDeco given SymDeco = symDeco object TastyImpl extends TastyAPI with type Symbol = String val symDeco = new SymDeco with - def (sym: Symbol) name = sym + def (sym: Symbol).name = sym class D[T] @@ -141,11 +141,11 @@ end PostConditions object AnonymousInstances extends Common with given Ord[Int] with - def (x: Int) compareTo (y: Int) = + def (x: Int).compareTo(y: Int) = if (x < y) -1 else if (x > y) +1 else 0 given [T: Ord] : Ord[List[T]] with - def (xs: List[T]) compareTo (ys: List[T]): Int = (xs, ys) match + def (xs: List[T]).compareTo(ys: List[T]): Int = (xs, ys).match case (Nil, Nil) => 0 case (Nil, _) => -1 case (_, Nil) => +1 @@ -162,10 +162,10 @@ object AnonymousInstances extends Common with def second = xs.tail.head given [From, To]: (c: Convertible[From, To]) => Convertible[List[From], List[To]] with - def (x: List[From]) convert: List[To] = x.map(c.convert) + def (x: List[From]).convert: List[To] = x.map(c.convert) given Monoid[String] with - def (x: String) combine (y: String): String = x.concat(y) + def (x: String).combine(y: String): String = x.concat(y) def unit: String = "" def sum[T: Monoid](xs: List[T]): T = @@ -174,11 +174,11 @@ end AnonymousInstances object Implicits extends Common with implicit object IntOrd extends Ord[Int] with - def (x: Int) compareTo (y: Int) = + def (x: Int).compareTo(y: Int) = if (x < y) -1 else if (x > y) +1 else 0 class ListOrd[T: Ord] extends Ord[List[T]] with - def (xs: List[T]) compareTo (ys: List[T]): Int = (xs, ys) match + def (xs: List[T]).compareTo(ys: List[T]): Int = (xs, ys).match case (Nil, Nil) => 0 case (Nil, _) => -1 case (_, Nil) => +1 @@ -189,7 +189,7 @@ object Implicits extends Common with class given_Convertible_List_List[From, To](implicit c: Convertible[From, To]) extends Convertible[List[From], List[To]] with - def (x: List[From]) convert: List[To] = x.map(c.convert) + def (x: List[From]).convert: List[To] = x.map(c.convert) implicit def given_Convertible_List_List[From, To](implicit c: Convertible[From, To]) : Convertible[List[From], List[To]] = new given_Convertible_List_List[From, To] @@ -199,7 +199,7 @@ object Implicits extends Common with xs.reduceLeft((x, y) => if (x < y) y else x) def descending[T](implicit asc: Ord[T]): Ord[T] = new Ord[T] with - def (x: T) compareTo (y: T) = asc.compareTo(y)(x) + def (x: T).compareTo(y: T) = asc.compareTo(y)(x) def minimum[T](xs: List[T])(implicit cmp: Ord[T]) = maximum(xs)(descending) diff --git a/tests/pos/reference/extension-methods.scala b/tests/pos/reference/extension-methods.scala index d8e3d566a615..614efe611314 100644 --- a/tests/pos/reference/extension-methods.scala +++ b/tests/pos/reference/extension-methods.scala @@ -2,14 +2,14 @@ object ExtMethods with case class Circle(x: Double, y: Double, radius: Double) - def (c: Circle) circumference: Double = c.radius * math.Pi * 2 + def (c: Circle).circumference: Double = c.radius * math.Pi * 2 val circle = Circle(0, 0, 1) circle.circumference assert(circle.circumference == circumference(circle)) trait StringSeqOps { - def (xs: Seq[String]) longestStrings = { + def (xs: Seq[String]).longestStrings = { val maxLength = xs.map(_.length).max xs.filter(_.length == maxLength) } @@ -57,7 +57,7 @@ object ExtMethods with def largest(n: Int) = xs.sorted.takeRight(n) given stringOps1: AnyRef { - def (xs: Seq[String]) longestStrings: Seq[String] = { + def (xs: Seq[String]).longestStrings: Seq[String] = { val maxLength = xs.map(_.length).max xs.filter(_.length == maxLength) } diff --git a/tests/pos/reference/opaque.scala b/tests/pos/reference/opaque.scala index 617fbbe17b2d..5b0b24aad768 100644 --- a/tests/pos/reference/opaque.scala +++ b/tests/pos/reference/opaque.scala @@ -38,8 +38,8 @@ object Access { def (x: Permissions) & (y: Permissions): Permissions = x & y def (x: PermissionChoice) | (y: PermissionChoice): PermissionChoice = x | y - def (x: Permissions) is (y: Permissions) = (x & y) == y - def (x: Permissions) isOneOf (y: PermissionChoice) = (x & y) != 0 + def (x: Permissions).is(y: Permissions) = (x & y) == y + def (x: Permissions).isOneOf(y: PermissionChoice) = (x & y) != 0 val NoPermission: Permission = 0 val ReadOnly: Permission = 1 diff --git a/tests/pos/toplevel-opaque-xm/Logarithm_1.scala b/tests/pos/toplevel-opaque-xm/Logarithm_1.scala index 88f280a7f4a1..c9e4e439713b 100644 --- a/tests/pos/toplevel-opaque-xm/Logarithm_1.scala +++ b/tests/pos/toplevel-opaque-xm/Logarithm_1.scala @@ -16,7 +16,7 @@ implicit object Logarithm { // Extension methods define opaque types' public APIs // This is the second way to unlift the logarithm type - def (x: Logarithm) toDouble: Double = math.exp(x) + def (x: Logarithm).toDouble: Double = math.exp(x) def (x: Logarithm) + (y: Logarithm) = Logarithm(math.exp(x) + math.exp(y)) def (x: Logarithm) * (y: Logarithm): Logarithm = Logarithm(x + y) } diff --git a/tests/run-macros/f-interpolator-neg/Macros_1.scala b/tests/run-macros/f-interpolator-neg/Macros_1.scala index 8a852f3a3e4c..d21875d53c98 100644 --- a/tests/run-macros/f-interpolator-neg/Macros_1.scala +++ b/tests/run-macros/f-interpolator-neg/Macros_1.scala @@ -6,7 +6,7 @@ import scala.language.implicitConversions object TestFooErrors { // Defined in tests implicit object StringContextOps { - inline def (ctx: => StringContext) foo (args: => Any*): List[(Boolean, Int, Int, Int, String)] = ${ Macro.fooErrors('ctx, 'args) } + inline def (ctx: => StringContext).foo(args: => Any*): List[(Boolean, Int, Int, Int, String)] = ${ Macro.fooErrors('ctx, 'args) } } } diff --git a/tests/run/Pouring.scala b/tests/run/Pouring.scala index b61d54bf1a3f..add83608862a 100644 --- a/tests/run/Pouring.scala +++ b/tests/run/Pouring.scala @@ -8,7 +8,7 @@ class Pouring(capacity: Vector[Int]) with case Fill(g) => content.updated(g, capacity(g)) case Pour(from, to) => val amount = content(from) min (capacity(to) - content(to)) - def (s: Content) adjust (g: Glass, delta: Int) = s.updated(g, s(g) + delta) + def (s: Content).adjust(g: Glass, delta: Int) = s.updated(g, s(g) + delta) content.adjust(from, -amount).adjust(to, amount) case Empty(glass: Glass) diff --git a/tests/run/eq-xmethod.scala b/tests/run/eq-xmethod.scala index 6e9bfa5dd855..5231063bf62b 100644 --- a/tests/run/eq-xmethod.scala +++ b/tests/run/eq-xmethod.scala @@ -6,7 +6,7 @@ object Test extends App { class N object N extends N - def (x: N) _eq (y: R | N) = y eq N + def (x: N)._eq(y: R | N) = y eq N val r1, r2 = new R assert(r1 _eq r1) diff --git a/tests/run/exports.scala b/tests/run/exports.scala index fe978ea2f375..54ca2235fee7 100644 --- a/tests/run/exports.scala +++ b/tests/run/exports.scala @@ -12,7 +12,7 @@ object Test extends App { class Scanner { def scan() = println("scanning") - def (x: Any) scanned = scan() + def (x: Any).scanned = scan() } object Scanner extends Scanner diff --git a/tests/run/extension-methods.scala b/tests/run/extension-methods.scala index 538b7bd7dffa..82552aaf9e82 100644 --- a/tests/run/extension-methods.scala +++ b/tests/run/extension-methods.scala @@ -1,18 +1,18 @@ object Test extends App { - def (x: Int) em: Boolean = x > 0 + def (x: Int).em: Boolean = x > 0 assert(1.em == em(1)) case class Circle(x: Double, y: Double, radius: Double) - def (c: Circle) circumference: Double = c.radius * math.Pi * 2 + def (c: Circle).circumference: Double = c.radius * math.Pi * 2 val circle = new Circle(1, 1, 2.0) assert(circle.circumference == circumference(circle)) - def (xs: Seq[String]) longestStrings: Seq[String] = { + def (xs: Seq[String]).longestStrings: Seq[String] = { val maxLength = xs.map(_.length).max xs.filter(_.length == maxLength) } @@ -29,7 +29,7 @@ object Test extends App { assert(Nil.flattened == Nil) trait SemiGroup[T] { - def (x: T) combine (y: T): T + def (x: T).combine(y: T): T } trait Monoid[T] extends SemiGroup[T] { def unit: T @@ -37,7 +37,7 @@ object Test extends App { // An instance declaration: implicit object StringMonoid extends Monoid[String] { - def (x: String) combine (y: String): String = x.concat(y) + def (x: String).combine(y: String): String = x.concat(y) def unit: String = "" } @@ -48,14 +48,14 @@ object Test extends App { println(sum(names)) trait Ord[T] { - def (x: T) compareTo (y: T): Int + def (x: T).compareTo(y: T): Int def (x: T) < (y: T) = x.compareTo(y) < 0 def (x: T) > (y: T) = x.compareTo(y) > 0 val minimum: T } implicit object IntOrd extends Ord[Int] { - def (x: Int) compareTo (y: Int) = + def (x: Int).compareTo(y: Int) = if (x < y) -1 else if (x > y) +1 else 0 val minimum = Int.MinValue } diff --git a/tests/run/extmethod-overload.scala b/tests/run/extmethod-overload.scala index fe80ccebf54a..c5454944bd63 100644 --- a/tests/run/extmethod-overload.scala +++ b/tests/run/extmethod-overload.scala @@ -88,13 +88,13 @@ object Test extends App { class C { def xx (x: Any) = 2 } - def (c: C) xx (x: Int) = 1 + def (c: C).xx(x: Int) = 1 val c = new C assert(c.xx(1) == 2) // member method takes precedence object D { - def (x: Int) yy (y: Int) = x + y + def (x: Int).yy(y: Int) = x + y } given (x: Int) extended with { @@ -114,8 +114,8 @@ object Test extends App { def b: Long = a } - def (rectangle: Rectangle) area: Long = 0 - def (square: Square) area: Long = square.a * square.a + def (rectangle: Rectangle).area: Long = 0 + def (square: Square).area: Long = square.a * square.a val rectangles = List(GenericRectangle(2, 3), Square(5)) val areas = rectangles.map(_.area) assert(areas.sum == 0) diff --git a/tests/run/extmethods2.scala b/tests/run/extmethods2.scala index 9ed54f3a39b7..90441a4aa97d 100644 --- a/tests/run/extmethods2.scala +++ b/tests/run/extmethods2.scala @@ -4,8 +4,8 @@ object Test extends App { given stringListOps: TC => Object { type T = List[String] - def (x: T) foo (y: T) = (x ++ y, summon[TC]) - def (x: T) bar (y: Int) = (x(0)(y), summon[TC]) + def (x: T).foo(y: T) = (x ++ y, summon[TC]) + def (x: T).bar(y: Int) = (x(0)(y), summon[TC]) } def test(given TC) = { diff --git a/tests/run/instances-anonymous.scala b/tests/run/instances-anonymous.scala index 4d4774e98bf1..8746c623a0e0 100644 --- a/tests/run/instances-anonymous.scala +++ b/tests/run/instances-anonymous.scala @@ -1,7 +1,7 @@ object Test extends App { implicit object O { - def (x: Int) em: Boolean = x > 0 + def (x: Int).em: Boolean = x > 0 } assert(1.em == O.em(1)) @@ -17,7 +17,7 @@ object Test extends App { println(circle.circumference) given AnyRef { - def (xs: Seq[String]) longestStrings: Seq[String] = { + def (xs: Seq[String]).longestStrings: Seq[String] = { val maxLength = xs.map(_.length).max xs.filter(_.length == maxLength) } @@ -39,14 +39,14 @@ object Test extends App { assert(Nil.flattened == Nil) trait SemiGroup[T] { - def (x: T) combine (y: T): T + def (x: T).combine(y: T): T } trait Monoid[T] extends SemiGroup[T] { def unit: T } given Monoid[String] { - def (x: String) combine (y: String): String = x.concat(y) + def (x: String).combine(y: String): String = x.concat(y) def unit: String = "" } @@ -57,20 +57,20 @@ object Test extends App { println(sum(names)) trait Ord[T] { - def (x: T) compareTo (y: T): Int + def (x: T).compareTo(y: T): Int def (x: T) < (y: T) = x.compareTo(y) < 0 def (x: T) > (y: T) = x.compareTo(y) > 0 val minimum: T } given Ord[Int] { - def (x: Int) compareTo (y: Int) = + def (x: Int).compareTo(y: Int) = if (x < y) -1 else if (x > y) +1 else 0 val minimum = Int.MinValue } given [T: Ord] : Ord[List[T]] { - def (xs: List[T]) compareTo (ys: List[T]): Int = (xs, ys) match { + def (xs: List[T]).compareTo(ys: List[T]): Int = (xs, ys).match { case (Nil, Nil) => 0 case (Nil, _) => -1 case (_, Nil) => +1 diff --git a/tests/run/instances.scala b/tests/run/instances.scala index 0778d8a4fed5..033bc81c0f52 100644 --- a/tests/run/instances.scala +++ b/tests/run/instances.scala @@ -1,7 +1,7 @@ object Test extends App { implicit object O { - def (x: Int) em: Boolean = x > 0 + def (x: Int).em: Boolean = x > 0 } assert(1.em == O.em(1)) @@ -44,13 +44,13 @@ object Test extends App { assert(Nil.flattened == Nil) trait SemiGroup[T] with - def (x: T) combine (y: T): T + def (x: T).combine(y: T): T trait Monoid[T] extends SemiGroup[T] with def unit: T given StringMonoid : Monoid[String] with - def (x: String) combine (y: String): String = x.concat(y) + def (x: String).combine(y: String): String = x.concat(y) def unit: String = "" // Abstracting over a typeclass with a context bound: @@ -60,19 +60,19 @@ object Test extends App { println(sum(names)) trait Ord[T] with - def (x: T) compareTo (y: T): Int + def (x: T).compareTo(y: T): Int def (x: T) < (y: T) = x.compareTo(y) < 0 def (x: T) > (y: T) = x.compareTo(y) > 0 val minimum: T end Ord given Ord[Int] with - def (x: Int) compareTo (y: Int) = + def (x: Int).compareTo(y: Int) = if (x < y) -1 else if (x > y) +1 else 0 val minimum = Int.MinValue given listOrd[T: Ord]: Ord[List[T]] with - def (xs: List[T]) compareTo (ys: List[T]): Int = (xs, ys) match + def (xs: List[T]).compareTo(ys: List[T]): Int = (xs, ys).match case (Nil, Nil) => 0 case (Nil, _) => -1 case (_, Nil) => +1 diff --git a/tests/run/string-context-implicits-with-conversion.scala b/tests/run/string-context-implicits-with-conversion.scala index 4f3e3eb5d314..f72d96989c3a 100644 --- a/tests/run/string-context-implicits-with-conversion.scala +++ b/tests/run/string-context-implicits-with-conversion.scala @@ -1,6 +1,6 @@ object Lib { - def (sc: StringContext) showMe(args: Showed*): String = sc.s(args: _*) + def (sc: StringContext).showMe(args: Showed*): String = sc.s(args: _*) opaque type Showed = String diff --git a/tests/run/toplevel-implicits/a.b.scala b/tests/run/toplevel-implicits/a.b.scala index bfa0051d546a..a360f4d6e491 100644 --- a/tests/run/toplevel-implicits/a.b.scala +++ b/tests/run/toplevel-implicits/a.b.scala @@ -3,7 +3,7 @@ package implicits case class C() implicit object Cops { - def (x: C) pair (y: C) = (x, y) + def (x: C).pair(y: C) = (x, y) } class D { diff --git a/tests/semanticdb/expect/Enums.scala b/tests/semanticdb/expect/Enums.scala index 28f2a8a4372e..6ccd2a1fdf8d 100644 --- a/tests/semanticdb/expect/Enums.scala +++ b/tests/semanticdb/expect/Enums.scala @@ -12,10 +12,10 @@ object Enums with case Hearts, Spades, Clubs, Diamonds object Suits with - def (suit: Suits) isRed: Boolean = + def (suit: Suits).isRed: Boolean = suit == Hearts || suit == Diamonds - def (suit: Suits) isBlack: Boolean = suit match + def (suit: Suits).isBlack: Boolean = suit match case Spades | Diamonds => true case _ => false diff --git a/tests/semanticdb/expect/Givens.scala b/tests/semanticdb/expect/Givens.scala index 9460d5cba0fd..b5b5548a0b00 100644 --- a/tests/semanticdb/expect/Givens.scala +++ b/tests/semanticdb/expect/Givens.scala @@ -16,11 +16,11 @@ object Givens trait Monoid[A] def empty: A - def (x: A) combine (y: A): A + def (x: A).combine(y: A): A given Monoid[String] def empty = "" - def (x: String) combine (y: String) = x + y + def (x: String).combine(y: String) = x + y inline given int2String: Conversion[Int, String] = _.toString From 9e6022bcb7e16aa6e0ff0205f12742c2c944a7f9 Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Tue, 7 Jan 2020 17:29:05 +0100 Subject: [PATCH 07/11] New syntax for collective extension methods It's now: ```scala extension listOps of [T](xs: List[T]) with { def second = xs.tail.head def third: T = xs.tail.tail.head } ``` instead of ```scala given listOps: [T](xs: List[T]) extended with { ... } --- .../src/dotty/tools/dotc/core/StdNames.scala | 1 + .../dotty/tools/dotc/parsing/Parsers.scala | 49 +++++++++++++++---- .../dotty/tools/dotc/parsing/Scanners.scala | 1 + ...2019-12-20-21th-dotty-milestone-release.md | 2 +- docs/docs/internals/syntax.md | 10 ++-- .../reference/contextual/extension-methods.md | 22 +++++---- .../contextual/relationship-implicits.md | 2 +- tests/neg/extension-methods.scala | 2 +- tests/neg/extmethod-overload.scala | 4 +- tests/neg/i5455.scala | 2 +- tests/neg/i6801.scala | 2 +- tests/neg/i6900.scala | 2 +- tests/neg/i7529.scala | 2 +- tests/neg/missing-implicit1.scala | 2 +- tests/pos/i6900.scala | 2 +- tests/pos/i7084.scala | 2 +- tests/pos/i7087.scala | 2 +- tests/pos/implicit-scope.scala | 2 +- tests/pos/matrixOps.scala | 2 +- tests/pos/mirror-implicit-scope.scala | 4 +- tests/pos/reference/delegates.scala | 10 ++-- tests/pos/reference/extension-methods.scala | 6 +-- tests/pos/reference/opaque.scala | 2 +- .../pos/tasty-reflect-opaque-api-proto.scala | 2 +- tests/run/extension-specificity.scala | 4 +- tests/run/extmethod-overload.scala | 2 +- tests/run/extmethods2.scala | 6 +-- tests/run/i6902.scala | 4 +- tests/run/instances-anonymous.scala | 6 +-- tests/run/instances.scala | 8 +-- 30 files changed, 101 insertions(+), 66 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/core/StdNames.scala b/compiler/src/dotty/tools/dotc/core/StdNames.scala index f4179577ad90..03b92d13d73d 100644 --- a/compiler/src/dotty/tools/dotc/core/StdNames.scala +++ b/compiler/src/dotty/tools/dotc/core/StdNames.scala @@ -529,6 +529,7 @@ object StdNames { val notify_ : N = "notify" val null_ : N = "null" val nullExpr: N = "nullExpr" + val of: N = "of" val ofDim: N = "ofDim" val opaque: N = "opaque" val open: N = "open" diff --git a/compiler/src/dotty/tools/dotc/parsing/Parsers.scala b/compiler/src/dotty/tools/dotc/parsing/Parsers.scala index 42373809eaf1..4cf93b3a378d 100644 --- a/compiler/src/dotty/tools/dotc/parsing/Parsers.scala +++ b/compiler/src/dotty/tools/dotc/parsing/Parsers.scala @@ -188,7 +188,7 @@ object Parsers { /* -------------- TOKEN CLASSES ------------------------------------------- */ def isIdent = in.isIdent - def isIdent(name: Name) = in.token == IDENTIFIER && in.name == name + def isIdent(name: Name) = in.isIdent(name) def isSimpleLiteral = simpleLiteralTokens contains in.token def isLiteral = literalTokens contains in.token def isNumericLit = numericLitTokens contains in.token @@ -216,10 +216,11 @@ object Parsers { in.canStartExprTokens.contains(in.token) && !in.isSoftModifierInModifierPosition def isDefIntro(allowedMods: BitSet, excludedSoftModifiers: Set[TermName] = Set.empty): Boolean = - in.token == AT || - (defIntroTokens `contains` in.token) || - (allowedMods `contains` in.token) || - in.isSoftModifierInModifierPosition && !excludedSoftModifiers.contains(in.name) + in.token == AT + || defIntroTokens.contains(in.token) + || allowedMods.contains(in.token) + || in.isSoftModifierInModifierPosition && !excludedSoftModifiers.contains(in.name) + || isIdent(nme.extension) && followingIsExtension() def isStatSep: Boolean = in.isNewLine || in.token == SEMI @@ -944,6 +945,13 @@ object Parsers { lookahead.skipParens() lookahead.token == COLON || lookahead.token == SUBTYPE + def followingIsExtension() = + val lookahead = in.LookaheadScanner() + lookahead.nextToken() + if lookahead.isIdent && !lookahead.isIdent(nme.of) then + lookahead.nextToken() + lookahead.isIdent(nme.of) + /* --------- OPERAND/OPERATOR STACK --------------------------------------- */ var opStack: List[OpInfo] = Nil @@ -3275,6 +3283,7 @@ object Parsers { * | [‘case’] ‘object’ ObjectDef * | ‘enum’ EnumDef * | ‘given’ GivenDef + * | ‘extension’ ExtensionDef */ def tmplDef(start: Int, mods: Modifiers): Tree = in.token match { @@ -3293,8 +3302,11 @@ object Parsers { case GIVEN => givenDef(start, mods, atSpan(in.skipToken()) { Mod.Given() }) case _ => - syntaxErrorOrIncomplete(ExpectedStartOfTopLevelDefinition()) - EmptyTree + if isIdent(nme.extension) && followingIsExtension() then + extensionDef(start, mods) + else + syntaxErrorOrIncomplete(ExpectedStartOfTopLevelDefinition()) + EmptyTree } /** ClassDef ::= id ClassConstr TemplateOpt @@ -3522,6 +3534,23 @@ object Parsers { finalizeDef(gdef, mods1, start) } + /** ExtensionDef ::= [id] ‘of’ ExtParamClause {GivenParamClause} ‘with’ ExtMethods + */ + def extensionDef(start: Offset, mods: Modifiers): ModuleDef = + in.nextToken() + val name = if isIdent && !isIdent(nme.of) then ident() else EmptyTermName + if !isIdent(nme.of) then syntaxErrorOrIncomplete("`of` expected") + if isIdent(nme.of) then in.nextToken() + val tparams = typeParamClauseOpt(ParamOwner.Def) + val extParams = paramClause(0, prefix = true) + val givenParamss = paramClauses(givenOnly = true) + accept(WITH) + if !in.isNestedStart then syntaxError("Extension without extension methods") + val templ = templateBodyOpt(makeConstructor(tparams, extParams :: givenParamss), Nil, Nil) + templ.body.foreach(checkExtensionMethod(tparams, _)) + val edef = ModuleDef(name, templ) + finalizeDef(edef, addFlag(mods, Given), start) + /* -------- TEMPLATES ------------------------------------------- */ /** SimpleConstrApp ::= AnnotType {ParArgumentExprs} @@ -3681,7 +3710,7 @@ object Parsers { def templateStatSeq(): (ValDef, List[Tree]) = checkNoEscapingPlaceholders { var self: ValDef = EmptyValDef val stats = new ListBuffer[Tree] - if (isExprIntro) { + if (isExprIntro && !isDefIntro(modifierTokens)) { val first = expr1() if (in.token == ARROW) { first match { @@ -3707,10 +3736,10 @@ object Parsers { stats ++= importClause(IMPORT, Import) else if (in.token == EXPORT) stats ++= importClause(EXPORT, Export.apply) - else if (isExprIntro) - stats += expr1() else if (isDefIntro(modifierTokensOrCase)) stats +++= defOrDcl(in.offset, defAnnotsMods(modifierTokens)) + else if (isExprIntro) + stats += expr1() else if (!isStatSep) { exitOnError = mustStartStat syntaxErrorOrIncomplete("illegal start of definition") diff --git a/compiler/src/dotty/tools/dotc/parsing/Scanners.scala b/compiler/src/dotty/tools/dotc/parsing/Scanners.scala index 0d3024a8ccee..a3d49fa09322 100644 --- a/compiler/src/dotty/tools/dotc/parsing/Scanners.scala +++ b/compiler/src/dotty/tools/dotc/parsing/Scanners.scala @@ -1017,6 +1017,7 @@ object Scanners { def isNewLine = token == NEWLINE || token == NEWLINES def isIdent = token == IDENTIFIER || token == BACKQUOTED_IDENT + def isIdent(name: Name) = token == IDENTIFIER && this.name == name def isNestedStart = token == LBRACE || token == INDENT def isNestedEnd = token == RBRACE || token == OUTDENT diff --git a/docs/blog/_posts/2019-12-20-21th-dotty-milestone-release.md b/docs/blog/_posts/2019-12-20-21th-dotty-milestone-release.md index b409cc8c417b..e1d5cc8835a7 100644 --- a/docs/blog/_posts/2019-12-20-21th-dotty-milestone-release.md +++ b/docs/blog/_posts/2019-12-20-21th-dotty-milestone-release.md @@ -183,7 +183,7 @@ given extension (s: String) { ... } ``` or ```scala -given listOps: [T](xs: List[T]) extended with { ... } +extension listOps of [T](xs: List[T]) with { ... } given (s: String) extended with { ... } ``` diff --git a/docs/docs/internals/syntax.md b/docs/docs/internals/syntax.md index dfe6fe5957a9..08fc95e33a8e 100644 --- a/docs/docs/internals/syntax.md +++ b/docs/docs/internals/syntax.md @@ -103,7 +103,7 @@ yield ### Soft keywords ``` -as derives inline opaque open +derives extension inline opaque open ~ * | & + - ``` @@ -378,7 +378,7 @@ TmplDef ::= ([‘case’] ‘class’ | ‘trait’) ClassDef | [‘case’] ‘object’ ObjectDef | ‘enum’ EnumDef | ‘given’ GivenDef - | Export + | ‘extension’ ExtensionDef ClassDef ::= id ClassConstr [Template] ClassDef(mods, name, tparams, templ) ClassConstr ::= [ClsTypeParamClause] [ConstrMods] ClsParamClauses with DefDef(_, , Nil, vparamss, EmptyTree, EmptyTree) as first stat ConstrMods ::= {Annotation} [AccessModifier] @@ -388,11 +388,11 @@ GivenDef ::= [GivenSig (‘:’ | <:)] {FunArgTypes ‘=>’} AnnotType ‘=’ Expr | [GivenSig ‘:’] {FunArgTypes ‘=>’} ConstrApps [[‘with’] TemplateBody] - | [id ‘:’] ExtParamClause {GivenParamClause} - ‘extended’ ‘with’ ExtMethods GivenSig ::= [id] [DefTypeParamClause] {GivenParamClause} +ExtensionDef ::= [id] ‘of’ ExtParamClause {GivenParamClause} + ‘with’ ExtMethods +ExtMethods ::= ‘{’ ‘def’ DefDef {semi ‘def’ DefDef} ‘}’ ExtParamClause ::= [DefTypeParamClause] ‘(’ DefParam ‘)’ -ExtMethods ::= [nl] ‘{’ ‘def’ DefDef {semi ‘def’ DefDef} ‘}’ Template ::= InheritClauses [[‘with’] TemplateBody] Template(constr, parents, self, stats) InheritClauses ::= [‘extends’ ConstrApps] [‘derives’ QualId {‘,’ QualId}] ConstrApps ::= ConstrApp {(‘,’ | ‘with’) ConstrApp} diff --git a/docs/docs/reference/contextual/extension-methods.md b/docs/docs/reference/contextual/extension-methods.md index 78ef20c07a19..bf8140562684 100644 --- a/docs/docs/reference/contextual/extension-methods.md +++ b/docs/docs/reference/contextual/extension-methods.md @@ -80,7 +80,7 @@ So `circle.circumference` translates to `CircleOps.circumference(circle)`, provi ### Operators -The extension method syntax also applies to the definition of operators. +The extension method syntax also applies to the definition of operators. In this case it is allowed and preferable to omit the period between the leading parameter list and the operator. In each case the definition syntax mirrors the way the operator is applied. Examples: @@ -122,24 +122,25 @@ If an extension method has type parameters, they come immediately after the `def ```scala List(1, 2, 3).second[Int] ``` -### Given Instances for Extension Methods +### Collective Extensions -`given` extensions are given instances that define extension methods and nothing else. Examples: +A collective extension defines one or more concrete methods that have the same type parameters +and prefix parameter. Examples: ```scala -given stringOps: (xs: Seq[String]) extended with { +extension stringOps of (xs: Seq[String]) with { def longestStrings: Seq[String] = { val maxLength = xs.map(_.length).max xs.filter(_.length == maxLength) } } -given listOps: [T](xs: List[T]) extended with { +extension listOps of [T](xs: List[T]) with { def second = xs.tail.head def third: T = xs.tail.tail.head } -given [T](xs: List[T])(given Ordering[T]) extended with { +extension of [T](xs: List[T])(given Ordering[T]) with { def largest(n: Int) = xs.sorted.takeRight(n) } ``` @@ -163,6 +164,8 @@ given given_largest_of_List_T: AnyRef { } ``` +`extension` and `of` are soft keywords. They can also be used as a regular identifiers. + ### Syntax Here are the syntax changes for extension methods and given extensions relative @@ -170,8 +173,9 @@ to the [current syntax](../../internals/syntax.md). `extension` is a soft keywor ``` DefSig ::= ... | ExtParamClause [nl] [‘.’] id DefParamClauses -GivenDef ::= ... - [id ‘:’] ‘extension’ ExtParamClause {GivenParamClause} ExtMethods ExtParamClause ::= [DefTypeParamClause] ‘(’ DefParam ‘)’ -ExtMethods ::= [nl] ‘{’ ‘def’ DefDef {semi ‘def’ DefDef} ‘}’ +TmplDef ::= ... + | ‘extension’ ExtensionDef +ExtensionDef ::= [id] ‘of’ ExtParamClause {GivenParamClause} ‘with’ ExtMethods +ExtMethods ::= ‘{’ ‘def’ DefDef {semi ‘def’ DefDef} ‘}’ ``` diff --git a/docs/docs/reference/contextual/relationship-implicits.md b/docs/docs/reference/contextual/relationship-implicits.md index f9ac61f7ddf9..8c03b5fc1dbd 100644 --- a/docs/docs/reference/contextual/relationship-implicits.md +++ b/docs/docs/reference/contextual/relationship-implicits.md @@ -65,7 +65,7 @@ Anonymous given instances that define extension methods get their name from the name of the first extension method and the toplevel type constructor of its first parameter. For example, the given instance ```scala -given [T] (xs: List[T]) extended with { +extension of [T] (xs: List[T]) with { def second = ... } ``` diff --git a/tests/neg/extension-methods.scala b/tests/neg/extension-methods.scala index 356a52c3ff3c..ca870a223a4f 100644 --- a/tests/neg/extension-methods.scala +++ b/tests/neg/extension-methods.scala @@ -10,7 +10,7 @@ object Test { "".l2 // error 1.l1 // error - given [T](xs: List[T]) extended with { + extension of [T](xs: List[T]) with { def (x: Int).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 diff --git a/tests/neg/extmethod-overload.scala b/tests/neg/extmethod-overload.scala index ecf6afc73402..333f0c34e19d 100644 --- a/tests/neg/extmethod-overload.scala +++ b/tests/neg/extmethod-overload.scala @@ -1,8 +1,8 @@ object Test { - given a: (x: Int) extended with + extension a of (x: Int) with def |+| (y: Int) = x + y - given b: (x: Int) extended with { + extension b of (x: Int) with { def |+| (y: String) = x + y.length } assert((1 |+| 2) == 3) // error ambiguous diff --git a/tests/neg/i5455.scala b/tests/neg/i5455.scala index c73da95e2062..7c7676a74c43 100644 --- a/tests/neg/i5455.scala +++ b/tests/neg/i5455.scala @@ -11,7 +11,7 @@ object Library { def toInt(n: Nat): Int = n } - given (x: Nat) extended with + extension of (x: Nat) with def * (y: Nat): Nat = x * y def toInt: Int = x } diff --git a/tests/neg/i6801.scala b/tests/neg/i6801.scala index 94e417e7719c..ea27fd3a583c 100644 --- a/tests/neg/i6801.scala +++ b/tests/neg/i6801.scala @@ -1,4 +1,4 @@ -given myNumericOps: [T](x: T) extended with { +extension myNumericOps of [T](x: T) with { def + (y: T)(given n: Numeric[T]): T = n.plus(x,y) } def foo[T: Numeric](x: T) = 1f + x // error: no implicit argument of type Numeric[Any] diff --git a/tests/neg/i6900.scala b/tests/neg/i6900.scala index 7e696e320828..a680d074853d 100644 --- a/tests/neg/i6900.scala +++ b/tests/neg/i6900.scala @@ -1,7 +1,7 @@ object Test2 { // Works with extension method - given [A](a: A) extended with + extension of [A](a: A) with def foo[C]: C => A = _ => a // error: extension method cannot have type parameters 1.foo.foo diff --git a/tests/neg/i7529.scala b/tests/neg/i7529.scala index 974d59b268b0..64c19a42b6da 100644 --- a/tests/neg/i7529.scala +++ b/tests/neg/i7529.scala @@ -1,4 +1,4 @@ -given fooOps: [A](a: A) extended with +extension fooOps of [A](a: A) with @nonsense // error: not found: nonsense def foo = ??? \ No newline at end of file diff --git a/tests/neg/missing-implicit1.scala b/tests/neg/missing-implicit1.scala index 88d5bb23a51d..239771dd42b2 100644 --- a/tests/neg/missing-implicit1.scala +++ b/tests/neg/missing-implicit1.scala @@ -7,7 +7,7 @@ object testObjectInstance with object instances { given zipOption: Zip[Option] = ??? given traverseList: Traverse[List] = ??? - given listExtension: [T](xs: List[T]) extended with + extension listExtension of [T](xs: List[T]) with def second: T = xs.tail.head def [T](xs: List[T]) first: T = xs.head } diff --git a/tests/pos/i6900.scala b/tests/pos/i6900.scala index 3c3b23c11b9f..48cd4b4d6bad 100644 --- a/tests/pos/i6900.scala +++ b/tests/pos/i6900.scala @@ -21,7 +21,7 @@ object Test1 { object Test2 { // Works with extension method - given [A, C](a: A) extended with + extension of [A, C](a: A) with def foo: C => A = _ => a 1.foo.foo diff --git a/tests/pos/i7084.scala b/tests/pos/i7084.scala index b76042ee431c..8c353879a69b 100644 --- a/tests/pos/i7084.scala +++ b/tests/pos/i7084.scala @@ -2,7 +2,7 @@ object Test { type Foo - given (y: Any) extended with { + extension of (y: Any) with { def g(given Foo): Any = ??? } diff --git a/tests/pos/i7087.scala b/tests/pos/i7087.scala index c6340d61730f..f5d7f622d141 100644 --- a/tests/pos/i7087.scala +++ b/tests/pos/i7087.scala @@ -6,7 +6,7 @@ type F[T] = T match { case G[a] => String } -given [T](tup: T) extended with { +extension of [T](tup: T) with { def g(given Foo: F[T]) = ??? } diff --git a/tests/pos/implicit-scope.scala b/tests/pos/implicit-scope.scala index 813b6b42775f..cfe65ca934b6 100644 --- a/tests/pos/implicit-scope.scala +++ b/tests/pos/implicit-scope.scala @@ -9,7 +9,7 @@ object A { type FlagSet = opaques.FlagSet def FlagSet(bits: Long): FlagSet = opaques.FlagSet(bits) - given (xs: FlagSet) extended with { + extension of (xs: FlagSet) with { def bits: Long = opaques.toBits(xs) def | (ys: FlagSet): FlagSet = FlagSet(xs.bits | ys.bits) } diff --git a/tests/pos/matrixOps.scala b/tests/pos/matrixOps.scala index 067d23830877..baf4671ae2aa 100644 --- a/tests/pos/matrixOps.scala +++ b/tests/pos/matrixOps.scala @@ -3,7 +3,7 @@ object Test with type Matrix = Array[Array[Double]] type Vector = Array[Double] - given (m: Matrix) extended with + extension of (m: Matrix) with def nRows = m.length def nCols = m(0).length def row(i: Int): Vector = m(i) diff --git a/tests/pos/mirror-implicit-scope.scala b/tests/pos/mirror-implicit-scope.scala index a4c2dbcd7b3a..c185ab8d303c 100644 --- a/tests/pos/mirror-implicit-scope.scala +++ b/tests/pos/mirror-implicit-scope.scala @@ -3,14 +3,14 @@ import scala.deriving._ object Test { object K0 { type Generic[T] = Mirror { type Scope = K0.type ; type MirroredType = T ; type MirroredElemTypes } - given [T <: Product](gen: Generic[T]) extended with { + extension of [T <: Product](gen: Generic[T]) with { inline def toRepr (t: T): gen.MirroredElemTypes = Tuple.fromProduct(t).asInstanceOf } } object K1 { type Generic[F[_]] = Mirror { type Scope = K1.type ; type MirroredType = F ; type MirroredElemTypes[_] } - given [F[_] <: Product, T](gen: Generic[F]) extended with { + extension of [F[_] <: Product, T](gen: Generic[F]) with { inline def toRepr (t: F[T]): gen.MirroredElemTypes[T] = Tuple.fromProduct(t).asInstanceOf } } diff --git a/tests/pos/reference/delegates.scala b/tests/pos/reference/delegates.scala index 4252868bf9aa..536ac3456a2f 100644 --- a/tests/pos/reference/delegates.scala +++ b/tests/pos/reference/delegates.scala @@ -39,12 +39,12 @@ object Instances extends Common with if (fst != 0) fst else xs1.compareTo(ys1) end listOrd - given stringOps: (xs: Seq[String]) extended with + extension stringOps of (xs: Seq[String]) with def longestStrings: Seq[String] = val maxLength = xs.map(_.length).max xs.filter(_.length == maxLength) - given [T](xs: List[T]) extended with + extension of [T](xs: List[T]) with def second = xs.tail.head def third = xs.tail.tail.head @@ -133,7 +133,7 @@ object PostConditions with def result[T](given x: WrappedResult[T]): T = x - given [T](x: T) extended with + extension of [T](x: T) with def ensuring(condition: (given WrappedResult[T]) => Boolean): T = assert(condition(given x)) x @@ -153,12 +153,12 @@ object AnonymousInstances extends Common with val fst = x.compareTo(y) if (fst != 0) fst else xs1.compareTo(ys1) - given (xs: Seq[String]) extended with + extension of (xs: Seq[String]) with def longestStrings: Seq[String] = val maxLength = xs.map(_.length).max xs.filter(_.length == maxLength) - given [T](xs: List[T]) extended with + extension of [T](xs: List[T]) with def second = xs.tail.head given [From, To]: (c: Convertible[From, To]) => Convertible[List[From], List[To]] with diff --git a/tests/pos/reference/extension-methods.scala b/tests/pos/reference/extension-methods.scala index 614efe611314..3ac5e5f05934 100644 --- a/tests/pos/reference/extension-methods.scala +++ b/tests/pos/reference/extension-methods.scala @@ -41,19 +41,19 @@ object ExtMethods with List(1, 2, 3).second[Int] - given stringOps: (xs: Seq[String]) extended with { + extension stringOps of (xs: Seq[String]) with { def longestStrings: Seq[String] = { val maxLength = xs.map(_.length).max xs.filter(_.length == maxLength) } } - given listOps: [T](xs: List[T]) extended with + extension listOps of [T](xs: List[T]) with def second = xs.tail.head def third: T = xs.tail.tail.head - given [T](xs: List[T])(given Ordering[T]) extended with + extension of [T](xs: List[T])(given Ordering[T]) with def largest(n: Int) = xs.sorted.takeRight(n) given stringOps1: AnyRef { diff --git a/tests/pos/reference/opaque.scala b/tests/pos/reference/opaque.scala index 5b0b24aad768..9f2c3cc8e6c6 100644 --- a/tests/pos/reference/opaque.scala +++ b/tests/pos/reference/opaque.scala @@ -12,7 +12,7 @@ object Logarithms { } // Extension methods define opaque types' public APIs - given (x: Logarithm) extended with { + extension of (x: Logarithm) with { def toDouble: Double = math.exp(x) def + (y: Logarithm): Logarithm = Logarithm(math.exp(x) + math.exp(y)) def * (y: Logarithm): Logarithm = Logarithm(x + y) diff --git a/tests/pos/tasty-reflect-opaque-api-proto.scala b/tests/pos/tasty-reflect-opaque-api-proto.scala index 089e3c28437d..7f8e2d273e47 100644 --- a/tests/pos/tasty-reflect-opaque-api-proto.scala +++ b/tests/pos/tasty-reflect-opaque-api-proto.scala @@ -10,7 +10,7 @@ class Reflect(val internal: CompilerInterface) { opaque type Term <: Tree = internal.Term object Tree { - given ops: (tree: Tree) extended with { + extension ops of (tree: Tree) with { def show: String = ??? } } diff --git a/tests/run/extension-specificity.scala b/tests/run/extension-specificity.scala index 35ac40ff1e3a..95a57392b8af 100644 --- a/tests/run/extension-specificity.scala +++ b/tests/run/extension-specificity.scala @@ -1,10 +1,10 @@ class A class B extends A -given a: (x: A) extended with +extension a of (x: A) with def foo: Int = 1 -given b: (x: B) extended with +extension b of (x: B) with def foo: Int = 2 @main def Test = diff --git a/tests/run/extmethod-overload.scala b/tests/run/extmethod-overload.scala index c5454944bd63..c01c4f634a6f 100644 --- a/tests/run/extmethod-overload.scala +++ b/tests/run/extmethod-overload.scala @@ -97,7 +97,7 @@ object Test extends App { def (x: Int).yy(y: Int) = x + y } - given (x: Int) extended with { + extension of (x: Int) with { def yy (y: Int) = x - y } diff --git a/tests/run/extmethods2.scala b/tests/run/extmethods2.scala index 90441a4aa97d..101b5e1093d6 100644 --- a/tests/run/extmethods2.scala +++ b/tests/run/extmethods2.scala @@ -16,15 +16,15 @@ object Test extends App { test(given TC()) object A { - given listOps: [T](xs: List[T]) extended with { + extension listOps of [T](xs: List[T]) with { def second: T = xs.tail.head def third: T = xs.tail.tail.head def concat(ys: List[T]) = xs ++ ys } - given polyListOps: [T, U](xs: List[T]) extended with { + extension polyListOps of [T, U](xs: List[T]) with { def zipp(ys: List[U]): List[(T, U)] = xs.zip(ys) } - given (xs: List[Int]) extended with { + extension of (xs: List[Int]) with { def prod = (1 /: xs)(_ * _) } } diff --git a/tests/run/i6902.scala b/tests/run/i6902.scala index e3a40dd02f1e..843ade0dd54d 100644 --- a/tests/run/i6902.scala +++ b/tests/run/i6902.scala @@ -1,6 +1,6 @@ object Test { - given [A](a: A) extended with { def <<< : A = a } - given (b: Int) extended with { def <<<< : Int = b } + extension of [A](a: A) with { def <<< : A = a } + extension of (b: Int) with { def <<<< : Int = b } def main(args: Array[String]): Unit = { 1.<<< diff --git a/tests/run/instances-anonymous.scala b/tests/run/instances-anonymous.scala index 8746c623a0e0..65150f54c03c 100644 --- a/tests/run/instances-anonymous.scala +++ b/tests/run/instances-anonymous.scala @@ -8,7 +8,7 @@ object Test extends App { case class Circle(x: Double, y: Double, radius: Double) - given (c: Circle) extended with { + extension of (c: Circle) with { def circumference: Double = c.radius * math.Pi * 2 } @@ -25,13 +25,13 @@ object Test extends App { val names = List("hi", "hello", "world") assert(names.longestStrings == List("hello", "world")) - given [T](xs: Seq[T]) extended with { + extension of [T](xs: Seq[T]) with { def second = xs.tail.head } assert(names.longestStrings.second == "world") - given [T](xs: List[List[T]]) extended with { + extension of [T](xs: List[List[T]]) with { def flattened = xs.foldLeft[List[T]](Nil)(_ ++ _) } diff --git a/tests/run/instances.scala b/tests/run/instances.scala index 033bc81c0f52..e1a0e34c64e2 100644 --- a/tests/run/instances.scala +++ b/tests/run/instances.scala @@ -8,14 +8,14 @@ object Test extends App { case class Circle(x: Double, y: Double, radius: Double) - given circleOps: (c: Circle) extended with + extension circleOps of (c: Circle) with def circumference: Double = c.radius * math.Pi * 2 val circle = new Circle(1, 1, 2.0) assert(circle.circumference == circleOps.circumference(circle)) - given stringOps: (xs: Seq[String]) extended with + extension stringOps of (xs: Seq[String]) with def longestStrings: Seq[String] = val maxLength = xs.map(_.length).max xs.filter(_.length == maxLength) @@ -23,12 +23,12 @@ object Test extends App { val names = List("hi", "hello", "world") assert(names.longestStrings == List("hello", "world")) - given [T](xs: Seq[T]) extended with + extension of [T](xs: Seq[T]) with def second = xs.tail.head assert(names.longestStrings.second == "world") - given listListOps: [T](xs: List[List[T]]) extended with + extension listListOps of [T](xs: List[List[T]]) with def flattened = xs.foldLeft[List[T]](Nil)(_ ++ _) // A right associative op. Note: can't use given extension for this! From a89a9cc92a6e9277ce03f9cc9edf26ca457bb3e1 Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Tue, 7 Jan 2020 18:01:31 +0100 Subject: [PATCH 08/11] Change naming scheme for anonymous extensions They now start with `extension_` instead of `given_`. --- .../src/dotty/tools/dotc/ast/Desugar.scala | 47 +++++++++---------- .../src/dotty/tools/dotc/typer/Typer.scala | 2 +- .../reference/contextual/extension-methods.md | 2 +- .../contextual/relationship-implicits.md | 16 +++++-- 4 files changed, 35 insertions(+), 32 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/ast/Desugar.scala b/compiler/src/dotty/tools/dotc/ast/Desugar.scala index e5231018126a..6eedc729c226 100644 --- a/compiler/src/dotty/tools/dotc/ast/Desugar.scala +++ b/compiler/src/dotty/tools/dotc/ast/Desugar.scala @@ -954,10 +954,6 @@ object desugar { else tree } - /** Invent a name for an anonympus given of type or template `impl`. */ - def inventGivenName(impl: Tree)(implicit ctx: Context): SimpleName = - s"given_${inventName(impl)}".toTermName.asSimpleName - /** The normalized name of `mdef`. This means * 1. Check that the name does not redefine a Scala core class. * If it does redefine, issue an error and return a mangled name instead of the original one. @@ -965,7 +961,7 @@ object desugar { */ def normalizeName(mdef: MemberDef, impl: Tree)(implicit ctx: Context): Name = { var name = mdef.name - if (name.isEmpty) name = name.likeSpaced(inventGivenName(impl)) + if (name.isEmpty) name = name.likeSpaced(inventGivenOrExtensionName(impl)) if (ctx.owner == defn.ScalaPackageClass && defn.reservedScalaClassNames.contains(name.toTypeName)) { def kind = if (name.isTypeName) "class" else "object" ctx.error(em"illegal redefinition of standard $kind $name", mdef.sourcePos) @@ -974,27 +970,26 @@ object desugar { name } - /** Invent a name for an anonymous instance with template `impl`. - */ - private def inventName(impl: Tree)(implicit ctx: Context): String = impl match { - case impl: Template => - if (impl.parents.isEmpty) - impl.body.find { - case dd: DefDef if dd.mods.is(Extension) => true - case _ => false - } - match { - case Some(DefDef(name, _, (vparam :: _) :: _, _, _)) => - s"${name}_of_${inventTypeName(vparam.tpt)}" - case _ => - ctx.error(i"anonymous instance must implement a type or have at least one extension method", impl.sourcePos) - nme.ERROR.toString - } - else - impl.parents.map(inventTypeName(_)).mkString("_") - case impl: Tree => - inventTypeName(impl) - } + /** Invent a name for an anonympus given or extension of type or template `impl`. */ + def inventGivenOrExtensionName(impl: Tree)(given ctx: Context): SimpleName = + val str = impl match + case impl: Template => + if impl.parents.isEmpty then + impl.body.find { + case dd: DefDef if dd.mods.is(Extension) => true + case _ => false + } + match + case Some(DefDef(name, _, (vparam :: _) :: _, _, _)) => + s"extension_${name}_${inventTypeName(vparam.tpt)}" + case _ => + ctx.error(i"anonymous instance must implement a type or have at least one extension method", impl.sourcePos) + nme.ERROR.toString + else + impl.parents.map(inventTypeName(_)).mkString("given_", "_", "") + case impl: Tree => + "given_" ++ inventTypeName(impl) + str.toTermName.asSimpleName private class NameExtractor(followArgs: Boolean) extends UntypedTreeAccumulator[String] { private def extractArgs(args: List[Tree])(implicit ctx: Context): String = diff --git a/compiler/src/dotty/tools/dotc/typer/Typer.scala b/compiler/src/dotty/tools/dotc/typer/Typer.scala index da56757fc755..107eb7eb0f57 100644 --- a/compiler/src/dotty/tools/dotc/typer/Typer.scala +++ b/compiler/src/dotty/tools/dotc/typer/Typer.scala @@ -1541,7 +1541,7 @@ class Typer extends Namer var name = tree.name if (name == nme.WILDCARD && tree.mods.is(Given)) { val Typed(_, tpt): @unchecked = tree.body - name = desugar.inventGivenName(tpt) + name = desugar.inventGivenOrExtensionName(tpt) } if (name == nme.WILDCARD) body1 else { diff --git a/docs/docs/reference/contextual/extension-methods.md b/docs/docs/reference/contextual/extension-methods.md index bf8140562684..b95f7a0cf5ee 100644 --- a/docs/docs/reference/contextual/extension-methods.md +++ b/docs/docs/reference/contextual/extension-methods.md @@ -158,7 +158,7 @@ given listOps: AnyRef { def [T](xs: List[T]) second = xs.tail.head def [T](xs: List[T]) third: T = xs.tail.tail.head } -given given_largest_of_List_T: AnyRef { +given extension_largest_List_T: AnyRef { def [T](xs: List[T]) largest (given Ordering[T])(n: Int) = xs.sorted.takeRight(n) } diff --git a/docs/docs/reference/contextual/relationship-implicits.md b/docs/docs/reference/contextual/relationship-implicits.md index 8c03b5fc1dbd..bffd02060a2b 100644 --- a/docs/docs/reference/contextual/relationship-implicits.md +++ b/docs/docs/reference/contextual/relationship-implicits.md @@ -61,15 +61,23 @@ The synthesized type names are formed from Tuples are treated as transparent, i.e. a type `F[(X, Y)]` would get the synthesized name `F_X_Y`. Directly implemented function types `A => B` are represented as `A_to_B`. Function types used as arguments to other type constructors are represented as `Function`. -Anonymous given instances that define extension methods -get their name from the name of the first extension method and the toplevel type -constructor of its first parameter. For example, the given instance +### Anonymous Collective Extensions + +Anonymous collective extensions also get compiler synthesized names, which are formed from + + - the prefix `extension_` + - the name of the first defined extension method + - the simple name of the first parameter type of this extension method + - the simple name(s) of the toplevel argument type constructors to this type. + +For example, the extension ```scala extension of [T] (xs: List[T]) with { def second = ... } ``` -gets the synthesized name `given_second_of_List_T`. +gets the synthesized name `extension_second_List_T`. + ### Given Clauses From 6dd4a99010352cf9945c725991081e87c4b64143 Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Mon, 13 Jan 2020 11:01:08 +0100 Subject: [PATCH 09/11] Rearrange doc pages Rearrange doc pages so that we can merge before the next release. --- .../contextual/extension-methods-new.md | 181 ++++++++++++++++++ .../reference/contextual/extension-methods.md | 43 ++--- 2 files changed, 200 insertions(+), 24 deletions(-) create mode 100644 docs/docs/reference/contextual/extension-methods-new.md diff --git a/docs/docs/reference/contextual/extension-methods-new.md b/docs/docs/reference/contextual/extension-methods-new.md new file mode 100644 index 000000000000..b95f7a0cf5ee --- /dev/null +++ b/docs/docs/reference/contextual/extension-methods-new.md @@ -0,0 +1,181 @@ +--- +layout: doc-page +title: "Extension Methods" +--- + +Extension methods allow one to add methods to a type after the type is defined. Example: + +```scala +case class Circle(x: Double, y: Double, radius: Double) + +def (c: Circle).circumference: Double = c.radius * math.Pi * 2 +``` + +Like regular methods, extension methods can be invoked with infix `.`: + +```scala +val circle = Circle(0, 0, 1) +circle.circumference +``` + +### Translation of Extension Methods + +Extension methods are methods that have a parameter clause in front of the defined +identifier. They translate to methods where the leading parameter section is moved +to after the defined identifier. So, the definition of `circumference` above translates +to the plain method, and can also be invoked as such: +```scala +def circumference(c: Circle): Double = c.radius * math.Pi * 2 + +assert(circle.circumference == circumference(circle)) +``` + +### Translation of Calls to Extension Methods + +When is an extension method applicable? There are two possibilities. + + - An extension method is applicable if it is visible under a simple name, by being defined + or inherited or imported in a scope enclosing the application. + - An extension method is applicable if it is a member of some given instance at the point of the application. + +As an example, consider an extension method `longestStrings` on `Seq[String]` defined in a trait `StringSeqOps`. + +```scala +trait StringSeqOps { + def (xs: Seq[String]).longestStrings = { + val maxLength = xs.map(_.length).max + xs.filter(_.length == maxLength) + } +} +``` +We can make the extension method available by defining a given `StringSeqOps` instance, like this: +```scala +given ops1: StringSeqOps +``` +Then +```scala +List("here", "is", "a", "list").longestStrings +``` +is legal everywhere `ops1` is available. Alternatively, we can define `longestStrings` as a member of a normal object. But then the method has to be brought into scope to be usable as an extension method. + +```scala +object ops2 extends StringSeqOps +import ops2.longestStrings +List("here", "is", "a", "list").longestStrings +``` +The precise rules for resolving a selection to an extension method are as follows. + +Assume a selection `e.m[Ts]` where `m` is not a member of `e`, where the type arguments `[Ts]` are optional, +and where `T` is the expected type. The following two rewritings are tried in order: + + 1. The selection is rewritten to `m[Ts](e)`. + 2. If the first rewriting does not typecheck with expected type `T`, and there is a given instance `i` + in either the current scope or in the implicit scope of `T`, and `i` defines an extension + method named `m`, then selection is expanded to `i.m[Ts](e)`. + This second rewriting is attempted at the time where the compiler also tries an implicit conversion + from `T` to a type containing `m`. If there is more than one way of rewriting, an ambiguity error results. + +So `circle.circumference` translates to `CircleOps.circumference(circle)`, provided +`circle` has type `Circle` and `CircleOps` is given (i.e. it is visible at the point of call or it is defined in the companion object of `Circle`). + +### Operators + +The extension method syntax also applies to the definition of operators. +In this case it is allowed and preferable to omit the period between the leading parameter list +and the operator. In each case the definition syntax mirrors the way the operator is applied. +Examples: +```scala +def (x: String) < (y: String) = ... +def (x: Elem) +: (xs: Seq[Elem]) = ... +def (x: Number) min (y: Number) = ... + +"ab" < "c" +1 +: List(2, 3) +x min 3 +``` +The three definitions above translate to +```scala +def < (x: String)(y: String) = ... +def +: (xs: Seq[Elem])(x: Elem) = ... +def min(x: Number)(y: Number) = ... +``` +Note the swap of the two parameters `x` and `xs` when translating +the right-binding operator `+:` to an extension method. This is analogous +to the implementation of right binding operators as normal methods. + +### Generic Extensions + +The `StringSeqOps` examples extended a specific instance of a generic type. It is also possible to extend a generic type by adding type parameters to an extension method. Examples: + +```scala +def [T](xs: List[T]) second = + xs.tail.head + +def [T](xs: List[List[T]]) flattened = + xs.foldLeft[List[T]](Nil)(_ ++ _) + +def [T: Numeric](x: T) + (y: T): T = + summon[Numeric[T]].plus(x, y) +``` + +If an extension method has type parameters, they come immediately after the `def` and are followed by the extended parameter. When calling a generic extension method, any explicitly given type arguments follow the method name. So the `second` method can be instantiated as follows: +```scala +List(1, 2, 3).second[Int] +``` +### Collective Extensions + +A collective extension defines one or more concrete methods that have the same type parameters +and prefix parameter. Examples: + +```scala +extension stringOps of (xs: Seq[String]) with { + def longestStrings: Seq[String] = { + val maxLength = xs.map(_.length).max + xs.filter(_.length == maxLength) + } +} + +extension listOps of [T](xs: List[T]) with { + def second = xs.tail.head + def third: T = xs.tail.tail.head +} + +extension of [T](xs: List[T])(given Ordering[T]) with { + def largest(n: Int) = xs.sorted.takeRight(n) +} +``` +If a given extension is anonymous (as in the last clause), its name is synthesized from the name of the first defined extension method. + +The extensions above are equivalent to the following regular given instances where the implemented parent is `AnyRef` and the parameters in the `extension` clause are repeated in each extension method definition: +```scala +given stringOps: AnyRef { + def (xs: Seq[String]).longestStrings: Seq[String] = { + val maxLength = xs.map(_.length).max + xs.filter(_.length == maxLength) + } +} +given listOps: AnyRef { + def [T](xs: List[T]) second = xs.tail.head + def [T](xs: List[T]) third: T = xs.tail.tail.head +} +given extension_largest_List_T: AnyRef { + def [T](xs: List[T]) largest (given Ordering[T])(n: Int) = + xs.sorted.takeRight(n) +} +``` + +`extension` and `of` are soft keywords. They can also be used as a regular identifiers. + +### Syntax + +Here are the syntax changes for extension methods and given extensions relative +to the [current syntax](../../internals/syntax.md). `extension` is a soft keyword, recognized only after a `given`. It can be used as an identifier everywhere else. +``` +DefSig ::= ... + | ExtParamClause [nl] [‘.’] id DefParamClauses +ExtParamClause ::= [DefTypeParamClause] ‘(’ DefParam ‘)’ +TmplDef ::= ... + | ‘extension’ ExtensionDef +ExtensionDef ::= [id] ‘of’ ExtParamClause {GivenParamClause} ‘with’ ExtMethods +ExtMethods ::= ‘{’ ‘def’ DefDef {semi ‘def’ DefDef} ‘}’ +``` diff --git a/docs/docs/reference/contextual/extension-methods.md b/docs/docs/reference/contextual/extension-methods.md index b95f7a0cf5ee..7ad12fe80bdb 100644 --- a/docs/docs/reference/contextual/extension-methods.md +++ b/docs/docs/reference/contextual/extension-methods.md @@ -3,12 +3,15 @@ layout: doc-page title: "Extension Methods" --- +**Note:** The syntax of extension methods is about to change. Here is the +[doc page with the new syntax](./extension-methods-new.html), supported from Dotty 0.22 onwards. + Extension methods allow one to add methods to a type after the type is defined. Example: ```scala case class Circle(x: Double, y: Double, radius: Double) -def (c: Circle).circumference: Double = c.radius * math.Pi * 2 +def (c: Circle) circumference: Double = c.radius * math.Pi * 2 ``` Like regular methods, extension methods can be invoked with infix `.`: @@ -42,7 +45,7 @@ As an example, consider an extension method `longestStrings` on `Seq[String]` de ```scala trait StringSeqOps { - def (xs: Seq[String]).longestStrings = { + def (xs: Seq[String]) longestStrings = { val maxLength = xs.map(_.length).max xs.filter(_.length == maxLength) } @@ -81,25 +84,21 @@ So `circle.circumference` translates to `CircleOps.circumference(circle)`, provi ### Operators The extension method syntax also applies to the definition of operators. -In this case it is allowed and preferable to omit the period between the leading parameter list -and the operator. In each case the definition syntax mirrors the way the operator is applied. +In each case the definition syntax mirrors the way the operator is applied. Examples: ```scala def (x: String) < (y: String) = ... def (x: Elem) +: (xs: Seq[Elem]) = ... -def (x: Number) min (y: Number) = ... "ab" < "c" 1 +: List(2, 3) -x min 3 ``` -The three definitions above translate to +The two definitions above translate to ```scala def < (x: String)(y: String) = ... def +: (xs: Seq[Elem])(x: Elem) = ... -def min(x: Number)(y: Number) = ... ``` -Note the swap of the two parameters `x` and `xs` when translating +Note that swap of the two parameters `x` and `xs` when translating the right-binding operator `+:` to an extension method. This is analogous to the implementation of right binding operators as normal methods. @@ -122,25 +121,24 @@ If an extension method has type parameters, they come immediately after the `def ```scala List(1, 2, 3).second[Int] ``` -### Collective Extensions +### Given Instances for Extension Methods -A collective extension defines one or more concrete methods that have the same type parameters -and prefix parameter. Examples: +`given` extensions are given instances that define extension methods and nothing else. Examples: ```scala -extension stringOps of (xs: Seq[String]) with { +given stringOps: (xs: Seq[String]) extended with { def longestStrings: Seq[String] = { val maxLength = xs.map(_.length).max xs.filter(_.length == maxLength) } } -extension listOps of [T](xs: List[T]) with { +given listOps: [T](xs: List[T]) extended with { def second = xs.tail.head def third: T = xs.tail.tail.head } -extension of [T](xs: List[T])(given Ordering[T]) with { +given [T](xs: List[T])(given Ordering[T]) extended with { def largest(n: Int) = xs.sorted.takeRight(n) } ``` @@ -149,7 +147,7 @@ If a given extension is anonymous (as in the last clause), its name is synthesiz The extensions above are equivalent to the following regular given instances where the implemented parent is `AnyRef` and the parameters in the `extension` clause are repeated in each extension method definition: ```scala given stringOps: AnyRef { - def (xs: Seq[String]).longestStrings: Seq[String] = { + def (xs: Seq[String]) longestStrings: Seq[String] = { val maxLength = xs.map(_.length).max xs.filter(_.length == maxLength) } @@ -158,24 +156,21 @@ given listOps: AnyRef { def [T](xs: List[T]) second = xs.tail.head def [T](xs: List[T]) third: T = xs.tail.tail.head } -given extension_largest_List_T: AnyRef { +given given_largest_of_List_T: AnyRef { def [T](xs: List[T]) largest (given Ordering[T])(n: Int) = xs.sorted.takeRight(n) } ``` -`extension` and `of` are soft keywords. They can also be used as a regular identifiers. - ### Syntax Here are the syntax changes for extension methods and given extensions relative to the [current syntax](../../internals/syntax.md). `extension` is a soft keyword, recognized only after a `given`. It can be used as an identifier everywhere else. ``` DefSig ::= ... - | ExtParamClause [nl] [‘.’] id DefParamClauses + | ExtParamClause [nl] id DefParamClauses +GivenDef ::= ... + [id ‘:’] ‘extension’ ExtParamClause {GivenParamClause} ExtMethods ExtParamClause ::= [DefTypeParamClause] ‘(’ DefParam ‘)’ -TmplDef ::= ... - | ‘extension’ ExtensionDef -ExtensionDef ::= [id] ‘of’ ExtParamClause {GivenParamClause} ‘with’ ExtMethods -ExtMethods ::= ‘{’ ‘def’ DefDef {semi ‘def’ DefDef} ‘}’ +ExtMethods ::= [nl] ‘{’ ‘def’ DefDef {semi ‘def’ DefDef} ‘}’ ``` From 6c380b237fbcdf39d95312f595f55973e718d7e8 Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Mon, 13 Jan 2020 11:22:49 +0100 Subject: [PATCH 10/11] Make `with` optional in extension methods --- compiler/src/dotty/tools/dotc/parsing/Parsers.scala | 2 +- docs/docs/internals/syntax.md | 5 ++--- docs/docs/reference/contextual/extension-methods-new.md | 8 ++++---- docs/docs/reference/contextual/relationship-implicits.md | 2 +- tests/neg/extension-methods.scala | 2 +- tests/neg/extmethod-overload.scala | 5 +++-- tests/neg/i5455.scala | 3 ++- tests/neg/i6801.scala | 2 +- tests/pos/i7084.scala | 2 +- tests/pos/i7087.scala | 2 +- tests/pos/implicit-scope.scala | 2 +- tests/pos/mirror-implicit-scope.scala | 4 ++-- tests/pos/reference/extension-methods.scala | 2 +- tests/pos/reference/opaque.scala | 2 +- tests/pos/tasty-reflect-opaque-api-proto.scala | 2 +- tests/run/extmethod-overload.scala | 2 +- tests/run/extmethods2.scala | 6 +++--- tests/run/i6902.scala | 4 ++-- tests/run/instances-anonymous.scala | 6 +++--- 19 files changed, 32 insertions(+), 31 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/parsing/Parsers.scala b/compiler/src/dotty/tools/dotc/parsing/Parsers.scala index 4cf93b3a378d..3263fbfea8a3 100644 --- a/compiler/src/dotty/tools/dotc/parsing/Parsers.scala +++ b/compiler/src/dotty/tools/dotc/parsing/Parsers.scala @@ -3544,7 +3544,7 @@ object Parsers { val tparams = typeParamClauseOpt(ParamOwner.Def) val extParams = paramClause(0, prefix = true) val givenParamss = paramClauses(givenOnly = true) - accept(WITH) + possibleTemplateStart() if !in.isNestedStart then syntaxError("Extension without extension methods") val templ = templateBodyOpt(makeConstructor(tparams, extParams :: givenParamss), Nil, Nil) templ.body.foreach(checkExtensionMethod(tparams, _)) diff --git a/docs/docs/internals/syntax.md b/docs/docs/internals/syntax.md index 08fc95e33a8e..e688e554584b 100644 --- a/docs/docs/internals/syntax.md +++ b/docs/docs/internals/syntax.md @@ -389,9 +389,8 @@ GivenDef ::= [GivenSig (‘:’ | <:)] {FunArgTypes ‘=>’} | [GivenSig ‘:’] {FunArgTypes ‘=>’} ConstrApps [[‘with’] TemplateBody] GivenSig ::= [id] [DefTypeParamClause] {GivenParamClause} -ExtensionDef ::= [id] ‘of’ ExtParamClause {GivenParamClause} - ‘with’ ExtMethods -ExtMethods ::= ‘{’ ‘def’ DefDef {semi ‘def’ DefDef} ‘}’ +ExtensionDef ::= [id] ‘of’ ExtParamClause {GivenParamClause} ExtMethods +ExtMethods ::= [nl] ‘{’ ‘def’ DefDef {semi ‘def’ DefDef} ‘}’ ExtParamClause ::= [DefTypeParamClause] ‘(’ DefParam ‘)’ Template ::= InheritClauses [[‘with’] TemplateBody] Template(constr, parents, self, stats) InheritClauses ::= [‘extends’ ConstrApps] [‘derives’ QualId {‘,’ QualId}] diff --git a/docs/docs/reference/contextual/extension-methods-new.md b/docs/docs/reference/contextual/extension-methods-new.md index b95f7a0cf5ee..d92c96cc1085 100644 --- a/docs/docs/reference/contextual/extension-methods-new.md +++ b/docs/docs/reference/contextual/extension-methods-new.md @@ -128,19 +128,19 @@ A collective extension defines one or more concrete methods that have the same t and prefix parameter. Examples: ```scala -extension stringOps of (xs: Seq[String]) with { +extension stringOps of (xs: Seq[String]) { def longestStrings: Seq[String] = { val maxLength = xs.map(_.length).max xs.filter(_.length == maxLength) } } -extension listOps of [T](xs: List[T]) with { +extension listOps of [T](xs: List[T]) { def second = xs.tail.head def third: T = xs.tail.tail.head } -extension of [T](xs: List[T])(given Ordering[T]) with { +extension of [T](xs: List[T])(given Ordering[T]) { def largest(n: Int) = xs.sorted.takeRight(n) } ``` @@ -176,6 +176,6 @@ DefSig ::= ... ExtParamClause ::= [DefTypeParamClause] ‘(’ DefParam ‘)’ TmplDef ::= ... | ‘extension’ ExtensionDef -ExtensionDef ::= [id] ‘of’ ExtParamClause {GivenParamClause} ‘with’ ExtMethods +ExtensionDef ::= [id] ‘of’ ExtParamClause {GivenParamClause} ExtMethods ExtMethods ::= ‘{’ ‘def’ DefDef {semi ‘def’ DefDef} ‘}’ ``` diff --git a/docs/docs/reference/contextual/relationship-implicits.md b/docs/docs/reference/contextual/relationship-implicits.md index bffd02060a2b..580159c17422 100644 --- a/docs/docs/reference/contextual/relationship-implicits.md +++ b/docs/docs/reference/contextual/relationship-implicits.md @@ -72,7 +72,7 @@ Anonymous collective extensions also get compiler synthesized names, which are f For example, the extension ```scala -extension of [T] (xs: List[T]) with { +extension of [T] (xs: List[T]) { def second = ... } ``` diff --git a/tests/neg/extension-methods.scala b/tests/neg/extension-methods.scala index ca870a223a4f..6ca874b70bb8 100644 --- a/tests/neg/extension-methods.scala +++ b/tests/neg/extension-methods.scala @@ -10,7 +10,7 @@ object Test { "".l2 // error 1.l1 // error - extension of [T](xs: List[T]) with { + extension of [T](xs: List[T]) { def (x: Int).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 diff --git a/tests/neg/extmethod-overload.scala b/tests/neg/extmethod-overload.scala index 333f0c34e19d..0e48071e8993 100644 --- a/tests/neg/extmethod-overload.scala +++ b/tests/neg/extmethod-overload.scala @@ -1,8 +1,9 @@ object Test { - extension a of (x: Int) with + extension a of (x: Int) { def |+| (y: Int) = x + y + } - extension b of (x: Int) with { + extension b of (x: Int) { def |+| (y: String) = x + y.length } assert((1 |+| 2) == 3) // error ambiguous diff --git a/tests/neg/i5455.scala b/tests/neg/i5455.scala index 7c7676a74c43..e2c06faaaee3 100644 --- a/tests/neg/i5455.scala +++ b/tests/neg/i5455.scala @@ -11,9 +11,10 @@ object Library { def toInt(n: Nat): Int = n } - extension of (x: Nat) with + extension of (x: Nat) { def * (y: Nat): Nat = x * y def toInt: Int = x + } } object User extends App { diff --git a/tests/neg/i6801.scala b/tests/neg/i6801.scala index ea27fd3a583c..24bb4e3268ec 100644 --- a/tests/neg/i6801.scala +++ b/tests/neg/i6801.scala @@ -1,4 +1,4 @@ -extension myNumericOps of [T](x: T) with { +extension myNumericOps of [T](x: T) { def + (y: T)(given n: Numeric[T]): T = n.plus(x,y) } def foo[T: Numeric](x: T) = 1f + x // error: no implicit argument of type Numeric[Any] diff --git a/tests/pos/i7084.scala b/tests/pos/i7084.scala index 8c353879a69b..99bff45eda84 100644 --- a/tests/pos/i7084.scala +++ b/tests/pos/i7084.scala @@ -2,7 +2,7 @@ object Test { type Foo - extension of (y: Any) with { + extension of (y: Any) { def g(given Foo): Any = ??? } diff --git a/tests/pos/i7087.scala b/tests/pos/i7087.scala index f5d7f622d141..21505d5ee587 100644 --- a/tests/pos/i7087.scala +++ b/tests/pos/i7087.scala @@ -6,7 +6,7 @@ type F[T] = T match { case G[a] => String } -extension of [T](tup: T) with { +extension of [T](tup: T) { def g(given Foo: F[T]) = ??? } diff --git a/tests/pos/implicit-scope.scala b/tests/pos/implicit-scope.scala index cfe65ca934b6..25ba05fbc46a 100644 --- a/tests/pos/implicit-scope.scala +++ b/tests/pos/implicit-scope.scala @@ -9,7 +9,7 @@ object A { type FlagSet = opaques.FlagSet def FlagSet(bits: Long): FlagSet = opaques.FlagSet(bits) - extension of (xs: FlagSet) with { + extension of (xs: FlagSet) { def bits: Long = opaques.toBits(xs) def | (ys: FlagSet): FlagSet = FlagSet(xs.bits | ys.bits) } diff --git a/tests/pos/mirror-implicit-scope.scala b/tests/pos/mirror-implicit-scope.scala index c185ab8d303c..4b711d7e1f3b 100644 --- a/tests/pos/mirror-implicit-scope.scala +++ b/tests/pos/mirror-implicit-scope.scala @@ -3,14 +3,14 @@ import scala.deriving._ object Test { object K0 { type Generic[T] = Mirror { type Scope = K0.type ; type MirroredType = T ; type MirroredElemTypes } - extension of [T <: Product](gen: Generic[T]) with { + extension of [T <: Product](gen: Generic[T]) { inline def toRepr (t: T): gen.MirroredElemTypes = Tuple.fromProduct(t).asInstanceOf } } object K1 { type Generic[F[_]] = Mirror { type Scope = K1.type ; type MirroredType = F ; type MirroredElemTypes[_] } - extension of [F[_] <: Product, T](gen: Generic[F]) with { + extension of [F[_] <: Product, T](gen: Generic[F]) { inline def toRepr (t: F[T]): gen.MirroredElemTypes[T] = Tuple.fromProduct(t).asInstanceOf } } diff --git a/tests/pos/reference/extension-methods.scala b/tests/pos/reference/extension-methods.scala index 3ac5e5f05934..3e254af31d7b 100644 --- a/tests/pos/reference/extension-methods.scala +++ b/tests/pos/reference/extension-methods.scala @@ -41,7 +41,7 @@ object ExtMethods with List(1, 2, 3).second[Int] - extension stringOps of (xs: Seq[String]) with { + extension stringOps of (xs: Seq[String]) { def longestStrings: Seq[String] = { val maxLength = xs.map(_.length).max xs.filter(_.length == maxLength) diff --git a/tests/pos/reference/opaque.scala b/tests/pos/reference/opaque.scala index 9f2c3cc8e6c6..b0dbaed6f9cf 100644 --- a/tests/pos/reference/opaque.scala +++ b/tests/pos/reference/opaque.scala @@ -12,7 +12,7 @@ object Logarithms { } // Extension methods define opaque types' public APIs - extension of (x: Logarithm) with { + extension of (x: Logarithm) { def toDouble: Double = math.exp(x) def + (y: Logarithm): Logarithm = Logarithm(math.exp(x) + math.exp(y)) def * (y: Logarithm): Logarithm = Logarithm(x + y) diff --git a/tests/pos/tasty-reflect-opaque-api-proto.scala b/tests/pos/tasty-reflect-opaque-api-proto.scala index 7f8e2d273e47..f0f9f4dfdc41 100644 --- a/tests/pos/tasty-reflect-opaque-api-proto.scala +++ b/tests/pos/tasty-reflect-opaque-api-proto.scala @@ -10,7 +10,7 @@ class Reflect(val internal: CompilerInterface) { opaque type Term <: Tree = internal.Term object Tree { - extension ops of (tree: Tree) with { + extension ops of (tree: Tree) { def show: String = ??? } } diff --git a/tests/run/extmethod-overload.scala b/tests/run/extmethod-overload.scala index c01c4f634a6f..68d49b513028 100644 --- a/tests/run/extmethod-overload.scala +++ b/tests/run/extmethod-overload.scala @@ -97,7 +97,7 @@ object Test extends App { def (x: Int).yy(y: Int) = x + y } - extension of (x: Int) with { + extension of (x: Int) { def yy (y: Int) = x - y } diff --git a/tests/run/extmethods2.scala b/tests/run/extmethods2.scala index 101b5e1093d6..1e60a217eb54 100644 --- a/tests/run/extmethods2.scala +++ b/tests/run/extmethods2.scala @@ -16,15 +16,15 @@ object Test extends App { test(given TC()) object A { - extension listOps of [T](xs: List[T]) with { + extension listOps of [T](xs: List[T]) { def second: T = xs.tail.head def third: T = xs.tail.tail.head def concat(ys: List[T]) = xs ++ ys } - extension polyListOps of [T, U](xs: List[T]) with { + extension polyListOps of [T, U](xs: List[T]) { def zipp(ys: List[U]): List[(T, U)] = xs.zip(ys) } - extension of (xs: List[Int]) with { + extension of (xs: List[Int]) { def prod = (1 /: xs)(_ * _) } } diff --git a/tests/run/i6902.scala b/tests/run/i6902.scala index 843ade0dd54d..b95bda4660cd 100644 --- a/tests/run/i6902.scala +++ b/tests/run/i6902.scala @@ -1,6 +1,6 @@ object Test { - extension of [A](a: A) with { def <<< : A = a } - extension of (b: Int) with { def <<<< : Int = b } + extension of [A](a: A) { def <<< : A = a } + extension of (b: Int) { def <<<< : Int = b } def main(args: Array[String]): Unit = { 1.<<< diff --git a/tests/run/instances-anonymous.scala b/tests/run/instances-anonymous.scala index 65150f54c03c..f17e1fc74904 100644 --- a/tests/run/instances-anonymous.scala +++ b/tests/run/instances-anonymous.scala @@ -8,7 +8,7 @@ object Test extends App { case class Circle(x: Double, y: Double, radius: Double) - extension of (c: Circle) with { + extension of (c: Circle) { def circumference: Double = c.radius * math.Pi * 2 } @@ -25,13 +25,13 @@ object Test extends App { val names = List("hi", "hello", "world") assert(names.longestStrings == List("hello", "world")) - extension of [T](xs: Seq[T]) with { + extension of [T](xs: Seq[T]) { def second = xs.tail.head } assert(names.longestStrings.second == "world") - extension of [T](xs: List[List[T]]) with { + extension of [T](xs: List[List[T]]) { def flattened = xs.foldLeft[List[T]](Nil)(_ ++ _) } From ab3de898243b297efe8b833dfec5b236b74435bb Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Mon, 13 Jan 2020 11:42:03 +0100 Subject: [PATCH 11/11] Syntax change: "extension on" instead of "extension of" "extension on" is more precise. The thing after the "of/on" is a parameter. The definition is not an extension "of" this parameter (what does that even mean?), but rather an extension of the parameter's underlying type. Note that Dart also uses "extension on". --- compiler/src/dotty/tools/dotc/core/StdNames.scala | 2 +- compiler/src/dotty/tools/dotc/parsing/Parsers.scala | 10 +++++----- .../reference/contextual/extension-methods-new.md | 13 +++++++------ .../reference/contextual/relationship-implicits.md | 2 +- tests/neg/extension-methods.scala | 2 +- tests/neg/extmethod-overload.scala | 4 ++-- tests/neg/i5455.scala | 2 +- tests/neg/i6801.scala | 2 +- tests/neg/i6900.scala | 2 +- tests/neg/i7529.scala | 2 +- tests/neg/missing-implicit1.scala | 2 +- tests/pos/i6900.scala | 2 +- tests/pos/i7084.scala | 2 +- tests/pos/i7087.scala | 2 +- tests/pos/implicit-scope.scala | 2 +- tests/pos/matrixOps.scala | 2 +- tests/pos/mirror-implicit-scope.scala | 4 ++-- tests/pos/reference/delegates.scala | 10 +++++----- tests/pos/reference/extension-methods.scala | 6 +++--- tests/pos/reference/opaque.scala | 2 +- tests/pos/tasty-reflect-opaque-api-proto.scala | 2 +- tests/run/extension-specificity.scala | 4 ++-- tests/run/extmethod-overload.scala | 2 +- tests/run/extmethods2.scala | 6 +++--- tests/run/i6902.scala | 4 ++-- tests/run/instances-anonymous.scala | 6 +++--- tests/run/instances.scala | 8 ++++---- 27 files changed, 54 insertions(+), 53 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/core/StdNames.scala b/compiler/src/dotty/tools/dotc/core/StdNames.scala index 03b92d13d73d..362b7815db6a 100644 --- a/compiler/src/dotty/tools/dotc/core/StdNames.scala +++ b/compiler/src/dotty/tools/dotc/core/StdNames.scala @@ -529,8 +529,8 @@ object StdNames { val notify_ : N = "notify" val null_ : N = "null" val nullExpr: N = "nullExpr" - val of: N = "of" val ofDim: N = "ofDim" + val on: N = "on" val opaque: N = "opaque" val open: N = "open" val ordinal: N = "ordinal" diff --git a/compiler/src/dotty/tools/dotc/parsing/Parsers.scala b/compiler/src/dotty/tools/dotc/parsing/Parsers.scala index 3263fbfea8a3..85c3e21f1358 100644 --- a/compiler/src/dotty/tools/dotc/parsing/Parsers.scala +++ b/compiler/src/dotty/tools/dotc/parsing/Parsers.scala @@ -948,9 +948,9 @@ object Parsers { def followingIsExtension() = val lookahead = in.LookaheadScanner() lookahead.nextToken() - if lookahead.isIdent && !lookahead.isIdent(nme.of) then + if lookahead.isIdent && !lookahead.isIdent(nme.on) then lookahead.nextToken() - lookahead.isIdent(nme.of) + lookahead.isIdent(nme.on) /* --------- OPERAND/OPERATOR STACK --------------------------------------- */ @@ -3538,9 +3538,9 @@ object Parsers { */ def extensionDef(start: Offset, mods: Modifiers): ModuleDef = in.nextToken() - val name = if isIdent && !isIdent(nme.of) then ident() else EmptyTermName - if !isIdent(nme.of) then syntaxErrorOrIncomplete("`of` expected") - if isIdent(nme.of) then in.nextToken() + val name = if isIdent && !isIdent(nme.on) then ident() else EmptyTermName + if !isIdent(nme.on) then syntaxErrorOrIncomplete("`on` expected") + if isIdent(nme.on) then in.nextToken() val tparams = typeParamClauseOpt(ParamOwner.Def) val extParams = paramClause(0, prefix = true) val givenParamss = paramClauses(givenOnly = true) diff --git a/docs/docs/reference/contextual/extension-methods-new.md b/docs/docs/reference/contextual/extension-methods-new.md index d92c96cc1085..bdca8071881c 100644 --- a/docs/docs/reference/contextual/extension-methods-new.md +++ b/docs/docs/reference/contextual/extension-methods-new.md @@ -128,19 +128,19 @@ A collective extension defines one or more concrete methods that have the same t and prefix parameter. Examples: ```scala -extension stringOps of (xs: Seq[String]) { +extension stringOps on (xs: Seq[String]) { def longestStrings: Seq[String] = { val maxLength = xs.map(_.length).max xs.filter(_.length == maxLength) } } -extension listOps of [T](xs: List[T]) { +extension listOps on [T](xs: List[T]) { def second = xs.tail.head def third: T = xs.tail.tail.head } -extension of [T](xs: List[T])(given Ordering[T]) { +extension on [T](xs: List[T])(given Ordering[T]) { def largest(n: Int) = xs.sorted.takeRight(n) } ``` @@ -164,18 +164,19 @@ given extension_largest_List_T: AnyRef { } ``` -`extension` and `of` are soft keywords. They can also be used as a regular identifiers. +`extension` and `on` are soft keywords. They can also be used as a regular identifiers. ### Syntax Here are the syntax changes for extension methods and given extensions relative -to the [current syntax](../../internals/syntax.md). `extension` is a soft keyword, recognized only after a `given`. It can be used as an identifier everywhere else. +to the [current syntax](../../internals/syntax.md). `extension` is a soft keyword, recognized only in tandem with `on`. It can be used as an identifier everywhere else. + ``` DefSig ::= ... | ExtParamClause [nl] [‘.’] id DefParamClauses ExtParamClause ::= [DefTypeParamClause] ‘(’ DefParam ‘)’ TmplDef ::= ... | ‘extension’ ExtensionDef -ExtensionDef ::= [id] ‘of’ ExtParamClause {GivenParamClause} ExtMethods +ExtensionDef ::= [id] ‘on’ ExtParamClause {GivenParamClause} ExtMethods ExtMethods ::= ‘{’ ‘def’ DefDef {semi ‘def’ DefDef} ‘}’ ``` diff --git a/docs/docs/reference/contextual/relationship-implicits.md b/docs/docs/reference/contextual/relationship-implicits.md index 580159c17422..0d6f39eefdad 100644 --- a/docs/docs/reference/contextual/relationship-implicits.md +++ b/docs/docs/reference/contextual/relationship-implicits.md @@ -72,7 +72,7 @@ Anonymous collective extensions also get compiler synthesized names, which are f For example, the extension ```scala -extension of [T] (xs: List[T]) { +extension on [T] (xs: List[T]) { def second = ... } ``` diff --git a/tests/neg/extension-methods.scala b/tests/neg/extension-methods.scala index 6ca874b70bb8..8396070e3282 100644 --- a/tests/neg/extension-methods.scala +++ b/tests/neg/extension-methods.scala @@ -10,7 +10,7 @@ object Test { "".l2 // error 1.l1 // error - extension of [T](xs: List[T]) { + extension on [T](xs: List[T]) { def (x: Int).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 diff --git a/tests/neg/extmethod-overload.scala b/tests/neg/extmethod-overload.scala index 0e48071e8993..33fafb70ddd1 100644 --- a/tests/neg/extmethod-overload.scala +++ b/tests/neg/extmethod-overload.scala @@ -1,9 +1,9 @@ object Test { - extension a of (x: Int) { + extension a on (x: Int) { def |+| (y: Int) = x + y } - extension b of (x: Int) { + extension b on (x: Int) { def |+| (y: String) = x + y.length } assert((1 |+| 2) == 3) // error ambiguous diff --git a/tests/neg/i5455.scala b/tests/neg/i5455.scala index e2c06faaaee3..e496cd68541f 100644 --- a/tests/neg/i5455.scala +++ b/tests/neg/i5455.scala @@ -11,7 +11,7 @@ object Library { def toInt(n: Nat): Int = n } - extension of (x: Nat) { + extension on (x: Nat) { def * (y: Nat): Nat = x * y def toInt: Int = x } diff --git a/tests/neg/i6801.scala b/tests/neg/i6801.scala index 24bb4e3268ec..0793f0757ad4 100644 --- a/tests/neg/i6801.scala +++ b/tests/neg/i6801.scala @@ -1,4 +1,4 @@ -extension myNumericOps of [T](x: T) { +extension myNumericOps on [T](x: T) { def + (y: T)(given n: Numeric[T]): T = n.plus(x,y) } def foo[T: Numeric](x: T) = 1f + x // error: no implicit argument of type Numeric[Any] diff --git a/tests/neg/i6900.scala b/tests/neg/i6900.scala index a680d074853d..b021af2a9d7e 100644 --- a/tests/neg/i6900.scala +++ b/tests/neg/i6900.scala @@ -1,7 +1,7 @@ object Test2 { // Works with extension method - extension of [A](a: A) with + extension on [A](a: A) with def foo[C]: C => A = _ => a // error: extension method cannot have type parameters 1.foo.foo diff --git a/tests/neg/i7529.scala b/tests/neg/i7529.scala index 64c19a42b6da..c42fa39c43cc 100644 --- a/tests/neg/i7529.scala +++ b/tests/neg/i7529.scala @@ -1,4 +1,4 @@ -extension fooOps of [A](a: A) with +extension fooOps on [A](a: A) with @nonsense // error: not found: nonsense def foo = ??? \ No newline at end of file diff --git a/tests/neg/missing-implicit1.scala b/tests/neg/missing-implicit1.scala index 239771dd42b2..19b6728c10c1 100644 --- a/tests/neg/missing-implicit1.scala +++ b/tests/neg/missing-implicit1.scala @@ -7,7 +7,7 @@ object testObjectInstance with object instances { given zipOption: Zip[Option] = ??? given traverseList: Traverse[List] = ??? - extension listExtension of [T](xs: List[T]) with + extension listExtension on [T](xs: List[T]) with def second: T = xs.tail.head def [T](xs: List[T]) first: T = xs.head } diff --git a/tests/pos/i6900.scala b/tests/pos/i6900.scala index 48cd4b4d6bad..71a3c0e3c1ac 100644 --- a/tests/pos/i6900.scala +++ b/tests/pos/i6900.scala @@ -21,7 +21,7 @@ object Test1 { object Test2 { // Works with extension method - extension of [A, C](a: A) with + extension on [A, C](a: A) with def foo: C => A = _ => a 1.foo.foo diff --git a/tests/pos/i7084.scala b/tests/pos/i7084.scala index 99bff45eda84..bb6ddb96428e 100644 --- a/tests/pos/i7084.scala +++ b/tests/pos/i7084.scala @@ -2,7 +2,7 @@ object Test { type Foo - extension of (y: Any) { + extension on (y: Any) { def g(given Foo): Any = ??? } diff --git a/tests/pos/i7087.scala b/tests/pos/i7087.scala index 21505d5ee587..8550ffd3e1aa 100644 --- a/tests/pos/i7087.scala +++ b/tests/pos/i7087.scala @@ -6,7 +6,7 @@ type F[T] = T match { case G[a] => String } -extension of [T](tup: T) { +extension on [T](tup: T) { def g(given Foo: F[T]) = ??? } diff --git a/tests/pos/implicit-scope.scala b/tests/pos/implicit-scope.scala index 25ba05fbc46a..5e013de27cda 100644 --- a/tests/pos/implicit-scope.scala +++ b/tests/pos/implicit-scope.scala @@ -9,7 +9,7 @@ object A { type FlagSet = opaques.FlagSet def FlagSet(bits: Long): FlagSet = opaques.FlagSet(bits) - extension of (xs: FlagSet) { + extension on (xs: FlagSet) { def bits: Long = opaques.toBits(xs) def | (ys: FlagSet): FlagSet = FlagSet(xs.bits | ys.bits) } diff --git a/tests/pos/matrixOps.scala b/tests/pos/matrixOps.scala index baf4671ae2aa..4d3d0891c604 100644 --- a/tests/pos/matrixOps.scala +++ b/tests/pos/matrixOps.scala @@ -3,7 +3,7 @@ object Test with type Matrix = Array[Array[Double]] type Vector = Array[Double] - extension of (m: Matrix) with + extension on (m: Matrix) with def nRows = m.length def nCols = m(0).length def row(i: Int): Vector = m(i) diff --git a/tests/pos/mirror-implicit-scope.scala b/tests/pos/mirror-implicit-scope.scala index 4b711d7e1f3b..295fbcb61bde 100644 --- a/tests/pos/mirror-implicit-scope.scala +++ b/tests/pos/mirror-implicit-scope.scala @@ -3,14 +3,14 @@ import scala.deriving._ object Test { object K0 { type Generic[T] = Mirror { type Scope = K0.type ; type MirroredType = T ; type MirroredElemTypes } - extension of [T <: Product](gen: Generic[T]) { + extension on [T <: Product](gen: Generic[T]) { inline def toRepr (t: T): gen.MirroredElemTypes = Tuple.fromProduct(t).asInstanceOf } } object K1 { type Generic[F[_]] = Mirror { type Scope = K1.type ; type MirroredType = F ; type MirroredElemTypes[_] } - extension of [F[_] <: Product, T](gen: Generic[F]) { + extension on [F[_] <: Product, T](gen: Generic[F]) { inline def toRepr (t: F[T]): gen.MirroredElemTypes[T] = Tuple.fromProduct(t).asInstanceOf } } diff --git a/tests/pos/reference/delegates.scala b/tests/pos/reference/delegates.scala index 536ac3456a2f..5bafa162bc20 100644 --- a/tests/pos/reference/delegates.scala +++ b/tests/pos/reference/delegates.scala @@ -39,12 +39,12 @@ object Instances extends Common with if (fst != 0) fst else xs1.compareTo(ys1) end listOrd - extension stringOps of (xs: Seq[String]) with + extension stringOps on (xs: Seq[String]) with def longestStrings: Seq[String] = val maxLength = xs.map(_.length).max xs.filter(_.length == maxLength) - extension of [T](xs: List[T]) with + extension on [T](xs: List[T]) with def second = xs.tail.head def third = xs.tail.tail.head @@ -133,7 +133,7 @@ object PostConditions with def result[T](given x: WrappedResult[T]): T = x - extension of [T](x: T) with + extension on [T](x: T) with def ensuring(condition: (given WrappedResult[T]) => Boolean): T = assert(condition(given x)) x @@ -153,12 +153,12 @@ object AnonymousInstances extends Common with val fst = x.compareTo(y) if (fst != 0) fst else xs1.compareTo(ys1) - extension of (xs: Seq[String]) with + extension on (xs: Seq[String]) with def longestStrings: Seq[String] = val maxLength = xs.map(_.length).max xs.filter(_.length == maxLength) - extension of [T](xs: List[T]) with + extension on [T](xs: List[T]) with def second = xs.tail.head given [From, To]: (c: Convertible[From, To]) => Convertible[List[From], List[To]] with diff --git a/tests/pos/reference/extension-methods.scala b/tests/pos/reference/extension-methods.scala index 3e254af31d7b..d70104184d5d 100644 --- a/tests/pos/reference/extension-methods.scala +++ b/tests/pos/reference/extension-methods.scala @@ -41,19 +41,19 @@ object ExtMethods with List(1, 2, 3).second[Int] - extension stringOps of (xs: Seq[String]) { + extension stringOps on (xs: Seq[String]) { def longestStrings: Seq[String] = { val maxLength = xs.map(_.length).max xs.filter(_.length == maxLength) } } - extension listOps of [T](xs: List[T]) with + extension listOps on [T](xs: List[T]) with def second = xs.tail.head def third: T = xs.tail.tail.head - extension of [T](xs: List[T])(given Ordering[T]) with + extension on [T](xs: List[T])(given Ordering[T]) with def largest(n: Int) = xs.sorted.takeRight(n) given stringOps1: AnyRef { diff --git a/tests/pos/reference/opaque.scala b/tests/pos/reference/opaque.scala index b0dbaed6f9cf..69ce019b6aee 100644 --- a/tests/pos/reference/opaque.scala +++ b/tests/pos/reference/opaque.scala @@ -12,7 +12,7 @@ object Logarithms { } // Extension methods define opaque types' public APIs - extension of (x: Logarithm) { + extension on (x: Logarithm) { def toDouble: Double = math.exp(x) def + (y: Logarithm): Logarithm = Logarithm(math.exp(x) + math.exp(y)) def * (y: Logarithm): Logarithm = Logarithm(x + y) diff --git a/tests/pos/tasty-reflect-opaque-api-proto.scala b/tests/pos/tasty-reflect-opaque-api-proto.scala index f0f9f4dfdc41..563a43598e61 100644 --- a/tests/pos/tasty-reflect-opaque-api-proto.scala +++ b/tests/pos/tasty-reflect-opaque-api-proto.scala @@ -10,7 +10,7 @@ class Reflect(val internal: CompilerInterface) { opaque type Term <: Tree = internal.Term object Tree { - extension ops of (tree: Tree) { + extension ops on (tree: Tree) { def show: String = ??? } } diff --git a/tests/run/extension-specificity.scala b/tests/run/extension-specificity.scala index 95a57392b8af..ce62c1244d8f 100644 --- a/tests/run/extension-specificity.scala +++ b/tests/run/extension-specificity.scala @@ -1,10 +1,10 @@ class A class B extends A -extension a of (x: A) with +extension a on (x: A) with def foo: Int = 1 -extension b of (x: B) with +extension b on (x: B) with def foo: Int = 2 @main def Test = diff --git a/tests/run/extmethod-overload.scala b/tests/run/extmethod-overload.scala index 68d49b513028..463cee98cc6e 100644 --- a/tests/run/extmethod-overload.scala +++ b/tests/run/extmethod-overload.scala @@ -97,7 +97,7 @@ object Test extends App { def (x: Int).yy(y: Int) = x + y } - extension of (x: Int) { + extension on (x: Int) { def yy (y: Int) = x - y } diff --git a/tests/run/extmethods2.scala b/tests/run/extmethods2.scala index 1e60a217eb54..a45a4b5abfdd 100644 --- a/tests/run/extmethods2.scala +++ b/tests/run/extmethods2.scala @@ -16,15 +16,15 @@ object Test extends App { test(given TC()) object A { - extension listOps of [T](xs: List[T]) { + extension listOps on [T](xs: List[T]) { def second: T = xs.tail.head def third: T = xs.tail.tail.head def concat(ys: List[T]) = xs ++ ys } - extension polyListOps of [T, U](xs: List[T]) { + extension polyListOps on [T, U](xs: List[T]) { def zipp(ys: List[U]): List[(T, U)] = xs.zip(ys) } - extension of (xs: List[Int]) { + extension on (xs: List[Int]) { def prod = (1 /: xs)(_ * _) } } diff --git a/tests/run/i6902.scala b/tests/run/i6902.scala index b95bda4660cd..18729d1df937 100644 --- a/tests/run/i6902.scala +++ b/tests/run/i6902.scala @@ -1,6 +1,6 @@ object Test { - extension of [A](a: A) { def <<< : A = a } - extension of (b: Int) { def <<<< : Int = b } + extension on [A](a: A) { def <<< : A = a } + extension on (b: Int) { def <<<< : Int = b } def main(args: Array[String]): Unit = { 1.<<< diff --git a/tests/run/instances-anonymous.scala b/tests/run/instances-anonymous.scala index f17e1fc74904..986e81980d90 100644 --- a/tests/run/instances-anonymous.scala +++ b/tests/run/instances-anonymous.scala @@ -8,7 +8,7 @@ object Test extends App { case class Circle(x: Double, y: Double, radius: Double) - extension of (c: Circle) { + extension on (c: Circle) { def circumference: Double = c.radius * math.Pi * 2 } @@ -25,13 +25,13 @@ object Test extends App { val names = List("hi", "hello", "world") assert(names.longestStrings == List("hello", "world")) - extension of [T](xs: Seq[T]) { + extension on [T](xs: Seq[T]) { def second = xs.tail.head } assert(names.longestStrings.second == "world") - extension of [T](xs: List[List[T]]) { + extension on [T](xs: List[List[T]]) { def flattened = xs.foldLeft[List[T]](Nil)(_ ++ _) } diff --git a/tests/run/instances.scala b/tests/run/instances.scala index e1a0e34c64e2..7a197b38496c 100644 --- a/tests/run/instances.scala +++ b/tests/run/instances.scala @@ -8,14 +8,14 @@ object Test extends App { case class Circle(x: Double, y: Double, radius: Double) - extension circleOps of (c: Circle) with + extension circleOps on (c: Circle) with def circumference: Double = c.radius * math.Pi * 2 val circle = new Circle(1, 1, 2.0) assert(circle.circumference == circleOps.circumference(circle)) - extension stringOps of (xs: Seq[String]) with + extension stringOps on (xs: Seq[String]) with def longestStrings: Seq[String] = val maxLength = xs.map(_.length).max xs.filter(_.length == maxLength) @@ -23,12 +23,12 @@ object Test extends App { val names = List("hi", "hello", "world") assert(names.longestStrings == List("hello", "world")) - extension of [T](xs: Seq[T]) with + extension on [T](xs: Seq[T]) with def second = xs.tail.head assert(names.longestStrings.second == "world") - extension listListOps of [T](xs: List[List[T]]) with + extension listListOps on [T](xs: List[List[T]]) with def flattened = xs.foldLeft[List[T]](Nil)(_ ++ _) // A right associative op. Note: can't use given extension for this!