Skip to content

Commit

Permalink
Better error message for polytypes wrapping capturing types (#21843)
Browse files Browse the repository at this point in the history
A type like

    [X] -> A ->{c} B

is currently not allowed since it expands to a PolyType wrapping a
CapturingType and we have an implementation restriction that requires
PolyTypes to wrap only FunctionTypes. It would be great if we could lift
that implementation restriction. Until we do so, we should have a better
error message, which this commit implements.
odersky authored Oct 27, 2024

Verified

This commit was created on GitHub.com and signed with GitHub’s verified signature.
2 parents 91ef921 + e6b7e3d commit f95d4b2
Showing 3 changed files with 33 additions and 20 deletions.
38 changes: 18 additions & 20 deletions compiler/src/dotty/tools/dotc/parsing/Parsers.scala
Original file line number Diff line number Diff line change
@@ -518,6 +518,22 @@ object Parsers {
tree
}

def makePolyFunction(tparams: List[Tree], body: Tree,
kind: String, errorTree: => Tree,
start: Offset, arrowOffset: Offset): Tree =
atSpan(start, arrowOffset):
getFunction(body) match
case None =>
syntaxError(em"Implementation restriction: polymorphic function ${kind}s must have a value parameter", arrowOffset)
errorTree
case Some(Function(_, _: CapturesAndResult)) =>
// A function tree like this will be desugared
// into a capturing type in the typer.
syntaxError(em"Implementation restriction: polymorphic function types cannot wrap function types that have capture sets", arrowOffset)
errorTree
case Some(f) =>
PolyFunction(tparams, body)

/* --------------- PLACEHOLDERS ------------------------------------------- */

/** The implicit parameters introduced by `_` in the current expression.
@@ -1539,11 +1555,6 @@ object Parsers {
private def getFunction(tree: Tree): Option[Function] = tree match {
case Parens(tree1) => getFunction(tree1)
case Block(Nil, tree1) => getFunction(tree1)
case Function(_, _: CapturesAndResult) =>
// A function tree like this will be desugared
// into a capturing type in the typer,
// so None is returned.
None
case t: Function => Some(t)
case _ => None
}
@@ -1757,13 +1768,7 @@ object Parsers {
else if in.token == ARROW || isPureArrow(nme.PUREARROW) then
val arrowOffset = in.skipToken()
val body = toplevelTyp(nestedIntoOK(in.token))
atSpan(start, arrowOffset):
getFunction(body) match
case Some(f) =>
PolyFunction(tparams, body)
case None =>
syntaxError(em"Implementation restriction: polymorphic function types must have a value parameter", arrowOffset)
Ident(nme.ERROR.toTypeName)
makePolyFunction(tparams, body, "type", Ident(nme.ERROR.toTypeName), start, arrowOffset)
else
accept(TLARROW)
typ()
@@ -2360,14 +2365,7 @@ object Parsers {
val tparams = typeParamClause(ParamOwner.Type)
val arrowOffset = accept(ARROW)
val body = expr(location)
atSpan(start, arrowOffset) {
getFunction(body) match
case Some(f) =>
PolyFunction(tparams, f)
case None =>
syntaxError(em"Implementation restriction: polymorphic function literals must have a value parameter", arrowOffset)
errorTermTree(arrowOffset)
}
makePolyFunction(tparams, body, "literal", errorTermTree(arrowOffset), start, arrowOffset)
case _ =>
val saved = placeholderParams
placeholderParams = Nil
8 changes: 8 additions & 0 deletions tests/neg-custom-args/captures/polyCaptures.check
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
-- Error: tests/neg-custom-args/captures/polyCaptures.scala:4:22 -------------------------------------------------------
4 |val runOpsCheck: [C^] -> (ops: List[() ->{C^} Unit]) ->{C^} Unit = runOps // error
| ^
| Implementation restriction: polymorphic function types cannot wrap function types that have capture sets
-- Error: tests/neg-custom-args/captures/polyCaptures.scala:5:23 -------------------------------------------------------
5 |val runOpsCheck2: [C^] => (ops: List[() ->{C^} Unit]) ->{C^} Unit = runOps // error
| ^
| Implementation restriction: polymorphic function types cannot wrap function types that have capture sets
7 changes: 7 additions & 0 deletions tests/neg-custom-args/captures/polyCaptures.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
class Box[X](val elem: X)

val runOps = [C^] => (b: Box[() ->{C^} Unit]) => b.elem()
val runOpsCheck: [C^] -> (ops: List[() ->{C^} Unit]) ->{C^} Unit = runOps // error
val runOpsCheck2: [C^] => (ops: List[() ->{C^} Unit]) ->{C^} Unit = runOps // error


0 comments on commit f95d4b2

Please sign in to comment.