Skip to content

Commit

Permalink
Allow with after class
Browse files Browse the repository at this point in the history
  • Loading branch information
odersky committed Jan 20, 2021
1 parent dd68bea commit acdee20
Show file tree
Hide file tree
Showing 319 changed files with 874 additions and 855 deletions.
48 changes: 35 additions & 13 deletions compiler/src/dotty/tools/dotc/parsing/Parsers.scala
Original file line number Diff line number Diff line change
Expand Up @@ -1288,7 +1288,7 @@ object Parsers {
else
in.nextToken()
if in.token != INDENT && in.token != LBRACE then
syntaxErrorOrIncomplete(i"indented definitions expected, ${in}")
syntaxErrorOrIncomplete(i"indented definitions expected, ${in} found")
else
newLineOptWhenFollowedBy(LBRACE)

Expand Down Expand Up @@ -2315,7 +2315,7 @@ object Parsers {
possibleTemplateStart()
val parents =
if in.isNestedStart then Nil
else constrApps(commaOK = false)
else constrApp() :: withConstrApps()
colonAtEOLOpt()
possibleTemplateStart(isNew = true)
parents match {
Expand Down Expand Up @@ -3494,7 +3494,7 @@ object Parsers {
val parents =
if (in.token == EXTENDS) {
in.nextToken()
constrApps(commaOK = true)
constrApps()
}
else Nil
Template(constr, parents, Nil, EmptyValDef, Nil)
Expand Down Expand Up @@ -3626,24 +3626,37 @@ object Parsers {
// Using Ident(tpnme.ERROR) to avoid causing cascade errors on non-user-written code
if in.token == LPAREN then parArgumentExprss(wrapNew(t)) else t

/** ConstrApps ::= ConstrApp {(‘,’ | ‘with’) ConstrApp}
/** ConstrApps ::= ConstrApp ({‘,’ ConstrApp} | {‘with’ ConstrApp})
*/
def constrApps(commaOK: Boolean): List[Tree] =
def constrApps(): List[Tree] =
val t = constrApp()
val ts =
if in.token == WITH || commaOK && in.token == COMMA then
in.nextToken()
constrApps(commaOK)
else Nil
val ts = if in.token == COMMA then commaConstrApps() else withConstrApps()
t :: ts

/** `{`,` ConstrApp} */
def commaConstrApps(): List[Tree] =
if in.token == COMMA then
in.nextToken()
constrApp() :: commaConstrApps()
else Nil

/** `{`with` ConstrApp} but no EOL allowed after `with`.
*/
def withConstrApps(): List[Tree] =
def isTemplateStart =
val la = in.lookahead
la.isAfterLineEnd || la.token == LBRACE
la.token == LBRACE
|| la.isAfterLineEnd
&& {
if migrateTo3 then
warning(
em"""In Scala 3, `with` at the end of a line will start definitions,
|so it cannot be used in front of a parent constructor anymore.
|Place the `with` at the beginning of the next line instead.""")
false
else
true
}
if in.token == WITH && !isTemplateStart then
in.nextToken()
constrApp() :: withConstrApps()
Expand All @@ -3662,7 +3675,7 @@ object Parsers {
in.sourcePos())
Nil
}
else constrApps(commaOK = true)
else constrApps()
}
else Nil
newLinesOptWhenFollowedBy(nme.derives)
Expand Down Expand Up @@ -3806,7 +3819,16 @@ object Parsers {
}
else {
stats += first
acceptStatSepUnlessAtEnd(stats)
if in.token == WITH then
syntaxError(
i"""end of statement expected but ${showToken(WITH)} found
|
|Maybe you meant to write a mixin in an extends clause?
|Note that this requires the `with` to come first now.
|I.e.
|
| with $first""")
else acceptStatSepUnlessAtEnd(stats)
}
}
var exitOnError = false
Expand Down
12 changes: 6 additions & 6 deletions compiler/src/dotty/tools/dotc/parsing/Scanners.scala
Original file line number Diff line number Diff line change
Expand Up @@ -1338,7 +1338,7 @@ object Scanners {
* InBraces a pair of braces { ... }
* Indented a pair of <indent> ... <outdent> tokens
*/
abstract class Region:
abstract class Region(val code: String):
/** The region enclosing this one, or `null` for the outermost region */
def outer: Region | Null

Expand Down Expand Up @@ -1367,17 +1367,17 @@ object Scanners {
knownWidth = enclosing.knownWidth
end Region

case class InString(multiLine: Boolean, outer: Region) extends Region
case class InParens(prefix: Token, outer: Region) extends Region
case class InBraces(outer: Region) extends Region
case class InCase(outer: Region) extends Region
case class InString(multiLine: Boolean, outer: Region) extends Region("IS")
case class InParens(prefix: Token, outer: Region) extends Region("IP")
case class InBraces(outer: Region) extends Region("IB")
case class InCase(outer: Region) extends Region("IC")

/** A class describing an indentation region.
* @param width The principal indendation width
* @param others Other indendation widths > width of lines in the same region
* @param prefix The token before the initial <indent> of the region
*/
case class Indented(width: IndentWidth, others: Set[IndentWidth], prefix: Token, outer: Region | Null) extends Region:
case class Indented(width: IndentWidth, others: Set[IndentWidth], prefix: Token, outer: Region | Null) extends Region("II"):
knownWidth = width

def topLevelRegion(width: IndentWidth) = Indented(width, Set(), EMPTY, null)
Expand Down
18 changes: 8 additions & 10 deletions docs/docs/internals/syntax.md
Original file line number Diff line number Diff line change
Expand Up @@ -83,7 +83,6 @@ comment ::= ‘/*’ “any sequence of characters; nested comments ar
nl ::= “new line character”
semi ::= ‘;’ | nl {nl}
colonEol ::= ": at end of line that can start a template body"
```

## Keywords
Expand Down Expand Up @@ -218,9 +217,8 @@ SimpleExpr ::= SimpleRef
| ‘$’ ‘{’ Block ‘}’
| Quoted
| quoteId -- only inside splices
| ‘new’ ConstrApp {‘with’ ConstrApp} New(constr | templ)
[[colonEol] TemplateBody
| ‘new’ [colonEol] TemplateBody
| ‘new’ ConstrApp {‘with’ ConstrApp} [TemplateBody] New(constr | templ)
| ‘new’ TemplateBody
| ‘(’ ExprsInParens ‘)’ Parens(exprs)
| SimpleExpr ‘.’ id Select(expr, id)
| SimpleExpr ‘.’ MatchClause
Expand Down Expand Up @@ -386,23 +384,23 @@ ClassDef ::= id ClassConstr [Template]
ClassConstr ::= [ClsTypeParamClause] [ConstrMods] ClsParamClauses with DefDef(_, <init>, Nil, vparamss, EmptyTree, EmptyTree) as first stat
ConstrMods ::= {Annotation} [AccessModifier]
ObjectDef ::= id [Template] ModuleDef(mods, name, template) // no constructor
EnumDef ::= id ClassConstr InheritClauses [colonEol] EnumBody
EnumDef ::= id ClassConstr InheritClauses EnumBody
GivenDef ::= [GivenSig] (Type [‘=’ Expr] | StructuralInstance)
GivenSig ::= [id] [DefTypeParamClause] {UsingParamClause} ‘:’ -- one of `id`, `DefParamClause`, `UsingParamClause` must be present
StructuralInstance ::= ConstrApp {‘with’ ConstrApp} ‘with’ TemplateBody
StructuralInstance ::= ConstrApp {‘with’ ConstrApp} TemplateBody
Extension ::= ‘extension’ [DefTypeParamClause] ‘(’ DefParam ‘)’
{UsingParamClause}] ExtMethods
ExtMethods ::= ExtMethod | [nl] ‘{’ ExtMethod {semi ExtMethod ‘}’
ExtMethod ::= {Annotation [nl]} {Modifier} ‘def’ DefDef
Template ::= InheritClauses [colonEol] [TemplateBody] Template(constr, parents, self, stats)
Template ::= InheritClauses [TemplateBody] Template(constr, parents, self, stats)
InheritClauses ::= [‘extends’ ConstrApps] [‘derives’ QualId {‘,’ QualId}]
ConstrApps ::= ConstrApp ({‘,’ ConstrApp} | {‘with’ ConstrApp})
ConstrApp ::= SimpleType1 {Annotation} {ParArgumentExprs} Apply(tp, args)
ConstrExpr ::= SelfInvocation
| ‘{’ SelfInvocation {semi BlockStat} ‘}’
SelfInvocation ::= ‘this’ ArgumentExprs {ArgumentExprs}
TemplateBody ::= [nl] ‘{’ [SelfType] TemplateStat {semi TemplateStat} ‘}’
TemplateBody ::= [nl | ‘with’] ‘{’ [SelfType] TemplateStat {semi TemplateStat} ‘}’
TemplateStat ::= Import
| Export
| {Annotation [nl]} {Modifier} Def
Expand All @@ -414,7 +412,7 @@ TemplateStat ::= Import
SelfType ::= id [‘:’ InfixType] ‘=>’ ValDef(_, name, tpt, _)
| ‘this’ ‘:’ InfixType ‘=>’
EnumBody ::= [nl] ‘{’ [SelfType] EnumStat {semi EnumStat} ‘}’
EnumBody ::= [nl | ‘with’] ‘{’ [SelfType] EnumStat {semi EnumStat} ‘}’
EnumStat ::= TemplateStat
| {Annotation [nl]} {Modifier} EnumCase
EnumCase ::= ‘case’ (id ClassConstr [‘extends’ ConstrApps]] | ids)
Expand All @@ -428,7 +426,7 @@ TopStat ::= Import
| PackageObject
| EndMarker
|
Packaging ::= ‘package’ QualId [nl | colonEol] ‘{’ TopStatSeq ‘}’ Package(qid, stats)
Packaging ::= ‘package’ QualId [nl| ‘with’] ‘{’ TopStatSeq ‘}’ Package(qid, stats)
PackageObject ::= ‘package’ ‘object’ ObjectDef object with package in mods.
CompilationUnit ::= {‘package’ QualId semi} TopStatSeq Package(qid, stats)
Expand Down
6 changes: 3 additions & 3 deletions docs/docs/reference/changed-features/compiler-plugins.md
Original file line number Diff line number Diff line change
Expand Up @@ -62,14 +62,14 @@ import dotty.tools.dotc.core.Symbols._
import dotty.tools.dotc.plugins.{PluginPhase, StandardPlugin}
import dotty.tools.dotc.transform.{Pickler, Staging}

class DivideZero extends StandardPlugin:
class DivideZero extends StandardPlugin with
val name: String = "divideZero"
override val description: String = "divide zero check"

def init(options: List[String]): List[PluginPhase] =
(new DivideZeroPhase) :: Nil

class DivideZeroPhase extends PluginPhase:
class DivideZeroPhase extends PluginPhase with
import tpd._

val phaseName = "divideZero"
Expand Down Expand Up @@ -108,7 +108,7 @@ import dotty.tools.dotc.core.Contexts.Context
import dotty.tools.dotc.core.Phases.Phase
import dotty.tools.dotc.plugins.ResearchPlugin

class DummyResearchPlugin extends ResearchPlugin:
class DummyResearchPlugin extends ResearchPlugin with
val name: String = "dummy"
override val description: String = "dummy research plugin"

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ The standard library defines an abstract class `Conversion`:
```scala
package scala
@java.lang.FunctionalInterface
abstract class Conversion[-T, +U] extends Function1[T, U]:
abstract class Conversion[-T, +U] extends Function1[T, U] with
def apply(x: T): U
```

Expand Down
2 changes: 1 addition & 1 deletion docs/docs/reference/changed-features/main-functions.md
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,7 @@ The Scala compiler generates a program from a `@main` method `f` as follows:
For instance, the `happyBirthDay` method above would generate additional code equivalent to the following class:

```scala
final class happyBirthday:
final class happyBirthday with
import scala.util.{CommandLineParser => CLP}
<static> def main(args: Array[String]): Unit =
try
Expand Down
6 changes: 3 additions & 3 deletions docs/docs/reference/changed-features/numeric-literals.md
Original file line number Diff line number Diff line change
Expand Up @@ -130,7 +130,7 @@ class MalformedNumber(msg: String = "malformed number literal") extends FromDigi
As a fully worked out example, here is an implementation of a new numeric class, `BigFloat`, that accepts numeric literals. `BigFloat` is defined in terms of a `BigInt` mantissa and an `Int` exponent:

```scala
case class BigFloat(mantissa: BigInt, exponent: Int):
case class BigFloat(mantissa: BigInt, exponent: Int) with
override def toString = s"${mantissa}e${exponent}"
```

Expand All @@ -145,7 +145,7 @@ The companion object of `BigFloat` defines an `apply` constructor method to cons
from a `digits` string. Here is a possible implementation:

```scala
object BigFloat:
object BigFloat with
import scala.util.FromDigits

def apply(digits: String): BigFloat =
Expand Down Expand Up @@ -206,7 +206,7 @@ To do this, replace the `FromDigits` instance in the `BigFloat` object by the fo
object BigFloat:
...

class FromDigits extends FromDigits.Floating[BigFloat]:
class FromDigits extends FromDigits.Floating[BigFloat] with
def fromDigits(digits: String) = apply(digits)

given FromDigits with
Expand Down
4 changes: 2 additions & 2 deletions docs/docs/reference/changed-features/pattern-matching.md
Original file line number Diff line number Diff line change
Expand Up @@ -121,7 +121,7 @@ For example:
<!-- To be kept in sync with tests/new/patmat-spec.scala -->

```scala
class FirstChars(s: String) extends Product:
class FirstChars(s: String) extends Product with
def _1 = s.charAt(0)
def _2 = s.charAt(1)

Expand All @@ -147,7 +147,7 @@ object FirstChars:
<!-- To be kept in sync with tests/new/patmat-spec.scala -->

```scala
class Nat(val x: Int):
class Nat(val x: Int) with
def get: Int = x
def isEmpty = x < 0

Expand Down
8 changes: 4 additions & 4 deletions docs/docs/reference/changed-features/structural-types.md
Original file line number Diff line number Diff line change
Expand Up @@ -34,13 +34,13 @@ configure how fields and methods should be resolved.
Here's an example of a structural type `Person`:

```scala
class Record(elems: (String, Any)*) extends Selectable:
class Record(elems: (String, Any)*) extends Selectable with
private val fields = elems.toMap
def selectDynamic(name: String): Any = fields(name)

type Person = Record { val name: String; val age: Int }
```

The type `Person` adds a _refinement_ to its parent type `Record` that defines the two fields `name` and `age`. We say the refinement is _structural_ since `name` and `age` are not defined in the parent type. But they exist nevertheless as members of class `Person`. For instance, the following
program would print "Emma is 42 years old.":

Expand Down Expand Up @@ -82,10 +82,10 @@ Structural types can also be accessed using [Java reflection](https://www.oracle
```scala
type Closeable = { def close(): Unit }

class FileInputStream:
class FileInputStream with
def close(): Unit

class Channel:
class Channel with
def close(): Unit
```

Expand Down
4 changes: 2 additions & 2 deletions docs/docs/reference/contextual/context-functions.md
Original file line number Diff line number Diff line change
Expand Up @@ -67,12 +67,12 @@ the aim is to construct tables like this:
The idea is to define classes for `Table` and `Row` that allow the
addition of elements via `add`:
```scala
class Table:
class Table with
val rows = new ArrayBuffer[Row]
def add(r: Row): Unit = rows += r
override def toString = rows.mkString("Table(", ", ", ")")

class Row:
class Row with
val cells = new ArrayBuffer[Cell]
def add(c: Cell): Unit = cells += c
override def toString = cells.mkString("Row(", ", ", ")")
Expand Down
8 changes: 4 additions & 4 deletions docs/docs/reference/contextual/conversions.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ title: "Implicit Conversions"
Implicit conversions are defined by given instances of the `scala.Conversion` class.
This class is defined in package `scala` as follows:
```scala
abstract class Conversion[-T, +U] extends (T => U):
abstract class Conversion[-T, +U] extends (T => U) with
def apply (x: T): U
```
For example, here is an implicit conversion from `String` to `Token`:
Expand Down Expand Up @@ -43,15 +43,15 @@ conversion from `Int` to `java.lang.Integer` can be defined as follows:

2. The "magnet" pattern is sometimes used to express many variants of a method. Instead of defining overloaded versions of the method, one can also let the method take one or more arguments of specially defined "magnet" types, into which various argument types can be converted. Example:
```scala
object Completions:
object Completions with

// The argument "magnet" type
enum CompletionArg:
enum CompletionArg with
case Error(s: String)
case Response(f: Future[HttpResponse])
case Status(code: Future[StatusCode])

object CompletionArg:
object CompletionArg with

// conversions defining the possible arguments to pass to `complete`
// these always come with CompletionArg
Expand Down
10 changes: 5 additions & 5 deletions docs/docs/reference/contextual/extension-methods.md
Original file line number Diff line number Diff line change
Expand Up @@ -174,21 +174,21 @@ There are four possible ways for an extension method to be applicable:
Here is an example for the first rule:

```scala
trait IntOps:
trait IntOps with
extension (i: Int) def isZero: Boolean = i == 0

extension (i: Int) def safeMod(x: Int): Option[Int] =
// extension method defined in same scope IntOps
if x.isZero then None
else Some(i % x)

object IntOpsEx extends IntOps:
object IntOpsEx extends IntOps with
extension (i: Int) def safeDiv(x: Int): Option[Int] =
// extension method brought into scope via inheritance from IntOps
if x.isZero then None
else Some(i / x)

trait SafeDiv:
trait SafeDiv with
import IntOpsEx._ // brings safeDiv and safeMod into scope

extension (i: Int) def divide(d: Int): Option[(Int, Int)] =
Expand All @@ -209,9 +209,9 @@ given ops1: IntOps with {} // brings safeMod into scope
By the third and fourth rule, an extension method is available if it is in the implicit scope of the receiver type or in a given instance in that scope. Example:

```scala
class List[T]:
class List[T] with
...
object List:
object List with
...
extension [T](xs: List[List[T]])
def flatten: List[T] = xs.foldLeft(Nil: List[T])(_ ++ _)
Expand Down
Loading

0 comments on commit acdee20

Please sign in to comment.