Skip to content

Commit 6a18b1a

Browse files
committed
Fix #7139: Implement kind-projector compatibility
- Support * placeholder (including in tuple and function types) - Support λ alternative to =>> syntax - Add tests
1 parent 2d2d3c0 commit 6a18b1a

File tree

6 files changed

+120
-5
lines changed

6 files changed

+120
-5
lines changed

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

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -610,6 +610,7 @@ object StdNames {
610610
final val BAR : N = "|"
611611
final val DOLLAR: N = "$"
612612
final val GE: N = ">="
613+
final val LAMBDA: N = "λ"
613614
final val LE: N = "<="
614615
final val MINUS: N = "-"
615616
final val NE: N = "!="

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

Lines changed: 83 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1322,8 +1322,19 @@ object Parsers {
13221322
def functionRest(params: List[Tree]): Tree =
13231323
atSpan(start, accept(ARROW)) {
13241324
val t = typ()
1325+
13251326
if (imods.isOneOf(Given | Erased)) new FunctionWithMods(params, t, imods)
1326-
else Function(params, t)
1327+
else if (ctx.settings.YkindProjector.value) {
1328+
val (newParams :+ newT, tparams) = replaceKindProjectorPlaceholders(params :+ t)
1329+
1330+
if (tparams.isEmpty) {
1331+
Function(params, t)
1332+
} else {
1333+
LambdaTypeTree(tparams, Function(newParams, newT))
1334+
}
1335+
} else {
1336+
Function(params, t)
1337+
}
13271338
}
13281339
def funArgTypesRest(first: Tree, following: () => Tree) = {
13291340
val buf = new ListBuffer[Tree] += first
@@ -1413,6 +1424,23 @@ object Parsers {
14131424
}
14141425
}
14151426

1427+
/** Replaces kind-projector's `*` in a list of types arguments with synthetic names,
1428+
* returning the new argument list and the synthetic type definitions.
1429+
*/
1430+
private def replaceKindProjectorPlaceholders(params: List[Tree]): (List[Tree], List[TypeDef]) = {
1431+
val tparams = new ListBuffer[TypeDef]
1432+
1433+
val newParams = params.map {
1434+
case Ident(tpnme.raw.STAR) =>
1435+
val name = tpnme.syntheticTypeParamName(tparams.length)
1436+
tparams += TypeDef(name, TypeBoundsTree(EmptyTree, EmptyTree))
1437+
Ident(name)
1438+
case other => other
1439+
}
1440+
1441+
(newParams, tparams.toList)
1442+
}
1443+
14161444
private def implicitKwPos(start: Int): Span =
14171445
Span(start, start + nme.IMPLICITkw.asSimpleName.length)
14181446

@@ -1529,7 +1557,6 @@ object Parsers {
15291557
typeBounds().withSpan(Span(start, in.lastOffset, start))
15301558
}
15311559
else if (isIdent(nme.*) && ctx.settings.YkindProjector.value) {
1532-
syntaxError("`*` placeholders are not implemented yet")
15331560
typeIdent()
15341561
}
15351562
else if (isSplice)
@@ -1550,8 +1577,60 @@ object Parsers {
15501577
private def simpleTypeRest(t: Tree): Tree = in.token match {
15511578
case HASH => simpleTypeRest(typeProjection(t))
15521579
case LBRACKET => simpleTypeRest(atSpan(startOffset(t)) {
1553-
AppliedTypeTree(rejectWildcardType(t), typeArgs(namedOK = false, wildOK = true)) })
1554-
case _ => t
1580+
val applied = rejectWildcardType(t)
1581+
val args = typeArgs(namedOK = false, wildOK = true)
1582+
1583+
if (ctx.settings.YkindProjector.value) {
1584+
def fail(): Tree = {
1585+
syntaxError(
1586+
"λ requires a single argument of the form X => ... or (X, Y) => ...",
1587+
Span(t.span.start, in.lastOffset)
1588+
)
1589+
AppliedTypeTree(applied, args)
1590+
}
1591+
1592+
applied match {
1593+
case Ident(tpnme.raw.LAMBDA) =>
1594+
args match {
1595+
case List(Function(params, body)) =>
1596+
val typeDefs = params.collect {
1597+
case Ident(name) => TypeDef(name.toTypeName, TypeBoundsTree(EmptyTree, EmptyTree))
1598+
}
1599+
if (typeDefs.length != params.length) fail()
1600+
else LambdaTypeTree(typeDefs, body)
1601+
case _ =>
1602+
fail()
1603+
}
1604+
case _ =>
1605+
val (newArgs, tparams) = replaceKindProjectorPlaceholders(args)
1606+
1607+
if (tparams.isEmpty) {
1608+
AppliedTypeTree(applied, args)
1609+
} else {
1610+
LambdaTypeTree(tparams, AppliedTypeTree(applied, newArgs))
1611+
}
1612+
}
1613+
1614+
} else {
1615+
AppliedTypeTree(applied, args)
1616+
}
1617+
})
1618+
case _ =>
1619+
if (ctx.settings.YkindProjector.value) {
1620+
t match {
1621+
case Tuple(params) =>
1622+
val (newParams, tparams) = replaceKindProjectorPlaceholders(params)
1623+
1624+
if (tparams.isEmpty) {
1625+
t
1626+
} else {
1627+
LambdaTypeTree(tparams, Tuple(newParams))
1628+
}
1629+
case _ => t
1630+
}
1631+
} else {
1632+
t
1633+
}
15551634
}
15561635

15571636
private def typeProjection(t: Tree): Tree = {

compiler/test/dotty/tools/vulpix/TestConfiguration.scala

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,8 @@ object TestConfiguration {
1616
"-Yno-deep-subtypes",
1717
"-Yno-double-bindings",
1818
"-Yforce-sbt-phases",
19-
"-Xverify-signatures"
19+
"-Xverify-signatures",
20+
"-Ykind-projector"
2021
)
2122

2223
val basicClasspath = mkClasspath(List(

tests/neg/kind-projector.check

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
-- Error: tests/neg/kind-projector.scala:7:23 --------------------------------------------------------------------------
2+
7 |class Bar3 extends Foo[λ[List[x] => Int]] // error
3+
| ^^^^^^^^^^^^^^^^^
4+
| λ requires a single argument of the form X => ... or (X, Y) => ...
5+
-- Error: tests/neg/kind-projector.scala:5:23 --------------------------------------------------------------------------
6+
5 |class Bar1 extends Foo[Either[*, *]] // error
7+
| ^^^^^^^^^^^^
8+
| Type argument Either has not the same kind as its bound <: [_$1] => Any
9+
-- Error: tests/neg/kind-projector.scala:6:22 --------------------------------------------------------------------------
10+
6 |class Bar2 extends Foo[*] // error
11+
| ^
12+
| Type argument X0 has not the same kind as its bound <: [_$1] => Any

tests/neg/kind-projector.scala

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
package kind_projector_neg
2+
3+
trait Foo[F[_]]
4+
5+
class Bar1 extends Foo[Either[*, *]] // error
6+
class Bar2 extends Foo[*] // error
7+
class Bar3 extends Foo[λ[List[x] => Int]] // error

tests/pos/kind-projector.scala

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
package kind_projector
2+
3+
trait Foo[F[_]]
4+
trait Qux[F[_, _]]
5+
trait Baz[F[_], A, B]
6+
7+
class Bar1 extends Foo[Either[Int, *]]
8+
class Bar2 extends Foo[Either[*, Int]]
9+
class Bar3 extends Foo[* => Int]
10+
class Bar4 extends Foo[Int => *]
11+
class Bar5 extends Foo[(Int, *, Int)]
12+
class Bar6 extends Foo[λ[x => Either[Int, x]]]
13+
class Bar7 extends Qux[λ[(x, y) => Either[y, x]]]
14+
class Bar8 extends Foo[Baz[Int => *, *, Int]]
15+
class Bar9 extends Foo[λ[x => Baz[x => *, Int, x]]]

0 commit comments

Comments
 (0)