Skip to content

Use predictions instead of post-hoc conversions when dealing with : and => #15251

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 2 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
111 changes: 86 additions & 25 deletions compiler/src/dotty/tools/dotc/parsing/Parsers.scala
Original file line number Diff line number Diff line change
Expand Up @@ -444,9 +444,12 @@ object Parsers {

/** Convert tree to formal parameter
*/
def convertToParam(tree: Tree, mods: Modifiers, expected: String = "formal parameter"): ValDef = tree match {
def convertToParam(tree: Tree, mods: Modifiers, expected: String = "formal parameter"): ValDef = tree match
case param: ValDef =>
param.withMods(param.mods | mods.flags)
case id @ Ident(name) =>
makeParameter(name.asTermName, TypeTree(), mods, isBackquoted = isBackquoted(id)).withSpan(tree.span)
// the following three cases are needed only for 2.x parameters without enclosing parentheses
case Typed(_, tpt: TypeBoundsTree) =>
syntaxError(s"not a legal $expected", tree.span)
makeParameter(nme.ERROR, tree, mods)
Expand All @@ -457,7 +460,6 @@ object Parsers {
case _ =>
syntaxError(s"not a legal $expected", tree.span)
makeParameter(nme.ERROR, tree, mods)
}

/** Convert (qual)ident to type identifier
*/
Expand Down Expand Up @@ -891,6 +893,16 @@ object Parsers {
val next = in.lookahead.token
next == LBRACKET || next == LPAREN


def followingIsSelfType() =
val lookahead = in.LookaheadScanner()
lookahead.nextToken()
lookahead.token == COLON
&& {
lookahead.nextToken()
canStartTypeTokens(lookahead.token)
}

/** Is current ident a `*`, and is it followed by a `)`, `, )`, `,EOF`? The latter two are not
syntactically valid, but we need to include them here for error recovery. */
def followingIsVararg(): Boolean =
Expand All @@ -905,6 +917,21 @@ object Parsers {
}
}

/** When encountering a `:`, is that in the first binding of a lambda?
* @pre location of the enclosing expression is `InParens`, so there is am open `(`.
*/
def followingisLambdaParams() =
val lookahead = in.LookaheadScanner()
lookahead.nextToken()
while lookahead.token != RPAREN && lookahead.token != EOF do
if lookahead.token == LPAREN then lookahead.skipParens()
else lookahead.nextToken()
lookahead.token == RPAREN
&& {
lookahead.nextToken()
lookahead.isArrow
}

/* --------- OPERAND/OPERATOR STACK --------------------------------------- */

var opStack: List[OpInfo] = Nil
Expand Down Expand Up @@ -2273,7 +2300,7 @@ object Parsers {
placeholderParams = param :: placeholderParams
atSpan(start) { Ident(pname) }
case LPAREN =>
atSpan(in.offset) { makeTupleOrParens(inParens(exprsInParensOpt())) }
atSpan(in.offset) { makeTupleOrParens(inParens(exprsInParensOrBindings())) }
case LBRACE | INDENT =>
canApply = false
blockExpr()
Expand Down Expand Up @@ -2343,7 +2370,17 @@ object Parsers {
val app = applyToClosure(t, in.offset, convertToParams(termIdent()))
simpleExprRest(app, location, canApply = true)
case _ =>
t
t match
case id @ Ident(name)
if in.isColon() && location == Location.InParens && followingisLambdaParams() =>
if name.is(WildcardParamName) then
assert(name == placeholderParams.head.name)
placeholderParams = placeholderParams.tail
atSpan(startOffset(id)) {
makeParameter(name.asTermName, typedOpt(), Modifiers(), isBackquoted = isBackquoted(id))
}
case _ =>
t
}
}

Expand Down Expand Up @@ -2377,9 +2414,20 @@ object Parsers {
}

/** ExprsInParens ::= ExprInParens {`,' ExprInParens}
* Bindings ::= Binding {`,' Binding}
*/
def exprsInParensOpt(): List[Tree] =
if (in.token == RPAREN) Nil else commaSeparated(exprInParens)
def exprsInParensOrBindings(): List[Tree] =
if in.token == RPAREN then Nil
else in.currentRegion.withCommasExpected {
var isFormalParams = false
def exprOrBinding() =
if isFormalParams then binding(Modifiers())
else
val t = exprInParens()
if t.isInstanceOf[ValDef] then isFormalParams = true
t
commaSeparatedRest(exprOrBinding(), exprOrBinding)
}

/** ParArgumentExprs ::= `(' [‘using’] [ExprsInParens] `)'
* | `(' [ExprsInParens `,'] PostfixExpr `*' ')'
Expand Down Expand Up @@ -3892,7 +3940,37 @@ object Parsers {
stats.toList
}

/** TemplateStatSeq ::= [id [`:' Type] `=>'] TemplateStat {semi TemplateStat}
/** SelfType ::= id [‘:’ InfixType] ‘=>’
* | ‘this’ ‘:’ InfixType ‘=>’
*/
def selfType(): ValDef =
if (in.isIdent || in.token == THIS)
&& (in.lookahead.token == COLON && followingIsSelfType()
|| in.lookahead.token == ARROW)
then
atSpan(in.offset) {
val selfName =
if in.token == THIS then
in.nextToken()
nme.WILDCARD
else ident()
val selfTpt =
if in.token == COLON then
in.nextToken()
infixType()
else
if selfName == nme.WILDCARD then accept(COLON)
TypeTree()
if in.token == ARROW then
in.token = SELFARROW // suppresses INDENT insertion after `=>`
in.nextToken()
else
syntaxError("`=>` expected after self type")
makeSelfDef(selfName, selfTpt)
}
else EmptyValDef

/** TemplateStatSeq ::= [SelfType] TemplateStat {semi TemplateStat}
* TemplateStat ::= Import
* | Export
* | Annotations Modifiers Def
Expand All @@ -3904,25 +3982,8 @@ object Parsers {
* | Annotations Modifiers EnumCase
*/
def templateStatSeq(): (ValDef, List[Tree]) = checkNoEscapingPlaceholders {
var self: ValDef = EmptyValDef
val stats = new ListBuffer[Tree]
if isExprIntro && !isDefIntro(modifierTokens) then
val first = expr1()
if in.token == ARROW then
first match {
case Typed(tree @ This(EmptyTypeIdent), tpt) =>
self = makeSelfDef(nme.WILDCARD, tpt).withSpan(first.span)
case _ =>
val ValDef(name, tpt, _) = convertToParam(first, EmptyModifiers, "self type clause")
if (name != nme.ERROR)
self = makeSelfDef(name, tpt).withSpan(first.span)
}
in.token = SELFARROW // suppresses INDENT insertion after `=>`
in.nextToken()
else
stats += first
statSepOrEnd(stats)
end if
val self = selfType()
while
var empty = false
if (in.token == IMPORT)
Expand Down
2 changes: 1 addition & 1 deletion compiler/src/dotty/tools/dotc/parsing/Tokens.scala
Original file line number Diff line number Diff line change
Expand Up @@ -233,7 +233,7 @@ object Tokens extends TokensCommon {
final val canStartExprTokens2: TokenSet = canStartExprTokens3 | BitSet(DO)

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

final val templateIntroTokens: TokenSet = BitSet(CLASS, TRAIT, OBJECT, ENUM, CASECLASS, CASEOBJECT)

Expand Down
2 changes: 1 addition & 1 deletion tests/neg/cycles.scala
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,6 @@ class T2 {
type U = X | Int
}
object T12 {
??? : (T1 {})#U // old-error: conflicting bounds
val _ : (T1 {})#U = ??? // old-error: conflicting bounds
??? : (T2 {})#U // old-error: conflicting bounds
}
12 changes: 4 additions & 8 deletions tests/neg/i13769.check
Original file line number Diff line number Diff line change
@@ -1,10 +1,6 @@
-- Error: tests/neg/i13769.scala:2:18 ----------------------------------------------------------------------------------
2 |val te = tup.map((x: _ <: Int) => List(x)) // error // error
| ^^^^^^^^^^^
| not a legal formal parameter
-- [E006] Not Found Error: tests/neg/i13769.scala:2:39 -----------------------------------------------------------------
2 |val te = tup.map((x: _ <: Int) => List(x)) // error // error
| ^
| Not found: x
-- [E035] Syntax Error: tests/neg/i13769.scala:2:21 --------------------------------------------------------------------
2 |val te = tup.map((x: _ <: Int) => List(x)) // error
| ^^^^^^^^
| Unbound wildcard type
|
| longer explanation available when compiling with `-explain`
2 changes: 1 addition & 1 deletion tests/neg/i13769.scala
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
val tup = (1, "s")
val te = tup.map((x: _ <: Int) => List(x)) // error // error
val te = tup.map((x: _ <: Int) => List(x)) // error
2 changes: 1 addition & 1 deletion tests/neg/i1424.scala
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
class Test {
(x: Int) => x // error: not a legal self type clause // error: not found x
(x: Int) => x // error: not a legal self type clause
}
2 changes: 1 addition & 1 deletion tests/neg/i4373b.scala
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
// ==> 05bef7805687ba94da37177f7568e3ba7da1f91c.scala <==
class x0 {
x1: // error
x1:
x0 | _ // error
// error
2 changes: 1 addition & 1 deletion tests/neg/i7818.scala
Original file line number Diff line number Diff line change
@@ -1 +1 @@
def foo = (x: @) => () // error // error
def foo = (x: @) => () // error
1 change: 0 additions & 1 deletion tests/neg/parser-stability-16.scala
Original file line number Diff line number Diff line change
Expand Up @@ -3,4 +3,3 @@ class x0[x0] {
}
trait x3 extends x0 { // error
x1 = 0 object // error // error
// error
2 changes: 1 addition & 1 deletion tests/neg/parser-stability-5.scala
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
trait x0 {
x1 : { // error
x1 : {
var x2
// error