Skip to content
Merged
Show file tree
Hide file tree
Changes from 9 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion compiler/src/dotty/tools/dotc/ast/Desugar.scala
Original file line number Diff line number Diff line change
Expand Up @@ -800,10 +800,12 @@ object desugar {
Nil
}
}
val classMods = if mods.is(Given) then mods &~ Given | Synthetic else mods
cpy.TypeDef(cdef: TypeDef)(
name = className,
rhs = cpy.Template(impl)(constr, parents1, clsDerived, self1,
tparamAccessors ::: vparamAccessors ::: normalizedBody ::: caseClassMeths)): TypeDef
tparamAccessors ::: vparamAccessors ::: normalizedBody ::: caseClassMeths)
).withMods(classMods)
}

// install the watch on classTycon
Expand Down
53 changes: 42 additions & 11 deletions compiler/src/dotty/tools/dotc/parsing/Parsers.scala
Original file line number Diff line number Diff line change
Expand Up @@ -913,7 +913,7 @@ object Parsers {
lookahead.nextToken()
skipParams()
skipParams()
lookahead.isIdent(nme.as)
lookahead.token == COLON || lookahead.isIdent(nme.as)

def followingIsExtension() =
val next = in.lookahead.token
Expand Down Expand Up @@ -1281,11 +1281,11 @@ object Parsers {

def possibleTemplateStart(isNew: Boolean = false): Unit =
in.observeColonEOL()
if in.token == COLONEOL then
if in.token == COLONEOL || in.token == WITHEOL then
if in.lookahead.isIdent(nme.end) then in.token = NEWLINE
else
in.nextToken()
if in.token != INDENT then
if in.token != INDENT && in.token != LBRACE then
syntaxErrorOrIncomplete(i"indented definitions expected, ${in}")
else
newLineOptWhenFollowedBy(LBRACE)
Expand Down Expand Up @@ -3516,9 +3516,8 @@ object Parsers {
syntaxError(i"extension clause can only define methods", stat.span)
}

/** GivenDef ::= [GivenSig] Type ‘=’ Expr
* | [GivenSig] ConstrApps [TemplateBody]
* GivenSig ::= [id] [DefTypeParamClause] {UsingParamClauses} ‘as’
/** GivenDef ::= [GivenSig] (Type [‘=’ Expr] | StructuralInstance)
* GivenSig ::= [id] [DefTypeParamClause] {UsingParamClauses} ‘:’
*/
def givenDef(start: Offset, mods: Modifiers, givenMod: Mod) = atSpan(start, nameStart) {
var mods1 = addMod(mods, givenMod)
Expand All @@ -3535,23 +3534,35 @@ object Parsers {
else Nil
newLinesOpt()
val noParams = tparams.isEmpty && vparamss.isEmpty
val newSyntax = in.token == COLON
if !(name.isEmpty && noParams) then
accept(nme.as)
val parents = constrApps(commaOK = true)
if in.token == EQUALS && parents.length == 1 && parents.head.isType then
if isIdent(nme.as) then in.nextToken()
else accept(COLON)
val parents = constrApp() :: withConstrApps()
val parentsIsType = parents.length == 1 && parents.head.isType
if in.token == EQUALS && parentsIsType then
accept(EQUALS)
mods1 |= Final
if noParams && !mods.is(Inline) then
mods1 |= Lazy
ValDef(name, parents.head, subExpr())
else
DefDef(name, tparams, vparamss, parents.head, subExpr())
else if newSyntax && in.token != WITH && in.token != WITHEOL && parentsIsType then
if name.isEmpty then
syntaxError(em"anonymous given cannot be abstract")
DefDef(name, tparams, vparamss, parents.head, EmptyTree)
else
possibleTemplateStart()
val tparams1 = tparams.map(tparam => tparam.withMods(tparam.mods | PrivateLocal))
val vparamss1 = vparamss.map(_.map(vparam =>
vparam.withMods(vparam.mods &~ Param | ParamAccessor | Protected)))
val templ = templateBodyOpt(makeConstructor(tparams1, vparamss1), parents, Nil)
val constr = makeConstructor(tparams1, vparamss1)
val templ =
if newSyntax || in.token == WITHEOL || in.token == WITH then
withTemplate(constr, parents)
else
possibleTemplateStart()
templateBodyOpt(makeConstructor(tparams1, vparamss1), parents, Nil)
if tparams.isEmpty && vparamss.isEmpty then ModuleDef(name, templ)
else TypeDef(name.toTypeName, templ)
end gdef
Expand Down Expand Up @@ -3626,6 +3637,18 @@ object Parsers {
else Nil
t :: ts


/** `{`with` ConstrApp} but no EOL allowed after `with`.
*/
def withConstrApps(): List[Tree] =
if in.token == WITH then
in.observeWithEOL() // converts token to WITHEOL if at end of line
if in.token == WITH && in.lookahead.token != LBRACE then
in.nextToken()
constrApp() :: withConstrApps()
else Nil
else Nil

/** Template ::= InheritClauses [TemplateBody]
* InheritClauses ::= [‘extends’ ConstrApps] [‘derives’ QualId {‘,’ QualId}]
*/
Expand Down Expand Up @@ -3691,6 +3714,14 @@ object Parsers {
template(emptyConstructor)
r

/** with Template, with EOL <indent> interpreted */
def withTemplate(constr: DefDef, parents: List[Tree]): Template =
if in.token != WITHEOL then accept(WITH)
possibleTemplateStart() // consumes a WITHEOL token
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do we miss else here?

Suggested change
possibleTemplateStart() // consumes a WITHEOL token
else possibleTemplateStart() // consumes a WITHEOL token

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No. possibleTemplateStart expects a { or a WITHEOL.

val (self, stats) = templateBody()
Template(constr, parents, Nil, self, stats)
.withSpan(Span(constr.span.orElse(parents.head.span).start, in.lastOffset))

/* -------- STATSEQS ------------------------------------------- */

/** Create a tree representing a packaging */
Expand Down
13 changes: 9 additions & 4 deletions compiler/src/dotty/tools/dotc/parsing/Scanners.scala
Original file line number Diff line number Diff line change
Expand Up @@ -150,6 +150,7 @@ object Scanners {
private[Scanners] var allowLeadingInfixOperators = true

var debugTokenStream = false
val showLookAheadOnDebug = false

val rewrite = ctx.settings.rewrite.value.isDefined
val oldSyntax = ctx.settings.oldSyntax.value
Expand Down Expand Up @@ -315,7 +316,8 @@ object Scanners {
}

final def printState() =
if debugTokenStream then print("[" + show + "]")
if debugTokenStream && (showLookAheadOnDebug || !isInstanceOf[LookaheadScanner]) then
print(s"[$show${if isInstanceOf[LookaheadScanner] then "(LA)" else ""}]")

/** Insert `token` at assumed `offset` in front of current one. */
def insert(token: Token, offset: Int) = {
Expand Down Expand Up @@ -505,12 +507,15 @@ object Scanners {
|Previous indent : $lastWidth
|Latest indent : $nextWidth"""

def observeColonEOL(): Unit =
if token == COLON then
private def switchAtEOL(testToken: Token, eolToken: Token): Unit =
if token == testToken then
lookAhead()
val atEOL = isAfterLineEnd || token == EOF
reset()
if atEOL then token = COLONEOL
if atEOL then token = eolToken

def observeColonEOL(): Unit = switchAtEOL(COLON, COLONEOL)
def observeWithEOL(): Unit = switchAtEOL(WITH, WITHEOL)

def observeIndented(): Unit =
if indentSyntax && isNewLine then
Expand Down
23 changes: 12 additions & 11 deletions compiler/src/dotty/tools/dotc/parsing/Tokens.scala
Original file line number Diff line number Diff line change
Expand Up @@ -121,14 +121,14 @@ abstract class TokensCommon {
def isKeyword(token: Token): Boolean = keywords contains token

/** parentheses */
final val LPAREN = 90; enter(LPAREN, "'('")
final val RPAREN = 91; enter(RPAREN, "')'")
final val LBRACKET = 92; enter(LBRACKET, "'['")
final val RBRACKET = 93; enter(RBRACKET, "']'")
final val LBRACE = 94; enter(LBRACE, "'{'")
final val RBRACE = 95; enter(RBRACE, "'}'")
final val INDENT = 96; enter(INDENT, "indent")
final val OUTDENT = 97; enter(OUTDENT, "unindent")
final val LPAREN = 91; enter(LPAREN, "'('")
final val RPAREN = 92; enter(RPAREN, "')'")
final val LBRACKET = 93; enter(LBRACKET, "'['")
final val RBRACKET = 94; enter(RBRACKET, "']'")
final val LBRACE = 95; enter(LBRACE, "'{'")
final val RBRACE = 96; enter(RBRACE, "'}'")
final val INDENT = 97; enter(INDENT, "indent")
final val OUTDENT = 98; enter(OUTDENT, "unindent")

final val firstParen = LPAREN
final val lastParen = OUTDENT
Expand Down Expand Up @@ -204,10 +204,11 @@ object Tokens extends TokensCommon {
final val QUOTE = 87; enter(QUOTE, "'")

final val COLONEOL = 88; enter(COLONEOL, ":", ": at eol")
final val SELFARROW = 89; enter(SELFARROW, "=>") // reclassified ARROW following self-type
final val WITHEOL = 89; enter(WITHEOL, "with", "with at eol")
final val SELFARROW = 90; enter(SELFARROW, "=>") // reclassified ARROW following self-type

/** XML mode */
final val XMLSTART = 98; enter(XMLSTART, "$XMLSTART$<") // TODO: deprecate
final val XMLSTART = 99; enter(XMLSTART, "$XMLSTART$<") // TODO: deprecate

final val alphaKeywords: TokenSet = tokenRange(IF, MACRO)
final val symbolicKeywords: TokenSet = tokenRange(USCORE, CTXARROW)
Expand Down Expand Up @@ -276,7 +277,7 @@ object Tokens extends TokensCommon {
final val closingRegionTokens = BitSet(RBRACE, RPAREN, RBRACKET, CASE) | statCtdTokens

final val canStartIndentTokens: BitSet =
statCtdTokens | BitSet(COLONEOL, EQUALS, ARROW, LARROW, WHILE, TRY, FOR, IF)
statCtdTokens | BitSet(COLONEOL, WITHEOL, EQUALS, ARROW, LARROW, WHILE, TRY, FOR, IF)
// `if` is excluded because it often comes after `else` which makes for awkward indentation rules TODO: try to do without the exception

/** Faced with the choice between a type and a formal parameter, the following
Expand Down
2 changes: 1 addition & 1 deletion compiler/src/dotty/tools/dotc/typer/Checking.scala
Original file line number Diff line number Diff line change
Expand Up @@ -443,7 +443,7 @@ object Checking {
fail(ParamsNoInline(sym.owner))
if sym.isInlineMethod && !sym.is(Deferred) && sym.allOverriddenSymbols.nonEmpty then
checkInlineOverrideParameters(sym)
if (sym.isOneOf(GivenOrImplicit)) {
if (sym.is(Implicit)) {
if (sym.owner.is(Package))
fail(TopLevelCantBeImplicit(sym))
if (sym.isType)
Expand Down
2 changes: 1 addition & 1 deletion compiler/src/dotty/tools/dotc/typer/Namer.scala
Original file line number Diff line number Diff line change
Expand Up @@ -241,7 +241,7 @@ class Namer { typer: Typer =>

tree match {
case tree: TypeDef if tree.isClassDef =>
val flags = checkFlags(tree.mods.flags &~ GivenOrImplicit)
val flags = checkFlags(tree.mods.flags &~ Implicit)
val name = checkNoConflict(tree.name, flags.is(Private), tree.span).asTypeName
val cls =
createOrRefine[ClassSymbol](tree, name, flags, ctx.owner,
Expand Down
4 changes: 3 additions & 1 deletion compiler/src/dotty/tools/dotc/typer/RefChecks.scala
Original file line number Diff line number Diff line change
Expand Up @@ -503,7 +503,9 @@ object RefChecks {
def prelude = (
if (clazz.isAnonymousClass || clazz.is(Module)) "object creation impossible"
else if (mustBeMixin) s"$clazz needs to be a mixin"
else s"$clazz needs to be abstract") + ", since"
else if clazz.is(Synthetic) then "instance cannot be created"
else s"$clazz needs to be abstract"
) + ", since"

if (abstractErrors.isEmpty) abstractErrors ++= List(prelude, msg)
else abstractErrors += msg
Expand Down
7 changes: 4 additions & 3 deletions docs/docs/internals/syntax.md
Original file line number Diff line number Diff line change
Expand Up @@ -205,9 +205,11 @@ Expr1 ::= [‘inline’] ‘if’ ‘(’ Expr ‘)’ {nl} Expr [[
| [SimpleExpr ‘.’] id ‘=’ Expr Assign(expr, expr)
| SimpleExpr1 ArgumentExprs ‘=’ Expr Assign(expr, expr)
| PostfixExpr [Ascription]
| StructuralInstance
| ‘inline’ InfixExpr MatchClause
Ascription ::= ‘:’ InfixType Typed(expr, tp)
| ‘:’ Annotation {Annotation} Typed(expr, Annotated(EmptyTree, annot)*)
StructuralInstance ::= ConstrApp {‘with’ ConstrApp} ‘with’ TemplateBody New templ
Catches ::= ‘catch’ (Expr | ExprCaseClause)
PostfixExpr ::= InfixExpr [id] PostfixOp(expr, op)
InfixExpr ::= PrefixExpr
Expand Down Expand Up @@ -396,9 +398,8 @@ ClassConstr ::= [ClsTypeParamClause] [ConstrMods] ClsParamClauses
ConstrMods ::= {Annotation} [AccessModifier]
ObjectDef ::= id [Template] ModuleDef(mods, name, template) // no constructor
EnumDef ::= id ClassConstr InheritClauses EnumBody EnumDef(mods, name, tparams, template)
GivenDef ::= [GivenSig] Type ‘=’ Expr
| [GivenSig] ConstrApps [TemplateBody]
GivenSig ::= [id] [DefTypeParamClause] {UsingParamClause} ‘as’ -- one of `id`, `DefParamClause`, `UsingParamClause` must appear
GivenDef ::= [GivenSig] (Type [‘=’ Expr] | StructuralInstance)
GivenSig ::= [id] [DefTypeParamClause] {UsingParamClause} ‘:’ -- one of `id`, `DefParamClause`, `UsingParamClause` must be present
Extension ::= ‘extension’ [DefTypeParamClause] ‘(’ DefParam ‘)’
{UsingParamClause}] ExtMethods
ExtMethods ::= ExtMethod | [nl] ‘{’ ExtMethod {semi ExtMethod ‘}’
Expand Down
4 changes: 2 additions & 2 deletions docs/docs/reference/changed-features/implicit-resolution.md
Original file line number Diff line number Diff line change
Expand Up @@ -41,10 +41,10 @@ no longer applies.
**3.** Package prefixes no longer contribute to the implicit search scope of a type. Example:
```scala
package p
given a as A
given a: A = A()

object o {
given b as B
given b: B = B()
type C
}
```
Expand Down
22 changes: 8 additions & 14 deletions docs/docs/reference/changed-features/numeric-literals.md
Original file line number Diff line number Diff line change
Expand Up @@ -133,38 +133,32 @@ should produce the `BigFloat` number `BigFloat(-123, 997)`:
The companion object of `BigFloat` defines an `apply` constructor method to construct a `BigFloat`
from a `digits` string. Here is a possible implementation:
```scala
object BigFloat {
object BigFloat:
import scala.util.FromDigits

def apply(digits: String): BigFloat = {
val (mantissaDigits, givenExponent) = digits.toUpperCase.split('E') match {
def apply(digits: String): BigFloat =
val (mantissaDigits, givenExponent) = digits.toUpperCase.split('E') match
case Array(mantissaDigits, edigits) =>
val expo =
try FromDigits.intFromDigits(edigits)
catch {
case ex: FromDigits.NumberTooLarge =>
throw FromDigits.NumberTooLarge(s"exponent too large: $edigits")
}
catch case ex: FromDigits.NumberTooLarge =>
throw FromDigits.NumberTooLarge(s"exponent too large: $edigits")
(mantissaDigits, expo)
case Array(mantissaDigits) =>
(mantissaDigits, 0)
}
val (intPart, exponent) = mantissaDigits.split('.') match {
val (intPart, exponent) = mantissaDigits.split('.') match
case Array(intPart, decimalPart) =>
(intPart ++ decimalPart, givenExponent - decimalPart.length)
case Array(intPart) =>
(intPart, givenExponent)
}
BigFloat(BigInt(intPart), exponent)
}
```
To accept `BigFloat` literals, all that's needed in addition is a `given` instance of type
`FromDigits.Floating[BigFloat]`:
```scala
given FromDigits as FromDigits.Floating[BigFloat] {
given FromDigits: FromDigits.Floating[BigFloat] with
def fromDigits(digits: String) = apply(digits)
}
} // end BigFloat
end BigFloat
```
Note that the `apply` method does not check the format of the `digits` argument. It is
assumed that only valid arguments are passed. For calls coming from the compiler
Expand Down
2 changes: 1 addition & 1 deletion docs/docs/reference/changed-features/structural-types.md
Original file line number Diff line number Diff line change
Expand Up @@ -131,7 +131,7 @@ than other classes. Here is an example:
trait Vehicle extends reflect.Selectable {
val wheels: Int
}
val i3 = new Vehicle { // i3: Vehicle { val range: Int }
val i3 = Vehicle with { // i3: Vehicle { val range: Int }
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This change needs to be reverted.

val wheels = 4
val range = 240
}
Expand Down
12 changes: 5 additions & 7 deletions docs/docs/reference/contextual/by-name-context-parameters.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,14 +10,12 @@ trait Codec[T] {
def write(x: T): Unit
}

given intCodec as Codec[Int] = ???
given intCodec: Codec[Int] = ???

given optionCodec[T](using ev: => Codec[T]) as Codec[Option[T]] {
def write(xo: Option[T]) = xo match {
given optionCodec[T](using ev: => Codec[T]): Codec[Option[T]] with
def write(xo: Option[T]) = xo match
case Some(x) => ev.write(x)
case None =>
}
}

val s = summon[Codec[Option[Int]]]

Expand All @@ -36,7 +34,7 @@ The precise steps for synthesizing an argument for a by-name context parameter o
1. Create a new given of type `T`:

```scala
given lv as T = ???
given lv: T = ???
```
where `lv` is an arbitrary fresh name.

Expand All @@ -46,7 +44,7 @@ The precise steps for synthesizing an argument for a by-name context parameter o


```scala
{ given lv as T = E; lv }
{ given lv: T = E; lv }
```

Otherwise, return `E` unchanged.
Expand Down
6 changes: 3 additions & 3 deletions docs/docs/reference/contextual/context-functions.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ Context functions are written using `?=>` as the "arrow" sign.
They are applied to synthesized arguments, in
the same way methods with context parameters are applied. For instance:
```scala
given ec as ExecutionContext = ...
given ec: ExecutionContext = ...

def f(x: Int): ExecutionContext ?=> Int = ...

Expand Down Expand Up @@ -86,13 +86,13 @@ with context function types as parameters to avoid the plumbing boilerplate
that would otherwise be necessary.
```scala
def table(init: Table ?=> Unit) = {
given t as Table // note the use of a creator application; same as: given t as Table = new Table
given t: Table = Table()
init
t
}

def row(init: Row ?=> Unit)(using t: Table) = {
given r as Row
given r: Row = Row()
init
t.add(r)
}
Expand Down
Loading