Skip to content

Commit ef7f849

Browse files
committed
Uniformise naming of clauses in right associative extension methods
1 parent 7c08c0e commit ef7f849

File tree

4 files changed

+45
-37
lines changed

4 files changed

+45
-37
lines changed

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

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -915,16 +915,16 @@ object desugar {
915915
name = normalizeName(mdef, mdef.tpt).asTermName,
916916
paramss =
917917
if mdef.name.isRightAssocOperatorName then
918-
val (typaramss, paramss) = mdef.paramss.span(isTypeParamClause) // first extract type parameters
918+
val (rightTyParams, paramss) = mdef.paramss.span(isTypeParamClause) // first extract type parameters
919919

920920
paramss match
921-
case params :: paramss1 => // `params` must have a single parameter and without `given` flag
921+
case rightParam :: paramss1 => // `rightParam` must have a single parameter and without `given` flag
922922

923923
def badRightAssoc(problem: String) =
924924
report.error(em"right-associative extension method $problem", mdef.srcPos)
925925
extParamss ++ mdef.paramss
926926

927-
params match
927+
rightParam match
928928
case ValDefs(vparam :: Nil) =>
929929
if !vparam.mods.is(Given) then
930930
// we merge the extension parameters with the method parameters,
@@ -934,8 +934,10 @@ object desugar {
934934
// def %:[E](f: F)(g: G)(using H): Res = ???
935935
// will be encoded as
936936
// def %:[A](using B)[E](f: F)(c: C)(using D)(g: G)(using H): Res = ???
937-
val (leadingUsing, otherExtParamss) = extParamss.span(isUsingOrTypeParamClause)
938-
leadingUsing ::: typaramss ::: params :: otherExtParamss ::: paramss1
937+
//
938+
// If you change the names of the clauses below, also change them in right-associative-extension-methods.md
939+
val (leftTyParamsAndLeadingUsing, leftParamAndTrailingUsing) = extParamss.span(isUsingOrTypeParamClause)
940+
leftTyParamsAndLeadingUsing ::: rightTyParams ::: rightParam :: leftParamAndTrailingUsing ::: paramss1
939941
else
940942
badRightAssoc("cannot start with using clause")
941943
case _ =>

compiler/src/dotty/tools/dotc/printing/RefinedPrinter.scala

Lines changed: 15 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -895,30 +895,31 @@ class RefinedPrinter(_ctx: Context) extends PlainPrinter(_ctx) {
895895
if isExtension then
896896
val paramss =
897897
if tree.name.isRightAssocOperatorName then
898+
// If you change the names of the clauses below, also change them in right-associative-extension-methods.md
898899
// we have the following encoding of tree.paramss:
899-
// (leadingTyParamss ++ leadingUsing
900-
// ++ rightTyParamss ++ rightParamss
901-
// ++ leftParamss ++ trailingUsing ++ rest)
900+
// (leftTyParams ++ leadingUsing
901+
// ++ rightTyParams ++ rightParam
902+
// ++ leftParam ++ trailingUsing ++ rest)
902903
// e.g.
903904
// extension [A](using B)(c: C)(using D)
904905
// def %:[E](f: F)(g: G)(using H): Res = ???
905906
// will have the following values:
906-
// - leadingTyParamss = List(`[A]`)
907+
// - leftTyParams = List(`[A]`)
907908
// - leadingUsing = List(`(using B)`)
908-
// - rightTyParamss = List(`[E]`)
909-
// - rightParamss = List(`(f: F)`)
910-
// - leftParamss = List(`(c: C)`)
909+
// - rightTyParams = List(`[E]`)
910+
// - rightParam = List(`(f: F)`)
911+
// - leftParam = List(`(c: C)`)
911912
// - trailingUsing = List(`(using D)`)
912913
// - rest = List(`(g: G)`, `(using H)`)
913-
// we need to swap (rightTyParams ++ rightParamss) with (leftParamss ++ trailingUsing)
914-
val (leadingTyParamss, rest1) = tree.paramss.span(isTypeParamClause)
914+
// we need to swap (rightTyParams ++ rightParam) with (leftParam ++ trailingUsing)
915+
val (leftTyParams, rest1) = tree.paramss.span(isTypeParamClause)
915916
val (leadingUsing, rest2) = rest1.span(isUsingClause)
916-
val (rightTyParamss, rest3) = rest2.span(isTypeParamClause)
917-
val (rightParamss, rest4) = rest3.splitAt(1)
918-
val (leftParamss, rest5) = rest4.splitAt(1)
917+
val (rightTyParams, rest3) = rest2.span(isTypeParamClause)
918+
val (rightParam, rest4) = rest3.splitAt(1)
919+
val (leftParam, rest5) = rest4.splitAt(1)
919920
val (trailingUsing, rest6) = rest5.span(isUsingClause)
920-
if leftParamss.nonEmpty then
921-
leadingTyParamss ::: leadingUsing ::: leftParamss ::: trailingUsing ::: rightTyParamss ::: rightParamss ::: rest6
921+
if leftParam.nonEmpty then
922+
leftTyParams ::: leadingUsing ::: leftParam ::: trailingUsing ::: rightTyParams ::: rightParam ::: rest6
922923
else
923924
tree.paramss // it wasn't a binary operator, after all.
924925
else

docs/_docs/reference/contextual/right-associative-extension-methods.md

Lines changed: 15 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -4,44 +4,48 @@ title: "Right-Associative Extension Methods: Details"
44
nightlyOf: https://docs.scala-lang.org/scala3/reference/contextual/right-associative-extension-methods.html
55
---
66

7+
<!-- In case the names of the clauses are modified, also modify them in ClassLikeSupport, Desugar, and RefinedPrinter -->
8+
79
The most general signature an extension method can have is as follows:
8-
- An optional type clause `leadingTyParamss`
10+
- An optional type clause `leftTyParams`
911
- A possibly empty list of using clauses `leadingUsing`
10-
- A single parameter `leftParamss`
12+
- A single parameter `leftParam` (in an explicit term clause)
1113
- A possibly empty list of using clauses `trailingUsing`
1214
- A name (preceded by the `def` keyword)
13-
- An optional type clause `rightTyParamss`
14-
- An optional explicit term clause `rightParamss`
15+
- An optional type clause `rightTyParams`
16+
- An optional single parameter `rightParam` (in an explicit term clause)
1517
- Any number of any clauses `rest`
1618

1719
For example:
1820

1921
```scala
20-
extension [T] // <-- leadingTyParamss
22+
extension [T] // <-- leftTyParams
2123
(using a: A, b: B)(using c: C) // <-- leadingUsing
22-
(x: X) // <-- leftParamss
24+
(x: X) // <-- leftParam
2325
(using d: D) // <-- trailingUsing
24-
def +:: [U] // <-- rightTyParamss
25-
(y: Y) // <-- rightParamss
26+
def +:: [U] // <-- rightTyParams
27+
(y: Y) // <-- rightParam
2628
[V](using e: E)[W](z: Z) // <-- rest
2729
```
2830

2931

3032
An extension method is treated as a right-associative operator
3133
(as in [SLS §6.12.3](https://www.scala-lang.org/files/archive/spec/2.13/06-expressions.html#infix-operations))
3234
if it has a name ending in `:`, and is immediately followed by a
33-
single explicit term parameter (in other words, `rightParamss` is present). In the example above, that parameter is `(y: Y)`.
35+
single explicit term parameter (in other words, `rightParam` is present). In the example above, that parameter is `(y: Y)`.
3436

3537
The Scala compiler pre-processes a right-associative infix operation such as `x +: xs`
3638
to `xs.+:(x)` if `x` is a pure expression or a call-by-name parameter and to `val y = x; xs.+:(y)` otherwise. This is necessary since a regular right-associative infix method
3739
is defined in the class of its right operand. To make up for this swap,
38-
the expansion of right-associative extension methods performs the inverse parameter swap. More precisely, if `rightParamss` is present, the total parameter sequence
40+
the expansion of right-associative extension methods performs the inverse parameter swap. More precisely, if `rightParam` is present, the total parameter sequence
3941
of the extension method's expansion is:
4042

4143
```
42-
leadingTyParamss leadingUsing rightTyParamss rightParamss leftParamss trailingUsing rest
44+
leftTyParams leadingUsing rightTyParams rightParam leftParam trailingUsing rest
4345
```
4446

47+
In other words, we swap `leftParams trailingUsing` with `rightTyParam rightParam`.
48+
4549
For instance, the `+::` method above would become
4650

4751
```scala

scaladoc/src/dotty/tools/scaladoc/tasty/ClassLikeSupport.scala

Lines changed: 8 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -349,16 +349,17 @@ trait ClassLikeSupport:
349349
val unshuffledMemberInfoParamLists =
350350
if methodSymbol.isExtensionMethod && methodSymbol.isRightAssoc then
351351
// Taken from RefinedPrinter.scala
352-
val (leadingTyParamss, rest1) = memberInfo.paramLists.span(_.isType)
352+
// If you change the names of the clauses below, also change them in right-associative-extension-methods.md
353+
val (leftTyParams, rest1) = memberInfo.paramLists.span(_.isType)
353354
val (leadingUsing, rest2) = rest1.span(_.isUsing)
354-
val (rightTyParamss, rest3) = rest2.span(_.isType)
355-
val (rightParamss, rest4) = rest3.splitAt(1)
356-
val (leftParamss, rest5) = rest4.splitAt(1)
355+
val (rightTyParams, rest3) = rest2.span(_.isType)
356+
val (rightParam, rest4) = rest3.splitAt(1)
357+
val (leftParam, rest5) = rest4.splitAt(1)
357358
val (trailingUsing, rest6) = rest5.span(_.isUsing)
358-
if leftParamss.nonEmpty then
359-
// leadingTyParamss ::: leadingUsing ::: leftParamss ::: trailingUsing ::: rightTyParamss ::: rightParamss ::: rest6
359+
if leftParam.nonEmpty then
360+
// leftTyParams ::: leadingUsing ::: leftParam ::: trailingUsing ::: rightTyParams ::: rightParam ::: rest6
360361
// because of takeRight after, this is equivalent to the following:
361-
rightTyParamss ::: rightParamss ::: rest6
362+
rightTyParams ::: rightParam ::: rest6
362363
else
363364
memberInfo.paramLists // it wasn't a binary operator, after all.
364365
else

0 commit comments

Comments
 (0)