Skip to content

Commit 54f0a85

Browse files
committed
Implement given/using syntax
1 parent ba174c0 commit 54f0a85

File tree

5 files changed

+44
-109
lines changed

5 files changed

+44
-109
lines changed

compiler/src/dotty/tools/dotc/core/StdNames.scala

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -601,6 +601,7 @@ object StdNames {
601601
val universe: N = "universe"
602602
val update: N = "update"
603603
val updateDynamic: N = "updateDynamic"
604+
val using: N = "using"
604605
val value: N = "value"
605606
val valueOf : N = "valueOf"
606607
val values: N = "values"

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

Lines changed: 33 additions & 77 deletions
Original file line numberDiff line numberDiff line change
@@ -911,6 +911,7 @@ object Parsers {
911911

912912
/** Are the next tokens a prefix of a formal parameter or given type?
913913
* @pre: current token is LPAREN
914+
* TODO: Drop once syntax has stabilized
914915
*/
915916
def followingIsParamOrGivenType() =
916917
val lookahead = in.LookaheadScanner()
@@ -944,7 +945,6 @@ object Parsers {
944945
else
945946
lookahead.token == SUBTYPE // TODO: remove
946947
|| lookahead.isIdent(nme.as)
947-
|| lookahead.token == WITH && lookahead.ch != Chars.LF // TODO: remove LF test
948948

949949
def followingIsExtension() =
950950
val lookahead = in.LookaheadScanner()
@@ -1344,8 +1344,8 @@ object Parsers {
13441344
* MonoFunType ::= FunArgTypes (‘=>’ | ‘?=>’) Type
13451345
* PolyFunType ::= HKTypeParamClause '=>' Type
13461346
* FunArgTypes ::= InfixType
1347-
* | `(' [ [ ‘['erased'] FunArgType {`,' FunArgType } ] `)'
1348-
* | '(' [ ‘['erased'] TypedFunParam {',' TypedFunParam } ')'
1347+
* | `(' [ [ ‘[using]’ ‘['erased'] FunArgType {`,' FunArgType } ] `)'
1348+
* | '(' [ ‘[using]’ ‘['erased'] TypedFunParam {',' TypedFunParam } ')'
13491349
*/
13501350
def typ(): Tree = {
13511351
val start = in.offset
@@ -2184,7 +2184,6 @@ object Parsers {
21842184
* | SimpleExpr `.' MatchClause
21852185
* | SimpleExpr (TypeArgs | NamedTypeArgs)
21862186
* | SimpleExpr1 ArgumentExprs
2187-
* | SimpleExpr ContextArguments
21882187
* Quoted ::= ‘'’ ‘{’ Block ‘}’
21892188
* | ‘'’ ‘[’ Type ‘]’
21902189
*/
@@ -2253,8 +2252,6 @@ object Parsers {
22532252
case DOT =>
22542253
in.nextToken()
22552254
simpleExprRest(selector(t), canApply = true)
2256-
case DOTWITH =>
2257-
simpleExprRest(contextArguments(t), canApply = true)
22582255
case LBRACKET =>
22592256
val tapp = atSpan(startOffset(t), in.offset) { TypeApply(t, typeArgs(namedOK = true, wildOK = false)) }
22602257
simpleExprRest(tapp, canApply = true)
@@ -2294,13 +2291,13 @@ object Parsers {
22942291
def exprsInParensOpt(): List[Tree] =
22952292
if (in.token == RPAREN) Nil else commaSeparated(exprInParens)
22962293

2297-
/** ParArgumentExprs ::= `(' [‘given’] [ExprsInParens] `)'
2294+
/** ParArgumentExprs ::= `(' [‘using’] [ExprsInParens] `)'
22982295
* | `(' [ExprsInParens `,'] PostfixExpr `:' `_' `*' ')'
22992296
*/
23002297
def parArgumentExprs(): (List[Tree], Boolean) = inParens {
23012298
if in.token == RPAREN then
23022299
(Nil, false)
2303-
else if in.token == GIVEN then
2300+
else if in.token == GIVEN || isIdent(nme.using) then
23042301
in.nextToken()
23052302
(commaSeparated(argumentExpr), true)
23062303
else
@@ -2331,7 +2328,7 @@ object Parsers {
23312328
else fn
23322329
}
23332330

2334-
/** ParArgumentExprss ::= {ParArgumentExprs | ContextArguments}
2331+
/** ParArgumentExprss ::= {ParArgumentExprs}
23352332
*
23362333
* Special treatment for arguments to primary constructor annotations.
23372334
* (...) is considered an argument only if it does not look like a formal
@@ -2356,8 +2353,6 @@ object Parsers {
23562353
parArgumentExprss(
23572354
atSpan(startOffset(fn)) { mkApply(fn, parArgumentExprs()) }
23582355
)
2359-
else if in.token == DOTWITH then
2360-
parArgumentExprss(contextArguments(fn))
23612356
else fn
23622357
}
23632358

@@ -2387,14 +2382,6 @@ object Parsers {
23872382
else Block(stats, EmptyTree)
23882383
}
23892384

2390-
/** ContextArguments ::= ‘.’ ‘with’ ArgumentExprs */
2391-
def contextArguments(t: Tree): Tree =
2392-
if in.token == DOTWITH then
2393-
atSpan(t.span.start, in.skipToken()) {
2394-
Apply(t, argumentExprs()._1).setGivenApply()
2395-
}
2396-
else t
2397-
23982385
/** Guard ::= if PostfixExpr
23992386
*/
24002387
def guard(): Tree =
@@ -2886,23 +2873,22 @@ object Parsers {
28862873
def typeParamClauseOpt(ownerKind: ParamOwner.Value): List[TypeDef] =
28872874
if (in.token == LBRACKET) typeParamClause(ownerKind) else Nil
28882875

2889-
/** AnnotTypes ::= AnnotType {‘,’ AnnotType}
2890-
* Types ::= Type {‘,’ Type}
2876+
/** ContextTypes ::= Type {‘,’ Type}
28912877
*/
2892-
def givenTypes(parseType: () => Tree, nparams: Int, ofClass: Boolean): List[ValDef] =
2893-
val tps = commaSeparated(parseType)
2878+
def contextTypes(ofClass: Boolean, nparams: Int): List[ValDef] =
2879+
val tps = commaSeparated(typ)
28942880
var counter = nparams
28952881
def nextIdx = { counter += 1; counter }
28962882
val paramFlags = if ofClass then Private | Local | ParamAccessor else Param
28972883
tps.map(makeSyntheticParameter(nextIdx, _, paramFlags | Synthetic | Given))
28982884

2899-
/** ClsParamClause ::= ‘(’ [‘erased’] ClsParams ‘)’
2900-
* GivenClsParamClause::= 'with' (‘(’ (ClsParams | Types) ‘)’ | AnnotTypes)
2885+
/** ClsParamClause ::= ‘(’ [‘erased’] ClsParams ‘)’ | UsingClsParamClause
2886+
* UsingClsParamClause::= ‘(’ ‘using’ [‘erased’] (ClsParams | ContextTypes) ‘)’
29012887
* ClsParams ::= ClsParam {‘,’ ClsParam}
29022888
* ClsParam ::= {Annotation}
29032889
*
2904-
* DefParamClause ::= ‘(’ [‘erased’] DefParams ‘)’
2905-
* GivenParamClause ::= ‘with’ (‘(’ (DefParams | Types) ‘)’ | AnnotTypes)
2890+
* DefParamClause ::= ‘(’ [‘erased’] DefParams ‘)’ | UsingParamClause
2891+
* UsingParamClause ::= ‘(’ ‘using’ [‘erased’] (DefParams | ContextTypes) ‘)’
29062892
* DefParams ::= DefParam {‘,’ DefParam}
29072893
* DefParam ::= {Annotation} [‘inline’] Param
29082894
*
@@ -2915,17 +2901,17 @@ object Parsers {
29152901
ofCaseClass: Boolean = false, // owner is a case class
29162902
prefix: Boolean = false, // clause precedes name of an extension method
29172903
givenOnly: Boolean = false, // only given parameters allowed
2918-
firstClause: Boolean = false, // clause is the first in regular list of clauses
2919-
prefixMods: Modifiers = EmptyModifiers // is `Given` if this is a with clause
2904+
firstClause: Boolean = false // clause is the first in regular list of clauses
29202905
): List[ValDef] = {
2921-
var impliedMods: Modifiers = prefixMods
2906+
var impliedMods: Modifiers = EmptyModifiers
29222907

2923-
def impliedModOpt(token: Token, mod: () => Mod): Boolean =
2924-
if in.token == token then
2925-
impliedMods = addMod(impliedMods, atSpan(in.skipToken()) { mod() })
2926-
true
2908+
def addParamMod(mod: () => Mod) = impliedMods = addMod(impliedMods, atSpan(in.skipToken()) { mod() })
2909+
2910+
def paramMods() =
2911+
if in.token == IMPLICIT then addParamMod(() => Mod.Implicit())
29272912
else
2928-
false
2913+
if in.token == GIVEN || isIdent(nme.using) then addParamMod(() => Mod.Given())
2914+
if in.token == ERASED then addParamMod(() => Mod.Erased())
29292915

29302916
def param(): ValDef = {
29312917
val start = in.offset
@@ -2984,32 +2970,22 @@ object Parsers {
29842970
val clause =
29852971
if prefix then param() :: Nil
29862972
else
2987-
if !impliedModOpt(IMPLICIT, () => Mod.Implicit()) then
2988-
impliedModOpt(GIVEN, () => Mod.Given())
2989-
impliedModOpt(ERASED, () => Mod.Erased())
2973+
paramMods()
29902974
if givenOnly && !impliedMods.is(Given) then
2991-
syntaxError("Normal parameter clause cannot follow context parameter clause")
2975+
syntaxError("`using` expected")
29922976
val isParams =
29932977
!impliedMods.is(Given)
29942978
|| startParamTokens.contains(in.token)
29952979
|| isIdent && (in.name == nme.inline || in.lookaheadIn(BitSet(COLON)))
29962980
if isParams then commaSeparated(() => param())
2997-
else givenTypes(typ, nparams, ofClass)
2981+
else contextTypes(ofClass, nparams)
29982982
checkVarArgsRules(clause)
29992983
clause
30002984
}
30012985
}
30022986

30032987
/** ClsParamClauses ::= {ClsParamClause} [[nl] ‘(’ [‘implicit’] ClsParams ‘)’]
3004-
* | ClsParamClause ClsParamClauses
3005-
* | ClsParamClauses1
3006-
* ClsParamClauses1 ::= WithClsParamClause ClsParamClauses
3007-
* | AnnotTypes ClsParamClauses1ClsParamClauses
30082988
* DefParamClauses ::= {DefParamClause} [[nl] ‘(’ [‘implicit’] DefParams ‘)’]
3009-
* | DefParamClause DefParamClauses
3010-
* | DefParamClauses1
3011-
* DefParamClauses1 ::= WithCaramClause DefParamClauses
3012-
* | AnnotTypes DeParamClauses1
30132989
*
30142990
* @return The parameter definitions
30152991
*/
@@ -3019,33 +2995,18 @@ object Parsers {
30192995

30202996
def recur(firstClause: Boolean, nparams: Int): List[List[ValDef]] =
30212997
newLineOptWhenFollowedBy(LPAREN)
3022-
val prefixMods =
3023-
if in.token == WITH && in.ch != Chars.LF then // TODO: remove LF test
3024-
in.nextToken()
3025-
Modifiers(Given)
3026-
else
3027-
EmptyModifiers
30282998
if in.token == LPAREN then
30292999
val paramsStart = in.offset
30303000
val params = paramClause(
30313001
nparams,
30323002
ofClass = ofClass,
30333003
ofCaseClass = ofCaseClass,
30343004
givenOnly = givenOnly,
3035-
firstClause = firstClause,
3036-
prefixMods = prefixMods)
3005+
firstClause = firstClause)
30373006
val lastClause = params.nonEmpty && params.head.mods.flags.is(Implicit)
3038-
val isGivenClause = prefixMods.is(Given)
3039-
|| params.nonEmpty && params.head.mods.flags.is(Given)
30403007
params :: (
30413008
if lastClause then Nil
30423009
else recur(firstClause = false, nparams + params.length))
3043-
else if prefixMods.is(Given) then
3044-
val params = givenTypes(annotType, nparams, ofClass)
3045-
params :: (
3046-
if in.token == WITH then recur(firstClause = false, nparams + params.length)
3047-
else Nil
3048-
)
30493010
else Nil
30503011
end recur
30513012

@@ -3090,8 +3051,8 @@ object Parsers {
30903051

30913052
/** ImportSelectors ::= id [‘=>’ id | ‘=>’ ‘_’] [‘,’ ImportSelectors]
30923053
* | WildCardSelector {‘,’ WildCardSelector}
3093-
* WildCardSelector ::= ‘given’ [InfixType]
3094-
* | ‘_' [‘:’ InfixType]
3054+
* WildCardSelector ::= ‘given’ (‘_' | InfixType)
3055+
* | ‘_'
30953056
*/
30963057
def importSelectors(idOK: Boolean): List[ImportSelector] =
30973058
val selToken = in.token
@@ -3543,10 +3504,7 @@ object Parsers {
35433504

35443505
/** GivenDef ::= [GivenSig] [‘_’ ‘<:’] Type ‘=’ Expr
35453506
* | [GivenSig] ConstrApps [TemplateBody]
3546-
* GivenSig ::= [id] [DefTypeParamClause] {WithParamsOrTypes} ‘as’
3547-
* ExtParamClause ::= [DefTypeParamClause] DefParamClause
3548-
* ExtMethods ::= [nl] ‘{’ ‘def’ DefDef {semi ‘def’ DefDef} ‘}’
3549-
* WithParamsOrTypes ::= WithParamClause | AnnotTypes
3507+
* GivenSig ::= [id] [DefTypeParamClause] {UsingParamClauses} ‘as’
35503508
*/
35513509
def givenDef(start: Offset, mods: Modifiers, instanceMod: Mod) = atSpan(start, nameStart) {
35523510
var mods1 = addMod(mods, instanceMod)
@@ -3571,18 +3529,17 @@ object Parsers {
35713529
templ.body.foreach(checkExtensionMethod(tparams, _))
35723530
ModuleDef(name, templ)
35733531
else
3574-
val hasLabel = !name.isEmpty && in.token == COLON || in.isIdent(nme.as)
3532+
val hasLabel = !name.isEmpty && in.token == COLON || isIdent(nme.as)
35753533
if hasLabel then in.nextToken()
35763534
val tparams = typeParamClauseOpt(ParamOwner.Def)
35773535
val paramsStart = in.offset
35783536
val vparamss =
3579-
if in.token == WITH && in.ch != Chars.LF // TODO: remove LF test
3580-
|| in.token == LPAREN && followingIsParamOrGivenType()
3537+
if in.token == LPAREN && followingIsParamOrGivenType()
35813538
then paramClauses()
35823539
else Nil
35833540
def checkAllGivens(vparamss: List[List[ValDef]], what: String) =
35843541
vparamss.foreach(_.foreach(vparam =>
3585-
if !vparam.mods.is(Given) then syntaxError(em"$what must be `given`", vparam.span)))
3542+
if !vparam.mods.is(Given) then syntaxError(em"$what must be preceded by `using`", vparam.span)))
35863543
checkAllGivens(vparamss, "parameter of given instance")
35873544
val parents =
35883545
if in.token == SUBTYPE && !hasLabel then
@@ -3620,7 +3577,7 @@ object Parsers {
36203577
finalizeDef(gdef, mods1, start)
36213578
}
36223579

3623-
/** ExtensionDef ::= [id] ‘on’ ExtParamClause GivenParamClauses ExtMethods
3580+
/** ExtensionDef ::= [id] ‘on’ ExtParamClause {UsingParamClause} ExtMethods
36243581
*/
36253582
def extensionDef(start: Offset, mods: Modifiers): ModuleDef =
36263583
in.nextToken()
@@ -3644,8 +3601,7 @@ object Parsers {
36443601
val constrApp: () => Tree = () => {
36453602
val t = rejectWildcardType(annotType(), fallbackTree = Ident(nme.ERROR))
36463603
// Using Ident(nme.ERROR) to avoid causing cascade errors on non-user-written code
3647-
if in.token == LPAREN || in.token == DOTWITH then parArgumentExprss(wrapNew(t))
3648-
else t
3604+
if in.token == LPAREN then parArgumentExprss(wrapNew(t)) else t
36493605
}
36503606

36513607
/** ConstrApps ::= ConstrApp {(‘,’ | ‘with’) ConstrApp}

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

Lines changed: 1 addition & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -593,11 +593,7 @@ object Scanners {
593593
this.copyFrom(prev)
594594
}
595595

596-
/** - Join CASE + CLASS => CASECLASS,
597-
* CASE + OBJECT => CASEOBJECT,
598-
* SEMI + ELSE => ELSE,
599-
* COLON + <EOL> => COLONEOL
600-
* DOT + WITH => DOTWITH
596+
/** - Join CASE + CLASS => CASECLASS, CASE + OBJECT => CASEOBJECT, SEMI + ELSE => ELSE, COLON + <EOL> => COLONEOL
601597
* - Insert missing OUTDENTs at EOF
602598
*/
603599
def postProcessToken(): Unit = {
@@ -623,10 +619,6 @@ object Scanners {
623619
} else if (token == EOF) { // e.g. when the REPL is parsing "val List(x, y, _*,"
624620
/* skip the trailing comma */
625621
} else reset()
626-
case DOT =>
627-
lookahead()
628-
if token == WITH then fuse(DOTWITH)
629-
else reset()
630622
case COLON =>
631623
if colonSyntax then observeColonEOL()
632624
case EOF | RBRACE =>

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

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -204,7 +204,6 @@ object Tokens extends TokensCommon {
204204
final val QUOTE = 87; enter(QUOTE, "'")
205205

206206
final val COLONEOL = 88; enter(COLONEOL, ":", ": at eol")
207-
final val DOTWITH = 89; enter(DOTWITH, ".with")
208207

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

0 commit comments

Comments
 (0)