Skip to content

Commit bb073b4

Browse files
committed
Disambiguate given instances and parameters
1 parent fdb718f commit bb073b4

File tree

2 files changed

+69
-23
lines changed

2 files changed

+69
-23
lines changed

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

Lines changed: 57 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -2307,9 +2307,59 @@ object Parsers {
23072307
def paramClauses(ofClass: Boolean = false,
23082308
ofCaseClass: Boolean = false,
23092309
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+
23102357
def recur(firstClause: Boolean, nparams: Int, contextualOnly: Boolean): List[List[ValDef]] = {
23112358
var initialMods = EmptyModifiers
2359+
val isNewLine = in.token == NEWLINE
23122360
newLineOptWhenFollowedBy(LPAREN)
2361+
if (in.token == NEWLINE && in.next.token == GIVEN && !followingIsInstanceDef)
2362+
in.nextToken()
23132363
if (in.token == GIVEN) {
23142364
in.nextToken()
23152365
initialMods |= Given
@@ -2318,22 +2368,10 @@ object Parsers {
23182368
in.nextToken()
23192369
initialMods |= Erased
23202370
}
2321-
val isContextual = initialMods.is(Given)
2371+
val isGiven = initialMods.is(Given)
23222372
newLineOptWhenFollowedBy(LPAREN)
2323-
def isParamClause: Boolean =
2324-
!isContextual || {
2325-
val lookahead = in.lookaheadScanner
2326-
lookahead.nextToken()
2327-
paramIntroTokens.contains(lookahead.token) && {
2328-
lookahead.token != IDENTIFIER ||
2329-
lookahead.name == nme.inline || {
2330-
lookahead.nextToken()
2331-
lookahead.token == COLON
2332-
}
2333-
}
2334-
}
2335-
if (in.token == LPAREN && isParamClause) {
2336-
if (contextualOnly && !isContextual)
2373+
if (in.token == LPAREN && (!isGiven || followingIsParamClause)) {
2374+
if (contextualOnly && !isGiven)
23372375
if (ofInstance) syntaxError(em"parameters of instance definitions must come after `given'")
23382376
else syntaxError(em"normal parameters cannot come after `given' clauses")
23392377
val params = paramClause(
@@ -2342,15 +2380,15 @@ object Parsers {
23422380
firstClause = firstClause,
23432381
initialMods = initialMods)
23442382
val lastClause = params.nonEmpty && params.head.mods.flags.is(Implicit)
2345-
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))
23462384
}
2347-
else if (isContextual) {
2385+
else if (isGiven) {
23482386
val tps = commaSeparated(() => annotType())
23492387
var counter = nparams
23502388
def nextIdx = { counter += 1; counter }
23512389
val paramFlags = if (ofClass) Private | Local | ParamAccessor else Param
23522390
val params = tps.map(makeSyntheticParameter(nextIdx, _, paramFlags | Synthetic | Given))
2353-
params :: recur(firstClause = false, nparams + params.length, isContextual)
2391+
params :: recur(firstClause = false, nparams + params.length, isGiven)
23542392
}
23552393
else Nil
23562394
}
@@ -2819,7 +2857,7 @@ object Parsers {
28192857
val name = if (isIdent && (!newStyle || in.name != nme.as)) ident() else EmptyTermName
28202858
val tparams = typeParamClauseOpt(ParamOwner.Def)
28212859
val parents =
2822-
if (!newStyle && in.token == FOR || newStyle && isIdent(nme.as)) {
2860+
if (!newStyle && in.token == FOR || isIdent(nme.as)) { // for the moment, accept both `given for` and `given as`
28232861
in.nextToken()
28242862
tokenSeparated(COMMA, constrApp)
28252863
}

tests/pos/i5978.scala

Lines changed: 12 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -9,11 +9,11 @@ package p1 {
99
object TextParser {
1010
given TP as TokenParser[Char, Position[CharSequence]] {}
1111

12-
def f given
13-
TokenParser[Char, Position[CharSequence]] = ???
12+
def f
13+
given TokenParser[Char, Position[CharSequence]] = ???
1414

15-
given FromCharToken as Conversion[Char, Position[CharSequence]] given
16-
(T: TokenParser[Char, Position[CharSequence]]) = ???
15+
given FromCharToken as Conversion[Char, Position[CharSequence]]
16+
given (T: TokenParser[Char, Position[CharSequence]]) = ???
1717
}
1818

1919
object Testcase {
@@ -80,4 +80,12 @@ package p3 {
8080
}
8181
}
8282
}
83+
}
84+
package p4 {
85+
class TC
86+
given A as TC
87+
given B[X[_], Y] as TC
88+
89+
given C as TC
90+
given TC
8391
}

0 commit comments

Comments
 (0)