Skip to content

Commit 7600437

Browse files
committed
Remove syntactic deviation for ascriptions in parentheses
Previously the parser accepted `(x: A => B)` as an expression, but only if there were parentheses around it. This was necessary so that we could parse a potential formal parameter of a closure `(x: A => B) => ...` as an expression and then convert to a type. But it is not backed by the grammar. The grammar requires `x: (A => B)` whether in parentheses or not. We now follow the grammar for source versions from 3.2 on.
1 parent ba8ad47 commit 7600437

File tree

8 files changed

+74
-59
lines changed

8 files changed

+74
-59
lines changed

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

Lines changed: 57 additions & 54 deletions
Original file line numberDiff line numberDiff line change
@@ -1412,48 +1412,14 @@ object Parsers {
14121412
def typ(): Tree = {
14131413
val start = in.offset
14141414
var imods = Modifiers()
1415-
def functionRest(params: List[Tree]): Tree =
1416-
val paramSpan = Span(start, in.lastOffset)
1417-
atSpan(start, in.offset) {
1418-
if in.token == TLARROW then
1419-
if !imods.flags.isEmpty || params.isEmpty then
1420-
syntaxError(em"illegal parameter list for type lambda", start)
1421-
in.token = ARROW
1422-
else
1423-
for case ValDef(_, tpt: ByNameTypeTree, _) <- params do
1424-
syntaxError(em"parameter of type lambda may not be call-by-name", tpt.span)
1425-
in.nextToken()
1426-
return TermLambdaTypeTree(params.asInstanceOf[List[ValDef]], typ())
1427-
1428-
if in.token == CTXARROW then
1429-
in.nextToken()
1430-
imods |= Given
1431-
else
1432-
accept(ARROW)
1433-
val t = typ()
1434-
1435-
if imods.isOneOf(Given | Erased) then
1436-
if imods.is(Given) && params.isEmpty then
1437-
syntaxError("context function types require at least one parameter", paramSpan)
1438-
new FunctionWithMods(params, t, imods)
1439-
else if !ctx.settings.YkindProjector.isDefault then
1440-
val (newParams :+ newT, tparams) = replaceKindProjectorPlaceholders(params :+ t): @unchecked
1441-
1442-
lambdaAbstract(tparams, Function(newParams, newT))
1443-
else
1444-
Function(params, t)
1445-
}
1446-
14471415
var isValParamList = false
1448-
14491416
val t =
1450-
if (in.token == LPAREN) {
1417+
if in.token == LPAREN then
14511418
in.nextToken()
1452-
if (in.token == RPAREN) {
1419+
if in.token == RPAREN then
14531420
in.nextToken()
1454-
functionRest(Nil)
1455-
}
1456-
else {
1421+
functionTypeRest(Nil, start, Modifiers())
1422+
else
14571423
if isErased then imods = addModifier(imods)
14581424
val paramStart = in.offset
14591425
val ts = in.currentRegion.withCommasExpected {
@@ -1468,8 +1434,8 @@ object Parsers {
14681434
}
14691435
accept(RPAREN)
14701436
if isValParamList || in.isArrow then
1471-
functionRest(ts)
1472-
else {
1437+
functionTypeRest(ts, start, imods)
1438+
else
14731439
val ts1 =
14741440
for (t <- ts) yield
14751441
t match {
@@ -1485,33 +1451,29 @@ object Parsers {
14851451
withTypeRest(
14861452
annotTypeRest(
14871453
simpleTypeRest(tuple)))))
1488-
}
1489-
}
1490-
}
1491-
else if (in.token == LBRACKET) {
1454+
else if in.token == LBRACKET then
14921455
val start = in.offset
14931456
val tparams = typeParamClause(ParamOwner.TypeParam)
1494-
if (in.token == TLARROW)
1457+
if in.token == TLARROW then
14951458
atSpan(start, in.skipToken())(LambdaTypeTree(tparams, toplevelTyp()))
1496-
else if (in.token == ARROW) {
1459+
else if in.token == ARROW then
14971460
val arrowOffset = in.skipToken()
14981461
val body = toplevelTyp()
14991462
atSpan(start, arrowOffset) {
15001463
if (isFunction(body))
15011464
PolyFunction(tparams, body)
1502-
else {
1465+
else
15031466
syntaxError("Implementation restriction: polymorphic function types must have a value parameter", arrowOffset)
15041467
Ident(nme.ERROR.toTypeName)
1505-
}
15061468
}
1507-
}
1508-
else { accept(TLARROW); typ() }
1509-
}
1510-
else if (in.token == INDENT) enclosed(INDENT, typ())
1469+
else
1470+
accept(TLARROW)
1471+
typ()
1472+
else if in.token == INDENT then enclosed(INDENT, typ())
15111473
else infixType()
15121474

15131475
in.token match {
1514-
case ARROW | CTXARROW => functionRest(t :: Nil)
1476+
case ARROW | CTXARROW => functionTypeRest(t :: Nil, start, imods)
15151477
case MATCH => matchType(t)
15161478
case FORSOME => syntaxError(ExistentialTypesNoLongerSupported()); t
15171479
case _ =>
@@ -1521,6 +1483,39 @@ object Parsers {
15211483
}
15221484
}
15231485

1486+
def functionTypeRest(params: List[Tree], start: Offset, mods: Modifiers): Tree =
1487+
var imods = mods
1488+
val paramSpan = Span(start, in.lastOffset)
1489+
atSpan(start, in.offset) {
1490+
if in.token == TLARROW then
1491+
if !imods.flags.isEmpty || params.isEmpty then
1492+
syntaxError(em"illegal parameter list for type lambda", start)
1493+
in.token = ARROW
1494+
else
1495+
for case ValDef(_, tpt: ByNameTypeTree, _) <- params do
1496+
syntaxError(em"parameter of type lambda may not be call-by-name", tpt.span)
1497+
in.nextToken()
1498+
return TermLambdaTypeTree(params.asInstanceOf[List[ValDef]], typ())
1499+
1500+
if in.token == CTXARROW then
1501+
in.nextToken()
1502+
imods |= Given
1503+
else
1504+
accept(ARROW)
1505+
val t = typ()
1506+
1507+
if imods.isOneOf(Given | Erased) then
1508+
if imods.is(Given) && params.isEmpty then
1509+
syntaxError("context function types require at least one parameter", paramSpan)
1510+
new FunctionWithMods(params, t, imods)
1511+
else if !ctx.settings.YkindProjector.isDefault then
1512+
val (newParams :+ newT, tparams) = replaceKindProjectorPlaceholders(params :+ t): @unchecked
1513+
lambdaAbstract(tparams, Function(newParams, newT))
1514+
else
1515+
Function(params, t)
1516+
}
1517+
end functionTypeRest
1518+
15241519
private def makeKindProjectorTypeDef(name: TypeName): TypeDef = {
15251520
val isVarianceAnnotated = name.startsWith("+") || name.startsWith("-")
15261521
// We remove the variance marker from the name without passing along the specified variance at all
@@ -1884,7 +1879,15 @@ object Parsers {
18841879
else TypeTree().withSpan(Span(in.lastOffset))
18851880

18861881
def typeDependingOn(location: Location): Tree =
1887-
if location.inParens then typ()
1882+
if location.inParens then
1883+
if sourceVersion.isAtLeast(`3.2`) then
1884+
val start = in.offset
1885+
var t = infixType()
1886+
if in.isArrow then
1887+
t = functionTypeRest(t :: Nil, start, Modifiers())
1888+
report.error(em"function type in type ascription must be enclosed in parentheses", t.srcPos)
1889+
t
1890+
else typ()
18881891
else if location.inPattern then rejectWildcardType(refinedType())
18891892
else infixType()
18901893

tests/neg/fun-ascription.check

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
-- Error: tests/neg/fun-ascription.scala:2:17 --------------------------------------------------------------------------
2+
2 |val x1 = (f: Int => Int) // error
3+
| ^^^^^^^^^^
4+
| function type in type ascription must be enclosed in parentheses
5+
-- Error: tests/neg/fun-ascription.scala:3:16 --------------------------------------------------------------------------
6+
3 |val x2 = f: Int => Int // error
7+
| ^
8+
| parentheses are required around the parameter of a lambda
9+
| This construct can be rewritten automatically under -rewrite -source 3.0-migration.

tests/neg/fun-ascription.scala

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
def f[T](x: T): T = x
2+
val x1 = (f: Int => Int) // error
3+
val x2 = f: Int => Int // error

tests/pos/i7851.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ object WrapperTest {
3030
val test2: (Wrapped[Float], Wrapped[Float], Wrapped[Float]) => Wrapped[Float] = { (x, y, z) => x }
3131

3232
def main(args: Array[String]): Unit = {
33-
wrappedFunction(test1: (Wrapped[Float], Wrapped[Float], Wrapped[Float]) => Wrapped[Float])(5f, 11f, 3f)
33+
wrappedFunction(test1: ((Wrapped[Float], Wrapped[Float], Wrapped[Float]) => Wrapped[Float]))(5f, 11f, 3f)
3434
wrappedFunction(test1)(5f, 11f, 3f)
3535
wrappedFunction(test2)(5f, 11f, 3f)
3636
}

tests/pos/t4176b.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
object Test {
22
def foo(a1: String*) = a1
33
// val fooEta = foo _
4-
(foo: Seq[String] => Seq[String])
4+
foo: (Seq[String] => Seq[String])
55
}

tests/pos/t8120.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,5 +5,5 @@ object A {
55
implicit class RichAny(a: Any) {
66
def m(a: Any): Int = 0
77
}
8-
(new C).m({ case (x, y) => x } : Any => Any)
8+
(new C).m({ case (x, y) => x } : (Any => Any))
99
}

tests/run-macros/quote-matching-optimize-4/Macro_1.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ object Macro {
2222
case '{ ($ls: List[T]).filter($f).filter($g) } =>
2323
optimize('{ $ls.filter(x => $f(x) && $g(x)) })
2424

25-
case '{ type u; type v; ($ls: List[`u`]).map($f: `u` => `v`).map($g: `v` => T) } =>
25+
case '{ type u; type v; ($ls: List[`u`]).map($f: (`u` => `v`)).map($g: (`v` => T)) } =>
2626
optimize('{ $ls.map(x => $g($f(x))) })
2727

2828
case _ => x

tests/run/lambda-unit.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ object Test {
1313

1414
def main(args: Array[String]): Unit = {
1515
fun("")
16-
(fun: Object => Any)("")
16+
(fun: (Object => Any))("")
1717
sam.foo("")
1818
genericSam.foo("")
1919
}

0 commit comments

Comments
 (0)