Skip to content

Commit 7ace3d7

Browse files
committed
Require that non-extending augments only add extension methods
1 parent f18245d commit 7ace3d7

File tree

4 files changed

+127
-3
lines changed

4 files changed

+127
-3
lines changed

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2154,7 +2154,7 @@ object Types {
21542154
if (ctx.erasedTypes) tref
21552155
else cls.info match {
21562156
case cinfo: ClassInfo => cinfo.selfType
2157-
case cinfo: ErrorType if ctx.mode.is(Mode.Interactive) => cinfo
2157+
case _: ErrorType | NoType if ctx.mode.is(Mode.Interactive) => cls.info
21582158
// can happen in IDE if `cls` is stale
21592159
}
21602160

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

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2304,14 +2304,25 @@ object Parsers {
23042304
}
23052305

23062306
/** Augmentation ::= ‘augment’ BindingTypePattern
2307-
* [[nl] ImplicitParamClause] TemplateClause
2307+
* [[nl] ImplicitParamClause] Additions
23082308
* BindingTypePattern ::= AnnotType
2309+
* Additions ::= ‘extends’ Template
2310+
* | [nl] ‘{’ ‘def’ DefDef {semi ‘def’ DefDef} ‘}’
23092311
*/
23102312
def augmentation(): Augment = atPos(in.skipToken(), nameStart) {
23112313
val augmented = withinTypePattern(binding = true)(annotType())
23122314
val vparamss = paramClauses(tpnme.EMPTY, ofAugmentation = true).take(1)
23132315
val constr = makeConstructor(Nil, vparamss)
2316+
val isSimpleExtension = in.token != EXTENDS
23142317
val templ = templateClauseOpt(constr, bodyRequired = true)
2318+
if (isSimpleExtension) {
2319+
def checkDef(tree: Tree) = tree match {
2320+
case _: DefDef | EmptyValDef => // ok
2321+
case _ => syntaxError("`def` expected", tree.pos.startPos)
2322+
}
2323+
checkDef(templ.self)
2324+
templ.body.foreach(checkDef)
2325+
}
23152326
Augment(augmented, templ)
23162327
}
23172328

docs/docs/internals/syntax.md

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -248,7 +248,9 @@ ArgumentPatterns ::= ‘(’ [Patterns] ‘)’
248248
| ‘(’ [Patterns ‘,’] Pattern2 ‘:’ ‘_’ ‘*’ ‘)’
249249
250250
Augmentation ::= ‘augment’ BindingTypePattern
251-
[[nl] ImplicitParamClause] TemplateClause Augment(name, templ)
251+
[[nl] ImplicitParamClause] AugmentClause Augment(name, templ)
252+
AugmentClause ::= ‘extends’ Template
253+
| [nl] ‘{’ ‘def’ DefDef {semi ‘def’ DefDef} ‘}’
252254
BindingTypePattern::= AnnotType
253255
```
254256

tests/neg/augment.scala

Lines changed: 111 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,111 @@
1+
import Predef.{any2stringadd => _, _}
2+
object augments {
3+
4+
// Simple extension methods
5+
6+
case class Circle(x: Double, y: Double, radius: Double)
7+
8+
augment Circle {
9+
def circumference = this.radius * math.Pi * 2
10+
private val p = math.Pi // error: `def` expected
11+
}
12+
13+
// Trait implementations
14+
15+
trait HasArea {
16+
def area: Double
17+
}
18+
19+
augment Circle extends HasArea {
20+
def area = this.radius * this.radius * math.Pi
21+
}
22+
23+
// Generic trait implementations
24+
25+
augment List[type T] {
26+
type I = Int // error: `def` expected
27+
def second = this.tail.head
28+
}
29+
30+
// Specific trait implementations
31+
32+
augment List[Int] {
33+
import java.lang._
34+
def maxx = (0 /: this)(_ `max` _)
35+
}
36+
37+
augment Array[Int] {
38+
def maxx = (0 /: this)(_ `max` _)
39+
}
40+
41+
// Conditional extension methods
42+
43+
case class Rectangle[T](x: T, y: T, width: T, height: T)
44+
45+
trait Eql[T] {
46+
def eql (x: T, y: T): Boolean
47+
}
48+
49+
augment Rectangle[type T: Eql] {
50+
def isSquare: Boolean = implicitly[Eql[T]].eql(this.width, this.height)
51+
}
52+
53+
// Simple generic augments
54+
55+
augment (type T) {
56+
def ~[U](that: U): (T, U) = (this, that)
57+
}
58+
59+
// Conditional generic augments
60+
61+
trait HasEql[T] {
62+
def === (that: T): Boolean
63+
}
64+
65+
augment (type T: Eql) extends HasEql[T] {
66+
def === (that: T): Boolean = implicitly[Eql[T]].eql(this, that)
67+
}
68+
69+
augment Rectangle[type T: Eql] extends HasEql[Rectangle[T]] {
70+
def === (that: Rectangle[T]) =
71+
this.x === that.x &&
72+
this.y === that.y &&
73+
this.width == that.width &&
74+
this.height == that.height
75+
}
76+
77+
// Generic augments with additional parameters
78+
79+
augment List[List[type U]] {
80+
def flattened: List[U] = (this :\ (Nil: List[U]))(_ ++ _)
81+
}
82+
83+
augment (type T: Eql, T) {
84+
def isSame = this._1 === this._2
85+
}
86+
}
87+
88+
import augments._
89+
object Test extends App {
90+
val c = Circle(0, 1, 2)
91+
println(c.area)
92+
93+
implicit object IntHasEql extends Eql[Int] {
94+
def eql (x: Int, y: Int): Boolean = x == y
95+
}
96+
97+
println(1 ~ "a")
98+
99+
val r1 = Rectangle(0, 0, 2, 2)
100+
val r2 = Rectangle(0, 0, 2, 3)
101+
println(r1.isSquare)
102+
println(r2.isSquare)
103+
println(r1 === r1)
104+
println(r1 === r2)
105+
println(List(1, 2, 3).second)
106+
println(List(List(1), List(2, 3)).flattened)
107+
println(List(List(1), List(2, 3)).flattened.maxx)
108+
println(Array(1, 2, 3).maxx)
109+
println((2, 3).isSame)
110+
println((3, 3).isSame)
111+
}

0 commit comments

Comments
 (0)