Skip to content

Commit 2dddaf2

Browse files
committed
Disallow use of PolyFunction in user code
The `PolyFunction` trait should only be used for compiler generated encoded lambdas. Any other use case that was allowed before was accidental. In the future, we might consider supporting these if there is a good use case. This would probably require a SIP.
1 parent 1b4b390 commit 2dddaf2

File tree

10 files changed

+41
-71
lines changed

10 files changed

+41
-71
lines changed

Diff for: compiler/src/dotty/tools/dotc/transform/PostTyper.scala

+2-6
Original file line numberDiff line numberDiff line change
@@ -287,6 +287,7 @@ class PostTyper extends MacroTransform with InfoTransformer { thisPhase =>
287287
super.transform(tree)(using gadtCtx)
288288
case tree: Ident =>
289289
if tree.isType then
290+
Checking.checkPolyFunctionType(tree)
290291
checkNotPackage(tree)
291292
else
292293
checkNoConstructorProxy(tree)
@@ -299,6 +300,7 @@ class PostTyper extends MacroTransform with InfoTransformer { thisPhase =>
299300
registerNeedsInlining(tree)
300301
if name.isTypeName then
301302
Checking.checkRealizable(qual.tpe, qual.srcPos)
303+
Checking.checkPolyFunctionType(tree)
302304
withMode(Mode.Type)(super.transform(checkNotPackage(tree)))
303305
else
304306
checkNoConstructorProxy(tree)
@@ -370,7 +372,6 @@ class PostTyper extends MacroTransform with InfoTransformer { thisPhase =>
370372
val callTrace = ref(call.symbol)(using ctx.withSource(pos.source)).withSpan(pos.span)
371373
cpy.Inlined(tree)(callTrace, transformSub(bindings), transform(expansion)(using inlineContext(tree)))
372374
case templ: Template =>
373-
Checking.checkPolyFunctionExtension(templ)
374375
withNoCheckNews(templ.parents.flatMap(newPart)) {
375376
forwardParamAccessors(templ)
376377
synthMbr.addSyntheticMembers(
@@ -382,15 +383,13 @@ class PostTyper extends MacroTransform with InfoTransformer { thisPhase =>
382383
case tree: ValDef =>
383384
registerIfHasMacroAnnotations(tree)
384385
checkErasedDef(tree)
385-
Checking.checkPolyFunctionType(tree.tpt)
386386
val tree1 = cpy.ValDef(tree)(rhs = normalizeErasedRhs(tree.rhs, tree.symbol))
387387
if tree1.removeAttachment(desugar.UntupledParam).isDefined then
388388
checkStableSelection(tree.rhs)
389389
processValOrDefDef(super.transform(tree1))
390390
case tree: DefDef =>
391391
registerIfHasMacroAnnotations(tree)
392392
checkErasedDef(tree)
393-
Checking.checkPolyFunctionType(tree.tpt)
394393
annotateContextResults(tree)
395394
val tree1 = cpy.DefDef(tree)(rhs = normalizeErasedRhs(tree.rhs, tree.symbol))
396395
processValOrDefDef(superAcc.wrapDefDef(tree1)(super.transform(tree1).asInstanceOf[DefDef]))
@@ -495,9 +494,6 @@ class PostTyper extends MacroTransform with InfoTransformer { thisPhase =>
495494
)
496495
case Block(_, Closure(_, _, tpt)) if ExpandSAMs.needsWrapperClass(tpt.tpe) =>
497496
superAcc.withInvalidCurrentClass(super.transform(tree))
498-
case tree: RefinedTypeTree =>
499-
Checking.checkPolyFunctionType(tree)
500-
super.transform(tree)
501497
case _: Quote | _: QuotePattern =>
502498
ctx.compilationUnit.needsStaging = true
503499
super.transform(tree)

Diff for: compiler/src/dotty/tools/dotc/typer/Checking.scala

+4-31
Original file line numberDiff line numberDiff line change
@@ -825,38 +825,11 @@ object Checking {
825825
case _ =>
826826
end checkExperimentalImports
827827

828-
/** Checks that PolyFunction only have valid refinements.
829-
*
830-
* It only supports `apply` methods with one parameter list and optional type arguments.
831-
*/
832-
def checkPolyFunctionType(tree: Tree)(using Context): Unit = new TreeTraverser {
833-
def traverse(tree: Tree)(using Context): Unit = tree match
834-
case tree: RefinedTypeTree if tree.tpe.derivesFrom(defn.PolyFunctionClass) =>
835-
if tree.refinements.isEmpty then
836-
reportNoRefinements(tree.srcPos)
837-
tree.refinements.foreach {
838-
case refinement: DefDef if refinement.name != nme.apply =>
839-
report.error("PolyFunction only supports apply method refinements", refinement.srcPos)
840-
case refinement: DefDef if !defn.PolyFunctionOf.isValidPolyFunctionInfo(refinement.tpe.widen) =>
841-
report.error("Implementation restriction: PolyFunction apply must have exactly one parameter list and optionally type arguments. No by-name nor varags are allowed.", refinement.srcPos)
842-
case _ =>
843-
}
844-
case _: RefTree if tree.symbol == defn.PolyFunctionClass =>
845-
reportNoRefinements(tree.srcPos)
846-
case _ =>
847-
traverseChildren(tree)
848-
849-
def reportNoRefinements(pos: SrcPos) =
850-
report.error("PolyFunction subtypes must refine the apply method", pos)
851-
}.traverse(tree)
828+
/** Checks that PolyFunction is not used directly */
829+
def checkPolyFunctionType(tree: RefTree)(using Context): Unit =
830+
if tree.symbol == defn.PolyFunctionClass then
831+
report.error(s"Direct use of `PolyFunction` is not allowed. Use lambda instead.", tree.srcPos)
852832

853-
/** Check that users do not extend the `PolyFunction` trait.
854-
* We only allow compiler generated `PolyFunction`s.
855-
*/
856-
def checkPolyFunctionExtension(templ: Template)(using Context): Unit =
857-
templ.parents.find(_.tpe.derivesFrom(defn.PolyFunctionClass)) match
858-
case Some(parent) => report.error(s"`PolyFunction` marker trait is reserved for compiler generated refinements", parent.srcPos)
859-
case None =>
860833
}
861834

862835
trait Checking {

Diff for: tests/neg/i10075.check

+10-10
Original file line numberDiff line numberDiff line change
@@ -1,32 +1,32 @@
11
-- Error: tests/neg/i10075.scala:8:24 ----------------------------------------------------------------------------------
22
8 |trait PolyTrait extends PolyFunction // error
33
| ^^^^^^^^^^^^
4-
| `PolyFunction` marker trait is reserved for compiler generated refinements
4+
| Direct use of `PolyFunction` is not allowed. Use lambda instead.
55
-- Error: tests/neg/i10075.scala:10:24 ---------------------------------------------------------------------------------
6-
10 |class PolyClass extends PolyTrait { // error
7-
| ^^^^^^^^^
8-
| `PolyFunction` marker trait is reserved for compiler generated refinements
6+
10 |class PolyClass extends PolyFunction { // error
7+
| ^^^^^^^^^^^^
8+
| Direct use of `PolyFunction` is not allowed. Use lambda instead.
99
-- Error: tests/neg/i10075.scala:14:26 ---------------------------------------------------------------------------------
1010
14 |object PolyObject extends PolyFunction // error
1111
| ^^^^^^^^^^^^
12-
| `PolyFunction` marker trait is reserved for compiler generated refinements
12+
| Direct use of `PolyFunction` is not allowed. Use lambda instead.
1313
-- Error: tests/neg/i10075.scala:2:14 ----------------------------------------------------------------------------------
1414
2 |val foo = new PolyFunction { } // error
1515
| ^^^^^^^^^^^^
16-
| `PolyFunction` marker trait is reserved for compiler generated refinements
16+
| Direct use of `PolyFunction` is not allowed. Use lambda instead.
1717
-- Error: tests/neg/i10075.scala:3:14 ----------------------------------------------------------------------------------
1818
3 |val bar = new PolyFunction { def bar = 23 } // error
1919
| ^^^^^^^^^^^^
20-
| `PolyFunction` marker trait is reserved for compiler generated refinements
20+
| Direct use of `PolyFunction` is not allowed. Use lambda instead.
2121
-- Error: tests/neg/i10075.scala:4:14 ----------------------------------------------------------------------------------
2222
4 |val baz = new PolyFunction { def apply = 23 } // error
2323
| ^^^^^^^^^^^^
24-
| `PolyFunction` marker trait is reserved for compiler generated refinements
24+
| Direct use of `PolyFunction` is not allowed. Use lambda instead.
2525
-- Error: tests/neg/i10075.scala:5:14 ----------------------------------------------------------------------------------
2626
5 |val qux = new PolyFunction { def apply[T] = 47 } // error
2727
| ^^^^^^^^^^^^
28-
| `PolyFunction` marker trait is reserved for compiler generated refinements
28+
| Direct use of `PolyFunction` is not allowed. Use lambda instead.
2929
-- Error: tests/neg/i10075.scala:6:15 ----------------------------------------------------------------------------------
3030
6 |val quxx = new PolyFunction { def apply[T](x: T): T = x } // error
3131
| ^^^^^^^^^^^^
32-
| `PolyFunction` marker trait is reserved for compiler generated refinements
32+
| Direct use of `PolyFunction` is not allowed. Use lambda instead.

Diff for: tests/neg/i10075.scala

+2-1
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,14 @@
11
val poly = [T] => (x: T) => x
22
val foo = new PolyFunction { } // error
3+
val foo2 = new scala.PolyFunction { } // error
34
val bar = new PolyFunction { def bar = 23 } // error
45
val baz = new PolyFunction { def apply = 23 } // error
56
val qux = new PolyFunction { def apply[T] = 47 } // error
67
val quxx = new PolyFunction { def apply[T](x: T): T = x } // error
78

89
trait PolyTrait extends PolyFunction // error
910

10-
class PolyClass extends PolyTrait { // error
11+
class PolyClass extends PolyFunction { // error
1112
def apply[T](x: T): T = x
1213
}
1314

Diff for: tests/neg/i18302a.scala

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
11
def test = polyFun(1)
22

3-
def polyFun: PolyFunction { def apply(x: Int): Int } =
3+
def polyFun: PolyFunction { def apply(x: Int): Int } = // error
44
new PolyFunction { def apply(x: Int): Int = x + 1 } // error

Diff for: tests/neg/i18302b.check

+4-4
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
1-
-- Error: tests/neg/i18302b.scala:3:32 ---------------------------------------------------------------------------------
1+
-- Error: tests/neg/i18302b.scala:3:13 ---------------------------------------------------------------------------------
22
3 |def polyFun: PolyFunction { def apply(x: Int)(y: Int): Int } = // error
3-
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
4-
|Implementation restriction: PolyFunction apply must have exactly one parameter list and optionally type arguments. No by-name nor varags are allowed.
3+
| ^^^^^^^^^^^^
4+
| Direct use of `PolyFunction` is not allowed. Use lambda instead.
55
-- Error: tests/neg/i18302b.scala:4:6 ----------------------------------------------------------------------------------
66
4 | new PolyFunction: // error
77
| ^^^^^^^^^^^^
8-
| `PolyFunction` marker trait is reserved for compiler generated refinements
8+
| Direct use of `PolyFunction` is not allowed. Use lambda instead.

Diff for: tests/neg/i18302c.check

+4-4
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
1-
-- Error: tests/neg/i18302c.scala:4:32 ---------------------------------------------------------------------------------
1+
-- Error: tests/neg/i18302c.scala:4:13 ---------------------------------------------------------------------------------
22
4 |def polyFun: PolyFunction { def foo(x: Int): Int } = // error
3-
| ^^^^^^^^^^^^^^^^^^^^
4-
| PolyFunction only supports apply method refinements
3+
| ^^^^^^^^^^^^
4+
| Direct use of `PolyFunction` is not allowed. Use lambda instead.
55
-- Error: tests/neg/i18302c.scala:5:6 ----------------------------------------------------------------------------------
66
5 | new PolyFunction { def foo(x: Int): Int = x + 1 } // error
77
| ^^^^^^^^^^^^
8-
| `PolyFunction` marker trait is reserved for compiler generated refinements
8+
| Direct use of `PolyFunction` is not allowed. Use lambda instead.

Diff for: tests/neg/i18302d.check

+4-4
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
1-
-- Error: tests/neg/i18302d.scala:1:32 ---------------------------------------------------------------------------------
1+
-- Error: tests/neg/i18302d.scala:1:13 ---------------------------------------------------------------------------------
22
1 |def polyFun: PolyFunction { def apply: Int } = // error
3-
| ^^^^^^^^^^^^^^
4-
|Implementation restriction: PolyFunction apply must have exactly one parameter list and optionally type arguments. No by-name nor varags are allowed.
3+
| ^^^^^^^^^^^^
4+
| Direct use of `PolyFunction` is not allowed. Use lambda instead.
55
-- Error: tests/neg/i18302d.scala:2:6 ----------------------------------------------------------------------------------
66
2 | new PolyFunction { def apply: Int = 1 } // error
77
| ^^^^^^^^^^^^
8-
| `PolyFunction` marker trait is reserved for compiler generated refinements
8+
| Direct use of `PolyFunction` is not allowed. Use lambda instead.

Diff for: tests/neg/i18302e.check

+5-5
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,12 @@
11
-- Error: tests/neg/i18302e.scala:1:13 ---------------------------------------------------------------------------------
22
1 |def polyFun: PolyFunction { } = // error
3-
| ^^^^^^^^^^^^^^^^^
4-
| PolyFunction subtypes must refine the apply method
3+
| ^^^^^^^^^^^^
4+
| Direct use of `PolyFunction` is not allowed. Use lambda instead.
55
-- Error: tests/neg/i18302e.scala:2:6 ----------------------------------------------------------------------------------
66
2 | new PolyFunction { } // error
77
| ^^^^^^^^^^^^
8-
| `PolyFunction` marker trait is reserved for compiler generated refinements
8+
| Direct use of `PolyFunction` is not allowed. Use lambda instead.
99
-- Error: tests/neg/i18302e.scala:4:15 ---------------------------------------------------------------------------------
1010
4 |def polyFun(f: PolyFunction { }) = () // error
11-
| ^^^^^^^^^^^^^^^^^
12-
| PolyFunction subtypes must refine the apply method
11+
| ^^^^^^^^^^^^
12+
| Direct use of `PolyFunction` is not allowed. Use lambda instead.

Diff for: tests/neg/i18302f.check

+5-5
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,20 @@
11
-- Error: tests/neg/i18302f.scala:1:13 ---------------------------------------------------------------------------------
22
1 |def polyFun: PolyFunction = // error
33
| ^^^^^^^^^^^^
4-
| PolyFunction subtypes must refine the apply method
4+
| Direct use of `PolyFunction` is not allowed. Use lambda instead.
55
-- Error: tests/neg/i18302f.scala:2:6 ----------------------------------------------------------------------------------
66
2 | new PolyFunction { } // error
77
| ^^^^^^^^^^^^
8-
| `PolyFunction` marker trait is reserved for compiler generated refinements
8+
| Direct use of `PolyFunction` is not allowed. Use lambda instead.
99
-- Error: tests/neg/i18302f.scala:4:16 ---------------------------------------------------------------------------------
1010
4 |def polyFun2(a: PolyFunction) = () // error
1111
| ^^^^^^^^^^^^
12-
| PolyFunction subtypes must refine the apply method
12+
| Direct use of `PolyFunction` is not allowed. Use lambda instead.
1313
-- Error: tests/neg/i18302f.scala:6:14 ---------------------------------------------------------------------------------
1414
6 |val polyFun3: PolyFunction = // error
1515
| ^^^^^^^^^^^^
16-
| PolyFunction subtypes must refine the apply method
16+
| Direct use of `PolyFunction` is not allowed. Use lambda instead.
1717
-- Error: tests/neg/i18302f.scala:7:6 ----------------------------------------------------------------------------------
1818
7 | new PolyFunction { } // error
1919
| ^^^^^^^^^^^^
20-
| `PolyFunction` marker trait is reserved for compiler generated refinements
20+
| Direct use of `PolyFunction` is not allowed. Use lambda instead.

0 commit comments

Comments
 (0)