Skip to content

Commit 3c99306

Browse files
authored
Merge pull request #6773 from dotty-staging/change-given-syntax
Trial: `given as` instead of `delegate for`
2 parents cbc69fd + e9e926c commit 3c99306

27 files changed

+427
-321
lines changed

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -844,7 +844,7 @@ object desugar {
844844
*/
845845
def normalizeName(mdef: MemberDef, impl: Tree)(implicit ctx: Context): Name = {
846846
var name = mdef.name
847-
if (name.isEmpty) name = name.likeSpaced(s"${inventName(impl)}_instance".toTermName)
847+
if (name.isEmpty) name = name.likeSpaced(s"${inventName(impl)}_given".toTermName)
848848
if (ctx.owner == defn.ScalaPackageClass && defn.reservedScalaClassNames.contains(name.toTypeName)) {
849849
def kind = if (name.isTypeName) "class" else "object"
850850
ctx.error(em"illegal redefinition of standard $kind $name", mdef.sourcePos)

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

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,6 @@
11
package dotty.tools.dotc
22
package core
33

4-
import language.implicitConversions
5-
64
object Flags {
75

86
object opaques {

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

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -386,6 +386,7 @@ object StdNames {
386386
val array_length : N = "array_length"
387387
val array_update : N = "array_update"
388388
val arraycopy: N = "arraycopy"
389+
val as: N = "as"
389390
val asTerm: N = "asTerm"
390391
val asModule: N = "asModule"
391392
val asMethod: N = "asMethod"

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

Lines changed: 101 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -194,12 +194,16 @@ object Parsers {
194194
/** Is current token a hard or soft modifier (in modifier position or not)? */
195195
def isModifier: Boolean = modifierTokens.contains(in.token) || in.isSoftModifier
196196

197-
def isBindingIntro: Boolean =
198-
canStartBindingTokens.contains(in.token) &&
199-
!in.isSoftModifierInModifierPosition
197+
def isBindingIntro: Boolean = {
198+
in.token match {
199+
case USCORE | LPAREN => true
200+
case IDENTIFIER | BACKQUOTED_IDENT => in.lookaheadIn(BitSet(COLON, ARROW))
201+
case _ => false
202+
}
203+
} && !in.isSoftModifierInModifierPosition
200204

201205
def isExprIntro: Boolean =
202-
if (in.token == IMPLIED) in.lookaheadIn(BitSet(MATCH))
206+
if (in.token == IMPLIED || in.token == GIVEN) in.lookaheadIn(BitSet(MATCH))
203207
else (canStartExpressionTokens.contains(in.token) && !in.isSoftModifierInModifierPosition)
204208

205209
def isDefIntro(allowedMods: BitSet, excludedSoftModifiers: Set[TermName] = Set.empty): Boolean =
@@ -1274,7 +1278,7 @@ object Parsers {
12741278
* | SimpleExpr1 ArgumentExprs `=' Expr
12751279
* | Expr2
12761280
* | [‘inline’] Expr2 `match' `{' CaseClauses `}'
1277-
* | `delegate' `match' `{' ImplicitCaseClauses `}'
1281+
* | `given' `match' `{' ImplicitCaseClauses `}'
12781282
* Bindings ::= `(' [Binding {`,' Binding}] `)'
12791283
* Binding ::= (id | `_') [`:' Type]
12801284
* Expr2 ::= PostfixExpr [Ascription]
@@ -1293,7 +1297,7 @@ object Parsers {
12931297
if (in.token == MATCH) impliedMatch(start, imods)
12941298
else implicitClosure(start, location, imods)
12951299
}
1296-
else if(in.token == IMPLIED) {
1300+
else if (in.token == IMPLIED || in.token == GIVEN) {
12971301
in.nextToken()
12981302
if (in.token == MATCH)
12991303
impliedMatch(start, EmptyModifiers)
@@ -1478,7 +1482,7 @@ object Parsers {
14781482
*/
14791483
def impliedMatch(start: Int, imods: Modifiers) = {
14801484
def markFirstIllegal(mods: List[Mod]) = mods match {
1481-
case mod :: _ => syntaxError(em"illegal modifier for delegate match", mod.span)
1485+
case mod :: _ => syntaxError(em"illegal modifier for given match", mod.span)
14821486
case _ =>
14831487
}
14841488
imods.mods match {
@@ -1493,7 +1497,7 @@ object Parsers {
14931497
case pat => isVarPattern(pat)
14941498
}
14951499
if (!isImplicitPattern(pat))
1496-
syntaxError(em"not a legal pattern for a delegate match", pat.span)
1500+
syntaxError(em"not a legal pattern for a given match", pat.span)
14971501
}
14981502
result
14991503
}
@@ -2303,8 +2307,59 @@ object Parsers {
23032307
def paramClauses(ofClass: Boolean = false,
23042308
ofCaseClass: Boolean = false,
23052309
ofInstance: Boolean = false): List[List[ValDef]] = {
2310+
2311+
def followingIsParamClause: Boolean = {
2312+
val lookahead = in.lookaheadScanner
2313+
lookahead.nextToken()
2314+
paramIntroTokens.contains(lookahead.token) && {
2315+
lookahead.token != IDENTIFIER ||
2316+
lookahead.name == nme.inline || {
2317+
lookahead.nextToken()
2318+
lookahead.token == COLON
2319+
}
2320+
}
2321+
}
2322+
2323+
/** For given instance definitions we have a disambiguation problem:
2324+
* given A as B
2325+
* given C ...
2326+
* Is the second line a parameter `given C` for the first `given` definition, or is it
2327+
* a second `given` definition? We only know if we find a `for` or `as` in `...`
2328+
* The same problem arises for
2329+
* class A
2330+
* given C ...
2331+
* For method definitions we do not have this problem since a parameter clause
2332+
* in a method definition is always followed by something else. So in
2333+
* def m(...)
2334+
* given C ...
2335+
* we know that `given` must start a parameter list. It cannot be a new given` definition.
2336+
*/
2337+
def followingIsInstanceDef =
2338+
(ofClass || ofInstance) && {
2339+
val lookahead = in.lookaheadScanner // skips newline on startup
2340+
lookahead.nextToken() // skip the `given`
2341+
if (lookahead.token == IDENTIFIER || lookahead.token == BACKQUOTED_IDENT) {
2342+
lookahead.nextToken()
2343+
if (lookahead.token == LBRACKET) {
2344+
lookahead.nextToken()
2345+
var openBrackets = 1
2346+
while (openBrackets > 0 && lookahead.token != EOF) {
2347+
if (lookahead.token == LBRACKET) openBrackets += 1
2348+
else if (lookahead.token == RBRACKET) openBrackets -= 1
2349+
lookahead.nextToken()
2350+
}
2351+
}
2352+
}
2353+
lookahead.token == FOR ||
2354+
lookahead.token == IDENTIFIER && lookahead.name == nme.as
2355+
}
2356+
23062357
def recur(firstClause: Boolean, nparams: Int, contextualOnly: Boolean): List[List[ValDef]] = {
23072358
var initialMods = EmptyModifiers
2359+
val isNewLine = in.token == NEWLINE
2360+
newLineOptWhenFollowedBy(LPAREN)
2361+
if (in.token == NEWLINE && in.next.token == GIVEN && !followingIsInstanceDef)
2362+
in.nextToken()
23082363
if (in.token == GIVEN) {
23092364
in.nextToken()
23102365
initialMods |= Given
@@ -2313,22 +2368,10 @@ object Parsers {
23132368
in.nextToken()
23142369
initialMods |= Erased
23152370
}
2316-
val isContextual = initialMods.is(Given)
2371+
val isGiven = initialMods.is(Given)
23172372
newLineOptWhenFollowedBy(LPAREN)
2318-
def isParamClause: Boolean =
2319-
!isContextual || {
2320-
val lookahead = in.lookaheadScanner
2321-
lookahead.nextToken()
2322-
paramIntroTokens.contains(lookahead.token) && {
2323-
lookahead.token != IDENTIFIER ||
2324-
lookahead.name == nme.inline || {
2325-
lookahead.nextToken()
2326-
lookahead.token == COLON
2327-
}
2328-
}
2329-
}
2330-
if (in.token == LPAREN && isParamClause) {
2331-
if (contextualOnly && !isContextual)
2373+
if (in.token == LPAREN && (!isGiven || followingIsParamClause)) {
2374+
if (contextualOnly && !isGiven)
23322375
if (ofInstance) syntaxError(em"parameters of instance definitions must come after `given'")
23332376
else syntaxError(em"normal parameters cannot come after `given' clauses")
23342377
val params = paramClause(
@@ -2337,15 +2380,15 @@ object Parsers {
23372380
firstClause = firstClause,
23382381
initialMods = initialMods)
23392382
val lastClause = params.nonEmpty && params.head.mods.flags.is(Implicit)
2340-
params :: (if (lastClause) Nil else recur(firstClause = false, nparams + params.length, isContextual))
2383+
params :: (if (lastClause) Nil else recur(firstClause = false, nparams + params.length, isGiven))
23412384
}
2342-
else if (isContextual) {
2385+
else if (isGiven) {
23432386
val tps = commaSeparated(() => annotType())
23442387
var counter = nparams
23452388
def nextIdx = { counter += 1; counter }
23462389
val paramFlags = if (ofClass) Private | Local | ParamAccessor else Param
23472390
val params = tps.map(makeSyntheticParameter(nextIdx, _, paramFlags | Synthetic | Given))
2348-
params :: recur(firstClause = false, nparams + params.length, isContextual)
2391+
params :: recur(firstClause = false, nparams + params.length, isGiven)
23492392
}
23502393
else Nil
23512394
}
@@ -2359,12 +2402,12 @@ object Parsers {
23592402

23602403
type ImportConstr = (Boolean, Tree, List[Tree]) => Tree
23612404

2362-
/** Import ::= import [delegate] [ImportExpr {`,' ImportExpr}
2363-
* Export ::= export [delegate] [ImportExpr {`,' ImportExpr}
2405+
/** Import ::= `import' [`given'] [ImportExpr {`,' ImportExpr}
2406+
* Export ::= `export' [`given'] [ImportExpr {`,' ImportExpr}
23642407
*/
23652408
def importClause(leading: Token, mkTree: ImportConstr): List[Tree] = {
23662409
val offset = accept(leading)
2367-
val importDelegate = in.token == IMPLIED
2410+
val importDelegate = in.token == IMPLIED || in.token == GIVEN
23682411
if (importDelegate) in.nextToken()
23692412
commaSeparated(importExpr(importDelegate, mkTree)) match {
23702413
case t :: rest =>
@@ -2383,15 +2426,21 @@ object Parsers {
23832426

23842427
/** ImportSelectors ::= `{' {ImportSelector `,'} FinalSelector ‘}’
23852428
* FinalSelector ::= ImportSelector
2386-
* | ‘_’
2387-
* | ‘for’ InfixType {‘,’ InfixType}
2429+
* | ‘_’ [‘:’ Type]
23882430
*/
23892431
def importSelectors(): List[Tree] = in.token match {
23902432
case USCORE =>
2391-
wildcardIdent() :: Nil
2433+
atSpan(in.skipToken()) {
2434+
val id = Ident(nme.WILDCARD)
2435+
if (in.token == COLON) {
2436+
in.nextToken()
2437+
TypeBoundsTree(EmptyTree, typ())
2438+
}
2439+
else id
2440+
} :: Nil
23922441
case FOR =>
23932442
if (!importDelegate)
2394-
syntaxError(em"`for` qualifier only allowed in `import delegate`")
2443+
syntaxError(em"`for` qualifier only allowed in `import given`")
23952444
atSpan(in.skipToken()) {
23962445
var t = infixType()
23972446
while (in.token == COMMA) {
@@ -2695,7 +2744,7 @@ object Parsers {
26952744
/** TmplDef ::= ([‘case’] ‘class’ | ‘trait’) ClassDef
26962745
* | [‘case’] ‘object’ ObjectDef
26972746
* | ‘enum’ EnumDef
2698-
* | ‘instance’ InstanceDef
2747+
* | ‘given’ GivenDef
26992748
*/
27002749
def tmplDef(start: Int, mods: Modifiers): Tree = {
27012750
in.token match {
@@ -2711,8 +2760,8 @@ object Parsers {
27112760
objectDef(start, posMods(start, mods | Case | Module))
27122761
case ENUM =>
27132762
enumDef(start, posMods(start, mods | Enum))
2714-
case IMPLIED =>
2715-
instanceDef(start, mods, atSpan(in.skipToken()) { Mod.Delegate() })
2763+
case IMPLIED | GIVEN =>
2764+
instanceDef(in.token == GIVEN, start, mods, atSpan(in.skipToken()) { Mod.Delegate() })
27162765
case _ =>
27172766
syntaxErrorOrIncomplete(ExpectedStartOfTopLevelDefinition())
27182767
EmptyTree
@@ -2804,17 +2853,16 @@ object Parsers {
28042853
Template(constr, parents, Nil, EmptyValDef, Nil)
28052854
}
28062855

2807-
/** InstanceDef ::= [id] [DefTypeParamClause] InstanceBody
2808-
* InstanceParams ::= [DefTypeParamClause] {GivenParamClause}
2809-
* InstanceBody ::= [‘for’ ConstrApp {‘,’ ConstrApp }] {GivenParamClause} [TemplateBody]
2810-
* | ‘for’ Type {GivenParamClause} ‘=’ Expr
2856+
/** GivenDef ::= [id] [DefTypeParamClause] GivenBody
2857+
* GivenBody ::= [‘as ConstrApp {‘,’ ConstrApp }] {GivenParamClause} [TemplateBody]
2858+
* | ‘as’ Type {GivenParamClause} ‘=’ Expr
28112859
*/
2812-
def instanceDef(start: Offset, mods: Modifiers, instanceMod: Mod) = atSpan(start, nameStart) {
2860+
def instanceDef(newStyle: Boolean, start: Offset, mods: Modifiers, instanceMod: Mod) = atSpan(start, nameStart) {
28132861
var mods1 = addMod(mods, instanceMod)
2814-
val name = if (isIdent) ident() else EmptyTermName
2862+
val name = if (isIdent && (!newStyle || in.name != nme.as)) ident() else EmptyTermName
28152863
val tparams = typeParamClauseOpt(ParamOwner.Def)
28162864
val parents =
2817-
if (in.token == FOR) {
2865+
if (!newStyle && in.token == FOR || isIdent(nme.as)) { // for the moment, accept both `given for` and `given as`
28182866
in.nextToken()
28192867
tokenSeparated(COMMA, constrApp)
28202868
}
@@ -3121,8 +3169,16 @@ object Parsers {
31213169
setLastStatOffset()
31223170
if (in.token == IMPORT)
31233171
stats ++= importClause(IMPORT, Import)
3124-
else if (in.token == GIVEN)
3125-
stats += implicitClosure(in.offset, Location.InBlock, modifiers(closureMods))
3172+
else if (in.token == GIVEN) {
3173+
val start = in.offset
3174+
val mods = modifiers(closureMods)
3175+
mods.mods match {
3176+
case givenMod :: Nil if !isBindingIntro =>
3177+
stats += instanceDef(true, start, EmptyModifiers, Mod.Delegate().withSpan(givenMod.span))
3178+
case _ =>
3179+
stats += implicitClosure(in.offset, Location.InBlock, mods)
3180+
}
3181+
}
31263182
else if (isExprIntro)
31273183
stats += expr(Location.InBlock)
31283184
else if (isDefIntro(localModifierTokens, excludedSoftModifiers = Set(nme.`opaque`)))

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

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -216,16 +216,14 @@ object Tokens extends TokensCommon {
216216
USCORE, NULL, THIS, SUPER, TRUE, FALSE, RETURN, QUOTEID, XMLSTART)
217217

218218
final val canStartExpressionTokens: TokenSet = atomicExprTokens | BitSet(
219-
LBRACE, LPAREN, QUOTE, IF, DO, WHILE, FOR, NEW, TRY, THROW, IMPLIED)
219+
LBRACE, LPAREN, QUOTE, IF, DO, WHILE, FOR, NEW, TRY, THROW, IMPLIED, GIVEN)
220220

221221
final val canStartTypeTokens: TokenSet = literalTokens | identifierTokens | BitSet(
222222
THIS, SUPER, USCORE, LPAREN, AT)
223223

224-
final val canStartBindingTokens: TokenSet = identifierTokens | BitSet(USCORE, LPAREN)
225-
226224
final val templateIntroTokens: TokenSet = BitSet(CLASS, TRAIT, OBJECT, ENUM, CASECLASS, CASEOBJECT)
227225

228-
final val dclIntroTokens: TokenSet = BitSet(DEF, VAL, VAR, TYPE, IMPLIED)
226+
final val dclIntroTokens: TokenSet = BitSet(DEF, VAL, VAR, TYPE, IMPLIED, GIVEN)
229227

230228
final val defIntroTokens: TokenSet = templateIntroTokens | dclIntroTokens
231229

compiler/src/dotty/tools/dotc/printing/RefinedPrinter.scala

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -319,7 +319,7 @@ class RefinedPrinter(_ctx: Context) extends PlainPrinter(_ctx) {
319319
case _ => tree
320320
}
321321

322-
def importText(deleg: Boolean, expr: Tree, selectors: List[Tree]) = {
322+
def importText(givenOnly: Boolean, expr: Tree, selectors: List[Tree]) = {
323323
def selectorText(sel: Tree): Text = sel match {
324324
case Thicket(l :: r :: Nil) => toTextGlobal(l) ~ " => " ~ toTextGlobal(r)
325325
case _: Ident => toTextGlobal(sel)
@@ -329,7 +329,7 @@ class RefinedPrinter(_ctx: Context) extends PlainPrinter(_ctx) {
329329
case id :: Nil => toText(id)
330330
case _ => "{" ~ Text(selectors map selectorText, ", ") ~ "}"
331331
}
332-
(keywordText("delegate ") provided deleg) ~
332+
(keywordText("given ") provided givenOnly) ~
333333
toTextLocal(expr) ~ "." ~ selectorsText
334334
}
335335

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

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -91,26 +91,26 @@ class ImportInfo(symf: Context => Symbol, val selectors: List[untpd.Tree],
9191
myForwardMapping = myForwardMapping.updated(name, name)
9292
myReverseMapping = myReverseMapping.updated(name, name)
9393
case TypeBoundsTree(_, tpt) =>
94-
myWildcardImport = true // details are handled separately in impliedBounds
94+
myWildcardImport = true // details are handled separately in wildcardBounds
9595
}
9696
recur(sels1)
9797
case nil =>
9898
}
9999
recur(selectors)
100100
}
101101

102-
private[this] var myDelegateBound: Type = null
102+
private[this] var myWildcardBound: Type = null
103103

104-
def impliedBound(implicit ctx: Context): Type = {
105-
if (myDelegateBound == null)
106-
myDelegateBound = selectors.lastOption match {
104+
def wildcardBound(implicit ctx: Context): Type = {
105+
if (myWildcardBound == null)
106+
myWildcardBound = selectors.lastOption match {
107107
case Some(TypeBoundsTree(_, untpd.TypedSplice(tpt))) => tpt.tpe
108108
case Some(TypeBoundsTree(_, tpt)) =>
109-
myDelegateBound = NoType
109+
myWildcardBound = NoType
110110
ctx.typer.typedAheadType(tpt).tpe
111111
case _ => NoType
112112
}
113-
myDelegateBound
113+
myWildcardBound
114114
}
115115

116116
private def implicitFlags(implicit ctx: Context) =
@@ -128,8 +128,8 @@ class ImportInfo(symf: Context => Symbol, val selectors: List[untpd.Tree],
128128
val renamed = forwardMapping(ref.name)
129129
if (renamed == ref.name) ref :: Nil
130130
else if (renamed != null) new RenamedImplicitRef(ref, renamed) :: Nil
131-
else if (!impliedBound.exists ||
132-
normalizedCompatible(ref, impliedBound, keepConstraint = false)) ref :: Nil
131+
else if (!wildcardBound.exists ||
132+
normalizedCompatible(ref, wildcardBound, keepConstraint = false)) ref :: Nil
133133
else Nil
134134
}
135135
}

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

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -189,9 +189,9 @@ class Typer extends Namer
189189
var excl = EmptyFlags
190190
if (imp.importDelegate) reqd |= Delegate else excl |= Delegate
191191
var denot = pre.memberBasedOnFlags(name, reqd, excl).accessibleFrom(pre)(refctx)
192-
if (checkBounds && imp.impliedBound.exists)
192+
if (checkBounds && imp.wildcardBound.exists)
193193
denot = denot.filterWithPredicate(mbr =>
194-
NoViewsAllowed.normalizedCompatible(mbr.info, imp.impliedBound, keepConstraint = false))
194+
NoViewsAllowed.normalizedCompatible(mbr.info, imp.wildcardBound, keepConstraint = false))
195195

196196
// Pass refctx so that any errors are reported in the context of the
197197
// reference instead of the
@@ -234,7 +234,7 @@ class Typer extends Namer
234234
*/
235235
def wildImportRef(imp: ImportInfo)(implicit ctx: Context): Type =
236236
if (imp.isWildcardImport && !imp.excluded.contains(name.toTermName) && name != nme.CONSTRUCTOR)
237-
selection(imp, name, checkBounds = imp.importDelegate)
237+
selection(imp, name, checkBounds = true)
238238
else NoType
239239

240240
/** Is (some alternative of) the given predenotation `denot`

0 commit comments

Comments
 (0)