Skip to content

Commit f8bd21a

Browse files
committed
Change syntax of instance declarations
- Use `:` instead of `implements`. Still todo: Make the `:` context constraint abstract over such instance declarations.
1 parent 19d12d9 commit f8bd21a

File tree

10 files changed

+47
-41
lines changed

10 files changed

+47
-41
lines changed

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

Lines changed: 11 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -773,13 +773,17 @@ object desugar {
773773
}
774774

775775
private val collectNames = new untpd.UntypedTreeAccumulator[ListBuffer[String]] {
776+
def add(buf: ListBuffer[String], name: Name): ListBuffer[String] = {
777+
val alpha = name.toString.filter(isIdentifierPart)
778+
if (alpha.isEmpty) buf else buf += alpha
779+
}
776780
override def apply(buf: ListBuffer[String], tree: Tree)(implicit ctx: Context): ListBuffer[String] = tree match {
777781
case Ident(name) =>
778-
buf += name.toString
782+
add(buf, name)
779783
case Select(qual, name) =>
780-
apply(buf, qual) += name.toString
784+
add(apply(buf, qual), name)
781785
case TypeDef(name, rhs) =>
782-
apply(buf += "type" += name.toString, rhs)
786+
apply(add(buf += "type", name), rhs)
783787
case Apply(fn, _) =>
784788
apply(buf, fn)
785789
case _ =>
@@ -790,12 +794,11 @@ object desugar {
790794
private def extensionName(ext: Extension)(implicit ctx: Context): TypeName = {
791795
var buf = collectNames(new ListBuffer[String] += "extend", ext.extended)
792796
if (ext.impl.parents.nonEmpty)
793-
buf = collectNames(buf += "implements", ext.impl.parents)
794-
val ids = buf.toList.map(_.filter(isIdentifierPart)).filter(!_.isEmpty)
795-
ids.mkString("_").toTypeName
797+
buf = collectNames(buf += "", ext.impl.parents)
798+
buf.toList.mkString("_").toTypeName
796799
}
797800

798-
/** extend <type-pattern> <params> implements <parents> { <body>} }
801+
/** extend <type-pattern> <params> : <parents> { <body>} }
799802
* ->
800803
* implicit class <extension-name> <type-params> ($this: <extended>) <combined-params>
801804
* extends <parents> { <body1> }
@@ -855,7 +858,7 @@ object desugar {
855858

856859
val mods =
857860
if (isSimpleExtension) EmptyModifiers
858-
else EmptyModifiers.withAddedMod(Mod.TraitImplementation())
861+
else EmptyModifiers.withAddedMod(Mod.InstanceDecl())
859862
val icls =
860863
TypeDef(extName,
861864
cpy.Template(impl)(constr = constr1, parents = parents1, body = body1))

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -143,7 +143,7 @@ object untpd extends Trees.Instance[Untyped] with UntypedTreeInfo {
143143

144144
case class EnumCase() extends Mod(Flags.EmptyFlags)
145145

146-
case class TraitImplementation() extends Mod(Flags.EmptyFlags)
146+
case class InstanceDecl() extends Mod(Flags.EmptyFlags)
147147
}
148148

149149
/** Modifiers and annotations for definitions

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

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2306,20 +2306,20 @@ object Parsers {
23062306
/** Extension ::= ‘extend’ BindingTypePattern
23072307
* [[nl] ImplicitParamClause] ExtensionClause
23082308
* BindingTypePattern ::= AnnotType
2309-
* ExtensionClause ::= ‘implements’ Template
2309+
* ExtensionClause ::= : Template
23102310
* | [nl] ‘{’ ‘def’ DefDef {semi ‘def’ DefDef} ‘}’
23112311
*/
23122312
def extension(): Extension = atPos(in.skipToken(), nameStart) {
23132313
val extended = withinTypePattern(binding = true)(annotType())
23142314
val vparamss = paramClauses(tpnme.EMPTY, ofExtension = true).take(1)
23152315
val constr = makeConstructor(Nil, vparamss)
23162316
val templ =
2317-
if (in.token == IMPLEMENTS) {
2317+
if (in.token == COLON) {
23182318
in.nextToken()
23192319
template(constr, bodyRequired = true)._1
23202320
}
23212321
else {
2322-
if (in.token == EXTENDS) syntaxError("`implements` or `{` expected")
2322+
if (in.token == EXTENDS) syntaxError("`:` or `{` expected")
23232323
val templ = templateClauseOpt(constr, bodyRequired = true)
23242324
def checkDef(tree: Tree) = tree match {
23252325
case _: DefDef | EmptyValDef => // ok

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

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -180,7 +180,6 @@ object Tokens extends TokensCommon {
180180
final val ERASED = 64; enter(ERASED, "erased")
181181
final val OPAQUE = 65; enter(OPAQUE, "opaque")
182182
final val EXTEND = 66; enter(EXTEND, "extend")
183-
final val IMPLEMENTS = 67; enter(IMPLEMENTS, "implements")
184183

185184
/** special symbols */
186185
final val NEWLINE = 78; enter(NEWLINE, "end of statement", "new line")
@@ -201,7 +200,7 @@ object Tokens extends TokensCommon {
201200
/** XML mode */
202201
final val XMLSTART = 96; enter(XMLSTART, "$XMLSTART$<") // TODO: deprecate
203202

204-
final val alphaKeywords = tokenRange(IF, IMPLEMENTS)
203+
final val alphaKeywords = tokenRange(IF, EXTEND)
205204
final val symbolicKeywords = tokenRange(USCORE, VIEWBOUND)
206205
final val symbolicTokens = tokenRange(COMMA, VIEWBOUND)
207206
final val keywords = alphaKeywords | symbolicKeywords

compiler/src/dotty/tools/dotc/typer/Namer.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -944,7 +944,7 @@ class Namer { typer: Typer =>
944944
if (cls.isRefinementClass) ptype
945945
else {
946946
val pt = checkClassType(ptype, parent.pos,
947-
traitReq = (parent `ne` parents.head) || original.mods.hasMod[Mod.TraitImplementation],
947+
traitReq = (parent `ne` parents.head) || original.mods.hasMod[Mod.InstanceDecl],
948948
stablePrefixReq = true)
949949
if (pt.derivesFrom(cls)) {
950950
val addendum = parent match {

docs/docs/reference/extend/extension-implementations.md renamed to docs/docs/reference/extend/instance-declarations.md

Lines changed: 11 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,21 @@
11
---
22
layout: doc-page
3-
title: "Extension Implementations"
3+
title: "Instance Declarations"
44
---
55

6-
In addition to adding methods, an extension can also implement traits and classes. For instance,
6+
In addition to adding methods, an extension can also implement traits. Extensions implementing traits are also called _instance declarations_. For example,
77

88
```scala
99
trait HasArea {
1010
def area: Double
1111
}
1212

13-
extend Circle implements HasArea {
13+
extend Circle : HasArea {
1414
def area = this.radius * this.radius * math.Pi
1515
}
1616
```
1717

18-
This extension makes `Circle` implement the `HasArea` trait. Specifically, it defines an implicit subclass of `HasArea`
18+
This extension makes `Circle` an instance of the `HasArea` trait. Specifically, it defines an implicit subclass of `HasArea`
1919
which takes a `Circle` as argument and provides the given implementation. Hence, the implementation of the extension above would be like this
2020

2121
```scala
@@ -24,11 +24,11 @@ implicit class circleOps($this: Circle) extends HasArea {
2424
}
2525
```
2626

27-
An extension implementation can thus provide a kind of "implements" relationship that can be defined independently of the types it connects.
27+
An instance definition can thus provide a kind of "implements" relationship that can be defined independently of the types it connects.
2828

29-
### Adding Implementations to Generic Traits
29+
### Generic Instance Declarations
3030

31-
Just like extension methods, extension implementations can also be generic and their type parameters can have bounds.
31+
Just like extension methods, instance declarations can also be generic and their type parameters can have bounds.
3232

3333
For example, assume we have the following two traits, which define binary and unary (infix) equality tests:
3434

@@ -45,18 +45,20 @@ trait HasEql[T] {
4545
The following extension makes any type `T` with an implicit `Eql[T]` instance implement `HasEql`:
4646

4747
```scala
48-
extend (type T: Eql) implements HasEql[T] {
48+
extend (type T : Eql) : HasEql[T] {
4949
def === (that: T): Boolean = implicitly[Eql[T]].eql(this, that)
5050
}
5151
```
5252

53+
54+
5355
### Syntax of Extensions
5456

5557
The syntax of extensions is specified below as a delta with respect to the Scala syntax given [here](http://dotty.epfl.ch/docs/internals/syntax.html)
5658

5759
Extension ::= ‘extend’ BindingTypePattern
5860
[[nl] ImplicitParamClause] ExtensionClause
59-
ExtensionClause ::= ‘implements’ Template
61+
ExtensionClause ::= ‘:’ Template
6062
| [nl] ‘{’ ‘def’ DefDef {semi ‘def’ DefDef} ‘}’
6163

6264
ImplicitParamClause ::= [nl] ‘(’ ImplicitMods ClsParams ‘)’

docs/docs/reference/extend/translation.md

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ title: "Translation of Extensions"
55

66
Extensons are closely related to implicit classes and can be translated into them. In short,
77
an extension that just adds extension methods translates into an implicit value class whereas
8-
an extension with an `implements` clause translates
8+
an instance declaration translates
99
into a regular implicit class. The following sections sketch this translation.
1010

1111
Conversely, it is conceivable (and desirable) to replace most usages of implicit classes and value classes by extensions and [opaque types](../opaques.html). We plan to [drop](../dropped/implicit-value-classes.html)
@@ -31,7 +31,9 @@ Extensions are anonymous -- the first identifier given in an extension designate
3131
2. Drop an implicit parameter list if one is given and also drop any value arguments to parent constructors.
3232
3. Drop any tokens that are not keywords of identifiers.
3333
4. In each token resulting from the previous step, drop all characters that are not legal parts of alphanumeric identifiers. This leaves all characters that satisfy the `java.lang.Character.isUnicodeIdentifierPart` predicate as well as `$`. Among Ascii characters, the retained characters are all letters, digits, as well as `_` and `$`.
34-
5. Concatenate all non-empty strings resulting from the previous step with `_` separators.
34+
5. Concatenate all non-empty strings resulting from the previous step with `_` separators,
35+
except use `__` (i.e. two underscores) between identifiers coming from the type pattern
36+
and identifiers coming from the implemented traits (if there are any).
3537

3638
It is an error if two extensions defined in the same scope have the same computed name. These double definition errors can always be avoided by grouping augments together or, as a last resort, defining suitable type aliases. The scheme gives stable names that do not depend on the order in which definitions are given.
3739

@@ -69,11 +71,11 @@ implicit class extend_Seq_type_T_math_Ordering [T]($this: List[T]) extends AnyVa
6971
}
7072
```
7173

72-
### Translation of Extension Implementations
74+
### Translation of Instance Declarations
7375

7476
Now, assume an extension
7577

76-
extend <TP> <params> implements <parents> { <body> }
78+
extend <TP> <params> : <parents> { <body> }
7779

7880
Let again `(<tparams>, <T>)` be the decomposition of `<TP>`. This extension is translated to
7981

@@ -87,15 +89,15 @@ parameterized.
8789
For example, the extension
8890

8991
```scala
90-
extend (type T: Eql) implements HasEql[T] {
92+
extend (type T: Eql) : HasEql[T] {
9193
def === (that: T): Boolean = implicitly[Eql[T]].eql(this, that)
9294
}
9395
```
9496

9597
would be translated to
9698

9799
```scala
98-
implicit class extend_type_T_Eql_implements_HasEql_T [T]
100+
implicit class extend_type_T_Eql__HasEql_T [T]
99101
($this: T)(implicit $ev: Eql[T]) extends HasEql[T] {
100102
def === (that: T): Boolean = implicitly[Eql[T]].eql($this, that)
101103
}

docs/sidebar.yml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -33,8 +33,8 @@ sidebar:
3333
subsection:
3434
- title: Extension Methods
3535
url: docs/reference/extend/extension-methods.html
36-
- title: Extension Implementations
37-
url: docs/reference/extend/extension-implementations.html
36+
- title: Instance Declarations
37+
url: docs/reference/extend/instance-declarations.html
3838
- title: Translation of Extensions
3939
url: docs/reference/extend/translation.html
4040
- title: Enums

tests/neg/extensions.scala

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -24,13 +24,13 @@ object extensions {
2424

2525
abstract class HasAreaClass extends HasArea
2626

27-
extend Circle implements HasArea {
27+
extend Circle : HasArea {
2828
def area = this.radius * this.radius * math.Pi
2929
}
3030

31-
extend Circle2 extends HasArea {} // error: `implements` or `{` expected // error: `def` expected
31+
extend Circle2 extends HasArea {} // error: `:` or `{` expected // error: `def` expected
3232

33-
extend Circle implements HasAreaClass { // error: class HasAreaClass is not a trait
33+
extend Circle : HasAreaClass { // error: class HasAreaClass is not a trait
3434
def area = this.radius * this.radius * math.Pi
3535
}
3636

tests/run/extensions.scala

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ object extensions {
1515
def area: Double
1616
}
1717

18-
extend Circle implements HasArea {
18+
extend Circle : HasArea {
1919
def area = this.radius * this.radius * math.Pi
2020
}
2121

@@ -63,15 +63,15 @@ object extensions {
6363
def === (that: T): Boolean
6464
}
6565

66-
extend (type S: Eql) implements HasEql[S] {
66+
extend (type S : Eql) : HasEql[S] {
6767
def === (that: S): Boolean = implicitly[Eql[S]].eql(this, that)
6868
}
6969

7070
extend (type T2)(implicit ev: Eql[T2]) {
7171
def ==== (that: T2): Boolean = implicitly[Eql[T2]].eql(this, that)
7272
}
7373

74-
extend Rectangle[type T: Eql] implements HasEql[Rectangle[T]] {
74+
extend Rectangle[type T : Eql] : HasEql[Rectangle[T]] {
7575
def === (that: Rectangle[T]) =
7676
this.x === that.x &&
7777
this.y === that.y &&
@@ -81,16 +81,16 @@ object extensions {
8181
}
8282

8383
object extensions2 {
84-
import extensions.{Eql, extend_type_S_Eql_S_implements_HasEql_S}
84+
import extensions.{Eql, extend_type_S_Eql_S__HasEql_S}
8585
// Nested generic arguments
8686

8787
extend List[List[type U]] {
8888
def flattened: List[U] = (this :\ (Nil: List[U]))(_ ++ _)
8989
}
9090

91-
extend (type T: Eql, T) {
91+
extend (type T : Eql, T) {
9292
def isSame = this._1 === this._2
93-
def isSame2 = extend_type_S_Eql_S_implements_HasEql_S(this._1) == this._2
93+
def isSame2 = extend_type_S_Eql_S__HasEql_S(this._1) == this._2
9494
}
9595

9696
}

0 commit comments

Comments
 (0)