Skip to content

Commit 12938d6

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 f35df41 commit 12938d6

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
@@ -1388,48 +1388,14 @@ object Parsers {
13881388
def typ(): Tree = {
13891389
val start = in.offset
13901390
var imods = Modifiers()
1391-
def functionRest(params: List[Tree]): Tree =
1392-
val paramSpan = Span(start, in.lastOffset)
1393-
atSpan(start, in.offset) {
1394-
if in.token == TLARROW then
1395-
if !imods.flags.isEmpty || params.isEmpty then
1396-
syntaxError(em"illegal parameter list for type lambda", start)
1397-
in.token = ARROW
1398-
else
1399-
for case ValDef(_, tpt: ByNameTypeTree, _) <- params do
1400-
syntaxError(em"parameter of type lambda may not be call-by-name", tpt.span)
1401-
in.nextToken()
1402-
return TermLambdaTypeTree(params.asInstanceOf[List[ValDef]], typ())
1403-
1404-
if in.token == CTXARROW then
1405-
in.nextToken()
1406-
imods |= Given
1407-
else
1408-
accept(ARROW)
1409-
val t = typ()
1410-
1411-
if imods.isOneOf(Given | Erased) then
1412-
if imods.is(Given) && params.isEmpty then
1413-
syntaxError("context function types require at least one parameter", paramSpan)
1414-
new FunctionWithMods(params, t, imods)
1415-
else if !ctx.settings.YkindProjector.isDefault then
1416-
val (newParams :+ newT, tparams) = replaceKindProjectorPlaceholders(params :+ t): @unchecked
1417-
1418-
lambdaAbstract(tparams, Function(newParams, newT))
1419-
else
1420-
Function(params, t)
1421-
}
1422-
14231391
var isValParamList = false
1424-
14251392
val t =
1426-
if (in.token == LPAREN) {
1393+
if in.token == LPAREN then
14271394
in.nextToken()
1428-
if (in.token == RPAREN) {
1395+
if in.token == RPAREN then
14291396
in.nextToken()
1430-
functionRest(Nil)
1431-
}
1432-
else {
1397+
functionTypeRest(Nil, start, Modifiers())
1398+
else
14331399
if isErased then imods = addModifier(imods)
14341400
val paramStart = in.offset
14351401
val ts = in.currentRegion.withCommasExpected {
@@ -1444,8 +1410,8 @@ object Parsers {
14441410
}
14451411
accept(RPAREN)
14461412
if isValParamList || in.isArrow then
1447-
functionRest(ts)
1448-
else {
1413+
functionTypeRest(ts, start, imods)
1414+
else
14491415
val ts1 =
14501416
for (t <- ts) yield
14511417
t match {
@@ -1461,33 +1427,29 @@ object Parsers {
14611427
withTypeRest(
14621428
annotTypeRest(
14631429
simpleTypeRest(tuple)))))
1464-
}
1465-
}
1466-
}
1467-
else if (in.token == LBRACKET) {
1430+
else if in.token == LBRACKET then
14681431
val start = in.offset
14691432
val tparams = typeParamClause(ParamOwner.TypeParam)
1470-
if (in.token == TLARROW)
1433+
if in.token == TLARROW then
14711434
atSpan(start, in.skipToken())(LambdaTypeTree(tparams, toplevelTyp()))
1472-
else if (in.token == ARROW) {
1435+
else if in.token == ARROW then
14731436
val arrowOffset = in.skipToken()
14741437
val body = toplevelTyp()
14751438
atSpan(start, arrowOffset) {
14761439
if (isFunction(body))
14771440
PolyFunction(tparams, body)
1478-
else {
1441+
else
14791442
syntaxError("Implementation restriction: polymorphic function types must have a value parameter", arrowOffset)
14801443
Ident(nme.ERROR.toTypeName)
1481-
}
14821444
}
1483-
}
1484-
else { accept(TLARROW); typ() }
1485-
}
1486-
else if (in.token == INDENT) enclosed(INDENT, typ())
1445+
else
1446+
accept(TLARROW)
1447+
typ()
1448+
else if in.token == INDENT then enclosed(INDENT, typ())
14871449
else infixType()
14881450

14891451
in.token match {
1490-
case ARROW | CTXARROW => functionRest(t :: Nil)
1452+
case ARROW | CTXARROW => functionTypeRest(t :: Nil, start, imods)
14911453
case MATCH => matchType(t)
14921454
case FORSOME => syntaxError(ExistentialTypesNoLongerSupported()); t
14931455
case _ =>
@@ -1497,6 +1459,39 @@ object Parsers {
14971459
}
14981460
}
14991461

1462+
def functionTypeRest(params: List[Tree], start: Offset, mods: Modifiers): Tree =
1463+
var imods = mods
1464+
val paramSpan = Span(start, in.lastOffset)
1465+
atSpan(start, in.offset) {
1466+
if in.token == TLARROW then
1467+
if !imods.flags.isEmpty || params.isEmpty then
1468+
syntaxError(em"illegal parameter list for type lambda", start)
1469+
in.token = ARROW
1470+
else
1471+
for case ValDef(_, tpt: ByNameTypeTree, _) <- params do
1472+
syntaxError(em"parameter of type lambda may not be call-by-name", tpt.span)
1473+
in.nextToken()
1474+
return TermLambdaTypeTree(params.asInstanceOf[List[ValDef]], typ())
1475+
1476+
if in.token == CTXARROW then
1477+
in.nextToken()
1478+
imods |= Given
1479+
else
1480+
accept(ARROW)
1481+
val t = typ()
1482+
1483+
if imods.isOneOf(Given | Erased) then
1484+
if imods.is(Given) && params.isEmpty then
1485+
syntaxError("context function types require at least one parameter", paramSpan)
1486+
new FunctionWithMods(params, t, imods)
1487+
else if !ctx.settings.YkindProjector.isDefault then
1488+
val (newParams :+ newT, tparams) = replaceKindProjectorPlaceholders(params :+ t): @unchecked
1489+
lambdaAbstract(tparams, Function(newParams, newT))
1490+
else
1491+
Function(params, t)
1492+
}
1493+
end functionTypeRest
1494+
15001495
private def makeKindProjectorTypeDef(name: TypeName): TypeDef = {
15011496
val isVarianceAnnotated = name.startsWith("+") || name.startsWith("-")
15021497
// We remove the variance marker from the name without passing along the specified variance at all
@@ -1860,7 +1855,15 @@ object Parsers {
18601855
else TypeTree().withSpan(Span(in.lastOffset))
18611856

18621857
def typeDependingOn(location: Location): Tree =
1863-
if location.inParens then typ()
1858+
if location.inParens then
1859+
if sourceVersion.isAtLeast(`3.2`) then
1860+
val start = in.offset
1861+
var t = infixType()
1862+
if in.isArrow then
1863+
t = functionTypeRest(t :: Nil, start, Modifiers())
1864+
report.error(em"function type in type ascription must be enclosed in parentheses", t.srcPos)
1865+
t
1866+
else typ()
18641867
else if location.inPattern then rejectWildcardType(refinedType())
18651868
else infixType()
18661869

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)