Skip to content

Commit 52e539c

Browse files
authored
Merge pull request #10538 from dotty-staging/change-given-syntax-3
Givens without `as`
2 parents 962e605 + 7fde702 commit 52e539c

File tree

172 files changed

+690
-596
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

172 files changed

+690
-596
lines changed

compiler/src/dotty/tools/dotc/ast/Desugar.scala

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -800,10 +800,12 @@ object desugar {
800800
Nil
801801
}
802802
}
803+
val classMods = if mods.is(Given) then mods &~ Given | Synthetic else mods
803804
cpy.TypeDef(cdef: TypeDef)(
804805
name = className,
805806
rhs = cpy.Template(impl)(constr, parents1, clsDerived, self1,
806-
tparamAccessors ::: vparamAccessors ::: normalizedBody ::: caseClassMeths)): TypeDef
807+
tparamAccessors ::: vparamAccessors ::: normalizedBody ::: caseClassMeths)
808+
).withMods(classMods)
807809
}
808810

809811
// install the watch on classTycon

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

Lines changed: 42 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -913,7 +913,7 @@ object Parsers {
913913
lookahead.nextToken()
914914
skipParams()
915915
skipParams()
916-
lookahead.isIdent(nme.as)
916+
lookahead.token == COLON || lookahead.isIdent(nme.as)
917917

918918
def followingIsExtension() =
919919
val next = in.lookahead.token
@@ -1281,11 +1281,11 @@ object Parsers {
12811281

12821282
def possibleTemplateStart(isNew: Boolean = false): Unit =
12831283
in.observeColonEOL()
1284-
if in.token == COLONEOL then
1284+
if in.token == COLONEOL || in.token == WITHEOL then
12851285
if in.lookahead.isIdent(nme.end) then in.token = NEWLINE
12861286
else
12871287
in.nextToken()
1288-
if in.token != INDENT then
1288+
if in.token != INDENT && in.token != LBRACE then
12891289
syntaxErrorOrIncomplete(i"indented definitions expected, ${in}")
12901290
else
12911291
newLineOptWhenFollowedBy(LBRACE)
@@ -3512,9 +3512,8 @@ object Parsers {
35123512
syntaxError(i"extension clause can only define methods", stat.span)
35133513
}
35143514

3515-
/** GivenDef ::= [GivenSig] Type ‘=’ Expr
3516-
* | [GivenSig] ConstrApps [TemplateBody]
3517-
* GivenSig ::= [id] [DefTypeParamClause] {UsingParamClauses} ‘as’
3515+
/** GivenDef ::= [GivenSig] (Type [‘=’ Expr] | StructuralInstance)
3516+
* GivenSig ::= [id] [DefTypeParamClause] {UsingParamClauses} ‘:’
35183517
*/
35193518
def givenDef(start: Offset, mods: Modifiers, givenMod: Mod) = atSpan(start, nameStart) {
35203519
var mods1 = addMod(mods, givenMod)
@@ -3531,23 +3530,35 @@ object Parsers {
35313530
else Nil
35323531
newLinesOpt()
35333532
val noParams = tparams.isEmpty && vparamss.isEmpty
3533+
val newSyntax = in.token == COLON
35343534
if !(name.isEmpty && noParams) then
3535-
accept(nme.as)
3536-
val parents = constrApps(commaOK = true)
3537-
if in.token == EQUALS && parents.length == 1 && parents.head.isType then
3535+
if isIdent(nme.as) then in.nextToken()
3536+
else accept(COLON)
3537+
val parents = constrApp() :: withConstrApps()
3538+
val parentsIsType = parents.length == 1 && parents.head.isType
3539+
if in.token == EQUALS && parentsIsType then
35383540
accept(EQUALS)
35393541
mods1 |= Final
35403542
if noParams && !mods.is(Inline) then
35413543
mods1 |= Lazy
35423544
ValDef(name, parents.head, subExpr())
35433545
else
35443546
DefDef(name, tparams, vparamss, parents.head, subExpr())
3547+
else if newSyntax && in.token != WITH && in.token != WITHEOL && parentsIsType then
3548+
if name.isEmpty then
3549+
syntaxError(em"anonymous given cannot be abstract")
3550+
DefDef(name, tparams, vparamss, parents.head, EmptyTree)
35453551
else
3546-
possibleTemplateStart()
35473552
val tparams1 = tparams.map(tparam => tparam.withMods(tparam.mods | PrivateLocal))
35483553
val vparamss1 = vparamss.map(_.map(vparam =>
35493554
vparam.withMods(vparam.mods &~ Param | ParamAccessor | Protected)))
3550-
val templ = templateBodyOpt(makeConstructor(tparams1, vparamss1), parents, Nil)
3555+
val constr = makeConstructor(tparams1, vparamss1)
3556+
val templ =
3557+
if newSyntax || in.token == WITHEOL || in.token == WITH then
3558+
withTemplate(constr, parents)
3559+
else
3560+
possibleTemplateStart()
3561+
templateBodyOpt(makeConstructor(tparams1, vparamss1), parents, Nil)
35513562
if tparams.isEmpty && vparamss.isEmpty then ModuleDef(name, templ)
35523563
else TypeDef(name.toTypeName, templ)
35533564
end gdef
@@ -3622,6 +3633,18 @@ object Parsers {
36223633
else Nil
36233634
t :: ts
36243635

3636+
3637+
/** `{`with` ConstrApp} but no EOL allowed after `with`.
3638+
*/
3639+
def withConstrApps(): List[Tree] =
3640+
if in.token == WITH then
3641+
in.observeWithEOL() // converts token to WITHEOL if at end of line
3642+
if in.token == WITH && in.lookahead.token != LBRACE then
3643+
in.nextToken()
3644+
constrApp() :: withConstrApps()
3645+
else Nil
3646+
else Nil
3647+
36253648
/** Template ::= InheritClauses [TemplateBody]
36263649
* InheritClauses ::= [‘extends’ ConstrApps] [‘derives’ QualId {‘,’ QualId}]
36273650
*/
@@ -3687,6 +3710,14 @@ object Parsers {
36873710
template(emptyConstructor)
36883711
r
36893712

3713+
/** with Template, with EOL <indent> interpreted */
3714+
def withTemplate(constr: DefDef, parents: List[Tree]): Template =
3715+
if in.token != WITHEOL then accept(WITH)
3716+
possibleTemplateStart() // consumes a WITHEOL token
3717+
val (self, stats) = templateBody()
3718+
Template(constr, parents, Nil, self, stats)
3719+
.withSpan(Span(constr.span.orElse(parents.head.span).start, in.lastOffset))
3720+
36903721
/* -------- STATSEQS ------------------------------------------- */
36913722

36923723
/** Create a tree representing a packaging */

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

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -150,6 +150,7 @@ object Scanners {
150150
private[Scanners] var allowLeadingInfixOperators = true
151151

152152
var debugTokenStream = false
153+
val showLookAheadOnDebug = false
153154

154155
val rewrite = ctx.settings.rewrite.value.isDefined
155156
val oldSyntax = ctx.settings.oldSyntax.value
@@ -320,7 +321,8 @@ object Scanners {
320321
}
321322

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

325327
/** Insert `token` at assumed `offset` in front of current one. */
326328
def insert(token: Token, offset: Int) = {
@@ -510,12 +512,15 @@ object Scanners {
510512
|Previous indent : $lastWidth
511513
|Latest indent : $nextWidth"""
512514

513-
def observeColonEOL(): Unit =
514-
if token == COLON then
515+
private def switchAtEOL(testToken: Token, eolToken: Token): Unit =
516+
if token == testToken then
515517
lookAhead()
516518
val atEOL = isAfterLineEnd || token == EOF
517519
reset()
518-
if atEOL then token = COLONEOL
520+
if atEOL then token = eolToken
521+
522+
def observeColonEOL(): Unit = switchAtEOL(COLON, COLONEOL)
523+
def observeWithEOL(): Unit = switchAtEOL(WITH, WITHEOL)
519524

520525
def observeIndented(): Unit =
521526
if indentSyntax && isNewLine then

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

Lines changed: 12 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -121,14 +121,14 @@ abstract class TokensCommon {
121121
def isKeyword(token: Token): Boolean = keywords contains token
122122

123123
/** parentheses */
124-
final val LPAREN = 90; enter(LPAREN, "'('")
125-
final val RPAREN = 91; enter(RPAREN, "')'")
126-
final val LBRACKET = 92; enter(LBRACKET, "'['")
127-
final val RBRACKET = 93; enter(RBRACKET, "']'")
128-
final val LBRACE = 94; enter(LBRACE, "'{'")
129-
final val RBRACE = 95; enter(RBRACE, "'}'")
130-
final val INDENT = 96; enter(INDENT, "indent")
131-
final val OUTDENT = 97; enter(OUTDENT, "unindent")
124+
final val LPAREN = 91; enter(LPAREN, "'('")
125+
final val RPAREN = 92; enter(RPAREN, "')'")
126+
final val LBRACKET = 93; enter(LBRACKET, "'['")
127+
final val RBRACKET = 94; enter(RBRACKET, "']'")
128+
final val LBRACE = 95; enter(LBRACE, "'{'")
129+
final val RBRACE = 96; enter(RBRACE, "'}'")
130+
final val INDENT = 97; enter(INDENT, "indent")
131+
final val OUTDENT = 98; enter(OUTDENT, "unindent")
132132

133133
final val firstParen = LPAREN
134134
final val lastParen = OUTDENT
@@ -204,10 +204,11 @@ object Tokens extends TokensCommon {
204204
final val QUOTE = 87; enter(QUOTE, "'")
205205

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

209210
/** XML mode */
210-
final val XMLSTART = 98; enter(XMLSTART, "$XMLSTART$<") // TODO: deprecate
211+
final val XMLSTART = 99; enter(XMLSTART, "$XMLSTART$<") // TODO: deprecate
211212

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

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

282283
/** Faced with the choice between a type and a formal parameter, the following

compiler/src/dotty/tools/dotc/typer/Checking.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -443,7 +443,7 @@ object Checking {
443443
fail(ParamsNoInline(sym.owner))
444444
if sym.isInlineMethod && !sym.is(Deferred) && sym.allOverriddenSymbols.nonEmpty then
445445
checkInlineOverrideParameters(sym)
446-
if (sym.isOneOf(GivenOrImplicit)) {
446+
if (sym.is(Implicit)) {
447447
if (sym.owner.is(Package))
448448
fail(TopLevelCantBeImplicit(sym))
449449
if (sym.isType)

compiler/src/dotty/tools/dotc/typer/Namer.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -241,7 +241,7 @@ class Namer { typer: Typer =>
241241

242242
tree match {
243243
case tree: TypeDef if tree.isClassDef =>
244-
val flags = checkFlags(tree.mods.flags &~ GivenOrImplicit)
244+
val flags = checkFlags(tree.mods.flags &~ Implicit)
245245
val name = checkNoConflict(tree.name, flags.is(Private), tree.span).asTypeName
246246
val cls =
247247
createOrRefine[ClassSymbol](tree, name, flags, ctx.owner,

compiler/src/dotty/tools/dotc/typer/RefChecks.scala

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -503,7 +503,9 @@ object RefChecks {
503503
def prelude = (
504504
if (clazz.isAnonymousClass || clazz.is(Module)) "object creation impossible"
505505
else if (mustBeMixin) s"$clazz needs to be a mixin"
506-
else s"$clazz needs to be abstract") + ", since"
506+
else if clazz.is(Synthetic) then "instance cannot be created"
507+
else s"$clazz needs to be abstract"
508+
) + ", since"
507509

508510
if (abstractErrors.isEmpty) abstractErrors ++= List(prelude, msg)
509511
else abstractErrors += msg

docs/docs/internals/syntax.md

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -205,9 +205,11 @@ Expr1 ::= [‘inline’] ‘if’ ‘(’ Expr ‘)’ {nl} Expr [[
205205
| [SimpleExpr ‘.’] id ‘=’ Expr Assign(expr, expr)
206206
| SimpleExpr1 ArgumentExprs ‘=’ Expr Assign(expr, expr)
207207
| PostfixExpr [Ascription]
208+
| StructuralInstance
208209
| ‘inline’ InfixExpr MatchClause
209210
Ascription ::= ‘:’ InfixType Typed(expr, tp)
210211
| ‘:’ Annotation {Annotation} Typed(expr, Annotated(EmptyTree, annot)*)
212+
StructuralInstance ::= ConstrApp {‘with’ ConstrApp} ‘with’ TemplateBody New templ
211213
Catches ::= ‘catch’ (Expr | ExprCaseClause)
212214
PostfixExpr ::= InfixExpr [id] PostfixOp(expr, op)
213215
InfixExpr ::= PrefixExpr
@@ -396,9 +398,8 @@ ClassConstr ::= [ClsTypeParamClause] [ConstrMods] ClsParamClauses
396398
ConstrMods ::= {Annotation} [AccessModifier]
397399
ObjectDef ::= id [Template] ModuleDef(mods, name, template) // no constructor
398400
EnumDef ::= id ClassConstr InheritClauses EnumBody EnumDef(mods, name, tparams, template)
399-
GivenDef ::= [GivenSig] Type ‘=’ Expr
400-
| [GivenSig] ConstrApps [TemplateBody]
401-
GivenSig ::= [id] [DefTypeParamClause] {UsingParamClause} ‘as’ -- one of `id`, `DefParamClause`, `UsingParamClause` must appear
401+
GivenDef ::= [GivenSig] (Type [‘=’ Expr] | StructuralInstance)
402+
GivenSig ::= [id] [DefTypeParamClause] {UsingParamClause} ‘:’ -- one of `id`, `DefParamClause`, `UsingParamClause` must be present
402403
Extension ::= ‘extension’ [DefTypeParamClause] ‘(’ DefParam ‘)’
403404
{UsingParamClause}] ExtMethods
404405
ExtMethods ::= ExtMethod | [nl] ‘{’ ExtMethod {semi ExtMethod ‘}’

docs/docs/reference/changed-features/implicit-resolution.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -41,10 +41,10 @@ no longer applies.
4141
**3.** Package prefixes no longer contribute to the implicit search scope of a type. Example:
4242
```scala
4343
package p
44-
given a as A
44+
given a: A = A()
4545

4646
object o {
47-
given b as B
47+
given b: B = B()
4848
type C
4949
}
5050
```

docs/docs/reference/changed-features/numeric-literals.md

Lines changed: 8 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -133,38 +133,32 @@ should produce the `BigFloat` number `BigFloat(-123, 997)`:
133133
The companion object of `BigFloat` defines an `apply` constructor method to construct a `BigFloat`
134134
from a `digits` string. Here is a possible implementation:
135135
```scala
136-
object BigFloat {
136+
object BigFloat:
137137
import scala.util.FromDigits
138138

139-
def apply(digits: String): BigFloat = {
140-
val (mantissaDigits, givenExponent) = digits.toUpperCase.split('E') match {
139+
def apply(digits: String): BigFloat =
140+
val (mantissaDigits, givenExponent) = digits.toUpperCase.split('E') match
141141
case Array(mantissaDigits, edigits) =>
142142
val expo =
143143
try FromDigits.intFromDigits(edigits)
144-
catch {
145-
case ex: FromDigits.NumberTooLarge =>
146-
throw FromDigits.NumberTooLarge(s"exponent too large: $edigits")
147-
}
144+
catch case ex: FromDigits.NumberTooLarge =>
145+
throw FromDigits.NumberTooLarge(s"exponent too large: $edigits")
148146
(mantissaDigits, expo)
149147
case Array(mantissaDigits) =>
150148
(mantissaDigits, 0)
151-
}
152-
val (intPart, exponent) = mantissaDigits.split('.') match {
149+
val (intPart, exponent) = mantissaDigits.split('.') match
153150
case Array(intPart, decimalPart) =>
154151
(intPart ++ decimalPart, givenExponent - decimalPart.length)
155152
case Array(intPart) =>
156153
(intPart, givenExponent)
157-
}
158154
BigFloat(BigInt(intPart), exponent)
159-
}
160155
```
161156
To accept `BigFloat` literals, all that's needed in addition is a `given` instance of type
162157
`FromDigits.Floating[BigFloat]`:
163158
```scala
164-
given FromDigits as FromDigits.Floating[BigFloat] {
159+
given FromDigits: FromDigits.Floating[BigFloat] with
165160
def fromDigits(digits: String) = apply(digits)
166-
}
167-
} // end BigFloat
161+
end BigFloat
168162
```
169163
Note that the `apply` method does not check the format of the `digits` argument. It is
170164
assumed that only valid arguments are passed. For calls coming from the compiler

docs/docs/reference/contextual/by-name-context-parameters.md

Lines changed: 5 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -10,14 +10,12 @@ trait Codec[T] {
1010
def write(x: T): Unit
1111
}
1212

13-
given intCodec as Codec[Int] = ???
13+
given intCodec: Codec[Int] = ???
1414

15-
given optionCodec[T](using ev: => Codec[T]) as Codec[Option[T]] {
16-
def write(xo: Option[T]) = xo match {
15+
given optionCodec[T](using ev: => Codec[T]): Codec[Option[T]] with
16+
def write(xo: Option[T]) = xo match
1717
case Some(x) => ev.write(x)
1818
case None =>
19-
}
20-
}
2119

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

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

3836
```scala
39-
given lv as T = ???
37+
given lv: T = ???
4038
```
4139
where `lv` is an arbitrary fresh name.
4240

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

4745

4846
```scala
49-
{ given lv as T = E; lv }
47+
{ given lv: T = E; lv }
5048
```
5149

5250
Otherwise, return `E` unchanged.

docs/docs/reference/contextual/context-functions.md

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ Context functions are written using `?=>` as the "arrow" sign.
1313
They are applied to synthesized arguments, in
1414
the same way methods with context parameters are applied. For instance:
1515
```scala
16-
given ec as ExecutionContext = ...
16+
given ec: ExecutionContext = ...
1717

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

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

9494
def row(init: Row ?=> Unit)(using t: Table) = {
95-
given r as Row
95+
given r: Row = Row()
9696
init
9797
t.add(r)
9898
}

0 commit comments

Comments
 (0)