Skip to content

Commit 0dc0191

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 4ba7da3 commit 0dc0191

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
@@ -1407,7 +1407,7 @@ object Parsers {
14071407
}
14081408
else simpleExpr()
14091409

1410-
/** SimpleExpr ::= new Template
1410+
/** SimpleExpr ::= new’ (ConstrApp [TemplateBody] | TemplateBody)
14111411
* | BlockExpr
14121412
* | ‘'{’ BlockExprContents ‘}’
14131413
* | ‘'(’ ExprsInParens ‘)’
@@ -1451,15 +1451,7 @@ object Parsers {
14511451
atSpan(in.offset)(Quote(inBrackets(typ())))
14521452
case NEW =>
14531453
canApply = false
1454-
val start = in.skipToken()
1455-
val (impl, missingBody) = template(emptyConstructor)
1456-
impl.parents match {
1457-
case parent :: Nil if missingBody =>
1458-
if (parent.isType) ensureApplied(wrapNew(parent))
1459-
else parent.withSpan(Span(start, in.lastOffset))
1460-
case _ =>
1461-
New(impl.withSpan(Span(start, in.lastOffset)))
1462-
}
1454+
newExpr()
14631455
case _ =>
14641456
if (isLiteral) literal()
14651457
else {
@@ -1489,6 +1481,33 @@ object Parsers {
14891481
}
14901482
}
14911483

1484+
/** SimpleExpr ::= ‘new’ (ConstrApp {`with` ConstrApp} [TemplateBody] | TemplateBody)
1485+
*/
1486+
def newExpr(): Tree = {
1487+
val start = in.skipToken()
1488+
def reposition(t: Tree) = t.withSpan(Span(start, in.lastOffset))
1489+
newLineOptWhenFollowedBy(LBRACE)
1490+
val parents =
1491+
if (in.token == LBRACE) Nil
1492+
else constrApp() :: {
1493+
if (in.token == WITH) {
1494+
// Enable this for 3.1, when we drop `with` for inheritance:
1495+
// in.errorUnlessInScala2Mode(
1496+
// "anonymous class with multiple parents is no longer supported; use a named class instead")
1497+
in.nextToken()
1498+
tokenSeparated(WITH, constrApp)
1499+
}
1500+
else Nil
1501+
}
1502+
newLineOptWhenFollowedBy(LBRACE)
1503+
parents match {
1504+
case parent :: Nil if in.token != LBRACE =>
1505+
reposition(if (parent.isType) ensureApplied(wrapNew(parent)) else parent)
1506+
case _ =>
1507+
New(reposition(templateBodyOpt(emptyConstructor, parents, isEnum = false)))
1508+
}
1509+
}
1510+
14921511
/** ExprsInParens ::= ExprInParens {`,' ExprInParens}
14931512
*/
14941513
def exprsInParensOpt(): List[Tree] =
@@ -2414,18 +2433,18 @@ object Parsers {
24142433
}
24152434

24162435
def objectDefRest(start: Offset, mods: Modifiers, name: TermName): ModuleDef = {
2417-
val template = templateOpt(emptyConstructor)
2418-
finalizeDef(ModuleDef(name, template), mods, start)
2436+
val templ = templateOpt(emptyConstructor)
2437+
finalizeDef(ModuleDef(name, templ), mods, start)
24192438
}
24202439

2421-
/** EnumDef ::= id ClassConstr [`extends' [ConstrApps]] EnumBody
2440+
/** EnumDef ::= id ClassConstr [`extends' ConstrApps] EnumBody
24222441
*/
24232442
def enumDef(start: Offset, mods: Modifiers, enumMod: Mod): TypeDef = atSpan(start, nameStart) {
24242443
val modName = ident()
24252444
val clsName = modName.toTypeName
24262445
val constr = classConstr()
2427-
val impl = templateOpt(constr, isEnum = true)
2428-
finalizeDef(TypeDef(clsName, impl), addMod(mods, enumMod), start)
2446+
val templ = templateOpt(constr, isEnum = true)
2447+
finalizeDef(TypeDef(clsName, templ), addMod(mods, enumMod), start)
24292448
}
24302449

24312450
/** EnumCase = `case' (id ClassConstr [`extends' ConstrApps] | ids)
@@ -2481,34 +2500,48 @@ object Parsers {
24812500
else t
24822501
}
24832502

2484-
/** Template ::= ConstrApps [TemplateBody] | TemplateBody
2485-
* ConstrApps ::= ConstrApp {`with' ConstrApp}
2486-
*
2487-
* @return a pair consisting of the template, and a boolean which indicates
2488-
* whether the template misses a body (i.e. no {...} part).
2503+
/** ConstrApps ::= ConstrApp {‘with’ ConstrApp} (to be deprecated in 3.1)
2504+
* | ConstrApp {‘,’ ConstrApp}
24892505
*/
2490-
def template(constr: DefDef, isEnum: Boolean = false): (Template, Boolean) = {
2506+
def constrApps(): List[Tree] = {
2507+
val t = constrApp()
2508+
val ts =
2509+
if (in.token == WITH) {
2510+
in.nextToken()
2511+
tokenSeparated(WITH, constrApp)
2512+
}
2513+
else if (in.token == COMMA) {
2514+
in.nextToken()
2515+
tokenSeparated(COMMA, constrApp)
2516+
}
2517+
else Nil
2518+
t :: ts
2519+
}
2520+
2521+
/** Template ::= [‘extends’ ConstrApps] [TemplateBody]
2522+
*/
2523+
def template(constr: DefDef, isEnum: Boolean = false): Template = {
2524+
val parents =
2525+
if (in.token == EXTENDS) {
2526+
in.nextToken()
2527+
constrApps()
2528+
}
2529+
else Nil
24912530
newLineOptWhenFollowedBy(LBRACE)
2492-
if (in.token == LBRACE) (templateBodyOpt(constr, Nil, isEnum), false)
2493-
else {
2494-
val parents = tokenSeparated(WITH, constrApp)
2495-
newLineOptWhenFollowedBy(LBRACE)
2496-
if (isEnum && in.token != LBRACE)
2497-
syntaxErrorOrIncomplete(ExpectedTokenButFound(LBRACE, in.token))
2498-
val missingBody = in.token != LBRACE
2499-
(templateBodyOpt(constr, parents, isEnum), missingBody)
2500-
}
2531+
if (isEnum && in.token != LBRACE)
2532+
syntaxErrorOrIncomplete(ExpectedTokenButFound(LBRACE, in.token))
2533+
templateBodyOpt(constr, parents, isEnum)
25012534
}
25022535

2503-
/** TemplateOpt = [`extends' Template | TemplateBody]
2536+
/** TemplateOpt = [Template]
25042537
*/
2505-
def templateOpt(constr: DefDef, isEnum: Boolean = false): Template =
2506-
if (in.token == EXTENDS) { in.nextToken(); template(constr, isEnum)._1 }
2507-
else {
2508-
newLineOptWhenFollowedBy(LBRACE)
2509-
if (in.token == LBRACE) template(constr, isEnum)._1
2510-
else Template(constr, Nil, EmptyValDef, Nil)
2511-
}
2538+
def templateOpt(constr: DefDef, isEnum: Boolean = false): Template = {
2539+
newLineOptWhenFollowedBy(LBRACE)
2540+
if (in.token == EXTENDS || in.token == LBRACE)
2541+
template(constr, isEnum)
2542+
else
2543+
Template(constr, Nil, EmptyValDef, Nil)
2544+
}
25122545

25132546
/** TemplateBody ::= [nl] `{' TemplateStatSeq `}'
25142547
*/

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 ‘)’
@@ -341,14 +341,14 @@ DefDef ::= DefSig [(‘:’ | ‘<:’) Type] ‘=’ Expr
341341
TmplDef ::= ([‘case’] ‘class’ | ‘trait’) ClassDef
342342
| [‘case’] ‘object’ ObjectDef
343343
| ‘enum’ EnumDef
344-
ClassDef ::= id ClassConstr TemplateOpt ClassDef(mods, name, tparams, templ)
345-
ClassConstr ::= [ClsTypeParamClause] [ConstrMods] ClsParamClauses with DefDef(_, <init>, Nil, vparamss, EmptyTree, EmptyTree) as first stat
344+
ClassDef ::= id ClassConstr [Template] ClassDef(mods, name, tparams, templ)
345+
ClassConstr ::= [ClsTypeParamClause] [ConstrMods] ClsParamClauses with DefDef(_, <init>, Nil, vparamss, EmptyTree, EmptyTree) as first stat
346346
ConstrMods ::= {Annotation} [AccessModifier]
347-
ObjectDef ::= id TemplateOpt ModuleDef(mods, name, template) // no constructor
348-
EnumDef ::= id ClassConstr [‘extends’ [ConstrApps]] EnumBody EnumDef(mods, name, tparams, template)
349-
TemplateOpt ::= [‘extends’ Template | [nl] TemplateBody]
347+
ObjectDef ::= id [Template] ModuleDef(mods, name, template) // no constructor
348+
EnumDef ::= id ClassConstr [‘extends’ ConstrApps] EnumBody EnumDef(mods, name, tparams, template)
350349
Template ::= ConstrApps [TemplateBody] | TemplateBody Template(constr, parents, self, stats)
351350
ConstrApps ::= ConstrApp {‘with’ ConstrApp}
351+
| ConstrApp {‘,’ ConstrApp}
352352
ConstrApp ::= AnnotType {ArgumentExprs} Apply(tp, args)
353353
ConstrExpr ::= SelfInvocation
354354
| 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/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
}

tests/run/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

0 commit comments

Comments
 (0)