Skip to content

Commit 4aad689

Browse files
oderskyallanrenucci
authored andcommitted
Fix #5145: Improve treatment of soft modifiers
- Generalize treatment of `inline` to a set of soft modifiers - Make `opaque` a soft modifier - Fix parsing of soft modifiers so that they are treated as modifiers only when preceding a hard modifier or a definition keyword
1 parent 0a1aed2 commit 4aad689

File tree

7 files changed

+64
-31
lines changed

7 files changed

+64
-31
lines changed

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

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -74,7 +74,6 @@ object StdNames {
7474
final val IFkw: N = kw("if")
7575
final val IMPLICITkw: N = kw("implicit")
7676
final val IMPORTkw: N = kw("import")
77-
final val INLINEkw: N = kw("inline")
7877
final val LAZYkw: N = kw("lazy")
7978
final val MACROkw: N = kw("macro")
8079
final val MATCHkw: N = kw("match")
@@ -436,6 +435,7 @@ object StdNames {
436435
val implicitConversions: N = "implicitConversions"
437436
val implicitly: N = "implicitly"
438437
val in: N = "in"
438+
val `inline`: N = "inline"
439439
val info: N = "info"
440440
val inlinedEquals: N = "inlinedEquals"
441441
val internal: N = "internal"
@@ -478,6 +478,7 @@ object StdNames {
478478
val notify_ : N = "notify"
479479
val null_ : N = "null"
480480
val ofDim: N = "ofDim"
481+
val `opaque`: N = "opaque"
481482
val origin: N = "origin"
482483
val prefix : N = "prefix"
483484
val productArity: N = "productArity"

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

Lines changed: 17 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -166,22 +166,24 @@ object Parsers {
166166
def isSimpleLiteral: Boolean = simpleLiteralTokens contains in.token
167167
def isLiteral: Boolean = literalTokens contains in.token
168168
def isNumericLit: Boolean = numericLitTokens contains in.token
169-
def isModifier: Boolean = modifierTokens.contains(in.token) || isIdent(nme.INLINEkw)
170169
def isBindingIntro: Boolean = canStartBindingTokens contains in.token
171170
def isTemplateIntro: Boolean = templateIntroTokens contains in.token
172171
def isDclIntro: Boolean = dclIntroTokens contains in.token
173172
def isStatSeqEnd: Boolean = in.token == RBRACE || in.token == EOF
174173
def mustStartStat: Boolean = mustStartStatTokens contains in.token
175174

175+
/** Is current token a hard or soft modifier (in modifier position or not)? */
176+
def isModifier: Boolean = modifierTokens.contains(in.token) || in.isSoftModifier
177+
176178
def isExprIntro: Boolean =
177-
(canStartExpressionTokens `contains` in.token) &&
178-
(!isIdent(nme.INLINEkw) || lookaheadIn(canStartExpressionTokens))
179+
canStartExpressionTokens.contains(in.token) &&
180+
!in.isSoftModifierInModifierPosition
179181

180182
def isDefIntro(allowedMods: BitSet): Boolean =
181183
in.token == AT ||
182184
(defIntroTokens `contains` in.token) ||
183185
(allowedMods `contains` in.token) ||
184-
isIdent(nme.INLINEkw) && lookaheadIn(BitSet(AT) | defIntroTokens | allowedMods)
186+
in.isSoftModifierInModifierPosition
185187

186188
def isStatSep: Boolean =
187189
in.token == NEWLINE || in.token == NEWLINES || in.token == SEMI
@@ -455,14 +457,6 @@ object Parsers {
455457

456458
def commaSeparated[T](part: () => T): List[T] = tokenSeparated(COMMA, part)
457459

458-
/** Is the token following the current one in `tokens`? */
459-
def lookaheadIn(tokens: BitSet): Boolean = {
460-
val lookahead = in.lookaheadScanner
461-
do lookahead.nextToken()
462-
while (lookahead.token == NEWLINE || lookahead.token == NEWLINES)
463-
tokens.contains(lookahead.token)
464-
}
465-
466460
/* --------- OPERAND/OPERATOR STACK --------------------------------------- */
467461

468462
var opStack: List[OpInfo] = Nil
@@ -841,7 +835,7 @@ object Parsers {
841835

842836
/** Is current ident a `*`, and is it followed by a `)` or `,`? */
843837
def isPostfixStar: Boolean =
844-
in.name == nme.raw.STAR && lookaheadIn(BitSet(RPAREN, COMMA))
838+
in.name == nme.raw.STAR && in.lookaheadIn(BitSet(RPAREN, COMMA))
845839

846840
def infixTypeRest(t: Tree): Tree =
847841
infixOps(t, canStartTypeTokens, refinedType, isType = true, isOperator = !isPostfixStar)
@@ -899,7 +893,7 @@ object Parsers {
899893
val start = in.skipToken()
900894
typeBounds().withPos(Position(start, in.lastOffset, start))
901895
}
902-
else if (isIdent(nme.raw.TILDE) && lookaheadIn(BitSet(IDENTIFIER, BACKQUOTED_IDENT)))
896+
else if (isIdent(nme.raw.TILDE) && in.lookaheadIn(BitSet(IDENTIFIER, BACKQUOTED_IDENT)))
903897
atPos(in.offset) { PrefixOp(typeIdent(), path(thisOK = true)) }
904898
else path(thisOK = false, handleSingletonType) match {
905899
case r @ SingletonTypeTree(_) => r
@@ -1744,8 +1738,11 @@ object Parsers {
17441738
case PRIVATE => Mod.Private()
17451739
case PROTECTED => Mod.Protected()
17461740
case SEALED => Mod.Sealed()
1747-
case OPAQUE => Mod.Opaque()
1748-
case IDENTIFIER if name == nme.INLINEkw => Mod.Inline()
1741+
case IDENTIFIER =>
1742+
name match {
1743+
case nme.`inline` => Mod.Inline()
1744+
case nme.`opaque` => Mod.Opaque()
1745+
}
17491746
}
17501747

17511748
/** Drop `private' modifier when followed by a qualifier.
@@ -1816,7 +1813,8 @@ object Parsers {
18161813
@tailrec
18171814
def loop(mods: Modifiers): Modifiers = {
18181815
if (allowed.contains(in.token) ||
1819-
isIdent(nme.INLINEkw) && localModifierTokens.subsetOf(allowed)) {
1816+
in.isSoftModifier &&
1817+
localModifierTokens.subsetOf(allowed)) { // soft modifiers are admissible everywhere local modifiers are
18201818
val isAccessMod = accessModifierTokens contains in.token
18211819
val mods1 = addModifier(mods)
18221820
loop(if (isAccessMod) accessQualifierOpt(mods1) else mods1)
@@ -1957,7 +1955,7 @@ object Parsers {
19571955
}
19581956
}
19591957
else {
1960-
if (isIdent(nme.INLINEkw)) mods = addModifier(mods)
1958+
if (isIdent(nme.`inline`)) mods = addModifier(mods)
19611959
mods = atPos(start) { mods | Param }
19621960
}
19631961
atPos(start, nameStart) {
@@ -2616,7 +2614,7 @@ object Parsers {
26162614
if (in.token == IMPLICIT || in.token == ERASED) {
26172615
val start = in.offset
26182616
var imods = modifiers(funArgMods)
2619-
if (isBindingIntro && !isIdent(nme.INLINEkw))
2617+
if (isBindingIntro && !isIdent(nme.`inline`))
26202618
stats += implicitClosure(start, Location.InBlock, imods)
26212619
else
26222620
stats +++= localDef(start, imods)

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

Lines changed: 28 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ import util.NameTransformer.avoidIllegalChars
1111
import Tokens._
1212
import scala.annotation.{ switch, tailrec }
1313
import scala.collection.mutable
14-
import scala.collection.immutable.SortedMap
14+
import scala.collection.immutable.{SortedMap, BitSet}
1515
import rewrites.Rewrites.patch
1616

1717
object Scanners {
@@ -301,9 +301,6 @@ object Scanners {
301301
case _ =>
302302
}
303303

304-
/** A new Scanner that starts at the current token offset */
305-
def lookaheadScanner: Scanner = new Scanner(source, offset)
306-
307304
/** Produce next token, filling TokenData fields of Scanner.
308305
*/
309306
def nextToken(): Unit = {
@@ -637,6 +634,28 @@ object Scanners {
637634
else false
638635
}
639636

637+
// Lookahead ---------------------------------------------------------------
638+
639+
/** A new Scanner that starts at the current token offset */
640+
def lookaheadScanner: Scanner = new Scanner(source, offset)
641+
642+
/** Is the token following the current one in `tokens`? */
643+
def lookaheadIn(tokens: BitSet): Boolean = {
644+
val lookahead = lookaheadScanner
645+
do lookahead.nextToken()
646+
while (lookahead.token == NEWLINE || lookahead.token == NEWLINES)
647+
tokens.contains(lookahead.token)
648+
}
649+
650+
/** Is the current token in a position where a modifier is allowed? */
651+
def inModifierPosition(): Boolean = {
652+
val lookahead = lookaheadScanner
653+
do lookahead.nextToken()
654+
while (lookahead.token == NEWLINE || lookahead.token == NEWLINES ||
655+
lookahead.isSoftModifier)
656+
modifierFollowers.contains(lookahead.token)
657+
}
658+
640659
// Identifiers ---------------------------------------------------------------
641660

642661
private def getBackquotedIdent(): Unit = {
@@ -717,6 +736,11 @@ object Scanners {
717736
}
718737
}
719738

739+
def isSoftModifier: Boolean =
740+
token == IDENTIFIER && softModifierNames.contains(name)
741+
742+
def isSoftModifierInModifierPosition: Boolean =
743+
isSoftModifier && inModifierPosition()
720744

721745
// Literals -----------------------------------------------------------------
722746

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

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ package parsing
44

55
import collection.immutable.BitSet
66
import core.Decorators._
7+
import core.StdNames.nme
78

89
abstract class TokensCommon {
910
def maxToken: Int
@@ -93,7 +94,6 @@ abstract class TokensCommon {
9394
//final val FORSOME = 61; enter(FORSOME, "forSome") // TODO: deprecate
9495
//final val ENUM = 62; enter(ENUM, "enum")
9596
//final val ERASED = 63; enter(ERASED, "erased")
96-
//final val OPAQUE = 64; enter(OPAQUE, "opaque")
9797

9898
/** special symbols */
9999
final val COMMA = 70; enter(COMMA, "','")
@@ -178,7 +178,6 @@ object Tokens extends TokensCommon {
178178
final val FORSOME = 61; enter(FORSOME, "forSome") // TODO: deprecate
179179
final val ENUM = 62; enter(ENUM, "enum")
180180
final val ERASED = 63; enter(ERASED, "erased")
181-
final val OPAQUE = 64; enter(OPAQUE, "opaque")
182181

183182
/** special symbols */
184183
final val NEWLINE = 78; enter(NEWLINE, "end of statement", "new line")
@@ -199,7 +198,7 @@ object Tokens extends TokensCommon {
199198
/** XML mode */
200199
final val XMLSTART = 96; enter(XMLSTART, "$XMLSTART$<") // TODO: deprecate
201200

202-
final val alphaKeywords: TokenSet = tokenRange(IF, OPAQUE)
201+
final val alphaKeywords: TokenSet = tokenRange(IF, ERASED)
203202
final val symbolicKeywords: TokenSet = tokenRange(USCORE, VIEWBOUND)
204203
final val symbolicTokens: TokenSet = tokenRange(COMMA, VIEWBOUND)
205204
final val keywords: TokenSet = alphaKeywords | symbolicKeywords
@@ -227,7 +226,7 @@ object Tokens extends TokensCommon {
227226
final val defIntroTokens: TokenSet = templateIntroTokens | dclIntroTokens
228227

229228
final val localModifierTokens: TokenSet = BitSet(
230-
ABSTRACT, FINAL, SEALED, IMPLICIT, LAZY, ERASED, OPAQUE)
229+
ABSTRACT, FINAL, SEALED, IMPLICIT, LAZY, ERASED)
231230

232231
final val accessModifierTokens: TokenSet = BitSet(
233232
PRIVATE, PROTECTED)
@@ -237,6 +236,8 @@ object Tokens extends TokensCommon {
237236

238237
final val modifierTokensOrCase: TokenSet = modifierTokens | BitSet(CASE)
239238

239+
final val modifierFollowers = modifierTokens | defIntroTokens
240+
240241
/** Is token only legal as start of statement (eof also included)? */
241242
final val mustStartStatTokens: TokenSet = defIntroTokens | modifierTokens | BitSet(IMPORT, PACKAGE)
242243

@@ -247,4 +248,6 @@ object Tokens extends TokensCommon {
247248
TYPE, RPAREN, RBRACE, RBRACKET)
248249

249250
final val numericLitTokens: TokenSet = BitSet(INTLIT, LONGLIT, FLOATLIT, DOUBLELIT)
251+
252+
final val softModifierNames = Set(nme.`inline`, nme.`opaque`)
250253
}

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

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -70,8 +70,7 @@ object SyntaxHighlighting {
7070
case _ if alphaKeywords.contains(token) =>
7171
highlightRange(start, end, KeywordColor)
7272

73-
case IDENTIFIER if name == nme.INLINEkw =>
74-
// `inline` is a "soft" keyword
73+
case IDENTIFIER if scanner.isSoftModifierInModifierPosition =>
7574
highlightRange(start, end, KeywordColor)
7675

7776
case IDENTIFIER if name == nme.??? =>

tests/neg/inlinevals.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ object Test {
77
inline val N = 10
88
def X = 20
99

10-
inline inline val twice = 30 // error: repeated modifier // error: not found: inline
10+
inline inline val twice = 30 // error: repeated modifier
1111

1212
class C(inline x: Int, private inline val y: Int) { // error // error
1313
inline val foo: Int // error: abstract member may not be inline

tests/pos/i5145.scala

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
class Test {
2+
def foo(x: Int): Int = {
3+
val inline = 3
4+
def opaque(x: Int): Unit = ()
5+
opaque(3)
6+
inline
7+
}
8+
}

0 commit comments

Comments
 (0)