Skip to content

Commit 55d2017

Browse files
committed
Allow , in parents of template
New syntax A extends B, C { ... } instead of A extends B with C { ... } The old syntax is still allowed, but it's planned to phase it out together with `with` as a type operator. Also: disallow A extends { ... } (make it a migration warning under -language:Scala2) While doing these changes, applied some refactorings to handle templates in the parser which hopefully make the code clearer.
1 parent 63ee01a commit 55d2017

File tree

12 files changed

+98
-60
lines changed

12 files changed

+98
-60
lines changed

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

Lines changed: 71 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -1392,7 +1392,7 @@ object Parsers {
13921392
}
13931393
else simpleExpr()
13941394

1395-
/** SimpleExpr ::= new Template
1395+
/** SimpleExpr ::= new’ (ConstrApp [TemplateBody] | TemplateBody)
13961396
* | BlockExpr
13971397
* | ‘'{’ BlockExprContents ‘}’
13981398
* | ‘'(’ ExprsInParens ‘)’
@@ -1436,15 +1436,7 @@ object Parsers {
14361436
atPos(in.offset)(Quote(inBrackets(typ())))
14371437
case NEW =>
14381438
canApply = false
1439-
val start = in.skipToken()
1440-
val (impl, missingBody) = template(emptyConstructor)
1441-
impl.parents match {
1442-
case parent :: Nil if missingBody =>
1443-
if (parent.isType) ensureApplied(wrapNew(parent))
1444-
else parent.withPos(Position(start, in.lastOffset))
1445-
case _ =>
1446-
New(impl.withPos(Position(start, in.lastOffset)))
1447-
}
1439+
newExpr()
14481440
case _ =>
14491441
if (isLiteral) literal()
14501442
else {
@@ -1474,6 +1466,33 @@ object Parsers {
14741466
}
14751467
}
14761468

1469+
/** SimpleExpr ::= ‘new’ (ConstrApp {`with` ConstrApp} [TemplateBody] | TemplateBody)
1470+
*/
1471+
def newExpr(): Tree = {
1472+
val start = in.skipToken()
1473+
def reposition(t: Tree) = t.withPos(Position(start, in.lastOffset))
1474+
newLineOptWhenFollowedBy(LBRACE)
1475+
val parents =
1476+
if (in.token == LBRACE) Nil
1477+
else constrApp() :: {
1478+
if (in.token == WITH) {
1479+
// Enable this for 3.1, when we drop `with` for inheritance:
1480+
// in.errorUnlessInScala2Mode(
1481+
// "anonymous class with multiple parents is no longer supported; use a named class instead")
1482+
in.nextToken()
1483+
tokenSeparated(WITH, constrApp)
1484+
}
1485+
else Nil
1486+
}
1487+
newLineOptWhenFollowedBy(LBRACE)
1488+
parents match {
1489+
case parent :: Nil if in.token != LBRACE =>
1490+
reposition(if (parent.isType) ensureApplied(wrapNew(parent)) else parent)
1491+
case _ =>
1492+
New(reposition(templateBodyOpt(emptyConstructor, parents, isEnum = false)))
1493+
}
1494+
}
1495+
14771496
/** ExprsInParens ::= ExprInParens {`,' ExprInParens}
14781497
*/
14791498
def exprsInParensOpt(): List[Tree] =
@@ -2381,18 +2400,18 @@ object Parsers {
23812400
}
23822401

23832402
def objectDefRest(start: Offset, mods: Modifiers, name: TermName): ModuleDef = {
2384-
val template = templateOpt(emptyConstructor)
2385-
ModuleDef(name, template).withMods(mods).setComment(in.getDocComment(start))
2403+
val templ = templateOpt(emptyConstructor)
2404+
ModuleDef(name, templ).withMods(mods).setComment(in.getDocComment(start))
23862405
}
23872406

2388-
/** EnumDef ::= id ClassConstr [`extends' [ConstrApps]] EnumBody
2407+
/** EnumDef ::= id ClassConstr [`extends' ConstrApps] EnumBody
23892408
*/
23902409
def enumDef(start: Offset, mods: Modifiers, enumMod: Mod): TypeDef = atPos(start, nameStart) {
23912410
val modName = ident()
23922411
val clsName = modName.toTypeName
23932412
val constr = classConstr(clsName)
2394-
val impl = templateOpt(constr, isEnum = true)
2395-
TypeDef(clsName, impl).withMods(addMod(mods, enumMod)).setComment(in.getDocComment(start))
2413+
val templ = templateOpt(constr, isEnum = true)
2414+
TypeDef(clsName, templ).withMods(addMod(mods, enumMod)).setComment(in.getDocComment(start))
23962415
}
23972416

23982417
/** EnumCase = `case' (id ClassConstr [`extends' ConstrApps] | ids)
@@ -2448,34 +2467,48 @@ object Parsers {
24482467
else t
24492468
}
24502469

2451-
/** Template ::= ConstrApps [TemplateBody] | TemplateBody
2452-
* ConstrApps ::= ConstrApp {`with' ConstrApp}
2453-
*
2454-
* @return a pair consisting of the template, and a boolean which indicates
2455-
* whether the template misses a body (i.e. no {...} part).
2470+
/** ConstrApps ::= ConstrApp {‘with’ ConstrApp} (to be deprecated in 3.1)
2471+
* | ConstrApp {‘,’ ConstrApp}
24562472
*/
2457-
def template(constr: DefDef, isEnum: Boolean = false): (Template, Boolean) = {
2473+
def constrApps(): List[Tree] = {
2474+
val t = constrApp()
2475+
val ts =
2476+
if (in.token == WITH) {
2477+
in.nextToken()
2478+
tokenSeparated(WITH, constrApp)
2479+
}
2480+
else if (in.token == COMMA) {
2481+
in.nextToken()
2482+
tokenSeparated(COMMA, constrApp)
2483+
}
2484+
else Nil
2485+
t :: ts
2486+
}
2487+
2488+
/** Template ::= [‘extends’ ConstrApps] [TemplateBody]
2489+
*/
2490+
def template(constr: DefDef, isEnum: Boolean = false): Template = {
2491+
val parents =
2492+
if (in.token == EXTENDS) {
2493+
in.nextToken()
2494+
constrApps()
2495+
}
2496+
else Nil
24582497
newLineOptWhenFollowedBy(LBRACE)
2459-
if (in.token == LBRACE) (templateBodyOpt(constr, Nil, isEnum), false)
2460-
else {
2461-
val parents = tokenSeparated(WITH, constrApp)
2462-
newLineOptWhenFollowedBy(LBRACE)
2463-
if (isEnum && in.token != LBRACE)
2464-
syntaxErrorOrIncomplete(ExpectedTokenButFound(LBRACE, in.token))
2465-
val missingBody = in.token != LBRACE
2466-
(templateBodyOpt(constr, parents, isEnum), missingBody)
2467-
}
2498+
if (isEnum && in.token != LBRACE)
2499+
syntaxErrorOrIncomplete(ExpectedTokenButFound(LBRACE, in.token))
2500+
templateBodyOpt(constr, parents, isEnum)
24682501
}
24692502

2470-
/** TemplateOpt = [`extends' Template | TemplateBody]
2503+
/** TemplateOpt = [Template]
24712504
*/
2472-
def templateOpt(constr: DefDef, isEnum: Boolean = false): Template =
2473-
if (in.token == EXTENDS) { in.nextToken(); template(constr, isEnum)._1 }
2474-
else {
2475-
newLineOptWhenFollowedBy(LBRACE)
2476-
if (in.token == LBRACE) template(constr, isEnum)._1
2477-
else Template(constr, Nil, EmptyValDef, Nil)
2478-
}
2505+
def templateOpt(constr: DefDef, isEnum: Boolean = false): Template = {
2506+
newLineOptWhenFollowedBy(LBRACE)
2507+
if (in.token == EXTENDS || in.token == LBRACE)
2508+
template(constr, isEnum)
2509+
else
2510+
Template(constr, Nil, EmptyValDef, Nil)
2511+
}
24792512

24802513
/** TemplateBody ::= [nl] `{' TemplateStatSeq `}'
24812514
*/

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

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -250,6 +250,11 @@ object Scanners {
250250
isScala2Mode
251251
}
252252

253+
/** A migration warning if in Scala-2 mode, an error otherwise */
254+
def errorOrMigrationWarning(msg: String, pos: Position = Position(offset)): Unit =
255+
if (isScala2Mode) ctx.migrationWarning(msg, source.atPos(pos))
256+
else ctx.error(msg, source.atPos(pos))
257+
253258
// Get next token ------------------------------------------------------------
254259

255260
/** Are we directly in a string interpolation expression?

docs/docs/internals/syntax.md

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -186,7 +186,7 @@ PostfixExpr ::= InfixExpr [id]
186186
InfixExpr ::= PrefixExpr
187187
| InfixExpr id [nl] InfixExpr InfixOp(expr, op, expr)
188188
PrefixExpr ::= [‘-’ | ‘+’ | ‘~’ | ‘!’] SimpleExpr PrefixOp(expr, op)
189-
SimpleExpr ::= ‘new’ Template New(templ)
189+
SimpleExpr ::= ‘new’ (ConstrApp [TemplateBody] | TemplateBody) New(constr | templ)
190190
| BlockExpr
191191
| ''{’ BlockExprContents ‘}’
192192
| ‘'(’ ExprsInParens ‘)’
@@ -340,14 +340,14 @@ DefDef ::= DefSig [(‘:’ | ‘<:’) Type] ‘=’ Expr
340340
TmplDef ::= ([‘case’] ‘class’ | ‘trait’) ClassDef
341341
| [‘case’] ‘object’ ObjectDef
342342
| ‘enum’ EnumDef
343-
ClassDef ::= id ClassConstr TemplateOpt ClassDef(mods, name, tparams, templ)
344-
ClassConstr ::= [ClsTypeParamClause] [ConstrMods] ClsParamClauses with DefDef(_, <init>, Nil, vparamss, EmptyTree, EmptyTree) as first stat
343+
ClassDef ::= id ClassConstr [Template] ClassDef(mods, name, tparams, templ)
344+
ClassConstr ::= [ClsTypeParamClause] [ConstrMods] ClsParamClauses with DefDef(_, <init>, Nil, vparamss, EmptyTree, EmptyTree) as first stat
345345
ConstrMods ::= {Annotation} [AccessModifier]
346-
ObjectDef ::= id TemplateOpt ModuleDef(mods, name, template) // no constructor
347-
EnumDef ::= id ClassConstr [‘extends’ [ConstrApps]] EnumBody EnumDef(mods, name, tparams, template)
348-
TemplateOpt ::= [‘extends’ Template | [nl] TemplateBody]
346+
ObjectDef ::= id [Template] ModuleDef(mods, name, template) // no constructor
347+
EnumDef ::= id ClassConstr [‘extends’ ConstrApps] EnumBody EnumDef(mods, name, tparams, template)
349348
Template ::= ConstrApps [TemplateBody] | TemplateBody Template(constr, parents, self, stats)
350349
ConstrApps ::= ConstrApp {‘with’ ConstrApp}
350+
| ConstrApp {‘,’ ConstrApp}
351351
ConstrApp ::= AnnotType {ArgumentExprs} Apply(tp, args)
352352
ConstrExpr ::= SelfInvocation
353353
| ConstrBlock

tests/invalid/neg/typelevel.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -53,7 +53,7 @@ object Test {
5353
val rr1 = new Deco1(HCons(1, HNil)) ++ HNil
5454
val rr1a: HCons[Int, HNil.type] = rr1 // error (type error because no inline)
5555

56-
class Deco2(val as: HList) extends java.lang.Cloneable with java.lang.Comparable[Deco2] {
56+
class Deco2(val as: HList) extends java.lang.Cloneable, java.lang.Comparable[Deco2] {
5757
inline def ++ (bs: HList) = concat(as, bs)
5858
}
5959
}

tests/neg/i2770.scala

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ trait C2 extends B2 { type L[X, Y] <: String } // error: illegal override
88
trait D { type I }
99
trait E extends D { type I <: String }
1010
trait F extends D { type I >: String }
11-
trait G extends E with F // ok
11+
trait G extends E, F // ok
1212

1313
trait H extends D { type I >: Int }
14-
trait H2 extends E with H // error: illegal override
14+
trait H2 extends E, H // error: illegal override

tests/pos/Iter2.scala

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -58,16 +58,16 @@ object Iter2 {
5858
def fromIterator[B](it: Iterator[B]): C[B]
5959
}
6060

61-
trait Iterable[+IA] extends IterableOnce[IA] with FromIterator[Iterable] {
61+
trait Iterable[+IA] extends IterableOnce[IA], FromIterator[Iterable] {
6262
def view: View[IA] = new View(iterator)
6363
}
6464

65-
trait Seq[+AA] extends Iterable[AA] with FromIterator[Seq] {
65+
trait Seq[+AA] extends Iterable[AA], FromIterator[Seq] {
6666
def apply(i: Int): AA
6767
def length: Int
6868
}
6969

70-
sealed trait List[+A] extends Seq[A] with FromIterator[List] {
70+
sealed trait List[+A] extends Seq[A], FromIterator[List] {
7171
def isEmpty: Boolean
7272
def head: A
7373
def tail: List[A]
@@ -84,7 +84,7 @@ object Iter2 {
8484
if (isEmpty) 0 else 1 + tail.length
8585
}
8686

87-
class View[+A](it: Iterator[A]) extends Iterable[A] with FromIterator[View] {
87+
class View[+A](it: Iterator[A]) extends Iterable[A], FromIterator[View] {
8888
def iterator: Iterator[A] = it.copy
8989
def fromIterator[B](it: Iterator[B]): View[B] = new View(it)
9090
}
@@ -101,7 +101,7 @@ object Iter2 {
101101
def tail = ???
102102
}
103103

104-
class ArrayBuffer[A] private (initElems: Array[AnyRef], initLen: Int) extends Seq[A] with FromIterator[ArrayBuffer] {
104+
class ArrayBuffer[A] private (initElems: Array[AnyRef], initLen: Int) extends Seq[A], FromIterator[ArrayBuffer] {
105105
def this() = this(new Array[AnyRef](16), 0)
106106
def this(it: ArrayIterator[A]) = this(it.elems, it.len)
107107
private var elems: Array[AnyRef] = initElems
@@ -116,7 +116,7 @@ object Iter2 {
116116
def length = len
117117
}
118118
/*
119-
class SeqView[A](itf: () => Iterator) extends Seq[A] with FromIterator[SeqView] {
119+
class SeqView[A](itf: () => Iterator) extends Seq[A], FromIterator[SeqView] {
120120
def iterator = it
121121
def buildIterator = it
122122
def fromIterator[B](it: Iterator[B]) = it match {

tests/pos/this-types.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ trait C extends A {
99
type A_This = C_This
1010
type C_This <: C
1111
}
12-
trait D extends B with C {
12+
trait D extends B, C {
1313
type B_This = D_This
1414
type C_This = D_This
1515
type D_This <: D

tests/pos/traits_1.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ object Test {
1717
case _ => false
1818
}
1919
}
20-
trait BorderedColoredShape extends Shape with Bordered with Colored {
20+
trait BorderedColoredShape extends Shape, Bordered, Colored {
2121
override def equals(other: Any) = other match {
2222
case that: BorderedColoredShape => (
2323
super.equals(that) &&

tests/pos/typeclass-encoding.scala

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -65,7 +65,7 @@ object semiGroups {
6565
type StaticPart[X] = MonoidStatic[X]
6666
}
6767

68-
implicit object extend_Int_Monoid extends MonoidStatic[Int] with Implementation[Int] {
68+
implicit object extend_Int_Monoid extends MonoidStatic[Int], Implementation[Int] {
6969
type Implemented = Monoid
7070
def unit: Int = 0
7171
def inject($this: Int) = new Monoid {
@@ -74,7 +74,7 @@ object semiGroups {
7474
}
7575
}
7676

77-
implicit object extend_String_Monoid extends MonoidStatic[String] with Implementation[String] {
77+
implicit object extend_String_Monoid extends MonoidStatic[String], Implementation[String] {
7878
type Implemented = Monoid
7979
def unit = ""
8080
def inject($this: String): Monoid { type This = String } =

tests/run-separate-compilation/tasty-positioned.check

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,4 +5,4 @@ acbvasdfa columns:13-36 lines:12-12
55
acbvasdfa columns:13-24 lines:13-13
66
a
77
b columns:6-25 lines:15-16
8-
Foo columns:16-19 lines:17-17
8+
Foo columns:12-19 lines:17-17

tests/run/generic/SearchResult.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ import Shapes._
1111
*/
1212
sealed trait SearchResult extends Enum
1313

14-
object SearchResult extends {
14+
object SearchResult {
1515

1616
private val $values = new runtime.EnumValues[SearchResult]
1717
def valueOf = $values.fromInt

tests/run/t6090.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
class X { def ==(other: X) = true }
22
class V(val x: X) extends AnyVal
3-
object Test extends {
3+
object Test {
44
def main(args: Array[String]) =
55
assert((new V(new X) == new V(new X)))
66
}

0 commit comments

Comments
 (0)