From 29f5ce4ae08e2f72dde0bea0a4937ff426caa699 Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Sun, 27 Oct 2019 10:38:33 +0100 Subject: [PATCH 1/4] Syntax change for type parameters of extension methods The new syntax has the type parameters come first. I.e. ``` def [T](xs: List[T]) append (ys: List[T]): List[T] = ... ``` instead of ``` def (xs: List[T]) append [T] (ys: List[T]): List[T] = ... ``` Application is unchanged, so it is still ``` xs.append[String](ys) ``` An argument for the old syntax is that it aligns definition and call syntax. On the other hand, the new syntax maintains the general rule that parameter introductions always com before parameter uses. The decisive argument to switch is to be consistent with the new collective parameter syntax, where `append` would be written like this: ``` given [T](xs: List[T]) def append (ys: List[T]): List[T] = ... ``` To avoid misalignment of type parameters between definition and call syntax, we considered disallowing explicit type parameters for extension methods altogether, and to require that the method is called as a normal method instead. But that would not work for anonymous givens as in the last exaple above. --- .../src/dotty/tools/dotc/ast/Positioned.scala | 7 +-- compiler/src/dotty/tools/dotc/ast/Trees.scala | 11 ++--- .../dotty/tools/dotc/core/Decorators.scala | 4 +- .../dotty/tools/dotc/parsing/Parsers.scala | 43 +++++++++++-------- docs/docs/internals/syntax.md | 4 +- .../reference/contextual/extension-methods.md | 14 +++--- docs/docs/reference/contextual/typeclasses.md | 10 ++--- library/src-bootstrapped/scala/IArray.scala | 6 +-- .../scala/quoted/package.scala | 2 +- tests/neg/capture1.scala | 6 +-- tests/neg/i7060.scala | 2 +- tests/pos/i6734.scala | 8 ++-- tests/pos/i6847.scala | 4 +- tests/pos/i6900.scala | 2 +- tests/pos/i7041.scala | 2 +- tests/pos/i7087.scala | 2 +- tests/pos/mirror-implicit-scope.scala | 4 +- tests/pos/postconditions.scala | 9 ++-- tests/pos/reference/delegates.scala | 18 ++++---- tests/run/extension-methods.scala | 18 ++++---- tests/run/extmethod-overload.scala | 8 ++-- tests/run/i5606.scala | 2 +- tests/run/instances-anonymous.scala | 14 +++--- tests/run/instances.scala | 16 +++---- tests/run/opaque-immutable-array-xm.scala | 4 +- tests/run/rescue.scala | 4 +- tests/run/tupled-function-andThen.scala | 2 +- tests/run/tupled-function-apply.scala | 2 +- tests/run/tupled-function-compose.scala | 2 +- .../tupled-function-extension-method.scala | 10 ++--- tests/run/tupled-function-tupled.scala | 2 +- tests/run/tupled-function-untupled.scala | 2 +- 32 files changed, 122 insertions(+), 122 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/ast/Positioned.scala b/compiler/src/dotty/tools/dotc/ast/Positioned.scala index 6a9a0ae54c21..7a2c936c9a45 100644 --- a/compiler/src/dotty/tools/dotc/ast/Positioned.scala +++ b/compiler/src/dotty/tools/dotc/ast/Positioned.scala @@ -208,16 +208,13 @@ abstract class Positioned(implicit @constructorOnly src: SourceFile) extends Pro case tree: DefDef if tree.mods.is(Extension) => tree.vparamss match { case vparams1 :: vparams2 :: rest if !isLeftAssoc(tree.name) => - check(vparams2) check(tree.tparams) + check(vparams2) check(vparams1) check(rest) - case vparams1 :: rest => - check(vparams1) - check(tree.tparams) - check(rest) case _ => check(tree.tparams) + check(tree.vparamss) } check(tree.tpt) check(tree.rhs) diff --git a/compiler/src/dotty/tools/dotc/ast/Trees.scala b/compiler/src/dotty/tools/dotc/ast/Trees.scala index e301dcc06970..de33d26d6154 100644 --- a/compiler/src/dotty/tools/dotc/ast/Trees.scala +++ b/compiler/src/dotty/tools/dotc/ast/Trees.scala @@ -754,9 +754,10 @@ object Trees { def unforced: LazyTree[T] = preRhs protected def force(x: Tree[T @uncheckedVariance]): Unit = preRhs = x - override def disableOverlapChecks = rawMods.is(Given) - // disable order checks for implicit aliases since their given clause follows - // their for clause, but the two appear swapped in the DefDef. + override def disableOverlapChecks = rawMods.is(Extension) + // disable order checks for extension methods as long as we parse + // type parameters both before and after the leading parameter section. + // TODO drop this once syntax of type parameters has settled. } /** mods class name template or @@ -789,10 +790,6 @@ object Trees { def parents: List[Tree[T]] = parentsOrDerived // overridden by DerivingTemplate def derived: List[untpd.Tree] = Nil // overridden by DerivingTemplate - - override def disableOverlapChecks = true - // disable overlaps checks since templates of instance definitions have their - // `given` clause come last, which means that the constructor span can contain the parent spans. } diff --git a/compiler/src/dotty/tools/dotc/core/Decorators.scala b/compiler/src/dotty/tools/dotc/core/Decorators.scala index ee63fa5a816f..279ce0d29293 100644 --- a/compiler/src/dotty/tools/dotc/core/Decorators.scala +++ b/compiler/src/dotty/tools/dotc/core/Decorators.scala @@ -170,8 +170,8 @@ object Decorators { } } - implicit object reportDeco { - def (x: T) reporting[T]( + implicit class reportDeco[T](x: T) extends AnyVal { + def reporting( op: (given WrappedResult[T]) => String, printer: config.Printers.Printer = config.Printers.default): T = { printer.println(op(given WrappedResult(x))) diff --git a/compiler/src/dotty/tools/dotc/parsing/Parsers.scala b/compiler/src/dotty/tools/dotc/parsing/Parsers.scala index 6993a7a093c1..11c5642b037a 100644 --- a/compiler/src/dotty/tools/dotc/parsing/Parsers.scala +++ b/compiler/src/dotty/tools/dotc/parsing/Parsers.scala @@ -3088,10 +3088,11 @@ object Parsers { } } - /** DefDef ::= DefSig [(‘:’ | ‘<:’) Type] ‘=’ Expr - * | this ParamClause ParamClauses `=' ConstrExpr - * DefDcl ::= DefSig `:' Type - * DefSig ::= [‘(’ DefParam ‘)’ [nl]] id [DefTypeParamClause] ParamClauses + /** DefDef ::= DefSig [(‘:’ | ‘<:’) Type] ‘=’ Expr + * | this ParamClause ParamClauses `=' ConstrExpr + * DefDcl ::= DefSig `:' Type + * DefSig ::= id [DefTypeParamClause] DefParamClauses + * | ExtParamClause [nl] id DefParamClauses */ def defDefOrDcl(start: Offset, mods: Modifiers): Tree = atSpan(start, nameStart) { def scala2ProcedureSyntax(resultTypeStr: String) = { @@ -3120,27 +3121,35 @@ object Parsers { makeConstructor(Nil, vparamss, rhs).withMods(mods).setComment(in.getDocComment(start)) } else { - val (leadingParamss, flags) = - if (in.token == LPAREN) - try (paramClause(0, prefix = true) :: Nil, Method | Extension) - finally newLineOpt() + def extParamss = try paramClause(0, prefix = true) :: Nil finally newLineOpt() + val (leadingTparams, leadingVparamss, flags) = + if in.token == LBRACKET then + (typeParamClause(ParamOwner.Def), extParamss, Method | Extension) + else if in.token == LPAREN then + (Nil, extParamss, Method | Extension) else - (Nil, Method) + (Nil, Nil, Method) val mods1 = addFlag(mods, flags) val ident = termIdent() val name = ident.name.asTermName - val tparams = typeParamClauseOpt(ParamOwner.Def) - val vparamss = paramClauses() match { - case rparams :: rparamss if leadingParamss.nonEmpty && !isLeftAssoc(ident.name) => - rparams :: leadingParamss ::: rparamss + val tparams = + if in.token == LBRACKET then + if flags.is(Extension) then + if leadingTparams.isEmpty then + deprecationWarning("type parameters in extension methods should be written after `def`") + else + syntaxError("no type parameters allowed here") + typeParamClause(ParamOwner.Def) + else leadingTparams + val vparamss = paramClauses() match + case rparams :: rparamss if leadingVparamss.nonEmpty && !isLeftAssoc(ident.name) => + rparams :: leadingVparamss ::: rparamss case rparamss => - leadingParamss ::: rparamss - } + leadingVparamss ::: rparamss var tpt = fromWithinReturnType { - if (in.token == SUBTYPE && mods.is(Inline)) { + if in.token == SUBTYPE && mods.is(Inline) then in.nextToken() TypeBoundsTree(EmptyTree, toplevelTyp()) - } else typedOpt() } if (in.isScala2Mode) newLineOptWhenFollowedBy(LBRACE) diff --git a/docs/docs/internals/syntax.md b/docs/docs/internals/syntax.md index 6701fc6b3660..6fcfdb727cf5 100644 --- a/docs/docs/internals/syntax.md +++ b/docs/docs/internals/syntax.md @@ -357,8 +357,8 @@ Dcl ::= RefineDcl ValDcl ::= ids ‘:’ Type PatDef(_, ids, tpe, EmptyTree) VarDcl ::= ids ‘:’ Type PatDef(_, ids, tpe, EmptyTree) DefDcl ::= DefSig ‘:’ Type DefDef(_, name, tparams, vparamss, tpe, EmptyTree) -DefSig ::= [‘(’ DefParam ‘)’ [nl]] id - [DefTypeParamClause] DefParamClauses +DefSig ::= id [DefTypeParamClause] 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 646914287a01..b2f7e82f9769 100644 --- a/docs/docs/reference/contextual/extension-methods.md +++ b/docs/docs/reference/contextual/extension-methods.md @@ -94,7 +94,7 @@ given stringOps: { } given { - def (xs: List[T]) second[T] = xs.tail.head + def [T](xs: List[T]) second = xs.tail.head } ``` If such given instances are anonymous (as in the second clause), their name is synthesized from the name of the first defined extension method. @@ -106,8 +106,8 @@ as well as any type parameters of these extension methods into the given instanc For instance, here is a given instance with two extension methods. ```scala given listOps: { - def (xs: List[T]) second[T]: T = xs.tail.head - def (xs: List[T]) third[T]: T = xs.tail.tail.head + def [T](xs: List[T]) second: T = xs.tail.head + def [T](xs: List[T]) third: T = xs.tail.tail.head } ``` The repetition in the parameters can be avoided by hoisting the parameters up into the given instance itself. The following version is a shorthand for the code above. @@ -151,13 +151,13 @@ to the implementation of right binding operators as normal methods. 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 (xs: List[T]) second [T] = +def [T](xs: List[T]) second = xs.tail.head -def (xs: List[List[T]]) flattened [T] = +def [T](xs: List[List[T]]) flattened = xs.foldLeft[List[T]](Nil)(_ ++ _) -def (x: T) + [T : Numeric](y: T): T = +def [T: Numeric](x: T) + (y: T): T = summon[Numeric[T]].plus(x, y) ``` @@ -170,7 +170,7 @@ The required syntax extension just adds one clause for extension methods relativ to the [current syntax](../../internals/syntax.md). ``` DefSig ::= ... - | ‘(’ DefParam ‘)’ [nl] id [DefTypeParamClause] DefParamClauses + | ExtParamClause [nl] id DefParamClauses GivenDef ::= ... [GivenSig ‘:’] [ExtParamClause] TemplateBody ExtParamClause ::= [DefTypeParamClause] ‘(’ DefParam ‘)’ {GivenParamClause} diff --git a/docs/docs/reference/contextual/typeclasses.md b/docs/docs/reference/contextual/typeclasses.md index a605bb72fd56..11c042a49c8f 100644 --- a/docs/docs/reference/contextual/typeclasses.md +++ b/docs/docs/reference/contextual/typeclasses.md @@ -38,25 +38,25 @@ def sum[T: Monoid](xs: List[T]): T = ```scala trait Functor[F[_]] { - def (x: F[A]) map [A, B] (f: A => B): F[B] + def [A, B](x: F[A]) map (f: A => B): F[B] } trait Monad[F[_]] extends Functor[F] { - def (x: F[A]) flatMap [A, B] (f: A => F[B]): F[B] - def (x: F[A]) map [A, B] (f: A => B) = x.flatMap(f `andThen` pure) + def [A, B](x: F[A]) flatMap (f: A => F[B]): F[B] + def [A, B](x: F[A]) map (f: A => B) = x.flatMap(f `andThen` pure) def pure[A](x: A): F[A] } given listMonad: Monad[List] { - def (xs: List[A]) flatMap [A, B] (f: A => List[B]): List[B] = + def [A, B](xs: List[A]) flatMap (f: A => List[B]): List[B] = xs.flatMap(f) def pure[A](x: A): List[A] = List(x) } given readerMonad[Ctx]: Monad[[X] =>> Ctx => X] { - def (r: Ctx => A) flatMap [A, B] (f: A => Ctx => B): Ctx => B = + def [A, B](r: Ctx => A) flatMap (f: A => Ctx => B): Ctx => B = ctx => f(r(ctx))(ctx) def pure[A](x: A): Ctx => A = ctx => x diff --git a/library/src-bootstrapped/scala/IArray.scala b/library/src-bootstrapped/scala/IArray.scala index 47aa3739a7f9..3ab5cdfcdc5f 100644 --- a/library/src-bootstrapped/scala/IArray.scala +++ b/library/src-bootstrapped/scala/IArray.scala @@ -23,8 +23,8 @@ object opaques { 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[T]) apply[T <: Object] (n: Int): T = arr.asInstanceOf[Array[T]].apply(n) - def (arr: IArray[T]) apply[T] (n: Int): T = arr.asInstanceOf[Array[T]].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 @@ -37,7 +37,7 @@ object opaques { 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[T]) length[T] : Int = arr.asInstanceOf[Array[T]].length + def [T](arr: IArray[T]) length: Int = arr.asInstanceOf[Array[T]].length } } type IArray[+T] = opaques.IArray[T] diff --git a/library/src-bootstrapped/scala/quoted/package.scala b/library/src-bootstrapped/scala/quoted/package.scala index 720fbdb59ebe..bdd92f395114 100644 --- a/library/src-bootstrapped/scala/quoted/package.scala +++ b/library/src-bootstrapped/scala/quoted/package.scala @@ -4,6 +4,6 @@ package object quoted { implicit object ExprOps { @deprecated("Use scala.quoted.Expr.apply instead", "0.19.0") - def (x: T) toExpr[T: Liftable](given QuoteContext): Expr[T] = Expr(x) + def [T: Liftable](x: T) toExpr (given QuoteContext): Expr[T] = Expr(x) } } diff --git a/tests/neg/capture1.scala b/tests/neg/capture1.scala index 67ae41d5aea4..7b166bf5af1d 100644 --- a/tests/neg/capture1.scala +++ b/tests/neg/capture1.scala @@ -4,11 +4,11 @@ object Test extends App { val l: mutable.Seq[String] = mutable.ArrayBuffer() - def (xs: List[T]) emap[T, U] (f: T => U): List[U] = xs.map(f) + def [T, U](xs: List[T]) emap (f: T => U): List[U] = xs.map(f) - def (xs: List[T]) ereduce[T] (f: (T, T) => T): T = xs.reduceLeft(f) + def [T](xs: List[T]) ereduce (f: (T, T) => T): T = xs.reduceLeft(f) - def (xs: mutable.Seq[T]) append[T] (ys: mutable.Seq[T]): mutable.Seq[T] = xs ++ ys + def [T](xs: mutable.Seq[T]) append (ys: mutable.Seq[T]): mutable.Seq[T] = xs ++ ys List(l, mutable.ArrayBuffer(1)) .emap(list => list) diff --git a/tests/neg/i7060.scala b/tests/neg/i7060.scala index 06ec377d652c..5ab6d0f4905e 100644 --- a/tests/neg/i7060.scala +++ b/tests/neg/i7060.scala @@ -12,7 +12,7 @@ object PostConditions { def res[T](given b: Box[T]): T = b.t - def (e: T) ensure[T](cond: (given Box[T]) => Boolean): T = { + def [T](e: T) ensure (cond: (given Box[T]) => Boolean): T = { if (cond(given Box(e))) e else throw new AssertionError("condition not fulfilled") } diff --git a/tests/pos/i6734.scala b/tests/pos/i6734.scala index c796a867bb92..d7ebc726ae79 100644 --- a/tests/pos/i6734.scala +++ b/tests/pos/i6734.scala @@ -1,10 +1,10 @@ object Bug { - def (ab: (A, B)) pipe2[A, B, Z](f: (A, B) => Z): Z = f(ab._1, ab._2) + def [A, B, Z](ab: (A, B)) pipe2(f: (A, B) => Z): Z = f(ab._1, ab._2) - def (a: A) leftErr[A, B](b: B): A = (a, b).pipe2((a, b) => a) //Did not compile before. - def (a: A) leftOk1[A, B](b: B): A = Tuple2(a, b).pipe2((a, b) => a) //Compiles - def (a: A) leftOk2[A, B](b: B): A = { + def [A, B](a: A) leftErr(b: B): A = (a, b).pipe2((a, b) => a) //Did not compile before. + def [A, B](a: A) leftOk1(b: B): A = Tuple2(a, b).pipe2((a, b) => a) //Compiles + def [A, B](a: A) leftOk2(b: B): A = { val t = (a, b) t.pipe2((a, b) => a) //Compiles } diff --git a/tests/pos/i6847.scala b/tests/pos/i6847.scala index 518da6f429d4..3908a38b9930 100644 --- a/tests/pos/i6847.scala +++ b/tests/pos/i6847.scala @@ -1,11 +1,11 @@ trait Syntax[F[_]] { - def (a: A) ret[A]: F[A] + def [A](a: A) ret: F[A] } trait Instance[A] implicit val instanceSyntax: Syntax[Instance] = new Syntax[Instance] { - def (a: A) ret[A]: Instance[A] = new Instance[A] {} + def [A](a: A) ret: Instance[A] = new Instance[A] {} } object Instance { diff --git a/tests/pos/i6900.scala b/tests/pos/i6900.scala index 7b92fd23834f..63e01e3550ed 100644 --- a/tests/pos/i6900.scala +++ b/tests/pos/i6900.scala @@ -1,5 +1,5 @@ object Test { - given bla[A]: { def (a: A) foo[C]: C => A = _ => a } + given bla[A]: { def [C](a: A) foo: C => A = _ => a } 1.foo.foo 1.foo.foo[String] diff --git a/tests/pos/i7041.scala b/tests/pos/i7041.scala index 7c0eba57e49c..e7a77576374c 100644 --- a/tests/pos/i7041.scala +++ b/tests/pos/i7041.scala @@ -1,6 +1,6 @@ import scala.util.control.NonLocalReturns._ -inline def (op: => T) rescue[T, E <: Throwable] (fallback: PartialFunction[E, T]) = +inline def [T, E <: Throwable](op: => T) rescue (fallback: PartialFunction[E, T]) = try op catch { case ex: ReturnThrowable[_] => throw ex diff --git a/tests/pos/i7087.scala b/tests/pos/i7087.scala index af8a8b42f2ff..03e939ef7efd 100644 --- a/tests/pos/i7087.scala +++ b/tests/pos/i7087.scala @@ -7,7 +7,7 @@ type F[T] = T match { } given { - def (tup: T) g[T](given Foo: F[T]) = ??? + def [T](tup: T) g (given Foo: F[T]) = ??? } def f(x: G[Int])(given Foo: String) = x.g \ No newline at end of file diff --git a/tests/pos/mirror-implicit-scope.scala b/tests/pos/mirror-implicit-scope.scala index 895162bb3c49..c06a6d21d604 100644 --- a/tests/pos/mirror-implicit-scope.scala +++ b/tests/pos/mirror-implicit-scope.scala @@ -4,14 +4,14 @@ object Test { object K0 { type Generic[T] = Mirror { type Scope = K0.type ; type MirroredType = T ; type MirroredElemTypes } given { - inline def (gen: Generic[T]) toRepr[T <: Product](t: T): gen.MirroredElemTypes = Tuple.fromProduct(t).asInstanceOf + inline def [T <: Product](gen: Generic[T]) 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 { - inline def (gen: Generic[F]) toRepr[F[_] <: Product, T](t: F[T]): gen.MirroredElemTypes[T] = Tuple.fromProduct(t).asInstanceOf + inline def [F[_] <: Product, T](gen: Generic[F]) toRepr (t: F[T]): gen.MirroredElemTypes[T] = Tuple.fromProduct(t).asInstanceOf } } diff --git a/tests/pos/postconditions.scala b/tests/pos/postconditions.scala index 3ea7b0700063..bb80d9b8f6c9 100644 --- a/tests/pos/postconditions.scala +++ b/tests/pos/postconditions.scala @@ -1,16 +1,13 @@ -object PostConditions { +object PostConditions with opaque type WrappedResult[T] = T def result[T](given r: WrappedResult[T]): T = r - def (x: T) ensuring [T](condition: (given WrappedResult[T]) => Boolean): T = { + def [T](x: T) ensuring (condition: (given WrappedResult[T]) => Boolean): T = given WrappedResult[T] = x assert(condition) x - } -} -object Test { +object Test with import PostConditions.{ensuring, result} val s = List(1, 2, 3).sum.ensuring(result == 6) -} \ No newline at end of file diff --git a/tests/pos/reference/delegates.scala b/tests/pos/reference/delegates.scala index fbdb1e88be5d..2931ed4916c3 100644 --- a/tests/pos/reference/delegates.scala +++ b/tests/pos/reference/delegates.scala @@ -15,11 +15,11 @@ class Common with def unit: T trait Functor[F[_]] with - def (x: F[A]) map[A, B] (f: A => B): F[B] + def [A, B](x: F[A]) map (f: A => B): F[B] trait Monad[F[_]] extends Functor[F] with - def (x: F[A]) flatMap[A, B] (f: A => F[B]): F[B] - def (x: F[A]) map[A, B] (f: A => B) = x.flatMap(f `andThen` pure) + def [A, B](x: F[A]) flatMap (f: A => F[B]): F[B] + def [A, B](x: F[A]) map (f: A => B) = x.flatMap(f `andThen` pure) def pure[A](x: A): F[A] @@ -49,13 +49,13 @@ object Instances extends Common with def third = xs.tail.tail.head given listMonad: Monad[List] with - def (xs: List[A]) flatMap[A, B] (f: A => List[B]): List[B] = + def [A, B](xs: List[A]) flatMap (f: A => List[B]): List[B] = xs.flatMap(f) def pure[A](x: A): List[A] = List(x) given readerMonad[Ctx]: Monad[[X] =>> Ctx => X] with - def (r: Ctx => A) flatMap[A, B] (f: A => Ctx => B): Ctx => B = + def [A, B](r: Ctx => A) flatMap (f: A => Ctx => B): Ctx => B = ctx => f(r(ctx))(ctx) def pure[A](x: A): Ctx => A = ctx => x @@ -153,13 +153,13 @@ object AnonymousInstances extends Common with val fst = x.compareTo(y) if (fst != 0) fst else xs1.compareTo(ys1) - given with - def (xs: Seq[String]) longestStrings: Seq[String] = + given (xs: Seq[String]) + def longestStrings: Seq[String] = val maxLength = xs.map(_.length).max xs.filter(_.length == maxLength) - given with - def (xs: List[T]) second[T] = xs.tail.head + given [T](xs: List[T]) + def second = xs.tail.head given [From, To](given c: Convertible[From, To]) : Convertible[List[From], List[To]] with def (x: List[From]) convert: List[To] = x.map(c.convert) diff --git a/tests/run/extension-methods.scala b/tests/run/extension-methods.scala index 16001afc1f71..538b7bd7dffa 100644 --- a/tests/run/extension-methods.scala +++ b/tests/run/extension-methods.scala @@ -19,11 +19,11 @@ object Test extends App { val names = List("hi", "hello", "world") assert(names.longestStrings == List("hello", "world")) - def (xs: Seq[T]) second[T] = xs.tail.head + def [T](xs: Seq[T]) second = xs.tail.head assert(names.longestStrings.second == "world") - def (xs: List[List[T]]) flattened[T] = xs.foldLeft[List[T]](Nil)(_ ++ _) + def [T](xs: List[List[T]]) flattened = xs.foldLeft[List[T]](Nil)(_ ++ _) assert(List(names, List("!")).flattened == names :+ "!") assert(Nil.flattened == Nil) @@ -84,28 +84,28 @@ object Test extends App { println(max(List(1, 2, 3), List(2))) trait Functor[F[_]] { - def (x: F[A]) map [A, B](f: A => B): F[B] + def [A, B](x: F[A]) map (f: A => B): F[B] } trait Monad[F[_]] extends Functor[F] { - def (x: F[A]) - flatMap [A, B](f: A => F[B]): F[B] + def [A, B](x: F[A]) + flatMap (f: A => F[B]): F[B] - def (x: F[A]) - map [A, B](f: A => B) = x.flatMap(f `andThen` pure) + def [A, B](x: F[A]) + map (f: A => B) = x.flatMap(f `andThen` pure) def pure[A](x: A): F[A] } implicit object ListMonad extends Monad[List] { - def (xs: List[A]) flatMap [A, B](f: A => List[B]): List[B] = + def [A, B](xs: List[A]) flatMap (f: A => List[B]): List[B] = xs.flatMap(f) def pure[A](x: A): List[A] = List(x) } class ReaderMonad[Ctx] extends Monad[[X] =>> Ctx => X] { - def (r: Ctx => A) flatMap [A, B](f: A => Ctx => B): Ctx => B = + def [A, B](r: Ctx => A) flatMap (f: A => Ctx => B): Ctx => B = ctx => f(r(ctx))(ctx) def pure[A](x: A): Ctx => A = ctx => x diff --git a/tests/run/extmethod-overload.scala b/tests/run/extmethod-overload.scala index 9bd4dd555090..dd0fe7352eb7 100644 --- a/tests/run/extmethod-overload.scala +++ b/tests/run/extmethod-overload.scala @@ -26,8 +26,8 @@ object Test extends App { def (x: Int) |+| (y: Int) = x + y def (x: Int) |+| (y: String) = x + y.length - def (xs: List[T]) +++ [T] (ys: List[T]): List[T] = xs ++ ys ++ ys - def (xs: List[T]) +++ [T] (ys: Iterator[T]): List[T] = xs ++ ys ++ ys + def [T](xs: List[T]) +++ (ys: List[T]): List[T] = xs ++ ys ++ ys + def [T](xs: List[T]) +++ (ys: Iterator[T]): List[T] = xs ++ ys ++ ys } assert((1 |+| 2) == 3) @@ -58,8 +58,8 @@ object Test extends App { def (x: Int) |+| (y: Int) = x + y def (x: Int) |+| (y: String) = x + y.length - def (xs: List[T]) +++ [T] (ys: List[T]): List[T] = xs ++ ys ++ ys - def (xs: List[T]) +++ [T] (ys: Iterator[T]): List[T] = xs ++ ys ++ ys + def [T](xs: List[T]) +++ (ys: List[T]): List[T] = xs ++ ys ++ ys + def [T](xs: List[T]) +++ (ys: Iterator[T]): List[T] = xs ++ ys ++ ys } given Bar : Foo diff --git a/tests/run/i5606.scala b/tests/run/i5606.scala index a7b9fc340b60..f84c752e9b90 100644 --- a/tests/run/i5606.scala +++ b/tests/run/i5606.scala @@ -1,6 +1,6 @@ object Test extends App { - def (f: A => B) `$`[A, B](a: A): B = f(a) + def [A, B](f: A => B) `$` (a: A): B = f(a) assert((((a: Int) => a.toString()) `$` 10) == "10") diff --git a/tests/run/instances-anonymous.scala b/tests/run/instances-anonymous.scala index ab3ff4d05a7f..4e7d375df890 100644 --- a/tests/run/instances-anonymous.scala +++ b/tests/run/instances-anonymous.scala @@ -26,13 +26,13 @@ object Test extends App { assert(names.longestStrings == List("hello", "world")) given { - def (xs: Seq[T]) second[T] = xs.tail.head + def [T](xs: Seq[T]) second = xs.tail.head } assert(names.longestStrings.second == "world") given { - def (xs: List[List[T]]) flattened[T] = xs.foldLeft[List[T]](Nil)(_ ++ _) + def [T](xs: List[List[T]]) flattened = xs.foldLeft[List[T]](Nil)(_ ++ _) } assert(List(names, List("!")).flattened == names :+ "!") @@ -91,25 +91,25 @@ object Test extends App { println(max(List(1, 2, 3), List(2))) trait Functor[F[_]] { - def (x: F[A]) map[A, B] (f: A => B): F[B] + def [A, B](x: F[A]) map (f: A => B): F[B] } trait Monad[F[_]] extends Functor[F] { - def (x: F[A]) flatMap[A, B] (f: A => F[B]): F[B] - def (x: F[A]) map[A, B] (f: A => B) = x.flatMap(f `andThen` pure) + def [A, B](x: F[A]) flatMap (f: A => F[B]): F[B] + def [A, B](x: F[A]) map (f: A => B) = x.flatMap(f `andThen` pure) def pure[A](x: A): F[A] } given Monad[List] { - def (xs: List[A]) flatMap[A, B] (f: A => List[B]): List[B] = + def [A, B](xs: List[A]) flatMap (f: A => List[B]): List[B] = xs.flatMap(f) def pure[A](x: A): List[A] = List(x) } given [Ctx] : Monad[[X] =>> Ctx => X] { - def (r: Ctx => A) flatMap[A, B] (f: A => Ctx => B): Ctx => B = + def [A, B](r: Ctx => A) flatMap (f: A => Ctx => B): Ctx => B = ctx => f(r(ctx))(ctx) def pure[A](x: A): Ctx => A = ctx => x diff --git a/tests/run/instances.scala b/tests/run/instances.scala index fd1e4127aa61..482d7dc3ef24 100644 --- a/tests/run/instances.scala +++ b/tests/run/instances.scala @@ -26,18 +26,18 @@ object Test extends App { assert(names.longestStrings == List("hello", "world")) given seqOps: { - def (xs: Seq[T]) second[T] = xs.tail.head + def [T](xs: Seq[T]) second = xs.tail.head } assert(names.longestStrings.second == "world") given listListOps: { - def (xs: List[List[T]]) flattened[T] = xs.foldLeft[List[T]](Nil)(_ ++ _) + def [T](xs: List[List[T]]) flattened = xs.foldLeft[List[T]](Nil)(_ ++ _) } // A right associative op given prepend: { - def (x: T) ::[T] (xs: Seq[T]) = x +: xs + def [T](x: T) :: (xs: Seq[T]) = x +: xs } val ss: Seq[Int] = List(1, 2, 3) val ss1 = 0 :: ss @@ -99,25 +99,25 @@ object Test extends App { println(max(List(1, 2, 3), List(2))) trait Functor[F[_]] { - def (x: F[A]) map[A, B] (f: A => B): F[B] + def [A, B](x: F[A]) map (f: A => B): F[B] } trait Monad[F[_]] extends Functor[F] { - def (x: F[A]) flatMap[A, B] (f: A => F[B]): F[B] - def (x: F[A]) map[A, B] (f: A => B) = x.flatMap(f `andThen` pure) + def [A, B](x: F[A]) flatMap (f: A => F[B]): F[B] + def [A, B](x: F[A]) map (f: A => B) = x.flatMap(f `andThen` pure) def pure[A](x: A): F[A] } given ListMonad : Monad[List] { - def (xs: List[A]) flatMap[A, B] (f: A => List[B]): List[B] = + def [A, B](xs: List[A]) flatMap (f: A => List[B]): List[B] = xs.flatMap(f) def pure[A](x: A): List[A] = List(x) } given ReaderMonad[Ctx] : Monad[[X] =>> Ctx => X] { - def (r: Ctx => A) flatMap[A, B] (f: A => Ctx => B): Ctx => B = + def [A, B](r: Ctx => A) flatMap (f: A => Ctx => B): Ctx => B = ctx => f(r(ctx))(ctx) def pure[A](x: A): Ctx => A = ctx => x diff --git a/tests/run/opaque-immutable-array-xm.scala b/tests/run/opaque-immutable-array-xm.scala index 0052c7330d06..4f90a982c9d1 100644 --- a/tests/run/opaque-immutable-array-xm.scala +++ b/tests/run/opaque-immutable-array-xm.scala @@ -11,8 +11,8 @@ object Test extends App { // These should be inline but that does not work currently. Try again // once inliner is moved to ReifyQuotes - def (ia: IArray[A]) length[A]: Int = (ia: Array[A]).length - def (ia: IArray[A]) apply[A] (i: Int): A = (ia: Array[A])(i) + def [A](ia: IArray[A]) length: Int = (ia: Array[A]).length + def [A](ia: IArray[A]) apply (i: Int): A = (ia: Array[A])(i) // return a sorted copy of the array def sorted[A <: AnyRef : math.Ordering](ia: IArray[A]): IArray[A] = { diff --git a/tests/run/rescue.scala b/tests/run/rescue.scala index 22e7f3da2b32..5ae9a78e63ee 100644 --- a/tests/run/rescue.scala +++ b/tests/run/rescue.scala @@ -2,13 +2,13 @@ import scala.util.control.NonFatal import scala.util.control.NonLocalReturns._ object lib { - inline def (op: => T) rescue[T] (fallback: => T) = + inline def [T](op: => T) rescue (fallback: => T) = try op catch { case NonFatal(_) => fallback // ReturnThrowable is fatal error, thus ignored } - inline def (op: => T) rescue[T, E <: Throwable] (fallback: PartialFunction[E, T]) = + inline def [T, E <: Throwable](op: => T) rescue (fallback: PartialFunction[E, T]) = try op catch { // case ex: ReturnThrowable[_] => throw ex // bug #7041 diff --git a/tests/run/tupled-function-andThen.scala b/tests/run/tupled-function-andThen.scala index 46369dce5297..678ced420d9e 100644 --- a/tests/run/tupled-function-andThen.scala +++ b/tests/run/tupled-function-andThen.scala @@ -32,7 +32,7 @@ object Test { * @tparam GArgs the tuple type with the same types as the function arguments of G and return type of F * @tparam R the return type of G */ - def (f: F) andThen[F, G, FArgs <: Tuple, GArgs <: Tuple, R](g: G)(given tf: TupledFunction[F, FArgs => GArgs], tg: TupledFunction[G, GArgs => R]): FArgs => R = { + def [F, G, FArgs <: Tuple, GArgs <: Tuple, R](f: F) andThen (g: G)(given tf: TupledFunction[F, FArgs => GArgs], tg: TupledFunction[G, GArgs => R]): FArgs => R = { x => tg.tupled(g)(tf.tupled(f)(x)) } diff --git a/tests/run/tupled-function-apply.scala b/tests/run/tupled-function-apply.scala index 294d79ed4031..77798a49980d 100644 --- a/tests/run/tupled-function-apply.scala +++ b/tests/run/tupled-function-apply.scala @@ -113,6 +113,6 @@ object Test { * @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) apply[F, Args <: Tuple, R](args: Args)(given tf: TupledFunction[F, Args => R]): R = + def [F, Args <: Tuple, R](f: F) apply (args: Args)(given tf: TupledFunction[F, Args => R]): R = tf.tupled(f)(args) } \ No newline at end of file diff --git a/tests/run/tupled-function-compose.scala b/tests/run/tupled-function-compose.scala index 672718f20f6b..890659f31495 100644 --- a/tests/run/tupled-function-compose.scala +++ b/tests/run/tupled-function-compose.scala @@ -33,7 +33,7 @@ object Test { * @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 => tf.tupled(f)(tg.tupled(g)(x)) } diff --git a/tests/run/tupled-function-extension-method.scala b/tests/run/tupled-function-extension-method.scala index fef740d22b3d..f5c01f401844 100644 --- a/tests/run/tupled-function-extension-method.scala +++ b/tests/run/tupled-function-extension-method.scala @@ -34,14 +34,14 @@ object Test { class Expr[T](val x: T) // Specialized only for arity 0 and one as auto tupling will not provide the disired effect - def (e: Expr[() => R]) apply[R](): R = e.x() - def (e: Expr[Arg => R]) apply[Arg, R](arg: Arg): R = e.x(arg) - def (e: Expr[(given Arg) => R]) applyGiven[Arg, R](arg: Arg): R = e.x(given arg) + def [R](e: Expr[() => R]) apply (): R = e.x() + def [Arg, R](e: Expr[Arg => R]) apply (arg: Arg): R = e.x(arg) + def [Arg, R](e: Expr[(given Arg) => R]) applyGiven(arg: Arg): R = e.x(given arg) // Applied to all funtions of arity 2 or more (including more than 22 parameters) - def (e: Expr[F]) apply[F, Args <: Tuple, R](args: Args)(given tf: TupledFunction[F, Args => R]): R = + def [F, Args <: Tuple, R](e: Expr[F]) apply (args: Args)(given tf: TupledFunction[F, Args => R]): R = tf.tupled(e.x)(args) - def (e: Expr[F]) applyGiven[F, Args <: Tuple, R](args: Args)(given tf: TupledFunction[F, (given Args) => R]): R = + def [F, Args <: Tuple, R](e: Expr[F]) applyGiven (args: Args)(given tf: TupledFunction[F, (given Args) => R]): R = tf.tupled(e.x)(given args) } \ No newline at end of file diff --git a/tests/run/tupled-function-tupled.scala b/tests/run/tupled-function-tupled.scala index b18b1f4152d5..a905d92d7df3 100644 --- a/tests/run/tupled-function-tupled.scala +++ b/tests/run/tupled-function-tupled.scala @@ -24,5 +24,5 @@ object Test { * @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) } diff --git a/tests/run/tupled-function-untupled.scala b/tests/run/tupled-function-untupled.scala index 6c9ccd158976..c79cb9a91417 100644 --- a/tests/run/tupled-function-untupled.scala +++ b/tests/run/tupled-function-untupled.scala @@ -104,5 +104,5 @@ object Test { * @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) } From a5036f2d6d7ba3e2723dc3e681830253c9398e35 Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Mon, 28 Oct 2019 21:12:25 +0100 Subject: [PATCH 2/4] Add neg test --- compiler/test/dotty/tools/dotc/CompilationTests.scala | 3 ++- tests/neg-custom-args/extmethods-tparams.scala | 2 ++ 2 files changed, 4 insertions(+), 1 deletion(-) create mode 100644 tests/neg-custom-args/extmethods-tparams.scala diff --git a/compiler/test/dotty/tools/dotc/CompilationTests.scala b/compiler/test/dotty/tools/dotc/CompilationTests.scala index 32087a21ba85..4412ab86471b 100644 --- a/compiler/test/dotty/tools/dotc/CompilationTests.scala +++ b/compiler/test/dotty/tools/dotc/CompilationTests.scala @@ -141,7 +141,8 @@ class CompilationTests extends ParallelTesting { compileFile("tests/neg-custom-args/infix.scala", defaultOptions.and("-strict", "-deprecation", "-Xfatal-warnings")), compileFile("tests/neg-custom-args/missing-alpha.scala", defaultOptions.and("-strict", "-deprecation", "-Xfatal-warnings")), compileFile("tests/neg-custom-args/wildcards.scala", defaultOptions.and("-strict", "-deprecation", "-Xfatal-warnings")), - compileFile("tests/neg-custom-args/indentRight.scala", defaultOptions.and("-noindent", "-Xfatal-warnings")) + compileFile("tests/neg-custom-args/indentRight.scala", defaultOptions.and("-noindent", "-Xfatal-warnings")), + compileFile("tests/neg-custom-args/extmethods-tparams.scala", defaultOptions.and("-deprecation", "-Xfatal-warnings")) ).checkExpectedErrors() } diff --git a/tests/neg-custom-args/extmethods-tparams.scala b/tests/neg-custom-args/extmethods-tparams.scala new file mode 100644 index 000000000000..7f5c671a93cf --- /dev/null +++ b/tests/neg-custom-args/extmethods-tparams.scala @@ -0,0 +1,2 @@ +def (self: T) foo[T] = ??? // error +def [T1](self: T1) bar[T2] = ??? // error \ No newline at end of file From 774fbd10754c1dc609a394f11128d7639c9149d1 Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Mon, 28 Oct 2019 21:18:22 +0100 Subject: [PATCH 3/4] Fix docs and Parser --- compiler/src/dotty/tools/dotc/parsing/Parsers.scala | 6 +++--- docs/docs/reference/contextual/extension-methods.md | 6 ++++-- 2 files changed, 7 insertions(+), 5 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/parsing/Parsers.scala b/compiler/src/dotty/tools/dotc/parsing/Parsers.scala index 11c5642b037a..d87c9be72b11 100644 --- a/compiler/src/dotty/tools/dotc/parsing/Parsers.scala +++ b/compiler/src/dotty/tools/dotc/parsing/Parsers.scala @@ -3121,12 +3121,12 @@ 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 newLineOpt() val (leadingTparams, leadingVparamss, flags) = if in.token == LBRACKET then - (typeParamClause(ParamOwner.Def), extParamss, Method | Extension) + (typeParamClause(ParamOwner.Def), extParamss(), Method | Extension) else if in.token == LPAREN then - (Nil, extParamss, Method | Extension) + (Nil, extParamss(), Method | Extension) else (Nil, Nil, Method) val mods1 = addFlag(mods, flags) diff --git a/docs/docs/reference/contextual/extension-methods.md b/docs/docs/reference/contextual/extension-methods.md index b2f7e82f9769..70f58eb8b9b0 100644 --- a/docs/docs/reference/contextual/extension-methods.md +++ b/docs/docs/reference/contextual/extension-methods.md @@ -161,8 +161,10 @@ def [T: Numeric](x: T) + (y: T): T = summon[Numeric[T]].plus(x, y) ``` -As usual, type parameters of the extension method follow the defined method name. Nevertheless, such type parameters can already be used in the preceding parameter clause. - +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] +``` ### Syntax From 9af572dee0b20b4d349531d7b7bc22063d690359 Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Tue, 29 Oct 2019 09:37:15 +0100 Subject: [PATCH 4/4] Fix neg test --- tests/neg-custom-args/extmethods-tparams.scala | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/neg-custom-args/extmethods-tparams.scala b/tests/neg-custom-args/extmethods-tparams.scala index 7f5c671a93cf..e6bd6ebe7485 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 [T1](self: T1) bar[T2] = ??? // error \ No newline at end of file +def [T1](self: T1) bar[T2] = ??? // error // error \ No newline at end of file