Skip to content

Commit b44c6af

Browse files
authored
Merge pull request #7455 from dotty-staging/change-extmethod-tparams
Syntax change for type parameters of extension methods
2 parents d06a5ff + 9af572d commit b44c6af

34 files changed

+130
-125
lines changed

compiler/src/dotty/tools/dotc/ast/Positioned.scala

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -208,16 +208,13 @@ abstract class Positioned(implicit @constructorOnly src: SourceFile) extends Pro
208208
case tree: DefDef if tree.mods.is(Extension) =>
209209
tree.vparamss match {
210210
case vparams1 :: vparams2 :: rest if !isLeftAssoc(tree.name) =>
211-
check(vparams2)
212211
check(tree.tparams)
212+
check(vparams2)
213213
check(vparams1)
214214
check(rest)
215-
case vparams1 :: rest =>
216-
check(vparams1)
217-
check(tree.tparams)
218-
check(rest)
219215
case _ =>
220216
check(tree.tparams)
217+
check(tree.vparamss)
221218
}
222219
check(tree.tpt)
223220
check(tree.rhs)

compiler/src/dotty/tools/dotc/ast/Trees.scala

Lines changed: 4 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -754,9 +754,10 @@ object Trees {
754754
def unforced: LazyTree[T] = preRhs
755755
protected def force(x: Tree[T @uncheckedVariance]): Unit = preRhs = x
756756

757-
override def disableOverlapChecks = rawMods.is(Given)
758-
// disable order checks for implicit aliases since their given clause follows
759-
// their for clause, but the two appear swapped in the DefDef.
757+
override def disableOverlapChecks = rawMods.is(Extension)
758+
// disable order checks for extension methods as long as we parse
759+
// type parameters both before and after the leading parameter section.
760+
// TODO drop this once syntax of type parameters has settled.
760761
}
761762

762763
/** mods class name template or
@@ -789,10 +790,6 @@ object Trees {
789790

790791
def parents: List[Tree[T]] = parentsOrDerived // overridden by DerivingTemplate
791792
def derived: List[untpd.Tree] = Nil // overridden by DerivingTemplate
792-
793-
override def disableOverlapChecks = true
794-
// disable overlaps checks since templates of instance definitions have their
795-
// `given` clause come last, which means that the constructor span can contain the parent spans.
796793
}
797794

798795

compiler/src/dotty/tools/dotc/core/Decorators.scala

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -170,8 +170,8 @@ object Decorators {
170170
}
171171
}
172172

173-
implicit object reportDeco {
174-
def (x: T) reporting[T](
173+
implicit class reportDeco[T](x: T) extends AnyVal {
174+
def reporting(
175175
op: (given WrappedResult[T]) => String,
176176
printer: config.Printers.Printer = config.Printers.default): T = {
177177
printer.println(op(given WrappedResult(x)))

compiler/src/dotty/tools/dotc/parsing/Parsers.scala

Lines changed: 26 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -3089,10 +3089,11 @@ object Parsers {
30893089
}
30903090
}
30913091

3092-
/** DefDef ::= DefSig [(‘:’ | ‘<:’) Type] ‘=’ Expr
3093-
* | this ParamClause ParamClauses `=' ConstrExpr
3094-
* DefDcl ::= DefSig `:' Type
3095-
* DefSig ::= [‘(’ DefParam ‘)’ [nl]] id [DefTypeParamClause] ParamClauses
3092+
/** DefDef ::= DefSig [(‘:’ | ‘<:’) Type] ‘=’ Expr
3093+
* | this ParamClause ParamClauses `=' ConstrExpr
3094+
* DefDcl ::= DefSig `:' Type
3095+
* DefSig ::= id [DefTypeParamClause] DefParamClauses
3096+
* | ExtParamClause [nl] id DefParamClauses
30963097
*/
30973098
def defDefOrDcl(start: Offset, mods: Modifiers): Tree = atSpan(start, nameStart) {
30983099
def scala2ProcedureSyntax(resultTypeStr: String) = {
@@ -3121,27 +3122,35 @@ object Parsers {
31213122
makeConstructor(Nil, vparamss, rhs).withMods(mods).setComment(in.getDocComment(start))
31223123
}
31233124
else {
3124-
val (leadingParamss, flags) =
3125-
if (in.token == LPAREN)
3126-
try (paramClause(0, prefix = true) :: Nil, Method | Extension)
3127-
finally newLineOpt()
3125+
def extParamss() = try paramClause(0, prefix = true) :: Nil finally newLineOpt()
3126+
val (leadingTparams, leadingVparamss, flags) =
3127+
if in.token == LBRACKET then
3128+
(typeParamClause(ParamOwner.Def), extParamss(), Method | Extension)
3129+
else if in.token == LPAREN then
3130+
(Nil, extParamss(), Method | Extension)
31283131
else
3129-
(Nil, Method)
3132+
(Nil, Nil, Method)
31303133
val mods1 = addFlag(mods, flags)
31313134
val ident = termIdent()
31323135
val name = ident.name.asTermName
3133-
val tparams = typeParamClauseOpt(ParamOwner.Def)
3134-
val vparamss = paramClauses() match {
3135-
case rparams :: rparamss if leadingParamss.nonEmpty && !isLeftAssoc(ident.name) =>
3136-
rparams :: leadingParamss ::: rparamss
3136+
val tparams =
3137+
if in.token == LBRACKET then
3138+
if flags.is(Extension) then
3139+
if leadingTparams.isEmpty then
3140+
deprecationWarning("type parameters in extension methods should be written after `def`")
3141+
else
3142+
syntaxError("no type parameters allowed here")
3143+
typeParamClause(ParamOwner.Def)
3144+
else leadingTparams
3145+
val vparamss = paramClauses() match
3146+
case rparams :: rparamss if leadingVparamss.nonEmpty && !isLeftAssoc(ident.name) =>
3147+
rparams :: leadingVparamss ::: rparamss
31373148
case rparamss =>
3138-
leadingParamss ::: rparamss
3139-
}
3149+
leadingVparamss ::: rparamss
31403150
var tpt = fromWithinReturnType {
3141-
if (in.token == SUBTYPE && mods.is(Inline)) {
3151+
if in.token == SUBTYPE && mods.is(Inline) then
31423152
in.nextToken()
31433153
TypeBoundsTree(EmptyTree, toplevelTyp())
3144-
}
31453154
else typedOpt()
31463155
}
31473156
if (in.isScala2Mode) newLineOptWhenFollowedBy(LBRACE)

compiler/test/dotty/tools/dotc/CompilationTests.scala

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -141,7 +141,8 @@ class CompilationTests extends ParallelTesting {
141141
compileFile("tests/neg-custom-args/infix.scala", defaultOptions.and("-strict", "-deprecation", "-Xfatal-warnings")),
142142
compileFile("tests/neg-custom-args/missing-alpha.scala", defaultOptions.and("-strict", "-deprecation", "-Xfatal-warnings")),
143143
compileFile("tests/neg-custom-args/wildcards.scala", defaultOptions.and("-strict", "-deprecation", "-Xfatal-warnings")),
144-
compileFile("tests/neg-custom-args/indentRight.scala", defaultOptions.and("-noindent", "-Xfatal-warnings"))
144+
compileFile("tests/neg-custom-args/indentRight.scala", defaultOptions.and("-noindent", "-Xfatal-warnings")),
145+
compileFile("tests/neg-custom-args/extmethods-tparams.scala", defaultOptions.and("-deprecation", "-Xfatal-warnings"))
145146
).checkExpectedErrors()
146147
}
147148

docs/docs/internals/syntax.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -357,8 +357,8 @@ Dcl ::= RefineDcl
357357
ValDcl ::= ids ‘:’ Type PatDef(_, ids, tpe, EmptyTree)
358358
VarDcl ::= ids ‘:’ Type PatDef(_, ids, tpe, EmptyTree)
359359
DefDcl ::= DefSig ‘:’ Type DefDef(_, name, tparams, vparamss, tpe, EmptyTree)
360-
DefSig ::= [‘(’ DefParam ‘)’ [nl]] id
361-
[DefTypeParamClause] DefParamClauses
360+
DefSig ::= id [DefTypeParamClause] DefParamClauses
361+
| ExtParamClause [nl] id DefParamClauses
362362
TypeDcl ::= id [TypeParamClause] SubtypeBounds [‘=’ Type] TypeDefTree(_, name, tparams, bound
363363
364364
Def ::= ‘val’ PatDef

docs/docs/reference/contextual/extension-methods.md

Lines changed: 11 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -94,7 +94,7 @@ given stringOps: {
9494
}
9595

9696
given {
97-
def (xs: List[T]) second[T] = xs.tail.head
97+
def [T](xs: List[T]) second = xs.tail.head
9898
}
9999
```
100100
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
106106
For instance, here is a given instance with two extension methods.
107107
```scala
108108
given listOps: {
109-
def (xs: List[T]) second[T]: T = xs.tail.head
110-
def (xs: List[T]) third[T]: T = xs.tail.tail.head
109+
def [T](xs: List[T]) second: T = xs.tail.head
110+
def [T](xs: List[T]) third: T = xs.tail.tail.head
111111
}
112112
```
113113
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,26 +151,28 @@ to the implementation of right binding operators as normal methods.
151151
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:
152152

153153
```scala
154-
def (xs: List[T]) second [T] =
154+
def [T](xs: List[T]) second =
155155
xs.tail.head
156156

157-
def (xs: List[List[T]]) flattened [T] =
157+
def [T](xs: List[List[T]]) flattened =
158158
xs.foldLeft[List[T]](Nil)(_ ++ _)
159159

160-
def (x: T) + [T : Numeric](y: T): T =
160+
def [T: Numeric](x: T) + (y: T): T =
161161
summon[Numeric[T]].plus(x, y)
162162
```
163163

164-
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.
165-
164+
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:
165+
```scala
166+
List(1, 2, 3).second[Int]
167+
```
166168

167169
### Syntax
168170

169171
The required syntax extension just adds one clause for extension methods relative
170172
to the [current syntax](../../internals/syntax.md).
171173
```
172174
DefSig ::= ...
173-
| ‘(’ DefParam ‘)’ [nl] id [DefTypeParamClause] DefParamClauses
175+
| ExtParamClause [nl] id DefParamClauses
174176
GivenDef ::= ...
175177
[GivenSig ‘:’] [ExtParamClause] TemplateBody
176178
ExtParamClause ::= [DefTypeParamClause] ‘(’ DefParam ‘)’ {GivenParamClause}

docs/docs/reference/contextual/typeclasses.md

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -38,25 +38,25 @@ def sum[T: Monoid](xs: List[T]): T =
3838

3939
```scala
4040
trait Functor[F[_]] {
41-
def (x: F[A]) map [A, B] (f: A => B): F[B]
41+
def [A, B](x: F[A]) map (f: A => B): F[B]
4242
}
4343

4444
trait Monad[F[_]] extends Functor[F] {
45-
def (x: F[A]) flatMap [A, B] (f: A => F[B]): F[B]
46-
def (x: F[A]) map [A, B] (f: A => B) = x.flatMap(f `andThen` pure)
45+
def [A, B](x: F[A]) flatMap (f: A => F[B]): F[B]
46+
def [A, B](x: F[A]) map (f: A => B) = x.flatMap(f `andThen` pure)
4747

4848
def pure[A](x: A): F[A]
4949
}
5050

5151
given listMonad: Monad[List] {
52-
def (xs: List[A]) flatMap [A, B] (f: A => List[B]): List[B] =
52+
def [A, B](xs: List[A]) flatMap (f: A => List[B]): List[B] =
5353
xs.flatMap(f)
5454
def pure[A](x: A): List[A] =
5555
List(x)
5656
}
5757

5858
given readerMonad[Ctx]: Monad[[X] =>> Ctx => X] {
59-
def (r: Ctx => A) flatMap [A, B] (f: A => Ctx => B): Ctx => B =
59+
def [A, B](r: Ctx => A) flatMap (f: A => Ctx => B): Ctx => B =
6060
ctx => f(r(ctx))(ctx)
6161
def pure[A](x: A): Ctx => A =
6262
ctx => x

library/src-bootstrapped/scala/IArray.scala

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -23,8 +23,8 @@ object opaques {
2323
def (arr: IArray[Long]) apply (n: Int): Long = arr.asInstanceOf[Array[Long]].apply(n)
2424
def (arr: IArray[Float]) apply (n: Int): Float = arr.asInstanceOf[Array[Float]].apply(n)
2525
def (arr: IArray[Double]) apply (n: Int): Double = arr.asInstanceOf[Array[Double]].apply(n)
26-
def (arr: IArray[T]) apply[T <: Object] (n: Int): T = arr.asInstanceOf[Array[T]].apply(n)
27-
def (arr: IArray[T]) apply[T] (n: Int): T = arr.asInstanceOf[Array[T]].apply(n)
26+
def [T <: Object](arr: IArray[T]) apply (n: Int): T = arr.asInstanceOf[Array[T]].apply(n)
27+
def [T](arr: IArray[T]) apply (n: Int): T = arr.asInstanceOf[Array[T]].apply(n)
2828

2929
/** The number of elements in an immutable array
3030
* @param arr the immutable array
@@ -37,7 +37,7 @@ object opaques {
3737
def (arr: IArray[Float]) length: Int = arr.asInstanceOf[Array[Float]].length
3838
def (arr: IArray[Double]) length: Int = arr.asInstanceOf[Array[Double]].length
3939
def (arr: IArray[Object]) length: Int = arr.asInstanceOf[Array[Object]].length
40-
def (arr: IArray[T]) length[T] : Int = arr.asInstanceOf[Array[T]].length
40+
def [T](arr: IArray[T]) length: Int = arr.asInstanceOf[Array[T]].length
4141
}
4242
}
4343
type IArray[+T] = opaques.IArray[T]

library/src-bootstrapped/scala/quoted/package.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,6 @@ package object quoted {
44

55
implicit object ExprOps {
66
@deprecated("Use scala.quoted.Expr.apply instead", "0.19.0")
7-
def (x: T) toExpr[T: Liftable](given QuoteContext): Expr[T] = Expr(x)
7+
def [T: Liftable](x: T) toExpr (given QuoteContext): Expr[T] = Expr(x)
88
}
99
}

0 commit comments

Comments
 (0)