Skip to content

Commit 3dfd762

Browse files
authored
Revised given syntax (#21208)
Update given syntax to latest discussed variant in the SIP tests/pos/given-syntax.scala shows a semi-systematic list of possible syntax forms.
2 parents 9e4aea4 + 62c71c0 commit 3dfd762

19 files changed

+680
-145
lines changed

Diff for: compiler/src/dotty/tools/dotc/parsing/Parsers.scala

+112-48
Original file line numberDiff line numberDiff line change
@@ -972,18 +972,16 @@ object Parsers {
972972
followedByToken(LARROW) // `<-` comes before possible statement starts
973973
}
974974

975-
/** Are the next token the "GivenSig" part of a given definition,
976-
* i.e. an identifier followed by type and value parameters, followed by `:`?
975+
/** Are the next tokens a valid continuation of a named given def?
976+
* i.e. an identifier, possibly followed by type and value parameters, followed by `:`?
977977
* @pre The current token is an identifier
978978
*/
979-
def followingIsOldStyleGivenSig() =
979+
def followingIsGivenDefWithColon() =
980980
val lookahead = in.LookaheadScanner()
981981
if lookahead.isIdent then
982982
lookahead.nextToken()
983-
var paramsSeen = false
984983
def skipParams(): Unit =
985984
if lookahead.token == LPAREN || lookahead.token == LBRACKET then
986-
paramsSeen = true
987985
lookahead.skipParens()
988986
skipParams()
989987
else if lookahead.isNewLine then
@@ -1002,6 +1000,11 @@ object Parsers {
10021000
}
10031001
}
10041002

1003+
def followingIsArrow() =
1004+
val lookahead = in.LookaheadScanner()
1005+
lookahead.skipParens()
1006+
lookahead.token == ARROW
1007+
10051008
def followingIsExtension() =
10061009
val next = in.lookahead.token
10071010
next == LBRACKET || next == LPAREN
@@ -3441,7 +3444,11 @@ object Parsers {
34413444
/** ContextTypes ::= FunArgType {‘,’ FunArgType}
34423445
*/
34433446
def contextTypes(paramOwner: ParamOwner, numLeadParams: Int, impliedMods: Modifiers): List[ValDef] =
3444-
val tps = commaSeparated(() => paramTypeOf(() => toplevelTyp()))
3447+
typesToParams(
3448+
commaSeparated(() => paramTypeOf(() => toplevelTyp())),
3449+
paramOwner, numLeadParams, impliedMods)
3450+
3451+
def typesToParams(tps: List[Tree], paramOwner: ParamOwner, numLeadParams: Int, impliedMods: Modifiers): List[ValDef] =
34453452
var counter = numLeadParams
34463453
def nextIdx = { counter += 1; counter }
34473454
val paramFlags = if paramOwner.isClass then LocalParamAccessor else Param
@@ -3468,18 +3475,20 @@ object Parsers {
34683475
def termParamClause(
34693476
paramOwner: ParamOwner,
34703477
numLeadParams: Int, // number of parameters preceding this clause
3471-
firstClause: Boolean = false // clause is the first in regular list of clauses
3478+
firstClause: Boolean = false, // clause is the first in regular list of clauses
3479+
initialMods: Modifiers = EmptyModifiers
34723480
): List[ValDef] = {
3473-
var impliedMods: Modifiers = EmptyModifiers
3481+
var impliedMods: Modifiers = initialMods
34743482

34753483
def addParamMod(mod: () => Mod) = impliedMods = addMod(impliedMods, atSpan(in.skipToken()) { mod() })
34763484

34773485
def paramMods() =
34783486
if in.token == IMPLICIT then
34793487
addParamMod(() => Mod.Implicit())
3480-
else
3481-
if isIdent(nme.using) then
3482-
addParamMod(() => Mod.Given())
3488+
else if isIdent(nme.using) then
3489+
if initialMods.is(Given) then
3490+
syntaxError(em"`using` is already implied here, should not be given explicitly", in.offset)
3491+
addParamMod(() => Mod.Given())
34833492

34843493
def param(): ValDef = {
34853494
val start = in.offset
@@ -4144,18 +4153,67 @@ object Parsers {
41444153
* OldGivenSig ::= [id] [DefTypeParamClause] {UsingParamClauses} ‘:’
41454154
* StructuralInstance ::= ConstrApp {‘with’ ConstrApp} [‘with’ WithTemplateBody]
41464155
*
4147-
* NewGivenDef ::= [GivenConditional '=>'] NewGivenSig
4148-
* GivenConditional ::= [DefTypeParamClause | UsingParamClause] {UsingParamClause}
4149-
* NewGivenSig ::= GivenType ['as' id] ([‘=’ Expr] | TemplateBody)
4150-
* | ConstrApps ['as' id] TemplateBody
4151-
*
4156+
* NewGivenDef ::= [id ':'] GivenSig
4157+
* GivenSig ::= GivenImpl
4158+
* | '(' ')' '=>' GivenImpl
4159+
* | GivenConditional '=>' GivenSig
4160+
* GivenImpl ::= GivenType ([‘=’ Expr] | TemplateBody)
4161+
* | ConstrApps TemplateBody
4162+
* GivenConditional ::= DefTypeParamClause
4163+
* | DefTermParamClause
4164+
* | '(' FunArgTypes ')'
4165+
* | GivenType
41524166
* GivenType ::= AnnotType1 {id [nl] AnnotType1}
41534167
*/
41544168
def givenDef(start: Offset, mods: Modifiers, givenMod: Mod) = atSpan(start, nameStart) {
41554169
var mods1 = addMod(mods, givenMod)
41564170
val nameStart = in.offset
4157-
var name = if isIdent && followingIsOldStyleGivenSig() then ident() else EmptyTermName
41584171
var newSyntaxAllowed = in.featureEnabled(Feature.modularity)
4172+
val hasEmbeddedColon = !in.isColon && followingIsGivenDefWithColon()
4173+
val name = if isIdent && hasEmbeddedColon then ident() else EmptyTermName
4174+
4175+
def implemented(): List[Tree] =
4176+
if isSimpleLiteral then
4177+
rejectWildcardType(annotType()) :: Nil
4178+
else constrApp() match
4179+
case parent: Apply => parent :: moreConstrApps()
4180+
case parent if in.isIdent && newSyntaxAllowed =>
4181+
infixTypeRest(parent, _ => annotType1()) :: Nil
4182+
case parent => parent :: moreConstrApps()
4183+
4184+
// The term parameters and parent references */
4185+
def newTermParamssAndParents(numLeadParams: Int): (List[List[ValDef]], List[Tree]) =
4186+
if in.token == LPAREN && followingIsArrow() then
4187+
val params =
4188+
if in.lookahead.token == RPAREN && numLeadParams == 0 then
4189+
in.nextToken()
4190+
in.nextToken()
4191+
Nil
4192+
else
4193+
termParamClause(
4194+
ParamOwner.Given, numLeadParams, firstClause = true, initialMods = Modifiers(Given))
4195+
accept(ARROW)
4196+
if params.isEmpty then (params :: Nil, implemented())
4197+
else
4198+
val (paramss, parents) = newTermParamssAndParents(numLeadParams + params.length)
4199+
(params :: paramss, parents)
4200+
else
4201+
val parents = implemented()
4202+
if in.token == ARROW && parents.length == 1 && parents.head.isType then
4203+
in.nextToken()
4204+
val (paramss, parents1) = newTermParamssAndParents(numLeadParams + parents.length)
4205+
(typesToParams(parents, ParamOwner.Given, numLeadParams, Modifiers(Given)) :: paramss, parents1)
4206+
else
4207+
(Nil, parents)
4208+
4209+
/** Type parameters, term parameters and parent clauses */
4210+
def newSignature(): (List[TypeDef], (List[List[ValDef]], List[Tree])) =
4211+
val tparams =
4212+
if in.token == LBRACKET then
4213+
try typeParamClause(ParamOwner.Given)
4214+
finally accept(ARROW)
4215+
else Nil
4216+
(tparams, newTermParamssAndParents(numLeadParams = 0))
41594217

41604218
def moreConstrApps() =
41614219
if newSyntaxAllowed && in.token == COMMA then
@@ -4176,47 +4234,49 @@ object Parsers {
41764234
.asInstanceOf[List[ParamClause]]
41774235

41784236
val gdef =
4179-
val tparams = typeParamClauseOpt(ParamOwner.Given)
4180-
newLineOpt()
4181-
val vparamss =
4182-
if in.token == LPAREN && (in.lookahead.isIdent(nme.using) || name != EmptyTermName)
4183-
then termParamClauses(ParamOwner.Given)
4184-
else Nil
4185-
newLinesOpt()
4186-
val noParams = tparams.isEmpty && vparamss.isEmpty
4187-
val hasParamsOrId = !name.isEmpty || !noParams
4188-
if hasParamsOrId then
4189-
if in.isColon then
4190-
newSyntaxAllowed = false
4237+
val (tparams, (vparamss0, parents)) =
4238+
if in.isColon && !name.isEmpty then
41914239
in.nextToken()
4192-
else if newSyntaxAllowed then accept(ARROW)
4193-
else acceptColon()
4194-
val parents =
4195-
if isSimpleLiteral then
4196-
rejectWildcardType(annotType()) :: Nil
4197-
else constrApp() match
4198-
case parent: Apply => parent :: moreConstrApps()
4199-
case parent if in.isIdent && newSyntaxAllowed =>
4200-
infixTypeRest(parent, _ => annotType1()) :: Nil
4201-
case parent => parent :: moreConstrApps()
4202-
if newSyntaxAllowed && in.isIdent(nme.as) then
4203-
in.nextToken()
4204-
name = ident()
4205-
4240+
newSignature()
4241+
else if hasEmbeddedColon then
4242+
newSyntaxAllowed = false
4243+
val tparamsOld = typeParamClauseOpt(ParamOwner.Given)
4244+
newLineOpt()
4245+
val vparamssOld =
4246+
if in.token == LPAREN && (in.lookahead.isIdent(nme.using) || name != EmptyTermName)
4247+
then termParamClauses(ParamOwner.Given)
4248+
else Nil
4249+
acceptColon()
4250+
(tparamsOld, (vparamssOld, implemented()))
4251+
else
4252+
newSignature()
4253+
val hasParams = tparams.nonEmpty || vparamss0.nonEmpty
4254+
val vparamss = vparamss0 match
4255+
case Nil :: Nil => Nil
4256+
case _ => vparamss0
42064257
val parentsIsType = parents.length == 1 && parents.head.isType
42074258
if in.token == EQUALS && parentsIsType then
42084259
// given alias
42094260
accept(EQUALS)
42104261
mods1 |= Final
4211-
if noParams && !mods.is(Inline) then
4262+
if !hasParams && !mods.is(Inline) then
42124263
mods1 |= Lazy
42134264
ValDef(name, parents.head, subExpr())
42144265
else
42154266
DefDef(name, adjustDefParams(joinParams(tparams, vparamss)), parents.head, subExpr())
4216-
else if (isStatSep || isStatSeqEnd) && parentsIsType && !newSyntaxAllowed then
4267+
else if (isStatSep || isStatSeqEnd) && parentsIsType
4268+
&& !(name.isEmpty && newSyntaxAllowed)
4269+
// under new syntax, anonymous givens are translated to concrete classes,
4270+
// so it's treated as a structural instance.
4271+
then
42174272
// old-style abstract given
42184273
if name.isEmpty then
4219-
syntaxError(em"anonymous given cannot be abstract")
4274+
syntaxError(em"Anonymous given cannot be abstract, or maybe you want to define a concrete given and are missing a `()` argument?", in.lastOffset)
4275+
if newSyntaxAllowed then
4276+
warning(
4277+
em"""This defines an abstract given, which is deprecated. Use a `deferred` given instead.
4278+
|Or, if you intend to define a concrete given, follow the type with `()` arguments.""",
4279+
in.lastOffset)
42204280
DefDef(name, adjustDefParams(joinParams(tparams, vparamss)), parents.head, EmptyTree)
42214281
else
42224282
// structural instance
@@ -4228,12 +4288,16 @@ object Parsers {
42284288
val templ =
42294289
if isStatSep || isStatSeqEnd then
42304290
Template(constr, parents, Nil, EmptyValDef, Nil)
4231-
else if !newSyntaxAllowed || in.token == WITH then
4291+
else if !newSyntaxAllowed
4292+
|| in.token == WITH && tparams.isEmpty && vparamss.isEmpty
4293+
// if new syntax is still allowed and there are parameters, they mist be new style conditions,
4294+
// so old with-style syntax would not be allowed.
4295+
then
42324296
withTemplate(constr, parents)
42334297
else
42344298
possibleTemplateStart()
42354299
templateBodyOpt(constr, parents, Nil)
4236-
if noParams && !mods.is(Inline) then ModuleDef(name, templ)
4300+
if !hasParams && !mods.is(Inline) then ModuleDef(name, templ)
42374301
else TypeDef(name.toTypeName, templ)
42384302
end gdef
42394303
finalizeDef(gdef, mods1, start)

Diff for: docs/_docs/internals/syntax.md

+10-4
Original file line numberDiff line numberDiff line change
@@ -471,10 +471,16 @@ ConstrMods ::= {Annotation} [AccessModifier]
471471
ObjectDef ::= id [Template] ModuleDef(mods, name, template) // no constructor
472472
EnumDef ::= id ClassConstr InheritClauses EnumBody
473473
474-
GivenDef ::= [GivenConditional '=>'] GivenSig
475-
GivenConditional ::= [DefTypeParamClause | UsingParamClause] {UsingParamClause}
476-
GivenSig ::= GivenType ['as' id] ([‘=’ Expr] | TemplateBody)
477-
| ConstrApps ['as' id] TemplateBody
474+
GivenDef ::= [id ':'] GivenSig
475+
GivenSig ::= GivenImpl
476+
| '(' ')' '=>' GivenImpl
477+
| GivenConditional '=>' GivenSig
478+
GivenImpl ::= GivenType ([‘=’ Expr] | TemplateBody)
479+
| ConstrApps TemplateBody
480+
GivenConditional ::= DefTypeParamClause
481+
| DefTermParamClause
482+
| '(' FunArgTypes ')'
483+
| GivenType
478484
GivenType ::= AnnotType1 {id [nl] AnnotType1}
479485
480486
Extension ::= ‘extension’ [DefTypeParamClause] {UsingParamClause}

0 commit comments

Comments
 (0)